diff --git a/.gitattribute b/.gitattribute new file mode 100644 index 0000000..2c28a5e --- /dev/null +++ b/.gitattribute @@ -0,0 +1,6 @@ +*.go text eol=lf +*.json text eol=lf +*.dat text eol=lf +*.bin text eol=lf +*.proto text eol=lf +*.xml text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cd6942 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.exe~ +*.test +*.log +*.log.* +.idea +.vscode +*.ts +**/.DS_Store + +# linux可执行文件 +/dbproxy/dbproxy +/mgrsrv/mgrsrv +/gatesrv/gatesrv +/gamesrv/gamesrv +/worldsrv/worldsrv +/robot/robot +/schedulesrv/schedulesrv +/ranksrv/ranksrv + +/bin/* +**/backup diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 74e3d6e..1e01a0a 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,4 @@ -# game +## win88 +服务端业务代码 - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://git.pogorockgames.com/mango-games/server/game.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.pogorockgames.com/mango-games/server/game/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/api3th/common.go b/api3th/common.go new file mode 100644 index 0000000..96edf23 --- /dev/null +++ b/api3th/common.go @@ -0,0 +1,79 @@ +package api3th + +import "bytes" + +// 牌数据转换 +// 1 2 3 4 5 6 7 8 9 10 11 12 13 52 53 => +// A 2 3 4 5 6 7 8 9 T J Q K B C +func CardValueToShowCard(cards []int32) string { + ret := bytes.NewBuffer([]byte{}) + for _, v := range cards { + ret.WriteString(AIRecordMap[GetPoint(v)]) + } + return ret.String() +} + +// 扑克牌点数 +func GetPoint(v int32) int32 { + if v >= 52 { + return v + } + return v%13 + 1 +} + +var AIRecordMap = map[int32]string{ + 53: "C", + 52: "B", + 1: "A", + 2: "2", + 3: "3", + 4: "4", + 5: "5", + 6: "6", + 7: "7", + 8: "8", + 9: "9", + 10: "T", + 11: "J", + 12: "Q", + 13: "K", +} + +var AIPointMap = map[string]int32{ + "D": 53, + "X": 52, + "C": 53, + "B": 52, + "A": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + "T": 10, + "J": 11, + "Q": 12, + "K": 13, +} + +// 德州扑克接入的机器人接口需要的牌定义 +// SHDC 黑红梅方 +// 0:2S 1:3S ... 11:13S 12:1S +// 13:2H 14:3H ... 24:13H 25:1H +// 26:2D 27:3D ... 37:13D 38:1D +// 39:2C 40:3C ... 50:13C 51:1C +func DZPCardToAICard(card int32) int32 { + var ret int32 + // 点数 + if card%13 == 0 { + ret += 12 + } else { + ret = card%13 - 1 + } + // 花色 + ret += (3 - card/13) * 13 + return ret +} diff --git a/api3th/config.go b/api3th/config.go new file mode 100644 index 0000000..8d1002d --- /dev/null +++ b/api3th/config.go @@ -0,0 +1,240 @@ +package api3th + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/cihub/seelog" + "github.com/google/go-querystring/query" + "io" + "mongo.games.com/game/common" + "net/http" + "strings" + "sync" + "sync/atomic" + "time" +) + +const ( + // 智能机器人接口 + APITypeAI = 0 + // 智能化运营接口 + APITypeSmart = 1 +) + +type BaseConfig struct { + sync.Once + methods map[string]string + urlNames map[string]string + seqNo uint64 // 日志序号 + IPAddr string + AuthKey string // 接口认证秘钥 + TimeoutDuration time.Duration // 请求超时时长 + Name string // 游戏名称 + ApiType int // 接口类型 +} + +func (c *BaseConfig) LogName() string { + return fmt.Sprint(c.Name, "Logger") +} + +func (c *BaseConfig) Log() seelog.LoggerInterface { + return common.GetLoggerInstanceByName(c.LogName()) +} + +func (c *BaseConfig) OrderNum() uint64 { + return atomic.AddUint64(&c.seqNo, 1) +} + +func (c *BaseConfig) Register(method, name, urlName string) { + if method == "" { + method = "POST" + } + if _, ok := c.urlNames[name]; ok { + panic(fmt.Sprintf("api3th registered name:%s urlName:%s", name, urlName)) + } + c.urlNames[name] = urlName + c.methods[name] = method +} + +func (c *BaseConfig) Do(name string, req interface{}, args ...time.Duration) (resp []byte, err error) { + // 加载配置 + c.Once.Do(func() { + + c.AuthKey = common.CustomConfig.GetString(fmt.Sprintf("%sApiKey", c.Name)) + c.TimeoutDuration = time.Duration(common.CustomConfig.GetInt(fmt.Sprintf("%sApi3thTimeout", c.Name))) * time.Second + }) + + timeout := time.Duration(-1) + if len(args) > 0 { + timeout = args[0] + } + + url, ok := c.urlNames[name] + if !ok { + return nil, errors.New(fmt.Sprintf("api3th no register %s", name)) + } + + var code int + method := strings.ToUpper(c.methods[name]) + switch method { + case "POST": + code, resp, err = c.Post(url, req, timeout) + case "POSTFORM": + code, resp, err = c.PostForm(url, req) + case "POSTFORMTIMEOUT": + code, resp, err = c.PostFormTimeOut(url, req, timeout) + default: + err = errors.New(fmt.Sprintf("api3th method error %s", c.methods[name])) + } + if code != http.StatusOK { + err = errors.New(fmt.Sprint("error code ", code)) + } + return +} + +func (c *BaseConfig) Post(url string, req interface{}, timeout time.Duration) (int, []byte, error) { + logger := c.Log() + data, err := json.Marshal(req) + if err != nil { + logger.Error("json.Marshal() error ", err) + return 0, nil, err + } + seqNo := c.OrderNum() + logger.Tracef("%s PostRequest[%d] Url %s Param: %s", c.Name, seqNo, fmt.Sprint(c.IPAddr, url), string(data)) + r, err := http.NewRequest("POST", fmt.Sprint(c.IPAddr, url), bytes.NewBuffer(data)) + if err != nil { + logger.Errorf("%s PostRequest[%d] http.NewRequest() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + r.Header.Set("Content-Type", "application/json") + if c.AuthKey != "" { + r.Header.Set("Ocp-Apim-Subscription-Key", c.AuthKey) + } + cli := http.Client{} + if timeout >= 0 { + cli.Timeout = timeout + } else { + cli.Timeout = c.TimeoutDuration + } + resp, err := cli.Do(r) + if err != nil { + logger.Errorf("%s PostRequest[%d] httpClient.Do() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + defer resp.Body.Close() + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + logger.Errorf("%s PostRequest[%d] io.ReadAll() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + + logger.Tracef("%s PostRequest[%d] StatusCode=%d Body=%s", c.Name, seqNo, resp.StatusCode, string(respBytes)) + return resp.StatusCode, respBytes, nil +} + +func (c *BaseConfig) PostFormTimeOut(url string, req interface{}, timeout time.Duration) (int, []byte, error) { + logger := c.Log() + data, err := json.Marshal(req) + if err != nil { + logger.Error("json.Marshal() error ", err) + return 0, nil, err + } + seqNo := c.OrderNum() + logger.Tracef("%s PostRequest[%d] Url %s Param: %s", c.Name, seqNo, fmt.Sprint(c.IPAddr, url), string(data)) + + params, query_err := query.Values(req) + if query_err != nil { + logger.Errorf("(c *BaseConfig) PostForm query.Values %v", query_err) + return 0, nil, query_err + } + rp := strings.NewReader(params.Encode()) + + r, err := http.NewRequest("POST", fmt.Sprint(c.IPAddr, url), rp) + if err != nil { + logger.Errorf("%s PostRequest[%d] http.NewRequest() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if c.AuthKey != "" { + r.Header.Set("Ocp-Apim-Subscription-Key", c.AuthKey) + } + cli := http.Client{} + if timeout >= 0 { + cli.Timeout = timeout + } else { + cli.Timeout = c.TimeoutDuration + } + resp, err := cli.Do(r) + if err != nil { + //logger.Errorf("%s PostRequest[%d] httpClient.Do() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + defer resp.Body.Close() + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + logger.Errorf("%s PostRequest[%d] io.ReadAll() error: %v", c.Name, seqNo, err) + return 0, nil, err + } + + logger.Tracef("%s PostRequest[%d] StatusCode=%d Body=%s", c.Name, seqNo, resp.StatusCode, string(respBytes)) + return resp.StatusCode, respBytes, nil +} + +func (c *BaseConfig) PostForm(url1 string, req interface{}) (int, []byte, error) { + logger := c.Log() + + data, err := json.Marshal(req) + if err != nil { + logger.Error("json.Marshal() error ", err) + return 0, nil, err + } + c.Log().Info(fmt.Sprint(c.IPAddr, url1), string(data)) + + params, query_err := query.Values(req) + if query_err != nil { + logger.Errorf("(c *BaseConfig) PostForm query.Values %v", query_err) + return 0, nil, query_err + } + //c.Log().Info(fmt.Sprint(c.IPAddr, url1),params.Encode()) + //fmt.Println(vals.Encode()) + // + //params := req.(url.Values) + res, err := http.PostForm(fmt.Sprint(c.IPAddr, url1), params) + if err != nil { + logger.Errorf("(c *BaseConfig) PostForm http.PostForm %v", err) + return 0, nil, err + } + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + if err != nil { + logger.Errorf("(c *BaseConfig) PostForm io.ReadAll %v", err) + return 0, nil, err + } + + //println(url, " ret:", string(body[:])) + + //result := make(map[string]interface{}) + //json.Unmarshal(body, &result) + + return http.StatusOK, body, err +} + +func (c *BaseConfig) Switch() bool { + switch c.ApiType { + case 1: // 智能化运营 + return common.CustomConfig.GetBool(fmt.Sprintf("Use%sSmartApi3th", c.Name)) + default: // ai + return common.CustomConfig.GetBool(fmt.Sprintf("Use%sRobotApi3th", c.Name)) + } +} + +func NewBaseConfig(name string, apiType int) *BaseConfig { + ret := new(BaseConfig) + ret.methods = make(map[string]string) + ret.urlNames = make(map[string]string) + ret.Name = name + ret.ApiType = apiType + return ret +} diff --git a/api3th/doc.go b/api3th/doc.go new file mode 100644 index 0000000..d425044 --- /dev/null +++ b/api3th/doc.go @@ -0,0 +1,32 @@ +package api3th + +/* +1.三方接口接入方法参考推饼(智能化运营)和十点半(智能ai) +新增智能ai配置 config.json +"costum": { + "UseSDBRobotApi3th":true, + "SDBApi3thTimeout": 10, + "SDBApi3thAddr":"https://api.vonabcs.com", + "SDBApiKey": "284672e9733249c69a4d1558b9080f1e", +} +新增智能化运营配置 config.json +"costum": { + "UseTBSmartApi3th": true, + "TBApi3thTimeout": 10, + "TBApi3thAddr":"https://api.vonabcs.com", + "TBApiKey": "a8c38779-41e0-4a78-b996-8964e752a4d3", +} + +2.智能化运营配置在mode/gameparam.go 中的GameParam结构体中新增配置;参考百人炸金花和对战场21点 +新增配置 data/gameparam.json +"SmartHZJH": { + "SwitchPlatform": ["1"], + "SwitchABTestTailFilter": [], + "SwitchABTestSnIdFilter": [], + "ABTestTick":0 , + "SwitchPlatformABTestTick": [] , + "SwitchPlatformABTestTickSnIdFilter": [] +} + +3.在具体游戏中添加代码实现智能化运营或智能ai +*/ diff --git a/api3th/simlog.go b/api3th/simlog.go new file mode 100644 index 0000000..448923a --- /dev/null +++ b/api3th/simlog.go @@ -0,0 +1,64 @@ +package api3th + +import ( + "fmt" + "github.com/cihub/seelog" +) + +type TestLog struct { + seelog.LoggerInterface +} + +func (this *TestLog) Tracef(format string, params ...interface{}) { + fmt.Printf(format+"\n", params...) +} +func (this *TestLog) Debugf(format string, params ...interface{}) { + fmt.Printf(format+"\n", params...) +} +func (this *TestLog) Infof(format string, params ...interface{}) { + fmt.Printf(format+"\n", params...) +} +func (this *TestLog) Warnf(format string, params ...interface{}) error { + fmt.Printf(format+"\n", params...) + return nil +} +func (this *TestLog) Errorf(format string, params ...interface{}) error { + fmt.Printf(format+"\n", params...) + return nil +} +func (this *TestLog) Criticalf(format string, params ...interface{}) error { + fmt.Printf(format+"\n", params...) + return nil +} +func (this *TestLog) Trace(v ...interface{}) { + fmt.Println(v...) +} +func (this *TestLog) Debug(v ...interface{}) { + fmt.Println(v...) +} +func (this *TestLog) Info(v ...interface{}) { + fmt.Println(v...) +} +func (this *TestLog) Warn(v ...interface{}) error { + fmt.Println(v...) + return nil +} +func (this *TestLog) Error(v ...interface{}) error { + fmt.Println(v...) + return nil +} +func (this *TestLog) Critical(v ...interface{}) error { + fmt.Println(v...) + return nil +} +func (this *TestLog) Close() { +} +func (this *TestLog) Flush() { + +} +func (this *TestLog) Closed() bool { + return true +} +func (this *TestLog) SetAdditionalStackDepth(depth int) error { + return nil +} diff --git a/api3th/smart/chesstitians/api.go b/api3th/smart/chesstitians/api.go new file mode 100644 index 0000000..c44b201 --- /dev/null +++ b/api3th/smart/chesstitians/api.go @@ -0,0 +1,66 @@ +package tienlen + +import "mongo.games.com/game/api3th" + +var Config = api3th.NewBaseConfig("TIENLEN", api3th.APITypeSmart) + +const ( + Predict = "predict" + Legal = "legal" +) + +func init() { + Config.Register("PostFormTimeOut", Predict, "/predict") + Config.Register("PostFormTimeOut", Legal, "/legal") +} + +type PredictRequest struct { + Bomb_num int `url:"bomb_num" form:"bomb_num"` + Card_play_action_seq string `url:"card_play_action_seq" form:"card_play_action_seq"` + Last_move_0 string `url:"last_move_0" form:"last_move_0"` + Last_move_1 string `url:"last_move_1" form:"last_move_1"` + Last_move_2 string `url:"last_move_2" form:"last_move_2"` + Last_move_3 string `url:"last_move_3" form:"last_move_3"` + Num_cards_left_0 int `url:"num_cards_left_0" form:"num_cards_left_0"` + Num_cards_left_1 int `url:"num_cards_left_1" form:"num_cards_left_1"` + Num_cards_left_2 int `url:"num_cards_left_2" form:"num_cards_left_2"` + Num_cards_left_3 int `url:"num_cards_left_3" form:"num_cards_left_3"` + Other_hand_cards string `url:"other_hand_cards" form:"other_hand_cards"` + Played_cards_0 string `url:"played_cards_0" form:"played_cards_0"` + Played_cards_1 string `url:"played_cards_1" form:"played_cards_1"` + Played_cards_2 string `url:"played_cards_2" form:"played_cards_2"` + Played_cards_3 string `url:"played_cards_3" form:"played_cards_3"` + Player_hand_cards string `url:"player_hand_cards" form:"player_hand_cards"` + Player_position int `url:"player_position" form:"player_position"` +} + +type PredictResponse struct { + Message string `json:"message"` + Status int `json:"status"` + Result map[string]string `json:"result"` + WinRates map[string]string `json:"win_rates"` +} + +type LegalRequest struct { + Player_hand_cards int `url:"player_hand_cards" form:"player_hand_cards"` + Rival_move string `url:"rival_move" form:"rival_move"` +} + +type LegalResponse struct { + Legal_action string `json:"legal_action"` + Message string `json:"message"` + Status int `json:"status"` +} + +var CardToAiCard = map[int32]int32{ + -1: -1, 0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 7, 6: 8, 7: 9, 8: 10, 9: 11, 10: 12, 11: 0, 12: 1, + 13: 15, 14: 16, 15: 17, 16: 18, 17: 19, 18: 20, 19: 21, 20: 22, 21: 23, 22: 24, 23: 25, 24: 13, 25: 14, + 26: 28, 27: 29, 28: 30, 29: 31, 30: 32, 31: 33, 32: 34, 33: 35, 34: 36, 35: 37, 36: 38, 37: 26, 38: 27, + 39: 41, 40: 42, 41: 43, 42: 44, 43: 45, 44: 46, 45: 47, 46: 48, 47: 49, 48: 50, 49: 51, 50: 39, 51: 40, +} +var AiCardToCard = map[int32]int32{ + -1: -1, 2: 0, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 0: 11, 1: 12, + 15: 13, 16: 14, 17: 15, 18: 16, 19: 17, 20: 18, 21: 19, 22: 20, 23: 21, 24: 22, 25: 23, 13: 24, 14: 25, + 28: 26, 29: 27, 30: 28, 31: 29, 32: 30, 33: 31, 34: 32, 35: 33, 36: 34, 37: 35, 38: 36, 26: 37, 27: 38, + 41: 39, 42: 40, 43: 41, 44: 42, 45: 43, 46: 44, 47: 45, 48: 46, 49: 47, 50: 48, 51: 49, 39: 50, 40: 51, +} diff --git a/api3th/smart/tienlen/api.go b/api3th/smart/tienlen/api.go new file mode 100644 index 0000000..453a882 --- /dev/null +++ b/api3th/smart/tienlen/api.go @@ -0,0 +1,76 @@ +package tienlen + +import "mongo.games.com/game/api3th" + +var Config = api3th.NewBaseConfig("TIENLEN", api3th.APITypeSmart) + +const ( + Predict = "predict" + Legal = "legal" +) + +func init() { + Config.Register("PostFormTimeOut", Predict, "/predict") + Config.Register("PostFormTimeOut", Legal, "/legal") +} + +type PredictRequest struct { + Bomb_num int `url:"bomb_num" form:"bomb_num"` + Card_play_action_seq string `url:"card_play_action_seq" form:"card_play_action_seq"` + Last_move_0 string `url:"last_move_0" form:"last_move_0"` + Last_move_1 string `url:"last_move_1" form:"last_move_1"` + Last_move_2 string `url:"last_move_2" form:"last_move_2"` + Last_move_3 string `url:"last_move_3" form:"last_move_3"` + Num_cards_left_0 int `url:"num_cards_left_0" form:"num_cards_left_0"` + Num_cards_left_1 int `url:"num_cards_left_1" form:"num_cards_left_1"` + Num_cards_left_2 int `url:"num_cards_left_2" form:"num_cards_left_2"` + Num_cards_left_3 int `url:"num_cards_left_3" form:"num_cards_left_3"` + Other_hand_cards string `url:"other_hand_cards" form:"other_hand_cards"` + Played_cards_0 string `url:"played_cards_0" form:"played_cards_0"` + Played_cards_1 string `url:"played_cards_1" form:"played_cards_1"` + Played_cards_2 string `url:"played_cards_2" form:"played_cards_2"` + Played_cards_3 string `url:"played_cards_3" form:"played_cards_3"` + Player_hand_cards string `url:"player_hand_cards" form:"player_hand_cards"` + Player_position int `url:"player_position" form:"player_position"` + IsTienLenYule bool `url:"isTienLenYule" form:"isTienLenYule"` + IsFirstHand bool `url:"isFirstHand" form:"isFirstHand"` + Cards_left_0 []int32 `url:"cards_left_0" form:"cards_left_0"` + Cards_left_1 []int32 `url:"cards_left_1" form:"cards_left_1"` + Cards_left_2 []int32 `url:"cards_left_2" form:"cards_left_2"` + Cards_left_3 []int32 `url:"cards_left_3" form:"cards_left_3"` + Last_pos int32 `url:"last_pos" form:"last_pos"` + IsEnd bool `url:"isend" form:"isend"` + WinSnids []int32 `url:"winsnids" form:"winsnids"` + IsWin bool `url:"iswin" form:"iswin"` +} + +type PredictResponse struct { + Message string `json:"message"` + Status int `json:"status"` + Result map[string]string `json:"result"` + WinRates map[string]string `json:"win_rates"` +} + +type LegalRequest struct { + Player_hand_cards int `url:"player_hand_cards" form:"player_hand_cards"` + Rival_move string `url:"rival_move" form:"rival_move"` +} + +type LegalResponse struct { + Legal_action string `json:"legal_action"` + Message string `json:"message"` + Status int `json:"status"` +} + +var CardToAiCard = map[int32]int32{ + -1: -1, 0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 7, 6: 8, 7: 9, 8: 10, 9: 11, 10: 12, 11: 0, 12: 1, + 13: 15, 14: 16, 15: 17, 16: 18, 17: 19, 18: 20, 19: 21, 20: 22, 21: 23, 22: 24, 23: 25, 24: 13, 25: 14, + 26: 28, 27: 29, 28: 30, 29: 31, 30: 32, 31: 33, 32: 34, 33: 35, 34: 36, 35: 37, 36: 38, 37: 26, 38: 27, + 39: 41, 40: 42, 41: 43, 42: 44, 43: 45, 44: 46, 45: 47, 46: 48, 47: 49, 48: 50, 49: 51, 50: 39, 51: 40, +} +var AiCardToCard = map[int32]int32{ + -1: -1, 2: 0, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 0: 11, 1: 12, + 15: 13, 16: 14, 17: 15, 18: 16, 19: 17, 20: 18, 21: 19, 22: 20, 23: 21, 24: 22, 25: 23, 13: 24, 14: 25, + 28: 26, 29: 27, 30: 28, 31: 29, 32: 30, 33: 31, 34: 32, 35: 33, 36: 34, 37: 35, 38: 36, 26: 37, 27: 38, + 41: 39, 42: 40, 43: 41, 44: 42, 45: 43, 46: 44, 47: 45, 48: 46, 49: 47, 50: 48, 51: 49, 39: 50, 40: 51, +} diff --git a/build-sub.bat b/build-sub.bat new file mode 100644 index 0000000..ae9a943 --- /dev/null +++ b/build-sub.bat @@ -0,0 +1,11 @@ +@echo off +echo Build %1 task! +cd %1 +go fmt +go vet +go build -v +echo errorlevel:%errorlevel% +if "%errorlevel%"=="0" exit +if not "%errorlevel%"=="0" echo %1 build failed! +pause +exit \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..215fc6a --- /dev/null +++ b/build.bat @@ -0,0 +1,29 @@ +go env -w GO111MODULE=off +@echo "go fmt common..." +cd common +go fmt + +@echo "go fmt api3th..." +cd ../api3th +go fmt + +@echo "go fmt model..." +cd ../model +go fmt + +@echo "go fmt webapi..." +cd ../webapi +go fmt + +@echo "go fmt lib complete!" + +cd .. +start build-sub.bat dbproxy +start build-sub.bat mgrsrv +start build-sub.bat gatesrv +start build-sub.bat worldsrv +start build-sub.bat gamesrv +start build-sub.bat robot +start build-sub.bat ranksrv +@echo "Wait all build task complete!" +pause diff --git a/build_linux.bat b/build_linux.bat new file mode 100644 index 0000000..f589569 --- /dev/null +++ b/build_linux.bat @@ -0,0 +1,31 @@ +set CGO_ENABLED=0 +set GOOS=linux +set GOARCH=amd64 +go env -w GO111MODULE=off +cd gatesrv +go fmt +go build +cd ../mgrsrv +go fmt +go build +cd ../worldsrv +go fmt +go build +cd ../gamesrv +go fmt +go build +rem cd ../minigame +rem go fmt +rem go build +cd ../robot +go fmt +go build +cd ../dbproxy +go fmt +go build +cd ../ranksrv +go fmt +go build +cd .. +@echo "complete" +pause diff --git a/build_mac.bat b/build_mac.bat new file mode 100644 index 0000000..22bc2a9 --- /dev/null +++ b/build_mac.bat @@ -0,0 +1,31 @@ +set CGO_ENABLED=0 +set GOOS=darwin +set GOARCH=amd64 +go env -w GO111MODULE=off +cd routesrv +go fmt +go build +cd ../gatesrv +go fmt +go build +cd ../mgrsrv +go fmt +go build +cd ../worldsrv +go fmt +go build +cd ../gamesrv +go fmt +go build +cd ../robot +go fmt +go build +cd ../datasrv +go fmt +go build +cd ../schedulesrv +go fmt +go build +cd .. +@echo "complete" +pause diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..92ed5b8 --- /dev/null +++ b/clean.bat @@ -0,0 +1,14 @@ +del /F/S gatesrv\gatesrv +del /F/S gatesrv\gatesrv.exe +del /F/S gamesrv\gamesrv +del /F/S gamesrv\gamesrv.exe +del /F/S mgrsrv\mgrsrv +del /F/S mgrsrv\mgrsrv.exe +del /F/S worldsrv\worldsrv +del /F/S worldsrv\worldsrv.exe +del /F/S robot\robot +del /F/S robot\robot.exe +del /F/S dbproxy\dbproxy +del /F/S dbproxy\dbproxy.exe +del /F/S minigame\minigame +del /F/S minigame\minigame.exe \ No newline at end of file diff --git a/close.bat b/close.bat new file mode 100644 index 0000000..7472d30 --- /dev/null +++ b/close.bat @@ -0,0 +1,9 @@ +TASKKILL /F /IM gatesrv.exe +TASKKILL /F /IM mgrsrv.exe +TASKKILL /F /IM worldsrv.exe +TASKKILL /F /IM gamesrv.exe +TASKKILL /F /IM robot.exe +TASKKILL /F /IM minigame.exe +TASKKILL /F /IM dbproxy.exe +TASKKILL /F /IM ranksrv.exe +clrlogs.bat \ No newline at end of file diff --git a/clrlogs.bat b/clrlogs.bat new file mode 100644 index 0000000..51c3833 --- /dev/null +++ b/clrlogs.bat @@ -0,0 +1,14 @@ +del /F/S gatesrv\*.log +del /F/S gatesrv\*.log.* +del /F/S mgrsrv\*.log +del /F/S mgrsrv\*.log.* +del /F/S worldsrv\*.log +del /F/S worldsrv\*.log.* +del /F/S gamesrv\*.log +del /F/S gamesrv\*.log.* +del /F/S dbproxy\*.log +del /F/S dbproxy\*.log.* +del /F/S minigame\*.log +del /F/S minigame\*.log.* +del /F/S robot\*.log +del /F/S robot\*.log.* \ No newline at end of file diff --git a/common/action.go b/common/action.go new file mode 100644 index 0000000..f72be8d --- /dev/null +++ b/common/action.go @@ -0,0 +1,54 @@ +package common + +import ( + "mongo.games.com/goserver/core/logger" +) + +const ( + A_USER_BLACK = 1 //增加黑名单 +) + +type Action struct { + ActionID int //执行id + ActionParamInt64 []int //整形参数 + ActionParamFloat []float64 //浮点参数 + ActionParamString []string //字符串参数 +} + +var ActionMgrSington = &ActionMgr{ + pool: make(map[int]ActionBase), +} + +func init() { + +} + +type ActionMgr struct { + pool map[int]ActionBase +} + +func (this *ActionMgr) ActionGroup(need interface{}, action []*Action) bool { + for i := 0; i < len(action); i++ { + this.action(need, action[i]) + } + + return true +} + +func (this *ActionMgr) action(need interface{}, action *Action) bool { + a, ok := this.pool[action.ActionID] + if !ok { + logger.Logger.Warnf("no this action %v", action.ActionID) + return false + } + + return a.Action(need, action) +} + +func (this *ActionMgr) Register(cid int, c ActionBase) { + this.pool[cid] = c +} + +type ActionBase interface { + Action(need interface{}, action *Action) bool +} diff --git a/common/action_srvctrl.go b/common/action_srvctrl.go new file mode 100644 index 0000000..264ad44 --- /dev/null +++ b/common/action_srvctrl.go @@ -0,0 +1,54 @@ +package common + +import ( + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" +) + +type GameSessState int + +const ( + GAME_SESS_STATE_OFF GameSessState = iota //关闭状态 + GAME_SESS_STATE_ON //开启状态 +) + +var ServerCtrlCallback func(int32) + +func RegisteServerCtrlCallback(cb func(int32)) { + ServerCtrlCallback = cb +} + +type ServerCtrlPacketFactory struct { +} + +type ServerCtrlHandler struct { +} + +func (this *ServerCtrlPacketFactory) CreatePacket() interface{} { + pack := &server.ServerCtrl{} + return pack +} + +func (this *ServerCtrlHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + + if sc, ok := data.(*server.ServerCtrl); ok { + logger.Logger.Trace("ServerCtrlHandler.Process== ", *sc) + switch sc.GetCtrlCode() { + case SrvCtrlCloseCode: + module.Stop() + } + + //回调 + if ServerCtrlCallback != nil { + ServerCtrlCallback(sc.GetCtrlCode()) + } + } + return nil +} + +func init() { + netlib.RegisterHandler(int(server.SSPacketID_PACKET_MS_SRVCTRL), &ServerCtrlHandler{}) + netlib.RegisterFactory(int(server.SSPacketID_PACKET_MS_SRVCTRL), &ServerCtrlPacketFactory{}) +} diff --git a/common/aes.go b/common/aes.go new file mode 100644 index 0000000..cb7bef6 --- /dev/null +++ b/common/aes.go @@ -0,0 +1,172 @@ +package common + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "fmt" + "mongo.games.com/goserver/core/logger" + "regexp" + "strconv" +) + +var key = []byte("kjh-vgjhhionoommmkokmokoo$%JH") + +// 加密 +func EnCrypt(orig []byte) (str string) { + defer func() { + err := recover() + if err != nil { + logger.Logger.Errorf("EnCrypt %v Error %v", string(orig), err) + str = string(orig) + } + }() + //将秘钥中的每个字节累加,通过sum实现orig的加密工作 + sum := 0 + for i := 0; i < len(key); i++ { + sum += int(key[0]) + } + + //给明文补码 + var pkcs_code = PKCS5Padding(orig, 8) + + //通过秘钥,对补码后的明文进行加密 + for j := 0; j < len(pkcs_code); j++ { + pkcs_code[j] += byte(sum) + } + //base64.URLEncoding.EncodeToString() + return base64.URLEncoding.EncodeToString(pkcs_code) +} + +// 补码 +func PKCS5Padding(orig []byte, size int) []byte { + //计算明文的长度 + length := len(orig) + padding := size - length%size + //向byte类型的数组中重复添加padding + repeats := bytes.Repeat([]byte{byte(padding)}, padding) + return append(orig, repeats...) +} + +// 解密 +func DeCrypt(text string) (str string) { + defer func() { + err := recover() + if err != nil { + logger.Logger.Errorf("DeCrypt %v Error %v", text, err) + str = text + } + }() + //orig, err := base64.StdEncoding.DecodeString(text) + orig, err := base64.URLEncoding.DecodeString(text) + if err != nil { + return "密文类型错误" + } + sum := 0 + for i := 0; i < len(key); i++ { + sum += int(key[0]) + } + + //解密 + for j := 0; j < len(orig); j++ { + orig[j] -= byte(sum) + } + + //去码 + var pkcs_unCode = PKCS5UnPadding(orig) + return string(pkcs_unCode) +} + +// 去码 +func PKCS5UnPadding(orig []byte) []byte { + //获取最后一位补码的数字 + var tail = int(orig[len(orig)-1]) + return orig[:(len(orig) - tail)] +} + +var aesRule, _ = regexp.Compile(`^[0-9]+$`) + +const ( + aeskey = "DoNotEditThisKeyDoNotEditThisKey" // 加密的密钥,绝不可以更改 +) + +// 下面的字符串,也绝不可以更改 +var defaultLetters = []rune("idjGfiRogsFnkdKgokdfgdow07u6978uxcvvLiPiDfjafOd2fuFJYYGBJuykbvfk") + +func AesEncrypt(origDataStr string) (str string) { + defer func() { + err := recover() + if err != nil { + logger.Logger.Errorf("AesEncrypt %v Error %v", origDataStr, err) + str = origDataStr + } + }() + strlen := len(origDataStr) + b := aesRule.MatchString(origDataStr) + //不全是数字,或长度为零,不加密 + if !b || strlen == 0 { + return origDataStr + } + phonenum, errint := strconv.Atoi(origDataStr) + if errint != nil { + return origDataStr + } + + text := []byte(origDataStr) + //指定加密、解密算法为AES,返回一个AES的Block接口对象 + block, err := aes.NewCipher([]byte(aeskey)) + if err != nil { + panic(err) + } + //指定计数器,长度必须等于block的块尺寸 + iv := string(defaultLetters[phonenum%(len(defaultLetters))]) + count := []byte(fmt.Sprintf("%016v", iv)) + //指定分组模式 + blockMode := cipher.NewCTR(block, count) + //执行加密、解密操作 + message := make([]byte, len(text)) + blockMode.XORKeyStream(message, text) + //返回明文或密文 + return fmt.Sprintf("%v%v", iv, base64.StdEncoding.EncodeToString(message)) + //return base64.StdEncoding.EncodeToString(message) +} + +func AesDecrypt(cryptedstr string) (str string) { + defer func() { + err := recover() + if err != nil { + logger.Logger.Errorf("AesDecrypt %v Error %v", cryptedstr, err) + str = cryptedstr + } + }() + strlen := len(cryptedstr) + b := aesRule.MatchString(cryptedstr) + //全是数字,或长度为零,不解密 + if b || strlen == 0 { + return cryptedstr + } + + iv := cryptedstr[:1] + str = cryptedstr[1:] + text, err := base64.StdEncoding.DecodeString(str) + if err != nil { + logger.Logger.Errorf("AesDecrypt %v Err:%v", cryptedstr, err) + return cryptedstr + } + + //指定加密、解密算法为AES,返回一个AES的Block接口对象 + block, err := aes.NewCipher([]byte(aeskey)) + if err != nil { + panic(err) + } + //指定计数器,长度必须等于block的块尺寸 + count := []byte(fmt.Sprintf("%016v", iv)) + //指定分组模式 + blockMode := cipher.NewCTR(block, count) + //执行加密、解密操作 + message := make([]byte, len(text)) + blockMode.XORKeyStream(message, text) + //返回明文或密文 + return string(message) +} diff --git a/common/clientparam.go b/common/clientparam.go new file mode 100644 index 0000000..614d437 --- /dev/null +++ b/common/clientparam.go @@ -0,0 +1,11 @@ +package common + +const ( + ClientSessionAttribute_State int = iota + ClientSessionAttribute_GameServer + ClientSessionAttribute_Buf + ClientSessionAttribute_WorldServer + ClientSessionAttribute_PlayerData + ClientSessionAttribute_GroupTag + ClientSessionAttribute_RankServer +) diff --git a/common/clientstate.go b/common/clientstate.go new file mode 100644 index 0000000..2fe6be7 --- /dev/null +++ b/common/clientstate.go @@ -0,0 +1,19 @@ +package common + +const ( + ClientState_WaitBindBundle int = iota + ClientState_WaitLogin + ClientState_Logined + ClientState_WaiteGetPlayerInfo + ClientState_CreatePlayer + ClientState_GetPlayerInfo + ClientState_EnterGame + ClientState_EnterMap + ClientState_EnterFight + ClientState_WaitLogout + ClientState_Logouted + ClientState_Dropline + ClientState_Droplined + ClientState_WaitRehold + ClientState_Reholded +) diff --git a/common/comm.go b/common/comm.go new file mode 100644 index 0000000..e36b436 --- /dev/null +++ b/common/comm.go @@ -0,0 +1,90 @@ +package common + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + protocol_game "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +type WGCoinSceneChange struct { + SnId int32 + SceneId int32 +} + +type WGAddCoin struct { + SnId int32 + Coin int64 + GainWay int32 + Oper string + Remark string + Broadcast bool + WriteLog bool +} + +func createMulticastPacket(packetid int, data interface{}, sis ...*protocol.MCSessionUnion) (rawproto.Message, error) { + pack := &protocol.SSPacketMulticast{ + Sessions: sis, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Info("MulticastPacketFactory.CreateMulticastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func SendToGate(sid int64, packetid int, rawpack interface{}, s *netlib.Session) bool { + if s == nil || rawpack == nil || sid == 0 { + return false + } + pack, err := createMulticastPacket(packetid, rawpack, + &protocol.MCSessionUnion{ + Mccs: &protocol.MCClientSession{ + SId: proto.Int64(sid)}}) + if err == nil && pack != nil { + if d, err := netlib.MarshalPacket(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), pack); err == nil { + return s.Send(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), d, true) + } else { + logger.Logger.Warn("SendToGate err:", err) + } + } + return false +} + +func SendToActThrSrv(packetid int, rawpack interface{}) bool { + if rawpack == nil { + return false + } + + replaySess := srvlib.ServerSessionMgrSington.GetSession(GetSelfAreaId(), ActThrServerType, ActThrServerID) + if replaySess != nil { + return replaySess.Send(int(packetid), rawpack) + } + return false +} + +func TransmitToServer(sid int64, packetid int, rawpack interface{}, s *netlib.Session) bool { + if d, err := netlib.MarshalPacket(packetid, rawpack); err == nil { + pack := &protocol_game.SSTransmit{ + PacketData: d, + SessionId: sid, + } + proto.SetDefaults(pack) + return s.Send(int(protocol_game.TransmitPacketID_PACKET_SS_PACKET_TRANSMIT), pack, true) + } else { + logger.Logger.Warn("TransmitToServer err:", err) + } + return false +} diff --git a/common/condition.go b/common/condition.go new file mode 100644 index 0000000..1bd2f18 --- /dev/null +++ b/common/condition.go @@ -0,0 +1,192 @@ +package common + +import ( + "math" + "strings" +) + +const ( + CMP_OP_EQ = 0 + CMP_OP_GT = 1 + CMP_OP_LT = 2 + CMP_OP_GTE = 3 + CMP_OP_LTE = 4 + CMP_OP_NEQ = 5 +) + +type Condition struct { + ConditionID int //条件id + ConditionCMP int //比较类型 + ConditionParamInt64 int64 //整形参数 + ConditionParamFloat64 float64 //浮点参数 + ConditionParamString string //字符串参数 +} + +func (this *Condition) CmpInt(n int64) bool { + switch this.ConditionCMP { + case CMP_OP_EQ: + if n == this.ConditionParamInt64 { + return true + } + case CMP_OP_GT: + if n > this.ConditionParamInt64 { + return true + } + case CMP_OP_LT: + if n < this.ConditionParamInt64 { + return true + } + case CMP_OP_GTE: + if n >= this.ConditionParamInt64 { + return true + } + case CMP_OP_LTE: + if n <= this.ConditionParamInt64 { + return true + } + case CMP_OP_NEQ: + if n != this.ConditionParamInt64 { + return true + } + } + return false +} + +func (this *Condition) CmpFloat64(n float64) bool { + switch this.ConditionCMP { + case CMP_OP_EQ: + if n == this.ConditionParamFloat64 { + return true + } + case CMP_OP_GT: + if n > this.ConditionParamFloat64 { + return true + } + case CMP_OP_LT: + if n < this.ConditionParamFloat64 { + return true + } + case CMP_OP_GTE: + if n >= this.ConditionParamFloat64 { + return true + } + case CMP_OP_LTE: + if n <= this.ConditionParamFloat64 { + return true + } + case CMP_OP_NEQ: + if math.Abs(n-this.ConditionParamFloat64) > 0.00001 { + return true + } + } + return false +} + +func (this *Condition) CmpString(n string) bool { + switch this.ConditionCMP { + case CMP_OP_EQ: + if strings.Compare(n, this.ConditionParamString) == 0 { + return true + } + case CMP_OP_NEQ: + if strings.Compare(n, this.ConditionParamString) != 0 { + return true + } + case CMP_OP_GT: + if strings.Compare(n, this.ConditionParamString) > 0 { + return true + } + case CMP_OP_LT: + if strings.Compare(n, this.ConditionParamString) < 0 { + return true + } + case CMP_OP_GTE: + if strings.Compare(n, this.ConditionParamString) >= 0 { + return true + } + case CMP_OP_LTE: + if strings.Compare(n, this.ConditionParamString) <= 0 { + return true + } + } + return false +} + +func (this *Condition) CmpBool(n bool) bool { + cmpBool := false + if this.ConditionParamInt64 > 0 { + cmpBool = true + } + switch this.ConditionCMP { + case CMP_OP_EQ: + if n == cmpBool { + return true + } + case CMP_OP_NEQ: + if n != cmpBool { + return true + } + } + return false +} + +//todo 检查参数,临时处理,暂时没想到更好的办法 + +var ConditionMgrSington = &ConditionMgr{ + pool: make(map[int]CheckBase), +} + +type ConditionMgr struct { + pool map[int]CheckBase +} + +func (this *ConditionMgr) CheckGroup(need interface{}, con [][]*Condition) bool { + if len(con) <= 0 { + return false + } + //组内或,组间与 + for i := 0; i < len(con); i++ { + cArray := con[i] + isOk := false + for j := 0; j < len(cArray); j++ { + if this.check(need, cArray[j]) { + isOk = true + break + } + } + if !isOk { + return false + } + } + + return true +} + +func (this *ConditionMgr) check(need interface{}, con *Condition) bool { + c, ok := this.pool[con.ConditionID] + if !ok { + return false + } + + return c.Check(need, con) +} + +func (this *ConditionMgr) Register(cid int, c CheckBase) { + this.pool[cid] = c +} + +type CheckBase interface { + Check(need interface{}, condition *Condition) bool +} + +// //////////////////////////////////////////////////////////// +const ( + C_USER_PROMOTER = 1 //判定用户推广员 + C_USER_ISNEW = 2 //是否新用户 + C_USER_ISHAVETEL = 3 //是否正式用户 + C_USER_ISFIRST = 4 //是否首充 + C_USER_ISDAYFIRST = 5 //是否当日首充 + C_USER_PAYEXCHANGE = 6 //充值提现比 + C_USER_PAYNNUM = 7 //充值额度 + +) diff --git a/common/config.go b/common/config.go new file mode 100644 index 0000000..3347b65 --- /dev/null +++ b/common/config.go @@ -0,0 +1,121 @@ +package common + +import ( + "encoding/json" + + "mongo.games.com/goserver/core" +) + +var Config = Configuration{} + +type Configuration struct { + AppId string + SrvId string + IsDevMode bool +} + +func (this *Configuration) Name() string { + return "common" +} + +func (this *Configuration) Init() error { + return nil +} + +func (this *Configuration) Close() error { + return nil +} + +func init() { + core.RegistePackage(&Config) + core.RegistePackage(&CustomConfig) +} + +var CustomConfig = make(CustomConfiguration) + +type CustomConfiguration map[string]interface{} + +func (this *CustomConfiguration) Name() string { + return "costum" +} + +func (this *CustomConfiguration) Init() error { + return nil +} + +func (this *CustomConfiguration) Close() error { + return nil +} + +func (this *CustomConfiguration) GetString(key string) string { + if v, exist := (*this)[key]; exist { + if str, ok := v.(string); ok { + return str + } + } + return "" +} + +func (this *CustomConfiguration) GetStrings(key string) (strs []string) { + if v, exist := (*this)[key]; exist { + if vals, ok := v.([]interface{}); ok { + for _, s := range vals { + if str, ok := s.(string); ok { + strs = append(strs, str) + } + } + return + } + } + return +} + +func (this *CustomConfiguration) GetCustomCfgs(key string) (strs []*CustomConfiguration) { + if v, exist := (*this)[key]; exist { + if vals, ok := v.([]interface{}); ok { + for _, s := range vals { + if data, ok := s.(map[string]interface{}); ok { + var pkg *CustomConfiguration + modelBuff, _ := json.Marshal(data) + err := json.Unmarshal(modelBuff, &pkg) + if err == nil { + strs = append(strs, pkg) + } + } + } + return + } + } + return +} + +func (this *CustomConfiguration) GetInts(key string) (strs []int) { + if v, exist := (*this)[key]; exist { + if vals, ok := v.([]interface{}); ok { + for _, s := range vals { + if str, ok := s.(float64); ok { + strs = append(strs, int(str)) + } + } + return + } + } + return +} +func (this *CustomConfiguration) GetInt(key string) int { + if v, exist := (*this)[key]; exist { + if val, ok := v.(float64); ok { + return int(val) + } + } + return 0 +} + +func (this *CustomConfiguration) GetBool(key string) bool { + if v, exist := (*this)[key]; exist { + if val, ok := v.(bool); ok { + return val + } + } + return false +} diff --git a/common/configencryptor.go b/common/configencryptor.go new file mode 100644 index 0000000..99bf13e --- /dev/null +++ b/common/configencryptor.go @@ -0,0 +1,119 @@ +package common + +const ( + aa uint = 0x7E + bb = 0x33 + cc = 0xA1 + ENCRYPT_KEY1 = 0xa61fce5e // A = 0x20, B = 0xFD, C = 0x07, first = 0x1F, key = a61fce5e + ENCRYPT_KEY2 = 0x443ffc04 // A = 0x7A, B = 0xCF, C = 0xE5, first = 0x3F, key = 443ffc04 + ENCRYPT_KEY3 = 0x12345678 +) + +var ConfigFE = &ConfigFileEncryptor{} + +type ConfigFileEncryptor struct { + m_nPos1 int + m_nPos2 int + m_nPos3 int + m_cGlobalEncrypt EncryptCode +} +type EncryptCode struct { + m_bufEncrypt1 [256]uint8 + m_bufEncrypt2 [256]uint8 + m_bufEncrypt3 [256]uint8 +} + +func (this *EncryptCode) init(key1, key2, key3 uint) { + var a1, b1, c1, fst1 uint + a1 = ((key1 >> 0) & 0xFF) ^ aa + b1 = ((key1 >> 8) & 0xFF) ^ bb + c1 = ((key1 >> 24) & 0xFF) ^ cc + fst1 = (key1 >> 16) & 0xFF + + var a2, b2, c2, fst2 uint + a2 = ((key2 >> 0) & 0xFF) ^ aa + b2 = ((key2 >> 8) & 0xFF) ^ bb + c2 = ((key2 >> 24) & 0xFF) ^ cc + fst2 = (key2 >> 16) & 0xFF + + i := 0 + nCode := uint8(fst1) + for i = 0; i < 256; i++ { + this.m_bufEncrypt1[i] = nCode + nCode = (uint8(a1)*nCode*nCode + uint8(b1)*nCode + uint8(c1)) & 0xFF + } + + nCode = uint8(fst2) + for i = 0; i < 256; i++ { + this.m_bufEncrypt2[i] = nCode + nCode = (uint8(a2)*nCode*nCode + uint8(b2)*nCode + uint8(c2)) & 0xFF + } + + for i = 0; i < 256; i++ { + this.m_bufEncrypt3[i] = uint8(((key3 >> uint(i%4)) ^ uint(i)) & 0xff) + } +} +func (this *ConfigFileEncryptor) init(key1, key2, key3 uint) { + this.m_cGlobalEncrypt.init(key1, key2, key3) +} +func (this *ConfigFileEncryptor) IsCipherText(buf []byte) bool { + size := len(buf) + if size < 4 { + return false + } + //0x1b454e43 + if buf[size-1] == 0x43 && buf[size-2] == 0x4e && buf[size-3] == 0x45 && buf[size-4] == 0x1b { + return true + } + return false +} +func (this *ConfigFileEncryptor) Encrypt(buf []byte) []byte { + size := len(buf) + oldPos1, oldPos2, oldPos3 := this.m_nPos1, this.m_nPos2, this.m_nPos3 + for i := 0; i < size; i++ { + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt1[this.m_nPos1] + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt2[this.m_nPos2] + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt3[this.m_nPos3] + this.m_nPos1++ + if this.m_nPos1 >= 256 { + this.m_nPos1 = 0 + this.m_nPos2++ + if this.m_nPos2 >= 256 { + this.m_nPos2 = 0 + } + } + this.m_nPos3++ + if this.m_nPos3 >= 256 { + this.m_nPos3 = 0 + } + } + this.m_nPos1, this.m_nPos2, this.m_nPos3 = oldPos1, oldPos2, oldPos3 + buf = append(buf, 0x1b, 0x45, 0x4e, 0x43) + return buf +} +func (this *ConfigFileEncryptor) Decrtypt(buf []byte) []byte { + size := len(buf) - 4 + oldPos1, oldPos2, oldPos3 := this.m_nPos1, this.m_nPos2, this.m_nPos3 + for i := 0; i < size; i++ { + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt1[this.m_nPos1] + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt2[this.m_nPos2] + buf[i] ^= this.m_cGlobalEncrypt.m_bufEncrypt3[this.m_nPos3] + this.m_nPos1++ + if this.m_nPos1 >= 256 { + this.m_nPos1 = 0 + this.m_nPos2++ + if this.m_nPos2 >= 256 { + this.m_nPos2 = 0 + } + } + this.m_nPos3++ + if this.m_nPos3 >= 256 { + this.m_nPos3 = 0 + } + } + this.m_nPos1, this.m_nPos2, this.m_nPos3 = oldPos1, oldPos2, oldPos3 + return buf[:size] +} +func init() { + ConfigFE.init(ENCRYPT_KEY1, ENCRYPT_KEY2, ENCRYPT_KEY3) +} diff --git a/common/constant.go b/common/constant.go new file mode 100644 index 0000000..ce97408 --- /dev/null +++ b/common/constant.go @@ -0,0 +1,782 @@ +package common + +import ( + "fmt" + "strings" +) + +// todo +// 游戏模式ID +// 多个id会属于同一个游戏,需要判断游戏类型时添加一个方法 +// 例如:判断是否是象棋游戏 +// func IsChess(id int) bool { +// switch id { +// case GameId_Chesstitians, +// GameId_ChesstitiansMakruk, +// GameId_ChesstitiansCambodian, +// GameId_ChesstitiansCambodianRobot: +// return true +// } +// return false +//} + +const ( + GameId_Unknow int = iota + __GameId_Hundred_Min__ = 100 //################百人类################ + GameId_HundredDZNZ = 101 //德州牛仔 + GameId_HundredYXX = 102 //鱼虾蟹 + GameId_Baccarat = 103 //百家乐 + GameId_RollPoint = 104 //骰宝 + GameId_Roulette = 105 //轮盘 + GameId_DragonVsTiger = 106 //龙虎斗 + GameId_RedVsBlack = 107 //红黑大战 + GameId_RollCoin = 108 //奔驰宝马 + GameId_RollAnimals = 109 //飞禽走兽 + GameId_RollColor = 110 //森林舞会 + GameId_RedPack = 111 //红包扫雷 + GameId_Crash = 112 //碰撞游戏 + __GameId_VS_Min__ = 200 //################对战类################ + GameId_DezhouPoker = 201 //德州扑克 + GameId_DezhouPoker_5To2 = 202 //德州扑克5选2 + GameId_OmahaPoker = 203 //奥马哈 + GameId_TenHalf = 204 //十点半 + GameId_FiveCardStud = 205 //梭哈 + GameId_BlackJack = 206 //21点 + GameId_TienLen = 207 // tienlen自由桌经典版 + GameId_TienLen_yl = 208 // tienlen自由桌娱乐版 + GameId_TienLen_toend = 209 // tienlen自由桌经典版(打到底) + GameId_TienLen_yl_toend = 210 // tienlen自由桌娱乐版(打到底) + GameId_TienLen_m = 807 // tienlen经典比赛场 + GameId_TienLen_m_toend = 808 // tienlen经典比赛场(打到底) + GameId_TaLa = 220 //tala + GameId_SamLoc = 230 //samloc + GameId_TienLenSelect = 240 // tienlen经典版 + GameId_TienLenSelect_yl = 241 // tienlen娱乐版 + GameId_TienLenRank = 242 // tienlen排位赛经典 + GameId_TienLenRank_yl = 243 // tienlen排位赛娱乐 + GameId_TienLenSelect_toend = 244 // tienlen经典版(打到底) + GameId_TienLenSelect_yl_toend = 245 // tienlen娱乐版(打到底) + GameId_TienLenRank_toend = 246 // tienlen排位赛经典(打到底) + GameId_TienLenRank_yl_toend = 247 // tienlen排位赛娱乐(打到底) + GameId_Chesstitians = 501 //国际象棋 + GameId_ChesstitiansMakruk = 511 //国际象棋变体 (Makruk 泰国象棋) + GameId_ChesstitiansCambodian = 521 // 柬埔寨象棋 (Cambodian 柬埔寨象棋) + GameId_ChesstitiansCambodianRobot = 522 // 柬埔寨象棋人机对战 + GameID_Thirteen4 = 211 // 十三张(四人场) + GameID_Thirteen8 = 212 // 十三张(八人场) + GameID_ThirteenFree = 213 // 十三张(自由场经典场) + GameID_ThirteenFreeLaiZi = 214 // 十三张(自由场癞子场) + __GameId_Slot_Min__ = 300 //################拉霸类################ + GameId_CaiShen = 301 //财神 + GameId_Avengers = 302 //复仇者联盟 + GameId_EasterIsland = 303 //复活岛 + GameId_IceAge = 304 //冰河世纪 + GameId_TamQuoc = 305 //百战成神 + GameId_Fruits = 306 //水果拉霸 + GameId_Richblessed = 307 //多福多财 + __GameId_Fishing_Min__ = 400 //################捕鱼类################ + GameId_HFishing = 401 //欢乐捕鱼 + GameId_TFishing = 402 //天天捕鱼 + __GameId_Casual_Min__ = 500 //################休闲类################ + __GameId_MiniGame_Min__ = 600 //################小游类################ + GameId_Candy = 601 //糖果小游戏 + GameId_MiniPoker = 602 //MiniPoker + GameId_BOOM = 603 //卡丁车 + GameId_LuckyDice = 604 //幸运筛子 + GameId_CaoThap = 605 //CaoThap + GameId_AngerUncle = 606 // 愤怒大叔 + GameId_SmallRoket = 607 // 小火箭 + __GameId_ThrGame_Min__ = 700 //################三方类################ + GameId_Thr_Dg = 701 //DG Game + GameId_Thr_XHJ = 901 //DG Game +) + +// IsTienLen TienLen游戏 +func IsTienLen(gameId int) bool { + return InSliceInt32(GetTienlenGameID(), int32(gameId)) +} + +func GetTienlenGameID() []int32 { + //todo 还要维护游戏id,好麻烦,还容易忘 + return []int32{ + GameId_TienLen, GameId_TienLen_yl, + GameId_TienLen_toend, GameId_TienLen_yl_toend, + GameId_TienLen_m, GameId_TienLen_m_toend, + GameId_TienLenSelect, GameId_TienLenSelect_toend, + GameId_TienLenSelect_yl, GameId_TienLenSelect_yl_toend, + GameId_TienLenRank, GameId_TienLenRank_toend, + GameId_TienLenRank_yl, GameId_TienLenRank_yl_toend, + } +} + +// IsTienLenYuLe TienLen娱乐 +func IsTienLenYuLe(gameId int) bool { + switch gameId { + case GameId_TienLen_yl, + GameId_TienLen_yl_toend, + GameId_TienLenSelect_yl, + GameId_TienLenRank_yl, + GameId_TienLenSelect_yl_toend, + GameId_TienLenRank_yl_toend: + return true + } + return false +} + +// IsTienLenToEnd TienLen打到底 +func IsTienLenToEnd(gameId int) bool { + switch gameId { + case GameId_TienLen_toend, + GameId_TienLen_yl_toend, + GameId_TienLen_m_toend, + GameId_TienLenSelect_toend, + GameId_TienLenSelect_yl_toend, + GameId_TienLenRank_toend, + GameId_TienLenRank_yl_toend: + return true + } + return false +} + +// IsChess 象棋游戏 +func IsChess(gameId int) bool { + switch gameId { + case GameId_Chesstitians, + GameId_ChesstitiansMakruk, + GameId_ChesstitiansCambodian, + GameId_ChesstitiansCambodianRobot: + return true + } + return false +} + +// IsThirteen 十三张游戏 +func IsThirteen(gameId int) bool { + switch gameId { + case GameID_Thirteen4, + GameID_Thirteen8, + GameID_ThirteenFree, + GameID_ThirteenFreeLaiZi: + return true + } + return false +} + +// IsThirteen 十三张游戏 +func IsSmallRocket(gameId int) bool { + switch gameId { + case GameId_SmallRoket: + return true + } + return false +} + +// IsLocalGame 自动分场模式的游戏 +// 根据 DB_Createroom.xlsx 给玩家分场或创建房间 +func IsLocalGame(gameId int) bool { + switch gameId { + case GameId_TienLen, GameId_TienLen_yl, + GameId_TienLen_toend, GameId_TienLen_yl_toend, + GameId_TaLa, + GameId_SamLoc, + GameID_ThirteenFree, GameID_ThirteenFreeLaiZi: + return true + } + return false +} + +// IsPlayerPool 需要统计在个人水池的游戏 +func IsPlayerPool(gameId int) bool { + if IsTienLen(gameId) || IsThirteen(gameId) { + return true + } + return false +} + +// 房间编号区间 +const ( + PrivateSceneStartId = 10000000 + PrivateSceneMaxId = 99999999 + MatchSceneStartId = 100000000 + MatchSceneMaxId = 199999999 + HundredSceneStartId = 200000000 + HundredSceneMaxId = 299999999 + HallSceneStartId = 300000000 + HallSceneMaxId = 399999999 + MiniGameSceneStartId = 400000000 + MiniGameSceneMaxId = 409999999 + CoinSceneStartId = 1000000000 //区间预留大点,因为队列匹配比较耗id,假定一天100w牌局,那么这个id区间够用1000天 + CoinSceneMaxId = 1999999999 + DgSceneId = 99 +) + +// 房间模式 +const ( + SceneMode_Public = iota //公共房间 + SceneMode_Club //俱乐部房间 + SceneMode_Private //私人房间 + SceneMode_Match //赛事房间 + SceneMode_Thr //三方房间 +) + +// 场景级别 +//const ( +// SceneLvl_Test = -1 // 试玩场(不要钱) +// SceneLvl_Experience = 0 // 体验场(花小钱) +// SceneLvl_Primary = 1 // 初级场 +// SceneLvl_Middle = 2 // 中级场 +// SceneLvl_Senior = 3 // 高级场 +// SceneLvl_Professor = 4 // 专家场 +//) + +// 房费选项 +//const ( +// RoomFee_Owner int32 = iota //房主 +// RoomFee_AA //AA +// RoomFee_Max +//) + +const ( + Platform_Rob = "__$G_P$__" + Platform_Sys = "0" + Channel_Rob = "$${ROBOT}$$" + PMCmd_SplitToken = "-" + PMCmd_AddCoin = "addcoin" + PMCmd_Privilege = "setprivilege" + DeviceOS_Android = "android" + DeviceOS_IOS = "ios" +) + +const ( + GainWay_NewPlayer int32 = 0 //0.新建角色 + GainWay_Pay = 1 //1.后台增加(主要是充值) + GainWay_ByPMCmd = 2 //2.pm命令 + GainWay_MatchBreakBack = 3 //3.退赛退还 + GainWay_MatchSystemSupply = 4 //4.比赛奖励 + GainWay_Exchange = 5 //5.兑换 + GainWay_ServiceFee = 6 //6.桌费 + GainWay_CoinSceneWin = 7 //7.金豆场赢取 + GainWay_CoinSceneLost = 8 //8.金豆场输 + GainWay_CoinSceneEnter = 9 //9.进入金币场预扣 + GainWay_ShopBuy = 10 //10.商城购买或者兑换 + GainWay_CoinSceneLeave = 11 //11.金豆场回兑 + GainWay_HundredSceneWin = 12 //12.万人场赢取 + GainWay_HundredSceneLost = 13 //13.万人场输 + GainWay_MessageAttach = 14 //14.邮件 + GainWay_SafeBoxSave = 15 //15.保险箱存入 + GainWay_SafeBoxTakeOut = 16 //16.保险箱取出 + GainWay_Fishing = 17 //17.捕鱼 + GainWay_CoinSceneExchange = 18 //18.金豆场兑换 + GainWay_UpgradeAccount = 19 //19.升级账号 + GainWay_API_AddCoin = 20 //20.API操作钱包 + GainWay_GoldCome = 21 //21.财神降临 + GainWay_Transfer_System2Thrid = 22 //22.系统平台转入到第三方平台的金币 + GainWay_Transfer_Thrid2System = 23 //23.第三方平台转入到系统平台的金币 + GainWay_RebateTask = 24 //24.返利获取 + GainWay_IOSINSTALLSTABLE = 25 //25.ios安装奖励 + GainWay_VirtualChange = 26 //26.德州虚拟账变 + GainWay_CreatePrivateScene = 27 //27.创建私有房间 + GainWay_PrivateSceneReturn = 28 //28.解散私有房间返还 + GainWay_OnlineRandCoin = 29 //29.红包雨 + GainWay_Expire = 30 //30.到期清理 + GainWay_PromoterBind = 31 //31.手动绑定推广员 + GainWay_GradeShopReturn = 32 //32.积分商城撤单退还积分 + GainWay_Api_In = 33 //33.转移金币 + GainWay_Api_Out = 34 //34.转移金币 + GainWay_Shop_Buy = 35 //35.购买记录 + GainWay_MAIL_MTEM = 36 //36.邮件领取道具 + GainWay_Item_Sale = 37 //37.道具出售 + GainWay_ReliefFund = 38 //38.领取救济金 + GainWay_Shop_Revoke = 39 //39.撤单 + GainWay_ActSign = 40 //40.签到 + GainWay_MatchSignup = 41 //比赛报名费用 + GainWay_MatchSeason = 42 //比赛赛季奖励 + GainWay_ActSignNew = 43 //43.新签到 + GainWay_ActTurnplate = 44 //44.轮盘 + GainWay_ActBlindBox = 45 //45.盲盒 + GainWay_ActFirstPay = 46 //46.首充 + GainWay_VIPGift = 47 //47.vip礼包 + GainWay_ActContinuousPay = 48 //48.连续充值 + GainWay_ActJybAward = 49 //49.礼包码兑换 + GainWay_LeaveDeduct = 50 //50.离开扣分 + GainWay_LeaveCombat = 51 //51.离开补偿 + GainWay_RankMatch = 52 //52.排位赛段位奖励 + GainWay_AddBag = 53 //53 增加背包接口调用 + GainWay_SmallRocket = 54 //54.小火箭收入 + GainWay_BindTel = 55 //55.绑定手机号 + GainWay_ReliefFund2 = 56 //56.救济金看视频双倍领取 + GainWay_ActTurnplate2 = 57 //57.轮盘看视频双倍领取 + GainWay_ActSignNew2 = 58 //58.签到看视频双倍领取 + GainWay_ItemUse = 59 //59.道具使用 + GainWay_PhoneScore = 60 //60.积分抽奖活动 + GainWay_RankReward = 61 //61.段位奖励 + GainWay_TaskReward = 62 //62.任务奖励 + GainWay_Interact = 63 //63.房间内互动效果 + GainWay_Collect = 64 //64.集卡活动 +) + +// 后台选择 金币变化类型 的充值 类型id号起始 +//const GainWaySort_Api = 1000 + +// 自定义局数起始索引 +//const CUSTOM_PER_GAME_INDEX_BEG int32 = 10 + +const ( + ClientRole_Agentor int = iota + ClientRole_Player + ClientRole_GM + ClientRole_Max +) + +const ( + CoinSceneOp_Enter int32 = iota //进入 + CoinSceneOp_Leave //离开 + CoinSceneOp_Change //换桌 + CoinSceneOp_AudienceEnter //观众进入 + CoinSceneOp_AudienceLeave //观众离开 + CoinSceneOp_AudienceChange //观众换桌 + CoinSceneOP_Server +) + +// 玩家离开原因 +const ( + PlayerLeaveReason_Normal int = iota //主动离开 + PlayerLeaveReason_Bekickout //钱不够被踢出 + PlayerLeaveReason_OnDestroy //房间销毁强制退出 + PlayerLeaveReason_CoinScene //玩家暂离金豆自由场 + PlayerLeaveReason_ChangeCoinScene //玩家换桌 + PlayerLeaveReason_OnBilled //结算完毕 + PlayerLeaveReason_DropLine //掉线离开 + PlayerLeaveReason_LongTimeNoOp //长时间未操作 + PlayerLeaveReason_GameTimes //超过游戏次数强制离开 + PlayerLeaveReason_RoomFull //房间人数已达上限 + PlayerLeaveReason_RoomClose //房间已解散 + PlayerLeaveReason_RoomMaxCoin //超出房间限制金币范围 + PlayerLeaveReason_AutoState //托管状态踢出房间 +) + +// 万分比 +const RATE_BASE_VALUE int32 = 10000 + +const ( + SceneState_Normal int = iota + SceneState_Fishing //鱼潮 +) + +const ( + PlayerType_Rob int32 = 0 + PlayerType_Undefine = 1 + PlayerType_Black = -1 + PlayerType_White = -2 +) + +const ( + CoinPoolAIModel_Default int32 = iota //默认 + CoinPoolAIModel_Normal //正常模式 + CoinPoolAIModel_ShouFen //收分模式 + CoinPoolAIModel_ZheZhong //折中模式 + CoinPoolAIModel_TuFen //吐分 + CoinPoolAIModel_Max // +) + +const ( + RobotServerType int = 9 + RobotServerId = 901 + DataServerType int = 10 + DataServerId = 1001 + AIServerType = 11 + AIServerId = 1101 + ActThrServerType int = 12 + ActThrServerID = 1201 +) + +// 踢号原因 +const ( + KickReason_Unknow int32 = iota + KickReason_OtherLogin //顶号 + KickReason_Freeze //冻结 + KickReason_ResLow = 5 //资源 + KickReason_AppLow = 6 //App版本 + + KickReason_CheckCodeErr = 7 //校验错误 + KickReason_TaskErr = 8 //任务错误 + KickReason_CantFindAcc = 9 //查找sid acc错误 + KickReason_DBLoadAcc = 10 //数据库登录错误 + KickReason_Logining //登陆中 + KickReason_Disconnection = 99 //网络断开 +) + +// 游戏类型 +const ( + GameType_Unknow int32 = iota + GameType_Hundred // 百人类 + GameType_PVP // 对战类 + GameType_Slot // 拉霸类 + GameType_Fishing // 捕鱼类 + GameType_Casual // 休闲类 + GameType_Mini // 小游戏类 + GameType_Thr // 三方类 +) + +func IsHundredType(gt int32) bool { + return gt == GameType_Hundred || gt == GameType_Slot || gt == GameType_Casual +} + +func IsCoinSceneType(gt int32) bool { + return gt == GameType_PVP || gt == GameType_Fishing +} + +const PreCreateSceneAudienceNum = 20 + +const ( + HorseRaceLampPriority_Coin int = iota //根据玩家输赢金额大小设置优先级(在最大最小限额的基础上) + HorseRaceLampPriority_Rand //随机(在最大最小限额的基础上) +) + +// 设备 +const ( + Web = 0 + Android = 1 + IOS = 2 + WebStr = "h5" + AndroidStr = "android" + IOSStr = "ios" +) + +var DeviceName = map[int]string{ + Web: WebStr, + Android: AndroidStr, + IOS: IOSStr, +} + +var DeviceNum = map[string]int{ + WebStr: Web, + AndroidStr: Android, + IOSStr: IOS, +} + +const ( + MatchTrueMan_Forbid int32 = -1 //禁止匹配真人 + MatchTrueMan_Unlimited = 0 //不限制 + MatchTrueMan_Priority = 1 //优先匹配真人 +) + +const ( + SingleAdjustModeNormal = 0 + SingleAdjustModeWin = 1 + SingleAdjustModeLose = 2 +) + +// 自动化标签(程序里产生的全部<0) +const ( + AutomaticTag_QZNN_Smart int32 = -1 +) + +const ( + SceneParamEx_DbGameFreeId = 0 + SceneParamEx_CanStartNum = 1 //游戏开始的要求人数 +) + +// 比赛参数 +const ( + PlayerIParam_MatchRank int = iota + PlayerIParam_IsQuit + PlayerIParam_MatchWeaken + PlayerIParam_TotalOfGames +) + +const ( + PARAMEX_GAMEFREEID int = iota //游戏id + PARAMEX_MATCH_COPYID //比赛副本id + PARAMEX_MATCH_ID //比赛id + PARAMEX_MATCH_PHASEIDX //赛程阶段idx + PARAMEX_MATCH_MMTYPE //赛制 + PARAMEX_MATCH_BASESCORE //底分 + PARAMEX_MATCH_NUMOFGAME //局数 + PARAMEX_MATCH_OUTSCORE //出局分数 + PARAMEX_MATCH_RESTPLAYERNUM //剩余人数 + PARAMEX_MATCH_STARTNUM //本轮开始人数 + //PARAMEX_MATCH_RANK //我的排名,构建房间消息时,额外补充,每个玩家不同 +) + +const ( + MaxLoopNum = 1000 + DefaultResult = 0 +) + +const ( + CodeTypeSMS = 0 // 短信验证码 + CodeTypeStr = 1 // 字符串验证码 + CodeTypeHuaKuai = 2 // 滑块验证码 + CodeTypeNo = 3 // 不使用验证码 +) + +const ( + ActId_Share int = iota //0.微信分享 + ActId_OnlineReward //1.在线奖励 + ActId_UpgradeAccount //2.升级账号 + ActId_GoldTask //3.财神任务 + ActId_GoldCome //4.财神降临 + ActId_LuckyTurntable //5.转盘活动 + ActId_Yeb //6.余额宝 + ActId_Card //7.周卡月卡 + ActId_RebateTask //8.返利获取 + ActId_IOSINSTALLSTABLE //9.ios安装奖励 + ActId_VipLevelBonus //10.vip日周月等级奖励 + ActId_LoginRandCoin //11.登录红包 + ActId_OnlineRandCoin //12.红包雨 + ActId_MatchSwitch //13.比赛开关 + ActId_PromoterBind //14.手动绑定推广员 + ActId_Lottery //15.彩金池 + ActId_Task //16.活跃任务 + ActId_PROMOTER //17.全民推广 + ActId_Activity //18.活动界面 + ActId_NewYear //19.新年暗号红包活动 + ActId_Guess //20.猜灯谜活动 + ActId_Sign //21.七日签到 + ExchangeId_Alipay //22.兑换到支付宝 + ExchangeId_Bank //23.兑换到银行卡 + ExchangeId_Wechat //24.兑换到微信 + ActId_Max +) + +// 匹配模式 +const ( + MatchMode_Normal int32 = iota //普通匹配 + MatchMode_Quene //队列匹配 +) + +const ( + SCENE_BIGWINHISTORY_MAXNUMBER = 40 // 爆奖记录最大数量 + SCENE_BIGWINHISTORY_LIMITNUMBER = 10 // 假数据生成临界值 + SCENE_BIGWINHISTORY_TIMEINTERVAL = 2 // 假数据生成定点时间间隔,单位:小时(实际时间 = 定点时间 + 随机时间) +) +const ( + OrderColumnInvalid = 0 // 默认 + OrderColumnCoinPayTotal = 1 // 充值 + OrderColumnCoinExchangeTotal = 2 // 提现 + OrderColumnTaxTotal = 3 // 税收 + OrderColumnRegisterTime = 4 // 注册时间 + OrderColumnRoomNumber = 5 // 游戏房间号 + OrderColumnLose = 6 // 输次数 + OrderColumnWin = 7 // 赢次数 + OrderColumnDraw = 8 // 平次数 + OrderColumnWinCoin = 9 // 赢分 + OrderColumnLoseCoin = 10 // 输分 +) + +const ( + LoginTypeGuest = 0 // 游客登录 + LoginTypeAccount = 1 // 账号登录 + LoginTypeTelCode = 2 // 手机号验证码登录 + LoginTypeTelegram = 5 // telegram登录 + LoginTypeGoogle = 6 // google,facebook 登录 + + RegisterTypeTel = 3 // 手机号注册 + RegisterTypeName = 4 // 账号注册 +) + +const ( + // 账号判断状态码 + LoginTypeNoExist = -1 + LoginError = 0 + LoginOk = 1 + LoginNew = 2 + LoginPasswordError = 3 + LoginFreeze = 4 + LoginTelExist = 8 + LoginTelCodeExpire = 9 // 手机号验证码登录过期 + + RegisterNotExist = 6 + RegisterExist = 7 + // 创建账号状态码 + InsertAccountOk = 9 +) + +const TelLoginValidDays = 7 // 手机号登录有效期,天 + +const ( + LoginLogTypeLogin int32 = iota // 登录 + LoginLogTypeLogout // 登出 + LoginLogTypeRehold // 重连 + LoginLogTypeDrop // 掉线 +) + +// 道具ID +const ( + ItemIDCoin = 100001 // 金币对应的itemId + ItemIDDiamond = 100002 // 钻石对应的itemId + ItemIDMoneyPond = 100003 // 玩家金币池对应物品Id + ItemIDVipExp = 100005 // VIP经验对应的物品Id + ItemIDPhoneScore = 100006 // 手机抽奖积分 + ItemIDWeekScore = 100004 // 周活跃积分 + ItemIDGiftBox = 50001 // 碎片礼盒 +) + +func ToItemId(id int32) int32 { + switch id { + case 1: + return ItemIDCoin + case 2: + return ItemIDDiamond + } + return id +} + +// 道具类型 +const ( + ItemTypePetFragments = 1 //宠物碎片 + ItemTypeCharacterFragments = 2 //角色碎片 + ItemTypeChangeCard = 3 //兑换卡 + ItemTypeOther = 4 //其他 + ItemTypeCoin = 5 //金币 + ItemTypeDiamond = 6 //钻石 + ItemTypeFishPower = 7 //捕鱼炮台 + ItemTypeMoneyPond = 8 //玩家金币池 + ItemTypePhoneCode = 9 //话费兑换卡 + ItemTypeVipExp = 10 //VIP经验 + ItemTypeShopScore = 11 //积分 + ItemTypeInteract = 12 // 互动表情 +) + +func GetKeyNoviceGameId(gameId int) string { + return fmt.Sprintf("novice-%v", gameId) +} + +func GetKeyGameType(gameType int) string { + return fmt.Sprintf("gametype-%v", gameType) +} + +// 账变类型 +const ( + BillTypeCoin = 0 // 金币 + BillTypeDiamond = 1 // 钻石 +) + +// 验证码类型 +const ( + SMSCodeTelBind = 1 // 绑定手机号验证码 + SMSCodeTelLogin = 2 // 手机号登录验证码 +) + +// 账号类型 +const ( + AccountTypeGoogle = 1 // 谷歌 + AccountTypeFacebook = 2 // facebook + AccountTypeTel = 3 // 手机号 +) + +func GetTelLoginCodeKey(platform, tel string) string { + return fmt.Sprintf("%v.%v.%v", SMSCodeTelLogin, platform, tel) +} + +func GetBindTelCodeKey(platform, tel string) string { + return fmt.Sprintf("%v.%v.%v", SMSCodeTelBind, platform, tel) +} + +func GetImageCodeKey(key string) string { + return fmt.Sprintf("%v.%v", "Image", key) +} + +const ( + SMSCodeValidTime = 60 // 访问频率限制 + SMSCodeValidTimeTelBind = 60 // 绑定手机号验证码有效期,单位秒 + SMSCodeValidTimeTelLogin = 600 // 手机号登录验证码有效期,单位秒 +) + +// 活动,任务类型 +const ( + TaskTypeAdv = 1 // 看广告次数 + TaskTypeBuyCoin = 2 // 买金币次数 + TaskTypeLogin = 3 // 登录次数 + TaskTypeWinTimes = 4 // 赢游戏次数 + TaskTypePlayTimes = 5 // 玩游戏次数 + TaskTypeRankMatchTimes = 6 // 排位赛次数 + TaskTypePay = 7 // 充值金额 + TaskTypeWinOrLose = 8 // 游戏输赢金币数量 + TaskTypeTienlenCount = 9 // tienlen游戏场次 + TaskTypeBindInviter = 10 // 绑定邀请人数量 + TaskTypeWinCoin = 11 // 赢取金币数量 + TaskTypeTienlenWinTimes = 12 // tienlen游戏赢取次数 + TaskTypeInviteScore = 13 // 邀请积分 + TaskTypeActivityScore = 14 // 周活跃积分数量 + TaskTypeFirstLogin = 15 // 每日首次登录 + TaskTypeInviteNum = 16 // 邀请绑定数量 +) + +const ( + TaskGameTypeTienlen = 1 // tienlen + TaskGameTypeThirteen = 2 // 十三张 + TaskGameTypeChess = 3 // 象棋 + TaskGameTypeSmallRocket = 4 // 小火箭 +) + +const ( + TaskActivityTypeEveryDay = 1 // 每日任务 + TaskActivityTypeWeek = 2 // 每周任务 + TaskActivityTypeNovice = 3 // 新手任务 + TaskActivityTypeInvite = 4 // 邀请任务 +) + +const HeadRange = 3 // 机器人头像id范围 + +const ( + InviteScoreTypeBind = 1 // 绑定邀请码 + InviteScoreTypeLogin = 2 // 每日登录 + InviteScoreTypePlayTimes = 3 // 每日参与任意游戏 + InviteScoreTypeRecharge = 4 // 充值 + InviteScoreTypeGameTimes = 5 // 每局游戏 +) + +const TaskIDInvitePlayGame = 1000001 +const TaskIDInviteRecharge = 1000002 +const TaskIDInviteFirstLogin = 1000003 + +func InMatchChannel(ls []string, n string) bool { + if n == "" || len(ls) == 0 { + return false + } + if n == Channel_Rob { + return true + } + if InSliceString(ls, n) { + return true + } + if InSliceString(ls, "Official") && strings.ToLower(n) == "main" { // 兼容 + return true + } + return false +} + +// 渠道名称 +const ( + ChannelDefault = ChannelOfficial + ChannelGooglePlay = "GooglePlay" + ChannelWeb = "Web" + ChannelOfficial = "Official" +) + +// 角色id +const ( + DefaultRoleId = RoleIDGirl // 默认角色 + RoleIDGirl = 2000001 + RoleIDBoy = 2000002 + RoleIDMagic = 2000003 +) + +var RolesIDs = []int32{RoleIDGirl, RoleIDBoy, RoleIDMagic} + +// 角色加成类型 +const ( + RoleAddADV = 1 // 看视频加成 + RoleAddCoin = 2 // 商城购买金币加成 + RoleAddRankScore = 3 // 排位积分加成 +) + +// 宠物id +const ( + DefaultPetId = PetIDChicken // 默认宠物 + PetIDChicken = 3000001 +) + +var PetIDs = []int32{PetIDChicken} diff --git a/common/ctrlcode.go b/common/ctrlcode.go new file mode 100644 index 0000000..7361af6 --- /dev/null +++ b/common/ctrlcode.go @@ -0,0 +1,8 @@ +package common + +const ( + SrvCtrlNilCode int32 = iota + SrvCtrlCloseCode + SrvCtrlStateSwitchCode + SrvCtrlResetMgoSession +) diff --git a/common/encryptor.go b/common/encryptor.go new file mode 100644 index 0000000..ba77277 --- /dev/null +++ b/common/encryptor.go @@ -0,0 +1,47 @@ +package common + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" +) + +type Encryptor struct { + buf1 [256]uint8 + buf2 [256]uint8 +} + +func (e *Encryptor) Init(appId, key string, ts int32) { + h1 := md5.New() + io.WriteString(h1, fmt.Sprintf("%v;%v;%v", appId, key, ts)) + raw1 := hex.EncodeToString(h1.Sum(nil)) + n1 := len(raw1) + for i := 0; i < 256; i++ { + e.buf1[i] = uint8((raw1[i%n1] ^ uint8(i)) & 0xff) + } + + h2 := md5.New() + io.WriteString(h2, key) + raw2 := hex.EncodeToString(h2.Sum(nil)) + n2 := len(raw2) + for i := 0; i < 256; i++ { + e.buf2[i] = uint8((raw2[i%n2] ^ uint8(i)) & 0xff) + } +} + +func (e *Encryptor) Encrypt(buf []byte, size int) { + var pos1, pos2 int + for i := 0; i < size; i++ { + buf[i] ^= e.buf1[pos1] + buf[i] ^= e.buf2[pos2] + pos1++ + if pos1 == 255 { + pos1 = 0 + pos2++ + if pos2 == 255 { + pos2 = 0 + } + } + } +} diff --git a/common/esayfunc.go b/common/esayfunc.go new file mode 100644 index 0000000..30dfa1b --- /dev/null +++ b/common/esayfunc.go @@ -0,0 +1,172 @@ +package common + +import ( + "crypto/md5" + "encoding/hex" + "io" + "math/rand" + "os" + "regexp" + + "encoding/json" + "math" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" +) + +const ( + RankServerType = 15 +) + +var seed int64 = 1 + +func GetSelfSrvType() int { + return netlib.Config.SrvInfo.Type +} + +func GetSelfSrvId() int { + return netlib.Config.SrvInfo.Id +} + +func GetSelfAreaId() int { + return netlib.Config.SrvInfo.AreaID +} + +func GetAccountSrvId() int { + return srvlib.ServerSessionMgrSington.GetServerId(GetSelfAreaId(), srvlib.AccountServerType) +} + +func GetGameSrvId() int { + return srvlib.ServerSessionMgrSington.GetServerId(GetSelfAreaId(), srvlib.GameServerType) +} +func GetGameSrvIds() []int { + return srvlib.ServerSessionMgrSington.GetServerIds(GetSelfAreaId(), srvlib.GameServerType) +} +func GetWorldSrvId() int { + return srvlib.ServerSessionMgrSington.GetServerId(GetSelfAreaId(), srvlib.WorldServerType) +} + +func GetRankSrvId() int { + return srvlib.ServerSessionMgrSington.GetServerId(GetSelfAreaId(), srvlib.RankServerType) +} + +func GetAppId() string { + return Config.AppId +} + +func GetClientSessionId(s *netlib.Session) srvlib.SessionId { + param := s.GetAttribute(srvlib.SessionAttributeClientSession) + if sid, ok := param.(srvlib.SessionId); ok { + return sid + } + return srvlib.SessionId(0) +} + +func GetRandInt(max int) int { + seed++ + rand.Seed(seed) + return rand.Intn(max) +} + +func Md5String(str string) string { + h := md5.New() + io.WriteString(h, str) + return hex.EncodeToString(h.Sum(nil)) +} + +func MakeMd5String(strs ...string) string { + buff := md5.New() + for _, value := range strs { + io.WriteString(buff, value) + } + return hex.EncodeToString(buff.Sum(nil)) +} + +func SetIntegerBit(num int32, index int32) int32 { + return num | (1 << uint(index-1)) +} + +func GetIntegerBit(num int32, index int32) bool { + if num&(1< 0 { + return true + } else { + return false + } +} + +// 校验身份证是否合法 +var IDReg, _ = regexp.Compile(`(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)`) +var REGEXP_IPRule, _ = regexp.Compile(`^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$`) +var ClubNameRule, _ = regexp.Compile("^[\u4e00-\u9fa5a-zA-Z-z0-9]+$") + +func IsValidID(id string) bool { + if IDReg != nil { + return IDReg.Match([]byte(id)) + } + return false +} + +func IsValidIP(Ip string) bool { + const UNKNOWIP = "0.0.0.0" + if Ip == "" || Ip == UNKNOWIP { + return false + } + if !REGEXP_IPRule.MatchString(Ip) { + return false + } + return true +} + +func JsonToStr(v interface{}) string { + buff, _ := json.Marshal(v) + return string(buff) +} + +func PathExists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +func MinI64(l int64, r int64) int64 { + if l < r { + return l + } else { + return r + } +} + +func AbsI64(l int64) int64 { + if l >= 0 { + return l + } else { + return -l + } +} + +// 如果结果为负,说明是玩家亏钱,结果为正,玩家赢钱。 +// 图像上是一个分段函数,绝对值极值为1 +func GetWinLossRate(win int64, loss int64) float64 { + ret := float64(0) + + if win > loss { + ret = float64(loss) / float64(win) + } else { + ret = -float64(win) / float64(loss) + } + + return ret +} + +// 得到一个初段慢,高段变快数 +func GetSoftMaxNum(cur float64, maxValue float64) float64 { + if cur > maxValue { + return maxValue + } + return cur * math.Sin((cur/maxValue)*math.Pi/2) +} diff --git a/common/exchangecode.go b/common/exchangecode.go new file mode 100644 index 0000000..b4bdf2f --- /dev/null +++ b/common/exchangecode.go @@ -0,0 +1,26 @@ +package common + +import ( + "bytes" + "crypto/rc4" + "encoding/base32" + "encoding/binary" + "math/rand" +) + +const encodeHex = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" + +var rc4Key = []byte{0x12, 0x57, 0xb8, 0xd8, 0x60, 0xae, 0x4c, 0xbd} + +var HexEncoding = base32.NewEncoding(encodeHex) +var Rc4Cipher, _ = rc4.NewCipher(rc4Key) + +// 生成兑换码 +func GeneExchangeCode(seq int32) string { + buf := bytes.NewBuffer(nil) + binary.Write(buf, binary.BigEndian, seq) + buf.WriteByte(byte(rand.Intn(0xff))) + var dst [5]byte + Rc4Cipher.XORKeyStream(dst[:], buf.Bytes()) + return HexEncoding.EncodeToString(dst[:]) +} diff --git a/common/handler.go b/common/handler.go new file mode 100644 index 0000000..a838736 --- /dev/null +++ b/common/handler.go @@ -0,0 +1,48 @@ +package common + +import ( + "fmt" + "reflect" + + "mongo.games.com/goserver/core/netlib" +) + +var handlers = make(map[int]Handler) + +type Handler interface { + Process(s *netlib.Session, packetid int, data interface{}, sid int64) error +} + +type HandlerWrapper func(s *netlib.Session, packetid int, data interface{}, sid int64) error + +func (hw HandlerWrapper) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + return hw(s, packetid, data, sid) +} + +func RegisterHandler(packetId int, h Handler) { + if _, ok := handlers[packetId]; ok { + panic(fmt.Sprintf("repeate register handler: %v Handler type=%v", packetId, reflect.TypeOf(h))) + } + + handlers[packetId] = h +} + +func GetHandler(packetId int) Handler { + if h, ok := handlers[packetId]; ok { + return h + } + + return nil +} + +func Register(mainId int, msgType interface{}, h func(s *netlib.Session, packetId int, data interface{}, sid int64) error) { + RegisterHandler(mainId, HandlerWrapper(h)) + f := func() interface{} { + tp := reflect.TypeOf(msgType) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return reflect.New(tp).Interface() + } + netlib.RegisterFactory(mainId, netlib.PacketFactoryWrapper(f)) +} diff --git a/common/imagecode.go b/common/imagecode.go new file mode 100644 index 0000000..0a1054a --- /dev/null +++ b/common/imagecode.go @@ -0,0 +1,27 @@ +package common + +import ( + "github.com/mojocn/base64Captcha" + "strings" +) + +var ImageStore = base64Captcha.DefaultMemStore + +func ImageCode() (b64s string, answer string, err error) { + driver := &base64Captcha.DriverString{ + Height: 50, + Width: 100, + NoiseCount: 0, + ShowLineOptions: base64Captcha.OptionShowHollowLine | base64Captcha.OptionShowSlimeLine, + Length: 5, + Source: "123456789qwertyuiopasdfghjklzxcvb", + } + var id string + c := base64Captcha.NewCaptcha(driver, ImageStore) + id, b64s, answer, err = c.Generate() + ImageStore.Verify(id, answer, true) + + b64s = strings.TrimPrefix(b64s, "data:image/png;base64,") + + return b64s, answer, err +} diff --git a/common/intconvert.go b/common/intconvert.go new file mode 100644 index 0000000..60478c8 --- /dev/null +++ b/common/intconvert.go @@ -0,0 +1,78 @@ +package common + +import ( + "math" +) + +/* + 整型安全转换 +*/ + +const ( + // MaxInt32 int32最大值 + MaxInt32 int32 = math.MaxInt32 + // MinInt32 int32最小值 + MinInt32 = math.MinInt32 +) + +const ( + // MaxUInt32 uint32最大值 + MaxUInt32 uint32 = math.MaxUint32 +) + +const ( + // MaxInt64 int64最大值 + MaxInt64 int64 = math.MaxInt64 + // MinInt64 int64最小值 + MinInt64 = math.MinInt64 +) + +const ( + // MaxUInt64 uint64最大值 + MaxUInt64 uint64 = math.MaxUint64 +) + +// MakeU64 将两个uint32拼凑为uint64 lo为低字节 hi为高字节 +func MakeU64(lo, hi uint32) uint64 { + return uint64(hi)<<32 | uint64(lo) +} + +// LowU32 返回uint64低字节(后uint32) +func LowU32(n uint64) uint32 { + return uint32(n & 0xffffffff) +} + +// HighU32 返回uint64高字节(前uint32) +func HighU32(n uint64) uint32 { + return uint32(n >> 32) +} + +// LowAndHighUI32 分别返回uint64低字节,高字节 +func LowAndHighUI32(n uint64) (uint32, uint32) { + return uint32(n & 0xffffffff), uint32(n >> 32) +} + +// MakeI64 将两个int32拼凑为int64 lo为低字节 hi为高字节 +func MakeI64(lo, hi int32) int64 { + return int64(hi)<<32 | int64(lo) +} + +// LowI32 返回int64低字节(后int32) +func LowI32(n int64) int32 { + return int32(n & 0xffffffff) +} + +// HighI32 返回int64高字节(前int32) +func HighI32(n int64) int32 { + return int32(n >> 32) +} + +// LowAndHighI32 分别返回int64低字节,高字节 +func LowAndHighI32(n int64) (int32, int32) { + return int32(n & 0xffffffff), int32(n >> 32) +} + +// LowAndHighI32 分别返回int64低字节,高字节 +func LowAndHighI64(n int64) (int64, int64) { + return n & 0xffffffff, n >> 32 +} diff --git a/common/log.go b/common/log.go new file mode 100644 index 0000000..dbb8522 --- /dev/null +++ b/common/log.go @@ -0,0 +1,77 @@ +package common + +import ( + "os" + "path/filepath" + + "github.com/howeyc/fsnotify" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" +) + +var LastModifyConfig int64 + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + var err error + workDir, err := os.Getwd() + if err != nil { + return err + } + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + + // Process events + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Warn("watch logger.xml modify goroutine err:", err) + } + }() + for { + select { + case ev, ok := <-watcher.Event: + if ok && ev != nil { + if ev.IsModify() { + obj := core.CoreObject() + if filepath.Base(ev.Name) == "logger.xml" { + if obj != nil { + obj.SendCommand(&loggerParamModifiedCommand{fileName: ev.Name}, false) + } + } + } + } else { + return + } + case err := <-watcher.Error: + logger.Logger.Warn("fsnotify error:", err) + } + } + logger.Logger.Warn("logger.xml watcher quit!") + }() + watcher.Watch(workDir) + return nil + }) +} + +type loggerParamModifiedCommand struct { + fileName string +} + +func (lmc *loggerParamModifiedCommand) Done(o *basic.Object) error { + logger.Logger.Info("===reload ", lmc.fileName) + data, err := os.ReadFile(lmc.fileName) + if err != nil { + return err + } + if len(data) != 0 { + err = logger.Reload(lmc.fileName) + if err != nil { + logger.Logger.Warn("===reload ", lmc.fileName, err) + } + } + return err +} diff --git a/common/log_mgr.go b/common/log_mgr.go new file mode 100644 index 0000000..08eb85a --- /dev/null +++ b/common/log_mgr.go @@ -0,0 +1,132 @@ +package common + +///////////////////////////////////////////////////////////// +//使用方法 +//1、定义自己日志模块名字 +//const( +// BaccaratLogger = "BaccaratLogger" +// ) +//2、通过自己定义的日志名获得日志实例,调用方式参考如下: +//-------getLoggerInstanceByName(BaccaratLogger).Trace(time.Now().String()) +//-------getLoggerInstanceByName(BaccaratLogger).Warn(time.Now().String()) +//-------getLoggerInstanceByName(BaccaratLogger).Error(time.Now().String()) +//-------getLoggerInstanceByName(BaccaratLogger).Debug(time.Now().String()) +//-------getLoggerInstanceByName(BaccaratLogger).Info(time.Now().String()) +//-------getLoggerInstanceByName(BaccaratLogger).Flush() +//3、如果自己定义的日志名在配置文件中没有找到,则使用默认的全局日志 +//4、可动态添加自己的日志配置,添加后即可生效 +//5、注意确保自己定义的日志模块名与配置日志的文件名一样 +//6、同时确保自己配置日志的文件中输出日志的文件名,参考如下: +// .......... +// .......... ↓↓↓这个日志输出的文件名 +// + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/logmgr_test.go b/common/logmgr_test.go new file mode 100644 index 0000000..7f69520 --- /dev/null +++ b/common/logmgr_test.go @@ -0,0 +1,23 @@ +package common + +import ( + "testing" + "time" +) + +const ( + BaccaratLogger = "BaccaratLogger" +) + +func TestLogMgr(t *testing.T) { + go func() { + for { + time.Sleep(time.Second) + GetLoggerInstanceByName(BaccaratLogger).Info(time.Now().String()) + GetLoggerInstanceByName(BaccaratLogger).Error(time.Now().String()) + GetLoggerInstanceByName(BaccaratLogger).Debug(time.Now().String()) + GetLoggerInstanceByName(BaccaratLogger).Trace(time.Now().String()) + GetLoggerInstanceByName(BaccaratLogger).Warn(time.Now().String()) + } + }() +} diff --git a/common/pbutils.go b/common/pbutils.go new file mode 100644 index 0000000..074b310 --- /dev/null +++ b/common/pbutils.go @@ -0,0 +1,85 @@ +package common + +import ( + "math/rand" +) + +// /////////////////////////////////////a2b +func MapToint32(a map[int]bool) (b []int32) { + b = make([]int32, len(a)) + i := 0 + for k, _ := range a { + b[i] = int32(k) + i++ + } + return +} +func Int32Toint(a []int32) (b []int) { + b = make([]int, len(a)) + for k, v := range a { + b[k] = int(v) + } + return +} +func ThreeTonullArray(a [3]int) (b []int32) { + b = make([]int32, len(a), len(a)) + for i := 0; i < len(a); i++ { + b[i] = int32(a[i]) + } + return +} +func RandInSliceIndex(pool []int) int { + var total int + for _, v := range pool { + total += v + } + val := int(rand.Int31n(int32(total))) + total = 0 + for index, v := range pool { + total += v + if total >= val { + return index + } + } + + return 0 +} +func IntToInt64(a []int) []int64 { + c := make([]int64, len(a), len(a)) + for k, v := range a { + c[k] = int64(v) + } + return c +} +func Int64Toint(a []int64) []int { + c := make([]int, len(a), len(a)) + for k, v := range a { + c[k] = int(v) + } + return c +} +func Int64ToInt32(a []int64) []int32 { + c := make([]int32, len(a), len(a)) + for k, v := range a { + c[k] = int32(v) + } + return c +} + +// 将数组类型转化为int切片类型的值 +func Int32SliceToInt(arr []int32) []int { + s := make([]int, 0) + for _, v := range arr { + s = append(s, int(v)) + } + return s +} + +// 将数组类型转化为int切片类型的值 +func IntSliceToInt32(arr []int) []int32 { + s := make([]int32, 0) + for _, v := range arr { + s = append(s, int32(v)) + } + return s +} diff --git a/common/poker.go b/common/poker.go new file mode 100644 index 0000000..ac64a4f --- /dev/null +++ b/common/poker.go @@ -0,0 +1,123 @@ +package common + +import ( + "fmt" + "strings" +) + +//牌序- K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2, A +// 52 53 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +// cards 将癞子牌转点数牌时使用 +var cards = [][]int32{ + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + {13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, + {26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, + {39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}, + {52, 53}, +} + +// GetPoint 获取牌点数 +func GetPoint(val int32) int32 { + switch { + case val == 52: + return 16 + case val == 53: + return 17 + default: + return val%13 + 1 + } +} + +// PointValue: 13 12 11 10 9 8 7 6 5 4 3 2 1 +// LogicValue: 13 12 11 10 9 8 7 6 5 4 3 15 14 +var pointLogic = [18]int32{ + 1: 14, 2: 15, + 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, + 14: 1, 15: 2, + 16: 16, 17: 17, +} + +// GetLogic 获取逻辑值 +func GetLogic(val int32) int32 { + return pointLogic[GetPoint(val)] +} + +// PointToLogic 根据点数获取逻辑值 +func PointToLogic(point int32) int32 { + return pointLogic[point] +} + +// LogicToPoint 根据逻辑值获取点数 +func LogicToPoint(logic int32) int32 { + return pointLogic[logic] +} + +// GetColor 获取花色 +func GetColor(val int32) int32 { + switch { + case val >= 0 && val <= 12: + return 0 + case val >= 13 && val <= 25: + return 1 + case val >= 26 && val <= 38: + return 2 + case val >= 39 && val <= 51: + return 3 + default: + return val + } +} + +// CreatCard 根据点数和花色生成牌 +func CreatCard(point int32, color int32, defaultColor int32) int32 { + if point == 16 { + return 52 + } + if point == 17 { + return 53 + } + if color < 0 || color > 3 { + color = defaultColor + } + if color < 0 || color > 3 { + color = 0 + } + return cards[int(color)][point-1] +} + +var color = [4]string{"♦", "♣", "♥", "♠"} + +func String(val int32) string { + switch { + case val == -1: + return "" + case val == 52: + return "小王" + case val == 53: + return "大王" + default: + return fmt.Sprint(GetPoint(val), color[GetColor(val)]) + } +} + +func StringCards(cards []int32) string { + s := strings.Builder{} + for _, v := range cards { + s.WriteString(String(v)) + s.WriteString(" ") + } + return s.String() +} +func StringCardsInt(cards []int) string { + s := strings.Builder{} + for _, v := range cards { + s.WriteString(String(int32(v))) + s.WriteString(" ") + } + return s.String() +} diff --git a/common/random.go b/common/random.go new file mode 100644 index 0000000..947e026 --- /dev/null +++ b/common/random.go @@ -0,0 +1,350 @@ +package common + +import ( + "fmt" + "math" + "math/rand" + "time" +) + +const ( + RAND32_M int32 = 2147483647 + RAND32_A = 48271 + RAND32_Q = RAND32_M / RAND32_A + RAND32_R = RAND32_M % RAND32_A +) + +type RandomGenerator struct { + rand32_state int32 +} + +func (this *RandomGenerator) RandomSeed(seed int32) { + this.rand32_state = seed +} + +func (this *RandomGenerator) Random() int32 { + hi := this.rand32_state / RAND32_Q + lo := this.rand32_state % RAND32_Q + test := RAND32_A*lo - RAND32_R*hi + if test > 0 { + this.rand32_state = test + } else { + this.rand32_state = test + RAND32_M + } + return this.rand32_state - 1 +} + +func (this *RandomGenerator) Rand32(max int32) int32 { + return this.Random() % max +} + +func (this *RandomGenerator) GetRandomSeed() int32 { + return this.rand32_state +} + +// [l..u) +func RandInt(args ...int) int { + switch len(args) { + case 0: + return rand.Int() + case 1: + if args[0] > 0 { + return rand.Intn(args[0]) + } else { + return 0 + } + default: + l := args[0] + u := args[1] + switch { + case l == u: + { + return l + } + case l > u: + { + return u + rand.Intn(l-u) + } + default: + { + return l + rand.Intn(u-l) + } + } + } +} + +func RandItemByAvg(s1 []int64) int64 { + if len(s1)%2 != 0 { + return 0 + } + rates := []int64{} + for i := 0; i < len(s1); i = i + 2 { + rates = append(rates, s1[i+1]) + } + index := RandInt(0, len(rates)) + return s1[index*2] +} + +func RandItemByWight(s1 []int64) int64 { + if len(s1)%2 != 0 { + return 0 + } + rates := []int64{} + for i := 0; i < len(s1); i = i + 2 { + rates = append(rates, s1[i+1]) + } + index := RandSliceIndexByWight(rates) + return s1[index*2] +} + +func RandSliceIndexByWight(s1 []int64) int { + total := int64(0) + for _, v := range s1 { + total += v + } + if total <= 0 { + return 0 + } + random := rand.Int63n(total) + total = 0 + for i, v := range s1 { + total += v + if random < total { + return i + } + } + return 0 +} + +func RandSliceIndexByWight31N(s1 []int32) int { + total := int32(0) + for _, v := range s1 { + total += v + } + if total <= 0 { + return 0 + } + random := rand.Int31n(total) + total = 0 + for i, v := range s1 { + total += v + if random < total { + return i + } + } + return 0 +} + +func RandSliceIndexByWightN(s1 []int) int { + total := 0 + for _, v := range s1 { + total += v + } + if total <= 0 { + return 0 + } + random := rand.Intn(total) + total = 0 + for i, v := range s1 { + total += v + if random < total { + return i + } + } + return 0 +} + +func RandNFromSlice(source []int, n int) []int { + if len(source) == 0 { + return source + } + if n > len(source) { + cycle := n / len(source) + rem := n % len(source) + for i := 0; i < cycle; i++ { + source = append(source, source...) + } + source = append(source, source[:rem]...) + } + idxs := rand.Perm(len(source)) + ret := make([]int, len(source)) + for i := 0; i < len(source); i++ { + ret[i] = source[idxs[i]] + } + return ret[:n] +} +func RandInt32Slice(source []int32) int32 { + if len(source) == 0 { + return 0 + } + return source[rand.Intn(len(source))] +} +func RandFromRange(minValue, maxValue int32) int32 { + if minValue < 0 || maxValue < 0 { + return 0 + } + if minValue >= maxValue { + return minValue + } + return rand.Int31n(maxValue-minValue+1) + minValue +} + +func RandFromRangeInt64(minValue, maxValue int64) int64 { + if minValue < 0 || maxValue < 0 { + return 0 + } + if minValue >= maxValue { + return minValue + } + return rand.Int63n(maxValue-minValue+1) + minValue +} + +func RandSmsCode() string { + //seed := rand.Int() + //code := seed % 999999 + //return strconv.Itoa(code) + return fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) +} +func RandSlice(n int) []int { + return rand.Perm(n) +} +func RandValueByRang(value int, l, u int) int { + r := RandInt(l, u) + return int(float64(value) * float64(r) / 100) +} + +// 随机一个索引,最大的是 中间索引 +func RandMaxMiddle(arrLen int) int { + if arrLen <= 0 { + return 0 + } + if arrLen%2 == 0 { + return rand.Intn(arrLen / 2) + } else { + return rand.Intn(arrLen/2 + 1) + } +} + +// 随机一个索引,最小的是 中间索引 +func RandLastMiddle(arrLen int) int { + if arrLen <= 0 { + return 0 + } + if arrLen%2 == 0 { + return rand.Intn(arrLen/2) + arrLen/2 + } else { + return rand.Intn(arrLen/2+1) + arrLen/2 + } +} + +// 获得一个线性的随机概率是否满足 +func RandLineInt64(curvalue, minValue, maxValue int64) bool { + ret := RandFromRangeInt64(minValue, maxValue) + if curvalue > ret { + return true + } + return false +} + +// 获得一个正态分布的随机概率是否满足 cur max 都需要传入正值,用法不太对 +func RandNormInt64(cur int64, max int64) bool { + t := math.Abs(rand.NormFloat64()) / 3 + if t > 1 { + t = 1 + } + if cur >= max { + return true + } + + return cur > int64(t*float64(max)) +} + +// 获得最小公倍数 +func GetMinCommonRate(array []int64) int64 { + ret := nlcm(array, len(array)) + return ret +} + +func gcd(a int64, b int64) int64 { + if a < b { + a, b = b, a + } + + if b == 0 { + return a + } else { + return gcd(b, a%b) + } +} + +func ngcd(array []int64, n int) int64 { + if n == 1 { + return array[0] + } + + return gcd(array[n-1], ngcd(array, n-1)) +} + +func lcm(a int64, b int64) int64 { + return a * b / gcd(a, b) +} + +func nlcm(array []int64, n int) int64 { + if n == 1 { + return array[0] + + } else { + return lcm(array[n-1], nlcm(array, n-1)) + } +} + +// 随机生成遍历的数组列表 +func GetRandomList(num int) []int { + var ret []int + for i := 0; i < num; i++ { + ret = append(ret, i) + r := RandInt(len(ret)) + ret[i], ret[r] = ret[r], ret[i] + } + + return ret +} + +// 从A中随机出B个数字 +func GetBNumFromA(A []int32, B int) []int32 { + if B >= len(A) { + return A + } + var ret []int32 + + var rate []int + for { + index := rand.Intn(len(A)) + if !InSliceInt(rate, index) { + rate = append(rate, index) + ret = append(ret, A[index]) + if len(ret) >= B { + return ret + } + } + } +} + +func GetBElementFromA(A []interface{}, B int) []interface{} { + if B >= len(A) { + return A + } + var ret []interface{} + + var rate []int + for { + index := rand.Intn(len(A)) + if !InSliceInt(rate, index) { + rate = append(rate, index) + ret = append(ret, A[index]) + if len(ret) >= B { + return ret + } + } + } +} diff --git a/common/random_test.go b/common/random_test.go new file mode 100644 index 0000000..8a95640 --- /dev/null +++ b/common/random_test.go @@ -0,0 +1,43 @@ +package common + +import ( + "testing" +) + +func TestRandom(t *testing.T) { + r := &RandomGenerator{} + r.RandomSeed(1000) + for i := 0; i < 100; i++ { + t.Log(r.Rand32(100)) + } +} +func TestRandMaxMiddle(t *testing.T) { + testData := [][]int{ + {0}, + {0, 1}, + {0, 1, 2}, + {0, 1, 2, 3}, + } + for _, value := range testData { + index := RandMaxMiddle(len(value)) + if index >= len(value) || index < 0 { + t.Error("Data", value) + t.Fatal("Error check in TestRandMaxMiddle") + } + } +} +func TestRandLastMiddle(t *testing.T) { + testData := [][]int{ + {0}, + {0, 1}, + {0, 1, 2}, + {0, 1, 2, 3}, + } + for _, value := range testData { + index := RandMaxMiddle(len(value)) + if index >= len(value) || index < 0 { + t.Error("Data", value) + t.Fatal("Error check in TestRandLastMiddle") + } + } +} diff --git a/common/rangerandid.go b/common/rangerandid.go new file mode 100644 index 0000000..46b96d8 --- /dev/null +++ b/common/rangerandid.go @@ -0,0 +1,155 @@ +package common + +import ( + "math/rand" + "time" +) + +const ( + MAX_TRY_RAND_CNT = 10 + RANDID_INVALID = -1 +) + +type BitPool struct { + flag []uint64 + cnt int +} + +func (bp *BitPool) Init(cnt int) { + bp.cnt = cnt/64 + 1 + bp.flag = make([]uint64, bp.cnt, bp.cnt) +} + +func (bp *BitPool) Mark(n int) bool { + idx := n / 64 + off := n % 64 + if idx < 0 || idx >= bp.cnt { + return false + } + + bp.flag[idx] |= 1 << uint(off) + return true +} + +func (bp *BitPool) Unmark(n int) bool { + idx := n / 64 + off := n % 64 + if idx < 0 || idx >= bp.cnt { + return false + } + + bp.flag[idx] &= ^(1 << uint(off)) + return true +} + +func (bp *BitPool) IsMark(n int) bool { + idx := n / 64 + off := n % 64 + if idx < 0 || idx >= bp.cnt { + return false + } + + return (bp.flag[idx] & (1 << uint(off))) != 0 +} + +type RandDistinctId struct { + rand *rand.Rand + bp BitPool + min int + max int + cap int +} + +func NewRandDistinctId(min, max int) *RandDistinctId { + var id RandDistinctId + id.Init(min, max) + return &id +} + +func (rid *RandDistinctId) Init(min, max int) { + rid.min = min + rid.max = max + rid.cap = max - min + rid.bp.Init(rid.cap) + rid.rand = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +func (rid *RandDistinctId) Alloc(id int) bool { + if !rid.bp.IsMark(id) { + rid.bp.Mark(id) + return true + } + return false +} + +func (rid *RandDistinctId) Free(id int) bool { + if rid.bp.IsMark(id) { + rid.bp.Unmark(id) + return true + } + return false +} + +func (rid *RandDistinctId) RandOne() int { + for i := 0; i < MAX_TRY_RAND_CNT; i++ { + pos := rid.rand.Intn(rid.cap) + if !rid.bp.IsMark(pos) { + rid.bp.Mark(pos) + return pos + rid.min + } + } + + //try one by one + rpos := rid.rand.Intn(rid.cap) + if rpos < rid.cap/2 { + for i := rpos; i >= 0; i-- { + if !rid.bp.IsMark(i) { + rid.bp.Mark(i) + return i + rid.min + } + } + } else { + for i := rpos; i < rid.cap; i++ { + if !rid.bp.IsMark(i) { + rid.bp.Mark(i) + return i + rid.min + } + } + } + return RANDID_INVALID +} + +//func memConsumed() uint64 { +// runtime.GC() //GC,排除对象影响 +// var memStat runtime.MemStats +// runtime.ReadMemStats(&memStat) +// return memStat.Sys +//} + +//func main() { +// var idPool RandDistinctId +// before := memConsumed() +// idPool.Init(MIN, MAX) +// after := memConsumed() +// +// idMap := make(map[int]bool) +// for i := 0; i < 1000000; i++ { +// id := idPool.RandId() +// if id != -1 { +// idMap[id] = true +// } +// } +// +// start := time.Now() +// for i := 0; i < 10000; i++ { +// id := idPool.RandId() +// if id != -1 { +// fmt.Println(i, "->", id) +// idMap[id] = true +// } +// +// } +// +// fmt.Println("total take:", time.Now().Sub(start), " len:", len(idMap)) +// fmt.Println(fmt.Sprintf("new %v pool consume %.3f MB", MAX-MIN, float64(after-before)/1024/1024)) +//} diff --git a/common/responseresult.go b/common/responseresult.go new file mode 100644 index 0000000..f1d492d --- /dev/null +++ b/common/responseresult.go @@ -0,0 +1,128 @@ +package common + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "mongo.games.com/goserver/core/admin" + "mongo.games.com/goserver/core/logger" +) + +const ( + ResponseTag_Ok int = iota //0 + ResponseTag_ParamError //1 + ResponseTag_NoFindService //2 + ResponseTag_NoFindUser //3 + ResponseTag_DataMarshalError //4 + ResponseTag_TransactYield //5 + ResponseTag_Unsupport //6 + ResponseTag_NoFindRoom //7 + ResponseTag_NoData //8 + ResponseTag_NoFindClub //9 + ResponseTag_ClubHadCreated //10 + ResponseTag_SrcCardCntNotEnough //11 + ResponseTag_SrcPlayerNotExist //12 + ResponseTag_DestPlayerNotExist //13 + ResponseTag_TransferCardFailed //14 + ResponseTag_OpFailed //15 + ResponseTag_InviterNotExist //16 + ResponseTag_HadSetInviter //17 + ResponseTag_CreateNewPlayerFailed //18 + ResponseTag_ClubAdminCountReachLimit //19 + ResponseTag_OnlyBeClubMember //20 + ResponseTag_FetchNiceIdFail //21 + ResponseTag_MoneyNotEnough //22 + ResponseTag_TransferMoneyFailed //23 + ResponseTag_SrcMoneyCntNotEnough //24 + ResponseTag_CoinNotEnough //25 + ResponseTag_CoinInUse //26 + ResponseTag_RMBNotEnough //27 + ResponseTag_AgentNotExist //28 +) + +type HttpResult struct { + Tag int + Msg interface{} +} + +func ResponseMsg(req *http.Request, res http.ResponseWriter, tag int, msg interface{}) bool { + result := &HttpResult{ + Tag: tag, + Msg: msg, + } + + data, err := json.Marshal(&result) + if err != nil { + logger.Logger.Info(req.RemoteAddr, " Marshal error:", err) + return false + } + + fmt.Println(string(data[:])) + dataLen := len(data) + res.Header().Set("Content-Length", fmt.Sprintf("%v", dataLen)) + res.Header().Set("Access-Control-Allow-Origin", "*") + res.WriteHeader(http.StatusOK) + pos := 0 + for pos < dataLen { + writeLen, err := res.Write(data[pos:]) + if err != nil { + logger.Logger.Info(req.RemoteAddr, " SendData error:", err) + return false + } + pos += writeLen + } + + return true +} + +func responseMsg2(req *http.Request, res http.ResponseWriter, params map[string]interface{}) bool { + data, err := json.Marshal(params) + if err != nil { + logger.Logger.Info(req.RemoteAddr, " Marshal error:", err) + return false + } + + fmt.Println(string(data[:])) + dataLen := len(data) + res.Header().Set("Content-Length", fmt.Sprintf("%v", dataLen)) + res.Header().Set("Access-Control-Allow-Origin", "*") + res.WriteHeader(http.StatusOK) + pos := 0 + for pos < dataLen { + writeLen, err := res.Write(data[pos:]) + if err != nil { + logger.Logger.Info(req.RemoteAddr, " SendData error:", err) + return false + } + pos += writeLen + } + + return true +} + +func RequestCheck(req *http.Request, whitelist []string) bool { + strs := strings.Split(req.RemoteAddr, ":") + if len(strs) != 2 { + return false + } + + if len(admin.Config.WhiteHttpAddr) > 0 { + for _, value := range admin.Config.WhiteHttpAddr { + if value == strs[0] { + return true + } + } + } + + if len(whitelist) > 0 { + for _, value := range whitelist { + if value == strs[0] { + return true + } + } + } + //都没设置也让过 + return len(whitelist) == 0 && len(admin.Config.WhiteHttpAddr) == 0 +} diff --git a/common/serializable.go b/common/serializable.go new file mode 100644 index 0000000..92b479f --- /dev/null +++ b/common/serializable.go @@ -0,0 +1,103 @@ +package common + +import ( + "bytes" + "encoding/binary" + "errors" + "hash/crc32" + "os" +) + +const ( + MDUMP_MAGIC_CODE uint32 = 0xdeadfee1 + MDUMP_FILEHEAD_LEN = 16 + MDUMP_VERSION_LASTEST = 1 +) + +var ( + errMdumpEmpty = errors.New(".mdump empty") + errMdumpFormat = errors.New(".mdump format error") + errMdumpCheckSum = errors.New(".mdump checksum error") + errMdumpTruncation = errors.New(".mdump truncation error") +) + +// 对象序列化接口 +type Serializable interface { + //序列化 + Marshal() ([]byte, error) + //反序列化 + Unmarshal([]byte, interface{}) error +} + +// 内存dump文件头 +type MDumpFileHead struct { + MagicCode uint32 //mdump文件标记 + Version uint32 //版本号 + DataLen uint32 //数据区长度 + CheckSum uint32 //校验和 +} + +func ReadMDumpFile(fileName string) (head *MDumpFileHead, data []byte, err error) { + buf, err := os.ReadFile(fileName) + if err != nil { + return + } + os.Remove(fileName) + //解析文件头 + head = &MDumpFileHead{} + err = binary.Read(bytes.NewReader(buf), binary.BigEndian, head) + if err != nil { + return + } + + if head.MagicCode != MDUMP_MAGIC_CODE { + err = errMdumpFormat + return + } + + data = buf[MDUMP_FILEHEAD_LEN:] + if uint32(len(data)) != head.DataLen { + err = errMdumpTruncation + return + } + + checkSum := crc32.ChecksumIEEE(data) + if checkSum != head.CheckSum { + err = errMdumpCheckSum + return + } + + return +} + +func WriteMDumpFile(fileName string, data []byte) error { + if len(data) == 0 { + return errMdumpEmpty + } + + checkSum := crc32.ChecksumIEEE(data) + head := MDumpFileHead{ + MagicCode: MDUMP_MAGIC_CODE, + Version: MDUMP_VERSION_LASTEST, + DataLen: uint32(len(data)), + CheckSum: checkSum, + } + + file, err := os.Create(fileName) + if err != nil { + return err + } + defer file.Close() + //写文件头 + err = binary.Write(file, binary.BigEndian, &head) + if err != nil { + return err + } + //写数据 + _, err = file.WriteAt(data, int64(MDUMP_FILEHEAD_LEN)) + if err != nil { + return err + } + + return err +} diff --git a/common/serializable_test.go b/common/serializable_test.go new file mode 100644 index 0000000..7838971 --- /dev/null +++ b/common/serializable_test.go @@ -0,0 +1,10 @@ +package common + +import "testing" + +func TestWriteMDumpFile(t *testing.T) { + err := WriteMDumpFile("test.mdump", []byte("hello world!")) + if err != nil { + t.Fatal("WriteMDumpFile fail", err) + } +} diff --git a/common/slice.go b/common/slice.go new file mode 100644 index 0000000..a748a3b --- /dev/null +++ b/common/slice.go @@ -0,0 +1,470 @@ +package common + +import ( + "fmt" + "math" + "sort" + "strconv" + "strings" +) + +func CopySliceInt32(s []int32) []int32 { + n := len(s) + if n != 0 { + temp := make([]int32, n, n) + copy(temp, s) + return temp + } + return nil +} + +func CopySliceInt64(s []int64) []int64 { + n := len(s) + if n != 0 { + temp := make([]int64, n, n) + copy(temp, s) + return temp + } + return nil +} + +func CopySliceIntToInt32(s []int) []int32 { + n := len(s) + if n != 0 { + temp := make([]int32, n, n) + for i := 0; i < n; i++ { + temp[i] = int32(s[i]) + } + return temp + } + return nil +} + +func CopySliceInt32ToInt(s []int32) []int { + n := len(s) + if n != 0 { + temp := make([]int, n, n) + for i := 0; i < n; i++ { + temp[i] = int(s[i]) + } + return temp + } + return nil +} + +func CopySliceInt32ToInt64(s []int32) []int64 { + n := len(s) + if n != 0 { + temp := make([]int64, n, n) + for i := 0; i < n; i++ { + temp[i] = int64(s[i]) + } + return temp + } + return nil +} + +func InSliceInt32(sl []int32, v int32) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} + +func InSliceInt32Slice(sl []int32, sub []int32) bool { + for _, vv := range sub { + if !InSliceInt32(sl, vv) { + return false + } + } + return true +} + +func DelSliceInt32(sl []int32, v int32) []int32 { + index := -1 + for key, value := range sl { + if value == v { + index = key + break + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl +} +func DelSliceInt64(sl []int64, v int64) []int64 { + index := -1 + for key, value := range sl { + if value == v { + index = key + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl +} +func DelSliceInt64s(cards []int64, sl []int64) []int64 { + c := make([]int64, len(cards)) + s := make([]int64, len(sl)) + copy(c, cards) + copy(s, sl) + for i := 0; i < len(sl); i++ { + for k, v := range c { + isF := false + for m, n := range s { + if v == n { + c = append(c[:k], c[k+1:]...) + s = append(s[:m], s[m+1:]...) + isF = true + break + } + } + if isF { + break + } + } + } + return c +} + +// Data. Predict: ["4"] +// a := []string{"1","2","3","4"} +// b := []string{"0","1","2","3"} +func GetASliceInt32NotInB(a []int32, b []int32) []int32 { + var c []int32 + temp := map[int32]struct{}{} + for _, val := range b { + if _, ok := temp[val]; !ok { + temp[val] = struct{}{} // 空struct 不占内存空间 + } + } + + for _, val := range a { + if _, ok := temp[val]; !ok { + c = append(c, val) + } + } + + return c +} + +func DelSliceIn32s(cards []int32, sl []int32) []int32 { + c := make([]int32, len(cards)) + s := make([]int32, len(sl)) + copy(c, cards) + copy(s, sl) + for i := 0; i < len(sl); i++ { + for k, v := range c { + isF := false + for m, n := range s { + if v == n { + c = append(c[:k], c[k+1:]...) + s = append(s[:m], s[m+1:]...) + isF = true + break + } + } + if isF { + break + } + } + } + return c +} +func DelSliceInt(sl []int, v int) []int { + index := -1 + for key, value := range sl { + if value == v { + index = key + break + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl +} + +func DelSliceString(sl []string, v string) ([]string, bool) { + index := -1 + for key, value := range sl { + if value == v { + index = key + break + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl, index != -1 +} + +func InSliceInt32Index(sl []int32, v int32) int { + for idx, vv := range sl { + if vv == v { + return idx + } + } + return -1 +} +func InSliceInt64Index(sl []int64, v int64) int { + for idx, vv := range sl { + if vv == v { + return idx + } + } + return -1 +} +func IntSliceEqual(left []int, right []int) bool { + if len(left) != len(right) { + return false + } + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} +func Int32SliceEqual(left []int32, right []int32) bool { + if len(left) != len(right) { + return false + } + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} + +func InSliceInt64(sl []int64, v int64) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} + +func InSliceInt(sl []int, v int) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} + +func InSliceString(sl []string, v string) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} + +func InSliceInterface(sl []interface{}, v interface{}) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} + +func InsertValueToSlice(index int, value int, arr []int) []int { + arr[index] = value + playerRandData := arr[index] + arr = append(arr[:index], arr[index+1:]...) + var coinRandDataSort = make([]int, 0, len(arr)) + isEnd := true + for i := 0; i < len(arr); i++ { + if arr[i] < playerRandData { + coinRandDataSort = append(coinRandDataSort, arr[:i]...) + coinRandDataSort = append(coinRandDataSort, playerRandData) + coinRandDataSort = append(coinRandDataSort, arr[i:]...) + isEnd = false + break + } + } + if isEnd { + coinRandDataSort = append(arr, playerRandData) + } + return coinRandDataSort +} +func IsLadderSlice(sl []int) bool { + sort.Ints(sl) + switch len(sl) { + case 0: + return false + case 1: + return true + default: + for i := 0; i < len(sl)-1; i++ { + if sl[i] < sl[i+1] { + return false + } + } + return true + } +} + +func IsSameSliceStr(dst []string, src []string) bool { + if len(dst) != len(src) { + return false + } + for i := 0; i < len(dst); i++ { + if dst[i] != src[i] { + return false + } + } + return true +} +func StrSliceInt(str string, sep string) ([]int, error) { + var err error + t := strings.Split(str, sep) + var ret []int + for i := 0; i < len(t); i++ { + if n, ok := strconv.Atoi(t[i]); ok == nil { + ret = append(ret, n) + } else { + ret = append(ret, 0) + err = ok + } + } + return ret, err +} + +func SliceIntEqual(dst []int, src []int) bool { + if len(dst) != len(src) { + return false + } + for i := 0; i < len(dst); i++ { + if dst[i] != src[i] { + return false + } + } + return true +} +func SliceInt64Equal(dst []int64, src []int64) bool { + if len(dst) != len(src) { + return false + } + for i := 0; i < len(dst); i++ { + if dst[i] != src[i] { + return false + } + } + return true +} +func SliceInt32Equal(dst []int32, src []int32) bool { + if len(dst) != len(src) { + return false + } + for i := 0; i < len(dst); i++ { + if dst[i] != src[i] { + return false + } + } + return true +} +func SliceNoRepeate(data []int) []int { + var newData []int + for _, value := range data { + repeate := false + for _, n := range newData { + if n == value { + repeate = true + } + } + if !repeate { + newData = append(newData, value) + } + } + return newData +} + +func SliceInterfaceToInt32(data []interface{}) []int32 { + cnt := len(data) + if cnt > 0 { + val := make([]int32, 0, cnt) + for _, f := range data { + val = append(val, int32(int32(f.(float64)))) + } + return val + } + return nil +} +func SliceMaxValue(sl []int) int { + maxValue := math.MinInt32 + for _, value := range sl { + if value > maxValue { + maxValue = value + } + } + return maxValue +} +func SliceMinValue(sl []int) int { + minValue := math.MaxInt32 + for _, value := range sl { + if value < minValue { + minValue = value + } + } + return minValue +} +func SliceValueCount(sl []int, value int) int { + var count int + for _, v := range sl { + if v == value { + count++ + } + } + return count +} +func SliceValueWeight(sl []int, index int) float64 { + if index < 0 || index > len(sl) { + return 0 + } + value := sl[index] + totle := 0 + for _, v := range sl { + totle += v + } + return float64(value) / float64(totle) +} + +type Int32Slice []int32 + +func (p Int32Slice) Len() int { return len(p) } +func (p Int32Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Int32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Int32Slice) Sort() { sort.Sort(p) } + +type Int64Slice []int64 + +func (p Int64Slice) Len() int { return len(p) } +func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Int64Slice) Sort() { sort.Sort(p) } + +type Float64Slice []float64 + +func (p Float64Slice) Len() int { return len(p) } +func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Float64Slice) Sort() { sort.Sort(p) } + +func Int32SliceToString(a []int32, delim string) string { + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(a)), delim), "[]") +} + +func StringSliceToString(a []string, delim string) string { + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(a)), delim), "[]") +} diff --git a/common/slice_test.go b/common/slice_test.go new file mode 100644 index 0000000..0e44802 --- /dev/null +++ b/common/slice_test.go @@ -0,0 +1,114 @@ +package common + +import ( + "math/rand" + "sort" + "testing" +) + +func TestInsertValueToSlice(t *testing.T) { + type InsertValueToSliceTestCase struct { + arr []int + index int + value int + } + testData := []InsertValueToSliceTestCase{ + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 0, value: 110}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 0, value: 1}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 0, value: 55}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 5, value: 110}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 5, value: 1}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 5, value: 55}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 9, value: 110}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 9, value: 1}, + {arr: []int{100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, index: 9, value: 55}, + } + for _, value := range testData { + newArr := InsertValueToSlice(value.index, value.value, value.arr) + if !IsLadderSlice(newArr) || len(newArr) != len(value.arr) { + t.Log(value) + t.Failed() + } + } +} +func TestSliceNoRepeate(t *testing.T) { + type TestCase struct { + Src []int + Dest []int + } + var testData = []TestCase{ + TestCase{Src: []int{}, Dest: []int{}}, + TestCase{Src: []int{1}, Dest: []int{1}}, + TestCase{Src: []int{1, 1}, Dest: []int{1}}, + TestCase{Src: []int{1, 1, 1}, Dest: []int{1}}, + TestCase{Src: []int{1, 2, 1}, Dest: []int{1, 2}}, + TestCase{Src: []int{1, 2, 3}, Dest: []int{1, 2, 3}}, + TestCase{Src: []int{1, 2, 3, 3}, Dest: []int{1, 2, 3}}, + TestCase{Src: []int{1, 2, 3, 3, 5}, Dest: []int{1, 2, 3, 5}}, + TestCase{Src: []int{1, 2, 3, 3, 5, 5}, Dest: []int{1, 2, 3, 5}}, + TestCase{Src: []int{1, 2, 3, 3, 5, 5, 6}, Dest: []int{1, 2, 3, 5, 6}}, + TestCase{Src: []int{1, 2, 3, 3, 5, 6, 6}, Dest: []int{1, 2, 3, 5, 6}}, + TestCase{Src: []int{1, 2, 3, 3, 5, 6, 7}, Dest: []int{1, 2, 3, 5, 6, 7}}, + } + for _, value := range testData { + dest := SliceNoRepeate(value.Src) + equal := SliceIntEqual(dest, value.Dest) + if !equal { + t.Error(value) + t.Error(dest) + t.Fatal() + } + } + for i := 0; i < 1000; i++ { + length := rand.Intn(100) + 1 + src := rand.Perm(length) + dest := make([]int, length) + copy(dest, src) + e := rand.Intn(50) + for s := 0; s < e; s++ { + src = append(src, rand.Intn(length)) + } + norepeate := SliceNoRepeate(src) + equal := SliceIntEqual(norepeate, dest) + if !equal { + sort.Ints(src) + sort.Ints(dest) + sort.Ints(norepeate) + t.Error(src) + t.Error(dest) + t.Error(norepeate) + t.Fatal() + } + } +} +func TestSliceDelValue(t *testing.T) { + type DelTestData struct { + Arr []int32 + Del int32 + } + var TestCase = []DelTestData{ + {Arr: []int32{1, 2, 3, 4, 5}, Del: 1}, + {Arr: []int32{1, 2, 3, 4, 5}, Del: 3}, + {Arr: []int32{1, 2, 3, 4, 5}, Del: 5}, + {Arr: []int32{1, 2, 3, 4, 5}, Del: 6}, + {Arr: []int32{1, 2, 3, 4, 5}, Del: 0}, + } + for i := 0; i < len(TestCase); i++ { + randArr := DelSliceInt32(TestCase[i].Arr, TestCase[i].Del) + if InSliceInt32(randArr, TestCase[i].Del) { + t.Log(randArr) + t.Log(TestCase[i].Del) + t.Fatal("Slice del value failed.") + } + } + for i := 0; i < 1000; i++ { + randArr := rand.Perm(rand.Intn(1000)) + delValue := rand.Intn(1000) + randArr = DelSliceInt(randArr, delValue) + if InSliceInt(randArr, delValue) { + t.Log(randArr) + t.Log(delValue) + t.Fatal("Slice del value failed.") + } + } +} diff --git a/common/sortobject.go b/common/sortobject.go new file mode 100644 index 0000000..010bad4 --- /dev/null +++ b/common/sortobject.go @@ -0,0 +1,25 @@ +package common + +import "sort" + +type SortObjectSlice []SortObject + +type SortObject struct { + SortValue int + ObjectValue int + RoomId int +} + +func (so SortObjectSlice) Len() int { + return len(so) +} +func (so SortObjectSlice) Less(i, j int) bool { + return so[i].SortValue > so[j].SortValue +} +func (so SortObjectSlice) Swap(i, j int) { + so[i], so[j] = so[j], so[i] +} + +func Sort(data SortObjectSlice) { + sort.Sort(data) +} diff --git a/common/srvmsg.go b/common/srvmsg.go new file mode 100644 index 0000000..5fc01d1 --- /dev/null +++ b/common/srvmsg.go @@ -0,0 +1,60 @@ +package common + +import ( + "fmt" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/player" +) + +const ( + SRVMSG_CODE_DEFAULT int32 = iota +) + +type SrvMsgSender interface { + SendToClient(packetid int, rawpack interface{}) bool +} + +func SendSrvMsg(sender SrvMsgSender, msgId int32, params ...interface{}) bool { + pack := CreateSrvMsg(msgId, params) + return sender.SendToClient(int(player.PlayerPacketID_PACKET_SC_SRVMSG), pack) +} + +func CreateSrvMsg(msgId int32, params ...interface{}) *player.SCSrvMsg { + pack := &player.SCSrvMsg{ + MsgId: msgId, + } + for _, p := range params { + switch d := p.(type) { + case string: + pack.Params = append(pack.Params, &player.SrvMsgParam{StrParam: d}) + case int: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case int8: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case int16: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case int32: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: d}) + case int64: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case uint: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case uint8: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case uint16: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case uint32: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case uint64: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case float32: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + case float64: + pack.Params = append(pack.Params, &player.SrvMsgParam{IntParam: int32(d)}) + default: + pack.Params = append(pack.Params, &player.SrvMsgParam{StrParam: fmt.Sprintf("%v", p)}) + } + } + proto.SetDefaults(pack) + return pack +} diff --git a/common/sstransmit.go b/common/sstransmit.go new file mode 100644 index 0000000..2aa5cf8 --- /dev/null +++ b/common/sstransmit.go @@ -0,0 +1,86 @@ +package common + +import ( + "fmt" + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/profile" + "mongo.games.com/goserver/core/utils" +) + +var ( + TransmitMaker = &SSTransmitPacketFactory{} +) + +type SSTransmitPacketFactory struct { +} + +type SSTransmitHandler struct { +} + +func (this *SSTransmitPacketFactory) CreatePacket() interface{} { + pack := &server.SSTransmit{} + return pack +} + +func (this *SSTransmitPacketFactory) CreateTransmitPacket(packetid int, data interface{}, sid int64) (rawproto.Message, error) { + pack := &server.SSTransmit{ + SessionId: sid, + } + if byteData, ok := data.([]byte); ok { + pack.PacketData = byteData + if byteData == nil || len(byteData) == 0 { + logger.Logger.Info("SSTransmitPacketFactory.CreateTransmitPacket PacketData is empty") + } + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.PacketData = byteData + if byteData == nil || len(byteData) == 0 { + logger.Logger.Info("SSTransmitPacketFactory.CreateTransmitPacket PacketData is empty") + } + } else { + logger.Logger.Info("SSTransmitPacketFactory.CreateTransmitPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *SSTransmitHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + //logger.Logger.Trace("SSTransmitHandler Process recv ", data) + if transmitPack, ok := data.(*server.SSTransmit); ok { + pd := transmitPack.GetPacketData() + sid := transmitPack.GetSessionId() + packetid, packet, err := netlib.UnmarshalPacket(pd) + if err == nil { + h := GetHandler(packetid) + if h != nil { + utils.DumpStackIfPanic(fmt.Sprintf("SSTransmitHandler.Process error, packetid:%v", packetid)) + watch := profile.TimeStatisticMgr.WatchStart(fmt.Sprintf("/action/packet:%v", packetid), profile.TIME_ELEMENT_ACTION) + err := h.Process(s, packetid, packet, sid) + if watch != nil { + watch.Stop() + } + if err != nil { + logger.Logger.Tracef("Packet [%d] error:", packetid, err) + } + return err + } else { + logger.Logger.Tracef("Packet %v not find handler.", packetid) + } + } else { + logger.Logger.Trace("SSTransmitHandler process err:", err) + } + } + return nil +} + +func init() { + netlib.RegisterHandler(int(server.TransmitPacketID_PACKET_SS_PACKET_TRANSMIT), &SSTransmitHandler{}) + netlib.RegisterFactory(int(server.TransmitPacketID_PACKET_SS_PACKET_TRANSMIT), TransmitMaker) +} diff --git a/common/sysmsg.go b/common/sysmsg.go new file mode 100644 index 0000000..3024391 --- /dev/null +++ b/common/sysmsg.go @@ -0,0 +1,7 @@ +package common + +// 系统消息编号 +const ( + SYSMSG_BEGIN int = iota + SYSMSG_PASSHU //因过胡规则,此牌不能胡 +) diff --git a/common/temp.txt b/common/temp.txt new file mode 100644 index 0000000..67eb83b --- /dev/null +++ b/common/temp.txt @@ -0,0 +1,30 @@ + GainWay_NewPlayer int32 = iota //0.新建角色 新建账号,系统赠送房卡%s张|新建账号,系统赠送金币%s个 remark:无 + GainWay_Pay //1.后台增加(主要是充值) %s,获得d%张房卡,剩余d%张 remark:充值X元 + GainWay_Transfer_In //2.俱乐部转移(转入) s%赠送您房卡d%张,剩余d%张 remark:玩家名称 + GainWay_AgentReturn //3.代创房间返还 - + GainWay_DrawPrize //4.领取奖励 领取奖励房卡d%张,剩余%d张 remark:无 + GainWay_Share //5.每日分享 每日分享活动,奖励d%房卡,剩余d%房卡 remark:无 + GainWay_Activty //6.活动获得 s%活动奖励d%金币,剩余d%金币 remark:活动名称 + GainWay_Game //7.玩游戏消耗 开房间房卡-d%张,剩余d%张 remark:无 + GainWay_Task //8.任务 完成%s任务,奖励房卡d%张,剩余d%张 remark:任务名称 + GainWay_ByPMCmd //9.pm命令 - + GainWay_MatchSignup //10.比赛报名 - + GainWay_MatchPlay //11.比赛回兑 - + GainWay_MatchAdd //12.比赛补充 - + GainWay_MatchBreakBack //13.退赛退还 - + GainWay_MatchSystemSupply //14.系统补给 - + GainWay_Exchange //15.兑换 兑换%s,红包%d分,剩余红包%d分 remark:房卡x张 + GainWay_Contribute //16.贡献 - + GainWay_ClubMoneySceneFee //17.俱乐部钻石场房费 - + GainWay_Transfer_Out //18.俱乐部转移(转出) 赠送给s%房卡d%张,剩余d%张 remark:玩家名称 + GainWay_ServiceFee //19.桌费 扣除桌费d%金币,剩余d%金币 remark:无 + GainWay_CoinSceneWin //20.金豆场赢取 参与游戏赢取d%金币,剩余d%金币 remark:无 + GainWay_CoinSceneLost //21.金豆场输 参与游戏输掉d%金币,剩余d%金币 remark:无 + GainWay_BindTel //22.绑定手机 绑定手机,获得房卡%d张,剩余%d张 remark:无 + GainWay_PlayerShare //23.隶属玩家每日分享 每日分享活动,领取房卡%d张,剩余%d张 remark:无 + GainWay_Invite //24.邀请玩家 邀请玩家,获得红包%d分,剩余%d分 remark:无 + GainWay_LoginSign //25.签到奖励 + GainWay_OnlineGift //26.签到奖励 + GainWay_CoinSceneEnter //27.进入金币场预扣 + GainWay_ShopBuy //28.商城购买或者兑换 使用%s,购买金币%d,剩余%d remark:房卡x张 + GainWay_CoinSceneLeave //29.离开金币场回兑 \ No newline at end of file diff --git a/common/time.go b/common/time.go new file mode 100644 index 0000000..d6156c0 --- /dev/null +++ b/common/time.go @@ -0,0 +1,145 @@ +package common + +import ( + "time" + + "mongo.games.com/goserver/core/timer" +) + +type WGDayTimeChange struct { + LastMin int + LastHour int + LastDay int + LastWeek int + LastMonth int +} + +func InSameDay(tNow, tPre time.Time) bool { + if tPre.IsZero() { + return true + } + + if tNow.Day() != tPre.Day() { + return false + } + + if tNow.Sub(tPre) < time.Hour*24 { + return true + } + return false +} +func InSameDayNoZero(tNow, tPre time.Time) bool { + + if tNow.Day() != tPre.Day() { + return false + } + + if tNow.Sub(tPre) < time.Hour*24 { + return true + } + return false +} + +func TsInSameDay(tsNow, tsPre int64) bool { + tNow := time.Unix(tsNow, 0) + tPre := time.Unix(tsPre, 0) + return InSameDay(tNow, tPre) +} + +func IsContinueDay(tNow, tPre time.Time) bool { + if tPre.IsZero() { + return true + } + tNext := tPre.AddDate(0, 0, 1) + if InSameDay(tNow, tNext) { + return true + } + return false +} + +func InSameMonth(tNow, tPre time.Time) bool { + if tPre.IsZero() { + return true + } + + if tNow.Month() != tPre.Month() { + return false + } + if tNow.Year() == tPre.Year() { + return true + } + return false +} + +func InSameWeek(tNow, tPre time.Time) bool { + if tPre.IsZero() { + return true + } + + preYear, preWeek := tPre.ISOWeek() + nowYear, nowWeek := tNow.ISOWeek() + if preYear == nowYear && preWeek == nowWeek { + return true + } + return false +} + +func TsInSameWeek(tsNow, tsPre int64) bool { + tNow := time.Unix(tsNow, 0) + tPre := time.Unix(tsPre, 0) + return InSameWeek(tNow, tPre) +} + +func DiffDay(tNow, tPre time.Time) int { // AddDate(0, 0, -1) 会显示差0天 + y, m, d := tPre.Date() + tStart := time.Date(y, m, d, 0, 0, 0, 0, tPre.Location()) + return int(tNow.Sub(tStart) / (time.Hour * 24)) +} + +func DiffDaybyTs(tNowts, tPrets int64) int { + + tNow := time.Unix(tNowts, 0) + tPre := time.Unix(tPrets, 0) + y, m, d := tPre.Date() + tStart := time.Date(y, m, d, 0, 0, 0, 0, tPre.Location()) + return int(tNow.Sub(tStart) / (time.Hour * 24)) +} + +func DiffMonth(tNow, tPre time.Time) int { + y1, m1, _ := tNow.Date() + y2, m2, _ := tPre.Date() + diffMonth := (y1-y2)*12 + (int(m1) - int(m2)) + return int(diffMonth) +} + +func DelayInvake(method func(), ud interface{}, delay time.Duration, times int) (timer.TimerHandle, bool) { + return timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + if method != nil { + method() + } + return true + }), ud, delay, times) +} + +func StrTimeToTs(strTime string) int64 { + rTime, err := time.ParseInLocation("2006-01-02 15:04:05", strTime, time.Local) + if err != nil { + return 0 + } + return rTime.Unix() +} +func TsToStrTime(tc int64) string { + //return time.Now().Format("2018-07-02 19:14:00") + return time.Unix(tc, 0).Format("2006-01-02 15:04:05") +} +func TsToStrDateTime(tc int64) string { + //return time.Now().Format("2018-07-02 19:14:00") + return time.Unix(tc, 0).Format("2006-01-02") +} + +func InTimeRange(beginHour, beginMinute, endHour, endMinute, checkHour, checkMinute int32) bool { + beginTime := beginHour*100 + beginMinute + endTime := endHour*100 + endMinute + checkTime := checkHour*100 + checkMinute + return beginTime <= checkTime && checkTime <= endTime +} diff --git a/common/time_test.go b/common/time_test.go new file mode 100644 index 0000000..a2a0b77 --- /dev/null +++ b/common/time_test.go @@ -0,0 +1,129 @@ +package common + +import ( + "testing" + "time" +) + +type TimeTestCaseData struct { + t1 time.Time + t2 time.Time + expectResult bool +} + +func TestInSameDay(t *testing.T) { + + testCases := []*TimeTestCaseData{ + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 17, 15, 12, 15, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 16, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: true, + }, + &TimeTestCaseData{ + t1: time.Date(2017, time.May, 16, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + } + + for i := 0; i < len(testCases); i++ { + tc := testCases[i] + if InSameDay(tc.t1, tc.t2) != tc.expectResult { + t.Fatal("IsSameDay(", tc.t1, tc.t2, ") expect result is ", tc.expectResult) + } + } +} + +func TestIsContinueDay(t *testing.T) { + + testCases := []*TimeTestCaseData{ + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 17, 15, 12, 15, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: true, + }, + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 16, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + &TimeTestCaseData{ + t1: time.Date(2017, time.May, 17, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + } + + for i := 0; i < len(testCases); i++ { + tc := testCases[i] + if IsContinueDay(tc.t1, tc.t2) != tc.expectResult { + t.Fatal("IsContinueDay(", tc.t1, tc.t2, ") expect result is ", tc.expectResult) + } + } +} + +func TestInSameMonth(t *testing.T) { + + testCases := []*TimeTestCaseData{ + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 17, 15, 12, 15, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: true, + }, + &TimeTestCaseData{ + t1: time.Date(2016, time.June, 1, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 31, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + &TimeTestCaseData{ + t1: time.Date(2017, time.May, 17, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + } + + for i := 0; i < len(testCases); i++ { + tc := testCases[i] + if InSameMonth(tc.t1, tc.t2) != tc.expectResult { + t.Fatal("InSameMonth(", tc.t1, tc.t2, ") expect result is ", tc.expectResult) + } + } +} + +func TestInSameWeek(t *testing.T) { + + testCases := []*TimeTestCaseData{ + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 17, 15, 12, 15, 0, time.Local), + t2: time.Date(2016, time.May, 16, 15, 12, 15, 0, time.Local), + expectResult: true, + }, + &TimeTestCaseData{ + t1: time.Date(2016, time.May, 16, 23, 59, 59, 0, time.Local), + t2: time.Date(2016, time.May, 15, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + &TimeTestCaseData{ + t1: time.Date(2016, time.January, 1, 23, 59, 59, 0, time.Local), + t2: time.Date(2015, time.December, 31, 15, 12, 15, 0, time.Local), + expectResult: true, + }, + &TimeTestCaseData{ + t1: time.Date(2024, time.March, 17, 23, 59, 59, 0, time.Local), + t2: time.Date(2024, time.March, 18, 15, 12, 15, 0, time.Local), + expectResult: false, + }, + } + + for i := 0; i < len(testCases); i++ { + tc := testCases[i] + if InSameWeek(tc.t1, tc.t2) != tc.expectResult { + t.Fatal("InSameWeek(", tc.t1, tc.t2, ") expect result is ", tc.expectResult) + } + } +} diff --git a/common/transtype.go b/common/transtype.go new file mode 100644 index 0000000..a9f849c --- /dev/null +++ b/common/transtype.go @@ -0,0 +1,108 @@ +package common + +import ( + "mongo.games.com/goserver/core/transact" +) + +const ( + TransType_Login transact.TransType = 1000 + TransType_Logout = 1001 + TransType_WebTrascate = 1002 + TransType_AddCoin = 1003 + TransType_ViewData = 1004 + TransType_DayTimeChange = 1005 + TransType_CoinSceneChange = 1006 + TransType_WebApi = 1007 + TransType_WebApi_ForRank = 1101 + TransType_GameSrvWebApi = 1008 + TransType_QueryCoinPool = 1009 + TransType_StopServer = 1010 + TransType_QueryAllCoinPool = 1011 + TransType_ActThrSrvWebApi = 1012 + TransType_MatchSceneChange = 1013 + TransType_MiniGameAddCoin = 1014 + TransType_ServerCtrl = 1015 +) + +type M2GWebTrascate struct { + Tag int + Param string +} + +type M2GWebApiRequest struct { + Path string + RawQuery string + ReqIp string + Body []byte +} + +type M2GWebApiResponse struct { + Body []byte +} + +type W2GQueryCoinPool struct { + GameId int32 + GameMode int32 + Platform string + GroupId int32 +} +type PlatformStates struct { + Platform string + GamesVal map[int32]*CoinPoolStatesInfo +} +type GamesIndex struct { + GameFreeId int32 + GroupId int32 +} +type QueryGames struct { + Index map[string][]*GamesIndex +} + +// 单个平台各游戏水池信息概况 +type CoinPoolStatesInfo struct { + GameId int32 //当前游戏id + GameFreeId int32 //游戏id + LowerLimit int32 //库存下限 + UpperLimit int32 //库存上限 + CoinValue int32 //当前库存值 + States int32 //水池状态 +} + +type CoinPoolSetting struct { + Platform string //平台id + GameFreeId int32 //游戏id + ServerId int32 //服务器id + GroupId int32 //组id + InitValue int32 //初始库存值 + LowerLimit int32 //库存下限 + UpperLimit int32 //库存上限 + UpperOffsetLimit int32 //上限偏移值 + MaxOutValue int32 //最大吐钱数 + ChangeRate int32 //库存变化速度 + MinOutPlayerNum int32 //最少吐钱人数 + UpperLimitOfOdds int32 //赔率上限(万分比) + CoinValue int64 //当前库存值 + PlayerNum int32 //当前在线人数 + BaseRate int32 //基础赔率 + CtroRate int32 //调节赔率 + HardTimeMin int32 //收分调节频率下限 + HardTimeMax int32 //收分调节频率上限 + NormalTimeMin int32 //正常调节频率下限 + NormalTimeMax int32 //正常调节频率上限 + EasyTimeMin int32 //放分调节频率下限 + EasyTimeMax int32 //放分调节频率上限 + EasrierTimeMin int32 //吐分调节频率下限 + EasrierTimeMax int32 //吐分分调节频率上限 + CpCangeType int32 + CpChangeInterval int32 + CpChangeTotle int32 + CpChangeLower int32 + CpChangeUpper int32 + ProfitRate int32 //收益比例 + ProfitPool int64 //当前收益池 + CoinPoolMode int32 //当前池模式 + ResetTime int32 + ProfitAutoRate int32 + ProfitManualRate int32 + ProfitUseManual bool +} diff --git a/dbproxy/README b/dbproxy/README new file mode 100644 index 0000000..9175bb8 --- /dev/null +++ b/dbproxy/README @@ -0,0 +1,3 @@ +1.动态数据库连接(配置文件修改立刻更新数据库连接或收到etcd通知) +2.rabbitmq消费端,日志,游戏记录等异步写入数据库 +3.提供数据库rpc调用 \ No newline at end of file diff --git a/dbproxy/config.json b/dbproxy/config.json new file mode 100644 index 0000000..3c242e4 --- /dev/null +++ b/dbproxy/config.json @@ -0,0 +1,61 @@ +{ + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 31004 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal": { + "SupportSignal": true + }, + "cmgo": { + "CfgFile": "./etc/mgo.json" + }, + "common": { + "AppId": "5c56d1644966f078bfb90c71", + "IsDevMode": true + }, + "costum": { + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQQosPrefetchCount": 2, + "RMQQosPrefetchSize": 0, + "RMQQosGlobal": true, + "etcdurl": [ + "127.0.0.1:2379" + ], + "etcduser": "", + "etcdpwd": "" + } +} \ No newline at end of file diff --git a/dbproxy/etc/mgo.json b/dbproxy/etc/mgo.json new file mode 100644 index 0000000..7f84ae4 --- /dev/null +++ b/dbproxy/etc/mgo.json @@ -0,0 +1,102 @@ +{ + "Global":{ + "user":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_global", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + }, + "log":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_log", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + }, + "monitor":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_monitor", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + } + }, + "Platforms":{ + "0":{ + "user":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_user_plt_000", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + }, + "log":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_log_plt_000", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + } + }, + "1":{ + "user":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_user_plt_001", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + }, + "log":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_log_plt_001", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + } + }, + "12":{ + "user":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_user_plt_012", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + }, + "log":{ + "HostName":"127.0.0.1", + "HostPort":27017, + "Database":"win88_log_plt_012", + "Username":"", + "Password":"", + "Options":"", + "CfgSign":"", + "CfgVer":0 + } + } + } +} \ No newline at end of file diff --git a/dbproxy/etcd.go b/dbproxy/etcd.go new file mode 100644 index 0000000..429e76f --- /dev/null +++ b/dbproxy/etcd.go @@ -0,0 +1,37 @@ +package main + +import ( + "strings" + + "go.etcd.io/etcd/client/v3" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/etcd" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + etcd.Register(etcd.ETCDKEY_SYS_PLT_DBCFG_PREFIX, webapi.PlatformDbConfig{}, func(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + + config, ok := data.(*webapi.PlatformDbConfig) + if !ok { + logger.Logger.Errorf("etcd completeKey:%s, data type error", completeKey) + return + } + + s := strings.TrimPrefix(completeKey, etcd.ETCDKEY_SYS_PLT_DBCFG_PREFIX) + arr := strings.Split(s, "/") + if len(arr) >= 1 { + pltId := arr[0] + //用户库 + mongo.MgoSessionMgrSington.UptCfgWithEtcd(pltId, "user", config.MongoDb) + //日志库 + mongo.MgoSessionMgrSington.UptCfgWithEtcd(pltId, "log", config.MongoDbLog) + } + }) +} diff --git a/dbproxy/logger.xml b/dbproxy/logger.xml new file mode 100644 index 0000000..f6eb37b --- /dev/null +++ b/dbproxy/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dbproxy/main.go b/dbproxy/main.go new file mode 100644 index 0000000..bcac8e1 --- /dev/null +++ b/dbproxy/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "log" + "net" + "net/http" + "net/rpc" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/module" + + _ "games.yol.com/win88" + _ "mongo.games.com/game/dbproxy/mq" + + "mongo.games.com/game/common" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +var rabbitMqConsumer *mq.RabbitMQConsumer + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + model.InitGameParam() + rabbitMqConsumer = mq.NewRabbitMQConsumer(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}) + if rabbitMqConsumer != nil { + rabbitMqConsumer.Start() + } + + //尝试初始化 + svc.GetOnePlayerIdFromBucket() + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + if rabbitMqConsumer != nil { + rabbitMqConsumer.Stop() + } + model.ShutdownRPClient() + return nil + }) +} + +func main() { + defer core.ClosePackages() + core.LoadPackages("config.json") + + rpc.HandleHTTP() // 采用http协议作为rpc载体 + lis, err := net.Listen(common.CustomConfig.GetString("MgoRpcCliNet"), common.CustomConfig.GetString("MgoRpcCliAddr")) + if err != nil { + log.Fatalln("fatal error: ", err) + } + go http.Serve(lis, nil) + + waitor := module.Start() + waitor.Wait("main()") +} diff --git a/dbproxy/mongo/config.go b/dbproxy/mongo/config.go new file mode 100644 index 0000000..f07434b --- /dev/null +++ b/dbproxy/mongo/config.go @@ -0,0 +1,70 @@ +package mongo + +import ( + "github.com/howeyc/fsnotify" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" +) + +/* + 当数据库配置文件修改后实时更新mongo数据库连接 +*/ + +type Configuration struct { + CfgFile string // 数据库配置文件路径 + watcher *fsnotify.Watcher +} + +func (c *Configuration) Name() string { + return "cmgo" +} + +func (c *Configuration) Init() error { + MgoSessionMgrSington.LoadConfig(c.CfgFile) + + var err error + c.watcher, err = fsnotify.NewWatcher() + if err != nil { + logger.Logger.Warnf("%s NewWatcher err:%v", c.CfgFile, err) + return err + } + err = c.watcher.Watch(c.CfgFile) + if err != nil { + logger.Logger.Warnf("%s Watch err:%v", c.CfgFile, err) + return err + } + + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Warnf("%s watch modify goroutine err:%v", c.CfgFile, err) + } + }() + for { + select { + case ev, ok := <-c.watcher.Event: + if ok && ev != nil { + if ev.IsModify() { + MgoSessionMgrSington.LoadConfig(c.CfgFile) + } + } else { + return + } + case err := <-c.watcher.Error: + logger.Logger.Info("fsnotify error:", err) + } + } + logger.Logger.Warnf("%s watcher quit!", c.CfgFile) + }() + return nil +} + +func (c *Configuration) Close() error { + c.watcher.Close() + MgoSessionMgrSington.Close() + return nil +} + +func init() { + core.RegistePackage(&Configuration{}) +} diff --git a/dbproxy/mongo/sessionmgr.go b/dbproxy/mongo/sessionmgr.go new file mode 100644 index 0000000..d2f3166 --- /dev/null +++ b/dbproxy/mongo/sessionmgr.go @@ -0,0 +1,326 @@ +package mongo + +import ( + "encoding/json" + "fmt" + "github.com/globalsign/mgo" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" + "os" + "sync" + "time" +) + +const ( + G_P = "__$G_P$__" +) + +var MgoSessionMgrSington = &MgoSessionMgr{ + pltCfgs: new(sync.Map), + pltMgos: make(map[string]*PlatformSession), +} + +type MgoCfg struct { + HostName string + HostPort int32 + Database string + Username string + Password string + Options string + CfgVer int32 + WithEtcd bool +} + +type Config struct { + Global map[string]*MgoCfg + Platforms map[string]map[string]*MgoCfg // 平台:内部库名称:数据库名称 +} + +type Collection struct { + *mgo.Collection +} + +type Database struct { + sync.RWMutex + *mgo.Database + mc map[string]*Collection +} + +func (db *Database) C(name string) (*Collection, bool) { + db.RLock() + c, ok := db.mc[name] + if ok { + db.RUnlock() + return c, false + } + db.RUnlock() + + db.Lock() + c, ok = db.mc[name] + if ok { + db.Unlock() + return c, false + } + cc := &Collection{Collection: db.Database.C(name)} + db.mc[name] = cc + db.Unlock() + + return cc, true +} + +type Session struct { + sync.RWMutex + *mgo.Session + db *Database + cfg *MgoCfg +} + +func (s *Session) DB() *Database { + s.RLock() + if s.db != nil { + s.RUnlock() + return s.db + } + s.RUnlock() + s.Lock() + s.db = &Database{Database: s.Session.DB(s.cfg.Database), mc: make(map[string]*Collection)} + s.Unlock() + return s.db +} + +type PlatformSession struct { + sync.RWMutex + sesses map[string]*Session // 内部库名称 +} + +func (ps *PlatformSession) GetSession(name string) (*Session, bool) { + ps.RLock() + s, ok := ps.sesses[name] + ps.RUnlock() + return s, ok +} + +func (ps *PlatformSession) SetSession(name string, s *Session) { + ps.Lock() + ps.sesses[name] = s + ps.Unlock() +} + +func (ps *PlatformSession) DiscardSession(name string, s *Session) { + ps.Lock() + old, ok := ps.sesses[name] + delete(ps.sesses, name) + ps.Unlock() + if ok && old != nil && old == s { + //1分钟后释放,防止有pending的任务在执行 + time.AfterFunc(time.Minute, func() { + old.Close() + }) + } +} + +type MgoSessionMgr struct { + sync.RWMutex + pltCfgs *sync.Map + pltMgos map[string]*PlatformSession // 平台名称 +} + +func newMgoSession(user, password, host string, port int32, options string) (s *mgo.Session, err error) { + login := "" + if user != "" { + login = user + ":" + password + "@" + } + if host == "" { + host = "localhost" + } + if port == 0 { + port = 27017 + } + if options != "" { + options = "?" + options + } + // http://goneat.org/pkg/labix.org/v2/mgo/#Session.Mongo + // [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options] + url := fmt.Sprintf("mongodb://%s%s:%d/admin%s", login, host, port, options) + //fmt.Println(url) + session, err := mgo.Dial(url) + if err != nil { + return nil, err + } + session.SetSafe(&mgo.Safe{}) + return session, nil +} + +func (msm *MgoSessionMgr) LoadConfig(cfgFile string) error { + data, err := os.ReadFile(cfgFile) + if err != nil { + logger.Logger.Errorf("MgoSessionMgr.LoadConfig ReadFile err:%v", err) + return err + } + + var cfg Config + err = json.Unmarshal(data, &cfg) + if err != nil { + logger.Logger.Errorf("MgoSessionMgr.LoadConfig Unmarshal err:%v", err) + return err + } + + //全局配置 + for key, cfg := range cfg.Global { + if _, ok := msm.UptCfg(G_P, key, cfg); ok { + if ps, olds, ok := msm.HasPltMgoSession(G_P, key); ok { + if olds.cfg.CfgVer < cfg.CfgVer { + ps.DiscardSession(key, olds) + } + } + } + } + + //平台配置 + for plt, cfgs := range cfg.Platforms { + for key, cfg := range cfgs { + if _, ok := msm.UptCfg(plt, key, cfg); ok { + if ps, olds, ok := msm.HasPltMgoSession(plt, key); ok { + if olds.cfg.CfgVer < cfg.CfgVer { + ps.DiscardSession(key, olds) + } + } + } + } + } + return nil +} + +func (msm *MgoSessionMgr) GetPlt(plt string) (*PlatformSession, bool) { + msm.RLock() + ps, ok := msm.pltMgos[plt] + msm.RUnlock() + return ps, ok +} + +func (msm *MgoSessionMgr) HasPltMgoSession(plt, key string) (*PlatformSession, *Session, bool) { + ps, ok := msm.GetPlt(plt) + if !ok { + return nil, nil, false + } + + s, ok := ps.GetSession(key) + return ps, s, ok +} + +func (msm *MgoSessionMgr) GetPltMgoSession(plt, key string) *Session { + ps, ok := msm.GetPlt(plt) + if !ok { + msm.Lock() + ps, ok = msm.pltMgos[plt] + if !ok { + ps = &PlatformSession{ + sesses: make(map[string]*Session), + } + msm.pltMgos[plt] = ps + } + msm.Unlock() + } + + if ps == nil { + return nil + } + + if s, ok := ps.GetSession(key); ok { + return s + } + + ps.Lock() + defer ps.Unlock() + s, ok := ps.sesses[key] + if ok { + return s + } + + if c, ok := msm.GetCfg(plt, key); ok { + s, err := newMgoSession(c.Username, c.Password, c.HostName, c.HostPort, c.Options) + if s == nil || err != nil { + logger.Logger.Error("GetPltMgoSession(%s,%s) err:%v", plt, key, err) + return nil + } + ss := &Session{Session: s, cfg: c} + ps.sesses[key] = ss + return ss + } + + return nil +} + +func (msm *MgoSessionMgr) GetGlobal(key string) *Session { + return msm.GetPltMgoSession(G_P, key) +} + +func (msm *MgoSessionMgr) GetCfg(plt, key string) (*MgoCfg, bool) { + if val, ok := msm.pltCfgs.Load(plt); ok { + if cfgs, ok := val.(*sync.Map); ok { + if cfg, ok := cfgs.Load(key); ok { + if c, ok := cfg.(*MgoCfg); ok { + return c, true + } + } + } + } + return nil, false +} + +func (msm *MgoSessionMgr) UptCfg(plt, key string, cfg *MgoCfg) (*MgoCfg, bool) { + if val, ok := msm.pltCfgs.Load(plt); ok { + cfgs, _ := val.(*sync.Map) + if old, ok := cfgs.Load(key); ok { + cfgs.Store(key, cfg) + return old.(*MgoCfg), true + } else { + cfgs.Store(key, cfg) + return nil, false + } + } else { + cfgs := new(sync.Map) + msm.pltCfgs.Store(plt, cfgs) + cfgs.Store(key, cfg) + return nil, false + } +} + +func (msm *MgoSessionMgr) UptCfgWithEtcd(plt, key string, cfg *webapi_proto.MongoDbSetting) { + //文件配置优先级高于etcd + if oldCfg, exist := msm.GetCfg(plt, key); exist && !oldCfg.WithEtcd { + return + } + + newcfg := &MgoCfg{ + HostName: cfg.HostName, + HostPort: cfg.HostPort, + Database: cfg.Database, + Username: cfg.Username, + Password: cfg.Password, + Options: cfg.Options, + CfgVer: cfg.CfgVer, + WithEtcd: true, + } + if _, ok := msm.UptCfg(plt, key, newcfg); ok { + if ps, olds, ok := msm.HasPltMgoSession(plt, key); ok { + if olds.cfg.CfgVer < cfg.CfgVer { + ps.DiscardSession(key, olds) + } + } + } + + return +} + +func (msm *MgoSessionMgr) Close() { + msm.RLock() + defer msm.RUnlock() + for _, plt := range msm.pltMgos { + plt.RLock() + for _, s := range plt.sesses { + s.Close() + } + plt.RUnlock() + } +} diff --git a/dbproxy/mq/c_apilog.go b/dbproxy/mq/c_apilog.go new file mode 100644 index 0000000..2f77882 --- /dev/null +++ b/dbproxy/mq/c_apilog.go @@ -0,0 +1,40 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.APILogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.APILog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.APILogsCollection() + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.APILogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_clientlog.go b/dbproxy/mq/c_clientlog.go new file mode 100644 index 0000000..4df5009 --- /dev/null +++ b/dbproxy/mq/c_clientlog.go @@ -0,0 +1,56 @@ +package mq + +func init() { + //mq.RegisteSubscriber(model.ClientLogCollName, func(e broker.Event) (err error) { + // msg := e.Message() + // if msg != nil { + // defer func() { + // if err != nil { + // mq.BackUp(e, err) + // } + // + // e.Ack() + // + // recover() + // }() + // + // var log model.ClientLog + // err = json.Unmarshal(msg.Body, &log) + // if err != nil { + // logger.Logger.Errorf("[mq] %s %v", model.ClientLogCollName, err) + // return + // } + // + // logger.Logger.Tracef("[mq] %s %v", model.ClientLogCollName, string(msg.Body)) + // + // data := map[string]interface{}{} + // err = json.Unmarshal([]byte(log.Data), &data) + // if err != nil { + // logger.Logger.Errorf("[mq] %s %v", model.ClientLogCollName, err) + // return + // } + // + // // 获取平台id + // platform := log.Platform + // if log.Platform == "" { + // id, ok := data["platform"] + // if ok { + // platform = string(id.([]byte)) + // } + // } + // + // data["ts"] = log.Ts + // if log.Snid > 0 { + // data["snid"] = log.Snid + // } + // + // c := svc.ClientLogStartCollection(platform) + // if c != nil { + // err = c.Insert(data) + // } + // + // return + // } + // return nil + //}, broker.Queue(model.ClientLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_coingivelog.go b/dbproxy/mq/c_coingivelog.go new file mode 100644 index 0000000..fb427ac --- /dev/null +++ b/dbproxy/mq/c_coingivelog.go @@ -0,0 +1,40 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.CoinGiveLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.CoinGiveLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.CoinGiveLogCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.CoinGiveLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_coinlog.go b/dbproxy/mq/c_coinlog.go new file mode 100644 index 0000000..a0948cf --- /dev/null +++ b/dbproxy/mq/c_coinlog.go @@ -0,0 +1,50 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.CoinLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.CoinLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + if log.Count == 0 { //玩家冲账探针 + RabbitMQPublisher.Send(model.TopicProbeCoinLogAck, log) + } else { + c := svc.CoinLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + if err == nil { + err = svc.InsertCoinWAL(log.Platform, model.NewCoinWAL(log.SnId, log.Count, log.LogType, log.InGame, log.CoinType, log.RoomId, log.Time.UnixNano())) + if err != nil { + return + } + } + } + } + return + } + return nil + }, broker.Queue(model.CoinLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_friendrecordlog.go b/dbproxy/mq/c_friendrecordlog.go new file mode 100644 index 0000000..646521b --- /dev/null +++ b/dbproxy/mq/c_friendrecordlog.go @@ -0,0 +1,36 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.FriendRecordLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.FriendRecord + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + err = svc.InsertFriendRecordLog(log) + return + } + return nil + }, broker.Queue(model.FriendRecordLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_gamegamedetailedlog.go b/dbproxy/mq/c_gamegamedetailedlog.go new file mode 100644 index 0000000..2f4206a --- /dev/null +++ b/dbproxy/mq/c_gamegamedetailedlog.go @@ -0,0 +1,44 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/logger" +) + +func init() { + mq.RegisterSubscriber(model.GameDetailedLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.GameDetailedLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + logger.Logger.Tracef("mq receive GameDetailedLog:%v", log) + c := svc.GameDetailedLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + if err != nil { + logger.Logger.Tracef("c.Insert(log) err:%v", err.Error()) + } + } + return + } + return nil + }, broker.Queue(model.GameDetailedLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_gameplayerlistlog.go b/dbproxy/mq/c_gameplayerlistlog.go new file mode 100644 index 0000000..63da3cd --- /dev/null +++ b/dbproxy/mq/c_gameplayerlistlog.go @@ -0,0 +1,40 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.GamePlayerListLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.GamePlayerListLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.GamePlayerListLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.GamePlayerListLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_invite.go b/dbproxy/mq/c_invite.go new file mode 100644 index 0000000..f268d8d --- /dev/null +++ b/dbproxy/mq/c_invite.go @@ -0,0 +1,71 @@ +package mq + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/astaxie/beego/cache" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +var InviteNumCache = cache.NewMemoryCache() + +func init() { + mq.RegisterSubscriber(model.EvtBindInvite, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.BindInvite + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + // 绑定 + err = svc.BindInviteSnId(log.Platform, log.SnId, log.InviteSnId, log.Ts) + if err != nil { + logger.Logger.Errorf("BindInviteSnId error:%v", err) + return err + } + + name := fmt.Sprintf("%v", log.InviteSnId) + b := InviteNumCache.Get(name) + n, _ := b.(int32) + if n > 0 { + n++ + } else { + n, err = svc.GetInviteNum(log.Platform, log.InviteSnId) + if err != nil { + logger.Logger.Errorf("BindInviteSnId error:%v", err) + return err + } + } + InviteNumCache.Put(name, n, int64(time.Hour.Seconds())) + + // 更新绑定数量 + RabbitMQPublisher.Send(model.AckBindNum, &model.BindNum{ + SnId: log.InviteSnId, + Num: n, + }) + + return + } + return nil + }, broker.Queue(model.EvtBindInvite), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_itemlog.go b/dbproxy/mq/c_itemlog.go new file mode 100644 index 0000000..fd47133 --- /dev/null +++ b/dbproxy/mq/c_itemlog.go @@ -0,0 +1,40 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.ItemLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.ItemLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.ItemLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.ItemLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_jackpotlog.go b/dbproxy/mq/c_jackpotlog.go new file mode 100644 index 0000000..577f1f3 --- /dev/null +++ b/dbproxy/mq/c_jackpotlog.go @@ -0,0 +1,31 @@ +package mq + +//func init() { +// mq.RegisteSubscriber(model.JackPotLogCollName, func(e broker.Event) (err error) { +// msg := e.Message() +// if msg != nil { +// defer func() { +// if err != nil { +// mq.BackUp(e, err) +// } +// +// e.Ack() +// +// recover() +// }() +// +// var log model.JackPotLog +// err = json.Unmarshal(msg.Body, &log) +// if err != nil { +// return +// } +// +// c := svc.JackPotLogsCollection(log.Platform) +// if c != nil { +// _, err = c.Upsert(bson.M{"_id": log.LogId}, log) +// } +// return +// } +// return nil +// }, broker.Queue(model.JackPotLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +//} diff --git a/dbproxy/mq/c_loginlog.go b/dbproxy/mq/c_loginlog.go new file mode 100644 index 0000000..784d3c9 --- /dev/null +++ b/dbproxy/mq/c_loginlog.go @@ -0,0 +1,57 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.LoginLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.LoginLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.LoginLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.LoginLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) + + //for test + //RegisterSubscriber(model.LoginLogCollName, func(e broker.Event) (err error) { + // msg := e.Message() + // if msg != nil { + // var log model.LoginLog + // err = json.Unmarshal(msg.Body, &log) + // if err != nil { + // return + // } + // + // logger.Logger.Trace(log) + // return + // } + // return nil + //}, broker.Queue(model.LoginLogCollName+"_echo"), rabbitmq.DurableQueue()) + //for test +} diff --git a/dbproxy/mq/c_onlinelog.go b/dbproxy/mq/c_onlinelog.go new file mode 100644 index 0000000..d8d5525 --- /dev/null +++ b/dbproxy/mq/c_onlinelog.go @@ -0,0 +1,43 @@ +package mq + +import ( + "encoding/json" + + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +func init() { + mq.RegisterSubscriber(model.OnlineLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.OnlineLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := new(svc.OnlineLogSvc) + if c != nil { + ret := false + err = c.InsertSignleOnlineLog(&log, &ret) + } + return + } + return nil + }, broker.Queue(model.OnlineLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_rank.go b/dbproxy/mq/c_rank.go new file mode 100644 index 0000000..44fa0b2 --- /dev/null +++ b/dbproxy/mq/c_rank.go @@ -0,0 +1,85 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/logger" +) + +func init() { + // 排位排行榜 + mq.RegisterSubscriber(model.MQRankSeason, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.PlayerRankScore + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + logger.Logger.Tracef("SubscriberPlayerRankScore: %+v", log) + + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + err := svc.RankSeasonUpsert(&log) + if err != nil { + logger.Logger.Errorf("RankSeasonUpsert err: %v", err) + } + return nil + }), true) + + return + } + return nil + }, broker.Queue(model.MQRankSeason), broker.DisableAutoAck(), rabbitmq.DurableQueue()) + + // 金币榜 + mq.RegisterSubscriber(model.MQRankPlayerCoin, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.RankPlayerCoin + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + logger.Logger.Tracef("SubscriberRankPlayerCoin: %+v", log) + + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + err := svc.RankPlayerCoinUpsert(&log) + if err != nil { + logger.Logger.Errorf("RankPlayerCoinUpsert err: %v", err) + } + return nil + }), true) + + return + } + return nil + }, broker.Queue(model.MQRankPlayerCoin), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_scenecoinlog.go b/dbproxy/mq/c_scenecoinlog.go new file mode 100644 index 0000000..2d9f444 --- /dev/null +++ b/dbproxy/mq/c_scenecoinlog.go @@ -0,0 +1,40 @@ +package mq + +import ( + "encoding/json" + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.SceneCoinLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.SceneCoinLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.SceneCoinLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.SceneCoinLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/c_welfarelog.go b/dbproxy/mq/c_welfarelog.go new file mode 100644 index 0000000..3bc4bb2 --- /dev/null +++ b/dbproxy/mq/c_welfarelog.go @@ -0,0 +1,41 @@ +package mq + +import ( + "encoding/json" + + "mongo.games.com/game/dbproxy/svc" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +func init() { + mq.RegisterSubscriber(model.WelfareLogCollName, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + if err != nil { + mq.BackUp(e, err) + } + + e.Ack() + + recover() + }() + + var log model.WelfareLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + c := svc.WelfareLogsCollection(log.Platform) + if c != nil { + err = c.Insert(log) + } + return + } + return nil + }, broker.Queue(model.WelfareLogCollName), broker.DisableAutoAck(), rabbitmq.DurableQueue()) +} diff --git a/dbproxy/mq/publisher.go b/dbproxy/mq/publisher.go new file mode 100644 index 0000000..c48c666 --- /dev/null +++ b/dbproxy/mq/publisher.go @@ -0,0 +1,33 @@ +package mq + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +var RabbitMQPublisher *mq.RabbitMQPublisher + +func init() { + ////首先加载游戏配置 + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + //rabbitmq打开链接 + RabbitMQPublisher = mq.NewRabbitMQPublisher(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}, common.CustomConfig.GetInt("RMQPublishBacklog")) + if RabbitMQPublisher != nil { + err := RabbitMQPublisher.Start() + if err != nil { + panic(err) + } + } + return nil + }) + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + //关闭rabbitmq连接 + if RabbitMQPublisher != nil { + RabbitMQPublisher.Stop() + } + + return nil + }) +} diff --git a/dbproxy/svc/h_crashhash.go b/dbproxy/svc/h_crashhash.go new file mode 100644 index 0000000..b85e1c7 --- /dev/null +++ b/dbproxy/svc/h_crashhash.go @@ -0,0 +1,71 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + CrashHashDBName = "user" + CrashHashCollName = "user_crashhash" + ErrHashDBNotOpen = model.NewDBError(CrashHashDBName, CrashHashCollName, model.NOT_OPEN) +) + +func CrashHashCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, CrashHashDBName) + if s != nil { + c, first := s.DB().C(CrashHashCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"wheel"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type CrashHashSvc struct { +} + +func (svc *CrashHashSvc) InsertCrashHash(args *model.CrashHash, ret *model.HashRet) error { + ccrashhashs := CrashHashCollection() + if ccrashhashs == nil { + ret.Tag = 4 + return ErrHashDBNotOpen + } + + err := ccrashhashs.Insert(args) + if err != nil { + logger.Logger.Info("InsertCrashHash error:", err) + ret.Tag = 4 + return nil + } + + ret.Hash = nil + ret.Tag = 5 + return nil +} + +func (svc *CrashHashSvc) GetCrashHash(args *model.HashIdArg, ret *model.HashRet) error { + cat := CrashHashCollection() + if cat == nil { + return nil + } + //err := cat.Find(bson.M{"wheel": args.Wheel}).All(&ret.Hash) + err := cat.Find(bson.M{}).All(&ret.Hash) + if err != nil { + logger.Logger.Error("Get model.GetCrashHash data eror.", err) + ret.Tag = 4 + return err + } + return nil +} + +var _CrashHashSvc = &CrashHashSvc{} + +func init() { + rpc.Register(_CrashHashSvc) +} diff --git a/dbproxy/svc/h_crashhashatom.go b/dbproxy/svc/h_crashhashatom.go new file mode 100644 index 0000000..9d69490 --- /dev/null +++ b/dbproxy/svc/h_crashhashatom.go @@ -0,0 +1,71 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + CrashHashAtomDBName = "user" + CrashHashAtomCollName = "user_crashhashatom" + ErrHashAtomDBNotOpen = model.NewDBError(CrashHashAtomDBName, CrashHashAtomCollName, model.NOT_OPEN) +) + +func CrashHashAtomCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, CrashHashAtomDBName) + if s != nil { + c, first := s.DB().C(CrashHashAtomCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"wheel"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type CrashHashAtomSvc struct { +} + +func (svc *CrashHashAtomSvc) InsertCrashHashAtom(args *model.CrashHashAtom, ret *model.HashRet) error { + ccrashhashatoms := CrashHashAtomCollection() + if ccrashhashatoms == nil { + ret.Tag = 4 + return ErrHashDBNotOpen + } + + err := ccrashhashatoms.Insert(args) + if err != nil { + logger.Logger.Info("InsertCrashHashAtom error:", err) + ret.Tag = 4 + return nil + } + + ret.Hash = nil + ret.Tag = 5 + return nil +} + +func (svc *CrashHashAtomSvc) GetCrashHashAtom(args *model.HashIdArg, ret *model.HashRet) error { + cat := CrashHashAtomCollection() + if cat == nil { + return nil + } + //err := cat.Find(bson.M{"wheel": args.Wheel}).All(&ret.Hash) + err := cat.Find(bson.M{}).All(&ret.Hash) + if err != nil { + logger.Logger.Error("Get model.GetCrashHashAtom data eror.", err) + ret.Tag = 4 + return err + } + return nil +} + +var _CrashHashAtomSvc = &CrashHashAtomSvc{} + +func init() { + rpc.Register(_CrashHashAtomSvc) +} diff --git a/dbproxy/svc/l_apilog.go b/dbproxy/svc/l_apilog.go new file mode 100644 index 0000000..af8227b --- /dev/null +++ b/dbproxy/svc/l_apilog.go @@ -0,0 +1,51 @@ +package svc + +import ( + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func APILogsCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetGlobal(model.APILogDBName) + if s != nil { + c_apilogrec, first := s.DB().C(model.APILogCollName) + if first { + c_apilogrec.EnsureIndex(mgo.Index{Key: []string{"path"}, Background: true, Sparse: true}) + c_apilogrec.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c_apilogrec + } + return nil +} + +type APILogSvc struct { +} + +func (svc *APILogSvc) InsertAPILog(log *model.APILog, ret *bool) (err error) { + clog := APILogsCollection() + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertAPILog error:", err) + return + } + *ret = true + return +} + +func (svc *APILogSvc) RemoveAPILog(ts int64, chged **mgo.ChangeInfo) (err error) { + *chged, err = APILogsCollection().RemoveAll(bson.M{"time": bson.M{"$lte": time.Unix(ts, 0)}}) + return +} + +func init() { + rpc.Register(new(APILogSvc)) +} diff --git a/dbproxy/svc/l_bankbind.go b/dbproxy/svc/l_bankbind.go new file mode 100644 index 0000000..8854c44 --- /dev/null +++ b/dbproxy/svc/l_bankbind.go @@ -0,0 +1,52 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + BankBindLogDBErr = errors.New("log_bankbind db open failed.") +) + +func BankBindCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.BankBindDBName) + if s != nil { + c, first := s.DB().C(model.BankBindCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"bankname"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"bankcard"}, Background: true, Sparse: true}) + } + return c + } + + return nil +} + +func InsertBankBindLog(log *model.BankBindLog) error { + clog := BankBindCollection(log.Platform) + if clog == nil { + return BankBindLogDBErr + } + return clog.Insert(log) +} + +type BankBindLogSvc struct { +} + +func (svc *BankBindLogSvc) InsertBankBindLog(log *model.BankBindLog, ret *bool) error { + err := InsertBankBindLog(log) + if err == nil { + *ret = true + } + return err +} + +func init() { + rpc.Register(new(BankBindLogSvc)) +} diff --git a/dbproxy/svc/l_blackwhitelist.go b/dbproxy/svc/l_blackwhitelist.go new file mode 100644 index 0000000..8e8e1a3 --- /dev/null +++ b/dbproxy/svc/l_blackwhitelist.go @@ -0,0 +1,102 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + BlackWhiteCoinLogDBName = "log" + BlackWhiteCoinLogCollName = "log_blackwhitelist" + BlackWhiteCoinLogErr = errors.New("User blackwhite log open failed.") +) + +func BlackWhiteCoinLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, BlackWhiteCoinLogDBName) + if s != nil { + c_blackwhiterec, first := s.DB().C(BlackWhiteCoinLogCollName) + if first { + c_blackwhiterec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_blackwhiterec.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c_blackwhiterec + } + return nil +} + +type BlackWhiteCoinSvc struct { +} + +func (svc *BlackWhiteCoinSvc) InsertBlackWhiteCoinLogs(logs []*model.BlackWhiteCoinLog, ret *model.BlackWhiteCoinRet) (err error) { + if len(logs) == 0 { + return errors.New("len(logs) == 0") + } + clog := BlackWhiteCoinLogsCollection(logs[0].Platform) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("svc.InsertBlackWhiteCoinLog error:", err) + ret.Err = err + return + } + return +} +func (svc *BlackWhiteCoinSvc) InsertBlackWhiteCoinLog(log *model.BlackWhiteCoinLog, ret *model.BlackWhiteCoinRet) error { + if log == nil { + return errors.New("log == nil") + } + clog := BlackWhiteCoinLogsCollection(log.Platform) + if clog == nil { + return BlackWhiteCoinLogErr + } + err := clog.Insert(log) + if err != nil { + logger.Logger.Error("svc.InsertBlackWhiteCoinLog error", err) + ret.Err = err + return err + } + return nil +} + +func (svc *BlackWhiteCoinSvc) RemoveBlackWhiteCoinLog(args *model.BlackWhiteCoinArg, ret *model.BlackWhiteCoinRet) error { + clog := BlackWhiteCoinLogsCollection(args.Platform) + if clog == nil { + return BlackWhiteCoinLogErr + } + ret.Err = clog.RemoveId(args.Id) + return ret.Err +} + +func (svc *BlackWhiteCoinSvc) GetBlackWhiteCoinLogByBillNo(args *model.BlackWhiteCoinArg, ret *model.BlackWhiteCoinRet) error { + clog := BlackWhiteCoinLogsCollection(args.Platform) + if clog == nil { + return BlackWhiteCoinLogErr + } + err := clog.Find(bson.M{"snid": args.SnId, "billno": args.BillNo}).One(ret.Data) + if err != nil { + logger.Logger.Error("svc.GetBlackWhiteCoinLogByBillNo error", err) + return err + } + return nil +} +func init() { + rpc.Register(&BlackWhiteCoinSvc{}) +} diff --git a/dbproxy/svc/l_burstjackpotlog.go b/dbproxy/svc/l_burstjackpotlog.go new file mode 100644 index 0000000..858cbec --- /dev/null +++ b/dbproxy/svc/l_burstjackpotlog.go @@ -0,0 +1,56 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +func BurstJackpotCoinLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.BurstJackpotLogDBName) + if s != nil { + c_burstjackpot, first := s.DB().C(model.BurstJackpotLogCollName) + if first { + c_burstjackpot.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c_burstjackpot.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c_burstjackpot.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c_burstjackpot.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_burstjackpot + } + return nil +} + +type BurstJackpotSvc struct { +} + +func (svc *BurstJackpotSvc) GetBurstJackpotLog(arg *model.BurstJackpotArg, ret *[]model.BurstJackpotLog) (err error) { + clog := BurstJackpotCoinLogsCollection(arg.Platform) + if clog == nil { + return + } + err = clog.Find(bson.M{"gameid": arg.GameId}).Limit(80).Sort("-ts").All(ret) + if err != nil { + logger.Logger.Error("GetBurstJackpotLog err:", err) + } + return +} +func (svc *BurstJackpotSvc) InsertBurstJackpotLogs(log *model.BurstJackpotLog, ret *bool) (err error) { + clog := BurstJackpotCoinLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("svc.InsertBurstJackpotLogs error:", err) + return + } + *ret = true + return +} +func init() { + rpc.Register(&BurstJackpotSvc{}) +} diff --git a/dbproxy/svc/l_chat.go b/dbproxy/svc/l_chat.go new file mode 100644 index 0000000..90dbf5d --- /dev/null +++ b/dbproxy/svc/l_chat.go @@ -0,0 +1,78 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + ChatDBName = "log" + ChatCollName = "log_chat" + ChatColError = errors.New("chat collection open failed") +) + +func ChatCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, ChatDBName) + if s != nil { + c, first := s.DB().C(ChatCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"bindsnid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type ChatSvc struct { +} + +func (svc *ChatSvc) UpsertChat(args *model.ChatByKey, ret *model.ChatRet) error { + cc := ChatCollection(args.Platform) + if cc == nil { + return ChatColError + } + _, err := cc.Upsert(bson.M{"bindsnid": args.BindSnId}, args.C) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertChat is err: ", err) + return err + } + ret.C = args.C + return nil +} + +func (svc *ChatSvc) QueryChatByKey(args *model.ChatByKey, ret *model.ChatRet) error { + fc := ChatCollection(args.Platform) + if fc == nil { + return ChatColError + } + err := fc.Find(bson.M{"bindsnid": args.BindSnId}).One(&ret.C) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("QueryChatByKey is err: ", err) + return err + } + return nil +} + +func (svc *ChatSvc) DelChat(args *model.ChatByKey, ret *bool) error { + cc := ChatCollection(args.Platform) + if cc == nil { + return ChatColError + } + err := cc.Remove(bson.M{"bindsnid": args.BindSnId}) + if err != nil { + logger.Logger.Error("DelChat is err: ", err) + return err + } + return nil +} + +var _ChatSvc = &ChatSvc{} + +func init() { + rpc.Register(_ChatSvc) +} diff --git a/dbproxy/svc/l_clientlog.go b/dbproxy/svc/l_clientlog.go new file mode 100644 index 0000000..be908a5 --- /dev/null +++ b/dbproxy/svc/l_clientlog.go @@ -0,0 +1,21 @@ +package svc + +import ( + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +func ClientLogStartCollection(platform string) *mongo.Collection { + if platform == "" { + platform = mongo.G_P + } + s := mongo.MgoSessionMgrSington.GetPltMgoSession(platform, model.ClientLogDBName) + if s != nil { + c_coinlogrec, first := s.DB().C(model.ClientLogCollName) + if first { + //c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_coinlogrec + } + return nil +} diff --git a/dbproxy/svc/l_coingivelog.go b/dbproxy/svc/l_coingivelog.go new file mode 100644 index 0000000..44cf239 --- /dev/null +++ b/dbproxy/svc/l_coingivelog.go @@ -0,0 +1,141 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + CoinGiveLogErr = errors.New("log coingive log open failed.") +) + +func CoinGiveLogCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.CoinGiveLogDBName) + if s != nil { + c_coinGiveLogRec, first := s.DB().C(model.CoinGiveLogCollName) + if first { + c_coinGiveLogRec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_coinGiveLogRec.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c_coinGiveLogRec.EnsureIndex(mgo.Index{Key: []string{"state"}, Background: true, Sparse: true}) + c_coinGiveLogRec.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + c_coinGiveLogRec.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c_coinGiveLogRec + } + return nil +} + +type CoinGiveLogSvc struct { +} + +func (svc *CoinGiveLogSvc) UpdateGiveCoinLastFlow(args *model.UpdateGiveCoinLastFlowArg, ret *bool) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + err = clog.Update(bson.M{"_id": bson.ObjectIdHex(args.LogId)}, bson.M{"$set": bson.M{"flow": args.Flow}}) + if err != nil { + logger.Logger.Warn("svc.InsertSignleLoginLog error:", err) + return + } + *ret = true + return +} + +func (svc *CoinGiveLogSvc) GetGiveCoinLastFlow(args *model.GetGiveCoinLastFlowArg, log *model.CoinGiveLog) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + + err = clog.Find(bson.M{"_id": bson.ObjectIdHex(args.LogId)}).One(&log) + return +} + +func (svc *CoinGiveLogSvc) GetCoinGiveLogList(args *model.GetCoinGiveLogListArg, logs *[]*model.CoinGiveLog) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + + err = clog.Find(bson.M{"snid": args.SnId, "state": 0, "ts": bson.M{"$gt": args.TsStart, "$lte": args.TsEnd}, "ver": args.Ver}).All(&logs) + return +} + +func (svc *CoinGiveLogSvc) GetCoinGiveLogListByState(args *model.GetCoinGiveLogListByStateArg, logs *[]*model.CoinGiveLog) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + + err = clog.Find(bson.M{"snid": args.SnId, "state": args.State}).All(&logs) + return +} + +func (svc *CoinGiveLogSvc) ResetCoinGiveLogList(args *model.ResetCoinGiveLogListArg, ret *bool) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + _, err = clog.UpdateAll(bson.M{"snid": args.SnId, "state": args.Ts}, bson.M{"$set": bson.M{"state": 0}}) + if err != nil { + logger.Logger.Warn("svc.ResetCoinGiveLogList error:", err) + return + } + *ret = true + return +} + +func (svc *CoinGiveLogSvc) SetCoinGiveLogList(args *model.SetCoinGiveLogListArg, ret *bool) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + + _, err = clog.UpdateAll(bson.M{"snid": args.SnId, "state": 0, "ts": bson.M{"$gt": args.TsStart, "$lte": args.TsEnd}}, bson.M{"$set": bson.M{"state": args.State}}) + if err != nil { + logger.Logger.Warn("svc.SetCoinGiveLogList error:", err) + return + } + *ret = true + return +} + +func (svc *CoinGiveLogSvc) UpdateCoinLog(args *model.UpdateCoinLogArg, ret *bool) (err error) { + clog := CoinGiveLogCollection(args.Plt) + if clog == nil { + return CoinGiveLogErr + } + + err = clog.Update(bson.M{"_id": bson.ObjectIdHex(args.LogId)}, bson.M{"$set": bson.M{"state": args.State}}) + if err != nil { + logger.Logger.Warn("svc.UpdateCoinLog error:", err) + return + } + *ret = true + return +} + +func (svc *CoinGiveLogSvc) InsertGiveCoinLog(log *model.CoinGiveLog, ret *bool) (err error) { + clog := CoinGiveLogCollection(log.Platform) + if clog == nil { + return CoinGiveLogErr + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertCoinGiveSLog error:", err) + return + } + *ret = true + return +} + +func init() { + rpc.Register(new(CoinGiveLogSvc)) +} diff --git a/dbproxy/svc/l_coinlog.go b/dbproxy/svc/l_coinlog.go new file mode 100644 index 0000000..619fe04 --- /dev/null +++ b/dbproxy/svc/l_coinlog.go @@ -0,0 +1,176 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + CoinLogErr = errors.New("log coinex log open failed.") +) + +const CoinLogMaxLimitPerQuery = 100 + +func CoinLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.CoinLogDBName) + if s != nil { + c_coinlogrec, first := s.DB().C(model.CoinLogCollName) + if first { + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"logtype"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"-time"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"oper"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"seqno"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"cointype"}, Background: true, Sparse: true}) + c_coinlogrec.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_coinlogrec + } + return nil +} + +type CoinLogSvc struct { +} + +func GetCoinLogBySnidAndInGameAndGreaterTs(plt string, id int32, roomid int32, ts int64) (ret []model.CoinLog, err error) { + if roomid != 0 { + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "roomid": roomid, "ingame": bson.M{"$gt": 0}, "ts": bson.M{"$gt": ts}}).All(&ret) + } else { + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "ingame": bson.M{"$gt": 0}, "ts": bson.M{"$gt": ts}}).All(&ret) + } + return +} + +func (svc *CoinLogSvc) GetCoinLogBySnidAndLessTs(args *model.GetCoinLogBySnidAndLessTsArg, ret *[]model.CoinLog) (err error) { + err = CoinLogsCollection(args.Plt).Find(bson.M{"snid": args.SnId, "time": bson.M{"$lt": time.Unix(args.Ts, 0)}}).Sort("-ts").Limit(CoinLogMaxLimitPerQuery).All(ret) + return +} + +func (svc *CoinLogSvc) GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange(args *model.GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeArg, ret *model.GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeRet) (err error) { + limitDataNum := args.ToIdx - args.FromIdx + if limitDataNum < 0 { + limitDataNum = 0 + } + if limitDataNum > CoinLogMaxLimitPerQuery { + limitDataNum = CoinLogMaxLimitPerQuery + } + + plt := args.Plt + startts := args.StartTs + endts := args.EndTs + logType := args.LogType + id := args.SnId + fromIndex := args.FromIdx + if (startts == 0 || endts == 0) && logType == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id}).Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else if startts == 0 || endts == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "logtype": logType}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "logtype": logType}).Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else if logType == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "ts": bson.M{"$gte": startts, "$lte": endts}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "ts": bson.M{"$gte": startts, "$lte": endts}}).Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "logtype": logType, "ts": bson.M{"$gte": startts, "$lte": endts}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "logtype": logType, "ts": bson.M{"$gte": startts, "$lte": endts}}).Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } + return +} + +func (svc *CoinLogSvc) GetCoinLogGame(args *model.GetCoinLogGameReq, ret *model.GetCoinLogGameRet) (err error) { + limitDataNum := args.ToIdx - args.FromIdx + if limitDataNum < 0 { + limitDataNum = 0 + } + if limitDataNum > CoinLogMaxLimitPerQuery { + limitDataNum = CoinLogMaxLimitPerQuery + } + + plt := args.Plt + startts := args.StartTs + endts := args.EndTs + billType := args.BillType // 0金币 1钻石 + id := args.SnId + fromIndex := args.FromIdx + if (startts == 0 || endts == 0) && billType == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "gamefreeid": bson.M{"$gt": 0}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "gamefreeid": bson.M{"$gt": 0}}).Sort("-time").Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else if startts == 0 || endts == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "cointype": billType, "gamefreeid": bson.M{"$gt": 0}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "cointype": billType, "gamefreeid": bson.M{"$gt": 0}}).Sort("-time").Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else if billType == 0 { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "gamefreeid": bson.M{"$gt": 0}, "ts": bson.M{"$gte": startts, "$lte": endts}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "gamefreeid": bson.M{"$gt": 0}, "ts": bson.M{"$gte": startts, "$lte": endts}}).Sort("-time").Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } else { + ret.Count, _ = CoinLogsCollection(plt).Find(bson.M{"snid": id, "cointype": billType, "gamefreeid": bson.M{"$gt": 0}, "ts": bson.M{"$gte": startts, "$lte": endts}}).Count() + err = CoinLogsCollection(plt).Find(bson.M{"snid": id, "cointype": billType, "gamefreeid": bson.M{"$gt": 0}, "ts": bson.M{"$gte": startts, "$lte": endts}}).Sort("-time").Skip(fromIndex).Limit(limitDataNum).All(&ret.Logs) + } + return +} + +func (svc *CoinLogSvc) InsertCoinLog(log *model.CoinLog, ret *bool) (err error) { + clog := CoinLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertCoinLog error:", err) + return + } + + //err = InsertCoinWAL(log.Platform, model.NewCoinWAL(log.SnId, log.Count, log.LogType, log.InGame, log.CoinType, log.RoomId, log.Time.UnixNano())) + //if err != nil { + // logger.Logger.Warn("InsertCoinWAL error:", err) + // return + //} + *ret = true + return +} +func (svc *CoinLogSvc) RemoveCoinLog(args *model.RemoveCoinLogOneArg, chged **mgo.ChangeInfo) (err error) { + clog := CoinLogsCollection(args.Plt) + if clog == nil { + return CoinLogErr + } + *chged, err = clog.RemoveAll(bson.M{"time": bson.M{"$lte": time.Unix(args.Ts, 0)}}) + return +} +func (svc *CoinLogSvc) RemoveCoinLogOne(args *model.RemoveCoinLogOneArg, ret *bool) (err error) { + clog := CoinLogsCollection(args.Plt) + if clog == nil { + return CoinLogErr + } + err = clog.RemoveId(args.Id) + if err != nil { + return + } + + *ret = true + return +} + +func (svc *CoinLogSvc) UpdateCoinLogRemark(args *model.UpdateCoinLogRemarkArg, ret *bool) (err error) { + clog := CoinLogsCollection(args.Plt) + if clog == nil { + return CoinLogErr + } + err = clog.UpdateId(args.Id, bson.M{"$set": bson.M{"remark": args.Remark}}) + if err != nil { + return + } + + *ret = true + return +} + +func init() { + rpc.Register(new(CoinLogSvc)) +} diff --git a/dbproxy/svc/l_coinwal.go b/dbproxy/svc/l_coinwal.go new file mode 100644 index 0000000..3bca03d --- /dev/null +++ b/dbproxy/svc/l_coinwal.go @@ -0,0 +1,120 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + CoinWALErr = errors.New("log_coinwal open failed.") +) + +func CoinWALCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.CoinWALDBName) + if s != nil { + c, first := s.DB().C(model.CoinWALCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"ingame"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"sceneid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"logtype"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"cointype"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"-ts"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func GetCoinWALBySnidAndInGameAndGreaterTs(plt string, id, sceneid int32, ts int64) (ret []model.CoinWAL, err error) { + cond := bson.M{"snid": id, "ingame": bson.M{"$gt": 0}, "ts": bson.M{"$gt": ts}} + if sceneid > 0 { + cond["sceneid"] = sceneid + } + err = CoinWALCollection(plt).Find(cond).All(&ret) + return +} + +func GetCoinWALBySnidAndCoinTypeAndGreaterTs(plt string, id, coinType int32, ts int64) (ret []model.CoinWAL, err error) { + err = CoinWALCollection(plt).Find(bson.M{"snid": id, "ingame": 0, "cointype": coinType, "ts": bson.M{"$gt": ts}}).All(&ret) + return +} + +func InsertCoinWAL(plt string, logs ...*model.CoinWAL) (err error) { + clog := CoinWALCollection(plt) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertCoinWAL error:", err) + return + } + return +} + +func RemoveCoinWALInGame(plt string, id, sceneid int32, ts int64) (err error) { + clog := CoinWALCollection(plt) + if clog == nil { + return CoinLogErr + } + + cond := bson.M{"snid": id, "ingame": bson.M{"$gt": 0}, "ts": bson.M{"$lte": ts}} + if sceneid > 0 { + cond["sceneid"] = sceneid + } + _, err = clog.RemoveAll(cond) + if err != nil { + return + } + + return +} + +func RemoveCoinWALByCoinType(plt string, id, cointype int32, ts int64) (err error) { + clog := CoinWALCollection(plt) + if clog == nil { + return CoinLogErr + } + + _, err = clog.RemoveAll(bson.M{"snid": id, "ingame": 0, "cointype": cointype, "ts": bson.M{"$lte": ts}}) + if err != nil { + return + } + + return +} + +type CoinWALSvc struct { +} + +func (svc *CoinWALSvc) GetCoinWALBySnidAndInGameAndGreaterTs(args *model.CoinWALWithSnid_InGame_GreaterTsArgs, ret *[]model.CoinWAL) (err error) { + *ret, err = GetCoinWALBySnidAndInGameAndGreaterTs(args.Plt, args.SnId, args.RoomId, args.Ts) + return +} + +func (svc *CoinWALSvc) RemoveCoinWALBySnidAndInGameAndGreaterTs(args *model.CoinWALWithSnid_InGame_GreaterTsArgs, ret *bool) (err error) { + err = RemoveCoinWALInGame(args.Plt, args.SnId, args.RoomId, args.Ts) + *ret = err == nil + return +} + +func init() { + rpc.Register(new(CoinWALSvc)) +} diff --git a/dbproxy/svc/l_dbshoplog.go b/dbproxy/svc/l_dbshoplog.go new file mode 100644 index 0000000..19c6ad7 --- /dev/null +++ b/dbproxy/svc/l_dbshoplog.go @@ -0,0 +1,103 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +func DbShopLogCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.DbShopDBName) + if s != nil { + dbShopRec, first := s.DB().C(model.DbShopCollName) + if first { + dbShopRec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + dbShopRec.EnsureIndex(mgo.Index{Key: []string{"pageid"}, Background: true, Sparse: true}) + dbShopRec.EnsureIndex(mgo.Index{Key: []string{"state"}, Background: true, Sparse: true}) + dbShopRec.EnsureIndex(mgo.Index{Key: []string{"shopid"}, Background: true, Sparse: true}) + } + return dbShopRec + } + return nil +} + +type DbShopLogSvc struct { +} + +func (svc *DbShopLogSvc) InsertDbShopLog(args *model.DbShopLogArgs, ret *bool) (err error) { + clog := DbShopLogCollection(args.Log.Platform) + if clog == nil { + return + } + logger.Logger.Trace("DbShopLogSvc.InsertDbShopLog") + err = clog.Insert(args.Log) + if err != nil { + logger.Logger.Error("DbShopLogSvc.InsertDbShopLog error:", err) + return + } + *ret = true + return +} +func (svc *DbShopLogSvc) GetDbShopLog(args *model.DbShopLogArgs, dbShop *model.DbShop) (err error) { + clog := DbShopLogCollection(args.Log.Platform) + if clog == nil { + logger.Logger.Error("GetDbShopLog == nil") + return nil + } + logger.Logger.Trace("DbShopLogSvc.GetDbShopLog") + err = clog.Find(bson.M{"_id": args.Log.LogId}).One(dbShop) + if err != nil { + logger.Logger.Error("DbShopLogSvc.GetDbShopLog error:", err) + return nil + } + return +} +func (svc *DbShopLogSvc) UpdateDbShopState(args *model.DbShopLogArgs, ret *bool) (err error) { + clog := DbShopLogCollection(args.Log.Platform) + if clog == nil { + logger.Logger.Error("UpdateDbShopState == nil") + return nil + } + logger.Logger.Trace("DbShopLogSvc.UpdateDbShopState") + err = clog.UpdateId(args.Log.LogId, bson.M{"$set": bson.M{"state": args.Log.State}}) + if err != nil { + logger.Logger.Error("DbShopLogSvc.UpdateDbShopState error:", err) + return nil + } + *ret = true + return +} +func (svc *DbShopLogSvc) GetDbShopLogsByPage(args *model.DbShopLogArgs, dbShops *[]*model.DbShop) (err error) { + clog := DbShopLogCollection(args.Log.Platform) + if clog == nil { + logger.Logger.Error("GetDbShopLogsByPage == nil") + return + } + logger.Logger.Trace("DbShopLogSvc.GetDbShopLogsByPage") + err = clog.Find(bson.M{"pageid": args.Log.PageId, "snid": args.Log.SnId}).Limit(100).Sort("-ts").All(dbShops) + if err != nil { + logger.Logger.Error("DbShopLogSvc.GetDbShopLogsByPage error:", err) + return + } + return +} +func (svc *DbShopLogSvc) GetDbShopLogsByState(args *model.DbShopLogArgs, dbShops *[]*model.DbShop) (err error) { + clog := DbShopLogCollection(args.Log.Platform) + if clog == nil { + logger.Logger.Error("GetDbShopLogsByPage == nil") + return + } + logger.Logger.Trace("DbShopLogSvc.GetDbShopLogsByPage") + err = clog.Find(bson.M{"state": args.Log.State, "snid": args.Log.SnId}).All(dbShops) + if err != nil { + logger.Logger.Error("DbShopLogSvc.GetDbShopLogsByPage error:", err) + return + } + return +} +func init() { + rpc.Register(new(DbShopLogSvc)) +} diff --git a/dbproxy/svc/l_dbviplog.go b/dbproxy/svc/l_dbviplog.go new file mode 100644 index 0000000..278db9d --- /dev/null +++ b/dbproxy/svc/l_dbviplog.go @@ -0,0 +1,43 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +func DbVipLogCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.DbVipDBName) + if s != nil { + dbVipRec, first := s.DB().C(model.DbVipCollName) + if first { + dbVipRec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + dbVipRec.EnsureIndex(mgo.Index{Key: []string{"type"}, Background: true, Sparse: true}) + } + return dbVipRec + } + return nil +} + +type DbVipLogSvc struct { +} + +func (svc *DbVipLogSvc) InsertDbVipLog(args *model.DbVipLogArgs, ret *bool) (err error) { + clog := DbVipLogCollection(args.Log.Platform) + if clog == nil { + return + } + logger.Logger.Trace("DbVipLogSvc.InsertDbVipLog") + err = clog.Insert(args.Log) + if err != nil { + logger.Logger.Error("DbVipLogSvc.InsertDbVipLog error:", err) + return + } + *ret = true + return +} +func init() { + rpc.Register(new(DbVipLogSvc)) +} diff --git a/dbproxy/svc/l_fishjacklog.go b/dbproxy/svc/l_fishjacklog.go new file mode 100644 index 0000000..3788c1e --- /dev/null +++ b/dbproxy/svc/l_fishjacklog.go @@ -0,0 +1,55 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "net/rpc" +) + +var ( + FishJackpotLogErr = errors.New("log fishjackpot log open failed.") +) + +type FishJackLogSvc struct{} + +func init() { + rpc.Register(new(FishJackLogSvc)) +} + +func FishJackpotLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.FishJackpotLogDBName) + if s != nil { + c, first := s.DB().C(model.FishJackpotLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"roomid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func (svc *FishJackLogSvc) GetFishJackpotLogByPlatform(args *model.GetLogArgs, ret *model.GetLogRet) error { + c := FishJackpotLogsCollection(args.Platform) + if c == nil { + return FishJackpotLogErr + } + err := c.Find(bson.M{"platform": args.Platform}).Sort("-ts").Limit(model.FishJackpotLogMaxLimitPerQuery).All(&ret.Logs) + return err +} + +func (svc *FishJackLogSvc) InsertSignleFishJackpotLog(args *model.InsertLogArgs, ret *model.InsertLogRet) error { + c := FishJackpotLogsCollection(args.Log.Platform) + if c == nil { + return FishJackpotLogErr + } + err := c.Insert(args.Log) + return err +} diff --git a/dbproxy/svc/l_friendapply.go b/dbproxy/svc/l_friendapply.go new file mode 100644 index 0000000..0237f78 --- /dev/null +++ b/dbproxy/svc/l_friendapply.go @@ -0,0 +1,78 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + FriendApplyDBName = "log" + FriendApplyCollName = "log_friendapply" + FriendApplyColError = errors.New("friendapply collection open failed") +) + +func FriendApplyCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, FriendApplyDBName) + if s != nil { + c, first := s.DB().C(FriendApplyCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type FriendApplySvc struct { +} + +func (svc *FriendApplySvc) UpsertFriendApply(args *model.FriendApplyByKey, ret *model.FriendApplyRet) error { + cc := FriendApplyCollection(args.Platform) + if cc == nil { + return FriendApplyColError + } + _, err := cc.Upsert(bson.M{"snid": args.SnId}, args.FA) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertFriendApply is err: ", err) + return err + } + ret.FA = args.FA + return nil +} + +func (svc *FriendApplySvc) QueryFriendApplyByKey(args *model.FriendApplyByKey, ret *model.FriendApplyRet) error { + fc := FriendApplyCollection(args.Platform) + if fc == nil { + return FriendApplyColError + } + err := fc.Find(bson.M{"snid": args.SnId}).One(&ret.FA) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryFriendApplyByKey is err: ", err) + return err + } + return nil +} + +func (svc *FriendApplySvc) DelFriendApply(args *model.FriendApplyByKey, ret *bool) error { + cc := FriendApplyCollection(args.Platform) + if cc == nil { + return FriendApplyColError + } + err := cc.Remove(bson.M{"snid": args.SnId}) + if err != nil { + logger.Logger.Error("DelFriendApply is err: ", err) + return err + } + return nil +} + +var _FriendApplySvc = &FriendApplySvc{} + +func init() { + rpc.Register(_FriendApplySvc) +} diff --git a/dbproxy/svc/l_friendrecordlog.go b/dbproxy/svc/l_friendrecordlog.go new file mode 100644 index 0000000..663d388 --- /dev/null +++ b/dbproxy/svc/l_friendrecordlog.go @@ -0,0 +1,111 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + FriendRecordLogErr = errors.New("friend record log open failed.") +) + +func FriendRecordLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.FriendRecordLogDBName) + if s != nil { + c_friendrecordlog, first := s.DB().C(model.FriendRecordLogCollName) + if first { + c_friendrecordlog.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_friendrecordlog.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c_friendrecordlog.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c_friendrecordlog.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_friendrecordlog + } + return nil +} +func InsertFriendRecordLog(log model.FriendRecord) error { + frlog := FriendRecordLogsCollection(log.Platform) + if frlog == nil { + return FriendRecordLogErr + } + count, err := frlog.Find(bson.M{"snid": log.SnId, "platform": log.Platform, "gameid": log.GameId}).Count() + if err != nil { + logger.Logger.Error("InsertFriendRecordLog: Find err: ", err) + return err + } + if count >= 50 { + var oldLogs []model.FriendRecord + err = frlog.Find(bson.M{"snid": log.SnId, "platform": log.Platform, "gameid": log.GameId}).Sort("-ts").Skip(19).All(&oldLogs) + if err != nil { + logger.Logger.Error("InsertFriendRecordLog: Find err: ", err) + return err + } + for _, oldLog := range oldLogs { + err = frlog.RemoveId(oldLog.LogId) + if err != nil { + logger.Logger.Error("InsertFriendRecordLog: RemoveId err: ", err) + return err + } + } + } + err = frlog.Insert(log) + + return err +} + +func (svc *FriendRecordLogSvc) GetFriendRecordLogBySnid(args *model.FriendRecordSnidArg, ret *model.FriendRecordSnidRet) error { + frlog := FriendRecordLogsCollection(args.Platform) + if frlog == nil { + return FriendRecordLogErr + } + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + if args.GameId != 0 { + sql = append(sql, bson.M{"gameid": args.GameId}) + } + if args.Size == 0 { + return nil + } + var datas []*model.FriendRecord + err := frlog.Find(bson.M{"$and": sql}).Sort("-ts").Limit(20).All(&datas) + if err != nil { + logger.Logger.Error("FriendRecordLogSvc error: ", err) + return err + } + if datas == nil { + return nil + } + frs := []*model.FriendRecord{} + for _, data := range datas { + fr := &model.FriendRecord{ + SnId: data.SnId, + GameId: data.GameId, + BaseScore: data.BaseScore, + IsWin: data.IsWin, + Platform: data.Platform, + Ts: data.Ts, + BillCoin: data.BillCoin, + MatchType: data.MatchType, + } + frs = append(frs, fr) + } + ret.FR = frs + return nil +} + +type FriendRecordLogSvc struct { +} + +func init() { + rpc.Register(new(FriendRecordLogSvc)) +} diff --git a/dbproxy/svc/l_friendunread.go b/dbproxy/svc/l_friendunread.go new file mode 100644 index 0000000..1f1b9b4 --- /dev/null +++ b/dbproxy/svc/l_friendunread.go @@ -0,0 +1,126 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + FriendUnreadDBName = "log" + FriendUnreadCollName = "log_friendunread" + FriendUnreadColError = errors.New("friendunread collection open failed") +) + +func FriendUnreadCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, FriendUnreadDBName) + if s != nil { + c, first := s.DB().C(FriendUnreadCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type FriendUnreadSvc struct { +} + +func (svc *FriendUnreadSvc) UpsertFriendUnread(args *model.FriendUnreadByKey, ret *model.FriendUnreadRet) error { + cc := FriendUnreadCollection(args.Platform) + if cc == nil { + return FriendUnreadColError + } + err := cc.Find(bson.M{"snid": args.SnId}).One(&ret.FU) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertFriendUnread Find is err: ", err) + return err + } + if err == mgo.ErrNotFound { + fu := model.NewFriendUnread(args.SnId) + fu.UnreadSnids = append(fu.UnreadSnids, &model.FriendUnreadSnid{ + SnId: args.UnreadSnid, + UnreadNum: 1, + }) + err = cc.Insert(fu) + } else { + had := false + for _, unreadSnids := range ret.FU.UnreadSnids { + if unreadSnids.SnId == args.UnreadSnid { + unreadSnids.UnreadNum++ + had = true + } + } + if !had { + ret.FU.UnreadSnids = append(ret.FU.UnreadSnids, &model.FriendUnreadSnid{ + SnId: args.UnreadSnid, + UnreadNum: 1, + }) + } + err = cc.Update(bson.M{"snid": args.SnId}, bson.M{"$set": bson.M{"unreadsnids": ret.FU.UnreadSnids}}) + } + if err != nil { + logger.Logger.Error("UpsertFriendUnread is err: ", err) + return err + } + return nil +} + +func (svc *FriendUnreadSvc) UpdateFriendUnread(args *model.FriendUnreadByKey, ret *model.FriendUnreadRet) error { + cc := FriendUnreadCollection(args.Platform) + if cc == nil { + return FriendUnreadColError + } + err := cc.Find(bson.M{"snid": args.SnId}).One(&ret.FU) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpdateFriendUnread Find is err: ", err) + return err + } + if err == mgo.ErrNotFound { + err = cc.Insert(args.FU) + } else { + err = cc.Update(bson.M{"snid": args.SnId}, bson.M{"$set": bson.M{"unreadsnids": args.FU.UnreadSnids}}) + } + if err != nil { + logger.Logger.Error("UpdateFriendUnread is err: ", err) + return err + } + return nil +} + +func (svc *FriendUnreadSvc) QueryFriendUnreadByKey(args *model.FriendUnreadByKey, ret *model.FriendUnreadRet) error { + fc := FriendUnreadCollection(args.Platform) + if fc == nil { + return FriendUnreadColError + } + err := fc.Find(bson.M{"snid": args.SnId}).One(&ret.FU) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("QueryFriendUnreadByKey is err: ", err) + return err + } + return nil +} + +func (svc *FriendUnreadSvc) DelFriendUnread(args *model.FriendUnreadByKey, ret *bool) error { + cc := FriendUnreadCollection(args.Platform) + if cc == nil { + return FriendUnreadColError + } + err := cc.Remove(bson.M{"snid": args.SnId}) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("DelFriendUnread is err: ", err) + return err + } + return nil +} + +var _FriendUnreadSvc = &FriendUnreadSvc{} + +func init() { + rpc.Register(_FriendUnreadSvc) +} diff --git a/dbproxy/svc/l_gamedetailed.go b/dbproxy/svc/l_gamedetailed.go new file mode 100644 index 0000000..0b248ec --- /dev/null +++ b/dbproxy/svc/l_gamedetailed.go @@ -0,0 +1,161 @@ +package svc + +import ( + "errors" + "math" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func GameDetailedLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.GameDetailedLogDBName) + if s != nil { + c_gamedetailed, first := s.DB().C(model.GameDetailedLogCollName) + if first { + c_gamedetailed.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c_gamedetailed.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c_gamedetailed.EnsureIndex(mgo.Index{Key: []string{"logid"}, Background: true, Sparse: true}) + c_gamedetailed.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + c_gamedetailed.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_gamedetailed + } + return nil +} + +type GameDetailedSvc struct { +} + +func (svc *GameDetailedSvc) InsertGameDetailedLog(log *model.GameDetailedLog, ret *bool) (err error) { + clog := GameDetailedLogsCollection(log.Platform) + if clog == nil { + logger.Logger.Error("svc.InsertGameDetailedLogs clog == nil") + return + } + + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("svc.InsertGameDetailedLogs error:", err) + return + } + *ret = true + return +} + +func (svc *GameDetailedSvc) RemoveGameDetailedLog(args *model.RemoveGameDetailedLogArg, ret *mgo.ChangeInfo) (err error) { + clog := GameDetailedLogsCollection(args.Plt) + if clog == nil { + logger.Logger.Error("svc.RemoveGameDetailedLog clog == nil") + return errors.New("clog == nil") + } + ret, err = clog.RemoveAll(bson.M{"time": bson.M{"$lt": args.Ts}}) + if err != nil { + logger.Logger.Error("svc.RemoveGameDetailedLog is error", err) + return err + } + + return nil +} + +func (svc *GameDetailedSvc) GetAllGameDetailedLogsByTs(args *model.GameDetailedArg, ret *[]model.GameDetailedLog) error { + gdlc := GameDetailedLogsCollection(args.Plt) + if gdlc == nil { + return nil + } + var sql = bson.M{"time": bson.M{"$gte": time.Unix(args.StartTime, 0), "$lte": time.Unix(args.EndTime, 0)}} + err := gdlc.Find(sql).All(ret) + if err != nil { + logger.Logger.Error("svc.GetAllGameDetailedLogsByTs ") + return err + } + return nil +} + +func (svc *GameDetailedSvc) GetAllGameDetailedLogsByGameIdAndTs(args *model.GameDetailedGameIdAndArg, ret *[]model.GameDetailedLog) error { + gdlc := GameDetailedLogsCollection(args.Plt) + if gdlc == nil { + return nil + } + var sql = bson.M{"gameid": args.Gameid} + err := gdlc.Find(sql).Sort("-ts").Limit(args.LimitNum).All(ret) + if err != nil { + logger.Logger.Error("svc.GetAllGameDetailedLogsByGameIdAndTs ") + return err + } + return nil +} + +func (svc *GameDetailedSvc) GetPlayerHistory(args *model.GetPlayerHistoryArg, ret *model.GameDetailedLog) error { + gdlc := GameDetailedLogsCollection(args.Plt) + if gdlc == nil { + return nil + } + err := gdlc.Find(bson.M{"logid": args.LogId}).One(ret) + if err != nil { + logger.Logger.Error("svc.GetPlayerHistory is error", err) + } + return nil +} + +func (svc *GameDetailedSvc) GetPlayerHistoryAPI(args *model.GetPlayerHistoryAPIArg, ret *model.GameDetailedLogRet) error { + logger.Logger.Tracef("GameDetailedSvc.GetPlayerHistoryAPI=====> args:%v", args) + gdlc := GameDetailedLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + var sql []bson.M + //if args.SnId != 0 { + // sql = append(sql, bson.M{"snid": args.SnId}) + //} + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + + if args.StartTime != 0 { + sql = append(sql, bson.M{"ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) + } + + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("svc.GetPlayerHistoryAPI Count error: ", err) + return err + } + gdt := model.GameDetailedLogType{} + if total == 0 { + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + return nil + } + gdt.PageSum = int(math.Ceil(float64(total) / float64(args.PageSize))) + if args.PageNo > gdt.PageSum { + args.PageNo = gdt.PageSum + } + if args.PageNo <= 0 { + args.PageNo = 1 + } + limitNum := (args.PageNo - 1) * args.PageSize + + var data []*model.GameDetailedLog + + err = gdlc.Find(bson.M{"$and": sql}).Sort("-ts").Limit(args.PageSize).Skip(limitNum).All(&data) + if err != nil { + logger.Logger.Warn("svc.GetPlayerHistoryAPI error: ", err) + return err + } + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + gdt.Data = data + ret.Gplt = gdt + logger.Logger.Tracef("GameDetailedSvc.GetPlayerHistoryAPI=====> ret:%v", ret) + return nil +} + +func init() { + rpc.Register(new(GameDetailedSvc)) +} diff --git a/dbproxy/svc/l_gameplayerlistlog.go b/dbproxy/svc/l_gameplayerlistlog.go new file mode 100644 index 0000000..616a669 --- /dev/null +++ b/dbproxy/svc/l_gameplayerlistlog.go @@ -0,0 +1,560 @@ +package svc + +import ( + "errors" + "math" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/common" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func GamePlayerListLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.GamePlayerListLogDBName) + if s != nil { + c_gameplayerlistlog, first := s.DB().C(model.GamePlayerListLogCollName) + if first { + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"packagetag"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"clubid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"sceneid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"gamedetailedlogid"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + c_gameplayerlistlog.EnsureIndex(mgo.Index{Key: []string{"name"}, Background: true, Sparse: true}) + } + return c_gameplayerlistlog + } + return nil +} + +type GamePlayerListSvc struct { +} + +func (svc *GamePlayerListSvc) InsertGamePlayerListLog(log *model.GamePlayerListLog, ret *bool) (err error) { + clog := GamePlayerListLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("GamePlayerListSvc.InsertGamePlayerListLog error:", err) + return + } + *ret = true + return +} + +func (svc *GamePlayerListSvc) GetPlayerCount(args *model.GamePlayerListArg, ret *model.GamePlayerListRet) error { + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + logger.Logger.Error("svc.GetPlayerCount gdlc == nil") + return nil + } + var sql []bson.M + sql = append(sql, bson.M{"snid": args.SnId, "ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) + sql = append(sql, bson.M{"clubid": args.ClubId}) + sql = append(sql, bson.M{"platform": args.Platform}) + gameTotal, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Error("svc.GetPlayerCount is error", err) + return err + } + if gameTotal == 0 { + return errors.New("gameTotal==0") + } + prt := new(model.GameTotalRecord) + prt.GameTotal = int32(gameTotal) + var tc []model.TotalCoin + err = gdlc.Pipe([]bson.M{ + {"$match": bson.M{ + "snid": args.SnId, + "clubid": args.ClubId, + "ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}, + }}, + {"$group": bson.M{ + "_id": bson.M{ + "snid": "$snid", + "clubid": "$clubid", + }, + "totalin": bson.M{"$sum": "$totalin"}, + "totalout": bson.M{"$sum": "$totalout"}, + "taxcoin": bson.M{"$sum": "$taxcoin"}, + "clubpumpcoin": bson.M{"$sum": "$clubpumpcoin"}, + }}}).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("svc.GetPlayerCount AllowDiskUse is error", err) + return err + } + if len(tc) > 0 { + d := tc[0] + prt.GameTotalCoin = int32(d.TotalIn + d.TotalOut) + prt.GameWinTotal = int32(d.TotalOut - d.TotalIn - d.TaxCoin - d.ClubPumpCoin) + } + ret.Gtr = prt + return nil +} + +func (svc *GamePlayerListSvc) GetPlayerListLog(args *model.GamePlayerListArg, ret *model.GamePlayerListRet) error { + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + if args.ClubId != 0 { + sql = append(sql, bson.M{"clubid": args.ClubId}) + } + if args.StartTime != 0 { + sql = append(sql, bson.M{"ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) + } + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("select log_gamedetailed error: ", err) + return err + } + gdt := model.GamePlayerListType{} + if total == 0 { + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + return nil + } + gdt.PageSum = int(math.Ceil(float64(total) / float64(args.PageSize))) + if args.PageNo > gdt.PageSum { + args.PageNo = gdt.PageSum + } + if args.PageNo <= 0 { + args.PageNo = 1 + } + limitNum := (args.PageNo - 1) * args.PageSize + + var data []*model.NeedGameRecord + + err = gdlc.Find(bson.M{"$and": sql}).Sort("-ts").Limit(args.PageSize).Skip(limitNum).All(&data) + if err != nil { + logger.Logger.Warn("select log_gameplayerlistlog error: ", err) + return err + } + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + gdt.Data = data + ret.Gplt = gdt + return nil +} +func (svc *GamePlayerListSvc) GetPlayerListByHall(args *model.GamePlayerListArg, ret *model.GamePlayerListRet) error { + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + + sql = append(sql, bson.M{"roomtype": args.RoomType}) + + if args.StartTime != 0 { + sql = append(sql, bson.M{"ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) + } + if args.GameClass <= 6 { + sql = append(sql, bson.M{"gameclass": args.GameClass}) + } + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("select log_gameplayerlistlog error: ", err) + return err + } + gdt := model.GamePlayerListType{} + if total == 0 { + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + return nil + } + gdt.PageSum = int(math.Ceil(float64(total) / float64(args.PageSize))) + if args.PageNo > gdt.PageSum { + args.PageNo = gdt.PageSum + } + if args.PageNo <= 0 { + args.PageNo = 1 + } + limitNum := (args.PageNo - 1) * args.PageSize + + var data []*model.NeedGameRecord + + err = gdlc.Find(bson.M{"$and": sql}).Sort("-ts").Limit(args.PageSize).Skip(limitNum).All(&data) + if err != nil { + logger.Logger.Error("svc.GetPlayerListByHall error: ", err) + return err + } + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + gdt.Data = data + ret.Gplt = gdt + return nil +} + +func (svc *GamePlayerListSvc) GetPlayerListByHallEx(args *model.GamePlayerListArg, ret *model.GamePlayerListRet) error { + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallEx=====> args:%v", args) + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + + sql = append(sql, bson.M{"roomtype": args.RoomType}) + sql = append(sql, bson.M{"gameid": args.GameId}) + + if args.StartTime != 0 { + sql = append(sql, bson.M{"ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) + } + if args.GameClass <= 6 { + sql = append(sql, bson.M{"gameclass": args.GameClass}) + } + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("svc.GetPlayerListByHallEx Count error: ", err) + return err + } + gdt := model.GamePlayerListType{} + if total == 0 { + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + return nil + } + gdt.PageSum = int(math.Ceil(float64(total) / float64(args.PageSize))) + if args.PageNo > gdt.PageSum { + args.PageNo = gdt.PageSum + } + if args.PageNo <= 0 { + args.PageNo = 1 + } + limitNum := (args.PageNo - 1) * args.PageSize + + var data []*model.NeedGameRecord + + err = gdlc.Find(bson.M{"$and": sql}).Sort("-ts").Limit(args.PageSize).Skip(limitNum).All(&data) + if err != nil { + logger.Logger.Warn("svc.GetPlayerListByHallEx error: ", err) + return err + } + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + gdt.Data = data + ret.Gplt = gdt + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallEx=====> ret:%v", ret) + return nil +} + +func (svc *GamePlayerListSvc) GetPlayerListByHallExAPI(args *model.GamePlayerListAPIArg, ret *model.GamePlayerListRet) error { + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallExAPI=====> args:%v", args) + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + + if args.StartTime != 0 { + sql = append(sql, bson.M{"ts": bson.M{"$gte": args.StartTime, "$lte": args.EndTime}}) // >= StartTime <= EndTime + } + + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("svc.GetPlayerListByHallExAPI Count error: ", err) + return err + } + gdt := model.GamePlayerListType{} + if total == 0 { + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + return nil + } + gdt.PageSum = int(math.Ceil(float64(total) / float64(args.PageSize))) + if args.PageNo > gdt.PageSum { + args.PageNo = gdt.PageSum + } + if args.PageNo <= 0 { + args.PageNo = 1 + } + limitNum := (args.PageNo - 1) * args.PageSize + + var data []*model.NeedGameRecord + + err = gdlc.Find(bson.M{"$and": sql}).Sort("-ts").Limit(args.PageSize).Skip(limitNum).All(&data) + if err != nil { + logger.Logger.Warn("svc.GetPlayerListByHallEx error: ", err) + return err + } + for _, v := range data { + //v.Username = "aaa" + //continue + a, err := _AccountSvc.getAccountBySnId(args.Platform, v.SnId) + if err != nil { + logger.Logger.Warnf("model.getAccountBySnId(%v) failed:%v", args.SnId, err) + return err + } + v.Username = a.UserName + } + gdt.PageNo = args.PageNo + gdt.PageSize = args.PageSize + gdt.Data = data + ret.Gplt = gdt + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallEx=====> ret:%v", ret) + return nil +} + +func (svc *GamePlayerListSvc) GetPlayerExistListByTs(args *model.GamePlayerExistListArg, ret *[]int64) error { + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallExAPI=====> args:%v", args) + gdlc := GamePlayerListLogsCollection(args.Platform) + if gdlc == nil { + return nil + } + t := time.Unix(args.StartTime, 0) // 客户端给的是明天的零点 + for i := 0; i != args.DayNum; i++ { + var sql []bson.M + if args.SnId != 0 { + sql = append(sql, bson.M{"snid": args.SnId}) + } + if args.Platform != "" { + sql = append(sql, bson.M{"platform": args.Platform}) + } + //startTime := time.Date(t.Year(), t.Month(), t.Day()-i, 0, 0, 0, 0, t.Location()).Unix() // 今日凌晨 + startTime := t.AddDate(0, 0, -i-1).Unix() + //endTime := time.Date(t.Year(), t.Month(), t.Day()-i+1, 0, 0, 0, 0, t.Location()).Unix() // 明日凌晨 + endTime := t.AddDate(0, 0, -i).Unix() + + sql = append(sql, bson.M{"ts": bson.M{"$gte": startTime, "$lt": endTime}}) // >= StartTime < EndTime + + total, err := gdlc.Find(bson.M{"$and": sql}).Count() + if err != nil { + logger.Logger.Warn("svc.GetPlayerListByHallExAPI Count error: ", err) + return err + } + if total == 0 { + continue + } + // 当日不为空 + *ret = append(*ret, startTime) + } + + logger.Logger.Tracef("GamePlayerListSvc.GetPlayerListByHallEx=====> ret:%v", ret) + return nil +} + +func (svc *GamePlayerListSvc) GetRecentGame(args *model.RecentGameReq, ret *model.RecentGameRet) error { + c := GamePlayerListLogsCollection(args.Platform) + if c == nil { + logger.Logger.Error("svc.GetRecentGame c == nil") + return nil + } + + endTime := time.Now() + startTime := endTime.AddDate(0, 0, -3) + + var tc []model.RecentGameIDs + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "snid": args.SnID, + "ts": bson.M{"$gte": startTime.Unix(), "$lte": endTime.Unix()}, + }}, + {"$group": bson.M{ + "_id": bson.M{ + "gameid": "$gameid", + }, + "gameid": bson.M{"$first": "$gameid"}, + "ts": bson.M{"$first": "$ts"}, + }}, + { + "$sort": bson.M{"ts": -1}, + }, + { + "$limit": 3, + }, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("svc.GetRecentGame AllowDiskUse is error", err) + return err + } + + if len(tc) > 0 { + for _, v := range tc { + ret.GameID = append(ret.GameID, v.GameID) + } + return nil + } + // 最近3天无游戏记录 + err = c.Pipe([]bson.M{ + {"$match": bson.M{"snid": args.SnID}}, + {"$group": bson.M{ + "_id": bson.M{ + "gameid": "$gameid", + }, + "gameid": bson.M{"$first": "$gameid"}, + "ts": bson.M{"$first": "$ts"}, + }}, + { + "$sort": bson.M{"ts": -1}, + }, + { + "$limit": 3, + }, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("svc.GetRecentGame AllowDiskUse is error", err) + return err + } + if len(tc) > 0 { + for _, v := range tc { + ret.GameID = append(ret.GameID, v.GameID) + } + } + return nil +} + +func (svc *GamePlayerListSvc) GetWinCoinListTienlen(args *model.FindWinCoinListArgs, ret *model.FindWinCoinListReply) error { + c := GamePlayerListLogsCollection(args.Platform) + if c == nil { + logger.Logger.Error("svc.GetWinCoinListTienlen c == nil") + return nil + } + + type M struct { + SnId int32 + Coin int64 + } + + var tc []*M + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "gameid": bson.M{"$in": common.GetTienlenGameID()}, + "ts": bson.M{"$gte": args.StartTs, "$lte": args.EndTs}, + }}, + {"$group": bson.M{ + "_id": bson.M{ + "snid": "$snid", + }, + "snid": bson.M{"$first": "$snid"}, + "coin": bson.M{"$sum": "$totalout"}, + }}, + { + "$sort": bson.M{"coin": -1}, + }, + { + "$limit": model.GameParamData.RankWinCoinMaxNum, + }, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("svc.GetWinCoinListTienlen AllowDiskUse is error", err) + return err + } + + u := PlayerDataCollection(args.Platform) + if u == nil { + return PlayerColError + } + + for _, v := range tc { + d := &model.PlayerBaseInfo2{} + err = u.Find(bson.M{"snid": v.SnId}).Select(bson.M{"name": 1, "roles": 1}).One(d) + if err != nil { + logger.Logger.Warnf("GetWinCoinListTienlen Find player is error:%v", err) + } + roleId := common.DefaultRoleId + if d.Roles != nil && d.Roles.ModId != 0 { + roleId = int(d.Roles.ModId) + } + ret.List = append(ret.List, &model.WinCoinInfo{ + SnId: v.SnId, + Name: d.Name, + Coin: v.Coin, + ModId: int32(roleId), + }) + } + + return nil +} + +func (svc *GamePlayerListSvc) GetWinCoinTienlen(args *model.FindWinCoinArgs, ret *model.FindWinCoinReply) error { + c := GamePlayerListLogsCollection(args.Platform) + if c == nil { + logger.Logger.Error("svc.GetWinCoinTienlen c == nil") + return nil + } + + type M struct { + Coin int64 + } + + var tc []*M + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "snid": args.SnId, + "gameid": bson.M{"$in": common.GetTienlenGameID()}, + "ts": bson.M{"$gte": args.StartTs, "$lte": args.EndTs}, + }}, + {"$group": bson.M{ + "_id": nil, + "coin": bson.M{"$sum": "$totalout"}, + }}, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("svc.GetWinCoinTienlen AllowDiskUse is error", err) + return err + } + + u := PlayerDataCollection(args.Platform) + if u == nil { + return PlayerColError + } + + d := &model.PlayerBaseInfo2{} + err = u.Find(bson.M{"snid": args.SnId}).Select(bson.M{"name": 1, "roles": 1}).One(d) + if err != nil { + logger.Logger.Warnf("GetWinCoinTienlen Find player is error:%v", err) + } + roleId := common.DefaultRoleId + if d.Roles != nil && d.Roles.ModId != 0 { + roleId = int(d.Roles.ModId) + } + ret.List = &model.WinCoinInfo{ + SnId: args.SnId, + Name: d.Name, + ModId: int32(roleId), + } + if len(tc) > 0 { + ret.List.Coin = tc[0].Coin + } + return nil +} + +func init() { + rpc.Register(new(GamePlayerListSvc)) +} diff --git a/dbproxy/svc/l_gradelog.go b/dbproxy/svc/l_gradelog.go new file mode 100644 index 0000000..3662bae --- /dev/null +++ b/dbproxy/svc/l_gradelog.go @@ -0,0 +1,315 @@ +package svc + +import ( + "errors" + "math" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + GradeLogErr = errors.New("log_grade open failed.") + BillGradeLogErr = errors.New("User billgradelog open failed.") +) + +const GradeLogMaxLimitPerQuery = 100 + +func GradeLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.GradeLogDBName) + if s != nil { + c, first := s.DB().C(model.GradeLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"inviterid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertGradeLog(log *model.GradeLog) error { + err := GradeLogsCollection(log.Platform).Insert(log) + if err != nil { + logger.Logger.Info("InsertGradeLog error:", err) + return err + } + return nil +} + +func InsertGradeLogs(logs ...*model.GradeLog) (err error) { + if len(logs) == 0 { + return nil + } + clog := GradeLogsCollection(logs[0].Platform) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertGradeLogs error:", err) + return + } + return +} + +func UpdateGradeLogStatus(plt string, id bson.ObjectId, status int32) error { + glc := GradeLogsCollection(plt) + if glc == nil { + return GradeLogErr + } + return glc.UpdateId(id, bson.M{"$set": bson.M{"status": status}}) +} + +func GetGradeLogByPageAndSnId(plt string, snid, pageNo, pageSize int32) (ret *model.GradeLogLog) { + clog := GradeLogsCollection(plt) + if clog == nil { + return nil + } + selecter := bson.M{"snid": snid} + //selecter := bson.M{"snid": snid, "logtype": common.GainWay_GradeMatchGet} + total, err := clog.Find(selecter).Count() + if err != nil || total == 0 { + return nil + } + ret = new(model.GradeLogLog) + ret.PageNum = int32(math.Ceil(float64(total) / float64(pageSize))) + if pageNo > ret.PageNum { + pageNo = ret.PageNum + } + if pageNo <= 0 { + pageNo = 1 + } + limitNum := (pageNo - 1) * pageSize + err = clog.Find(selecter).Skip(int(limitNum)).Limit(int(pageSize)).Sort("-time").All(&ret.Logs) + if err != nil { + logger.Logger.Error("Find gradelog data eror.", err) + return nil + } + ret.PageNo = pageNo + ret.PageSize = pageSize + return +} + +func RemoveGradeLog(plt string, id bson.ObjectId) error { + glc := GradeLogsCollection(plt) + if glc == nil { + return GradeLogErr + } + return glc.RemoveId(id) +} + +func GetGradeLogBySnidAndLessTs(plt string, id int32, ts time.Time) (ret []model.GradeLog, err error) { + err = GradeLogsCollection(plt).Find(bson.M{"snid": id, "time": bson.M{"$lt": ts}}).Sort("-time", "-count").Limit(GradeLogMaxLimitPerQuery).All(&ret) + return +} + +func BillGradeLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.BillGradeLogDBName) + if s != nil { + c, first := s.DB().C(model.BillGradeLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"logid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"timestamp"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertBillGradeLogs(logs ...*model.BillGradeLog) (err error) { + cpay := BillGradeLogsCollection(logs[0].Platform) + if cpay == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = cpay.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = cpay.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertBillGradeLogs error:", err) + return + } + return +} + +func InsertBillGradeLog(log *model.BillGradeLog) error { + cpay := BillGradeLogsCollection(log.Platform) + if cpay == nil { + return BillGradeLogErr + } + return cpay.Insert(log) +} + +func RemoveBillGradeLog(plt, logid string) error { + bglc := BillGradeLogsCollection(plt) + if bglc == nil { + return BillGradeLogErr + } + return bglc.Remove(bson.M{"logid": logid}) +} + +func UpdateBillGradeLogStatus(plt, logId string, status int32) error { + bglc := BillGradeLogsCollection(plt) + if bglc == nil { + return BillGradeLogErr + } + var conds []bson.M + conds = append(conds, bson.M{"status": bson.M{"$ne": 3}}) + conds = append(conds, bson.M{"status": bson.M{"$ne": 9}}) + sql := bson.M{"logid": logId, + "$and": conds, + } + return bglc.Update(sql, bson.M{"$set": bson.M{"status": status}}) +} + +func GetAllBillGradeLog(plt string, snid int32, ts int64) (ret []model.BillGradeLog, err error) { + err = BillGradeLogsCollection(plt).Find(bson.M{"snid": snid, "timestamp": bson.M{"$gt": ts}}).All(&ret) + return +} + +func GetBillGradeLogByLogId(plt, logId string) (ret *model.BillGradeLog, err error) { + var conds []bson.M + conds = append(conds, bson.M{"status": bson.M{"$ne": 3}}) + conds = append(conds, bson.M{"status": bson.M{"$ne": 9}}) + sql := bson.M{"logid": logId, + "$and": conds, + } + err = BillGradeLogsCollection(plt).Find(sql).One(&ret) + return +} + +func GetBillGradeLogByBillNo(plt string, snid int32, billNo int64) (*model.BillGradeLog, error) { + var log model.BillGradeLog + err := BillGradeLogsCollection(plt).Find(bson.M{"snid": snid, "billno": billNo}).One(&log) + return &log, err +} + +type GradeLogSvc struct { +} + +func (svc *GradeLogSvc) InsertGradeLog(args *model.GradeLog, ret *bool) (err error) { + err = InsertGradeLog(args) + if err == nil { + *ret = true + } + return +} + +func (svc *GradeLogSvc) InsertGradeLogs(args []*model.GradeLog, ret *bool) (err error) { + err = InsertGradeLogs(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *GradeLogSvc) UpdateGradeLogStatus(args *model.UpdateGradeLogStatusArgs, ret *bool) (err error) { + err = UpdateGradeLogStatus(args.Plt, args.Id, args.Status) + if err == nil { + *ret = true + } + return +} + +func (svc *GradeLogSvc) GetGradeLogByPageAndSnId(args *model.GetGradeLogByPageAndSnIdArgs, ret **model.GradeLogLog) (err error) { + *ret = GetGradeLogByPageAndSnId(args.Plt, args.SnId, args.PageNo, args.PageSize) + return +} + +func (svc *GradeLogSvc) GetGradeLogBySnidAndLessTs(args *model.GetGradeLogBySnidAndLessTsArgs, ret *[]model.GradeLog) (err error) { + *ret, err = GetGradeLogBySnidAndLessTs(args.Plt, args.SnId, args.Ts) + return +} + +func (svc *GradeLogSvc) RemoveGradeLog(args *model.RemoveGradeLogArgs, ret *bool) (err error) { + err = RemoveGradeLog(args.Plt, args.Id) + if err == nil { + *ret = true + } + return +} + +type BillGradeLogSvc struct { +} + +func (svc *BillGradeLogSvc) InsertBillGradeLogs(args []*model.BillGradeLog, ret *bool) (err error) { + err = InsertBillGradeLogs(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *BillGradeLogSvc) InsertBillGradeLog(args *model.BillGradeLog, ret *bool) (err error) { + err = InsertBillGradeLog(args) + if err == nil { + *ret = true + } + return +} + +func (svc *BillGradeLogSvc) RemoveBillGradeLog(args *model.RemoveBillGradeLogArgs, ret *bool) (err error) { + err = RemoveBillGradeLog(args.Plt, args.LogId) + if err == nil { + *ret = true + } + return +} + +func (svc *BillGradeLogSvc) UpdateBillGradeLogStatus(args *model.UpdateBillGradeLogStatusArgs, ret *bool) (err error) { + err = UpdateBillGradeLogStatus(args.Plt, args.LogId, args.Status) + if err == nil { + *ret = true + } + return +} + +func (svc *BillGradeLogSvc) GetAllBillGradeLog(args *model.GetAllBillGradeLogArgs, ret *[]model.BillGradeLog) (err error) { + *ret, err = GetAllBillGradeLog(args.Plt, args.SnId, args.Ts) + return +} + +func (svc *BillGradeLogSvc) GetBillGradeLogByLogId(args *model.GetBillGradeLogByLogIdArgs, ret **model.BillGradeLog) (err error) { + *ret, err = GetBillGradeLogByLogId(args.Plt, args.LogId) + return +} + +func (svc *BillGradeLogSvc) GetBillGradeLogByBillNo(args *model.GetBillGradeLogByBillNoArgs, ret **model.BillGradeLog) (err error) { + *ret, err = GetBillGradeLogByBillNo(args.Plt, args.SnId, args.BillNo) + return +} + +func init() { + rpc.Register(new(GradeLogSvc)) + rpc.Register(new(BillGradeLogSvc)) +} diff --git a/dbproxy/svc/l_hundredjacklog.go b/dbproxy/svc/l_hundredjacklog.go new file mode 100644 index 0000000..eee4fd1 --- /dev/null +++ b/dbproxy/svc/l_hundredjacklog.go @@ -0,0 +1,189 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + HundredjackpotLogErr = errors.New("log hundredjack log open failed.") +) + +const HundredjackpotLogMaxLimitPerQuery = 99 + +// HundredjackpotLogsCollection mgo连接 +func HundredjackpotLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.HundredjackpotLogDBName) + if s != nil { + c, first := s.DB().C(model.HundredjackpotLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"roomid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"ingame"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +// GetHundredjackpotLogTsByPlatformAndGameID 时间排名 +func GetHundredjackpotLogTsByPlatformAndGameID(platform string, gameid int32) (ret []model.HundredjackpotLog, err error) { + err = HundredjackpotLogsCollection(platform).Find(bson.M{"ingame": gameid}).Sort("-ts").Limit(HundredjackpotLogMaxLimitPerQuery).All(&ret) + return +} + +// GetHundredjackpotLogCoinByPlatformAndGameID 中奖金币排名 +func GetHundredjackpotLogCoinByPlatformAndGameID(platform string, gameid int32) (ret []model.HundredjackpotLog, err error) { + err = HundredjackpotLogsCollection(platform).Find(bson.M{"ingame": gameid}).Sort("-coin", "ts").Limit(HundredjackpotLogMaxLimitPerQuery).All(&ret) + return +} + +// GetHundredjackpotLogTsByPlatformAndGameFreeID 时间排名 +func GetHundredjackpotLogTsByPlatformAndGameFreeID(platform string, gamefreeid int32) (ret []model.HundredjackpotLog, err error) { + err = HundredjackpotLogsCollection(platform).Find(bson.M{"roomid": gamefreeid}).Sort("-ts").Limit(HundredjackpotLogMaxLimitPerQuery).All(&ret) + return +} + +// GetHundredjackpotLogCoinByPlatformAndGameFreeID 中奖金币排名 +func GetHundredjackpotLogCoinByPlatformAndGameFreeID(platform string, gamefreeid int32) (ret []model.HundredjackpotLog, err error) { + err = HundredjackpotLogsCollection(platform).Find(bson.M{"roomid": gamefreeid}).Sort("-coin", "ts").Limit(HundredjackpotLogMaxLimitPerQuery).All(&ret) + return +} + +// GetLastHundredjackpotLogBySnidAndGameID . +func GetLastHundredjackpotLogBySnidAndGameID(plt string, id int32, gamefreeid int32) (log *model.HundredjackpotLog, err error) { + var data model.HundredjackpotLog + err = HundredjackpotLogsCollection(plt).Find(bson.M{"snid": id, "roomid": gamefreeid}).Sort("-ts").Limit(1).One(&data) + if err == nil { + log = &data + } + return +} + +// GetHundredjackpotLogByID . +func GetHundredjackpotLogByID(plt string, id int32) ([]string, error) { + var data model.HundredjackpotLog + err := HundredjackpotLogsCollection(plt).Find(bson.M{"_id": id}).One(&data) + if err == nil { + return data.GameData, err + } + return nil, err +} + +// InsertHundredjackpotLog . +func InsertHundredjackpotLog(log *model.HundredjackpotLog) (err error) { + clog := HundredjackpotLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertHundredjackpotLog error:", err) + return + } + return +} + +// UpdateLikeNum 点赞次数 +func UpdateLikeNum(plt string, id bson.ObjectId, likeNum int32, linkeSnids string) error { + hlog := HundredjackpotLogsCollection(plt) + if hlog == nil { + return HundredjackpotLogErr + } + + return hlog.Update(bson.M{"_id": id}, bson.D{{"$set", bson.D{{"likenum", likeNum}, {"linkesnids", linkeSnids}}}}) +} + +// UpdatePlayBlackNum 回放次数 +func UpdatePlayBlackNum(plt string, id bson.ObjectId, playblackNum int32) ([]string, error) { + hlog := HundredjackpotLogsCollection(plt) + if hlog == nil { + return nil, HundredjackpotLogErr + } + err := hlog.Update(bson.M{"_id": id}, bson.D{{"$set", bson.D{{"playblacknum", playblackNum}}}}) + var data model.HundredjackpotLog + err = hlog.Find(bson.M{"_id": id}).One(&data) + if err == nil { + return data.GameData, err + } + return nil, err +} + +// RemoveHundredjackpotLogOne 移除 +func RemoveHundredjackpotLogOne(plt string, id bson.ObjectId) error { + cpay := HundredjackpotLogsCollection(plt) + if cpay == nil { + return HundredjackpotLogErr + } + return cpay.RemoveId(id) +} + +type HundredjackpotLogSvc struct { +} + +func (svc *HundredjackpotLogSvc) GetHundredjackpotLogTsByPlatformAndGameID(args *model.GetHundredjackpotLogArgs, ret *[]model.HundredjackpotLog) (err error) { + *ret, err = GetHundredjackpotLogTsByPlatformAndGameID(args.Plt, args.Id1) + return err +} + +func (svc *RebateLogSvc) GetHundredjackpotLogCoinByPlatformAndGameID(args *model.GetHundredjackpotLogArgs, ret *[]model.HundredjackpotLog) (err error) { + *ret, err = GetHundredjackpotLogCoinByPlatformAndGameID(args.Plt, args.Id1) + return err +} + +func (svc *RebateLogSvc) GetHundredjackpotLogTsByPlatformAndGameFreeID(args *model.GetHundredjackpotLogArgs, ret *[]model.HundredjackpotLog) (err error) { + *ret, err = GetHundredjackpotLogTsByPlatformAndGameFreeID(args.Plt, args.Id1) + return err +} + +func (svc *RebateLogSvc) GetHundredjackpotLogCoinByPlatformAndGameFreeID(args *model.GetHundredjackpotLogArgs, ret *[]model.HundredjackpotLog) (err error) { + *ret, err = GetHundredjackpotLogCoinByPlatformAndGameFreeID(args.Plt, args.Id1) + return err +} + +func (svc *RebateLogSvc) GetLastHundredjackpotLogBySnidAndGameID(args *model.GetHundredjackpotLogArgs, ret **model.HundredjackpotLog) (err error) { + *ret, err = GetLastHundredjackpotLogBySnidAndGameID(args.Plt, args.Id1, args.Id2) + return err +} + +func (svc *RebateLogSvc) InsertHundredjackpotLog(args *model.HundredjackpotLog, ret *bool) (err error) { + err = InsertHundredjackpotLog(args) + if err == nil { + *ret = true + } + return err +} + +func (svc *RebateLogSvc) UpdateLikeNum(args *model.UpdateLikeNumArgs, ret *bool) (err error) { + err = UpdateLikeNum(args.Plt, args.Id, args.LikeNum, args.LikeSnIds) + if err == nil { + *ret = true + } + return err +} + +func (svc *RebateLogSvc) UpdatePlayBlackNum(args *model.UpdatePlayBlackNumArgs, ret *[]string) (err error) { + *ret, err = UpdatePlayBlackNum(args.Plt, args.Id, args.PlayblackNum) + return err +} + +func (svc *RebateLogSvc) RemoveHundredjackpotLogOne(args *model.RemoveHundredjackpotLogOneArgs, ret *bool) (err error) { + err = RemoveHundredjackpotLogOne(args.Plt, args.Id) + if err == nil { + *ret = true + } + return err +} + +func init() { + rpc.Register(new(HundredjackpotLogSvc)) +} diff --git a/dbproxy/svc/l_itemlog.go b/dbproxy/svc/l_itemlog.go new file mode 100644 index 0000000..23f8225 --- /dev/null +++ b/dbproxy/svc/l_itemlog.go @@ -0,0 +1,43 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +func ItemLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.ItemLogDBName) + if s != nil { + c_itemlog, first := s.DB().C(model.ItemLogCollName) + if first { + c_itemlog.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_itemlog.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c_itemlog + } + return nil +} + +type ItemLogSvc struct { +} + +func (svc *ItemLogSvc) InsertItemLog(log *model.ItemLog, ret *bool) (err error) { + clog := ItemLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertItemLog error:", err) + return + } + *ret = true + return +} + +func init() { + rpc.Register(new(ItemLogSvc)) +} diff --git a/dbproxy/svc/l_jackpotlog.go b/dbproxy/svc/l_jackpotlog.go new file mode 100644 index 0000000..e56e3b3 --- /dev/null +++ b/dbproxy/svc/l_jackpotlog.go @@ -0,0 +1,64 @@ +package svc + +// +//func JackPotLogsCollection(plt string) *mongo.Collection { +// s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.JackPotLogDBName) +// if s != nil { +// c_jackpotlog, first := s.DB().C(model.JackPotLogCollName) +// if first { +// c_jackpotlog.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) +// c_jackpotlog.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) +// c_jackpotlog.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) +// c_jackpotlog.EnsureIndex(mgo.Index{Key: []string{"_id"}, Background: true, Sparse: true}) +// } +// return c_jackpotlog +// } +// return nil +//} +// +//type JackPotLogSvc struct { +//} +// +//func (svc *JackPotLogSvc) InsertJackPotLog(log *model.JackPotLog, ret *bool) (err error) { +// clog := JackPotLogsCollection(log.Platform) +// if clog == nil { +// return +// } +// _, err = clog.Upsert(bson.M{"_id": log.LogId}, log) +// if err != nil { +// logger.Logger.Warn("InsertJackPotLog error:", err) +// return +// } +// *ret = true +// return +//} +//func (svc *JackPotLogSvc) DelJackPotLog(args *model.DelJKData, ret *bool) (err error) { +// clog := JackPotLogsCollection(args.Platform) +// if clog == nil { +// return +// } +// ts := args.Ts.Unix() - int64(args.Ts.Second()) - int64(60*args.Ts.Minute()) - int64(60*60*args.Ts.Hour()) +// ts -= 3 * 24 * 60 * 60 +// _, err = clog.RemoveAll(bson.M{"ts": bson.M{"$lte": ts}}) +// if err != nil { +// logger.Logger.Warn("DelJackPotLog error:", err) +// return +// } +// *ret = true +// return +//} +//func (svc *JackPotLogSvc) GetJackPotLog(args *model.DelJKData, ret *[]model.JackPotLog) (err error) { +// clog := JackPotLogsCollection(args.Platform) +// if clog == nil { +// return +// } +// err = clog.Find(bson.M{"ts": bson.M{"$gte": args.Ts.Unix()}}).All(ret) +// if err != nil { +// logger.Logger.Warn("GetJackPotLog error:", err) +// return +// } +// return +//} +//func init() { +// rpc.Register(new(JackPotLogSvc)) +//} diff --git a/dbproxy/svc/l_jyb.go b/dbproxy/svc/l_jyb.go new file mode 100644 index 0000000..4e1d001 --- /dev/null +++ b/dbproxy/svc/l_jyb.go @@ -0,0 +1,196 @@ +package svc + +import ( + "errors" + "fmt" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + JybDBName = "log" + JybCollName = "log_jyb" + ErrJybDBNotOpen = model.NewDBError(JybDBName, JybCollName, model.NOT_OPEN) +) + +func JybCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, JybDBName) + if s != nil { + c, first := s.DB().C(JybCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"plakeyid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"codestart"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type JybSvc struct { +} + +// 初始化 +func (svc *JybSvc) InitJybItem(args *model.InitJybInfoArgs, ret *model.JybInfos) error { + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + err := cjyb.Find(bson.M{"platform": args.Plt}).All(&ret.Jybs) + if err != nil { + logger.Logger.Error("InitJybItem error: ", err) + return err + } + return nil +} + +// 创建礼包 +func (svc *JybSvc) CreateJybItem(args *model.CreateJyb, ret *model.JybInfo) error { + cjyb := JybCollection(args.Platform) + if cjyb == nil { + return ErrJybDBNotOpen + } + + jk := &model.JybKey{} + if jk = getJyb(cjyb, args.Platform); jk == nil { + logger.Logger.Warn("CreateJybItem is nil ") + return errors.New("jybkey is nil") + } + + if args.CodeType != 1 { // 通用红包不用生成code + if err := cjyb.Update(bson.M{"_id": jk.KeyId}, bson.D{{"$set", bson.D{{"keyint", jk.Keyint + model.Keystart}}}}); err != nil { + logger.Logger.Error("CreateJybItem Update error CodeType!=1", err) + return err + } + args.CodeStart = jk.Keyint + if args.Codelen == 0 { + args.Codelen = 12 + } + model.NewJybCode(args.JybInfo, args.Codelen, args.Num) + } else { + code := "" + for id := range args.Code { + code = id + } + if jk.GeCode == nil { + jk.GeCode = make(map[string]string) + } else if _, exist := jk.GeCode[code]; exist { + logger.Logger.Warn("CreateJybItem error code is exist") + return model.ErrJYBCode + } + jk.GeCode[code] = args.JybId.Hex() + if err := cjyb.Update(bson.M{"_id": jk.KeyId}, bson.D{{"$set", bson.D{{"gecode", jk.GeCode}}}}); err != nil { + logger.Logger.Error("CreateJybItem Update error CodeType==1", err) + return err + } + } + err := cjyb.Insert(args.JybInfo) + ret = args.JybInfo + if err != nil { + logger.Logger.Error("CreateJybItem Insert is error: ", err) + return err + } + return nil +} + +func (svc *JybSvc) GetJybItem(args *model.GetJybInfoArgs, ret *model.JybInfo) error { + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + id := bson.ObjectIdHex(args.Id) + err := cjyb.Find(bson.M{"_id": id}).One(ret) + if err != nil { + logger.Logger.Error("GetJybItem Find is error", err) + return err + } + return nil +} + +// getJyb +func getJyb(cjyb *mongo.Collection, plt string) *model.JybKey { + jk := &model.JybKey{} + id := fmt.Sprintf("%d_%s", model.Keystart, plt) + err := cjyb.Find(bson.M{"plakeyid": id}).One(jk) + if err != nil { + if err == mgo.ErrNotFound { + jk = model.NewJybKey(plt) + err = cjyb.Insert(jk) // 考虑创建失败~ + if err != nil { + logger.Logger.Error("getJyb NewJybKey Insert", err) + return nil + } + } else { + logger.Logger.Error("getJyb NewJybKey Find error ", err) + return nil + } + } + return jk +} + +func (svc *JybSvc) DelJyb(args *model.GetJybInfoArgs, ret *bool) error { + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + jbf := &model.JybInfo{} + err := cjyb.Find(bson.M{"_id": bson.ObjectIdHex(args.Id)}).One(jbf) + if err == nil && jbf.CodeType == 1 { + jk := &model.JybKey{} + if jk = getJyb(cjyb, args.Plt); jk == nil { + logger.Logger.Errorf("DelJyb is nil ") + return errors.New("jybkey is nil") + } + + for code, _ := range jbf.Code { + delete(jk.GeCode, code) + } + + if err := cjyb.Update(bson.M{"_id": jk.KeyId}, bson.D{{"$set", bson.D{{"gecode", jk.GeCode}}}}); err != nil { + logger.Logger.Errorf("DelJyb Update error ", err) + return err + } + } + err = cjyb.Remove(bson.M{"_id": bson.ObjectIdHex(args.Id)}) + return err +} + +// TODO +func (svc *JybSvc) DelCodeJyb(args *model.GetJybInfoArgs, ret *bool) error { + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + + *ret = true + jyb := &model.JybInfo{} + err := cjyb.Find(bson.M{"_id": bson.ObjectIdHex(args.Id)}).One(jyb) + + if jyb.CodeType == 1 { + logger.Logger.Infof("") + return nil + } + if _, exist := jyb.Code[args.UseCode]; !exist { // 已经领取 + return model.ErrJybISReceive + } + delete(jyb.Code, args.UseCode) // 局部map不存在并发安全问题 重复删除问题无法解决 + jyb.Receive++ + err = cjyb.Update(bson.M{"_id": jyb.JybId}, bson.D{{"$set", bson.D{{"code", jyb.Code}, {"receive", jyb.Receive}}}}) + if err != nil { + *ret = false + logger.Logger.Errorf("DelCodeJyb Update error ", err) + } + + return err +} + +var _JybSvc = &JybSvc{} + +func init() { + rpc.Register(_JybSvc) +} diff --git a/dbproxy/svc/l_jybuser.go b/dbproxy/svc/l_jybuser.go new file mode 100644 index 0000000..efa1a00 --- /dev/null +++ b/dbproxy/svc/l_jybuser.go @@ -0,0 +1,248 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + JybUserDBName = "log" + JybUserCollName = "log_jybuser" + ErrJybUserDBNotOpen = model.NewDBError(JybUserDBName, JybUserCollName, model.NOT_OPEN) +) + +func JybuserCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, JybUserDBName) + if s != nil { + c, first := s.DB().C(JybUserCollName) + if first { + // c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type JybUserSvc struct { +} + +// 验证+ +func (svc *JybUserSvc) VerifyJybUser(args *model.GetJybInfoArgs, ret *bool) error { + cjybuse := JybuserCollection(args.Plt) + if cjybuse == nil { + return ErrJybUserDBNotOpen + } + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + jyb := &model.JybInfo{} + err := cjyb.Find(bson.M{"_id": bson.ObjectIdHex(args.Id)}).One(jyb) + if err != nil { + logger.Logger.Errorf("VerifyJybUser sind:%v Find %v error %v", args.SnId, args.Id, err) + return err + } + if _, exist := jyb.Code[args.UseCode]; !exist { // 该兑换码已经被领取 + return model.ErrJybISReceive + } + + jybuser := &model.JybUserInfo{} + err = cjybuse.Find(bson.M{"snid": args.SnId, "platform": &args.Plt}).One(jybuser) + if err != nil { + if err == mgo.ErrNotFound { + info := model.NewJybUserInfo(args.SnId, args.Plt) + err = cjybuse.Insert(info) // 考虑创建失败~ + if err != nil { + logger.Logger.Error("sind:%v svc.VerifyJybUser is Insert err ", args.SnId, err) + return err + } + *ret = true // 说明没有领取过礼包 直接返回 + return nil + } + logger.Logger.Errorf("sind:%v VerifyJybUser error ", args.SnId, err) + return err + } + if _, exist := jybuser.JybInfos[args.Id]; exist { // 该类型礼包玩家已经领取过 + return model.ErrJYBPlCode + } + // 可以领取 + *ret = true + return nil +} + +// 验证+更新 +func (svc *JybUserSvc) VerifyUpJybUser(args *model.VerifyUpJybInfoArgs, ret *model.JybInfo) error { + cjybuse := JybuserCollection(args.Plt) + if cjybuse == nil { + return ErrJybUserDBNotOpen + } + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + if args.CodeType != 1 { + err := cjyb.Find(bson.M{"codestart": args.CodeStart}).One(ret) + if err != nil { + logger.Logger.Warn("VerifyUpJybUser snid %v error codestart %v code %v JybInfo %v", args.SnId, args.CodeStart, args.UseCode, err) + return model.ErrJybIsNotExist + } + if ret.StartTime > 0 && ret.EndTime > 0 { + ts := time.Now().Unix() + if ts < ret.StartTime || ts > ret.EndTime { // 未开始 + return model.ErrJybTsTimeErr + } + } + if _, exist := ret.Code[args.UseCode]; !exist { // 该兑换码已经被领取 + return model.ErrJybISReceive + } + } else { + jk := &model.JybKey{} + if jk = getJyb(cjyb, args.Plt); jk == nil { + logger.Logger.Errorf("DelJybjybkey is nil ") + return errors.New("jybkey is nil") + } + + if jk.GeCode == nil { // 不存在通用礼包 + logger.Logger.Warn("VerifyUpJybUser NewJybKey GeCode is nil") + return model.ErrJybIsNotExist + } else if id, exist := jk.GeCode[args.UseCode]; !exist { + logger.Logger.Warn("VerifyUpJybUser NewJybKey Find err") + return model.ErrJybIsNotExist + } else { + + cid := bson.ObjectIdHex(id) + err := cjyb.Find(bson.M{"_id": cid}).One(ret) + if err != nil { + logger.Logger.Errorf("VerifyUpJybUser snid %v error id %v %v", args.SnId, id, err) + return err + } + } + if ret.StartTime > 0 && ret.EndTime > 0 { + ts := time.Now().Unix() + if ts < ret.StartTime || ts > ret.EndTime { // 未开始 + return model.ErrJybTsTimeErr + } + } + } + + return upJybUser(cjybuse, cjyb, args.SnId, args.CodeType, args.Plt, args.UseCode, ret) +} + +func upJybUser(cjybuse, cjyb *mongo.Collection, snId, codeType int32, plt, useCode string, ret *model.JybInfo) error { + jybuser := &model.JybUserInfo{} + err := cjybuse.Find(bson.M{"snid": snId, "platform": plt}).One(jybuser) + if err != nil { + if err == mgo.ErrNotFound { + jybuser = model.NewJybUserInfo(snId, plt) + err = cjybuse.Insert(jybuser) // 考虑创建失败~ + if err != nil { + logger.Logger.Error("svc.VerifyJybUser is Insert err ", err) + return err + } + } else { + logger.Logger.Errorf("VerifyJybUser error ", err) + return err + } + } + jybuseerid := ret.JybId.Hex() + if jybuser.JybInfos == nil { + jybuser.JybInfos = make(map[string]int32) + } else if _, exist := jybuser.JybInfos[jybuseerid]; exist { // 该类型礼包玩家已经领取过 + return model.ErrJYBPlCode + } + + jybuser.JybInfos[jybuseerid] = 1 + + err = cjybuse.Update(bson.M{"_id": jybuser.JybUserId}, bson.D{{"$set", bson.D{{"jybinfos", jybuser.JybInfos}}}}) + if err != nil { + logger.Logger.Error("UpgradeJyb error ", err) + return err + } + if codeType != 1 { + delete(ret.Code, useCode) // 局部map不存在并发安全问题 重复删除问题无法解决 + } + ret.Receive++ + err = cjyb.Update(bson.M{"_id": ret.JybId}, bson.D{{"$set", bson.D{{"code", ret.Code}, {"receive", ret.Receive}}}}) + if err != nil { + logger.Logger.Errorf("UpgradeJyb error ", err) + + } + return err +} + +// 更新记录玩家领取过的礼包 先只是记录id 更新之前必须先验证 +func (svc *JybUserSvc) UpgradeJybUser(args *model.GetJybInfoArgs, ret *model.JybInfo) error { + cjybuse := JybuserCollection(args.Plt) + if cjybuse == nil { + return ErrJybUserDBNotOpen + } + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + cid := bson.ObjectIdHex(args.Id) + err := cjyb.Find(bson.M{"_id": cid}).One(ret) + if err != nil { + logger.Logger.Errorf("VerifyJybUser error id %v %v %v", args.Id, args, err) + return err + } + return upJybUser(cjybuse, cjyb, args.SnId, args.CodeType, args.Plt, args.UseCode, ret) +} + +// 回退 +func (svc *JybUserSvc) BackOffJybUser(args *model.GetJybInfoArgs, ret *bool) error { + cjybuse := JybuserCollection(args.Plt) + if cjybuse == nil { + return ErrJybUserDBNotOpen + } + cjyb := JybCollection(args.Plt) + if cjyb == nil { + return ErrJybDBNotOpen + } + + jyb := &model.JybInfo{} + err := cjyb.Find(bson.M{"_id": bson.ObjectIdHex(args.Id)}).One(jyb) + if err != nil { + logger.Logger.Error("BackOffJybUser error ", err) + return err + } + jyb.Code[args.UseCode] = 1 // 将该礼包存入 + jyb.Receive-- + err = cjyb.Update(bson.M{"_id": jyb.JybId}, bson.D{{"$set", bson.D{{"code", jyb.Code}, {"receive", jyb.Receive}}}}) + + jybuser := &model.JybUserInfo{} + err = cjybuse.Find(bson.M{"snid": args.SnId, "platform": &args.Plt}).One(jybuser) + if err != nil { + if err == mgo.ErrNotFound { // + info := model.NewJybUserInfo(args.SnId, args.Plt) + err = cjybuse.Insert(info) // 考虑创建失败~ + if err != nil { + logger.Logger.Error("svc.BackOffJybUser is Insert err ", err) + return err + } + *ret = true // 说明没有领取过礼包 直接返回 + return nil + } + logger.Logger.Errorf("BackOffJybUser error ", err) + return err + } + + delete(jybuser.JybInfos, args.Id) // 删除该玩家领取记录 + err = cjybuse.Update(bson.M{"_id": jybuser.JybUserId}, bson.D{{"$set", bson.D{{"jybinfos", jybuser.JybInfos}}}}) + + return err +} + +var _JybUserSvc = &JybUserSvc{} + +func init() { + rpc.Register(_JybUserSvc) +} diff --git a/dbproxy/svc/l_loginlog.go b/dbproxy/svc/l_loginlog.go new file mode 100644 index 0000000..312b23d --- /dev/null +++ b/dbproxy/svc/l_loginlog.go @@ -0,0 +1,60 @@ +package svc + +import ( + "net/rpc" + + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func LoginLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.LoginLogDBName) + if s != nil { + c_loginlogrec, first := s.DB().C(model.LoginLogCollName) + if first { + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"logtype"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"ip"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + c_loginlogrec.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c_loginlogrec + } + return nil +} + +type LoginsLogSvc struct { +} + +func (svc *LoginsLogSvc) InsertLoginLogs(logs []*model.LoginLog, ret *bool) (err error) { + for _, log := range logs { + err = svc.InsertSignleLoginLog(log, ret) + if err != nil { + return + } + } + *ret = true + return +} + +func (svc *LoginsLogSvc) InsertSignleLoginLog(log *model.LoginLog, ret *bool) (err error) { + clog := LoginLogsCollection(log.Platform) + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("svc.InsertSignleLoginLog error:", err) + return + } + *ret = true + return +} + +func init() { + rpc.Register(new(LoginsLogSvc)) +} diff --git a/dbproxy/svc/l_lotterylog.go b/dbproxy/svc/l_lotterylog.go new file mode 100644 index 0000000..d9e8e6f --- /dev/null +++ b/dbproxy/svc/l_lotterylog.go @@ -0,0 +1,109 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + LotteryLogDBErr = errors.New("log_lottery db open failed.") +) + +const LotteryLogMaxLimitPerQuery = 100 + +func LotteryLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.LotteryLogDBName) + if s != nil { + c, first := s.DB().C(model.LotteryLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertLotteryLog(log *model.LotteryLog) error { + err := LotteryLogsCollection(log.Platform).Insert(log) + if err != nil { + logger.Logger.Info("InsertLotteryLog error:", err) + return err + } + return nil +} + +func InsertLotteryLogs(logs ...*model.LotteryLog) (err error) { + clog := LotteryLogsCollection(logs[0].Platform) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertLotteryLogs error:", err) + return + } + return +} + +func RemoveLotteryLog(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + return LotteryLogsCollection(plt).RemoveAll(bson.M{"time": bson.M{"$lt": ts}}) +} + +func GetLotteryLogBySnidAndLessTs(plt string, id int32, ts time.Time) (ret []model.LotteryLog, err error) { + err = LotteryLogsCollection(plt).Find(bson.M{"snid": id, "time": bson.M{"$lt": ts}}).Limit(LotteryLogMaxLimitPerQuery).All(&ret) + return +} + +type LotteryLogSvc struct { +} + +func (svc *LotteryLogSvc) InsertLotteryLogs(args []*model.LotteryLog, ret *bool) (err error) { + err = InsertLotteryLogs(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *LotteryLogSvc) InsertLotteryLog(args *model.LotteryLog, ret *bool) (err error) { + err = InsertLotteryLog(args) + if err == nil { + *ret = true + } + return +} + +func (svc *LotteryLogSvc) RemoveLotteryLog(args *model.RemoveLotteryLogArgs, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemoveLotteryLog(args.Plt, args.Ts) + return +} + +func (svc *LotteryLogSvc) GetLotteryLogBySnidAndLessTs(args *model.GetLotteryLogBySnidAndLessTsArgs, ret *[]model.LotteryLog) (err error) { + *ret, err = GetLotteryLogBySnidAndLessTs(args.Plt, args.Id, args.Ts) + return +} + +func init() { + rpc.Register(new(LotteryLogSvc)) +} diff --git a/dbproxy/svc/l_matchlog.go b/dbproxy/svc/l_matchlog.go new file mode 100644 index 0000000..1fedaee --- /dev/null +++ b/dbproxy/svc/l_matchlog.go @@ -0,0 +1,77 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + MatchLogDBErr = errors.New("log_matchlog db open failed.") +) + +func MatchLogCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.MatchLogDBName) + if s != nil { + c, first := s.DB().C(model.MatchLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertMatchLogs(logs ...*model.MatchLog) (err error) { + clog := MatchLogCollection(logs[0].Platform) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertMatchLogs error:", err) + return + } + return +} + +func RemoveMatchLogs(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + return MatchLogCollection(plt).RemoveAll(bson.M{"endtime": bson.M{"$lt": ts}}) +} + +type MatchLogSvc struct { +} + +func (svc *MatchLogSvc) InsertMatchLogs(args []*model.MatchLog, ret *bool) (err error) { + err = InsertMatchLogs(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *MatchLogSvc) RemoveMatchLogs(args *model.RemoveMatchLogsArgs, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemoveMatchLogs(args.Plt, args.Ts) + return +} + +func init() { + rpc.Register(new(MatchLogSvc)) +} diff --git a/dbproxy/svc/l_matchrec.go b/dbproxy/svc/l_matchrec.go new file mode 100644 index 0000000..d23b648 --- /dev/null +++ b/dbproxy/svc/l_matchrec.go @@ -0,0 +1,89 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + MatchRecDBErr = errors.New("log_matchrec db open failed.") +) + +func MatchRecCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.MatchRecDBName) + if s != nil { + c, first := s.DB().C(model.MatchRecCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"matchid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertMatchRecs(logs ...*model.MatchRec) (err error) { + clog := MatchRecCollection(logs[0].Platform) + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertMatchRecs error:", err) + return + } + return +} + +func FetchMatchRecs(plt string, snid int32) (recs []model.MatchRec, err error) { + start := time.Now().AddDate(0, 0, -7) + err = MatchRecCollection(plt).Find(bson.M{"snid": snid, "ts": bson.M{"$gt": start}}).All(&recs) + return +} + +func RemoveMatchRecs(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + return MatchRecCollection(plt).RemoveAll(bson.M{"ts": bson.M{"$lt": ts}}) +} + +type MatchRecSvc struct { +} + +func (svc *MatchRecSvc) InsertMatchRecs(args []*model.MatchRec, ret *bool) (err error) { + err = InsertMatchRecs(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *MatchRecSvc) FetchMatchRecs(args *model.FetchMatchRecsArgs, ret *[]model.MatchRec) (err error) { + *ret, err = FetchMatchRecs(args.Plt, args.SnId) + return +} + +func (svc *MatchRecSvc) RemoveMatchRecs(args *model.RemoveMatchRecsArgs, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemoveMatchRecs(args.Plt, args.Ts) + return +} + +func init() { + rpc.Register(new(MatchRecSvc)) +} diff --git a/dbproxy/svc/l_matchseason.go b/dbproxy/svc/l_matchseason.go new file mode 100644 index 0000000..b544a3c --- /dev/null +++ b/dbproxy/svc/l_matchseason.go @@ -0,0 +1,91 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + MatchSeasonDBName = "log" + MatchSeasonCollName = "log_matchseason" + MatchSeasonColError = errors.New("MatchSeason collection open failed") +) + +func MatchSeasonCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, MatchSeasonDBName) + if s != nil { + c, first := s.DB().C(MatchSeasonCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type MatchSeasonSvc struct { +} + +func (svc *MatchSeasonSvc) UpsertMatchSeason(args *model.MatchSeason, ret *model.MatchSeasonRet) error { + cc := MatchSeasonCollection(args.Platform) + if cc == nil { + return MatchSeasonColError + } + _, err := cc.Upsert(bson.M{"snid": args.SnId}, args) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("UpsertMatchSeason is err: ", err) + return err + } + return nil +} + +func (svc *MatchSeasonSvc) QueryMatchSeasonByKey(args *model.MatchSeasonByKey, ret *model.MatchSeasonRet) error { + fc := MatchSeasonCollection(args.Platform) + if fc == nil { + return MatchSeasonColError + } + err := fc.Find(bson.M{"snid": args.SnId}).One(&ret.Ms) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryMatchSeasonByKey is err: ", err) + return err + } + return nil +} + +func (svc *MatchSeasonSvc) QueryMatchSeason(platform string, ret *model.MatchSeasonRets) error { + fc := MatchSeasonCollection(platform) + if fc == nil { + return MatchSeasonColError + } + + err := fc.Find(bson.M{"platform": platform}).Sort("-lv").Limit(model.GameParamData.MatchSeasonRankMaxNum).All(&ret.Mss) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("QueryMatchSeason is err: ", err) + return err + } + return nil +} + +func (svc *MatchSeasonSvc) DelMatchSeason(args *model.MatchSeasonByKey, ret *bool) error { + cc := MatchSeasonCollection(args.Platform) + if cc == nil { + return MatchSeasonColError + } + err := cc.Remove(bson.M{"snid": args.SnId}) + if err != nil { + logger.Logger.Error("DelMatchSeason is err: ", err) + return err + } + return nil +} + +var _MatchSeasonSvc = &MatchSeasonSvc{} + +func init() { + rpc.Register(_MatchSeasonSvc) +} diff --git a/dbproxy/svc/l_matchseasonid.go b/dbproxy/svc/l_matchseasonid.go new file mode 100644 index 0000000..02abf7c --- /dev/null +++ b/dbproxy/svc/l_matchseasonid.go @@ -0,0 +1,64 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + MatchSeasonIdDBName = "log" + MatchSeasonIdCollName = "log_matchseasonid" + MatchSeasonIdColError = errors.New("MatchSeasonId collection open failed") +) + +func MatchSeasonIdCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, MatchSeasonIdDBName) + if s != nil { + c, first := s.DB().C(MatchSeasonIdCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type MatchSeasonIdSvc struct { +} + +func (svc *MatchSeasonIdSvc) UpsertMatchSeasonId(args *model.MatchSeasonId, ret *model.MatchSeasonIdRet) error { + cc := MatchSeasonIdCollection(args.Platform) + if cc == nil { + return MatchSeasonIdColError + } + _, err := cc.Upsert(bson.M{"platform": args.Platform}, args) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertMatchSeasonId is err: ", err) + return err + } + return nil +} + +func (svc *MatchSeasonIdSvc) QueryMatchSeasonId(args *model.MatchSeasonId, ret *model.MatchSeasonIdRet) error { + fc := MatchSeasonIdCollection(args.Platform) + if fc == nil { + return MatchSeasonIdColError + } + err := fc.Find(bson.M{"platform": args.Platform}).One(&ret.MsId) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryMatchSeasonId is err: ", err) + return err + } + return nil +} + +var _MatchSeasonIdSvc = &MatchSeasonIdSvc{} + +func init() { + rpc.Register(_MatchSeasonIdSvc) +} diff --git a/dbproxy/svc/l_matchseasonrank.go b/dbproxy/svc/l_matchseasonrank.go new file mode 100644 index 0000000..3b38cc4 --- /dev/null +++ b/dbproxy/svc/l_matchseasonrank.go @@ -0,0 +1,77 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + MatchSeasonRankDBName = "log" + MatchSeasonRankCollName = "log_matchseasonrank" + MatchSeasonRankColError = errors.New("MatchSeasonRank collection open failed") +) + +func MatchSeasonRankCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, MatchSeasonRankDBName) + if s != nil { + c, first := s.DB().C(MatchSeasonRankCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"name"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"lv"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"updatets"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type MatchSeasonRankSvc struct { +} + +func (svc *MatchSeasonRankSvc) UpsertMatchSeasonRank(args *model.MatchSeasonRankByKey, ret *model.MatchSeasonRankRet) error { + cc := MatchSeasonRankCollection(args.Platform) + if cc == nil { + return MatchSeasonRankColError + } + _, err := cc.RemoveAll(nil) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertMatchSeasonRank RemoveAll is err: ", err) + return err + } + if args.MsRanks != nil && len(args.MsRanks) > 0 { + for _, rank := range args.MsRanks { + err := cc.Insert(rank) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertMatchSeasonRank is err: ", err) + return err + } + } + } + return nil +} + +func (svc *MatchSeasonRankSvc) QueryMatchSeasonRank(args *model.MatchSeasonRankByKey, ret *model.MatchSeasonRankRet) error { + fc := MatchSeasonRankCollection(args.Platform) + if fc == nil { + return MatchSeasonRankColError + } + err := fc.Find(bson.M{"platform": args.Platform}).All(&ret.MsRanks) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("QueryMatchSeasonRank is err: ", err) + return err + } + return nil +} + +var _MatchSeasonRankSvc = &MatchSeasonRankSvc{} + +func init() { + rpc.Register(_MatchSeasonRankSvc) +} diff --git a/dbproxy/svc/l_monthlyprofitpool.go b/dbproxy/svc/l_monthlyprofitpool.go new file mode 100644 index 0000000..c6d563a --- /dev/null +++ b/dbproxy/svc/l_monthlyprofitpool.go @@ -0,0 +1,100 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + MonthlyProfitPoolDBErr = errors.New("log_monthlyprofitpool open failed.") +) + +func MonthlyProfitPoolCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.MonthlyProfitPoolDBName) + if s != nil { + c, first := s.DB().C(model.MonthlyProfitPoolCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"serverid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertMonthlyProfitPool(logs ...*model.MonthlyProfitPool) (err error) { + clog := MonthlyProfitPoolCollection() + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertMonthlyProfitPool error:", err) + return + } + return +} +func InsertSignleMonthlyProfitPool(log *model.MonthlyProfitPool) (err error) { + clog := MonthlyProfitPoolCollection() + if clog == nil { + return + } + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertSignleMonthlyProfitPool error:", err) + return + } + return +} + +func RemoveMonthlyProfitPool(ts time.Time) (*mgo.ChangeInfo, error) { + return MonthlyProfitPoolCollection().RemoveAll(bson.M{"time": bson.M{"$lt": ts}}) +} + +type MonthlyProfitPoolSvc struct { +} + +func (svc *MonthlyProfitPoolSvc) InsertMonthlyProfitPool(logs []*model.MonthlyProfitPool, ret *bool) (err error) { + err = InsertMonthlyProfitPool(logs...) + if err == nil { + *ret = true + } + return +} + +func (svc *MonthlyProfitPoolSvc) InsertSignleMonthlyProfitPool(log *model.MonthlyProfitPool, ret *bool) (err error) { + err = InsertSignleMonthlyProfitPool(log) + if err == nil { + *ret = true + } + return +} + +func (svc *MonthlyProfitPoolSvc) RemoveMonthlyProfitPool(ts time.Time, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemoveMonthlyProfitPool(ts) + return +} + +func init() { + rpc.Register(new(MonthlyProfitPoolSvc)) +} diff --git a/dbproxy/svc/l_onlinelog.go b/dbproxy/svc/l_onlinelog.go new file mode 100644 index 0000000..f2b3b9d --- /dev/null +++ b/dbproxy/svc/l_onlinelog.go @@ -0,0 +1,77 @@ +package svc + +import ( + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +func OnlineLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.OnlineLogDBName) + if s != nil { + c_onlinelogrec, first := s.DB().C(model.OnlineLogCollName) + if first { + c_onlinelogrec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_onlinelogrec.EnsureIndex(mgo.Index{Key: []string{"onlinetype"}, Background: true, Sparse: true}) + c_onlinelogrec.EnsureIndex(mgo.Index{Key: []string{"onlinets"}, Background: true, Sparse: true}) + c_onlinelogrec.EnsureIndex(mgo.Index{Key: []string{"offlinetype"}, Background: true, Sparse: true}) + c_onlinelogrec.EnsureIndex(mgo.Index{Key: []string{"offlinets"}, Background: true, Sparse: true}) + } + return c_onlinelogrec + } + return nil +} + +type OnlineLogSvc struct { +} + +func (svc *OnlineLogSvc) InsertOnlineLogs(logs []*model.OnlineLog, ret *bool) (err error) { + for _, log := range logs { + err = svc.InsertSignleOnlineLog(log, ret) + if err != nil { + return + } + } + *ret = true + return +} + +func (svc *OnlineLogSvc) InsertSignleOnlineLog(log *model.OnlineLog, ret *bool) (err error) { + clog := OnlineLogsCollection(log.Platform) + if clog == nil { + return + } + if log.LogId == "" { + logger.Logger.Warn("svc.InsertSignleOnlineLog error, id") + return + } + + switch { + case (log.OnlineType == common.LoginLogTypeLogin || log.OnlineType == common.LoginLogTypeRehold) && + log.OfflineType == 0 && log.OnlineTs > 0 && log.OfflineTs == 0: + // 登录,重连 + case (log.OfflineType == common.LoginLogTypeDrop || log.OfflineType == common.LoginLogTypeLogout) && + log.OnlineTs > 0 && log.OfflineTs > 0: + // 掉线,登出 + default: + logger.Logger.Warn("svc.InsertSignleOnlineLog error type") + return + } + _, err = clog.Upsert(bson.M{"_id": log.LogId}, log) + if err != nil { + logger.Logger.Warn("svc.InsertSignleLoginLog error:", err) + return + } + *ret = true + return +} + +func init() { + rpc.Register(new(OnlineLogSvc)) +} diff --git a/dbproxy/svc/l_privatescenelog.go b/dbproxy/svc/l_privatescenelog.go new file mode 100644 index 0000000..30cb39b --- /dev/null +++ b/dbproxy/svc/l_privatescenelog.go @@ -0,0 +1,79 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "net/rpc" + "time" +) + +var ( + PrivateSceneLogDBErr = errors.New("log_privatescene db open failed.") +) + +func PrivateSceneLogCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.PrivateSceneLogDBName) + if s != nil { + c, first := s.DB().C(model.PrivateSceneLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"packagetag"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertPrivateSceneLogs(log *model.PrivateSceneLog) error { + clog := PrivateSceneLogCollection(log.Platform) + if clog == nil { + return PrivateSceneLogDBErr + } + + return clog.Insert(log) +} + +func GetPrivateSceneLogBySnId(plt string, snid int32, limit int) ([]*model.PrivateSceneLog, error) { + clog := PrivateSceneLogCollection(plt) + if clog == nil { + return nil, PrivateSceneLogDBErr + } + + var logs []*model.PrivateSceneLog + err := clog.Find(bson.M{"snid": snid}).Sort("-createtime").Limit(limit).All(&logs) + return logs, err +} + +func RemovePrivateSceneLogs(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + return PrivateSceneLogCollection(plt).RemoveAll(bson.M{"createtime": bson.M{"$lt": ts}}) +} + +type PrivateSceneLogSvc struct { +} + +func (svc *PrivateSceneLogSvc) InsertPrivateSceneLogs(log *model.PrivateSceneLog, ret *bool) error { + err := InsertPrivateSceneLogs(log) + if err == nil { + *ret = true + } + return err +} + +func (svc *RebateLogSvc) GetPrivateSceneLogBySnId(args *model.GetPrivateSceneLogBySnIdArgs, ret *[]*model.PrivateSceneLog) (err error) { + *ret, err = GetPrivateSceneLogBySnId(args.Plt, args.SnId, args.Limit) + return err +} + +func (svc *RebateLogSvc) RemovePrivateSceneLogs(args *model.RemovePrivateSceneLogsArgs, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemovePrivateSceneLogs(args.Plt, args.Ts) + return err +} + +func init() { + rpc.Register(new(PrivateSceneLogSvc)) +} diff --git a/dbproxy/svc/l_rankplayercoin.go b/dbproxy/svc/l_rankplayercoin.go new file mode 100644 index 0000000..5050030 --- /dev/null +++ b/dbproxy/svc/l_rankplayercoin.go @@ -0,0 +1,75 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" + "time" +) + +var ( + RankPlayerCoinDBName = "log" + RankPlayerCoinCollName = "log_rankplayercoin" + RankPlayerCoinColError = errors.New("RankPlayer collection open failed") +) + +func RankPlayerCoinCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, RankPlayerCoinDBName) + if s != nil { + c, first := s.DB().C(RankPlayerCoinCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"coin"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func RankPlayerCoinUpsert(args *model.RankPlayerCoin) error { + cc := RankPlayerCoinCollection(args.Platform) + if cc == nil { + return RankPlayerCoinColError + } + args.UpdateTs = time.Now().Unix() + _, err := cc.Upsert(bson.M{"snid": args.SnId}, args) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("RankPlayerCoinSvc.Upsert is err: ", err) + return err + } + return nil +} + +type RankPlayerCoinSvc struct { +} + +func (svc *RankPlayerCoinSvc) Upsert(args *model.RankPlayerCoin, ret *bool) error { + err := RankPlayerCoinUpsert(args) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *RankPlayerCoinSvc) Find(args *model.FindPlayerCoinListArgs, ret *model.FindPlayerCoinListReply) error { + fc := RankPlayerCoinCollection(args.Platform) + if fc == nil { + return RankPlayerCoinColError + } + + err := fc.Find(bson.M{}).Sort("-coin").Limit(model.GameParamData.RankPlayerCoinMaxNum).All(&ret.List) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryMatchSeason is err: ", err) + return err + } + return nil +} + +func init() { + rpc.Register(new(RankPlayerCoinSvc)) +} diff --git a/dbproxy/svc/l_rankseasion.go b/dbproxy/svc/l_rankseasion.go new file mode 100644 index 0000000..3e8364f --- /dev/null +++ b/dbproxy/svc/l_rankseasion.go @@ -0,0 +1,88 @@ +package svc + +import ( + "errors" + "fmt" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + RankSeasonDBName = "log" + RankSeasonCollName = "log_rankmatch" + RankSeasonColError = errors.New("RankSeason collection open failed") +) + +func RankSeasonCollection(plt string, rankType int32) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, RankSeasonDBName) + if s != nil { + c, first := s.DB().C(fmt.Sprintf("%s_%d", RankSeasonCollName, rankType)) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"isrobot", "seasonid", "score"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func RankSeasonUpsert(args *model.PlayerRankScore) error { + cc := RankSeasonCollection(args.Platform, args.RankType) + if cc == nil { + return RankSeasonColError + } + args.UpdateTs = time.Now().Unix() + _, err := cc.Upsert(bson.M{"snid": args.SnId}, args) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("RankSeasonSvc.Upsert is err: ", err) + return err + } + return nil +} + +type RankSeasonSvc struct { +} + +func (svc *RankSeasonSvc) Upsert(args *model.PlayerRankScore, ret *bool) error { + err := RankSeasonUpsert(args) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *RankSeasonSvc) FindRankSeason(args *model.FindPlayerRankSeasonListArgs, ret *model.FindPlayerRankSeasonListReply) error { + fc := RankSeasonCollection(args.Platform, args.RankType) + if fc == nil { + return MatchSeasonColError + } + + season, err := FindRankSeason(args.Platform) + if err != nil { + return err + } + + if model.GameParamData.CloseRankListRobot { + err = fc.Find(bson.M{"isrobot": false, "seasonid": season.SeasonId}).Sort("-score").Limit(model.GameParamData.RankSeasonMaxNum).All(&ret.List) + } else { + err = fc.Find(bson.M{"seasonid": season.SeasonId}).Sort("-score").Limit(model.GameParamData.RankSeasonMaxNum).All(&ret.List) + } + + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryMatchSeason is err: ", err) + return err + } + return nil +} + +func init() { + rpc.Register(new(RankSeasonSvc)) +} diff --git a/dbproxy/svc/l_rankseasonid.go b/dbproxy/svc/l_rankseasonid.go new file mode 100644 index 0000000..c29a44d --- /dev/null +++ b/dbproxy/svc/l_rankseasonid.go @@ -0,0 +1,72 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + RankSeasonIdDBName = "log" + RankSeasonIdCollName = "log_rankseasonid" + RankSeasonIdColError = errors.New("RankSeasonId collection open failed") +) + +func RankSeasonIdCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, RankSeasonIdDBName) + if s != nil { + c, first := s.DB().C(RankSeasonIdCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type RankSeasonIdSvc struct { +} + +func (svc *RankSeasonIdSvc) Upsert(args *model.RankSeasonId, ret *bool) error { + cc := RankSeasonIdCollection(args.Platform) + if cc == nil { + return RankSeasonIdColError + } + _, err := cc.Upsert(bson.M{"platform": args.Platform}, args) + if err != nil { + logger.Logger.Error("RankSeasonIdSvc.Upsert is err: ", err) + return err + } + *ret = true + return nil +} + +func FindRankSeason(platform string) (*model.RankSeasonId, error) { + fc := RankSeasonIdCollection(platform) + if fc == nil { + return nil, RankSeasonIdColError + } + var ret model.RankSeasonId + err := fc.Find(bson.M{"platform": platform}).One(&ret) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("RankSeasonIdSvc.FindOne is err: ", err) + return nil, err + } + return &ret, nil +} + +func (svc *RankSeasonIdSvc) FindOne(args *model.RankSeasonId, ret *model.RankSeasonIdRet) error { + r, err := FindRankSeason(args.Platform) + ret.MsId = r + return err +} + +func init() { + rpc.Register(new(RankSeasonIdSvc)) +} diff --git a/dbproxy/svc/l_rebatetasklog.go b/dbproxy/svc/l_rebatetasklog.go new file mode 100644 index 0000000..1785a2f --- /dev/null +++ b/dbproxy/svc/l_rebatetasklog.go @@ -0,0 +1,93 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "math" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" + "time" +) + +var ( + RebateLogDBErr = errors.New("log_rebatetask db open failed.") +) + +func RebateCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.RebateLogDBName) + if s != nil { + c, first := s.DB().C(model.RebateLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"id"}, Unique: true, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertRebateLog(plt string, data *model.Rebate) error { + rebatec := RebateCollection(plt) + if rebatec == nil { + return nil + } + data.Id = bson.NewObjectId() + data.Ts = time.Now().Unix() + err := rebatec.Insert(data) + if err != nil { + logger.Logger.Error("Insert rebate data eror.", err) + return err + } + return nil +} + +func GetRebateLog(plt string, pageNo, pageSize int, snId int32) (r *model.RebateLog, err error) { + r = new(model.RebateLog) + rebatec := RebateCollection(plt) + if rebatec == nil { + return + } + total, err := rebatec.Find(bson.M{"snid": snId}).Count() + if err != nil || total == 0 { + return nil, err + } + r.PageSum = int(math.Ceil(float64(total) / float64(pageSize))) + if pageNo > r.PageSum { + pageNo = r.PageSum + } + if pageNo <= 0 { + pageNo = 1 + } + limitNum := (pageNo - 1) * pageSize + err = rebatec.Find(bson.M{"snid": snId}).Skip(limitNum).Limit(pageSize).Sort("-ts").All(&r.Rebates) + if err != nil { + logger.Logger.Error("Find rebate data eror.", err) + return nil, err + } + r.PageNo = pageNo + r.PageSize = pageSize + return +} + +type RebateLogSvc struct { +} + +func (svc *RebateLogSvc) InsertRebateLog(log *model.BankBindLog, ret *bool) error { + err := InsertBankBindLog(log) + if err == nil { + *ret = true + } + return err +} + +func (svc *RebateLogSvc) GetRebateLog(args *model.GetRebateLogArgs, ret **model.RebateLog) (err error) { + *ret, err = GetRebateLog(args.Plt, args.PageNo, args.PageSize, args.SnId) + return err +} + +func init() { + rpc.Register(new(RebateLogSvc)) +} diff --git a/dbproxy/svc/l_safeboxlog.go b/dbproxy/svc/l_safeboxlog.go new file mode 100644 index 0000000..369bd3a --- /dev/null +++ b/dbproxy/svc/l_safeboxlog.go @@ -0,0 +1,126 @@ +package svc + +import ( + "encoding/json" + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +const SafeBoxMaxLimit = 50 + +var SafeBoxLogErr = errors.New("log_safeboxrec db open failed.") + +func SafeBoxCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.SafeBoxDBName) + if s != nil { + c, first := s.DB().C(model.SafeBoxCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"userid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"promoter"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertSafeBox(log *model.SafeBoxRec) error { + c_safeboxrec := SafeBoxCollection(log.Platform) + if c_safeboxrec == nil { + return SafeBoxLogErr + } + err := c_safeboxrec.Insert(log) + if err != nil { + buff, _ := json.Marshal(log) + logger.Logger.Error("InsertSafeBoxCoinLog param:", string(buff)) + logger.Logger.Error("InsertSafeBoxCoinLog error:", err) + return err + } + return nil +} + +func GetSafeBoxs(plt string, userid int32) (recs []model.SafeBoxRec, err error) { + csafeboxrec := SafeBoxCollection(plt) + if csafeboxrec == nil { + return nil, SafeBoxLogErr + } + + logger.Logger.Trace("GetSafeBoxs:", userid) + err = csafeboxrec.Find(bson.M{"userid": userid}).Sort("-time").Limit(SafeBoxMaxLimit).All(&recs) + return +} + +func RemoveSafeBoxs(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + return SafeBoxCollection(plt).RemoveAll(bson.M{"time": bson.M{"$lt": ts}}) +} + +func RemoveSafeBoxCoinLog(plt string, id bson.ObjectId) error { + sbc := SafeBoxCollection(plt) + if sbc == nil { + return SafeBoxLogErr + } + return sbc.RemoveId(id) +} + +func GetSafeBoxCoinLog(plt string, ts time.Time) (recs []model.SafeBoxRec, err error) { + csafeboxrec := SafeBoxCollection(plt) + if csafeboxrec == nil { + return nil, SafeBoxLogErr + } + + err = csafeboxrec.Find(bson.M{"time": bson.M{"$gt": ts}}).All(&recs) + return +} + +type SafeBoxRecSvc struct { +} + +func (svc *SafeBoxRecSvc) InsertSafeBox(log *model.SafeBoxRec, ret *bool) (err error) { + err = InsertSafeBox(log) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *SafeBoxRecSvc) GetSafeBoxs(args *model.GetSafeBoxsArgs, ret *[]model.SafeBoxRec) (err error) { + *ret, err = GetSafeBoxs(args.Plt, args.SnId) + if err != nil { + return err + } + return nil +} + +func (svc *SafeBoxRecSvc) RemoveSafeBoxs(args *model.RemoveSafeBoxsArgs, ret **mgo.ChangeInfo) (err error) { + *ret, err = RemoveSafeBoxs(args.Plt, args.Ts) + return +} + +func (svc *SafeBoxRecSvc) RemoveSafeBoxCoinLog(args *model.RemoveSafeBoxCoinLogArgs, ret *bool) (err error) { + err = RemoveSafeBoxCoinLog(args.Plt, args.Id) + if err != nil { + return err + } + *ret = true + return +} + +func (svc *SafeBoxRecSvc) GetSafeBoxCoinLog(args *model.GetSafeBoxCoinLogArgs, ret *[]model.SafeBoxRec) (err error) { + *ret, err = GetSafeBoxCoinLog(args.Plt, args.Ts) + if err != nil { + return err + } + return +} + +func init() { + rpc.Register(new(SafeBoxRecSvc)) +} diff --git a/dbproxy/svc/l_scenecoinlog.go b/dbproxy/svc/l_scenecoinlog.go new file mode 100644 index 0000000..eb4c751 --- /dev/null +++ b/dbproxy/svc/l_scenecoinlog.go @@ -0,0 +1,56 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + SceneCoinLogErr = errors.New("log_scenecoin db open failed.") +) + +func SceneCoinLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.SceneCoinLogDBName) + if s != nil { + c_scenecoinlogrec, first := s.DB().C(model.SceneCoinLogCollName) + if first { + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"snid", "-time"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"gameidex"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"gameid"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"sceneid"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"gamemode"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + c_scenecoinlogrec.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c_scenecoinlogrec + } + return nil +} + +type SceneCoinLogSvc struct { +} + +func (svc *SceneCoinLogSvc) InsertSceneCoinLog(log *model.SceneCoinLog, ret *bool) (err error) { + clog := SceneCoinLogsCollection(log.Platform) + if clog == nil { + return + } + + err = clog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertSceneCoinLog error:", err) + return + } + *ret = true + return +} + +func init() { + rpc.Register(new(SceneCoinLogSvc)) +} diff --git a/dbproxy/svc/l_upgradeaccountcoin.go b/dbproxy/svc/l_upgradeaccountcoin.go new file mode 100644 index 0000000..ea182f6 --- /dev/null +++ b/dbproxy/svc/l_upgradeaccountcoin.go @@ -0,0 +1,65 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + UpgradeAccountCoinLogErr = errors.New("log_upgradeaccountcoin db open failed.") +) + +func UpgradeAccountCoinCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.UpgradeAccountCoinDBName) + if s != nil { + c, first := s.DB().C(model.UpgradeAccountCoinCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"ip", "date"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func GetUpgradeAccountCoinLogsByIPAndDate(ip, platform string, date int32) (int32, error) { + upgradeaccountcoins := UpgradeAccountCoinCollection(platform) + if upgradeaccountcoins == nil { + return 0, UpgradeAccountCoinLogErr + } + n, err := upgradeaccountcoins.Find(bson.M{"ip": ip, "date": date}).Count() + return int32(n), err +} + +func InsertUpgradeAccountCoinLog(log *model.UpGradeAccountCoin) error { + upgradeaccountcoins := UpgradeAccountCoinCollection(log.Platform) + if upgradeaccountcoins == nil { + return UpgradeAccountCoinLogErr + } + + return upgradeaccountcoins.Insert(log) +} + +type UpGradeAccountCoinSvc struct { +} + +func (svc *UpGradeAccountCoinSvc) GetUpgradeAccountCoinLogsByIPAndDate(args *model.UpGradeAccountCoinArgs, count *int32) (err error) { + *count, err = GetUpgradeAccountCoinLogsByIPAndDate(args.Ip, args.Plt, args.Date) + return +} + +func (svc *UpGradeAccountCoinSvc) InsertUpgradeAccountCoinLog(args *model.UpGradeAccountCoin, ret *bool) (err error) { + err = InsertUpgradeAccountCoinLog(args) + if err == nil { + *ret = true + } + return +} + +func init() { + rpc.Register(new(UpGradeAccountCoinSvc)) +} diff --git a/dbproxy/svc/l_welfarelog.go b/dbproxy/svc/l_welfarelog.go new file mode 100644 index 0000000..95a9309 --- /dev/null +++ b/dbproxy/svc/l_welfarelog.go @@ -0,0 +1,70 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + WelfareLogErr = errors.New("log coinex log open failed.") +) + +func WelfareLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.WelfareLogDBName) + if s != nil { + c_welfarelogrec, first := s.DB().C(model.WelfareLogCollName) + if first { + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"logtype"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"wtype"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"-time"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"oper"}, Background: true, Sparse: true}) + c_welfarelogrec.EnsureIndex(mgo.Index{Key: []string{"seqno"}, Background: true, Sparse: true}) + } + return c_welfarelogrec + } + return nil +} + +type WelfareLogSvc struct { +} + +func (svc *WelfareLogSvc) InsertWelfareLog(log *model.WelfareLog, ret *bool) (err error) { + wlog := WelfareLogsCollection(log.Platform) + if wlog == nil { + return + } + err = wlog.Insert(log) + if err != nil { + logger.Logger.Warn("InsertWelfareLog error:", err) + return + } + + *ret = true + return +} + +func (svc *WelfareLogSvc) RemoveWelfareLogOne(args *model.RemoveWelfareLogOneArg, ret *bool) (err error) { + wlog := WelfareLogsCollection(args.Plt) + if wlog == nil { + return WelfareLogErr + } + err = wlog.RemoveId(args.Id) + if err != nil { + logger.Logger.Warn("RemoveWelfareLogOne error:", err) + return + } + + *ret = true + return +} + +func init() { + rpc.Register(new(WelfareLogSvc)) +} diff --git a/dbproxy/svc/m_monitor.go b/dbproxy/svc/m_monitor.go new file mode 100644 index 0000000..9fe96be --- /dev/null +++ b/dbproxy/svc/m_monitor.go @@ -0,0 +1,139 @@ +package svc + +import ( + "encoding/gob" + "errors" + "fmt" + "net/rpc" + "strings" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/mgrsrv/api" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/profile" + "mongo.games.com/goserver/core/schedule" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" +) + +var ( + MonitorErr = errors.New("monitor_data open failed.") +) + +func MonitorDataCollection(cname string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetGlobal(model.MonitorDBName) + if s != nil { + c, first := s.DB().C(fmt.Sprintf("%v_%v", model.MonitorPrefixName, strings.ToLower(cname))) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"srvid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"srvtype"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"key"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type MonitorDataSvc struct { +} + +func (svc *MonitorDataSvc) InsertMonitorData(args *model.MonitorDataArg, ret *bool) (err error) { + clog := MonitorDataCollection(args.Name) + if clog == nil { + return + } + + err = clog.Insert(args.Log) + if err != nil { + logger.Logger.Warnf("InsertMonitorData(%v) error:%v", args.Name, err) + return + } + *ret = true + return +} + +func (svc *MonitorDataSvc) UpsertMonitorData(args *model.MonitorDataArg, ret *bool) (err error) { + clog := MonitorDataCollection(args.Name) + if clog == nil { + return + } + + var existLog model.MonitorData + err = clog.Find(bson.M{"srvid": args.Log.SrvId, "srvtype": args.Log.SrvType, "key": args.Log.Key}).One(&existLog) + if err == nil { + args.Log.LogId = existLog.LogId + err = clog.Update(bson.M{"_id": args.Log.LogId}, args.Log) + if err != nil { + logger.Logger.Warnf("UpsertMonitorData(%v) Update error:%v", args.Name, err) + return + } + } else { + if err == mgo.ErrNotFound { + err = clog.Insert(args.Log) + if err != nil { + logger.Logger.Warnf("UpsertMonitorData(%v) Insert error:%v", args.Name, err) + return + } + } + } + *ret = true + return +} + +func (svc *MonitorDataSvc) RemoveMonitorData(t time.Time, changes *[]*mgo.ChangeInfo) (err error) { + s := mongo.MgoSessionMgrSington.GetGlobal(model.MonitorDBName) + if s != nil { + db := s.DB() + if db != nil { + cnames, err := db.CollectionNames() + if err == nil { + if len(cnames) != 0 { + for _, name := range cnames { + c, _ := db.C(name) + if c != nil { + chginfo, err := c.RemoveAll(bson.M{"time": bson.M{"$lt": t}}) + if err == nil { + *changes = append(*changes, chginfo) + } + } + } + } + } + } + } + return +} + +func init() { + //gob registe + // gob:go语言特有的编解码工具,Register需要在初始化时或提前调用 + // rpc编解码依赖gob,rpc需要知道怎么编解码接口数据(客户端服务端都需要知道),以下结构体在rpc参数中被转成了interface,所以这里需要告诉gob接口类型的具体结构 + gob.Register(model.PlayerOLStats{}) + gob.Register(map[string]*model.APITransactStats{}) + gob.Register(api.ApiStats{}) // 这里依赖了mgrsrv的结构体,会导致mgrsrv初始化,最好是把结构体放到一个公共包里,不要两个不相关的程序有直接依赖 + gob.Register(map[string]api.ApiStats{}) + gob.Register(mgo.Stats{}) + gob.Register(profile.TimeElement{}) + gob.Register(map[string]profile.TimeElement{}) + gob.Register(netlib.ServiceStats{}) + gob.Register(map[int]netlib.ServiceStats{}) + gob.Register(schedule.TaskStats{}) + gob.Register(map[string]schedule.TaskStats{}) + gob.Register(transact.TransStats{}) + gob.Register(map[int]transact.TransStats{}) + gob.Register(utils.PanicStackInfo{}) + gob.Register(map[string]utils.PanicStackInfo{}) + gob.Register(basic.CmdStats{}) + gob.Register(map[string]basic.CmdStats{}) + gob.Register(utils.RuntimeStats{}) + //gob registe + + rpc.Register(new(MonitorDataSvc)) +} diff --git a/dbproxy/svc/u_account.go b/dbproxy/svc/u_account.go new file mode 100644 index 0000000..ae8cdab --- /dev/null +++ b/dbproxy/svc/u_account.go @@ -0,0 +1,558 @@ +package svc + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "fmt" + "io" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/common" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + AccountDBName = "user" + AccountCollName = "user_account" + AccountDelBackupCollName = "user_account_del_backup" + ErrAccDBNotOpen = model.NewDBError(AccountDBName, AccountCollName, model.NOT_OPEN) +) + +func AccountCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, AccountDBName) + if s != nil { + c, first := s.DB().C(AccountCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"username", "tagkey"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"tel", "tagkey"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"registerts"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func AccountDelBackupCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, AccountDBName) + if s != nil { + c, _ := s.DB().C(AccountDelBackupCollName) + return c + } + return nil +} + +type AccountSvc struct { +} + +func (svc *AccountSvc) AccountIsExist(args *model.AccIsExistArg, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + ret.Tag = 0 + return ErrAccDBNotOpen + } + + acc := &model.Account{} + + switch args.LoginType { + case common.LoginTypeGuest: + //游客登录 + err := caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + if errors.Is(err, mgo.ErrNotFound) { + ret.Tag = common.LoginNew + return nil + } + return err + } + raw := fmt.Sprintf("%v%v%v", acc.PassWord, common.GetAppId(), args.Ts) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + if pwd != args.Password { + logger.Logger.Warnf("Password is error:%v raw:%v get:%v expect:%v", acc.AccountId, raw, args.Password, pwd) + ret.Tag = common.LoginPasswordError + return nil + } + + case common.LoginTypeAccount: + //帐号登录 + err := caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + logger.Logger.Info("Tel or Password is error:", err) + ret.Tag = common.LoginPasswordError + return nil + } + rawt := fmt.Sprintf("%v%v%v", acc.TelPassWord, common.GetAppId(), args.Ts) + ht := md5.New() + io.WriteString(ht, rawt) + pwdt := hex.EncodeToString(ht.Sum(nil)) + if pwdt != args.Password { + logger.Logger.Warnf("Password is error:%v raw:%v get:%v expect:%v", acc.AccountId, rawt, args.Password, pwdt) + ret.Tag = common.LoginPasswordError + return nil + } + + case common.LoginTypeTelCode: + err := caccounts.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(acc) + if err != nil { + if errors.Is(err, mgo.ErrNotFound) { + ret.Tag = common.LoginNew + return nil + } + return err + } + + if args.CodeValid { + // 刷新密码和有效期 + raw := fmt.Sprintf("%v%v", bson.NewObjectId().Hex(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + acc.TelPassWord = pwd + acc.TelPassWordExpire = time.Now().AddDate(0, 0, common.TelLoginValidDays).Unix() + + err = caccounts.UpdateId(acc.AccountId, acc) + if err != nil { + return err + } + } else { + // 验证密码和有效期 + rawt := fmt.Sprintf("%v%v%v", acc.TelPassWord, common.GetAppId(), args.Ts) + ht := md5.New() + io.WriteString(ht, rawt) + pwdt := hex.EncodeToString(ht.Sum(nil)) + if pwdt != args.Password { + logger.Logger.Warnf("Password is error:%v raw:%v get:%v expect:%v", acc.AccountId, rawt, args.Password, pwdt) + ret.Tag = common.LoginPasswordError + return nil + } + // 判断是否过期 + if time.Now().Unix()-acc.TelPassWordExpire > 0 { // 过期 + logger.Logger.Tracef("TelPassWordExpire AccountId:%v", acc.AccountId) + ret.Tag = common.LoginTelCodeExpire + return nil + } + } + + case common.LoginTypeTelegram: + // telegram + err := caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + if errors.Is(err, mgo.ErrNotFound) { + ret.Tag = common.LoginNew + return nil + } + return err + } + if args.VerifyToken { + if acc.PassWord != args.Password { + logger.Logger.Warnf("Password is error:%v get:%v ", acc.AccountId, args.Password) + ret.Tag = common.LoginPasswordError + return nil + } + } + + case common.LoginTypeGoogle: + err := caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + if errors.Is(err, mgo.ErrNotFound) { + ret.Tag = common.LoginNew + return nil + } + return err + } + // 验证 + + case common.RegisterTypeTel: + //帐号注册,验证手机号 + err := caccounts.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(acc) + if err != nil { + + } else { + //手机号已经注册 + ret.Tag = common.LoginTelExist + return nil + } + + //帐号注册,验证游客帐号 + err = caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + //设备号没有注册 + //logger.Logger.Info("Username is not Exist:", err) + ret.Tag = common.RegisterNotExist + return nil + } else { + //设备号已经注册 + //logger.Logger.Info("Username is Exist:", err) + ret.Tag = common.RegisterExist + return nil + } + + case common.RegisterTypeName: + //账号注册 不需要验证手机号 + //帐号注册,验证游客帐号 + err := caccounts.Find(bson.M{"username": args.Username, "tagkey": args.TagKey}).One(acc) + if err != nil { + //设备号没有注册 + //logger.Logger.Info("Username is not Exist:", err) + ret.Tag = common.RegisterNotExist + return nil + } else { + //设备号已经注册 + //logger.Logger.Info("Username is Exist:", err) + ret.Tag = common.RegisterExist + return nil + } + + default: + ret.Tag = common.LoginTypeNoExist + return nil + } + + // freeze + if acc.State > time.Now().Unix() { + ret.Tag = common.LoginFreeze + ret.Acc = acc + return nil + } + // ok + ret.Tag = common.LoginOk + ret.Acc = acc + return nil +} + +func (svc *AccountSvc) AccountTelIsRegiste(args *model.AccIsExistArg, exist *bool) error { + caccounts := AccountCollection(args.Platform) + if caccounts != nil { + err := caccounts.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(nil) + if err == nil { + *exist = true + } + } + + *exist = false + return nil +} + +func (svc *AccountSvc) InsertAccount(args *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + id, err := GetOnePlayerIdFromBucket() + if err == nil { + args.SnId = id + } + + err = caccounts.Insert(args) + if err != nil { + logger.Logger.Info("InsertAccount error:", err) + return nil + } + + ret.Acc = args + ret.Tag = common.InsertAccountOk + return nil +} + +func (svc *AccountSvc) InsertTelAccount(args *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + id, err := GetOnePlayerIdFromBucket() + if err == nil { + args.SnId = id + } + + err = caccounts.Insert(args) + if err != nil { + logger.Logger.Info("InsertAccount error:", err) + return nil + } + + ret.Acc = args + ret.Tag = common.InsertAccountOk + return nil +} + +func (svc *AccountSvc) UpgradeAccount(args *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + acc := &model.Account{} + err := caccounts.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(acc) + if err == nil && acc.AccountId != args.AccountId { + return fmt.Errorf("tel used account %v", acc.AccountId.Hex()) + } + if acc.Tel != "" { + return errors.New("Tel is Bind") + } + err = caccounts.Update(bson.M{"_id": args.AccountId}, bson.D{{"$set", bson.D{{"tel", args.Tel}, {"telpassword", args.PassWord}}}}) + if err != nil { + logger.Logger.Info("UpgradeAccount error ", err) + } + + return err +} + +func (svc *AccountSvc) BindTelAccount(args *model.BindTelAccountArgs, ret *model.BindTelAccountRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + ret.Code = 1 + + acc := &model.Account{} + err := caccounts.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(acc) + if err == nil { + return fmt.Errorf("tel used account %v", acc.AccountId.Hex()) + } + if !errors.Is(err, mgo.ErrNotFound) { + return err + } + if acc.Tel != "" { + return errors.New("Tel is Bind") + } + err = caccounts.Update(bson.M{"_id": bson.ObjectIdHex(args.AccId)}, bson.D{{"$set", bson.D{{"tel", args.Tel}}}}) + if err != nil { + logger.Logger.Info("BindTelAccount error ", err) + return err + } + + ret.Acc = acc + ret.Code = 0 + + return err +} + +func (svc *AccountSvc) LogoutAccount(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + return caccounts.Update(bson.M{"_id": acc.AccountId}, bson.D{{"$set", bson.D{{"logintimes", acc.LoginTimes}, {"lastlogouttime", acc.LastLogoutTime}}}}) +} + +func (svc *AccountSvc) FreezeAccount(args *model.AccFreezeArg, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + endTime := time.Now().Unix() + int64(time.Minute.Seconds())*int64(args.FreezeTime) + return caccounts.Update(bson.M{"_id": bson.ObjectIdHex(args.AccId)}, bson.D{{"$set", bson.D{{"state", endTime}}}}) +} + +func (svc *AccountSvc) UnfreezeAccount(args *model.AccFreezeArg, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + return caccounts.Update(bson.M{"_id": bson.ObjectIdHex(args.AccId)}, bson.D{{"$set", bson.D{{"state", 0}}}}) +} + +func (svc *AccountSvc) getAccount(plt, accId string) (*model.Account, error) { + caccounts := AccountCollection(plt) + if caccounts == nil { + return nil, ErrAccDBNotOpen + } + + var acc model.Account + err := caccounts.Find(bson.M{"_id": bson.ObjectIdHex(accId)}).One(&acc) + if err != nil { + return nil, err + } + + return &acc, err +} + +func (svc *AccountSvc) getAccountBySnId(plt string, snid int32) (*model.Account, error) { + caccounts := AccountCollection(plt) + if caccounts == nil { + return nil, ErrAccDBNotOpen + } + + var acc model.Account + err := caccounts.Find(bson.M{"snid": snid}).One(&acc) + if err != nil { + return nil, err + } + + return &acc, err +} + +func (svc *AccountSvc) GetAccountBySnId(args *model.UserSnidArg, ret *model.AccRet) error { + var err error + ret.Acc, err = svc.getAccountBySnId(args.Platform, args.Snid) + return err +} + +func (svc *AccountSvc) getAccountByUserName(plt, username string) (*model.Account, error) { + caccounts := AccountCollection(plt) + if caccounts == nil { + return nil, ErrAccDBNotOpen + } + + var acc model.Account + err := caccounts.Find(bson.M{"username": username}).One(&acc) + if err != nil { + return nil, err + } + + return &acc, err +} +func (svc *AccountSvc) GetAccountByUserName(args *model.UserNameArg, ret *model.AccRet) error { + var err error + ret.Acc, err = svc.getAccountByUserName(args.Platform, args.UserName) + return err +} + +func (svc *AccountSvc) GetAccount(args *model.AccIdArg, ret *model.AccRet) error { + var err error + ret.Acc, err = svc.getAccount(args.Platform, args.AccId) + return err +} + +// 删除账号 +func (svc *AccountSvc) RemoveAccount(args *model.AccIdArg, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + return caccounts.RemoveId(bson.ObjectIdHex(args.AccId)) +} + +// pwd暂时用明文.需要使用MD5加密。参照task_login.go +func (svc *AccountSvc) EditAccountPwd(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + hashsum := model.GenAccountPwd(acc.PassWord) + return caccounts.Update(bson.M{"_id": acc.AccountId}, bson.D{{"$set", bson.D{{"backpassword", acc.TelPassWord}, {"telpassword", hashsum}}}}) +} + +func (svc *AccountSvc) ResetBackAccountPwd(args *model.AccIdArg, ret *model.AccRet) error { + caccounts := AccountCollection(args.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + var acc model.Account + err := caccounts.Find(bson.M{"_id": bson.ObjectIdHex(args.AccId)}).One(&acc) + if err != nil { + return err + } + if len(acc.BackPassWord) > 0 { + return caccounts.Update(bson.M{"_id": acc.AccountId}, bson.D{{"$set", bson.D{{"telpassword", acc.BackPassWord}}}}) + } + return nil +} + +/* + * 修改帐号密码 + */ +func (svc *AccountSvc) UpdatePlayerPassword(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + return caccounts.Update(bson.M{"_id": acc.AccountId, "telpassword": acc.BackPassWord}, bson.D{{"$set", bson.D{{"telpassword", acc.PassWord}}}}) +} + +/* + * 修改Token帐号密码 + */ +func (svc *AccountSvc) UpdatePlayerTokenPassword(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + return caccounts.Update(bson.M{"_id": acc.AccountId}, bson.D{{"$set", bson.D{{"password", acc.PassWord}}}}) +} + +/* + * 找回密码 + */ +func (svc *AccountSvc) GetBackPlayerPassword(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + return caccounts.Update(bson.M{"tel": acc.Tel, "tagkey": acc.TagKey}, bson.D{{"$set", bson.D{{"telpassword", acc.PassWord}}}}) +} +func (svc *AccountSvc) UpdateAccountPlatformInfo(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + return caccounts.Update(bson.M{"_id": acc.AccountId}, + bson.D{{"$set", bson.M{"platform": acc.Platform, + "channel": acc.Channel, "promoter": acc.Promoter, "inviterid": acc.InviterId, "packegetag": acc.PackegeTag, "promotertree": acc.PromoterTree}}}) +} + +func (svc *AccountSvc) GetRobotAccounts(limit int, accs *[]model.Account) error { + caccounts := AccountCollection(mongo.G_P) + if caccounts == nil { + return nil + } + *accs = make([]model.Account, 0, limit) + err := caccounts.Find(bson.M{"platform": common.Platform_Rob}).Limit(limit).All(&accs) + if err != nil { + logger.Logger.Info("GetAllRobotAccounts error:", err) + return nil + } + return nil +} + +func (svc *AccountSvc) SaveToDelBackupAccount(acc *model.Account, ret *model.AccRet) error { + cDelBackup := AccountDelBackupCollection(acc.Platform) + if cDelBackup == nil { + return ErrAccDBNotOpen + } + + _, err := cDelBackup.Upsert(bson.M{"_id": acc.AccountId}, acc) + if err != nil { + logger.Logger.Info("InsertDelBackupAccount error:", err) + } + + return err +} + +func (svc *AccountSvc) UpdateAccount(acc *model.Account, ret *model.AccRet) error { + caccounts := AccountCollection(acc.Platform) + if caccounts == nil { + return ErrAccDBNotOpen + } + + err := caccounts.UpdateId(acc.AccountId, acc) + if err != nil { + logger.Logger.Info("UpdateAccount error:", err) + } + + return err +} + +var _AccountSvc = &AccountSvc{} + +func init() { + rpc.Register(_AccountSvc) +} diff --git a/dbproxy/svc/u_actmonitorlist.go b/dbproxy/svc/u_actmonitorlist.go new file mode 100644 index 0000000..0c54b62 --- /dev/null +++ b/dbproxy/svc/u_actmonitorlist.go @@ -0,0 +1,97 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/mongo" + "net/rpc" +) + +var ( + c_actmonitorrec *mongo.Collection + ActMonitorDBName = "user" + ActMonitorCollName = "user_actmonitorlist" + ActMonitorErr = errors.New("user_actmonitorlist log open failed.") +) + +func ActMonitorCollection() *mongo.Collection { + if c_actmonitorrec == nil || !c_actmonitorrec.IsValid() { + c_actmonitorrec = mongo.DatabaseC(ActMonitorDBName, ActMonitorCollName) + if c_actmonitorrec != nil { + c_actmonitorrec.Hold() + c_actmonitorrec.EnsureIndex(mgo.Index{Key: []string{"seqno"}, Background: true, Sparse: true}) + c_actmonitorrec.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_actmonitorrec.EnsureIndex(mgo.Index{Key: []string{"createtime"}, Background: true, Sparse: true}) + c_actmonitorrec.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + } + return c_actmonitorrec +} + +type ActMonitorSvc struct { +} + +func (svc *ActMonitorSvc) GetAllActMonitorData(id int, ret *model.ActMonitoRet) error { + cat := ActMonitorCollection() + if cat == nil { + return nil + } + err := cat.Find(bson.M{}).All(&ret.Data) + if err != nil { + logger.Logger.Error("Get model.ActMonitor data eror.", err) + ret.Err = err + return err + } + return nil +} +func (svc *ActMonitorSvc) UpsertSignleActMonitor(am *model.ActMonitor, ret *model.ActMonitoRet) (err error) { + cat := ActMonitorCollection() + if cat == nil { + return errors.New("svc.ActMonitor is nil") + } + _, err = cat.Upsert(bson.M{"seqno": am.SeqNo}, am) + if err != nil { + logger.Logger.Warn("svc.UpsertSignleActMonitor error:", err) + ret.Err = err + return + } + return +} + +func (svc *ActMonitorSvc) UpdateSignleActMonitor(am *model.ActMonitor, ret *model.ActMonitoRet) (err error) { + cat := ActMonitorCollection() + if cat == nil { + return errors.New("model.ActMonitor is nil") + } + old := new(model.ActMonitor) + if err = cat.Find(bson.M{"seqno": am.SeqNo}).One(old); err != nil { + logger.Logger.Error("FindSignleActMonitor error:", err) + ret.Err = err + return + } + am.CreateTime = old.CreateTime + if err = cat.Update(bson.M{"seqno": am.SeqNo}, am); err != nil { + logger.Logger.Error("UpdateSignleActMonitor error:", err) + ret.Err = err + } + return +} + +func (svc *ActMonitorSvc) RemoveActMonitorOne(seqno int, ret *model.ActMonitoRet) error { + cat := ActMonitorCollection() + if cat == nil { + return ActMonitorErr + } + ret.Err = cat.Remove(bson.M{"seqno": seqno}) + if ret.Err != nil { + logger.Logger.Error("srv.RemoveActMonitorOne error", ret.Err) + return ret.Err + } + return nil +} +func init() { + rpc.Register(&ActMonitorSvc{}) +} diff --git a/dbproxy/svc/u_bag.go b/dbproxy/svc/u_bag.go new file mode 100644 index 0000000..2c500d1 --- /dev/null +++ b/dbproxy/svc/u_bag.go @@ -0,0 +1,138 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + BagDBName = "user" + BagCollName = "user_bag" + ErrBagDBNotOpen = model.NewDBError(BagDBName, BagCollName, model.NOT_OPEN) +) + +func BagCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, BagDBName) + if s != nil { + c, first := s.DB().C(BagCollName) + if first { + // c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type BagSvc struct { +} + +func (svc *BagSvc) GetBagItem(args *model.GetBagInfoArgs, ret *model.BagInfo) error { + cbag := BagCollection(args.Plt) + if cbag == nil { + return errors.New("cbag is nil") + } + err := cbag.Find(bson.M{"snid": args.SnId, "platform": &args.Plt}).One(ret) + if err != nil && err != mgo.ErrNotFound { + //if err == mgo.ErrNotFound { + // ret = model.NewBagInfo(args.SnId, args.Plt) + // err = cbag.Insert(ret) // 考虑创建失败~ + // logger.Logger.Error("svc.GetBagItem is Insert", err) + // return nil + //} + logger.Logger.Error("svc.GetBagItem is error: ", err) + return nil + } + return nil +} + +func (svc *BagSvc) UpgradeBag(args *model.BagInfo, ret *bool) error { + cbag := BagCollection(args.Platform) + if cbag == nil { + return ErrBagDBNotOpen + } + *ret = true + bag := &model.BagInfo{} + err := cbag.Find(bson.M{"snid": args.SnId}).One(bag) + if err != nil && err != mgo.ErrNotFound { + *ret = false + logger.Logger.Error("UpgradeBag err:", err) + return err + } + args.BagId = bag.BagId + if args.BagId == "" { + args.BagId = bson.NewObjectId() + } + _, err = cbag.Upsert(bson.M{"_id": args.BagId}, args) + if err != nil { + *ret = false + logger.Logger.Info("UpgradeBag error ", err) + } + return err +} + +func (svc *BagSvc) AddBagItem(args *model.BagInfo, ret *bool) error { + cbag := BagCollection(args.Platform) + if cbag == nil { + return ErrBagDBNotOpen + } + *ret = true + bag := &model.BagInfo{} + err := cbag.Find(bson.M{"snid": args.SnId}).One(bag) + if err != nil && err != mgo.ErrNotFound { + *ret = false + logger.Logger.Error("AddBagItem err:", err) + return err + } + + if bag.BagId == "" { + bag.BagId = bson.NewObjectId() + } + for id, v := range args.BagItem { + if item, exist := bag.BagItem[id]; !exist { + bag.BagItem[id] = &model.Item{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + ObtainTime: time.Now().Unix(), + } + } else { + item.ItemNum += v.ItemNum + } + } + _, err = cbag.Upsert(bson.M{"_id": bag.BagId}, bag) + if err != nil { + *ret = false + logger.Logger.Info("AddBagItem error ", err) + } + return err +} + +func (svc *BagSvc) UpdateBag(args *model.BagInfo, ret *bool) error { + cbag := BagCollection(args.Platform) + if cbag == nil { + *ret = false + return ErrBagDBNotOpen + } + + *ret = true + err := cbag.UpdateId(args.BagId, args) + if err != nil { + *ret = false + logger.Logger.Info("UpdateBag error:", err) + } + + return err +} + +var _BagSvc = &BagSvc{} + +func init() { + rpc.Register(_BagSvc) +} diff --git a/dbproxy/svc/u_coinpoolsetting.go b/dbproxy/svc/u_coinpoolsetting.go new file mode 100644 index 0000000..e117446 --- /dev/null +++ b/dbproxy/svc/u_coinpoolsetting.go @@ -0,0 +1,110 @@ +package svc + +import ( + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func CoinPoolSettingCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.CoinPoolSettingDBName) + if s != nil { + c, first := s.DB().C(model.CoinPoolSettingCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"serverid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func CoinPoolSettingHisCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.CoinPoolSettingHisDBName) + if s != nil { + c, first := s.DB().C(model.CoinPoolSettingHisCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"serverid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func GetAllCoinPoolSettingData() (datas []model.CoinPoolSetting, err error) { + c := CoinPoolSettingCollection() + if c != nil { + err = c.Find(nil).All(&datas) + if err != nil { + logger.Logger.Trace("GetAllCoinPoolSettingData err:", err) + return + } + } + return +} + +func UpsertCoinPoolSetting(cps, old *model.CoinPoolSetting) (err error) { + if cps != nil { + c := CoinPoolSettingCollection() + if c != nil { + _, err = c.Upsert(bson.M{"_id": cps.Id}, cps) + if err != nil { + return err + } + } + } + if old != nil { + c := CoinPoolSettingHisCollection() + if c != nil { + old.Id = bson.NewObjectId() + old.UptTime = time.Now() + err = c.Insert(old) + if err != nil { + return err + } + } + } + return +} + +// 删除水池历史调控记录 +func RemoveCoinPoolSettingHis(ts time.Time) (*mgo.ChangeInfo, error) { + return CoinPoolSettingHisCollection().RemoveAll(bson.M{"upttime": bson.M{"$lt": ts}}) +} + +type CoinPoolSettingSvc struct { +} + +func (svc *CoinPoolSettingSvc) GetAllCoinPoolSettingData(args *struct{}, datas *[]model.CoinPoolSetting) (err error) { + *datas, err = GetAllCoinPoolSettingData() + return +} + +func (svc *CoinPoolSettingSvc) UpsertCoinPoolSetting(args *model.UpsertCoinPoolSettingArgs, ret *bool) (err error) { + err = UpsertCoinPoolSetting(args.Cps, args.Old) + if err != nil { + return err + } + *ret = true + return +} + +func (svc *CoinPoolSettingSvc) RemoveCoinPoolSettingHis(args *time.Time, ret *mgo.ChangeInfo) (err error) { + ret, err = RemoveCoinPoolSettingHis(*args) + if err != nil { + return err + } + return +} + +func init() { + rpc.Register(new(CoinPoolSettingSvc)) +} diff --git a/dbproxy/svc/u_friend.go b/dbproxy/svc/u_friend.go new file mode 100644 index 0000000..ed308e2 --- /dev/null +++ b/dbproxy/svc/u_friend.go @@ -0,0 +1,87 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + FriendDBName = "user" + FriendCollName = "user_friend" + FriendColError = errors.New("friend collection open failed") +) + +func FriendCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, FriendDBName) + if s != nil { + c, first := s.DB().C(FriendCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type FriendSvc struct { +} + +func (svc *FriendSvc) UpsertFriend(args *model.Friend, ret *model.FriendRet) error { + fc := FriendCollection(args.Platform) + if fc == nil { + return FriendColError + } + if args.Id == "" { + args.Id = bson.NewObjectId() + } + _, err := fc.Upsert(bson.M{"platform": args.Platform, "snid": args.SnId}, args) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("UpsertFriend is err: ", err) + return err + } + ret.Fri = args + return nil +} + +func (svc *FriendSvc) QueryFriendByKey(args *model.FriendByKey, ret *model.FriendRet) error { + fc := FriendCollection(args.Platform) + if fc == nil { + return FriendColError + } + err := fc.Find(bson.M{"platform": args.Platform, "snid": args.SnId}).One(&ret.Fri) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("QueryFriendByKey is err: ", err) + return err + } + return nil +} +func (svc *FriendSvc) QueryFriendsByKey(args *model.FriendsByKey, ret *model.FriendsRet) error { + fc := FriendCollection(args.Platform) + if fc == nil { + return FriendColError + } + var sql []bson.M + if len(args.SnIds) != 0 { + for _, snid := range args.SnIds { + sql = append(sql, bson.M{"snid": snid}) + } + } + err := fc.Find(bson.M{"$or": sql}).All(&ret.Fris) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("QueryFriendsByKey is err: ", err) + return err + } + return nil +} + +var _FriendSvc = &FriendSvc{} + +func init() { + rpc.Register(_FriendSvc) +} diff --git a/dbproxy/svc/u_gamedata.go b/dbproxy/svc/u_gamedata.go new file mode 100644 index 0000000..c3fe78e --- /dev/null +++ b/dbproxy/svc/u_gamedata.go @@ -0,0 +1,60 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + GameKVDataDBName = "user" + GameKVDataCollName = "user_gamekvdata" +) + +func GameKVDatasCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, GameKVDataDBName) + if s != nil { + c, first := s.DB().C(GameKVDataCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"key"}, Unique: true, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type GameKVDataSvc struct { +} + +func (svc *GameKVDataSvc) GetAllGameKVData(dummy struct{}, ret *[]*model.GameKVData) error { + c := GameKVDatasCollection() + if c != nil { + err := c.Find(nil).All(ret) + if err != nil { + logger.Logger.Trace("GetAllGameKVData err:", err) + return err + } + } + return nil +} + +func (svc *GameKVDataSvc) UptGameKVData(args *model.GameKVData, ret *bool) error { + c := GameKVDatasCollection() + if c != nil { + info, err := c.Upsert(bson.M{"key": args.Key}, args) + logger.Logger.Trace("UptGameKVData :", info, err) + return err + } + + *ret = true + return nil +} + +var _GameKVDataSvc = &GameKVDataSvc{} + +func init() { + rpc.Register(_GameKVDataSvc) +} diff --git a/dbproxy/svc/u_horseracelamp.go b/dbproxy/svc/u_horseracelamp.go new file mode 100644 index 0000000..50f9f46 --- /dev/null +++ b/dbproxy/svc/u_horseracelamp.go @@ -0,0 +1,128 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ErrHorseRaceLampDBNotOpen = model.NewDBError(model.HorseRaceLampDBName, model.HorseRaceLampCollName, model.NOT_OPEN) + +func HorseRaceLampCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.HorseRaceLampDBName) + if s != nil { + c, first := s.DB().C(model.HorseRaceLampCollName) + if first { + // c.EnsureIndex(mgo.Index{Key: []string{"username", "tagkey"}, Background: true, Sparse: true}) + // c.EnsureIndex(mgo.Index{Key: []string{"tel", "tagkey"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type HorseRaceLampSvc struct { +} + +var horseRaceLampSvc = &HorseRaceLampSvc{} + +func (svc *HorseRaceLampSvc) GetAllHorseRaceLamp(args string, ret *[]model.HorseRaceLamp) error { + logger.Logger.Info("HorseRaceLampSvc GetAllHorseRaceLamp") + + c := HorseRaceLampCollection(args) + if c != nil { + err := c.Find(bson.M{}).All(ret) + if err != nil { + return err + } + } + return nil +} +func (svc *HorseRaceLampSvc) GetHorseRaceLamp(args *model.GetHorseRaceLampArgs, ret *model.HorseRaceLamp) (err error) { + c := HorseRaceLampCollection(args.Platform) + if c == nil { + return ErrHorseRaceLampDBNotOpen + } + err = c.Find(bson.M{"_id": args.Id}).One(ret) + return +} +func (svc *HorseRaceLampSvc) RemoveHorseRaceLamp(args *model.RemoveHorseRaceLampArg, ret *bool) error { + logger.Logger.Info("HorseRaceLampSvc RemoveHorseRaceLamp") + c := HorseRaceLampCollection(args.Platform) + if c != nil { + _, err := c.RemoveAll(bson.M{"_id": bson.ObjectIdHex(args.Key)}) + if err != nil { + return err + } + *ret = true + } + return nil +} +func (svc *HorseRaceLampSvc) InsertHorseRaceLamp(args *model.InsertHorseRaceLampArgs, ret *bool) (err error) { + c := HorseRaceLampCollection(args.Platform) + if c == nil { + return ErrHorseRaceLampDBNotOpen + } + switch len(args.HorseRaceLamps) { + case 1: + err = c.Insert(args.HorseRaceLamps[0]) + default: + docs := make([]interface{}, 0, len(args.HorseRaceLamps)) + for _, notice := range args.HorseRaceLamps { + docs = append(docs, notice) + } + err = c.Insert(docs...) + } + if err != nil { + logger.Logger.Error("InsertHorseRaceLamps error:", err) + return + } + *ret = true + return +} +func (svc *HorseRaceLampSvc) EditHorseRaceLamp(args *model.EditHorseRaceLampArg, ret *bool) error { + logger.Logger.Info("HorseRaceLampSvc EditHorseRaceLamp") + c := HorseRaceLampCollection(args.Platform) + if c != nil { + if !bson.IsObjectIdHex(args.Key) { + return errors.New("key is invalid bson.ObjectId") + } + Id := bson.ObjectIdHex(args.Key) + _, err := c.Upsert(bson.M{"_id": Id}, bson.D{{"$set", bson.D{{"channel", args.Channel}, + {"title", args.Title}, {"content", args.Content}, {"footer", args.Footer}, + {"starttime", args.StartTime}, {"interval", args.Interval}, {"count", args.Count}, + {"priority", args.Priority}, {"msgtype", args.MsgType}, {"platform", args.Platform}, {"state", args.State}, + {"target", args.Target}, {"standsec", args.StandSec}}}}) + if err != nil { + logger.Logger.Warn("EditHorseRaceLamp to db failed.", err) + return err + } + *ret = true + } + return nil +} +func (svc *HorseRaceLampSvc) GetHorseRaceLampInRangeTsLimitByRange(args *model.QueryHorseRaceLampArg, ret *model.QueryHorseRaceLampRet) (err error) { + logger.Logger.Info("HorseRaceLampSvc EditHorseRaceLamp") + c := HorseRaceLampCollection(args.Platform) + if c == nil { + return ErrHorseRaceLampDBNotOpen + } + if len(args.Platform) == 0 { + ret.Count, _ = c.Find(bson.M{"state": args.State, "msgtype": args.MsgType}).Count() + } else { + ret.Count, _ = c.Find(bson.M{"platform": args.Platform, "state": args.State, "msgtype": args.MsgType}).Count() + } + if len(args.Platform) == 0 { + err = c.Find(bson.M{"state": args.State, "msgtype": args.MsgType}).Sort("createtime").Skip(args.FromIndex).Limit(args.LimitDataNum).All(&ret.Data) + } else { + selector := bson.M{"platform": args.Platform, "state": args.State, "msgtype": args.MsgType} + err = c.Find(selector).Sort("createtime").Skip(args.FromIndex).Limit(args.LimitDataNum).All(&ret.Data) + } + return +} +func init() { + rpc.Register(horseRaceLampSvc) +} diff --git a/dbproxy/svc/u_invitecode.go b/dbproxy/svc/u_invitecode.go new file mode 100644 index 0000000..d2f0beb --- /dev/null +++ b/dbproxy/svc/u_invitecode.go @@ -0,0 +1,110 @@ +package svc + +import ( + "bytes" + "errors" + "math/rand" + "net/rpc" + "sync" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +/* + 邀请码生成规则 + 1. 邀请码长度为8位,由数字和字母组成 + 2. 0-9,a-z,A-Z随机组合 +*/ + +var ( + InviteCodeDBName = "user" + InviteCodeCollName = "user_invitecode" + InviteCodeColError = errors.New("InviteCode collection open failed") + InviteCodeMutex = sync.Mutex{} +) + +func InviteCodeCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, InviteCodeDBName) + if s != nil { + c, first := s.DB().C(InviteCodeCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Unique: true, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"code"}, Unique: true, Background: true, Sparse: true}) + } + return c + } + + return nil +} + +func GetInviteCode(plt string, snid int32) (string, error) { + c := InviteCodeCollection(plt) + if c == nil { + return "", InviteCodeColError + } + + col := new(model.InviteCode) + err := c.Find(bson.M{"snid": snid}).One(col) + notFound := errors.Is(err, mgo.ErrNotFound) + if err != nil && !notFound { + return "", err + } + + if notFound || col.Code == "" { + InviteCodeMutex.Lock() + defer InviteCodeMutex.Unlock() + // 创建 + var n int + for i := 0; i < 100; i++ { + code := getInviteCode(8) + n, err = c.Find(bson.M{"code": code}).Count() + if err != nil { + return "", err + } + if n > 0 { + continue + } + col.Id = bson.NewObjectId() + col.SnId = snid + col.Code = code + _, err = c.Upsert(bson.M{"snid": snid}, col) + break + } + } + + return col.Code, err +} + +const StringList = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func getInviteCode(n int) string { + b := bytes.NewBuffer(make([]byte, 0, n)) + for i := 0; i < n; i++ { + b.WriteByte(StringList[rand.Intn(len(StringList))]) + } + return b.String() +} + +type InviteCodeSvc struct { +} + +func (i *InviteCodeSvc) GetSnIdByCode(req *model.InviteSnIdReq, ret *model.InviteSnIdRet) error { + c := InviteCodeCollection(req.Platform) + if c == nil { + return InviteCodeColError + } + col := new(model.InviteCode) + err := c.Find(bson.M{"code": req.Code}).One(col) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + return err + } + ret.SnId = col.SnId + return nil +} + +func init() { + rpc.Register(new(InviteCodeSvc)) +} diff --git a/dbproxy/svc/u_invitescore.go b/dbproxy/svc/u_invitescore.go new file mode 100644 index 0000000..927913d --- /dev/null +++ b/dbproxy/svc/u_invitescore.go @@ -0,0 +1,325 @@ +package svc + +import ( + "errors" + "net/rpc" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + rankproto "mongo.games.com/game/protocol/rank" +) + +var ( + InviteScoreDBName = "user" + InviteScoreCollName = "user_invitescore" + InviteScoreColError = errors.New("InviteScore collection open failed") +) + +func InviteScoreCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, InviteScoreDBName) + if s != nil { + c, first := s.DB().C(InviteScoreCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"invitesnid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"tp"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true}) + } + return c + } + + return nil +} + +type BindScoreSvc struct { +} + +func (b *BindScoreSvc) GetInviteScore(req *model.InviteScoreReq, ret *model.InviteScoreRet) error { + c := InviteScoreCollection(req.Platform) + if c == nil { + return InviteScoreColError + } + + type M struct { + Score int64 + } + + var tc []M + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "invitesnid": req.SnId, + }}, + {"$group": bson.M{ + "_id": nil, + "score": bson.M{"$sum": "$score"}, + }}, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("GetInviteScore AllowDiskUse is error", err) + return err + } + + if len(tc) > 0 { + ret.Score = tc[0].Score + c := PlayerDataCollection(req.Platform) + if c == nil { + return PlayerColError + } + err = c.Update(bson.M{"snid": req.SnId}, bson.M{"$set": bson.M{"invitescore": tc[0].Score}}) + if err != nil { + logger.Logger.Error("GetInviteScore update invitescore is error", err) + return err + } + } + + tc = tc[:0] + err = c.Pipe([]bson.M{ + {"$match": bson.M{ + "invitesnid": req.SnId, + "score": bson.M{"$gt": 0}, + }}, + {"$group": bson.M{ + "_id": nil, + "score": bson.M{"$sum": "$score"}, + }}, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("GetInviteScore z AllowDiskUse is error", err) + return err + } + + if len(tc) > 0 { + ret.ZScore = tc[0].Score + } + + return nil +} + +func (b *BindScoreSvc) SaveInviteScore(req *model.InviteScore, ret *bool) error { + logger.Logger.Tracef("SaveInviteScore req:%+v", *req) + if req.InviteSnId == 0 { + return nil + } + req.Id = bson.NewObjectId() + u := PlayerDataCollection(req.Platform) + if u == nil { + return PlayerColError + } + + type M struct { + InviteScore int64 + } + r := new(M) + err := u.Find(bson.M{"snid": req.InviteSnId}).Select(bson.M{"invitescore": 1}).One(r) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Errorf("GetInviteScore Find error:%v", err) + return err + } + + if req.Score < 0 { + if -req.Score > r.InviteScore { + req.Score = -r.InviteScore + } + } + + c := InviteScoreCollection(req.Platform) + if c == nil { + return InviteScoreColError + } + + err = c.Insert(req) + if err != nil { + logger.Logger.Errorf("SaveInviteScore Insert error:%v", err) + return err + } + + err = u.Update(bson.M{"snid": req.InviteSnId}, bson.M{"$inc": bson.M{"invitescore": req.Score}}) + if err != nil { + logger.Logger.Errorf("inc InviteScore error:%v", err) + return err + } + + *ret = true + return nil +} + +func (b *BindScoreSvc) GetInviteRankList(req *model.FindPlayerRankInviteListArgs, ret *model.FindPlayerRankInviteListReply) error { + c := InviteScoreCollection(req.Platform) + if c == nil { + return InviteScoreColError + } + + NowTime := time.Now().Local() + endTime := NowTime.UnixNano() + startTime := NowTime.AddDate(-100, 0, 0).UnixNano() + + year, month, day := NowTime.Date() + today := time.Date(year, month, day, 0, 0, 0, 0, time.Local) + if req.RankType == int32(rankproto.RankInvite_InviteType_Week) { + // 本周起始日期(周日) + startTime = today.AddDate(0, 0, -int(today.Weekday())).UnixNano() + } else if req.RankType == int32(rankproto.RankInvite_InviteType_Month) { + // 本月起始日期 + startTime = time.Date(year, month, 1, 0, 0, 0, 0, time.Local).UnixNano() + //startTime = NowTime.AddDate(0, 0, -NowTime.Day()+1).UnixNano() + } + + type M struct { + InviteSnId int32 // 邀请人id + Score int64 // 积分 + } + + var tc []M + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "score": bson.M{"$gt": 0}, + "ts": bson.M{"$gte": startTime, "$lte": endTime}, + }}, + {"$group": bson.M{ + "_id": bson.M{ + "invitesnid": "$invitesnid", + }, + "invitesnid": bson.M{"$first": "$invitesnid"}, + "score": bson.M{"$sum": "$score"}, + }}, + { + "$sort": bson.M{"ts": -1}, + }, + { + "$sort": bson.M{"score": -1}, + }, + { + "$limit": model.GameParamData.RankInviteMaxNum, + }, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("GetInviteRankList z AllowDiskUse is error", err) + return err + } + + var conds []int32 + if len(tc) > 0 { + + type PInfo struct { + SnId int32 + Name string // 昵称 + Roles *model.RolePetInfo + } + + var retPlayerList []PInfo + + cplayerdata := PlayerDataCollection(req.Platform) + if cplayerdata == nil { + return err + } + + for i := 0; i < len(tc); i++ { + conds = append(conds, tc[i].InviteSnId) + } + + selecter := bson.M{"snid": bson.M{"$in": conds}} + //err = cplayerdata.Find(selecter).Select(bson.M{"snid": 1, "name": 1, "roles": 1}).All(&retPlayerList) + err = cplayerdata.Find(selecter).Select(bson.M{"snid": 1, "name": 1, "roles": 1}).All(&retPlayerList) + if err != nil { + logger.Logger.Error("svc.FindInvitePlayerList is error", err) + return nil + } + + for _, inviteInfo := range tc { + for _, playerData := range retPlayerList { + if inviteInfo.InviteSnId == playerData.SnId { + var RankInvite model.PlayerRankInvite + RankInvite.Name = playerData.Name + RankInvite.Score = inviteInfo.Score + RankInvite.SnId = inviteInfo.InviteSnId + + // 头像模型ID + roleId := common.DefaultRoleId + if playerData.Roles != nil { + roleId = int(playerData.Roles.ModId) + } + RankInvite.ModId = int32(roleId) + + RankInvite.InviteNum, _ = GetInviteNum(req.Platform, inviteInfo.InviteSnId) + ret.List = append(ret.List, &RankInvite) + + break + } + } + } + ret.RankType = req.RankType + } + + return nil +} + +func (b *BindScoreSvc) GetInviteList(req *model.InviteLisArgs, ret *model.InviteListRet) error { + c := InviteScoreCollection(req.Platform) + if c == nil { + return InviteScoreColError + } + + type M struct { + SnId int32 + Score int64 + } + + var tc []M + err := c.Pipe([]bson.M{ + {"$match": bson.M{ + "invitesnid": req.SnId, + }}, + {"$group": bson.M{ + "_id": bson.M{ + "snid": "$snid", + }, + "snid": bson.M{"$first": "$snid"}, + "score": bson.M{"$sum": "$score"}, + }}, + { + "$sort": bson.M{ + "score": -1, + }, + }, + }).AllowDiskUse().All(&tc) + if err != nil { + logger.Logger.Error("GetInviteList AllowDiskUse is error", err) + return err + } + + u := PlayerDataCollection(req.Platform) + if u == nil { + return PlayerColError + } + + for _, v := range tc { + d := &model.PlayerBaseInfo2{} + err = u.Find(bson.M{"snid": v.SnId}).Select(bson.M{"name": 1, "createtime": 1, "roles": 1}).One(d) + if err != nil { + logger.Logger.Warnf("GetInviteList Find player is error:%v", err) + } + roleId := common.DefaultRoleId + if d.Roles != nil && d.Roles.ModId != 0 { + roleId = int(d.Roles.ModId) + } + ret.List = append(ret.List, &model.InviteInfo{ + Name: d.Name, + SnId: v.SnId, + CreateTs: d.CreateTime.Unix(), + Score: v.Score, + ModId: int32(roleId), + }) + } + + return nil +} + +func init() { + rpc.Register(new(BindScoreSvc)) +} diff --git a/dbproxy/svc/u_lottery.go b/dbproxy/svc/u_lottery.go new file mode 100644 index 0000000..e74b905 --- /dev/null +++ b/dbproxy/svc/u_lottery.go @@ -0,0 +1,65 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + LotteryDBErr = errors.New("user_coinlog db open failed.") +) + +func LotteryCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.LotteryDBName) + if s != nil { + c, first := s.DB().C(model.LotteryCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func GetAllLottery() (ret []model.Lottery, err error) { + err = LotteryCollection().Find(nil).All(&ret) + return +} + +func UpsertLottery(item *model.Lottery) (err error) { + c := LotteryCollection() + if c == nil { + return + } + _, err = c.UpsertId(item.Id, item) + if err != nil { + logger.Logger.Warn("UpsertLottery error:", err) + return + } + return +} + +type LotterySvc struct { +} + +func (svc *LotterySvc) GetAllLottery(args struct{}, ret *[]model.Lottery) (err error) { + *ret, err = GetAllLottery() + return +} + +func (svc *LotterySvc) UpsertLottery(args *model.Lottery, ret *bool) (err error) { + err = UpsertLottery(args) + if err == nil { + *ret = true + } + return +} + +func init() { + rpc.Register(new(LotterySvc)) +} diff --git a/dbproxy/svc/u_message.go b/dbproxy/svc/u_message.go new file mode 100644 index 0000000..74f713f --- /dev/null +++ b/dbproxy/svc/u_message.go @@ -0,0 +1,235 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +func MessageCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.MessageDBName) + if s != nil { + c, first := s.DB().C(model.MessageCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"state"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type MessageSvc struct { +} + +var _MessageSvc = &MessageSvc{} + +func init() { + rpc.Register(_MessageSvc) +} + +func (svc *MessageSvc) GetMessage(args *model.GetMessageArgs, ret *[]model.Message) error { + logger.Logger.Info("MessageSvc GetMessage") + c := MessageCollection(args.Plt) + if c != nil { + var sql []bson.M + sql = append(sql, bson.M{"state": 0}) + sql = append(sql, bson.M{"state": 1}) + err := c.Find(bson.M{"snid": args.SnId, "$or": sql}).All(ret) + return err + } + return nil +} + +func (svc *MessageSvc) GetNotDelMessage(args *model.GetMessageArgs, ret *[]model.Message) error { + logger.Logger.Info("MessageSvc GetMessage") + c := MessageCollection(args.Plt) + if c != nil { + err := c.Find(bson.M{"snid": args.SnId, "state": bson.M{"$ne": model.MSGSTATE_REMOVEED}}).All(ret) + return err + } + return nil +} + +func (svc *MessageSvc) GetSubscribeMessage(args string, ret *[]model.Message) error { + logger.Logger.Info("MessageSvc GetSubscribeMessage") + c := MessageCollection(args) + if c != nil { + err := c.Find(bson.M{"snid": 0}).All(ret) + return err + } + return nil +} +func (svc *MessageSvc) GetMessageInRangeTsLimitByRange(args *model.MsgArgs, ret *model.RetMsg) error { + logger.Logger.Info("MessageSvc GetMessageInRangeTsLimitByRange") + c := MessageCollection(args.Platform) + if c == nil { + return errors.New("c == nil") + } + limitDataNum := args.ToIndex - args.FromIndex + if limitDataNum < 0 { + limitDataNum = 0 + } + var selecter bson.M + if args.StartTs == 0 && args.EndTs == 0 { + if len(args.Platform) == 0 { + selecter = bson.M{"mtype": args.MsgType} + } else { + selecter = bson.M{"mtype": args.MsgType, "platform": args.Platform} + } + } else { + if len(args.Platform) == 0 { + selecter = bson.M{"mtype": args.MsgType, "creatts": bson.M{"$gte": args.StartTs, "$lte": args.EndTs}} + } else { + selecter = bson.M{"mtype": args.MsgType, "platform": args.Platform, "creatts": bson.M{"$gte": args.StartTs, "$lte": args.EndTs}} + } + } + if args.DestSnId != -1 { + selecter["snid"] = args.DestSnId + } + //下面的Count和Skip均是Mgo中比较耗时的操作,根据线上日志来看两个语句执行时间已经超过10s(具体mgo中数据量保密) + //那重点就根据业务优化下面两句即可 + count, _ := c.Find(selecter).Count() + err := c.Find(selecter).Skip(args.FromIndex).Limit(limitDataNum).All(&ret.Msg) + ret.Count = count + return err +} + +func (svc *MessageSvc) ReadMessage(args *model.ReadMsgArgs, ret *bool) error { + logger.Logger.Info("MessageSvc ReadMessage") + c := MessageCollection(args.Platform) + if c != nil { + err := c.Update(bson.M{"_id": args.Id}, bson.D{{"$set", bson.D{{"state", model.MSGSTATE_READED}}}}) + if err != nil { + return err + } + *ret = true + } + return nil +} +func (svc *MessageSvc) DelMessage(args *model.DelMsgArgs, ret *bool) error { + logger.Logger.Info("MessageSvc DelMessage") + c := MessageCollection(args.Platform) + if c != nil { + var err error + if args.Del == 0 { + err = c.Remove(bson.M{"_id": args.Id}) // 目前需求 + } else { + err = c.Update(bson.M{"_id": args.Id}, bson.D{{"$set", bson.D{{"state", model.MSGSTATE_REMOVEED}}}}) + } + if err != nil { + return err + } + *ret = true + } + return nil +} +func (svc *MessageSvc) DelAllMessage(args *model.DelAllMsgArgs, ret *bool) error { + logger.Logger.Info("MessageSvc DelMessage") + c := MessageCollection(args.Platform) + if c != nil { + //for _, id := range args.Ids { + // var err error + // if args.Del == 0 { + // err = c.Remove(bson.M{"_id": id}) // 目前需求 + // } else { + // err = c.Update(bson.M{"_id": id}, bson.D{{"$set", bson.D{{"state", model.MSGSTATE_REMOVEED}}}}) + // } + // if err != nil { + // return err + // } + //} + //*ret = true + _, err := c.RemoveAll(bson.M{"oper": 1, "state": model.MSGSTATE_REMOVEED}) + err = c.Remove(bson.M{"oper": 1, "state": model.MSGSTATE_REMOVEED}) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("DelAllMessage:", err) + return err + } + *ret = true + } + return nil +} +func (svc *MessageSvc) GetMessageAttach(args *model.AttachMsgArgs, ret *bool) error { + logger.Logger.Info("MessageSvc GetMessageAttach") + c := MessageCollection(args.Platform) + if c != nil { + err := c.Update(bson.M{"_id": args.Id}, bson.D{{"$set", bson.D{{"attachstate", model.MSGATTACHSTATE_GOT}}}}) + if err != nil { + return err + } + *ret = true + } + return nil +} +func (svc *MessageSvc) GetMessageById(args *model.GetMsgArg, ret *model.Message) error { + logger.Logger.Info("MessageSvc GetMessageById") + c := MessageCollection(args.Platform) + if c != nil { + Id := bson.ObjectIdHex(args.IdStr) + err := c.Find(bson.M{"_id": Id}).One(ret) + return err + } + return nil +} + +func (svc *MessageSvc) GetMessageAttachs(args *model.AttachMsgsArgs, ret *[]string) error { + logger.Logger.Info("MessageSvc GetMessageAttachs") + c := MessageCollection(args.Platform) + if c != nil { + for _, Idstr := range args.Ids { + Id := bson.ObjectIdHex(Idstr) + var msg model.Message + if err := c.Find(bson.M{"_id": Id}).One(&msg); err == nil { + if msg.AttachState == model.MSGATTACHSTATE_GOT { + continue + } + err := c.Update(bson.M{"_id": Id}, bson.D{{"$set", bson.D{{"attachstate", model.MSGATTACHSTATE_GOT}}}}) + if err != nil { + logger.Logger.Infof("MessageSvc GetMessageAttachs attachstate err %v", err) + continue + } + + *ret = append(*ret, msg.Id.Hex()) + } + + } + return nil + } + return errors.New("redis is nil") +} + +func (svc *MessageSvc) InsertMessage(args *model.InsertMsgArg, ret *bool) error { + //logger.Logger.Trace("MessageSvc InsertMessage") + c := MessageCollection(args.Platform) + var err error + if c != nil { + if len(args.Msgs) == 1 { + err = c.Insert(args.Msgs[0]) + } else if len(args.Msgs) > 1 { + docs := make([]interface{}, 0, len(args.Msgs)) + for _, msg := range args.Msgs { + docs = append(docs, msg) + } + err = c.Insert(docs...) + } + if err != nil { + return err + } + *ret = true + } + return nil +} +func (svc *MessageSvc) RemoveMessages(args *model.AttachMsgArgs, ret *mgo.ChangeInfo) (err error) { + logger.Logger.Info("MessageSvc GetMessageAttach") + c := MessageCollection(args.Platform) + if c != nil { + ret, err = c.RemoveAll(bson.M{"creatts": bson.M{"$lt": args.Ts}}) + } + return +} diff --git a/dbproxy/svc/u_paycoinlog.go b/dbproxy/svc/u_paycoinlog.go new file mode 100644 index 0000000..63b577b --- /dev/null +++ b/dbproxy/svc/u_paycoinlog.go @@ -0,0 +1,212 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + PayCoinLogErr = errors.New("user_coinlog db open failed.") +) + +const ( + PayCoinLogType_Coin int32 = iota //金币对账日志 + PayCoinLogType_SafeBoxCoin //保险箱对账日志 + PayCoinLogType_Club //俱乐部对账日志 + PayCoinLogType_Ticket //比赛入场券 +) + +func PayCoinLogsCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, model.PayCoinLogDBName) + if s != nil { + c, first := s.DB().C(model.PayCoinLogCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid", "logtype", "timestamp"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"snid", "billno"}, Unique: true, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"timestamp"}, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertPayCoinLogs(plt string, logs ...*model.PayCoinLog) (err error) { + cpay := PayCoinLogsCollection(plt) + if cpay == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = cpay.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = cpay.Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertPayCoinLog error:", err) + return + } + + if len(logs) == 1 { + err = InsertCoinWAL(plt, model.NewCoinWAL(logs[0].SnId, logs[0].Coin+logs[0].CoinEx, logs[0].Inside, 0, logs[0].LogType, 0, logs[0].TimeStamp)) + } else { + docs := make([]*model.CoinWAL, 0, len(logs)) + for _, log := range logs { + docs = append(docs, model.NewCoinWAL(log.SnId, log.Coin+log.CoinEx, log.Inside, 0, log.LogType, 0, log.TimeStamp)) + } + err = InsertCoinWAL(plt, docs...) + } + + return +} + +func InsertPayCoinLog(plt string, log *model.PayCoinLog) error { + cpay := PayCoinLogsCollection(plt) + if cpay == nil { + return PayCoinLogErr + } + err := cpay.Insert(log) + if err == nil { + err = InsertCoinWAL(plt, model.NewCoinWAL(log.SnId, log.Coin+log.CoinEx, log.Inside, 0, log.LogType, 0, log.TimeStamp)) + } + return err +} + +func RemovePayCoinLog(plt string, id bson.ObjectId) error { + cpay := PayCoinLogsCollection(plt) + if cpay == nil { + return PayCoinLogErr + } + return cpay.RemoveId(id) +} + +func UpdatePayCoinLogStatus(plt string, id bson.ObjectId, status int32) error { + cpay := PayCoinLogsCollection(plt) + if cpay == nil { + return PayCoinLogErr + } + return cpay.UpdateId(id, bson.M{"$set": bson.M{"status": status}}) +} + +func GetAllPayCoinLog(plt string, snid int32, ts int64) (ret []model.PayCoinLog, err error) { + err = PayCoinLogsCollection(plt).Find(bson.M{"snid": snid, "logtype": PayCoinLogType_Coin, "timestamp": bson.M{"$gt": ts}}).All(&ret) + return +} + +func GetAllPaySafeBoxCoinLog(plt string, snid int32, ts int64) (ret []model.PayCoinLog, err error) { + err = PayCoinLogsCollection(plt).Find(bson.M{"snid": snid, "logtype": PayCoinLogType_SafeBoxCoin, "timestamp": bson.M{"$gt": ts}}).All(&ret) + return +} + +func GetAllPayClubCoinLog(plt string, snid int32, ts int64) (ret []model.PayCoinLog, err error) { + err = PayCoinLogsCollection(plt).Find(bson.M{"snid": snid, "logtype": PayCoinLogType_Club, "timestamp": bson.M{"$gt": ts}}).All(&ret) + return +} + +func GetAllPayTicketCoinLog(plt string, snid int32, ts int64) (ret []model.PayCoinLog, err error) { + err = PayCoinLogsCollection(plt).Find(bson.M{"snid": snid, "logtype": PayCoinLogType_Ticket, "timestamp": bson.M{"$gt": ts}}).All(&ret) + return +} + +func GetPayCoinLogByBillNo(plt string, snid int32, billNo int64) (*model.PayCoinLog, error) { + var log model.PayCoinLog + err := PayCoinLogsCollection(plt).Find(bson.M{"snid": snid, "billno": billNo}).One(&log) + return &log, err +} + +type PayCoinLogSvc struct { +} + +var _PayCoinLogSvc = &PayCoinLogSvc{} + +func (svc *PayCoinLogSvc) InsertPayCoinLogs(args *model.InsertPayCoinLogArgs, ret *bool) (err error) { + err = InsertPayCoinLogs(args.Plt, args.Logs...) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *PayCoinLogSvc) InsertPayCoinLog(args *model.InsertPayCoinLogArgs, ret *bool) (err error) { + err = InsertPayCoinLogs(args.Plt, args.Logs...) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *PayCoinLogSvc) RemovePayCoinLog(args *model.RemovePayCoinLogArgs, ret *bool) (err error) { + err = RemovePayCoinLog(args.Plt, args.Id) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *PayCoinLogSvc) UpdatePayCoinLogStatus(args *model.UpdatePayCoinLogStatusArgs, ret *bool) (err error) { + err = UpdatePayCoinLogStatus(args.Plt, args.Id, args.Status) + if err != nil { + return err + } + *ret = true + return nil +} + +func (svc *PayCoinLogSvc) GetAllPayCoinLog(args *model.GetPayCoinLogArgs, ret *[]model.PayCoinLog) (err error) { + *ret, err = GetAllPayCoinLog(args.Plt, args.SnId, args.Cond) + if err != nil { + return err + } + return nil +} + +func (svc *PayCoinLogSvc) GetAllPaySafeBoxCoinLog(args *model.GetPayCoinLogArgs, ret *[]model.PayCoinLog) (err error) { + *ret, err = GetAllPaySafeBoxCoinLog(args.Plt, args.SnId, args.Cond) + if err != nil { + return err + } + return nil +} + +func (svc *PayCoinLogSvc) GetAllPayClubCoinLog(args *model.GetPayCoinLogArgs, ret *[]model.PayCoinLog) (err error) { + *ret, err = GetAllPayClubCoinLog(args.Plt, args.SnId, args.Cond) + if err != nil { + return err + } + return nil +} + +func (svc *PayCoinLogSvc) GetAllPayTicketCoinLog(args *model.GetPayCoinLogArgs, ret *[]model.PayCoinLog) (err error) { + *ret, err = GetAllPayTicketCoinLog(args.Plt, args.SnId, args.Cond) + if err != nil { + return err + } + return nil +} + +func (svc *PayCoinLogSvc) GetPayCoinLogByBillNo(args *model.GetPayCoinLogArgs, ret **model.PayCoinLog) (err error) { + *ret, err = GetPayCoinLogByBillNo(args.Plt, args.SnId, args.Cond) + if err != nil { + return err + } + return nil +} + +func init() { + rpc.Register(_PayCoinLogSvc) +} diff --git a/dbproxy/svc/u_player.go b/dbproxy/svc/u_player.go new file mode 100644 index 0000000..b0ca506 --- /dev/null +++ b/dbproxy/svc/u_player.go @@ -0,0 +1,1682 @@ +package svc + +import ( + "encoding/json" + "errors" + "fmt" + "net/rpc" + "os" + "reflect" + "strconv" + "strings" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + PlayerDBName = "user" + PlayerCollName = "user_playerinfo" + PlayerDelBackupCollName = "user_playerinfo_del_backup" + PlayerColError = errors.New("Playerinfo collection open failed") +) + +func PlayerDataCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, PlayerDBName) + if s != nil { + c_playerdata, first := s.DB().C(PlayerCollName) + if first { + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"accountid"}, Unique: true, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"snid"}, Unique: true, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"channel"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"tel"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"bankaccount"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"alipayaccount"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"alipayaccname"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"bankaccname"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"invitecode"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"invitesnid"}, Background: true, Sparse: true}) + c_playerdata.EnsureIndex(mgo.Index{Key: []string{"name"}, Background: true, Sparse: true}) + } + return c_playerdata + } + + return nil +} + +type PlayerDelBackupDataSvc struct { +} + +func PlayerDelBackupDataCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, PlayerDBName) + if s != nil { + c_playerdata, _ := s.DB().C(PlayerDelBackupCollName) + return c_playerdata + } + return nil +} + +type PlayerDataSvc struct { +} + +//func (svc *PlayerDataSvc) SavePlayerRebate(pd *model.PlayerData, ret *bool) error { +// cplayerdata := PlayerDataCollection(pd.Platform) +// if cplayerdata == nil { +// return nil +// } +// err := cplayerdata.Update(bson.M{"snid": pd.SnId}, bson.D{{"$set", bson.D{{"rebatedata", pd.RebateData}, {"totalconvertibleflow", pd.TotalConvertibleFlow}}}}) +// if err != nil { +// logger.Logger.Trace("SavePlayerRebate failed:", err) +// ret.Err = err +// return err +// } +// return nil +//} + +// 获取代理信息 +func (svc *PlayerDataSvc) GetAgentInfo(args *model.GetAgentInfoArgs, ret *model.PlayerData) error { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + err := cplayerdata.Find(bson.M{"tel": args.Tel}).One(ret) + if err != nil { + logger.Logger.Error("GetAgentInfo failed:", err) + return err + } + if CorrectData(ret) { + } + return nil +} +func (svc *PlayerDataSvc) GetPlayerData(args *model.GetPlayerDataArgs, ret *model.PlayerDataRet) error { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + + pd := &model.PlayerData{} + fileName := fmt.Sprintf("%v.json", args.Acc) + _, err := os.Stat(fileName) + if err == nil || os.IsExist(err) { + pd = model.RestorePlayerData(fileName) + err = os.Remove(fileName) + } else { + if args.Acc != "" { + err = cplayerdata.Find(bson.M{"accountid": args.Acc}).One(pd) + } + } + + if err != nil && err.Error() == mgo.ErrNotFound.Error() { + logger.Logger.Trace("GetPlayerinfo Find failed:", err) + + if !bson.IsObjectIdHex(args.Acc) { + logger.Logger.Warn("NewPlayer failed: acc is illeage ", args.Acc) + return nil + } + + a, err := _AccountSvc.getAccount(args.Plt, args.Acc) + if err != nil { + logger.Logger.Warnf("model.GetAccount(%v) failed:%v", args.Acc, err) + return nil + } + + id := a.SnId + if id == 0 { + id, err = GetOnePlayerIdFromBucket() + if err != nil { + logger.Logger.Warn("NewPlayer failed:", err) + return err + } + } + + var dataParams model.PlayerParams + json.Unmarshal([]byte(a.Params), &dataParams) + + name := dataParams.Name + if name == "" { + name = model.GameParamData.GuestDefaultName //fmt.Sprintf("Guest%v", id) + } + if name == "" { + name = "Guest" + } + + pd = model.NewPlayerData(args.Acc, name, id, a.Channel, a.Platform, a.Promoter, a.InviterId, a.PromoterTree, a.Params, + a.Tel, a.PackegeTag, dataParams.Ip, 0, dataParams.UnionId, a.DeviceInfo, a.SubPromoter, a.TagKey, a.AccountType) + if pd != nil { + err = cplayerdata.Insert(pd) + if err != nil { + logger.Logger.Errorf("GetPlayerinfo Insert err:%v acc:%v snid:%v", err, args.Acc, id) + return err + } + ret.IsNew = true + ret.Pd = pd + return nil + } + return nil + } + + //todo 修正玩家的离线时间,上次登录时间,创建时间 + pd.LastLogoutTime = pd.LastLogoutTime.Local() + pd.LastLoginTime = pd.LastLoginTime.Local() + pd.CreateTime = pd.CreateTime.Local() + + if CorrectData(pd) { + } + ret.Pd = pd + return nil +} + +// 谷歌 facebook 账号数据 +func (svc *PlayerDataSvc) CreatePlayerDataByThird(args *model.CreatePlayer, ret *model.PlayerDataRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + acc := args.AccId + pd := &model.PlayerData{} + fileName := fmt.Sprintf("%v.json", acc) + _, err = os.Stat(fileName) + if err == nil || os.IsExist(err) { + pd = model.RestorePlayerData(fileName) + err = os.Remove(fileName) + } else { + if acc != "" { + err = cplayerdata.Find(bson.M{"accountid": acc}).One(pd) + } + } + + if err != nil && err.Error() == mgo.ErrNotFound.Error() { + logger.Logger.Trace("CreatePlayerDataByThird Find failed:", err) + + if !bson.IsObjectIdHex(acc) { + logger.Logger.Warn("NewPlayer failed: acc is illeage ", acc) + return + } + var a *model.Account + a, err = _AccountSvc.getAccount(args.Plt, args.AccId) + if err != nil { + logger.Logger.Warnf("_AccountSvc.getAccount(%v,%v) failed:%v", args.Plt, args.AccId, err) + return + } + + id := a.SnId + if id == 0 { + id, err = GetOnePlayerIdFromBucket() + if err != nil { + logger.Logger.Warn("NewPlayer failed:", err) + return + } + } + + //name := model.GameParamData.GuestDefaultName //fmt.Sprintf("db%v", id) + name := args.NickName + if name == "" { + name = "Guest" + } + var dataParams model.PlayerParams + json.Unmarshal([]byte(a.Params), &dataParams) + pd = model.NewPlayerDataThird(acc, name, args.HeadUrl, id, a.Channel, a.Platform, a.Promoter, a.InviterId, + a.PromoterTree, a.Params, a.Tel, a.PackegeTag, dataParams.Ip, a.SubPromoter, a.TagKey, a.AccountType, a.DeviceOs) + if pd != nil { + err = cplayerdata.Insert(pd) + if err != nil { + logger.Logger.Trace("CreatePlayerDataByThird Insert failed:", err) + return + } + ret.Pd = pd + ret.IsNew = true + return + } + return + } + if CorrectData(pd) { + } + ret.Pd = pd + return nil +} + +func (svc *PlayerDataSvc) CreatePlayerDataOnRegister(args *model.PlayerDataArg, ret *model.PlayerDataRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + acc := args.AccId + pd := &model.PlayerData{} + fileName := fmt.Sprintf("%v.json", acc) + _, err = os.Stat(fileName) + if err == nil || os.IsExist(err) { + pd = model.RestorePlayerData(fileName) + err = os.Remove(fileName) + } else { + if acc != "" { + err = cplayerdata.Find(bson.M{"accountid": acc}).One(pd) + } + } + + if err != nil && err.Error() == mgo.ErrNotFound.Error() { + logger.Logger.Trace("CreatePlayerDataOnRegister Find failed:", err) + + if !bson.IsObjectIdHex(acc) { + logger.Logger.Warn("NewPlayer failed: acc is illeage ", acc) + return + } + var a *model.Account + a, err = _AccountSvc.getAccount(args.Plt, args.AccId) + if err != nil { + logger.Logger.Warnf("_AccountSvc.getAccount(%v,%v) failed:%v", args.Plt, args.AccId, err) + return + } + + id := a.SnId + if id == 0 { + id, err = GetOnePlayerIdFromBucket() + if err != nil { + logger.Logger.Warn("NewPlayer failed:", err) + return + } + } + + name := model.GameParamData.GuestDefaultName //fmt.Sprintf("Guest%v", id) + if name == "" && args.Name != "" { + name = args.Name + } + if name == "" { + name = "Guest" + } + var dataParams model.PlayerParams + json.Unmarshal([]byte(a.Params), &dataParams) + pd = model.NewPlayerData(acc, name, id, a.Channel, a.Platform, a.Promoter, a.InviterId, + a.PromoterTree, a.Params, a.Tel, a.PackegeTag, dataParams.Ip, int64(args.AddCoin), + "", a.DeviceInfo, a.SubPromoter, a.TagKey, a.AccountType) + pd.HeadUrl = args.HeadUrl + if pd != nil { + err = cplayerdata.Insert(pd) + if err != nil { + logger.Logger.Trace("CreatePlayerDataOnRegister Insert failed:", err) + return + } + ret.Pd = pd + ret.IsNew = true + return + } + return + } + if CorrectData(pd) { + } + ret.Pd = pd + return +} + +func (svc *PlayerDataSvc) GetPlayerDataBySnId(args *model.GetPlayerDataBySnIdArgs, ret *model.PlayerDataRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + + var code string + err = cplayerdata.Find(bson.M{"snid": args.SnId}).One(&ret.Pd) + if err != nil { + logger.Logger.Tracef("from %v Get %v player data error:%v", args.Plt, args.SnId, err) + if args.CreateIfNotExist && err == mgo.ErrNotFound { + a, err := _AccountSvc.getAccountBySnId(args.Plt, args.SnId) + if err != nil { + logger.Logger.Warnf("model.getAccountBySnId(%v) failed:%v", args.SnId, err) + return err + } + + var dataParams model.PlayerParams + json.Unmarshal([]byte(a.Params), &dataParams) + + name := dataParams.Name + if name == "" { + name = model.GameParamData.GuestDefaultName //fmt.Sprintf("Guest%v", id) + } + if name == "" { + name = "Guest" + } + + pd := model.NewPlayerData(a.AccountId.Hex(), name, a.SnId, a.Channel, a.Platform, a.Promoter, a.InviterId, a.PromoterTree, a.Params, + a.Tel, a.PackegeTag, dataParams.Ip, 0, dataParams.UnionId, a.DeviceInfo, a.SubPromoter, a.TagKey, a.AccountType) + if pd != nil { + err = cplayerdata.Insert(pd) + if err != nil { + logger.Logger.Errorf("GetPlayerDataBySnId Insert err:%v acc:%v snid:%v", err, a.AccountId.Hex(), a.SnId) + return err + } + if pd.InviteCode == "" { + code, err = GetInviteCode(pd.Platform, pd.SnId) + pd.InviteCode = code + } + ret.IsNew = true + ret.Pd = pd + return nil + } + } + return err + } + if ret.Pd.InviteCode == "" { + code, err = GetInviteCode(ret.Pd.Platform, ret.Pd.SnId) + ret.Pd.InviteCode = code + } + if args.CorrectData && ret.Pd != nil { + CorrectData(ret.Pd) + } + + return nil +} + +//func (svc *PlayerDataSvc) UpdateCreateCreateClubNum(snId int32, ret *model.PlayerDataRet) error { +// cplayerdata := PlayerDataCollection() +// if cplayerdata == nil { +// return nil +// } +// err := cplayerdata.Update(bson.M{"snid": snId}, bson.D{{"$inc", bson.D{{"createclubnum", 1}}}}) +// if err != nil { +// logger.Logger.Error("Update player safeboxcoin error:", err) +// ret.Err = err +// return err +// } +// return nil +//} + +func (svc *PlayerDataSvc) GetPlayerDatasBySnIds(args *model.GetPlayerDatasBySnIdsArgs, ret *[]*model.PlayerData) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + err = cplayerdata.Find(bson.M{"snid": bson.M{"$in": args.SnIds}}).All(ret) + if err != nil { + logger.Logger.Tracef("GetPlayerDatasBySnIds(snids=%v) error:%v", args.SnIds, err) + return + } + + if args.CorrectData { + for _, e := range *ret { + if CorrectData(e) { + + } + } + } + + return +} +func (svc *PlayerDataSvc) GetPlayerDataByUnionId(args *model.GetPlayerDataByUnionIdArgs, ret *model.PlayerDataRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"unionid": args.UnionId}).One(&ret.Pd) + if err != nil { + logger.Logger.Tracef("GetPlayerDataByUnionId % player data error:%v", args.UnionId, err) + return + } + if args.CorrectData { + if CorrectData(ret.Pd) { + + } + } + return +} + +func (svc *PlayerDataSvc) GetPlayerTel(args *model.GetPlayerTelArgs, ret *model.GetPlayerTelRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"tel": args.Tel}).Select(bson.M{"snid": 1}).One(ret) + if err != nil { + logger.Logger.Warn("model.PlayerData.Find err:", err) + return + } + return +} +func (svc *PlayerDataSvc) GetPlayerCoin(args *model.GetPlayerCoinArgs, ret *model.GetPlayerCoinRet) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"snid": args.SnId}).Select(bson.M{"coin": 1, "safeboxcoin": 1}).One(ret) + if err != nil { + logger.Logger.Trace("GetPlayerCoin error:", err) + ret.Err = err + return + } + return +} + +func SavePlayerData(pd *model.PlayerData) (err error) { + cplayerdata := PlayerDataCollection(pd.Platform) + if cplayerdata == nil { + return + } + if pd != nil { + model.RecalcuPlayerCheckSum(pd) + _, err = cplayerdata.Upsert(bson.M{"_id": pd.Id}, pd) + if err != nil { + logger.Logger.Errorf("model.SavePlayerData %v err:%v", pd.SnId, err) + return + } + + //清理coinWAL + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + RemoveCoinWALByCoinType(pd.Platform, pd.SnId, PayCoinLogType_Coin, pd.CoinPayTs) + RemoveCoinWALInGame(pd.Platform, pd.SnId, 0, pd.GameCoinTs) + return nil + }), nil, "RemoveCoinWAL").StartByFixExecutor("RemoveCoinWAL") + } + return +} + +/* + * 保存玩家的全部信息 + */ +func (svc *PlayerDataSvc) SavePlayerData(pd *model.PlayerData, ret *bool) (err error) { + err = SavePlayerData(pd) + *ret = err == nil + return +} + +func (svc *PlayerDataSvc) RemovePlayer(args *model.RemovePlayerArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + err = cplayerdata.Remove(bson.M{"snid": args.SnId}) + if err != nil { + logger.Logger.Info("Remove player failed.") + return + } + *ret = true + return +} +func (svc *PlayerDataSvc) RemovePlayerByAcc(args *model.RemovePlayerByAccArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return nil + } + err = cplayerdata.Remove(bson.M{"accountid": args.Acc}) + if err != nil { + logger.Logger.Info("Remove player failed.") + return + } + *ret = true + return +} + +/* + * 检查手机号是否存在 + */ +func (svc *PlayerDataSvc) GetPlayerSnid(args *model.GetPlayerSnidArgs, ret *int32) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"accountid": args.Acc}).Select(bson.M{"snid": 1}).One(ret) + if err != nil { + logger.Logger.Error("svc.GetPlayerSnid is error ", err) + return + } + return +} + +/* + * 检查昵称是否存在 + */ +func (svc *PlayerDataSvc) PlayerNickIsExist(args *model.PlayerNickIsExistArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"name": args.Name}).One(nil) + if err != nil { + logger.Logger.Error("svc.PlayerNickIsExist is error", err) + return err + } + *ret = true + return +} + +/* + * 检查玩家是否存在 + */ +func (svc *PlayerDataSvc) PlayerIsExistBySnId(args *model.PlayerIsExistBySnIdArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"snid": args.SnId}).One(nil) + if err != nil { + logger.Logger.Error("svc.GetPlayerIsExistBySnId is error", err) + return + } + + *ret = true + return +} + +// 修改推广包标识 +func (svc *PlayerDataSvc) UpdatePlayerPackageId(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.M{"packageid": args.Tag, + "platform": strconv.Itoa(int(args.Platform)), "channel": strconv.Itoa(int(args.Channel)), + "beunderagentcode": strconv.Itoa(int(args.Promoter)), "inviterid": args.Inviterid, "promotertree": args.PromoterTree}}}) + if err != nil { + logger.Logger.Error("Update player packageid error:", err) + return err + } + *ret = true + return nil +} +func (svc *PlayerDataSvc) UpdatePlayerPackageIdByStr(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.M{"packageid": args.Tag, + "platform": args.PlatformStr, "channel": args.ChannelStr, + "beunderagentcode": args.PromoterStr, "inviterid": args.Inviterid, "promotertree": args.PromoterTree}}}) + if err != nil { + logger.Logger.Trace("UpdatePlayerPackageIdByStr error:", err) + return err + } + + *ret = true + return nil +} + +func (svc *PlayerDataSvc) UpdatePlayerPackageIdByAcc(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"accountid": args.AccId}, bson.D{{"$set", bson.M{"packageid": args.Tag, + "platform": args.PlatformStr, "channel": args.ChannelStr, + "beunderagentcode": args.PromoterStr, "inviterid": args.Inviterid, "promotertree": args.PromoterTree}}}) + if err != nil { + logger.Logger.Trace("UpdatePlayerPackageIdByAcc error:", err) + return + } + + *ret = true + return +} + +// 修改平台 +func (svc *PlayerDataSvc) UpdatePlayerPlatform(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", + bson.D{{"platform", args.PlatformStr}, {"channel", args.ChannelStr}}}}) + if err != nil { + logger.Logger.Trace("Update player nick error:", err) + return + } + + *ret = true + return +} + +// 修改玩家无级代推广员id +func (svc *PlayerDataSvc) UpdatePlayerPromoterTree(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"promotertree", args.PromoterTree}}}}) + if err != nil { + logger.Logger.Trace("UpdatePlayerPromoterTree error:", err) + return + } + + *ret = true + return +} + +// 修改玩家全民推广 +func (svc *PlayerDataSvc) UpdatePlayerInviteID(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"inviterid", args.Inviterid}}}}) + if err != nil { + logger.Logger.Trace("UpdatePlayerInviteID error:", err) + return + } + + *ret = true + return +} + +/* + * 修改昵称 + */ +func (svc *PlayerDataSvc) UpdatePlayerNick(args *model.UpdatePlayerInfo, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"accountid": args.Acc}, bson.D{{"$set", bson.D{{"name", args.Nick}}}}) + if err != nil { + logger.Logger.Trace("Update player nick error:", err) + return + } + + *ret = true + return +} + +/* + * 修改黑名单类型 + */ +func (svc *PlayerDataSvc) UpdatePlayerBlacklistType(args *model.UpdatePlayerBlacklistTypeArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"blacklisttype", args.BlackListType}}}}) + if err != nil { + logger.Logger.Trace("Update player blacklisttype error:", err) + return + } + + *ret = true + return +} + +/* + * 修改玩家备注信息 + */ +func (svc *PlayerDataSvc) UpdatePlayerMarkInfo(args *model.UpdatePlayerMarkInfoArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"markinfo", args.MarkInfo}}}}) + if err != nil { + logger.Logger.Warnf("Update player mark error:", err) + return + } + + *ret = true + return +} + +/* + * 修改玩家特殊白名单 + */ +func (svc *PlayerDataSvc) UpdatePlayerWhiteFlag(args *model.UpdatePlayerWhiteFlagArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"whiteflag", args.WhiteFlag}}}}) + if err != nil { + logger.Logger.Warnf("Update player whiteFlag error:", err) + } + + *ret = true + return +} + +/* + * 修改玩家支付信息 + */ +func (svc *PlayerDataSvc) UpdatePlayerPayAct(args *model.UpdatePlayerPayActArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"payactstate", args.PayActState}}}}) + if err != nil { + logger.Logger.Warnf("Update player payact error:", err) + return + } + + *ret = true + return +} + +/* + * 修改玩家电销标记 + */ +func (svc *PlayerDataSvc) UpdatePlayerTelephonePromoter(args *model.UpdatePlayerTelephonePromoterArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", + bson.D{{"telephonepromoter", args.TelephonePromoter}}}}) + if err != nil { + logger.Logger.Warnf("Update player telephonepromoter error:", err) + return + } + + *ret = true + return +} + +/* + * 修改玩家电销标记 + */ +func (svc *PlayerDataSvc) UpdatePlayerTelephoneCallNum(args *model.UpdatePlayerTelephonePromoterArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", + bson.D{{"telephonecallnum", args.TelephoneCallNum}}}}) + if err != nil { + logger.Logger.Warnf("Update player telephonecallnum error:", err) + return + } + + *ret = true + return +} + +/* + * 修改头像 + */ +func (svc *PlayerDataSvc) UpdatePlayeIcon(args *model.UpdatePlayeIconArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"accountid": args.Acc}, bson.D{{"$set", bson.D{{"head", args.Head}}}}) + if err != nil { + logger.Logger.Trace("Update player icon error:", err) + return + } + + *ret = true + return +} + +/* + * 修改性别 + */ +func (svc *PlayerDataSvc) UpdatePlayeSex(args *model.UpdatePlayeSexArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"accountid": args.Acc}, bson.D{{"$set", bson.D{{"sex", args.Sex}}}}) + if err != nil { + logger.Logger.Trace("Update player sex error:", err) + return + } + + *ret = true + return +} + +/* + * 检查手机号是否存在 + */ +func (svc *PlayerDataSvc) PlayerTelIsExist(args *model.PlayerTelIsExistArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"tel": args.Tel, "tagkey": args.TagKey}).One(nil) + if err != nil { + return + } + + *ret = true + return +} + +/* + * 绑定手机号 + */ +func (svc *PlayerDataSvc) UpdatePlayerTel(args *model.UpdatePlayerTelArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"tel", args.Tel}}}}) + if err != nil { + logger.Logger.Warn("UpdatePlayerTel error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) ResetPlayer(args *model.ResetPlayerArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"tel", ""}, {"delete", 1}}}}) + if err != nil { + logger.Logger.Warn("UpdatePlayerTel error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) CountAlipayAccountCount(args *model.CountAlipayAccountCountArgs, ret *int) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + n, err := cplayerdata.Find(bson.M{"alipayaccount": args.AliPayAccount}).Count() + if err != nil { + logger.Logger.Warn("CountAlipayAccountCount error:", err) + return err + } + *ret = n + return +} + +func (svc *PlayerDataSvc) CountBankAlipayNameCount(args *model.CountBankAlipayNameCountArgs, ret *int) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + var conds []bson.M + conds = append(conds, bson.M{"alipayaccname": args.AliPayAccName}) + conds = append(conds, bson.M{"bankaccname": args.BankAccName}) + n, err := cplayerdata.Find(bson.M{"snid": bson.M{"$ne": args.SnId}, "$or": conds}).Count() + if err != nil { + logger.Logger.Warn("CountBankAlipayNameCount error:", err) + return + } + *ret = n + return +} + +/* + * 绑定支付宝账号 + */ +func (svc *PlayerDataSvc) UpdatePlayerAlipay(args *model.UpdatePlayerAlipayArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", + bson.D{{"alipayaccount", args.AliPayAccount}, {"alipayaccname", args.AliPayAccName}}}}) + if err != nil { + logger.Logger.Warn("UpdatePlayerAlipay error:", err) + return + } + *ret = true + return +} +func (svc *PlayerDataSvc) UpdatePlayerAlipayAccount(args *model.UpdatePlayerAlipayArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"alipayaccount", args.AliPayAccount}}}}) + if err != nil { + logger.Logger.Warn("UpdatePlayerAlipay error:", err) + return + } + *ret = true + return +} +func (svc *PlayerDataSvc) UpdatePlayerAlipayName(args *model.UpdatePlayerAlipayArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", + bson.D{{"alipayaccname", args.AliPayAccName}}}}) + if err != nil { + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) CountBankAccountCount(args *model.CountBankAccountCountArgs, ret *int) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + n, err := cplayerdata.Find(bson.M{"bankaccount": args.BankAccount}).Count() + if err != nil { + logger.Logger.Warn("CountBankAccountCount error:", err) + return + } + *ret = n + return +} + +/* + * 绑定银行账号 + */ +func (svc *PlayerDataSvc) UpdatePlayerBank(args *model.UpdatePlayerBankArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("user_playerinfo not open") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"bank", args.Bank}, + {"bankaccount", args.BankAccount}, {"bankaccname", args.BankAccName}}}}) + if err != nil { + logger.Logger.Warn("UpdatePlayerBank error:", err) + return + } + *ret = true + return +} + +/* + * 修改玩家是否返利 + */ +func (svc *PlayerDataSvc) UpdatePlayerIsRebate(args *model.UpdatePlayerIsRebateArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"iscanrebate", args.IsCanRebate}}}}) + if err != nil { + logger.Logger.Trace("Update player isRebate error:", err) + return + } + *ret = true + return +} + +/* + * 修改玩家是否可以修改昵称 + */ +func (svc *PlayerDataSvc) UpdatePlayerIsStopRename(args *model.UpdatePlayerIsStopRenameArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("param err") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"isstoprename", args.IsStopReName}}}}) + if err != nil { + logger.Logger.Trace("Update player isRename error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) PlayerRebindSnId(args *model.PlayerRebindSnIdArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return errors.New("cplayerdata == nil") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"snid", args.NewSnId}}}}) + if err != nil { + logger.Logger.Trace("PlayerRebindSnId error:", err) + return + } + *ret = true + return +} + +/* + * 修改保险箱密码 + */ +func (svc *PlayerDataSvc) UpdateSafeBoxPassword(args *model.PlayerSafeBoxArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + err = cplayerdata.Update(bson.M{"accountid": args.Acc, "safeboxpassword": args.OldPassWord}, + bson.D{{"$set", bson.D{{"safeboxpassword", args.PassWord}}}}) + if err != nil { + logger.Logger.Trace("Update player safeboxpassword error:", err) + return + } + *ret = true + return +} + +/* + * 找回保险箱密码 + */ +func (svc *PlayerDataSvc) GetBackSafeBoxPassword(args *model.PlayerSafeBoxArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return errors.New("GetBackSafeBoxPassword error") + } + err = cplayerdata.Update(bson.M{"tel": args.Tel, "tagkey": args.TagKey}, + bson.D{{"$set", bson.D{{"safeboxpassword", args.PassWord}}}}) + if err != nil { + logger.Logger.Trace("Update player safeboxpassword error:", err) + return + } + *ret = true + return +} + +/* + * 重置保险箱密码 + */ +func (svc *PlayerDataSvc) ResetSafeBoxPassword(args *model.PlayerSafeBoxArgs, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return PlayerColError + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{{"safeboxpassword", args.PassWord}}}}) + if err != nil { + logger.Logger.Trace("Reset player safeboxpassword error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) UpdatePlayerCoin(args *model.PlayerSafeBoxCoin, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be closed") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{ + {"coin", args.Coin}, + {"diamond", args.Diamond}, + {"safeboxcoin", args.SafeBoxCoin}, + {"coinpayts", args.CoinPayTs}, + {"safeboxcoints", args.SafeBoxCoinTs}, + {"moneypaytotal", args.Money}, + {"shopid", args.ShopId}}}}) + if err != nil { + logger.Logger.Error("UpdatePlayerCoin error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) UpdatePlayerSetCoin(args *model.PlayerSafeBoxCoin, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be closed") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{ + {"coin", args.Coin}}}}) + if err != nil { + logger.Logger.Error("svc.UpdatePlayerSetCoin error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) UpdatePlayerLastExchangeTime(args *model.PlayerSafeBoxCoin, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be closed") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{ + {"lastexchangetime", args.LastexChangeTime}}}}) + if err != nil { + logger.Logger.Error("svc.UpdateLastExchangeTime error:", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) UpdatePlayerExchageFlow(args *model.PlayerSafeBoxCoin, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be closed") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{ + {"totalconvertibleflow", args.TotalConvertibleFlow}, + {"totalflow", args.TotalFlow}}}}) + if err != nil { + logger.Logger.Error("svc.UpdatePlayerExchageFlow error:", err) + return + } + *ret = true + return +} +func (svc *PlayerDataSvc) UpdatePlayerExchageFlowAndOrder(args *model.PlayerSafeBoxCoin, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be closed") + } + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bson.D{ + {"totalconvertibleflow", args.TotalConvertibleFlow}, + {"lastexchangeorder", args.LastexChangeOrder}}}}) + if err != nil { + logger.Logger.Error("svc.UpdatePlayerExchageFlowAndOrder error:", err) + return + } + *ret = true + return +} + +/* + * 查找用户 + */ +func (svc *PlayerDataSvc) FindPlayerList(args *model.PlayerSelect, ret *[]*model.PlayerBaseInfo) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + + var conds []bson.M + if args.Id != 0 { + conds = append(conds, bson.M{"snid": args.Id}) + } + if len(args.Tel) > 0 { + conds = append(conds, bson.M{"tel": args.Tel}) + } + if len(args.NickName) > 0 { + conds = append(conds, bson.M{"name": args.NickName}) + } + if args.Coinl > 0 || args.Coinh > 0 { + conds = append(conds, bson.M{"coin": bson.M{"$gt": args.Coinl, "$lt": args.Coinh}}) + } + if len(args.Alipay) > 0 { + conds = append(conds, bson.M{"alipayaccount": args.Alipay}) + } + if args.Registerl > 0 || args.Registerh > 0 { + conds = append(conds, bson.M{"createtime": bson.M{"$gt": time.Unix(int64(args.Registerl), 0), "$lt": time.Unix(int64(args.Registerh), 0)}}) + } + if len(args.Channel) > 0 { + conds = append(conds, bson.M{"channel": args.Channel}) + } + selecter := bson.M{"$or": conds} + if args.Limit > 0 { + err = cplayerdata.Find(selecter).Limit(args.Limit).All(ret) + } else { + err = cplayerdata.Find(selecter).All(ret) + } + if err != nil { + logger.Logger.Error("svc.FindPlayerList is error", err) + return nil + } + + return +} + +func (svc *PlayerDataSvc) UpdatePlayerElement(args *model.UpdateElement, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return fmt.Errorf("db may be close") + } + playerMap := make(map[string]interface{}) + json.Unmarshal([]byte(args.PlayerMap), &playerMap) + + var o model.PlayerData + r_t := reflect.TypeOf(o) + var bsonInfo bson.D + var docelem bson.DocElem + for k, v := range playerMap { + if f, ok := r_t.FieldByName(k); ok { + if reflect.TypeOf(v).ConvertibleTo(f.Type) { + docelem.Name = strings.ToLower(k) + docelem.Value = v + bsonInfo = append(bsonInfo, docelem) + } else { + logger.Logger.Warnf("UpdatePlayerElement Type not fit %v field, get %v", k, v) + } + } else { + logger.Logger.Warnf("UpdatePlayerElement no %v field", k) + } + } + + err = cplayerdata.Update(bson.M{"snid": args.SnId}, bson.D{{"$set", bsonInfo}}) + if err != nil { + logger.Logger.Warnf("UpdatePlayerElement error:%v", err) + return + } + + *ret = true + return +} + +func (svc *PlayerDataSvc) GetPlayerBaseInfo(args *model.GetPlayerBaseInfoArgs, ret *model.PlayerBaseInfo) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + err = cplayerdata.Find(bson.M{"snid": args.SnId, "platform": &args.Plt}).One(ret) + if err != nil && err != mgo.ErrNotFound { + logger.Logger.Error("svc.GetPlayerBaseInfo is error: ", err) + return + } + return +} + +func (svc *PlayerDataSvc) SetBlackWhiteLevel(args *model.BlackWhiteLevel, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return nil + } + cond := bson.M{"snid": args.SnId} + if args.Platform != "" { + cond["platform"] = args.Platform + } + err = cplayerdata.Update(cond, bson.D{{"$set", bson.D{{"wblevel", args.WBLevel}, + {"wbcointotalin", args.WbCoinTotalIn}, + {"wbcointotalout", args.WbCoinTotalOut}, {"wbcoinlimit", args.WbCoinLimit}, + {"wbtime", args.T}, {"wbmaxnum", args.WbMaxNum}, {"wbstate", args.WbState}}}}) + if err != nil { + logger.Logger.Info("SetBlackWhiteLevel error ", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) SetBlackWhiteLevelUnReset(args *model.BlackWhiteLevel, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return nil + } + cond := bson.M{"snid": args.SnId} + err = cplayerdata.Update(cond, bson.D{{"$set", bson.D{{"wblevel", args.WBLevel}, + {"wbcoinlimit", args.WbCoinLimit}, + {"wbtime", args.T}, {"wbmaxnum", args.WbMaxNum}, {"wbstate", args.WbState}}}}) + if err != nil { + logger.Logger.Info("SetBlackWhiteLevel error ", err) + return + } + + *ret = true + return +} + +func (svc *PlayerDataSvc) UpdateAllPlayerPackageTag(args *model.UpdatePackageId, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.PlatformStr) + if cplayerdata == nil { + return nil + } + + info, err := cplayerdata.UpdateAll(bson.M{"packageid": args.PackageTag}, bson.D{{"$set", + bson.D{{"platform", args.PlatformStr}, {"channel", args.ChannelStr}, + {"beunderagentcode", args.PromoterStr}, {"promotertree", args.PromoterTree}}}}) + if err != nil { + logger.Logger.Info("svc.UpdateAllPlayerPackageTag error ", err) + return + } + logger.Logger.Info("svc.UpdateAllPlayerPackageTag result:", info) + *ret = true + return +} + +func (svc *PlayerDataSvc) SetVipLevel(args *model.SetPlayerAtt, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return nil + } + cond := bson.M{"snid": args.SnId} + err = cplayerdata.Update(cond, bson.D{{"$set", bson.D{{"forcevip", args.VipLevel}, + {"vip", args.VipLevel}}}}) + if err != nil { + logger.Logger.Info("SetVipLevel error ", err) + return + } + *ret = true + return +} + +func (svc *PlayerDataSvc) SetGMLevel(args *model.SetPlayerAtt, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return nil + } + cond := bson.M{"snid": args.SnId} + err = cplayerdata.Update(cond, bson.D{{"$set", bson.D{{"gmlevel", args.GmLevel}}}}) + if err != nil { + logger.Logger.Info("SetGMLevel error ", err) + return + } + *ret = true + return +} +func (svc *PlayerDataSvc) GetSameIpPlayer(args *model.GetSameParamPlayerArgs, ret *[]int32) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + + //临时结构 + var t []struct { + SnId int32 + } + err = cplayerdata.Find(bson.M{"ip": args.Param}).Select(bson.M{"snid": 1}).All(&t) + if err != nil { + logger.Logger.Error("svc.GetSameIpPlayer is error", err) + return + } + for _, v := range t { + *ret = append(*ret, v.SnId) + } + + fmt.Println("GetSameIpPlayer....", ret) + return +} +func (svc *PlayerDataSvc) GetSameBankNamePlayer(args *model.GetSameParamPlayerArgs, ret *[]int32) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + //临时结构 + var t []struct { + SnId int32 + } + err = cplayerdata.Find(bson.M{"bankaccname": args.Param}).Select(bson.M{"snid": 1}).All(&t) + if err != nil { + logger.Logger.Error("svc.GetSameBankNamePlayer is error", err) + return + } + + for _, value := range t { + *ret = append(*ret, value.SnId) + } + return +} +func (svc *PlayerDataSvc) GetSameBankCardPlayer(args *model.GetSameParamPlayerArgs, ret *[]int32) (err error) { + cplayerdata := PlayerDataCollection(args.Plt) + if cplayerdata == nil { + return + } + //临时结构 + var t []struct { + SnId int32 + } + err = cplayerdata.Find(bson.M{"bankaccount": args.Param}).Select(bson.M{"snid": 1}).All(&t) + if err != nil { + logger.Logger.Error("svc.GetSameBankCardPlayer is error", err) + return + } + + for _, value := range t { + *ret = append(*ret, value.SnId) + } + return +} + +func (svc *PlayerDataSvc) GetRobotPlayers(limit int, ret *[]*model.PlayerData) (err error) { + cplayerdata := PlayerDataCollection(mongo.G_P) + if cplayerdata == nil { + return + } + data := make([]*model.PlayerData, 0, limit) + err = cplayerdata.Find(bson.M{"isrob": true}).Limit(limit).All(&ret) + if err != nil { + logger.Logger.Error("svc.GetRobotPlayers is error", err) + return + } + + *ret = data + return +} + +/* + * 保存玩家的删除备份全部信息 + */ +func (svc *PlayerDelBackupDataSvc) SaveDelBackupPlayerData(pd *model.PlayerData, ret *bool) (err error) { + tPlayerDelBackup := PlayerDelBackupDataCollection(pd.Platform) + if tPlayerDelBackup == nil { + return nil + } + if pd != nil { + model.RecalcuPlayerCheckSum(pd) + _, err = tPlayerDelBackup.UpsertId(pd.Id, pd) + if err != nil { + logger.Logger.Errorf("svc.SaveDelBackupPlayerData %v err:%v", pd.SnId, err) + return + } + *ret = true + return + } + return +} + +func (svc *PlayerDelBackupDataSvc) GetPlayerData(req *model.PlayerIsExistBySnIdArgs, ret *model.PlayerData) error { + tPlayerDelBackup := PlayerDelBackupDataCollection(req.Plt) + if tPlayerDelBackup == nil { + return nil + } + return tPlayerDelBackup.Find(bson.M{"snid": req.SnId}).One(ret) +} + +func (svc *PlayerDataSvc) SetLogicLevel(args *model.LogicInfoArg, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return + } + cond := bson.M{"snid": bson.M{"$in": args.SnIds}} + var info *mgo.ChangeInfo + if args.LogicLevel == 0 { //清空所有分层信息 + info, err = cplayerdata.UpdateAll(cond, bson.D{{"$set", bson.D{{"logiclevels", []int32{}}}}}) + } else { + info, err = cplayerdata.UpdateAll(cond, bson.D{{"$addToSet", bson.D{{"logiclevels", args.LogicLevel}}}}) + } + if err != nil { + logger.Logger.Error("SetLogicLevel error ", err) + return + } + *ret = true + logger.Logger.Tracef("SetLogicLevel UpdataAll:%#v", info) + return +} + +func (svc *PlayerDataSvc) ClrLogicLevel(args *model.LogicInfoArg, ret *bool) (err error) { + cplayerdata := PlayerDataCollection(args.Platform) + if cplayerdata == nil { + return nil + } + cond := bson.M{"snid": bson.M{"$in": args.SnIds}} + info, err := cplayerdata.UpdateAll(cond, bson.D{{"$pull", bson.D{{"logiclevels", args.LogicLevel}}}}) + if err != nil { + logger.Logger.Error("ClrLogicLevel error ", err) + return err + } + *ret = true + logger.Logger.Tracef("ClrLogicLevel UpdataAll:%#v", info) + return nil +} + +func (svc *PlayerDataSvc) GetPlayerInviteSnid(req *model.PlayerIsExistBySnIdArgs, resp *int32) error { + c := PlayerDataCollection(req.Plt) + if c == nil { + return PlayerColError + } + + type t struct { + InviteSnId int32 + } + + res := &t{} + + err := c.Find(bson.M{"snid": req.SnId}).Select(bson.M{"invitesnid": 1}).One(res) + if err != nil && errors.Is(err, mgo.ErrNotFound) { + return err + } + + *resp = res.InviteSnId + + return nil +} + +func (svc *PlayerDataSvc) GetInviteNum(req *model.PlayerIsExistBySnIdArgs, resp *int32) error { + n, err := GetInviteNum(req.Plt, req.SnId) + if err != nil { + return err + } + *resp = n + return nil +} + +func GetInviteNum(platform string, snId int32) (int32, error) { + c := PlayerDataCollection(platform) + if c == nil { + return 0, PlayerColError + } + + n, err := c.Find(bson.M{"invitesnid": snId}).Count() + if err != nil { + return 0, err + } + + return int32(n), nil +} + +func BindInviteSnId(platform string, snId, inviteSnId int32, ts int64) error { + c := PlayerDataCollection(platform) + if c == nil { + return PlayerColError + } + + err := c.Update(bson.M{"snid": snId}, bson.M{"$set": bson.M{"invitesnid": inviteSnId}}) + if err != nil { + logger.Logger.Error("BindInviteSnId error ", err) + return err + } + + return nil +} + +func CorrectData(pd *model.PlayerData) bool { + //var coinTotal int64 + dirty := false + //金币冲账 + //coinlogs, err := GetCoinWALBySnidAndCoinTypeAndGreaterTs(pd.Platform, pd.SnId, PayCoinLogType_Coin, pd.CoinPayTs) + //if err == nil && len(coinlogs) != 0 { + // oldTs := pd.CoinPayTs + // oldCoin := pd.Coin + // var cnt int64 + // + // for i := 0; i < len(coinlogs); i++ { + // cnt = coinlogs[i].Count + // pd.Coin += cnt + // coinTotal += cnt + // + // if coinlogs[i].LogType == common.GainWay_ShopBuy { + // pd.CoinPayTotal += cnt + // if pd.TodayGameData == nil { + // pd.TodayGameData = model.NewPlayerGameCtrlData() + // } + // pd.TodayGameData.RechargeCoin += cnt //累加当天充值金额 + // } + // if coinlogs[i].Ts > pd.CoinPayTs { + // pd.CoinPayTs = coinlogs[i].Ts + // } + // dirty = true + // } + // + // newTs := pd.CoinPayTs + // newCoin := pd.Coin + // logger.Logger.Warnf("PlayerData(%v) CorrectData before:CoinPayTs=%v before:Coin=%v after:CoinPayTs=%v after:Coin=%v", pd.SnId, oldTs, oldCoin, newTs, newCoin) + //} + ////保险箱冲账 + //boxCoinLogs, err := GetCoinWALBySnidAndCoinTypeAndGreaterTs(pd.Platform, pd.SnId, PayCoinLogType_SafeBoxCoin, pd.SafeBoxCoinTs) + //if err == nil && len(boxCoinLogs) != 0 { + // oldTs := pd.SafeBoxCoinTs + // oldCoin := pd.SafeBoxCoin + // for i := 0; i < len(boxCoinLogs); i++ { + // pd.SafeBoxCoin += boxCoinLogs[i].Count + // coinTotal += boxCoinLogs[i].Count + // if boxCoinLogs[i].LogType == common.GainWay_ShopBuy { + // pd.CoinPayTotal += boxCoinLogs[i].Count + // if pd.TodayGameData == nil { + // pd.TodayGameData = model.NewPlayerGameCtrlData() + // } + // pd.TodayGameData.RechargeCoin += boxCoinLogs[i].Count //累加当天充值金额 + // } + // if boxCoinLogs[i].Ts > pd.SafeBoxCoinTs { + // pd.SafeBoxCoinTs = boxCoinLogs[i].Ts + // } + // dirty = true + // } + // + // newTs := pd.SafeBoxCoinTs + // newCoin := pd.SafeBoxCoin + // logger.Logger.Warnf("PlayerData(%v) CorrectData before:SafeBoxCoinTs=%v before:SafeBoxCoin=%v after:SafeBoxCoinTs=%v after:SafeBoxCoin=%v", pd.SnId, oldTs, oldCoin, newTs, newCoin) + //} + // + ////比赛入场券冲账 + //ticketLogs, err := GetCoinWALBySnidAndCoinTypeAndGreaterTs(pd.Platform, pd.SnId, PayCoinLogType_Ticket, pd.TicketPayTs) + //if err == nil && len(ticketLogs) != 0 { + // oldTs := pd.TicketPayTs + // oldTicket := pd.Ticket + // for i := 0; i < len(ticketLogs); i++ { + // pd.Ticket += ticketLogs[i].Count + // pd.TicketTotal += ticketLogs[i].Count + // if ticketLogs[i].Ts > pd.TicketPayTs { + // pd.TicketPayTs = ticketLogs[i].Ts + // } + // dirty = true + // } + // + // newTs := pd.TicketPayTs + // newTicket := pd.Ticket + // logger.Logger.Warnf("PlayerData(%v) CorrectData before:TicketPayTs=%v before:Ticket=%v after:TicketPayTs=%v after:Ticket=%v", pd.SnId, oldTs, oldTicket, newTs, newTicket) + //} + + //同步游服丢失的金币变化 + if SyncGameCoin(pd, 0, pd.GameCoinTs) { + dirty = true + } + //确保金币不小于0 + if pd.Coin < 0 { + logger.Logger.Warnf("PlayerData(%v) CorrectData found pd.Coin<0(%v)", pd.SnId, pd.Coin) + if pd.SafeBoxCoin > 0 { + pd.SafeBoxCoin += pd.Coin + if pd.SafeBoxCoin < 0 { + logger.Logger.Warnf("PlayerData(%v) CorrectData found pd.SafeBoxCoin<0(%v)", pd.SnId, pd.SafeBoxCoin) + pd.SafeBoxCoin = 0 + } + } + pd.Coin = 0 + dirty = true + } + + if dirty { + SavePlayerData(pd) + } + + return dirty +} + +func SyncGameCoin(pd *model.PlayerData, sceneid int, enterts int64) bool { + dirty := false + //游服金币冲账 + endts := time.Now().UnixNano() + gamecoinlogs, err := GetCoinWALBySnidAndInGameAndGreaterTs(pd.Platform, pd.SnId, int32(sceneid), enterts) + if err == nil && len(gamecoinlogs) != 0 { + oldCoin := pd.Coin + var cnt int64 + for i := 0; i < len(gamecoinlogs); i++ { + ts := gamecoinlogs[i].Ts + if ts >= enterts && ts <= endts { + cnt = gamecoinlogs[i].Count + pd.Coin += cnt + if ts > pd.GameCoinTs { + pd.GameCoinTs = ts + } + dirty = true + } + } + + newCoin := pd.Coin + newTs := pd.GameCoinTs + logger.Logger.Warnf("PlayerData(%v) SyncGameCoin before:enterts=%v before:Coin=%v after:GameCoinTs=%v after:Coin=%v", pd.SnId, enterts, oldCoin, newTs, newCoin) + } + return dirty +} + +func init() { + rpc.Register(&PlayerDataSvc{}) + rpc.Register(&PlayerDelBackupDataSvc{}) +} diff --git a/dbproxy/svc/u_playerbucketid.go b/dbproxy/svc/u_playerbucketid.go new file mode 100644 index 0000000..5979f83 --- /dev/null +++ b/dbproxy/svc/u_playerbucketid.go @@ -0,0 +1,174 @@ +package svc + +import ( + "errors" + "math/rand" + "sync" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +/* + 玩家id生成算法 +*/ + +const ( + PlayerBucketIDMin = 10000000 // 第一个玩家id + BatchPlayerBucketIDCnt = 99999999 // 玩家id自增步长 +) + +var ( + PlayerBucketIdsDBName = "user" + PlayerBucketIdsCollName = "user_bucketids" + PlayerBucketIdsAutoId = bson.ObjectIdHex("60f69b4a09dbe3323c632f25") // 记录id使用到多少了 + ErrPlayerBucketIdsDBNotOpen = model.NewDBError(PlayerBucketIdsDBName, PlayerBucketIdsCollName, model.NOT_OPEN) + playerBucketId = &model.PlayerBucketId{} + playerBucketIdsCursor = int32(0) + playerBucketIdLock = sync.Mutex{} +) + +type PlayerBucketAutoId struct { + Id bson.ObjectId `bson:"_id"` + AutoId int +} + +func PlayerBucketIdCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, PlayerBucketIdsDBName) + if s != nil { + c, first := s.DB().C(PlayerBucketIdsCollName) + if first { + var id PlayerBucketAutoId + err := c.Find(bson.M{"_id": PlayerBucketIdsAutoId}).One(&id) + if err != nil && errors.Is(err, mgo.ErrNotFound) { + id.Id = PlayerBucketIdsAutoId + id.AutoId = PlayerBucketIDMin + c.Insert(id) + } + } + return c + } + return nil +} + +// id 增加 +func autoIncPlayerBucketId() (int, error) { + c := PlayerBucketIdCollection() + if c == nil { + return 0, ErrPlayerBucketIdsDBNotOpen + } + change := mgo.Change{ + Update: bson.M{"$inc": bson.M{"autoid": BatchPlayerBucketIDCnt}}, + ReturnNew: true, + } + doc := PlayerBucketAutoId{} + _, err := c.Find(bson.M{"_id": PlayerBucketIdsAutoId}).Apply(change, &doc) + if err == nil { + return doc.AutoId, nil + } + return 0, err +} + +type PlayerBucketId struct { + Id bson.ObjectId `bson:"_id"` + StartPos int32 + EndPos int32 + Used bool +} + +func (receiver *PlayerBucketId) IsValid(cursor int32) bool { + return receiver.Id.Valid() && + receiver.StartPos != 0 && + receiver.EndPos != 0 && + receiver.StartPos != receiver.EndPos && + cursor >= receiver.StartPos && + cursor <= receiver.EndPos && + cursor != 0 +} + +// 生成一批玩家id +func genABatchPlayerBucketId() error { + maxV, err := autoIncPlayerBucketId() + if err != nil { + return err + } + var ( + minV = maxV - BatchPlayerBucketIDCnt + bucketSize = 100 // 最长100个玩家id是连续的 + bucketArr []*PlayerBucketId + ) + cnt := ((maxV - minV) / bucketSize) + 1 + bucketArr = make([]*PlayerBucketId, 0, cnt) + for i := minV; i < maxV; i = i + bucketSize { + bucketArr = append(bucketArr, &PlayerBucketId{ + Id: bson.NewObjectId(), + StartPos: int32(i), + EndPos: int32(i + bucketSize - 1), + Used: false, + }) + } + for i, _ := range bucketArr { + rnd := rand.Intn(len(bucketArr)) + bucketArr[i], bucketArr[rnd] = bucketArr[rnd], bucketArr[i] + } + c := PlayerBucketIdCollection() + docs := make([]interface{}, 0, len(bucketArr)) + for _, log := range bucketArr { + docs = append(docs, log) + } + if c != nil { + for len(docs) > 0 { + cnt := len(docs) + if cnt > 1000 { + cnt = 1000 + } + err = c.Insert(docs[:cnt]...) + docs = docs[cnt:] + } + } + return err +} + +// GetOnePlayerIdFromBucket 获取一个玩家id +func GetOnePlayerIdFromBucket() (pid int32, err error) { + playerBucketIdLock.Lock() + defer playerBucketIdLock.Unlock() + + if !playerBucketId.IsValid(playerBucketIdsCursor) { + var flag bool + c := PlayerBucketIdCollection() + if c != nil { + change := mgo.Change{ + Update: bson.M{"$set": bson.M{"used": true}}, + ReturnNew: true, + } + redo: + _, err := c.Find(bson.M{"used": false}).Apply(change, playerBucketId) + if err != nil { + logger.Logger.Warnf("GetOnePlayerIdFromBucket Find failed:%v", err) + if !flag && errors.Is(err, mgo.ErrNotFound) { + err = genABatchPlayerBucketId() + flag = true + if err == nil { + goto redo + } + } + return 0, err + } + err = c.RemoveId(playerBucketId.Id) + if err != nil { + logger.Logger.Warnf("GetOnePlayerIdFromBucket RemoveId(%v) failed:%v ", playerBucketId.Id, err) + } + playerBucketIdsCursor = playerBucketId.StartPos + } else { + return 0, ErrPlayerBucketIdsDBNotOpen + } + } + pid = playerBucketIdsCursor + playerBucketIdsCursor++ + return pid, nil +} diff --git a/dbproxy/svc/u_playersingleadjust.go b/dbproxy/svc/u_playersingleadjust.go new file mode 100644 index 0000000..4109f8d --- /dev/null +++ b/dbproxy/svc/u_playersingleadjust.go @@ -0,0 +1,100 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" +) + +// 单控数据表 +var ( + SingleAdjustDBName = "user" + SingleAdjustCollName = "user_singleadjust" + SingleAdjustColError = errors.New("SingleAdjust collection open failed") +) + +func SingleAdjustCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, SingleAdjustDBName) + if s != nil { + c_sj, first := s.DB().C(SingleAdjustCollName) + if first { + c_sj.EnsureIndex(mgo.Index{Key: []string{"platform", "snid", "gamefreeid"}, Unique: true, Background: true, Sparse: true}) + c_sj.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + c_sj.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + c_sj.EnsureIndex(mgo.Index{Key: []string{"gamefreeid"}, Background: true, Sparse: true}) + } + return c_sj + } + + return nil +} + +type SingleAdjustSvc struct { +} + +func (svc *SingleAdjustSvc) QueryAllSingleAdjust(platform string, ret *model.SingleAdjustRet) error { + c_sj := SingleAdjustCollection(platform) + if c_sj == nil { + return SingleAdjustColError + } + err := c_sj.Find(nil).All(&ret.Ret) + if err != nil { + return err + } + return nil +} +func (svc *SingleAdjustSvc) QueryAllSingleAdjustByKey(args *model.SingleAdjustByKey, ret *model.SingleAdjustRet) error { + c_sj := SingleAdjustCollection(args.Platform) + if c_sj == nil { + return SingleAdjustColError + } + err := c_sj.Find(bson.M{"platform": args.Platform, "snid": args.SnId}).All(&ret.Ret) + if err != nil { + logger.Logger.Warn("QueryAllSingleAdjustByKey is err: ", err) + return err + } + return nil +} +func (svc *SingleAdjustSvc) AddNewSingleAdjust(args *model.PlayerSingleAdjust, ret *bool) error { + c_sj := SingleAdjustCollection(args.Platform) + if c_sj == nil { + return SingleAdjustColError + } + err := c_sj.Insert(args) + if err != nil { + return err + } + *ret = true + return nil +} +func (svc *SingleAdjustSvc) EditSingleAdjust(args *model.PlayerSingleAdjust, ret *bool) error { + c_sj := SingleAdjustCollection(args.Platform) + if c_sj == nil { + return SingleAdjustColError + } + err := c_sj.Update(bson.M{"platform": args.Platform, "snid": args.SnId, "gamefreeid": args.GameFreeId}, args) + if err != nil { + return err + } + *ret = true + return nil +} +func (svc *SingleAdjustSvc) DeleteSingleAdjust(args *model.PlayerSingleAdjust, ret *bool) error { + c_sj := SingleAdjustCollection(args.Platform) + if c_sj == nil { + return SingleAdjustColError + } + err := c_sj.Remove(bson.M{"platform": args.Platform, "snid": args.SnId, "gamefreeid": args.GameFreeId}) + if err != nil { + return err + } + *ret = true + return nil +} +func init() { + rpc.Register(&SingleAdjustSvc{}) +} diff --git a/dbproxy/svc/u_rankdata.go b/dbproxy/svc/u_rankdata.go new file mode 100644 index 0000000..0cc7d9c --- /dev/null +++ b/dbproxy/svc/u_rankdata.go @@ -0,0 +1,61 @@ +package svc + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + //"mongo.games.com/goserver/core/logger" + "net/rpc" +) + +var ( + RankDBName = "user" + RankCollName = "user_rank" + RankDataDBErr = errors.New("user_rankdata db open failed.") +) + +func RankDataCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, RankDBName) + if s != nil { + c, first := s.DB().C(RankCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"key"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type RankDataSvc struct { +} + +func (svc *RankDataSvc) InitRankData(key string, ret *[]*model.RankData) (err error) { + s := RankDataCollection(key) + if s != nil { + err := s.Find(bson.M{}).All(ret) + if err != nil { + return err + } + + return nil + } + return nil +} + +func (svc *RankDataSvc) SaveRankData(args *model.SaveRankDataArgs, ret *bool) (err error) { + s := RankDataCollection(args.Plt) + if s == nil { + return RankDataDBErr + } + + _, err = s.Upsert(bson.M{"key": args.Key}, args.Data) + *ret = err == nil + + return err +} + +func init() { + rpc.Register(new(RankDataSvc)) +} diff --git a/dbproxy/svc/u_rankseason.go b/dbproxy/svc/u_rankseason.go new file mode 100644 index 0000000..0b65216 --- /dev/null +++ b/dbproxy/svc/u_rankseason.go @@ -0,0 +1,65 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + PlayerRankSeasonDBName = "user" + PlayerRankSeasonCollName = "user_rankseason" + PlayerRankSeasonColError = errors.New("PlayerRankSeason collection open failed") +) + +func PlayerRankSeasonCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, PlayerRankSeasonDBName) + if s != nil { + c, first := s.DB().C(PlayerRankSeasonCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +type PlayerRankSeasonSvc struct { +} + +func (svc *PlayerRankSeasonSvc) Upsert(args *model.PlayerRankSeason, ret *bool) error { + c := PlayerRankSeasonCollection(args.Platform) + if c == nil { + return PlayerRankSeasonColError + } + _, err := c.Upsert(bson.M{"snid": args.SnId}, args) + if err != nil { + logger.Logger.Error("PlayerRankSeasonSvc.Upsert is err: ", err) + return err + } + *ret = true + return nil +} + +func (svc *PlayerRankSeasonSvc) Find(args *model.FindPlayerRankSeasonArgs, ret *model.FindPlayerRankSeasonReply) error { + c := PlayerRankSeasonCollection(args.Platform) + if c == nil { + return PlayerRankSeasonColError + } + err := c.Find(bson.M{"snid": bson.D{{"$in", args.Id}}}).All(&ret.List) + if err != nil && !errors.Is(err, mgo.ErrNotFound) { + logger.Logger.Error("PlayerRankSeasonSvc.Find is err: ", err) + return err + } + return nil +} + +func init() { + rpc.Register(new(PlayerRankSeasonSvc)) +} diff --git a/dbproxy/svc/u_sysprofitcoin.go b/dbproxy/svc/u_sysprofitcoin.go new file mode 100644 index 0000000..08bad24 --- /dev/null +++ b/dbproxy/svc/u_sysprofitcoin.go @@ -0,0 +1,80 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" +) + +var ( + SysProfitCoinDBErr = errors.New("user_sysprofitcoin db open failed.") +) + +func SysProfitCoinCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.SysProfitCoinDBName) + if s != nil { + c, first := s.DB().C(model.SysProfitCoinCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"key"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InitSysProfitCoinData(key string) *model.SysProfitCoin { + s := SysProfitCoinCollection() + data := &model.SysProfitCoin{} + if s != nil { + err := s.Find(bson.M{"key": key}).One(data) + if err != nil { + if err.Error() == mgo.ErrNotFound.Error() { + data.LogId = bson.NewObjectId() + data.Key = key + data.ProfitCoin = make(map[string]*model.SysCoin) + s.Insert(data) + } else { + logger.Logger.Trace("InitSysProfitCoinData err:", err) + return nil + } + } + return data + } + return nil +} + +// 保存 +func SaveSysProfitCoin(data *model.SysProfitCoin) error { + s := SysProfitCoinCollection() + if s == nil { + return SysProfitCoinDBErr + } + + _, err := s.Upsert(bson.M{"_id": data.LogId}, data) + return err +} + +type SysProfitCoinSvc struct { +} + +func (svc *SysProfitCoinSvc) InitSysProfitCoinData(key string, ret **model.SysProfitCoin) (err error) { + *ret = InitSysProfitCoinData(key) + return nil +} + +func (svc *SysProfitCoinSvc) SaveSysProfitCoin(args *model.SysProfitCoin, ret *bool) (err error) { + err = SaveSysProfitCoin(args) + if err == nil { + *ret = true + } + return +} + +func init() { + rpc.Register(new(SysProfitCoinSvc)) +} diff --git a/dbproxy/svc/u_thdplatform.go b/dbproxy/svc/u_thdplatform.go new file mode 100644 index 0000000..7685afc --- /dev/null +++ b/dbproxy/svc/u_thdplatform.go @@ -0,0 +1,91 @@ +package svc + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "github.com/wendal/errors" + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net/rpc" + "time" +) + +var ( + PlatformOfThirdPlatformDBErr = errors.New("user_thdplatform open failed.") +) + +func ThdPlatformCollection() *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(mongo.G_P, model.ThdPlatformDBName) + if s != nil { + c, first := s.DB().C(model.ThdPlatformCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"platform"}, Background: true, Sparse: true}) + } + return c + } + return nil +} + +func InsertThirdPlatform(platforms ...*model.PlatformOfThirdPlatform) (err error) { + switch len(platforms) { + case 0: + return errors.New("no data") + case 1: + err = ThdPlatformCollection().Insert(platforms[0]) + default: + docs := make([]interface{}, 0, len(platforms)) + for _, p := range platforms { + docs = append(docs, p) + } + err = ThdPlatformCollection().Insert(docs...) + } + if err != nil { + logger.Logger.Warn("InsertThirdPlatform error:", err) + return + } + return +} + +func UpdateThirdPlatform(platform *model.PlatformOfThirdPlatform) (err error) { + platform.LastTime = time.Now() + err = ThdPlatformCollection().Update(bson.M{"_id": platform.Id}, platform) + if err != nil { + logger.Logger.Info("UpdateThirdPlatform to db failed.") + return err + } + return nil +} + +func GetAllThirdPlatform() (ret []model.PlatformOfThirdPlatform, err error) { + err = ThdPlatformCollection().Find(bson.M{}).All(&ret) + return +} + +type PlatformOfThirdPlatformSvc struct { +} + +func (svc *PlatformOfThirdPlatformSvc) InsertThirdPlatform(args []*model.PlatformOfThirdPlatform, ret *bool) (err error) { + err = InsertThirdPlatform(args...) + if err == nil { + *ret = true + } + return +} + +func (svc *PlatformOfThirdPlatformSvc) UpdateThirdPlatform(args *model.PlatformOfThirdPlatform, ret *bool) (err error) { + err = UpdateThirdPlatform(args) + if err == nil { + *ret = true + } + return +} + +func (svc *PlatformOfThirdPlatformSvc) GetAllThirdPlatform(args struct{}, ret *[]model.PlatformOfThirdPlatform) (err error) { + *ret, err = GetAllThirdPlatform() + return +} + +func init() { + rpc.Register(new(PlatformOfThirdPlatformSvc)) +} diff --git a/doc/todo.txt b/doc/todo.txt new file mode 100644 index 0000000..33208f7 --- /dev/null +++ b/doc/todo.txt @@ -0,0 +1,11 @@ +业务日志数据(如:帐变,牌局记录,登录日志等) + 1.rabitmq接口和实例整理 + 2.gamesrv日志类型的db写入,推送到rabitmqmq中,通过指定服务消费mq中的记录做真正写入处理 + 3.worldsrv日志类型的db写入,推送到rabitmqmq中,通过指定服务消费mq中的记录做真正写入处理 + +删除冗余代码 +worldsrv EtcdMgrSington 替换 +worldsrv 分包 +使用go mod +移除proto + diff --git a/doc/游戏结算统计.txt b/doc/游戏结算统计.txt new file mode 100644 index 0000000..8834999 --- /dev/null +++ b/doc/游戏结算统计.txt @@ -0,0 +1,7 @@ +游戏结算时需要调用的方法: +1.玩家游戏记录 +sceneEx.SaveGamePlayerListLog +2.牌局记录 +sceneEx.SaveGameDetailedLog +3.玩家游戏数据统计 +sceneEx.Statistics \ No newline at end of file diff --git a/doc/调控.txt b/doc/调控.txt new file mode 100644 index 0000000..e2f826b --- /dev/null +++ b/doc/调控.txt @@ -0,0 +1,53 @@ +调控生效规则 + 匹配场: + 调控生效:黑白名单,新手补偿,个人水池,场次水池 + 统计黑白名单玩家黑白名单独立输赢金额(相关方法Statistics) + 统计个人水池: 普通非黑白,非新手,非机器人(相关方法Statistics) + 统计场次池:将普通非黑白,非新手,非机器人玩家输赢金额放入水池,赢分扣税,输分不扣(相关方法Statistics) + + 比赛场: + 调控生效:无 + 统计黑白名单玩家黑白名单独立输赢金额:无 + 统计个人池:无 + 统计场次池:无 + + 私人房: + 调控生效:无 + 统计黑白名单玩家黑白名单独立输赢金额:无 + 统计个人池:无 + 统计场次池:无 + + 公共房有机器人: + 同匹配场 + + 公共房无机器人: + 调控生效:无 + 统计黑白名单玩家黑白名单独立输赢金额:无 + 统计个人池:无 + 统计场次池:无 + +输赢统计区分方式 + 黑白名单:不区分 + 新手调控:游戏id + 个人水池:游戏类型 + 场次水池:场次id + +调控相关方法 + +gamesrv + +// BlackWhiteOdds 黑白名单调控概率 +func (this *Player) BlackWhiteOdds(gameId int) (int32, bool) + +// NoviceOdds 新手补偿概率 +func (this *Player) NoviceOdds(gameId int) (int32, bool) + +// PlayerPoolOdds 个人水池概率 +func (this *Player) PlayerPoolOdds() int32 + +// Statistics 玩家游戏数据统计 +// 统计个人水池(个人赔率) +func (this *Scene) Statistics(param *StaticParam) + +// GetPlayerOdds 获取玩家发牌调控概率 +func (this *Scene) GetPlayerOdds(p *Player, gameId int, hasRobot bool) int32 \ No newline at end of file diff --git a/etcd/client.go b/etcd/client.go new file mode 100644 index 0000000..5b090f1 --- /dev/null +++ b/etcd/client.go @@ -0,0 +1,260 @@ +package etcd + +import ( + "context" + "time" + + "go.etcd.io/etcd/client/v3" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" +) + +/* + etcd常用操作和数据监听 +*/ + +type ( + InitFunc func() int64 + WatchFunc func(context.Context, int64) + FuncPair struct { + initFunc InitFunc + watchFunc WatchFunc + } +) + +type Client struct { + cli *clientv3.Client + functions []FuncPair + closed bool +} + +func (this *Client) IsClosed() bool { + return this.closed +} + +func (this *Client) Ctx() context.Context { + if this.cli != nil { + return this.cli.Ctx() + } + return context.TODO() +} + +func (this *Client) Open(etcdUrl []string, userName, passWord string, dialTimeout time.Duration) error { + var err error + + this.cli, err = clientv3.New(clientv3.Config{ + Endpoints: etcdUrl, + Username: userName, + Password: passWord, + DialTimeout: dialTimeout, + DialKeepAliveTime: 5 * time.Second, + DialKeepAliveTimeout: 30 * time.Second, + }) + + if err != nil { + logger.Logger.Errorf("EtcdClient.open(%v) err:%v", etcdUrl, err) + return err + } + + this.closed = false + return err +} + +func (this *Client) Close() error { + logger.Logger.Warn("EtcdClient.close") + this.closed = true + if this.cli != nil { + return this.cli.Close() + } + return nil +} + +// PutValue 添加键值对 +func (this *Client) PutValue(key, value string) (*clientv3.PutResponse, error) { + resp, err := this.cli.Put(context.TODO(), key, value) + if err != nil { + logger.Logger.Warnf("EtcdClient.PutValue(%v,%v) error:%v", key, value, err) + } + return resp, err +} + +// GetValue 查询 +func (this *Client) GetValue(key string) (*clientv3.GetResponse, error) { + resp, err := this.cli.Get(context.TODO(), key) + if err != nil { + logger.Logger.Warnf("EtcdClient.GetValue(%v) error:%v", key, err) + } + return resp, err +} + +// DelValue 返回删除了几条数据 +func (this *Client) DelValue(key string) (*clientv3.DeleteResponse, error) { + res, err := this.cli.Delete(context.TODO(), key) + if err != nil { + logger.Logger.Warnf("EtcdClient.DelValue(%v) error:%v", key, err) + } + return res, err +} + +// DelValueWithPrefix 按照前缀删除 +func (this *Client) DelValueWithPrefix(prefix string) (*clientv3.DeleteResponse, error) { + res, err := this.cli.Delete(context.TODO(), prefix, clientv3.WithPrefix()) + if err != nil { + logger.Logger.Warnf("EtcdClient.DelValueWithPrefix(%v) error:%v", prefix, err) + } + return res, err +} + +// GetValueWithPrefix 获取前缀 +func (this *Client) GetValueWithPrefix(prefix string) (*clientv3.GetResponse, error) { + resp, err := this.cli.Get(context.TODO(), prefix, clientv3.WithPrefix()) + if err != nil { + logger.Logger.Warnf("EtcdClient.GetValueWIthPrefix(%v) error:%v", prefix, err) + } + return resp, err +} + +// WatchWithPrefix 监测前缀创建事件 +func (this *Client) WatchWithPrefix(prefix string, revision int64) clientv3.WatchChan { + if this.cli != nil { + opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithCreatedNotify()} + if revision > 0 { + opts = append(opts, clientv3.WithRev(revision)) + } + return this.cli.Watch(clientv3.WithRequireLeader(context.Background()), prefix, opts...) + } + return nil +} + +// Compact 压缩数据 +//func (this *Client) Compact() { +// if this.closed { +// return +// } +// +// resp, err := this.GetValue("@@@GET_LASTEST_REVISION@@@") +// if err == nil { +// ctx, _ := context.WithCancel(this.cli.Ctx()) +// start := time.Now() +// compactResponse, err := this.cli.Compact(ctx, resp.Header.Revision, clientv3.WithCompactPhysical()) +// if err == nil { +// logger.Logger.Infof("EtcdClient.Compact From %v CompactResponse %v take %v", resp.Header.Revision, compactResponse.Header, time.Now().Sub(start)) +// } else { +// logger.Logger.Errorf("EtcdClient.Compact From %v CompactResponse:%v take:%v err:%v", resp.Header.Revision, compactResponse, time.Now().Sub(start), err) +// } +// endpoints := this.cli.Endpoints() +// for _, endpoint := range endpoints { +// ctx1, _ := context.WithCancel(this.cli.Ctx()) +// start := time.Now() +// defragmentResponse, err := this.cli.Defragment(ctx1, endpoint) +// if err == nil { +// logger.Logger.Infof("EtcdClient.Defragment %v,%v take %v", endpoint, defragmentResponse.Header, time.Now().Sub(start)) +// } else { +// logger.Logger.Errorf("EtcdClient.Defragment DefragmentResponse:%v take:%v err:%v", defragmentResponse, time.Now().Sub(start), err) +// } +// } +// } +//} + +// AddFunc 添加监听函数 +func (this *Client) AddFunc(initFunc InitFunc, watchFunc WatchFunc) { + funcPair := FuncPair{ + initFunc: initFunc, + watchFunc: watchFunc, + } + this.functions = append(this.functions, funcPair) +} + +// ReInitAndWatchAll 重新监听 +func (this *Client) ReInitAndWatchAll() { + if this.closed { + return + } + + oldFunc := this.functions + this.functions = nil + for i := 0; i < len(oldFunc); i++ { + this.InitAndWatch(oldFunc[i].initFunc, oldFunc[i].watchFunc) + } +} + +// InitAndWatch 开始监听 +func (this *Client) InitAndWatch(initFunc InitFunc, watchFunc WatchFunc) { + funcPair := FuncPair{ + initFunc: initFunc, + watchFunc: watchFunc, + } + this.functions = append(this.functions, funcPair) + lastRevision := initFunc() + ctx, _ := context.WithCancel(this.cli.Ctx()) + watchFunc(ctx, lastRevision+1) +} + +// GoWatch 异步监听 +func (this *Client) GoWatch(ctx context.Context, revision int64, prefix string, f func(res clientv3.WatchResponse) error) { + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Errorf("etcd watch WithPrefix(%v) panic:%v", prefix, err) + } + logger.Logger.Warnf("etcd watch WithPrefix(%v) quit!!!", prefix) + }() + var times int64 + for !this.closed { + times++ + logger.Logger.Warnf("etcd watch WithPrefix(%v) base revision %v start[%v]!!!", prefix, revision, times) + rch := this.WatchWithPrefix(prefix, revision) + for { + skip := false + select { + case _, ok := <-ctx.Done(): + if !ok { + return + } + case wresp, ok := <-rch: + if !ok { + logger.Logger.Warnf("etcd watch WithPrefix(%v) be closed", prefix) + skip = true + break + } + if wresp.Header.Revision > revision { + revision = wresp.Header.Revision + } + if wresp.Canceled { + logger.Logger.Warnf("etcd watch WithPrefix(%v) be closed, reason:%v", prefix, wresp.Err()) + skip = true + break + } + if err := wresp.Err(); err != nil { + logger.Logger.Warnf("etcd watch WithPrefix(%v) err:%v", prefix, wresp.Err()) + continue + } + if !model.GameParamData.UseEtcd { + continue + } + if len(wresp.Events) == 0 { + continue + } + + logger.Logger.Tracef("@goWatch %v changed, header:%#v", prefix, wresp.Header) + obj := core.CoreObject() + if obj != nil { + func(res clientv3.WatchResponse) { + obj.SendCommand(basic.CommandWrapper(func(*basic.Object) error { + return f(res) + }), true) + }(wresp) + } + } + + if skip { + break + } + } + time.Sleep(time.Second) + } + }() +} diff --git a/etcd/init.go b/etcd/init.go new file mode 100644 index 0000000..5050c32 --- /dev/null +++ b/etcd/init.go @@ -0,0 +1,17 @@ +package etcd + +import ( + "mongo.games.com/goserver/core" +) + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + mgr.Start() + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + mgr.Shutdown() + return nil + }) +} diff --git a/etcd/keyconf.go b/etcd/keyconf.go new file mode 100644 index 0000000..ae44b90 --- /dev/null +++ b/etcd/keyconf.go @@ -0,0 +1,55 @@ +package etcd + +const ( + // 系统配置 + ETCDKEY_SYS_ROOT_PREFIX = "/sys/" + ETCDKEY_SYS_PLT_DBCFG_PREFIX = "/sys/plt/dbcfg/" + + //业务配置 + ETCDKEY_ROOT_PREFIX = "/mongo.games.com/game/" + ETCDKEY_PLATFORM_PREFIX = "/mongo.games.com/game/plt/config/" + ETCDKEY_BULLETIN_PREFIX = "/mongo.games.com/game/plt/bulletin/" + ETCDKEY_AGENTCUSTOMER_PREFIX = "/mongo.games.com/game/plt/agent_customer/" + ETCDKEY_GAME_CONFIG_GLOBAL = "/mongo.games.com/game/plt/game_config_global" // 超管平台游戏开关 + ETCDKEY_GAMECONFIG_PREFIX = "/mongo.games.com/game/plt/game_config/" // 平台游戏配置 + ETCDKEY_PACKAGE_PREFIX = "/mongo.games.com/game/plt/package/" + ETCDKEY_GROUPCONFIG_PREFIX = "/mongo.games.com/game/group_config/" + ETCDKEY_BLACKLIST_PREFIX = "/mongo.games.com/game/plt/black_list/" + ETCDKEY_ACT_SIGNIN_PREFIX = "/mongo.games.com/game/activity/signin/" + ETCDKEY_ACT_TASK_PREFIX = "/mongo.games.com/game/activity/task/" + ETCDKEY_ACT_GOLDTASK_PREFIX = "/mongo.games.com/game/activity/goldtask/" + ETCDKEY_ACT_GOLDCOME_PREFIX = "/mongo.games.com/game/activity/goldcome/" + ETCDKEY_ACT_ONLINEREWARD_PREFIX = "/mongo.games.com/game/activity/onlinereward/" + ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX = "/mongo.games.com/game/activity/lucklyturntable/" + ETCDKEY_ACT_YEB_PREFIX = "/mongo.games.com/game/activity/yeb/" + ETCDKEY_CONFIG_REBATE = "/mongo.games.com/game/plt/game_rebate_config/" + ETCDKEY_PROMOTER_PREFIX = "/mongo.games.com/game/plt/promoter/" + ETCDKEY_ACT_VIP_PREFIX = "/mongo.games.com/game/plt/actvip/" + ETCDKEY_ACT_WEIXIN_SHARE_PREFIX = "/mongo.games.com/game/plt/actshare/" + ETCDKEY_ACT_GIVE_PREFIX = "/mongo.games.com/game/plt/actgive/" + ETCDKEY_ACT_PAY_PREFIX = "/mongo.games.com/game/plt/payact/" + ETCDKEY_ACT_RANDCOIN_PREFIX = "/mongo.games.com/game/plt/randcoin/" + ETCDKEY_ACT_FPAY_PREFIX = "/mongo.games.com/game/plt/fpay/" + ETCDKEY_PLATFORM_PROFITCONTROL = "/mongo.games.com/game/plt/profitcontrol/" + ETCDKEY_MATCH_PROFIX = "/mongo.games.com/game/match/" + ETCDKEY_ACT_TICKET_PROFIX = "/mongo.games.com/game/activity/ticket/" + ETCDKEY_ACT_TICKET_RUNNING = "/mongo.games.com/game/activity/ticket/running" + ETCDKEY_MATCH_GRADESHOP = "/mongo.games.com/game/match/gradeshop/" + ETCDKEY_CONFIG_LOGICLEVEL = "/mongo.games.com/game/logiclevel/" + ETCDKEY_SHOP_EXCHANGE = "/mongo.games.com/game/exchange_shop" + ETCDKEY_GAME_NOTICE = "/mongo.games.com/game/common_notice" + ETCDKEY_SHOP_ITEM = "/mongo.games.com/game/item_shop" + ETCDKEY_GAME_MATCH = "/mongo.games.com/game/game_match" + ETCDKEY_ACT_TURNPLATE = "/mongo.games.com/game/act_turnplate" + ETCDKEY_ACT_7SIGN = "/mongo.games.com/game/act_7sign" + ETCDKEY_ACT_BLINDBOX = "/mongo.games.com/game/act_blindbox" + ETCDKEY_ACT_FIRSTPAY = "/mongo.games.com/game/act_FirstPay" + ETCDKEY_ACT_CONTINUOUSPAY = "/mongo.games.com/game/act_ContinuousPay" + ETCDKEY_VIP_CFG = "/mongo.games.com/game/VIPcfg" + ETCDKEY_WBCtrl_CFG = "/mongo.games.com/game/WBCtrlCfg" + ETCDKEY_PACKAGE_ENTRYSWITCH = "/mongo.games.com/game/plt/entryswitch/" //界面入口开关 + ETCDKEY_CHESSRANK_CFG = "/mongo.games.com/game/plt/chessrank/" // 象棋段位配置 + ETCDKEY_PLAYERPOOL = "/mongo.games.com/game/plt/playerpool/" // 个人水池调控配置 + ETCDKEY_GAME_CONFIG = "/mongo.games.com/game/plt/gameconfig/" // 游戏管理/全局配置 + ETCDKEY_ACT_PHONELOTTERY = "/mongo.games.com/game/act_phoneLottery" +) diff --git a/etcd/manager.go b/etcd/manager.go new file mode 100644 index 0000000..0a5765d --- /dev/null +++ b/etcd/manager.go @@ -0,0 +1,123 @@ +package etcd + +import ( + "context" + "reflect" + "time" + + "go.etcd.io/etcd/client/v3" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" +) + +var mgr = &Manager{Client: new(Client)} + +type Manager struct { + *Client +} + +// Register . +func (this *Manager) Register(key string, msgType interface{}, f func(completeKey string, isInit bool, event *clientv3.Event, data interface{})) { + + createFunc := func() interface{} { + tp := reflect.TypeOf(msgType) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return reflect.New(tp).Interface() + } + + initFunc := func() int64 { + logger.Logger.Info("ETCD 拉取数据:", key) + res, err := this.GetValueWithPrefix(key) + if err == nil { + for i := int64(0); i < res.Count; i++ { + data := createFunc() + v, ok := data.(proto.Message) + if !ok { + logger.Logger.Errorf("ETCD %v error: not proto message", key) + continue + } + err := proto.Unmarshal(res.Kvs[i].Value, v) + if err != nil { + logger.Logger.Errorf("ETCD %v unmarshal error:%v", key, err) + continue + } + logger.Logger.Tracef("ETCD 拉取成功 %v ==> %v", string(res.Kvs[i].Key), v) + event := &clientv3.Event{ + Type: 0, + } + f(string(res.Kvs[i].Key), true, event, v) + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("ETCD get WithPrefix(%v) panic:%v", key, err) + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, key, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypePut: + data := createFunc() + v, ok := data.(proto.Message) + if !ok { + logger.Logger.Errorf("ETCD %v error: not proto message", string(ev.Kv.Key)) + continue + } + err := proto.Unmarshal(ev.Kv.Value, v) + if err != nil { + logger.Logger.Errorf("etcd unmarshal(%v) error:%v", string(ev.Kv.Key), err) + continue + } + logger.Logger.Tracef("ETCD 更新事件 %v ==> %v", string(ev.Kv.Key), v) + f(string(ev.Kv.Key), false, ev, v) + case clientv3.EventTypeDelete: + logger.Logger.Tracef("ETCD 删除事件 %v", string(ev.Kv.Key)) + f(string(ev.Kv.Key), false, ev, nil) + } + } + return nil + }) + } + + this.AddFunc(initFunc, watchFunc) +} + +func (this *Manager) Start() { + logger.Logger.Infof("EtcdClient开始连接url:%v;etcduser:%v;etcdpwd:%v", common.CustomConfig.GetStrings("etcdurl"), common.CustomConfig.GetString("etcduser"), common.CustomConfig.GetString("etcdpwd")) + err := this.Open(common.CustomConfig.GetStrings("etcdurl"), common.CustomConfig.GetString("etcduser"), common.CustomConfig.GetString("etcdpwd"), time.Minute) + if err != nil { + logger.Logger.Tracef("Manager.Open err:%v", err) + } + this.ReInitAndWatchAll() +} + +func (this *Manager) Shutdown() { + this.Close() +} + +func (this *Manager) Reset() { + this.Close() + this.Start() +} + +// Register 注册etcd监听方法 +// key:监听的key +// msgType:数据类型 +// f:数据变更回调方法, completeKey:完整键, isInit:第一次主动拉取数据,event:事件类型, data:已经反序列化的数据,类型为msgType +func Register(key string, msgType interface{}, f func(completeKey string, isInit bool, event *clientv3.Event, data interface{})) { + mgr.Register(key, msgType, f) +} + +func Reset() { + mgr.Reset() +} diff --git a/gamerule/avengers/avengers.go b/gamerule/avengers/avengers.go new file mode 100644 index 0000000..c0f541d --- /dev/null +++ b/gamerule/avengers/avengers.go @@ -0,0 +1,266 @@ +package avengers + +import ( + "fmt" + "math/rand" + "time" +) + +var SpinID int64 = 100000 // todo + +type LineData struct { + Index int + Element int + Count int + Score int + Position []int32 +} + +func isLine(data []int) (e int, count int) { + e = data[0] + count = 1 + for i := 1; i < len(data); i++ { + if data[i] == e { + count++ + } else { + break + } + } + if count < 3 { + return -1, -1 + } + return +} +func CalcLine(data []int, betLines []int64) (lines []LineData) { + var cards = make([]int, len(data)) + for i, v := range data { + cards[i] = v + } + for i := 10; i < 15; i++ { + if cards[i] == Element_WILD || cards[i-5] == Element_WILD || cards[i-10] == Element_WILD { + cards[i] = Element_WILD + cards[i-5] = Element_WILD + cards[i-10] = Element_WILD + } + } + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + normalData := []int{} + realData := []int{} + epos := []int32{} + if cards[lineTemplate[0]] == Element_FREESPIN || cards[lineTemplate[0]] == Element_BONUS { + continue + } + for _, pos := range lineTemplate { + edata = append(edata, cards[pos]) + if cards[pos] != Element_WILD { + normalData = append(normalData, cards[pos]) + } + realData = append(realData, data[pos]) + epos = append(epos, int32(pos+1)) + } + + if len(edata) == len(normalData) { + head, count := isLine(edata) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + } + } else { + normalData = DelSliceRepEle(normalData) + for _, value := range normalData { + replaceData := []int{} + for i := 0; i < len(edata); i++ { + if realData[0] == Element_JACKPOT { // WILD 不能替换 JACKPOT + replaceData = append(replaceData, realData[i]) + } else { + if edata[i] == Element_WILD { + replaceData = append(replaceData, value) + } else { + replaceData = append(replaceData, edata[i]) + } + } + } + head, count := isLine(replaceData) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + break + } + } + } + } + return +} + +//去除切片在的重复的元素 +func DelSliceRepEle(data []int) (res []int) { + if len(data) == 1 { + return data + } + eleFlag := make(map[int]bool) + for i := 0; i < len(data)-1; i++ { + if eleFlag[data[i]] { + continue + } + eleFlag[data[i]] = true + res = append(res, data[i]) + } + return res +} + +type CaclResult struct { + WinLines [][]LineData //多次中奖的线 + IsJackpot bool //是否爆奖 + AllScore int //总中奖倍率 + BonusScore int //小游戏奖励 + SpinFreeTimes int //免费旋转次数 +} + +func CaclScore(cards []int, betLines []int64) (int32, int32, int32, int32, bool) { + curscore := 0 + alllinenum := 0 //中奖线路总数 + allscore := 0 + spinFree := 0 + isJackpot := false + lines := CalcLine(cards, betLines) + for _, line := range lines { + if line.Element == Element_FREESPIN { + spinFree += FreeSpinTimesRate[line.Count] + continue + } + if line.Element == Element_JACKPOT && line.Count == LINE_CELL { + isJackpot = true + } + curscore = LineScore[line.Element][line.Count-1] + line.Score = curscore + allscore = allscore + curscore + alllinenum++ + } + bounsCount := 0 + for _, card := range cards { + if card == Element_BONUS { + bounsCount++ + } + if card == Element_JACKPOT { + spinFree++ + } + } + rand.Seed(time.Now().UnixNano()) + //bounsScore:= SmallGameByBouns[bounsCount][0]+rand.Intn(SmallGameByBouns[bounsCount][1]) + spinFreeTimes := FreeSpinTimesRate[spinFree] + return int32(alllinenum), int32(allscore), int32(0), int32(spinFreeTimes), isJackpot +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} + +//prizeFund > limit * roomId走这个检查 +func CheckBigWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD || slotsData[4] == Element_WILD || slotsData[9] == Element_WILD || slotsData[14] == Element_WILD { + return true + } + return false +} + +//prizeFund < limit * roomId走这个更严格的检查 +func CheckSuperWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD || slotsData[4] == Element_WILD || slotsData[9] == Element_WILD || slotsData[14] == Element_WILD { + return true + } + if (slotsData[1] == Element_WILD || slotsData[6] == Element_WILD || slotsData[11] == Element_WILD) && (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) { + return true + } + if (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) && (slotsData[3] == Element_WILD || slotsData[8] == Element_WILD || slotsData[13] == Element_WILD) { + return true + } + return false +} + +type Symbol int + +const ( + SYMBOL1 Symbol = iota + 1 // 现金池不足 + SYMBOL2 // 现金池充足 +) + +func GenerateSlotsData_v2(s Symbol) ([]int, int) { + var tryCount = 0 +Next: + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0, ELEMENT_TOTAL) + + for i := 0; i < ELEMENT_TOTAL; i++ { + if s == SYMBOL1 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1)-3)+3]) + } else { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1))]) + } + + } else if s == SYMBOL2 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2)-5)+5]) + } else { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2))]) + } + } + } + tryCount++ + if (s == SYMBOL1 && CheckSuperWin(slotsData)) || (s == SYMBOL2 && CheckBigWin(slotsData)) { + goto Next + } + return slotsData, tryCount +} + +type BonusGameResult struct { + BonusData []int64 //每次点击的显示奖金,有几个就点击几次,最后一次点击是0 + DataMultiplier int64 //第一级界面小游戏奖金总和 + Mutiplier int //最终小游戏的倍率 + TotalPrizeValue int64 //最终小游戏的奖励 +} + +var gSeedV = time.Now().UnixNano() + +func GenerateBonusGame(totalBet int, startBonus int) BonusGameResult { + if totalBet <= 0 || startBonus <= 0 { + return BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + } + } + var bg = BonusGameResult{ + BonusData: make([]int64, 0), + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: int64(0), + } + gSeedV++ + rand.Seed(gSeedV) + for _, e := range BonusStepArr { + rnd := rand.Intn(len(e)) + prizeValue := int64(e[rnd]*float64(totalBet) + 0.00001) + bg.TotalPrizeValue += prizeValue + bg.DataMultiplier += prizeValue + bg.BonusData = append(bg.BonusData, prizeValue) + if prizeValue == 0 { + break + } + } + bg.Mutiplier = startBonus + rand.Intn(3) + bg.TotalPrizeValue *= int64(bg.Mutiplier) + return bg +} diff --git a/gamerule/avengers/avengers_test.go b/gamerule/avengers/avengers_test.go new file mode 100644 index 0000000..ab350e8 --- /dev/null +++ b/gamerule/avengers/avengers_test.go @@ -0,0 +1,298 @@ +package avengers + +import ( + "fmt" + "math/rand" + "sort" + "testing" + "time" +) + +func TestIsLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{0, 0, 0, 1, 1}, line: 3}, + {data: []int{0, 0, 0, 0, 1}, line: 4}, + {data: []int{0, 0, 0, 0, 0}, line: 5}, + } + for _, value := range testData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error line data:", value) + t.Fatal("TestIsLine") + } + } + errorData := []TestData{ + {data: []int{1, 0, 0, 0, 1}, line: -1}, + {data: []int{1, 1, 0, 0, 0}, line: -1}, + {data: []int{1, 1, 0, 1, 1}, line: -1}, + } + for _, value := range errorData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error data:", value) + t.Fatal("TestIsLine") + } + } +} +func TestCalcLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{2, 1, 3, 4, 1, 5, 6, 1, 7, 8, 1, 0, 8, 1, 7}, line: 1}, + {data: []int{6, 7, 5, 7, 6, 5, 6, 8, 8, 7, 7, 7, 6, 2, 9}, line: 1}, + {data: []int{10, 5, 9, 1, 9, 2, 7, 9, 9, 9, 5, 3, 2, 2, 9}, line: 4}, + {data: []int{3, 10, 6, 1, 3, 7, 3, 3, 3, 3, 7, 6, 10, 8, 0}, line: 6}, + {data: []int{9, 7, 10, 1, 5, 10, 4, 6, 9, 5, 7, 5, 1, 2, 4}, line: 2}, + {data: []int{9, 3, 0, 2, 0, 0, 3, 9, 0, 5, 9, 2, 2, 8, 2}, line: 2}, + } + for _, value := range testData { + lines := CalcLine(value.data, AllBetLines) + if len(lines) != value.line { + t.Log("lines:", lines) + t.Log("Error line data:", value.data) + t.Fatal("TestIsLine") + } + } +} +func TestCalcLineScore(t *testing.T) { + lines := CalcLine([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}, AllBetLines) + t.Log(lines) + line, allscore, _, _, _ := CaclScore([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}, AllBetLines) + PrintHuman([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}) + t.Logf("lineNum:%v allScore:%v", line, allscore) + t.Fatal("TestCalcLineScore") +} + +func TestRandCalcLineScore(t *testing.T) { + var cards, c = generateSlotsData() + t.Logf("尝试次数:%v次,Data:%v", c, cards) + PrintHuman(cards) + + //lines := CalcLine(cards, AllBetLines) + //for _, line := range lines { + // t.Log(AllLineDraw[line.Index]) + //} + // + //t.Log(lines) + //line, allscore, _, _, _ := CaclScore(cards, AllBetLines) + //t.Logf("lineNum:%v allScore:%v", line, allscore) + //t.Fatal("TestRandCalcLineScore") +} +func TestDelSliceRepEle(t *testing.T) { + type TestData struct { + data []int + rdata []int + } + testData := []TestData{ + {data: []int{1}, rdata: []int{1}}, + {data: []int{1, 2}, rdata: []int{1, 2}}, + {data: []int{1, 1, 2}, rdata: []int{1, 2}}, + {data: []int{1, 2, 2}, rdata: []int{1, 2}}, + {data: []int{1, 2, 2, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 2, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 1, 2, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 1, 1, 1}, rdata: []int{1}}, + } + for _, value := range testData { + rdata := DelSliceRepEle(value.data) + if !SliceEqual(rdata, value.rdata) { + t.Error(value.data) + t.Error(rdata) + t.Fatal("TestDelSliceRepEle") + } + } +} + +/* + * 切片是否相等 + */ +func SliceEqual(left []int, right []int) bool { + if len(left) != len(right) { + return false + } + sort.Ints(left) + sort.Ints(right) + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} + +//func TestRollNormalElement(t *testing.T) { +// card := make([]int, ROLLLINEMAX, ROLLLINEMAX) +// count := 100000 +// now := time.Now() +// for i := 0; i < count; i++ { +// card = RollNormalElement(card) +// } +// t.Logf("RollNormalElement rand %v data cost %v second.", count, time.Now().Sub(now).Seconds()) +//} +//func TestRollFreeElement(t *testing.T) { +// card := make([]int, ROLLLINEMAX, ROLLLINEMAX) +// count := 100000 +// now := time.Now() +// for i := 0; i < count; i++ { +// card = RollFreeElement(card, rand.Intn(2)) +// } +// t.Logf("RollFreeElement rand %v data cost %v second.", count, time.Now().Sub(now).Seconds()) +//} + +func generateSlotsData() ([]int, int) { + var tryCount = 0 +Next: + var slotsData = make([]int, 0, ELEMENT_TOTAL) + rand.Seed(time.Now().UnixNano()) + for i := 0; i < ELEMENT_TOTAL; i++ { + slotsData = append(slotsData, symbolSmall[rand.Intn(len(symbolSmall))]) + } + tryCount++ + fmt.Print("tryCount:", tryCount) + if CheckBigWin(slotsData) { + goto Next + } + return slotsData, tryCount +} + +var symbolSmall = []int{1, 2, 3, 3, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 7, 4, 4, 4, 4, 4, 1, 1, 5, 5, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10} + +//随机取元素(从不同的分布里面取) + +//该玩家的投入产出比来决定是否会出现两个通配同时还与水池有关 +//水池过多的时候可以允许两个通配 +//正常水池只允许一个通配 +//水池太低则从牌库里面取不中奖的数据 + +func TestGenerateBonusGame(t *testing.T) { + type args struct { + totalBet int + startBonus int + } + tests := []struct { + name string + args args + want BonusGameResult + }{ + { + name: "bet:100 bonus*3", + args: args{ + totalBet: 100, + startBonus: 1, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:100 bonus*5", + args: args{ + totalBet: 100, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:1000 bonus*2", + args: args{ + totalBet: 100, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:1000 bonus*0", + args: args{ + totalBet: 1000, + startBonus: 0, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:0 bonus*3", + args: args{ + totalBet: 0, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := GenerateBonusGame(tt.args.totalBet, tt.args.startBonus) + t.Logf("BonusData:%v Len:%v", r.BonusData, len(r.BonusData)) + t.Logf("TotalPrizeValue:%v", r.TotalPrizeValue) + t.Logf("Mutiplier:%v", r.Mutiplier) + t.Logf("DataMultiplier:%v", r.DataMultiplier) + }) + } +} + +func TestGenerateSlotsData_v2(t *testing.T) { + type args struct { + s Symbol + } + tests := []struct { + name string + args args + want []int + want1 int + }{ + { + name: "01", + args: args{ + s: SYMBOL1, + }, + want: nil, + want1: 0, + }, + { + name: "02", + args: args{ + s: SYMBOL2, + }, + want: nil, + want1: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v, times := GenerateSlotsData_v2(tt.args.s) + t.Logf("GenerateSlotsData_v2() data = %v, times %v", v, times) + lines := CalcLine(v, AllBetLines) + t.Logf("lines:%v", lines) + PrintHuman(v) + CaclScore(v, AllBetLines) + }) + } +} diff --git a/gamerule/avengers/constants.go b/gamerule/avengers/constants.go new file mode 100644 index 0000000..eb51463 --- /dev/null +++ b/gamerule/avengers/constants.go @@ -0,0 +1,165 @@ +package avengers + +// 复仇者联盟 +const ( + Element_WILD int = iota + 1 //1 通配 + Element_FREESPIN //2 免费旋转 + Element_BONUS //3 奖金 + Element_JACKPOT //4 奖池 + Element_SPIDERMAN //5 蜘蛛侠 + Element_SCARLETT //6 黑寡妇 + Element_GROOT //7 Groot + Element_EYE //8 眼睛 + Element_SHIELD //9 盾牌 + Element_HAMMER //10 雷霆之锤 + Element_DIAMOND //11 钻石 + Element_Max +) + +var Element_NAME_MAP = map[int]string{ + Element_WILD: "[ * ]", + Element_FREESPIN: "[ FREE ]", + Element_BONUS: "[ BONUS ]", + Element_JACKPOT: "[ JACKPOT]", + Element_SPIDERMAN: "[ 蜘蛛侠 ]", + Element_SCARLETT: "[ 黑寡妇 ]", + Element_GROOT: "[ Groot ]", + Element_EYE: "[ EYE ]", + Element_SHIELD: "[ 盾牌 ]", + Element_HAMMER: "[ 雷霆之锤 ]", + Element_DIAMOND: "[ 钻石 ]", + -1: "[ - ]", +} + +const LINE_ROW int = 3 //行数 +const LINE_CELL int = 5 //列数 +const LINENUM int = 25 //线条数 +const ELEMENT_TOTAL = LINE_ROW * LINE_CELL + +// 所有元素对应的赔率 +var LineScore = [Element_Max][LINE_CELL]int{ + {0, 0, 0, 0, 0}, //占位 + {0, 0, 0, 0, 0}, //* + {0, 0, 0, 0, 0}, //FreeSpin + {0, 0, 0, 0, 0}, //Bouns + {0, 0, 50, 200, 0}, //Jackpot + {0, 0, 15, 100, 200}, //Spiderman + {0, 0, 10, 55, 150}, //黑寡妇 + {0, 0, 10, 40, 100}, //groot + {0, 0, 5, 30, 70}, //眼睛 + {0, 0, 5, 25, 55}, //盾牌 + {0, 0, 5, 15, 40}, //雷霆之锤 + {0, 0, 5, 10, 30}, //钻石 +} + +/* 所有线条数组 + * 0 1 2 3 4 + * 5 6 7 8 9 + * 10 11 12 13 14 + */ +var AllLineArray = [][]int{ + {5, 6, 7, 8, 9}, //线条1 + {0, 1, 2, 3, 4}, //线条2 + {10, 11, 12, 13, 14}, //线条3 + {10, 6, 2, 8, 14}, //线条4 + {0, 6, 12, 8, 4}, //线条5 + {5, 1, 2, 3, 9}, //线条6 + {5, 11, 12, 13, 9}, //线条7 + {0, 1, 7, 13, 14}, //线条8 + {10, 11, 7, 3, 4}, //线条9 + {5, 11, 7, 3, 9}, //线条10 + {5, 1, 7, 13, 9}, //线条11 + {0, 6, 7, 8, 4}, //线条12 + {10, 6, 7, 8, 14}, //线条13 + {0, 6, 2, 8, 4}, //线条14 + {10, 6, 12, 8, 14}, //线条15 + {5, 6, 2, 8, 9}, //线条16 + {5, 6, 12, 8, 9}, //线条17 + {0, 1, 12, 3, 4}, //线条18 + {10, 11, 2, 13, 14}, //线条19 + {0, 11, 12, 13, 4}, //线条20 + {10, 1, 2, 3, 14}, //线条21 + {5, 1, 12, 3, 9}, //线条22 + {5, 11, 2, 13, 9}, //线条23 + {0, 11, 2, 13, 4}, //线条24 + {10, 1, 12, 3, 14}, //线条25 +} + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25} + +// 免费次数奖励 +var FreeSpinTimesRate = [LINE_CELL]int{0, 0, 1, 5, 15} + +const BonusStepNum = 12 + +var BonusStepArr = [BonusStepNum][]float64{ + {0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0, 2.0}, + {0.0, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0}, + {0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.5, 1.5, 2.0}, + {0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.5, 2.0}, + {0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.5, 2.0}, + {0.0, 0.0, 0.0, 0.5, 1.0, 1.5, 2.0}, + {0.0, 0.0, 0.0, 0.5, 1.0, 1.5}, + {0.0, 0.0, 0.0, 0.5, 2.0}, + {0.0, 0.0, 0.0, 1.0}, + {0.0, 0.0, 1.5}, + {0.0, 1.5}, + {0.0}, +} + +// ver 2 +var symbol1 = []int{ + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, + 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +} +var symbol2 = []int{ + 1, 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, +} + +var MissData = [][]int{ + {11, 7, 8, 9, 9, 3, 11, 10, 11, 5, 11, 4, 9, 11, 11}, + {9, 4, 10, 10, 9, 7, 8, 9, 8, 10, 2, 10, 8, 9, 8}, + {6, 11, 11, 10, 9, 10, 7, 2, 2, 8, 6, 5, 9, 5, 11}, + {5, 7, 4, 11, 10, 6, 5, 9, 9, 8, 10, 8, 10, 7, 9}, + {7, 8, 1, 4, 11, 11, 6, 8, 11, 3, 3, 9, 10, 10, 9}, + {10, 11, 6, 9, 2, 11, 4, 7, 4, 9, 9, 8, 8, 11, 10}, + {11, 8, 10, 8, 10, 3, 10, 1, 10, 9, 11, 4, 8, 4, 10}, + {6, 7, 10, 9, 9, 11, 10, 9, 8, 10, 8, 4, 11, 4, 9}, + {8, 2, 6, 9, 7, 10, 11, 7, 4, 11, 6, 9, 8, 11, 10}, + {8, 7, 4, 10, 3, 10, 5, 9, 5, 6, 6, 8, 10, 11, 11}, + {10, 8, 8, 9, 10, 6, 11, 11, 11, 2, 10, 4, 11, 11, 9}, + {5, 7, 11, 7, 9, 2, 10, 9, 11, 5, 10, 4, 4, 10, 11}, + {4, 7, 1, 4, 9, 9, 5, 6, 11, 8, 6, 8, 7, 10, 11}, + {7, 11, 11, 9, 7, 8, 4, 10, 8, 9, 10, 9, 11, 4, 10}, + {4, 8, 9, 9, 11, 10, 11, 1, 4, 8, 9, 4, 10, 11, 7}, + {6, 10, 11, 5, 11, 10, 6, 9, 11, 7, 6, 1, 4, 10, 11}, + {5, 4, 9, 4, 7, 6, 8, 1, 11, 9, 10, 11, 10, 10, 10}, + {11, 5, 4, 6, 9, 3, 2, 11, 3, 5, 5, 8, 11, 11, 11}, + {4, 7, 11, 1, 9, 8, 5, 3, 10, 10, 11, 8, 5, 7, 9}, +} + +// jack params +const ( + AVENGERS_JACKPOT_InitJackpot int = iota //初始化奖池数量 + AVENGERS_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赚取投注的多少倍 + AVENGERS_JACKPOT_LIMITWIN_PRIZEHIGH //现金池充足时 最多赚取投注的多少倍 +) diff --git a/gamerule/baccarat/1_test.go b/gamerule/baccarat/1_test.go new file mode 100644 index 0000000..77bc590 --- /dev/null +++ b/gamerule/baccarat/1_test.go @@ -0,0 +1,91 @@ +package baccarat + +import ( + "fmt" + "testing" +) + +func TestExample(t *testing.T) { + //p := NewPoker() + //cards := p.GetTIE() + //PrintNewCards(cards, 1, p) + //cards = p.GetBankerWin() + //PrintNewCards(cards, 2, p) + //cards = p.GetXianWin() + //PrintNewCards(cards, 3, p) + //cards = p.GetBankerAndBankerPair() + //PrintNewCards(cards, 4, p) + //cards = p.GetBankerAndXianPair() + //PrintNewCards(cards, 5, p) + //cards = p.GetXianAndXianPair() + //PrintNewCards(cards, 6, p) + //cards = p.GetXianAndBankerPair() + //PrintNewCards(cards, 7, p) + //cards = p.GetBankerAndBankerXianPair() + //PrintNewCards(cards, 8, p) + //cards = p.GetXianAndBankerXianPair() + //PrintNewCards(cards, 9, p) + //cards = p.GetTieAndBankerPair() + //PrintNewCards(cards, 10, p) + //cards = p.GetTieAndXianPair() + //PrintNewCards(cards, 11, p) + //cards = p.GetTieAndBankerXianPair() + //PrintNewCards(cards, 12, p) + + //cards := []int32{7, 7, -1, 5, 11, -1} + //PrintNewCards(cards, 1, p) +} +func PrintNewCards(cs []int32, n int, p *Poker) { + if len(cs) == 0 { + fmt.Println(" n ") + return + } + PrintTheCards(cs, n) + r := false + ncs := make([]int32, 0, 0) + r, ncs = p.SingleRepairCard(cs) + fmt.Println(r) + if r { + cs = ncs + PrintTheCards(cs, n) + } + PrintTheCards2(cs, n) + fmt.Println("==========================================") + cs = []int32{-1, -1, -1, -1, -1, -1} +} +func PrintTheCards(cs []int32, n int) { + if len(cs) == 0 { + fmt.Println(" ") + return + } + fmt.Print(n, ": ") + for i := 0; i < 6; i++ { + if i == 3 { + fmt.Print(" ") + } + if cs[i] != -1 { + fmt.Print(cs[i]%13+1, " ") + } else { + fmt.Print(-1, " ") + } + } + fmt.Println(" ") +} +func PrintTheCards2(cs []int32, n int) { + if len(cs) == 0 { + fmt.Println(" ") + return + } + fmt.Print(n, ": ") + for i := 0; i < 6; i++ { + if i == 3 { + fmt.Print(" ") + } + if cs[i] != -1 { + fmt.Print(cs[i]%52, " ") + } else { + fmt.Print(-1, " ") + } + } + fmt.Println(" ") +} diff --git a/gamerule/baccarat/constants.go b/gamerule/baccarat/constants.go new file mode 100644 index 0000000..5c054f7 --- /dev/null +++ b/gamerule/baccarat/constants.go @@ -0,0 +1,56 @@ +package baccarat + +import "time" + +//////////////////////////////////////////////////////////////////////////////// +//场景状态 +//////////////////////////////////////////////////////////////////////////////// +const ( + BaccaratSceneStateStakeAnt int = iota //准备押注状态 + BaccaratSceneStateStake //押注状态 + BaccaratSceneStateOpenCardAnt //准备开牌状态 + BaccaratSceneStateOpenCard //开牌状态 + BaccaratSceneStateBilled //结算状态 + BaccaratSceneStateMax +) + +//////////////////////////////////////////////////////////////////////////////// +//百家乐超时设置 +//////////////////////////////////////////////////////////////////////////////// +const ( + BaccaratStakeAntTimeout = time.Second * 2 //准备押注 + BaccaratStakeTimeout = time.Second * 11 //押注 + BaccaratOpenCardAntTimeout = time.Second * 1 //准备 + BaccaratOpenCardTimeout = time.Second * 9 //开牌 + BaccaratBilledTimeout = time.Second * 5 //结算 + BaccaratRecordTime = 5 //回收金币记录时间 + BaccaratBatchSendBetTimeout = time.Second * 1 //发送下注数据时间间隔 +) + +//////////////////////////////////////////////////////////////////////////////// +//玩家操作,玩家也就两个操作 +//////////////////////////////////////////////////////////////////////////////// +const ( + BaccaratPlayerOpBet int = iota //下注 + BaccaratPlayerOpGetOLList //获取在线列表 + BaccaratPlayerOpUpBanker //上庄 + BaccaratPlayerOpNowDwonBanker //在庄的下庄 + BaccaratPlayerOpUpList //上庄列表 + BaccaratPlayerOpDwonBanker //下庄 +) + +const ( + GameId_Baccarat = 35 +) + +//////////////////////////////////////////////////////////////////////////////// +//百家乐下注区域,注意是位标记 +//////////////////////////////////////////////////////////////////////////////// +const ( + BACCARAT_ZONE_TIE int = 1 << iota //和1 + BACCARAT_ZONE_BANKER //庄家2 + BACCARAT_ZONE_PLAYER //闲家4 + BACCARAT_ZONE_BANKER_DOUBLE //庄家对8 + BACCARAT_ZONE_PLAYER_DOUBLE //闲家对16 + BACCARAT_ZONE_MAX +) diff --git a/gamerule/baccarat/poker.go b/gamerule/baccarat/poker.go new file mode 100644 index 0000000..5177909 --- /dev/null +++ b/gamerule/baccarat/poker.go @@ -0,0 +1,195 @@ +package baccarat + +import ( + "math/rand" + "time" +) + +const ( + POKER_CART_CNT int = 52 + PER_CARD_COLOR_MAX = 13 +) + +//随机数种子,在连续随机时,1纳秒内就要计算多次 +//如果使用UnixNano()则会造成不是真正的随机数,在这里需要采用自增的形式 +var BaccaratRandSeed = time.Now().UnixNano() + +type Poker struct { + buf []int32 + pos int +} + +func NewPoker() *Poker { + p := &Poker{} + //BaccaratRandSeed = time.Now().UnixNano() + //rand.Seed(BaccaratRandSeed) + BaccaratRandSeed++ + rand.Seed(BaccaratRandSeed) + p.Shuffle() + return p +} + +//将n副牌洗在一起 +func (this *Poker) Shuffle() { + //6-8随机 + n := (rand.Intn(3) + 6) * POKER_CART_CNT + this.buf = nil + for i := 0; i < n; i++ { + this.buf = append(this.buf, int32(i)) + } + for i := 0; i < n; i++ { + j := rand.Intn(i + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} + +//这个地方的洗牌只会对未发的牌操作,已发的牌是不会变的 +func (this *Poker) ShuffleNumCard(n int) { + //BaccaratRandSeed = atomic.AddInt64(&BaccaratRandSeed, 1) + //rand.Seed(BaccaratRandSeed) + l := len(this.buf) + for i := this.pos; i < this.pos+n; i++ { + j := this.pos + rand.Intn(l-this.pos-1) + 1 + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} + +//随机拿牌 +func (this *Poker) Next() (int32, bool) { + flag := false + if len(this.buf) < 6 { + flag = true + this.Shuffle() + } + n := rand.Intn(len(this.buf)) + c := this.buf[n] % int32(POKER_CART_CNT) + this.buf = append(this.buf[:n], this.buf[n+1:]...) + return c, flag +} +func (this *Poker) FindCard(c int32) (nc int32) { + nc = -1 + for k, v := range this.buf { + num := v%13 + 1 + if c == num || (num > 10 && c == 0) { + nc = v % int32(POKER_CART_CNT) + this.buf = append(this.buf[:k], this.buf[k+1:]...) + break + } + } + return +} + +//返还牌 +func (this *Poker) PutIn(c []int32) { + for _, v := range c { + if v != -1 { + this.buf = append(this.buf, v) + } + } +} + +//拿出一组牌 +func (this *Poker) TakeOut(c []int32) { + if len(c) == 0 { + return + } + for _, v := range c { + for m, n := range this.buf { + if v == n { + this.buf = append(this.buf[:m], this.buf[m+1:]...) + break + } + } + } +} + +func (this *Poker) TryNextN(n int) int32 { + if this.pos+n >= len(this.buf) { + return -1 + } + return this.buf[this.pos+n] +} + +func (this *Poker) ChangeNextN(n int, c int32) bool { + if this.pos+n >= len(this.buf) { + return false + } + this.buf[this.pos+n] = c + return true +} + +func (this *Poker) Count() int { + return len(this.buf) +} + +//card为-1则不在统计 +func GetPointNum(cards []int32, pos ...int) int32 { + value := int32(0) + for _, e := range pos { + temp := cards[e]%13 + 1 + if temp > 0 && temp < 10 { + value += temp + if value >= 10 { + value -= 10 + } + } + } + return int32(value) +} + +//分析是否满足闲家补一张的条件 +func (this *Poker) IsNeedPlayerAndOne(cards []int32) bool { + player_point := GetPointNum(cards, 0, 1) + if player_point < 6 { + banker_point := GetPointNum(cards, 3, 4) + if banker_point == 8 || banker_point == 9 { + return false + } + return true + } + return false +} + +//分析是否满足庄家补一张的条件 +func (this *Poker) IsNeedBankerAndOne(cards []int32) bool { + player_point := GetPointNum(cards, 0, 1) + if player_point == 8 || player_point == 9 { + return false + } + //闲没补牌 player_card_value结果为0 所以更改10的值 + player_card_value := int32(-1) + if cards[2]%13 != -1 { + player_card_value = cards[2]%13 + 1 + } + if player_card_value >= 10 { + player_card_value = 0 + } + banker_point := GetPointNum(cards, 3, 4) + switch banker_point { + case 3: + if player_card_value == 8 { + return false + } + case 4: + if player_card_value == 8 || player_card_value == 9 || + player_card_value == 0 { + return false + } + case 5: + if player_card_value == 1 || player_card_value == 2 || + player_card_value == 3 || player_card_value == 8 || + player_card_value == 9 || player_card_value == 0 { + return false + } + case 6: + if player_card_value != 6 && player_card_value != 7 { + return false + } + case 7: + return false + case 8, 9: + //只要有一个是天王就不用补牌 + return false + } + return true +} diff --git a/gamerule/baccarat/poker_test.go b/gamerule/baccarat/poker_test.go new file mode 100644 index 0000000..60959e2 --- /dev/null +++ b/gamerule/baccarat/poker_test.go @@ -0,0 +1,60 @@ +package baccarat + +import ( + "testing" +) + +//测试发牌、洗牌、点数 +func TestPoker(t *testing.T) { + p := NewPoker() + //t.Log(p.TryNextN(0)) + //t.Log(p.Next()) + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } + //for k,e := range p.buf { + // t.Log("牌值:",e) + // t.Log("牌点数:",e%13) + // t.Log("百家乐中点数:",GetPointNum(p.buf,k)) + //} + + //局部洗牌 + p.ShuffleNumCard(6) + + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } + //局部洗牌 + p.ShuffleNumCard(6) + + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } + //局部洗牌 + p.ShuffleNumCard(6) + + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } +} + +//测试快照 +func TestSnapshot(t *testing.T) { + p := NewPoker() + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } + + //x := p.Snapshot() + //局部洗牌 + t.Log("=1111111111111111111111111111111111111=") + p.ShuffleNumCard(20) + for k, e := range p.buf { + t.Log("第", k+1, "张牌为", e) + } + + //t.Log("=222222222222222222222222222222222222222=") + //for k, e := range x.buf { + // t.Log("第", k+1, "张牌为", e) + //} +} diff --git a/gamerule/baccarat/singleadjust.go b/gamerule/baccarat/singleadjust.go new file mode 100644 index 0000000..e640b3f --- /dev/null +++ b/gamerule/baccarat/singleadjust.go @@ -0,0 +1,706 @@ +package baccarat + +import ( + "math/rand" +) + +//开和 +func (this *Poker) GetTIE() (cards []int32) { + for k := 0; k < 20; k++ { + banker := [3]int32{-1, -1, -1} + xian := [3]int32{-1, -1, -1} + for i := 0; i < 2; i++ { + c, _ := this.Next() + banker[i] = c + } + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + xc, _ := this.Next() + xian[0] = xc + bankNum := num(banker) + c := int(xian[0]%13 + 1) + if c >= 10 { + c = 0 + } + nc := this.FindCard(int32(bankNum - c)) + if nc == -1 { + this.PutIn(banker[:]) + this.PutIn(xian[:]) + continue + } + xian[1] = nc + if isPair(xian) { + this.PutIn(banker[:]) + this.PutIn(xian[:]) + } else { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } + } + return nil +} + +//开庄赢 没有对子 +func (this *Poker) GetBankerWin() (cards []int32) { + banker := [3]int32{-1, -1, -1} + xian := [3]int32{-1, -1, -1} + for k := 0; k < 20; k++ { + for i := 0; i < 2; i++ { + c, _ := this.Next() + banker[i] = c + } + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + for i := 0; i < 2; i++ { + c, _ := this.Next() + xian[i] = c + } + if isPair(xian) { + this.PutIn(xian[:]) + continue + } + bankerNum := num(banker) + xianNum := num(xian) + if bankerNum > xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else if bankerNum < xianNum { + cards = append(cards, banker[:]...) + cards = append(cards, xian[:]...) + return + } else { + this.PutIn(banker[:]) + this.PutIn(xian[:]) + banker = [3]int32{-1, -1, -1} + xian = [3]int32{-1, -1, -1} + } + } + return nil +} + +//开闲赢 没有对子 +func (this *Poker) GetXianWin() (cards []int32) { + banker := [3]int32{-1, -1, -1} + xian := [3]int32{-1, -1, -1} + for k := 0; k < 20; k++ { + for i := 0; i < 2; i++ { + c, _ := this.Next() + banker[i] = c + } + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + for i := 0; i < 2; i++ { + c, _ := this.Next() + xian[i] = c + } + if isPair(xian) { + this.PutIn(xian[:]) + continue + } + bankerNum := num(banker) + xianNum := num(xian) + if bankerNum > xianNum { + cards = append(cards, banker[:]...) + cards = append(cards, xian[:]...) + return + } else if bankerNum < xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(banker[:]) + this.PutIn(xian[:]) + banker = [3]int32{-1, -1, -1} + xian = [3]int32{-1, -1, -1} + } + } + return nil +} + +//开庄赢 带庄对 +func (this *Poker) GetBankerAndBankerPair() (cards []int32) { + banker := [3]int32{-1, -1, -1} + irand := this.GetRandPair() + if irand == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{irand, irand}) + if len(npair) != 2 { + return nil + } + this.buf = buf + banker = [3]int32{npair[0], npair[1], -1} + bankerNum := num(banker) + for i := 0; i < 20; i++ { + xian := [3]int32{-1, -1, -1} + for k := 0; k < 2; k++ { + c, _ := this.Next() + xian[k] = c + } + if isPair(xian) { + this.PutIn(xian[:]) + continue + } + xianNum := num(xian) + if bankerNum > xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(xian[:]) + } + } + this.PutIn(banker[:]) + return nil +} + +//开庄赢 带闲对 +func (this *Poker) GetBankerAndXianPair() (cards []int32) { + xian := [3]int32{-1, -1, -1} + irand := this.GetRandPair() + if irand == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{irand, irand}) + if len(npair) != 2 { + return nil + } + this.buf = buf + xian = [3]int32{npair[0], npair[1], -1} + + xianNum := num(xian) + for i := 0; i < 20; i++ { + banker := [3]int32{-1, -1, -1} + for k := 0; k < 2; k++ { + c, _ := this.Next() + banker[k] = c + } + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + bankerNum := num(banker) + if bankerNum > xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(banker[:]) + } + } + this.PutIn(xian[:]) + return nil +} + +//开闲赢 带闲对 +func (this *Poker) GetXianAndXianPair() (cards []int32) { + xian := [3]int32{-1, -1, -1} + imax := this.GetRandPair() + if imax == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{imax, imax}) + if len(npair) != 2 { + return nil + } + this.buf = buf + xian = [3]int32{npair[0], npair[1], -1} + xianNum := num(xian) + for i := 0; i < 20; i++ { + banker := [3]int32{-1, -1, -1} + for k := 0; k < 2; k++ { + c, _ := this.Next() + banker[k] = c + } + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + bankerNum := num(banker) + if bankerNum < xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(banker[:]) + } + } + this.PutIn(xian[:]) + return nil +} + +//开闲赢 带庄对 +func (this *Poker) GetXianAndBankerPair() (cards []int32) { + banker := [3]int32{-1, -1, -1} + irand := this.GetRandPair() + if irand == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{irand, irand}) + if len(npair) != 2 { + return nil + } + this.buf = buf + banker = [3]int32{npair[0], npair[1], -1} + bankerNum := num(banker) + for i := 0; i < 20; i++ { + xian := [3]int32{-1, -1, -1} + for k := 0; k < 2; k++ { + c, _ := this.Next() + xian[k] = c + } + if isPair(xian) { + this.PutIn(xian[:]) + continue + } + xianNum := num(xian) + if bankerNum < xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(xian[:]) + } + } + this.PutIn(banker[:]) + return nil +} + +//开和赢 带庄对 +func (this *Poker) GetTieAndBankerPair() (cards []int32) { + banker := [3]int32{-1, -1, -1} + irand := this.GetRandPair() + if irand == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{irand, irand}) + if len(npair) != 2 { + return nil + } + this.buf = buf + banker = [3]int32{npair[0], npair[1], -1} + bankerNum := num(banker) + for i := 0; i < 20; i++ { + xian := [3]int32{-1, -1, -1} + //for k := 0; k < 2; k++ { + // c, _ := this.Next() + // xian[k] = c + //} + + c, _ := this.Next() + xian[0] = c + bn := bankerNum + xn := c%13 + 1 + if xn >= 10 { + xn = 0 + } + if bankerNum < int(xn) { + bn += 10 + } + another := this.FindCard(int32(bn) - xn) + if another == -1 { + this.PutIn(xian[:]) + continue + } + xian[1] = another + + if isPair(xian) { + this.PutIn(xian[:]) + continue + } + xianNum := num(xian) + if bankerNum == xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(xian[:]) + } + } + this.PutIn(banker[:]) + return nil +} + +//开和赢 带闲对 +func (this *Poker) GetTieAndXianPair() (cards []int32) { + xian := [3]int32{-1, -1, -1} + imax := this.GetRandPair() + if imax == -1 { + return nil + } + buf, npair := delSclice(this.buf, []int32{imax, imax}) + if len(npair) != 2 { + return nil + } + this.buf = buf + xian = [3]int32{npair[0], npair[1], -1} + xianNum := num(xian) + for i := 0; i < 20; i++ { + banker := [3]int32{-1, -1, -1} + c, _ := this.Next() + banker[0] = c + xn := xianNum + bn := c%13 + 1 + if bn >= 10 { + bn = 0 + } + if xianNum < int(bn) { + xn += 10 + } + another := this.FindCard(int32(xn) - bn) + if another == -1 { + this.PutIn(banker[:]) + continue + } + banker[1] = another + if isPair(banker) { + this.PutIn(banker[:]) + continue + } + bankerNum := num(banker) + if bankerNum == xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else { + this.PutIn(banker[:]) + } + } + this.PutIn(xian[:]) + return nil +} + +//开庄赢 开庄对 闲对 +func (this *Poker) GetBankerAndBankerXianPair() (cards []int32) { + irand := this.GetMinRandPair() + if irand == -1 { + return nil + } + imax := this.GetMaxPair(irand) + if imax == -1 { + return nil + } + + buf, minpair := delSclice(this.buf, []int32{irand, irand}) + if len(minpair) != 2 { + return nil + } + this.buf = buf + buf2, maxpair := delSclice(this.buf, []int32{imax, imax}) + if len(maxpair) != 2 { + return nil + } + this.buf = buf2 + banker := [3]int32{maxpair[0], maxpair[1], -1} + xian := [3]int32{minpair[0], minpair[1], -1} + + bankerNum := num(banker) + xianNum := num(xian) + if bankerNum > xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else if bankerNum < xianNum { + cards = append(cards, banker[:]...) + cards = append(cards, xian[:]...) + return + } + this.PutIn(banker[:]) + this.PutIn(xian[:]) + return nil +} + +//开闲赢 开庄对 闲对 +func (this *Poker) GetXianAndBankerXianPair() (cards []int32) { + irand := this.GetMinRandPair() + if irand == 100 { + return nil + } + imax := this.GetMaxPair(irand) + if imax == -1 { + return nil + } + + buf, minpair := delSclice(this.buf, []int32{irand, irand}) + if len(minpair) != 2 { + return nil + } + this.buf = buf + buf2, maxpair := delSclice(this.buf, []int32{imax, imax}) + if len(maxpair) != 2 { + return nil + } + this.buf = buf2 + xian := [3]int32{maxpair[0], maxpair[1], -1} + banker := [3]int32{minpair[0], minpair[1], -1} + + bankerNum := num(banker) + xianNum := num(xian) + if bankerNum < xianNum { + cards = append(cards, xian[:]...) + cards = append(cards, banker[:]...) + return + } else if bankerNum > xianNum { + cards = append(cards, banker[:]...) + cards = append(cards, xian[:]...) + return + } + this.PutIn(banker[:]) + this.PutIn(xian[:]) + return nil +} + +//开和 开庄对 闲对 +func (this *Poker) GetTieAndBankerXianPair() (cards []int32) { + ifour := this.GetFourCards() + if ifour == -1 { + return nil + } + buf, ifours := delSclice(this.buf, []int32{ifour, ifour, ifour, ifour}) + if len(ifours) != 4 { + return nil + } + this.buf = buf + return []int32{ifours[0], ifours[1], -1, ifours[2], ifours[3], -1} +} +func (this *Poker) GetFourCards() int32 { + ipairs := make(map[int32]int) + for _, v := range this.buf { + ipairs[v%13+1]++ + } + isclice := make([]int32, 0) + for k, v := range ipairs { + if v >= 4 { + isclice = append(isclice, k) + } + } + if len(isclice) == 0 { + return -1 + } + return isclice[rand.Intn(len(isclice))] +} +func (this *Poker) GetRandPair() int32 { + ipairs := make(map[int32]int) + randpair := make([]int32, 0) + for _, v := range this.buf { + i := v%13 + 1 + ipairs[i]++ + if ipairs[i] >= 2 { + isHave := false + for _, n := range randpair { + if n == i { + isHave = true + break + } + } + if !isHave { + randpair = append(randpair, i) + } + } + } + if len(randpair) == 0 { + return -1 + } + optimal := make([]int32, 0) + for _, v := range randpair { + if v < 10 { + optimal = append(optimal, v) + } + } + if len(optimal) > 0 { + return optimal[rand.Intn(len(optimal))] + } + return randpair[rand.Intn(len(randpair))] +} +func (this *Poker) GetMinRandPair() int32 { + ipairs := make(map[int32]int) + randpair := make([]int32, 0) + for _, v := range this.buf { + i := v%13 + 1 + if i == 1 || i == 2 || i == 6 || i == 7 { + ipairs[i]++ + } + if ipairs[i] >= 2 { + randpair = append(randpair, i) + } + } + if len(randpair) == 0 { + return -1 + } + return randpair[rand.Intn(len(randpair))] +} +func (this *Poker) GetMaxPair(irand int32) int32 { + ipairs := make(map[int32]int) + imaxpair := make([]int32, 0) + for _, v := range this.buf { + i := v%13 + 1 + ipairs[i]++ + if i < 10 && i > irand && ipairs[i] >= 2 { + imaxpair = append(imaxpair, i) + } + } + if len(imaxpair) == 0 { + return -1 + } + return imaxpair[rand.Intn(len(imaxpair))] +} +func delSclice(y, d []int32) (ny, np []int32) { + ny = make([]int32, len(y)) + np = make([]int32, 0) + copy(ny, y) + cmap := make(map[int32]int) + for i := 0; i < len(d); i++ { + for k, v := range ny { + c := v%13 + 1 + color := rand.Int31n(4) + if cmap[color] >= 1 { + color++ + color = color % 4 + } + if c == d[i] && (v%52+1)/13 == color { + cmap[color]++ + np = append(np, v%52) + ny = append(ny[:k], ny[k+1:]...) + break + } + } + } + return ny, np +} + +func num(cards [3]int32) (num int) { + for _, c := range cards { + temp := int(c%13 + 1) + if temp > 0 && temp < 10 { + num += temp + if num >= 10 { + num -= 10 + } + } + } + return +} + +func isPair(cards [3]int32) bool { + a := cards[0]%13 + 1 + b := cards[1]%13 + 1 + c := cards[2]%13 + 1 + if a != b && a != c && b != c { + return false + } + return true +} + +//单控补牌 +func (this *Poker) SingleRepairCard(cards []int32) (bool, []int32) { + xianPoint := GetPointNum(cards[:], 0, 1) //闲家点数 + bankerPoint := GetPointNum(cards[:], 3, 4) //庄家点数 + if xianPoint == 8 || xianPoint == 9 || + bankerPoint == 8 || bankerPoint == 9 { + return true, cards + } + + xianRepair := make(map[int32]bool) + for i := int32(0); i <= 5; i++ { + xianRepair[i] = true + } + xianNotRepair := make([]int32, 0) + + if bankerPoint == 3 { + xianNotRepair = append(xianNotRepair, 8) + } else if bankerPoint == 4 { + xianNotRepair = append(xianNotRepair, 8, 9, 0) + } else if bankerPoint == 5 { + xianNotRepair = append(xianNotRepair, 1, 2, 3, 8, 9, 0) + } else if bankerPoint == 6 { + xianNotRepair = append(xianNotRepair, 1, 2, 3, 4, 5, 8, 9, 0) + } else if bankerPoint == 7 { + if _, ok := xianRepair[xianPoint]; ok { + //闲必须补0 + for i := 0; i < this.Count(); i++ { + c, _ := this.Next() + if (c%13 + 1) >= 10 { + cards[2] = c % 52 + return true, cards + } else { + this.PutIn([]int32{c}) + } + } + } + return true, cards + } + //庄补牌 + bankerNeedCard := func() bool { + xianrepair := cards[2] + if xianrepair == -1 && bankerPoint == 6 { + return true + } + for i := 0; i < this.Count(); i++ { + c, _ := this.Next() + repair := c%13 + 1 + bankerpt := bankerPoint + if repair < 10 { + bankerpt += repair + if bankerpt >= 10 { + bankerpt -= 10 + } + } + xianpt := GetPointNum(cards[:], 0, 1, 2) + if (bankerPoint > xianPoint && bankerpt > xianpt) || + (bankerPoint < xianPoint && bankerpt < xianpt) || + (bankerPoint == xianPoint && bankerpt == xianpt) { + cards[5] = c % 52 + return true + } + this.PutIn([]int32{c}) + } + return false + } + //闲补牌 + if _, ok := xianRepair[xianPoint]; ok { + for i := 0; i < this.Count(); i++ { + c, _ := this.Next() + repair := c%13 + 1 + isHave := false + for _, v := range xianNotRepair { + rnew := repair + if rnew >= 10 { + rnew = 0 + } + if rnew == v { + isHave = true + break + } + } + //三张牌一样 + isSame := cards[0]%13+1 == cards[1]%13+1 && cards[0]%13+1 == repair && cards[1]%13+1 == repair + if !isHave { + //20%的概率 三张相同 + r := rand.Intn(100) + if isSame && r < 80 { + this.PutIn([]int32{c}) + continue + } + cards[2] = c % 52 + if bankerNeedCard() { + return true, cards + } else { + cards[2] = -1 + } + } + this.PutIn([]int32{c}) + } + } + if bankerNeedCard() { + return true, cards + } + return false, cards +} diff --git a/gamerule/blackjack/card.go b/gamerule/blackjack/card.go new file mode 100644 index 0000000..e759f90 --- /dev/null +++ b/gamerule/blackjack/card.go @@ -0,0 +1,55 @@ +package blackjack + +import ( + "math/rand" + "time" +) + +var Cards = []*Card{ + {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, + {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, + {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, + {39}, {40}, {41}, {42}, {43}, {44}, {45}, {46}, {47}, {48}, {49}, {50}, {51}, +} + +func RandomShuffle(cards *[]*Card) { + r := rand.New(rand.NewSource(time.Now().Unix())) + *cards = (*cards)[:0] + for i := 0; i < PokerNum; i++ { + *cards = append(*cards, Cards...) + } + r.Shuffle(len(*cards), func(i, j int) { + (*cards)[i], (*cards)[j] = (*cards)[j], (*cards)[i] + }) +} + +type Card struct { + value int +} + +func (c *Card) Value() int { + return c.value +} + +func (c *Card) Point() int { + v := c.value%13 + 1 + switch { + case v >= 2 && v <= 10: + return v + case v > 10 && v <= 13: + return 10 + case v == 1: + return 1 + } + return 0 +} + +func NewCardDefault() *Card { + return &Card{100} +} + +func NewCard(n int32) *Card { + return &Card{ + value: int(n), + } +} diff --git a/gamerule/blackjack/constants.go b/gamerule/blackjack/constants.go new file mode 100644 index 0000000..97b1eee --- /dev/null +++ b/gamerule/blackjack/constants.go @@ -0,0 +1,104 @@ +package blackjack + +import ( + "time" +) + +const MaxCardNum = 5 // 最大手牌数量 +const MaxPlayer = 5 // 玩家数量 +const PokerNum = 8 // 8副牌 +const OneCard = 100 // 牌背 +const AudienceNum = 1 // 总人数 + +// 房间模式 +const ( + RoomModeClassic = 0 // 经典模式 +) + +// 游戏状态 +const ( + StatusWait = 0 // 等待状态 + StatusReady = 1 // 准备状态 + StatusBet = 2 // 下注状态 + StatusDeal = 3 // 发牌状态 + StatusBuy = 4 // 买保险状态 + StatusBuyEnd = 5 // 保险结算状态 + StatusPlayer = 6 // 闲家操作 + StatusBanker = 7 // 庄家操作 + StatusEnd = 8 // 结算状态 + StatusMax = 9 // 游戏状态数量 +) + +// 玩家操作 +const ( + SubBet = 0 // 下注 + SubBuy = 1 // 买保险 0不买 1买 + SubFenPai = 2 // 分牌 + SubDouble = 3 // 双倍 + SubSkip = 4 // 停牌 + SubOuts = 5 // 要牌 + SubLeave = 6 // 离开 + SubSit = 7 // 坐下 + SubSkipLeft = 40 + SubSkipBomb = 41 +) + +// 正在操作左右牌 +const ( + OpDefault = 0 // 默认操作 + OpRight = 1 // 操作右牌 +) + +// 超时时间 +const ( + TimeoutReady = time.Second * 3 // 准备时间 + TimeoutBet = time.Second * 15 // 下注时间 + TimeoutDeal = time.Second * 3 // 发牌时间 + TimeoutBuy = time.Second * 10 // 买保险时间 + TimeoutBuyEnd = time.Second * 3 // 保险结算时间(每人) + TimeoutPlayer = time.Second * 10 // 闲家操作时间 + TimeoutBanker = time.Second * 5 // 庄家操作时间 + TimeoutEnd = time.Second * 5 // 结算时间() + TimeoutEndWin = time.Second * 5 // 结算时间(全赢)5s + TimeoutEndLost = time.Second * 6 // 结算时间(全输)6s + TimeoutEndWinAndLost = time.Second * 8 // 结算时间(有输有赢)8s + TimeoutDelayOp = time.Millisecond * 800 //玩家等待操作时间 0.8s +) + +//结算结果 +const ( + ResultDefault = iota //默认结果 + ResultWin //赢 + ResultLost //输 + ResultWinAndLost //有输有赢 +) + +// 赔率 +const ( + //BaoRate = 2 // 保险金赔率1:2 + A10Rate = 1.5 // 黑杰克赔率2:3 + FiveRate = 1.5 // 五小龙赔率2:3 + //C21Rate = 1 // 普通21赔率1:1 + OtherRate = 1 // 其他点赔率1:1 +) + +func CardsToInt32(cards []*Card) []int32 { + var ret = make([]int32, 0, len(cards)) + for _, v := range cards { + ret = append(ret, int32(v.Value())) + } + return ret +} + +var Action = map[string]int{ + "split": SubFenPai, + "double": SubDouble, + "supply": SubOuts, + "pass": SubSkip, +} + +const ( + CharacterA = 1 // 激进性格 + CharacterB = 2 // 正常性格 + CharacterC = 3 // 保守性格 +) diff --git a/gamerule/blackjack/logic.go b/gamerule/blackjack/logic.go new file mode 100644 index 0000000..b5eb529 --- /dev/null +++ b/gamerule/blackjack/logic.go @@ -0,0 +1,116 @@ +package blackjack + +import ( + "errors" +) + +// 牌型 +const ( + CardTypeInvalid = 0 + CardTypeA10 = 1 // 黑杰克:一张A 一张10 + CardTypeFive = 2 // 五小龙:五张牌且没有爆牌 + CardTypeOther = 3 // 其它点数:点数小于等于21 + CardTypeBoom = 4 // 爆牌:点数大于21 +) + +var CardTypeSort = map[int32]int{ + CardTypeBoom: 1, + CardTypeOther: 2, + CardTypeFive: 3, + CardTypeA10: 4, +} + +// 获取牌型和点数 +func GetCardsType(cards []*Card) (int32, []int32) { + l := len(cards) + if l <= 0 || l > MaxCardNum { + return 0, []int32{} + } + // 黑杰克 + if l == 2 { + if (cards[0].Point() == 1 && cards[1].Point() == 10) || (cards[0].Point() == 10 && cards[1].Point() == 1) { + return CardTypeA10, []int32{21} + } + } + // 所有点数 + var point int32 + for _, v := range cards { + point += int32(v.Point()) + } + points := []int32{point} + for _, v := range cards { + if v.Point() == 1 { + if point+10 > 21 { + break + } + point += 10 + points = append(points, point) + } + } + i := -1 // 最大点数且不爆的点数下标 + for k, v := range points { + if v <= 21 { + i = k + } else { + break + } + } + // 五小龙 + if l == 5 { + if i > -1 { + if points[i] == 21 { + return CardTypeFive, []int32{21} + } else { + return CardTypeFive, points[:i+1] + } + } + } + // 爆牌 + if points[0] > 21 { + return CardTypeBoom, []int32{points[0]} + } + // 其他点数 + if points[i] == 21 { + return CardTypeOther, []int32{21} + } + return CardTypeOther, points[:i+1] +} + +// 手牌比大小 +// bankCards 庄家手牌 +// playerCards 闲家手牌 +// 返回值 +// -1 c1 < c2 +// 0 c1 == c2 +// 1 c1 > c2 +func CompareCards(bankCards, playerCards []*Card) (int, error) { + t1, p1 := GetCardsType(bankCards) + t2, p2 := GetCardsType(playerCards) + + if t1 == CardTypeInvalid || t2 == CardTypeInvalid { + return 0, errors.New("Invalid CardType ") + } + // 牌型比较 + if CardTypeSort[t1] > CardTypeSort[t2] { + return 1, nil + } + if CardTypeSort[t1] < CardTypeSort[t2] { + return -1, nil + } + // 庄闲都爆牌,则庄赢 + if t1 == CardTypeBoom { + return 1, nil + } + // 黑杰克,五小龙 平局 + if t1 == CardTypeA10 || t1 == CardTypeFive { + return 0, nil + } + // 比点数 + if p1[len(p1)-1] > p2[len(p2)-1] { + return 1, nil + } + if p1[len(p1)-1] == p2[len(p2)-1] { + return 0, nil + } + return -1, nil +} diff --git a/gamerule/blackjack/logic_test.go b/gamerule/blackjack/logic_test.go new file mode 100644 index 0000000..bbcb68c --- /dev/null +++ b/gamerule/blackjack/logic_test.go @@ -0,0 +1,17 @@ +package blackjack + +import ( + "fmt" + "testing" +) + +func TestCompareCards(t *testing.T) { + cs := []*Card{{4}, {18}, {51}} + bs := []*Card{} + n, err := CompareCards(bs, cs) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(n) +} diff --git a/gamerule/caishen/caishen.go b/gamerule/caishen/caishen.go new file mode 100644 index 0000000..1aea536 --- /dev/null +++ b/gamerule/caishen/caishen.go @@ -0,0 +1,265 @@ +package caishen + +import ( + "fmt" + "math/rand" + "time" +) + +var SpinID int64 = 100000 // todo + +type LineData struct { + Index int + Element int + Count int + Score int + Position []int32 +} + +//图案*3+连线数量=赔率索引,返回元素值和数量 +func isLine(data []int) (e int, count int) { + e = data[0] + count = 1 + for i := 1; i < len(data); i++ { + if data[i] == e { + count++ + } else { + break + } + } + if count < 3 { + return -1, -1 + } + return +} +func CalcLine(data []int, betLines []int64) (lines []LineData) { + var cards = make([]int, len(data)) + for i, v := range data { + cards[i] = v + } + for i := 10; i < 15; i++ { + if cards[i] == Element_WILD || cards[i-5] == Element_WILD || cards[i-10] == Element_WILD { + cards[i] = Element_WILD + cards[i-5] = Element_WILD + cards[i-10] = Element_WILD + } + } + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + normalData := []int{} + realData := []int{} + epos := []int32{} + if cards[lineTemplate[0]] == Element_SCATTER || cards[lineTemplate[0]] == Element_BONUS { + continue + } + for _, pos := range lineTemplate { + edata = append(edata, cards[pos]) + if cards[pos] != Element_WILD { + normalData = append(normalData, cards[pos]) + } + realData = append(realData, data[pos]) + epos = append(epos, int32(pos+1)) + } + + if len(edata) == len(normalData) { + head, count := isLine(edata) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + } + } else { + normalData = DelSliceRepEle(normalData) + for _, value := range normalData { + replaceData := []int{} + for i := 0; i < len(edata); i++ { + if realData[0] == Element_JACKPOT { // WILD 不能替换 JACKPOT + replaceData = append(replaceData, realData[i]) + } else { + if edata[i] == Element_WILD { + replaceData = append(replaceData, value) + } else { + replaceData = append(replaceData, edata[i]) + } + } + } + head, count := isLine(replaceData) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + break + } + } + } + } + return +} + +//去除切片在的重复的元素 +func DelSliceRepEle(data []int) (res []int) { + if len(data) == 1 { + return data + } + eleFlag := make(map[int]bool) + for i := 0; i < len(data)-1; i++ { + if eleFlag[data[i]] { + continue + } + eleFlag[data[i]] = true + res = append(res, data[i]) + } + return res +} + +type CaclResult struct { + WinLines [][]LineData //多次中奖的线 + IsJackpot bool //是否爆奖 + AllScore int //总中奖倍率 + BonusScore int //小游戏奖励 + SpinFreeTimes int //免费旋转次数 +} + +func CaclScore(cards []int, betLines []int64) (int32, int32, int32, int32, bool) { + curscore := 0 + alllinenum := 0 //中奖线路总数 + allscore := 0 + spinFree := 0 + isJackpot := false + lines := CalcLine(cards, betLines) + for _, line := range lines { + if line.Element == Element_SCATTER { + spinFree += FreeSpinTimesRate[line.Count] + continue + } + if line.Element == Element_JACKPOT && line.Count == LINE_CELL { + isJackpot = true + } + curscore = LineScore[line.Element][line.Count-1] + line.Score = curscore + allscore = allscore + curscore + alllinenum++ + } + bounsCount := 0 + for _, card := range cards { + if card == Element_BONUS { + bounsCount++ + } + if card == Element_JACKPOT { + spinFree++ + } + } + rand.Seed(time.Now().UnixNano()) + //bounsScore:= SmallGameByBouns[bounsCount][0]+rand.Intn(SmallGameByBouns[bounsCount][1]) + spinFreeTimes := FreeSpinTimesRate[spinFree] + return int32(alllinenum), int32(allscore), int32(0), int32(spinFreeTimes), isJackpot +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} + +//prizeFund > limit * roomId走这个检查 +func CheckBigWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD || slotsData[4] == Element_WILD || slotsData[9] == Element_WILD || slotsData[14] == Element_WILD { + return true + } + return false +} + +//prizeFund < limit * roomId走这个更严格的检查 +func CheckSuperWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD || slotsData[4] == Element_WILD || slotsData[9] == Element_WILD || slotsData[14] == Element_WILD { + return true + } + if (slotsData[1] == Element_WILD || slotsData[6] == Element_WILD || slotsData[11] == Element_WILD) && (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) { + return true + } + if (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) && (slotsData[3] == Element_WILD || slotsData[8] == Element_WILD || slotsData[13] == Element_WILD) { + return true + } + return false +} + +type Symbol int + +const ( + SYMBOL1 Symbol = iota + 1 + SYMBOL2 +) + +func GenerateSlotsData_v2(s Symbol) ([]int, int) { + var tryCount = 0 +Next: + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0, ELEMENT_TOTAL) + for i := 0; i < ELEMENT_TOTAL; i++ { + if s == SYMBOL1 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1)-3)+3]) + } else { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1))]) + } + } else if s == SYMBOL2 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2)-5)+5]) + } else { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2))]) + } + } + } + tryCount++ + if (s == SYMBOL1 && CheckSuperWin(slotsData)) || (s == SYMBOL2 && CheckBigWin(slotsData)) { + goto Next + } + return slotsData, tryCount +} + +type BonusGameResult struct { + BonusData []int64 //每次点击的显示奖金,有几个就点击几次,最后一次点击是0 + DataMultiplier int64 //第一级界面小游戏奖金总和 + Mutiplier int //最终小游戏的倍率 + TotalPrizeValue int64 //最终小游戏的奖励 +} + +var gSeedV = time.Now().UnixNano() + +func GenerateBonusGame(totalBet int, startBonus int) BonusGameResult { + if totalBet <= 0 || startBonus <= 0 { + return BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + } + } + var bg = BonusGameResult{ + BonusData: make([]int64, 0), + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: int64(0), + } + gSeedV++ + rand.Seed(gSeedV) + for _, e := range BonusStepArr { + rnd := rand.Intn(len(e)) + prizeValue := int64(e[rnd]*float64(totalBet) + 0.00001) + bg.TotalPrizeValue += prizeValue + bg.DataMultiplier += prizeValue + bg.BonusData = append(bg.BonusData, prizeValue) + if prizeValue == 0 { + break + } + } + bg.Mutiplier = startBonus + rand.Intn(3) + bg.TotalPrizeValue *= int64(bg.Mutiplier) + return bg +} diff --git a/gamerule/caishen/caishen_test.go b/gamerule/caishen/caishen_test.go new file mode 100644 index 0000000..20a3390 --- /dev/null +++ b/gamerule/caishen/caishen_test.go @@ -0,0 +1,134 @@ +package caishen + +import ( + "testing" +) + +func TestGenBonusGame(t *testing.T) { + GenerateBonusGame(10, 3) +} +func TestGenerateBonusGame(t *testing.T) { + type args struct { + totalBet int + startBonus int + } + tests := []struct { + name string + args args + want BonusGameResult + }{ + { + name: "bet:100 bonus*3", + args: args{ + totalBet: 100, + startBonus: 1, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:100 bonus*5", + args: args{ + totalBet: 100, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:1000 bonus*2", + args: args{ + totalBet: 100, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:1000 bonus*0", + args: args{ + totalBet: 1000, + startBonus: 0, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + { + name: "bet:0 bonus*3", + args: args{ + totalBet: 0, + startBonus: 3, + }, + want: BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := GenerateBonusGame(tt.args.totalBet, tt.args.startBonus) + t.Logf("BonusData:%v", r.BonusData) + t.Logf("TotalPrizeValue:%v", r.TotalPrizeValue) + t.Logf("Mutiplier:%v", r.Mutiplier) + t.Logf("DataMultiplier:%v", r.DataMultiplier) + }) + } +} + +//func TestGenerateSlotsData_v2(t *testing.T) { +// type args struct { +// s Symbol +// } +// tests := []struct { +// name string +// args args +// want []int +// want1 int +// }{ +// { +// name: "01", +// args: args{ +// s: SYMBOL1, +// }, +// want: nil, +// want1: 0, +// }, +// { +// name: "02", +// args: args{ +// s: SYMBOL2, +// }, +// want: nil, +// want1: 0, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// v, times := GenerateSlotsData_v2(tt.args.s) +// t.Logf("GenerateSlotsData_v2() data = %v, times %v", v, times) +// lines := CalcLine(v, AllBetLines) +// t.Logf("lines:%v", lines) +// PrintHuman(v) +// CaclScore(v, AllBetLines) +// }) +// } +//} diff --git a/gamerule/caishen/constants.go b/gamerule/caishen/constants.go new file mode 100644 index 0000000..6133c39 --- /dev/null +++ b/gamerule/caishen/constants.go @@ -0,0 +1,795 @@ +package caishen + +// 财神 +const ( + Element_WILD int = iota + 1 //1 通配 + Element_SCATTER //2 Scatter=FreeSpins + Element_BONUS //3 Bonus + Element_JACKPOT //4 Jackpot + Element_J //5 J + Element_Q //6 Q + Element_K //7 K + Element_TREE //8 摇钱树 + Element_BAG //9 福袋 + Element_10 //10 10 + Element_A //11 A + Element_Max +) + +var Element_NAME_MAP = map[int]string{ + Element_WILD: "[ WILD ]", + Element_SCATTER: "[SCATTER]", + Element_BONUS: "[ BONUS ]", + Element_JACKPOT: "[JACKPOT]", + Element_J: "[ J ]", + Element_Q: "[ Q ]", + Element_K: "[ K ]", + Element_TREE: "[ 摇钱树 ]", + Element_BAG: "[ 福袋 ]", + Element_10: "[ 10 ]", + Element_A: "[ A ]", + -1: "[ - ]", +} + +const LINE_ROW int = 3 //行数 +const LINE_CELL int = 5 //列数 +const LINENUM int = 25 //线条数 +const ELEMENT_TOTAL = LINE_ROW * LINE_CELL + +/* 所有线条数组 + * 0 1 2 3 4 + * 5 6 7 8 9 + * 10 11 12 13 14 + */ +var AllLineArray = [][]int{ + {5, 6, 7, 8, 9}, //线条1 + {0, 1, 2, 3, 4}, //线条2 + {10, 11, 12, 13, 14}, //线条3 + {10, 6, 2, 8, 14}, //线条4 + {0, 6, 12, 8, 4}, //线条5 + {5, 1, 2, 3, 9}, //线条6 + {5, 11, 12, 13, 9}, //线条7 + {0, 1, 7, 13, 14}, //线条8 + {10, 11, 7, 3, 4}, //线条9 + {5, 11, 7, 3, 9}, //线条10 + {5, 1, 7, 13, 9}, //线条11 + {0, 6, 7, 8, 4}, //线条12 + {10, 6, 7, 8, 14}, //线条13 + {0, 6, 2, 8, 4}, //线条14 + {10, 6, 12, 8, 14}, //线条15 + {5, 6, 2, 8, 9}, //线条16 + {5, 6, 12, 8, 9}, //线条17 + {0, 1, 12, 3, 4}, //线条18 + {10, 11, 2, 13, 14}, //线条19 + {0, 11, 12, 13, 4}, //线条20 + {10, 1, 2, 3, 14}, //线条21 + {5, 1, 12, 3, 9}, //线条22 + {5, 11, 2, 13, 9}, //线条23 + {0, 11, 2, 13, 4}, //线条24 + {10, 1, 12, 3, 14}, //线条25 +} + +var AllLineMatrix = [][]int{ + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条4 + + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条5 + + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条6 + + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条7 + + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条8 + + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条9 + + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条10 + + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条11 + + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条12 + + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条13 + + ///////////////////// + // *-------*-------* + // \ / \ / + // ----*-------*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + }, //线条14 + + ///////////////////// + // ----------------- + // + // ----*-------*---- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, + }, //线条15 + + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条16 + + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条17 + + ///////////////////// + // *---*-------*---* + // \ / + // ----------------- + // \ / + // --------*-------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + }, //线条18 + + ///////////////////// + // --------*-------- + // / \ + // ----------------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + }, //线条19 + + ///////////////////// + // *---------------* + // \ / + // ----------------- + // \ / + // ----*---*---*---- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + }, //线条20 + + ///////////////////// + // ----*---*---*---- + // / \ + // ----------------- + // / \ + // *---------------* + ///////////////////// + { + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + }, //线条21 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条22 + + ///////////////////// + // --------*-------- + // / \ + // *---------------* + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + }, //线条23 + + ///////////////////// + + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条24 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条25 +} + +var AllLineDraw = []string{ + ` + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + `, + + ` + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + `, + + ` + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条4 + `, + + ` + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条5 + `, + + ` + ///////////////////// + // *---*-------*---* + // \ / + // --------*-------- + // + // ----------------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + }, //线条6 + `, + + ` + ///////////////////// + // ----------------- + // + // --------*-------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + }, //线条7 + `, + + ` + ///////////////////// + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条8 + `, + + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条9 + `, + + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条10 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条11 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条12 + `, + + ` + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条13 + `, + + ` + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条14 + `, + + ` + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条15 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条16 + `, + + ` + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条17 + `, + + ` + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条18 + `, + + ` + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条19 + `, + + ` + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条20 + `, +} + +// 所有元素对应的赔率 +var LineScore = [Element_Max][LINE_CELL]int{ + {0, 0, 0, 0, 0}, //占位 + {0, 0, 0, 0, 0}, //* + {0, 0, 0, 0, 0}, //Scatter + {0, 0, 0, 0, 0}, //Bonus + {0, 0, 50, 100, 0}, //Jackpot + {0, 0, 40, 75, 300}, //J + {0, 0, 30, 50, 150}, //Q + {0, 0, 20, 30, 100}, //K + {0, 0, 10, 20, 40}, //摇钱树 + {0, 0, 5, 10, 25}, //福袋 + {0, 0, 3, 9, 15}, //10 + {0, 0, 2, 4, 8}, //A +} + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25} + +// 免费次数奖励 +var FreeSpinTimesRate = [LINE_CELL]int{0, 0, 4, 8, 12} + +const BonusStepNum = 12 + +// ver 2 +var symbol1 = []int{ + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, + 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +} +var symbol2 = []int{ + 1, 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, +} + +var BonusStepArr = [BonusStepNum][]float64{ + []float64{0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0, 2.0}, + []float64{0.0, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0}, + []float64{0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.5, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 1.0, 1.5}, + []float64{0.0, 0.0, 0.0, 0.5, 2.0}, + []float64{0.0, 0.0, 0.0, 1.0}, + []float64{0.0, 0.0, 1.5}, + []float64{0.0, 0.5}, + []float64{0.0}, +} + +var MissData = [][]int{ + []int{9, 11, 11, 9, 7, 7, 8, 11, 4, 9, 2, 11, 10, 10, 10}, + []int{11, 8, 8, 10, 10, 4, 10, 10, 8, 2, 11, 6, 9, 10, 9}, + []int{7, 7, 11, 2, 11, 2, 11, 8, 5, 10, 9, 4, 10, 9, 11}, + []int{11, 2, 5, 4, 9, 9, 8, 11, 11, 5, 7, 10, 4, 10, 8}, + []int{4, 10, 8, 4, 3, 10, 6, 11, 10, 9, 9, 1, 11, 6, 11}, + []int{7, 9, 7, 11, 8, 8, 4, 8, 9, 11, 10, 11, 10, 8, 6}, + []int{2, 8, 11, 11, 11, 9, 10, 11, 9, 3, 11, 4, 8, 8, 6}, + []int{6, 8, 9, 9, 9, 11, 10, 10, 4, 5, 8, 4, 9, 11, 11}, + []int{8, 10, 8, 5, 8, 10, 3, 11, 11, 9, 9, 7, 11, 10, 5}, + []int{9, 6, 8, 8, 3, 7, 5, 10, 6, 6, 2, 2, 1, 3, 11}, + []int{9, 2, 4, 9, 10, 11, 8, 10, 11, 8, 4, 10, 11, 11, 9}, + []int{2, 3, 11, 6, 10, 10, 11, 11, 11, 2, 7, 8, 10, 9, 9}, + []int{4, 4, 8, 10, 7, 8, 11, 10, 7, 9, 11, 10, 9, 11, 8}, + []int{4, 8, 7, 10, 7, 9, 10, 8, 8, 11, 6, 4, 10, 9, 10}, + []int{10, 5, 9, 10, 10, 6, 8, 8, 6, 9, 11, 6, 11, 9, 10}, + []int{5, 4, 8, 9, 3, 2, 11, 10, 8, 6, 10, 7, 9, 6, 11}, + []int{9, 2, 11, 4, 4, 4, 11, 11, 10, 7, 8, 9, 10, 9, 9}, + []int{9, 8, 11, 2, 7, 6, 10, 6, 10, 9, 11, 4, 7, 7, 8}, + []int{2, 6, 10, 9, 7, 9, 5, 1, 8, 9, 11, 2, 8, 4, 10}, +} + +// jack params +const ( + CAISHEN_JACKPOT_InitJackpot int = iota //初始化奖池数量 + CAISHEN_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赚取投注的多少倍 + CAISHEN_JACKPOT_LIMITWIN_PRIZEHIGH //现金池充足时 最多赚取投注的多少倍 +) diff --git a/gamerule/candy/candy.go b/gamerule/candy/candy.go new file mode 100644 index 0000000..a96aaa9 --- /dev/null +++ b/gamerule/candy/candy.go @@ -0,0 +1,178 @@ +package candy + +import ( + "fmt" + "math/rand" + "time" +) + +// var SpinID int64 = 100000 // todo + +type LineData struct { + Index int + Element int + Count int + Score int64 + Position []int32 +} + +//图案*3+连线数量=赔率索引,返回元素值和数量 +func isLine(data []int) (e int, count int) { + tempData := make([]int, LINE_CELL) + copy(tempData, data) + dataMap := make(map[int]int, LINE_CELL) + + for _, v := range tempData { + if _, ok := dataMap[v]; !ok { + dataMap[v] = 1 + } else { + dataMap[v]++ + } + } + for _, v := range tempData { + if dataMap[v] >= 3 { + return v, dataMap[v] + } else if dataMap[v] >= 2 && (v == Element_QUEKEO || v == Element_RANHMI) { + return v, dataMap[v] + } + } + return -1, -1 +} + +// 是否特殊线 +func isSpecialLine(data []int) (flag bool, m int) { + tempData := make([]int, LINE_CELL) + copy(tempData, data) + dataMap := make(map[int]int, LINE_CELL) + + for _, v := range tempData { + if _, ok := dataMap[v]; !ok { + dataMap[v] = 1 + } else { + dataMap[v]++ + } + } + //if dataMap[Element_VUANGMIEN] == 1 && dataMap[Element_RONG] == 1 && dataMap[Element_NGOC] == 1 { + // flag = true + // m = LINE_BIGWILD + //} + if dataMap[Element_VUANGMIEN] == 1 && dataMap[Element_QUEKEO] == 1 && dataMap[Element_RANHMI] == 1 { + flag = true + m = LINE_SMALLWILD + } + return +} + +func CalcLine(data []int, betLines []int64) (lines []LineData) { + var isBigWild bool + var cards = make([]int, len(data)) + copy(cards, data) + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + normalData := []int{} + realData := []int{} + epos := []int32{} + for _, pos := range lineTemplate { + edata = append(edata, cards[pos]) + if cards[pos] != Element_VUANGMIEN { + normalData = append(normalData, cards[pos]) + } + realData = append(realData, data[pos]) + epos = append(epos, int32(pos+1)) + } + + if len(edata) == len(normalData) || len(normalData) == 0 { + head, count := isLine(edata) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + } + } else { + normalData = DelSliceRepEle(normalData) + if len(normalData) == LINE_CELL-1 { + if f, m := isSpecialLine(edata); f { + if m == LINE_SMALLWILD { + lines = append(lines, LineData{index, Element_Min, LINE_CELL, LineScore[Element_Min][LINE_CELL-1], epos[:LINE_CELL]}) + break + } + // 特殊奖励(高倍率 15,20,30,50 多个中线仅计算一次分数) + if m == LINE_BIGWILD { + var specialScore int64 + if !isBigWild { + isBigWild = true + specialScore = luckyDataRate[rand.Intn(len(luckyDataRate))] * 10 // (特殊倍率 * 10倍,返回时候 / 10 还原,底注配置10,暂不会出现问题) + } + lines = append(lines, LineData{index, Element_Min, LINE_CELL, specialScore, epos[:LINE_CELL]}) + continue + } + } + } + for _, value := range normalData { + replaceData := []int{} + for i := 0; i < len(edata); i++ { + if edata[i] == Element_VUANGMIEN { + replaceData = append(replaceData, value) + } else { + replaceData = append(replaceData, edata[i]) + } + } + head, count := isLine(replaceData) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + break + } + } + } + } + return +} + +//去除切片在的重复的元素 +func DelSliceRepEle(data []int) (res []int) { + if len(data) == 1 { + return data + } + eleFlag := make(map[int]bool) + for i := 0; i < len(data); i++ { + if eleFlag[data[i]] { + continue + } + eleFlag[data[i]] = true + res = append(res, data[i]) + } + return res +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} + +var gSeedV = time.Now().UnixNano() + +func GenerateSlotsData_v2(s Symbol) []int { + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0, ELEMENT_TOTAL) + for i := 0; i < ELEMENT_TOTAL; i++ { + if s == SYMBOL1 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1))]) + } else if s == SYMBOL2 { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2))]) + } + } + return slotsData +} + +func GenerateSlotsData_v3() []int { + return defalutData[rand.Intn(len(defalutData))] +} diff --git a/gamerule/candy/candy_test.go b/gamerule/candy/candy_test.go new file mode 100644 index 0000000..cd5109d --- /dev/null +++ b/gamerule/candy/candy_test.go @@ -0,0 +1,49 @@ +package candy + +import ( + "reflect" + "testing" +) + +func TestCalcLine(t *testing.T) { + type args struct { + data []int + betLines []int64 + } + tests := []struct { + name string + args args + wantLines []LineData + }{ + { + "test1", + args{ + []int{1, 1, 1, 2, 2, 2, 3, 3, 3}, + []int64{1, 2, 3}, + }, + []LineData{ + {1, 1, 3, 0, []int32{1, 2, 3}}, + {2, 2, 3, 800, []int32{4, 5, 6}}, + {3, 3, 3, 400, []int32{7, 8, 9}}, + }, + }, + { + "test bigwild", + args{ + []int{1, 2, 3, 1, 5, 6, 3, 3, 3}, + []int64{1, 2}, + }, + []LineData{ + {1, 0, 3, 0, []int32{1, 2, 3}}, + {2, 0, 3, 12, []int32{4, 5, 6}}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotLines := CalcLine(tt.args.data, tt.args.betLines); !reflect.DeepEqual(gotLines, tt.wantLines) { + t.Errorf("CalcLine() = %v, want %v", gotLines, tt.wantLines) + } + }) + } +} diff --git a/gamerule/candy/constants.go b/gamerule/candy/constants.go new file mode 100644 index 0000000..7c42de9 --- /dev/null +++ b/gamerule/candy/constants.go @@ -0,0 +1,188 @@ +package candy + +// 糖果 +const ( + Element_Min int = iota // 0 (wild+蓝色圆缺+红色三角) + Element_VUANGMIEN // 1 JACKPOT WILD + Element_RONG // 2 橘色圆形 + Element_NGOC // 3 绿色方形 + Element_XEDAOMO // 4 紫色长条 + Element_QUEKEO // 5 蓝色圆缺 + Element_RANHMI // 6 红色三角 + Element_Max +) + +var DebugSlotData = []int{ + 1, 1, 1, + 5, 2, 2, + 6, 2, 3, +} +var Element_NAME_MAP = map[int]string{ + Element_VUANGMIEN: "[JACKPOT WILD]", + Element_RONG: "[橘色圆形]", + Element_NGOC: "[绿色方形]", + Element_XEDAOMO: "[紫色长条]", + Element_QUEKEO: "[蓝色圆缺]", + Element_RANHMI: "[红色三角]", + -1: "[ - ]", +} + +var AllLineArray = [][]int{ + {0, 1, 2}, //线条1 + {3, 4, 5}, //线条2 + {6, 7, 8}, //线条3 + {0, 7, 2}, //线条4 + {6, 1, 8}, //线条5 + {0, 4, 2}, //线条6 + {0, 4, 8}, //线条7 + {6, 4, 2}, //线条8 + {3, 7, 5}, //线条9 + {3, 1, 5}, //线条10 + {6, 4, 8}, //线条11 + {0, 1, 5}, //线条12 + {3, 4, 8}, //线条13 + {3, 4, 2}, //线条14 + {6, 7, 5}, //线条15 + {3, 1, 2}, //线条16 + {6, 4, 5}, //线条17 + {0, 4, 5}, //线条18 + {3, 7, 8}, //线条19 + {0, 7, 5}, //线条20 +} + +const ( + LINE_ROW int = 3 //行数 + LINE_CELL int = 3 //列数 + LINENUM int = 20 //线条数 + ELEMENT_TOTAL = LINE_ROW * LINE_CELL +) + +//所有元素对应的赔率 (统一 * 10倍,返回时候 / 10 还原,底注配置10,暂不会出现问题) +var LineScore = [Element_Max][LINE_CELL]int64{ + {0, 0, 12}, // 0 (wild+橘色圆形+绿色方形) + {0, 0, 0}, // 1 JACKPOT WILD + {0, 0, 850}, // 2 橘色圆形 + {0, 0, 400}, // 3 绿色方形 + {0, 0, 200}, // 4 紫色长条 + {0, 8, 70}, // 5 蓝色圆缺 + {0, 4, 30}, // 6 红色三角 +} + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + +// len:100 +var symbol1 = []int{ + 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +} + +// len:117 +var symbol2 = []int{ + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +} + +// len:20 +var defalutData = [][]int{ + {5, 2, 6, 4, 4, 6, 5, 4, 6}, + {4, 4, 6, 4, 2, 6, 5, 2, 6}, + {6, 5, 2, 4, 5, 2, 6, 4, 3}, + {2, 4, 3, 5, 3, 3, 4, 4, 6}, + {4, 2, 2, 4, 5, 4, 2, 5, 6}, + {4, 5, 4, 4, 2, 6, 4, 3, 4}, + {4, 3, 6, 2, 4, 6, 4, 5, 2}, + {6, 4, 4, 6, 5, 2, 3, 5, 4}, + {3, 3, 4, 4, 6, 2, 4, 3, 5}, + {3, 3, 5, 2, 2, 6, 4, 2, 6}, + {4, 5, 3, 4, 5, 4, 2, 5, 4}, + {5, 4, 6, 4, 4, 6, 5, 4, 2}, + {5, 4, 6, 5, 3, 4, 4, 2, 3}, + {5, 2, 6, 3, 4, 6, 5, 2, 6}, + {3, 5, 4, 4, 2, 6, 4, 5, 4}, + {6, 5, 2, 6, 5, 2, 4, 5, 2}, + {3, 5, 6, 3, 4, 6, 4, 4, 6}, + {3, 4, 2, 6, 3, 5, 3, 2, 2}, + {2, 6, 3, 4, 4, 2, 3, 4, 2}, + {4, 2, 2, 4, 4, 2, 6, 5, 2}, + {2, 4, 4, 6, 2, 3, 5, 3, 3}, +} + +// len:1000 +var luckyDataRate = []int64{ + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, +} + +// jack params +const ( + CANDY_JACKPOT_InitJackpot int = iota //初始化奖池倍率 + CANDY_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赚取投注的多少倍 + CANDY_JACKPOT_LIMITWIN_PRIZEHIGH_ROOM // 当前房间 现金池充足时 最多赚取投注的多少倍 +) + +// 对应房间的scenetype +const ( + ROOM1 int = iota + 1 + ROOM2 + ROOM3 + ROOM4 +) + +type Symbol int + +const ( + SYMBOL1 Symbol = iota + 1 + SYMBOL2 +) + +// 特殊中线 +const ( + LINE_BIGWILD = iota // 0 (wild+橘色圆形+绿色方形) + LINE_SMALLWILD // 1 (wild+蓝色圆缺+红色三角) +) diff --git a/gamerule/caothap/caothap.go b/gamerule/caothap/caothap.go new file mode 100644 index 0000000..1efab31 --- /dev/null +++ b/gamerule/caothap/caothap.go @@ -0,0 +1,32 @@ +package caothap + +import ( + "math/rand" + "time" +) + +func CardInit() []int32 { + return cardDataShuffle(cardData[:]) +} + +func cardDataShuffle(cardData []int32) []int32 { + rand.Seed(time.Now().UnixNano()) + num := len(cardData) + for i := 0; i < num; i++ { + n := rand.Intn(num - i) + cardData[i], cardData[n] = cardData[n], cardData[i] + } + return cardData +} + +func IsCardA(card int32) bool { + return getCardValue(card) == 12 +} +func getCardValue(card int32) int { + return int(card) % 13 +} + +// 提供外部接口 +func GetCardValue(card int32) int { + return getCardValue(card) +} diff --git a/gamerule/caothap/constants.go b/gamerule/caothap/constants.go new file mode 100644 index 0000000..6e2e7df --- /dev/null +++ b/gamerule/caothap/constants.go @@ -0,0 +1,44 @@ +package caothap + +// CaoThap +const ( + CARDSTYPE_NUM = 13 // 牌型数 + CARDDATANUM = 52 // 牌库数 + JACKPOTTIMEINTERVAL = 24 // 单位小时 + + STEP_FIRSTINIT = 0 // 初始化校验 + STEP_BALANCE = 1 // 结算校验 + STEP_JACKPOT_LIMIT = 7 // 爆奖校验 + + TURNID_FIRSTINIT = 10000 // 初始化 时用TurnID + TIMEINTERVAL = 120 // 超时时间 +) + +const ( + CAOTHAP_JACKPOT_InitJackpot int = iota //初始化奖池倍率 + CAOTHAP_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赢分 + CAOTHAP_JACKPOT_LIMITWIN_PRIZEHIGH //现金池充足时 最多赢分 +) + +// CardID 下标索引 CardName 下标索引对应值 +var cardName = [CARDDATANUM]string{ + "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♠", // 0-12 黑桃 + "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♣", // 13-25 梅花 + "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦", "A♦", // 26-38 方片 + "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♥", // 39-51 红桃 +} + +// 牌库数据 +var cardData = [CARDDATANUM]int32{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +} + +var DebugCardData = [CARDDATANUM]int32{ + 12, 25, 38, 51, 4, 5, 6, 7, 8, 9, 10, 11, 0, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 2, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 3, +} diff --git a/gamerule/chess/chessjpz.go b/gamerule/chess/chessjpz.go new file mode 100644 index 0000000..a31ff35 --- /dev/null +++ b/gamerule/chess/chessjpz.go @@ -0,0 +1,1024 @@ +package chess + +import ( + "fmt" + "strconv" + "strings" +) + +var pieceJPZ = []Piece{ + BR, BN, BS, BM, BK, BS, BN, BR, + "", "", "", "", "", "", "", "", + BP, BP, BP, BP, BP, BP, BP, BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, WP, WP, WP, WP, WP, WP, WP, + "", "", "", "", "", "", "", "", + WR, WN, WS, WK, WM, WS, WN, WR, +} + +const ( + MoveWK = 0 + MoveWM = 1 + MoveBM = 2 + MoveBK = 3 +) + +// RecordJPZ 走棋记录 +type RecordJPZ struct { + FromTo [2]*PositionPiece + CambodianMove [4]bool +} + +// ChessJPZ 棋盘 +type ChessJPZ struct { + Variant int + Width int // 宽度 + Height int // 高度 + Count int // 格子数量 + Act bool // 是否该白字走,false白棋 + Round int // 回合数 + Buf []Piece // 棋子 + + Castling [4]bool // 王车易位可行性 [KQkq] + EnPassant int // 过路兵位置 + CambodianMove [4]bool // 柬埔寨移动可行性(DEde,D白王,E白仕,d黑仕,e黑王) + + Records []*RecordJPZ // 走棋记录 + Check [2]bool // 将军 + Checkmate [2]bool // 将死 +} + +func (c *ChessJPZ) GetChess() []string { + ret := make([]string, len(c.Buf)) + for k, v := range c.Buf { + ret[k] = string(v) + } + return ret +} + +func (c *ChessJPZ) GetAct() string { + if c.Act { + return "b" + } else { + return "w" + } +} + +func (c *ChessJPZ) GetCastling() []bool { + ret := make([]bool, len(c.Castling)) + for k, v := range c.Castling { + ret[k] = v + } + return ret +} + +func (c *ChessJPZ) GetEnPassant() int { + return c.EnPassant +} + +func (c *ChessJPZ) GetCambodianMove() []bool { + ret := make([]bool, len(c.CambodianMove)) + for k, v := range c.CambodianMove { + ret[k] = v + } + return ret +} + +func (c *ChessJPZ) GetRound() int { + return c.Round +} + +func (c *ChessJPZ) GetPiece(index int) Piece { + return c.Buf[index] +} + +func (c *ChessJPZ) GetCheck() []bool { + ret := make([]bool, len(c.Check)) + for k, v := range c.Check { + ret[k] = v + } + return ret +} + +func (c *ChessJPZ) GetCheckmate() []bool { + ret := make([]bool, len(c.Checkmate)) + for k, v := range c.Checkmate { + ret[k] = v + } + return ret +} + +func (c *ChessJPZ) NextAct() { + // 判断将军 + c.Check[0] = c.IsCheck(true) + c.Check[1] = c.IsCheck(false) + // 判断将死 + c.Checkmate[0] = false + c.Checkmate[1] = false + if !c.Act && c.IsCheckmate(true) { + c.Checkmate[0] = true + } + if c.Act && c.IsCheckmate(false) { + c.Checkmate[1] = true + } + + // 下一个玩家操作,记录回合数 + if !c.Act { + c.Round++ + } + c.Act = !c.Act +} + +func (c *ChessJPZ) PrintChess() { + f := func(n int) string { + str := fmt.Sprintf(" %d", n+1) + if c.IsWhiteMove() && n == 0 || c.IsBlackMove() && n == -1 { + str = strings.Replace(str, " ", "*", 1) + } + return str + } + + splitStr := " " + for y := c.Height - 1; y >= 0; y-- { + fmt.Print(f(y)) + for x := 0; x < c.Width; x++ { + fmt.Print(splitStr) + idx, _ := XYToIndex(c.Width, c.Height, x, y) + value := c.Buf[idx] + if value == "" { + fmt.Print("--") + } else { + fmt.Print(value) + } + splitStr = " " + } + fmt.Println() + splitStr = " " + } + fmt.Print(" ") + for x := 0; x < c.Width; x++ { + fmt.Print(splitStr) + fmt.Print(fmt.Sprintf(" %d", x+1)) + splitStr = " " + } + fmt.Println() +} + +func (c *ChessJPZ) GenFenChessBoard() string { + var build strings.Builder + splitStr := "" + emptyCnt := 0 + + writeEmptyCount := func() { + if emptyCnt > 0 { + countStr := fmt.Sprintf("%d", emptyCnt) + build.WriteString(countStr) + emptyCnt = 0 + } + } + + for y := c.Height - 1; y >= 0; y-- { + build.WriteString(splitStr) + for x := 0; x < c.Width; x++ { + idx, _ := XYToIndex(c.Width, c.Height, x, y) + value := c.Buf[idx] + if value == "" { + //build.WriteString("") + emptyCnt++ + } else { + writeEmptyCount() + t := value[1:] + c := value[:1] + if c == "W" { + build.WriteString(string(t)) + } else if c == "B" { + build.WriteString(strings.ToLower(string(t))) + } + } + } + writeEmptyCount() + splitStr = "/" + } + return build.String() +} + +func (c *ChessJPZ) GenFenCastling() string { + return "-" // 客户端需要支持王车易位走位后才可使用 + //var build strings.Builder + //if this.castling[0] { + // build.WriteString("K") + //} + //if this.castling[1] { + // build.WriteString("Q") + //} + //if this.castling[2] { + // build.WriteString("k") + //} + //if this.castling[3] { + // build.WriteString("q") + //} + //str := build.String() + //if len(str) == 0 { + // return "-" + //} + //return build.String() +} + +func (c *ChessJPZ) GenFenCambodianMove() string { + var build strings.Builder + if c.CambodianMove[MoveWK] { + build.WriteString("D") + } + if c.CambodianMove[MoveWM] { + build.WriteString("E") + } + if c.CambodianMove[MoveBM] { + build.WriteString("d") + } + if c.CambodianMove[MoveBK] { + build.WriteString("e") + } + str := build.String() + if len(str) == 0 { + return "-" + } + return build.String() +} + +func (c *ChessJPZ) GenFenEnPassant() string { + return "-" +} + +func (c *ChessJPZ) GenHalfRoundCount() string { + return "0" +} + +func (c *ChessJPZ) GenRoundCount() string { + return fmt.Sprintf("%d", c.Round) +} + +func (c *ChessJPZ) GenFen() string { + var build strings.Builder + build.WriteString("fen ") + build.WriteString(c.GenFenChessBoard()) + build.WriteString(" ") + build.WriteString(c.GetAct()) + build.WriteString(" ") + build.WriteString(c.GenFenCambodianMove()) + build.WriteString(" ") + build.WriteString(c.GenFenEnPassant()) + build.WriteString(" ") + build.WriteString(c.GenHalfRoundCount()) + build.WriteString(" ") + build.WriteString(c.GenRoundCount()) + return build.String() +} + +func (c *ChessJPZ) UciTwoPosToIdxList(str string) []int64 { + result := []int64{-1, -1} + result[0] = c.UciOnePosToIdx(str[0:2]) + result[1] = c.UciOnePosToIdx(str[2:4]) // 这里出现过g5f6m这样的返回,暂时不清楚这个m表示的意义,是兵进行吃子造成的 + return result +} + +func (c *ChessJPZ) UciOnePosToIdx(str string) int64 { + x := int(strings.ToLower(str[:1])[0] - 'a') + y, _ := strconv.Atoi(str[1:]) + y-- + idx := y*c.Width + x + //fmt.Println("UciOnePosToIdx", str, x, y, idx) + return int64(idx) +} + +func (c *ChessJPZ) GetChessNum(piece Piece) int { + n := 0 + for _, v := range c.Buf { + if piece == v { + n++ + } + } + return n +} + +func (c *ChessJPZ) GetWhiteNum() int { + n := 0 + for _, v := range c.Buf { + if IsWhite(v) { + n++ + } + } + return n +} + +func (c *ChessJPZ) GetBlackNum() int { + n := 0 + for _, v := range c.Buf { + if IsBlack(v) { + n++ + } + } + return n +} + +func (c *ChessJPZ) Has(piece Piece) bool { + for _, v := range c.Buf { + if v == piece { + return true + } + } + return false +} + +func (c *ChessJPZ) GetVariant() int { + return c.Variant +} + +func (c *ChessJPZ) SetChess(strings []string) { + for k, v := range strings { + c.Buf[k] = Piece(v) + } +} + +func (c *ChessJPZ) Set(bufStr []string, bufPiece []Piece, act string, castling []bool, cambodianMove []bool, enPassant int, round int) { + //var buf2 []Piece + //if len(bufStr) > 0 { + // buf2 = make([]Piece, len(bufStr)) + // for x := 0; x < c.Width; x++ { + // for y := 0; y < c.Height; y++ { + // buf2[(c.Height-1-y)*c.Width+x] = Piece(bufStr[y*c.Width+x]) + // } + // } + //} else { + // buf2 = make([]Piece, len(bufPiece)) + // for x := 0; x < c.Width; x++ { + // for y := 0; y < c.Height; y++ { + // buf2[(c.Height-1-y)*c.Width+x] = bufPiece[y*c.Width+x] + // } + // } + //} + + if len(bufStr) > 0 { + for k, v := range bufStr { + c.Buf[k] = Piece(v) + } + } + if len(bufPiece) > 0 { + for k, v := range bufPiece { + c.Buf[k] = v + } + } + + if act == "w" { + c.Act = false + } else if act == "b" { + c.Act = true + } + + if len(castling) == 4 { + for k, v := range castling { + c.Castling[k] = v + } + } + + if len(cambodianMove) == 4 { + for k, v := range cambodianMove { + c.CambodianMove[k] = v + } + } + + c.EnPassant = enPassant + c.Round = round +} + +func (c *ChessJPZ) Init() { + c.Variant = ChessVarCambodian + c.Width = 8 + c.Height = 8 + c.Count = c.Width * c.Height + c.Act = false + c.Round = 1 + + if c.Buf == nil || len(c.Buf) != c.Count { + c.Buf = make([]Piece, c.Count) + } + for x := 0; x < c.Width; x++ { + for y := 0; y < c.Height; y++ { + c.Buf[(c.Height-1-y)*c.Width+x] = pieceJPZ[y*c.Width+x] + } + } + + c.Castling = [4]bool{true, true, true, true} + c.EnPassant = -1 + c.CambodianMove = [4]bool{true, true, true, true} + c.Records = c.Records[:0] + c.Check = [2]bool{false, false} + c.Checkmate = [2]bool{false, false} +} + +// GetWidth 获取棋盘的宽度 +func (c *ChessJPZ) GetWidth() int { + return c.Width +} + +// GetHeight 获取棋盘的高度 +func (c *ChessJPZ) GetHeight() int { + return c.Height +} + +// MoveR 获取某个位置的车能走的下一步位置 +func (c *ChessJPZ) MoveR(x, y int) []*PositionPiece { + // 坐标转换成索引 + index, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是车 + o := c.Buf[index] + if !IsR(o) { + return nil + } + + var validMoves []*PositionPiece + + // 遍历四个方向 + for _, dir := range StraightDirections { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + for newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err != nil { + break + } + + // 获取新位置的棋子 + piece := c.Buf[index] + + if IsEmpty(piece) { + // 记录位置 + validMoves = append(validMoves, &PositionPiece{Index: index}) + } else { + if IsWhite(piece) != IsWhite(o) { + validMoves = append(validMoves, &PositionPiece{Index: index, P: piece}) + } + break + } + + // 新位置是空的,继续向前走 + newX += dx + newY += dy + } + } + + return validMoves +} + +// MoveN 获取某个位置的马能走的下一步位置 +func (c *ChessJPZ) MoveN(x, y int) []*PositionPiece { + // 坐标转换成索引 + index, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是马 + o := c.Buf[index] + if !IsN(o) { + return nil + } + + var validMoves []*PositionPiece + + for _, dir := range KnightDirections { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err == nil { + // 获取新位置的棋子 + piece := c.Buf[index] + + if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) { + validMoves = append(validMoves, &PositionPiece{Index: index, P: piece}) + } + } + } + } + + return validMoves +} + +// MoveS 获取某个位置的象能走的下一步位置 +func (c *ChessJPZ) MoveS(x, y int) []*PositionPiece { + // 坐标转换成索引 + index, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是象 + o := c.Buf[index] + if !IsS(o) { + return nil + } + + var validMoves []*PositionPiece + + directions := QueenDirections + if IsWhite(o) { + directions = append(directions, Position{Y: 1}) + } else { + directions = append(directions, Position{Y: -1}) + } + + // 遍历象的移动方向 + for _, dir := range directions { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err == nil { + // 获取新位置的棋子 + piece := c.Buf[index] + + if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) { + validMoves = append(validMoves, &PositionPiece{Index: index, P: piece}) + } + } + } + } + + return validMoves +} + +// MoveM 获取某个位置的士能走的下一步位置 +func (c *ChessJPZ) MoveM(x, y int) []*PositionPiece { + // 坐标转换成索引 + index, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是士 + o := c.Buf[index] + if !IsM(o) { + return nil + } + + var validMoves []*PositionPiece + + for _, dir := range QueenDirections { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err == nil { + // 获取新位置的棋子 + piece := c.Buf[index] + + if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) { + validMoves = append(validMoves, &PositionPiece{Index: index, P: piece}) + } + } + } + } + + if IsWhite(o) && c.CambodianMove[MoveWM] && index == 4 { + index, err = XYToIndex(c.Width, c.Height, x, y+2) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + } + if IsBlack(o) && c.CambodianMove[MoveBM] && index == 59 { + index, err = XYToIndex(c.Width, c.Height, x, y-2) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + } + + return validMoves +} + +// MoveK 获取某个位置的王能走的下一步位置 +func (c *ChessJPZ) MoveK(x, y int) []*PositionPiece { + // 坐标转换成索引 + i, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是王 + o := c.Buf[i] + if !IsK(o) { + return nil + } + + var validMoves []*PositionPiece + + for _, dir := range KingDirections { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err == nil { + // 获取新位置的棋子 + piece := c.Buf[index] + + if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) { + validMoves = append(validMoves, &PositionPiece{Index: index, P: piece}) + } + } + } + } + + // 没有移动过,不能被将军,不能有棋子 + if IsWhite(o) && c.CambodianMove[MoveWK] { + if !c.IsCheck(false) { + //index, err := XYToIndex(c.Width, c.Height, x-1, y+2) + //if err == nil && IsEmpty(c.Buf[index]) { + // validMoves = append(validMoves, &PositionPiece{Index: index}) + //} + //index, err = XYToIndex(c.Width, c.Height, x+1, y+2) + //if err == nil && IsEmpty(c.Buf[index]) { + // validMoves = append(validMoves, &PositionPiece{Index: index}) + //} + index, err := XYToIndex(c.Width, c.Height, x-2, y+1) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + index, err = XYToIndex(c.Width, c.Height, x+2, y+1) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + } + } + // 没有移动过,不能被将军,不能有棋子 + if IsBlack(o) && c.CambodianMove[MoveBK] { + // 不能被将军,不能有棋子 + if !c.IsCheck(true) { + //index, err := XYToIndex(c.Width, c.Height, x-1, y-2) + //if err == nil && IsEmpty(c.Buf[index]) { + // validMoves = append(validMoves, &PositionPiece{Index: index}) + //} + //index, err = XYToIndex(c.Width, c.Height, x+1, y-2) + //if err == nil && IsEmpty(c.Buf[index]) { + // validMoves = append(validMoves, &PositionPiece{Index: index}) + //} + index, err := XYToIndex(c.Width, c.Height, x-2, y-1) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + index, err = XYToIndex(c.Width, c.Height, x+2, y-1) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + } + } + + // 移动之后不能被将军 + var moves []*PositionPiece + for _, v := range validMoves { + c.Move(i, v.Index) + if IsWhite(o) { + if !c.IsCheck(false) { + moves = append(moves, v) + } + } else { + if !c.IsCheck(true) { + moves = append(moves, v) + } + } + c.MoveBack() + } + + return moves +} + +// MoveP 获取某个位置的兵能走的下一步位置 +func (c *ChessJPZ) MoveP(x, y int) []*PositionPiece { + // 坐标转换成索引 + index, err := XYToIndex(c.Width, c.Height, x, y) + if err != nil { + return nil + } + + // 判断这个位置是不是兵 + o := c.Buf[index] + if !IsP(o) { + return nil + } + + var validMoves []*PositionPiece + + newY := y + isWhite := IsWhite(o) + if isWhite { + newY++ + } else { + newY-- + } + + // 前进一格 + index, err = XYToIndex(c.Width, c.Height, x, newY) + if err == nil && IsEmpty(c.Buf[index]) { + validMoves = append(validMoves, &PositionPiece{Index: index}) + } + + // 斜吃 + index, err = XYToIndex(c.Width, c.Height, x-1, newY) + if err == nil && !IsEmpty(c.Buf[index]) && IsWhite(c.Buf[index]) != isWhite { + validMoves = append(validMoves, &PositionPiece{Index: index, P: c.Buf[index]}) + } + index, err = XYToIndex(c.Width, c.Height, x+1, newY) + if err == nil && !IsEmpty(c.Buf[index]) && IsWhite(c.Buf[index]) != isWhite { + validMoves = append(validMoves, &PositionPiece{Index: index, P: c.Buf[index]}) + } + + return validMoves +} + +func (c *ChessJPZ) movePositions(index int, flag bool) []*PositionPiece { + x, y, err := IndexToXY(c.Width, c.Height, index) + if err != nil { + return nil + } + + o := c.Buf[index] + if IsEmpty(o) { + return nil + } + + var moves []*PositionPiece + switch o { + case WR, BR: + moves = c.MoveR(x, y) + case WN, BN: + moves = c.MoveN(x, y) + case WS, BS: + moves = c.MoveS(x, y) + case WM, BM: + moves = c.MoveM(x, y) + case WK, BK: + moves = c.MoveK(x, y) + case WP, BP: + moves = c.MoveP(x, y) + } + + // 被将军必须应将,走子后不能被将军 + if !flag { + return moves + } + var moves2 []*PositionPiece + for _, v := range moves { + c.Move(index, v.Index) + if IsWhite(o) && c.IsCheck(false) || IsBlack(o) && c.IsCheck(true) { + c.MoveBack() + continue + } + c.MoveBack() + moves2 = append(moves2, v) + } + + return moves2 +} + +// GetMovePositions 获取指定索引位置的棋子能走的位置 +func (c *ChessJPZ) GetMovePositions(index int) []*PositionPiece { + return c.movePositions(index, true) +} + +func (c *ChessJPZ) IsWhiteMove() bool { + return !c.Act +} + +func (c *ChessJPZ) IsBlackMove() bool { + return c.Act +} + +// GetIndexes 获取指定棋子的位置 +func (c *ChessJPZ) GetIndexes(p Piece) []int { + var positions []int + for k, v := range c.Buf { + if v == p { + positions = append(positions, k) + } + } + return positions +} + +// IsCheck 判断将军 +func (c *ChessJPZ) IsCheck(isWhite bool) bool { + for k, v := range c.Buf { + if IsEmpty(v) || IsWhite(v) != isWhite { + continue + } + if !IsK(v) { + for _, vv := range c.movePositions(k, false) { + if IsK(vv.P) { + return true + } + } + } else { + // 白王是否吃黑王 + x, y, _ := IndexToXY(c.Width, c.Height, k) + for _, dir := range KingDirections { + dx, dy := dir.X, dir.Y + newX, newY := x+dx, y+dy + + // 检查新位置是否在棋盘范围内 + if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height { + // 获取新位置的索引 + index, err := XYToIndex(c.Width, c.Height, newX, newY) + if err == nil { + if IsK(c.Buf[index]) { + return true + } + } + } + } + } + } + return false +} + +// IsCheckmate 判断将死 +func (c *ChessJPZ) IsCheckmate(isWhite bool) bool { + if !c.IsCheck(isWhite) { + return false + } + // 走任何一步,都将军 + for k, v := range c.Buf { + if !IsEmpty(v) && IsWhite(v) != isWhite { + for _, vv := range c.movePositions(k, false) { + c.Move(k, vv.Index) + if !c.IsCheck(isWhite) { + c.MoveBack() + return false + } + c.MoveBack() + } + } + } + + return true +} + +// CanMove 判断是否可以移动 +func (c *ChessJPZ) CanMove(fromIndex, toIndex int) bool { + if fromIndex < 0 || fromIndex >= len(c.Buf) || toIndex < 0 || toIndex >= len(c.Buf) { + return false + } + + if fromIndex == toIndex { + return false + } + + from := c.Buf[fromIndex] + if IsEmpty(from) { + return false + } + + // 检查操作权 + if c.Act == IsWhite(from) { + return false + } + + list := c.GetMovePositions(fromIndex) + for _, v := range list { + if v.Index == toIndex { + return true + } + } + + return false +} + +// Move 移动棋子 +func (c *ChessJPZ) Move(fromIndex, toIndex int) *PositionPiece { + if fromIndex < 0 || fromIndex >= len(c.Buf) || toIndex < 0 || toIndex >= len(c.Buf) { + return nil + } + + if fromIndex == toIndex { + return nil + } + + from := c.Buf[fromIndex] + if IsEmpty(from) { + return nil + } + + fromPiece := &PositionPiece{ + Index: fromIndex, + P: c.Buf[fromIndex], + } + to := &PositionPiece{ + Index: toIndex, + P: c.Buf[toIndex], + } + + c.Records = append(c.Records, &RecordJPZ{ + FromTo: [2]*PositionPiece{fromPiece, to}, + CambodianMove: c.CambodianMove, + }) + + // 王,士移动 + switch fromPiece.P { + case WK: + if c.CambodianMove[MoveWK] { + c.CambodianMove[MoveWK] = false + } + case BK: + if c.CambodianMove[MoveBK] { + c.CambodianMove[MoveBK] = false + } + case WM: + if c.CambodianMove[MoveWM] && fromIndex == 4 { + c.CambodianMove[MoveWM] = false + } + case BM: + if c.CambodianMove[MoveBM] && fromIndex == 59 { + c.CambodianMove[MoveBM] = false + } + } + + // 兵升士 + if IsP(fromPiece.P) { + _, y, err := IndexToXY(c.Width, c.Height, toIndex) + if err == nil { + if IsBlack(fromPiece.P) { + if y == 2 { + c.Buf[fromIndex] = BM + } + } else { + if y == 5 { + c.Buf[fromIndex] = WM + } + } + } + } + + c.Buf[toIndex] = c.Buf[fromIndex] + c.Buf[fromIndex] = "" + return to +} + +// MoveBack 悔棋一步 +func (c *ChessJPZ) MoveBack() bool { + if len(c.Records) == 0 { + return false + } + + record := c.Records[len(c.Records)-1] + c.Records = c.Records[:len(c.Records)-1] + + c.CambodianMove = record.CambodianMove + c.Buf[record.FromTo[0].Index] = record.FromTo[0].P + c.Buf[record.FromTo[1].Index] = record.FromTo[1].P + return true +} + +// IsStop 是否无子可走 +func (c *ChessJPZ) IsStop(isWhite bool) bool { + for k, v := range c.Buf { + if !IsEmpty(v) && IsWhite(v) == isWhite { + if len(c.GetMovePositions(k)) > 0 { + return false + } + } + } + + return true +} + +// GetMoveAllPositions 所有可移动棋子及位置 +func (c *ChessJPZ) GetMoveAllPositions(isWhite bool) [][2]*PositionPiece { + var positions [][2]*PositionPiece + for k, v := range c.Buf { + if !IsEmpty(v) && IsWhite(v) == isWhite { + for _, p := range c.GetMovePositions(k) { + positions = append(positions, [2]*PositionPiece{{ + Index: k, + P: v, + }, p}) + } + } + } + return positions +} diff --git a/gamerule/chess/chessjpz_test.go b/gamerule/chess/chessjpz_test.go new file mode 100644 index 0000000..63f9ca3 --- /dev/null +++ b/gamerule/chess/chessjpz_test.go @@ -0,0 +1,947 @@ +package chess + +import ( + "fmt" + "math/rand" + "strings" + "testing" +) + +func (c *ChessJPZ) TestPrint() { + for row := 0; row < c.Height; row++ { + for col := 0; col < c.Width; col++ { + idx := (c.Height-1-row)*c.Width + col + piece := c.Buf[idx] + if IsEmpty(piece) { + fmt.Printf("|. %2d| ", idx) // 打印空格表示没有棋子 + } else { + fmt.Printf("|%s%2d| ", piece, idx) + } + } + fmt.Println() + } +} + +func (c *ChessJPZ) TestPrintType() { + fmt.Println("[]Piece{") + for row := 0; row < c.Height; row++ { + for col := 0; col < c.Width; col++ { + idx := (c.Height-1-row)*c.Width + col + piece := c.Buf[idx] + if IsEmpty(piece) { + fmt.Printf("\"\"") // 打印空格表示没有棋子 + } else { + fmt.Printf("%s", piece) + } + if col < c.Width-1 { + fmt.Print(",") + } + } + fmt.Println() + } + fmt.Println("}") +} + +func (c *ChessJPZ) TestSet(buf []Piece, bufString []string, cambodianMove []bool) { + var buf2 []Piece + if len(buf) > 0 { + buf2 = make([]Piece, len(buf)) + for x := 0; x < c.Width; x++ { + for y := 0; y < c.Height; y++ { + buf2[(c.Height-1-y)*c.Width+x] = buf[y*c.Width+x] + } + } + } else { + buf2 = make([]Piece, len(bufString)) + for x := 0; x < c.Width; x++ { + for y := 0; y < c.Height; y++ { + buf2[(c.Height-1-y)*c.Width+x] = Piece(bufString[y*c.Width+x]) + } + } + } + + c.Buf = buf2 + if len(cambodianMove) == 4 { + for k, v := range cambodianMove { + c.CambodianMove[k] = v + } + } +} + +func Print() { + c := &ChessJPZ{} + c.Init() + c.TestPrint() + c.TestPrintType() +} + +func RandChess() { + // 生成随机数据 + b := make([]Piece, len(pieceJPZ)) + copy(b, pieceJPZ) + + // 随机删除棋子 + for k, v := range b { + if IsEmpty(v) || IsK(v) { + continue + } + if rand.Intn(2) == 1 { + b[k] = "" + } + } + // 随机移动棋子 + rand.Shuffle(len(b), func(i, j int) { + b[i], b[j] = b[j], b[i] + }) + fmt.Println("[]Piece{") + for row := 0; row < 8; row++ { + for col := 0; col < 8; col++ { + idx := (8-1-row)*8 + col + piece := b[idx] + if IsEmpty(piece) { + fmt.Printf("\"\"") // 打印空格表示没有棋子 + } else { + fmt.Printf("%s", piece) + } + if col < 7 { + fmt.Print(",") + } + } + fmt.Println() + } + fmt.Println("}") +} + +func TestNoTest(t *testing.T) { + //Print() + RandChess() +} + +func TestChessJPZ_GetIndexes(t *testing.T) { + // BR, BN, BS, BM, BK, BS, BN, BR, + // "", "", "", "", "", "", "", "", + // BP, BP, BP, BP, BP, BP, BP, BP, + // "", "", "", "", "", "", "", "", + // "", "", "", "", "", "", "", "", + // WP, WP, WP, WP, WP, WP, WP, WP, + // "", "", "", "", "", "", "", "", + // WR, WN, WS, WK, WM, WS, WN, WR, + data := map[Piece][]int{ + WR: {0, 7}, + WN: {1, 6}, + WS: {2, 5}, + WM: {4}, + WK: {3}, + WP: {16, 17, 18, 19, 20, 21, 22, 23}, + BR: {56, 63}, + BN: {57, 62}, + BS: {58, 61}, + BM: {59}, + BK: {60}, + BP: {40, 41, 42, 43, 44, 45, 46, 47}, + } + + c := new(ChessJPZ) + c.Init() + //c.TestPrint() + + for k, target := range data { + result := c.GetIndexes(k) + if len(result) != len(target) { + t.Fatalf("%s: target:%v, result:%v\n", k, target, result) + } + for _, r := range result { + has := false + for _, i := range target { + if i == r { + has = true + break + } + } + if !has { + t.Fatalf("%s: target:%v, result:%v\n", k, target, result) + } + } + } +} + +func TestChessJPZ_GetMovePositions(t *testing.T) { + type Test struct { + Buf []Piece + WMMoved bool + BMMoved bool + WKMoved bool + BKMoved bool + + Index int + + Moves []*PositionPiece + } + + // 测试数据 + data := []Test{ + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 0, + Moves: []*PositionPiece{ + {Index: 8, P: ""}, + }, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 1, + Moves: []*PositionPiece{ + {Index: 11, P: ""}, + }, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 2, + Moves: []*PositionPiece{{Index: 9}, {Index: 11}, {Index: 10}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 3, + Moves: []*PositionPiece{{Index: 10}, {Index: 11}, {Index: 12}, {Index: 9}, {Index: 13}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 4, + Moves: []*PositionPiece{{Index: 11}, {Index: 13}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 5, + Moves: []*PositionPiece{{Index: 12}, {Index: 14}, {Index: 13}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 6, + Moves: []*PositionPiece{{Index: 12}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 7, + Moves: []*PositionPiece{{Index: 15}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 16, + Moves: []*PositionPiece{{Index: 24}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 17, + Moves: []*PositionPiece{{Index: 25}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 40, + Moves: []*PositionPiece{{Index: 32}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 41, + Moves: []*PositionPiece{{Index: 33}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 56, + Moves: []*PositionPiece{{Index: 48}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 57, + Moves: []*PositionPiece{{Index: 51}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 58, + Moves: []*PositionPiece{{Index: 49}, {Index: 50}, {Index: 51}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 59, + Moves: []*PositionPiece{{Index: 50}, {Index: 52}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 60, + Moves: []*PositionPiece{{Index: 51}, {Index: 52}, {Index: 53}, {Index: 50}, {Index: 54}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 61, + Moves: []*PositionPiece{{Index: 52}, {Index: 54}, {Index: 53}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 62, + Moves: []*PositionPiece{{Index: 52}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Index: 63, + Moves: []*PositionPiece{{Index: 55}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: false, + Index: 60, + Moves: []*PositionPiece{{Index: 51}, {Index: 52}, {Index: 53}}, + }, + { + Buf: pieceJPZ, + WMMoved: true, + BMMoved: true, + BKMoved: true, + WKMoved: false, + Index: 3, + Moves: []*PositionPiece{{Index: 10}, {Index: 11}, {Index: 12}}, + }, + { + Buf: []Piece{ + BR, BN, BS, BM, BK, BS, BN, BR, + "", "", "", "", "", "", "", "", + BP, BP, BP, BP, BP, BP, BP, BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, WP, WP, WP, "", WP, WP, WP, + "", "", "", "", "", "", "", "", + WR, WN, WS, WK, WM, WS, WN, WR, + }, + BMMoved: true, + WKMoved: true, + BKMoved: true, + WMMoved: false, + Index: 4, + Moves: []*PositionPiece{{Index: 11}, {Index: 13}}, + }, + { + Buf: []Piece{ + BR, BN, BS, BM, BK, BS, BN, BR, + "", "", "", "", "", "", "", "", + BP, BP, BP, BP, BP, BP, BP, BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, WP, WP, WP, "", WP, WP, WP, + "", "", "", "", "", "", "", "", + WR, WN, WS, WK, WM, WS, WN, WR, + }, + WMMoved: true, + Index: 4, + Moves: []*PositionPiece{{Index: 11}, {Index: 13}, {Index: 20}}, + }, + { + Buf: []Piece{ + BR, BN, BS, BM, BK, BS, BN, BR, + "", "", "", "", "", "", "", "", + BP, BP, BP, "", BP, BP, BP, BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, WP, WP, WP, "", WP, WP, WP, + "", "", "", "", "", "", "", "", + WR, WN, WS, WK, WM, WS, WN, WR, + }, + WMMoved: true, + WKMoved: true, + BKMoved: true, + BMMoved: false, + Index: 59, + Moves: []*PositionPiece{{Index: 50}, {Index: 52}}, + }, + { + Buf: []Piece{ + BR, BN, BS, BM, BK, BS, BN, BR, + "", "", "", "", "", "", "", "", + BP, BP, BP, "", BP, BP, BP, BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, WP, WP, WP, "", WP, WP, WP, + "", "", "", "", "", "", "", "", + WR, WN, WS, WK, WM, WS, WN, WR, + }, + BMMoved: true, + Index: 59, + Moves: []*PositionPiece{{Index: 50}, {Index: 52}, {Index: 43}}, + }, + } + + f := func(name string, moves []*PositionPiece) { + b := strings.Builder{} + for _, s := range moves { + b.WriteString(fmt.Sprintf("%+v,", *s)) + } + fmt.Println(name, b.String()) + } + + c := new(ChessJPZ) + c.Init() + //c.TestPrint() + for _, v := range data { + c.TestSet(v.Buf, nil, []bool{v.WKMoved, v.WMMoved, v.BMMoved, v.BKMoved}) + moves := c.GetMovePositions(v.Index) + if len(v.Moves) != len(moves) { + c.TestPrint() + fmt.Printf("Index:%v\n", v.Index) + f("Target Moves:", v.Moves) + f("Result Moves:", moves) + t.Fail() + } + for _, m := range moves { + has := false + for _, v := range v.Moves { + if v.Index == m.Index && v.P == m.P { + has = true + break + } + } + if !has { + c.TestPrint() + fmt.Printf("Index:%v\n", v.Index) + f("Target Moves:", v.Moves) + f("Result Moves:", moves) + t.Fail() + } + } + } +} + +func TestChessJPZ_GetMovePositions2(t *testing.T) { + testChess := []Piece{ + BR, "", BS, BM, BK, BN, "", "", + "", "", "", "", "", "", "", WR, + BP, "", WM, WN, "", WM, "", BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, "", BP, "", "", BP, "", WP, + "", "", "", "", "", "", "", "", + "", "", WS, WK, WM, "", "", "", + } + + type Test struct { + Buf []Piece + WMMoved bool + BMMoved bool + WKMoved bool + BKMoved bool + Index int + Moves []*PositionPiece + } + + data := []Test{ + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 2, + Moves: []*PositionPiece{{Index: 9}, {Index: 11}, {Index: 10}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 3, + Moves: []*PositionPiece{{Index: 10}, {Index: 13}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 4, + Moves: []*PositionPiece{{Index: 11}, {Index: 13}, {Index: 20}}, + }, + { + Buf: testChess, + Index: 2, + Moves: []*PositionPiece{{Index: 9}, {Index: 11}, {Index: 10}}, + }, + { + Buf: testChess, + Index: 3, + Moves: []*PositionPiece{{Index: 10}}, + }, + { + Buf: testChess, + Index: 4, + Moves: []*PositionPiece{{Index: 11}, {Index: 13}}, + }, + + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 16, + Moves: []*PositionPiece{{Index: 24}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 18, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 21, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 23, + Moves: []*PositionPiece{{Index: 31}}, + }, + { + Buf: testChess, + Index: 16, + Moves: []*PositionPiece{{Index: 24}}, + }, + { + Buf: testChess, + Index: 18, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 21, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 23, + Moves: []*PositionPiece{{Index: 31}}, + }, + + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 40, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 42, + Moves: []*PositionPiece{{Index: 33}, {Index: 49}, {Index: 35}, {Index: 51}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 43, + Moves: []*PositionPiece{{Index: 53}, {Index: 37}, {Index: 49}, {Index: 33}, {Index: 60, P: BK}, {Index: 28}, {Index: 58, P: BS}, {Index: 26}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 45, + Moves: []*PositionPiece{{Index: 36}, {Index: 52}, {Index: 38}, {Index: 54}}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 47, + Moves: []*PositionPiece{}, + }, + + { + Buf: testChess, + Index: 40, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 42, + Moves: []*PositionPiece{{Index: 33}, {Index: 49}, {Index: 35}, {Index: 51}}, + }, + { + Buf: testChess, + Index: 43, + Moves: []*PositionPiece{{Index: 53}, {Index: 37}, {Index: 49}, {Index: 33}, {Index: 60, P: BK}, {Index: 28}, {Index: 58, P: BS}, {Index: 26}}, + }, + { + Buf: testChess, + Index: 45, + Moves: []*PositionPiece{{Index: 36}, {Index: 52}, {Index: 38}, {Index: 54}}, + }, + { + Buf: testChess, + Index: 47, + Moves: []*PositionPiece{}, + }, + + { + Buf: testChess, + Index: 55, + Moves: []*PositionPiece{{Index: 47, P: BP}, {Index: 63}, {Index: 54}, {Index: 53}, {Index: 52}, {Index: 51}, {Index: 50}, {Index: 49}, {Index: 48}}, + }, + + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 56, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 58, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 59, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 60, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, WMMoved: true, BMMoved: true, WKMoved: true, BKMoved: true, + Index: 61, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 56, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 58, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 59, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 60, + Moves: []*PositionPiece{}, + }, + { + Buf: testChess, + Index: 61, + Moves: []*PositionPiece{}, + }, + } + + f := func(name string, moves []*PositionPiece) { + b := strings.Builder{} + for _, s := range moves { + b.WriteString(fmt.Sprintf("%+v,", *s)) + } + fmt.Println(name, b.String()) + } + + c := new(ChessJPZ) + c.Init() + //c.TestPrint() + for _, v := range data { + c.TestSet(v.Buf, nil, []bool{v.WKMoved, v.WMMoved, v.BMMoved, v.BKMoved}) + moves := c.GetMovePositions(v.Index) + if len(v.Moves) != len(moves) { + c.TestPrint() + fmt.Printf("Index:%v\n", v.Index) + f("Target Moves:", v.Moves) + f("Result Moves:", moves) + t.Fatal() + } + for _, m := range moves { + has := false + for _, v := range v.Moves { + if v.Index == m.Index && v.P == m.P { + has = true + break + } + } + if !has { + c.TestPrint() + fmt.Printf("Index:%v\n", v.Index) + f("Target Moves:", v.Moves) + f("Result Moves:", moves) + t.Fatal() + } + } + } + +} + +func TestChessJPZ_GetMoveAllPositions(t *testing.T) { + type Test struct { + Buf []Piece + WMMoved bool + BMMoved bool + WKMoved bool + BKMoved bool + WhiteMoves, BlackMoves [][2]*PositionPiece + } + + data := []Test{ + { + Buf: []Piece{ + "", "", "", "", BS, "", "", "", + "", "", BM, "", "", "", BS, "", + "", "", "", "", WK, "", "", "", + "", "", BN, "", "", "", "", BR, + "", WM, "", WR, WN, BP, "", "", + "", "", BK, "", "", BP, "", WP, + "", WR, "", "", "", "", "", "", + WM, "", "", "", "", "", "", "", + }, + WhiteMoves: [][2]*PositionPiece{{{Index: 25, P: WM}, {Index: 34, P: BN}}, {{Index: 28, P: WN}, {Index: 34, P: BN}}}, + BlackMoves: [][2]*PositionPiece{{{Index: 18, P: BK}, {Index: 27, P: WR}}}, + }, + } + + f := func(name string, moves [][2]*PositionPiece) { + b := strings.Builder{} + for _, s := range moves { + b.WriteString(fmt.Sprintf("%+v,", *s[0])) + b.WriteString(fmt.Sprintf("%+v,", *s[1])) + } + fmt.Println(name, b.String()) + } + + c := new(ChessJPZ) + c.Init() + //c.TestPrint() + for _, v := range data { + c.TestSet(v.Buf, nil, []bool{v.WKMoved, v.WMMoved, v.BMMoved, v.BKMoved}) + moves := c.GetMoveAllPositions(true) + if len(v.WhiteMoves) != len(moves) { + c.TestPrint() + f("Target WhiteMoves:", v.WhiteMoves) + f("Result WhiteMoves:", moves) + t.Fatal() + } + for _, m := range moves { + has := false + for _, v := range v.WhiteMoves { + if v[0].Index == m[0].Index && v[0].P == m[0].P && v[1].Index == m[1].Index && v[1].P == m[1].P { + has = true + break + } + } + if !has { + c.TestPrint() + f("Target WhiteMoves:", v.WhiteMoves) + f("Result WhiteMoves:", moves) + t.Fatal() + } + } + + moves = c.GetMoveAllPositions(false) + if len(v.BlackMoves) != len(moves) { + c.TestPrint() + f("Target BlackMoves:", v.BlackMoves) + f("Result BlackMoves:", moves) + t.Fatal() + } + for _, m := range moves { + has := false + for _, v := range v.BlackMoves { + if v[0].Index == m[0].Index && v[0].P == m[0].P && v[1].Index == m[1].Index && v[1].P == m[1].P { + has = true + break + } + } + if !has { + c.TestPrint() + f("Target BlackMoves:", v.BlackMoves) + f("Result BlackMoves:", moves) + t.Fatal() + } + } + } + +} + +func TestChessJPZ_IsCheck(t *testing.T) { + type Test struct { + Buf []Piece + WMMoved bool + BMMoved bool + WKMoved bool + BKMoved bool + Check [2]bool + Checkmate [2]bool + Stop [2]bool + } + + data := []Test{ + { + Buf: []Piece{ + BR, "", BS, BM, BK, BN, "", "", + "", "", "", "", "", "", "", WR, + BP, "", WM, WN, "", WM, "", BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, "", BP, "", "", BP, "", WP, + "", "", "", "", "", "", "", "", + "", "", WS, WK, WM, "", "", "", + }, + WMMoved: true, + BMMoved: true, + WKMoved: true, + BKMoved: true, + Check: [2]bool{true, false}, + Checkmate: [2]bool{true, false}, + Stop: [2]bool{false, true}, + }, + { + Buf: []Piece{ + BR, "", BS, BM, BK, BN, "", "", + "", "", "", "", "", "", "", WR, + BP, "", WM, WN, "", WM, "", BP, + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + WP, "", BP, "", "", BP, "", WP, + "", "", "", "", "", "", "", "", + "", "", WS, WK, WM, "", "", "", + }, + Check: [2]bool{true, false}, + Checkmate: [2]bool{true, false}, + Stop: [2]bool{false, true}, + }, + { + Buf: []Piece{ + "", "", "", "", BS, "", "", "", + "", "", BM, "", "", "", BS, "", + "", "", "", "", WK, "", "", "", + "", "", BN, "", "", "", "", BR, + "", WM, "", WR, WN, BP, "", "", + "", "", BK, "", "", BP, "", WP, + "", WR, "", "", "", "", "", "", + WM, "", "", "", "", "", "", "", + }, + Check: [2]bool{true, true}, + Checkmate: [2]bool{false, false}, + Stop: [2]bool{false, false}, + }, + { + Buf: []Piece{ + "", "", "", "", BS, "", "", "", + "", "", BM, "", "", "", BS, "", + "", BK, "", "", WK, "", "", "", + "", "", BP, "", "", "", "", BR, + "", "", WP, "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + }, + Check: [2]bool{false, false}, + Checkmate: [2]bool{false, false}, + Stop: [2]bool{true, false}, + }, + { + Buf: []Piece{ + "", "", "", "", WR, "", WR, "", + "", "", BM, BR, "", BK, "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", WN, "", WP, "", + "", "", "", BP, "", "", "", "", + "", "", BS, "", WP, "", WS, "", + "", "", WS, "", WK, "", BM, "", + "", "", "", "", WM, "", "", "", + }, + Check: [2]bool{true, false}, + Checkmate: [2]bool{true, false}, + Stop: [2]bool{false, true}, + }, + } + + c := new(ChessJPZ) + c.Init() + //c.TestPrint() + for _, v := range data { + c.TestSet(v.Buf, nil, []bool{v.WKMoved, v.WMMoved, v.BMMoved, v.BKMoved}) + if v.Check[0] != c.IsCheck(true) || v.Check[1] != c.IsCheck(false) { + c.TestPrint() + fmt.Println("Target Check:", v.Check) + fmt.Println("Result Check:", c.IsCheck(true), c.IsCheck(false)) + t.Fatal() + } + if v.Checkmate[0] != c.IsCheckmate(true) || v.Checkmate[1] != c.IsCheckmate(false) { + c.TestPrint() + fmt.Println("Target Checkmate:", v.Checkmate) + fmt.Println("Result Checkmate:", c.IsCheckmate(true), c.IsCheckmate(false)) + t.Fatal() + } + if v.Stop[0] != c.IsStop(true) || v.Stop[1] != c.IsStop(false) { + c.TestPrint() + fmt.Println("Target Stop:", v.Stop) + fmt.Println("Result Stop:", c.IsStop(true), c.IsStop(false)) + t.Fatal() + } + } +} diff --git a/gamerule/chess/constants.go b/gamerule/chess/constants.go new file mode 100644 index 0000000..39fe5dd --- /dev/null +++ b/gamerule/chess/constants.go @@ -0,0 +1,79 @@ +package chess + +import "time" + +const ( + TestOpen bool = false //测试开关 + MaxNumOfPlayer int = 2 //最多人数 + InvalidPos int = -1 + PlayerStepLimit = 64 // 残局计步最大回合数 +) + +const ( + WaitStartTimeout = time.Second * 3 // 开始游戏 + HandCardTimeout = time.Second * 1 // 发牌 + PlayerOpTimeout = time.Second * 16 // 玩家操作阶段 + BilledTimeout = time.Second * 1 // 结算 + + PlayerTotalTime = time.Minute*15 + time.Second // 下棋总时长 +) + +// 场景状态 +const ( + SceneStateWaitPlayer int = iota // 等待玩家 + SceneStateWaitStart // 开始游戏 + SceneStateChessInit // 发送棋盘数据 + SceneStatePlayerOp // 玩家操作 + SceneStateBilled // 结算 + SceneStateMax +) + +// 玩家操作 +const ( + PlayerOpNull int32 = iota //0,初始值 + PlayerOpPlay //1,出牌 + PlayerOpPass //2,过牌 + PlayerOpStart //3,房主开始游戏 + PlayerOpSurrender //4, 投降操作 + PlayerOpTest //5, 测试 + PlayerOpStep //6,计步操作(1 开始,0结束) + PlayerOpDraw //7,求和 1请求求和 2拒绝求和 3同意求和 + PlayerOpNextPlay //8,再来一局 + + // 人机对战操作 + PlayerOpAISwitch //9,ai开关 0关闭 1开启 + PlayerOpAIRestart //10,重新开始 + PlayerOpAISwap //11,黑白互换 + PlayerOpAISuggest //12,建议下一步 + PlayerOpAIBack //13,悔棋一步 +) + +const ( + BilledTypeRobot = 1 // 人机对战 + BilledTypeGT = 2 // 我方段位大于对方段位 + BilledTypeEQ = 3 // 我方段位等于对方段位 + BilledTypeLT = 4 // 我方段位小于对方段位 + BilledTypeMatch = 5 // 比赛场 +) + +const ( + Win = 1 // 赢 + Lose = 2 // 输 + Draw = 0 // 平 +) + +const ( + RequestDraw = 1 // 请求求和 + RefuseDraw = 2 // 拒绝求和 + AgreeDraw = 3 // 同意求和 + CancelDraw = 4 // 撤销求和 +) + +const ( + StepCancel = 0 // 取消计步 + StepStart = 1 // 开始计步 +) + +const ( + StaticsChessTime = "chesstimes" +) diff --git a/gamerule/chess/directions.go b/gamerule/chess/directions.go new file mode 100644 index 0000000..ffc2743 --- /dev/null +++ b/gamerule/chess/directions.go @@ -0,0 +1,24 @@ +package chess + +// 方向 +var ( + Up = Position{Y: -1} + Down = Position{Y: 1} + Left = Position{X: -1} + Right = Position{X: 1} +) + +// StraightDirections 四个直线方向 +var StraightDirections = []Position{Up, Down, Left, Right} + +// KnightDirections 马可以走的8个方向 +var KnightDirections = []Position{ + {2, 1}, {2, -1}, {-2, 1}, {-2, -1}, + {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, +} + +// QueenDirections 仕斜走四个方桑 +var QueenDirections = []Position{{-1, -1}, {-1, 1}, {1, -1}, {1, 1}} + +// KingDirections 王走的8个方向 +var KingDirections = []Position{{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}} diff --git a/gamerule/chess/logic.go b/gamerule/chess/logic.go new file mode 100644 index 0000000..ba2a468 --- /dev/null +++ b/gamerule/chess/logic.go @@ -0,0 +1,205 @@ +package chess + +import "fmt" + +type Piece string + +const ( + White = "W" + Black = "B" + R = "R" + N = "N" + S = "S" + M = "M" + K = "K" + Q = "Q" + P = "P" + + WR Piece = "WR" + WN Piece = "WN" + WS Piece = "WS" + WM Piece = "WM" + WK Piece = "WK" + WQ Piece = "WQ" + WP Piece = "WP" + BR Piece = "BR" + BN Piece = "BN" + BS Piece = "BS" + BM Piece = "BM" + BK Piece = "BK" + BQ Piece = "BQ" + BP Piece = "BP" + EM Piece = "" +) + +func IsWhite(p Piece) bool { + if IsEmpty(p) { + return false + } + return p[0] == 'W' +} + +func IsBlack(p Piece) bool { + if IsEmpty(p) { + return false + } + return p[0] == 'B' +} + +func IsEmpty(p Piece) bool { + return p == "" || p == " " || p == " " || p == "." +} + +func IsR(p Piece) bool { + return len(p) == 2 && p[1] == 'R' +} + +func IsN(p Piece) bool { + return len(p) == 2 && p[1] == 'N' +} + +func IsS(p Piece) bool { + return len(p) == 2 && p[1] == 'S' +} + +func IsM(p Piece) bool { + return len(p) == 2 && p[1] == 'M' +} + +func IsK(p Piece) bool { + return len(p) == 2 && p[1] == 'K' +} + +func IsQ(p Piece) bool { + return len(p) == 2 && p[1] == 'Q' +} + +func IsP(p Piece) bool { + return len(p) == 2 && p[1] == 'P' +} + +func ToPiece(color, chessType string) Piece { + if color != White && color != Black { + return "" + } + return Piece(fmt.Sprint(color, chessType)) +} + +func XYToIndex(width, height, x, y int) (int, error) { + if x < 0 || x >= width || y < 0 || y >= height { + return -1, fmt.Errorf("坐标超出范围") + } + + return y*width + x, nil +} + +func IndexToXY(width, height, index int) (int, int, error) { + if index < 0 || index >= width*height { + return -1, -1, fmt.Errorf("索引超出范围") + } + + x := index % width + y := index / width + + return x, y, nil +} + +// Position 坐标 +type Position struct { + X, Y int +} + +// PositionPiece 坐标及这个坐标存在的棋子 +type PositionPiece struct { + Index int // 坐标 + P Piece // 被吃的对方棋子 +} + +type Logic interface { + // Init 重置棋盘 + Init() + // GetWidth 获取宽度 + GetWidth() int + // GetHeight 获取高度 + GetHeight() int + // GetChess 获取棋子数据 + GetChess() []string + // GetAct 获取该谁走 + GetAct() string + // GetCastling 王车易位可行性 + GetCastling() []bool + // GetEnPassant 过路兵位置 + GetEnPassant() int + // GetCambodianMove 获取柬埔寨象棋王士特殊走子状态 + GetCambodianMove() []bool + // GetRound 获取回合数 + GetRound() int + // GetPiece 获取棋子 + GetPiece(index int) Piece + // GetChessNum 获取棋子数量 + GetChessNum(piece Piece) int + // GetWhiteNum 获取白子数量 + GetWhiteNum() int + // GetBlackNum 获取黑子数量 + GetBlackNum() int + // Has 是否有棋子 + Has(piece Piece) bool + // GetVariant 获取规则类型 + GetVariant() int + + // GetMovePositions 获取指定索引位置的棋子能走的位置 + GetMovePositions(index int) []*PositionPiece + // GetMoveAllPositions 所有可移动棋子及位置 + GetMoveAllPositions(isWhite bool) [][2]*PositionPiece + // GetIndexes 获取指定棋子的位置 + GetIndexes(p Piece) []int + // IsCheck 判断将军 + IsCheck(isWhite bool) bool + // IsCheckmate 判断将死 + IsCheckmate(isWhite bool) bool + // Move 移动棋子 + Move(fromIndex, toIndex int) *PositionPiece + // MoveBack 悔棋一步 + MoveBack() bool + // CanMove 判断是否可以移动 + CanMove(fromIndex, toIndex int) bool + // IsStop 判断无子可走 + IsStop(isWhite bool) bool + // IsWhiteMove 是否该白旗走 + IsWhiteMove() bool + // IsBlackMove 是否该黑棋字走 + IsBlackMove() bool + + // GetCheck 获取将军状态 + GetCheck() []bool + // GetCheckmate 获取将死状态 + GetCheckmate() []bool + // NextAct 切换对方操作 + NextAct() + + // SetChess 设置棋盘 + SetChess([]string) + // Set 设置数据 + Set(bufStr []string, bufPiece []Piece, act string, castling []bool, cambodianMove []bool, enPassant int, round int) + // PrintChess 打印 + PrintChess() + // GenFen fen记谱法 + GenFen() string + // UciTwoPosToIdxList ai + UciTwoPosToIdxList(fromTo string) []int64 +} + +const ( + ChessVarChess int = iota + ChessVarMakruk + ChessVarCambodian +) + +func NewChess(v int) Logic { + switch v { + case ChessVarChess, ChessVarMakruk, ChessVarCambodian: + return new(ChessJPZ) + default: + return new(ChessJPZ) + } +} diff --git a/gamerule/common/poker-recognize.go b/gamerule/common/poker-recognize.go new file mode 100644 index 0000000..e95bc74 --- /dev/null +++ b/gamerule/common/poker-recognize.go @@ -0,0 +1,49 @@ +package common + +import ( + "fmt" +) + +//牌序- K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +var pokerMap = map[int]string{1: "A", 11: "J", 12: "Q", 13: "K"} +var pokerColor = []string{"♦", "♣", "♥", "♠"} + +const ( + pokerMax = 13 + colorMax = 4 +) + +func PokerTostring(params []int) string { + pokerString := "" + if len(params) == 0 { + return pokerString + } + for i := 0; i < len(params); i++ { + number := params[i] + color := number / pokerMax + if color >= colorMax { + fmt.Println("Param ", params[i], " is not poker value.") + pokerString += "X" + continue + } + value := number%pokerMax + 1 + if _, ok := pokerMap[value]; ok { + pokerString += fmt.Sprint(pokerColor[color], pokerMap[value]) + } else { + pokerString += fmt.Sprint(pokerColor[color], value) + } + } + return pokerString +} +func PokerArrToString(params [][]int) string { + pokerString := "[" + for i := 0; i < len(params); i++ { + pokerString = pokerString + "[" + PokerTostring(params[i]) + "]" + } + pokerString += "]" + return pokerString +} diff --git a/gamerule/common/slice.go b/gamerule/common/slice.go new file mode 100644 index 0000000..9336547 --- /dev/null +++ b/gamerule/common/slice.go @@ -0,0 +1,22 @@ +package common + +// Data. Predict: ["4"] +// a := []string{"1","2","3","4"} +// b := []string{"0","1","2","3"} +func GetASliceInt32NotInB(a []int32, b []int32) []int32 { + var c []int32 + temp := map[int32]struct{}{} + for _, val := range b { + if _, ok := temp[val]; !ok { + temp[val] = struct{}{} // 空struct 不占内存空间 + } + } + + for _, val := range a { + if _, ok := temp[val]; !ok { + c = append(c, val) + } + } + + return c +} diff --git a/gamerule/crash/constant.go b/gamerule/crash/constant.go new file mode 100644 index 0000000..054da2f --- /dev/null +++ b/gamerule/crash/constant.go @@ -0,0 +1,35 @@ +package crash + +import "time" + +const ( + CrashStakeAntTimeout = time.Second * 1 //准备押注 + CrashStakeTimeout = time.Second * 6 //押注 + CrashOpenCardAntTimeout = time.Second * 1 //准备开始 + CrashOpenCardTimeout = time.Minute * 30 //开始 + CrashBilledTimeout = time.Millisecond * 3500 //结算 + CrashBatchSendBetTimeout = time.Second * 1 //发送下注数据时间间隔 +) + +//场景状态 +const ( + CrashSceneStateStakeAnt int = iota //准备押注 + CrashSceneStateStake //押注 + CrashSceneStateOpenCardAnt //准备开始 + CrashSceneStateOpenCard //开始 + CrashSceneStateBilled //结算 + CrashSceneStateMax +) + +//玩家操作 +const ( + CrashPlayerOpBet int = iota //下注 + CrashPlayerOpGetOLList //获取在线列表 + CrashPlayerOpParachute //跳伞 +) + +//倍率 +const ( + MinMultiple = 100 //最小倍率 + MaxMultiple = 10000 //最大倍率 +) diff --git a/gamerule/crash/poker.go b/gamerule/crash/poker.go new file mode 100644 index 0000000..66ac673 --- /dev/null +++ b/gamerule/crash/poker.go @@ -0,0 +1,122 @@ +package crash + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "math" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" + "strconv" + "time" +) + +const ( + POKER_CART_CNT int = 10000 +) + +var cardSeed = time.Now().UnixNano() + +type Card struct { + Hashstr string + Explode int32 +} + +type Poker struct { + buf [POKER_CART_CNT]*Card + pos int + gameHash string + wheel int +} + +// Sha256加密 +func Sha256(src string) string { + m := sha256.New() + m.Write([]byte(src)) + res := hex.EncodeToString(m.Sum(nil)) + return res +} + +func HashToMultiple(oldgameHash string, wheel int) int32 { + //计算当前传来的gameHash + jsHash := Sha256(fmt.Sprintf("%v%v", oldgameHash, model.GameParamData.AtomGameHash[wheel])) + + //哈希计算 + s13 := jsHash[0:13] + h, _ := strconv.ParseInt(s13, 16, 0) + e := math.Pow(2, 52) + result := math.Floor((96 * e) / (e - float64(h))) + if result < 101 { + result = 0 + } + if result > 10000 { + result = 10000 + } + return int32(result) +} + +func NewPoker(period, wheel int) *Poker { + if len(model.GameParamData.InitGameHash)-1 < wheel { + wheel = 0 + } + gameHash := model.GameParamData.InitGameHash[wheel] //"ff6c5b1daa1068897377f7a64a762eafda4d225f25bf8e3bb476a7c4f2d10468" + p := &Poker{} + p.init(gameHash, period, wheel) + return p +} + +//const salt = `0ead8d98e67a7c9197a6bb0e664bb84adbeb25e4e0db63d2158e48b98a50534d` + +func (this *Poker) init(gameHash string, period, wheel int) { + if this.wheel != wheel { + this.wheel = wheel + } + + for i := POKER_CART_CNT - 1; i >= 0; i-- { + //logger.Logger.Info("gameHash:",gameHash) + oldgameHash := gameHash + + //生成下一个gmaeHash + if oldgameHash != "" { + gameHash = Sha256(fmt.Sprintf("%v", oldgameHash)) + } else { + gameHash = Sha256(fmt.Sprintf("%v%v", gameHash, model.GameParamData.AtomGameHash[wheel])) + } + //logger.Logger.Info("newgameHash:",gameHash) + this.gameHash = gameHash + + result := HashToMultiple(oldgameHash, wheel) + + //当前哈希对 + this.buf[i] = &Card{ + Hashstr: oldgameHash, + Explode: int32(result), + } + logger.Logger.Infof("curgameHash:%v %v nextgameHash:%v", oldgameHash, result, gameHash) + } + //this.Shuffle() + this.pos = period +} + +func (this *Poker) Next() (*Card, int, int) { + if this.pos >= len(this.buf) { + if len(model.GameParamData.InitGameHash) > this.wheel+1 { + this.wheel++ + this.gameHash = model.GameParamData.AtomGameHash[this.wheel] + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UptIntKVGameData("CrashWheel", int64(this.wheel)) + }), nil, "UptCrashWheelKVGameData").Start() + //gameHash := Sha256(fmt.Sprintf("%v", time.Now().UnixNano())) + this.init(this.gameHash, 0, this.wheel) + this.pos = 0 + c := this.buf[this.pos] + this.pos++ + return c, this.pos, this.wheel + } + c := this.buf[this.pos] + this.pos++ + return c, this.pos, this.wheel +} diff --git a/gamerule/dezhoupoker/cardkind_test.go b/gamerule/dezhoupoker/cardkind_test.go new file mode 100644 index 0000000..72b63ff --- /dev/null +++ b/gamerule/dezhoupoker/cardkind_test.go @@ -0,0 +1,117 @@ +package dezhoupoker + +import ( + "fmt" + "testing" +) + +//牌序- K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +func TestCards(t *testing.T) { + + cardInfo1 := &CardsInfo{Kind: KindOfCard_Straight, KindCards: []int32{26, 22, 23, 24, 25}} + cardInfo1.CalValue() + + cardInfo2 := &CardsInfo{Kind: KindOfCard_Straight, KindCards: []int32{39, 35, 23, 24, 25}} + cardInfo2.CalValue() + + fmt.Println(cardInfo1.ValueScore, cardInfo2.ValueScore) + + fmt.Println("ts") +} + +func TestKindOfCardFigureUpEx_FigureUpByCard(t *testing.T) { + testcases := []struct { + Cards []int32 + Kind int32 + }{ + {[]int32{2, 3}, KindOfCard_HighCard}, + {[]int32{2, 3, 7, 9, 19}, KindOfCard_HighCard}, + {[]int32{0, 13}, KindOfCard_OnePair}, + {[]int32{2, 15, 7, 16, 17}, KindOfCard_OnePair}, + {[]int32{2, 15, 3, 16, 17}, KindOfCard_TwoPair}, + {[]int32{2, 15, 3, 16, 17, 18}, KindOfCard_TwoPair}, + {[]int32{2, 15, 3, 16, 17, 18, 7}, KindOfCard_TwoPair}, + {[]int32{2, 15, 28, 16, 17, 18}, KindOfCard_ThreeKind}, + {[]int32{2, 15, 28, 41, 17, 18}, KindOfCard_FourKind}, + {[]int32{2, 16, 17, 18, 19, 9}, KindOfCard_Straight}, + {[]int32{2, 16, 17, 18, 19}, KindOfCard_Straight}, + {[]int32{21, 16, 17, 18, 19}, KindOfCard_Flush}, + {[]int32{21, 16, 17, 18, 19, 1}, KindOfCard_Flush}, + {[]int32{3, 16, 4, 17, 30, 1}, KindOfCard_Fullhouse}, + {[]int32{3, 16, 4, 17, 30}, KindOfCard_Fullhouse}, + {[]int32{15, 16, 17, 18, 19}, KindOfCard_StraightFlush}, + {[]int32{15, 16, 17, 18, 19, 1}, KindOfCard_StraightFlush}, + {[]int32{0, 9, 10, 11, 12, 1}, KindOfCard_RoyalFlush}, + } + + for _, c := range testcases { + ci := KindOfCardFigureUpExSington.FigureUpByCard(c.Cards) + if ci == nil || ci.Kind != c.Kind { + t.Errorf("KindOfCardFigureUpEx_FigureUpByCard test cards%v, expect=%v but ci=%#v", c.Cards, c.Kind, ci) + } + } +} + +func TestKindOfCardFigureUpEx_IsTing(t *testing.T) { + testcases := []struct { + Cards []int32 + Kind int32 + }{ + {[]int32{2, 3, 8, 18, 19, 9}, KindOfCard_Straight}, + {[]int32{2, 3, 17, 18, 10, 9}, KindOfCard_Straight}, + {[]int32{1, 16, 17, 18, 19}, KindOfCard_Flush}, + {[]int32{1, 3, 4, 7, 19, 40}, KindOfCard_Flush}, + {[]int32{15, 16, 0, 18, 19}, KindOfCard_StraightFlush}, + {[]int32{15, 16, 17, 18, 7}, KindOfCard_StraightFlush}, + {[]int32{0, 9, 25, 11, 12, 1}, KindOfCard_RoyalFlush}, + {[]int32{30, 9, 10, 11, 12, 1}, KindOfCard_RoyalFlush}, + {[]int32{30, 47, 22, 33, 50, 31}, KindOfCard_Straight}, + } + for _, c := range testcases { + if !KindOfCardFigureUpExSington.IsTing(c.Cards, c.Kind) { + t.Errorf("KindOfCardFigureUpEx_FigureUpByCard test cards%v, expectTing=%v but not", c.Cards, c.Kind) + } + } +} + +func TestHandCardShowStr(t *testing.T) { + testcases := []struct { + Cards []int32 + ShowStr string + }{ + {[]int32{0, 10}, "JAs"}, + {[]int32{2, 3}, "34s"}, + {[]int32{13, 10}, "JA"}, + {[]int32{9, 22}, "TT"}, + {[]int32{10, 23}, "JJ"}, + {[]int32{2, 0}, "3As"}, + } + for _, c := range testcases { + str := HandCardShowStr(c.Cards) + if str != c.ShowStr { + t.Errorf("HandCardShowStr test cards%v, expect=%v but %v", c.Cards, c.ShowStr, str) + } + } +} + +func TestKindOfCardIsBetter(t *testing.T) { + testcases := []struct { + HandCard []int32 + CommonCard []int32 + }{ + {[]int32{2, 3}, []int32{16, 7, 8}}, + {[]int32{2, 3}, []int32{16, 7, 8, 4}}, + {[]int32{2, 3}, []int32{16, 7, 8, 15, 9}}, + {[]int32{2, 3}, []int32{16, 7, 8, 15, 28}}, + } + for _, c := range testcases { + if !KindOfCardIsBetter(c.HandCard, c.CommonCard) { + t.Errorf("KindOfCardIsBetter test handcards%v, commoncard%v expect better", c.HandCard, c.CommonCard) + } + } +} diff --git a/gamerule/dezhoupoker/constants.go b/gamerule/dezhoupoker/constants.go new file mode 100644 index 0000000..279bd26 --- /dev/null +++ b/gamerule/dezhoupoker/constants.go @@ -0,0 +1,150 @@ +package dezhoupoker + +import "time" + +//////////////////////////////////////////////////////////////////////////////// +//德州扑克 +//////////////////////////////////////////////////////////////////////////////// +//房间类型 +const ( + RoomMode_Normal int = iota //德州 + RoomMode_FiveXTwo //德州五选二 + RoomMode_Max +) +const ( + MaxNumOfPlayer int32 = 9 //最多人数 + MaxNumOfPlayerTwo int32 = 8 //最多人数(五选二) + HandCardNum int32 = 2 //手牌数量 + CommunityCardNum int32 = 5 //公牌 + TotalCardNum int32 = HandCardNum + CommunityCardNum + FlopCardNum int32 = 3 //翻牌 + TurnCardPos int32 = FlopCardNum + 1 //转牌位置 + RiverCardPos int32 = TurnCardPos + 1 //河牌位置 + + ANTE_SCORE int32 = 0 //前注 +) +const ( + BEHIND_CARD int32 = 55 + INVALIDE_POS int32 = -1 + INVALIDE_CARD int32 = -1 +) + +//dz创建房间的参数信息 +const ( + DZSceneParam_MaxCoin int = iota // + + DZThreeSceneParam_Max +) + +const ( + RobotGameTimesMin int32 = 5 //机器人参与游戏次数下限 + RobotGameTimesMax int32 = 10 //机器人参与游戏次数上限 +) + +const ( + CardType_HandCard int32 = iota //公牌 + CardType_FlopCard + CardType_TrunCard + CardType_RiverCard +) + +const ( + RoleType_Player int32 = iota //普通玩家 + RoleType_Banker + RoleType_SmallBlind + RoleType_BigBlind +) + +//玩家位置 +const ( + Pos_UTG int32 = iota //Under the gun 枪口 + Pos_UTG1 //Under the gun +1 枪口+1 + Pos_MP1 //Middle position 1 中位1 + Pos_MP2 //Middle position 2 中位2 + Pos_HJ //Hijack 劫位 + Pos_CO //Cut off 关位 + Pos_BTN //Button 庄家 + Pos_SB //Small blind 小盲位 + Pos_BB //Big blind 大盲位 +) + +var PosDesc = []string{"枪口", "枪口+1", "中位1", "中位2", "劫位", "关位", "庄位", "小盲位", "大盲位"} + +const ( + DezhouPokerOffsetTimeout = 1 //结算等待时间 + + DezhouPokerWaitPlayerTimeout = time.Second * 10 //等待真人时间 + DezhouPokerWaitStartTimeout = time.Second * 3 //延迟开始时间 + //DezhouPokerSelectBankerAndBlindsTimeout = time.Second * 2 //选庄时间 + DezhouPokerAntTimeout = time.Second * 1 //前注时间 + //DezhouPokerBlindsTimeout = time.Second * 0 //大小盲下注时间 + DezhouPokerHandCardTimeout = time.Second * 1 //发手牌等待时间 + DezhouPokerSelectCardTimeout = time.Second * 10 //选手牌等待时间 + + DezhouPokerHandCardBetTimeout = time.Second * 15 * DezhouPokerOffsetTimeout //发手牌等待时间 + DezhouPokerFlopTimeout = time.Second * 1 //发3张翻牌等待时间 + DezhouPokerFlopBetTimeout = time.Second * 15 * DezhouPokerOffsetTimeout //发3张翻牌等待时间 + DezhouPokerTurnTimeout = time.Second * 1 //发1张转牌等待时间 + DezhouPokerTurnBetTimeout = time.Second * 15 * DezhouPokerOffsetTimeout //发1张转牌等待时间 + DezhouPokerRiverTimeout = time.Second * 1 //发1张转牌等待时间 + DezhouPokerRiverBetTimeout = time.Second * 15 * DezhouPokerOffsetTimeout //发1张转牌等待时间 + //DezhouPokerBilledTimeout = time.Second * 10//结算等待时间 + DezhouPokerBilledTimeoutNormal = time.Second * 2 //结算等待时间 + DezhouPokerBilledTimeoutMiddle = time.Second * 1 //结算等待时间 + DezhouPokerBilledTimeoutAllIn = time.Second * 1 //补牌 + DezhouPokerBilledTimeoutPerPlayerBilled = time.Second * 2 //等待结束 + DezhouPokerBilledTimeoutWaitCheckCard = time.Second * 3 //结算结束,预留一点时间让玩家能看清牌型 +) + +//场景状态 +const ( + DezhouPokerSceneStateWaitPlayer int = iota //0 人数不够开启游戏,等待玩家上线 + DezhouPokerSceneStateWaitStart //1 人数够开启游戏, 延迟X秒开始游戏 + DezhouPokerSceneStateSelectBankerAndBlinds //2 选庄家和大小盲 + + DezhouPokerSceneStateAnte //3 下前注0,预留 + DezhouPokerSceneStateBlinds //4 下大小盲 + + DezhouPokerSceneStateHandCard //5 发手牌 + DezhouPokerSceneStateSelectCard //6 选牌状态 + DezhouPokerSceneStateHandCardBet //7 手牌下注1 + + DezhouPokerSceneStateFlop //8 发3张翻牌 + DezhouPokerSceneStateFlopBet //9 翻牌下注2 + + DezhouPokerSceneStateTurn //10 转牌 + DezhouPokerSceneStateTurnBet //11 转牌下注3 + + DezhouPokerSceneStateRiver //12 河牌 + DezhouPokerSceneStateRiverBet //13 河牌下注4 + + DezhouPokerSceneStateBilled //14 结算方式选择 + DezhouPokerSceneStateBilledNormal //15 正常结算 + DezhouPokerSceneStateBilledMiddle //16 半路结算 还剩一个未弃牌的玩家 + DezhouPokerSceneStateBilledAllIn //17 全部allin, 或者只有一个未Allin + + DezhouPokerSceneStateGameEnd //18 结算状态 + + DezhouPokerSceneStateMax +) + +//玩家操作。当轮第一个下注的是加注。大小盲 不显示任何动作 +const ( + DezhouPokerPlayerOpNull int32 = iota //0,初始值 + DezhouPokerPlayerOpCallAntes //1,下底注 + DezhouPokerPlayerOpCall //2,跟进 + DezhouPokerPlayerOpFold //3,弃牌 + DezhouPokerPlayerOpCheck //4,让牌 + DezhouPokerPlayerOpRaise //5,加注 + DezhouPokerPlayerOpAllIn //6,全压 + DezhouPokerPlayerOpSmallBlind //7,小盲 + DezhouPokerPlayerOpBigBlind //8,大盲 + + DezhouPokerPlayerOpAutoBuyIn //9,自动买入 买入数量 + DezhouPokerPlayerOpAutoBuyInCfg //10,请求自动买入配置 + DezhouPokerPlayerOpSelectCard //11,选牌 + + //DezhouPokerPlayerOpSitDown //坐下 + //DezhouPokerPlayerOpStandUp //站起 + +) diff --git a/gamerule/dezhoupoker/dezhouPokerAlgorithm.go b/gamerule/dezhoupoker/dezhouPokerAlgorithm.go new file mode 100644 index 0000000..c7edb71 --- /dev/null +++ b/gamerule/dezhoupoker/dezhouPokerAlgorithm.go @@ -0,0 +1,823 @@ +package dezhoupoker + +import ( + "fmt" + "sort" +) + +const ( + KindOfCard_HighCard int32 = iota //0 高牌:先比最大的牌,如相同则依次比剩余的单张 + KindOfCard_OnePair //1 一对:先比对子,对子相同则依次比单张。 + KindOfCard_TwoPair //2 两对:先比大对,再比小对,都相同则比单张。 + KindOfCard_ThreeKind //3 三条:先比三条,三条相同比单张 + KindOfCard_Straight //4 顺子:比顺子的大小。A2345是最小的顺子。 + KindOfCard_Flush //5 同花:比最大的单张,如相同则依次比剩余的单张。 + KindOfCard_Fullhouse //6 葫芦:先比三条,三条相同比对子 + KindOfCard_FourKind //7 四条:先比四条,四条相同比单张。 + KindOfCard_StraightFlush //8 同花顺 + KindOfCard_RoyalFlush //9 皇家同花顺 + + KindOfCard_Invalide //无效边池对应的牌型 + KindOfCard_Max +) + +//对子延伸牌型 +const ( + KindOfCardEx_OverPair int32 = iota //高对(overpair)是一个玩家自己手上的对子,它比公共牌上任何一张牌可能组成的对子更大 + KindOfCardEx_TopPair //由玩家手里的一张牌和最大的一张公共牌组成的对子叫做顶对(top pair) + KindOfCardEx_MiddlePair //玩家手上的一张牌和牌面上的一张中等牌组成的对子 + KindOfCardEx_UnderPair //比所有公共牌数字都小的对子。因此,任何与牌面组成的对子将打败低对 +) + +var KindOfCardStr = []string{ + "高牌", + "一对", + "两对", + "三条", + "顺子", + "同花", + "葫芦", + "四条", + "同花顺", + "皇家同花顺", + "无效", +} + +type CardData struct { + Color int32 + Value int32 + Card int32 +} + +func (this *CardData) Init(card int32) { + this.Card = card + this.Color = this.Card / PER_CARD_COLOR_MAX + this.Value = this.Card % PER_CARD_COLOR_MAX +} + +//---------------------------------------------------------------------------------------------------------------------------------------------------------------- +type CardDataManager struct { + CardDataPool []CardData //带花色排序 + CardData2Pool []CardData //不带花色排序 + + CardValueCount map[int32]int32 + CardColorCount map[int32]int32 +} + +func (this *CardDataManager) Init() { + this.CardValueCount = make(map[int32]int32) + this.CardColorCount = make(map[int32]int32) +} +func (this *CardDataManager) AddCard(card int32) { + var cardData CardData + cardData.Init(card) + + this.CardDataPool = append(this.CardDataPool, cardData) + + if _, ok := this.CardValueCount[cardData.Value]; ok { + this.CardValueCount[cardData.Value]++ + } else { + this.CardValueCount[cardData.Value] = 1 + } + + if _, ok := this.CardColorCount[cardData.Color]; ok { + this.CardColorCount[cardData.Color]++ + } else { + this.CardColorCount[cardData.Color] = 1 + } +} + +func (this *CardDataManager) ReCal() { + this.CardData2Pool = append(this.CardData2Pool, this.CardDataPool...) + + //升序 + len := len(this.CardData2Pool) + for i := 0; i < len; i++ { + for j := i + 1; j < len; j++ { + if this.CardData2Pool[i].Value > this.CardData2Pool[j].Value { + this.CardData2Pool[i], this.CardData2Pool[j] = this.CardData2Pool[j], this.CardData2Pool[i] + } + } + } +} + +//---------------------------------------------------------------------------------------------------------------------------------------------------------------- +type CardsInfo struct { + Kind int32 //牌型 + KindCards []int32 + Value int64 //牌力大小。KK_KK_KK_VV_VV_VV_VV_VV + ValueScore int32 //计算得分,在数值控制里面用 +} + +func (this *CardsInfo) KindStr() string { + return KindOfCardStr[this.Kind] +} + +func (this *CardsInfo) MakeValue(kindValue, kind1Value, kind2Value, poker1Value, poker2Value, poker3Value, poker4Value, poker5Value int32) int64 { + kind_value := kindValue*10000 + kind1Value*100 + kind2Value + poker_value := poker1Value*100000000 + poker2Value*1000000 + poker3Value*10000 + poker4Value*100 + poker5Value + result_Value := int64(kind_value)*10000000000 + int64(poker_value) + return result_Value +} + +func (this *CardsInfo) CalValue() { + switch this.Kind { + case KindOfCard_RoyalFlush: + this.Value = this.MakeValue(this.Kind, 0, 0, 0, 0, 0, 0, 0) + case KindOfCard_StraightFlush: + this.Value = this.MakeValue(this.Kind, 0, 0, this.ValueToWeight(this.KindCards[0]), 0, 0, 0, 0) + case KindOfCard_FourKind: + this.Value = this.MakeValue(this.Kind, this.ValueToWeight(this.KindCards[0]), 0, this.ValueToWeight(this.KindCards[4]), 0, 0, 0, 0) + case KindOfCard_Fullhouse: + this.Value = this.MakeValue(this.Kind, this.ValueToWeight(this.KindCards[0]), this.ValueToWeight(this.KindCards[3]), 0, 0, 0, 0, 0) + case KindOfCard_Flush: + this.Value = this.MakeValue(this.Kind, 0, 0, this.ValueToWeight(this.KindCards[0]), this.ValueToWeight(this.KindCards[1]), this.ValueToWeight(this.KindCards[2]), this.ValueToWeight(this.KindCards[3]), this.ValueToWeight(this.KindCards[4])) + case KindOfCard_Straight: + this.Value = this.MakeValue(this.Kind, 0, 0, this.ValueToWeight(this.KindCards[0]), 0, 0, 0, 0) + case KindOfCard_ThreeKind: + this.Value = this.MakeValue(this.Kind, this.ValueToWeight(this.KindCards[0]), 0, this.ValueToWeight(this.KindCards[3]), this.ValueToWeight(this.KindCards[4]), 0, 0, 0) + case KindOfCard_TwoPair: + this.Value = this.MakeValue(this.Kind, this.ValueToWeight(this.KindCards[0]), this.ValueToWeight(this.KindCards[2]), this.ValueToWeight(this.KindCards[4]), 0, 0, 0, 0) + case KindOfCard_OnePair: + this.Value = this.MakeValue(this.Kind, this.ValueToWeight(this.KindCards[0]), 0, this.ValueToWeight(this.KindCards[2]), this.ValueToWeight(this.KindCards[3]), this.ValueToWeight(this.KindCards[4]), 0, 0) + case KindOfCard_HighCard: + this.Value = this.MakeValue(this.Kind, 0, 0, this.ValueToWeight(this.KindCards[0]), this.ValueToWeight(this.KindCards[1]), this.ValueToWeight(this.KindCards[2]), this.ValueToWeight(this.KindCards[3]), this.ValueToWeight(this.KindCards[4])) + default: + this.Value = this.MakeValue(this.Kind, 0, 0, 0, 0, 0, 0, 0) + } + +} + +func (this *CardsInfo) ValueToWeight(pokerCard int32) int32 { + cardValue := pokerCard % PER_CARD_COLOR_MAX + if cardValue == POKER_A { + return POKER_A_Weight + } else { + return cardValue + } +} + +//---------------------------------------------------------------------------------------------------------------------------------------------------------------- +type KindOfCardFigureUp struct { +} + +var KindOfCardFigureUpSington = &KindOfCardFigureUp{} + +func (this *KindOfCardFigureUp) FigureUpByCard(handcards [HandCardNum]int32, communityCards [CommunityCardNum]int32) *CardsInfo { + return this.figureUp(handcards[:], communityCards[:]) +} + +func (this *KindOfCardFigureUp) figureUp(handcards []int32, communityCards []int32) *CardsInfo { + var tempCard []int + for i := int32(0); i < HandCardNum; i++ { + tempCard = append(tempCard, int(handcards[i])) + } + for i := int32(0); i < CommunityCardNum; i++ { + tempCard = append(tempCard, int(communityCards[i])) + } + //按照升序排序 + sort.Ints(tempCard) + + var cardDataManager CardDataManager + cardDataManager.Init() + for i := int32(0); i < TotalCardNum; i++ { + cardDataManager.AddCard(int32(tempCard[i])) + } + cardDataManager.ReCal() + + //根据value 排序 + cardsInfo := this.CalCardKind(&cardDataManager) + if cardsInfo != nil { + cardsInfo.CalValue() + } + return cardsInfo +} + +func (this *KindOfCardFigureUp) CalCardKind(cardDataManager *CardDataManager) *CardsInfo { + + card_info := this.IsRoyalFlush(cardDataManager) + if card_info != nil { + return card_info + } + card_info = this.IsStraightFlush(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFourKind(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFullhouse(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFlush(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsStraight(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsThreeKind(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsTwoPair(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsOnePair(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsHighCard(cardDataManager) + if card_info != nil { + return card_info + } + + fmt.Println("出错了, 永远不应该走到这里 : ", cardDataManager.CardDataPool) + return nil +} + +//皇家同花顺 KindOfCard_RoyalFlush +func (this *KindOfCardFigureUp) IsRoyalFlush(cardDataManager *CardDataManager) *CardsInfo { + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardDataPool[TotalCardNum-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := TotalCardNum - 2; i >= 0; i-- { + if cardDataManager.CardDataPool[i].Card != cur_poker.Card-1 || + cardDataManager.CardDataPool[i].Color != cur_poker.Color { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 4 { + break + } + } + + if len(rst_kind_card) < 4 { + return nil + } + + if cur_poker.Value != POKER_10 { + return nil + } + //已经找到10,J,Q,K, 找同色 A + for i := int32(0); i < TotalCardNum; i++ { + if cardDataManager.CardDataPool[i].Value == POKER_A && cardDataManager.CardDataPool[i].Color == cur_poker.Color { + cardInfo := &CardsInfo{ + Kind: KindOfCard_RoyalFlush, + } + //牌型对应的牌 + //A + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardDataPool[i].Card) + //KQJ 10 + for i := 0; i < len(rst_kind_card); i++ { + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card[i]) + } + return cardInfo + } + } + + return nil +} + +//同花顺 KindOfCard_StraightFlush +func (this *KindOfCardFigureUp) IsStraightFlush(cardDataManager *CardDataManager) *CardsInfo { + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardDataPool[TotalCardNum-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := TotalCardNum - 2; i >= 0; i-- { + if cardDataManager.CardDataPool[i].Card != cur_poker.Card-1 || cur_poker.Color != cardDataManager.CardDataPool[i].Card/PER_CARD_COLOR_MAX { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 5 { + break + } + } + + if len(rst_kind_card) == 5 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_StraightFlush, + } + + //牌型对应的牌 + for i := 0; i < len(rst_kind_card); i++ { + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card[i]) + } + return cardInfo + } else { + return nil + } +} + +//四条:先比四条,四条相同比单张。 KindOfCard_FourKind +func (this *KindOfCardFigureUp) IsFourKind(cardDataManager *CardDataManager) *CardsInfo { + + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 4 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_FourKind, + } + + //4张 牌型牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value == cardValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + //第5张牌型牌 + fithCard := int32(0) + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Card != cardInfo.KindCards[0] { + if curCard.Value == POKER_A { + fithCard = curCard.Card + break + } else { + if cardValue != curCard.Value && fithCard < curCard.Value { + fithCard = curCard.Card + } + } + } + } + cardInfo.KindCards = append(cardInfo.KindCards, fithCard) + return cardInfo + } + } + return nil +} + +//葫芦:先比三条,三条相同比对子 KindOfCard_Fullhouse +func (this *KindOfCardFigureUp) IsFullhouse(cardDataManager *CardDataManager) *CardsInfo { + + card2Value := INVALIDE_CARD + card3Value := INVALIDE_CARD + card3Count := int32(0) + + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + card3Count++ + + if card3Value == POKER_A { + continue + } + + if card3Value < cardValue || cardValue == POKER_A { + card3Value = cardValue + } + } else if cardCount == 2 { + if card2Value == POKER_A { + continue + } + + if card2Value < cardValue || cardValue == POKER_A { + card2Value = cardValue + } + } + } + + //总共7张,如果有两个3条,就必然不会有2对.如果有两个3条,把较小的哪个当做两对 + if card3Count >= 2 { + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + if card3Value != cardValue { + card2Value = cardValue + break + } + } + } + } + + if card2Value != INVALIDE_CARD && card3Value != INVALIDE_CARD { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Fullhouse, + } + + //3对 + for i := int32(0); i < TotalCardNum; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card3Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + //2对 + for i := int32(0); i < TotalCardNum; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card2Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + return cardInfo + } + + return nil +} + +//同花:比最大的单张,如相同则依次比剩余的单张。 KindOfCard_Flush +func (this *KindOfCardFigureUp) IsFlush(cardDataManager *CardDataManager) *CardsInfo { + sameColorCount := int32(0) + sameColorValue := int32(0) + + for colorValue, colorCount := range cardDataManager.CardColorCount { + if colorCount > sameColorCount { + sameColorCount = colorCount + sameColorValue = colorValue + } + } + + if sameColorCount < 5 { + return nil + } + + cardInfo := &CardsInfo{ + Kind: KindOfCard_Flush, + } + + bHasPoker_A := false + var temp_rst []int + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Color == sameColorValue { + temp_rst = append(temp_rst, int(curCard.Card)) + + if curCard.Value == POKER_A { + bHasPoker_A = true + } + } + } + sort.Ints(temp_rst) + + if bHasPoker_A { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[0])) + + data_len := len(temp_rst) + for i := data_len - 1; i >= 0; i-- { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[i])) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } else { + data_len := len(temp_rst) + for i := data_len - 1; i >= 0; i-- { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[i])) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + + return cardInfo +} + +//顺子:比顺子的大小。A2345是最小的顺子,AKQJ10是最大的顺子。 KindOfCard_Straight +func (this *KindOfCardFigureUp) IsStraight(cardDataManager *CardDataManager) *CardsInfo { + //需要先判断是最大的顺子,才能再判定普通顺子 + card_info := this.IsStraightMax(cardDataManager) + if card_info == nil { + card_info = this.IsStraightNormal(cardDataManager) + } + return card_info +} +func (this *KindOfCardFigureUp) IsStraightNormal(cardDataManager *CardDataManager) *CardsInfo { + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardData2Pool[TotalCardNum-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := TotalCardNum - 2; i >= 0; i-- { + if cardDataManager.CardData2Pool[i].Value == cur_poker.Value { + continue + } + if cardDataManager.CardData2Pool[i].Value != cur_poker.Value-1 { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 5 { + break + } + } + + if len(rst_kind_card) == 5 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Straight, + } + cardInfo.KindCards = rst_kind_card + return cardInfo + } else { + return nil + } +} +func (this *KindOfCardFigureUp) IsStraightMax(cardDataManager *CardDataManager) *CardsInfo { + var cur_poker CardData + cur_poker.Init(cardDataManager.CardData2Pool[TotalCardNum-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := TotalCardNum - 2; i >= 0; i-- { + if cardDataManager.CardData2Pool[i].Value == cur_poker.Value { + continue + } + if cardDataManager.CardData2Pool[i].Value != cur_poker.Value-1 { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 4 { + break + } + } + + if len(rst_kind_card) < 4 { + return nil + } + + if cur_poker.Value == POKER_10 && cardDataManager.CardData2Pool[0].Value == POKER_A { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Straight, + } + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card...) + return cardInfo + } else { + return nil + } +} + +//三条:先比三条,三条相同比单张 KindOfCard_ThreeKind +func (this *KindOfCardFigureUp) IsThreeKind(cardDataManager *CardDataManager) *CardsInfo { + santiaoValue := int32(INVALIDE_CARD) + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + santiaoValue = cardValue + } + } + if santiaoValue == INVALIDE_CARD { + return nil + } + cardInfo := &CardsInfo{ + Kind: KindOfCard_ThreeKind, + } + + //前3张 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == santiaoValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + if cardDataManager.CardData2Pool[0].Value == POKER_A && santiaoValue != POKER_A { + //第4张牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + + //第5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != santiaoValue && curCard.Value != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + break + } + } + } else { + //第4张 第 5 张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != santiaoValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } + return cardInfo +} + +//两对:先比大对,再比小对,都相同则比单张。 KindOfCard_TwoPair +func (this *KindOfCardFigureUp) IsTwoPair(cardDataManager *CardDataManager) *CardsInfo { + + var card2Value []int + + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 2 { + card2Value = append(card2Value, int(cardValue)) + } + } + if len(card2Value) < 2 { + return nil + } + sort.Ints(card2Value) + + cardInfo := &CardsInfo{ + Kind: KindOfCard_TwoPair, + } + + if len(card2Value) == 3 { + if card2Value[0] == int(POKER_A) { + //删除中间 + card2Value = append(card2Value[:1], card2Value[2:]...) + } else { + //删除第一个 + card2Value = append(card2Value[:0], card2Value[1:]...) + } + } + if card2Value[0] == int(POKER_A) { + card2Value[0], card2Value[1] = card2Value[1], card2Value[0] + } + for j := 1; j >= 0; j-- { + //2对 + for i := int32(0); i < TotalCardNum; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == int32(card2Value[j]) { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + } + + //第5张牌 + if cardDataManager.CardData2Pool[0].Value == POKER_A && int32(card2Value[1]) != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + } else { + //第5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != int32(card2Value[0]) && curCard.Value != int32(card2Value[1]) { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + break + } + } + } + + return cardInfo +} + +//一对:先比对子,对子相同则依次比单张。 KindOfCard_OnePair +func (this *KindOfCardFigureUp) IsOnePair(cardDataManager *CardDataManager) *CardsInfo { + + card1Value := INVALIDE_CARD + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 2 { + card1Value = cardValue + break + } + } + if card1Value == INVALIDE_CARD { + return nil + } + + cardInfo := &CardsInfo{ + Kind: KindOfCard_OnePair, + } + + //1对 + for i := int32(0); i < TotalCardNum; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card1Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + //第3 ~ 5张牌 + if cardDataManager.CardData2Pool[0].Value == POKER_A && card1Value != POKER_A { + //牌型牌 + //第 3 张 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + + //第4 ~ 5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != card1Value && curCard.Value != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } else { + //第3 ~ 5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != card1Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } + + return cardInfo +} + +//高牌:先比最大的牌,如相同则依次比剩余的单张 KindOfCard_HighCard +func (this *KindOfCardFigureUp) IsHighCard(cardDataManager *CardDataManager) *CardsInfo { + cardInfo := &CardsInfo{ + Kind: KindOfCard_HighCard, + } + + if cardDataManager.CardData2Pool[0].Value == POKER_A { + //牌型牌 + //第 1 张牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + + //第2 ~ 5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } else { + //第1 ~ 5张牌 + for i := TotalCardNum - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + + return cardInfo +} diff --git a/gamerule/dezhoupoker/dezhouPokerAlgorithmEx.go b/gamerule/dezhoupoker/dezhouPokerAlgorithmEx.go new file mode 100644 index 0000000..7c8ca82 --- /dev/null +++ b/gamerule/dezhoupoker/dezhouPokerAlgorithmEx.go @@ -0,0 +1,980 @@ +package dezhoupoker + +import ( + "sort" +) + +//---------------------------------------------------------------------------------------------------------------------------------------------------------------- +type KindOfCardFigureUpEx struct { +} + +var KindOfCardFigureUpExSington = &KindOfCardFigureUpEx{} + +func (this *KindOfCardFigureUpEx) FigureUpByCard(cards []int32) *CardsInfo { + if len(cards) == 0 { + return nil + } + var tempCard []int + for _, v := range cards { + tempCard = append(tempCard, int(v)) + } + + //按照升序排序 + sort.Ints(tempCard) + + var cardDataManager CardDataManager + cardDataManager.Init() + for i := 0; i < len(tempCard); i++ { + cardDataManager.AddCard(int32(tempCard[i])) + } + cardDataManager.ReCal() + + //根据value 排序 + cardsInfo := this.CalCardKind(&cardDataManager) + if cardsInfo != nil && len(cards) > 5 { + cardsInfo.CalValue() + } + return cardsInfo +} + +func (this *KindOfCardFigureUpEx) CalCardKind(cardDataManager *CardDataManager) *CardsInfo { + + card_info := this.IsRoyalFlush(cardDataManager) + if card_info != nil { + return card_info + } + card_info = this.IsStraightFlush(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFourKind(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFullhouse(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsFlush(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsStraight(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsThreeKind(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsTwoPair(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsOnePair(cardDataManager) + if card_info != nil { + return card_info + } + + card_info = this.IsHighCard(cardDataManager) + if card_info != nil { + return card_info + } + + return nil +} + +//皇家同花顺 KindOfCard_RoyalFlush +func (this *KindOfCardFigureUpEx) IsRoyalFlush(cardDataManager *CardDataManager) *CardsInfo { + + cardCount := len(cardDataManager.CardDataPool) + if cardCount == 0 { + return nil + } + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardDataPool[cardCount-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := cardCount - 2; i >= 0; i-- { + if cardDataManager.CardDataPool[i].Card != cur_poker.Card-1 || + cardDataManager.CardDataPool[i].Color != cur_poker.Color { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 4 { + break + } + } + + if len(rst_kind_card) < 4 { + return nil + } + + if cur_poker.Value != POKER_10 { + return nil + } + //已经找到10,J,Q,K, 找同色 A + for i := 0; i < cardCount; i++ { + if cardDataManager.CardDataPool[i].Value == POKER_A && cardDataManager.CardDataPool[i].Color == cur_poker.Color { + cardInfo := &CardsInfo{ + Kind: KindOfCard_RoyalFlush, + } + //牌型对应的牌 + //A + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardDataPool[i].Card) + //KQJ 10 + for i := 0; i < len(rst_kind_card); i++ { + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card[i]) + } + return cardInfo + } + } + + return nil +} + +//同花顺 KindOfCard_StraightFlush +func (this *KindOfCardFigureUpEx) IsStraightFlush(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount == 0 { + return nil + } + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardDataPool[cardCount-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := cardCount - 2; i >= 0; i-- { + if cardDataManager.CardDataPool[i].Card != cur_poker.Card-1 || cur_poker.Color != cardDataManager.CardDataPool[i].Card/PER_CARD_COLOR_MAX { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardDataPool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 5 { + break + } + } + + if len(rst_kind_card) == 5 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_StraightFlush, + } + + //牌型对应的牌 + for i := 0; i < len(rst_kind_card); i++ { + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card[i]) + } + return cardInfo + } else { + return nil + } +} + +//四条:先比四条,四条相同比单张。 KindOfCard_FourKind +func (this *KindOfCardFigureUpEx) IsFourKind(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 4 { + return nil + } + + for typeCardValue, typeCardCount := range cardDataManager.CardValueCount { + if typeCardCount == 4 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_FourKind, + } + + //4张 牌型牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value == typeCardValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + if cardCount >= 5 { + //第5张牌型牌 + fithCard := int32(0) + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Card != cardInfo.KindCards[0] { + if curCard.Value == POKER_A { + fithCard = curCard.Card + break + } else { + if fithCard < curCard.Value { + fithCard = curCard.Card + } + } + } + } + cardInfo.KindCards = append(cardInfo.KindCards, fithCard) + } + return cardInfo + } + } + return nil +} + +//葫芦:先比三条,三条相同比对子 KindOfCard_Fullhouse +func (this *KindOfCardFigureUpEx) IsFullhouse(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 5 { + return nil + } + + card2Value := INVALIDE_CARD + card3Value := INVALIDE_CARD + card3Count := int32(0) + + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + card3Count++ + if card3Value == POKER_A { + continue + } + + if card3Value < cardValue || cardValue == POKER_A { + card3Value = cardValue + } + } else if cardCount == 2 { + if card2Value == POKER_A { + continue + } + + if card2Value < cardValue || cardValue == POKER_A { + card2Value = cardValue + } + } + } + + //总共7张,如果有两个3条,就必然不会有2对.如果有两个3条,把较小的哪个当做两对 + if card3Count >= 2 { + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + if card3Value != cardValue { + card2Value = cardValue + break + } + } + } + } + + if card2Value != INVALIDE_CARD && card3Value != INVALIDE_CARD { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Fullhouse, + } + + //3对 + for i := 0; i < cardCount; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card3Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + //2对 + for i := 0; i < cardCount; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card2Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + return cardInfo + } + + return nil +} + +//同花:比最大的单张,如相同则依次比剩余的单张。 KindOfCard_Flush +func (this *KindOfCardFigureUpEx) IsFlush(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 5 { + return nil + } + + sameColorCount := int32(0) + sameColorValue := int32(0) + + for colorValue, colorCount := range cardDataManager.CardColorCount { + if colorCount > sameColorCount { + sameColorCount = colorCount + sameColorValue = colorValue + } + } + + if sameColorCount < 5 { + return nil + } + + cardInfo := &CardsInfo{ + Kind: KindOfCard_Flush, + } + + bHasPoker_A := false + var temp_rst []int + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Color == sameColorValue { + temp_rst = append(temp_rst, int(curCard.Card)) + + if curCard.Value == POKER_A { + bHasPoker_A = true + } + } + } + sort.Ints(temp_rst) + + if bHasPoker_A { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[0])) + + data_len := len(temp_rst) + for i := data_len - 1; i >= 0; i-- { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[i])) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } else { + data_len := len(temp_rst) + for i := data_len - 1; i >= 0; i-- { + cardInfo.KindCards = append(cardInfo.KindCards, int32(temp_rst[i])) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + + return cardInfo +} + +//顺子:比顺子的大小。A2345是最小的顺子,AKQJ10是最大的顺子。 KindOfCard_Straight +func (this *KindOfCardFigureUpEx) IsStraight(cardDataManager *CardDataManager) *CardsInfo { + card_info := this.IsStraightNormal(cardDataManager) + if card_info == nil { + card_info = this.IsStraightMax(cardDataManager) + } + return card_info +} +func (this *KindOfCardFigureUpEx) IsStraightNormal(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 5 { + return nil + } + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardData2Pool[cardCount-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := cardCount - 2; i >= 0; i-- { + if cardDataManager.CardData2Pool[i].Value == cur_poker.Value { + continue + } + if cardDataManager.CardData2Pool[i].Value != cur_poker.Value-1 { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 5 { + break + } + } + + if len(rst_kind_card) == 5 { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Straight, + } + cardInfo.KindCards = rst_kind_card + return cardInfo + } else { + return nil + } +} +func (this *KindOfCardFigureUpEx) IsStraightMax(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount == 0 { + return nil + } + + var cur_poker CardData + cur_poker.Init(cardDataManager.CardData2Pool[cardCount-1].Card) + var rst_kind_card []int32 + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + for i := cardCount - 2; i >= 0; i-- { + if cardDataManager.CardData2Pool[i].Value == cur_poker.Value { + continue + } + if cardDataManager.CardData2Pool[i].Value != cur_poker.Value-1 { + rst_kind_card = nil + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + rst_kind_card = append(rst_kind_card, cur_poker.Card) + continue + } + + cur_poker.Init(cardDataManager.CardData2Pool[i].Card) + + rst_kind_card = append(rst_kind_card, cur_poker.Card) + + if len(rst_kind_card) >= 4 { + break + } + } + if len(rst_kind_card) < 4 { + return nil + } + + if cur_poker.Value == POKER_10 && cardDataManager.CardData2Pool[0].Value == POKER_A { + cardInfo := &CardsInfo{ + Kind: KindOfCard_Straight, + } + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + cardInfo.KindCards = append(cardInfo.KindCards, rst_kind_card...) + return cardInfo + } else { + return nil + } +} + +//三条:先比三条,三条相同比单张 KindOfCard_ThreeKind +func (this *KindOfCardFigureUpEx) IsThreeKind(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 3 { + return nil + } + + santiaoValue := int32(INVALIDE_CARD) + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 3 { + santiaoValue = cardValue + } + } + if santiaoValue == INVALIDE_CARD { + return nil + } + cardInfo := &CardsInfo{ + Kind: KindOfCard_ThreeKind, + } + + //前3张 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == santiaoValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + if cardDataManager.CardData2Pool[0].Value == POKER_A && santiaoValue != POKER_A { + if cardCount >= 4 { + //第4张牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + } + + if cardCount >= 5 { + //第5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != santiaoValue && curCard.Value != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + break + } + } + } + } else { + //第4张 第 5 张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != santiaoValue { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } + return cardInfo +} + +//两对:先比大对,再比小对,都相同则比单张。 KindOfCard_TwoPair +func (this *KindOfCardFigureUpEx) IsTwoPair(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 4 { + return nil + } + + var card2Value []int + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 2 { + card2Value = append(card2Value, int(cardValue)) + } + } + if len(card2Value) < 2 { + return nil + } + sort.Ints(card2Value) + + cardInfo := &CardsInfo{ + Kind: KindOfCard_TwoPair, + } + + if len(card2Value) == 3 { + if card2Value[0] == int(POKER_A) { + //删除中间 + card2Value = append(card2Value[:1], card2Value[2:]...) + } else { + //删除第一个 + card2Value = append(card2Value[:0], card2Value[1:]...) + } + } + if card2Value[0] == int(POKER_A) { + card2Value[0], card2Value[1] = card2Value[1], card2Value[0] + } + for j := 1; j >= 0; j-- { + //2对 + for i := 0; i < cardCount; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == int32(card2Value[j]) { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + } + + if cardCount >= 5 { + //第5张牌 + if cardDataManager.CardData2Pool[0].Value == POKER_A && int32(card2Value[1]) != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + } else { + //第5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != int32(card2Value[0]) && curCard.Value != int32(card2Value[1]) { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + break + } + } + } + } + + return cardInfo +} + +//一对:先比对子,对子相同则依次比单张。 KindOfCard_OnePair +func (this *KindOfCardFigureUpEx) IsOnePair(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount < 2 { + return nil + } + + card1Value := INVALIDE_CARD + for cardValue, cardCount := range cardDataManager.CardValueCount { + if cardCount == 2 { + card1Value = cardValue + break + } + } + if card1Value == INVALIDE_CARD { + return nil + } + + cardInfo := &CardsInfo{ + Kind: KindOfCard_OnePair, + } + + //1对 + for i := 0; i < cardCount; i++ { + curCard := cardDataManager.CardDataPool[i] + + if curCard.Value == card1Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + } + } + + //第3 ~ 5张牌 + if cardDataManager.CardData2Pool[0].Value == POKER_A && card1Value != POKER_A { + //牌型牌 + //第 3 张 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + + //第4 ~ 5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != card1Value && curCard.Value != POKER_A { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } else { + //第3 ~ 5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + if curCard.Value != card1Value { + //牌型牌 + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + } + + return cardInfo +} + +//高牌:先比最大的牌,如相同则依次比剩余的单张 KindOfCard_HighCard +func (this *KindOfCardFigureUpEx) IsHighCard(cardDataManager *CardDataManager) *CardsInfo { + cardCount := len(cardDataManager.CardDataPool) + if cardCount == 0 { + return nil + } + + cardInfo := &CardsInfo{ + Kind: KindOfCard_HighCard, + } + + if cardDataManager.CardData2Pool[0].Value == POKER_A { + //牌型牌 + //第 1 张牌 + cardInfo.KindCards = append(cardInfo.KindCards, cardDataManager.CardData2Pool[0].Card) + + //第2 ~ 5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } else { + //第1 ~ 5张牌 + for i := cardCount - 1; i >= 0; i-- { + curCard := cardDataManager.CardData2Pool[i] + + cardInfo.KindCards = append(cardInfo.KindCards, curCard.Card) + if len(cardInfo.KindCards) >= 5 { + break + } + } + } + + return cardInfo +} + +func (this *KindOfCardFigureUpEx) IsTing(cards []int32, kind int32) bool { + for i := int32(0); i < POKER_CNT; i++ { + if hasCard(cards, i) { + continue + } + + var tempCard []int + for _, v := range cards { + tempCard = append(tempCard, int(v)) + } + tempCard = append(tempCard, int(i)) + //按照升序排序 + sort.Ints(tempCard) + + var cardDataManager CardDataManager + cardDataManager.Init() + for i := 0; i < len(tempCard); i++ { + cardDataManager.AddCard(int32(tempCard[i])) + } + cardDataManager.ReCal() + + ci := this.CalCardKind(&cardDataManager) + if ci != nil && ci.Kind == kind { + return true + } + } + + return false +} + +func (this *KindOfCardFigureUpEx) IsTingKinds(cards []int32, kinds []int32) bool { + for _, k := range kinds { + if this.IsTing(cards, k) { + return true + } + } + + return false +} + +func (this *KindOfCardFigureUpEx) TingCount(cards, exclude []int32, kind int32) int { + cnt := 0 + for i := int32(0); i < POKER_CNT; i++ { + if hasCard(exclude, i) { + continue + } + if hasCard(cards, i) { + continue + } + + var tempCard []int + for _, v := range cards { + tempCard = append(tempCard, int(v)) + } + tempCard = append(tempCard, int(i)) + //按照升序排序 + sort.Ints(tempCard) + + var cardDataManager CardDataManager + cardDataManager.Init() + for i := 0; i < len(tempCard); i++ { + cardDataManager.AddCard(int32(tempCard[i])) + } + cardDataManager.ReCal() + + ci := this.CalCardKind(&cardDataManager) + if ci != nil && ci.Kind == kind { + cnt++ + } + } + + return cnt +} + +func (this *KindOfCardFigureUpEx) TingKindsCount(cards, exclude, kinds []int32) int { + cnt := 0 + for _, k := range kinds { + cnt += this.TingCount(cards, exclude, k) + } + return cnt +} + +func hasCard(cards []int32, c int32) bool { + for _, _c := range cards { + if _c == c { + return true + } + } + + return false +} + +func IsOverPair(ci *CardsInfo, handCard []int32, commonCard []int32) bool { + if ci == nil || ci.Kind != KindOfCard_OnePair { + return false + } + if len(handCard) != 2 { + return false + } + + if handCard[0]%PER_CARD_COLOR_MAX != handCard[1]%PER_CARD_COLOR_MAX { + return false + } + + max := CardValueMap[handCard[0]%PER_CARD_COLOR_MAX] + for _, c := range commonCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v > max { + return false + } + } + + return true +} + +func IsUnderPair(ci *CardsInfo, handCard []int32, commonCard []int32) bool { + if ci == nil || ci.Kind != KindOfCard_OnePair { + return false + } + if len(handCard) != 2 { + return false + } + + if handCard[0]%PER_CARD_COLOR_MAX != handCard[1]%PER_CARD_COLOR_MAX { + return false + } + + pv := CardValueMap[handCard[0]%PER_CARD_COLOR_MAX] + for _, c := range commonCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v < pv { + return false + } + } + + return true +} + +func IsTopPair(ci *CardsInfo, handCard []int32, commonCard []int32) bool { + if ci == nil || ci.Kind != KindOfCard_OnePair { + return false + } + if len(handCard) != 2 { + return false + } + + pv := CardValueMap[ci.KindCards[0]%PER_CARD_COLOR_MAX] + max := 0 + for _, c := range commonCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v > max { + max = v + } + } + + if pv != max { + return false + } + + for _, c := range handCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v == max { + return true + } + } + + return false +} + +func IsMiddlePair(ci *CardsInfo, handCard []int32, commonCard []int32) bool { + if ci == nil || ci.Kind != KindOfCard_OnePair { + return false + } + if len(handCard) != 2 { + return false + } + + pv := CardValueMap[ci.KindCards[0]%PER_CARD_COLOR_MAX] + max := 0 + min := 13 + for _, c := range commonCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v > max { + max = v + } + if v < min { + min = v + } + } + + if pv <= min || pv >= max { + return false + } + + for _, c := range handCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v == pv { + return true + } + } + + return false +} + +func IsButtomPair(ci *CardsInfo, handCard []int32, commonCard []int32) bool { + if ci == nil || ci.Kind != KindOfCard_OnePair { + return false + } + if len(handCard) != 2 { + return false + } + + pv := CardValueMap[ci.KindCards[0]%PER_CARD_COLOR_MAX] + min := 13 + for _, c := range commonCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v < min { + min = v + } + } + + if pv != min { + return false + } + + for _, c := range handCard { + v := CardValueMap[c%PER_CARD_COLOR_MAX] + if v == min { + return true + } + } + + return false +} +func KindOfCardIsBetter(handCard []int32, commonCard []int32) bool { + ok, _, _ := KindOfCardIsBetterEx(handCard, commonCard) + return ok +} + +//牌型是否较上个阶段有所提升 +func KindOfCardIsBetterEx(handCard []int32, commonCard []int32) (bool, *CardsInfo, *CardsInfo) { + var pre, cur *CardsInfo + switch len(commonCard) { + case 3: + pre = KindOfCardFigureUpExSington.FigureUpByCard(commonCard) + cur = KindOfCardFigureUpExSington.FigureUpByCard(append(handCard, commonCard...)) + case 4: + pre = KindOfCardFigureUpExSington.FigureUpByCard(commonCard) + cur = KindOfCardFigureUpExSington.FigureUpByCard(append(handCard, commonCard...)) + case 5: + pre = KindOfCardFigureUpExSington.FigureUpByCard(commonCard) + cur = KindOfCardFigureUpExSington.FigureUpByCard(append(handCard, commonCard...)) + } + if pre != nil && cur != nil { + if cur.Kind > pre.Kind { + return true, pre, cur + } + } + return false, pre, cur +} diff --git a/gamerule/dezhoupoker/dezhouPokerCardScore.go b/gamerule/dezhoupoker/dezhouPokerCardScore.go new file mode 100644 index 0000000..1d94ccd --- /dev/null +++ b/gamerule/dezhoupoker/dezhouPokerCardScore.go @@ -0,0 +1,329 @@ +package dezhoupoker + +import ( + "fmt" + "math/rand" + "mongo.games.com/algorithm" +) + +var cardKindValue = make(map[int32]int32) + +/* +散牌分值=散牌里单牌分值最大的分数 +一对子分值=对子分值 +二对分值=对子1分值+对子2分值+单张分值 +三条分值=三张分值+两个单张分值 +顺子分值=对应顺子分值 +同花分值=对应同花分值 +葫芦分值=(三张分值+对子分值)*2 +四条分值=四张分值+单张分值 +同花顺分值=同花顺分值 +皇家同花顺=皇家同花顺分值 +*/ +func CalCardsKindScore(cardsInfo *CardsInfo) int32 { + switch cardsInfo.Kind { + case KindOfCard_RoyalFlush: + return CalScoreByKindAndCard(KindOfCard_RoyalFlush, cardsInfo.KindCards[4]) + case KindOfCard_StraightFlush: + return CalScoreByKindAndCard(KindOfCard_StraightFlush, cardsInfo.KindCards[4]) + case KindOfCard_FourKind: + return CalScoreByKindAndCard(KindOfCard_FourKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4]) + case KindOfCard_Fullhouse: + return (CalScoreByKindAndCard(KindOfCard_ThreeKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_TwoPair, cardsInfo.KindCards[3])) * 2 + case KindOfCard_Flush: + return CalScoreByKindAndCard(KindOfCard_Flush, cardsInfo.KindCards[0]) + case KindOfCard_Straight: + return CalScoreByKindAndCard(KindOfCard_Straight, cardsInfo.KindCards[4]) + case KindOfCard_ThreeKind: + return CalScoreByKindAndCard(KindOfCard_ThreeKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[3]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4]) + case KindOfCard_TwoPair: + return CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[2]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4]) + case KindOfCard_OnePair: + return CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[0]) + case KindOfCard_HighCard: + return CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[0]) + default: + } + return 0 +} + +func MakeCardKindKey(kind int32, card int32) int32 { + return kind*10000 + card +} + +func SetCardKindValue(key, value int32) { + cardKindValue[key] = value +} + +func CalScoreByKindAndCard(kind int32, card int32) int32 { + if v, exist := cardKindValue[MakeCardKindKey(kind, card)]; exist { + return v + } + return 0 +} + +type PlayerCard struct { + HandCard []int32 + UserData interface{} + WinningProbability int32 + AllCardKind [KindOfCard_Max]int32 + WinCardKind [KindOfCard_Max]int32 + winTimes int32 + CI *CardsInfo +} + +type GameCtx struct { + PlayerCards []*PlayerCard + CommonCard []int32 + RestCard []int32 + CommonCardCnt int + Possibilities int +} + +func (gctx *GameCtx) GetMaxKindOfCard() int32 { + maxKind := KindOfCard_HighCard + for _, pc := range gctx.PlayerCards { + if pc != nil && pc.CI != nil && pc.CI.Kind > maxKind { + maxKind = pc.CI.Kind + } + } + return maxKind +} + +func (gctx *GameCtx) GetMaxCardInfo() *CardsInfo { + var max *CardsInfo + for _, pc := range gctx.PlayerCards { + if pc != nil && pc.CI != nil { + if max == nil { + max = pc.CI + } else if pc.CI.Value > max.Value { + max = pc.CI + } + } + } + return max +} + +func CalWinningProbability(ctx *GameCtx) { + if ctx == nil { + return + } + + possibilities := uint64(0) + max := int64(0) + maxIdx := make([]int, 0, len(ctx.PlayerCards)) + oldCC := len(ctx.CommonCard) + m := 5 - oldCC + if m == 0 { //手牌和公牌全部确定 + for k := 0; k < len(ctx.PlayerCards); k++ { + ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard) + ctx.PlayerCards[k].CI.CalValue() + if ctx.PlayerCards[k].CI.Value > max { + max = ctx.PlayerCards[k].CI.Value + maxIdx = maxIdx[0:0] + maxIdx = append(maxIdx, k) + } else if ctx.PlayerCards[k].CI.Value == max { + maxIdx = append(maxIdx, k) + } + ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++ + } + if len(maxIdx) != 0 { + for _, idx := range maxIdx { + ctx.PlayerCards[idx].winTimes++ + ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++ + } + } + + possibilities = uint64(len(maxIdx)) + } else { //计算各种组合的可能 + //先填充够5张公牌的位置 + for i := oldCC; i < 5; i++ { + ctx.CommonCard = append(ctx.CommonCard, -1) + } + + n := len(ctx.RestCard) + atable := make([]int32, n) + for i := 0; i < n; i++ { + atable[i] = int32(i) + } + + possibilities = algorithm.CombNumber(uint64(n), uint64(m)) + if possibilities > 10000 { + possibilities = 10000 + } + + c := algorithm.CombinerSelectUseRecursion(atable, m) + if len(c) > int(possibilities) { + for i := 0; i < int(possibilities); i++ { + r := rand.Intn(len(c)) + //设置公牌 + for j := 0; j < len(c[r]); j++ { + ctx.CommonCard[oldCC+j] = ctx.RestCard[c[r][j]] + } + + max = 0 + maxIdx = maxIdx[0:0] + for k := 0; k < len(ctx.PlayerCards); k++ { + ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard) + ctx.PlayerCards[k].CI.CalValue() + if ctx.PlayerCards[k].CI.Value > max { + max = ctx.PlayerCards[k].CI.Value + maxIdx = maxIdx[0:0] + maxIdx = append(maxIdx, k) + } else if ctx.PlayerCards[k].CI.Value == max { + maxIdx = append(maxIdx, k) + } + ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++ + } + if len(maxIdx) != 0 { + for _, idx := range maxIdx { + ctx.PlayerCards[idx].winTimes++ + ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++ + } + } + } + } else { + for i := 0; i < len(c); i++ { + //设置公牌 + for j := 0; j < len(c[i]); j++ { + ctx.CommonCard[oldCC+j] = ctx.RestCard[c[i][j]] + } + + max = 0 + maxIdx = maxIdx[0:0] + for k := 0; k < len(ctx.PlayerCards); k++ { + ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard) + ctx.PlayerCards[k].CI.CalValue() + if ctx.PlayerCards[k].CI.Value > max { + max = ctx.PlayerCards[k].CI.Value + maxIdx = maxIdx[0:0] + maxIdx = append(maxIdx, k) + } else if ctx.PlayerCards[k].CI.Value == max { + maxIdx = append(maxIdx, k) + } + ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++ + } + if len(maxIdx) != 0 { + for _, idx := range maxIdx { + ctx.PlayerCards[idx].winTimes++ + ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++ + } + } + } + } + } + + fmt.Printf("Common Card:") + if oldCC > 0 { + for i := 0; i < oldCC; i++ { + fmt.Printf("%s", Card(ctx.CommonCard[i])) + } + ci := KindOfCardFigureUpExSington.FigureUpByCard(ctx.CommonCard[:oldCC]) + if ci != nil { + fmt.Printf(":%s", ci.KindStr()) + } + } + fmt.Println() + + ctx.Possibilities = int(possibilities) + for i := 0; i < len(ctx.PlayerCards); i++ { + ctx.PlayerCards[i].WinningProbability = int32(int64(ctx.PlayerCards[i].winTimes) * 10000 / int64(possibilities)) + fmt.Printf("UserData<%#v> %s%s WinningProbability=%.2f%% \n", ctx.PlayerCards[i].UserData, Card(ctx.PlayerCards[i].HandCard[0]), Card(ctx.PlayerCards[i].HandCard[1]), float32(ctx.PlayerCards[i].WinningProbability)/100) + } +} + +func PossibleKindOfCards(ctx *GameCtx) []int32 { + kinds := make([]int32, KindOfCard_Max) + if ctx == nil { + return kinds + } + + if ctx.CommonCardCnt < 5 { + return kinds + } + + n := len(ctx.RestCard) + atable := make([]int32, n) + for i := 0; i < n; i++ { + atable[i] = int32(i) + } + + var handCard [2]int32 + c := algorithm.CombinerSelectUseRecursion(atable, 2) + for i := 0; i < len(c); i++ { + //设置公牌 + for j := 0; j < len(c[i]); j++ { + handCard[j] = ctx.RestCard[c[i][j]] + } + + ci := KindOfCardFigureUpSington.figureUp(handCard[:], ctx.CommonCard) + if ci != nil { + kinds[ci.Kind]++ + } + } + return kinds +} + +func CaculKindOfCardCount(kinds []int32, expectKind []int32) (int32, int32) { + var cnt int32 + for _, k := range expectKind { + cnt += kinds[k] + } + var total int32 + for _, v := range kinds { + total += v + } + return total, cnt +} + +func CaculGreaterKindOfCardCount(kinds []int32, cmpKind int32) (int32, int32) { + var cnt int32 + var total int32 + for k, v := range kinds { + total += v + if k >= int(cmpKind) { + cnt++ + } + } + return total, cnt +} + +func PossibleGreaterKindOfCards(ctx *GameCtx, cmpCI *CardsInfo) (int, int) { + if ctx == nil { + return 0, 1 + } + + if ctx.CommonCardCnt < 5 { + return 0, 1 + } + + if cmpCI == nil { + return 0, 1 + } + + n := len(ctx.RestCard) + atable := make([]int32, n) + for i := 0; i < n; i++ { + atable[i] = int32(i) + } + + var cnt int + var handCard [2]int32 + c := algorithm.CombinerSelectUseRecursion(atable, 2) + for i := 0; i < len(c); i++ { + //设置公牌 + for j := 0; j < len(c[i]); j++ { + handCard[j] = ctx.RestCard[c[i][j]] + } + + ci := KindOfCardFigureUpSington.figureUp(handCard[:], ctx.CommonCard) + if ci != nil { + ci.CalValue() + } + + if ci.Value > cmpCI.Value { + cnt++ + } + } + return cnt, len(c) +} diff --git a/gamerule/dezhoupoker/poker.go b/gamerule/dezhoupoker/poker.go new file mode 100644 index 0000000..5df643c --- /dev/null +++ b/gamerule/dezhoupoker/poker.go @@ -0,0 +1,230 @@ +package dezhoupoker + +import ( + "fmt" + "math/rand" + "sort" + "time" +) + +// +//牌序- K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +const ( + POKER_A int32 = 0 + POKER_2 int32 = 1 + POKER_3 int32 = 2 + POKER_4 int32 = 3 + POKER_5 int32 = 4 + POKER_6 int32 = 5 + POKER_7 int32 = 6 + POKER_8 int32 = 7 + POKER_9 int32 = 8 + POKER_10 int32 = 9 + POKER_J int32 = 10 + POKER_Q int32 = 11 + POKER_K int32 = 12 + + POKER_CNT int32 = 52 + + PER_CARD_COLOR_MAX = 13 + POKER_A_Weight int32 = 13 +) + +const ( + CardColor_Diamond int32 = iota //0,方块 + CardColor_Spade //1,梅花 + CardColor_Heart //2,红桃 + CardColor_Club //3,黑桃 + CardColor_Joker //4,王 +) + +var CardColor = []string{"♦", "♣", "♥", "♠"} +var PokerValue = []string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"} +var CardValueMap = [PER_CARD_COLOR_MAX]int{13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} + +type Card int + +func (c Card) String() string { + if c >= 0 && c < 53 { + switch c { + case 52: + return "[大王]" + case 53: + return "[小王]" + default: + cc := c / PER_CARD_COLOR_MAX + cv := c % PER_CARD_COLOR_MAX + return fmt.Sprintf("[%s%s]", CardColor[cc], PokerValue[cv]) + } + } + return "[-]" +} + +func (c Card) Color() int { + return int(c) / PER_CARD_COLOR_MAX +} + +func (c Card) Value() int { + return CardValueMap[int(c)%PER_CARD_COLOR_MAX] +} + +func HandCardShowStr(cards []int32) string { + if len(cards) != 2 { + return "" + } + + temp := []int{CardValueMap[cards[0]%PER_CARD_COLOR_MAX], CardValueMap[cards[1]%PER_CARD_COLOR_MAX]} + sort.Ints(temp) + + str := PokerValue[temp[0]%PER_CARD_COLOR_MAX] + PokerValue[temp[1]%PER_CARD_COLOR_MAX] + if temp[0] != temp[1] && cards[0]/PER_CARD_COLOR_MAX == cards[1]/PER_CARD_COLOR_MAX { + str += "s" + } + return str +} + +type Poker struct { + buf [POKER_CNT]Card + pos int +} + +func NewPoker() *Poker { + p := &Poker{} + p.init() + return p +} + +func (this *Poker) init() { + for i := int32(0); i < POKER_CNT; i++ { + this.buf[i] = Card(i) + } + rand.Seed(time.Now().UnixNano()) + this.Shuffle() +} + +func (this *Poker) Shuffle() { + for i := int32(0); i < POKER_CNT; i++ { + j := rand.Intn(int(i) + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } + this.pos = 0 +} + +func (this *Poker) NextByHands(n int) Card { + interval := make(map[int][]Card) + interval[0] = []Card{1, 2, 3, 4, 5} + interval[1] = []Card{6, 7, 8, 9} + interval[2] = []Card{10, 11, 12, 0} + var rate []int + if n == 0 { + rate = []int{30, 37, 33} + } else if n == 1 { + rate = []int{30, 35, 35} + } else if n == 2 { + rate = []int{30, 36, 34} + } else { + return this.Next() + } + m := randInSliceIndex(rate) + cards := interval[m] + if len(cards) != 0 { + r := rand.Intn(len(cards)) + cardr := cards[r] + this.FindCardByR(cardr) + } + return this.Next() +} + +func (this *Poker) FindCardByR(n Card) { + a := []Card{} + for k, v := range this.buf { + if v%13 == n && k >= this.pos { + a = append(a, v) + } + } + if len(a) != 0 { + r := rand.Intn(len(a)) + for k, v := range this.buf { + if v == a[r] { + if this.pos <= len(this.buf) { + this.buf[k], this.buf[this.pos] = this.buf[this.pos], this.buf[k] + } + break + } + } + } +} + +func (this *Poker) Next() Card { + if this.pos >= len(this.buf) { + return -1 + } + c := this.buf[this.pos] + this.pos++ + return c +} + +func (this *Poker) MakeCard(cardValue, cardColor int32) int32 { + return cardColor*PER_CARD_COLOR_MAX + cardValue +} + +func (this *Poker) Count() int { + if len(this.buf) >= this.pos { + return len(this.buf) - this.pos + } + return 0 +} + +func (this *Poker) GetRestCard() []int32 { + cnt := this.Count() + if cnt <= 0 { + return []int32{} + } + ret := make([]int32, cnt) + for i := 0; i < cnt; i++ { + ret[i] = int32(this.buf[this.pos+i]) + } + return ret +} + +func (this *Poker) DelCard(c int32) { + if this.Count() <= 0 { + return + } + + for i := this.pos; i < len(this.buf); i++ { + if int32(this.buf[i]) == c { + this.buf[i], this.buf[this.pos] = this.buf[this.pos], this.buf[i] + this.pos++ + return + } + } +} + +func (this *Poker) DelCards(cards []int32) { + for _, c := range cards { + this.DelCard(c) + } +} + +func randInSliceIndex(pool []int) int { + var total int + for _, v := range pool { + total += v + } + val := int(rand.Int31n(int32(total))) + total = 0 + for index, v := range pool { + total += v + if total >= val { + return index + } + } + + return 0 +} diff --git a/gamerule/dragonvstiger/cardkindparam.go b/gamerule/dragonvstiger/cardkindparam.go new file mode 100644 index 0000000..95c9909 --- /dev/null +++ b/gamerule/dragonvstiger/cardkindparam.go @@ -0,0 +1,127 @@ +package dragonvstiger + +import ( + "bytes" + "encoding/gob" +) + +type CardKindParam struct { + flag int +} + +func (this *CardKindParam) MarkFlag(flag int) { + flag = 1 << uint(flag) + this.flag |= flag +} +func (this *CardKindParam) UnmarkFlag(flag int) { + flag = 1 << uint(flag) + this.flag &= ^flag +} +func (this *CardKindParam) IsMarkFlag(flag int) bool { + flag = 1 << uint(flag) + if (this.flag & flag) != 0 { + return true + } + return false +} +func (this *CardKindParam) GetFlag() int { + return this.flag +} +func (this *CardKindParam) SetFlag(flag int) { + this.flag = flag +} +func (this *CardKindParam) String() string { + buff := "" + for i := 0; i < CardsKind_Max; i++ { + if this.IsMarkFlag(i) { + buff += kindofcardstr[i] + buff += "|" + } + } + return buff +} +func (this *CardKindParam) Clone() *CardKindParam { + ckp := &CardKindParam{} + for i := CardsKind_Normal; i < CardsKind_Max; i++ { + ckp.MarkFlag(i) + } + return ckp +} +func (this *CardKindParam) Marshal() ([]byte, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(this) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} +func (this *CardKindParam) Unmarshal(data []byte) error { + md := &CardKindParam{} + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + err := dec.Decode(md) + if err != nil { + return err + } else { + for i := CardsKind_Normal; i < CardsKind_Max; i++ { + if md.IsMarkFlag(i) { + this.MarkFlag(i) + } + } + return nil + } +} + +type KindOfCard struct { + kind int + maxValue int + maxColor int + cards []int +} + +func (this *KindOfCard) GetKind() int { return this.kind } +func (this *KindOfCard) GetMax() int { return this.maxValue } +func (this *KindOfCard) GetColor() int { return this.maxColor } +func (this *KindOfCard) GetCards() []int { return this.cards } +func (this *KindOfCard) IsAAA() bool { + return this.kind == CardsKind_ThreeSame || this.kind == CardsKind_Boom +} +func (this *KindOfCard) Marshal() ([]byte, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(this) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} +func (this *KindOfCard) Unmarshal(data []byte) error { + koc := &KindOfCard{} + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + err := dec.Decode(koc) + if err != nil { + return err + } else { + this.kind = koc.kind + this.maxValue = koc.maxValue + this.maxColor = koc.maxColor + this.cards = koc.cards + return nil + } +} + +var kindofcardstr = []string{ + "CardsKind_Normal", + "CardsKind_Double", + "CardsKind_ThreeSort", + "CardsKind_SameColor", + "CardsKind_A23", + "CardsKind_SameColorSort", + "CardsKind_ThreeSame", + "CardsKind_235Double", + "CardsKind_Boom", + "CardsKind_235Boom", + "CardsKind_Max", +} diff --git a/gamerule/dragonvstiger/constants.go b/gamerule/dragonvstiger/constants.go new file mode 100644 index 0000000..7b8bcac --- /dev/null +++ b/gamerule/dragonvstiger/constants.go @@ -0,0 +1,61 @@ +package dragonvstiger + +import ( + "time" +) + +const ( + CardsKind_Normal int = iota //0.高牌 + CardsKind_Double //1.对子 + CardsKind_A23 //2.顺子(地龙) + CardsKind_ThreeSort //3.顺子 + CardsKind_SameColor //4.金花(同花) + CardsKind_ColorA23 //5.同花顺(地龙) + CardsKind_SameColorSort //6.同花顺 + CardsKind_ThreeSame //7.豹子 + CardsKind_235Double + CardsKind_Boom //9.AAA + CardsKind_235Boom + CardsKind_Max +) +const ( + DragonVsTigerStakeAntTimeout = time.Second * 2 //准备押注 + DragonVsTigerStakeTimeout = time.Second * 10 //押注 + DragonVsTigerOpenCardAntTimeout = time.Second * 1 //准备开牌 + DragonVsTigerOpenCardTimeout = time.Second * 5 //开牌 + DragonVsTigerBilledTimeout = time.Millisecond * 3500 //结算 + DragonVsTigerRecordTime = 5 //回收金币记录时间 + DragonVsTigerBatchSendBetTimeout = time.Second * 1 //发送下注数据时间间隔 +) + +//场景状态 +const ( + DragonVsTigerSceneStateStakeAnt int = iota //准备押注 + DragonVsTigerSceneStateStake //押注 + DragonVsTigerSceneStateOpenCardAnt //准备开牌 + DragonVsTigerSceneStateOpenCard //开牌 + DragonVsTigerSceneStateBilled //结算 + DragonVsTigerSceneStateMax +) + +//玩家操作 +const ( + DragonVsTigerPlayerOpBet int = iota //下注 + DragonVsTigerPlayerOpGetOLList //获取在线列表 + DragonVsTigerPlayerOpUpBanker //上庄 + DragonVsTigerPlayerOpDwonBanker //下庄 + DragonVsTigerPlayerOpUpList //上庄列表 + DragonVsTigerPlayerOpNowDwonBanker //在庄的下庄 +) +const MaxBankerNum = 10 +const ( + DVST_ZONE_DRAW int = iota + DVST_ZONE_DRAGON + DVST_ZONE_TIGER + DVST_ZONE_MAX +) +const ( + ROBOT_TYPE_DVTRANDOM int = iota + ROBOT_TYPE_DVTFWIN + ROBOT_TYPE_DVTIWIN +) diff --git a/gamerule/dragonvstiger/poker.go b/gamerule/dragonvstiger/poker.go new file mode 100644 index 0000000..0c5314f --- /dev/null +++ b/gamerule/dragonvstiger/poker.go @@ -0,0 +1,127 @@ +package dragonvstiger + +import ( + "math/rand" + "time" +) + +const ( + POKER_CART_CNT = 52 + PER_CARD_COLOR_MAX = 13 + CardColor_Max = 4 + Hand_CardNum = 3 + A_CARD = 0 + K_CARD = 12 + T_CARD = 9 +) + +var cardSeed = time.Now().UnixNano() + +type Card int + +var CardValueMap = [PER_CARD_COLOR_MAX]int{13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} + +type Poker struct { + buf []Card + //todo test 指定的牌放在最前面 + ctrlPokers []int64 +} +type PokerMemData struct { + Buf []Card +} + +func NewPoker() *Poker { + p := &Poker{} + p.Shuffle() + return p +} + +func (this *Poker) Shuffle() { + this.buf = this.buf[:0] + for i := 0; i < POKER_CART_CNT; i++ { + this.buf = append(this.buf, Card(i)) + } + cardSeed++ + rand.Seed(cardSeed) + for i := 0; i < len(this.buf); i++ { + j := rand.Intn(i + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} +func (this *Poker) Next() Card { + if len(this.buf) <= 0 { + return -1 + } + c := this.buf[0] + this.buf = this.buf[1:] + return c +} + +func (this *Poker) Count() int { + return len(this.buf) +} + +func (this *Poker) TryDTDraw(flag int32) (int32, int32) { + dCard := this.Next() + tCard := this.Next() + d := dCard % PER_CARD_COLOR_MAX + t := tCard % PER_CARD_COLOR_MAX + switch flag { + case 1: // 龙 + if d > t { + break + } + if d < t { + dCard, tCard = tCard, dCard + break + } + // 龙给一张大于1的牌 + for i := 0; i < len(this.buf); i++ { + if this.buf[i]%PER_CARD_COLOR_MAX > 0 { + dCard, this.buf[i] = this.buf[i], dCard + break + } + } + d = dCard % PER_CARD_COLOR_MAX + for i := 0; i < len(this.buf); i++ { + if d > this.buf[i]%PER_CARD_COLOR_MAX { + tCard, this.buf[i] = this.buf[i], tCard + break + } + } + case 2: // 虎 + if d < t { + break + } + if d > t { + dCard, tCard = tCard, dCard + break + } + // 虎给一张大于1的牌 + for i := 0; i < len(this.buf); i++ { + if this.buf[i]%PER_CARD_COLOR_MAX > 0 { + tCard, this.buf[i] = this.buf[i], tCard + break + } + } + t = tCard % PER_CARD_COLOR_MAX + for i := 0; i < len(this.buf); i++ { + if t > this.buf[i]%PER_CARD_COLOR_MAX { + dCard, this.buf[i] = this.buf[i], dCard + break + } + } + default: + // 和 + if d == t { + break + } + for i := 0; i < len(this.buf); i++ { + if this.buf[i]%PER_CARD_COLOR_MAX == d { + tCard, this.buf[i] = this.buf[i], tCard + break + } + } + } + return int32(dCard), int32(tCard) +} diff --git a/gamerule/easterisland/constants.go b/gamerule/easterisland/constants.go new file mode 100644 index 0000000..6e365c4 --- /dev/null +++ b/gamerule/easterisland/constants.go @@ -0,0 +1,864 @@ +package easterisland + +// easter island +const ( + Element_WILD int = iota + 1 //1 通配 + Element_SCATTER //2 Scatter=FreeSpins + Element_BONUS //3 Bonus + Element_EASTERISLAND //4 Jackpot + Element_Y //5 黄色狐狸 + Element_P //6 紫色蜥蜴 + Element_G //7 绿色青蛙 + Element_A //8 红桃样式 + Element_B //9 方砖样式 + Element_C //10 梅花样式 + Element_D //11 黑桃样式 + Element_Max +) + +var Element_NAME_MAP = map[int]string{ + Element_WILD: "[ WIND ]", + Element_SCATTER: "[ FREE ]", + Element_BONUS: "[ BONUS ]", + Element_EASTERISLAND: "[JACKPOT]", + Element_Y: "[黄色狐狸]", + Element_P: "[紫色蜥蜴]", + Element_G: "[绿色青蛙]", + Element_A: "[ 红 ]", + Element_B: "[ 方 ]", + Element_C: "[ 梅 ]", + Element_D: "[ 黑 ]", + -1: "[ - ]", +} + +var AllLineMatrix = [][]int{ + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条4 + + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条5 + + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条6 + + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条7 + + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条8 + + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条9 + + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条10 + + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条11 + + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条12 + + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条13 + + ///////////////////// + // *-------*-------* + // \ / \ / + // ----*-------*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + }, //线条14 + + ///////////////////// + // ----------------- + // + // ----*-------*---- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, + }, //线条15 + + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条16 + + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条17 + + ///////////////////// + // *---*-------*---* + // \ / + // ----------------- + // \ / + // --------*-------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + }, //线条18 + + ///////////////////// + // --------*-------- + // / \ + // ----------------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + }, //线条19 + + ///////////////////// + // *---------------* + // \ / + // ----------------- + // \ / + // ----*---*---*---- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + }, //线条20 + + ///////////////////// + // ----*---*---*---- + // / \ + // ----------------- + // / \ + // *---------------* + ///////////////////// + { + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + }, //线条21 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条22 + + ///////////////////// + + // --------*-------- + // / \ + // *---------------* + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + }, //线条23 + + ///////////////////// + + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条24 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条25 +} + +/* 所有线条数组 + *0 1 2 3 4 + *5 6 7 8 9 + *10 11 12 13 14 + */ +var AllLineArray = [][]int{ + {5, 6, 7, 8, 9}, //线条1 + {0, 1, 2, 3, 4}, //线条2 + {10, 11, 12, 13, 14}, //线条3 + {0, 6, 12, 8, 4}, //线条4 + {10, 6, 2, 8, 14}, //线条5 + {5, 1, 2, 3, 9}, //线条6 + {5, 11, 12, 13, 9}, //线条7 + {0, 1, 7, 13, 14}, //线条8 + {10, 11, 7, 3, 4}, //线条9 + {5, 11, 7, 3, 9}, //线条10 + {5, 1, 7, 13, 9}, //线条11 + {0, 6, 7, 8, 4}, //线条12 + {10, 6, 7, 8, 14}, //线条13 + {0, 6, 2, 8, 4}, //线条14 + {10, 6, 12, 8, 14}, //线条15 + {5, 6, 2, 8, 9}, //线条16 + {5, 6, 12, 8, 9}, //线条17 + {0, 1, 12, 3, 4}, //线条18 + {10, 11, 2, 13, 14}, //线条19 + {0, 11, 12, 13, 4}, //线条20 + {10, 1, 2, 3, 14}, //线条21 + {5, 11, 2, 13, 9}, //线条23 + {5, 1, 12, 3, 9}, //线条22 + {0, 11, 2, 13, 4}, //线条24 + {10, 1, 12, 3, 14}, //线条25 +} + +var AllLineDraw = []string{ + ` + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + `, + + ` + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + `, + + ` + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条4 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条5 + `, + + ` + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条6 + `, + + ` + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条7 + `, + + ` + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条8 + `, + + ` + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条9 + `, + + ` + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条10 + `, + + ` + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条11 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条12 + `, + + ` + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条13 + `, + + ` + ///////////////////// + // *-------*-------* + // \ / \ / + // ----*-------*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + }, //线条14 + `, + + ` + ///////////////////// + // ----------------- + // + // ----*-------*---- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, + }, //线条15 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条16 + `, + + ` + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条17 + `, + + ` + ///////////////////// + // *---*-------*---* + // \ / + // ----------------- + // \ / + // --------*-------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + }, //线条18 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // ----------------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + }, //线条19 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----------------- + // \ / + // ----*---*---*---- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + }, //线条20 + `, + ` + ///////////////////// + // ----*---*---*---- + // / \ + // ----------------- + // / \ + // *---------------* + ///////////////////// + { + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + }, //线条21 + `, + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条22 + `, + ` + // --------*-------- + // / \ + // *---------------* + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + }, //线条23 + `, + ` + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条24 + `, + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条25 + `, +} + +const LINE_ROW int = 3 //行数 +const LINE_CELL int = 5 //列数 +const LINENUM int = 25 //线条数 +const ELEMENT_TOTAL = LINE_ROW * LINE_CELL + +// 所有元素对应的赔率 +var LineScore = [Element_Max][LINE_CELL]int{ + {0, 0, 0, 0, 0}, //占位 + {0, 0, 0, 0, 0}, //* + {0, 0, 0, 0, 0}, //Scatter + {0, 0, 0, 0, 0}, //Bonus + {0, 0, 50, 200, 0}, //Jackpot + {0, 0, 15, 100, 200}, //黄色 + {0, 0, 10, 55, 150}, //紫色 + {0, 0, 10, 40, 100}, //绿色 + {0, 0, 5, 30, 70}, //红 + {0, 0, 5, 20, 55}, //方 + {0, 0, 3, 15, 40}, //梅 + {0, 0, 3, 10, 30}, //黑 +} + +// 免费次数奖励 +var FreeSpinTimesRate = [LINE_CELL]int{0, 0, 3, 6, 18} + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25} + +const BonusStepNum = 12 + +var BonusStepArr = [BonusStepNum][]float64{ + []float64{0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0, 2.0}, + []float64{0.0, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.5, 1.5, 2.0}, + []float64{0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.5, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 1.0, 1.5, 2.0}, + []float64{0.0, 0.0, 0.0, 0.5, 1.0, 1.5}, + []float64{0.0, 0.0, 0.0, 0.5, 2.0}, + []float64{0.0, 0.0, 0.0, 1.0}, + []float64{0.0, 0.0, 1.5}, + []float64{0.0, 1.5}, + []float64{0.0}, +} + +// ver 2 +var symbol1 = []int{ + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, + 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +} +var symbol2 = []int{ + 1, 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, +} + +var MissData = [][]int{ + []int{11, 7, 8, 9, 9, 3, 11, 10, 11, 5, 11, 4, 9, 11, 11}, + []int{9, 4, 10, 10, 9, 7, 8, 9, 8, 10, 2, 10, 8, 9, 8}, + []int{6, 11, 11, 10, 9, 10, 7, 2, 2, 8, 6, 5, 9, 5, 11}, + []int{5, 7, 4, 11, 10, 6, 5, 9, 9, 8, 10, 8, 10, 7, 9}, + []int{7, 8, 1, 4, 11, 11, 6, 8, 11, 3, 3, 9, 10, 10, 9}, + []int{10, 11, 6, 9, 2, 11, 4, 7, 4, 9, 9, 8, 8, 11, 10}, + []int{11, 8, 10, 8, 10, 3, 10, 1, 10, 9, 11, 4, 8, 4, 10}, + []int{6, 7, 10, 9, 9, 11, 10, 9, 8, 10, 8, 4, 11, 4, 9}, + []int{8, 2, 6, 9, 7, 10, 11, 7, 4, 11, 6, 9, 8, 11, 10}, + []int{8, 7, 4, 10, 3, 10, 5, 9, 5, 6, 6, 8, 10, 11, 11}, + []int{10, 8, 8, 9, 10, 6, 11, 11, 11, 2, 10, 4, 11, 11, 9}, + []int{5, 7, 11, 7, 9, 2, 10, 9, 11, 5, 10, 4, 4, 10, 11}, + []int{4, 7, 1, 4, 9, 9, 5, 6, 11, 8, 6, 8, 7, 10, 11}, + []int{7, 11, 11, 9, 7, 8, 4, 10, 8, 9, 10, 9, 11, 4, 10}, + []int{4, 8, 9, 9, 11, 10, 11, 1, 4, 8, 9, 4, 10, 11, 7}, + []int{6, 10, 11, 5, 11, 10, 6, 9, 11, 7, 6, 1, 4, 10, 11}, + []int{5, 4, 9, 4, 7, 6, 8, 1, 11, 9, 10, 11, 10, 10, 10}, + []int{11, 5, 4, 6, 9, 3, 2, 11, 3, 5, 5, 8, 11, 11, 11}, + []int{4, 7, 11, 1, 9, 8, 5, 3, 10, 10, 11, 8, 5, 7, 9}, +} + +// jack params +const ( + EL_JACKPOT_InitJackpot int = iota //初始化奖池数量 + EL_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赚取投注的多少倍 + EL_JACKPOT_LIMITWIN_PRIZEHIGH //现金池充足时 最多赚取投注的多少倍 +) diff --git a/gamerule/easterisland/easterisland.go b/gamerule/easterisland/easterisland.go new file mode 100644 index 0000000..9209729 --- /dev/null +++ b/gamerule/easterisland/easterisland.go @@ -0,0 +1,270 @@ +package easterisland + +import ( + "fmt" + "math/rand" + "time" +) + +var SpinID int64 = 100000 // todo + +type LineData struct { + Index int + Element int + Count int + Score int + Position []int32 +} + +// 图案*3+连线数量=赔率索引,返回元素值和数量 +func isLine(data []int) (e int, count int) { + e = data[0] + count = 1 + for i := 1; i < len(data); i++ { + if data[i] == e { + count++ + } else { + break + } + } + if count < 3 { + return -1, -1 + } + return +} +func CalcLine(data []int, betLines []int64) (lines []LineData) { + var cards = make([]int, len(data)) + for i, v := range data { + cards[i] = v + } + for i := 10; i < 15; i++ { + if cards[i] == Element_WILD || cards[i-5] == Element_WILD || cards[i-10] == Element_WILD { + cards[i] = Element_WILD + cards[i-5] = Element_WILD + cards[i-10] = Element_WILD + } + } + //fmt.Println("************************************************") + //fmt.Println("data:",data) + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + normalData := []int{} + realData := []int{} + epos := []int32{} + if cards[lineTemplate[0]] == Element_SCATTER || cards[lineTemplate[0]] == Element_BONUS { + continue + } + for _, pos := range lineTemplate { + edata = append(edata, cards[pos]) + if cards[pos] != Element_WILD { + normalData = append(normalData, cards[pos]) + } + realData = append(realData, data[pos]) + epos = append(epos, int32(pos+1)) + } + + if len(edata) == len(normalData) { //没有万能图案 + head, count := isLine(edata) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + } + } else { + normalData = DelSliceRepEle(normalData) + for _, value := range normalData { + replaceData := []int{} + for i := 0; i < len(edata); i++ { + if realData[0] == Element_EASTERISLAND { // WILD 不能替换 JACKPOT + replaceData = append(replaceData, realData[i]) + } else { + if edata[i] == Element_WILD { + replaceData = append(replaceData, value) + } else { + replaceData = append(replaceData, edata[i]) + } + } + } + head, count := isLine(replaceData) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos[:count]}) + //fmt.Printf("*******************%v****************************\n", lines) + break + } + } + } + } + //fmt.Print("lines:",lines) + return +} + +// 去除切片在的重复的元素 +func DelSliceRepEle(data []int) (res []int) { + if len(data) == 1 { + return data + } + eleFlag := make(map[int]bool) + for i := 0; i < len(data)-1; i++ { + if eleFlag[data[i]] { + continue + } + eleFlag[data[i]] = true + res = append(res, data[i]) + } + return res +} + +type CaclResult struct { + WinLines [][]LineData //多次中奖的线 + IsJackpot bool //是否爆奖 + AllScore int //总中奖倍率 + BonusScore int //小游戏奖励 + SpinFreeTimes int //免费旋转次数 +} + +func CaclScore(cards []int, betLines []int64) (int32, int32, int32, int32, bool) { + curscore := 0 + alllinenum := 0 //中奖线路总数 + allscore := 0 + spinFree := 0 + isJackpot := false + lines := CalcLine(cards, betLines) + for _, line := range lines { + if line.Element == Element_SCATTER { + spinFree += FreeSpinTimesRate[line.Count] + continue + } + if line.Element == Element_EASTERISLAND && line.Count == LINE_CELL { + isJackpot = true + } + curscore = LineScore[line.Element][line.Count-1] + line.Score = curscore + allscore = allscore + curscore + alllinenum++ + } + bounsCount := 0 + for _, card := range cards { + if card == Element_BONUS { + bounsCount++ + } + if card == Element_SCATTER { + spinFree++ + } + } + rand.Seed(time.Now().UnixNano()) + //bounsScore:= SmallGameByBouns[bounsCount][0]+rand.Intn(SmallGameByBouns[bounsCount][1]) + spinFreeTimes := FreeSpinTimesRate[spinFree] + return int32(alllinenum), int32(allscore), int32(0), int32(spinFreeTimes), isJackpot +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} + +// prizeFund > limit * roomId走这个检查 +func CheckBigWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD { + return true + } + return false +} + +// prizeFund < limit * roomId走这个更严格的检查 +func CheckSuperWin(slotsData []int) bool { + if slotsData[0] == Element_WILD || slotsData[5] == Element_WILD || slotsData[10] == Element_WILD { + return true + } + if (slotsData[1] == Element_WILD || slotsData[6] == Element_WILD || slotsData[11] == Element_WILD) && (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) { + return true + } + if (slotsData[2] == Element_WILD || slotsData[7] == Element_WILD || slotsData[12] == Element_WILD) && (slotsData[3] == Element_WILD || slotsData[8] == Element_WILD || slotsData[13] == Element_WILD) { + return true + } + return false +} + +type Symbol int + +const ( + SYMBOL1 Symbol = iota + 1 // 现金池不足 + SYMBOL2 // 现金池充足 +) + +var gSeedV = time.Now().UnixNano() + +func GenerateSlotsData_v2(s Symbol) ([]int, int) { + var tryCount = 0 +Next: + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0, ELEMENT_TOTAL) + for i := 0; i < ELEMENT_TOTAL; i++ { + if s == SYMBOL1 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1)-3)+3]) + } else { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1))]) + } + } else if s == SYMBOL2 { + if i == 0 || i == 5 || i == 10 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1)-5)+5]) + } else { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2))]) + } + } + } + tryCount++ + fmt.Print("tryCount:", tryCount) + if (s == SYMBOL1 && CheckSuperWin(slotsData)) || (s == SYMBOL2 && CheckBigWin(slotsData)) { + goto Next + } + return slotsData, tryCount +} + +type BonusGameResult struct { + BonusData []int64 //每次点击的显示奖金,有几个就点击几次,最后一次点击是0 + DataMultiplier int64 //第一级界面小游戏奖金总和 + Mutiplier int //最终小游戏的倍率 + TotalPrizeValue int64 //最终小游戏的奖励 +} + +func GenerateBonusGame(totalBet int, startBonus int) BonusGameResult { + if totalBet <= 0 || startBonus <= 0 { + return BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + } + } + var bg = BonusGameResult{ + BonusData: make([]int64, 0), + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: int64(0), + } + gSeedV++ + rand.Seed(gSeedV) + for _, e := range BonusStepArr { + rnd := rand.Intn(len(e)) + prizeValue := int64(e[rnd]*float64(totalBet) + 0.00001) + bg.TotalPrizeValue += prizeValue + bg.DataMultiplier += prizeValue + bg.BonusData = append(bg.BonusData, prizeValue) + if prizeValue == 0 { + break + } + } + bg.Mutiplier = startBonus + rand.Intn(3) + bg.TotalPrizeValue *= int64(bg.Mutiplier) + return bg +} diff --git a/gamerule/easterisland/easterisland_test.go b/gamerule/easterisland/easterisland_test.go new file mode 100644 index 0000000..dede72a --- /dev/null +++ b/gamerule/easterisland/easterisland_test.go @@ -0,0 +1,161 @@ +package easterisland + +import ( + "fmt" + "math/rand" + "sort" + "testing" + "time" +) + +func TestIsLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{0, 0, 0, 1, 1}, line: 3}, + {data: []int{0, 0, 0, 0, 1}, line: 4}, + {data: []int{0, 0, 0, 0, 0}, line: 5}, + } + for _, value := range testData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error line data:", value) + t.Fatal("TestIsLine") + } + } + errorData := []TestData{ + {data: []int{1, 0, 0, 0, 1}, line: -1}, + {data: []int{1, 1, 0, 0, 0}, line: -1}, + {data: []int{1, 1, 0, 1, 1}, line: -1}, + } + for _, value := range errorData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error data:", value) + t.Fatal("TestIsLine") + } + } +} + +//func TestCalcLine(t *testing.T) { +// type TestData struct { +// data []int +// line int +// } +// testData := []TestData{ +// {data: []int{2, 1, 3, 4, 1, 5, 6, 1, 7, 8, 1, 0, 8, 1, 7}, line: 1}, +// {data: []int{6, 7, 5, 7, 6, 5, 6, 8, 8, 7, 7, 7, 6, 2, 9}, line: 1}, +// {data: []int{10, 5, 9, 1, 9, 2, 7, 9, 9, 9, 5, 3, 2, 2, 9}, line: 4}, +// {data: []int{3, 10, 6, 1, 3, 7, 3, 3, 3, 3, 7, 6, 10, 8, 0}, line: 6}, +// {data: []int{9, 7, 10, 1, 5, 10, 4, 6, 9, 5, 7, 5, 1, 2, 4}, line: 2}, +// {data: []int{9, 3, 0, 2, 0, 0, 3, 9, 0, 5, 9, 2, 2, 8, 2}, line: 2}, +// } +// for _, value := range testData { +// lines := CalcLine(value.data) +// if len(lines) != value.line { +// t.Log("lines:", lines) +// t.Log("Error line data:", value.data) +// t.Fatal("TestIsLine") +// } +// } +//} +//func TestCalcLineScore(t *testing.T) { +// lines := CalcLine([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}) +// t.Log(lines) +// line, allscore, _, _, _ := CaclScore([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}) +// PrintHuman([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 1, 10, 8, 10, 4}) +// t.Logf("lineNum:%v allScore:%v", line, allscore) +// t.Fatal("TestCalcLineScore") +//} + +// func TestRandCalcLineScore(t *testing.T) { +// var cards, c = generateSlotsData() +// t.Logf("尝试次数:%v次,Data:%v", c, cards) +// PrintHuman(cards) +// lines := CalcLine(cards) +// t.Log(lines) +// line, allscore, _, _, _ := CaclScore(cards) +// t.Logf("lineNum:%v allScore:%v", line, allscore) +// t.Fatal("TestRandCalcLineScore") +// } +func TestDelSliceRepEle(t *testing.T) { + type TestData struct { + data []int + rdata []int + } + testData := []TestData{ + {data: []int{1}, rdata: []int{1}}, + {data: []int{1, 2}, rdata: []int{1, 2}}, + {data: []int{1, 1, 2}, rdata: []int{1, 2}}, + {data: []int{1, 2, 2}, rdata: []int{1, 2}}, + {data: []int{1, 2, 2, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 2, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 1, 2, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 2, 3, 3}, rdata: []int{1, 2, 3}}, + {data: []int{1, 1, 1, 1}, rdata: []int{1}}, + } + for _, value := range testData { + rdata := DelSliceRepEle(value.data) + if !SliceEqual(rdata, value.rdata) { + t.Error(value.data) + t.Error(rdata) + t.Fatal("TestDelSliceRepEle") + } + } +} + +/* + * 切片是否相等 + */ +func SliceEqual(left []int, right []int) bool { + if len(left) != len(right) { + return false + } + sort.Ints(left) + sort.Ints(right) + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} + +//func TestRollNormalElement(t *testing.T) { +// card := make([]int, ROLLLINEMAX, ROLLLINEMAX) +// count := 100000 +// now := time.Now() +// for i := 0; i < count; i++ { +// card = RollNormalElement(card) +// } +// t.Logf("RollNormalElement rand %v data cost %v second.", count, time.Now().Sub(now).Seconds()) +//} +//func TestRollFreeElement(t *testing.T) { +// card := make([]int, ROLLLINEMAX, ROLLLINEMAX) +// count := 100000 +// now := time.Now() +// for i := 0; i < count; i++ { +// card = RollFreeElement(card, rand.Intn(2)) +// } +// t.Logf("RollFreeElement rand %v data cost %v second.", count, time.Now().Sub(now).Seconds()) +//} + +func generateSlotsData() ([]int, int) { + var tryCount = 0 +Next: + var slotsData = make([]int, 0, ELEMENT_TOTAL) + rand.Seed(time.Now().UnixNano()) + for i := 0; i < ELEMENT_TOTAL; i++ { + slotsData = append(slotsData, symbolSmall[rand.Intn(len(symbolSmall))]) + } + tryCount++ + fmt.Print("tryCount:", tryCount) + if CheckBigWin(slotsData) { + goto Next + } + return slotsData, tryCount +} + +var symbolSmall = []int{1, 2, 3, 3, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 7, 4, 4, 4, 4, 4, 1, 1, 5, 5, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10} diff --git a/gamerule/fishing/balance.go b/gamerule/fishing/balance.go new file mode 100644 index 0000000..7d80057 --- /dev/null +++ b/gamerule/fishing/balance.go @@ -0,0 +1,92 @@ +package fishing + +//2.新增配置信息 +const ( + //2.1 历史流水调整系数相关配置 + Wa = 100000 //历史流水的调整基准,高于此值后历史流水系数不再变化 + Ramax = 1.3 //历史流水调整系数最大值 + Ramin = 0.97 //历史流水调整系数最小值 + + //2.2 历史输赢调整系数相关配置 + Hmax_chu = 20000 + Hmin_chu = -15000 + Beta_chu = 0.0001 + Alpha_chu = 0.00015 + + Hmax_zho = 100000 + Hmin_zho = -100000 + Beta_zho = 0.0001 + Alpha_zho = 0.0001 + + Hmax_gao = 200000 + Hmin_gao = -300000 + Beta_gao = 0.0002 + Alpha_gao = 0.00005 + + R1 = 1 + Theta = 0.5 + + //2.3 充值行为调整系数相关配置 + P1 = 10000 //充值流水分段1 + Rp1 = 1.01 //在充值后水流在[0,P1]之间的调整系数 + P2 = 30000 //充值流水分段2 + Rp2 = 1.001 //在充值后水流在[P1,P2]之间的调整系数 + Rp3 = 1 //在充值后水流在[P2,∞]之间的调整系数 + + //2.4 每日优惠系数相关配置 + D1 = 2000 //日流水分段1 + Rd1 = 1.01 //日水流在[0,D1)之间的调整系数 + D2 = 5000 //日流水分段2 + Rd2 = 1.001 //日水流在[D1,D2)之间的调整系数 + Rd3 = 1 //日水流在[D2,∞)之间的调整系数 + + //2.5 捕鱼平台调整系数相关配置 + Tmin = 0 //平台收益底线 + R2 = 1 //平台收益调整系数基准值 + Rtmin = 0.5 //平台收益调整系数最小值 + Delta_chu = 0.00001 //初级场下压系数 + Delta_zho = 0.00001 //中级场下压系数 + Delta_gao = 0.00001 //高级场下压系数 + + //2.6 捕鱼命中调整 + JunioroolRate1 = 0.8 // 初级水池影响X + JuniorPoolRate2 = 1 + JuniorPoolRate3 = 1.2 + MiddleroolRate1 = 0.6 // 中级水池影响X + MiddleroolRate2 = 1 + MiddleroolRate3 = 1.1 + HighpoolRate1 = 0.3 // 高级水池影响X + HighPoolRate2 = 1 + HighPoolRate3 = 1.05 + // 个人风控区间Y + YRate1 = 1.4 + YRate2 = 1.3 + YRate3 = 1.1 + YRate4 = 0.8 + YRate5 = 0.5 + YMdRate1 = 1.3 + YMdRate2 = 1.2 + YMdRate3 = 1.1 + YMdRate4 = 0.8 + YMdRate5 = 0.5 + YHgRate1 = 1.2 + YHgRate2 = 1.1 + YHgRate3 = 1.05 + YHgRate4 = 0.8 + YHgRate5 = 0.5 + // 新手保护Z + Z1 = 1.1 + Z2 = 1.0 // 非新手 + // 盈利比区间H + HRate1 = 1.2 + HRate2 = 1.1 + HRate3 = 1.0 + HRate4 = 0.8 + HRate5 = 0.5 +) + +var ( + FishHail int32 = 10 // 初级场抽成 + MdFishHail int32 = 10 // 中级抽成 + HgFishHail int32 = 10 // 高级抽成 +) diff --git a/gamerule/fishing/constants.go b/gamerule/fishing/constants.go new file mode 100644 index 0000000..2b827c5 --- /dev/null +++ b/gamerule/fishing/constants.go @@ -0,0 +1,44 @@ +package fishing + +// 房间类型 +const ( + RoomMode_HuanLe = 0 //欢乐捕鱼 + RoomMode_TianTian = 0 //天天捕鱼 + RoomMode_Max +) + +const ( + Event_Group_Max = 999 //1~999标识同组鱼,例如:一网打尽之类的 + Event_Ring = 10000 //冰冻事件 + Event_Booms = 10001 //全屏炸弹 + Event_Boom = 10002 //局部炸弹 + Event_Same = 10003 //托盘鱼事件 + Event_Lightning = 10004 //连锁闪电 + Event_Rand = 10005 //随机事件 + Event_FreePower = 10006 //免费炮台事件 + Event_NewBoom = 10007 //新的局部炸弹事件,返奖不包括自己 + Event_Bit = 10008 //钻头鱼事件 + Event_TreasureChest = 10009 //连续开宝箱事件 + Event_Player_UpLevel = 10010 //玩家升级事件 + Event_Player_UnMaxPower = 10011 //解锁最大炮倍事件 + Event_Player_UnPowerList = 10012 //解锁炮台 +) +const ( //特殊鱼和BOSS鱼 + Fish_CaiShen = 11601 //财神鱼 + SUNWUKONG = 4001 //孙悟空 + YUHUANGDADI = 4002 //玉皇大帝 +) + +const ( + ROOM_LV_CHU = 1 //初级场 + ROOM_LV_ZHO = 2 //中级场 + ROOM_LV_GAO = 3 //高级场 + ROOM_LV_RICH = 4 //至尊场 +) +const MaxPlayer = 4 + +// BOSS类型 对应DB_Fish表里的isBoss字段 +const ( + Boss = 1 + WorldBoss = 2 +) diff --git a/gamerule/fishing/fishing.go b/gamerule/fishing/fishing.go new file mode 100644 index 0000000..d9bc1b0 --- /dev/null +++ b/gamerule/fishing/fishing.go @@ -0,0 +1,26 @@ +package fishing + +import "math" + +func PowerValide(sceneType int32, power int32) bool { + low := math.Pow10(int(sceneType)) + high := math.Pow10(int(sceneType) + 1) + if power < int32(low) || power > int32(high) { + return false + } else { + return true + } +} + +/* + 获取最小值 +*/ +func Min(nums ...int) int { + var min int + for _, val := range nums { + if min == 0 || val <= min { + min = val + } + } + return min +} diff --git a/gamerule/fishing/newbalance.go b/gamerule/fishing/newbalance.go new file mode 100644 index 0000000..81b2c68 --- /dev/null +++ b/gamerule/fishing/newbalance.go @@ -0,0 +1,417 @@ +package fishing + +/* + 捕鱼新数值体系数值设计 +*/ + +/* + 新手保护系数 +*/ +const ( + RookieProtectionCoefficient = 0.1 +) + +/* + 玩家默认倍率(返奖率) +*/ +const PlayerDefaultOdds = 0 + +/* + 玩家当日倍率状态界限 +*/ +const ( + TodayPlayerOddsDividingLineStart_Chu = 0.75 + TodayPlayerOddsDividingLineStart_Zho = 0.75 + TodayPlayerOddsDividingLineStart_Gao = 0.75 + TodayPlayerOddsDividingLineEnd_Chu = 0.85 + TodayPlayerOddsDividingLineEnd_Zho = 0.85 + TodayPlayerOddsDividingLineEnd_Gao = 0.85 +) + +/* + 玩家默认的倍率修正值 +*/ +const PlayerDefaultHitRateCorrection = 0 + +/* + 玩家新手保护修正 +*/ +const ( + RookieLimitChu = 10 + RookieLimitZho = 0 + RookieLimitGao = 0 +) + +/* + 单日盈利计算分界线 (目前是整点计数) +*/ +const ( + TodayTimeDividingLineEnd = 6 + TodayTimeDividingLineStart = 0 +) + +/* + 倍率区间 +*/ +var OddsRanges_Chu = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} +var OddsRanges_Zho = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} +var OddsRanges_Gao = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} + +/* + 当日倍率区间 +*/ +var TodayOddsRanges_Chu = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} + +var TodayOddsRanges_Zho = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} + +var TodayOddsRanges_Gao = [][]float64{ + {0, 50}, + {50, 60}, + {60, 70}, + {70, 80}, + {80, 85}, + {85, 90}, + {90, 93}, + {93, 96}, + {96, 98}, + {98, 100}, + {100, 103}, + {103, 105}, + {105, 108}, + {108, 111}, + {111, 115}, + {115, 120}, + {120, 125}, + {125, -1}, +} + +// 初级场 +var TodayOddsRangesForFishMultiple_Chu = [][]float64{ + {0, 50, 20, 60, 30, 70}, + {50, 60, 50, 80, 40, 80}, + {60, 70, 50, 85, 50, 85}, + {70, 80, 55, 85, 50, 85}, + {80, 85, 55, 85, 55, 85}, + {85, 90, 60, 85, 60, 90}, + {90, 93, 60, 85, 60, 90}, + {93, 96, -1, -1, -1, -1}, + {96, 98, -1, -1, -1, -1}, + {98, 100, -1, -1, -1, -1}, + {100, 103, 110, 120, 110, 120}, + {103, 105, 110, 130, 110, 130}, + {105, 108, 120, 140, 120, 140}, + {108, 111, 120, 140, 120, 140}, + {111, 115, 120, 150, 120, 150}, + {115, 120, 130, 160, 130, 150}, + {120, 125, 150, 180, 140, 170}, + {125, -1, 160, 200, 150, 200}, +} + +// 中级场 +var TodayOddsRangesForFishMultiple_Zho = [][]float64{ + {0, 50, 20, 60, 30, 70}, + {50, 60, 50, 80, 40, 80}, + {60, 70, 50, 85, 50, 85}, + {70, 80, 55, 85, 50, 85}, + {80, 85, 55, 85, 55, 85}, + {85, 90, 60, 85, 60, 90}, + {90, 93, 60, 85, 60, 90}, + {93, 96, -1, -1, -1, -1}, + {96, 98, -1, -1, -1, -1}, + {98, 100, -1, -1, -1, -1}, + {100, 103, 110, 120, 110, 120}, + {103, 105, 110, 130, 110, 130}, + {105, 108, 120, 140, 120, 140}, + {108, 111, 120, 140, 120, 140}, + {111, 115, 120, 150, 120, 150}, + {115, 120, 130, 160, 130, 150}, + {120, 125, 150, 180, 140, 170}, + {125, -1, 160, 200, 150, 200}, +} + +// 高级场 +var TodayOddsRangesForFishMultiple_Gao = [][]float64{ + {0, 50, 20, 60, 30, 70}, + {50, 60, 50, 80, 40, 80}, + {60, 70, 50, 85, 50, 85}, + {70, 80, 55, 85, 50, 85}, + {80, 85, 55, 85, 55, 85}, + {85, 90, 60, 85, 60, 90}, + {90, 93, 60, 85, 60, 90}, + {93, 96, -1, -1, -1, -1}, + {96, 98, -1, -1, -1, -1}, + {98, 100, -1, -1, -1, -1}, + {100, 103, 110, 120, 110, 120}, + {103, 105, 110, 130, 110, 130}, + {105, 108, 120, 140, 120, 140}, + {108, 111, 120, 140, 120, 140}, + {111, 115, 120, 150, 120, 150}, + {115, 120, 130, 160, 130, 150}, + {120, 125, 150, 180, 140, 170}, + {125, -1, 160, 200, 150, 200}, +} + +/* + 倍率区间所对应的修正值 +*/ +var HitRateCorrection_Chu = []float64{ + 30, 20, 16, 14, 12, 10, 8, 5, 2, 0, 0, -6, -10, -12, -15, -20, -25, -30, +} + +var HitRateCorrection_Zho = []float64{ + 30, 20, 16, 14, 12, 10, 8, 5, 2, 0, 0, -6, -10, -12, -15, -20, -25, -30, +} +var HitRateCorrection_Gao = []float64{ + 30, 20, 16, 14, 12, 10, 8, 5, 2, 0, 0, -6, -10, -12, -15, -20, -25, -30, +} + +/* + 当日倍率区间所对应得修正值 +*/ +var TodayHitRateCorrection_Chu = []float64{ + 20, 18, 16, 14, 12, 10, 8, 5, 2, 0, -5, -8, -10, -12, -15, -20, -25, -30, +} +var TodayHitRateCorrection_Zho = []float64{ + 20, 18, 16, 14, 12, 10, 8, 5, 2, 0, -5, -8, -10, -12, -15, -20, -25, -30, +} +var TodayHitRateCorrection_Gao = []float64{ + 20, 18, 16, 14, 12, 10, 8, 5, 2, 0, -5, -8, -10, -12, -15, -20, -25, -30, +} + +/* + 触发上限的概率区间 +*/ + +var SingleProfitInterventionProbabilityIntervalUpLimit_Chu = [][]float64{ + {1.5, 1.6, 50, 0.3}, + {1.6, 1.7, 60, 0.5}, + {1.7, 1.8, 80, 0.6}, + {1.8, -1, 100, 0.8}, + // 测试数据 + //{1.5, 1.6, 100, 0.3}, + //{1.6, 1.7, 100, 0.5}, + //{1.7, 1.8, 100, 0.6}, + //{1.8, -1, 100, 0.8}, +} +var SingleProfitInterventionProbabilityIntervalUpLimit_Zho = [][]float64{ + {1.5, 1.6, 50, 0.3}, + {1.6, 1.7, 60, 0.5}, + {1.7, 1.8, 80, 0.6}, + {1.8, -1, 100, 0.8}, + // 测试数据 + //{1.5, 1.6, 100, 0.3}, + //{1.6, 1.7, 100, 0.5}, + //{1.7, 1.8, 100, 0.6}, + //{1.8, -1, 100, 0.8}, +} +var SingleProfitInterventionProbabilityIntervalUpLimit_Gao = [][]float64{ + {1.5, 1.6, 50, 0.3}, + {1.6, 1.7, 60, 0.5}, + {1.7, 1.8, 80, 0.6}, + {1.8, -1, 100, 0.8}, + // 测试数据 + //{1.5, 1.6, 100, 0.3}, + //{1.6, 1.7, 100, 0.5}, + //{1.7, 1.8, 100, 0.6}, + //{1.8, -1, 100, 0.8}, +} + +/* + 触发下限的概率区间 +*/ +var SingleProfitInterventionProbabilityIntervalDownLimit_Chu = [][]float64{ + {-1, 0.3, 90}, + {0.3, 0.4, 80}, + {0.4, 0.5, 70}, + {0.5, 0.6, 60}, + // 测试数据 + //{-1, 0.2, 100}, + //{0.2, 0.3, 100}, + //{0.3, 0.4, 100}, +} +var SingleProfitInterventionProbabilityIntervalDownLimit_Zho = [][]float64{ + {-1, 0.3, 90}, + {0.3, 0.4, 80}, + {0.4, 0.5, 70}, + {0.5, 0.6, 60}, + // 测试数据 + //{-1, 0.2, 100}, + //{0.2, 0.3, 100}, + //{0.3, 0.4, 100}, +} +var SingleProfitInterventionProbabilityIntervalDownLimit_Gao = [][]float64{ + {-1, 0.3, 90}, + {0.3, 0.4, 80}, + {0.4, 0.5, 70}, + {0.5, 0.6, 60}, + // 测试数据 + //{-1, 0.2, 100}, + //{0.2, 0.3, 100}, + //{0.3, 0.4, 100}, +} + +/* + 触发下限鱼必死的开炮数区间 +*/ + +var KillFishShootingNum_Chu = [][]float64{ + {1, 10, 1, 5}, + {11, 40, 5, 20}, + {41, 99, 15, 35}, + {100, 300, 40, 60}, + {301, -1, 60, 120}, +} + +var KillFishShootingNum_Zho = [][]float64{ + {1, 10, 1, 5}, + {11, 40, 5, 20}, + {41, 99, 15, 35}, + {100, 300, 40, 60}, + {301, -1, 60, 120}, +} + +var KillFishShootingNum_Gao = [][]float64{ + {1, 10, 1, 5}, + {11, 40, 5, 20}, + {41, 99, 15, 35}, + {100, 300, 40, 60}, + {301, -1, 60, 120}, +} + +/* + 触发上限恢复正常的倍数 +*/ +var RecoveryUpLimitState_Chu = 1.1 +var RecoveryUpLimitState_Zho = 1.1 +var RecoveryUpLimitState_Gao = 1.1 + +/* + 触发下限恢复正常的倍数 +*/ +var RecoverDownLimitState_Chu = 0.8 +var RecoverDownLimitState_Zho = 0.8 +var RecoverDownLimitState_Gao = 0.8 + +/* + 黑名单相关修正系数 index 对应得是 黑名单等级 +*/ +var BlackLevelCoefficient = []float64{0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.01} + +/* + 白名单相关修正系数 index 对应得是 白名单等级 +*/ +var WhiteLevelCoefficient = []float64{1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2, 2.2, 2.5, 3} + +/* + 龙王宝箱玩法相关的配置项 +*/ +var TreasureChestReward = [][]int32{ + {15, 30}, {20, 10, 15}, {10, 20, 20}, {10, 20, 20}, {25, 15, 15}, {10, 10, 30}, {30, 10, 20}, {15, 25, 20}, {10, 10, 40}, {20, 20, 20}, {15, 10, 40}, {30, 15, 20}, {10, 15, 10, 30}, {20, 10, 15, 20}, {10, 20, 40}, {20, 30, 20}, {15, 10, 25, 20}, {20, 10, 10, 30}, {20, 30, 25}, {40, 15, 20}, {15, 30, 30}, {10, 15, 30, 20}, {20, 10, 50}, + {10, 30, 40}, {25, 10, 15, 30}, {30, 20, 10, 20}, {15, 20, 50}, {30, 25, 30}, {15, 25, 25, 20}, {20, 15, 10, 40}, {20, 10, 60}, {40, 20, 30}, {10, 30, 30, 20}, {25, 15, 10, 40}, {20, 15, 60}, {40, 25, 30}, {25, 20, 30, 20}, {20, 25, 10, 40}, {50, 10, 40}, {30, 20, 50}, {20, 10, 30, 40}, {15, 25, 40, 20}, {20, 10, 10, 30, 30}, + {40, 15, 50}, {30, 25, 50}, {20, 25, 40, 20}, {15, 30, 10, 20, 30}, {30, 20, 60}, {20, 20, 30, 40}, {20, 30, 10, 50}, {10, 30, 10, 40, 20}, {40, 30, 50}, {30, 20, 30, 40}, {20, 30, 10, 40, 20}, {50, 30, 50}, {30, 20, 30, 50}, {20, 30, 20, 60}, {10, 60, 10, 30, 20}, {60, 50, 30}, {40, 20, 30, 50}, {30, 30, 20, 60}, {10, 30, 20, 20, 60}, + {50, 30, 10, 60}, {40, 30, 30, 50}, {30, 40, 20, 60}, {20, 50, 20, 40, 20}, {50, 20, 60, 30}, {40, 30, 40, 50}, {30, 50, 20, 60}, {20, 30, 50, 20, 40}, {40, 10, 30, 100}, {30, 50, 20, 80}, {50, 20, 20, 60, 30}, {20, 40, 60, 20, 40}, {30, 40, 30, 100}, {50, 50, 20, 80}, {50, 20, 20, 80, 30}, {40, 20, 80, 20, 40}, {20, 60, 40, 30, 50}, {50, 50, 20, 100}, {30, 40, 30, 20, 100}, {50, 20, 20, 100, 30}, {40, 20, 80, 20, 60}, {20, 60, 40, 30, 20, 50}, {60, 50, 20, 100}, {30, 40, 40, 20, 100}, + {50, 20, 30, 100, 30}, {40, 20, 80, 30, 60}, {20, 60, 40, 30, 30, 50}, {60, 30, 100, 50}, {30, 20, 50, 40, 100}, {40, 20, 80, 20, 80}, {50, 20, 30, 50, 50, 40}, {20, 60, 30, 40, 30, 60}, {50, 30, 100, 80}, {30, 40, 50, 40, 100}, {40, 20, 100, 20, 80}, {40, 50, 20, 30, 50, 40, 30}, {20, 60, 30, 40, 30, 20, 60}, {100, 30, 40, 100, 30}, {20, 100, 40, 20, 120}, {40, 50, 60, 30, 50, 40, 30}, {30, 60, 30, 40, 30, 30, 80}, {100, 30, 50, 100, 40}, {20, 100, 30, 120, 50}, {40, 50, 80, 30, 50, 40, 30}, + {30, 60, 40, 30, 50, 30, 80}, {150, 30, 40, 100, 30}, {60, 100, 40, 30, 120}, {40, 50, 100, 30, 80, 50}, {30, 100, 30, 50, 30, 30, 80}, {150, 30, 40, 100, 30, 150}, {60, 100, 40, 80, 120, 100}, {200, 40, 50, 100, 30, 80}, {100, 60, 80, 100, 30, 30, 100}, +} + +var TreasureChestWeight = []int32{ + 80, 60, 40, 80, 40, 40, 40, 60, 60, 40, 40, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 40, 40, + 80, 40, 40, 80, 40, 40, 40, 80, 40, 40, 40, 40, 80, 40, 40, 80, 40, 40, 20, 20, 20, 20, + 12, 12, 12, 4, 12, 4, 12, 4, 8, 4, 12, 12, 4, 8, 2, 6, 6, 2, 4, 1, 3, 2, 3, 3, 2, 3, + 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} diff --git a/gamerule/fortunezhishen/constants.go b/gamerule/fortunezhishen/constants.go new file mode 100644 index 0000000..f083e9a --- /dev/null +++ b/gamerule/fortunezhishen/constants.go @@ -0,0 +1,209 @@ +package fortunezhishen + +//房间类型 +const ( + RoomMode_Classic int = iota //经典 + RoomMode_Max +) + +//场景状态 +const ( + FortuneZhiShenStateStart int = iota //默认状态 + FortuneZhiShenStateMax +) + +//玩家操作 +const ( + FortuneZhiShenPlayerOpStart int = iota + FortuneZhiShenPlayerOpSwitch +) +const ( + Normal = iota //正常 + FreeGame //免费游戏 + StopAndRotate //停留并旋转 + StopAndRotate2 //停留并旋转(免费游戏触发的) +) +const ( + Column = 3 + Row = 5 +) +const ( + MakeAFortune int32 = iota //发财 + Wild //财神 + Firecrackers //鞭炮 + Drum //鼓 + Jade //玉牌 + Copper //铜币 + A + K + Q + J + T + Gemstone //宝石 +) +const ( + BetGrandPrize = iota //巨奖 + BetBigPrize //大奖 + BetMidPrize //中奖 + BetSmallPrize //小奖 + Bet1_4 + Bet5_8 + Bet9_12 + Bet13_16 + Bet17_19 +) +const NowByte int64 = 10000 +const ( + GrandPrize = 21 //巨奖 + BigPrize = 22 //大奖 + MidPrize = 23 //中奖 + SmallPrize = 24 //小奖 +) + +type WinLine struct { + Lines []int32 + Poss []int32 + LineId int + Rate int64 +} +type WinResult struct { + Cards []int32 //15 横着排列 + WinLine []WinLine //赢的线数 + GemstoneRate []int64 //宝石倍率 + GemstoneNum int //宝石数量 + MakeAFortuneNum int //发财数量 + MidIcon int32 //中间的大图标是什么 + LastRes []int32 //上一局的结果 + NewAddGemstone int //新增宝石数量 +} + +var LineRateNum = [][]int{ + //发财 财神 鞭炮 鼓 玉牌 铜币 A K Q J T 宝石 + {1, 0, 5, 6, 6, 5, 10, 9, 8, 7, 6, 10}, //r1 + {1, 3, 4, 5, 4, 3, 11, 8, 6, 8, 7, 9}, //r2 + {1, 3, 4, 4, 5, 6, 12, 9, 8, 9, 8, 8}, //r3 + {1, 3, 4, 5, 4, 3, 11, 8, 6, 8, 9, 7}, //r4 + {1, 3, 4, 4, 5, 5, 10, 5, 4, 3, 3, 3}, //r5 +} + +//元素对应数量的倍率 +var EleNumRate = make(map[int32]map[int]int64) + +//小宝石概率 +//累计巨奖 累计大奖 红利中奖 红利小奖 1-4 5-8 9-12 13-16 17-19 +var SmallGemstoneRatePrize = []int{3, 7, 100, 390, 3500, 2700, 1800, 1000, 500} + +//大宝石概率 +//累计巨奖 累计大奖 红利中奖 红利小奖 1-4 5-8 9-12 13-16 17-19 +var BigGemstoneRatePrize = []int{3, 7, 100, 390, 1800, 2700, 3500, 1000, 500} + +//50种条线的结果 +var LineWinNum = [][]int{ + {5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4}, + {10, 11, 12, 13, 14}, + {0, 6, 12, 8, 4}, + {10, 6, 2, 8, 14}, + {0, 1, 7, 3, 4}, + {10, 11, 7, 13, 14}, + {5, 11, 12, 13, 9}, + {5, 1, 2, 3, 9}, + {0, 6, 7, 8, 4}, + /////// + {10, 6, 7, 8, 14}, + {0, 6, 2, 8, 4}, + {10, 6, 12, 8, 14}, + {5, 1, 7, 3, 9}, + {5, 11, 7, 13, 9}, + {5, 6, 2, 8, 9}, + {5, 6, 12, 8, 9}, + {0, 11, 2, 13, 4}, + {10, 1, 12, 3, 14}, + {5, 1, 12, 3, 9}, + /////// + {5, 11, 2, 13, 9}, + {0, 1, 12, 3, 4}, + {10, 11, 2, 13, 14}, + {0, 11, 12, 13, 4}, + {10, 1, 2, 3, 14}, + {0, 11, 7, 13, 4}, + {10, 1, 7, 3, 14}, + {5, 6, 7, 8, 14}, + {0, 1, 7, 13, 14}, + {10, 11, 7, 3, 4}, + /////// + {0, 6, 7, 8, 14}, + {10, 6, 7, 8, 4}, + {0, 6, 12, 8, 14}, + {10, 6, 2, 8, 4}, + {0, 1, 2, 3, 9}, + {10, 11, 12, 13, 9}, + {0, 6, 2, 8, 14}, + {10, 6, 12, 8, 4}, + {5, 1, 7, 13, 9}, + {5, 11, 7, 3, 9}, + /////// + {5, 6, 2, 3, 4}, + {5, 6, 12, 13, 14}, + {5, 1, 2, 8, 14}, + {5, 11, 12, 8, 4}, + {5, 1, 7, 13, 14}, + {5, 11, 7, 3, 4}, + {10, 6, 2, 3, 9}, + {0, 6, 12, 13, 9}, + {0, 1, 7, 13, 9}, + {10, 11, 7, 3, 9}, +} + +func init() { + EleNumRate[MakeAFortune] = make(map[int]int64) + EleNumRate[MakeAFortune][3] = 100 + EleNumRate[MakeAFortune][4] = 750 + EleNumRate[MakeAFortune][5] = 5000 + + EleNumRate[Firecrackers] = make(map[int]int64) + EleNumRate[Firecrackers][2] = 2 + EleNumRate[Firecrackers][3] = 10 + EleNumRate[Firecrackers][4] = 50 + EleNumRate[Firecrackers][5] = 150 + + EleNumRate[Drum] = make(map[int]int64) + EleNumRate[Drum][3] = 5 + EleNumRate[Drum][4] = 30 + EleNumRate[Drum][5] = 100 + + EleNumRate[Jade] = make(map[int]int64) + EleNumRate[Jade][3] = 5 + EleNumRate[Jade][4] = 25 + EleNumRate[Jade][5] = 100 + + EleNumRate[Copper] = make(map[int]int64) + EleNumRate[Copper][3] = 5 + EleNumRate[Copper][4] = 25 + EleNumRate[Copper][5] = 100 + + EleNumRate[A] = make(map[int]int64) + EleNumRate[A][3] = 5 + EleNumRate[A][4] = 20 + EleNumRate[A][5] = 75 + + EleNumRate[K] = make(map[int]int64) + EleNumRate[K][3] = 5 + EleNumRate[K][4] = 10 + EleNumRate[K][5] = 50 + + EleNumRate[Q] = make(map[int]int64) + EleNumRate[Q][3] = 5 + EleNumRate[Q][4] = 10 + EleNumRate[Q][5] = 50 + + EleNumRate[J] = make(map[int]int64) + EleNumRate[J][3] = 5 + EleNumRate[J][4] = 10 + EleNumRate[J][5] = 50 + + EleNumRate[T] = make(map[int]int64) + EleNumRate[T][3] = 5 + EleNumRate[T][4] = 10 + EleNumRate[T][5] = 50 +} diff --git a/gamerule/fortunezhishen/f_test.go b/gamerule/fortunezhishen/f_test.go new file mode 100644 index 0000000..f13c7cd --- /dev/null +++ b/gamerule/fortunezhishen/f_test.go @@ -0,0 +1,50 @@ +package fortunezhishen + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" +) + +func TestResult(t *testing.T) { + + //a :=,{]int{ + // A, A, Gemstone, Gemstone, K, + // Gemstone, Jade, A, K, A, + // Jade, Q, K, A, Jade, + //} + eleLineAppearRate := []int32{1, 568, 568, 568, 568, 568, 909, 1136, 1136, 1136, 1136, 1, 568} + fmt.Println(eleLineAppearRate) + + rand.Seed(time.Now().UnixNano()) + slotRateWeight := []int32{17000, 17000, 3000, 1500, 1000, 800, 700, 600, 500, 400, 200, 100, 80, 60, 40, 35, 30, 25, 20, 18, 16, 14, 12, 10, 9, 8, 7, 6, 5, 4, 3, 2} + levelRate := [][]int32{{0, 0}, {1, 50}, {51, 80}, {81, 120}, {121, 150}, {151, 180}, {181, 210}, + {211, 240}, {241, 270}, {271, 300}, {301, 350}, {351, 400}, {401, 450}, {451, 500}, {501, 600}, {601, 700}, {701, 800}, + {801, 900}, {901, 1000}, {1001, 1500}, {1501, 2000}, {2001, 2500}, {2501, 3000}, {3001, 3500}, {3501, 4000}, + {4001, 4500}, {4501, 5000}, {5001, 6000}, {6001, 7000}, {7001, 8000}, {8001, 9000}, {9001, 10000}} + + //GetLineEleVal(0, 1, 500000, eleLineAppearRate) + + fileName := fmt.Sprintf("fortunezhishen-%v-%d.csv", 0, 0) + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + defer file.Close() + if err != nil { + file, err = os.Create(fileName) + if err != nil { + return + } + } + for i := 0; i < 100000; i++ { + rIdx := RandSliceInt32IndexByWightN(slotRateWeight) + parsec := levelRate[rIdx] + var needRate int32 + if parsec[0] != 0 && parsec[1] != 0 { + needRate = rand.Int31n(parsec[1]-parsec[0]) + parsec[0] + } + str := fmt.Sprintf("%v\r\n", needRate) + file.WriteString(str) + } + +} diff --git a/gamerule/fortunezhishen/fortunezhishenAlgorithm.go b/gamerule/fortunezhishen/fortunezhishenAlgorithm.go new file mode 100644 index 0000000..5352622 --- /dev/null +++ b/gamerule/fortunezhishen/fortunezhishenAlgorithm.go @@ -0,0 +1,242 @@ +package fortunezhishen + +import ( + "math/rand" +) + +func (wls *WinResult) Init(gameState int) { + wls.Cards = make([]int32, 15) + wls.WinLine = nil + wls.MakeAFortuneNum = 0 + wls.GemstoneNum = 0 + wls.NewAddGemstone = 0 + if wls.GemstoneRate == nil || (gameState != StopAndRotate && gameState != StopAndRotate2) { + wls.MidIcon = -1 + wls.GemstoneRate = make([]int64, 15) + wls.LastRes = nil + } +} + +func (wls *WinResult) CreateLine(gameState int, eleLineAppearRate []int32) { + wls.Init(gameState) + //生成元素 + wls.result(gameState, eleLineAppearRate) + +} +func (wls *WinResult) Win(betIdx int, gameState int) { + if gameState == StopAndRotate || gameState == StopAndRotate2 { + wls.copyRes() + } + if gameState != StopAndRotate && gameState != StopAndRotate2 { + //生成输赢结果 + wls.getWinLine() + } + //查询宝石 + wls.initData(betIdx) +} + +func (wls *WinResult) copyRes() { + if len(wls.LastRes) > 0 { + for k, v := range wls.LastRes { + if v == Gemstone { + wls.Cards[k] = v + } + } + } +} +func (wls *WinResult) initData(betIdx int) { + var mid = []int{1, 2, 3, 6, 7, 8, 11, 12, 13} + if wls.MidIcon == Gemstone { + rate := wls.GetGemstoneRate(betIdx, true) + for _, id := range mid { + if wls.GemstoneRate[id] == 0 { + wls.GemstoneRate[id] = rate + } else { + break + } + } + } + for k, v := range wls.Cards { + if v == Gemstone { + wls.GemstoneNum++ + if wls.GemstoneRate[k] == 0 { + wls.NewAddGemstone++ + wls.GemstoneRate[k] = wls.GetGemstoneRate(betIdx, false) + } + } else if v == MakeAFortune { + wls.MakeAFortuneNum++ + } + } +} + +//根据概率 获取宝石倍数 +func (wls *WinResult) GetGemstoneRate(betIdx int, big bool) int64 { + grandPrize, bigPrize := true, true + if betIdx == 0 || betIdx == 1 || betIdx == 3 { + bigPrize = false + } + if betIdx == 0 || betIdx == 1 || betIdx == 2 || betIdx == 3 { + grandPrize = false + } + + nowGemstoneRatePrize := make([]int, len(SmallGemstoneRatePrize)) + if !big { + copy(nowGemstoneRatePrize, SmallGemstoneRatePrize) + } else { + copy(nowGemstoneRatePrize, BigGemstoneRatePrize) + bigPrize = true + } + if !grandPrize { + nowGemstoneRatePrize[0] = 0 + } + if !bigPrize { + nowGemstoneRatePrize[1] = 0 + } + + r := randSliceIndexByWightN(nowGemstoneRatePrize) + rate := rand.Int63n(4) + switch r { + case Bet1_4: + rate += 1 + case Bet5_8: + rate += 5 + case Bet9_12: + rate += 9 + case Bet13_16: + rate += 13 + case Bet17_19: + rate = rand.Int63n(3) + 17 + case BetGrandPrize: + rate = GrandPrize + case BetBigPrize: + rate = BigPrize + case BetMidPrize: + rate = MidPrize + case BetSmallPrize: + rate = SmallPrize + } + return rate +} + +//获取元素值 +func (wls *WinResult) result(gameState int, eleLineAppearRate []int32) { + var LineRate []int32 //正常元素概率(小宝石) 或者 正常元素概率2(大宝石) + var noWild []int32 //财神元素概率为0(第一列使用) + + eleLen := len(eleLineAppearRate) + if gameState == Normal || gameState == StopAndRotate { + LineRate = make([]int32, eleLen-1) + copy(LineRate, eleLineAppearRate[:eleLen-1]) + } else { + LineRate = make([]int32, eleLen-2) + copy(LineRate, eleLineAppearRate[:eleLen-2]) + LineRate = append(LineRate, eleLineAppearRate[eleLen-1]) + } + //财神元素概率为0 + noWild = make([]int32, eleLen-1) + copy(noWild, LineRate) + noWild[Wild] = 0 + //没有发财 + noMakeAFortune := make([]int32, len(LineRate)) + copy(noMakeAFortune, LineRate) + noMakeAFortune[MakeAFortune] = 0 + + //fmt.Println("元素概率 ", LineRate) + //fmt.Println("财神元素概率为0 ", noWild) + + n := 0 + var haveMakeAFortune = make([]int, 5) + for i := 0; i < Column; i++ { + for j := 0; j < Row; j++ { + if haveMakeAFortune[j] == 0 { + if j == 0 { + wls.Cards[n] = RandSliceInt32IndexByWightN(noWild) + } else { + wls.Cards[n] = RandSliceInt32IndexByWightN(LineRate) + } + if wls.Cards[n] == MakeAFortune { + haveMakeAFortune[j] = 1 + } + } else { + if j == 0 { + wls.Cards[n] = RandSliceInt32IndexByWightN(noWild) + } else { + wls.Cards[n] = RandSliceInt32IndexByWightN(noMakeAFortune) + } + } + n++ + } + } + if gameState == FreeGame { + r := RandSliceInt32IndexByWightN(LineRate) + wls.MidIcon = r + var mid = []int{1, 2, 3, 6, 7, 8, 11, 12, 13} + for _, key := range mid { + wls.Cards[key] = wls.MidIcon + } + } +} + +//获取结果 +func (wls *WinResult) getWinLine() { + n := 0 + var flag int32 = -1 + for k, cols := range LineWinNum { + flag = wls.Cards[cols[0]] + //宝石 发财不参与线数 + if flag == Gemstone || flag == MakeAFortune { + continue + } + var line []int32 + var pos []int32 + for _, key := range cols { + if flag == wls.Cards[key] || (Wild == wls.Cards[key] && k != 0) { + n++ + line = append(line, wls.Cards[key]) + pos = append(pos, int32(key)) + } else { + if n >= 3 || (flag == Firecrackers && n >= 2) { + wls.WinLine = append(wls.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(flag, len(line)), + }) + } + n = 0 + pos = nil + line = nil + break + } + if n == 5 { + wls.WinLine = append(wls.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(flag, len(line)), + }) + n = 0 + pos = nil + line = nil + } + } + } + //test code + //if len(wls.WinLine) > 0 { + // for k, v := range wls.WinLine { + // fmt.Println("=============") + // fmt.Print(k, " ") + // PrintWin(v.Lines) + // fmt.Println(k, "位置 ", v.Poss, " 中奖线号:", v.LineId) + // } + // fmt.Println("=============") + //} +} +func GetRate(ele int32, num int) int64 { + if data, ok := EleNumRate[ele]; ok { + if r, ok2 := data[num]; ok2 { + return r + } + } + return 0 +} diff --git a/gamerule/fortunezhishen/func.go b/gamerule/fortunezhishen/func.go new file mode 100644 index 0000000..26d991e --- /dev/null +++ b/gamerule/fortunezhishen/func.go @@ -0,0 +1,163 @@ +package fortunezhishen + +import ( + "fmt" + "math/rand" + "strconv" +) + +func GetLineEleVal(betIdx, gameState int, needRate int64, eleLineAppearRate []int32) (WinResult, []int, [][]int32) { + var preInt [][]int32 + for i := 0; i < 1000; i++ { + var wls WinResult + wls.CreateLine(gameState, eleLineAppearRate) + wls.Win(betIdx, gameState) + var rate int64 + for _, v := range wls.WinLine { + rate += v.Rate + } + //fmt.Printf("%v || rate %v", wls.Cards, rate) + //fmt.Println() + var n int64 = 30 + if gameState == FreeGame { + n = 50 + } + if rate >= needRate-10 && rate <= needRate+n && wls.GemstoneNum < 6 && wls.MakeAFortuneNum < 3 { + var poss []int32 + for _, v := range wls.WinLine { + poss = append(poss, v.Poss...) + } + var noPoss []int + for k, v := range wls.Cards { + isF := false + for _, pn := range poss { + if k == int(pn) { + isF = true + break + } + } + if !isF && v != MakeAFortune && v != Gemstone { + noPoss = append(noPoss, k) + } + } + //fmt.Println("...........find rate: ", rate, " 第 ", i+1, " 次.") + return wls, noPoss, nil + } + if rate < 50 && len(preInt) < 10 && wls.GemstoneNum < 6 && wls.MakeAFortuneNum < 3 { + preInt = append(preInt, wls.Cards) + } + } + return WinResult{}, nil, preInt +} +func FindInxInArray(a int, b []int) bool { + for _, v := range b { + if a == v { + return true + } + } + return false +} +func RandSliceInt32IndexByWightN(s1 []int32) int32 { + total := 0 + for _, v := range s1 { + total += int(v) + } + if total <= 0 { + return 0 + } + random := rand.Intn(total) + total = 0 + for i, v := range s1 { + total += int(v) + if random < total { + return int32(i) + } + } + return 0 +} +func randSliceIndexByWightN(s1 []int) int { + total := 0 + for _, v := range s1 { + total += v + } + if total <= 0 { + return 0 + } + random := rand.Intn(total) + total = 0 + for i, v := range s1 { + total += v + if random < total { + return i + } + } + return 0 +} +func PrintWin(lines []int32) { + str := "" + for _, ele := range lines { + switch ele { + case MakeAFortune: + str += "发财," + case Wild: + str += "财神," + case Firecrackers: + str += "鞭炮," + case Drum: + str += "鼓 ," + case Jade: + str += "玉牌," + case Copper: + str += "铜币," + case A: + str += "A ," + case K: + str += "K ," + case Q: + str += "Q ," + case J: + str += "J ," + case T: + str += "T ," + case Gemstone: + str += "宝石," + } + } + fmt.Println(str) +} +func Print(res []int32) { + fmt.Println(res) + str := "" + for k, ele := range res { + switch ele { + case MakeAFortune: + str += "发财," + case Wild: + str += "财神," + case Firecrackers: + str += "鞭炮," + case Drum: + str += "鼓 ," + case Jade: + str += "玉牌," + case Copper: + str += "铜币," + case A: + str += "A ," + case K: + str += "K ," + case Q: + str += "Q ," + case J: + str += "J ," + case T: + str += "T ," + case Gemstone: + str += "宝石," + } + if (k+1)%5 == 0 { + fmt.Println("第", strconv.Itoa((k+1)/5), "行 ", str) + str = "" + } + } +} diff --git a/gamerule/fruits/1_test.go b/gamerule/fruits/1_test.go new file mode 100644 index 0000000..b362137 --- /dev/null +++ b/gamerule/fruits/1_test.go @@ -0,0 +1,71 @@ +package fruits + +import ( + "fmt" + "testing" +) + +func TestName(t *testing.T) { + eleLineAppearRate := [][]int32{ + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + {926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481}, + } + // + //fileName := fmt.Sprintf("classic888-%v-%d.csv", 0, 0) + //file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + //defer file.Close() + //if err != nil { + // file, err = os.Create(fileName) + // if err != nil { + // return + // } + //} + //file.WriteString("随机倍率\n") + //for i := 0; i < 100000; i++ { + // var wls WinResult + // wls.CreateLine(eleLineAppearRate) + // wls.Win() + // var rate int64 + // for _, v := range wls.WinLine { + // rate += v.Rate + // } + // str := fmt.Sprintf("%v\r\n", rate) + // file.WriteString(str) + //} + + var wls WinResult + wls.CreateLine(eleLineAppearRate, false) + //wls.EleValue[0] = Wild + //wls.EleValue[1] = Scatter + //wls.EleValue[2] = Bonus + //wls.EleValue[3] = Bonus + //wls.EleValue[4] = Bonus + wls.EleValue = []int32{0, 0, 0, 0, 0, 9, 10, 9, 1, 8, 8, 9, 10, 6, 3} + Print(wls.EleValue) + wls.Win() + fmt.Println(len(wls.WinLine)) + for _, wl := range wls.WinLine { + flag := wl.Lines[0] + var NowWildNum int + var MaxLinXu int + for _, n := range wl.Lines { + if flag != n && flag == Wild { + flag = n + } + if n == Wild { + NowWildNum++ + if NowWildNum > MaxLinXu { + MaxLinXu = NowWildNum + } + } else { + NowWildNum = 0 + } + } + fmt.Println("MaxLinXu", MaxLinXu) + } + +} diff --git a/gamerule/fruits/constants.go b/gamerule/fruits/constants.go new file mode 100644 index 0000000..e8ee478 --- /dev/null +++ b/gamerule/fruits/constants.go @@ -0,0 +1,175 @@ +package fruits + +// 房间类型 +const ( + RoomMode_Classic int = iota //经典 + RoomMode_Max +) + +// 场景状态 +const ( + FruitsStateStart int = iota //默认状态 + FruitsStateMax +) + +// 玩家操作 +const ( + FruitsPlayerOpStart int = iota + FruitsPlayerOpSwitch +) +const NowByte int64 = 10000 +const ( + Normal = iota //正常 + FreeGame //免费游戏 + MaryGame //玛丽游戏 +) +const ( + Column = 3 + Row = 5 +) +const ( + Wild int32 = iota //0 + Bonus //1 + Scatter //2 + Bar //3 + Cherry //4.樱桃 + Bell //5.铃铛 + Pineapple //6.菠萝 + Grape //7.葡萄 + Lemon //8.柠檬 + Watermelon //9.西瓜 + Banana //10.香蕉 + Apple //11.苹果 + Bomb //12.炸弹 + EleMax +) + +//0|0|0|0|1|0|1|1|1|1|1|1|1 + +//炸弹 香蕉 樱桃 西瓜 葡萄 菠萝 柠檬 苹果 + +//炸弹 香蕉 樱桃 西瓜 葡萄 菠萝 +//炸弹 西瓜 香蕉 苹果 樱桃 柠檬 +//炸弹 香蕉 菠萝 苹果 柠檬 葡萄 +//炸弹 西瓜 菠萝 樱桃 柠檬 葡萄 + +var MaryEleArray = []int32{ + Bomb, Banana, Cherry, Watermelon, Grape, Pineapple, + Bomb, Watermelon, Banana, Apple, Cherry, Lemon, + Bomb, Banana, Pineapple, Apple, Lemon, Grape, + Bomb, Watermelon, Pineapple, Cherry, Lemon, Grape, +} + +// 玛丽外围元素概率 +var MaryEleRate = []int32{ + 550, 500, 400, 100, 200, 700, + 550, 100, 500, 600, 400, 300, + 550, 500, 700, 600, 300, 200, + 550, 100, 700, 400, 300, 200, +} + +// 玛丽中间元素概率 +var MaryMidEleRate = []int32{0, 0, 0, 0, 400, 0, 700, 200, 300, 100, 500, 600, 0} + +// 元素对应数量的倍率 +var EleNumRate = make(map[int32]map[int]int64) + +// 9条线 +var LineWinNum = [][]int{ + {0, 1, 2, 3, 4}, + {5, 6, 7, 8, 9}, + {10, 11, 12, 13, 14}, + {0, 6, 12, 8, 4}, + {10, 6, 2, 8, 14}, + {0, 1, 7, 13, 14}, + {10, 11, 7, 3, 4}, + {5, 1, 7, 13, 9}, + {5, 11, 7, 3, 9}, +} + +type WinLine struct { + Lines []int32 + Poss []int32 + LineId int + Rate int64 +} +type WinResult struct { + EleValue []int32 + WinLine []WinLine //赢的线数 + //玛丽游戏 + MaryOutSide int32 //外围索引 + MaryMidArray []int32 //中间数组 + MaryOutRate int64 + MaryMidRate int64 + MaryLianXu int32 //中间n连续 +} + +var EleWeight = [][]int32{ + {25, 100, 200, 170, 210, 255, 602, 1385, 1680, 3865, 10500}, + {3365, 1273, 1000, 625, 685, 975, 1490, 4310, 9755, 10500}, + {5766, 1050, 1050, 730, 815, 860, 1255, 1750, 3500, 8420, 9510}, + {175, 1601, 1150, 645, 775, 820, 1460, 1680, 4050, 6000, 7850}, + {150, 1498, 1168, 650, 688, 699, 8731, 1100, 2150, 2240, 2811}, + + {720, 495, 949, 780, 870, 945, 1170, 3150, 5715, 3210, 3270}, + {5300, 564, 730, 1100, 1200, 1215, 3210, 3040, 3530, 2700, 3970}, + {7800, 0, 0, 800, 880, 935, 2800, 5000, 4800, 6080, 7200}, + {930, 1070, 1273, 1210, 1885, 2550, 4330, 5090, 5600, 9135, 9999}, + {968, 1119, 1000, 1630, 2345, 3050, 3475, 3800, 3600, 4180, 5000}, + + {0, 0, 0, 0, 709, 0, 563, 6316, 5204, 9945, 1568, 1064, 88}, + {0, 0, 0, 0, 673, 0, 539, 6317, 5296, 9958, 537, 1032}, +} + +func init() { + EleNumRate[Bonus] = make(map[int]int64) + EleNumRate[Bonus][3] = 25 + EleNumRate[Bonus][4] = 50 + EleNumRate[Bonus][5] = 400 + + EleNumRate[Scatter] = make(map[int]int64) + EleNumRate[Scatter][3] = 100 + EleNumRate[Scatter][4] = 200 + EleNumRate[Scatter][5] = 1750 + + EleNumRate[Bar] = make(map[int]int64) + EleNumRate[Bar][3] = 75 + EleNumRate[Bar][4] = 175 + EleNumRate[Bar][5] = 1250 + + EleNumRate[Cherry] = make(map[int]int64) + EleNumRate[Cherry][3] = 45 + EleNumRate[Cherry][4] = 100 + EleNumRate[Cherry][5] = 800 + + EleNumRate[Bell] = make(map[int]int64) + EleNumRate[Bell][3] = 35 + EleNumRate[Bell][4] = 80 + EleNumRate[Bell][5] = 650 + + EleNumRate[Pineapple] = make(map[int]int64) + EleNumRate[Pineapple][3] = 30 + EleNumRate[Pineapple][4] = 70 + EleNumRate[Pineapple][5] = 550 + + EleNumRate[Grape] = make(map[int]int64) + EleNumRate[Grape][3] = 25 + EleNumRate[Grape][4] = 50 + EleNumRate[Grape][5] = 400 + + EleNumRate[Lemon] = make(map[int]int64) + EleNumRate[Lemon][3] = 15 + EleNumRate[Lemon][4] = 40 + EleNumRate[Lemon][5] = 250 + + EleNumRate[Watermelon] = make(map[int]int64) + EleNumRate[Watermelon][3] = 3 + EleNumRate[Watermelon][4] = 10 + EleNumRate[Watermelon][5] = 85 + + EleNumRate[Banana] = make(map[int]int64) + EleNumRate[Banana][2] = 1 + EleNumRate[Banana][3] = 3 + EleNumRate[Banana][4] = 10 + EleNumRate[Banana][5] = 75 +} diff --git a/gamerule/fruits/fruitsAlgorithm.go b/gamerule/fruits/fruitsAlgorithm.go new file mode 100644 index 0000000..ec8a115 --- /dev/null +++ b/gamerule/fruits/fruitsAlgorithm.go @@ -0,0 +1,359 @@ +package fruits + +func (w *WinResult) Init() { + w.EleValue = make([]int32, 15) + w.WinLine = nil +} + +// 玛丽游戏 +func (w *WinResult) InitMary() { + w.MaryOutSide = -1 + w.MaryMidArray = nil + w.MaryOutRate = 0 + w.MaryMidRate = 0 + w.MaryLianXu = 0 +} + +// 正常游戏 免费游戏 +func (w *WinResult) CreateLine(ele [][]int32, free bool) { + w.Init() + w.result(ele, free) + //Print(w.EleValue) +} +func (w *WinResult) Win() { + w.getWinLine() +} + +// 玛丽游戏 +func (w *WinResult) CreateMary(maryGame [][]int32) { + w.InitMary() + w.MaryOutSide = RandSliceInt32IndexByWightN(maryGame[0]) + for i := 0; i < 4; i++ { + ele := RandSliceInt32IndexByWightN(maryGame[1]) + w.MaryMidArray = append(w.MaryMidArray, ele) + } + //fmt.Println("外圈元素", w.MaryOutSide) + //fmt.Println("内圈元素", w.MaryMidArray) + w.MaryWin() +} +func (w *WinResult) MaryWin() { + var outRate int64 + switch w.MaryOutSide { + case Watermelon: + outRate += 200 + case Grape: + outRate += 100 + case Lemon: + outRate += 70 + case Cherry: + outRate += 50 + case Banana: + outRate += 20 + case Apple: + outRate += 10 + case Pineapple: + outRate += 5 + } + var flag = w.MaryMidArray[0] + var n int32 + for _, v := range w.MaryMidArray { + if flag != v { + break + } + n++ + } + for _, v := range w.MaryMidArray { + if w.MaryOutSide == v { + w.MaryOutRate = outRate + break + } + } + if n >= 3 { + if n == 3 { + w.MaryMidRate = 20 + } else if n == 4 { + w.MaryMidRate = 500 + } + w.MaryLianXu = n + } + //fmt.Println("外圈倍率:", w.MaryOutRate) + //fmt.Println("内圈倍率", w.MaryMidRate) +} + +func (w *WinResult) result(ele [][]int32, free bool) { + sl1 := make(map[int]bool) + sl2 := make(map[int]bool) + sl3 := make(map[int]bool) + n := 0 + for i := 0; i < Column; i++ { + for j := 0; j < Row; j++ { + val := RandSliceInt32IndexByWightN(ele[j]) + if val == Scatter { + if !sl1[j] { + sl1[j] = true + } else { + noScatter := make([]int32, len(ele[j])) + copy(noScatter, ele[j]) + noScatter[Scatter] = 0 + noScatter[Bonus] = 0 + noScatter[Wild] = 0 + val = RandSliceInt32IndexByWightN(noScatter) + } + } else if val == Bonus { + if !sl2[j] { + sl2[j] = true + } else { + noBonus := make([]int32, len(ele[j])) + copy(noBonus, ele[j]) + noBonus[Scatter] = 0 + noBonus[Bonus] = 0 + noBonus[Wild] = 0 + val = RandSliceInt32IndexByWightN(noBonus) + } + } else if val == Wild { + if !sl3[j] { + sl3[j] = true + } else { + noWild := make([]int32, len(ele[j])) + copy(noWild, ele[j]) + noWild[Scatter] = 0 + noWild[Bonus] = 0 + noWild[Wild] = 0 + val = RandSliceInt32IndexByWightN(noWild) + } + } + w.EleValue[n] = val + n++ + } + } + + if free { + //免费 不中 玛丽游戏和奖池 + eleVal := make([]int32, len(w.EleValue)) + copy(eleVal, w.EleValue) + var wls WinResult + wls.Init() + wls.EleValue = eleVal + wls.Win() + for _, v := range wls.WinLine { + flag := v.Lines[0] + if flag == Scatter { + noScatter := make([]int32, len(ele[2])) + copy(noScatter, ele[2]) + noScatter[Wild] = 0 + noScatter[Scatter] = 0 + vv := RandSliceInt32IndexByWightN(noScatter) + w.EleValue[v.Poss[2]] = vv + } else { + wildNum := 0 + for _, el := range v.Lines { + if el == Wild { + wildNum++ + } else { + wildNum = 0 + } + } + if wildNum >= 3 { + noWild := make([]int32, len(ele[2])) + copy(noWild, ele[2]) + noWild[Wild] = 0 + noWild[Scatter] = 0 + w.EleValue[v.Poss[2]] = RandSliceInt32IndexByWightN(noWild) + } + } + } + } +} + +func (w *WinResult) getWinLine() { + n := 0 + var flag int32 = -1 + for k, cols := range LineWinNum { + flag = w.EleValue[cols[0]] + //Bonus Scatter 不参与线数 Bonus下班单独计算 + if flag == Bonus || flag == Scatter { + continue + } + var line []int32 + var pos []int32 + for _, key := range cols { + //不计算 Bonus + if (flag == w.EleValue[key] || Wild == w.EleValue[key] || flag == Wild) && w.EleValue[key] != Bonus && + w.EleValue[key] != Scatter { + if Wild != w.EleValue[key] { + flag = w.EleValue[key] + } + n++ + line = append(line, w.EleValue[key]) + pos = append(pos, int32(key)) + } else { + if n >= 3 || (flag == Banana && n >= 2) { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(flag, n), + }) + } + n = 0 + pos = nil + line = nil + break + } + if n == 5 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(flag, n), + }) + n = 0 + pos = nil + line = nil + } + } + } + + w.getBonusAndScatter() + + //test code + //if len(w.WinLine) > 0 { + // fmt.Println("====== 赢的总线数 =======", len(w.WinLine)) + // for k, v := range w.WinLine { + // fmt.Print(k+1, " ") + // PrintWin(v.Lines) + // fmt.Println(k+1, "位置 ", v.Poss, " 中奖线号:", v.LineId, " 线元素:", v.Lines, " 倍率:", v.Rate) + // } + //} +} +func (w *WinResult) getBonusAndScatter() { + //只计算Bonus和Scatter + for k, cols := range LineWinNum { + var n int + var line []int32 + var pos []int32 + for l, key := range cols { + if w.EleValue[key] == Bonus { + n++ + line = append(line, w.EleValue[key]) + pos = append(pos, int32(key)) + } else { + if n >= 3 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Bonus, n), + }) + } + n = 0 + pos = nil + line = nil + continue + } + if l == 4 { + if n >= 3 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Bonus, n), + }) + n = 0 + pos = nil + line = nil + } + } + } + n = 0 + pos = nil + line = nil + for l, key := range cols { + if w.EleValue[key] == Scatter { + n++ + line = append(line, w.EleValue[key]) + pos = append(pos, int32(key)) + } else { + if n >= 3 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Scatter, n), + }) + } + n = 0 + pos = nil + line = nil + continue + } + if l == 4 { + if n >= 3 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Scatter, n), + }) + n = 0 + pos = nil + line = nil + } + } + } + n = 0 + pos = nil + line = nil + for l, key := range cols { + if w.EleValue[key] == Wild { + n++ + line = append(line, w.EleValue[key]) + pos = append(pos, int32(key)) + } else { + if n >= 3 { + isHave := false + for _, i2 := range w.WinLine { + if i2.LineId == k+1 { + isHave = true + break + } + } + if !isHave { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Wild, n), + }) + } + } + n = 0 + pos = nil + line = nil + continue + } + if l == 4 { + if n >= 3 { + isHave := false + for _, i2 := range w.WinLine { + if i2.LineId == k+1 { + isHave = true + break + } + } + if !isHave { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: k + 1, + Rate: GetRate(Wild, n), + }) + } + n = 0 + pos = nil + line = nil + } + } + } + } +} diff --git a/gamerule/fruits/func.go b/gamerule/fruits/func.go new file mode 100644 index 0000000..ca48b40 --- /dev/null +++ b/gamerule/fruits/func.go @@ -0,0 +1,215 @@ +package fruits + +import ( + "fmt" + "math/rand" + "strconv" +) + +func GetLineEleVal(gameState int, needRate int64, eleLineAppearRate [][]int32, isLow bool) (WinResult, []int, [][]int32) { + var preInt [][]int32 + for i := 0; i < 1000; i++ { + var wls WinResult + wls.CreateLine(eleLineAppearRate, false) + wls.Win() + var rate int64 + var bonusNum int + var wildNum int + for _, v := range wls.WinLine { + if len(v.Lines) == 0 { + continue + } + rate += v.Rate + if v.Lines[0] == Bonus { + bonusNum += len(v.Lines) + } else if v.Lines[0] == Wild { + wildNum += len(v.Lines) + } + NowWildNum := 0 + for _, l := range v.Lines { + if l != Wild && NowWildNum > 0 { + if NowWildNum < 3 { + NowWildNum = 0 + } + } else if l == Wild { + NowWildNum++ + } + } + if NowWildNum >= 3 { + wildNum += NowWildNum + } + } + + //fmt.Printf("%v || rate %v", wls.EleValue, rate) + //fmt.Println() + var n int64 = 5 + if gameState == FreeGame { + n = 50 + } + if wildNum >= 3 || bonusNum >= 3 { + continue + } + if isLow { + continue + } + if rate >= needRate-n && rate <= needRate+n { + var poss []int32 + for _, v := range wls.WinLine { + poss = append(poss, v.Poss...) + } + var noPoss []int + for k := range wls.EleValue { + isF := false + for _, pn := range poss { + if k == int(pn) { + isF = true + break + } + } + if !isF { + noPoss = append(noPoss, k) + } + } + //fmt.Println("...........find rate: ", rate, " 第 ", i+1, " 次.") + return wls, noPoss, nil + } + if rate != 0 && rate < 50 && len(preInt) < 10 { + preInt = append(preInt, wls.EleValue) + } + } + return WinResult{}, nil, preInt +} +func GetLinePos(lineId int) []int { + if lineId <= 9 || lineId >= 1 { + return LineWinNum[lineId-1] + } + return nil +} +func GetRate(ele int32, num int) int64 { + if data, ok := EleNumRate[ele]; ok { + if r, ok2 := data[num]; ok2 { + return r + } + } + return 0 +} +func RandSliceInt32IndexByWightN(s1 []int32) int32 { + total := 0 + for _, v := range s1 { + total += int(v) + } + if total <= 0 { + return 0 + } + random := rand.Intn(total) + total = 0 + for i, v := range s1 { + total += int(v) + if random < total { + return int32(i) + } + } + return 0 +} +func PrintFruit(idx int32) (str string) { + switch idx { + case Wild: + str += "Wild" + case Bonus: + str += "Bonus" + case Scatter: + str += "SCATTER" + case Bar: + str += "Bar" + case Cherry: + str += "樱桃" + case Bell: + str += "铃铛" + case Pineapple: + str += "菠萝" + case Grape: + str += "葡萄" + case Lemon: + str += "柠檬" + case Watermelon: + str += "西瓜" + case Banana: + str += "香蕉" + case Apple: + str += "苹果" + case Bomb: + str += "炸弹" + } + return str +} + +func Print(res []int32) { + fmt.Println(res, len(res)) + str := "" + for k, ele := range res { + switch ele { + case Wild: + str += "Wild," + case Bonus: + str += "Bonus," + case Scatter: + str += "Scatter," + case Bar: + str += "Bar," + case Cherry: + str += "樱桃," + case Bell: + str += "铃铛," + case Pineapple: + str += "菠萝," + case Grape: + str += "葡萄," + case Lemon: + str += "柠檬," + case Watermelon: + str += "西瓜," + case Apple: + str += "苹果," + case Banana: + str += "香蕉," + } + if (k+1)%5 == 0 { + fmt.Println("第", strconv.Itoa((k+1)/5), "行 ", str) + str = "" + } + } +} +func PrintWin(lines []int32) { + str := "" + for _, ele := range lines { + switch ele { + case Wild: + str += "Wild," + case Bonus: + str += "Bonus," + case Scatter: + str += "Scatter," + case Bar: + str += "Bar," + case Cherry: + str += "樱桃," + case Bell: + str += "铃铛," + case Pineapple: + str += "菠萝," + case Grape: + str += "葡萄," + case Lemon: + str += "柠檬," + case Watermelon: + str += "西瓜," + case Banana: + str += "香蕉," + case Apple: + str += "苹果," + case Bomb: + str += "炸弹," + } + } + fmt.Println(str) +} diff --git a/gamerule/hundredyxx/constants.go b/gamerule/hundredyxx/constants.go new file mode 100644 index 0000000..9e6380a --- /dev/null +++ b/gamerule/hundredyxx/constants.go @@ -0,0 +1,94 @@ +package hundredyxx + +import ( + "time" +) + +const DICE_NUM int = 3 //骰子数量 +const MAX_RATE = 1 + +//骰子 +const ( + PointKind_Yu int = iota //鱼(红) + PointKind_Ji //鸡(红) + PointKind_Xia //虾(绿) + PointKind_Xie //蟹(绿) + PointKind_HuLu //葫芦(蓝) + PointKind_Lu //鹿(蓝) + PointKind_Max +) + +//下注区域 +const ( + BetField_Single_1 int = iota + BetField_Single_2 + BetField_Single_3 + BetField_Single_4 + BetField_Single_5 + BetField_Single_6 + BetField_Double_7 + BetField_Double_8 + BetField_Double_9 + BetField_Double_10 + BetField_Double_11 + BetField_Double_12 + BetField_Double_13 + BetField_Double_14 + BetField_Double_15 + BetField_Double_16 + BetField_Double_17 + BetField_Double_18 + BetField_Double_19 + BetField_Double_20 + BetField_Double_21 + BetField_MAX +) + +//////////////////////////////////////////////////////////////////////////////// +//百人鱼虾蟹 +//////////////////////////////////////////////////////////////////////////////// +const ( + HundredYXXSendCardTimeout = time.Second * 3 //摇骰子 + HundredYXXStakeTimeout = time.Second * 17 //押注 + HundredYXXOpenCardTimeout = time.Second * 5 //开奖 + HundredYXXBilledTimeout = time.Second * 7 //结算 + HundredYXXSendBetTime = time.Second * 1 //发送下注数据时间间隔 +) + +//场景状态 +const ( + HundredYXXSceneStateSendCard int = iota //摇骰子 + HundredYXXSceneStateStake //下注 + HundredYXXSceneStateOpenCard //开奖 + HundredYXXSceneStateBilled //结算 + HundredYXXSceneStateMax +) + +//玩家操作 +const ( + HundredYXXPlayerOpBet int = iota //下注 + HundredYXXPlayerOpUpBanker //上庄 + HundredYXXPlayerOpDwonBanker //下庄 + HundredYXXPlayerOpUpList //上庄列表 + HundredYXXTrend //走势 + HundredYXXPlayerList //玩家列表 + HundredYXXPlayerOpNowDwonBanker //在庄的下庄 +) + +var BetFieldWinPoint = [][]int{{PointKind_Yu}, {PointKind_Xia}, {PointKind_HuLu}, + {PointKind_Lu}, {PointKind_Xie}, {PointKind_Ji}, + {PointKind_Yu, PointKind_Xie}, {PointKind_Lu, PointKind_Xia}, {PointKind_Yu, PointKind_HuLu}, + {PointKind_Lu, PointKind_Ji}, {PointKind_Xie, PointKind_HuLu}, {PointKind_Ji, PointKind_Xia}, + {PointKind_Yu, PointKind_Ji}, {PointKind_Lu, PointKind_HuLu}, {PointKind_Yu, PointKind_Xia}, + {PointKind_Xia, PointKind_HuLu}, {PointKind_Yu, PointKind_Lu}, {PointKind_Xie, PointKind_Xia}, + {PointKind_HuLu, PointKind_Ji}, {PointKind_Xie, PointKind_Lu}, {PointKind_Ji, PointKind_Xie}, +} + +//var BetFieldMultiple = []int{1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5} +var WinKindStr = []string{"鱼", "虾", "葫芦", "鹿", "蟹", "鸡", + "鱼和蟹", "鹿和虾", "鱼和葫芦", + "鹿和鸡", "蟹和葫芦", "鸡和虾", + "鱼和鸡", "鹿和葫芦", "鱼和虾", + "虾和葫芦", "鱼和鹿", "蟹和虾", + "葫芦和鸡", "蟹和鹿", "鸡和蟹", +} diff --git a/gamerule/hundredyxx/point.go b/gamerule/hundredyxx/point.go new file mode 100644 index 0000000..7a119d2 --- /dev/null +++ b/gamerule/hundredyxx/point.go @@ -0,0 +1,48 @@ +package hundredyxx + +import ( + "math/rand" + "time" +) + +type DiceBox struct { + first *rand.Rand + second *rand.Rand + third *rand.Rand + count int + Point [DICE_NUM]int32 +} + +var AllRoll = [][DICE_NUM]int32{} + +func CreateDiceBox() *DiceBox { + return &DiceBox{ + count: 0, + first: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + second: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + third: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + Point: [DICE_NUM]int32{}, + } +} +func (bb *DiceBox) Roll() { + if bb.count > 10 { + bb.first = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.second = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.third = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.count = 0 + } + bb.count++ + bb.Point[0] = bb.first.Int31n(6) + bb.Point[1] = bb.second.Int31n(6) + bb.Point[2] = bb.third.Int31n(6) +} + +func init() { + for i := int32(0); i < 6; i++ { + for j := int32(0); j < 6; j++ { + for k := int32(0); k < 6; k++ { + AllRoll = append(AllRoll, [DICE_NUM]int32{i, j, k}) + } + } + } +} \ No newline at end of file diff --git a/gamerule/hundredyxx/point_test.go b/gamerule/hundredyxx/point_test.go new file mode 100644 index 0000000..0dab85f --- /dev/null +++ b/gamerule/hundredyxx/point_test.go @@ -0,0 +1,109 @@ +package hundredyxx + +import ( + "testing" +) + +func TestCreateBlackBox(t *testing.T) { + box := CreateDiceBox() + for i := 0; i < 50; i++ { + box.Roll() + //fmt.Println(box.Point) + //fmt.Println(CalcPoint(box.Point)) + } +} + +//var rate = [BetArea_Max]int32{ +// 1,1,1,1,1,1,//Area1-6 +// 60,30,20,12,8,6,6,6,6,8,12,20,30,60,//Sum4-17 +// 1,1,//small,big +// 10,10,10,10,10,10,//Double1-6 +// 30,//Boom +// 200,200,200,200,200,200,//Boom1-6 +//} +//func TestCalcPoint(t *testing.T) { +// type TestCase struct { +// point [RollNum]int32 +// score [BetArea_Max]int32 +// } +// testCase := []TestCase{ +// { +// point: [RollNum]int32{0, 1, 2}, +// score: [BetArea_Max]int32{ +// 1, 1, 1, 0, 0, 0, +// 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 1, 0, +// 0, 0, 0, 0, 0, 0, +// 0, +// 0, 0, 0, 0, 0, 0}, +// }, { +// point: [RollNum]int32{1, 1, 2}, +// score: [BetArea_Max]int32{ +// 0, 2, 1, 0, 0, 0, +// 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 1, 0, +// 0, 10, 0, 0, 0, 0, +// 0, +// 0, 0, 0, 0, 0, 0}, +// }, { +// point: [RollNum]int32{4, 3, 2}, +// score: [BetArea_Max]int32{ +// 0, 0, 1, 1, 1, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, +// 0, 1, +// 0, 0, 0, 0, 0, 0, +// 0, +// 0, 0, 0, 0, 0, 0}, +// }, { +// point: [RollNum]int32{5, 5, 5}, +// score: [BetArea_Max]int32{ +// 0, 0, 0, 0, 0, 3, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, +// 0, 0, 0, 0, 0, 10, +// 30, +// 0, 0, 0, 0, 0, 200}, +// }, { +// point: [RollNum]int32{1, 1, 1}, +// score: [BetArea_Max]int32{ +// 0, 3, 0, 0, 0, 0, +// 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, +// 0, 10, 0, 0, 0, 0, +// 30, +// 0, 200, 0, 0, 0, 0}, +// }, { +// point: [RollNum]int32{3, 4, 5}, +// score: [BetArea_Max]int32{ +// 0, 0, 0, 1, 1, 1, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, +// 0, 1, +// 0, 0, 0, 0, 0, 0, +// 0, +// 0, 0, 0, 0, 0, 0}, +// }, +// } +// for _, value := range testCase { +// score := CalcPoint(value.point) +// if !Int32SliceEqual(score[:], value.score[:]) { +// fmt.Println(score) +// fmt.Println(value.score) +// t.Logf("Point %v calc score error:", value.point) +// t.Fatal("TestCalcPoint test fetal.") +// } +// } +//} +//func Int32SliceEqual(left []int32, right []int32) bool { +// if len(left) != len(right) { +// return false +// } +// for i := 0; i < len(left); i++ { +// if left[i] != right[i] { +// return false +// } +// } +// return true +//} +//func TestGlobalData(t *testing.T) { +// fmt.Println(MaxChipColl) +//} diff --git a/gamerule/hunting/constants.go b/gamerule/hunting/constants.go new file mode 100644 index 0000000..3027a6c --- /dev/null +++ b/gamerule/hunting/constants.go @@ -0,0 +1,374 @@ +package hunting + +import ( + "math/rand" + "mongo.games.com/game/common" +) + +// 0 没有宝石 + +// level1 +// 1 河心石 +// 2 荒玉石 +// 3 紫水晶 +// 4 血榴石 +// 5 锡黄晶 + +// level2 +// 6 蓝锆晶 +// 7 精灵榄 +// 8 魇蛟眼 +// 9 红玉髓 +// 10 龙眼石 + +// level3 +// 11 海洋之眼 +// 12 梦境翡翠 +// 13 影歌紫玉 +// 14 地狱炎石 +// 15 王者琥珀 + +// 16 炸弹 + +const ( + // 关卡数量 + LevelNum = 4 + // 掉线重连保持时长,单位秒 + DropLineTime = 30 + // 最短多少秒通知一次奖池金额 + CoinPoolTime = 1 + // 没有宝石 + NoGem = 0 + // 炸弹 + Bomb = 16 + // 多长时间未操作踢出房间 + TimeoutSecond = 180 +) + +// 游戏状态 +const ( + GamingState = 0 + GameStateMax = 1 // 状态数量 +) + +func levelNum(n int) int { + if n-1 < 0 { + return 0 + } + if n-1 >= LevelNum { + return LevelNum - 1 + } + return n - 1 +} + +var levelPass = []int32{15, 15, 15, 0} + +// 通关需要的炸弹数量 +func LevelPassBombs(level int) int32 { + return levelPass[levelNum(level)] +} + +var levelGemsNum = []int{4*4 + 4, 5*5 + 5, 6*6 + 6, 0} + +// 每关的初始宝石数量 +func LevelGemsNum(level int) int { + return levelGemsNum[levelNum(level)] +} + +var levelColumn = []int{4, 5, 6, 0} + +// 每关的宝石列数 +func LevelColumn(level int) int { + return levelColumn[levelNum(level)] +} + +var levelGemType = []int32{1, 2, 3, 4, 5} + +// 每关的宝石类型 +func LevelGemType(level int) []int32 { + l := levelNum(level) + if l == 0 { + return levelGemType + } + ret := make([]int32, len(levelGemType)) + for i := 0; i < len(ret); i++ { + ret[i] = levelGemType[i] + int32(len(ret)*l) + } + return ret +} + +var GemTypeName = map[int32]string{ + 1: "1河心石", + 2: "2荒玉石", + 3: "3紫水晶", + 4: "4血榴石", + 5: "5锡黄晶", + 6: "1蓝锆晶", + 7: "2精灵榄", + 8: "3魇蛟眼", + 9: "4红玉髓", + 10: "5龙眼石", + 11: "1海洋之眼", + 12: "2梦境翡翠", + 13: "3影歌紫玉", + 14: "4地狱炎石", + 15: "5王者琥珀", + 16: "炸弹", +} + +var gemProbability = []int{4100, 2700, 2000, 800, 400} + +func GetGemIndex(r *rand.Rand, num int) int { + n := r.Intn(10000) + sum := 0 + for i := 0; i < num; i++ { + sum += gemProbability[i] + if n < sum { + return i + } + } + return 0 +} + +var levelDelete = []int{4, 5, 6, 0} + +// 每关宝石最少可消除的连接数量 +func LevelDelete(level int) int { + return levelDelete[levelNum(level)] +} + +var rate = []int32{2, 3, 4, 10, 20, 40, 80, 160, 320, 640, 1000, 2000, 3000, 4000, 5000, 6000, 7000} +var level1GemRate = [][]int32{ + rate[:11], + rate[1:12], + rate[2:13], + rate[3:14], + rate[4:15], +} +var level2GemRate = [][]int32{ + level1GemRate[1], + level1GemRate[2], + level1GemRate[3], + level1GemRate[4], + rate[5:16], +} +var level3GemRate = [][]int32{ + level2GemRate[1], + level2GemRate[2], + level2GemRate[3], + level2GemRate[4], + rate[6:17], +} + +// GemsRate 宝石倍率, 倍率比实际倍率扩大了10倍,使用后要除以10 +// n 宝石连接数量 +// tp 宝石类型 +func GemsRate(level int, n int, tp int32) int64 { + if tp == Bomb { + return 0 + } + switch levelNum(level) { + case 0: + if n < 4 || n > 14 { + return 0 + } + return int64(level1GemRate[(tp-1)%5][n-4]) + case 1: + if n < 5 || n > 15 { + return 0 + } + return int64(level2GemRate[(tp-1)%5][n-5]) + case 2: + if n < 6 || n > 16 { + return 0 + } + return int64(level3GemRate[(tp-1)%5][n-6]) + } + return 0 +} + +func rateScope(level int, a, b int32, f func([][]int32) []int32) []int32 { + if a > b { + a, b = b, a + } + var ret []int32 + switch levelNum(level) { + case 0: + ret = f(level1GemRate) + case 1: + ret = f(level2GemRate) + case 2: + ret = f(level3GemRate) + } + tmp := map[int32]struct{}{} + for i := 0; i < len(ret); i++ { + tmp[ret[i]] = struct{}{} + } + ret = ret[:0] + for k := range tmp { + ret = append(ret, k) + } + return ret +} + +// [a, b) +func RateScope(level int, a, b int32) []int32 { + return rateScope(level, a, b, func(data [][]int32) []int32 { + var ret []int32 + for _, v := range data { + for i := 0; i < len(v); i++ { + if v[i] >= b { + break + } + if v[i] < a { + continue + } + ret = append(ret, v[i]) + } + } + return ret + }) +} + +// [a, b] +func RateScope1(level int, a, b int32) []int32 { + return rateScope(level, a, b, func(data [][]int32) []int32 { + var ret []int32 + for _, v := range data { + for i := 0; i < len(v); i++ { + if v[i] > b { + break + } + if v[i] < a { + continue + } + ret = append(ret, v[i]) + } + } + return ret + }) +} + +func rateGems(level int, r int32, data [][]int32) map[int32]int { + ret := map[int32]int{} + for k, v := range data { + var n = LevelDelete(level) - 1 + for i := 0; i < len(v); i++ { + n++ + if v[i] < r { + continue + } + if v[i] > r { + break + } + if v[i] == r { + ret[int32(k)+1+int32(5*levelNum(level))] = n + break + } + } + } + return ret +} + +// RateGems 根据需要的倍率,查询连接宝石和类型 +// rate 倍率 +func RateGems(level int, r int32) map[int32]int { + switch levelNum(level) { + case 0: + return rateGems(level, r, level1GemRate) + case 1: + return rateGems(level, r, level2GemRate) + case 2: + return rateGems(level, r, level3GemRate) + } + return map[int32]int{} +} + +func GetsRateMin(level int) map[int32][]int { + ret := make(map[int32][]int) + gt := LevelGemType(level) + for i := 0; i < len(gt); i++ { + num := 1 + var n = GemsRate(level, num, gt[i]) + for n < 10 { + if n > 0 { + ret[gt[i]] = append(ret[gt[i]], num) + } + num++ + n = GemsRate(level, num, gt[i]) + } + } + return ret +} + +// CoinPoolRate 奖池倍率 +// n 宝石连接数量 +func CoinPoolRate(level int, n int) int64 { + switch levelNum(level) { + case 0: + if n < 15 { + return 0 + } + if n <= 16 { + return int64((n - 14) * 10) + } + return 20 + case 1: + if n < 16 { + return 0 + } + if n <= 18 { + return int64((n - 15) * 10) + } + return 30 + case 2: + if n < 17 { + return 0 + } + if n <= 21 { + return int64((n - 16) * 10) + } + return 50 + } + return 0 +} + +// 每次下注,出现炸弹的概率是25% +func GetBomb(r *rand.Rand) bool { + return r.Intn(100) < 25 +} + +// 翻牌概率 +var cardRate = []int32{3500, 3000, 2000, 1000, 500} + +var baseRate = [][]int64{ + {2, 5, 10, 15, 20, 30}, + {1, 3, 6, 10, 15, 20}, + {1, 2, 4, 6, 8, 10}, +} + +func CardScore(r *rand.Rand, baseScore int64, sceneType int32) int64 { + var sum int32 + var i int + n := r.Int31n(10000) + for k, v := range cardRate { + sum += v + if n < sum { + i = k + break + } + } + + var arr []int64 + switch sceneType { + case common.SceneType_Primary: + arr = baseRate[0] + case common.SceneType_Middle: + arr = baseRate[1] + default: // 高级场、试玩场 + arr = baseRate[2] + } + a := baseScore * arr[i] + b := baseScore * arr[i+1] + return a + r.Int63n(b-a+1) +} diff --git a/gamerule/hunting/logic.go b/gamerule/hunting/logic.go new file mode 100644 index 0000000..bf9916d --- /dev/null +++ b/gamerule/hunting/logic.go @@ -0,0 +1,623 @@ +package hunting + +import ( + "bytes" + "container/list" + "fmt" + "math" + "math/rand" + "sort" +) + +// GetXY 获取宝石位置 +// n 一维数组中宝石索引 +// 返回对应的二维数组的索引 +func GetXY(level int, n int) (x, y int) { + return getXY(LevelColumn(level), n) +} + +func getXY(col int, n int) (x, y int) { + return int(n) % col, int(n) / col +} + +// XYToIndex 二维坐标转一维索引 +func XYToIndex(level int, x, y int) int { + return xyToIndex(LevelColumn(level), x, y) +} + +func xyToIndex(n int, x, y int) int { + return x + n*y +} + +// NoGemsData 没有任何宝石的初始状态 +func NoGemsData(level int) [][]int32 { + gems := make([][]int32, LevelColumn(level)+1) + for i := 0; i < len(gems); i++ { + gems[i] = make([]int32, LevelColumn(level)) + } + return gems +} + +func GemsDataCopy(a, b [][]int32) { + for i := 0; i < len(a); i++ { + copy(a[i], b[i]) + } +} + +func createGems(r *rand.Rand, level int, f func(r *rand.Rand, num int) int) (data [][]int32, bombs int32) { + if GetBomb(r) { + // 有炸弹时减少宝石连接数量 + data = NewGemsLessBomb(r, level) + bombs++ + return + } + var ret []int32 + gt := LevelGemType(level) + n := LevelGemsNum(level) // 宝石数量 + for i := 0; i < n; i++ { + ret = append(ret, gt[f(r, len(gt))]) + } + r.Shuffle(len(ret), func(i, j int) { + ret[i], ret[j] = ret[j], ret[i] + }) + n = LevelColumn(level) + for i := 0; i*n < len(ret); i++ { + data = append(data, ret[i*n:i*n+n]) + } + return +} + +// NewGems 随机宝石 +// 返回宝石和炸弹数 +func NewGems(r *rand.Rand, level int) (data [][]int32, bombs int32) { + return createGems(r, level, GetGemIndex) +} + +// NewGemsNoLink 创建没有可消除的宝石 +func NewGemsNoLink(r *rand.Rand, level int) (data [][]int32) { + n := LevelGemsNum(level) + data = NoGemsData(level) + index := r.Perm(n) + AddGemsLess2(level, data, index) + return +} + +func NewGemsBombsEnd(r *rand.Rand, level int) (data [][]int32) { + data = NewGemsNoLink(r, level) + x, y := GetXY(level, r.Intn(LevelGemsNum(level)-LevelColumn(level))) + for j := len(data) - 1; j > y; j-- { + data[j][x] = data[j-1][x] + } + data[y][x] = Bomb + return +} + +func GemsC(r *rand.Rand, level int) [][]int32 { + data := NoGemsData(level) + colNum := len(data[0]) + rowNum := len(data) + + var gt int32 + var n int + m := GetsRateMin(level) + for k, v := range m { + gt = k + n = v[r.Intn(len(v))] + break + } + if n == 0 { + return data + } + + indexes := map[int]struct{}{ + r.Intn(LevelGemsNum(level)): {}, + } + var x, y int + for ; n > 0 && len(indexes) > 0; n-- { + for k := range indexes { + x, y = getXY(colNum, k) + delete(indexes, k) + break + } + data[y][x] = gt + if y+1 < rowNum && data[y+1][x] == NoGem { + indexes[xyToIndex(colNum, x, y+1)] = struct{}{} + } + if y-1 >= 0 && data[y-1][x] == NoGem { + indexes[xyToIndex(colNum, x, y-1)] = struct{}{} + } + if x-1 >= 0 && data[y][x-1] == NoGem { + indexes[xyToIndex(colNum, x-1, y)] = struct{}{} + } + if x+1 < colNum && data[y][x+1] == NoGem { + indexes[xyToIndex(colNum, x+1, y)] = struct{}{} + } + } + return data +} + +// NewGemsLess 创建所有宝石使消除宝石倍率小于1,并且只能消除一次 +func NewGemsLess(r *rand.Rand, level int) (data [][]int32) { + if r.Intn(100) < 40 { + // 不能消除 + return NewGemsNoLink(r, level) + } + var ret = NoGemsData(level) + for { + // 满足条件的情况很多,这里应该不会循环很多次,通常一次就可以搜索到 + data = GemsC(r, level) + AddGemsLess2(level, data, nil) + + GemsDataCopy(ret, data) + + // 验证 + del := Check(level, data) + if len(del) == 0 { + return ret + } + if len(del) > 1 { + continue + } + for _, v := range del[0] { + x, y := GetXY(level, v) + data[y][x] = NoGem + } + AddGemsLess2(level, data, Drop(data)) + if len(Check(level, data)) == 0 { + break + } + } + return ret +} + +// NewGemsLessBomb 创建包含炸弹的所有宝石,并且炸弹消除后最多只能再消除一次不 +func NewGemsLessBomb(r *rand.Rand, level int) (data [][]int32) { + var ret = NoGemsData(level) + for { + data = NewGemsLess(r, level) + // 炸弹不放最上方一排 + x, y := GetXY(level, r.Intn(LevelGemsNum(level)-LevelColumn(level))) + for j := len(data) - 1; j > y; j-- { + data[j][x] = data[j-1][x] + } + data[y][x] = Bomb + + GemsDataCopy(ret, data) + + // 验证,炸弹消除后最多再消除一次 + data[y][x] = NoGem + AddGemsLess2(level, data, Drop(data)) + del := Check(level, data) + if len(del) == 0 { + break + } + if len(del) == 1 { + for _, v := range del[0] { + x, y := GetXY(level, v) + data[y][x] = NoGem + } + AddGemsLess2(level, data, Drop(data)) + if len(Check(level, data)) == 0 { + break + } + } + } + return ret +} + +// Check 查询消除位置 +// 有炸弹时只返回消除炸弹;没有炸弹的时候有多组需要消除就都查询出来 +// 返回值,外层数组是有多少组可以消除,内层是可消除宝石所在的一维数组的索引 +func Check(level int, data [][]int32) [][]int { + return check(level, data, LevelColumn(level), len(data)-1) +} + +func check(level int, data [][]int32, colNum, rowNum int) [][]int { + var ret [][]int + if colNum <= 0 || rowNum <= 0 { + return ret + } + + // tmp 用来记录已连接位置 + var tmp = make([][]bool, rowNum) + for i := 0; i < len(tmp); i++ { + tmp[i] = make([]bool, colNum) + } + + var place [][2]int + + for y := 0; y < rowNum; y++ { + for x := 0; x < colNum; x++ { + if data[y][x] == NoGem { + tmp[y][x] = true + continue + } + if data[y][x] == Bomb { + ret = [][]int{{xyToIndex(colNum, x, y)}} + return ret + } + if tmp[y][x] { + continue + } + tmp[y][x] = true + // 搜索连接(广度优先搜索) + place = place[:0] + place = append(place, [2]int{x, y}) + var index []int + for len(place) > 0 { + e := place[0] + place = place[1:] + index = append(index, xyToIndex(colNum, e[0], e[1])) + //fmt.Printf("(%d,%d) ",e[0],e[1]) + // 下 + if Y := e[1] - 1; Y >= 0 && data[Y][e[0]] == data[e[1]][e[0]] && !tmp[Y][e[0]] { + place = append(place, [2]int{e[0], Y}) + tmp[Y][e[0]] = true + } + // 右 + if X := e[0] + 1; X < colNum && data[e[1]][X] == data[e[1]][e[0]] && !tmp[e[1]][X] { + place = append(place, [2]int{X, e[1]}) + tmp[e[1]][X] = true + } + // 左 + if X := e[0] - 1; X >= 0 && data[e[1]][X] == data[e[1]][e[0]] && !tmp[e[1]][X] { + place = append(place, [2]int{X, e[1]}) + tmp[e[1]][X] = true + } + // 上 + if Y := e[1] + 1; Y < rowNum && data[Y][e[0]] == data[e[1]][e[0]] && !tmp[Y][e[0]] { + place = append(place, [2]int{e[0], Y}) + tmp[Y][e[0]] = true + } + } + //fmt.Println() + ret = append(ret, index) + } + } + for i := 0; i < len(ret); { + if len(ret[i]) < LevelDelete(level) { + ret = append(ret[:i], ret[i+1:]...) + } else { + // 排序 + sort.Slice(ret[i], func(a, b int) bool { + return ret[i][a] < ret[i][b] + }) + i++ + } + } + return ret +} + +func checkXY(level int, data [][]int32, colNum, rowNum, x, y int) []int { + var ret []int + if colNum <= 0 || rowNum <= 0 { + return ret + } + + // tmp 用来记录已查看位置 + var tmp = make([][]bool, rowNum) + for i := 0; i < len(tmp); i++ { + tmp[i] = make([]bool, colNum) + } + + gt := data[y][x] + tmp[y][x] = true + + queue := list.New() + queue.PushBack(XYToIndex(level, x, y)) + for queue.Len() > 0 { + i := queue.Remove(queue.Front()).(int) + ret = append(ret, i) + x, y := GetXY(level, i) + + // 下 + if y-1 >= 0 && !tmp[y-1][x] { + tmp[y-1][x] = true + if data[y-1][x] == gt { + queue.PushBack(XYToIndex(level, x, y-1)) + } + } + + // 右 + if x+1 < colNum && !tmp[y][x+1] { + tmp[y][x+1] = true + if data[y][x+1] == gt { + queue.PushBack(XYToIndex(level, x+1, y)) + } + } + + // 左 + if x-1 >= 0 && !tmp[y][x-1] { + tmp[y][x-1] = true + if data[y][x-1] == gt { + queue.PushBack(XYToIndex(level, x-1, y)) + } + } + + // 上 + if y+1 < rowNum && !tmp[y+1][x] { + tmp[y+1][x] = true + if data[y+1][x] == gt { + queue.PushBack(XYToIndex(level, x, y+1)) + } + } + } + return ret +} + +// Drop 宝石下落 +// 消除宝石后将上方的宝石向下移动 +// 返回需要补充宝石的位置(一维数组的索引) +func Drop(data [][]int32) []int { + var ret []int + if len(data) == 0 { + return ret + } + colNum := len(data[0]) + rowNum := len(data) + a, b := 0, 0 + flag := false + for i := 0; i < colNum; i++ { + a, b = 0, 0 + flag = false + for ; a < rowNum && b < rowNum; a++ { + if data[a][i] != NoGem { + continue + } + if !flag { + flag = true + b = a + 1 + } else { + b++ + } + for ; b < rowNum; b++ { + if data[b][i] != NoGem { + data[b][i], data[a][i] = data[a][i], data[b][i] + break + } + } + } + j := a - 1 + if data[j][i] == NoGem { + ret = append(ret, xyToIndex(colNum, i, j)) + } + j++ + for ; j < rowNum; j++ { + ret = append(ret, xyToIndex(colNum, i, j)) + } + } + sort.Slice(ret, func(i, j int) bool { + return ret[i] < ret[j] + }) + return ret +} + +// AddGemsLess 填充空缺的宝石, 使连接宝石最少 +// 返回添加的宝石 +func AddGemsLess(r *rand.Rand, level int, data [][]int32, index []int) []int32 { + //if len(index) > LevelDelete(level) { + return AddGemsLess2(level, data, index) + //} + // 遍历所有情况,当空缺宝石数量太多时搜索速度太慢,所以只有填充宝石比较少的时候才这样搜 + //var ret = make([]int32, len(index)) + //var minRate int64 = math.MaxInt64 + //findGemsLose(r, level, data, LevelGemType(level), index, 0, ret, &minRate) + //return ret +} + +func findGemsLose(r *rand.Rand, level int, data [][]int32, gt []int32, index []int, i int, gems []int32, minRate *int64) bool { + r.Shuffle(len(gt), func(i, j int) { + gt[i], gt[j] = gt[j], gt[i] + }) + x, y := getXY(len(data[0]), index[i]) + for _, v := range gt { + data[y][x] = v + if i == len(index)-1 { + del := check(level, data, LevelColumn(level), len(data)) + var rate int64 + for j := 0; j < len(del); j++ { + x, y := getXY(len(data[0]), del[j][0]) + rate += GemsRate(level, len(del[j]), data[y][x]) + } + if rate < *minRate { + for i := 0; i < len(index); i++ { + x, y := getXY(len(data[0]), index[i]) + gems[i] = data[y][x] + } + *minRate = rate + } + if *minRate == 0 { + // 停止搜索 + return true + } + } else { + if findGemsLose(r, level, data, gt, index, i+1, gems, minRate) { + return true + } + } + } + // 继续搜索 + return false +} + +// AddGemsLess2 填充空缺的宝石,使连接宝石最少 +// 返回添加的宝石 +// 按空缺位置,从前往后依次填充新宝石,填充的宝石需要满足以下几个条件 +// 相邻位置:上,下,左,右,左上,右上,左下,右下 8 个位置 +// 1.和相邻位置宝石不相同,如果有多个类型可选,随机选择一种, +// 2.和相邻位置宝石连接最少 +func AddGemsLess2(level int, data [][]int32, index []int) []int32 { + var ret = make([]int32, 0, len(index)) + var m map[int32]int + var n int + colNum := len(data[0]) + rowNum := len(data) + + if len(index) == 0 { + for j := 0; j < rowNum; j++ { + for i := 0; i < colNum; i++ { + if data[j][i] == NoGem { + index = append(index, xyToIndex(colNum, i, j)) + } + } + } + } + + gt := LevelGemType(level) + var gts = make([]int32, 0, len(gt)) + gtMap := make(map[int32]struct{}) + for i := 0; i < len(gt); i++ { + gtMap[gt[i]] = struct{}{} + } + for i := 0; i < len(index); i++ { + x, y := getXY(colNum, index[i]) + m = make(map[int32]int) + // 相邻8个位置 + if y+1 < rowNum && data[y+1][x] != NoGem { + m[data[y+1][x]] += 1 + } + if y-1 >= 0 && data[y-1][x] != NoGem { + m[data[y-1][x]] += 1 + } + if x-1 >= 0 && data[y][x-1] != NoGem { + m[data[y][x-1]] += 1 + } + if x+1 < colNum && data[y][x+1] != NoGem { + m[data[y][x+1]] += 1 + } + if x-1 >= 0 && y+1 < rowNum && data[y+1][x-1] != NoGem { + m[data[y+1][x-1]] += 1 + } + if x+1 < colNum && y+1 < rowNum && data[y+1][x+1] != NoGem { + m[data[y+1][x+1]] += 1 + } + if x-1 >= 0 && y-1 >= 0 && data[y-1][x-1] != NoGem { + m[data[y-1][x-1]] += 1 + } + if x+1 < colNum && y-1 >= 0 && data[y-1][x+1] != NoGem { + m[data[y-1][x+1]] += 1 + } + + if len(m) != len(gt) { + for k := range gtMap { + if _, ok := m[k]; !ok { + ret = append(ret, k) + data[y][x] = k + break + } + } + } else { + // 连接数最少 + n = math.MaxInt64 + gts = gts[:0] + for k := range m { + data[y][x] = k + l := len(checkXY(level, data, colNum, rowNum, x, y)) + if l < n { + n = l + gts = gts[:0] + gts = append(gts, k) + } else if l == n { + gts = append(gts, k) + } + } + data[y][x] = gts[0] + ret = append(ret, gts[0]) + } + } + return ret +} + +// NewGemsRate 根据需要倍率随机生成宝石,并且只能消除一次 +func NewGemsRate(r *rand.Rand, level int, rate int32) [][]int32 { + data := NoGemsData(level) + colNum := len(data[0]) + rowNum := len(data) - 1 + + var gt int32 + var n int + for k, v := range RateGems(level, rate) { + gt = k + n = v + break + } + if n == 0 { + return NewGemsNoLink(r, level) + } + indexes := map[int]struct{}{ + r.Intn(LevelGemsNum(level) - LevelColumn(level)): {}, + } + var x, y int + for ; n > 0 && len(indexes) > 0; n-- { + for k := range indexes { + x, y = getXY(colNum, k) + delete(indexes, k) + break + } + data[y][x] = gt + if y+1 < rowNum && data[y+1][x] == NoGem { + indexes[xyToIndex(colNum, x, y+1)] = struct{}{} + } + if y-1 >= 0 && data[y-1][x] == NoGem { + indexes[xyToIndex(colNum, x, y-1)] = struct{}{} + } + if x-1 >= 0 && data[y][x-1] == NoGem { + indexes[xyToIndex(colNum, x-1, y)] = struct{}{} + } + if x+1 < colNum && data[y][x+1] == NoGem { + indexes[xyToIndex(colNum, x+1, y)] = struct{}{} + } + } + AddGemsLess2(level, data, nil) + + var ret = NoGemsData(level) + GemsDataCopy(ret, data) + + del := Check(level, data) + if len(del) != 1 { + return NewGemsRate(r, level, rate) + } + for _, v := range del[0] { + x, y := GetXY(level, v) + data[y][x] = NoGem + } + AddGemsLess2(level, data, Drop(data)) + if len(Check(level, data)) == 0 { + return ret + } + return NewGemsRate(r, level, rate) +} + +func b(n int32) int32 { + if n == 80 { + n = '.' + } + if n == 64 { + n = ' ' + } + return n +} + +func toStringLine(data []int32) string { + if len(data) == 0 { + return "" + } + ret := bytes.Buffer{} + ret.WriteString(fmt.Sprintf("[%s", string(b(data[0]+64)))) + for i := 1; i < len(data); i++ { + ret.WriteString(fmt.Sprintf(" %s", string(b(data[i]+64)))) + } + ret.WriteString("]") + return ret.String() +} + +// ToString 将宝石都打印出来,用于bug调试 +// 不同宝石用 A,B,C... 表示;用 . 表示炸弹 +func ToString(data [][]int32) string { + ret := bytes.Buffer{} + for i := len(data) - 1; i >= 0; i-- { + ret.WriteString(toStringLine(data[i])) + ret.WriteString("\n") + } + return ret.String() +} diff --git a/gamerule/hunting/logic_test.go b/gamerule/hunting/logic_test.go new file mode 100644 index 0000000..8ad4d6c --- /dev/null +++ b/gamerule/hunting/logic_test.go @@ -0,0 +1,337 @@ +package hunting + +import ( + "encoding/json" + "fmt" + "math/rand" + "sort" + "strconv" + "testing" + "time" +) + +var rd *rand.Rand + +func init() { + rd = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +func CreateTestData() { + for i := 0; i < 1; i++ { + level := rd.Intn(3) + 1 + data, b := NewGems(rd, level) + s1, _ := json.Marshal(data) + fmt.Printf("%d,%d\n", level, b) + + result := make([][]int32, len(data)) + for i := 0; i < len(result); i++ { + result[i] = make([]int32, len(data[i])) + copy(result[i], data[i]) + } + + index := Check(level, data) + for _, v := range index { + for j := 0; j < len(v); j++ { + x, y := GetXY(level, v[j]) + result[y][x] = -22 + } + } + s2, _ := json.Marshal(result) + + for j := len(data) - 1; j >= 0; j-- { + fmt.Printf("%v\t%v\n", toStringLine(data[j]), toStringLine(result[j])) + } + fmt.Println("测试数据:") + fmt.Printf("{\"%s\",\"%s\",\"%s\"},\n", s1, s2, fmt.Sprint(level)) + } +} + +func TestCheck(t *testing.T) { + //CreateTestData() + testData := [][]string{ + {"[[2,1,1,1],[1,2,4,3],[1,3,3,1],[3,1,2,4],[1,2,5,1]]", "[[2,1,1,1],[1,2,4,3],[1,3,3,1],[3,1,2,4],[1,2,5,1]]", "1"}, + {"[[3,2,16,1],[1,1,1,1],[1,3,1,2],[1,2,1,2],[2,1,3,2]]", "[[3,2,-22,1],[1,1,1,1],[1,3,1,2],[1,2,1,2],[2,1,3,2]]", "1"}, + {"[[6,6,9,8,8],[7,6,8,7,6],[9,7,6,7,9],[7,7,9,8,7],[8,6,6,6,8],[8,6,7,6,7]]", "[[6,6,9,8,8],[7,6,8,7,6],[9,7,6,7,9],[7,7,9,8,7],[8,6,6,6,8],[8,6,7,6,7]]", "2"}, + {"[[1,2,3,2],[3,2,1,1],[1,2,1,3],[1,2,1,1],[1,2,3,3]]", "[[1,-22,3,2],[3,-22,-22,-22],[1,-22,-22,3],[1,-22,-22,-22],[1,2,3,3]]", "1"}, + {"[[4,1,1,2],[1,1,3,3],[1,3,1,1],[1,3,4,1],[1,3,4,1]]", "[[4,-22,-22,2],[-22,-22,3,3],[-22,3,1,1],[-22,3,4,1],[1,3,4,1]]", "1"}, + {"[[4,1,1,3],[2,1,3,2],[3,3,1,3],[1,5,1,2],[4,3,2,1]]", "[[4,1,1,3],[2,1,3,2],[3,3,1,3],[1,5,1,2],[4,3,2,1]]", "1"}, + {"[[11,12,11,12,11,12],[13,13,11,11,12,11],[15,11,12,13,13,12],[12,12,12,12,13,15],[15,14,13,12,11,15],[12,13,12,11,11,11],[12,11,12,13,12,14]]", "[[11,12,11,12,11,12],[13,13,11,11,12,11],[15,11,-22,13,13,12],[-22,-22,-22,-22,13,15],[15,14,13,-22,11,15],[12,13,12,11,11,11],[12,11,12,13,12,14]]", "3"}, + {"[[11,12,13,11,11,11],[11,11,11,11,11,14],[13,15,11,11,11,11],[12,12,13,11,12,13],[11,11,13,11,12,13],[12,13,13,14,11,12],[11,11,12,15,12,11]]", "[[-22,12,13,-22,-22,-22],[-22,-22,-22,-22,-22,14],[13,15,-22,-22,-22,-22],[12,12,13,-22,12,13],[11,11,13,-22,12,13],[12,13,13,14,11,12],[11,11,12,15,12,11]]", "3"}, + {"[[11,11,11,12,15,13],[11,12,15,11,12,12],[13,12,15,13,11,11],[13,11,12,15,13,12],[13,13,12,12,11,11],[12,11,12,12,12,11],[11,13,12,12,12,11]]", "[[11,11,11,12,15,13],[11,12,15,11,12,12],[13,12,15,13,11,11],[13,11,-22,15,13,12],[13,13,-22,-22,11,11],[12,11,-22,-22,-22,11],[11,13,12,12,12,11]]", "3"}, + {"[[13,12,12,12,12,13],[11,13,12,13,13,13],[11,14,11,12,12,12],[13,11,14,12,11,12],[11,14,11,15,14,12],[11,14,11,11,12,11],[11,13,13,11,13,13]]", "[[13,12,12,12,12,13],[11,13,12,13,13,13],[11,14,11,-22,-22,-22],[13,11,14,-22,11,-22],[11,14,11,15,14,-22],[11,14,11,11,12,11],[11,13,13,11,13,13]]", "3"}, + {"[[7,8,7,9,10],[7,6,6,6,7],[8,6,8,6,7],[7,6,7,7,7],[7,6,7,6,6],[7,7,6,6,7]]", "[[7,8,7,9,10],[7,-22,-22,-22,-22],[8,-22,8,-22,-22],[7,-22,-22,-22,-22],[7,-22,-22,6,6],[7,7,6,6,7]]", "2"}, + {"[[16,6,7,7,6],[8,6,6,7,6],[8,7,9,6,8],[7,8,8,6,6],[7,6,9,6,7],[8,8,6,6,10]]", "[[-22,6,7,7,6],[8,6,6,7,6],[8,7,9,6,8],[7,8,8,6,6],[7,6,9,6,7],[8,8,6,6,10]]", "2"}, + } + for _, v := range testData { + var gems, result [][]int32 + json.Unmarshal([]byte(v[0]), &gems) + level, _ := strconv.Atoi(v[2]) + result = make([][]int32, len(gems)) + for i := 0; i < len(result); i++ { + result[i] = make([]int32, len(gems[i])) + copy(result[i], gems[i]) + } + index := Check(level, gems) + for _, v := range index { + for j := 0; j < len(v); j++ { + x, y := GetXY(level, v[j]) + result[y][x] = -22 + } + } + s, _ := json.Marshal(result) + s2 := string(s) + if v[1] != string(s) { + var su [][]int32 + json.Unmarshal([]byte(v[1]), &su) + fmt.Printf("%-12s\t%-12s\t%-12s\n", "Gems:", "want:", "result:") + for i := len(gems) - 1; i >= 0; i-- { + fmt.Printf("%v\t%v\t%v\n", toStringLine(gems[i]), toStringLine(su[i]), toStringLine(result[i])) + } + fmt.Println("正确数据:") + fmt.Printf("{\"%s\",\"%s\",\"%s\"},\n", v[0], s2, fmt.Sprint(level)) + t.Error("1") + } + } +} + +func testDrop(data [][]int32) []int { + var ret []int + if len(data) == 0 { + return ret + } + colNum := len(data[0]) + rowNum := len(data) + + result := make([][]int32, len(data)) + for i := 0; i < len(result); i++ { + result[i] = make([]int32, len(data[i])) + copy(result[i], data[i]) + } + + var y int + for i := 0; i < colNum; i++ { + y = 0 + for j := 0; j < rowNum; j++ { + if data[j][i] != NoGem { + result[y][i] = data[j][i] + y++ + } + } + } + for i := 0; i < colNum; i++ { + for j := 0; j < rowNum; j++ { + if data[j][i] == NoGem { + ret = append(ret, int(xyToIndex(colNum, i, j))) + } + } + } + sort.Slice(ret, func(i, j int) bool { + return ret[i] < ret[j] + }) + return ret +} + +func TestDrop(t *testing.T) { + for i := 0; i < 1000; i++ { + // 随机创建宝石 + level := rd.Intn(3) + 1 + data, _ := NewGems(rd, level) + //fmt.Printf("%d,%d\n", level, b) + //fmt.Println(toString(data)) + // 随机删除宝石 + n := LevelGemsNum(level) + delNum := rd.Intn(n) + for i := 0; i < delNum; i++ { + x, y := GetXY(level, rd.Intn(n)) + data[y][x] = NoGem + } + //fmt.Println(toString(data)) + // 对比测试结果 + index := Drop(data) + //fmt.Println(toString(data)) + res := testDrop(data) + //fmt.Println(index) + //fmt.Println(res) + if len(index) != len(res) { + t.Error("1") + } + for j := 0; j < len(index); j++ { + if index[j] != res[j] { + t.Error("2") + } + } + } +} + +func TestNoLink(t *testing.T) { + level := 3 + n := 10000 + sum := 0 + for j := 0; j < n; j++ { + i := 0 + for { + i++ + data, b := NewGems(rd, level) + if b > 0 { + continue + } + del := Check(level, data) + var rate int64 + for j := 0; j < len(del); j++ { + x, y := GetXY(level, del[j][0]) + rate += GemsRate(level, len(del[j]), data[y][x]) + } + if rate == 0 { + sum += i + break + } + } + } + fmt.Println("平均搜索次数:", sum/n) // 平均搜索次数: 4 +} + +func TestLess(t *testing.T) { + level := 3 + n := 10000 + sum := 0 + for j := 0; j < n; j++ { + i := 0 + for { + i++ + data, b := NewGems(rd, level) + if b > 0 { + continue + } + del := Check(level, data) + var rate int64 + for j := 0; j < len(del); j++ { + x, y := GetXY(level, del[j][0]) + rate += GemsRate(level, len(del[j]), data[y][x]) + } + if rate < 10 { + sum += i + break + } + } + } + fmt.Println("平均搜索次数:", sum/n) // 平均搜索次数: 3 +} + +func TestAddGemsLess(t *testing.T) { + var data [][]int32 + var b int32 = 1 + // 随机创建宝石 + level := rd.Intn(3) + 1 + for b > 0 { + data, b = NewGems(rd, level) + } + // 随机删除宝石 + n := LevelGemsNum(level) + delNum := rd.Intn(n) + for i := 0; i < delNum; i++ { + x, y := GetXY(level, rd.Intn(n)) + data[y][x] = NoGem + } + fmt.Println(ToString(data)) + // 对比测试结果 + index := Drop(data) + fmt.Println(ToString(data)) + gems := AddGemsLess(rd, level, data, index) + for i := 0; i < len(index); i++ { + x, y := GetXY(level, index[i]) + data[y][x] = gems[i] + } + fmt.Println(ToString(data)) +} + +func TestNewGemsLessBomb(t *testing.T) { + level := rd.Intn(3) + 1 + data := NewGemsLessBomb(rd, level) + fmt.Println(ToString(data)) +} + +func TestNewGemsNoLink(t *testing.T) { + var i int + var arr [][]int + var data [][]int32 + for len(arr) == 0 && i < 10000 { + data = NewGemsNoLink(rd, 3) + arr = Check(3, data) + i++ + } + if len(arr) > 0 { + fmt.Println(ToString(data)) + t.Error() + } +} + +func TestNewGemsLess(t *testing.T) { + data := NewGemsNoLink(rd, 1) + fmt.Println(ToString(data)) +} + +func TestGemsC(t *testing.T) { + data := GemsC(rd, 2) + fmt.Println(ToString(data)) +} + +func TestAddGemsLess2(t *testing.T) { + n := 1 + for i := 0; i < n; { + level := 3 + data := GemsC(rd, level) + AddGemsLess2(level, data, nil) + arr := Check(level, data) + if len(arr) == 1 { + i++ + fmt.Println(ToString(data)) + for _, v := range arr[0] { + x, y := GetXY(level, v) + data[y][x] = 0 + } + fmt.Println(ToString(data)) + index := Drop(data) + fmt.Println(ToString(data)) + + gems := AddGemsLess2(level, data, index) + for i := 0; i < len(index); i++ { + x, y := GetXY(level, index[i]) + data[y][x] = gems[i] + } + + if len(Check(level, data)) > 0 { + t.Error() + } + } + } +} + +func TestRateGems(t *testing.T) { + //for k, v := range RateGems(1, 80) { + // fmt.Println(k, v) + //} + data := NewGemsRate(rd, 1, 6000) + fmt.Println(ToString(data)) +} + +func TestNewGemsRate(t *testing.T) { + for i := 1; i <= 3; i++ { + for _, v := range rate { + data := NewGemsRate(rd, i, v) + del := Check(i, data) + if len(del) == 0 { + continue + } + var rate int64 + for j := 0; j < len(del); j++ { + x, y := GetXY(i, del[j][0]) + rate += GemsRate(i, len(del[j]), data[y][x]) + } + //fmt.Println(i, v, rate) + //fmt.Println(ToString(data)) + if rate != int64(v) { + t.Error("error") + } + } + } +} diff --git a/gamerule/iceage/constants.go b/gamerule/iceage/constants.go new file mode 100644 index 0000000..fa314a8 --- /dev/null +++ b/gamerule/iceage/constants.go @@ -0,0 +1,157 @@ +package iceage + +// 冰河世纪 +const ( + Element_BONUS int = iota + 1 //1 奖金 + Element_FREESPIN //2 免费旋转 + Element_JACKPOT //3 大奖 + Element_NUT //4 坚果 + Element_EGG_BLUE //5 蓝蛋 + Element_EGG_GREEN //6 绿蛋 + Element_EGG_GOLD //7 金蛋 + Element_Max +) + +var Element_NAME_MAP = map[int]string{ + Element_BONUS: "[ BONUS ]", + Element_FREESPIN: "[ FREE ]", + Element_JACKPOT: "[JACKPOT]", + Element_NUT: "[ 坚果 ]", + Element_EGG_BLUE: "[ 蓝蛋 ]", + Element_EGG_GREEN: "[ 绿蛋 ]", + Element_EGG_GOLD: "[ 金蛋 ]", + -1: "[ - ]", +} + +const LINE_ROW int = 3 //行数 +const LINE_CELL int = 5 //列数 +const LINENUM int = 20 //线条数 +const ELEMENT_TOTAL = LINE_ROW * LINE_CELL + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + +// 所有元素对应的赔率 +var LineScore = [Element_Max][LINE_CELL]int{ + {0, 0, 0, 0, 0}, //占位 + {0, 0, 0, 0, 0}, //1 奖金 + {0, 0, 0, 0, 0}, //2 免费旋转 + {0, 0, 75, 600, 0}, //3 大奖 + {0, 0, 0, 6, 30}, //4 坚果 + {0, 0, 3, 12, 50}, //5 蓝蛋 + {0, 0, 10, 100, 350}, //6 绿蛋 + {0, 0, 12, 120, 450}, //7 金蛋 +} + +// 免费次数奖励 +var FreeSpinTimesRate = []int{0, 0, 1, 3, 8} + +// 奖池参数信息,对应gamefree表里面的Jackpot字段 +const ( + ICEAGE_JACKPOT_InitJackpot int = iota //初始化奖池数量 + ICEAGE_JACKPOT_LIMITWIN_PRIZELOW //奖池不足时 最多赚取投注的多少倍 + ICEAGE_JACKPOT_LIMITWIN_PRIZEHIGH //奖池充足时 最多赚取投注的多少倍 + ICEAGE_JACKPOT_Max +) + +var AllLineArray = [][]int{ + {5, 6, 7, 8, 9}, //线条1 + {0, 1, 2, 3, 4}, //线条2 + {10, 11, 12, 13, 14}, //线条3 + {5, 6, 2, 8, 9}, //线条4 + {5, 6, 12, 8, 9}, //线条5 + {0, 1, 7, 3, 4}, //线条6 + {10, 11, 7, 13, 14}, //线条7 + {0, 11, 2, 13, 4}, //线条8 + {10, 1, 12, 3, 14}, //线条9 + {5, 1, 12, 3, 9}, //线条10 + {10, 6, 2, 8, 14}, //线条11 + {0, 6, 12, 8, 4}, //线条12 + {5, 11, 7, 3, 9}, //线条13 + {5, 1, 7, 13, 9}, //线条14 + {10, 6, 7, 8, 14}, //线条15 + {0, 6, 7, 8, 4}, //线条16 + {5, 11, 12, 13, 9}, //线条17 + {5, 1, 2, 3, 9}, //线条18 + {10, 11, 7, 3, 4}, //线条19 + {0, 1, 7, 13, 14}, //线条20 +} +var MissData = map[string][][]int{ + "DefaultData": { + {1, 2, 4, 4, 2, 1, 3, 4, 5, 6, 6, 7, 7, 6, 5}, + {4, 1, 2, 3, 3, 3, 4, 5, 6, 1, 7, 2, 4, 6, 2}, + {2, 2, 7, 5, 7, 5, 6, 7, 4, 4, 7, 6, 3, 4, 1}, + {1, 2, 4, 4, 2, 1, 3, 4, 5, 6, 6, 7, 7, 6, 5}, + {2, 2, 7, 5, 7, 5, 6, 7, 4, 4, 7, 6, 3, 4, 1}, + {7, 7, 6, 6, 3, 4, 6, 6, 1, 4, 4, 2, 6, 6, 4}, + {7, 7, 5, 2, 1, 3, 7, 5, 1, 5, 6, 6, 5, 7, 4}, + {4, 4, 7, 5, 1, 4, 7, 6, 7, 5, 3, 7, 3, 5, 5}, + {5, 5, 6, 7, 3, 7, 7, 3, 2, 7, 6, 4, 6, 4, 3}, + {5, 6, 7, 3, 6, 1, 3, 4, 7, 1, 5, 3, 2, 5, 4}, + {6, 5, 3, 7, 2, 2, 5, 1, 7, 4, 1, 7, 6, 7, 7}, + {5, 6, 7, 3, 6, 1, 3, 4, 7, 1, 5, 3, 2, 5, 4}, + {5, 7, 7, 5, 7, 6, 4, 6, 1, 2, 6, 5, 7, 4, 3}, + {7, 7, 6, 4, 5, 7, 7, 5, 7, 2, 7, 6, 6, 7, 2}, + {7, 7, 7, 6, 5, 6, 3, 7, 5, 7, 7, 5, 7, 2, 6}, + {7, 4, 7, 2, 6, 5, 2, 6, 4, 6, 7, 5, 6, 1, 6}, + {3, 7, 5, 5, 7, 7, 7, 7, 5, 6, 3, 7, 5, 3, 6}, + {5, 5, 6, 3, 7, 4, 7, 2, 5, 3, 7, 5, 6, 7, 7}, + {5, 6, 7, 3, 7, 5, 7, 7, 6, 2, 4, 3, 7, 7, 7}, + {7, 7, 5, 4, 2, 3, 3, 6, 7, 5, 5, 7, 4, 4, 7}, + {7, 1, 4, 7, 1, 4, 5, 7, 6, 3, 7, 6, 6, 5, 5}, + {4, 7, 7, 6, 6, 5, 6, 1, 7, 2, 7, 7, 7, 3, 4}, + {5, 7, 5, 6, 1, 5, 6, 5, 4, 6, 5, 2, 5, 1, 3}, + {7, 4, 6, 3, 7, 7, 4, 6, 5, 7, 6, 7, 5, 1, 4}, + }, + "DefaultData_v1": { + {5, 5, 5, 5, 1, 4, 5, 2, 1, 5, 4, 5, 2, 7, 6}, + {3, 5, 5, 4, 6, 5, 4, 2, 4, 6, 5, 1, 5, 5, 4}, + {1, 7, 3, 7, 6, 5, 6, 1, 1, 5, 7, 5, 5, 4, 3}, + {1, 4, 1, 7, 7, 7, 3, 7, 3, 7, 6, 7, 1, 4, 7}, + {6, 5, 1, 4, 1, 1, 5, 4, 6, 7, 4, 4, 4, 6, 4}, + {4, 6, 1, 5, 6, 5, 4, 5, 3, 6, 4, 5, 2, 7, 6}, + {2, 4, 5, 5, 5, 7, 7, 7, 5, 7, 6, 5, 3, 3, 7}, + {6, 4, 3, 7, 5, 4, 4, 4, 5, 7, 7, 6, 5, 4, 1}, + {3, 5, 5, 1, 2, 6, 4, 7, 6, 5, 5, 4, 5, 5, 1}, + {1, 4, 1, 5, 7, 2, 1, 1, 7, 5, 4, 5, 1, 5, 6}, + {4, 7, 7, 4, 7, 6, 6, 4, 1, 6, 4, 4, 6, 6, 5}, + {5, 1, 5, 3, 6, 5, 5, 5, 5, 7, 5, 1, 4, 5, 7}, + {5, 5, 6, 5, 4, 5, 1, 5, 4, 1, 5, 5, 5, 5, 5}, + {5, 5, 5, 5, 3, 3, 5, 1, 5, 1, 6, 1, 4, 1, 7}, + {5, 6, 4, 5, 4, 5, 5, 3, 6, 5, 6, 4, 5, 6, 2}, + {5, 6, 5, 1, 4, 5, 5, 7, 1, 5, 4, 5, 5, 1, 6}, + {5, 5, 5, 4, 4, 5, 3, 4, 4, 6, 1, 2, 4, 4, 7}, + {6, 4, 4, 4, 4, 6, 2, 4, 5, 2, 4, 2, 4, 2, 1}, + {3, 2, 6, 3, 4, 1, 1, 1, 1, 1, 4, 5, 7, 3, 4}, + {5, 5, 6, 1, 6, 1, 5, 4, 7, 1, 4, 6, 5, 4, 4}, + {7, 7, 5, 5, 4, 7, 1, 4, 1, 7, 3, 5, 7, 1, 4}, + {1, 1, 5, 5, 5, 3, 5, 4, 4, 2, 5, 7, 6, 5, 1}, + {1, 4, 4, 4, 4, 5, 5, 7, 1, 2, 1, 5, 5, 6, 5}, + {7, 4, 3, 5, 3, 1, 7, 7, 5, 4, 4, 7, 5, 1, 7}, + {4, 4, 1, 6, 7, 4, 5, 3, 6, 4, 5, 4, 4, 4, 5}, + {1, 7, 6, 5, 7, 6, 4, 5, 1, 4, 5, 5, 4, 5, 1}, + {2, 5, 4, 6, 5, 5, 7, 5, 6, 5, 5, 3, 6, 5, 6}, + {6, 3, 5, 5, 4, 5, 5, 6, 5, 4, 6, 1, 6, 4, 1}, + {4, 5, 7, 1, 7, 5, 1, 5, 6, 5, 1, 3, 5, 5, 7}, + {5, 5, 4, 5, 6, 5, 1, 5, 1, 1, 7, 5, 7, 5, 4}, + {6, 5, 5, 2, 5, 5, 5, 1, 4, 4, 7, 4, 4, 5, 7}, + {6, 3, 5, 7, 7, 5, 5, 7, 7, 6, 1, 4, 3, 4, 6}, + {6, 3, 5, 6, 5, 5, 5, 1, 6, 1, 5, 7, 4, 3, 3}, + {3, 5, 4, 5, 1, 6, 3, 2, 5, 5, 5, 7, 5, 3, 5}, + {4, 1, 5, 5, 5, 7, 4, 5, 4, 1, 3, 4, 4, 3, 1}, + {6, 5, 3, 1, 3, 5, 2, 5, 7, 3, 4, 6, 1, 7, 4}, + {6, 3, 7, 4, 4, 5, 6, 5, 4, 5, 5, 5, 4, 6, 5}, + {4, 3, 6, 4, 4, 3, 5, 3, 5, 3, 4, 5, 4, 2, 4}, + {1, 5, 5, 6, 6, 5, 6, 5, 1, 7, 4, 1, 2, 2, 5}, + {6, 6, 5, 5, 5, 1, 6, 4, 7, 5, 1, 3, 6, 4, 7}, + {5, 4, 7, 7, 3, 5, 5, 1, 4, 1, 4, 4, 4, 4, 4}, + {6, 5, 7, 1, 5, 6, 4, 7, 1, 4, 7, 7, 7, 1, 5}, + {4, 2, 4, 4, 6, 1, 7, 7, 1, 5, 5, 5, 5, 1, 6}, + {1, 7, 2, 3, 4, 2, 2, 1, 1, 5, 7, 5, 7, 7, 6}, + {1, 4, 7, 4, 5, 6, 7, 6, 4, 1, 5, 6, 2, 7, 6}, + {7, 7, 4, 5, 4, 5, 1, 5, 4, 2, 6, 5, 4, 3, 1}, + {5, 1, 5, 5, 4, 4, 4, 4, 7, 5, 1, 5, 4, 7, 1}, + {6, 6, 2, 6, 1, 4, 5, 6, 4, 7, 5, 4, 1, 5, 6}, + {6, 5, 6, 7, 5, 5, 7, 7, 1, 5, 1, 6, 1, 3, 4}, + {1, 3, 5, 7, 5, 5, 5, 1, 1, 6, 1, 5, 5, 6, 5}, + }, +} diff --git a/gamerule/iceage/iceage.go b/gamerule/iceage/iceage.go new file mode 100644 index 0000000..d1d54b5 --- /dev/null +++ b/gamerule/iceage/iceage.go @@ -0,0 +1,204 @@ +package iceage + +import ( + "fmt" + "math/rand" + "sort" + "time" +) + +type LineData struct { + Index int + Element int + Count int + Score int +} + +var SpinID int64 = 100000 // todo + +//图案*3+连线数量=赔率索引,返回元素值和数量 +func IsLine(data []int) (e int, count int) { + e = data[0] + count = 1 + for i := 1; i < len(data); i++ { + if data[i] == e { + count++ + } else { + break + } + } + if (e != Element_NUT && count < 3) || (e == Element_NUT && count < 4) { + return -1, -1 + } + return +} +func CalcLine(data []int, betLines []int64) (lines []LineData) { + //fmt.Println("************************************************") + //fmt.Println("data:",data) + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + normalData := []int{} + for _, pos := range lineTemplate { + edata = append(edata, data[pos]) + normalData = append(normalData, data[pos]) + } + //fmt.Println("edata:",edata) + //fmt.Println("normalData:",normalData) + if len(edata) == len(normalData) { + head, count := IsLine(edata) + if head >= 0 { + lines = append(lines, LineData{index, head, count, LineScore[head][count-1]}) + } + } else { + //fmt.Printf("Line:%v-%v\n", index, lineTemplate) + //fmt.Println("edata:", edata) + //fmt.Println("normalData:", normalData) + normalData = DelSliceRepEle(normalData) + //fmt.Println("normalData:", normalData) + for _, value := range normalData { + _ = value + replaceData := []int{} + for i := 0; i < len(edata); i++ { + replaceData = append(replaceData, edata[i]) + } + head, count := IsLine(replaceData) + if head >= 0 { + lines = append(lines, LineData{index, head, count, 0}) + //fmt.Printf("*******************%v****************************\n", lines) + break + } + } + } + } + //fmt.Print("lines:",lines) + return +} + +//去除切片在的重复的元素 +func DelSliceRepEle(data []int) []int { + if len(data) == 1 { + return data + } + sort.Ints(data) + for i := 0; i < len(data)-1; { + if data[i] == data[i+1] { + data = append(data[:i], data[i+1:]...) + } else { + i++ + } + } + return data +} + +var gSeedV = time.Now().UnixNano() + +//生成消消乐之后的数据,distributionData为将要注入的数据源 +func MakePlan(cards []int, distributionData []int32, betLines []int64) (newCards []int) { + var c = make([]int, len(cards)) + for i, card := range cards { + c[i] = card + } + var lines = CalcLine(cards, betLines) + //fmt.Printf("input card:%v \n",cards) + //PrintHuman(cards) + //fmt.Printf("calc result:%v \n",lines) + if len(lines) == 0 { + return nil + } + for _, line := range lines { + //if line.Score > 0 { + var l = AllLineArray[line.Index-1] + for pos := 0; pos < line.Count; pos++ { + c[l[pos]] = -1 + } + //} + } + //fmt.Printf("after trim card:%v \n",c) + //PrintHuman(c) + for i := 10; i < 15; i++ { + if c[i] == -1 && c[i-5] != -1 { + c[i], c[i-5] = c[i-5], c[i] + } + } + for i := 5; i < 10; i++ { + if c[i] == -1 && c[i-5] != -1 { + c[i], c[i-5] = c[i-5], c[i] + } + } + for i := 10; i < 15; i++ { + if c[i] == -1 && c[i-5] != -1 { + c[i], c[i-5] = c[i-5], c[i] + } + } + //fmt.Printf("after plan card:%v \n",c) + //PrintHuman(c) + var dD = make([]int, 0) + for _, e := range distributionData { + if int(e) == Element_BONUS || int(e) == Element_FREESPIN || int(e) == Element_JACKPOT { + continue + } + dD = append(dD, int(e)) + } + gSeedV++ + rand.Seed(gSeedV) + for i, card := range c { + if card == -1 { + c[i] = dD[rand.Intn(len(dD))] + } + } + //fmt.Printf("after fill card:%v \n",c) + //PrintHuman(c) + return c +} + +type CaclResult struct { + WinLines []LineData //中奖的线 + AllScore int //总中奖倍率 + BonusScore int //小游戏奖励 + SpinFreeTimes int //免费旋转次数 +} + +func CaclScore(cards []int, betLines []int64) (int32, int32, int32, int32, bool) { + curscore := 0 + alllinenum := 0 //中奖线路总数 + allscore := 0 + spinFree := 0 + isJackpot := false + lines := CalcLine(cards, betLines) + for _, line := range lines { + if line.Element == Element_FREESPIN { + spinFree += FreeSpinTimesRate[line.Count-1] + continue + } + if line.Element == Element_JACKPOT && line.Count == LINE_CELL { + isJackpot = true + } + curscore = LineScore[line.Element][line.Count-1] + allscore = allscore + curscore + alllinenum++ + } + bounsCount := 0 + for _, card := range cards { + if card == Element_BONUS { + bounsCount++ + } + } + rand.Seed(time.Now().UnixNano()) + //bounsScore:= SmallGameByBouns[bounsCount][0]+rand.Intn(SmallGameByBouns[bounsCount][1]) + return int32(alllinenum), int32(allscore), int32(0), int32(spinFree), isJackpot +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} diff --git a/gamerule/iceage/iceage_test.go b/gamerule/iceage/iceage_test.go new file mode 100644 index 0000000..dbd56f8 --- /dev/null +++ b/gamerule/iceage/iceage_test.go @@ -0,0 +1,107 @@ +package iceage + +import ( + "fmt" + "math/rand" + "testing" + "time" +) + +func TestIsLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{0, 0, 0, 1, 1}, line: 3}, + {data: []int{0, 0, 0, 0, 1}, line: 4}, + {data: []int{0, 0, 0, 0, 0}, line: 5}, + } + for _, value := range testData { + if _, count := IsLine(value.data); count != value.line { + t.Error(IsLine(value.data)) + t.Error("Error line data:", value) + t.Fatal("TestIsLine") + } + } + errorData := []TestData{ + {data: []int{1, 0, 0, 0, 1}, line: -1}, + {data: []int{1, 1, 0, 0, 0}, line: -1}, + {data: []int{1, 1, 0, 1, 1}, line: -1}, + } + for _, value := range errorData { + if _, count := IsLine(value.data); count != value.line { + t.Error(IsLine(value.data)) + t.Error("Error data:", value) + t.Fatal("TestIsLine") + } + } +} +func TestCalcLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{2, 1, 3, 4, 1, 5, 6, 1, 7, 8, 1, 0, 8, 1, 7}, line: 1}, + {data: []int{6, 7, 5, 7, 6, 5, 6, 8, 8, 7, 7, 7, 6, 2, 9}, line: 1}, + {data: []int{10, 5, 9, 1, 9, 2, 7, 9, 9, 9, 5, 3, 2, 2, 9}, line: 4}, + {data: []int{3, 10, 6, 1, 3, 7, 3, 3, 3, 3, 7, 6, 10, 8, 0}, line: 6}, + {data: []int{9, 7, 10, 1, 5, 10, 4, 6, 9, 5, 7, 5, 1, 2, 4}, line: 2}, + {data: []int{9, 3, 0, 2, 0, 0, 3, 9, 0, 5, 9, 2, 2, 8, 2}, line: 2}, + } + for _, value := range testData { + lines := CalcLine(value.data, AllBetLines) + if len(lines) != value.line { + t.Log("lines:", lines) + t.Log("Error line data:", value.data) + t.Fatal("TestIsLine") + } + } +} +func TestCalcLineScore(t *testing.T) { + lines := CalcLine([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 0, 10, 8, 10, 4}, AllBetLines) + t.Log(lines) + line, allscore, _, _, _ := CaclScore([]int{9, 10, 7, 9, 7, 6, 7, 6, 10, 10, 0, 10, 8, 10, 4}, AllBetLines) + t.Logf("lineNum:%v allScore:%v", line, allscore) + t.Fatal("TestCalcLineScore") +} +func TestRandCalcLineScore(t *testing.T) { + var cards, c = generateSlotsData() + t.Logf("尝试次数:%v次,Data:%v", c, cards) + PrintHuman(cards) + lines := CalcLine(cards, AllBetLines) + t.Log(lines) + line, allscore, _, _, _ := CaclScore(cards, AllBetLines) + t.Logf("lineNum:%v allScore:%v", line, allscore) + t.Fatal("TestRandCalcLineScore") +} +func TestMakePlan(t *testing.T) { + type TestData struct { + data []int + prms []int32 + } + testData := []TestData{ + {data: []int{6, 6, 5, 7, 7, 3, 4, 6, 7, 5, 5, 1, 3, 4, 5}, prms: []int32{1, 2, 3, 3, 1, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, 4}}, + {data: []int{4, 2, 3, 5, 1, 7, 4, 3, 4, 6, 5, 5, 4, 7, 3}, prms: []int32{1, 2, 3, 3, 1, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, 4}}, + {data: []int{6, 2, 2, 7, 5, 6, 6, 6, 6, 6, 4, 6, 4, 3, 2}, prms: []int32{1, 2, 3, 3, 1, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, 4}}, + } + for _, value := range testData { + t.Log("old:", value.data) + line := MakePlan(value.data, value.prms, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}) + t.Log("new:", line) + } +} +func generateSlotsData() ([]int, int) { + var tryCount = 0 + var slotsData = make([]int, 0, ELEMENT_TOTAL) + rand.Seed(time.Now().UnixNano()) + for i := 0; i < ELEMENT_TOTAL; i++ { + slotsData = append(slotsData, symbolSmall[rand.Intn(len(symbolSmall))]) + } + tryCount++ + fmt.Print("tryCount:", tryCount) + return slotsData, tryCount +} + +var symbolSmall = []int{1, 2, 3, 3, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 7, 4, 4, 4, 4, 4, 1, 1, 5, 5, 7, 7, 7} diff --git a/gamerule/luckydice/constants.go b/gamerule/luckydice/constants.go new file mode 100644 index 0000000..05f45f3 --- /dev/null +++ b/gamerule/luckydice/constants.go @@ -0,0 +1,14 @@ +package luckydice + +// BetSide 下注大小 +const ( + Big int32 = iota + Small +) + +// 机器人下注倍率 须乘上底注 +var BetValues = [][]int64{ + {10, 20, 30, 40, 50, 60, 70, 80, 0, 90, 100, 150, 200, 250, 350, 500}, //poor 2000 + {10, 20, 50, 100, 200, 300, 400, 0, 500, 1000, 5550, 6000, 6660, 9990}, //normal 2000 + {10, 20, 50, 100, 150, 300, 500, 0, 1000, 1500, 2000, 3000, 5000, 10000, 15000, 20000}, //rich 999 +} diff --git a/gamerule/luckydice/luckydice.go b/gamerule/luckydice/luckydice.go new file mode 100644 index 0000000..0380deb --- /dev/null +++ b/gamerule/luckydice/luckydice.go @@ -0,0 +1,58 @@ +package luckydice + +import ( + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func GetBotBetValue(baseScore int64) int64 { + botWealth := rand.Intn(4999) + if botWealth < 2000 { // poor + botWealth = 0 + } else if botWealth < 4000 { // normal + botWealth = 1 + } else { // rich + botWealth = 2 + } + betValues := BetValues[botWealth] + betValue := betValues[rand.Intn(len(betValues))] + if betValue == 0 { + betValue = betValues[0] + rand.Int63n(betValues[len(betValues)-1]-betValues[0]) + } + return betValue * baseScore +} + +func GetBotBetSide() int32 { + return rand.Int31n(2) +} + +func GetDiceResult() (dr *DiceResult) { + dr = &DiceResult{} + dr.Update() + return +} + +type DiceResult struct { + Dice1 int32 + Dice2 int32 + Dice3 int32 +} + +func (dr *DiceResult) BigSmall() int32 { + if dr.Dice1+dr.Dice2+dr.Dice3 < 11 { + return Small + } else { + return Big + } +} + +func (dr *DiceResult) Update() *DiceResult { + dr.Dice1 = 1 + rand.Int31n(6) + dr.Dice2 = 1 + rand.Int31n(6) + dr.Dice3 = 1 + rand.Int31n(6) + return dr +} diff --git a/gamerule/minipoker/cards.go b/gamerule/minipoker/cards.go new file mode 100644 index 0000000..0520181 --- /dev/null +++ b/gamerule/minipoker/cards.go @@ -0,0 +1,257 @@ +package minipoker + +var cardType = [][][]int32{ + // cardType3 + { + {2, 14, 15, 28, 41}, + {2, 11, 15, 28, 41}, + {2, 6, 19, 32, 45}, + {0, 8, 21, 34, 47}, + {11, 24, 37, 49, 50}, + {7, 20, 29, 33, 46}, + {2, 4, 17, 30, 43}, + {4, 17, 30, 34, 43}, + {10, 23, 36, 37, 49}, + {11, 24, 37, 46, 50}, + {5, 18, 26, 31, 44}, + {8, 10, 23, 36, 49}, + {3, 16, 29, 39, 42}, + {4, 11, 24, 37, 50}, + {2, 15, 16, 28, 41}, + {10, 11, 24, 37, 50}, + {3, 16, 26, 29, 42}, + {10, 11, 23, 36, 49}, + {10, 23, 36, 41, 49}, + {4, 17, 26, 30, 43}, + }, + // cardType4 + { + {10, 17, 23, 36, 43}, + {3, 5, 16, 29, 31}, + {11, 17, 24, 30, 37}, + {2, 20, 33, 41, 46}, + {0, 16, 26, 29, 39}, + {5, 14, 27, 31, 44}, + {11, 19, 24, 32, 37}, + {16, 17, 29, 30, 43}, + {1, 4, 14, 17, 40}, + {1, 14, 27, 31, 44}, + {8, 18, 21, 31, 34}, + {4, 8, 30, 34, 43}, + {5, 12, 18, 31, 38}, + {8, 9, 34, 47, 48}, + {1, 14, 23, 36, 40}, + {7, 25, 38, 46, 51}, + {1, 15, 27, 28, 41}, + {7, 16, 20, 29, 42}, + {3, 7, 33, 42, 46}, + {12, 16, 29, 42, 51}, + }, + // cardType5 + { + {13, 15, 17, 20, 25}, + {27, 28, 30, 35, 38}, + {13, 14, 19, 20, 25}, + {40, 46, 47, 48, 51}, + {27, 30, 31, 37, 38}, + {14, 16, 18, 23, 25}, + {16, 19, 21, 22, 25}, + {3, 5, 7, 10, 11}, + {28, 29, 30, 31, 33}, + {14, 16, 17, 21, 25}, + {0, 1, 7, 9, 11}, + {26, 28, 33, 35, 38}, + {13, 14, 17, 19, 21}, + {40, 41, 44, 47, 48}, + {27, 29, 31, 34, 38}, + {39, 40, 41, 43, 47}, + {0, 3, 6, 9, 12}, + {4, 6, 10, 11, 12}, + {39, 41, 43, 45, 46}, + {40, 41, 42, 44, 47}, + }, + // cardType6 + { + {9, 21, 33, 44, 45}, + {10, 24, 34, 38, 48}, + {4, 5, 19, 41, 42}, + {5, 17, 19, 20, 42}, + {9, 32, 36, 46, 47}, + {8, 24, 35, 49, 51}, + {1, 4, 15, 18, 42}, + {1, 15, 17, 42, 44}, + {1, 15, 25, 39, 42}, + {20, 22, 32, 36, 47}, + {5, 9, 32, 33, 34}, + {9, 11, 12, 47, 49}, + {6, 9, 18, 34, 46}, + {6, 8, 22, 23, 46}, + {15, 25, 39, 40, 42}, + {10, 24, 25, 34, 48}, + {8, 12, 22, 36, 37}, + {8, 33, 36, 45, 48}, + {18, 27, 28, 29, 30}, + {0, 1, 15, 29, 30}, + }, + // cardType7 + { + {14, 15, 27, 40, 43}, + {8, 10, 23, 36, 51}, + {9, 22, 35, 40, 43}, + {9, 22, 23, 27, 48}, + {0, 10, 13, 18, 39}, + {6, 10, 23, 36, 42}, + {19, 28, 32, 44, 45}, + {3, 12, 16, 29, 39}, + {1, 19, 32, 34, 45}, + {20, 27, 33, 35, 46}, + {2, 19, 32, 40, 45}, + {6, 7, 19, 32, 49}, + {0, 17, 28, 30, 43}, + {13, 26, 27, 39, 45}, + {9, 12, 14, 35, 48}, + {4, 7, 21, 33, 46}, + {10, 11, 23, 31, 49}, + {8, 9, 28, 34, 47}, + {4, 13, 14, 27, 40}, + {25, 37, 38, 42, 51}, + }, + // cardType8 + { + {5, 9, 23, 35, 36}, + {22, 28, 31, 35, 41}, + {5, 6, 18, 21, 32}, + {10, 22, 30, 43, 49}, + {2, 10, 15, 24, 49}, + {10, 12, 23, 25, 40}, + {30, 32, 43, 45, 51}, + {10, 20, 22, 23, 35}, + {8, 13, 17, 39, 43}, + {23, 28, 36, 37, 50}, + {3, 17, 35, 42, 43}, + {1, 6, 27, 31, 45}, + {25, 35, 44, 48, 51}, + {1, 10, 14, 29, 49}, + {12, 22, 25, 47, 48}, + {23, 30, 31, 36, 44}, + {1, 3, 37, 42, 50}, + {0, 4, 26, 30, 49}, + {1, 7, 13, 26, 40}, + {2, 28, 37, 39, 50}, + }, + // cardType9 + { + {6, 9, 14, 22, 37}, + {2, 9, 16, 22, 25}, + {16, 24, 26, 35, 37}, + {5, 22, 29, 43, 48}, + {19, 24, 29, 33, 37}, + {5, 20, 38, 40, 51}, + {11, 17, 22, 29, 50}, + {11, 17, 32, 37, 44}, + {11, 15, 20, 24, 44}, + {9, 24, 37, 43, 44}, + {22, 35, 41, 50, 51}, + {10, 12, 15, 18, 36}, + {10, 32, 46, 47, 49}, + {1, 24, 35, 45, 50}, + {15, 34, 37, 38, 50}, + {12, 19, 31, 38, 40}, + {9, 11, 21, 42, 50}, + {14, 16, 35, 45, 48}, + {2, 6, 12, 25, 34}, + {14, 15, 35, 48, 49}, + }, + // cardType10 + { + {6, 19, 28, 34, 46}, + {1, 13, 14, 28, 34}, + {6, 9, 25, 45, 47}, + {15, 17, 19, 45, 49}, + {27, 37, 40, 44, 51}, + {1, 14, 30, 33, 48}, + {3, 6, 16, 20, 39}, + {4, 6, 10, 17, 34}, + {2, 5, 26, 27, 31}, + {0, 20, 39, 42, 48}, + {6, 9, 15, 19, 24}, + {0, 10, 13, 21, 51}, + {20, 23, 28, 29, 41}, + {3, 17, 30, 31, 48}, + {1, 21, 27, 49, 51}, + {12, 22, 32, 45, 46}, + {7, 24, 26, 32, 46}, + {6, 22, 28, 32, 39}, + {4, 5, 8, 31, 37}, + {0, 10, 39, 44, 51}, + }, + // cardType11 + { + {2, 14, 17, 44, 49}, + {0, 5, 30, 38, 49}, + {4, 24, 35, 39, 47}, + {26, 29, 45, 48, 51}, + {4, 5, 7, 13, 35}, + {8, 25, 36, 43, 45}, + {2, 4, 6, 20, 49}, + {22, 25, 31, 33, 40}, + {26, 27, 30, 42, 47}, + {11, 20, 22, 30, 32}, + {3, 24, 35, 36, 47}, + {4, 11, 34, 40, 45}, + {1, 5, 9, 21, 51}, + {10, 26, 38, 42, 45}, + {6, 8, 28, 40, 51}, + {6, 12, 13, 22, 24}, + {22, 30, 38, 44, 45}, + {8, 24, 41, 45, 51}, + {6, 8, 12, 28, 48}, + {11, 17, 39, 41, 47}, + }, + // cardType12 + { + {6, 7, 8, 9, 10}, + {7, 8, 9, 10, 11}, + {34, 35, 36, 37, 38}, + {33, 34, 35, 36, 37}, + {45, 46, 47, 48, 49}, + {19, 20, 21, 22, 23}, + {44, 45, 46, 47, 48}, + {31, 32, 33, 34, 35}, + {32, 33, 34, 35, 36}, + {21, 22, 23, 24, 25}, + {5, 6, 7, 8, 9}, + {8, 9, 10, 11, 12}, + {18, 19, 20, 21, 22}, + {47, 48, 49, 50, 51}, + {20, 21, 22, 23, 24}, + {46, 47, 48, 49, 50}, + }, + // cardType13 + { + {16, 17, 18, 19, 20}, + {13, 14, 15, 16, 25}, + {26, 27, 28, 29, 38}, + {4, 5, 6, 7, 8}, + {14, 15, 16, 17, 18}, + {17, 18, 19, 20, 21}, + {2, 3, 4, 5, 6}, + {15, 16, 17, 18, 19}, + {13, 14, 15, 16, 17}, + {43, 44, 45, 46, 47}, + {41, 42, 43, 44, 45}, + {0, 1, 2, 3, 4}, + {1, 2, 3, 4, 5}, + {27, 28, 29, 30, 31}, + {30, 31, 32, 33, 34}, + {39, 40, 41, 42, 51}, + {42, 43, 44, 45, 46}, + {28, 29, 30, 31, 32}, + {0, 1, 2, 3, 12}, + {26, 27, 28, 29, 30}, + {3, 4, 5, 6, 7}, + {39, 40, 41, 42, 43}, + {29, 30, 31, 32, 33}, + {40, 41, 42, 43, 44}, + }, +} diff --git a/gamerule/minipoker/constants.go b/gamerule/minipoker/constants.go new file mode 100644 index 0000000..2a3b04f --- /dev/null +++ b/gamerule/minipoker/constants.go @@ -0,0 +1,65 @@ +package minipoker + +// MiniPoker +const ( + CARDTYPE_MIN int = iota + 2 // 2 MIN + CARDTYPE_FOURCARD // 3 四张 + CARDTYPE_THREETAKEPAIR // 4 三带对 + CARDTYPE_FLUSH // 5 同花 + CARDTYPE_STRAIGHT // 6 顺子 + CARDTYPE_THREETAKESINGLE // 7 三带单 + CARDTYPE_TWOPAIR // 8 两对 + CARDTYPE_ONEPAIR_J // 9 一对 J+ (对J 对Q 对K 对A) + CARDTYPE_ONEPAIR // 10 一对 (2-10 的对子) + CARDTYPE_HIGHCARD // 11 散牌 + CARDTYPE_STRAIGHT_FLUSH_J // 12 同花顺 J (7、8、9、10开头的顺子) 爆奖 + CARDTYPE_STRAIGHT_FLUSH // 13 同花顺 (A、2、3、4、5、6开头的顺子) + CARDTYPE_MAX // 14 MAX +) + +const ( + CARDSTYPE_NUM = 13 // 牌型数 + CARDDATANUM = 52 // 牌库数 + CARDNUM = 5 // 牌数 + JACKPOTTIMEINTERVAL = 24 // 单位小时 +) + +// jack params +const ( + MINIPOKER_JACKPOT_InitJackpot int = iota //初始化奖池倍率 +) + +// 牌型赔率 做相应 * 10 / 10 处理 +var cardsTypeRate = [CARDSTYPE_NUM]int{0, 0, 1500, 500, 200, 130, 80, 50, 25, 0, 0, 0, 10000} + +// CardID 下标索引 CardName 下标索引对应值 +var cardName = [CARDDATANUM]string{ + "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♠", // 0-12 黑桃 + "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♣", // 13-25 梅花 + "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦", "A♦", // 26-38 方片 + "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♥", // 39-51 红桃 +} + +// 牌库数据 +var cardData = [CARDDATANUM]int32{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +} + +// CardID 下标索引 CardName 下标索引对应值 +var cardName2 = [CARDDATANUM]string{ + "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦", // 26-38 方片 + "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", // 13-25 梅花 + "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", // 39-51 红桃 + "A♠", "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", // 0-12 黑桃 +} + +// 牌库数据 +var cardData2 = [CARDDATANUM]int32{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +} diff --git a/gamerule/minipoker/minipoker.go b/gamerule/minipoker/minipoker.go new file mode 100644 index 0000000..a394d6d --- /dev/null +++ b/gamerule/minipoker/minipoker.go @@ -0,0 +1,388 @@ +package minipoker + +import ( + "math/rand" + "sort" + "time" +) + +type Int32Slice []int32 + +func (a Int32Slice) Len() int { return len(a) } +func (a Int32Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a Int32Slice) Less(i, j int) bool { return a[i] < a[j] } + +func CardInit() []int32 { + return cardDataShuffle(cardData[:]) +} + +func cardDataShuffle(cardData []int32) []int32 { + rand.Seed(time.Now().UnixNano()) + num := len(cardData) + for i := 0; i < num; i++ { + n := rand.Intn(num - i) + cardData[i], cardData[n] = cardData[n], cardData[i] + } + return cardData +} + +func GetCards() []int32 { + cardsData := cardDataShuffle(cardData[:]) // 洗牌 + // 随机开始区牌索引 + pos := rand.Intn(CARDDATANUM - CARDNUM) + return cardsData[pos : pos+CARDNUM] +} +func GetCardsName(cards []int32) string { + var s string + for _, v := range cards { + s += cardName[int(v)] + } + return s +} + +func GetWinRate(cardsType int) int32 { + return int32(cardsTypeRate[cardsType-1]) +} +func CheckCardsType(cardsType int) bool { + if cardsType < CARDTYPE_MIN || cardsType > CARDTYPE_MAX { + return false + } + if cardsType != CARDTYPE_STRAIGHT_FLUSH && + cardsType != CARDTYPE_FOURCARD && cardsType != CARDTYPE_THREETAKEPAIR && cardsType != CARDTYPE_FLUSH { + return true + } + return false +} + +// 计算牌型分 +func CalcCardsTypeScore(betValue int64, cardsType int) int64 { + return betValue * int64(cardsTypeRate[cardsType-1]) / 10 +} + +// 计算牌型 +func CalcCardsType(cards []int32) int { + if len(cards) != CARDNUM { + return -1 + } + cardsTemp := make([]int32, len(cards)) + copy(cardsTemp, cards) + // 同花顺 + if isStraightFlush(cardsTemp) { + if isStraightFlushJ(cardsTemp) { + return CARDTYPE_STRAIGHT_FLUSH_J + } + return CARDTYPE_STRAIGHT_FLUSH + } + // 四张 + if isFourCard(cardsTemp) { + return CARDTYPE_FOURCARD + } + // 三带对 + if isThreeCard(cardsTemp) { + cardsDataTemp := make([]int32, len(cards)) + copy(cardsDataTemp, cards) + rmdCard := rmThreeCard(cardsDataTemp) + if isHavePair(rmdCard) { + return CARDTYPE_THREETAKEPAIR + } + } + // 同花 + if isFlush(cardsTemp) { + return CARDTYPE_FLUSH + } + // 顺子 + if isStraight(cardsTemp) { + return CARDTYPE_STRAIGHT + } + // 三带单 + if isThreeCard(cardsTemp) { + cardsDataTemp := make([]int32, len(cards)) + copy(cardsDataTemp, cards) + rmdCard := rmThreeCard(cardsDataTemp) + if !isHavePair(rmdCard) { + return CARDTYPE_THREETAKESINGLE + } + } + // 两对 + if isHavePair(cardsTemp) { + cardsDataTemp := make([]int32, len(cards)) + copy(cardsDataTemp, cards) + rmdCard := rmPair(cardsDataTemp) + if isHavePair(rmdCard) { + return CARDTYPE_TWOPAIR + } + } + // 一对 + if isHavePair(cardsTemp) { + if isPairJ(cardsTemp) { + return CARDTYPE_ONEPAIR_J + } + return CARDTYPE_ONEPAIR + } + // 散牌 + if isHighCard(cardsTemp) { + return CARDTYPE_HIGHCARD + } + return -1 +} + +// 同花顺 J +func isStraightFlushJ(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + cardsValue := getCardsValue(cards) + sort.Sort(Int32Slice(cardsValue)) + if cardsValue[1] > 6 { + return true + } + return false +} + +// 同花顺 +func isStraightFlush(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + if isStraight(cards) && isFlush(cards) { + return true + } + return false +} + +// 顺子 +func isStraight(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + cardsValue := getCardsValue(cards) + sort.Sort(Int32Slice(cardsValue)) + cv := cardsValue[0] + + for i := 1; i < len(cardsValue); i++ { + //// A2345,A10JQK + // //if i == len(cardsValue)-1 && cardsValue[i] == 12 && (cardsValue[0] == 0 || cardsValue[len(cardsValue)-1] == 11) { + // // return true + // //} + // //if cardsValue[i] != cv+1 { + // // break + // //} + // //if i == len(cardsValue)-1 { + // // return true + // //} + // //cv = cardsValue[i] + + if cardsValue[i] != cv+1 { + if cardsValue[0] == 0 && i == 1 && cardsValue[1] == 9 { //A10JQK + continue + } + return false + } + + cv = cardsValue[i] + return true + } + return false +} + +// 同花 +func isFlush(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + cardsType := getCardsType(cards) + ct := cardsType[0] + for i := 1; i < len(cardsType); i++ { + if ct != cardsType[i] { + return false + } + } + return true +} + +// 四张 +func isFourCard(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + cardsValue := getCardsValue(cards) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + if cvMap[v] >= 4 { + return true + } + } + return false +} + +// 三张 +func isThreeCard(cards []int32) bool { + if len(cards) != CARDNUM { + return false + } + cardsValue := getCardsValue(cards) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + if cvMap[v] >= 3 { + return true + } + } + return false +} + +// 去除三张 +func rmThreeCard(cards []int32) []int32 { + if len(cards) != CARDNUM { + return nil + } + cardsTemp := make([]int32, len(cards)) + copy(cardsTemp, cards) + + cardsValue := getCardsValue(cardsTemp) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + } + + var rmCardValue int32 + for c, n := range cvMap { + if n == 3 { + rmCardValue = c + } + } + for i := 0; i < len(cardsTemp); i++ { + if int32(getCardValue(cardsTemp[i])) == rmCardValue { + cardsTemp = append(cardsTemp[:i], cardsTemp[i+1:]...) + if i > 0 { + i-- + } + } + } + return cardsTemp +} + +// 去除对子 +func rmPair(cards []int32) []int32 { + if len(cards) != CARDNUM { + return nil + } + cardsTemp := make([]int32, len(cards)) + copy(cardsTemp, cards) + + cardsValue := getCardsValue(cardsTemp) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + } + + var rmCardValue int32 + for c, n := range cvMap { + if n == 2 { + rmCardValue = c + } + } + for i := 0; i < len(cardsTemp); i++ { + if int32(getCardValue(cardsTemp[i])) == rmCardValue { + cardsTemp = append(cardsTemp[:i], cardsTemp[i+1:]...) + if i > 0 { + i-- + } + } + } + return cardsTemp +} + +// 对子 +func isHavePair(cards []int32) bool { + cardsValue := getCardsValue(cards) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + if cvMap[v] >= 2 { + return true + } + } + return false +} + +// 对子(J、Q、K、A) +func isPairJ(cards []int32) bool { + cardsValue := getCardsValue(cards) + sort.Sort(Int32Slice(cardsValue)) + for i := 1; i < len(cardsValue); i++ { + if cardsValue[i-1] == cardsValue[i] && (cardsValue[i] > 9 || cardsValue[i] == 0) { + return true + } + } + return false +} + +// 散牌 +func isHighCard(cards []int32) bool { + if !isFlush(cards) && !isStraight(cards) && !isFourCard(cards) && !isThreeCard(cards) && !isHavePair(cards) { + return true + } + return false +} + +// 获取一对的值和数量 (测试跑数据使用,日后可删除) +func GetCardsPair(cards []int32, cardsMap map[int32]int32) { + cardsValue := getCardsValue(cards) + cvMap := make(map[int32]int, len(cardsValue)) + for _, v := range cardsValue { + if _, ok := cvMap[v]; !ok { + cvMap[v] = 0 + } + cvMap[v]++ + if cvMap[v] == 2 { + if _, ok := cardsMap[v]; !ok { + cardsMap[v] = 0 + continue + } + cardsMap[v]++ + } + } + return +} + +func getCardsValue(cards []int32) []int32 { + cardsTemp := make([]int32, len(cards)) + copy(cardsTemp, cards) + for i := 0; i < len(cardsTemp); i++ { + cardsTemp[i] = int32(getCardValue(cardsTemp[i])) + } + return cardsTemp +} + +func getCardsType(cards []int32) []int32 { + cardsTemp := make([]int32, len(cards)) + copy(cardsTemp, cards) + for i := 0; i < len(cardsTemp); i++ { + cardsTemp[i] = int32(getCardType(cardsTemp[i])) + } + return cardsTemp +} + +func getCardType(card int32) int { + return int(card) / 13 +} +func getCardValue(card int32) int { + return int(card) % 13 +} diff --git a/gamerule/minipoker/minipoker_test.go b/gamerule/minipoker/minipoker_test.go new file mode 100644 index 0000000..bd21a78 --- /dev/null +++ b/gamerule/minipoker/minipoker_test.go @@ -0,0 +1,136 @@ +package minipoker + +import ( + "fmt" + "os" + "testing" +) + +var betValue = int64(10) + +func TestCalcCardsTypeScore(t *testing.T) { + type args struct { + betValue int64 + cardsType int + } + tests := []struct { + name string + args args + want int64 + }{ + {"cardtype-3", args{betValue, 3}, 1500}, + {"cardtype-4", args{betValue, 4}, 500}, + {"cardtype-5", args{betValue, 5}, 200}, + {"cardtype-6", args{betValue, 6}, 130}, + {"cardtype-7", args{betValue, 7}, 80}, + {"cardtype-8", args{betValue, 8}, 50}, + {"cardtype-9", args{betValue, 9}, 25}, + {"cardtype-10", args{betValue, 10}, 0}, + {"cardtype-11", args{betValue, 11}, 0}, + {"cardtype-12", args{betValue, 12}, 0}, + {"cardtype-13", args{betValue, 13}, 10000}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalcCardsTypeScore(tt.args.betValue, tt.args.cardsType); got != tt.want { + t.Errorf("CalcCardsTypeScore() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCalcCardsType(t *testing.T) { + type args struct { + cards []int32 + } + tests := []struct { + name string + args args + want int + }{} + fileName := "test.csv" + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + defer file.Close() + if err != nil { + file, err = os.Create(fileName) + if err != nil { + return + } + } + file.WriteString("cards\t cardsType\t wantCardsType\r\n") + for i := 0; i < len(cardType); i++ { + for j := 0; j < len(cardType[i]); j++ { + tests = append(tests, struct { + name string + args args + want int + }{ + fmt.Sprintf("cardtype-%d-%d", i+3, j), + args{cardType[i][j]}, + i + 3, + }) + } + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalcCardsType(tt.args.cards); got != tt.want { + if (tt.want == 10 || tt.want == 11) && got != 10 && got != 11 { + t.Errorf("CalcCardsType() = %v, want %v", got, tt.want) + // str := fmt.Sprintf("{%v}\n", tt.args.cards) + str := fmt.Sprintf("%v\t %v\t %v\r\n", tt.args.cards, got, tt.want) + file.WriteString(str) + } + } + }) + } +} + +func TestGetCardsName(t *testing.T) { + type args struct { + cards []int32 + } + tests := []struct { + name string + args args + want string + }{ + {"cardtype-3-2", args{[]int32{6, 19, 28, 34, 46}}, "test"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetCardsName(tt.args.cards); got != tt.want { + t.Errorf("GetCardsName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetCardsPair(t *testing.T) { + type args struct { + cards []int32 + cardsMap map[int32]int32 + } + tests := []struct { + name string + args args + }{} + var cardsMap = make(map[int32]int32, 0) + for i := 0; i < len(pair); i++ { + tests = append(tests, struct { + name string + args args + }{ + fmt.Sprintf("GetCardsPair-%d", i+1), + args{pair[i], cardsMap}, + }) + } + for _, tt := range tests { + // t.Run(tt.name, func(t *testing.T) { + GetCardsPair(tt.args.cards, cardsMap) + // }) + } + for k, v := range cardsMap { + fmt.Printf("cardvalue %v num %v \n", k, v) + } +} diff --git a/gamerule/minipoker/pair.go b/gamerule/minipoker/pair.go new file mode 100644 index 0000000..50efbd7 --- /dev/null +++ b/gamerule/minipoker/pair.go @@ -0,0 +1,10004 @@ +package minipoker + +var pair = [][]int32{ + {6, 19, 28, 34, 46}, + {1, 13, 14, 28, 34}, + {6, 9, 25, 45, 47}, + {15, 17, 19, 45, 49}, + {27, 37, 40, 44, 51}, + {1, 14, 30, 33, 48}, + {3, 6, 16, 20, 39}, + {4, 6, 10, 17, 34}, + {2, 5, 26, 27, 31}, + {0, 20, 39, 42, 48}, + {6, 9, 15, 19, 24}, + {0, 10, 13, 21, 51}, + {20, 23, 28, 29, 41}, + {3, 17, 30, 31, 48}, + {1, 21, 27, 49, 51}, + {12, 22, 32, 45, 46}, + {7, 24, 26, 32, 46}, + {6, 22, 28, 32, 39}, + {4, 5, 8, 31, 37}, + {0, 10, 39, 44, 51}, + {14, 17, 30, 39, 47}, + {15, 24, 27, 28, 34}, + {3, 18, 27, 32, 40}, + {6, 15, 18, 20, 46}, + {21, 30, 32, 45, 50}, + {5, 9, 30, 43, 49}, + {8, 15, 28, 29, 44}, + {0, 1, 7, 19, 40}, + {4, 30, 41, 42, 50}, + {17, 21, 24, 32, 45}, + {5, 11, 18, 38, 48}, + {4, 27, 29, 42, 48}, + {7, 9, 15, 33, 51}, + {2, 7, 22, 41, 45}, + {14, 17, 22, 27, 51}, + {0, 4, 14, 33, 40}, + {0, 26, 28, 37, 46}, + {3, 9, 26, 42, 51}, + {5, 13, 18, 43, 48}, + {7, 8, 14, 28, 34}, + {9, 27, 29, 30, 43}, + {6, 14, 24, 40, 44}, + {7, 19, 29, 30, 46}, + {5, 8, 15, 20, 41}, + {3, 5, 15, 28, 51}, + {10, 19, 25, 27, 32}, + {18, 26, 31, 36, 42}, + {6, 7, 18, 33, 37}, + {2, 11, 13, 31, 44}, + {7, 14, 33, 35, 47}, + {0, 10, 13, 27, 33}, + {1, 3, 10, 13, 16}, + {2, 13, 19, 39, 43}, + {10, 11, 26, 39, 51}, + {25, 29, 31, 37, 42}, + {4, 5, 36, 43, 47}, + {1, 14, 21, 41, 50}, + {3, 5, 26, 28, 31}, + {3, 15, 16, 21, 50}, + {0, 21, 39, 46, 49}, + {4, 7, 9, 17, 31}, + {9, 16, 29, 39, 44}, + {4, 14, 36, 43, 45}, + {8, 18, 38, 42, 47}, + {1, 21, 31, 40, 41}, + {0, 5, 8, 15, 31}, + {15, 27, 28, 29, 43}, + {4, 7, 13, 30, 50}, + {5, 29, 34, 44, 50}, + {1, 3, 5, 42, 45}, + {12, 17, 31, 44, 46}, + {8, 22, 30, 43, 45}, + {16, 17, 20, 26, 30}, + {5, 19, 26, 45, 49}, + {3, 4, 17, 36, 48}, + {5, 18, 32, 43, 46}, + {3, 5, 21, 42, 51}, + {8, 13, 18, 31, 37}, + {1, 9, 15, 28, 38}, + {1, 29, 39, 42, 51}, + {3, 29, 31, 37, 51}, + {4, 7, 26, 33, 48}, + {2, 3, 16, 35, 39}, + {18, 20, 24, 31, 41}, + {6, 7, 10, 45, 47}, + {2, 8, 15, 20, 49}, + {2, 5, 8, 19, 34}, + {2, 14, 19, 27, 38}, + {28, 29, 30, 40, 43}, + {3, 15, 28, 36, 46}, + {10, 13, 17, 43, 46}, + {5, 10, 15, 31, 35}, + {6, 30, 36, 43, 50}, + {4, 22, 27, 30, 34}, + {19, 28, 34, 47, 51}, + {3, 5, 15, 17, 31}, + {14, 15, 19, 28, 39}, + {5, 12, 18, 19, 35}, + {15, 19, 28, 37, 51}, + {6, 20, 27, 44, 45}, + {12, 16, 29, 32, 39}, + {32, 42, 43, 45, 50}, + {1, 8, 20, 26, 39}, + {13, 16, 27, 40, 47}, + {1, 5, 30, 38, 43}, + {0, 9, 25, 34, 39}, + {15, 33, 38, 39, 46}, + {8, 13, 33, 34, 51}, + {7, 28, 36, 41, 43}, + {3, 9, 12, 31, 42}, + {26, 27, 34, 40, 42}, + {29, 34, 45, 47, 48}, + {6, 13, 15, 29, 42}, + {7, 18, 19, 26, 31}, + {6, 19, 24, 42, 47}, + {14, 20, 41, 44, 46}, + {4, 13, 30, 40, 44}, + {8, 21, 29, 41, 45}, + {5, 8, 12, 26, 34}, + {4, 13, 22, 39, 47}, + {2, 6, 23, 41, 46}, + {4, 14, 23, 30, 38}, + {20, 25, 43, 44, 46}, + {3, 7, 31, 33, 37}, + {3, 16, 25, 28, 46}, + {1, 2, 20, 28, 45}, + {20, 33, 40, 44, 48}, + {13, 22, 23, 39, 51}, + {8, 15, 17, 25, 47}, + {5, 13, 20, 26, 50}, + {0, 13, 28, 44, 48}, + {13, 32, 41, 45, 46}, + {1, 11, 17, 40, 51}, + {1, 18, 35, 40, 45}, + {1, 14, 39, 44, 50}, + {1, 24, 40, 44, 48}, + {0, 10, 31, 38, 44}, + {9, 14, 16, 42, 44}, + {17, 19, 21, 32, 46}, + {15, 17, 27, 28, 37}, + {6, 11, 16, 45, 46}, + {7, 13, 18, 39, 50}, + {13, 18, 27, 39, 48}, + {1, 14, 16, 20, 38}, + {21, 34, 43, 44, 46}, + {0, 22, 37, 39, 46}, + {17, 19, 28, 41, 51}, + {9, 15, 32, 36, 45}, + {3, 13, 20, 24, 39}, + {5, 9, 21, 42, 47}, + {1, 34, 43, 47, 49}, + {7, 13, 17, 18, 43}, + {8, 19, 33, 45, 51}, + {2, 15, 22, 24, 38}, + {2, 5, 10, 33, 46}, + {13, 19, 21, 47, 51}, + {2, 4, 8, 21, 45}, + {2, 9, 28, 32, 46}, + {15, 20, 21, 31, 46}, + {16, 26, 30, 40, 43}, + {13, 20, 32, 34, 46}, + {3, 18, 22, 31, 36}, + {3, 14, 21, 22, 34}, + {0, 11, 18, 26, 30}, + {3, 12, 19, 27, 32}, + {0, 5, 18, 21, 24}, + {7, 27, 33, 41, 42}, + {4, 5, 11, 14, 18}, + {5, 16, 22, 29, 51}, + {4, 8, 21, 32, 40}, + {0, 17, 26, 27, 29}, + {12, 18, 29, 30, 44}, + {5, 14, 29, 35, 44}, + {2, 18, 26, 35, 39}, + {5, 6, 18, 26, 46}, + {28, 31, 34, 44, 48}, + {4, 11, 16, 26, 29}, + {20, 27, 29, 37, 42}, + {11, 15, 30, 35, 41}, + {13, 16, 21, 30, 47}, + {2, 13, 18, 22, 44}, + {2, 14, 41, 45, 51}, + {1, 14, 21, 29, 49}, + {3, 17, 27, 30, 37}, + {3, 14, 24, 40, 46}, + {0, 4, 26, 32, 46}, + {15, 28, 30, 32, 39}, + {0, 7, 23, 24, 26}, + {0, 8, 23, 39, 42}, + {1, 2, 3, 20, 33}, + {1, 15, 38, 40, 46}, + {0, 4, 27, 30, 37}, + {4, 7, 16, 36, 43}, + {4, 9, 18, 26, 39}, + {18, 19, 31, 50, 51}, + {8, 13, 36, 39, 40}, + {2, 15, 26, 36, 44}, + {16, 19, 22, 32, 34}, + {3, 12, 16, 27, 41}, + {22, 32, 36, 37, 45}, + {1, 13, 18, 25, 40}, + {2, 16, 28, 47, 48}, + {4, 15, 24, 26, 39}, + {5, 26, 34, 47, 48}, + {2, 13, 15, 21, 30}, + {1, 5, 18, 19, 46}, + {21, 29, 30, 42, 50}, + {0, 15, 28, 33, 43}, + {21, 28, 30, 43, 45}, + {2, 14, 20, 26, 46}, + {7, 11, 21, 46, 48}, + {14, 16, 22, 42, 50}, + {7, 18, 19, 33, 34}, + {4, 7, 23, 33, 34}, + {3, 8, 14, 19, 21}, + {0, 22, 26, 28, 50}, + {5, 7, 34, 44, 49}, + {4, 8, 11, 30, 35}, + {0, 2, 12, 16, 28}, + {2, 4, 22, 40, 41}, + {5, 6, 10, 32, 50}, + {4, 13, 23, 43, 48}, + {24, 27, 31, 38, 44}, + {9, 12, 18, 44, 49}, + {4, 7, 19, 46, 50}, + {0, 13, 25, 29, 46}, + {4, 13, 17, 22, 42}, + {6, 10, 25, 32, 44}, + {12, 17, 23, 31, 44}, + {3, 5, 8, 20, 44}, + {7, 11, 45, 46, 51}, + {10, 18, 21, 31, 48}, + {6, 11, 32, 35, 51}, + {0, 3, 4, 36, 42}, + {10, 31, 32, 44, 46}, + {3, 21, 25, 35, 42}, + {1, 8, 20, 27, 30}, + {7, 8, 11, 34, 51}, + {4, 33, 34, 41, 43}, + {4, 14, 22, 43, 45}, + {1, 6, 17, 24, 45}, + {2, 13, 28, 34, 50}, + {0, 12, 26, 31, 37}, + {18, 23, 26, 44, 46}, + {6, 20, 30, 33, 35}, + {16, 41, 42, 44, 45}, + {5, 10, 18, 33, 40}, + {8, 9, 19, 25, 32}, + {9, 13, 21, 27, 47}, + {21, 29, 31, 47, 51}, + {13, 23, 30, 43, 50}, + {5, 19, 31, 39, 47}, + {3, 10, 17, 21, 30}, + {16, 29, 31, 33, 49}, + {0, 15, 22, 39, 46}, + {8, 26, 31, 34, 48}, + {5, 7, 33, 40, 42}, + {0, 4, 7, 37, 46}, + {14, 15, 36, 40, 42}, + {7, 17, 21, 39, 43}, + {18, 20, 29, 35, 42}, + {10, 12, 13, 26, 44}, + {5, 15, 27, 41, 42}, + {14, 26, 27, 28, 32}, + {21, 25, 31, 47, 50}, + {13, 20, 26, 36, 43}, + {1, 4, 14, 28, 31}, + {14, 15, 30, 32, 43}, + {1, 14, 19, 25, 43}, + {7, 17, 22, 28, 33}, + {3, 11, 15, 23, 29}, + {8, 30, 38, 42, 43}, + {2, 15, 23, 39, 47}, + {0, 3, 32, 39, 43}, + {0, 11, 13, 15, 33}, + {0, 3, 5, 10, 18}, + {9, 33, 40, 46, 47}, + {0, 7, 15, 16, 42}, + {3, 5, 19, 31, 33}, + {3, 19, 22, 24, 29}, + {28, 31, 35, 39, 41}, + {3, 4, 24, 30, 41}, + {2, 12, 15, 37, 42}, + {2, 4, 6, 14, 28}, + {12, 28, 33, 35, 46}, + {13, 15, 38, 39, 49}, + {0, 5, 26, 27, 37}, + {1, 6, 23, 32, 33}, + {11, 16, 17, 27, 43}, + {2, 3, 5, 11, 29}, + {0, 23, 26, 42, 45}, + {3, 9, 11, 15, 29}, + {2, 4, 7, 28, 45}, + {3, 5, 19, 21, 44}, + {1, 16, 27, 50, 51}, + {4, 22, 24, 39, 43}, + {7, 8, 16, 20, 30}, + {29, 33, 43, 44, 46}, + {7, 21, 26, 38, 46}, + {3, 13, 17, 26, 44}, + {0, 1, 34, 36, 40}, + {2, 15, 25, 30, 31}, + {2, 20, 22, 24, 33}, + {4, 30, 32, 36, 47}, + {6, 17, 22, 32, 51}, + {7, 21, 31, 33, 43}, + {7, 17, 28, 41, 49}, + {0, 5, 6, 25, 39}, + {2, 15, 19, 21, 50}, + {21, 24, 40, 47, 49}, + {6, 15, 18, 19, 48}, + {1, 5, 13, 40, 42}, + {12, 19, 26, 35, 39}, + {9, 15, 41, 44, 47}, + {0, 18, 30, 33, 39}, + {7, 11, 15, 20, 43}, + {2, 5, 17, 18, 34}, + {0, 1, 27, 34, 44}, + {13, 16, 28, 41, 49}, + {3, 5, 16, 19, 20}, + {0, 5, 8, 13, 32}, + {8, 26, 32, 39, 51}, + {1, 4, 17, 18, 32}, + {2, 19, 23, 28, 48}, + {4, 8, 30, 46, 49}, + {6, 19, 28, 42, 50}, + {13, 16, 19, 31, 39}, + {12, 17, 26, 31, 43}, + {7, 13, 20, 23, 32}, + {15, 16, 42, 43, 49}, + {4, 12, 13, 26, 29}, + {0, 13, 25, 32, 47}, + {2, 8, 21, 30, 33}, + {11, 15, 28, 40, 46}, + {1, 3, 14, 33, 50}, + {17, 21, 25, 40, 43}, + {17, 19, 27, 39, 43}, + {19, 29, 32, 34, 49}, + {1, 5, 9, 16, 42}, + {8, 16, 19, 42, 46}, + {1, 7, 19, 22, 33}, + {6, 13, 27, 44, 45}, + {8, 11, 14, 33, 46}, + {3, 10, 42, 43, 50}, + {0, 7, 22, 36, 46}, + {2, 7, 15, 35, 47}, + {2, 12, 26, 31, 41}, + {21, 25, 30, 33, 43}, + {28, 31, 43, 44, 50}, + {6, 8, 10, 32, 48}, + {5, 8, 13, 15, 31}, + {4, 10, 30, 34, 45}, + {0, 5, 10, 44, 45}, + {4, 21, 29, 43, 48}, + {2, 3, 12, 41, 49}, + {2, 6, 28, 40, 48}, + {15, 20, 38, 41, 45}, + {7, 38, 40, 44, 46}, + {3, 21, 29, 36, 37}, + {8, 11, 18, 28, 44}, + {20, 26, 39, 41, 48}, + {7, 17, 18, 33, 42}, + {0, 12, 17, 26, 34}, + {0, 12, 19, 37, 45}, + {0, 6, 8, 13, 27}, + {14, 23, 28, 29, 40}, + {12, 15, 21, 28, 48}, + {2, 6, 8, 11, 45}, + {0, 6, 10, 14, 26}, + {4, 25, 30, 42, 47}, + {2, 6, 15, 43, 46}, + {3, 23, 33, 42, 50}, + {6, 32, 33, 42, 51}, + {11, 26, 33, 39, 45}, + {11, 17, 21, 22, 30}, + {3, 7, 22, 33, 43}, + {0, 17, 26, 45, 47}, + {2, 7, 34, 47, 51}, + {17, 30, 33, 35, 36}, + {8, 24, 27, 28, 40}, + {4, 8, 16, 22, 42}, + {2, 5, 13, 16, 31}, + {4, 13, 42, 43, 51}, + {2, 17, 30, 47, 48}, + {3, 11, 13, 16, 30}, + {15, 20, 28, 40, 48}, + {7, 8, 19, 21, 44}, + {4, 22, 30, 46, 47}, + {6, 8, 24, 29, 45}, + {0, 28, 33, 35, 46}, + {19, 27, 32, 41, 46}, + {0, 18, 19, 32, 50}, + {5, 14, 15, 41, 42}, + {17, 30, 33, 38, 45}, + {4, 14, 19, 43, 44}, + {0, 26, 34, 42, 48}, + {6, 10, 25, 45, 47}, + {7, 12, 15, 16, 41}, + {15, 17, 27, 36, 41}, + {6, 18, 44, 49, 50}, + {8, 11, 19, 32, 39}, + {11, 17, 43, 44, 47}, + {3, 12, 19, 20, 32}, + {7, 21, 35, 38, 47}, + {4, 21, 30, 36, 41}, + {6, 12, 32, 39, 50}, + {8, 14, 20, 31, 44}, + {2, 5, 17, 31, 47}, + {1, 8, 17, 30, 36}, + {2, 6, 27, 40, 49}, + {3, 4, 39, 42, 51}, + {3, 4, 7, 20, 34}, + {9, 28, 33, 34, 46}, + {0, 3, 34, 42, 51}, + {14, 28, 35, 41, 43}, + {5, 14, 36, 38, 44}, + {6, 14, 17, 19, 21}, + {1, 9, 27, 29, 45}, + {8, 13, 21, 33, 42}, + {3, 6, 21, 47, 49}, + {17, 24, 27, 40, 45}, + {2, 5, 18, 30, 49}, + {6, 16, 22, 26, 39}, + {4, 8, 30, 35, 38}, + {3, 8, 14, 15, 47}, + {7, 8, 15, 40, 41}, + {1, 2, 3, 14, 48}, + {9, 15, 20, 41, 47}, + {2, 9, 14, 28, 44}, + {4, 10, 14, 16, 27}, + {16, 23, 29, 39, 51}, + {2, 19, 23, 25, 28}, + {11, 13, 29, 41, 42}, + {13, 15, 17, 22, 39}, + {0, 4, 17, 22, 29}, + {1, 15, 17, 23, 27}, + {6, 16, 19, 38, 40}, + {7, 14, 16, 29, 47}, + {0, 15, 28, 29, 47}, + {0, 17, 28, 34, 43}, + {2, 12, 18, 32, 41}, + {10, 18, 31, 32, 43}, + {6, 10, 13, 39, 46}, + {5, 18, 22, 26, 40}, + {16, 19, 26, 34, 45}, + {4, 9, 10, 18, 44}, + {1, 18, 19, 27, 51}, + {21, 28, 29, 41, 51}, + {5, 7, 9, 23, 31}, + {12, 13, 16, 30, 42}, + {3, 4, 34, 37, 42}, + {0, 10, 15, 26, 42}, + {3, 17, 32, 38, 45}, + {0, 10, 13, 37, 41}, + {1, 3, 16, 24, 31}, + {6, 13, 21, 26, 31}, + {10, 14, 15, 27, 32}, + {9, 13, 26, 42, 50}, + {21, 28, 34, 35, 39}, + {3, 6, 12, 14, 29}, + {1, 3, 6, 12, 29}, + {5, 10, 17, 30, 38}, + {4, 11, 16, 42, 48}, + {8, 9, 16, 34, 51}, + {14, 17, 27, 37, 38}, + {5, 26, 31, 38, 43}, + {6, 22, 36, 44, 45}, + {4, 19, 39, 45, 50}, + {0, 7, 8, 26, 51}, + {32, 33, 37, 45, 49}, + {13, 15, 39, 45, 49}, + {0, 13, 28, 40, 44}, + {3, 4, 6, 28, 41}, + {7, 28, 33, 38, 44}, + {10, 18, 20, 31, 39}, + {2, 23, 39, 40, 41}, + {5, 32, 44, 49, 51}, + {4, 23, 29, 43, 47}, + {5, 6, 28, 43, 44}, + {4, 7, 8, 24, 43}, + {3, 16, 20, 40, 48}, + {0, 26, 29, 41, 51}, + {13, 14, 15, 26, 45}, + {5, 7, 26, 46, 48}, + {0, 3, 5, 10, 31}, + {0, 1, 6, 11, 19}, + {16, 19, 40, 42, 50}, + {0, 1, 21, 40, 46}, + {3, 10, 18, 31, 32}, + {1, 13, 27, 32, 35}, + {1, 2, 31, 32, 40}, + {6, 26, 43, 45, 51}, + {5, 8, 20, 21, 26}, + {12, 19, 27, 28, 40}, + {8, 13, 15, 21, 40}, + {6, 12, 30, 43, 44}, + {3, 13, 14, 40, 45}, + {8, 28, 41, 42, 46}, + {2, 4, 6, 14, 30}, + {2, 8, 9, 34, 40}, + {3, 4, 22, 30, 44}, + {0, 19, 24, 32, 43}, + {3, 14, 23, 25, 27}, + {2, 15, 19, 23, 40}, + {4, 15, 28, 34, 36}, + {6, 11, 18, 32, 39}, + {12, 16, 39, 42, 44}, + {6, 12, 26, 45, 50}, + {6, 25, 32, 33, 36}, + {8, 24, 34, 39, 46}, + {21, 27, 39, 47, 51}, + {1, 5, 11, 27, 28}, + {20, 30, 34, 38, 47}, + {8, 22, 27, 34, 38}, + {4, 17, 34, 44, 51}, + {11, 14, 31, 33, 44}, + {0, 2, 4, 5, 28}, + {1, 7, 8, 14, 39}, + {0, 21, 22, 30, 39}, + {14, 34, 39, 45, 47}, + {8, 11, 15, 21, 43}, + {3, 15, 41, 44, 46}, + {1, 2, 6, 32, 34}, + {1, 7, 12, 14, 23}, + {2, 21, 28, 29, 33}, + {15, 17, 21, 22, 43}, + {0, 2, 20, 28, 38}, + {9, 21, 29, 44, 47}, + {0, 3, 14, 35, 42}, + {3, 16, 22, 26, 38}, + {3, 4, 9, 23, 30}, + {13, 16, 17, 20, 29}, + {5, 11, 20, 30, 46}, + {19, 25, 27, 35, 40}, + {2, 3, 16, 26, 38}, + {21, 30, 47, 49, 50}, + {5, 14, 27, 46, 47}, + {2, 8, 16, 41, 51}, + {10, 21, 41, 46, 47}, + {3, 11, 14, 42, 46}, + {4, 7, 25, 43, 44}, + {6, 18, 19, 33, 41}, + {10, 15, 21, 35, 41}, + {5, 17, 18, 21, 36}, + {5, 10, 19, 32, 48}, + {14, 19, 27, 30, 31}, + {4, 17, 26, 34, 41}, + {1, 7, 26, 39, 47}, + {21, 25, 26, 34, 43}, + {3, 7, 16, 36, 39}, + {17, 28, 39, 43, 50}, + {5, 12, 16, 31, 48}, + {4, 11, 17, 32, 33}, + {7, 8, 18, 21, 22}, + {2, 6, 8, 16, 28}, + {7, 17, 18, 31, 41}, + {1, 18, 28, 44, 46}, + {1, 9, 27, 43, 51}, + {15, 17, 18, 37, 43}, + {10, 16, 24, 28, 42}, + {6, 7, 9, 21, 47}, + {16, 25, 36, 42, 44}, + {0, 1, 24, 39, 48}, + {2, 6, 13, 15, 18}, + {6, 8, 15, 20, 46}, + {19, 26, 30, 32, 38}, + {1, 4, 17, 32, 34}, + {3, 9, 16, 21, 43}, + {16, 22, 24, 40, 42}, + {5, 19, 21, 34, 42}, + {0, 10, 13, 20, 28}, + {14, 21, 27, 43, 49}, + {11, 15, 16, 38, 42}, + {0, 3, 39, 44, 51}, + {1, 4, 19, 26, 40}, + {6, 8, 21, 26, 27}, + {27, 32, 40, 41, 50}, + {15, 19, 32, 33, 48}, + {15, 17, 22, 27, 40}, + {3, 7, 21, 42, 50}, + {9, 15, 24, 33, 41}, + {2, 14, 15, 21, 43}, + {7, 9, 16, 39, 46}, + {3, 7, 11, 21, 46}, + {21, 24, 26, 38, 47}, + {1, 3, 5, 8, 27}, + {6, 18, 24, 26, 39}, + {8, 14, 25, 32, 34}, + {13, 20, 23, 26, 29}, + {11, 15, 34, 36, 41}, + {7, 28, 31, 44, 48}, + {7, 18, 19, 20, 38}, + {17, 23, 38, 42, 43}, + {20, 25, 31, 44, 50}, + {10, 15, 29, 31, 44}, + {1, 17, 21, 24, 27}, + {5, 18, 32, 39, 46}, + {1, 3, 32, 39, 42}, + {5, 18, 24, 28, 43}, + {3, 6, 16, 36, 43}, + {13, 26, 44, 46, 50}, + {0, 31, 37, 44, 51}, + {16, 19, 32, 35, 38}, + {13, 26, 32, 34, 50}, + {1, 6, 20, 26, 33}, + {14, 34, 36, 40, 50}, + {4, 8, 16, 33, 46}, + {1, 3, 24, 26, 29}, + {0, 2, 9, 13, 25}, + {4, 10, 37, 43, 47}, + {2, 17, 30, 33, 50}, + {1, 6, 21, 34, 41}, + {13, 27, 39, 44, 46}, + {19, 31, 36, 39, 45}, + {0, 3, 16, 24, 30}, + {4, 28, 29, 30, 50}, + {5, 11, 15, 44, 46}, + {11, 30, 43, 46, 47}, + {1, 3, 8, 10, 16}, + {32, 40, 45, 48, 50}, + {1, 7, 10, 13, 27}, + {29, 36, 42, 44, 46}, + {4, 13, 14, 17, 19}, + {15, 21, 22, 47, 51}, + {2, 13, 15, 31, 38}, + {5, 9, 14, 19, 45}, + {11, 12, 14, 29, 40}, + {12, 16, 21, 29, 50}, + {5, 10, 20, 37, 46}, + {1, 9, 27, 32, 42}, + {5, 13, 27, 31, 37}, + {4, 17, 24, 34, 48}, + {13, 15, 27, 39, 50}, + {1, 7, 17, 30, 35}, + {1, 9, 10, 24, 40}, + {8, 34, 39, 41, 51}, + {12, 17, 18, 31, 37}, + {23, 28, 41, 42, 45}, + {7, 23, 27, 37, 46}, + {1, 6, 10, 17, 45}, + {16, 17, 29, 35, 45}, + {14, 26, 30, 31, 44}, + {2, 25, 34, 41, 43}, + {19, 21, 32, 37, 41}, + {1, 13, 18, 40, 45}, + {2, 8, 15, 26, 51}, + {6, 7, 9, 33, 51}, + {6, 19, 27, 41, 50}, + {3, 29, 30, 44, 47}, + {5, 11, 17, 28, 41}, + {2, 7, 11, 15, 40}, + {3, 18, 31, 40, 45}, + {7, 15, 19, 41, 48}, + {3, 8, 25, 42, 46}, + {5, 10, 18, 24, 38}, + {5, 13, 15, 21, 44}, + {4, 8, 25, 34, 37}, + {0, 15, 16, 26, 37}, + {4, 20, 21, 37, 46}, + {25, 29, 30, 31, 44}, + {16, 27, 34, 40, 48}, + {1, 11, 17, 18, 44}, + {8, 21, 24, 43, 45}, + {6, 12, 28, 44, 45}, + {23, 29, 32, 43, 45}, + {2, 3, 6, 22, 42}, + {0, 5, 23, 33, 39}, + {0, 7, 13, 31, 51}, + {14, 17, 27, 28, 48}, + {1, 16, 29, 48, 49}, + {14, 26, 40, 44, 47}, + {32, 37, 38, 45, 49}, + {3, 5, 18, 25, 35}, + {4, 15, 17, 39, 50}, + {3, 14, 20, 40, 45}, + {6, 7, 17, 18, 33}, + {5, 9, 11, 31, 38}, + {5, 14, 33, 44, 45}, + {1, 7, 9, 14, 41}, + {15, 17, 24, 30, 32}, + {7, 19, 20, 30, 36}, + {1, 14, 23, 25, 29}, + {0, 1, 16, 39, 50}, + {11, 21, 34, 39, 41}, + {16, 17, 29, 40, 46}, + {18, 44, 45, 47, 49}, + {20, 33, 37, 42, 45}, + {0, 16, 17, 18, 26}, + {13, 14, 19, 37, 45}, + {20, 21, 24, 25, 47}, + {8, 14, 33, 36, 40}, + {6, 14, 15, 21, 41}, + {2, 5, 16, 28, 37}, + {8, 10, 18, 31, 37}, + {5, 8, 13, 18, 37}, + {7, 20, 24, 29, 32}, + {16, 34, 38, 47, 48}, + {19, 33, 36, 40, 45}, + {0, 2, 6, 37, 45}, + {10, 13, 24, 26, 33}, + {2, 17, 26, 41, 50}, + {1, 30, 38, 40, 48}, + {2, 5, 15, 40, 51}, + {15, 18, 31, 43, 45}, + {7, 33, 35, 42, 47}, + {20, 21, 34, 36, 44}, + {8, 21, 27, 38, 39}, + {10, 17, 20, 28, 33}, + {1, 13, 19, 38, 40}, + {8, 20, 28, 46, 48}, + {0, 2, 18, 25, 44}, + {2, 6, 14, 24, 45}, + {0, 7, 14, 20, 21}, + {6, 21, 32, 43, 46}, + {7, 11, 15, 46, 47}, + {8, 17, 29, 30, 37}, + {2, 23, 24, 41, 51}, + {7, 24, 26, 28, 39}, + {20, 21, 26, 30, 33}, + {17, 27, 37, 40, 42}, + {13, 17, 19, 39, 40}, + {0, 19, 29, 31, 44}, + {2, 22, 29, 41, 46}, + {28, 31, 34, 38, 47}, + {1, 10, 14, 16, 45}, + {6, 7, 12, 18, 45}, + {5, 7, 30, 44, 51}, + {7, 14, 25, 33, 41}, + {4, 8, 17, 38, 50}, + {2, 3, 7, 16, 18}, + {14, 17, 31, 35, 40}, + {9, 14, 20, 33, 45}, + {3, 4, 28, 30, 34}, + {0, 2, 17, 37, 43}, + {3, 16, 23, 30, 47}, + {5, 32, 35, 45, 49}, + {0, 8, 14, 40, 48}, + {4, 18, 26, 34, 39}, + {28, 31, 39, 41, 43}, + {11, 17, 34, 44, 47}, + {29, 30, 42, 47, 49}, + {3, 11, 26, 34, 42}, + {8, 28, 41, 42, 48}, + {2, 13, 15, 16, 50}, + {14, 26, 40, 41, 45}, + {15, 21, 34, 40, 49}, + {3, 11, 15, 22, 29}, + {11, 19, 32, 38, 47}, + {21, 25, 33, 34, 49}, + {2, 6, 18, 39, 44}, + {16, 24, 29, 30, 35}, + {1, 31, 40, 41, 48}, + {1, 27, 31, 42, 43}, + {2, 15, 23, 25, 32}, + {14, 17, 20, 40, 42}, + {21, 27, 33, 38, 46}, + {0, 2, 3, 15, 37}, + {18, 23, 26, 32, 39}, + {4, 5, 19, 27, 31}, + {12, 13, 26, 46, 47}, + {5, 7, 17, 42, 43}, + {3, 17, 26, 41, 43}, + {7, 18, 20, 29, 39}, + {5, 11, 20, 27, 46}, + {2, 4, 28, 40, 47}, + {15, 41, 43, 44, 49}, + {2, 15, 34, 39, 50}, + {26, 28, 33, 36, 46}, + {14, 21, 34, 37, 49}, + {5, 13, 28, 39, 43}, + {6, 10, 13, 32, 34}, + {1, 33, 37, 39, 40}, + {0, 7, 19, 38, 45}, + {3, 11, 31, 33, 44}, + {4, 6, 13, 20, 43}, + {3, 30, 39, 41, 42}, + {1, 11, 23, 40, 46}, + {2, 9, 12, 15, 18}, + {10, 12, 32, 40, 45}, + {1, 13, 31, 40, 51}, + {3, 29, 31, 40, 51}, + {30, 34, 38, 46, 47}, + {5, 12, 26, 31, 50}, + {2, 3, 7, 42, 50}, + {4, 17, 31, 45, 50}, + {3, 33, 39, 44, 46}, + {2, 21, 37, 42, 47}, + {5, 7, 12, 18, 35}, + {5, 29, 36, 38, 44}, + {2, 13, 14, 20, 33}, + {23, 33, 43, 46, 47}, + {16, 18, 25, 41, 42}, + {3, 6, 29, 44, 49}, + {4, 22, 31, 44, 49}, + {8, 19, 29, 32, 37}, + {9, 10, 11, 30, 43}, + {4, 13, 15, 29, 42}, + {2, 7, 13, 28, 44}, + {0, 8, 14, 47, 51}, + {4, 6, 10, 29, 30}, + {3, 21, 25, 34, 44}, + {1, 3, 15, 20, 29}, + {0, 13, 24, 33, 47}, + {11, 17, 30, 33, 36}, + {0, 2, 3, 39, 40}, + {1, 6, 15, 41, 47}, + {23, 29, 40, 42, 45}, + {6, 16, 19, 33, 51}, + {7, 8, 15, 20, 26}, + {4, 30, 33, 41, 42}, + {5, 6, 13, 35, 45}, + {4, 6, 15, 19, 22}, + {1, 18, 33, 40, 41}, + {3, 8, 14, 19, 42}, + {16, 18, 22, 29, 45}, + {7, 33, 36, 39, 40}, + {0, 23, 28, 32, 41}, + {14, 27, 31, 34, 46}, + {7, 29, 30, 37, 42}, + {13, 17, 18, 31, 38}, + {24, 26, 33, 39, 47}, + {14, 15, 27, 34, 38}, + {2, 16, 27, 29, 39}, + {20, 33, 35, 36, 43}, + {15, 16, 19, 42, 48}, + {10, 13, 26, 41, 46}, + {18, 28, 29, 43, 44}, + {5, 19, 22, 45, 46}, + {18, 23, 31, 33, 41}, + {0, 4, 5, 24, 39}, + {4, 17, 26, 32, 38}, + {8, 16, 25, 29, 35}, + {13, 14, 27, 29, 46}, + {20, 22, 42, 46, 51}, + {9, 15, 38, 41, 44}, + {7, 9, 10, 33, 40}, + {8, 10, 34, 41, 43}, + {18, 19, 28, 40, 45}, + {4, 15, 28, 32, 38}, + {1, 10, 11, 32, 40}, + {8, 12, 15, 20, 46}, + {0, 5, 19, 20, 26}, + {4, 13, 14, 40, 50}, + {3, 4, 23, 43, 50}, + {2, 6, 7, 40, 46}, + {1, 31, 32, 43, 45}, + {7, 33, 38, 47, 48}, + {4, 7, 30, 34, 50}, + {7, 14, 31, 36, 46}, + {23, 31, 37, 44, 45}, + {1, 3, 5, 15, 41}, + {32, 33, 34, 41, 45}, + {12, 21, 34, 40, 44}, + {1, 18, 19, 26, 27}, + {5, 21, 33, 42, 46}, + {2, 9, 18, 28, 47}, + {8, 21, 23, 30, 51}, + {5, 6, 8, 31, 50}, + {7, 8, 11, 33, 38}, + {14, 22, 34, 40, 51}, + {11, 16, 22, 23, 42}, + {4, 17, 36, 40, 48}, + {2, 11, 18, 33, 46}, + {6, 17, 21, 32, 38}, + {7, 13, 14, 24, 46}, + {1, 8, 9, 14, 39}, + {22, 24, 31, 44, 51}, + {5, 9, 27, 40, 49}, + {8, 16, 17, 47, 49}, + {18, 25, 31, 47, 49}, + {1, 4, 14, 32, 44}, + {24, 29, 30, 33, 46}, + {2, 18, 20, 33, 34}, + {0, 13, 22, 36, 41}, + {8, 14, 25, 30, 34}, + {8, 13, 14, 29, 34}, + {6, 35, 42, 45, 49}, + {3, 4, 6, 29, 49}, + {5, 19, 21, 28, 41}, + {11, 16, 17, 30, 47}, + {27, 33, 36, 39, 40}, + {6, 32, 33, 35, 37}, + {0, 12, 20, 26, 44}, + {8, 21, 38, 44, 45}, + {17, 18, 30, 33, 39}, + {8, 14, 22, 40, 45}, + {3, 4, 16, 22, 44}, + {25, 26, 29, 31, 44}, + {17, 18, 31, 41, 45}, + {7, 12, 14, 19, 33}, + {5, 19, 27, 37, 40}, + {1, 24, 28, 40, 43}, + {16, 27, 29, 35, 44}, + {3, 18, 21, 28, 47}, + {5, 9, 31, 45, 46}, + {19, 31, 33, 46, 48}, + {4, 5, 23, 31, 42}, + {17, 28, 41, 45, 50}, + {11, 33, 34, 42, 46}, + {3, 5, 11, 19, 45}, + {9, 20, 41, 46, 47}, + {11, 15, 19, 31, 45}, + {5, 30, 31, 37, 40}, + {0, 6, 13, 23, 47}, + {14, 19, 23, 30, 45}, + {1, 2, 8, 47, 49}, + {5, 28, 31, 39, 49}, + {7, 14, 37, 40, 49}, + {5, 12, 13, 44, 50}, + {0, 2, 27, 28, 50}, + {11, 13, 18, 31, 34}, + {2, 19, 23, 38, 41}, + {0, 6, 17, 31, 44}, + {8, 27, 29, 37, 42}, + {5, 15, 21, 35, 44}, + {0, 7, 15, 34, 41}, + {9, 18, 26, 32, 44}, + {4, 17, 26, 28, 32}, + {4, 13, 28, 32, 41}, + {0, 2, 4, 6, 45}, + {8, 33, 43, 46, 49}, + {1, 28, 41, 42, 44}, + {5, 20, 30, 41, 46}, + {14, 17, 25, 31, 44}, + {4, 29, 30, 40, 48}, + {8, 9, 23, 32, 34}, + {17, 18, 27, 36, 43}, + {4, 17, 20, 28, 45}, + {8, 16, 21, 39, 49}, + {5, 10, 32, 45, 48}, + {6, 26, 28, 42, 45}, + {7, 16, 32, 33, 40}, + {8, 10, 19, 28, 34}, + {1, 8, 15, 40, 42}, + {2, 4, 24, 29, 42}, + {4, 6, 8, 28, 43}, + {2, 15, 26, 31, 37}, + {28, 41, 42, 46, 49}, + {1, 5, 12, 40, 50}, + {2, 6, 9, 26, 28}, + {8, 18, 32, 43, 45}, + {7, 8, 23, 30, 33}, + {13, 39, 40, 47, 50}, + {20, 24, 32, 38, 45}, + {2, 11, 19, 42, 45}, + {12, 19, 21, 27, 40}, + {16, 27, 30, 33, 43}, + {3, 16, 20, 21, 30}, + {4, 21, 36, 43, 44}, + {31, 34, 39, 42, 44}, + {4, 5, 23, 39, 43}, + {0, 3, 16, 22, 50}, + {5, 31, 33, 35, 37}, + {2, 5, 18, 38, 39}, + {1, 11, 31, 40, 48}, + {3, 5, 6, 26, 31}, + {10, 16, 26, 39, 43}, + {19, 32, 34, 40, 42}, + {5, 6, 23, 43, 44}, + {3, 5, 15, 23, 29}, + {3, 5, 21, 32, 34}, + {1, 6, 7, 40, 44}, + {7, 22, 26, 39, 40}, + {14, 15, 23, 35, 40}, + {5, 18, 25, 34, 43}, + {11, 21, 35, 46, 47}, + {10, 33, 34, 44, 47}, + {29, 34, 41, 47, 48}, + {4, 5, 38, 39, 44}, + {5, 15, 20, 31, 34}, + {3, 5, 16, 32, 43}, + {5, 16, 29, 37, 49}, + {1, 15, 40, 42, 45}, + {4, 35, 40, 43, 51}, + {2, 3, 5, 19, 45}, + {1, 13, 14, 15, 20}, + {1, 27, 33, 42, 44}, + {1, 23, 31, 33, 46}, + {1, 21, 34, 42, 48}, + {5, 12, 14, 27, 33}, + {21, 31, 34, 42, 46}, + {15, 28, 37, 39, 45}, + {5, 9, 14, 24, 27}, + {3, 19, 25, 32, 33}, + {2, 10, 21, 27, 41}, + {4, 18, 26, 44, 49}, + {11, 19, 36, 45, 48}, + {2, 11, 21, 28, 43}, + {26, 33, 35, 44, 46}, + {14, 20, 26, 27, 51}, + {20, 33, 38, 49, 50}, + {4, 5, 6, 16, 32}, + {12, 19, 21, 30, 43}, + {5, 20, 23, 31, 40}, + {3, 5, 8, 21, 22}, + {3, 9, 12, 20, 29}, + {16, 23, 29, 34, 38}, + {2, 10, 15, 40, 51}, + {15, 25, 28, 43, 46}, + {0, 8, 13, 22, 37}, + {4, 9, 32, 43, 46}, + {1, 18, 27, 35, 36}, + {1, 6, 27, 29, 37}, + {21, 29, 40, 47, 48}, + {11, 13, 14, 19, 40}, + {2, 15, 21, 35, 44}, + {16, 29, 46, 49, 50}, + {7, 18, 20, 23, 50}, + {4, 16, 17, 33, 37}, + {5, 7, 19, 33, 37}, + {2, 26, 37, 41, 51}, + {7, 13, 26, 27, 34}, + {8, 11, 15, 21, 38}, + {2, 27, 35, 40, 47}, + {6, 12, 16, 33, 46}, + {8, 18, 35, 40, 44}, + {13, 19, 23, 44, 45}, + {14, 27, 39, 43, 48}, + {0, 15, 41, 42, 47}, + {9, 13, 15, 31, 44}, + {0, 2, 7, 9, 28}, + {0, 5, 9, 43, 44}, + {19, 21, 27, 45, 48}, + {13, 16, 29, 38, 43}, + {2, 3, 18, 29, 39}, + {16, 19, 21, 45, 49}, + {18, 26, 38, 39, 46}, + {4, 31, 34, 35, 44}, + {7, 9, 17, 33, 50}, + {2, 4, 5, 13, 15}, + {5, 6, 7, 9, 33}, + {1, 3, 7, 20, 32}, + {16, 25, 29, 34, 39}, + {4, 5, 32, 34, 45}, + {8, 11, 26, 39, 44}, + {14, 17, 24, 43, 45}, + {4, 9, 25, 26, 43}, + {14, 25, 36, 40, 44}, + {17, 21, 32, 45, 50}, + {1, 2, 11, 17, 28}, + {2, 16, 22, 23, 28}, + {2, 5, 22, 29, 44}, + {2, 6, 18, 19, 39}, + {2, 19, 27, 41, 46}, + {8, 20, 24, 32, 45}, + {1, 21, 23, 27, 44}, + {0, 1, 3, 6, 29}, + {3, 18, 22, 39, 42}, + {9, 18, 33, 36, 44}, + {2, 15, 17, 23, 31}, + {0, 16, 20, 39, 41}, + {8, 21, 24, 40, 51}, + {5, 8, 16, 30, 44}, + {1, 27, 29, 35, 39}, + {5, 12, 18, 47, 48}, + {10, 14, 27, 30, 31}, + {6, 14, 31, 33, 40}, + {3, 8, 22, 42, 51}, + {1, 23, 24, 33, 46}, + {0, 4, 24, 30, 46}, + {1, 14, 41, 44, 47}, + {1, 7, 30, 36, 43}, + {10, 14, 28, 41, 48}, + {5, 15, 34, 44, 48}, + {16, 21, 26, 34, 51}, + {1, 5, 14, 35, 37}, + {0, 2, 36, 39, 43}, + {14, 29, 40, 46, 48}, + {2, 17, 28, 39, 49}, + {4, 7, 14, 17, 50}, + {1, 13, 17, 25, 40}, + {5, 7, 13, 44, 50}, + {5, 8, 30, 44, 46}, + {0, 2, 4, 27, 39}, + {16, 17, 32, 39, 42}, + {1, 5, 18, 25, 28}, + {6, 8, 27, 30, 47}, + {3, 23, 26, 39, 48}, + {12, 14, 15, 27, 37}, + {18, 29, 32, 38, 44}, + {0, 16, 29, 32, 50}, + {6, 13, 15, 20, 28}, + {0, 2, 4, 17, 23}, + {6, 8, 36, 44, 45}, + {3, 9, 15, 42, 49}, + {3, 16, 19, 35, 38}, + {19, 20, 26, 34, 45}, + {0, 3, 13, 20, 43}, + {7, 18, 29, 34, 42}, + {14, 17, 21, 40, 42}, + {3, 21, 29, 35, 41}, + {1, 4, 10, 27, 50}, + {2, 15, 30, 31, 32}, + {7, 10, 20, 28, 44}, + {6, 19, 21, 35, 36}, + {3, 9, 12, 14, 29}, + {19, 20, 27, 33, 43}, + {2, 26, 28, 44, 50}, + {7, 8, 19, 21, 48}, + {4, 19, 24, 32, 41}, + {3, 4, 17, 27, 35}, + {8, 11, 14, 20, 46}, + {7, 11, 34, 38, 47}, + {2, 8, 16, 21, 23}, + {1, 27, 28, 33, 35}, + {2, 30, 38, 41, 44}, + {2, 5, 44, 47, 50}, + {4, 23, 28, 43, 48}, + {3, 13, 28, 36, 39}, + {9, 12, 14, 27, 28}, + {3, 5, 30, 39, 44}, + {15, 17, 32, 33, 45}, + {8, 21, 23, 28, 43}, + {1, 6, 14, 31, 39}, + {3, 29, 33, 38, 47}, + {5, 13, 26, 30, 41}, + {1, 2, 8, 28, 49}, + {3, 9, 30, 42, 46}, + {1, 20, 26, 46, 50}, + {8, 9, 17, 30, 44}, + {23, 31, 44, 48, 51}, + {4, 23, 35, 40, 43}, + {7, 9, 17, 39, 43}, + {19, 32, 42, 46, 49}, + {4, 6, 19, 35, 49}, + {0, 21, 28, 44, 47}, + {1, 5, 27, 35, 37}, + {3, 4, 8, 42, 50}, + {4, 10, 13, 17, 19}, + {4, 9, 25, 43, 46}, + {3, 5, 6, 31, 51}, + {14, 18, 21, 27, 49}, + {5, 10, 30, 31, 32}, + {7, 17, 18, 31, 37}, + {7, 15, 19, 32, 43}, + {13, 19, 26, 44, 47}, + {5, 9, 31, 37, 40}, + {4, 5, 40, 44, 46}, + {8, 17, 25, 43, 49}, + {21, 32, 44, 46, 47}, + {2, 16, 24, 30, 42}, + {0, 8, 21, 33, 48}, + {5, 6, 15, 41, 47}, + {30, 34, 37, 41, 47}, + {0, 2, 8, 15, 19}, + {4, 7, 8, 22, 47}, + {10, 28, 29, 34, 47}, + {2, 20, 29, 42, 45}, + {14, 22, 24, 27, 49}, + {10, 27, 32, 35, 45}, + {9, 15, 21, 47, 51}, + {4, 11, 18, 30, 41}, + {4, 8, 37, 44, 47}, + {13, 17, 18, 42, 44}, + {1, 3, 26, 40, 44}, + {14, 16, 37, 42, 46}, + {4, 11, 17, 46, 49}, + {1, 5, 8, 34, 49}, + {0, 1, 26, 34, 45}, + {0, 18, 19, 28, 31}, + {2, 5, 21, 42, 47}, + {0, 9, 13, 24, 25}, + {21, 24, 30, 39, 43}, + {1, 4, 15, 33, 40}, + {20, 25, 26, 37, 39}, + {3, 4, 15, 28, 34}, + {0, 14, 24, 40, 43}, + {7, 17, 28, 30, 49}, + {0, 26, 28, 38, 47}, + {4, 18, 20, 36, 44}, + {10, 28, 29, 40, 42}, + {11, 13, 27, 40, 46}, + {0, 8, 14, 26, 37}, + {21, 22, 34, 46, 50}, + {3, 11, 12, 16, 43}, + {2, 12, 14, 30, 41}, + {19, 23, 24, 44, 45}, + {12, 17, 19, 27, 30}, + {4, 5, 30, 32, 42}, + {2, 5, 6, 26, 32}, + {15, 25, 28, 29, 50}, + {3, 8, 12, 16, 26}, + {2, 7, 13, 41, 48}, + {4, 9, 31, 43, 45}, + {18, 20, 23, 31, 32}, + {12, 18, 19, 43, 45}, + {7, 14, 26, 27, 42}, + {2, 16, 41, 47, 48}, + {0, 2, 12, 13, 23}, + {1, 34, 35, 37, 47}, + {3, 5, 9, 43, 44}, + {5, 21, 29, 31, 38}, + {2, 18, 24, 31, 48}, + {13, 31, 32, 45, 51}, + {6, 11, 15, 30, 41}, + {7, 11, 42, 46, 48}, + {16, 18, 26, 31, 45}, + {5, 20, 33, 40, 50}, + {0, 7, 17, 34, 47}, + {13, 18, 28, 30, 41}, + {0, 29, 42, 46, 48}, + {7, 18, 20, 47, 50}, + {3, 30, 32, 45, 48}, + {5, 29, 36, 41, 44}, + {21, 34, 38, 39, 46}, + {1, 4, 16, 29, 33}, + {6, 31, 40, 44, 47}, + {6, 20, 26, 31, 39}, + {0, 1, 2, 41, 48}, + {14, 15, 23, 28, 37}, + {3, 7, 17, 42, 45}, + {0, 5, 29, 39, 45}, + {4, 8, 15, 21, 40}, + {0, 18, 20, 31, 34}, + {14, 21, 25, 31, 34}, + {14, 24, 28, 31, 40}, + {25, 26, 30, 43, 49}, + {13, 15, 23, 26, 50}, + {11, 32, 40, 43, 45}, + {3, 28, 32, 41, 49}, + {0, 1, 35, 38, 40}, + {11, 20, 35, 44, 46}, + {3, 19, 31, 38, 44}, + {2, 7, 34, 41, 50}, + {1, 2, 17, 28, 39}, + {8, 9, 10, 21, 33}, + {7, 33, 37, 39, 44}, + {1, 17, 24, 27, 49}, + {6, 24, 27, 28, 41}, + {1, 3, 13, 34, 39}, + {20, 26, 30, 33, 41}, + {0, 39, 42, 47, 50}, + {5, 6, 12, 28, 45}, + {0, 5, 7, 9, 44}, + {0, 2, 20, 29, 41}, + {3, 5, 13, 16, 40}, + {8, 9, 21, 24, 29}, + {7, 13, 20, 23, 50}, + {1, 13, 14, 28, 45}, + {0, 2, 4, 15, 19}, + {0, 18, 28, 39, 48}, + {0, 1, 6, 45, 48}, + {4, 18, 19, 44, 46}, + {3, 13, 42, 44, 49}, + {7, 20, 23, 24, 34}, + {2, 4, 14, 15, 24}, + {1, 5, 8, 19, 31}, + {8, 21, 23, 25, 31}, + {1, 4, 6, 38, 45}, + {4, 5, 6, 28, 44}, + {3, 14, 19, 22, 32}, + {1, 2, 14, 32, 35}, + {0, 2, 3, 11, 28}, + {19, 27, 33, 46, 48}, + {2, 7, 21, 26, 46}, + {7, 22, 26, 33, 34}, + {10, 18, 19, 31, 48}, + {2, 7, 16, 19, 46}, + {3, 4, 7, 29, 36}, + {27, 40, 45, 47, 50}, + {0, 33, 37, 39, 43}, + {7, 21, 29, 33, 39}, + {17, 27, 36, 40, 42}, + {1, 2, 7, 33, 34}, + {0, 2, 26, 35, 40}, + {22, 33, 36, 43, 46}, + {4, 7, 10, 12, 43}, + {8, 18, 21, 26, 49}, + {7, 21, 34, 35, 37}, + {3, 7, 30, 33, 41}, + {13, 14, 18, 21, 34}, + {8, 12, 24, 27, 47}, + {3, 17, 21, 42, 46}, + {6, 21, 24, 43, 45}, + {1, 14, 19, 26, 41}, + {5, 13, 32, 39, 50}, + {9, 20, 32, 34, 47}, + {16, 18, 24, 31, 51}, + {6, 13, 19, 42, 47}, + {19, 28, 32, 38, 46}, + {3, 5, 32, 44, 47}, + {8, 29, 47, 50, 51}, + {8, 13, 31, 47, 48}, + {6, 9, 24, 32, 41}, + {2, 4, 29, 30, 31}, + {4, 5, 17, 46, 49}, + {3, 6, 19, 28, 37}, + {11, 21, 32, 45, 49}, + {12, 15, 17, 28, 42}, + {2, 15, 16, 17, 45}, + {8, 19, 32, 46, 51}, + {2, 21, 27, 34, 37}, + {0, 2, 8, 34, 42}, + {4, 11, 15, 43, 44}, + {4, 11, 17, 20, 26}, + {2, 7, 20, 36, 48}, + {22, 27, 38, 40, 42}, + {3, 5, 23, 30, 44}, + {7, 9, 17, 21, 46}, + {7, 33, 42, 44, 45}, + {6, 9, 15, 16, 28}, + {8, 17, 43, 46, 49}, + {12, 17, 26, 43, 50}, + {7, 20, 23, 28, 44}, + {11, 25, 30, 36, 43}, + {6, 23, 31, 34, 45}, + {2, 30, 35, 37, 43}, + {0, 6, 8, 11, 21}, + {0, 19, 44, 45, 51}, + {0, 5, 6, 19, 49}, + {5, 28, 31, 40, 43}, + {8, 14, 15, 21, 33}, + {8, 13, 17, 34, 35}, + {4, 16, 21, 27, 34}, + {8, 17, 30, 45, 46}, + {2, 18, 28, 36, 39}, + {1, 12, 14, 21, 26}, + {1, 13, 36, 40, 45}, + {3, 18, 24, 42, 51}, + {4, 19, 28, 32, 39}, + {1, 12, 22, 27, 44}, + {1, 4, 11, 27, 41}, + {8, 18, 29, 34, 45}, + {12, 20, 28, 41, 48}, + {6, 8, 14, 19, 33}, + {14, 29, 31, 35, 40}, + {8, 17, 21, 27, 37}, + {0, 20, 26, 27, 31}, + {1, 24, 27, 38, 46}, + {3, 13, 26, 28, 30}, + {24, 29, 34, 42, 49}, + {6, 20, 23, 35, 46}, + {3, 6, 26, 42, 47}, + {13, 14, 31, 33, 44}, + {7, 16, 33, 40, 50}, + {15, 24, 28, 39, 46}, + {4, 16, 17, 36, 45}, + {2, 11, 12, 31, 44}, + {1, 5, 11, 38, 40}, + {11, 25, 29, 42, 48}, + {4, 7, 20, 24, 36}, + {7, 11, 21, 29, 47}, + {4, 6, 14, 17, 46}, + {1, 4, 14, 15, 20}, + {10, 15, 18, 28, 32}, + {0, 3, 7, 20, 25}, + {7, 9, 27, 36, 40}, + {0, 6, 25, 28, 32}, + {8, 13, 24, 26, 45}, + {1, 2, 5, 31, 39}, + {9, 11, 15, 28, 32}, + {2, 8, 15, 20, 31}, + {14, 15, 28, 33, 37}, + {0, 7, 10, 35, 39}, + {8, 10, 14, 34, 50}, + {0, 4, 14, 22, 40}, + {31, 42, 44, 50, 51}, + {2, 7, 33, 35, 37}, + {2, 6, 12, 17, 41}, + {20, 24, 31, 35, 46}, + {1, 18, 30, 40, 42}, + {3, 8, 38, 41, 47}, + {20, 21, 27, 40, 49}, + {5, 8, 39, 44, 50}, + {0, 2, 28, 37, 49}, + {0, 7, 18, 26, 27}, + {17, 28, 30, 39, 42}, + {17, 26, 39, 41, 48}, + {12, 13, 35, 39, 40}, + {13, 18, 37, 44, 48}, + {1, 14, 26, 29, 47}, + {3, 29, 37, 44, 48}, + {10, 16, 21, 25, 34}, + {30, 39, 43, 45, 48}, + {27, 40, 43, 44, 45}, + {4, 6, 12, 15, 28}, + {5, 22, 29, 32, 42}, + {0, 4, 13, 20, 21}, + {8, 13, 21, 30, 38}, + {6, 10, 17, 18, 31}, + {8, 22, 36, 40, 47}, + {0, 4, 7, 24, 30}, + {2, 7, 13, 15, 18}, + {2, 7, 9, 33, 39}, + {12, 21, 28, 41, 45}, + {8, 16, 18, 29, 46}, + {6, 27, 39, 40, 44}, + {16, 21, 22, 34, 41}, + {1, 26, 28, 30, 43}, + {4, 12, 17, 42, 44}, + {2, 17, 28, 37, 42}, + {1, 33, 45, 46, 47}, + {5, 18, 20, 40, 41}, + {0, 14, 22, 25, 39}, + {19, 24, 38, 45, 49}, + {0, 6, 14, 21, 47}, + {0, 10, 12, 37, 39}, + {3, 5, 6, 16, 37}, + {4, 5, 7, 21, 44}, + {9, 14, 19, 27, 34}, + {13, 24, 25, 26, 32}, + {12, 20, 24, 35, 46}, + {0, 13, 19, 20, 38}, + {13, 16, 32, 34, 47}, + {3, 5, 26, 31, 40}, + {4, 8, 15, 17, 31}, + {2, 11, 20, 22, 28}, + {2, 24, 34, 47, 51}, + {17, 26, 43, 47, 49}, + {2, 5, 8, 18, 22}, + {3, 13, 21, 23, 26}, + {6, 17, 21, 26, 30}, + {0, 19, 32, 36, 48}, + {0, 4, 13, 15, 16}, + {0, 5, 24, 31, 45}, + {5, 21, 27, 47, 51}, + {3, 6, 16, 33, 47}, + {0, 6, 21, 33, 46}, + {0, 6, 35, 39, 46}, + {4, 19, 20, 25, 30}, + {4, 10, 20, 34, 43}, + {15, 16, 28, 48, 51}, + {4, 6, 15, 28, 50}, + {4, 15, 29, 30, 46}, + {3, 21, 27, 47, 49}, + {28, 35, 36, 38, 41}, + {21, 29, 33, 46, 48}, + {1, 4, 8, 12, 34}, + {0, 11, 20, 25, 33}, + {3, 23, 27, 35, 40}, + {18, 34, 46, 47, 48}, + {7, 16, 17, 42, 47}, + {8, 28, 30, 32, 43}, + {0, 2, 27, 32, 40}, + {3, 13, 35, 39, 41}, + {1, 2, 4, 35, 41}, + {10, 17, 33, 43, 44}, + {8, 23, 40, 44, 47}, + {4, 7, 10, 21, 33}, + {1, 15, 24, 29, 41}, + {2, 13, 19, 30, 32}, + {14, 20, 25, 33, 45}, + {20, 33, 35, 42, 47}, + {3, 18, 23, 31, 48}, + {17, 19, 35, 43, 50}, + {3, 7, 33, 36, 37}, + {4, 5, 8, 9, 43}, + {7, 14, 33, 36, 41}, + {21, 26, 31, 44, 51}, + {8, 11, 19, 42, 45}, + {2, 6, 21, 23, 47}, + {6, 12, 21, 26, 47}, + {4, 15, 33, 46, 50}, + {5, 18, 29, 39, 45}, + {0, 9, 13, 31, 40}, + {6, 28, 30, 32, 44}, + {2, 27, 31, 34, 40}, + {3, 15, 27, 28, 44}, + {29, 30, 32, 40, 42}, + {4, 11, 17, 27, 39}, + {4, 11, 14, 25, 27}, + {1, 30, 38, 40, 42}, + {0, 4, 13, 29, 31}, + {8, 15, 16, 26, 28}, + {8, 21, 23, 27, 35}, + {10, 17, 21, 30, 31}, + {10, 16, 18, 40, 42}, + {16, 28, 30, 33, 46}, + {4, 32, 34, 41, 43}, + {3, 6, 32, 34, 44}, + {7, 13, 26, 28, 34}, + {2, 7, 28, 37, 45}, + {6, 9, 36, 45, 50}, + {3, 7, 14, 20, 30}, + {8, 17, 30, 48, 51}, + {13, 17, 26, 28, 31}, + {8, 10, 21, 24, 28}, + {5, 13, 14, 17, 43}, + {14, 30, 35, 43, 47}, + {2, 9, 17, 26, 28}, + {5, 13, 18, 25, 32}, + {0, 22, 24, 33, 39}, + {3, 12, 18, 21, 34}, + {15, 22, 31, 41, 46}, + {4, 8, 17, 26, 41}, + {1, 9, 20, 27, 30}, + {4, 19, 37, 43, 46}, + {17, 20, 41, 43, 44}, + {3, 5, 11, 18, 33}, + {12, 18, 27, 44, 50}, + {2, 19, 21, 31, 45}, + {7, 17, 22, 41, 43}, + {1, 15, 30, 40, 47}, + {3, 18, 42, 46, 48}, + {13, 29, 35, 39, 46}, + {25, 27, 37, 40, 44}, + {7, 15, 28, 31, 51}, + {13, 19, 32, 35, 40}, + {1, 2, 6, 9, 40}, + {16, 38, 39, 42, 43}, + {3, 8, 18, 34, 50}, + {2, 28, 35, 36, 47}, + {16, 42, 47, 49, 51}, + {5, 13, 17, 18, 41}, + {7, 14, 18, 23, 27}, + {0, 5, 10, 18, 25}, + {1, 21, 30, 31, 43}, + {3, 19, 28, 30, 43}, + {19, 21, 33, 42, 45}, + {0, 12, 26, 46, 49}, + {7, 14, 26, 28, 46}, + {15, 26, 28, 46, 49}, + {19, 20, 24, 30, 45}, + {5, 10, 11, 18, 33}, + {19, 23, 29, 45, 47}, + {1, 7, 27, 35, 38}, + {2, 9, 19, 32, 40}, + {1, 15, 27, 31, 32}, + {3, 15, 18, 23, 44}, + {7, 30, 43, 47, 51}, + {9, 27, 32, 39, 45}, + {1, 14, 23, 25, 30}, + {6, 9, 21, 34, 49}, + {21, 24, 32, 36, 47}, + {2, 5, 13, 18, 34}, + {16, 19, 20, 26, 42}, + {6, 14, 26, 35, 45}, + {16, 21, 39, 42, 48}, + {20, 23, 33, 40, 42}, + {19, 29, 40, 42, 43}, + {1, 13, 40, 45, 51}, + {1, 4, 14, 25, 39}, + {3, 17, 28, 37, 43}, + {3, 5, 31, 32, 50}, + {9, 10, 28, 32, 41}, + {4, 8, 34, 35, 36}, + {7, 22, 32, 33, 44}, + {1, 2, 4, 8, 17}, + {12, 16, 29, 40, 44}, + {8, 11, 13, 39, 45}, + {1, 5, 17, 29, 44}, + {1, 6, 19, 23, 38}, + {0, 7, 30, 36, 43}, + {1, 13, 17, 21, 34}, + {12, 17, 24, 28, 30}, + {3, 14, 36, 40, 43}, + {9, 16, 29, 32, 36}, + {11, 28, 34, 44, 47}, + {6, 7, 17, 31, 44}, + {17, 18, 25, 26, 31}, + {8, 17, 26, 39, 40}, + {17, 22, 28, 43, 50}, + {2, 15, 19, 27, 42}, + {7, 21, 23, 41, 47}, + {19, 24, 32, 35, 49}, + {1, 3, 15, 37, 41}, + {6, 12, 23, 32, 48}, + {2, 17, 30, 46, 50}, + {14, 15, 16, 28, 34}, + {7, 9, 12, 20, 39}, + {6, 21, 30, 40, 47}, + {7, 11, 14, 18, 46}, + {2, 7, 33, 38, 44}, + {14, 17, 27, 33, 39}, + {7, 14, 27, 35, 51}, + {2, 6, 8, 21, 27}, + {2, 8, 27, 36, 41}, + {8, 19, 27, 45, 48}, + {16, 34, 35, 46, 47}, + {5, 11, 20, 33, 47}, + {0, 6, 22, 30, 39}, + {13, 32, 38, 45, 48}, + {2, 4, 6, 16, 29}, + {5, 6, 13, 18, 48}, + {2, 5, 7, 24, 28}, + {0, 8, 14, 20, 46}, + {0, 3, 8, 30, 39}, + {0, 1, 3, 8, 39}, + {4, 11, 16, 17, 36}, + {13, 17, 26, 29, 33}, + {2, 5, 15, 23, 25}, + {2, 10, 28, 45, 48}, + {1, 16, 19, 23, 40}, + {3, 13, 24, 26, 45}, + {18, 29, 36, 37, 44}, + {8, 17, 18, 31, 50}, + {3, 4, 24, 34, 43}, + {20, 22, 29, 42, 49}, + {6, 12, 41, 45, 47}, + {4, 16, 20, 45, 46}, + {6, 17, 28, 43, 48}, + {6, 13, 18, 44, 47}, + {13, 14, 27, 32, 34}, + {0, 4, 17, 28, 36}, + {1, 14, 19, 25, 42}, + {13, 22, 27, 33, 40}, + {1, 7, 13, 28, 33}, + {8, 11, 20, 26, 34}, + {0, 15, 16, 33, 41}, + {7, 19, 24, 32, 42}, + {18, 33, 35, 38, 46}, + {1, 5, 14, 16, 30}, + {8, 17, 20, 27, 46}, + {1, 17, 27, 44, 45}, + {6, 16, 18, 29, 35}, + {8, 9, 34, 39, 51}, + {29, 34, 42, 43, 46}, + {2, 7, 15, 48, 49}, + {0, 14, 20, 34, 47}, + {2, 3, 10, 28, 50}, + {9, 16, 20, 31, 33}, + {1, 2, 27, 29, 32}, + {3, 18, 20, 31, 34}, + {7, 15, 20, 38, 50}, + {19, 24, 43, 45, 49}, + {0, 5, 6, 39, 51}, + {5, 27, 31, 33, 45}, + {7, 14, 20, 37, 39}, + {17, 30, 40, 49, 51}, + {8, 13, 25, 26, 30}, + {12, 29, 39, 42, 47}, + {3, 9, 29, 38, 40}, + {4, 8, 19, 28, 41}, + {8, 13, 20, 33, 45}, + {19, 26, 32, 50, 51}, + {4, 6, 23, 39, 43}, + {4, 17, 18, 27, 34}, + {1, 8, 16, 29, 32}, + {5, 6, 15, 31, 38}, + {16, 28, 30, 43, 47}, + {7, 28, 31, 33, 43}, + {6, 7, 19, 36, 43}, + {20, 23, 24, 25, 46}, + {11, 27, 28, 36, 41}, + {10, 20, 28, 33, 35}, + {0, 11, 15, 21, 41}, + {0, 2, 37, 41, 45}, + {1, 21, 23, 25, 34}, + {3, 5, 17, 30, 40}, + {2, 18, 29, 31, 49}, + {0, 2, 19, 39, 47}, + {20, 31, 36, 46, 51}, + {0, 13, 22, 34, 44}, + {1, 2, 40, 45, 50}, + {7, 18, 19, 35, 45}, + {2, 16, 29, 48, 49}, + {6, 11, 14, 45, 47}, + {12, 21, 28, 35, 41}, + {0, 6, 24, 26, 42}, + {9, 14, 15, 26, 39}, + {1, 6, 32, 38, 44}, + {1, 3, 16, 25, 48}, + {1, 27, 30, 31, 41}, + {4, 10, 15, 43, 48}, + {0, 21, 22, 29, 42}, + {1, 10, 24, 27, 30}, + {5, 18, 24, 25, 45}, + {19, 23, 27, 37, 40}, + {0, 8, 10, 34, 50}, + {0, 14, 17, 26, 48}, + {8, 19, 21, 31, 46}, + {19, 32, 35, 40, 46}, + {1, 7, 12, 36, 46}, + {12, 22, 28, 32, 45}, + {4, 13, 32, 34, 43}, + {5, 13, 17, 44, 48}, + {0, 25, 30, 39, 49}, + {13, 32, 34, 37, 39}, + {0, 6, 16, 26, 27}, + {1, 2, 13, 14, 30}, + {3, 7, 9, 31, 46}, + {0, 2, 9, 31, 41}, + {8, 9, 27, 33, 40}, + {5, 13, 18, 30, 40}, + {2, 38, 41, 43, 50}, + {5, 13, 41, 44, 50}, + {0, 5, 18, 21, 46}, + {2, 10, 27, 28, 43}, + {3, 16, 31, 45, 50}, + {2, 33, 45, 46, 50}, + {4, 20, 30, 42, 47}, + {2, 7, 18, 46, 49}, + {0, 6, 8, 23, 39}, + {5, 7, 24, 31, 40}, + {4, 17, 24, 25, 47}, + {6, 17, 30, 35, 44}, + {1, 14, 23, 38, 39}, + {19, 21, 34, 38, 43}, + {0, 27, 39, 41, 44}, + {2, 7, 16, 20, 22}, + {0, 1, 12, 14, 45}, + {2, 34, 38, 41, 48}, + {15, 23, 28, 35, 51}, + {12, 15, 19, 41, 42}, + {15, 24, 29, 41, 48}, + {1, 4, 19, 40, 47}, + {0, 3, 11, 18, 44}, + {5, 7, 36, 41, 44}, + {7, 22, 32, 46, 47}, + {4, 5, 17, 29, 32}, + {5, 18, 24, 33, 36}, + {2, 4, 8, 15, 31}, + {6, 12, 19, 33, 42}, + {6, 7, 23, 32, 42}, + {15, 18, 25, 31, 48}, + {17, 26, 39, 44, 50}, + {7, 20, 37, 49, 51}, + {6, 19, 25, 27, 50}, + {5, 10, 14, 17, 44}, + {3, 6, 7, 17, 43}, + {14, 23, 27, 41, 42}, + {7, 20, 21, 35, 42}, + {4, 7, 8, 33, 44}, + {8, 21, 30, 37, 46}, + {0, 2, 3, 13, 51}, + {6, 7, 12, 21, 47}, + {4, 10, 30, 35, 41}, + {0, 1, 5, 39, 48}, + {5, 6, 19, 20, 30}, + {4, 19, 20, 42, 43}, + {5, 12, 14, 40, 45}, + {3, 26, 29, 31, 46}, + {3, 25, 34, 47, 48}, + {7, 8, 41, 42, 47}, + {4, 8, 30, 36, 51}, + {9, 20, 21, 30, 33}, + {0, 17, 20, 23, 30}, + {17, 33, 34, 39, 46}, + {3, 24, 29, 31, 41}, + {13, 20, 29, 42, 51}, + {3, 15, 22, 26, 39}, + {5, 12, 15, 16, 29}, + {5, 15, 31, 36, 45}, + {12, 17, 22, 36, 43}, + {16, 18, 21, 32, 47}, + {0, 13, 32, 38, 47}, + {20, 26, 30, 31, 39}, + {0, 3, 8, 19, 45}, + {0, 8, 11, 21, 51}, + {3, 17, 19, 32, 46}, + {1, 10, 27, 39, 44}, + {5, 16, 21, 46, 47}, + {6, 10, 28, 37, 41}, + {6, 31, 32, 47, 51}, + {1, 14, 21, 30, 35}, + {3, 7, 16, 24, 44}, + {7, 8, 15, 18, 47}, + {4, 5, 9, 14, 30}, + {2, 3, 21, 29, 49}, + {15, 34, 45, 47, 50}, + {0, 14, 33, 40, 47}, + {19, 32, 38, 40, 46}, + {19, 26, 32, 34, 35}, + {13, 15, 27, 28, 29}, + {12, 15, 27, 33, 41}, + {14, 15, 16, 28, 46}, + {5, 11, 20, 26, 39}, + {6, 15, 18, 20, 31}, + {12, 19, 29, 39, 45}, + {1, 17, 27, 33, 41}, + {8, 21, 33, 42, 48}, + {7, 13, 35, 39, 50}, + {1, 4, 6, 8, 34}, + {11, 25, 27, 31, 44}, + {16, 31, 33, 39, 44}, + {17, 18, 24, 27, 44}, + {3, 11, 16, 31, 45}, + {5, 13, 20, 27, 40}, + {6, 20, 32, 34, 40}, + {0, 9, 15, 24, 28}, + {2, 11, 41, 45, 46}, + {11, 15, 17, 28, 36}, + {6, 16, 18, 45, 51}, + {4, 15, 20, 33, 49}, + {20, 21, 23, 43, 47}, + {3, 25, 29, 30, 35}, + {2, 3, 17, 24, 43}, + {13, 14, 19, 36, 40}, + {4, 10, 30, 35, 50}, + {1, 17, 24, 27, 41}, + {6, 7, 11, 19, 51}, + {2, 6, 28, 31, 39}, + {7, 18, 20, 23, 39}, + {25, 26, 30, 39, 47}, + {10, 14, 20, 24, 40}, + {1, 14, 18, 37, 41}, + {16, 21, 31, 41, 44}, + {5, 6, 9, 18, 40}, + {13, 14, 18, 30, 44}, + {11, 17, 35, 42, 43}, + {1, 8, 13, 26, 50}, + {3, 4, 8, 10, 21}, + {6, 12, 13, 19, 42}, + {8, 15, 41, 46, 50}, + {0, 13, 22, 32, 50}, + {2, 15, 22, 39, 45}, + {5, 19, 41, 44, 47}, + {12, 13, 26, 43, 46}, + {6, 14, 15, 19, 46}, + {2, 7, 23, 26, 28}, + {15, 18, 19, 28, 35}, + {10, 18, 26, 28, 41}, + {8, 14, 17, 40, 41}, + {26, 27, 32, 45, 48}, + {3, 15, 22, 42, 51}, + {14, 29, 40, 47, 51}, + {0, 14, 26, 43, 50}, + {1, 11, 27, 29, 31}, + {4, 15, 18, 26, 41}, + {9, 14, 28, 41, 49}, + {8, 13, 24, 35, 39}, + {9, 17, 26, 30, 31}, + {6, 18, 32, 33, 34}, + {7, 14, 23, 27, 51}, + {0, 11, 22, 26, 29}, + {7, 26, 39, 42, 49}, + {14, 15, 20, 41, 49}, + {7, 12, 20, 37, 48}, + {21, 24, 30, 32, 34}, + {2, 19, 22, 32, 34}, + {5, 20, 24, 31, 48}, + {3, 30, 41, 42, 48}, + {0, 19, 29, 45, 51}, + {12, 26, 30, 39, 46}, + {4, 13, 26, 37, 41}, + {1, 14, 17, 19, 25}, + {12, 16, 29, 44, 47}, + {4, 18, 28, 35, 41}, + {6, 7, 18, 31, 50}, + {17, 21, 24, 30, 41}, + {13, 15, 24, 41, 44}, + {2, 3, 8, 25, 34}, + {0, 9, 17, 26, 32}, + {5, 21, 32, 43, 47}, + {10, 31, 38, 44, 45}, + {18, 19, 20, 33, 51}, + {3, 4, 6, 13, 29}, + {8, 19, 20, 21, 50}, + {0, 16, 29, 36, 43}, + {6, 15, 19, 20, 48}, + {16, 18, 30, 31, 32}, + {2, 17, 26, 39, 51}, + {9, 11, 21, 45, 47}, + {5, 16, 17, 30, 45}, + {8, 9, 21, 39, 46}, + {25, 30, 36, 43, 45}, + {4, 26, 31, 33, 44}, + {0, 18, 25, 30, 31}, + {2, 7, 44, 45, 46}, + {3, 13, 30, 37, 42}, + {0, 5, 8, 15, 34}, + {1, 5, 14, 19, 24}, + {9, 19, 26, 32, 33}, + {5, 8, 25, 34, 41}, + {6, 8, 26, 34, 42}, + {5, 6, 15, 30, 31}, + {2, 9, 10, 14, 15}, + {1, 13, 18, 19, 26}, + {4, 11, 33, 43, 47}, + {2, 29, 30, 39, 43}, + {6, 14, 24, 32, 36}, + {2, 3, 26, 28, 31}, + {3, 21, 28, 34, 48}, + {17, 22, 27, 40, 46}, + {6, 19, 25, 37, 41}, + {2, 22, 27, 28, 34}, + {1, 7, 26, 27, 38}, + {0, 1, 14, 16, 20}, + {0, 19, 34, 46, 47}, + {14, 28, 37, 41, 42}, + {5, 42, 43, 44, 48}, + {0, 8, 21, 32, 33}, + {0, 15, 28, 37, 38}, + {11, 16, 21, 29, 39}, + {5, 26, 29, 42, 49}, + {5, 29, 43, 44, 48}, + {6, 12, 26, 30, 43}, + {0, 25, 39, 43, 48}, + {1, 7, 29, 34, 42}, + {4, 16, 34, 38, 43}, + {1, 8, 12, 34, 50}, + {14, 22, 24, 27, 43}, + {14, 21, 34, 38, 49}, + {1, 5, 18, 24, 30}, + {20, 32, 33, 38, 40}, + {2, 17, 19, 32, 36}, + {21, 29, 32, 34, 36}, + {8, 19, 28, 31, 41}, + {2, 10, 14, 15, 26}, + {9, 14, 15, 40, 46}, + {14, 19, 22, 41, 45}, + {4, 41, 43, 45, 46}, + {3, 9, 27, 39, 42}, + {18, 23, 27, 44, 47}, + {18, 21, 31, 38, 48}, + {14, 18, 23, 31, 47}, + {18, 26, 31, 32, 36}, + {27, 33, 38, 45, 46}, + {1, 20, 24, 33, 38}, + {2, 11, 15, 38, 39}, + {3, 8, 34, 35, 44}, + {11, 13, 33, 45, 46}, + {1, 8, 13, 22, 47}, + {2, 18, 21, 31, 33}, + {5, 9, 14, 31, 36}, + {10, 21, 29, 39, 47}, + {15, 19, 36, 41, 50}, + {18, 19, 21, 22, 31}, + {24, 32, 33, 45, 47}, + {13, 26, 28, 36, 46}, + {0, 5, 17, 35, 43}, + {1, 4, 32, 33, 45}, + {19, 34, 39, 43, 47}, + {10, 13, 18, 33, 39}, + {5, 6, 8, 19, 25}, + {7, 14, 33, 35, 39}, + {2, 10, 25, 27, 28}, + {13, 16, 29, 30, 44}, + {4, 14, 17, 19, 44}, + {27, 32, 35, 41, 45}, + {1, 15, 26, 35, 40}, + {3, 10, 17, 30, 38}, + {14, 17, 18, 42, 44}, + {0, 26, 30, 45, 50}, + {22, 34, 40, 45, 47}, + {1, 8, 20, 28, 40}, + {8, 20, 22, 33, 45}, + {11, 15, 25, 36, 41}, + {3, 29, 35, 45, 50}, + {7, 19, 26, 41, 46}, + {6, 7, 23, 46, 50}, + {2, 8, 19, 32, 42}, + {17, 19, 28, 39, 41}, + {15, 26, 28, 31, 50}, + {3, 29, 30, 37, 39}, + {5, 11, 17, 44, 47}, + {5, 12, 15, 31, 37}, + {1, 40, 43, 47, 48}, + {3, 16, 18, 30, 41}, + {0, 2, 24, 26, 35}, + {1, 19, 40, 41, 47}, + {7, 13, 28, 46, 49}, + {13, 26, 34, 35, 49}, + {7, 12, 15, 26, 41}, + {2, 13, 38, 39, 43}, + {13, 19, 36, 39, 46}, + {14, 20, 23, 27, 34}, + {15, 16, 34, 41, 51}, + {1, 7, 18, 21, 33}, + {11, 18, 22, 33, 46}, + {12, 18, 22, 29, 44}, + {9, 16, 21, 34, 49}, + {1, 2, 27, 30, 36}, + {1, 9, 27, 39, 42}, + {18, 31, 41, 46, 47}, + {7, 12, 26, 30, 46}, + {6, 34, 40, 47, 49}, + {2, 15, 45, 49, 50}, + {6, 26, 34, 37, 39}, + {21, 23, 34, 43, 48}, + {8, 24, 32, 36, 47}, + {5, 6, 9, 19, 33}, + {20, 21, 27, 31, 34}, + {1, 18, 24, 30, 43}, + {13, 14, 30, 38, 39}, + {14, 24, 25, 27, 47}, + {3, 4, 6, 14, 30}, + {30, 31, 41, 42, 43}, + {2, 13, 16, 34, 42}, + {7, 11, 17, 34, 46}, + {17, 21, 34, 36, 50}, + {3, 11, 36, 42, 44}, + {8, 13, 32, 47, 48}, + {7, 13, 20, 37, 43}, + {4, 9, 31, 36, 43}, + {14, 20, 30, 40, 41}, + {0, 4, 9, 14, 26}, + {6, 13, 25, 32, 36}, + {23, 32, 42, 43, 45}, + {20, 24, 25, 33, 45}, + {10, 13, 15, 28, 32}, + {5, 31, 33, 43, 51}, + {0, 15, 33, 36, 46}, + {5, 30, 33, 39, 44}, + {27, 28, 32, 42, 45}, + {2, 5, 33, 44, 47}, + {4, 33, 38, 45, 46}, + {24, 30, 39, 42, 43}, + {5, 14, 25, 36, 40}, + {12, 28, 36, 37, 41}, + {1, 13, 15, 38, 40}, + {2, 20, 21, 25, 47}, + {2, 16, 28, 44, 46}, + {1, 4, 9, 17, 37}, + {2, 3, 12, 16, 22}, + {0, 3, 5, 27, 39}, + {20, 26, 29, 46, 48}, + {10, 30, 33, 41, 43}, + {0, 29, 40, 41, 42}, + {0, 5, 27, 43, 44}, + {4, 15, 17, 19, 27}, + {9, 28, 29, 41, 47}, + {3, 11, 23, 41, 42}, + {6, 40, 41, 42, 45}, + {7, 16, 22, 24, 42}, + {7, 17, 33, 39, 44}, + {4, 10, 14, 27, 38}, + {0, 18, 19, 26, 34}, + {5, 18, 29, 43, 49}, + {6, 9, 30, 45, 46}, + {15, 17, 24, 28, 49}, + {1, 4, 30, 33, 39}, + {10, 27, 32, 40, 44}, + {7, 13, 19, 26, 49}, + {18, 19, 27, 29, 44}, + {8, 29, 30, 43, 45}, + {8, 13, 25, 26, 29}, + {5, 7, 15, 17, 44}, + {3, 6, 17, 32, 47}, + {13, 29, 39, 47, 49}, + {2, 15, 36, 45, 46}, + {1, 15, 21, 29, 47}, + {3, 4, 10, 29, 37}, + {12, 15, 20, 46, 48}, + {3, 31, 34, 43, 44}, + {5, 21, 30, 34, 37}, + {17, 33, 44, 46, 49}, + {7, 8, 20, 30, 49}, + {2, 15, 19, 31, 38}, + {0, 2, 13, 20, 21}, + {5, 15, 18, 46, 50}, + {0, 4, 31, 32, 45}, + {3, 15, 17, 37, 42}, + {1, 9, 15, 28, 49}, + {8, 22, 32, 34, 40}, + {19, 33, 43, 46, 51}, + {2, 6, 13, 15, 29}, + {16, 20, 41, 46, 50}, + {15, 25, 30, 35, 43}, + {4, 17, 31, 33, 36}, + {0, 21, 32, 38, 47}, + {4, 9, 21, 30, 36}, + {28, 29, 33, 45, 46}, + {16, 17, 19, 37, 42}, + {2, 18, 21, 33, 34}, + {6, 14, 19, 23, 25}, + {4, 18, 35, 43, 45}, + {3, 4, 19, 33, 43}, + {13, 18, 25, 39, 48}, + {7, 14, 19, 40, 49}, + {1, 8, 13, 39, 49}, + {7, 19, 26, 30, 45}, + {1, 27, 30, 33, 37}, + {0, 13, 30, 31, 32}, + {7, 8, 10, 19, 33}, + {0, 10, 26, 27, 47}, + {3, 9, 15, 20, 46}, + {3, 30, 33, 41, 43}, + {4, 10, 15, 17, 18}, + {6, 9, 15, 45, 47}, + {12, 13, 18, 22, 31}, + {5, 17, 18, 22, 47}, + {0, 31, 41, 44, 48}, + {20, 26, 27, 33, 48}, + {3, 31, 32, 40, 42}, + {29, 36, 38, 40, 42}, + {1, 30, 37, 40, 45}, + {10, 24, 31, 34, 44}, + {8, 19, 32, 36, 42}, + {2, 13, 20, 21, 46}, + {0, 11, 19, 45, 47}, + {7, 9, 23, 33, 34}, + {2, 16, 18, 29, 50}, + {3, 8, 18, 29, 45}, + {8, 15, 16, 24, 42}, + {12, 20, 23, 34, 47}, + {3, 15, 17, 28, 49}, + {3, 14, 15, 21, 42}, + {4, 15, 19, 30, 31}, + {8, 12, 27, 34, 46}, + {14, 31, 40, 49, 50}, + {20, 33, 34, 49, 51}, + {5, 13, 14, 17, 31}, + {19, 23, 29, 33, 42}, + {1, 6, 32, 34, 51}, + {4, 9, 19, 30, 44}, + {7, 11, 17, 18, 30}, + {0, 14, 30, 31, 39}, + {15, 26, 28, 35, 46}, + {5, 18, 29, 32, 49}, + {3, 16, 33, 38, 39}, + {6, 15, 21, 28, 39}, + {1, 14, 34, 37, 42}, + {3, 15, 16, 21, 26}, + {0, 21, 34, 38, 46}, + {12, 34, 36, 37, 47}, + {14, 23, 33, 40, 45}, + {2, 15, 36, 40, 44}, + {1, 3, 14, 47, 48}, + {10, 21, 24, 25, 34}, + {7, 26, 34, 39, 40}, + {32, 36, 37, 45, 47}, + {3, 7, 16, 39, 41}, + {0, 11, 27, 35, 39}, + {0, 3, 13, 19, 48}, + {13, 19, 22, 27, 32}, + {11, 17, 30, 32, 42}, + {5, 11, 13, 18, 29}, + {2, 5, 21, 44, 46}, + {21, 22, 27, 31, 40}, + {9, 15, 20, 28, 51}, + {16, 20, 29, 34, 51}, + {4, 7, 28, 30, 39}, + {4, 5, 25, 28, 30}, + {5, 7, 46, 50, 51}, + {2, 6, 30, 38, 41}, + {21, 25, 31, 34, 35}, + {8, 17, 25, 35, 43}, + {0, 15, 28, 36, 48}, + {6, 20, 29, 35, 45}, + {4, 21, 22, 42, 43}, + {0, 3, 14, 20, 27}, + {7, 8, 18, 38, 46}, + {27, 29, 39, 42, 51}, + {16, 21, 37, 41, 47}, + {9, 20, 21, 45, 47}, + {3, 10, 16, 27, 45}, + {1, 11, 31, 33, 46}, + {2, 8, 16, 28, 32}, + {2, 7, 21, 33, 35}, + {1, 5, 14, 36, 45}, + {0, 3, 16, 24, 44}, + {8, 21, 23, 40, 42}, + {4, 16, 21, 30, 41}, + {3, 26, 34, 39, 50}, + {1, 7, 33, 41, 50}, + {4, 13, 26, 36, 38}, + {0, 1, 17, 35, 43}, + {3, 8, 21, 27, 35}, + {1, 29, 30, 31, 42}, + {4, 20, 26, 43, 44}, + {5, 6, 8, 14, 32}, + {3, 8, 10, 38, 42}, + {9, 29, 31, 33, 44}, + {4, 15, 17, 38, 49}, + {9, 26, 39, 46, 50}, + {25, 28, 39, 40, 41}, + {0, 2, 9, 19, 26}, + {3, 14, 29, 33, 41}, + {8, 27, 35, 40, 49}, + {6, 13, 26, 27, 46}, + {6, 17, 21, 31, 45}, + {2, 5, 8, 21, 46}, + {1, 2, 15, 26, 36}, + {7, 12, 19, 20, 41}, + {2, 6, 15, 16, 50}, + {1, 17, 19, 27, 28}, + {2, 6, 14, 28, 38}, + {2, 15, 23, 34, 40}, + {1, 13, 31, 35, 44}, + {15, 21, 28, 32, 36}, + {3, 13, 23, 29, 32}, + {6, 18, 20, 33, 51}, + {3, 14, 16, 37, 51}, + {17, 20, 34, 42, 43}, + {1, 4, 17, 35, 47}, + {1, 8, 34, 37, 48}, + {18, 19, 20, 33, 41}, + {3, 8, 22, 42, 46}, + {0, 18, 22, 31, 33}, + {4, 31, 38, 39, 44}, + {16, 18, 27, 44, 47}, + {8, 17, 20, 46, 48}, + {4, 20, 24, 27, 46}, + {3, 13, 33, 39, 41}, + {18, 24, 27, 40, 47}, + {2, 3, 8, 24, 28}, + {8, 12, 36, 46, 47}, + {1, 8, 13, 40, 42}, + {2, 15, 31, 32, 40}, + {16, 20, 28, 41, 49}, + {3, 17, 31, 44, 50}, + {19, 32, 40, 41, 43}, + {2, 7, 13, 26, 49}, + {4, 15, 28, 45, 46}, + {4, 16, 29, 36, 41}, + {6, 19, 25, 48, 50}, + {2, 19, 27, 41, 50}, + {0, 6, 21, 27, 39}, + {3, 12, 17, 23, 29}, + {6, 7, 10, 20, 48}, + {0, 9, 16, 29, 47}, + {5, 14, 19, 27, 50}, + {1, 10, 13, 33, 39}, + {14, 15, 17, 32, 40}, + {3, 4, 21, 47, 48}, + {0, 1, 21, 36, 40}, + {13, 14, 29, 30, 43}, + {1, 17, 43, 46, 49}, + {1, 21, 33, 38, 46}, + {12, 33, 39, 43, 46}, + {20, 24, 33, 42, 51}, + {14, 20, 21, 27, 42}, + {9, 17, 42, 43, 47}, + {0, 20, 25, 35, 39}, + {15, 16, 30, 33, 42}, + {10, 17, 18, 24, 31}, + {2, 5, 21, 44, 51}, + {10, 29, 34, 38, 42}, + {14, 27, 36, 45, 46}, + {3, 25, 29, 43, 47}, + {11, 13, 19, 32, 44}, + {6, 22, 31, 32, 43}, + {3, 29, 31, 39, 48}, + {1, 27, 36, 42, 45}, + {5, 13, 22, 39, 41}, + {2, 8, 18, 31, 38}, + {14, 25, 34, 40, 49}, + {4, 30, 35, 37, 38}, + {7, 10, 20, 39, 40}, + {2, 15, 24, 25, 39}, + {6, 11, 16, 32, 43}, + {3, 4, 6, 17, 24}, + {5, 25, 31, 35, 37}, + {5, 16, 26, 31, 33}, + {5, 12, 15, 29, 44}, + {17, 34, 38, 47, 50}, + {1, 16, 17, 30, 51}, + {2, 3, 27, 41, 51}, + {4, 7, 30, 50, 51}, + {3, 12, 16, 17, 47}, + {0, 10, 13, 30, 31}, + {10, 12, 31, 44, 47}, + {2, 7, 12, 15, 19}, + {1, 13, 16, 27, 33}, + {0, 3, 10, 25, 29}, + {2, 4, 34, 38, 41}, + {6, 17, 32, 34, 38}, + {4, 31, 32, 44, 47}, + {0, 7, 20, 21, 35}, + {16, 21, 36, 38, 47}, + {2, 6, 10, 12, 19}, + {3, 15, 18, 37, 42}, + {18, 21, 23, 31, 43}, + {4, 13, 18, 19, 26}, + {0, 7, 12, 35, 46}, + {2, 6, 25, 32, 46}, + {6, 25, 34, 45, 49}, + {1, 21, 27, 28, 44}, + {2, 4, 11, 23, 28}, + {6, 27, 33, 45, 50}, + {0, 18, 26, 35, 40}, + {8, 9, 19, 37, 45}, + {3, 5, 21, 23, 29}, + {3, 4, 9, 20, 42}, + {5, 6, 7, 31, 38}, + {6, 12, 19, 22, 30}, + {14, 28, 33, 41, 43}, + {5, 14, 20, 24, 46}, + {16, 33, 38, 40, 42}, + {8, 11, 13, 27, 39}, + {1, 13, 16, 29, 44}, + {15, 22, 25, 28, 39}, + {2, 27, 30, 33, 41}, + {1, 29, 36, 42, 48}, + {1, 7, 27, 37, 41}, + {20, 21, 35, 46, 49}, + {7, 8, 22, 30, 47}, + {18, 19, 23, 31, 48}, + {20, 21, 32, 45, 49}, + {7, 11, 31, 39, 46}, + {21, 30, 43, 49, 51}, + {0, 3, 29, 32, 48}, + {12, 20, 28, 33, 45}, + {7, 14, 20, 32, 47}, + {0, 1, 13, 41, 46}, + {5, 16, 18, 33, 49}, + {6, 7, 11, 18, 20}, + {2, 7, 18, 23, 28}, + {7, 18, 26, 46, 51}, + {16, 36, 42, 43, 46}, + {1, 13, 16, 32, 42}, + {6, 27, 29, 45, 50}, + {9, 27, 33, 40, 43}, + {6, 28, 34, 45, 50}, + {1, 4, 17, 36, 51}, + {4, 7, 18, 31, 38}, + {5, 7, 12, 20, 26}, + {3, 21, 40, 42, 51}, + {2, 13, 23, 28, 43}, + {0, 4, 12, 43, 45}, + {1, 7, 13, 27, 37}, + {14, 22, 27, 29, 34}, + {15, 24, 31, 34, 47}, + {21, 31, 32, 33, 47}, + {1, 8, 18, 21, 25}, + {14, 15, 26, 41, 46}, + {2, 4, 10, 43, 48}, + {9, 17, 31, 43, 50}, + {11, 20, 21, 34, 43}, + {4, 6, 9, 32, 44}, + {3, 16, 31, 36, 37}, + {3, 16, 39, 48, 49}, + {5, 18, 20, 38, 41}, + {3, 10, 14, 33, 40}, + {19, 24, 30, 43, 46}, + {32, 33, 46, 48, 49}, + {16, 20, 29, 31, 49}, + {4, 17, 23, 24, 32}, + {9, 12, 19, 32, 50}, + {2, 11, 20, 28, 32}, + {19, 29, 30, 32, 38}, + {0, 5, 25, 31, 36}, + {0, 4, 8, 13, 29}, + {6, 8, 20, 24, 45}, + {7, 20, 22, 36, 42}, + {8, 19, 25, 32, 44}, + {8, 14, 26, 27, 51}, + {21, 32, 36, 45, 51}, + {13, 20, 30, 36, 39}, + {8, 31, 32, 36, 44}, + {8, 27, 42, 44, 47}, + {9, 21, 26, 39, 51}, + {0, 19, 34, 45, 51}, + {2, 9, 24, 26, 28}, + {6, 31, 33, 34, 45}, + {3, 5, 15, 44, 45}, + {13, 21, 24, 45, 47}, + {4, 18, 38, 43, 48}, + {3, 9, 12, 16, 43}, + {4, 28, 38, 41, 42}, + {3, 4, 5, 17, 28}, + {0, 6, 29, 44, 45}, + {20, 21, 39, 47, 51}, + {6, 17, 19, 25, 35}, + {34, 35, 38, 39, 47}, + {6, 16, 38, 40, 42}, + {10, 13, 26, 31, 51}, + {7, 23, 27, 40, 48}, + {2, 37, 41, 42, 49}, + {10, 21, 24, 34, 42}, + {6, 7, 20, 42, 47}, + {8, 15, 34, 35, 37}, + {13, 19, 20, 46, 48}, + {2, 3, 12, 24, 29}, + {2, 13, 21, 41, 46}, + {0, 3, 7, 21, 42}, + {10, 16, 27, 33, 42}, + {0, 14, 20, 31, 44}, + {6, 18, 41, 45, 46}, + {4, 8, 30, 33, 41}, + {11, 21, 31, 45, 47}, + {0, 13, 15, 19, 22}, + {1, 2, 28, 29, 33}, + {3, 23, 29, 47, 48}, + {2, 11, 21, 34, 45}, + {15, 17, 21, 25, 28}, + {8, 18, 26, 34, 50}, + {13, 23, 26, 27, 42}, + {13, 18, 39, 45, 51}, + {4, 14, 29, 43, 45}, + {13, 17, 22, 30, 38}, + {5, 9, 23, 26, 31}, + {4, 10, 19, 27, 32}, + {8, 14, 38, 44, 47}, + {2, 5, 27, 38, 41}, + {9, 28, 30, 43, 50}, + {2, 5, 31, 36, 46}, + {5, 16, 32, 34, 47}, + {7, 10, 32, 38, 46}, + {4, 8, 11, 23, 34}, + {2, 6, 34, 41, 46}, + {0, 26, 35, 43, 44}, + {10, 21, 28, 30, 34}, + {1, 15, 27, 35, 37}, + {11, 13, 31, 43, 44}, + {6, 13, 26, 28, 29}, + {11, 13, 26, 30, 34}, + {4, 6, 27, 30, 47}, + {4, 6, 12, 36, 45}, + {20, 21, 24, 30, 43}, + {14, 33, 36, 44, 46}, + {1, 25, 40, 43, 48}, + {10, 25, 33, 46, 50}, + {8, 10, 30, 45, 47}, + {5, 7, 17, 29, 30}, + {1, 40, 41, 47, 49}, + {13, 20, 31, 45, 46}, + {9, 19, 20, 32, 44}, + {16, 31, 42, 46, 49}, + {22, 23, 26, 39, 50}, + {3, 4, 5, 29, 48}, + {8, 13, 21, 27, 50}, + {2, 7, 15, 43, 49}, + {2, 6, 32, 35, 39}, + {11, 31, 41, 44, 49}, + {2, 6, 17, 41, 47}, + {2, 12, 15, 23, 32}, + {0, 20, 21, 39, 51}, + {17, 30, 32, 41, 48}, + {2, 10, 17, 18, 28}, + {1, 26, 27, 37, 48}, + {6, 27, 38, 45, 50}, + {19, 23, 26, 45, 46}, + {3, 18, 20, 33, 51}, + {3, 7, 34, 41, 47}, + {7, 17, 28, 30, 51}, + {6, 12, 20, 28, 45}, + {2, 3, 14, 28, 35}, + {2, 15, 21, 22, 42}, + {1, 16, 17, 43, 50}, + {12, 23, 27, 33, 40}, + {4, 18, 36, 43, 51}, + {6, 20, 21, 29, 34}, + {1, 5, 29, 31, 41}, + {17, 29, 30, 41, 50}, + {16, 20, 21, 33, 43}, + {2, 12, 15, 33, 48}, + {2, 17, 22, 41, 45}, + {7, 27, 33, 36, 51}, + {2, 19, 20, 32, 37}, + {6, 34, 45, 46, 49}, + {8, 12, 15, 17, 34}, + {5, 6, 24, 25, 32}, + {29, 31, 32, 33, 46}, + {14, 23, 26, 27, 42}, + {2, 15, 27, 43, 47}, + {4, 12, 26, 39, 41}, + {18, 20, 33, 36, 40}, + {2, 8, 10, 28, 45}, + {7, 9, 33, 34, 38}, + {1, 7, 18, 27, 32}, + {7, 19, 25, 29, 32}, + {4, 22, 26, 37, 43}, + {6, 13, 19, 42, 43}, + {0, 13, 32, 37, 43}, + {3, 16, 17, 28, 47}, + {2, 13, 26, 40, 46}, + {2, 7, 9, 44, 46}, + {3, 15, 19, 32, 36}, + {5, 18, 29, 33, 45}, + {4, 5, 17, 47, 48}, + {0, 4, 19, 26, 47}, + {5, 14, 26, 28, 31}, + {3, 5, 28, 31, 47}, + {27, 29, 37, 40, 46}, + {4, 8, 21, 32, 42}, + {20, 31, 33, 45, 49}, + {13, 15, 17, 36, 43}, + {0, 16, 17, 42, 46}, + {3, 7, 8, 14, 40}, + {2, 20, 27, 40, 49}, + {17, 21, 31, 34, 49}, + {6, 13, 23, 39, 41}, + {16, 26, 29, 46, 48}, + {0, 10, 11, 21, 39}, + {7, 13, 16, 20, 45}, + {6, 33, 34, 38, 45}, + {7, 9, 20, 30, 32}, + {5, 36, 38, 44, 47}, + {0, 18, 21, 28, 44}, + {13, 27, 30, 32, 45}, + {5, 8, 22, 23, 34}, + {0, 39, 40, 42, 48}, + {4, 6, 28, 45, 50}, + {1, 7, 19, 20, 36}, + {4, 10, 14, 17, 37}, + {8, 20, 21, 35, 39}, + {7, 17, 18, 25, 44}, + {5, 7, 10, 18, 40}, + {10, 12, 24, 32, 45}, + {2, 13, 26, 34, 43}, + {4, 9, 13, 32, 45}, + {2, 13, 28, 30, 31}, + {4, 13, 23, 30, 38}, + {8, 15, 31, 38, 44}, + {1, 18, 22, 43, 44}, + {17, 30, 36, 45, 51}, + {3, 6, 10, 16, 26}, + {7, 14, 24, 30, 46}, + {11, 16, 34, 41, 47}, + {6, 14, 15, 31, 41}, + {23, 30, 43, 44, 46}, + {1, 13, 16, 42, 46}, + {0, 9, 11, 27, 40}, + {4, 17, 32, 34, 51}, + {26, 39, 44, 47, 51}, + {20, 27, 35, 40, 51}, + {10, 14, 32, 39, 45}, + {20, 23, 32, 46, 50}, + {8, 23, 27, 31, 44}, + {8, 24, 32, 45, 48}, + {1, 19, 25, 29, 45}, + {1, 14, 28, 43, 45}, + {23, 34, 35, 38, 47}, + {0, 10, 17, 40, 43}, + {11, 16, 20, 29, 41}, + {4, 12, 32, 42, 45}, + {6, 14, 28, 35, 45}, + {16, 33, 43, 46, 50}, + {8, 12, 31, 40, 47}, + {0, 22, 25, 26, 37}, + {20, 23, 29, 38, 46}, + {1, 31, 38, 39, 40}, + {3, 9, 26, 29, 46}, + {0, 7, 35, 43, 46}, + {3, 9, 33, 42, 47}, + {25, 32, 34, 36, 47}, + {12, 21, 27, 34, 39}, + {7, 17, 22, 43, 51}, + {1, 11, 14, 21, 30}, + {5, 7, 32, 40, 45}, + {30, 32, 35, 43, 44}, + {4, 12, 17, 18, 49}, + {0, 6, 13, 25, 29}, + {3, 25, 28, 30, 41}, + {3, 16, 20, 21, 39}, + {10, 16, 17, 28, 30}, + {7, 22, 27, 32, 40}, + {8, 28, 30, 35, 43}, + {6, 14, 18, 40, 42}, + {21, 28, 39, 47, 51}, + {8, 12, 19, 29, 32}, + {3, 25, 27, 29, 33}, + {1, 14, 31, 43, 45}, + {12, 27, 31, 40, 43}, + {0, 6, 29, 40, 45}, + {3, 8, 29, 31, 51}, + {4, 31, 32, 43, 50}, + {14, 35, 40, 42, 51}, + {5, 8, 15, 18, 43}, + {1, 14, 21, 23, 42}, + {5, 21, 38, 47, 50}, + {14, 15, 24, 33, 41}, + {8, 11, 17, 31, 34}, + {15, 23, 34, 41, 44}, + {0, 9, 17, 23, 30}, + {15, 20, 30, 33, 37}, + {2, 6, 26, 41, 46}, + {24, 28, 33, 46, 49}, + {15, 30, 31, 33, 43}, + {4, 9, 30, 47, 49}, + {8, 14, 18, 27, 46}, + {5, 11, 18, 41, 47}, + {1, 13, 39, 41, 47}, + {5, 13, 26, 28, 48}, + {1, 8, 26, 39, 44}, + {1, 9, 28, 40, 51}, + {10, 14, 40, 41, 47}, + {1, 25, 27, 31, 49}, + {0, 5, 8, 33, 47}, + {1, 14, 15, 21, 32}, + {14, 19, 26, 42, 45}, + {4, 6, 7, 17, 50}, + {4, 5, 7, 24, 43}, + {1, 13, 15, 28, 31}, + {14, 23, 40, 44, 45}, + {0, 1, 28, 33, 46}, + {1, 8, 9, 15, 47}, + {13, 18, 22, 31, 37}, + {6, 14, 36, 40, 47}, + {0, 13, 43, 46, 51}, + {4, 12, 16, 18, 31}, + {15, 21, 27, 33, 47}, + {2, 16, 20, 21, 28}, + {7, 19, 25, 33, 39}, + {4, 9, 13, 20, 30}, + {4, 18, 19, 45, 50}, + {6, 23, 31, 35, 44}, + {11, 15, 17, 31, 41}, + {16, 17, 27, 30, 37}, + {1, 8, 14, 18, 22}, + {0, 25, 26, 31, 46}, + {5, 6, 45, 48, 51}, + {17, 19, 43, 44, 48}, + {4, 11, 28, 34, 43}, + {0, 14, 15, 39, 42}, + {21, 22, 25, 40, 47}, + {5, 6, 17, 31, 41}, + {3, 4, 20, 30, 47}, + {0, 1, 27, 38, 41}, + {3, 15, 16, 33, 43}, + {8, 14, 21, 26, 35}, + {3, 10, 27, 32, 45}, + {3, 24, 28, 32, 41}, + {6, 7, 21, 33, 50}, + {5, 10, 11, 21, 44}, + {10, 18, 21, 38, 44}, + {0, 18, 35, 41, 44}, + {1, 5, 8, 14, 15}, + {5, 7, 12, 20, 37}, + {1, 8, 19, 22, 45}, + {15, 26, 34, 41, 46}, + {3, 15, 29, 40, 47}, + {5, 19, 25, 30, 43}, + {13, 21, 26, 31, 46}, + {5, 9, 27, 44, 47}, + {15, 18, 28, 39, 46}, + {13, 14, 15, 38, 40}, + {14, 26, 28, 30, 39}, + {21, 32, 36, 39, 45}, + {2, 24, 27, 31, 40}, + {10, 14, 21, 24, 40}, + {0, 15, 26, 30, 37}, + {3, 20, 23, 29, 47}, + {15, 16, 17, 36, 43}, + {0, 1, 7, 22, 27}, + {12, 14, 33, 36, 46}, + {5, 22, 31, 36, 42}, + {2, 4, 7, 28, 49}, + {4, 10, 17, 35, 47}, + {9, 14, 26, 27, 51}, + {3, 8, 14, 16, 26}, + {0, 6, 26, 50, 51}, + {13, 26, 28, 45, 46}, + {1, 4, 21, 34, 42}, + {7, 8, 9, 39, 46}, + {12, 13, 19, 26, 37}, + {2, 33, 36, 41, 51}, + {4, 13, 14, 40, 45}, + {5, 25, 27, 34, 40}, + {16, 18, 26, 39, 48}, + {7, 18, 33, 41, 47}, + {19, 24, 34, 41, 47}, + {1, 6, 7, 46, 48}, + {7, 34, 39, 44, 46}, + {4, 8, 34, 40, 49}, + {13, 14, 15, 35, 41}, + {20, 21, 46, 49, 51}, + {4, 13, 17, 22, 25}, + {1, 8, 40, 43, 48}, + {4, 6, 21, 47, 50}, + {1, 18, 20, 29, 33}, + {4, 26, 33, 43, 48}, + {8, 23, 34, 45, 50}, + {2, 6, 27, 40, 42}, + {7, 21, 30, 33, 48}, + {3, 11, 17, 18, 31}, + {1, 8, 18, 45, 47}, + {3, 7, 20, 26, 35}, + {5, 13, 19, 21, 34}, + {3, 15, 28, 40, 47}, + {0, 8, 12, 26, 28}, + {8, 13, 18, 26, 45}, + {1, 2, 3, 16, 20}, + {6, 10, 20, 32, 44}, + {2, 21, 39, 43, 47}, + {5, 13, 33, 39, 42}, + {2, 15, 17, 44, 45}, + {7, 21, 46, 48, 49}, + {6, 8, 15, 34, 46}, + {21, 22, 30, 34, 36}, + {20, 21, 30, 42, 46}, + {16, 17, 32, 35, 42}, + {3, 6, 12, 16, 26}, + {21, 28, 34, 42, 45}, + {1, 14, 17, 48, 51}, + {1, 4, 30, 36, 47}, + {5, 10, 19, 32, 50}, + {4, 8, 15, 17, 23}, + {6, 13, 15, 17, 26}, + {4, 11, 15, 19, 30}, + {0, 5, 7, 20, 42}, + {0, 13, 21, 37, 46}, + {0, 13, 23, 48, 51}, + {1, 2, 17, 33, 43}, + {1, 4, 43, 49, 50}, + {3, 5, 16, 20, 21}, + {10, 13, 21, 25, 34}, + {6, 31, 37, 44, 47}, + {14, 22, 27, 43, 45}, + {0, 4, 13, 29, 44}, + {7, 17, 23, 26, 43}, + {0, 6, 7, 8, 19}, + {5, 8, 12, 20, 33}, + {17, 20, 33, 39, 44}, + {6, 26, 27, 39, 42}, + {2, 12, 15, 22, 43}, + {4, 14, 17, 29, 37}, + {0, 15, 29, 42, 49}, + {19, 31, 45, 49, 51}, + {4, 17, 28, 35, 45}, + {2, 12, 15, 26, 47}, + {11, 14, 15, 32, 45}, + {15, 20, 36, 38, 46}, + {2, 23, 28, 45, 47}, + {0, 6, 13, 30, 40}, + {0, 28, 41, 43, 45}, + {2, 27, 28, 32, 37}, + {15, 17, 21, 34, 42}, + {0, 20, 33, 35, 49}, + {1, 10, 26, 27, 51}, + {0, 17, 28, 43, 49}, + {15, 18, 20, 31, 39}, + {10, 15, 20, 46, 48}, + {5, 15, 28, 36, 45}, + {15, 28, 37, 45, 47}, + {0, 13, 16, 21, 50}, + {3, 16, 17, 27, 37}, + {6, 17, 29, 32, 33}, + {3, 8, 14, 21, 28}, + {11, 14, 19, 40, 44}, + {0, 1, 24, 29, 42}, + {4, 10, 24, 30, 48}, + {3, 6, 28, 45, 51}, + {11, 17, 31, 44, 51}, + {8, 21, 30, 37, 49}, + {26, 30, 31, 37, 39}, + {0, 2, 28, 32, 46}, + {9, 17, 27, 29, 40}, + {7, 8, 22, 33, 40}, + {9, 20, 33, 34, 43}, + {1, 15, 28, 29, 30}, + {2, 10, 28, 32, 51}, + {7, 36, 39, 43, 46}, + {4, 9, 31, 44, 47}, + {5, 13, 19, 21, 31}, + {2, 6, 8, 11, 15}, + {5, 44, 46, 47, 49}, + {1, 6, 14, 18, 50}, + {6, 7, 22, 33, 51}, + {12, 14, 18, 31, 41}, + {16, 20, 28, 31, 46}, + {0, 8, 17, 18, 43}, + {1, 2, 14, 16, 47}, + {2, 20, 23, 35, 46}, + {2, 8, 9, 15, 43}, + {0, 8, 30, 39, 50}, + {7, 10, 28, 33, 35}, + {28, 32, 34, 37, 41}, + {5, 11, 13, 14, 26}, + {7, 17, 20, 28, 36}, + {7, 13, 18, 33, 40}, + {5, 20, 32, 44, 47}, + {2, 6, 21, 25, 41}, + {15, 17, 30, 32, 39}, + {4, 12, 17, 18, 21}, + {16, 29, 34, 41, 49}, + {2, 19, 43, 44, 45}, + {7, 15, 28, 38, 49}, + {15, 18, 28, 29, 34}, + {3, 16, 26, 31, 36}, + {0, 8, 11, 19, 32}, + {12, 20, 21, 43, 47}, + {2, 9, 15, 17, 50}, + {5, 10, 17, 18, 33}, + {1, 14, 15, 26, 30}, + {3, 13, 15, 22, 42}, + {6, 27, 40, 41, 42}, + {0, 17, 25, 39, 40}, + {8, 23, 38, 47, 50}, + {1, 5, 24, 39, 44}, + {0, 6, 29, 38, 39}, + {1, 2, 26, 35, 39}, + {3, 15, 17, 34, 43}, + {1, 2, 15, 37, 44}, + {7, 17, 21, 22, 34}, + {7, 35, 40, 46, 47}, + {6, 16, 19, 23, 24}, + {4, 14, 17, 26, 44}, + {12, 21, 30, 43, 50}, + {12, 14, 39, 40, 48}, + {9, 21, 31, 44, 51}, + {4, 18, 20, 40, 43}, + {18, 28, 32, 41, 47}, + {4, 17, 20, 32, 36}, + {33, 38, 46, 47, 49}, + {5, 16, 42, 46, 47}, + {16, 21, 30, 47, 51}, + {13, 27, 33, 38, 39}, + {4, 7, 24, 43, 51}, + {1, 13, 18, 25, 27}, + {13, 16, 22, 39, 40}, + {5, 18, 26, 33, 37}, + {12, 15, 23, 28, 32}, + {1, 4, 5, 13, 44}, + {29, 31, 41, 44, 48}, + {0, 13, 23, 33, 47}, + {3, 5, 17, 25, 29}, + {4, 10, 25, 27, 40}, + {5, 9, 21, 29, 47}, + {5, 13, 18, 25, 35}, + {6, 20, 33, 36, 51}, + {1, 25, 28, 41, 46}, + {2, 6, 12, 13, 26}, + {10, 16, 29, 32, 37}, + {1, 7, 20, 31, 51}, + {5, 10, 11, 42, 44}, + {3, 31, 33, 42, 49}, + {1, 6, 20, 45, 49}, + {5, 25, 27, 40, 43}, + {0, 2, 8, 18, 21}, + {5, 18, 32, 34, 48}, + {5, 15, 21, 43, 47}, + {8, 16, 22, 27, 29}, + {4, 6, 8, 27, 45}, + {4, 22, 26, 41, 43}, + {1, 14, 15, 17, 20}, + {18, 27, 28, 32, 45}, + {10, 11, 15, 19, 45}, + {18, 19, 34, 43, 47}, + {4, 5, 21, 33, 44}, + {3, 9, 39, 40, 42}, + {6, 7, 29, 32, 43}, + {1, 23, 40, 48, 51}, + {1, 5, 16, 20, 46}, + {7, 15, 16, 24, 29}, + {0, 13, 24, 28, 45}, + {0, 18, 22, 31, 43}, + {26, 30, 31, 39, 50}, + {5, 8, 43, 44, 45}, + {21, 30, 34, 39, 42}, + {1, 14, 17, 44, 45}, + {2, 27, 28, 46, 51}, + {13, 15, 39, 43, 49}, + {12, 16, 26, 28, 41}, + {7, 20, 26, 28, 37}, + {2, 7, 17, 25, 43}, + {0, 10, 17, 30, 31}, + {1, 12, 26, 34, 47}, + {16, 20, 23, 34, 46}, + {2, 16, 32, 41, 47}, + {5, 9, 14, 27, 37}, + {28, 33, 42, 46, 47}, + {1, 6, 15, 38, 40}, + {3, 6, 36, 45, 46}, + {9, 11, 15, 19, 41}, + {19, 21, 27, 31, 47}, + {11, 16, 19, 25, 42}, + {2, 32, 36, 45, 47}, + {5, 26, 44, 46, 51}, + {4, 8, 9, 30, 33}, + {0, 9, 13, 24, 28}, + {14, 17, 43, 47, 49}, + {13, 30, 31, 39, 42}, + {8, 25, 29, 31, 42}, + {14, 27, 30, 42, 47}, + {6, 20, 25, 26, 32}, + {6, 15, 18, 19, 51}, + {18, 19, 23, 41, 45}, + {9, 11, 30, 34, 43}, + {4, 7, 16, 19, 29}, + {4, 11, 15, 33, 46}, + {6, 13, 16, 42, 49}, + {10, 13, 32, 40, 45}, + {18, 20, 30, 33, 48}, + {8, 19, 21, 24, 46}, + {2, 15, 17, 27, 38}, + {4, 6, 10, 19, 41}, + {10, 19, 26, 37, 45}, + {1, 17, 27, 47, 51}, + {3, 23, 42, 44, 45}, + {3, 29, 30, 34, 35}, + {9, 29, 37, 42, 49}, + {2, 5, 16, 17, 43}, + {6, 15, 18, 34, 41}, + {5, 6, 14, 22, 44}, + {30, 33, 34, 41, 46}, + {7, 18, 23, 28, 44}, + {7, 9, 19, 44, 46}, + {0, 4, 28, 39, 48}, + {0, 1, 20, 33, 44}, + {0, 3, 13, 32, 44}, + {14, 19, 32, 39, 48}, + {3, 13, 16, 46, 47}, + {0, 3, 13, 17, 36}, + {13, 21, 26, 36, 41}, + {7, 13, 14, 27, 44}, + {3, 12, 16, 31, 43}, + {4, 19, 23, 34, 47}, + {2, 7, 8, 28, 44}, + {1, 29, 40, 41, 45}, + {2, 20, 22, 32, 33}, + {1, 8, 30, 34, 45}, + {6, 15, 21, 34, 42}, + {0, 15, 41, 47, 51}, + {6, 14, 19, 22, 41}, + {2, 16, 18, 22, 44}, + {1, 3, 8, 22, 42}, + {2, 3, 6, 15, 30}, + {0, 6, 20, 34, 45}, + {0, 5, 8, 18, 48}, + {6, 22, 30, 38, 45}, + {5, 10, 31, 34, 35}, + {2, 7, 13, 34, 39}, + {30, 43, 44, 47, 49}, + {1, 2, 3, 15, 22}, + {5, 20, 43, 46, 49}, + {0, 12, 21, 36, 47}, + {4, 14, 25, 43, 49}, + {3, 19, 29, 44, 50}, + {6, 14, 28, 45, 51}, + {2, 7, 26, 36, 39}, + {8, 23, 35, 44, 47}, + {1, 5, 12, 24, 44}, + {4, 17, 24, 28, 38}, + {2, 4, 15, 18, 22}, + {4, 8, 28, 30, 42}, + {2, 6, 14, 32, 35}, + {5, 8, 27, 38, 40}, + {0, 2, 3, 21, 41}, + {1, 5, 19, 24, 31}, + {1, 6, 14, 42, 43}, + {0, 12, 17, 19, 26}, + {1, 12, 23, 27, 31}, + {7, 22, 42, 44, 46}, + {14, 15, 23, 25, 40}, + {1, 15, 34, 41, 44}, + {16, 29, 37, 38, 47}, + {1, 12, 14, 18, 45}, + {0, 4, 6, 27, 39}, + {3, 17, 33, 41, 42}, + {0, 34, 40, 47, 51}, + {15, 17, 25, 28, 35}, + {1, 8, 13, 34, 50}, + {7, 11, 28, 33, 34}, + {6, 16, 19, 23, 50}, + {7, 23, 32, 33, 44}, + {0, 2, 6, 19, 40}, + {4, 5, 7, 29, 33}, + {2, 7, 17, 18, 20}, + {2, 3, 4, 28, 51}, + {15, 19, 25, 36, 41}, + {18, 27, 44, 46, 49}, + {2, 19, 24, 41, 42}, + {8, 9, 18, 30, 34}, + {5, 21, 25, 44, 49}, + {8, 18, 38, 44, 45}, + {5, 25, 26, 34, 47}, + {1, 17, 22, 27, 36}, + {9, 20, 33, 39, 45}, + {5, 9, 11, 14, 31}, + {9, 33, 39, 46, 50}, + {3, 15, 29, 35, 47}, + {2, 7, 11, 20, 29}, + {6, 8, 25, 39, 47}, + {12, 22, 27, 31, 40}, + {6, 17, 32, 36, 46}, + {12, 18, 28, 41, 42}, + {6, 7, 8, 18, 47}, + {2, 3, 13, 42, 49}, + {2, 6, 15, 22, 49}, + {3, 5, 6, 42, 48}, + {1, 11, 21, 28, 41}, + {31, 32, 39, 44, 48}, + {3, 16, 35, 37, 39}, + {7, 19, 30, 32, 35}, + {15, 16, 32, 34, 41}, + {2, 13, 26, 40, 51}, + {10, 17, 39, 43, 50}, + {0, 16, 32, 42, 44}, + {0, 6, 26, 42, 50}, + {15, 28, 42, 43, 47}, + {8, 15, 41, 44, 48}, + {19, 24, 30, 32, 39}, + {8, 12, 16, 20, 29}, + {6, 13, 31, 39, 43}, + {16, 17, 19, 34, 45}, + {1, 5, 6, 27, 36}, + {1, 17, 20, 33, 34}, + {19, 21, 24, 32, 35}, + {0, 4, 24, 25, 30}, + {18, 19, 26, 41, 44}, + {29, 42, 45, 48, 50}, + {6, 10, 40, 45, 47}, + {1, 10, 25, 27, 35}, + {15, 23, 25, 29, 42}, + {2, 8, 10, 22, 47}, + {6, 8, 32, 42, 50}, + {4, 19, 32, 36, 50}, + {3, 5, 7, 15, 42}, + {3, 15, 42, 45, 48}, + {9, 18, 28, 29, 31}, + {13, 25, 26, 30, 47}, + {3, 4, 8, 19, 32}, + {15, 20, 25, 33, 35}, + {7, 9, 13, 28, 46}, + {21, 25, 33, 46, 49}, + {28, 33, 36, 44, 46}, + {18, 25, 36, 44, 50}, + {8, 24, 34, 40, 44}, + {1, 3, 19, 32, 33}, + {9, 20, 33, 36, 45}, + {3, 7, 23, 35, 46}, + {7, 9, 17, 20, 21}, + {0, 4, 18, 22, 30}, + {0, 29, 30, 35, 43}, + {15, 17, 22, 28, 51}, + {2, 33, 36, 46, 50}, + {16, 24, 26, 29, 43}, + {0, 8, 28, 34, 40}, + {6, 20, 26, 33, 51}, + {13, 21, 32, 39, 41}, + {2, 3, 27, 29, 43}, + {1, 3, 6, 27, 43}, + {9, 15, 19, 34, 41}, + {1, 7, 12, 13, 27}, + {2, 11, 21, 27, 40}, + {7, 14, 28, 43, 46}, + {24, 27, 29, 33, 46}, + {28, 29, 33, 40, 41}, + {7, 29, 33, 45, 51}, + {6, 8, 25, 31, 45}, + {15, 19, 28, 38, 40}, + {1, 6, 15, 24, 27}, + {4, 5, 19, 23, 31}, + {11, 14, 18, 20, 40}, + {5, 10, 11, 31, 34}, + {6, 17, 19, 28, 42}, + {10, 30, 31, 35, 43}, + {13, 14, 17, 39, 49}, + {4, 16, 17, 19, 20}, + {6, 13, 29, 37, 42}, + {14, 20, 27, 35, 38}, + {4, 26, 31, 34, 47}, + {0, 9, 17, 26, 27}, + {7, 16, 29, 32, 39}, + {2, 6, 15, 44, 50}, + {4, 7, 13, 16, 33}, + {3, 7, 15, 19, 32}, + {3, 11, 28, 32, 42}, + {13, 20, 33, 37, 38}, + {1, 3, 10, 15, 27}, + {2, 20, 26, 29, 46}, + {2, 7, 13, 27, 41}, + {3, 8, 13, 15, 29}, + {14, 25, 26, 40, 43}, + {13, 39, 46, 48, 51}, + {2, 10, 17, 30, 38}, + {1, 14, 24, 28, 33}, + {18, 25, 29, 39, 44}, + {1, 14, 39, 43, 47}, + {3, 13, 21, 39, 49}, + {20, 22, 36, 40, 46}, + {13, 14, 17, 26, 36}, + {22, 23, 24, 27, 40}, + {2, 6, 9, 32, 51}, + {2, 15, 16, 17, 32}, + {4, 5, 30, 41, 51}, + {7, 11, 23, 42, 46}, + {3, 7, 22, 25, 29}, + {1, 4, 7, 20, 22}, + {22, 30, 42, 43, 45}, + {3, 4, 29, 32, 48}, + {13, 28, 34, 41, 46}, + {0, 9, 13, 25, 42}, + {8, 10, 25, 40, 47}, + {6, 14, 28, 32, 47}, + {12, 13, 39, 42, 44}, + {1, 6, 19, 28, 48}, + {0, 14, 25, 26, 34}, + {28, 29, 42, 43, 47}, + {14, 19, 32, 36, 51}, + {18, 19, 26, 43, 44}, + {2, 15, 22, 33, 37}, + {1, 38, 40, 41, 49}, + {15, 19, 35, 41, 50}, + {1, 8, 26, 27, 31}, + {1, 7, 17, 18, 20}, + {10, 19, 20, 30, 32}, + {29, 42, 45, 48, 51}, + {1, 4, 30, 45, 51}, + {17, 28, 32, 41, 50}, + {20, 24, 29, 34, 47}, + {20, 25, 31, 37, 46}, + {5, 12, 24, 26, 31}, + {0, 17, 27, 32, 43}, + {4, 24, 30, 36, 46}, + {2, 17, 24, 27, 43}, + {2, 18, 23, 44, 51}, + {0, 6, 8, 12, 13}, + {5, 7, 32, 36, 46}, + {30, 34, 35, 43, 44}, + {1, 15, 18, 44, 49}, + {2, 6, 27, 42, 45}, + {6, 19, 30, 34, 39}, + {0, 9, 18, 41, 44}, + {8, 16, 35, 44, 47}, + {3, 14, 19, 31, 45}, + {13, 32, 38, 45, 50}, + {7, 12, 13, 36, 39}, + {17, 19, 30, 37, 49}, + {6, 20, 29, 37, 42}, + {5, 10, 18, 37, 46}, + {0, 2, 39, 43, 47}, + {6, 7, 19, 27, 48}, + {14, 20, 30, 33, 36}, + {12, 21, 26, 39, 50}, + {1, 2, 3, 27, 48}, + {8, 14, 18, 32, 44}, + {8, 32, 36, 41, 45}, + {2, 16, 29, 43, 48}, + {19, 26, 29, 31, 39}, + {13, 15, 16, 39, 51}, + {3, 39, 40, 42, 45}, + {7, 16, 19, 34, 45}, + {7, 8, 20, 40, 43}, + {21, 34, 44, 48, 49}, + {11, 27, 30, 31, 43}, + {1, 2, 4, 24, 41}, + {10, 12, 17, 18, 44}, + {21, 26, 37, 39, 44}, + {0, 8, 10, 21, 43}, + {2, 18, 19, 42, 44}, + {6, 9, 27, 38, 45}, + {13, 14, 31, 40, 50}, + {19, 27, 31, 45, 50}, + {7, 10, 15, 28, 39}, + {1, 19, 20, 31, 40}, + {1, 2, 14, 19, 37}, + {0, 5, 20, 28, 31}, + {4, 19, 32, 33, 37}, + {1, 17, 19, 32, 50}, + {29, 30, 41, 43, 46}, + {2, 5, 26, 39, 43}, + {7, 17, 41, 43, 49}, + {6, 17, 23, 43, 50}, + {0, 4, 16, 39, 49}, + {19, 21, 32, 38, 41}, + {2, 20, 25, 36, 41}, + {19, 36, 41, 45, 48}, + {1, 13, 31, 42, 44}, + {4, 17, 31, 36, 46}, + {6, 20, 31, 32, 37}, + {1, 4, 32, 40, 48}, + {3, 5, 14, 27, 30}, + {22, 32, 39, 43, 45}, + {3, 5, 13, 31, 45}, + {11, 29, 42, 44, 45}, + {8, 25, 34, 39, 48}, + {2, 17, 22, 28, 49}, + {1, 5, 26, 42, 44}, + {10, 13, 31, 39, 43}, + {7, 26, 35, 37, 39}, + {7, 16, 25, 27, 40}, + {2, 17, 36, 41, 44}, + {0, 26, 31, 33, 47}, + {6, 11, 29, 32, 43}, + {5, 12, 24, 43, 44}, + {7, 11, 31, 42, 46}, + {1, 14, 33, 45, 48}, + {2, 20, 23, 33, 38}, + {7, 9, 13, 20, 44}, + {0, 12, 27, 33, 40}, + {5, 7, 11, 43, 44}, + {8, 13, 34, 36, 46}, + {0, 14, 20, 21, 27}, + {5, 20, 27, 34, 40}, + {0, 2, 24, 39, 42}, + {1, 6, 12, 16, 45}, + {4, 21, 28, 39, 43}, + {10, 20, 21, 33, 40}, + {5, 14, 19, 27, 33}, + {0, 12, 21, 34, 41}, + {6, 8, 36, 41, 47}, + {0, 13, 14, 45, 50}, + {19, 24, 30, 41, 43}, + {5, 6, 7, 10, 33}, + {3, 16, 27, 31, 47}, + {0, 9, 13, 37, 42}, + {1, 5, 18, 43, 48}, + {7, 20, 28, 38, 45}, + {15, 19, 36, 43, 45}, + {14, 21, 27, 38, 46}, + {15, 29, 38, 42, 45}, + {0, 6, 11, 13, 21}, + {14, 28, 37, 39, 40}, + {0, 5, 18, 40, 49}, + {2, 18, 21, 23, 44}, + {11, 15, 21, 29, 34}, + {3, 16, 19, 39, 43}, + {6, 7, 13, 20, 24}, + {26, 27, 29, 39, 47}, + {29, 30, 41, 43, 49}, + {3, 16, 18, 37, 49}, + {4, 18, 20, 31, 40}, + {1, 14, 25, 41, 43}, + {0, 2, 3, 4, 41}, + {1, 22, 28, 41, 47}, + {4, 19, 22, 39, 43}, + {12, 13, 15, 31, 41}, + {15, 29, 41, 43, 46}, + {10, 31, 44, 47, 50}, + {14, 27, 28, 29, 35}, + {8, 32, 34, 39, 41}, + {11, 17, 42, 43, 49}, + {6, 8, 13, 18, 32}, + {1, 6, 21, 45, 46}, + {2, 17, 19, 22, 43}, + {0, 1, 3, 40, 47}, + {10, 20, 38, 44, 46}, + {5, 7, 18, 27, 39}, + {2, 16, 20, 28, 35}, + {2, 9, 11, 15, 43}, + {7, 29, 42, 49, 51}, + {3, 11, 21, 35, 42}, + {3, 13, 14, 29, 45}, + {5, 10, 16, 31, 37}, + {8, 15, 21, 31, 42}, + {6, 10, 15, 22, 45}, + {4, 39, 43, 44, 47}, + {1, 5, 12, 29, 42}, + {12, 17, 21, 24, 34}, + {0, 2, 27, 39, 43}, + {3, 10, 18, 42, 50}, + {18, 42, 44, 49, 50}, + {3, 4, 31, 36, 42}, + {8, 18, 19, 28, 44}, + {15, 18, 32, 41, 43}, + {12, 17, 21, 34, 39}, + {12, 14, 15, 17, 41}, + {6, 17, 24, 30, 49}, + {17, 18, 29, 32, 42}, + {12, 33, 42, 46, 48}, + {1, 21, 27, 35, 41}, + {8, 11, 21, 32, 36}, + {13, 15, 26, 37, 43}, + {4, 12, 14, 21, 30}, + {3, 21, 34, 38, 45}, + {11, 13, 18, 25, 31}, + {0, 3, 21, 30, 42}, + {1, 14, 15, 32, 43}, + {7, 22, 30, 40, 43}, + {2, 11, 16, 33, 46}, + {1, 17, 27, 42, 46}, + {0, 9, 36, 39, 50}, + {2, 3, 15, 34, 48}, + {13, 37, 39, 40, 48}, + {10, 11, 22, 34, 47}, + {1, 10, 12, 14, 37}, + {3, 14, 29, 45, 49}, + {5, 16, 20, 31, 34}, + {4, 15, 23, 41, 50}, + {6, 11, 39, 45, 48}, + {0, 2, 14, 41, 43}, + {7, 11, 30, 31, 33}, + {4, 6, 8, 32, 33}, + {13, 23, 31, 39, 41}, + {0, 13, 24, 31, 35}, + {6, 16, 20, 28, 33}, + {19, 31, 32, 43, 50}, + {7, 10, 26, 27, 33}, + {0, 3, 26, 27, 41}, + {4, 17, 29, 45, 50}, + {3, 19, 30, 36, 45}, + {1, 16, 25, 39, 42}, + {14, 18, 27, 45, 50}, + {18, 21, 45, 46, 47}, + {1, 4, 26, 27, 28}, + {4, 28, 33, 41, 49}, + {14, 36, 38, 40, 48}, + {0, 14, 27, 33, 41}, + {13, 15, 24, 32, 41}, + {15, 19, 29, 41, 50}, + {19, 33, 45, 49, 50}, + {4, 43, 49, 50, 51}, + {5, 8, 17, 21, 23}, + {0, 1, 7, 14, 19}, + {4, 27, 28, 29, 43}, + {2, 24, 31, 41, 47}, + {14, 15, 16, 28, 45}, + {26, 28, 30, 41, 51}, + {14, 16, 39, 42, 43}, + {14, 16, 18, 25, 42}, + {14, 19, 32, 33, 44}, + {3, 5, 16, 17, 32}, + {5, 10, 12, 31, 43}, + {3, 8, 25, 30, 34}, + {13, 26, 28, 38, 43}, + {5, 23, 31, 35, 38}, + {1, 12, 40, 42, 45}, + {3, 30, 39, 42, 46}, + {0, 26, 31, 42, 48}, + {0, 29, 33, 41, 46}, + {15, 18, 41, 43, 47}, + {0, 24, 29, 31, 39}, + {7, 31, 34, 46, 51}, + {4, 11, 30, 31, 36}, + {13, 19, 25, 32, 42}, + {0, 13, 22, 23, 29}, + {1, 3, 26, 40, 51}, + {0, 18, 22, 29, 39}, + {3, 10, 29, 39, 45}, + {27, 33, 35, 42, 46}, + {4, 8, 27, 30, 39}, + {2, 6, 15, 26, 40}, + {6, 7, 14, 19, 34}, + {5, 14, 28, 40, 46}, + {18, 26, 29, 31, 43}, + {13, 19, 24, 31, 44}, + {14, 18, 24, 29, 31}, + {25, 26, 32, 35, 45}, + {0, 6, 41, 45, 50}, + {5, 28, 38, 39, 41}, + {17, 30, 32, 35, 49}, + {7, 19, 21, 33, 37}, + {8, 21, 22, 40, 50}, + {12, 18, 30, 40, 43}, + {7, 11, 14, 30, 33}, + {18, 29, 33, 38, 46}, + {6, 28, 37, 45, 51}, + {15, 17, 30, 36, 40}, + {1, 14, 16, 39, 43}, + {8, 13, 14, 21, 49}, + {9, 20, 26, 33, 38}, + {21, 32, 34, 40, 48}, + {25, 30, 32, 36, 45}, + {6, 13, 19, 35, 38}, + {9, 18, 30, 31, 47}, + {3, 8, 24, 29, 40}, + {1, 23, 33, 46, 47}, + {5, 26, 33, 44, 47}, + {3, 17, 24, 30, 48}, + {10, 13, 21, 40, 47}, + {0, 7, 12, 13, 50}, + {7, 24, 32, 39, 45}, + {5, 6, 7, 32, 42}, + {9, 16, 23, 42, 47}, + {4, 5, 26, 31, 48}, + {4, 17, 21, 24, 27}, + {7, 13, 18, 22, 44}, + {14, 19, 27, 35, 39}, + {6, 13, 30, 32, 50}, + {2, 16, 19, 28, 44}, + {3, 18, 22, 26, 44}, + {3, 20, 32, 37, 46}, + {17, 19, 22, 44, 45}, + {4, 24, 43, 48, 49}, + {3, 8, 16, 32, 51}, + {7, 13, 22, 33, 44}, + {3, 29, 40, 41, 49}, + {18, 22, 40, 44, 46}, + {11, 32, 33, 45, 49}, + {7, 12, 37, 46, 47}, + {5, 9, 39, 44, 49}, + {15, 17, 19, 32, 44}, + {6, 17, 26, 35, 45}, + {13, 26, 37, 47, 48}, + {20, 28, 37, 38, 46}, + {16, 17, 18, 29, 37}, + {8, 9, 21, 25, 50}, + {5, 7, 17, 43, 49}, + {3, 12, 35, 40, 42}, + {0, 31, 34, 39, 50}, + {0, 10, 28, 39, 48}, + {4, 17, 26, 38, 45}, + {14, 23, 29, 42, 43}, + {15, 18, 20, 22, 28}, + {10, 34, 35, 43, 47}, + {28, 32, 34, 35, 47}, + {13, 26, 34, 41, 46}, + {26, 39, 44, 48, 51}, + {2, 6, 19, 34, 50}, + {1, 20, 31, 40, 41}, + {13, 31, 44, 46, 48}, + {18, 27, 34, 40, 42}, + {3, 13, 29, 31, 36}, + {18, 28, 31, 45, 50}, + {13, 28, 35, 38, 39}, + {0, 5, 8, 18, 35}, + {5, 28, 41, 48, 49}, + {7, 9, 14, 18, 20}, + {6, 17, 18, 36, 43}, + {19, 32, 34, 42, 46}, + {11, 32, 34, 45, 51}, + {20, 38, 41, 46, 47}, + {5, 6, 33, 40, 44}, + {2, 13, 18, 30, 44}, + {6, 43, 44, 45, 48}, + {0, 7, 25, 26, 28}, + {32, 33, 41, 45, 47}, + {23, 27, 40, 41, 48}, + {14, 17, 34, 45, 47}, + {14, 25, 30, 39, 40}, + {14, 27, 38, 39, 43}, + {0, 1, 31, 39, 50}, + {1, 4, 6, 13, 32}, + {23, 27, 33, 40, 42}, + {0, 14, 35, 40, 50}, + {9, 26, 30, 43, 49}, + {9, 14, 25, 40, 46}, + {13, 26, 40, 41, 51}, + {9, 18, 19, 21, 44}, + {8, 14, 28, 40, 44}, + {5, 19, 26, 32, 38}, + {2, 5, 15, 30, 34}, + {15, 26, 31, 44, 47}, + {0, 16, 33, 42, 45}, + {7, 16, 26, 29, 37}, + {1, 17, 40, 47, 51}, + {23, 26, 30, 33, 43}, + {14, 16, 38, 39, 40}, + {3, 28, 29, 43, 44}, + {16, 29, 30, 41, 47}, + {21, 24, 31, 34, 42}, + {21, 29, 40, 42, 44}, + {0, 2, 23, 41, 43}, + {2, 5, 6, 19, 39}, + {17, 18, 28, 30, 46}, + {9, 17, 26, 29, 30}, + {7, 17, 25, 33, 47}, + {12, 15, 24, 27, 40}, + {3, 4, 11, 27, 29}, + {2, 12, 29, 42, 43}, + {0, 4, 15, 41, 47}, + {0, 26, 27, 31, 42}, + {11, 13, 14, 27, 43}, + {16, 21, 23, 35, 47}, + {7, 8, 25, 44, 47}, + {32, 45, 46, 47, 49}, + {0, 22, 39, 43, 46}, + {9, 30, 31, 43, 46}, + {5, 21, 22, 28, 44}, + {1, 14, 17, 42, 51}, + {29, 30, 42, 44, 49}, + {5, 12, 16, 19, 29}, + {6, 9, 25, 32, 47}, + {1, 7, 20, 34, 51}, + {12, 30, 39, 43, 49}, + {3, 20, 28, 42, 51}, + {1, 13, 14, 31, 32}, + {2, 28, 29, 30, 34}, + {2, 7, 14, 15, 38}, + {6, 20, 23, 39, 45}, + {7, 28, 36, 46, 47}, + {10, 34, 43, 45, 47}, + {26, 27, 30, 39, 45}, + {1, 13, 17, 33, 43}, + {0, 10, 38, 39, 46}, + {2, 13, 39, 40, 42}, + {17, 30, 33, 38, 50}, + {3, 8, 9, 16, 39}, + {4, 7, 11, 16, 46}, + {0, 11, 26, 28, 35}, + {0, 1, 12, 14, 48}, + {10, 14, 26, 39, 44}, + {10, 12, 17, 30, 45}, + {0, 14, 40, 42, 47}, + {2, 19, 23, 28, 42}, + {2, 28, 36, 48, 51}, + {1, 2, 28, 43, 48}, + {3, 8, 20, 27, 29}, + {1, 8, 15, 19, 27}, + {2, 3, 6, 29, 37}, + {9, 28, 33, 41, 44}, + {5, 18, 29, 39, 47}, + {4, 21, 33, 44, 46}, + {0, 7, 11, 25, 26}, + {0, 29, 30, 31, 42}, + {4, 8, 26, 33, 39}, + {7, 29, 30, 33, 47}, + {0, 7, 13, 19, 47}, + {2, 28, 37, 39, 46}, + {7, 16, 29, 32, 41}, + {2, 3, 8, 29, 49}, + {3, 4, 25, 43, 46}, + {0, 2, 4, 20, 46}, + {3, 4, 12, 18, 44}, + {0, 5, 19, 26, 50}, + {12, 18, 26, 34, 39}, + {3, 7, 30, 32, 42}, + {7, 20, 26, 29, 31}, + {21, 30, 37, 47, 51}, + {17, 23, 34, 42, 43}, + {12, 14, 27, 29, 43}, + {1, 10, 14, 30, 31}, + {20, 25, 28, 40, 41}, + {18, 31, 46, 49, 51}, + {9, 10, 17, 30, 50}, + {17, 22, 29, 40, 42}, + {11, 20, 23, 31, 33}, + {17, 35, 43, 46, 50}, + {4, 8, 30, 31, 51}, + {20, 21, 24, 32, 34}, + {0, 4, 10, 16, 17}, + {1, 17, 33, 39, 43}, + {18, 28, 36, 43, 44}, + {13, 20, 38, 39, 49}, + {1, 3, 4, 17, 35}, + {7, 11, 18, 46, 51}, + {6, 20, 30, 36, 43}, + {3, 11, 31, 35, 44}, + {3, 16, 21, 43, 44}, + {3, 20, 33, 47, 51}, + {5, 8, 21, 39, 51}, + {1, 24, 33, 44, 46}, + {0, 12, 16, 21, 42}, + {1, 25, 27, 28, 36}, + {5, 6, 8, 44, 50}, + {5, 15, 41, 48, 50}, + {19, 22, 43, 45, 46}, + {9, 26, 33, 39, 47}, + {13, 25, 34, 39, 41}, + {7, 18, 30, 39, 46}, + {0, 1, 30, 39, 49}, + {5, 6, 9, 24, 45}, + {16, 20, 39, 41, 46}, + {7, 18, 27, 31, 51}, + {9, 16, 29, 34, 37}, + {0, 18, 31, 36, 38}, + {0, 5, 17, 28, 30}, + {2, 3, 11, 15, 49}, + {5, 13, 18, 32, 41}, + {0, 18, 31, 37, 51}, + {0, 8, 23, 34, 50}, + {16, 19, 29, 44, 49}, + {15, 25, 29, 36, 41}, + {19, 30, 31, 32, 33}, + {21, 24, 26, 27, 40}, + {14, 20, 21, 46, 49}, + {6, 11, 13, 18, 39}, + {1, 2, 10, 18, 40}, + {0, 10, 15, 39, 50}, + {0, 7, 10, 21, 26}, + {6, 26, 36, 39, 42}, + {11, 17, 21, 35, 47}, + {13, 16, 27, 38, 39}, + {4, 7, 27, 40, 50}, + {33, 34, 36, 45, 47}, + {1, 13, 31, 36, 40}, + {28, 31, 32, 41, 43}, + {9, 20, 33, 41, 43}, + {2, 18, 26, 31, 46}, + {11, 19, 20, 33, 48}, + {12, 18, 37, 41, 44}, + {0, 13, 14, 33, 38}, + {15, 19, 34, 40, 41}, + {7, 14, 19, 20, 47}, + {8, 36, 38, 45, 47}, + {2, 3, 10, 16, 34}, + {2, 23, 26, 35, 39}, + {2, 30, 31, 36, 44}, + {8, 19, 21, 25, 43}, + {8, 28, 30, 41, 46}, + {9, 18, 24, 25, 31}, + {5, 18, 25, 29, 30}, + {15, 20, 41, 44, 45}, + {2, 19, 21, 34, 39}, + {13, 25, 28, 41, 46}, + {0, 5, 14, 17, 31}, + {2, 4, 18, 28, 35}, + {0, 3, 12, 23, 29}, + {11, 17, 20, 29, 46}, + {2, 4, 9, 39, 41}, + {1, 25, 27, 31, 47}, + {1, 10, 25, 33, 40}, + {2, 15, 20, 27, 36}, + {14, 25, 26, 27, 37}, + {20, 21, 39, 43, 47}, + {15, 28, 37, 44, 47}, + {0, 27, 32, 38, 39}, + {3, 14, 29, 47, 48}, + {3, 19, 23, 29, 38}, + {1, 13, 26, 37, 46}, + {3, 7, 12, 33, 39}, + {4, 21, 22, 34, 51}, + {12, 29, 39, 42, 49}, + {4, 17, 21, 41, 42}, + {5, 35, 40, 44, 45}, + {4, 23, 26, 39, 46}, + {4, 14, 15, 17, 42}, + {1, 10, 13, 33, 40}, + {5, 10, 13, 17, 39}, + {15, 16, 23, 27, 28}, + {5, 10, 18, 21, 24}, + {15, 16, 20, 39, 41}, + {16, 31, 40, 42, 45}, + {2, 10, 21, 31, 44}, + {2, 7, 20, 35, 51}, + {6, 13, 27, 45, 46}, + {1, 2, 5, 10, 40}, + {1, 14, 20, 44, 47}, + {7, 8, 19, 40, 45}, + {6, 17, 36, 43, 48}, + {3, 11, 16, 46, 51}, + {15, 16, 28, 31, 38}, + {15, 30, 40, 43, 48}, + {3, 8, 10, 16, 17}, + {6, 17, 22, 32, 37}, + {16, 17, 19, 29, 51}, + {14, 19, 39, 40, 48}, + {2, 4, 5, 18, 42}, + {4, 21, 36, 38, 47}, + {4, 27, 30, 32, 42}, + {16, 32, 38, 45, 47}, + {4, 10, 21, 26, 47}, + {15, 41, 44, 46, 48}, + {0, 16, 30, 39, 44}, + {4, 16, 29, 33, 37}, + {12, 19, 24, 27, 45}, + {5, 16, 42, 47, 50}, + {2, 3, 15, 21, 30}, + {4, 19, 27, 33, 46}, + {3, 5, 8, 34, 35}, + {13, 26, 27, 37, 41}, + {6, 11, 16, 32, 33}, + {20, 21, 38, 45, 46}, + {5, 32, 36, 45, 48}, + {0, 25, 28, 41, 46}, + {1, 24, 27, 41, 42}, + {5, 12, 15, 33, 46}, + {8, 22, 29, 31, 47}, + {8, 14, 22, 40, 42}, + {6, 9, 16, 33, 42}, + {0, 11, 30, 39, 41}, + {28, 33, 35, 43, 46}, + {4, 7, 15, 46, 50}, + {0, 3, 4, 6, 43}, + {6, 7, 21, 40, 45}, + {3, 8, 18, 44, 51}, + {7, 18, 30, 40, 43}, + {0, 6, 10, 16, 39}, + {24, 33, 39, 46, 48}, + {5, 12, 16, 39, 42}, + {0, 2, 8, 26, 32}, + {16, 17, 30, 33, 36}, + {3, 15, 23, 24, 41}, + {13, 29, 31, 35, 39}, + {18, 25, 29, 30, 42}, + {6, 19, 37, 42, 48}, + {8, 19, 20, 46, 50}, + {7, 15, 28, 35, 45}, + {5, 18, 20, 23, 48}, + {0, 1, 6, 12, 40}, + {18, 22, 23, 31, 33}, + {15, 19, 36, 37, 45}, + {6, 7, 26, 39, 41}, + {1, 8, 25, 27, 35}, + {0, 10, 19, 45, 47}, + {8, 10, 16, 34, 41}, + {0, 6, 13, 28, 33}, + {21, 27, 41, 45, 47}, + {10, 14, 21, 27, 39}, + {9, 23, 32, 45, 47}, + {8, 23, 25, 44, 47}, + {8, 15, 18, 23, 34}, + {3, 7, 28, 41, 50}, + {3, 7, 26, 33, 37}, + {5, 14, 17, 27, 37}, + {1, 2, 39, 40, 46}, + {13, 14, 17, 34, 40}, + {2, 4, 12, 17, 48}, + {16, 24, 29, 33, 39}, + {1, 33, 34, 40, 45}, + {0, 13, 14, 28, 47}, + {14, 18, 31, 43, 49}, + {14, 20, 38, 40, 44}, + {5, 11, 21, 40, 44}, + {6, 21, 32, 35, 43}, + {1, 4, 13, 24, 30}, + {2, 18, 30, 41, 50}, + {24, 33, 41, 46, 48}, + {1, 5, 14, 47, 48}, + {6, 9, 11, 18, 19}, + {7, 11, 13, 18, 39}, + {0, 1, 10, 27, 30}, + {2, 20, 23, 24, 33}, + {4, 19, 23, 37, 45}, + {2, 12, 19, 28, 49}, + {19, 32, 33, 34, 37}, + {1, 5, 6, 25, 40}, + {4, 15, 16, 42, 48}, + {15, 21, 24, 28, 42}, + {4, 7, 21, 25, 34}, + {19, 21, 34, 36, 37}, + {4, 8, 10, 21, 46}, + {2, 23, 33, 37, 46}, + {6, 25, 29, 45, 49}, + {4, 15, 34, 43, 46}, + {4, 32, 34, 43, 51}, + {8, 15, 19, 24, 41}, + {19, 21, 33, 47, 49}, + {8, 13, 19, 26, 42}, + {3, 18, 22, 31, 47}, + {0, 1, 2, 13, 33}, + {7, 12, 14, 45, 46}, + {8, 33, 35, 39, 46}, + {5, 10, 31, 41, 45}, + {7, 10, 20, 35, 44}, + {0, 5, 18, 35, 43}, + {16, 22, 31, 34, 42}, + {5, 31, 33, 39, 41}, + {16, 18, 29, 37, 46}, + {6, 23, 30, 32, 38}, + {3, 4, 28, 42, 46}, + {5, 6, 32, 33, 50}, + {4, 12, 16, 17, 47}, + {5, 14, 27, 32, 42}, + {2, 8, 12, 20, 47}, + {4, 8, 15, 22, 41}, + {2, 4, 6, 24, 32}, + {9, 17, 18, 25, 30}, + {1, 18, 24, 27, 29}, + {7, 11, 29, 39, 46}, + {7, 28, 29, 35, 41}, + {13, 29, 33, 34, 46}, + {13, 15, 18, 39, 45}, + {1, 3, 29, 30, 45}, + {3, 5, 9, 18, 23}, + {3, 6, 22, 32, 41}, + {4, 6, 16, 19, 21}, + {13, 21, 29, 38, 42}, + {7, 18, 25, 33, 36}, + {14, 25, 34, 40, 41}, + {5, 20, 21, 29, 34}, + {17, 25, 27, 42, 43}, + {4, 8, 20, 29, 47}, + {10, 15, 28, 47, 48}, + {28, 34, 35, 41, 46}, + {6, 16, 32, 39, 40}, + {3, 7, 16, 21, 45}, + {4, 6, 9, 30, 37}, + {2, 14, 27, 35, 39}, + {21, 26, 27, 40, 42}, + {2, 3, 17, 37, 41}, + {2, 17, 24, 41, 47}, + {7, 26, 34, 37, 39}, + {6, 11, 21, 43, 45}, + {2, 6, 7, 19, 31}, + {18, 31, 32, 37, 48}, + {8, 34, 40, 43, 50}, + {6, 32, 34, 40, 42}, + {3, 12, 35, 42, 46}, + {13, 29, 32, 45, 46}, + {0, 18, 35, 44, 49}, + {2, 20, 31, 39, 44}, + {1, 8, 21, 26, 29}, + {4, 6, 26, 32, 40}, + {12, 14, 15, 19, 40}, + {10, 19, 32, 44, 50}, + {8, 20, 21, 26, 44}, + {8, 17, 21, 29, 37}, + {7, 16, 18, 33, 48}, + {23, 33, 41, 46, 51}, + {0, 6, 9, 26, 49}, + {1, 3, 34, 42, 43}, + {7, 8, 14, 26, 27}, + {1, 10, 24, 40, 45}, + {1, 14, 20, 21, 36}, + {26, 27, 39, 41, 49}, + {19, 28, 32, 35, 43}, + {17, 19, 22, 26, 30}, + {2, 5, 15, 26, 49}, + {3, 7, 20, 25, 37}, + {0, 6, 12, 26, 43}, + {6, 15, 24, 45, 47}, + {7, 15, 44, 45, 46}, + {1, 16, 40, 45, 51}, + {0, 1, 18, 36, 44}, + {5, 18, 25, 46, 47}, + {7, 31, 33, 34, 42}, + {1, 29, 35, 42, 51}, + {2, 5, 21, 23, 28}, + {9, 21, 30, 34, 49}, + {18, 24, 28, 41, 42}, + {3, 10, 16, 30, 48}, + {6, 7, 19, 35, 50}, + {2, 28, 33, 45, 49}, + {3, 13, 19, 32, 34}, + {2, 7, 8, 27, 47}, + {2, 4, 8, 16, 41}, + {19, 23, 39, 45, 46}, + {15, 21, 28, 45, 49}, + {2, 4, 17, 27, 32}, + {3, 11, 14, 21, 47}, + {12, 27, 40, 44, 45}, + {2, 5, 31, 33, 39}, + {4, 24, 25, 27, 43}, + {6, 19, 20, 23, 28}, + {8, 13, 22, 23, 47}, + {18, 21, 38, 44, 46}, + {5, 11, 32, 45, 47}, + {17, 20, 28, 30, 45}, + {1, 2, 28, 39, 45}, + {2, 16, 18, 22, 29}, + {0, 3, 9, 31, 39}, + {4, 16, 26, 39, 47}, + {4, 9, 21, 31, 43}, + {3, 8, 16, 33, 43}, + {7, 13, 21, 29, 34}, + {0, 4, 8, 15, 26}, + {4, 25, 28, 33, 41}, + {19, 34, 45, 46, 51}, + {5, 7, 11, 27, 31}, + {28, 29, 32, 40, 45}, + {5, 13, 31, 35, 45}, + {13, 31, 37, 38, 44}, + {16, 18, 21, 27, 31}, + {6, 27, 32, 35, 38}, + {4, 7, 19, 30, 39}, + {0, 7, 8, 17, 30}, + {1, 12, 14, 17, 18}, + {1, 2, 27, 38, 48}, + {1, 23, 32, 40, 41}, + {8, 19, 28, 30, 45}, + {7, 14, 18, 20, 29}, + {14, 40, 46, 50, 51}, + {19, 25, 29, 40, 45}, + {10, 17, 39, 42, 43}, + {1, 12, 15, 26, 41}, + {0, 5, 9, 13, 37}, + {6, 12, 18, 24, 31}, + {4, 5, 9, 31, 33}, + {4, 17, 26, 37, 46}, + {11, 14, 19, 25, 45}, + {3, 14, 16, 24, 30}, + {2, 19, 25, 41, 50}, + {8, 24, 26, 34, 43}, + {1, 4, 8, 30, 41}, + {12, 26, 32, 45, 49}, + {4, 17, 33, 47, 48}, + {1, 3, 26, 39, 50}, + {12, 17, 28, 42, 43}, + {17, 32, 37, 45, 47}, + {5, 8, 35, 39, 47}, + {10, 27, 31, 33, 40}, + {1, 7, 8, 27, 49}, + {1, 3, 14, 18, 24}, + {7, 19, 20, 38, 48}, + {2, 3, 11, 29, 38}, + {5, 18, 32, 38, 46}, + {0, 14, 24, 39, 43}, + {1, 9, 14, 29, 34}, + {1, 10, 20, 27, 42}, + {6, 10, 19, 33, 50}, + {8, 25, 29, 34, 49}, + {17, 20, 25, 29, 43}, + {6, 30, 33, 37, 45}, + {14, 16, 37, 40, 45}, + {0, 4, 20, 22, 39}, + {5, 10, 31, 40, 42}, + {5, 13, 16, 21, 42}, + {5, 18, 21, 26, 41}, + {2, 13, 26, 32, 42}, + {11, 19, 27, 29, 40}, + {6, 17, 33, 40, 46}, + {0, 8, 21, 24, 32}, + {1, 3, 33, 39, 40}, + {2, 12, 15, 32, 39}, + {2, 11, 16, 34, 47}, + {0, 18, 39, 42, 51}, + {27, 33, 37, 41, 46}, + {1, 2, 28, 34, 44}, + {2, 15, 23, 34, 50}, + {0, 5, 27, 39, 45}, + {13, 19, 31, 32, 48}, + {0, 18, 26, 41, 46}, + {10, 14, 15, 21, 47}, + {6, 13, 19, 41, 48}, + {1, 26, 39, 43, 48}, + {5, 27, 28, 29, 41}, + {23, 24, 34, 39, 47}, + {7, 19, 27, 33, 36}, + {16, 28, 29, 44, 48}, + {16, 29, 34, 46, 48}, + {11, 31, 35, 39, 44}, + {15, 18, 20, 30, 33}, + {3, 4, 8, 16, 46}, + {3, 13, 34, 47, 51}, + {7, 17, 27, 30, 45}, + {2, 29, 41, 46, 50}, + {0, 6, 8, 19, 23}, + {14, 15, 20, 26, 33}, + {1, 17, 24, 40, 47}, + {12, 26, 32, 35, 39}, + {8, 13, 29, 42, 49}, + {3, 9, 30, 43, 45}, + {2, 14, 15, 34, 51}, + {3, 12, 27, 32, 40}, + {8, 12, 13, 17, 43}, + {14, 23, 31, 40, 43}, + {0, 3, 17, 43, 45}, + {12, 15, 22, 28, 31}, + {5, 26, 31, 35, 36}, + {15, 23, 41, 42, 44}, + {2, 10, 15, 24, 33}, + {4, 13, 19, 39, 48}, + {8, 26, 46, 47, 49}, + {20, 27, 33, 41, 51}, + {0, 1, 2, 11, 26}, + {2, 11, 17, 43, 45}, + {7, 17, 30, 36, 48}, + {13, 23, 26, 42, 44}, + {0, 6, 28, 39, 40}, + {5, 7, 15, 31, 39}, + {1, 2, 17, 43, 51}, + {4, 16, 26, 40, 42}, + {3, 13, 28, 41, 49}, + {8, 33, 34, 37, 39}, + {1, 11, 27, 39, 47}, + {1, 10, 15, 16, 42}, + {15, 21, 24, 40, 41}, + {26, 31, 39, 42, 43}, + {22, 33, 34, 39, 47}, + {1, 13, 26, 35, 43}, + {17, 21, 26, 34, 50}, + {15, 31, 40, 44, 51}, + {12, 21, 34, 40, 49}, + {8, 19, 32, 41, 46}, + {0, 2, 6, 8, 34}, + {6, 15, 18, 19, 21}, + {2, 3, 9, 31, 44}, + {1, 19, 29, 32, 36}, + {11, 27, 39, 40, 48}, + {8, 25, 33, 34, 36}, + {1, 3, 8, 27, 43}, + {8, 18, 29, 38, 44}, + {2, 9, 17, 18, 30}, + {5, 14, 18, 36, 42}, + {4, 20, 27, 31, 43}, + {3, 23, 32, 42, 44}, + {1, 20, 24, 34, 46}, + {5, 7, 25, 29, 31}, + {3, 4, 17, 33, 35}, + {16, 21, 42, 49, 51}, + {2, 35, 41, 47, 50}, + {1, 5, 11, 18, 42}, + {6, 9, 25, 32, 39}, + {8, 17, 22, 44, 47}, + {3, 9, 37, 42, 43}, + {4, 18, 22, 42, 43}, + {5, 17, 18, 22, 29}, + {2, 8, 15, 36, 42}, + {2, 4, 14, 16, 43}, + {3, 21, 28, 32, 45}, + {6, 19, 21, 22, 26}, + {2, 26, 29, 42, 50}, + {6, 20, 28, 46, 51}, + {19, 32, 48, 50, 51}, + {1, 5, 8, 12, 40}, + {4, 30, 40, 45, 49}, + {10, 13, 20, 29, 33}, + {5, 7, 15, 43, 46}, + {5, 14, 25, 27, 39}, + {8, 14, 23, 32, 47}, + {1, 15, 20, 33, 48}, + {0, 4, 6, 8, 17}, + {8, 15, 17, 21, 32}, + {24, 26, 39, 40, 44}, + {3, 7, 13, 26, 30}, + {12, 13, 31, 37, 44}, + {10, 24, 30, 43, 44}, + {1, 6, 25, 27, 41}, + {7, 8, 17, 23, 33}, + {4, 6, 17, 23, 35}, + {1, 4, 15, 16, 29}, + {14, 19, 23, 27, 47}, + {17, 19, 28, 41, 42}, + {4, 16, 19, 32, 39}, + {3, 4, 14, 40, 46}, + {4, 11, 17, 26, 42}, + {2, 17, 30, 40, 45}, + {1, 14, 18, 23, 32}, + {12, 28, 35, 39, 41}, + {11, 20, 29, 35, 42}, + {4, 13, 15, 25, 26}, + {13, 21, 34, 36, 42}, + {4, 14, 27, 33, 47}, + {6, 22, 32, 41, 44}, + {12, 20, 29, 46, 47}, + {5, 6, 15, 18, 26}, + {6, 13, 33, 35, 46}, + {14, 27, 28, 34, 48}, + {4, 21, 23, 32, 47}, + {0, 2, 38, 41, 45}, + {6, 19, 25, 26, 33}, + {5, 9, 18, 23, 25}, + {14, 27, 31, 46, 50}, + {1, 9, 10, 26, 39}, + {1, 9, 26, 32, 40}, + {1, 21, 32, 34, 38}, + {4, 5, 26, 40, 44}, + {5, 26, 34, 39, 41}, + {6, 24, 30, 33, 45}, + {14, 16, 28, 32, 42}, + {4, 30, 42, 47, 50}, + {22, 28, 41, 44, 46}, + {6, 22, 26, 32, 33}, + {0, 6, 10, 18, 45}, + {4, 14, 31, 37, 44}, + {6, 8, 10, 24, 47}, + {3, 15, 28, 40, 49}, + {2, 8, 19, 32, 43}, + {3, 11, 23, 42, 44}, + {1, 9, 13, 17, 43}, + {5, 6, 23, 32, 50}, + {8, 21, 43, 44, 51}, + {21, 22, 32, 34, 42}, + {1, 28, 41, 44, 46}, + {0, 16, 30, 33, 43}, + {3, 33, 46, 48, 50}, + {2, 15, 19, 33, 44}, + {0, 3, 19, 26, 41}, + {2, 6, 23, 28, 29}, + {18, 28, 29, 31, 43}, + {3, 9, 31, 40, 42}, + {0, 17, 19, 30, 34}, + {1, 21, 35, 47, 50}, + {3, 6, 27, 32, 35}, + {3, 13, 35, 42, 50}, + {9, 20, 23, 28, 41}, + {7, 12, 16, 34, 46}, + {14, 20, 24, 33, 44}, + {3, 13, 32, 38, 39}, + {0, 10, 32, 45, 48}, + {5, 18, 22, 32, 47}, + {5, 13, 17, 20, 44}, + {1, 6, 14, 26, 31}, + {1, 23, 26, 27, 38}, + {8, 11, 16, 21, 38}, + {8, 12, 19, 32, 40}, + {5, 12, 26, 29, 39}, + {4, 9, 28, 37, 41}, + {0, 14, 23, 38, 40}, + {12, 33, 36, 46, 48}, + {4, 15, 29, 30, 34}, + {4, 14, 29, 40, 48}, + {17, 26, 31, 35, 44}, + {3, 12, 29, 33, 48}, + {4, 17, 23, 44, 48}, + {11, 12, 14, 27, 31}, + {1, 11, 12, 30, 43}, + {13, 26, 27, 28, 38}, + {5, 8, 13, 31, 41}, + {0, 5, 13, 20, 21}, + {10, 12, 15, 19, 32}, + {12, 19, 32, 34, 48}, + {22, 28, 31, 32, 44}, + {8, 14, 31, 35, 44}, + {6, 19, 28, 35, 36}, + {2, 10, 27, 40, 51}, + {2, 6, 7, 33, 49}, + {5, 6, 16, 33, 46}, + {3, 6, 14, 30, 42}, + {1, 2, 6, 19, 26}, + {4, 17, 22, 31, 46}, + {14, 32, 45, 46, 50}, + {14, 16, 39, 40, 51}, + {7, 14, 27, 35, 42}, + {3, 12, 15, 29, 47}, + {14, 17, 36, 40, 45}, + {1, 19, 37, 38, 45}, + {0, 1, 8, 9, 27}, + {2, 15, 38, 40, 46}, + {3, 33, 35, 42, 51}, + {1, 9, 27, 32, 51}, + {8, 26, 39, 40, 44}, + {0, 3, 28, 34, 47}, + {2, 14, 32, 45, 50}, + {10, 14, 20, 32, 45}, + {2, 3, 21, 26, 42}, + {0, 22, 26, 32, 33}, + {0, 7, 19, 26, 27}, + {0, 1, 4, 8, 39}, + {11, 21, 32, 38, 45}, + {0, 30, 33, 37, 43}, + {16, 21, 32, 37, 42}, + {4, 9, 17, 24, 40}, + {2, 32, 43, 45, 49}, + {4, 32, 45, 50, 51}, + {1, 14, 19, 29, 39}, + {1, 5, 6, 39, 44}, + {3, 7, 27, 41, 46}, + {1, 10, 37, 40, 41}, + {4, 14, 15, 23, 28}, + {3, 11, 13, 16, 40}, + {10, 17, 28, 39, 43}, + {2, 22, 27, 41, 47}, + {0, 7, 24, 34, 47}, + {5, 17, 27, 30, 36}, + {16, 17, 26, 32, 42}, + {5, 12, 13, 29, 39}, + {9, 17, 34, 43, 46}, + {2, 31, 35, 41, 50}, + {5, 8, 12, 15, 34}, + {17, 42, 43, 48, 50}, + {13, 38, 39, 44, 48}, + {6, 15, 20, 42, 46}, + {6, 18, 31, 35, 47}, + {0, 1, 10, 12, 14}, + {16, 20, 25, 33, 35}, + {12, 13, 26, 31, 47}, + {3, 12, 20, 26, 29}, + {10, 13, 32, 42, 45}, + {1, 2, 5, 20, 27}, + {7, 19, 26, 27, 32}, + {15, 17, 26, 40, 41}, + {14, 29, 30, 41, 42}, + {7, 9, 24, 32, 45}, + {8, 19, 33, 34, 39}, + {0, 14, 19, 26, 31}, + {10, 15, 16, 31, 41}, + {0, 4, 17, 20, 21}, + {1, 20, 21, 40, 48}, + {21, 33, 46, 48, 49}, + {0, 5, 6, 19, 40}, + {3, 6, 8, 33, 34}, + {2, 6, 7, 38, 46}, + {5, 9, 18, 23, 51}, + {5, 20, 41, 46, 49}, + {9, 11, 13, 39, 43}, + {13, 14, 18, 23, 26}, + {1, 22, 34, 40, 45}, + {3, 18, 22, 31, 49}, + {6, 12, 31, 45, 48}, + {5, 16, 22, 23, 42}, + {1, 8, 21, 30, 44}, + {19, 20, 41, 45, 47}, + {13, 14, 30, 40, 49}, + {8, 33, 35, 40, 46}, + {5, 12, 15, 27, 44}, + {4, 8, 20, 22, 33}, + {10, 19, 20, 46, 48}, + {4, 20, 24, 43, 44}, + {3, 26, 30, 39, 51}, + {6, 18, 19, 41, 47}, + {8, 19, 21, 41, 43}, + {14, 21, 26, 31, 39}, + {1, 7, 10, 20, 34}, + {0, 3, 24, 39, 45}, + {9, 14, 34, 36, 40}, + {2, 5, 41, 48, 50}, + {4, 10, 26, 27, 30}, + {1, 6, 12, 19, 22}, + {21, 22, 46, 47, 51}, + {14, 24, 40, 43, 44}, + {3, 4, 19, 29, 44}, + {12, 19, 32, 42, 44}, + {2, 20, 34, 35, 47}, + {10, 15, 19, 37, 41}, + {0, 5, 15, 44, 45}, + {13, 20, 23, 33, 37}, + {16, 20, 29, 36, 47}, + {8, 12, 19, 27, 45}, + {12, 13, 26, 31, 36}, + {1, 2, 21, 28, 45}, + {20, 32, 33, 41, 48}, + {5, 23, 31, 43, 47}, + {18, 22, 24, 34, 47}, + {1, 6, 15, 40, 42}, + {6, 18, 40, 41, 44}, + {14, 27, 33, 45, 47}, + {2, 16, 30, 36, 42}, + {2, 12, 21, 28, 33}, + {16, 20, 22, 33, 45}, + {7, 17, 25, 31, 33}, + {17, 19, 35, 37, 43}, + {6, 17, 37, 39, 45}, + {0, 13, 23, 28, 30}, + {9, 13, 18, 26, 32}, + {2, 3, 5, 42, 51}, + {8, 13, 31, 34, 40}, + {4, 6, 7, 14, 33}, + {12, 24, 26, 30, 39}, + {2, 7, 14, 23, 46}, + {1, 14, 19, 23, 38}, + {2, 19, 27, 40, 48}, + {13, 19, 29, 40, 45}, + {2, 15, 24, 38, 49}, + {8, 10, 21, 24, 40}, + {2, 21, 22, 36, 41}, + {2, 3, 7, 33, 47}, + {2, 15, 23, 34, 35}, + {0, 8, 23, 39, 50}, + {3, 16, 27, 33, 49}, + {10, 13, 17, 33, 39}, + {29, 30, 31, 34, 43}, + {13, 19, 32, 37, 48}, + {3, 6, 20, 33, 41}, + {4, 8, 14, 21, 26}, + {1, 11, 31, 44, 48}, + {16, 21, 23, 31, 44}, + {3, 7, 8, 41, 47}, + {1, 14, 41, 42, 46}, + {17, 26, 31, 41, 44}, + {5, 6, 12, 31, 37}, + {0, 1, 17, 26, 34}, + {7, 12, 33, 43, 50}, + {6, 17, 26, 31, 39}, + {18, 27, 37, 44, 48}, + {2, 4, 6, 17, 33}, + {2, 28, 37, 44, 51}, + {11, 16, 26, 29, 30}, + {4, 5, 30, 34, 36}, + {21, 38, 40, 42, 47}, + {5, 18, 26, 36, 51}, + {16, 18, 21, 41, 42}, + {15, 24, 28, 38, 49}, + {7, 12, 42, 45, 46}, + {0, 28, 30, 34, 43}, + {2, 10, 12, 15, 34}, + {2, 15, 31, 36, 46}, + {1, 2, 29, 40, 47}, + {1, 2, 21, 28, 42}, + {5, 13, 17, 26, 36}, + {11, 15, 21, 34, 35}, + {0, 13, 45, 49, 51}, + {2, 8, 13, 39, 49}, + {3, 4, 24, 25, 29}, + {0, 4, 6, 25, 32}, + {3, 4, 17, 22, 25}, + {3, 27, 32, 38, 40}, + {7, 24, 46, 47, 49}, + {8, 27, 33, 34, 36}, + {7, 9, 15, 27, 33}, + {3, 9, 11, 16, 20}, + {31, 32, 41, 43, 44}, + {3, 18, 31, 41, 47}, + {7, 21, 25, 31, 46}, + {0, 5, 7, 14, 40}, + {18, 26, 44, 47, 48}, + {18, 20, 24, 33, 42}, + {1, 8, 27, 29, 49}, + {7, 33, 40, 44, 49}, + {13, 14, 27, 43, 50}, + {2, 3, 13, 19, 39}, + {2, 6, 29, 42, 44}, + {10, 28, 30, 41, 44}, + {0, 3, 19, 42, 51}, + {8, 13, 16, 28, 39}, + {2, 26, 30, 41, 45}, + {6, 7, 28, 37, 45}, + {1, 14, 25, 43, 48}, + {14, 21, 34, 36, 45}, + {11, 14, 16, 28, 41}, + {18, 26, 31, 48, 51}, + {3, 8, 27, 36, 42}, + {16, 17, 26, 32, 39}, + {2, 16, 25, 39, 42}, + {2, 32, 35, 41, 50}, + {10, 20, 30, 35, 46}, + {13, 20, 21, 33, 48}, + {23, 25, 31, 43, 44}, + {17, 32, 36, 43, 48}, + {8, 12, 24, 33, 34}, + {1, 17, 33, 37, 46}, + {4, 32, 45, 47, 51}, + {13, 17, 21, 30, 38}, + {9, 16, 29, 43, 46}, + {20, 26, 30, 31, 43}, + {3, 16, 25, 26, 28}, + {5, 6, 16, 17, 30}, + {11, 19, 29, 45, 51}, + {6, 19, 28, 44, 47}, + {13, 22, 32, 37, 45}, + {6, 12, 19, 22, 29}, + {3, 26, 27, 29, 34}, + {7, 16, 20, 22, 50}, + {8, 15, 21, 26, 40}, + {8, 9, 18, 30, 44}, + {16, 29, 40, 45, 49}, + {7, 12, 13, 26, 40}, + {4, 8, 17, 22, 33}, + {1, 9, 21, 27, 30}, + {3, 21, 22, 28, 42}, + {15, 20, 21, 24, 47}, + {16, 21, 34, 36, 44}, + {8, 13, 17, 37, 39}, + {8, 17, 22, 34, 50}, + {16, 26, 29, 30, 36}, + {7, 8, 39, 46, 51}, + {10, 13, 32, 45, 47}, + {0, 4, 11, 33, 46}, + {6, 9, 14, 40, 49}, + {12, 19, 22, 32, 40}, + {2, 17, 28, 33, 51}, + {17, 19, 24, 42, 45}, + {0, 8, 15, 27, 41}, + {1, 7, 9, 40, 45}, + {14, 19, 21, 34, 36}, + {0, 3, 14, 40, 47}, + {1, 5, 18, 20, 37}, + {2, 22, 28, 29, 49}, + {19, 34, 44, 47, 50}, + {13, 17, 20, 46, 48}, + {17, 19, 35, 39, 45}, + {1, 3, 6, 7, 32}, + {7, 10, 13, 26, 32}, + {1, 27, 42, 43, 50}, + {1, 17, 18, 40, 46}, + {5, 18, 28, 48, 51}, + {2, 6, 13, 32, 50}, + {14, 18, 30, 40, 51}, + {0, 6, 26, 33, 41}, + {9, 10, 20, 26, 39}, + {6, 14, 19, 23, 44}, + {14, 16, 32, 37, 45}, + {17, 25, 30, 37, 45}, + {2, 7, 12, 21, 34}, + {16, 31, 33, 37, 44}, + {9, 21, 31, 37, 44}, + {14, 27, 30, 47, 49}, + {1, 11, 12, 40, 48}, + {2, 14, 26, 41, 42}, + {0, 8, 15, 19, 45}, + {0, 6, 18, 39, 48}, + {10, 14, 27, 31, 37}, + {1, 26, 34, 42, 47}, + {0, 4, 14, 28, 41}, + {0, 20, 42, 46, 49}, + {2, 7, 10, 14, 41}, + {1, 2, 18, 20, 27}, + {7, 9, 20, 26, 40}, + {15, 41, 42, 45, 47}, + {0, 14, 15, 28, 44}, + {0, 4, 6, 43, 44}, + {27, 30, 31, 42, 44}, + {5, 18, 30, 35, 42}, + {30, 36, 37, 43, 51}, + {3, 18, 21, 23, 42}, + {1, 6, 25, 40, 41}, + {4, 7, 11, 36, 46}, + {0, 9, 13, 29, 40}, + {17, 20, 21, 30, 42}, + {14, 16, 30, 38, 42}, + {2, 3, 29, 31, 36}, + {4, 21, 34, 35, 40}, + {0, 2, 17, 39, 49}, + {7, 26, 38, 43, 46}, + {8, 13, 19, 25, 26}, + {2, 6, 8, 19, 43}, + {14, 16, 25, 29, 46}, + {10, 32, 43, 45, 47}, + {1, 11, 21, 23, 27}, + {3, 18, 31, 35, 40}, + {2, 13, 16, 23, 28}, + {0, 4, 7, 15, 39}, + {2, 4, 11, 14, 43}, + {14, 22, 27, 38, 50}, + {7, 12, 14, 27, 43}, + {1, 7, 20, 39, 45}, + {13, 32, 40, 45, 48}, + {11, 12, 14, 33, 40}, + {1, 29, 30, 32, 40}, + {3, 18, 20, 30, 43}, + {4, 13, 17, 22, 23}, + {20, 26, 28, 39, 42}, + {15, 16, 19, 21, 28}, + {8, 18, 19, 21, 27}, + {20, 24, 29, 39, 42}, + {1, 4, 10, 27, 47}, + {14, 17, 35, 40, 49}, + {3, 13, 29, 33, 43}, + {4, 5, 16, 20, 31}, + {8, 16, 26, 33, 46}, + {13, 15, 20, 38, 39}, + {18, 20, 29, 30, 43}, + {13, 18, 26, 37, 40}, + {3, 5, 6, 17, 19}, + {4, 7, 19, 27, 46}, + {8, 21, 31, 39, 40}, + {6, 8, 16, 19, 30}, + {30, 33, 34, 36, 46}, + {15, 20, 34, 47, 51}, + {10, 16, 24, 33, 46}, + {1, 6, 11, 45, 51}, + {1, 17, 19, 25, 43}, + {15, 25, 41, 44, 46}, + {0, 14, 28, 31, 39}, + {4, 33, 34, 47, 50}, + {1, 13, 15, 25, 28}, + {3, 7, 19, 36, 46}, + {2, 20, 24, 46, 49}, + {8, 13, 26, 29, 44}, + {8, 14, 16, 33, 42}, + {8, 16, 19, 21, 36}, + {4, 9, 17, 21, 31}, + {1, 7, 31, 40, 41}, + {16, 21, 34, 35, 43}, + {1, 8, 25, 33, 46}, + {0, 5, 18, 22, 41}, + {3, 7, 8, 40, 42}, + {0, 7, 39, 41, 42}, + {5, 14, 18, 43, 48}, + {13, 29, 34, 42, 51}, + {3, 16, 26, 31, 48}, + {1, 5, 7, 31, 35}, + {5, 8, 20, 37, 46}, + {0, 1, 11, 12, 39}, + {10, 18, 26, 31, 33}, + {4, 14, 30, 42, 51}, + {0, 20, 31, 39, 47}, + {0, 16, 27, 40, 47}, + {2, 5, 11, 18, 39}, + {2, 8, 17, 29, 42}, + {14, 20, 27, 32, 42}, + {3, 33, 38, 41, 46}, + {2, 8, 22, 33, 41}, + {12, 20, 42, 43, 46}, + {8, 11, 20, 30, 34}, + {0, 16, 28, 41, 48}, + {0, 7, 25, 39, 44}, + {1, 7, 14, 34, 48}, + {16, 23, 29, 37, 45}, + {6, 11, 14, 17, 43}, + {1, 16, 17, 30, 35}, + {2, 15, 34, 37, 49}, + {17, 22, 30, 39, 49}, + {4, 7, 27, 28, 33}, + {11, 13, 20, 27, 39}, + {14, 28, 31, 38, 40}, + {0, 12, 20, 31, 46}, + {5, 19, 20, 26, 39}, + {13, 16, 22, 31, 44}, + {3, 15, 26, 35, 42}, + {18, 31, 37, 42, 47}, + {1, 6, 11, 26, 45}, + {1, 6, 11, 27, 43}, + {5, 7, 25, 39, 46}, + {24, 30, 32, 35, 43}, + {5, 15, 20, 28, 51}, + {3, 18, 32, 41, 44}, + {3, 16, 21, 23, 32}, + {1, 33, 34, 43, 47}, + {9, 16, 37, 41, 42}, + {0, 4, 17, 45, 47}, + {4, 22, 34, 42, 43}, + {4, 19, 21, 25, 47}, + {0, 10, 32, 45, 46}, + {4, 6, 24, 45, 48}, + {8, 12, 19, 24, 47}, + {5, 14, 17, 31, 35}, + {17, 22, 33, 45, 46}, + {1, 6, 15, 19, 47}, + {7, 8, 40, 42, 47}, + {6, 25, 32, 34, 36}, + {17, 32, 37, 43, 46}, + {3, 8, 17, 47, 48}, + {14, 15, 26, 27, 36}, + {6, 13, 32, 36, 44}, + {13, 17, 24, 26, 51}, + {6, 24, 44, 45, 49}, + {15, 32, 41, 43, 48}, + {26, 29, 32, 35, 42}, + {12, 13, 26, 29, 30}, + {15, 20, 34, 40, 47}, + {0, 3, 16, 18, 24}, + {0, 11, 15, 34, 47}, + {6, 30, 43, 44, 48}, + {14, 17, 26, 30, 34}, + {1, 14, 20, 24, 29}, + {0, 13, 25, 33, 45}, + {7, 8, 21, 49, 50}, + {4, 20, 22, 26, 39}, + {1, 3, 10, 42, 44}, + {0, 3, 12, 39, 40}, + {15, 36, 41, 42, 50}, + {6, 27, 31, 40, 50}, + {3, 8, 16, 30, 48}, + {5, 24, 39, 44, 45}, + {9, 26, 27, 30, 40}, + {2, 5, 36, 44, 50}, + {5, 6, 8, 15, 47}, + {10, 13, 21, 26, 31}, + {1, 6, 17, 30, 35}, + {6, 18, 28, 36, 44}, + {30, 31, 38, 40, 44}, + {6, 20, 32, 35, 50}, + {2, 3, 9, 10, 16}, + {6, 15, 23, 41, 48}, + {0, 1, 25, 27, 48}, + {3, 18, 19, 23, 42}, + {2, 16, 22, 33, 41}, + {17, 30, 33, 45, 47}, + {1, 20, 25, 27, 50}, + {11, 20, 39, 46, 47}, + {0, 2, 4, 31, 41}, + {13, 21, 45, 47, 48}, + {7, 9, 27, 33, 51}, + {2, 7, 15, 26, 31}, + {0, 30, 32, 43, 50}, + {1, 4, 9, 27, 39}, + {5, 8, 23, 31, 42}, + {4, 8, 19, 28, 43}, + {2, 5, 13, 25, 26}, + {8, 14, 21, 25, 28}, + {10, 13, 22, 29, 39}, + {15, 28, 29, 30, 50}, + {9, 15, 27, 41, 44}, + {18, 25, 28, 34, 44}, + {5, 12, 16, 19, 31}, + {4, 9, 19, 30, 37}, + {0, 12, 33, 46, 50}, + {11, 18, 31, 33, 41}, + {15, 17, 20, 46, 47}, + {14, 31, 40, 45, 46}, + {11, 14, 27, 47, 49}, + {4, 27, 40, 42, 44}, + {6, 8, 12, 20, 45}, + {4, 5, 20, 33, 34}, + {5, 7, 14, 33, 38}, + {17, 19, 27, 34, 40}, + {21, 26, 34, 35, 46}, + {2, 22, 37, 41, 44}, + {10, 11, 17, 19, 32}, + {3, 8, 14, 45, 47}, + {30, 34, 42, 46, 47}, + {2, 10, 12, 34, 41}, + {0, 26, 42, 48, 50}, + {3, 5, 12, 18, 39}, + {8, 12, 13, 21, 32}, + {4, 15, 41, 44, 49}, + {1, 3, 11, 17, 43}, + {0, 6, 8, 32, 42}, + {0, 2, 12, 20, 41}, + {9, 10, 20, 33, 51}, + {3, 4, 20, 26, 43}, + {0, 15, 26, 33, 40}, + {3, 16, 41, 49, 50}, + {2, 20, 40, 46, 49}, + {2, 5, 26, 37, 41}, + {5, 16, 25, 32, 45}, + {5, 13, 23, 31, 48}, + {18, 21, 27, 44, 51}, + {5, 6, 19, 40, 43}, + {19, 28, 30, 38, 45}, + {6, 28, 38, 41, 50}, + {3, 7, 33, 37, 45}, + {1, 2, 15, 18, 24}, + {8, 16, 29, 30, 41}, + {5, 11, 17, 18, 38}, + {5, 9, 20, 21, 31}, + {10, 19, 27, 45, 48}, + {5, 8, 22, 31, 49}, + {13, 23, 27, 32, 39}, + {4, 8, 36, 43, 50}, + {0, 20, 30, 44, 46}, + {21, 22, 32, 43, 45}, + {19, 20, 33, 41, 42}, + {2, 7, 32, 33, 44}, + {2, 18, 20, 29, 44}, + {3, 32, 45, 48, 51}, + {1, 18, 29, 30, 44}, + {3, 6, 15, 29, 40}, + {3, 4, 20, 33, 47}, + {14, 16, 36, 40, 50}, + {6, 18, 24, 34, 47}, + {4, 5, 19, 26, 39}, + {8, 12, 16, 19, 21}, + {0, 7, 39, 42, 45}, + {3, 10, 13, 16, 51}, + {1, 26, 33, 46, 47}, + {6, 10, 15, 39, 45}, + {1, 3, 14, 25, 32}, + {1, 19, 29, 30, 32}, + {0, 13, 22, 24, 30}, + {2, 28, 34, 42, 50}, + {2, 13, 14, 26, 33}, + {2, 15, 24, 25, 42}, + {13, 26, 27, 48, 51}, + {20, 28, 29, 31, 42}, + {8, 11, 14, 40, 43}, + {3, 21, 26, 27, 47}, + {2, 8, 39, 44, 47}, + {2, 28, 36, 47, 51}, + {4, 16, 19, 40, 43}, + {7, 17, 25, 31, 46}, + {2, 14, 18, 31, 45}, + {15, 21, 34, 39, 43}, + {3, 18, 23, 37, 42}, + {17, 29, 33, 41, 46}, + {3, 13, 34, 41, 42}, + {8, 10, 17, 35, 43}, + {8, 11, 17, 34, 48}, + {0, 5, 23, 26, 29}, + {15, 25, 28, 31, 42}, + {11, 21, 27, 35, 40}, + {1, 6, 14, 18, 34}, + {3, 4, 17, 48, 49}, + {1, 2, 31, 38, 40}, + {0, 3, 13, 17, 41}, + {11, 21, 22, 34, 39}, + {11, 14, 26, 28, 39}, + {1, 5, 11, 36, 40}, + {1, 21, 28, 32, 47}, + {2, 32, 36, 44, 45}, + {14, 17, 27, 35, 41}, + {1, 20, 33, 34, 36}, + {3, 29, 37, 40, 49}, + {14, 19, 22, 40, 41}, + {7, 14, 19, 24, 33}, + {0, 21, 22, 23, 47}, + {10, 15, 24, 28, 30}, + {0, 5, 13, 23, 35}, + {5, 18, 32, 41, 51}, + {7, 20, 24, 47, 51}, + {7, 8, 20, 26, 50}, + {5, 8, 19, 34, 41}, + {5, 38, 39, 44, 48}, + {22, 27, 28, 41, 45}, + {3, 4, 19, 29, 38}, + {5, 14, 18, 28, 35}, + {18, 22, 31, 45, 46}, + {2, 11, 14, 15, 51}, + {0, 5, 14, 31, 48}, + {0, 7, 11, 36, 39}, + {18, 26, 39, 40, 49}, + {0, 29, 31, 32, 39}, + {3, 29, 38, 41, 48}, + {17, 26, 27, 34, 40}, + {3, 4, 43, 46, 48}, + {30, 34, 38, 41, 47}, + {8, 11, 21, 30, 44}, + {1, 5, 13, 39, 45}, + {6, 8, 17, 24, 43}, + {14, 33, 34, 46, 48}, + {0, 3, 18, 40, 42}, + {10, 21, 34, 40, 50}, + {6, 14, 20, 27, 47}, + {3, 7, 13, 29, 41}, + {3, 4, 12, 42, 48}, + {6, 7, 10, 14, 20}, + {13, 22, 39, 43, 49}, + {8, 16, 17, 40, 43}, + {0, 7, 32, 46, 51}, + {8, 31, 35, 44, 50}, + {14, 32, 40, 43, 49}, + {9, 24, 32, 33, 45}, + {21, 29, 33, 34, 39}, + {7, 8, 22, 33, 42}, + {0, 23, 25, 26, 47}, + {1, 4, 15, 30, 36}, + {18, 19, 20, 45, 49}, + {3, 9, 14, 42, 45}, + {5, 32, 44, 48, 50}, + {1, 5, 6, 27, 41}, + {14, 17, 28, 33, 43}, + {8, 20, 33, 37, 48}, + {7, 9, 41, 46, 47}, + {16, 26, 29, 30, 41}, + {3, 10, 16, 22, 51}, + {16, 20, 29, 35, 40}, + {2, 5, 12, 13, 26}, + {7, 21, 24, 43, 47}, + {16, 17, 24, 25, 30}, + {10, 19, 31, 42, 44}, + {8, 13, 20, 44, 47}, + {1, 9, 17, 28, 43}, + {0, 13, 33, 42, 50}, + {18, 20, 27, 43, 46}, + {6, 35, 36, 38, 45}, + {23, 29, 30, 34, 47}, + {10, 14, 17, 27, 38}, + {12, 34, 44, 47, 48}, + {8, 14, 15, 39, 47}, + {16, 17, 36, 43, 48}, + {8, 13, 21, 32, 41}, + {3, 13, 26, 35, 41}, + {7, 8, 21, 42, 44}, + {3, 14, 27, 34, 46}, + {16, 26, 41, 42, 48}, + {4, 6, 19, 48, 51}, + {0, 27, 32, 45, 49}, + {14, 15, 18, 31, 34}, + {8, 31, 32, 41, 45}, + {4, 19, 26, 38, 39}, + {19, 31, 40, 41, 44}, + {7, 9, 18, 20, 29}, + {23, 24, 26, 39, 40}, + {6, 11, 18, 19, 23}, + {14, 24, 40, 41, 48}, + {2, 26, 36, 41, 50}, + {1, 13, 23, 39, 44}, + {4, 11, 17, 23, 41}, + {6, 8, 12, 16, 34}, + {0, 5, 28, 34, 39}, + {5, 26, 31, 40, 49}, + {0, 33, 37, 39, 40}, + {6, 7, 15, 27, 40}, + {0, 5, 7, 18, 45}, + {8, 25, 29, 41, 47}, + {0, 6, 25, 32, 43}, + {2, 15, 18, 27, 43}, + {4, 7, 20, 42, 47}, + {7, 20, 22, 44, 50}, + {2, 5, 15, 21, 49}, + {0, 16, 19, 41, 42}, + {0, 1, 29, 42, 49}, + {13, 20, 26, 29, 50}, + {2, 3, 14, 18, 31}, + {19, 26, 28, 41, 43}, + {7, 16, 18, 31, 35}, + {5, 17, 30, 32, 40}, + {8, 12, 19, 33, 45}, + {20, 26, 34, 46, 51}, + {13, 27, 29, 33, 40}, + {8, 19, 23, 28, 47}, + {1, 4, 13, 14, 22}, + {5, 16, 19, 32, 38}, + {1, 15, 32, 34, 45}, + {6, 13, 14, 22, 27}, + {4, 30, 31, 36, 45}, + {2, 20, 26, 30, 46}, + {4, 5, 30, 39, 46}, + {4, 6, 7, 20, 47}, + {19, 31, 38, 45, 49}, + {9, 17, 18, 20, 43}, + {16, 30, 31, 39, 43}, + {7, 19, 35, 40, 45}, + {1, 13, 40, 48, 49}, + {3, 8, 38, 46, 47}, + {0, 1, 13, 19, 38}, + {2, 4, 13, 15, 37}, + {15, 17, 34, 40, 47}, + {7, 20, 24, 26, 40}, + {17, 19, 32, 38, 40}, + {0, 1, 6, 8, 47}, + {3, 20, 22, 32, 42}, + {4, 14, 17, 25, 33}, + {7, 12, 21, 26, 33}, + {5, 17, 26, 44, 46}, + {17, 20, 28, 41, 50}, + {0, 25, 31, 35, 44}, + {8, 18, 21, 29, 39}, + {1, 14, 22, 39, 46}, + {4, 23, 30, 37, 45}, + {12, 17, 21, 29, 30}, + {8, 12, 21, 28, 32}, + {7, 27, 36, 39, 46}, + {9, 14, 27, 30, 51}, + {5, 16, 18, 41, 47}, + {1, 17, 18, 30, 51}, + {5, 6, 12, 28, 32}, + {0, 25, 34, 39, 42}, + {3, 4, 25, 35, 42}, + {12, 19, 21, 45, 49}, + {0, 1, 20, 39, 44}, + {2, 4, 15, 37, 46}, + {8, 33, 37, 47, 49}, + {3, 13, 20, 36, 42}, + {6, 13, 15, 19, 33}, + {7, 15, 23, 28, 30}, + {5, 12, 20, 23, 31}, + {1, 3, 10, 39, 40}, + {26, 31, 34, 47, 51}, + {3, 13, 27, 36, 42}, + {13, 19, 26, 28, 51}, + {2, 3, 15, 43, 46}, + {4, 8, 26, 47, 51}, + {3, 11, 16, 34, 39}, + {9, 20, 21, 28, 47}, + {1, 16, 19, 21, 47}, + {4, 11, 17, 32, 36}, + {4, 11, 20, 21, 33}, + {8, 17, 19, 25, 34}, + {1, 4, 6, 9, 40}, + {4, 17, 22, 27, 29}, + {19, 20, 29, 33, 40}, + {4, 15, 19, 43, 44}, + {0, 20, 26, 27, 47}, + {1, 14, 20, 24, 47}, + {2, 13, 24, 36, 39}, + {16, 17, 20, 21, 46}, + {4, 13, 18, 22, 44}, + {13, 34, 38, 39, 42}, + {4, 17, 21, 33, 38}, + {8, 24, 28, 41, 49}, + {4, 16, 17, 26, 31}, + {7, 14, 30, 31, 33}, + {15, 21, 26, 31, 44}, + {11, 13, 19, 34, 45}, + {19, 27, 29, 36, 42}, + {2, 26, 27, 38, 40}, + {12, 16, 22, 26, 29}, + {4, 6, 9, 19, 49}, + {0, 7, 17, 18, 33}, + {5, 33, 41, 44, 47}, + {5, 32, 36, 41, 45}, + {2, 7, 19, 27, 33}, + {1, 14, 18, 19, 23}, + {3, 8, 14, 34, 35}, + {5, 17, 19, 31, 34}, + {23, 29, 30, 42, 46}, + {11, 29, 32, 35, 45}, + {4, 9, 20, 29, 43}, + {3, 19, 20, 33, 39}, + {7, 30, 31, 39, 44}, + {4, 16, 17, 32, 47}, + {15, 21, 28, 42, 48}, + {1, 5, 18, 35, 39}, + {14, 15, 33, 35, 41}, + {16, 32, 40, 45, 47}, + {9, 11, 19, 28, 45}, + {2, 6, 8, 25, 47}, + {4, 13, 41, 43, 51}, + {4, 15, 28, 40, 50}, + {0, 4, 14, 43, 51}, + {5, 21, 38, 41, 44}, + {8, 19, 40, 43, 45}, + {4, 5, 24, 31, 36}, + {28, 29, 34, 37, 47}, + {6, 7, 24, 40, 46}, + {2, 15, 22, 37, 46}, + {14, 25, 26, 30, 43}, + {4, 29, 38, 42, 45}, + {12, 13, 21, 34, 43}, + {27, 28, 29, 41, 49}, + {3, 12, 21, 29, 40}, + {4, 20, 27, 40, 47}, + {6, 20, 28, 39, 41}, + {6, 24, 31, 44, 49}, + {2, 15, 26, 40, 46}, + {7, 15, 22, 24, 41}, + {8, 32, 44, 45, 50}, + {12, 20, 26, 34, 46}, + {11, 28, 38, 39, 41}, + {8, 14, 31, 44, 46}, + {3, 7, 20, 22, 36}, + {1, 11, 12, 15, 40}, + {0, 1, 18, 28, 31}, + {4, 7, 8, 22, 30}, + {3, 8, 15, 18, 31}, + {1, 13, 14, 28, 51}, + {5, 6, 14, 19, 23}, + {0, 4, 16, 32, 39}, + {7, 13, 14, 20, 37}, + {5, 13, 15, 18, 40}, + {6, 9, 16, 30, 43}, + {5, 14, 16, 23, 31}, + {2, 4, 23, 41, 46}, + {3, 8, 24, 28, 47}, + {19, 28, 35, 37, 41}, + {16, 24, 28, 41, 48}, + {6, 8, 11, 12, 21}, + {10, 13, 14, 29, 40}, + {3, 7, 15, 24, 33}, + {6, 18, 23, 28, 41}, + {1, 14, 29, 34, 39}, + {26, 30, 39, 44, 48}, + {3, 15, 18, 29, 46}, + {4, 11, 12, 18, 44}, + {1, 27, 42, 46, 51}, + {8, 9, 12, 39, 47}, + {11, 21, 36, 42, 47}, + {4, 8, 11, 26, 47}, + {3, 4, 5, 42, 51}, + {2, 3, 11, 16, 22}, + {1, 21, 26, 27, 43}, + {2, 6, 19, 39, 50}, + {1, 9, 13, 17, 40}, + {15, 27, 41, 42, 50}, + {1, 11, 35, 40, 42}, + {0, 13, 17, 19, 31}, + {0, 12, 26, 34, 42}, + {2, 27, 31, 39, 40}, + {4, 18, 31, 39, 41}, + {14, 21, 27, 38, 50}, + {20, 21, 32, 46, 50}, + {11, 12, 21, 41, 47}, + {14, 18, 20, 31, 47}, + {2, 16, 19, 43, 45}, + {4, 11, 16, 18, 31}, + {29, 31, 41, 42, 49}, + {1, 3, 14, 33, 39}, + {18, 32, 39, 42, 44}, + {14, 27, 30, 41, 48}, + {0, 1, 11, 13, 23}, + {2, 8, 19, 21, 38}, + {8, 21, 27, 30, 48}, + {8, 32, 33, 42, 46}, + {6, 7, 32, 37, 40}, + {13, 27, 32, 39, 42}, + {0, 8, 27, 36, 39}, + {1, 18, 27, 38, 47}, + {1, 8, 19, 21, 39}, + {6, 12, 20, 40, 45}, + {2, 4, 19, 27, 30}, + {4, 15, 18, 25, 30}, + {13, 15, 27, 34, 47}, + {0, 8, 19, 28, 47}, + {8, 36, 47, 48, 51}, + {7, 16, 19, 20, 39}, + {3, 16, 19, 22, 37}, + {12, 21, 31, 34, 36}, + {3, 15, 20, 25, 28}, + {15, 23, 28, 37, 43}, + {10, 32, 44, 45, 46}, + {7, 17, 20, 25, 36}, + {7, 13, 17, 31, 39}, + {4, 12, 14, 26, 40}, + {11, 13, 21, 29, 39}, + {5, 6, 27, 29, 45}, + {3, 6, 13, 25, 29}, + {7, 28, 35, 42, 46}, + {13, 21, 30, 40, 43}, + {12, 16, 34, 37, 47}, + {2, 6, 18, 20, 31}, + {18, 34, 36, 38, 47}, + {14, 22, 32, 42, 45}, + {24, 28, 33, 41, 42}, + {0, 6, 13, 17, 27}, + {4, 9, 20, 32, 43}, + {1, 9, 33, 36, 46}, + {8, 19, 41, 47, 50}, + {4, 5, 9, 16, 31}, + {4, 11, 39, 43, 46}, + {20, 23, 29, 41, 42}, + {2, 31, 38, 41, 48}, + {7, 23, 34, 35, 46}, + {0, 7, 23, 27, 46}, + {21, 24, 30, 31, 34}, + {5, 26, 30, 34, 43}, + {3, 8, 10, 21, 46}, + {9, 16, 40, 42, 50}, + {16, 26, 29, 41, 46}, + {1, 6, 14, 29, 43}, + {3, 14, 15, 25, 27}, + {13, 21, 38, 42, 47}, + {4, 11, 22, 40, 43}, + {7, 17, 19, 38, 46}, + {0, 8, 12, 26, 48}, + {4, 6, 18, 35, 45}, + {8, 26, 27, 34, 38}, + {9, 27, 38, 40, 50}, + {32, 34, 39, 44, 47}, + {5, 15, 29, 40, 44}, + {6, 19, 24, 25, 39}, + {2, 4, 12, 36, 43}, + {2, 6, 8, 47, 49}, + {21, 28, 29, 37, 41}, + {3, 13, 16, 38, 41}, + {7, 17, 20, 24, 41}, + {2, 19, 29, 38, 41}, + {9, 20, 29, 37, 42}, + {1, 6, 18, 38, 45}, + {3, 21, 38, 40, 42}, + {5, 7, 16, 17, 29}, + {5, 12, 13, 41, 44}, + {1, 6, 14, 30, 42}, + {3, 8, 19, 21, 40}, + {1, 2, 39, 40, 44}, + {0, 6, 34, 40, 45}, + {2, 27, 32, 41, 48}, + {6, 11, 13, 14, 32}, + {4, 18, 27, 31, 39}, + {2, 7, 10, 25, 33}, + {0, 24, 25, 39, 48}, + {2, 5, 21, 25, 41}, + {3, 7, 23, 34, 42}, + {3, 28, 37, 38, 42}, + {0, 8, 21, 22, 36}, + {0, 13, 18, 20, 34}, + {1, 31, 36, 40, 47}, + {1, 15, 19, 24, 45}, + {23, 27, 31, 40, 41}, + {8, 9, 19, 30, 47}, + {3, 11, 28, 33, 41}, + {6, 16, 25, 31, 44}, + {13, 22, 25, 26, 44}, + {1, 16, 38, 42, 47}, + {4, 19, 25, 32, 42}, + {6, 12, 18, 30, 45}, + {0, 2, 20, 22, 41}, + {18, 27, 28, 33, 41}, + {1, 13, 21, 26, 51}, + {1, 11, 23, 27, 28}, + {2, 3, 6, 16, 23}, + {14, 28, 33, 36, 41}, + {4, 19, 20, 32, 37}, + {3, 5, 8, 21, 35}, + {4, 9, 17, 18, 28}, + {7, 10, 29, 33, 47}, + {12, 30, 33, 39, 46}, + {8, 21, 30, 40, 41}, + {5, 6, 18, 21, 38}, + {1, 8, 17, 38, 43}, + {0, 1, 11, 16, 26}, + {5, 16, 28, 44, 48}, + {2, 4, 14, 30, 31}, + {0, 9, 11, 21, 26}, + {15, 19, 41, 43, 48}, + {8, 19, 28, 37, 41}, + {9, 14, 40, 47, 49}, + {19, 27, 38, 42, 45}, + {8, 9, 14, 40, 46}, + {27, 30, 34, 40, 48}, + {16, 26, 28, 39, 51}, + {1, 10, 32, 40, 44}, + {1, 18, 34, 35, 44}, + {10, 15, 21, 34, 48}, + {11, 18, 44, 46, 47}, + {2, 11, 40, 41, 49}, + {2, 29, 33, 41, 48}, + {5, 12, 20, 35, 44}, + {2, 12, 26, 41, 43}, + {11, 20, 29, 36, 46}, + {8, 12, 21, 30, 35}, + {6, 14, 25, 32, 44}, + {2, 7, 19, 20, 35}, + {7, 21, 23, 27, 47}, + {16, 21, 23, 29, 31}, + {2, 15, 39, 47, 50}, + {3, 9, 24, 29, 46}, + {19, 20, 26, 39, 40}, + {0, 16, 24, 39, 45}, + {24, 32, 33, 34, 47}, + {8, 21, 24, 26, 41}, + {29, 35, 41, 42, 44}, + {6, 14, 21, 40, 51}, + {17, 23, 26, 30, 33}, + {11, 21, 22, 32, 45}, + {0, 2, 18, 20, 39}, + {0, 7, 15, 45, 46}, + {2, 23, 31, 35, 41}, + {16, 20, 37, 38, 42}, + {3, 13, 19, 28, 41}, + {5, 15, 39, 40, 44}, + {5, 18, 32, 37, 49}, + {13, 36, 39, 46, 47}, + {4, 14, 26, 30, 51}, + {6, 20, 29, 33, 50}, + {9, 33, 46, 47, 50}, + {6, 17, 25, 34, 47}, + {6, 8, 12, 33, 34}, + {6, 8, 24, 28, 32}, + {16, 17, 43, 47, 49}, + {2, 19, 28, 29, 37}, + {3, 4, 30, 35, 39}, + {8, 15, 19, 32, 51}, + {11, 12, 16, 29, 30}, + {6, 18, 32, 35, 36}, + {1, 2, 14, 44, 45}, + {19, 30, 41, 43, 50}, + {15, 19, 27, 32, 50}, + {13, 25, 29, 42, 49}, + {16, 17, 30, 31, 47}, + {5, 6, 18, 28, 39}, + {3, 14, 16, 19, 25}, + {14, 24, 27, 34, 41}, + {14, 26, 28, 37, 40}, + {12, 13, 20, 40, 46}, + {2, 16, 18, 44, 49}, + {6, 8, 25, 41, 45}, + {14, 18, 31, 43, 46}, + {4, 6, 8, 34, 37}, + {1, 4, 14, 48, 51}, + {14, 27, 34, 41, 49}, + {1, 7, 14, 21, 24}, + {12, 27, 37, 40, 46}, + {3, 36, 37, 39, 42}, + {4, 29, 39, 41, 42}, + {7, 21, 26, 46, 49}, + {1, 6, 25, 45, 50}, + {14, 26, 32, 36, 45}, + {0, 2, 20, 33, 45}, + {1, 7, 10, 29, 40}, + {16, 21, 29, 41, 48}, + {1, 5, 11, 21, 44}, + {6, 8, 18, 32, 40}, + {1, 12, 31, 40, 42}, + {7, 13, 23, 38, 46}, + {10, 12, 13, 31, 39}, + {3, 10, 37, 42, 43}, + {13, 15, 31, 34, 41}, + {3, 5, 31, 43, 50}, + {10, 26, 32, 39, 46}, + {13, 39, 45, 46, 51}, + {1, 2, 6, 10, 28}, + {2, 4, 28, 34, 51}, + {0, 4, 13, 41, 49}, + {8, 17, 18, 28, 30}, + {0, 2, 16, 41, 46}, + {4, 16, 18, 41, 43}, + {6, 11, 19, 27, 44}, + {4, 7, 20, 22, 31}, + {6, 13, 17, 19, 36}, + {1, 3, 16, 46, 47}, + {7, 39, 46, 47, 48}, + {20, 27, 33, 37, 45}, + {8, 17, 41, 43, 50}, + {15, 26, 32, 39, 42}, + {13, 16, 42, 45, 49}, + {20, 29, 33, 48, 50}, + {13, 14, 15, 36, 40}, + {8, 21, 27, 48, 49}, + {5, 20, 31, 38, 43}, + {1, 11, 13, 14, 38}, + {3, 26, 32, 39, 41}, + {0, 7, 29, 42, 43}, + {20, 21, 27, 40, 44}, + {13, 15, 18, 41, 48}, + {15, 17, 27, 31, 41}, + {2, 9, 25, 37, 41}, + {22, 27, 31, 34, 47}, + {1, 17, 18, 43, 45}, + {3, 11, 16, 31, 51}, + {16, 18, 27, 33, 42}, + {33, 35, 36, 43, 46}, + {2, 20, 25, 28, 32}, + {5, 7, 16, 23, 31}, + {10, 27, 28, 41, 45}, + {4, 11, 30, 31, 40}, + {20, 29, 32, 45, 47}, + {27, 32, 36, 40, 48}, + {31, 32, 41, 44, 46}, + {4, 17, 24, 31, 32}, + {14, 16, 26, 27, 43}, + {1, 30, 34, 47, 48}, + {6, 21, 27, 44, 47}, + {0, 14, 28, 39, 51}, + {0, 10, 26, 40, 51}, + {3, 34, 38, 47, 49}, + {0, 1, 2, 34, 39}, + {10, 25, 27, 28, 40}, + {3, 24, 25, 26, 42}, + {14, 18, 27, 30, 47}, + {0, 13, 32, 33, 50}, + {0, 2, 15, 23, 50}, + {7, 20, 47, 49, 50}, + {13, 17, 23, 35, 39}, + {1, 3, 11, 14, 48}, + {5, 13, 15, 39, 42}, + {3, 32, 34, 38, 42}, + {7, 15, 25, 28, 37}, + {1, 14, 17, 23, 33}, + {0, 7, 25, 39, 48}, + {0, 17, 25, 40, 43}, + {7, 14, 26, 32, 45}, + {5, 21, 29, 33, 47}, + {0, 13, 22, 30, 41}, + {17, 21, 41, 47, 48}, + {0, 11, 22, 34, 47}, + {3, 9, 25, 30, 43}, + {5, 8, 33, 44, 45}, + {13, 16, 28, 39, 40}, + {12, 13, 33, 40, 46}, + {6, 24, 25, 32, 42}, + {0, 2, 13, 21, 46}, + {3, 15, 23, 29, 33}, + {2, 18, 31, 33, 50}, + {3, 4, 6, 13, 17}, + {3, 6, 15, 16, 25}, + {5, 6, 19, 20, 27}, + {5, 20, 27, 29, 42}, + {2, 11, 28, 39, 47}, + {0, 19, 22, 26, 37}, + {6, 12, 20, 26, 45}, + {6, 20, 32, 43, 48}, + {6, 8, 21, 43, 51}, + {14, 19, 23, 27, 33}, + {6, 21, 31, 39, 45}, + {0, 4, 16, 30, 36}, + {2, 27, 29, 31, 42}, + {19, 26, 37, 39, 46}, + {1, 8, 16, 40, 43}, + {4, 18, 20, 24, 33}, + {6, 11, 13, 31, 39}, + {2, 21, 25, 27, 40}, + {6, 11, 25, 32, 41}, + {14, 17, 19, 37, 43}, + {15, 17, 40, 43, 47}, + {2, 11, 15, 21, 32}, + {6, 13, 27, 39, 48}, + {15, 33, 35, 36, 46}, + {4, 7, 20, 29, 34}, + {6, 13, 21, 38, 39}, + {3, 5, 21, 43, 47}, + {6, 9, 16, 29, 50}, + {0, 3, 15, 41, 49}, + {6, 14, 27, 36, 50}, + {11, 14, 29, 40, 51}, + {6, 7, 26, 39, 40}, + {8, 19, 30, 40, 43}, + {10, 21, 37, 47, 48}, + {18, 26, 39, 42, 45}, + {0, 6, 9, 45, 49}, + {6, 11, 13, 32, 40}, + {2, 12, 26, 28, 49}, + {6, 20, 40, 44, 46}, + {8, 11, 19, 20, 47}, + {7, 8, 30, 34, 50}, + {9, 15, 28, 43, 47}, + {3, 10, 18, 28, 29}, + {1, 2, 10, 28, 35}, + {2, 15, 20, 21, 31}, + {13, 32, 45, 47, 49}, + {15, 27, 29, 34, 42}, + {12, 19, 22, 33, 45}, + {18, 21, 24, 47, 49}, + {12, 15, 16, 29, 32}, + {5, 7, 15, 32, 44}, + {9, 33, 36, 46, 50}, + {5, 11, 18, 45, 49}, + {2, 21, 24, 26, 41}, + {13, 18, 38, 39, 45}, + {17, 20, 27, 41, 46}, + {5, 18, 27, 35, 42}, + {6, 9, 19, 27, 34}, + {21, 26, 27, 28, 40}, + {1, 7, 27, 29, 45}, + {3, 8, 9, 14, 21}, + {4, 19, 31, 45, 46}, + {10, 14, 21, 28, 40}, + {16, 20, 46, 47, 48}, + {1, 12, 34, 37, 47}, + {11, 15, 34, 41, 48}, + {4, 10, 24, 31, 43}, + {7, 29, 33, 37, 43}, + {19, 32, 47, 48, 50}, + {14, 28, 32, 39, 45}, + {5, 17, 18, 24, 35}, + {16, 19, 26, 29, 41}, + {3, 13, 29, 40, 43}, + {4, 19, 29, 35, 42}, + {4, 15, 27, 30, 38}, + {6, 8, 13, 21, 35}, + {17, 21, 24, 25, 47}, + {5, 27, 31, 35, 37}, + {15, 27, 29, 40, 46}, + {4, 19, 20, 26, 43}, + {2, 19, 26, 27, 28}, + {1, 4, 17, 42, 49}, + {13, 14, 26, 30, 48}, + {1, 5, 31, 35, 42}, + {7, 9, 20, 26, 45}, + {19, 32, 39, 42, 47}, + {1, 19, 35, 40, 41}, + {14, 19, 27, 28, 29}, + {7, 20, 26, 27, 45}, + {15, 25, 41, 43, 48}, + {3, 17, 21, 30, 50}, + {20, 23, 31, 46, 48}, + {4, 5, 17, 22, 42}, + {13, 26, 27, 36, 50}, + {1, 8, 12, 14, 33}, + {4, 23, 43, 47, 51}, + {4, 16, 28, 33, 42}, + {14, 19, 45, 46, 48}, + {7, 20, 28, 43, 49}, + {0, 15, 28, 36, 47}, + {3, 6, 12, 19, 26}, + {4, 15, 20, 26, 28}, + {4, 15, 19, 43, 46}, + {3, 6, 11, 26, 29}, + {8, 15, 28, 49, 50}, + {0, 3, 6, 19, 38}, + {1, 21, 25, 27, 29}, + {14, 32, 45, 48, 49}, + {2, 11, 12, 14, 15}, + {3, 10, 22, 29, 37}, + {0, 15, 17, 43, 48}, + {4, 11, 17, 19, 40}, + {2, 3, 28, 44, 51}, + {0, 6, 45, 48, 49}, + {4, 5, 33, 46, 50}, + {11, 18, 23, 43, 44}, + {1, 12, 21, 31, 40}, + {2, 3, 17, 32, 45}, + {4, 7, 8, 19, 30}, + {0, 2, 16, 22, 28}, + {1, 10, 35, 40, 46}, + {2, 12, 14, 40, 46}, + {8, 22, 27, 37, 47}, + {15, 33, 41, 43, 44}, + {0, 7, 18, 22, 46}, + {17, 29, 32, 40, 42}, + {20, 33, 35, 36, 39}, + {4, 8, 17, 29, 50}, + {14, 20, 26, 27, 42}, + {2, 12, 26, 41, 46}, + {0, 23, 27, 39, 47}, + {6, 8, 21, 25, 35}, + {1, 5, 7, 18, 38}, + {2, 15, 36, 42, 48}, + {13, 25, 33, 34, 39}, + {5, 11, 28, 41, 46}, + {0, 16, 31, 39, 51}, + {1, 20, 33, 41, 42}, + {5, 18, 23, 27, 47}, + {7, 15, 46, 50, 51}, + {3, 15, 22, 30, 42}, + {6, 35, 43, 45, 46}, + {14, 36, 39, 40, 51}, + {19, 34, 45, 49, 50}, + {6, 24, 34, 40, 45}, + {7, 29, 34, 35, 42}, + {5, 7, 12, 31, 47}, + {5, 11, 12, 41, 44}, + {5, 14, 20, 44, 49}, + {13, 17, 31, 43, 45}, + {17, 27, 31, 41, 44}, + {2, 10, 13, 26, 50}, + {1, 26, 27, 43, 47}, + {0, 9, 17, 19, 26}, + {3, 11, 21, 29, 43}, + {2, 13, 25, 30, 41}, + {4, 9, 15, 28, 29}, + {20, 24, 43, 46, 49}, + {0, 3, 11, 20, 46}, + {0, 14, 21, 22, 40}, + {10, 14, 27, 37, 51}, + {1, 7, 11, 29, 42}, + {20, 21, 25, 41, 47}, + {17, 21, 33, 41, 43}, + {15, 19, 27, 32, 48}, + {4, 6, 24, 28, 41}, + {3, 18, 24, 29, 30}, + {2, 6, 8, 15, 24}, + {21, 28, 33, 34, 49}, + {8, 13, 30, 40, 43}, + {1, 6, 21, 41, 45}, + {2, 20, 28, 30, 45}, + {2, 18, 31, 45, 50}, + {4, 17, 29, 31, 41}, + {4, 7, 11, 17, 18}, + {4, 16, 18, 20, 31}, + {1, 8, 21, 31, 38}, + {3, 5, 18, 19, 51}, + {7, 20, 22, 32, 37}, + {1, 17, 34, 47, 50}, + {6, 7, 17, 46, 50}, + {3, 6, 19, 27, 41}, + {21, 30, 31, 38, 47}, + {5, 14, 18, 35, 42}, + {6, 8, 11, 32, 42}, + {8, 34, 42, 44, 49}, + {16, 17, 30, 33, 47}, + {0, 5, 28, 41, 51}, + {13, 15, 33, 37, 46}, + {2, 32, 39, 45, 46}, + {5, 16, 17, 29, 40}, + {5, 14, 27, 32, 35}, + {0, 26, 31, 32, 34}, + {7, 17, 20, 31, 38}, + {3, 25, 39, 42, 43}, + {2, 6, 7, 17, 28}, + {0, 13, 17, 29, 32}, + {0, 2, 19, 26, 42}, + {1, 28, 30, 41, 42}, + {2, 3, 8, 34, 38}, + {3, 15, 26, 28, 35}, + {12, 18, 28, 32, 44}, + {1, 13, 15, 20, 46}, + {4, 6, 20, 30, 42}, + {4, 7, 19, 22, 32}, + {6, 19, 30, 42, 49}, + {6, 17, 21, 30, 37}, + {0, 19, 25, 32, 46}, + {2, 15, 16, 37, 48}, + {7, 23, 24, 29, 42}, + {16, 27, 37, 42, 43}, + {15, 17, 20, 26, 28}, + {9, 18, 26, 31, 41}, + {4, 16, 28, 43, 50}, + {0, 1, 16, 22, 29}, + {4, 10, 29, 30, 44}, + {8, 11, 21, 28, 46}, + {2, 15, 17, 27, 32}, + {7, 14, 18, 27, 47}, + {19, 25, 32, 42, 49}, + {0, 6, 13, 34, 43}, + {1, 5, 6, 21, 47}, + {17, 25, 31, 34, 47}, + {8, 17, 26, 32, 43}, + {4, 19, 33, 46, 50}, + {17, 19, 21, 32, 38}, + {1, 2, 16, 29, 31}, + {6, 19, 29, 46, 51}, + {1, 3, 31, 34, 47}, + {2, 8, 15, 18, 40}, + {5, 15, 16, 31, 35}, + {27, 28, 33, 41, 42}, + {6, 30, 41, 44, 45}, + {7, 16, 19, 33, 51}, + {2, 3, 28, 30, 31}, + {2, 8, 14, 15, 29}, + {1, 14, 25, 33, 34}, + {1, 8, 12, 36, 40}, + {3, 15, 20, 28, 50}, + {25, 27, 31, 41, 44}, + {6, 32, 35, 43, 47}, + {5, 7, 18, 39, 40}, + {1, 5, 23, 27, 51}, + {9, 16, 31, 42, 51}, + {4, 9, 18, 32, 43}, + {2, 5, 19, 31, 40}, + {12, 15, 20, 26, 39}, + {1, 16, 22, 34, 42}, + {11, 30, 38, 43, 45}, + {26, 31, 36, 44, 46}, + {25, 30, 32, 33, 43}, + {0, 6, 20, 26, 35}, + {2, 6, 25, 27, 28}, + {6, 15, 16, 28, 48}, + {10, 13, 28, 39, 44}, + {6, 8, 42, 47, 49}, + {10, 18, 31, 34, 43}, + {5, 6, 7, 17, 44}, + {5, 32, 40, 42, 44}, + {5, 22, 30, 31, 45}, + {20, 31, 33, 37, 51}, + {5, 21, 24, 30, 47}, + {9, 12, 18, 24, 31}, + {14, 20, 26, 39, 42}, + {5, 18, 27, 37, 38}, + {3, 5, 6, 27, 31}, + {5, 18, 30, 33, 48}, + {14, 20, 29, 33, 50}, + {16, 27, 29, 32, 51}, + {3, 18, 22, 29, 37}, + {11, 16, 42, 46, 47}, + {12, 13, 26, 45, 50}, + {5, 21, 30, 43, 49}, + {3, 23, 24, 25, 42}, + {12, 19, 32, 39, 40}, + {0, 37, 39, 41, 47}, + {9, 15, 36, 41, 46}, + {5, 6, 20, 22, 33}, + {8, 14, 19, 34, 38}, + {6, 12, 27, 32, 46}, + {1, 19, 36, 45, 46}, + {8, 16, 19, 21, 50}, + {3, 8, 15, 16, 26}, + {20, 41, 44, 45, 46}, + {2, 3, 16, 26, 40}, + {7, 14, 16, 18, 33}, + {28, 32, 43, 45, 46}, + {12, 14, 19, 24, 27}, + {7, 8, 34, 37, 45}, + {27, 30, 34, 47, 50}, + {29, 37, 42, 44, 51}, + {8, 19, 26, 38, 45}, + {1, 2, 34, 35, 47}, + {1, 2, 15, 34, 46}, + {30, 31, 43, 47, 49}, + {1, 6, 16, 33, 42}, + {5, 18, 34, 48, 51}, + {5, 6, 34, 41, 44}, + {2, 19, 33, 36, 45}, + {20, 39, 40, 46, 47}, + {16, 24, 29, 33, 36}, + {17, 18, 28, 41, 51}, + {5, 6, 14, 20, 31}, + {15, 29, 38, 42, 43}, + {30, 31, 34, 36, 44}, + {13, 20, 26, 28, 35}, + {0, 3, 9, 15, 16}, + {0, 15, 26, 45, 46}, + {4, 20, 24, 27, 33}, + {25, 27, 28, 29, 42}, + {0, 8, 20, 25, 26}, + {6, 26, 34, 42, 47}, + {13, 15, 31, 41, 47}, + {6, 12, 19, 34, 39}, + {7, 20, 31, 43, 47}, + {3, 4, 15, 35, 41}, + {8, 14, 21, 24, 36}, + {2, 16, 20, 37, 46}, + {4, 27, 30, 37, 44}, + {2, 9, 15, 24, 45}, + {14, 17, 21, 30, 32}, + {15, 18, 30, 41, 48}, + {2, 8, 15, 30, 42}, + {20, 30, 38, 43, 44}, + {8, 14, 20, 27, 31}, + {6, 21, 32, 33, 43}, + {7, 14, 19, 20, 51}, + {3, 9, 11, 18, 42}, + {3, 4, 5, 9, 44}, + {0, 6, 39, 41, 48}, + {13, 15, 33, 39, 49}, + {2, 8, 22, 28, 50}, + {15, 18, 41, 49, 51}, + {6, 8, 21, 31, 42}, + {4, 7, 14, 46, 50}, + {11, 13, 15, 17, 39}, + {3, 16, 19, 46, 51}, + {7, 9, 13, 14, 26}, + {5, 18, 35, 37, 40}, + {17, 18, 31, 32, 50}, + {5, 13, 18, 42, 45}, + {6, 29, 31, 45, 47}, + {3, 9, 24, 27, 29}, + {6, 16, 20, 32, 43}, + {7, 22, 28, 32, 41}, + {14, 29, 30, 34, 40}, + {2, 28, 32, 39, 51}, + {0, 5, 22, 31, 51}, + {0, 15, 22, 34, 41}, + {2, 6, 12, 15, 43}, + {0, 20, 39, 42, 47}, + {1, 17, 29, 31, 43}, + {20, 30, 35, 36, 43}, + {8, 27, 34, 39, 48}, + {2, 5, 13, 37, 39}, + {2, 8, 34, 43, 44}, + {2, 23, 35, 41, 44}, + {4, 13, 18, 30, 50}, + {0, 14, 22, 27, 33}, + {1, 8, 17, 20, 46}, + {5, 16, 28, 40, 41}, + {3, 33, 36, 41, 42}, + {34, 41, 45, 47, 51}, + {16, 18, 32, 45, 47}, + {18, 26, 31, 46, 47}, + {15, 16, 36, 42, 50}, + {2, 4, 15, 32, 36}, + {1, 18, 31, 38, 50}, + {11, 18, 21, 44, 49}, + {24, 27, 34, 43, 47}, + {1, 32, 40, 43, 49}, + {3, 29, 40, 46, 48}, + {5, 18, 24, 28, 51}, + {3, 5, 32, 37, 45}, + {13, 26, 32, 35, 46}, + {23, 30, 35, 37, 43}, + {5, 14, 18, 36, 38}, + {13, 39, 41, 42, 49}, + {16, 29, 34, 37, 39}, + {1, 4, 5, 7, 33}, + {7, 27, 29, 40, 51}, + {1, 13, 27, 48, 51}, + {4, 11, 30, 39, 40}, + {2, 7, 11, 20, 31}, + {2, 28, 34, 35, 43}, + {9, 14, 19, 32, 37}, + {5, 8, 17, 29, 43}, + {7, 18, 22, 32, 44}, + {0, 14, 15, 39, 49}, + {1, 22, 32, 45, 49}, + {12, 23, 31, 44, 50}, + {3, 4, 10, 16, 46}, + {0, 6, 13, 15, 34}, + {5, 14, 33, 44, 47}, + {12, 16, 21, 34, 43}, + {16, 17, 24, 29, 40}, + {2, 12, 31, 33, 44}, + {1, 3, 16, 17, 45}, + {2, 37, 41, 46, 51}, + {6, 12, 14, 23, 40}, + {2, 4, 5, 14, 17}, + {0, 13, 42, 43, 47}, + {4, 8, 21, 35, 40}, + {4, 5, 17, 20, 34}, + {6, 8, 21, 22, 46}, + {5, 10, 16, 22, 29}, + {0, 3, 8, 21, 35}, + {2, 10, 17, 28, 34}, + {8, 16, 20, 22, 46}, + {27, 36, 38, 40, 47}, + {23, 34, 45, 47, 50}, + {14, 20, 28, 46, 47}, + {1, 3, 11, 22, 40}, + {4, 9, 26, 27, 39}, + {2, 10, 15, 24, 40}, + {1, 2, 15, 38, 39}, + {13, 14, 20, 32, 46}, + {0, 4, 33, 35, 39}, + {6, 20, 29, 33, 39}, + {3, 5, 27, 29, 43}, + {0, 17, 29, 43, 49}, + {3, 19, 25, 34, 47}, + {7, 31, 32, 42, 46}, + {15, 19, 27, 28, 51}, + {11, 16, 21, 36, 42}, + {2, 25, 26, 41, 44}, + {19, 25, 26, 28, 39}, + {1, 11, 18, 26, 40}, + {0, 15, 16, 26, 36}, + {8, 22, 28, 34, 44}, + {1, 6, 19, 39, 43}, + {11, 17, 22, 30, 42}, + {24, 31, 35, 40, 44}, + {0, 4, 12, 26, 37}, + {5, 10, 19, 45, 46}, + {0, 12, 22, 27, 40}, + {3, 12, 16, 28, 30}, + {3, 20, 34, 41, 46}, + {8, 11, 20, 31, 47}, + {6, 13, 20, 30, 46}, + {10, 13, 14, 28, 40}, + {19, 32, 35, 39, 51}, + {13, 16, 19, 33, 45}, + {12, 27, 28, 36, 40}, + {5, 27, 28, 33, 41}, + {20, 25, 39, 45, 46}, + {2, 34, 39, 41, 50}, + {3, 25, 27, 29, 37}, + {4, 31, 42, 44, 48}, + {8, 16, 38, 41, 47}, + {3, 4, 30, 32, 49}, + {28, 32, 40, 41, 44}, + {3, 4, 14, 24, 27}, + {0, 2, 4, 25, 43}, + {12, 15, 28, 42, 47}, + {1, 6, 13, 14, 41}, + {12, 17, 22, 30, 49}, + {6, 24, 32, 33, 39}, + {6, 20, 23, 27, 45}, + {29, 31, 36, 43, 44}, + {0, 3, 9, 13, 41}, + {20, 28, 30, 33, 48}, + {2, 7, 9, 14, 15}, + {7, 16, 26, 34, 47}, + {3, 6, 17, 20, 33}, + {15, 23, 31, 32, 44}, + {3, 13, 24, 29, 38}, + {1, 29, 35, 42, 50}, + {6, 7, 29, 30, 45}, + {4, 10, 30, 33, 48}, + {6, 7, 12, 23, 46}, + {26, 31, 35, 44, 45}, + {1, 5, 11, 15, 41}, + {1, 4, 14, 18, 19}, + {8, 26, 34, 45, 50}, + {0, 24, 28, 39, 42}, + {8, 9, 21, 26, 45}, + {2, 6, 7, 31, 32}, + {31, 38, 40, 44, 47}, + {21, 24, 26, 35, 39}, + {2, 6, 20, 21, 46}, + {1, 11, 33, 34, 46}, + {1, 15, 17, 40, 44}, + {19, 25, 26, 32, 34}, + {5, 34, 40, 47, 48}, + {8, 19, 33, 47, 50}, + {2, 7, 21, 47, 49}, + {8, 31, 34, 42, 46}, + {8, 35, 44, 47, 50}, + {1, 22, 40, 44, 46}, + {2, 11, 26, 39, 45}, + {9, 18, 32, 34, 45}, + {7, 12, 42, 46, 49}, + {4, 15, 30, 34, 42}, + {2, 24, 38, 41, 43}, + {1, 18, 22, 34, 44}, + {3, 19, 33, 41, 42}, + {2, 7, 11, 28, 39}, + {13, 17, 20, 40, 46}, + {13, 14, 18, 27, 37}, + {2, 18, 20, 42, 44}, + {0, 6, 13, 14, 42}, + {3, 16, 18, 30, 36}, + {10, 34, 38, 39, 47}, + {14, 21, 26, 36, 40}, + {1, 8, 27, 35, 49}, + {5, 6, 19, 38, 43}, + {1, 3, 18, 40, 41}, + {22, 32, 34, 37, 47}, + {5, 16, 29, 46, 49}, + {2, 12, 14, 18, 31}, + {1, 17, 21, 34, 35}, + {1, 4, 25, 27, 31}, + {1, 7, 14, 41, 49}, + {2, 20, 33, 34, 43}, + {4, 15, 29, 30, 45}, + {4, 14, 27, 29, 39}, + {0, 2, 27, 36, 41}, + {3, 14, 21, 40, 43}, + {2, 41, 42, 45, 51}, + {8, 9, 19, 32, 37}, + {6, 14, 21, 29, 47}, + {0, 4, 13, 34, 44}, + {25, 30, 33, 43, 50}, + {4, 5, 6, 27, 44}, + {20, 30, 40, 45, 46}, + {0, 2, 18, 31, 48}, + {7, 28, 29, 30, 41}, + {7, 11, 14, 20, 38}, + {4, 6, 8, 21, 39}, + {3, 5, 14, 29, 32}, + {13, 22, 26, 27, 45}, + {0, 20, 24, 46, 51}, + {4, 17, 34, 42, 46}, + {4, 20, 28, 30, 49}, + {14, 29, 32, 37, 45}, + {4, 8, 18, 21, 39}, + {1, 13, 24, 26, 38}, + {6, 27, 36, 40, 44}, + {0, 19, 21, 25, 32}, + {8, 19, 23, 34, 35}, + {8, 16, 24, 25, 42}, + {2, 4, 17, 45, 50}, + {15, 28, 39, 45, 48}, + {0, 1, 13, 24, 51}, + {0, 1, 2, 37, 41}, + {3, 10, 12, 20, 29}, + {21, 26, 32, 33, 39}, + {19, 27, 38, 43, 45}, + {9, 24, 30, 43, 47}, + {2, 8, 9, 31, 47}, + {5, 9, 16, 28, 44}, + {5, 10, 15, 34, 41}, + {3, 6, 21, 28, 41}, + {6, 16, 24, 42, 43}, + {7, 11, 16, 32, 42}, + {6, 11, 19, 38, 41}, + {8, 12, 17, 42, 43}, + {1, 5, 11, 21, 34}, + {0, 6, 21, 47, 48}, + {3, 7, 18, 25, 46}, + {16, 26, 28, 29, 33}, + {10, 12, 22, 29, 42}, + {1, 3, 21, 27, 31}, + {19, 32, 44, 47, 50}, + {8, 19, 33, 38, 46}, + {4, 7, 15, 29, 43}, + {2, 21, 37, 44, 47}, + {9, 11, 18, 20, 31}, + {20, 22, 33, 37, 51}, + {6, 31, 33, 36, 46}, + {1, 23, 27, 28, 51}, + {7, 11, 20, 30, 38}, + {4, 8, 15, 17, 49}, + {14, 26, 32, 45, 49}, + {7, 14, 25, 31, 33}, + {2, 16, 17, 18, 28}, + {5, 27, 29, 42, 43}, + {4, 17, 24, 26, 44}, + {2, 19, 20, 34, 46}, + {8, 15, 41, 43, 45}, + {1, 2, 7, 15, 25}, + {1, 26, 27, 48, 50}, + {28, 29, 42, 46, 48}, + {0, 1, 10, 20, 46}, + {2, 25, 27, 29, 42}, + {7, 8, 13, 39, 45}, + {3, 19, 23, 32, 33}, + {1, 4, 32, 43, 51}, + {7, 20, 26, 32, 41}, + {2, 17, 23, 28, 47}, + {6, 30, 31, 32, 41}, + {3, 4, 5, 20, 43}, + {0, 19, 32, 36, 51}, + {8, 13, 23, 29, 47}, + {10, 15, 16, 17, 28}, + {4, 17, 21, 42, 46}, + {2, 4, 15, 18, 36}, + {15, 25, 32, 34, 47}, + {1, 4, 11, 14, 49}, + {2, 8, 37, 42, 47}, + {1, 8, 10, 12, 40}, + {10, 20, 27, 33, 38}, + {16, 18, 22, 31, 36}, + {5, 10, 18, 26, 32}, + {4, 8, 30, 32, 41}, + {8, 17, 32, 40, 45}, + {17, 27, 34, 38, 40}, + {0, 11, 18, 19, 44}, + {8, 13, 28, 41, 43}, + {16, 17, 35, 43, 47}, + {0, 2, 15, 20, 44}, + {4, 10, 26, 27, 40}, + {0, 28, 36, 39, 42}, + {1, 6, 9, 14, 30}, + {13, 18, 30, 31, 34}, + {6, 8, 11, 20, 21}, + {2, 5, 19, 26, 31}, + {2, 10, 32, 45, 47}, + {5, 15, 24, 30, 41}, + {4, 30, 33, 41, 49}, + {1, 6, 22, 23, 27}, + {5, 8, 15, 25, 28}, + {8, 20, 35, 47, 50}, + {14, 26, 28, 31, 40}, + {2, 7, 27, 41, 45}, + {2, 15, 21, 25, 42}, + {6, 19, 23, 33, 41}, + {2, 13, 21, 43, 47}, + {3, 5, 16, 24, 46}, + {17, 24, 26, 43, 51}, + {6, 8, 9, 18, 45}, + {2, 7, 27, 40, 47}, + {15, 20, 25, 33, 43}, + {1, 14, 23, 28, 45}, + {1, 14, 25, 35, 36}, + {3, 11, 13, 33, 42}, + {0, 33, 38, 39, 42}, + {1, 12, 18, 30, 44}, + {21, 23, 33, 38, 46}, + {4, 5, 8, 34, 41}, + {21, 32, 41, 47, 51}, + {0, 1, 3, 27, 46}, + {18, 21, 28, 31, 48}, + {6, 35, 38, 42, 45}, + {0, 3, 4, 13, 33}, + {3, 4, 11, 42, 47}, + {6, 14, 19, 36, 43}, + {6, 32, 37, 39, 41}, + {3, 4, 13, 15, 42}, + {1, 19, 32, 34, 36}, + {4, 14, 17, 32, 33}, + {2, 4, 15, 16, 49}, + {5, 13, 25, 30, 44}, + {5, 11, 13, 22, 26}, + {1, 3, 4, 24, 29}, + {9, 14, 24, 33, 46}, + {5, 20, 41, 42, 46}, + {7, 21, 28, 30, 33}, + {1, 6, 9, 20, 45}, + {10, 12, 18, 41, 44}, + {1, 11, 14, 26, 47}, + {1, 11, 22, 27, 45}, + {17, 18, 22, 25, 44}, + {2, 3, 13, 18, 44}, + {0, 9, 13, 27, 47}, + {5, 16, 23, 28, 42}, + {3, 5, 7, 9, 29}, + {4, 17, 24, 33, 51}, + {5, 11, 18, 47, 48}, + {2, 4, 20, 22, 46}, + {15, 41, 42, 46, 51}, + {15, 24, 32, 35, 41}, + {25, 32, 41, 45, 46}, + {0, 32, 38, 39, 41}, + {2, 9, 28, 32, 43}, + {3, 20, 33, 37, 49}, + {7, 17, 25, 30, 31}, + {0, 4, 26, 34, 48}, + {8, 15, 30, 47, 51}, + {8, 10, 11, 38, 47}, + {0, 11, 16, 21, 26}, + {19, 21, 39, 47, 48}, + {5, 16, 23, 41, 44}, + {4, 25, 27, 31, 40}, + {3, 18, 19, 21, 32}, + {11, 19, 20, 27, 45}, + {0, 2, 5, 41, 49}, + {1, 6, 20, 23, 46}, + {0, 2, 21, 30, 41}, + {4, 6, 11, 38, 45}, + {2, 16, 22, 41, 47}, + {11, 31, 39, 44, 46}, + {4, 12, 13, 21, 30}, + {3, 4, 7, 17, 21}, + {6, 32, 36, 39, 47}, + {5, 9, 18, 34, 41}, + {12, 15, 41, 42, 43}, + {8, 18, 28, 31, 42}, + {7, 28, 46, 48, 51}, + {15, 20, 29, 32, 45}, + {12, 16, 26, 35, 42}, + {6, 9, 14, 19, 43}, + {0, 12, 21, 36, 39}, + {18, 21, 26, 40, 47}, + {26, 34, 42, 43, 47}, + {0, 3, 4, 7, 13}, + {2, 5, 29, 31, 49}, + {0, 25, 39, 41, 45}, + {19, 20, 25, 42, 46}, + {10, 16, 29, 30, 50}, + {13, 23, 26, 32, 42}, + {15, 17, 29, 30, 39}, + {30, 36, 43, 44, 45}, + {15, 28, 29, 35, 43}, + {13, 19, 27, 30, 39}, + {1, 5, 32, 37, 45}, + {6, 16, 24, 45, 47}, + {18, 28, 32, 41, 43}, + {5, 14, 27, 28, 47}, + {0, 4, 7, 21, 46}, + {14, 16, 29, 37, 38}, + {10, 15, 24, 26, 41}, + {14, 16, 19, 26, 27}, + {0, 13, 31, 37, 38}, + {1, 5, 28, 34, 47}, + {1, 28, 30, 37, 43}, + {0, 28, 39, 42, 48}, + {26, 33, 34, 39, 45}, + {3, 4, 6, 16, 46}, + {0, 5, 13, 17, 51}, + {1, 3, 16, 39, 50}, + {10, 30, 38, 43, 47}, + {8, 9, 27, 46, 47}, + {19, 22, 32, 33, 38}, + {16, 17, 22, 29, 50}, + {7, 8, 13, 30, 46}, + {0, 5, 10, 40, 44}, + {8, 20, 25, 32, 34}, + {3, 4, 19, 20, 33}, + {17, 26, 27, 31, 43}, + {0, 5, 7, 26, 43}, + {17, 29, 31, 42, 49}, + {6, 18, 32, 40, 49}, + {25, 28, 30, 41, 50}, + {2, 14, 25, 28, 31}, + {11, 19, 26, 30, 45}, + {5, 17, 33, 34, 43}, + {13, 19, 20, 43, 46}, + {3, 23, 29, 33, 37}, + {3, 11, 29, 36, 40}, + {4, 14, 19, 23, 27}, + {4, 11, 25, 30, 49}, + {2, 11, 14, 28, 43}, + {4, 6, 17, 26, 41}, + {3, 13, 21, 41, 47}, + {15, 30, 35, 37, 41}, + {4, 30, 31, 42, 49}, + {3, 32, 42, 43, 44}, + {3, 15, 21, 30, 34}, + {14, 27, 35, 43, 46}, + {3, 7, 8, 15, 28}, + {2, 8, 19, 37, 41}, + {4, 27, 28, 41, 45}, + {1, 6, 24, 27, 34}, + {1, 2, 20, 31, 44}, + {0, 7, 17, 30, 41}, + {6, 12, 13, 34, 47}, + {17, 21, 33, 35, 46}, + {8, 21, 41, 43, 50}, + {10, 16, 34, 41, 42}, + {1, 8, 29, 40, 49}, + {4, 8, 15, 30, 40}, + {3, 5, 7, 29, 38}, + {14, 17, 22, 40, 47}, + {12, 14, 40, 41, 48}, + {0, 13, 27, 29, 30}, + {2, 4, 12, 43, 45}, + {7, 20, 24, 27, 48}, + {5, 8, 13, 18, 24}, + {17, 21, 25, 30, 48}, + {7, 42, 44, 46, 51}, + {0, 21, 33, 37, 47}, + {7, 12, 16, 46, 50}, + {3, 14, 27, 43, 44}, + {3, 10, 37, 41, 42}, + {27, 31, 44, 45, 50}, + {1, 12, 14, 26, 33}, + {5, 22, 26, 38, 44}, + {15, 16, 21, 41, 48}, + {5, 15, 32, 41, 42}, + {2, 8, 32, 47, 48}, + {31, 33, 46, 48, 51}, + {3, 5, 8, 15, 42}, + {4, 13, 14, 30, 45}, + {8, 17, 37, 43, 48}, + {13, 24, 27, 40, 45}, + {2, 5, 15, 24, 48}, + {16, 21, 25, 29, 35}, + {10, 31, 33, 46, 51}, + {3, 12, 15, 41, 48}, + {8, 18, 26, 28, 31}, + {21, 33, 34, 36, 50}, + {7, 8, 11, 15, 47}, + {6, 18, 20, 33, 41}, + {4, 7, 17, 41, 44}, + {6, 12, 14, 32, 39}, + {17, 29, 30, 46, 50}, + {5, 24, 34, 44, 49}, + {8, 23, 34, 39, 48}, + {23, 30, 32, 42, 43}, + {1, 2, 7, 21, 40}, + {0, 6, 13, 21, 25}, + {16, 22, 42, 43, 46}, + {0, 15, 17, 18, 30}, + {2, 6, 8, 29, 47}, + {3, 5, 7, 41, 44}, + {7, 19, 24, 33, 44}, + {5, 18, 22, 30, 41}, + {5, 6, 18, 41, 49}, + {8, 10, 13, 29, 47}, + {15, 20, 28, 32, 34}, + {7, 33, 36, 45, 50}, + {4, 20, 33, 38, 49}, + {4, 8, 12, 18, 30}, + {8, 12, 31, 34, 40}, + {21, 27, 30, 43, 45}, + {15, 28, 30, 35, 47}, + {2, 41, 42, 43, 51}, + {0, 15, 23, 39, 48}, + {4, 12, 20, 29, 42}, + {28, 29, 40, 42, 47}, + {6, 10, 29, 34, 45}, + {0, 20, 31, 36, 46}, + {4, 16, 29, 38, 50}, + {4, 20, 29, 39, 46}, + {5, 24, 26, 35, 39}, + {0, 6, 15, 26, 30}, + {0, 9, 14, 24, 26}, + {4, 29, 36, 41, 42}, + {13, 28, 34, 43, 47}, + {16, 21, 22, 27, 42}, + {8, 15, 42, 43, 47}, + {9, 19, 28, 45, 50}, + {15, 19, 28, 38, 43}, + {8, 25, 30, 43, 45}, + {26, 39, 43, 45, 49}, + {2, 15, 16, 26, 31}, + {4, 7, 12, 20, 48}, + {1, 39, 40, 42, 48}, + {11, 13, 16, 34, 47}, + {3, 18, 41, 42, 45}, + {1, 6, 10, 32, 41}, + {8, 14, 16, 37, 47}, + {11, 26, 29, 31, 39}, + {3, 9, 15, 27, 41}, + {2, 4, 6, 11, 19}, + {1, 6, 8, 43, 45}, + {16, 28, 36, 41, 43}, + {13, 15, 28, 33, 35}, + {17, 21, 22, 40, 43}, + {18, 31, 35, 37, 40}, + {2, 6, 26, 33, 45}, + {4, 8, 23, 29, 47}, + {2, 16, 18, 28, 46}, + {0, 15, 32, 34, 47}, + {20, 22, 32, 33, 51}, + {12, 20, 23, 32, 46}, + {1, 7, 9, 14, 25}, + {0, 12, 17, 19, 45}, + {1, 6, 16, 27, 30}, + {8, 15, 28, 36, 43}, + {3, 23, 26, 42, 43}, + {4, 16, 19, 28, 43}, + {13, 18, 22, 26, 29}, + {2, 9, 15, 26, 33}, + {6, 7, 32, 43, 51}, + {0, 9, 19, 34, 39}, + {2, 9, 11, 28, 45}, + {0, 26, 28, 36, 40}, + {15, 21, 29, 34, 36}, + {7, 25, 35, 46, 50}, + {5, 6, 12, 24, 45}, + {0, 1, 6, 19, 38}, + {10, 19, 34, 41, 45}, + {4, 14, 21, 25, 43}, + {4, 6, 11, 28, 45}, + {8, 10, 13, 21, 40}, + {4, 17, 28, 33, 38}, + {16, 17, 18, 25, 43}, + {0, 1, 27, 33, 50}, + {6, 16, 40, 41, 45}, + {1, 22, 24, 27, 44}, + {12, 15, 28, 34, 46}, + {2, 12, 33, 37, 46}, + {15, 21, 26, 34, 46}, + {4, 6, 32, 39, 51}, + {8, 29, 37, 40, 42}, + {1, 12, 33, 41, 46}, + {7, 27, 39, 43, 46}, + {2, 6, 12, 32, 43}, + {9, 13, 25, 33, 46}, + {11, 31, 33, 38, 46}, + {6, 29, 30, 45, 46}, + {1, 20, 27, 36, 38}, + {1, 6, 38, 40, 42}, + {2, 7, 28, 45, 51}, + {7, 12, 20, 27, 41}, + {1, 11, 17, 21, 27}, + {14, 19, 24, 27, 38}, + {14, 21, 30, 33, 47}, + {3, 7, 13, 42, 50}, + {14, 29, 33, 39, 46}, + {14, 22, 33, 46, 49}, + {3, 8, 15, 30, 43}, + {0, 3, 6, 39, 43}, + {2, 8, 13, 25, 28}, + {4, 8, 17, 35, 45}, + {5, 22, 24, 28, 41}, + {31, 34, 37, 47, 51}, + {0, 6, 14, 27, 35}, + {5, 18, 21, 46, 49}, + {3, 4, 8, 18, 30}, + {0, 2, 13, 14, 44}, + {1, 5, 18, 21, 42}, + {3, 5, 26, 39, 51}, + {6, 7, 20, 26, 38}, + {4, 8, 26, 43, 51}, + {13, 16, 34, 37, 39}, + {3, 21, 34, 39, 40}, + {0, 19, 26, 41, 48}, + {2, 15, 22, 46, 50}, + {2, 6, 24, 41, 47}, + {3, 13, 15, 34, 47}, + {16, 24, 31, 34, 47}, + {0, 5, 6, 27, 40}, + {0, 13, 23, 28, 40}, + {8, 14, 29, 38, 42}, + {6, 19, 21, 36, 39}, + {6, 10, 18, 33, 45}, + {10, 18, 30, 31, 51}, + {4, 10, 13, 15, 30}, + {16, 19, 23, 45, 48}, + {1, 18, 22, 29, 40}, + {16, 18, 19, 45, 46}, + {0, 8, 39, 40, 46}, + {15, 21, 26, 39, 44}, + {0, 10, 13, 16, 51}, + {33, 44, 45, 46, 50}, + {15, 17, 27, 30, 51}, + {17, 21, 26, 34, 49}, + {0, 4, 8, 15, 30}, + {16, 23, 29, 43, 44}, + {5, 11, 35, 36, 44}, + {1, 14, 21, 48, 51}, + {8, 13, 15, 21, 33}, + {0, 5, 14, 31, 49}, + {10, 16, 24, 34, 47}, + {3, 11, 20, 23, 29}, + {18, 23, 24, 40, 44}, + {1, 17, 22, 43, 44}, + {1, 4, 14, 15, 51}, + {0, 3, 29, 44, 51}, + {21, 34, 35, 41, 44}, + {0, 5, 14, 30, 40}, + {19, 26, 32, 37, 41}, + {9, 13, 15, 16, 28}, + {0, 2, 4, 5, 18}, + {8, 24, 25, 34, 42}, + {3, 17, 18, 34, 44}, + {21, 28, 29, 34, 36}, + {7, 15, 34, 37, 47}, + {5, 25, 29, 42, 43}, + {5, 10, 18, 40, 50}, + {9, 14, 15, 37, 41}, + {5, 7, 30, 35, 44}, + {0, 2, 17, 28, 48}, + {6, 15, 16, 22, 42}, + {6, 18, 31, 41, 48}, + {19, 21, 22, 26, 47}, + {4, 5, 12, 17, 46}, + {2, 13, 14, 18, 31}, + {0, 1, 3, 21, 26}, + {3, 15, 24, 29, 35}, + {4, 19, 24, 27, 45}, + {13, 17, 28, 36, 39}, + {3, 11, 16, 18, 20}, + {8, 28, 40, 41, 50}, + {4, 25, 32, 35, 43}, + {0, 14, 24, 40, 49}, + {0, 19, 26, 38, 40}, + {0, 6, 21, 37, 39}, + {8, 13, 19, 27, 34}, + {2, 24, 35, 41, 47}, + {10, 13, 18, 43, 44}, + {7, 14, 25, 32, 33}, + {11, 28, 33, 41, 47}, + {16, 18, 21, 30, 42}, + {5, 12, 34, 46, 47}, + {11, 19, 28, 32, 35}, + {0, 16, 31, 34, 42}, + {1, 14, 36, 38, 43}, + {6, 15, 28, 40, 46}, + {4, 18, 30, 35, 45}, + {17, 20, 21, 30, 40}, + {18, 29, 42, 47, 51}, + {7, 8, 16, 20, 37}, + {4, 13, 20, 26, 41}, + {2, 12, 28, 34, 35}, + {1, 14, 17, 20, 25}, + {4, 7, 15, 24, 28}, + {12, 28, 32, 41, 44}, + {7, 26, 34, 46, 51}, + {4, 5, 14, 31, 45}, + {4, 7, 13, 32, 39}, + {0, 18, 33, 44, 48}, + {11, 20, 28, 33, 39}, + {2, 16, 21, 34, 37}, + {7, 19, 24, 32, 41}, + {7, 12, 20, 47, 48}, + {1, 27, 37, 46, 49}, + {0, 13, 15, 36, 43}, + {17, 33, 40, 43, 45}, + {3, 6, 9, 14, 40}, + {10, 13, 15, 17, 43}, + {1, 17, 40, 48, 50}, + {18, 20, 33, 35, 36}, + {5, 11, 20, 29, 31}, + {1, 8, 16, 34, 39}, + {8, 18, 20, 33, 50}, + {7, 11, 19, 46, 47}, + {1, 26, 30, 43, 47}, + {7, 9, 24, 34, 46}, + {4, 13, 32, 38, 43}, + {2, 28, 36, 42, 46}, + {1, 3, 13, 14, 34}, + {7, 13, 42, 46, 47}, + {1, 14, 19, 41, 42}, + {11, 12, 18, 21, 44}, + {1, 8, 15, 28, 36}, + {1, 5, 16, 37, 42}, + {0, 18, 24, 26, 33}, + {4, 7, 17, 22, 45}, + {8, 27, 36, 40, 41}, + {0, 27, 29, 39, 41}, + {11, 16, 17, 22, 42}, + {12, 15, 28, 30, 47}, + {4, 15, 20, 38, 46}, + {5, 15, 40, 44, 51}, + {1, 3, 5, 33, 44}, + {5, 11, 13, 30, 44}, + {8, 12, 32, 35, 47}, + {2, 7, 11, 15, 17}, + {0, 14, 19, 27, 36}, + {1, 4, 21, 33, 47}, + {4, 14, 17, 41, 44}, + {3, 8, 21, 30, 36}, + {3, 29, 36, 41, 50}, + {0, 4, 7, 22, 26}, + {6, 27, 31, 32, 49}, + {6, 8, 12, 18, 47}, + {4, 6, 14, 32, 42}, + {2, 18, 19, 41, 42}, + {15, 34, 41, 46, 51}, + {8, 16, 21, 32, 41}, + {23, 29, 40, 42, 51}, + {11, 20, 33, 39, 51}, + {0, 22, 26, 33, 49}, + {16, 17, 22, 30, 36}, + {7, 33, 34, 39, 42}, + {2, 17, 25, 30, 44}, + {4, 16, 26, 42, 44}, + {10, 17, 37, 42, 43}, + {4, 11, 13, 17, 36}, + {1, 19, 35, 40, 46}, + {10, 14, 27, 30, 44}, + {15, 25, 28, 32, 33}, + {4, 7, 15, 20, 21}, + {6, 13, 20, 32, 50}, + {6, 13, 27, 32, 47}, + {10, 13, 26, 43, 45}, + {14, 28, 30, 31, 43}, + {0, 3, 8, 26, 33}, + {1, 8, 11, 41, 47}, + {0, 13, 23, 33, 43}, + {2, 4, 6, 27, 43}, + {2, 3, 6, 45, 51}, + {8, 21, 23, 39, 51}, + {8, 16, 40, 47, 51}, + {0, 3, 8, 20, 33}, + {1, 30, 32, 40, 47}, + {1, 6, 7, 27, 37}, + {3, 7, 20, 37, 38}, + {0, 16, 19, 30, 43}, + {21, 26, 37, 47, 51}, + {7, 8, 20, 27, 32}, + {6, 7, 19, 43, 48}, + {6, 21, 29, 32, 38}, + {2, 37, 39, 41, 42}, + {3, 30, 40, 42, 48}, + {1, 19, 32, 36, 38}, + {10, 13, 16, 26, 43}, + {3, 27, 29, 39, 43}, + {0, 11, 28, 32, 45}, + {14, 22, 28, 34, 47}, + {5, 13, 32, 39, 49}, + {1, 2, 5, 22, 27}, + {0, 9, 28, 39, 43}, + {1, 9, 12, 26, 39}, + {1, 7, 14, 29, 31}, + {7, 11, 18, 38, 46}, + {6, 10, 35, 39, 45}, + {0, 6, 13, 20, 48}, + {6, 7, 17, 21, 45}, + {21, 24, 29, 34, 41}, + {3, 5, 24, 31, 34}, + {1, 2, 27, 44, 48}, + {0, 3, 15, 16, 21}, + {2, 14, 15, 30, 38}, + {13, 18, 22, 31, 49}, + {16, 19, 27, 37, 45}, + {10, 19, 21, 29, 47}, + {9, 14, 39, 40, 50}, + {1, 2, 20, 35, 40}, + {7, 14, 18, 20, 43}, + {10, 17, 20, 25, 30}, + {7, 10, 20, 37, 47}, + {19, 24, 32, 43, 47}, + {8, 27, 42, 47, 51}, + {21, 30, 34, 36, 50}, + {1, 6, 9, 39, 40}, + {7, 26, 35, 46, 51}, + {1, 34, 35, 40, 41}, + {0, 6, 13, 24, 25}, + {3, 5, 18, 25, 41}, + {7, 16, 19, 31, 42}, + {6, 30, 45, 47, 51}, + {26, 32, 33, 34, 47}, + {6, 18, 29, 41, 44}, + {6, 16, 29, 36, 39}, + {3, 15, 25, 33, 41}, + {1, 6, 13, 35, 40}, + {0, 25, 32, 45, 49}, + {0, 8, 15, 41, 51}, + {6, 7, 18, 46, 48}, + {1, 4, 10, 30, 37}, + {2, 11, 31, 33, 44}, + {2, 3, 26, 29, 44}, + {0, 7, 20, 22, 24}, + {6, 18, 27, 32, 46}, + {21, 26, 32, 40, 47}, + {3, 15, 16, 26, 46}, + {6, 18, 21, 40, 44}, + {3, 13, 20, 39, 40}, + {13, 19, 25, 41, 45}, + {0, 12, 13, 15, 27}, + {1, 12, 22, 33, 46}, + {12, 21, 34, 45, 48}, + {20, 26, 30, 39, 40}, + {1, 15, 22, 27, 39}, + {22, 25, 29, 42, 50}, + {2, 4, 41, 48, 51}, + {4, 20, 29, 40, 42}, + {7, 16, 26, 29, 49}, + {1, 22, 23, 27, 33}, + {0, 23, 34, 41, 47}, + {0, 10, 13, 22, 41}, + {2, 6, 32, 40, 44}, + {7, 37, 38, 39, 46}, + {3, 13, 16, 34, 45}, + {6, 8, 18, 24, 32}, + {6, 8, 9, 13, 19}, + {5, 13, 27, 39, 41}, + {5, 6, 28, 35, 44}, + {15, 28, 30, 37, 49}, + {8, 15, 18, 32, 45}, + {7, 8, 15, 45, 46}, + {3, 7, 28, 37, 41}, + {2, 13, 15, 20, 23}, + {1, 16, 21, 29, 30}, + {0, 3, 4, 5, 18}, + {1, 13, 24, 26, 30}, + {4, 23, 30, 39, 42}, + {11, 14, 25, 27, 41}, + {1, 11, 15, 34, 41}, + {7, 12, 27, 40, 45}, + {10, 20, 24, 32, 46}, + {1, 27, 29, 43, 46}, + {1, 9, 21, 26, 34}, + {12, 14, 20, 33, 34}, + {4, 20, 39, 43, 47}, + {0, 6, 7, 30, 39}, + {3, 16, 20, 22, 43}, + {4, 20, 25, 41, 46}, + {1, 2, 3, 23, 28}, + {10, 12, 20, 30, 43}, + {2, 13, 28, 29, 30}, + {6, 13, 28, 32, 48}, + {18, 25, 30, 43, 45}, + {2, 3, 9, 16, 21}, + {20, 46, 47, 49, 50}, + {0, 1, 29, 30, 40}, + {5, 15, 31, 40, 50}, + {13, 15, 16, 21, 42}, + {4, 15, 16, 41, 46}, + {3, 4, 5, 31, 34}, + {7, 9, 13, 15, 28}, + {5, 10, 14, 44, 46}, + {3, 16, 24, 25, 32}, + {4, 16, 23, 29, 34}, + {6, 10, 20, 24, 46}, + {0, 17, 39, 49, 51}, + {7, 31, 33, 34, 51}, + {2, 13, 30, 43, 49}, + {18, 19, 20, 29, 42}, + {6, 14, 18, 27, 43}, + {7, 8, 28, 29, 47}, + {5, 33, 35, 41, 44}, + {16, 29, 31, 33, 35}, + {3, 15, 19, 22, 32}, + {1, 4, 20, 36, 46}, + {10, 15, 29, 35, 42}, + {0, 14, 27, 42, 47}, + {10, 15, 25, 30, 41}, + {8, 18, 24, 31, 46}, + {3, 19, 27, 32, 39}, + {8, 14, 21, 23, 35}, + {0, 5, 10, 18, 30}, + {6, 9, 16, 37, 42}, + {10, 18, 20, 33, 38}, + {2, 30, 41, 47, 51}, + {6, 33, 40, 46, 48}, + {28, 32, 40, 43, 45}, + {5, 14, 32, 38, 40}, + {3, 15, 42, 43, 47}, + {4, 22, 23, 39, 43}, + {0, 13, 16, 47, 48}, + {5, 10, 32, 35, 45}, + {13, 17, 19, 29, 42}, + {0, 12, 31, 43, 44}, + {6, 19, 29, 31, 47}, + {1, 2, 8, 22, 27}, + {1, 25, 28, 40, 45}, + {6, 15, 17, 29, 42}, + {10, 22, 31, 44, 50}, + {7, 18, 31, 39, 50}, + {1, 13, 27, 48, 49}, + {1, 4, 18, 31, 47}, + {0, 2, 13, 45, 51}, + {6, 11, 30, 43, 49}, + {5, 29, 34, 44, 51}, + {2, 6, 14, 33, 41}, + {2, 4, 12, 30, 35}, + {2, 7, 16, 30, 42}, + {8, 21, 29, 40, 43}, + {1, 15, 36, 40, 50}, + {15, 23, 31, 33, 41}, + {4, 10, 32, 33, 45}, + {21, 24, 34, 43, 46}, + {12, 15, 22, 33, 41}, + {15, 27, 34, 41, 51}, + {0, 19, 28, 39, 42}, + {2, 7, 11, 30, 46}, + {13, 26, 29, 40, 49}, + {3, 7, 29, 48, 51}, + {20, 22, 28, 33, 50}, + {14, 17, 26, 30, 38}, + {13, 25, 26, 28, 50}, + {1, 7, 17, 38, 40}, + {4, 14, 30, 32, 34}, + {0, 17, 31, 39, 42}, + {5, 29, 35, 40, 42}, + {7, 21, 22, 26, 33}, + {5, 18, 37, 38, 43}, + {13, 16, 18, 19, 45}, + {18, 24, 31, 41, 45}, + {1, 4, 14, 19, 44}, + {3, 16, 20, 36, 50}, + {0, 1, 14, 25, 45}, + {1, 3, 12, 42, 50}, + {28, 41, 44, 47, 51}, + {0, 2, 11, 21, 26}, + {18, 31, 32, 41, 43}, + {0, 8, 21, 28, 33}, + {1, 13, 14, 38, 42}, + {19, 23, 35, 42, 45}, + {6, 15, 31, 32, 37}, + {0, 8, 21, 23, 30}, + {18, 20, 25, 37, 44}, + {6, 34, 36, 45, 51}, + {5, 30, 31, 42, 48}, + {5, 10, 20, 33, 48}, + {6, 8, 16, 22, 47}, + {18, 21, 31, 32, 50}, + {15, 16, 28, 36, 38}, + {1, 13, 40, 43, 47}, + {17, 31, 35, 43, 45}, + {1, 10, 25, 27, 34}, + {19, 23, 28, 34, 41}, + {1, 3, 19, 30, 45}, + {0, 7, 26, 41, 45}, + {10, 14, 32, 41, 45}, + {0, 2, 19, 27, 41}, + {0, 20, 21, 22, 39}, + {4, 19, 31, 34, 45}, + {3, 14, 16, 46, 48}, + {20, 22, 26, 29, 33}, + {7, 12, 22, 33, 45}, + {19, 34, 41, 44, 45}, + {4, 11, 17, 32, 41}, + {18, 37, 39, 40, 44}, + {3, 16, 30, 36, 38}, + {1, 4, 27, 37, 49}, + {32, 37, 44, 45, 49}, + {5, 13, 28, 31, 51}, + {4, 16, 23, 28, 43}, + {7, 17, 20, 36, 48}, + {3, 16, 22, 24, 40}, + {7, 20, 23, 43, 48}, + {2, 5, 6, 13, 44}, + {7, 15, 29, 36, 41}, + {18, 32, 35, 38, 45}, + {11, 16, 21, 32, 45}, + {3, 21, 27, 29, 49}, + {21, 26, 28, 29, 42}, + {3, 4, 20, 30, 37}, + {0, 5, 6, 14, 44}, + {5, 10, 11, 13, 44}, + {2, 23, 28, 31, 39}, + {1, 2, 12, 39, 40}, + {2, 21, 23, 24, 47}, + {0, 4, 9, 28, 30}, + {4, 7, 11, 43, 45}, + {4, 6, 21, 46, 47}, + {0, 8, 13, 17, 24}, + {0, 9, 21, 34, 42}, + {7, 8, 26, 34, 36}, + {22, 28, 30, 33, 46}, + {14, 16, 28, 42, 44}, + {6, 8, 31, 32, 50}, + {1, 31, 43, 44, 50}, + {14, 17, 20, 32, 40}, + {5, 16, 44, 45, 49}, + {4, 6, 18, 32, 33}, + {2, 9, 15, 18, 23}, + {20, 25, 26, 32, 46}, + {3, 10, 19, 28, 29}, + {6, 27, 32, 46, 49}, + {16, 20, 33, 35, 50}, + {7, 11, 12, 28, 33}, + {2, 13, 33, 40, 41}, + {8, 12, 20, 21, 42}, + {1, 5, 18, 39, 41}, + {9, 20, 21, 23, 47}, + {0, 1, 13, 21, 45}, + {5, 12, 18, 22, 39}, + {13, 17, 35, 39, 50}, + {4, 26, 39, 40, 45}, + {1, 11, 22, 27, 29}, + {4, 32, 43, 46, 51}, + {4, 30, 33, 45, 48}, + {15, 17, 28, 32, 48}, + {1, 14, 19, 20, 34}, + {12, 30, 31, 32, 45}, + {1, 20, 23, 27, 29}, + {13, 18, 22, 27, 39}, + {13, 16, 42, 44, 51}, + {4, 17, 26, 48, 49}, + {6, 16, 18, 28, 41}, + {14, 31, 35, 36, 40}, + {6, 33, 46, 49, 51}, + {17, 18, 26, 30, 34}, + {1, 3, 11, 14, 38}, + {6, 19, 43, 47, 51}, + {7, 23, 29, 31, 46}, + {15, 29, 30, 43, 51}, + {0, 6, 8, 9, 26}, + {15, 16, 25, 27, 41}, + {3, 8, 21, 39, 50}, + {8, 31, 32, 40, 44}, + {2, 13, 20, 32, 46}, + {0, 16, 21, 24, 39}, + {3, 29, 34, 38, 43}, + {4, 17, 38, 48, 49}, + {0, 28, 38, 41, 49}, + {19, 27, 30, 35, 43}, + {6, 7, 20, 26, 29}, + {30, 31, 33, 36, 46}, + {3, 6, 18, 32, 46}, + {7, 13, 20, 28, 48}, + {2, 12, 21, 28, 50}, + {2, 15, 32, 43, 51}, + {4, 6, 16, 20, 45}, + {3, 17, 30, 45, 47}, + {19, 30, 32, 44, 50}, + {2, 4, 14, 38, 40}, + {12, 30, 32, 35, 43}, + {8, 17, 19, 33, 46}, + {5, 8, 20, 34, 48}, + {9, 15, 27, 28, 51}, + {1, 9, 11, 19, 32}, + {7, 13, 25, 26, 27}, + {2, 11, 26, 38, 39}, + {5, 20, 32, 42, 44}, + {3, 13, 32, 39, 40}, + {2, 4, 11, 14, 17}, + {13, 14, 17, 40, 45}, + {0, 13, 14, 19, 36}, + {6, 9, 26, 34, 45}, + {1, 18, 29, 44, 50}, + {1, 31, 33, 39, 40}, + {2, 3, 19, 35, 45}, + {0, 5, 24, 26, 41}, + {8, 14, 40, 43, 50}, + {5, 22, 24, 31, 40}, + {8, 25, 27, 30, 40}, + {18, 28, 40, 44, 51}, + {3, 10, 15, 20, 33}, + {7, 12, 16, 18, 42}, + {19, 20, 27, 28, 32}, + {1, 16, 27, 30, 31}, + {15, 25, 39, 41, 45}, + {7, 11, 36, 42, 46}, + {2, 15, 17, 23, 25}, + {7, 15, 27, 29, 42}, + {0, 14, 26, 37, 49}, + {3, 5, 8, 17, 18}, + {9, 19, 23, 44, 45}, + {1, 4, 30, 47, 48}, + {5, 13, 19, 22, 39}, + {6, 15, 26, 32, 35}, + {6, 13, 15, 45, 49}, + {7, 20, 23, 26, 35}, + {1, 32, 34, 37, 40}, + {12, 28, 33, 46, 48}, + {10, 26, 32, 35, 39}, + {2, 3, 13, 28, 43}, + {2, 17, 43, 45, 46}, + {4, 8, 13, 18, 30}, + {4, 18, 29, 42, 46}, + {8, 9, 17, 21, 39}, + {8, 10, 22, 42, 47}, + {2, 8, 13, 19, 28}, + {7, 39, 42, 46, 49}, + {6, 8, 13, 14, 34}, + {5, 6, 9, 16, 29}, + {1, 13, 22, 39, 47}, + {2, 8, 25, 30, 43}, + {3, 4, 8, 12, 43}, + {5, 16, 27, 44, 49}, + {1, 4, 7, 27, 28}, + {14, 18, 29, 44, 46}, + {16, 29, 32, 37, 39}, + {5, 8, 17, 19, 31}, + {8, 14, 18, 23, 27}, + {7, 8, 27, 33, 48}, + {0, 28, 38, 39, 42}, + {3, 14, 41, 42, 51}, + {3, 15, 29, 38, 49}, + {14, 18, 27, 41, 48}, + {15, 25, 41, 43, 47}, + {16, 19, 22, 31, 45}, + {1, 8, 21, 29, 50}, + {4, 8, 17, 18, 48}, + {1, 2, 5, 29, 42}, + {2, 13, 19, 28, 37}, + {10, 11, 18, 31, 43}, + {11, 12, 20, 35, 46}, + {5, 7, 36, 44, 50}, + {0, 5, 7, 13, 15}, + {1, 21, 27, 32, 39}, + {5, 7, 13, 26, 34}, + {7, 10, 12, 20, 47}, + {8, 14, 18, 44, 49}, + {7, 21, 33, 39, 50}, + {1, 13, 15, 40, 45}, + {6, 21, 32, 41, 49}, + {3, 8, 30, 37, 47}, + {8, 10, 16, 19, 42}, + {18, 23, 31, 48, 51}, + {6, 8, 19, 22, 28}, + {3, 6, 22, 25, 32}, + {5, 13, 18, 41, 49}, + {11, 21, 40, 44, 47}, + {2, 41, 44, 45, 51}, + {18, 20, 28, 33, 49}, + {7, 11, 26, 30, 46}, + {0, 7, 13, 22, 32}, + {3, 11, 31, 36, 42}, + {3, 17, 30, 40, 44}, + {0, 2, 26, 30, 46}, + {21, 32, 36, 37, 45}, + {6, 13, 21, 34, 43}, + {8, 10, 17, 30, 32}, + {30, 32, 34, 43, 44}, + {3, 11, 15, 16, 36}, + {9, 21, 27, 43, 47}, + {4, 9, 16, 21, 42}, + {4, 18, 20, 21, 46}, + {27, 32, 38, 45, 50}, + {6, 19, 22, 24, 29}, + {1, 17, 33, 45, 46}, + {17, 30, 34, 49, 50}, + {12, 14, 24, 34, 47}, + {13, 15, 29, 42, 48}, + {24, 27, 34, 41, 47}, + {13, 26, 33, 40, 42}, + {25, 29, 30, 42, 48}, + {7, 10, 11, 15, 33}, + {17, 18, 36, 43, 46}, + {6, 10, 28, 32, 46}, + {13, 26, 27, 29, 50}, + {1, 10, 29, 38, 42}, + {5, 9, 25, 28, 31}, + {3, 16, 24, 40, 45}, + {9, 14, 20, 33, 50}, + {9, 34, 40, 42, 47}, + {3, 23, 29, 44, 45}, + {7, 8, 18, 20, 38}, + {5, 9, 15, 17, 30}, + {0, 5, 12, 27, 44}, + {5, 16, 19, 29, 50}, + {15, 28, 36, 44, 50}, + {4, 14, 19, 21, 43}, + {1, 14, 23, 29, 46}, + {2, 21, 38, 40, 47}, + {0, 6, 32, 41, 43}, + {26, 27, 35, 37, 39}, + {2, 5, 13, 33, 39}, + {8, 23, 30, 43, 48}, + {8, 24, 28, 38, 41}, + {10, 19, 27, 30, 45}, + {8, 9, 26, 34, 38}, + {20, 26, 31, 33, 48}, + {14, 31, 35, 40, 47}, + {2, 13, 21, 33, 47}, + {17, 30, 33, 37, 39}, + {2, 5, 15, 21, 42}, + {4, 6, 16, 19, 46}, + {21, 40, 41, 43, 47}, + {1, 6, 30, 32, 39}, + {8, 9, 23, 33, 34}, + {5, 18, 19, 20, 28}, + {2, 14, 31, 38, 41}, + {0, 21, 23, 34, 42}, + {16, 27, 40, 43, 51}, + {5, 10, 34, 37, 44}, + {7, 9, 36, 44, 46}, + {6, 19, 39, 44, 50}, + {27, 34, 43, 47, 49}, + {1, 2, 3, 24, 42}, + {6, 9, 13, 16, 42}, + {5, 19, 32, 34, 48}, + {2, 20, 26, 39, 47}, + {3, 8, 34, 41, 44}, + {3, 22, 29, 30, 40}, + {1, 7, 16, 23, 46}, + {9, 15, 17, 39, 43}, + {2, 10, 28, 30, 46}, + {15, 28, 38, 44, 49}, + {3, 33, 36, 46, 47}, + {5, 16, 29, 39, 51}, + {18, 31, 39, 41, 45}, + {4, 7, 8, 26, 47}, + {2, 6, 24, 39, 41}, + {0, 9, 12, 13, 46}, + {13, 28, 29, 33, 39}, + {7, 21, 40, 41, 46}, + {15, 18, 25, 26, 44}, + {1, 21, 27, 28, 37}, + {2, 13, 20, 22, 28}, + {2, 7, 9, 23, 28}, + {8, 12, 14, 40, 41}, + {5, 28, 36, 41, 50}, + {11, 15, 21, 25, 41}, + {14, 37, 40, 44, 46}, + {1, 4, 32, 40, 44}, + {6, 14, 30, 45, 48}, + {4, 17, 22, 47, 50}, + {0, 1, 10, 39, 50}, + {3, 4, 35, 38, 43}, + {4, 27, 36, 43, 51}, + {5, 13, 20, 34, 44}, + {6, 9, 18, 30, 45}, + {27, 30, 36, 40, 50}, + {5, 12, 30, 31, 33}, + {2, 6, 15, 25, 44}, + {2, 21, 32, 38, 45}, + {6, 19, 21, 33, 49}, + {4, 12, 17, 21, 46}, + {14, 20, 27, 29, 37}, + {0, 3, 15, 29, 30}, + {6, 12, 17, 42, 45}, + {6, 17, 23, 43, 51}, + {14, 23, 39, 40, 48}, + {7, 26, 27, 39, 50}, + {2, 4, 6, 8, 15}, + {1, 10, 21, 45, 47}, + {21, 23, 30, 31, 34}, + {0, 3, 11, 28, 29}, + {3, 14, 34, 38, 42}, + {3, 26, 28, 32, 39}, + {14, 17, 31, 39, 40}, + {17, 24, 32, 45, 46}, + {13, 19, 26, 41, 43}, + {2, 17, 25, 33, 46}, + {10, 13, 19, 26, 41}, + {4, 8, 21, 32, 38}, + {19, 32, 35, 42, 50}, + {4, 15, 30, 39, 46}, + {1, 2, 15, 38, 48}, + {5, 8, 23, 32, 34}, + {16, 27, 35, 40, 44}, + {6, 18, 25, 33, 45}, + {6, 14, 30, 32, 51}, + {18, 22, 31, 39, 45}, + {5, 7, 20, 30, 32}, + {4, 7, 13, 15, 41}, + {7, 16, 20, 27, 32}, + {0, 26, 36, 41, 46}, + {8, 23, 31, 34, 37}, + {13, 30, 38, 42, 43}, + {17, 19, 22, 37, 45}, + {15, 19, 29, 33, 45}, + {11, 13, 26, 27, 33}, + {2, 3, 15, 21, 39}, + {1, 5, 34, 47, 48}, + {6, 12, 26, 33, 46}, + {8, 16, 17, 21, 41}, + {1, 6, 20, 23, 33}, + {6, 7, 20, 28, 38}, + {1, 15, 19, 25, 27}, + {4, 11, 13, 21, 26}, + {5, 29, 30, 31, 37}, + {0, 14, 30, 38, 39}, + {20, 26, 31, 39, 40}, + {1, 12, 30, 40, 49}, + {16, 24, 27, 28, 29}, + {1, 4, 20, 32, 45}, + {7, 12, 19, 32, 35}, + {27, 30, 32, 41, 43}, + {17, 28, 31, 37, 44}, + {3, 4, 23, 30, 51}, + {10, 19, 27, 31, 32}, + {1, 13, 34, 47, 49}, + {8, 16, 28, 31, 42}, + {2, 13, 15, 36, 44}, + {2, 3, 16, 21, 48}, + {31, 33, 35, 44, 49}, + {13, 14, 16, 29, 31}, + {1, 7, 17, 36, 43}, + {1, 12, 17, 43, 47}, + {2, 26, 33, 41, 48}, + {0, 2, 20, 30, 39}, + {3, 10, 16, 45, 48}, + {1, 16, 29, 41, 47}, + {26, 27, 39, 49, 50}, + {21, 22, 37, 41, 47}, + {7, 16, 18, 20, 47}, + {8, 21, 29, 38, 49}, + {4, 29, 30, 39, 46}, + {17, 38, 43, 49, 50}, + {19, 21, 24, 43, 45}, + {3, 18, 23, 27, 31}, + {2, 16, 37, 41, 43}, + {3, 4, 10, 43, 47}, + {13, 15, 36, 38, 39}, + {4, 17, 26, 41, 50}, + {11, 19, 20, 30, 45}, + {1, 3, 22, 25, 27}, + {0, 7, 9, 26, 36}, + {2, 15, 26, 29, 37}, + {5, 6, 12, 18, 27}, + {17, 21, 29, 42, 46}, + {11, 13, 19, 25, 45}, + {13, 19, 25, 44, 45}, + {5, 15, 18, 32, 33}, + {1, 15, 21, 25, 47}, + {3, 12, 30, 35, 42}, + {4, 30, 34, 39, 50}, + {13, 31, 34, 36, 39}, + {12, 15, 18, 44, 45}, + {8, 16, 22, 24, 47}, + {6, 7, 8, 19, 27}, + {6, 18, 31, 39, 51}, + {4, 14, 27, 33, 48}, + {3, 17, 21, 31, 43}, + {3, 6, 32, 33, 35}, + {5, 6, 23, 31, 47}, + {2, 36, 41, 45, 51}, + {2, 11, 18, 30, 31}, + {7, 9, 16, 45, 46}, + {2, 4, 15, 25, 33}, + {5, 13, 26, 27, 48}, + {15, 18, 20, 33, 45}, + {0, 29, 34, 41, 47}, + {5, 9, 13, 26, 33}, + {1, 13, 27, 47, 48}, + {3, 29, 41, 49, 50}, + {11, 20, 21, 38, 47}, + {21, 29, 34, 41, 48}, + {11, 15, 40, 41, 48}, + {7, 24, 44, 46, 51}, + {3, 8, 26, 28, 34}, + {13, 17, 20, 28, 46}, + {6, 7, 15, 19, 31}, + {0, 11, 39, 40, 47}, + {23, 28, 30, 41, 42}, + {1, 20, 23, 37, 46}, + {10, 18, 19, 32, 46}, + {7, 23, 33, 40, 43}, + {5, 13, 17, 19, 31}, + {2, 17, 19, 26, 43}, + {0, 17, 33, 37, 43}, + {2, 16, 30, 42, 51}, + {5, 21, 31, 49, 51}, + {16, 27, 32, 38, 40}, + {1, 16, 21, 31, 44}, + {14, 15, 28, 45, 48}, + {1, 16, 33, 44, 46}, + {0, 4, 21, 39, 46}, + {18, 24, 30, 41, 43}, + {25, 30, 39, 43, 48}, + {17, 33, 41, 46, 51}, + {7, 13, 19, 21, 32}, + {2, 14, 20, 36, 40}, + {8, 26, 29, 34, 43}, + {13, 26, 32, 34, 43}, + {0, 1, 6, 14, 25}, + {3, 5, 9, 39, 44}, + {5, 18, 43, 45, 48}, + {0, 20, 33, 43, 45}, + {3, 24, 26, 39, 51}, + {17, 26, 35, 43, 44}, + {0, 3, 13, 20, 31}, + {5, 13, 16, 22, 29}, + {27, 32, 42, 45, 49}, + {18, 36, 42, 44, 48}, + {1, 12, 28, 30, 40}, + {8, 31, 42, 44, 49}, + {0, 1, 6, 14, 37}, + {0, 29, 42, 46, 51}, + {3, 20, 28, 39, 46}, + {18, 20, 33, 35, 43}, + {26, 27, 35, 40, 43}, + {8, 12, 15, 17, 30}, + {3, 12, 16, 45, 48}, + {5, 31, 32, 35, 50}, + {0, 16, 19, 30, 32}, + {13, 18, 24, 31, 45}, + {0, 13, 40, 42, 51}, + {2, 26, 30, 41, 46}, + {12, 13, 26, 34, 50}, + {2, 7, 8, 31, 46}, + {13, 16, 24, 31, 39}, + {0, 14, 16, 28, 41}, + {0, 2, 10, 15, 21}, + {7, 11, 46, 48, 49}, + {2, 18, 24, 32, 44}, + {17, 19, 20, 42, 45}, + {11, 23, 31, 44, 46}, + {26, 28, 30, 43, 45}, + {11, 15, 18, 44, 45}, + {5, 26, 30, 44, 49}, + {6, 31, 41, 42, 44}, + {13, 14, 18, 37, 44}, + {6, 12, 15, 20, 46}, + {0, 6, 43, 45, 47}, + {2, 4, 13, 17, 29}, + {2, 9, 30, 41, 50}, + {9, 27, 40, 45, 51}, + {15, 18, 24, 31, 34}, + {19, 28, 32, 37, 43}, + {14, 18, 20, 23, 40}, + {12, 16, 41, 42, 43}, + {16, 18, 31, 43, 45}, + {0, 9, 21, 25, 39}, + {18, 19, 24, 31, 40}, + {1, 6, 7, 34, 45}, + {8, 26, 39, 48, 49}, + {10, 18, 26, 32, 45}, + {9, 15, 18, 27, 41}, + {17, 24, 26, 30, 36}, + {3, 30, 31, 41, 42}, + {14, 17, 43, 45, 51}, + {8, 9, 11, 32, 47}, + {20, 22, 32, 38, 45}, + {1, 6, 36, 45, 51}, + {0, 10, 13, 28, 35}, + {7, 14, 21, 33, 37}, + {1, 5, 9, 15, 28}, + {1, 34, 35, 44, 47}, + {12, 19, 34, 45, 48}, + {7, 12, 13, 22, 46}, + {6, 13, 15, 29, 32}, + {0, 13, 19, 23, 40}, + {5, 10, 18, 19, 28}, + {11, 12, 27, 31, 44}, + {0, 17, 18, 25, 44}, + {2, 6, 28, 43, 49}, + {8, 11, 12, 39, 47}, + {5, 18, 22, 26, 47}, + {7, 33, 34, 44, 48}, + {2, 11, 33, 39, 41}, + {26, 27, 30, 36, 43}, + {5, 7, 21, 42, 47}, + {13, 17, 26, 28, 46}, + {18, 24, 29, 32, 44}, + {3, 4, 7, 34, 46}, + {4, 6, 25, 30, 40}, + {8, 13, 29, 34, 40}, + {13, 18, 29, 38, 44}, + {0, 4, 26, 46, 48}, + {2, 19, 22, 26, 28}, + {12, 13, 14, 17, 40}, + {15, 28, 44, 45, 48}, + {9, 13, 27, 38, 39}, + {2, 8, 22, 41, 42}, + {13, 15, 25, 37, 41}, + {2, 7, 14, 27, 48}, + {0, 4, 11, 17, 28}, + {6, 8, 23, 34, 35}, + {4, 16, 29, 40, 44}, + {15, 24, 31, 32, 44}, + {7, 12, 16, 20, 28}, + {29, 34, 37, 42, 48}, + {0, 10, 16, 21, 34}, + {7, 21, 26, 41, 46}, + {0, 7, 13, 15, 29}, + {4, 8, 24, 29, 43}, + {3, 15, 19, 28, 30}, + {4, 6, 14, 18, 19}, + {22, 27, 39, 40, 44}, + {1, 12, 18, 34, 44}, + {6, 15, 21, 34, 40}, + {9, 18, 31, 37, 42}, + {1, 8, 15, 31, 34}, + {5, 18, 34, 43, 48}, + {2, 6, 28, 34, 43}, + {0, 32, 45, 49, 50}, + {1, 5, 14, 15, 23}, + {1, 21, 36, 42, 47}, + {16, 29, 33, 34, 39}, + {1, 4, 19, 21, 34}, + {7, 15, 16, 23, 29}, + {0, 2, 17, 30, 32}, + {3, 8, 12, 34, 49}, + {9, 13, 14, 27, 49}, + {9, 15, 17, 25, 41}, + {31, 34, 38, 44, 48}, + {4, 5, 30, 48, 51}, + {1, 11, 12, 36, 40}, + {0, 1, 26, 41, 45}, + {5, 6, 13, 43, 45}, + {7, 18, 24, 33, 48}, + {0, 13, 14, 17, 46}, + {4, 31, 34, 44, 48}, + {8, 23, 32, 34, 48}, + {3, 6, 13, 19, 38}, + {16, 23, 27, 35, 42}, + {0, 3, 28, 41, 49}, + {0, 4, 15, 39, 51}, + {0, 21, 40, 43, 47}, + {3, 16, 19, 30, 38}, + {1, 6, 14, 15, 43}, + {19, 20, 24, 32, 48}, + {8, 11, 14, 34, 46}, + {7, 17, 22, 38, 43}, + {8, 22, 26, 34, 46}, + {0, 13, 20, 43, 49}, + {5, 12, 13, 26, 35}, + {4, 9, 20, 24, 43}, + {1, 15, 21, 34, 43}, + {6, 17, 23, 38, 45}, + {14, 30, 40, 46, 50}, + {8, 14, 22, 29, 47}, + {6, 17, 36, 38, 45}, + {3, 4, 13, 27, 29}, + {3, 4, 26, 39, 47}, + {7, 19, 28, 44, 46}, + {3, 16, 30, 36, 40}, + {3, 18, 36, 42, 47}, + {17, 31, 34, 41, 47}, + {10, 28, 41, 46, 51}, + {0, 22, 23, 39, 41}, + {0, 13, 16, 27, 48}, + {0, 1, 11, 27, 49}, + {0, 28, 37, 39, 48}, + {11, 13, 25, 32, 45}, + {12, 17, 33, 44, 46}, + {2, 6, 13, 41, 50}, + {5, 10, 18, 32, 38}, + {20, 21, 22, 25, 47}, + {19, 27, 40, 43, 46}, + {17, 20, 26, 28, 46}, + {1, 8, 14, 28, 29}, + {0, 4, 10, 13, 31}, + {6, 14, 19, 30, 49}, + {2, 29, 40, 41, 49}, + {7, 19, 20, 43, 49}, + {2, 11, 34, 41, 46}, + {7, 24, 32, 44, 45}, + {1, 8, 15, 21, 50}, + {2, 5, 16, 29, 38}, + {10, 17, 19, 20, 45}, + {0, 22, 39, 50, 51}, + {16, 29, 38, 40, 49}, + {10, 16, 19, 26, 42}, + {7, 13, 16, 46, 48}, + {11, 13, 34, 41, 47}, + {3, 13, 24, 26, 44}, + {2, 18, 27, 40, 48}, + {26, 30, 39, 44, 45}, + {1, 4, 11, 31, 43}, + {34, 36, 44, 46, 47}, + {1, 4, 5, 39, 40}, + {2, 13, 14, 15, 30}, + {13, 39, 41, 46, 48}, + {4, 22, 36, 37, 43}, + {0, 3, 7, 24, 33}, + {2, 5, 15, 43, 47}, + {7, 23, 28, 33, 50}, + {5, 15, 18, 36, 38}, + {3, 16, 21, 25, 32}, + {5, 7, 8, 27, 33}, + {8, 25, 34, 42, 43}, + {14, 20, 27, 39, 48}, + {7, 13, 21, 25, 26}, + {10, 13, 26, 43, 50}, + {2, 19, 28, 29, 49}, + {0, 6, 7, 21, 47}, + {2, 3, 5, 18, 25}, + {5, 18, 29, 45, 49}, + {3, 5, 13, 32, 42}, + {2, 5, 22, 31, 46}, + {16, 17, 42, 45, 49}, + {17, 23, 29, 30, 38}, + {0, 9, 19, 28, 41}, + {2, 15, 33, 34, 42}, + {4, 6, 7, 46, 49}, + {0, 2, 29, 35, 39}, + {2, 10, 17, 19, 41}, + {7, 12, 15, 30, 41}, + {3, 5, 15, 20, 42}, + {0, 8, 17, 26, 50}, + {9, 14, 24, 27, 38}, + {6, 12, 19, 26, 48}, + {12, 16, 33, 46, 50}, + {2, 14, 15, 25, 50}, + {11, 12, 20, 33, 36}, + {4, 7, 26, 30, 51}, + {12, 19, 22, 43, 45}, + {4, 26, 30, 46, 48}, + {11, 14, 30, 41, 43}, + {3, 6, 15, 25, 42}, + {6, 30, 39, 43, 44}, + {5, 11, 22, 31, 33}, + {25, 26, 29, 39, 46}, + {0, 8, 13, 36, 44}, + {0, 4, 7, 17, 50}, + {5, 7, 13, 26, 32}, + {0, 10, 21, 33, 39}, + {2, 15, 22, 24, 46}, + {3, 6, 15, 25, 41}, + {14, 26, 27, 30, 44}, + {15, 27, 35, 37, 41}, + {4, 5, 7, 20, 37}, + {1, 3, 14, 15, 45}, + {17, 27, 43, 48, 50}, + {5, 6, 13, 26, 36}, + {6, 16, 31, 32, 43}, + {1, 10, 20, 38, 40}, + {13, 29, 33, 42, 48}, + {17, 18, 24, 42, 44}, + {16, 20, 36, 40, 42}, + {7, 16, 29, 40, 45}, + {2, 16, 26, 38, 39}, + {8, 17, 34, 35, 44}, + {14, 19, 32, 44, 49}, + {4, 5, 33, 36, 44}, + {1, 9, 15, 16, 42}, + {2, 27, 28, 42, 46}, + {13, 14, 27, 35, 36}, + {13, 14, 21, 32, 47}, + {8, 10, 20, 33, 37}, + {5, 19, 26, 33, 39}, + {5, 16, 32, 36, 44}, + {5, 15, 28, 30, 47}, + {19, 26, 39, 43, 50}, + {7, 16, 31, 32, 44}, + {11, 15, 17, 30, 42}, + {7, 25, 29, 39, 46}, + {8, 14, 34, 36, 48}, + {14, 28, 41, 46, 48}, + {21, 24, 34, 38, 46}, + {5, 6, 12, 22, 32}, + {14, 21, 36, 47, 51}, + {4, 14, 29, 43, 49}, + {3, 15, 27, 34, 41}, + {3, 29, 40, 44, 46}, + {8, 13, 24, 28, 39}, + {6, 8, 16, 30, 42}, + {2, 8, 24, 44, 47}, + {16, 21, 25, 31, 47}, + {0, 14, 40, 41, 51}, + {8, 9, 23, 31, 44}, + {2, 5, 10, 15, 35}, + {8, 12, 19, 21, 31}, + {7, 19, 22, 38, 45}, + {17, 27, 32, 34, 43}, + {4, 6, 20, 33, 50}, + {5, 36, 44, 45, 46}, + {6, 9, 29, 45, 47}, + {10, 21, 22, 28, 41}, + {1, 19, 25, 26, 27}, + {8, 17, 26, 29, 30}, + {0, 26, 29, 38, 40}, + {4, 15, 23, 28, 40}, + {8, 19, 31, 35, 44}, + {16, 18, 24, 41, 44}, + {2, 3, 28, 34, 39}, + {9, 12, 17, 19, 30}, + {3, 16, 21, 22, 30}, + {3, 6, 12, 16, 23}, + {6, 12, 13, 37, 39}, + {3, 10, 29, 32, 50}, + {1, 17, 30, 35, 51}, + {5, 6, 23, 45, 51}, + {1, 13, 17, 26, 51}, + {5, 14, 19, 38, 40}, + {16, 34, 38, 42, 46}, + {11, 21, 31, 32, 34}, + {19, 24, 45, 47, 51}, + {1, 5, 7, 14, 23}, + {5, 7, 12, 18, 27}, + {1, 5, 33, 42, 44}, + {4, 6, 35, 43, 44}, + {15, 20, 32, 41, 42}, + {1, 13, 16, 31, 44}, + {3, 17, 28, 30, 45}, + {9, 12, 17, 43, 50}, + {8, 19, 25, 29, 32}, + {3, 8, 21, 22, 30}, + {1, 3, 7, 8, 47}, + {0, 18, 20, 44, 45}, + {18, 20, 22, 26, 44}, + {0, 17, 29, 40, 43}, + {3, 14, 20, 35, 40}, + {2, 11, 15, 25, 46}, + {4, 10, 27, 43, 51}, + {0, 15, 39, 47, 48}, + {8, 28, 33, 35, 46}, + {2, 10, 16, 41, 45}, + {0, 8, 34, 40, 46}, + {9, 19, 44, 45, 46}, + {3, 15, 17, 25, 41}, + {1, 2, 19, 27, 44}, + {0, 8, 24, 26, 33}, + {0, 2, 22, 41, 42}, + {5, 9, 16, 17, 43}, + {1, 6, 29, 42, 44}, + {8, 15, 27, 33, 46}, + {4, 16, 17, 25, 34}, + {15, 17, 25, 28, 46}, + {3, 16, 23, 28, 44}, + {6, 13, 14, 37, 45}, + {16, 26, 29, 33, 37}, + {3, 26, 28, 30, 43}, + {5, 13, 26, 49, 51}, + {3, 6, 7, 18, 32}, + {4, 16, 28, 32, 41}, + {0, 13, 21, 22, 43}, + {1, 14, 18, 24, 47}, + {0, 13, 28, 42, 51}, + {11, 13, 15, 26, 32}, + {7, 31, 34, 35, 47}, + {5, 32, 38, 44, 49}, + {15, 21, 34, 43, 48}, + {4, 22, 27, 40, 51}, + {2, 11, 32, 34, 47}, + {22, 26, 27, 34, 39}, + {13, 18, 34, 44, 51}, + {1, 3, 21, 27, 30}, + {21, 23, 28, 33, 47}, + {0, 2, 10, 17, 41}, + {10, 15, 19, 25, 32}, + {0, 8, 15, 28, 29}, + {7, 8, 33, 44, 45}, + {7, 10, 16, 42, 43}, + {0, 1, 21, 30, 39}, + {4, 13, 26, 38, 40}, + {7, 23, 31, 32, 44}, + {14, 26, 27, 32, 48}, + {3, 4, 15, 29, 48}, + {2, 18, 19, 41, 47}, + {20, 24, 29, 33, 36}, + {10, 27, 28, 34, 47}, + {8, 10, 18, 43, 44}, + {5, 6, 8, 31, 51}, + {5, 15, 20, 24, 33}, + {1, 12, 13, 26, 49}, + {0, 7, 29, 33, 44}, + {3, 9, 19, 42, 43}, + {1, 5, 24, 31, 32}, + {8, 11, 22, 29, 47}, + {12, 17, 21, 26, 47}, + {14, 19, 22, 30, 32}, + {4, 12, 20, 33, 44}, + {14, 19, 20, 31, 32}, + {0, 20, 26, 34, 49}, + {1, 8, 20, 34, 48}, + {14, 18, 27, 32, 39}, + {6, 12, 31, 43, 45}, + {0, 12, 24, 32, 39}, + {3, 28, 29, 31, 35}, + {5, 13, 22, 44, 50}, + {16, 22, 26, 29, 36}, + {4, 25, 26, 30, 42}, + {9, 30, 37, 38, 43}, + {5, 14, 25, 33, 46}, + {3, 21, 43, 47, 48}, + {0, 2, 4, 43, 46}, + {3, 7, 16, 18, 39}, + {1, 11, 15, 40, 51}, + {11, 15, 21, 28, 49}, + {2, 6, 30, 40, 41}, + {0, 16, 29, 45, 47}, + {7, 16, 22, 33, 43}, + {2, 6, 19, 23, 46}, + {11, 14, 27, 32, 33}, + {7, 35, 41, 46, 47}, + {0, 25, 26, 29, 49}, + {4, 18, 25, 44, 47}, + {3, 16, 24, 28, 44}, + {1, 3, 28, 33, 46}, + {26, 28, 39, 40, 50}, + {4, 14, 17, 24, 33}, + {14, 27, 29, 33, 50}, + {2, 14, 15, 34, 38}, + {7, 14, 27, 28, 43}, + {5, 32, 34, 44, 49}, + {2, 3, 11, 14, 29}, + {5, 31, 42, 48, 49}, + {13, 14, 29, 34, 47}, + {5, 14, 15, 29, 41}, + {6, 7, 11, 25, 32}, + {2, 15, 39, 42, 43}, + {0, 10, 13, 32, 34}, + {17, 34, 40, 45, 47}, + {7, 8, 11, 20, 26}, + {3, 6, 12, 35, 45}, + {5, 22, 24, 27, 44}, + {6, 11, 19, 27, 29}, + {19, 26, 30, 39, 46}, + {3, 21, 35, 43, 47}, + {15, 16, 21, 33, 47}, + {4, 8, 20, 47, 51}, + {10, 16, 17, 35, 42}, + {5, 34, 43, 47, 50}, + {14, 19, 37, 40, 42}, + {4, 18, 22, 30, 49}, + {14, 17, 19, 30, 38}, + {6, 17, 34, 43, 46}, + {7, 13, 26, 43, 51}, + {0, 7, 18, 26, 41}, + {0, 32, 34, 47, 50}, + {13, 28, 30, 39, 40}, + {7, 19, 20, 36, 44}, + {6, 10, 21, 32, 37}, + {5, 10, 21, 27, 31}, + {1, 4, 16, 17, 47}, + {6, 10, 16, 28, 32}, + {8, 16, 25, 26, 29}, + {21, 38, 41, 44, 47}, + {6, 10, 11, 22, 32}, + {4, 15, 16, 33, 41}, + {6, 9, 19, 24, 31}, + {12, 27, 35, 36, 40}, + {1, 26, 27, 42, 44}, + {4, 5, 26, 38, 44}, + {7, 8, 14, 30, 33}, + {10, 14, 15, 20, 40}, + {11, 15, 19, 26, 45}, + {16, 26, 40, 42, 44}, + {9, 13, 15, 28, 38}, + {8, 17, 26, 29, 43}, + {5, 15, 18, 20, 22}, + {2, 4, 10, 30, 37}, + {13, 29, 39, 43, 47}, + {15, 18, 19, 30, 31}, + {17, 29, 33, 46, 50}, + {4, 18, 20, 30, 36}, + {14, 16, 25, 27, 45}, + {8, 11, 32, 47, 48}, + {26, 29, 30, 39, 40}, + {4, 21, 23, 29, 42}, + {6, 30, 41, 45, 47}, + {0, 2, 13, 21, 51}, + {0, 2, 19, 38, 39}, + {3, 11, 12, 27, 29}, + {1, 15, 19, 29, 40}, + {17, 18, 21, 34, 41}, + {6, 17, 31, 44, 48}, + {3, 4, 5, 13, 39}, + {7, 8, 19, 20, 42}, + {18, 26, 31, 36, 41}, + {2, 6, 31, 41, 48}, + {7, 27, 39, 40, 43}, + {15, 16, 21, 24, 34}, + {11, 16, 17, 43, 49}, + {0, 13, 18, 23, 30}, + {4, 21, 26, 39, 40}, + {3, 4, 10, 11, 42}, + {7, 8, 34, 35, 50}, + {2, 13, 20, 31, 33}, + {16, 21, 25, 29, 30}, + {14, 19, 37, 42, 45}, + {16, 29, 36, 38, 46}, + {6, 26, 33, 39, 51}, + {13, 31, 44, 45, 50}, + {29, 36, 37, 39, 42}, + {2, 6, 7, 41, 42}, + {3, 12, 22, 29, 36}, + {5, 6, 9, 16, 31}, + {4, 16, 17, 37, 38}, + {6, 13, 33, 39, 50}, + {20, 23, 31, 33, 41}, + {4, 14, 27, 31, 37}, + {2, 19, 24, 28, 35}, + {1, 6, 14, 20, 21}, + {7, 20, 21, 25, 41}, + {3, 13, 23, 41, 42}, + {1, 21, 34, 45, 46}, + {14, 16, 32, 33, 42}, + {20, 26, 28, 40, 46}, + {6, 8, 13, 37, 39}, + {4, 27, 29, 42, 46}, + {5, 14, 18, 32, 50}, + {6, 17, 22, 30, 47}, + {16, 21, 32, 34, 50}, + {5, 7, 15, 28, 35}, + {2, 3, 27, 41, 47}, + {7, 31, 33, 41, 50}, + {2, 6, 15, 43, 50}, + {1, 3, 29, 44, 48}, + {0, 17, 22, 32, 43}, + {24, 32, 38, 45, 48}, + {9, 16, 23, 42, 50}, + {0, 16, 20, 32, 45}, + {3, 11, 26, 29, 36}, + {4, 8, 35, 44, 47}, + {3, 17, 23, 42, 50}, + {2, 16, 18, 23, 31}, + {6, 16, 19, 21, 48}, + {14, 33, 40, 49, 50}, + {1, 4, 25, 27, 36}, + {3, 4, 17, 25, 36}, + {6, 8, 29, 34, 40}, + {0, 3, 4, 13, 37}, + {8, 13, 32, 40, 45}, + {1, 4, 14, 34, 35}, + {0, 10, 12, 39, 45}, + {0, 1, 18, 30, 39}, + {1, 24, 25, 30, 43}, + {13, 33, 34, 42, 46}, + {16, 22, 26, 27, 42}, + {13, 14, 15, 28, 33}, + {0, 27, 31, 40, 47}, + {7, 8, 17, 30, 31}, + {2, 12, 21, 27, 28}, + {27, 33, 38, 40, 49}, + {6, 10, 19, 26, 51}, + {2, 12, 15, 29, 36}, + {2, 7, 10, 32, 41}, + {8, 15, 31, 33, 44}, + {7, 16, 46, 49, 50}, + {0, 5, 6, 26, 50}, + {31, 34, 35, 39, 44}, + {3, 8, 11, 47, 51}, + {2, 30, 33, 41, 49}, + {1, 27, 33, 48, 49}, + {1, 9, 15, 41, 43}, + {5, 22, 38, 44, 47}, + {11, 21, 45, 46, 47}, + {3, 7, 32, 37, 42}, + {0, 5, 14, 26, 42}, + {2, 7, 20, 21, 42}, + {5, 7, 25, 44, 48}, + {0, 30, 33, 39, 50}, + {16, 26, 30, 31, 42}, + {0, 18, 26, 36, 50}, + {3, 29, 32, 37, 48}, + {4, 14, 18, 25, 44}, + {1, 2, 8, 21, 22}, + {10, 14, 16, 42, 47}, + {0, 5, 6, 45, 49}, + {0, 31, 33, 43, 46}, + {4, 12, 20, 37, 46}, + {14, 18, 30, 31, 41}, + {21, 29, 32, 47, 48}, + {2, 3, 14, 15, 24}, + {18, 30, 31, 40, 50}, + {12, 17, 21, 42, 47}, + {3, 8, 14, 27, 48}, + {4, 20, 22, 27, 46}, + {2, 19, 32, 38, 47}, + {1, 5, 13, 16, 18}, + {1, 2, 11, 23, 40}, + {29, 38, 39, 42, 47}, + {1, 19, 28, 40, 43}, + {6, 14, 16, 30, 40}, + {3, 9, 19, 24, 42}, + {5, 11, 18, 32, 36}, + {0, 29, 33, 37, 46}, + {12, 28, 41, 48, 49}, + {7, 18, 20, 21, 42}, + {2, 6, 29, 37, 42}, + {10, 14, 22, 27, 29}, + {3, 9, 15, 32, 45}, + {8, 27, 40, 44, 51}, + {0, 11, 30, 39, 44}, + {7, 20, 29, 36, 41}, + {20, 24, 31, 43, 44}, + {3, 8, 21, 22, 50}, + {6, 8, 30, 34, 38}, + {27, 28, 40, 48, 50}, + {0, 7, 27, 33, 51}, + {4, 5, 10, 30, 32}, + {2, 21, 41, 42, 44}, + {29, 33, 42, 47, 51}, + {8, 20, 21, 40, 50}, + {12, 34, 36, 46, 47}, + {1, 16, 27, 32, 37}, + {1, 20, 40, 48, 50}, + {4, 10, 38, 41, 43}, + {1, 6, 22, 23, 40}, + {20, 23, 34, 38, 47}, + {0, 17, 27, 30, 38}, + {5, 6, 18, 39, 42}, + {7, 9, 16, 24, 46}, + {14, 18, 27, 42, 50}, + {12, 15, 17, 18, 44}, + {14, 21, 26, 29, 42}, + {3, 9, 23, 27, 42}, + {2, 23, 25, 32, 45}, + {13, 19, 38, 44, 45}, + {5, 15, 16, 20, 46}, + {0, 8, 20, 26, 38}, + {5, 18, 21, 22, 29}, + {24, 27, 31, 40, 48}, + {4, 8, 29, 42, 44}, + {14, 15, 28, 35, 46}, + {4, 8, 11, 29, 30}, + {4, 5, 7, 17, 19}, + {2, 29, 33, 42, 47}, + {7, 10, 20, 44, 47}, + {4, 12, 21, 36, 47}, + {1, 15, 16, 35, 41}, + {1, 4, 31, 33, 46}, + {3, 16, 41, 44, 47}, + {19, 24, 29, 43, 45}, + {16, 24, 29, 32, 49}, + {8, 15, 24, 41, 44}, + {12, 31, 33, 34, 46}, + {17, 18, 20, 33, 39}, + {4, 8, 10, 20, 47}, + {7, 8, 43, 47, 49}, + {2, 15, 24, 33, 43}, + {2, 5, 7, 42, 44}, + {4, 13, 25, 28, 41}, + {14, 20, 39, 44, 46}, + {0, 8, 18, 23, 34}, + {17, 25, 30, 31, 37}, + {3, 7, 9, 16, 39}, + {2, 14, 16, 18, 44}, + {4, 8, 27, 44, 47}, + {3, 11, 34, 44, 47}, + {6, 15, 26, 34, 47}, + {1, 3, 10, 16, 20}, + {1, 6, 8, 27, 42}, + {2, 20, 30, 33, 44}, + {3, 13, 14, 40, 48}, + {0, 7, 8, 21, 45}, + {21, 31, 38, 44, 49}, + {4, 14, 27, 32, 38}, + {15, 23, 31, 35, 44}, + {8, 11, 21, 36, 51}, + {4, 18, 43, 47, 51}, + {0, 1, 17, 32, 39}, + {2, 20, 21, 30, 33}, + {4, 5, 23, 29, 42}, + {19, 34, 35, 44, 47}, + {4, 12, 14, 30, 32}, + {18, 27, 36, 40, 48}, + {5, 19, 26, 29, 42}, + {15, 20, 31, 42, 44}, + {10, 18, 20, 22, 31}, + {5, 10, 18, 33, 48}, + {8, 32, 45, 48, 49}, + {1, 5, 19, 24, 44}, + {7, 12, 30, 34, 47}, + {6, 17, 32, 47, 48}, + {14, 27, 43, 46, 49}, + {4, 7, 41, 43, 48}, + {8, 9, 13, 39, 45}, + {27, 40, 42, 45, 50}, + {11, 15, 28, 35, 42}, + {0, 2, 24, 28, 34}, + {18, 24, 27, 39, 40}, + {11, 20, 28, 46, 51}, + {2, 17, 26, 36, 41}, + {7, 14, 31, 40, 49}, + {3, 18, 29, 46, 51}, + {7, 8, 13, 27, 40}, + {6, 13, 14, 19, 41}, + {21, 28, 30, 47, 51}, + {1, 11, 15, 28, 44}, + {6, 15, 29, 35, 42}, + {4, 17, 29, 31, 35}, + {1, 2, 4, 9, 14}, + {1, 3, 22, 29, 46}, + {2, 3, 5, 42, 48}, + {5, 6, 13, 23, 44}, + {7, 14, 31, 43, 46}, + {8, 11, 32, 40, 47}, + {3, 17, 21, 23, 34}, + {3, 16, 22, 34, 46}, + {0, 26, 28, 33, 40}, + {0, 12, 28, 39, 46}, + {5, 7, 10, 28, 33}, + {1, 4, 15, 34, 40}, + {4, 9, 14, 33, 46}, + {1, 3, 14, 38, 50}, + {5, 9, 36, 43, 44}, + {4, 20, 21, 33, 38}, + {5, 9, 30, 41, 44}, + {6, 12, 19, 26, 42}, + {5, 14, 22, 24, 44}, + {6, 7, 26, 29, 46}, + {7, 9, 12, 24, 46}, + {14, 20, 33, 38, 41}, + {2, 18, 23, 27, 41}, + {1, 15, 26, 28, 50}, + {18, 30, 40, 44, 48}, + {3, 11, 17, 31, 42}, + {7, 16, 20, 32, 40}, + {0, 14, 26, 28, 34}, + {24, 25, 26, 39, 44}, + {1, 5, 16, 28, 44}, + {0, 8, 9, 26, 49}, + {13, 17, 21, 29, 30}, + {7, 10, 32, 42, 45}, + {7, 12, 20, 21, 49}, + {19, 21, 40, 47, 50}, + {0, 13, 22, 37, 45}, + {3, 15, 20, 21, 29}, + {1, 9, 11, 26, 39}, + {3, 8, 18, 34, 48}, + {13, 19, 29, 31, 45}, + {6, 15, 27, 32, 34}, + {4, 29, 30, 41, 49}, + {29, 30, 37, 42, 51}, + {16, 18, 20, 33, 49}, + {5, 19, 25, 30, 45}, + {4, 6, 8, 40, 45}, + {3, 18, 21, 29, 30}, + {8, 9, 30, 45, 47}, + {19, 29, 32, 36, 38}, + {5, 8, 10, 44, 50}, + {11, 14, 17, 38, 43}, + {6, 24, 31, 43, 45}, + {4, 10, 12, 14, 27}, + {20, 29, 33, 35, 51}, + {7, 17, 23, 43, 45}, + {5, 12, 18, 27, 49}, + {7, 9, 11, 19, 20}, + {3, 7, 20, 25, 28}, + {1, 17, 18, 20, 27}, + {4, 16, 32, 35, 42}, + {5, 18, 25, 40, 45}, + {7, 9, 21, 34, 43}, + {0, 20, 32, 35, 39}, + {4, 26, 28, 39, 44}, + {0, 6, 9, 13, 14}, + {0, 4, 23, 26, 42}, + {14, 23, 40, 46, 48}, + {1, 20, 21, 25, 27}, + {6, 14, 45, 46, 48}, + {2, 13, 28, 43, 48}, + {4, 12, 15, 34, 43}, + {1, 11, 17, 30, 39}, + {1, 14, 21, 24, 36}, + {5, 12, 16, 23, 31}, + {4, 5, 23, 43, 50}, + {8, 13, 17, 47, 50}, + {4, 12, 17, 47, 50}, + {3, 20, 31, 43, 44}, + {6, 23, 24, 44, 45}, + {8, 28, 31, 36, 44}, + {1, 5, 9, 27, 36}, + {13, 26, 31, 38, 41}, + {6, 12, 19, 26, 41}, + {2, 12, 18, 44, 48}, + {4, 20, 22, 33, 50}, + {3, 29, 35, 45, 51}, + {6, 18, 26, 32, 43}, + {2, 3, 7, 25, 28}, + {12, 16, 19, 21, 42}, + {16, 29, 33, 47, 48}, + {13, 19, 26, 42, 47}, + {20, 27, 34, 40, 51}, + {3, 15, 25, 26, 28}, + {5, 10, 15, 28, 50}, + {3, 29, 31, 49, 50}, + {11, 30, 33, 39, 43}, + {8, 20, 24, 33, 44}, + {5, 6, 10, 26, 45}, + {21, 24, 30, 34, 36}, + {14, 28, 38, 40, 49}, + {6, 8, 26, 28, 45}, + {2, 14, 29, 42, 47}, + {17, 19, 23, 29, 30}, + {10, 16, 25, 35, 42}, + {2, 4, 26, 41, 42}, + {23, 30, 35, 39, 43}, + {13, 14, 20, 37, 40}, + {2, 19, 41, 47, 49}, + {3, 6, 8, 25, 42}, + {6, 26, 28, 32, 35}, + {23, 27, 30, 37, 40}, + {15, 30, 41, 44, 46}, + {4, 17, 27, 38, 46}, + {2, 26, 31, 44, 48}, + {19, 25, 32, 44, 47}, + {1, 4, 7, 17, 47}, + {13, 30, 39, 41, 42}, + {1, 7, 12, 33, 43}, + {1, 15, 18, 24, 27}, + {16, 27, 33, 39, 46}, + {0, 2, 6, 24, 39}, + {0, 4, 7, 26, 34}, + {0, 3, 13, 40, 49}, + {0, 15, 35, 41, 47}, + {1, 32, 35, 45, 49}, + {2, 6, 11, 32, 44}, + {1, 14, 24, 28, 34}, + {11, 16, 17, 26, 39}, + {20, 31, 35, 36, 46}, + {3, 31, 38, 44, 50}, + {2, 15, 35, 36, 38}, + {0, 8, 25, 26, 49}, + {15, 20, 28, 45, 48}, + {1, 14, 25, 34, 41}, + {29, 33, 44, 46, 47}, + {16, 24, 34, 42, 45}, + {5, 9, 13, 31, 33}, + {1, 16, 18, 21, 29}, + {9, 29, 32, 42, 49}, + {7, 15, 27, 30, 33}, + {5, 6, 20, 33, 39}, + {5, 19, 30, 32, 41}, + {16, 20, 30, 39, 42}, + {12, 15, 19, 30, 41}, + {2, 8, 19, 32, 37}, + {22, 26, 30, 34, 47}, + {4, 10, 34, 43, 48}, + {1, 14, 21, 32, 35}, + {3, 4, 30, 38, 44}, + {19, 21, 23, 43, 47}, + {5, 8, 17, 23, 31}, + {1, 15, 32, 45, 46}, + {2, 16, 37, 41, 51}, + {14, 30, 36, 40, 51}, + {2, 5, 28, 29, 37}, + {3, 12, 21, 42, 44}, + {4, 11, 14, 27, 38}, + {1, 14, 24, 26, 42}, + {13, 19, 36, 39, 40}, + {4, 7, 20, 27, 38}, + {3, 18, 19, 31, 46}, + {8, 16, 26, 34, 43}, + {14, 24, 29, 40, 44}, + {5, 6, 14, 40, 46}, + {1, 3, 21, 42, 45}, + {1, 4, 5, 33, 46}, + {1, 3, 8, 34, 51}, + {3, 13, 27, 32, 45}, + {7, 19, 33, 35, 41}, + {14, 19, 20, 41, 45}, + {13, 26, 30, 46, 51}, + {0, 1, 27, 30, 50}, + {2, 8, 10, 15, 29}, + {9, 18, 27, 32, 45}, + {3, 8, 15, 21, 24}, + {10, 11, 21, 35, 47}, + {1, 12, 14, 22, 39}, + {3, 13, 17, 29, 35}, + {6, 10, 19, 30, 40}, + {0, 26, 44, 48, 50}, + {1, 27, 38, 44, 48}, + {0, 16, 19, 22, 39}, + {0, 16, 40, 42, 45}, + {3, 5, 16, 39, 40}, + {0, 8, 14, 32, 34}, + {2, 6, 17, 27, 32}, + {0, 13, 16, 20, 28}, + {15, 16, 35, 42, 44}, + {6, 11, 15, 25, 41}, + {10, 15, 17, 43, 44}, + {2, 13, 21, 23, 34}, + {13, 14, 18, 35, 40}, + {11, 12, 15, 17, 30}, + {1, 8, 22, 28, 47}, + {21, 22, 36, 46, 47}, + {7, 17, 29, 43, 49}, + {0, 16, 24, 42, 51}, + {1, 18, 26, 31, 49}, + {0, 21, 26, 35, 50}, + {2, 16, 24, 29, 44}, + {4, 5, 15, 30, 47}, + {1, 8, 25, 34, 48}, + {6, 33, 34, 45, 49}, + {2, 6, 17, 19, 29}, + {5, 10, 20, 33, 38}, + {0, 19, 30, 42, 43}, + {1, 3, 22, 37, 40}, + {13, 21, 28, 44, 47}, + {1, 8, 12, 21, 41}, + {0, 12, 15, 28, 33}, + {6, 29, 39, 42, 44}, + {9, 18, 20, 26, 46}, + {5, 7, 14, 22, 40}, + {1, 14, 23, 29, 32}, + {13, 23, 25, 26, 37}, + {0, 14, 20, 30, 43}, + {3, 18, 28, 29, 45}, + {15, 23, 34, 39, 47}, + {0, 3, 7, 17, 29}, + {9, 13, 24, 27, 40}, + {3, 23, 26, 42, 47}, + {7, 19, 21, 37, 45}, + {6, 14, 20, 30, 33}, + {4, 22, 34, 44, 47}, + {8, 13, 27, 32, 47}, + {8, 20, 21, 31, 40}, + {8, 29, 30, 45, 47}, + {1, 3, 14, 22, 46}, + {4, 5, 17, 27, 35}, + {7, 32, 38, 41, 45}, + {1, 13, 16, 27, 37}, + {20, 27, 44, 45, 46}, + {0, 8, 11, 13, 25}, + {3, 14, 18, 29, 39}, + {1, 40, 44, 46, 48}, + {16, 17, 31, 36, 44}, + {1, 9, 10, 27, 46}, + {9, 20, 21, 27, 47}, + {1, 7, 31, 40, 45}, + {13, 38, 39, 46, 49}, + {2, 17, 31, 39, 43}, + {3, 9, 24, 25, 42}, + {4, 5, 7, 30, 49}, + {13, 15, 26, 44, 46}, + {16, 20, 30, 38, 43}, + {3, 30, 33, 39, 46}, + {21, 30, 40, 42, 43}, + {0, 1, 17, 40, 50}, + {8, 19, 20, 34, 50}, + {0, 6, 26, 29, 38}, + {8, 12, 23, 47, 48}, + {5, 11, 15, 31, 35}, + {2, 28, 35, 38, 50}, + {25, 26, 34, 36, 39}, + {6, 18, 31, 34, 35}, + {1, 17, 26, 36, 40}, + {6, 19, 34, 39, 42}, + {1, 18, 40, 43, 48}, + {5, 8, 27, 40, 50}, + {3, 17, 19, 29, 37}, + {14, 18, 27, 43, 50}, + {8, 39, 45, 47, 48}, + {1, 14, 17, 28, 34}, + {8, 13, 16, 29, 50}, + {1, 7, 14, 26, 38}, + {3, 6, 26, 31, 45}, + {8, 13, 17, 20, 39}, + {10, 16, 21, 27, 42}, + {2, 7, 10, 31, 33}, + {4, 13, 17, 20, 48}, + {6, 31, 39, 45, 46}, + {7, 8, 34, 41, 50}, + {0, 7, 17, 25, 46}, + {1, 6, 36, 40, 46}, + {3, 7, 14, 24, 27}, + {4, 16, 29, 34, 44}, + {30, 34, 36, 41, 47}, + {8, 20, 39, 44, 47}, + {9, 15, 19, 32, 44}, + {3, 16, 21, 22, 38}, + {0, 17, 32, 40, 45}, + {3, 12, 13, 22, 26}, + {3, 4, 42, 47, 49}, + {1, 3, 8, 11, 34}, + {0, 4, 26, 28, 36}, + {2, 20, 37, 41, 42}, + {6, 7, 16, 30, 33}, + {0, 5, 16, 18, 43}, + {3, 4, 31, 32, 44}, + {6, 13, 16, 22, 32}, + {9, 11, 28, 30, 43}, + {1, 11, 36, 40, 42}, + {4, 6, 30, 33, 37}, + {11, 17, 30, 41, 49}, + {5, 9, 10, 13, 44}, + {12, 16, 19, 39, 45}, + {3, 17, 34, 39, 47}, + {3, 13, 26, 44, 49}, + {19, 21, 25, 29, 32}, + {0, 6, 36, 38, 39}, + {3, 19, 31, 42, 46}, + {1, 17, 18, 44, 48}, + {3, 16, 37, 44, 47}, + {0, 7, 39, 44, 47}, + {6, 15, 20, 45, 47}, + {8, 20, 25, 33, 37}, + {15, 16, 27, 37, 40}, + {4, 18, 22, 28, 30}, + {2, 18, 27, 28, 35}, + {15, 35, 40, 41, 47}, + {2, 5, 8, 39, 44}, + {5, 11, 18, 30, 51}, + {4, 7, 31, 34, 46}, + {0, 25, 33, 46, 50}, + {12, 13, 28, 39, 44}, + {1, 3, 29, 33, 43}, + {5, 28, 30, 37, 43}, + {7, 8, 17, 30, 36}, + {1, 6, 17, 27, 51}, + {1, 2, 25, 26, 27}, + {13, 19, 22, 39, 43}, + {13, 31, 41, 43, 44}, + {6, 19, 25, 29, 40}, + {0, 7, 13, 24, 47}, + {3, 16, 21, 37, 41}, + {0, 6, 15, 27, 39}, + {2, 13, 16, 28, 44}, + {17, 41, 42, 43, 48}, + {3, 14, 18, 25, 31}, + {0, 8, 30, 42, 47}, + {5, 38, 44, 47, 49}, + {2, 16, 18, 24, 42}, + {14, 16, 24, 38, 42}, + {15, 34, 37, 41, 44}, + {7, 19, 31, 34, 47}, + {16, 17, 35, 43, 51}, + {6, 12, 13, 36, 39}, + {4, 16, 29, 33, 36}, + {0, 15, 24, 26, 51}, + {13, 33, 37, 39, 42}, + {1, 9, 27, 33, 36}, + {8, 10, 11, 31, 47}, + {14, 22, 27, 37, 44}, + {7, 16, 24, 34, 47}, + {14, 27, 29, 33, 41}, + {0, 14, 17, 26, 38}, + {14, 27, 31, 39, 49}, + {6, 30, 31, 44, 46}, + {10, 18, 25, 33, 44}, + {4, 17, 18, 21, 46}, + {5, 21, 23, 34, 48}, + {17, 18, 25, 30, 32}, + {12, 14, 22, 31, 40}, + {19, 26, 33, 35, 46}, + {12, 26, 33, 42, 46}, + {8, 27, 33, 40, 50}, + {0, 6, 20, 35, 45}, + {33, 34, 45, 46, 48}, + {9, 15, 21, 41, 50}, + {1, 10, 26, 27, 42}, + {1, 8, 18, 38, 47}, + {2, 8, 12, 21, 22}, + {7, 30, 41, 46, 50}, + {4, 17, 31, 40, 50}, + {0, 26, 29, 44, 50}, + {2, 6, 17, 32, 38}, + {16, 17, 28, 43, 45}, + {3, 18, 33, 44, 49}, + {0, 17, 19, 45, 48}, + {8, 12, 13, 47, 48}, + {7, 14, 20, 37, 47}, + {1, 5, 27, 43, 49}, + {7, 14, 20, 24, 49}, + {1, 16, 42, 45, 49}, + {19, 22, 25, 29, 45}, + {4, 11, 17, 32, 44}, + {3, 7, 34, 39, 42}, + {22, 26, 32, 43, 45}, + {6, 29, 37, 45, 47}, + {8, 21, 29, 32, 40}, + {14, 21, 29, 42, 50}, + {2, 15, 20, 22, 44}, + {20, 22, 36, 39, 46}, + {8, 16, 17, 29, 39}, + {7, 12, 15, 28, 31}, + {2, 6, 7, 21, 32}, + {14, 16, 21, 47, 50}, + {7, 9, 16, 17, 33}, + {5, 15, 22, 28, 29}, + {3, 5, 16, 40, 41}, + {14, 17, 40, 42, 51}, + {2, 20, 27, 28, 37}, + {3, 6, 13, 31, 42}, + {0, 8, 15, 41, 46}, + {1, 26, 28, 29, 41}, + {10, 14, 27, 30, 46}, + {1, 8, 14, 20, 38}, + {2, 38, 40, 41, 50}, + {18, 33, 41, 44, 51}, + {0, 4, 12, 17, 28}, + {23, 25, 31, 44, 47}, + {21, 28, 41, 45, 48}, + {13, 23, 26, 30, 45}, + {17, 24, 25, 26, 30}, + {13, 19, 28, 31, 39}, + {17, 18, 23, 31, 32}, + {3, 19, 36, 42, 51}, + {20, 29, 30, 33, 38}, + {14, 17, 31, 33, 40}, + {6, 10, 27, 34, 45}, + {0, 13, 14, 21, 23}, + {2, 13, 26, 33, 42}, + {20, 37, 39, 46, 48}, + {1, 3, 15, 28, 50}, + {3, 8, 15, 21, 38}, + {2, 7, 8, 31, 47}, + {5, 9, 18, 27, 47}, + {22, 30, 39, 43, 47}, + {20, 32, 34, 44, 47}, + {5, 10, 24, 31, 39}, + {15, 28, 34, 37, 49}, + {13, 18, 24, 27, 40}, + {15, 18, 26, 39, 46}, + {4, 20, 22, 24, 46}, + {2, 4, 22, 31, 44}, + {2, 7, 10, 33, 35}, + {17, 18, 34, 41, 47}, + {8, 14, 32, 34, 51}, + {15, 18, 29, 31, 33}, + {14, 26, 28, 41, 49}, + {0, 4, 15, 17, 40}, + {8, 26, 31, 44, 46}, + {0, 6, 14, 16, 45}, + {1, 15, 20, 33, 44}, + {17, 31, 42, 44, 50}, + {2, 8, 24, 28, 40}, + {6, 18, 31, 34, 38}, + {0, 2, 5, 11, 13}, + {4, 17, 32, 42, 44}, + {10, 16, 22, 32, 45}, + {3, 15, 16, 26, 51}, + {0, 19, 21, 44, 47}, + {0, 7, 9, 28, 46}, + {0, 1, 12, 14, 44}, + {3, 25, 31, 41, 44}, + {1, 7, 27, 43, 49}, + {0, 4, 6, 18, 32}, + {4, 7, 12, 17, 40}, + {2, 6, 43, 45, 47}, + {10, 13, 20, 26, 47}, + {3, 5, 19, 42, 50}, + {2, 5, 18, 19, 20}, + {4, 6, 17, 29, 51}, + {3, 9, 17, 30, 31}, + {29, 34, 35, 41, 42}, + {18, 21, 32, 40, 44}, + {18, 21, 37, 44, 46}, + {14, 20, 25, 34, 46}, + {8, 12, 13, 15, 39}, + {13, 26, 28, 38, 46}, + {9, 15, 28, 29, 50}, + {4, 17, 37, 38, 41}, + {4, 15, 25, 33, 41}, + {5, 14, 20, 40, 50}, + {1, 8, 10, 11, 47}, + {5, 22, 23, 29, 44}, + {1, 8, 23, 31, 40}, + {11, 13, 26, 33, 44}, + {8, 31, 33, 47, 50}, + {11, 20, 33, 36, 51}, + {7, 16, 20, 27, 50}, + {20, 21, 33, 36, 37}, + {2, 3, 13, 29, 43}, + {3, 4, 20, 22, 33}, + {2, 5, 12, 13, 28}, + {0, 7, 12, 26, 37}, + {2, 18, 21, 28, 38}, + {1, 7, 25, 27, 49}, + {2, 8, 16, 19, 32}, + {3, 16, 23, 25, 48}, + {8, 16, 17, 24, 34}, + {8, 10, 21, 25, 48}, + {19, 27, 28, 45, 46}, + {7, 18, 20, 24, 45}, + {17, 30, 47, 50, 51}, + {15, 20, 21, 41, 50}, + {18, 21, 35, 44, 45}, + {10, 22, 34, 46, 47}, + {6, 17, 36, 45, 50}, + {1, 4, 14, 23, 37}, + {0, 17, 30, 40, 48}, + {6, 22, 43, 45, 46}, + {21, 25, 32, 34, 41}, + {12, 18, 20, 27, 44}, + {7, 20, 26, 29, 32}, + {0, 4, 20, 32, 39}, + {5, 6, 11, 20, 31}, + {3, 5, 6, 29, 48}, + {1, 2, 9, 14, 20}, + {1, 8, 18, 28, 34}, + {4, 19, 33, 43, 51}, + {0, 5, 7, 26, 50}, + {8, 10, 21, 38, 43}, + {0, 3, 20, 39, 44}, + {2, 8, 18, 22, 31}, + {3, 11, 26, 29, 49}, + {0, 22, 26, 33, 36}, + {6, 7, 19, 37, 40}, + {0, 3, 5, 31, 48}, + {7, 9, 11, 16, 29}, + {20, 31, 33, 39, 40}, + {0, 2, 7, 28, 34}, + {18, 23, 25, 44, 48}, + {5, 8, 22, 25, 47}, + {1, 21, 34, 49, 50}, + {1, 3, 6, 8, 45}, + {5, 26, 29, 34, 39}, + {7, 25, 26, 32, 39}, + {6, 7, 20, 31, 51}, + {18, 33, 37, 40, 46}, + {2, 14, 16, 19, 28}, + {3, 12, 13, 16, 17}, + {14, 15, 19, 45, 49}, + {12, 14, 15, 28, 42}, + {1, 11, 14, 32, 51}, + {2, 13, 20, 26, 38}, + {3, 8, 21, 23, 37}, + {9, 17, 18, 28, 30}, + {0, 5, 26, 28, 46}, + {8, 24, 25, 34, 41}, + {2, 15, 21, 38, 40}, + {1, 13, 18, 27, 34}, + {6, 7, 16, 20, 47}, + {11, 18, 21, 41, 47}, + {8, 9, 30, 33, 43}, + {6, 10, 19, 24, 29}, + {0, 8, 9, 34, 40}, + {5, 14, 30, 31, 38}, + {4, 7, 19, 27, 43}, + {14, 21, 34, 39, 50}, + {13, 14, 24, 35, 39}, + {10, 11, 17, 30, 35}, + {4, 6, 12, 17, 50}, + {4, 6, 14, 24, 43}, + {8, 12, 21, 22, 41}, + {1, 2, 30, 38, 43}, + {12, 13, 14, 26, 35}, + {0, 26, 28, 44, 50}, + {5, 6, 17, 20, 31}, + {5, 11, 13, 31, 40}, + {7, 27, 36, 40, 50}, + {1, 27, 43, 44, 50}, + {14, 16, 27, 30, 45}, + {13, 25, 26, 42, 50}, + {3, 4, 13, 16, 22}, + {1, 5, 27, 32, 34}, + {6, 12, 15, 32, 39}, + {5, 14, 22, 29, 44}, + {0, 1, 9, 14, 38}, + {8, 15, 17, 27, 40}, + {2, 12, 14, 28, 48}, + {13, 16, 18, 26, 34}, + {4, 7, 16, 36, 46}, + {2, 18, 21, 35, 47}, + {3, 19, 21, 32, 35}, + {3, 10, 14, 28, 41}, + {20, 28, 36, 39, 46}, + {0, 13, 27, 35, 47}, + {9, 17, 36, 40, 43}, + {1, 25, 27, 37, 43}, + {4, 6, 18, 20, 45}, + {14, 19, 32, 39, 43}, + {2, 19, 39, 41, 47}, + {12, 14, 18, 22, 31}, + {1, 8, 14, 17, 42}, + {1, 40, 43, 44, 48}, + {20, 23, 24, 33, 42}, + {0, 8, 20, 22, 34}, + {3, 8, 9, 10, 29}, + {5, 13, 15, 27, 44}, + {1, 15, 40, 49, 50}, + {2, 10, 35, 39, 41}, + {17, 23, 28, 30, 33}, + {5, 16, 23, 24, 29}, + {9, 16, 33, 41, 46}, + {0, 14, 24, 26, 35}, + {0, 13, 32, 41, 46}, + {1, 7, 17, 29, 33}, + {1, 10, 17, 18, 43}, + {3, 8, 22, 42, 45}, + {4, 16, 25, 30, 31}, + {28, 30, 38, 41, 50}, + {1, 8, 11, 15, 41}, + {2, 19, 30, 43, 47}, + {4, 15, 28, 32, 42}, + {3, 29, 38, 44, 46}, + {3, 19, 37, 42, 46}, + {2, 4, 21, 37, 41}, + {4, 22, 37, 41, 43}, + {25, 26, 32, 45, 50}, + {22, 27, 39, 40, 46}, + {3, 17, 20, 29, 41}, + {1, 16, 20, 27, 41}, + {1, 2, 4, 24, 27}, + {1, 14, 15, 16, 18}, + {14, 27, 28, 29, 33}, + {1, 12, 13, 39, 49}, + {4, 10, 31, 33, 44}, + {3, 4, 17, 23, 28}, + {10, 15, 32, 34, 41}, + {7, 11, 27, 38, 40}, + {5, 13, 18, 23, 50}, + {13, 16, 22, 39, 43}, + {4, 21, 25, 33, 43}, + {28, 31, 41, 45, 46}, + {4, 16, 22, 24, 30}, + {5, 8, 24, 47, 48}, + {1, 14, 20, 31, 39}, + {19, 20, 23, 43, 45}, + {7, 33, 34, 38, 45}, + {3, 11, 17, 26, 42}, + {1, 13, 22, 34, 39}, + {3, 9, 19, 45, 46}, + {3, 4, 6, 42, 44}, + {25, 30, 32, 43, 50}, + {0, 17, 20, 41, 46}, + {2, 23, 27, 31, 44}, + {0, 4, 11, 12, 43}, + {5, 6, 13, 17, 31}, + {15, 17, 19, 30, 39}, + {15, 19, 27, 40, 50}, + {7, 11, 20, 28, 32}, + {11, 21, 25, 32, 47}, + {1, 14, 21, 41, 49}, + {17, 31, 36, 43, 50}, + {6, 20, 30, 32, 37}, + {1, 25, 27, 30, 41}, + {7, 9, 17, 27, 30}, + {14, 26, 28, 39, 45}, + {3, 4, 16, 25, 44}, + {1, 15, 28, 31, 51}, + {3, 10, 33, 37, 42}, + {3, 26, 29, 41, 46}, + {1, 14, 33, 34, 43}, + {24, 26, 27, 35, 39}, + {3, 9, 10, 29, 37}, + {6, 20, 28, 32, 38}, + {13, 16, 19, 21, 32}, + {2, 7, 14, 25, 33}, + {6, 8, 13, 31, 32}, + {2, 6, 11, 25, 45}, + {6, 17, 27, 40, 50}, + {15, 19, 44, 45, 51}, + {3, 8, 10, 31, 44}, + {8, 21, 36, 42, 51}, + {12, 14, 18, 35, 44}, + {9, 11, 32, 38, 45}, + {6, 9, 27, 30, 45}, + {0, 2, 5, 37, 41}, + {21, 30, 34, 36, 41}, + {0, 1, 17, 18, 43}, + {5, 15, 29, 44, 51}, + {1, 27, 30, 36, 41}, + {1, 10, 14, 16, 31}, + {14, 25, 40, 43, 48}, + {13, 22, 39, 44, 46}, + {2, 19, 21, 25, 47}, + {0, 4, 9, 12, 43}, + {28, 29, 34, 36, 41}, + {7, 20, 22, 41, 45}, + {2, 15, 24, 26, 45}, + {12, 16, 24, 26, 42}, + {0, 1, 16, 27, 43}, + {17, 27, 28, 32, 40}, + {4, 14, 19, 20, 46}, + {6, 20, 42, 46, 47}, + {5, 12, 28, 36, 44}, + {5, 21, 31, 46, 50}, + {1, 8, 17, 27, 45}, + {0, 3, 6, 16, 50}, + {14, 33, 40, 48, 51}, + {2, 25, 34, 41, 50}, + {15, 34, 40, 47, 50}, + {3, 9, 28, 42, 43}, + {7, 12, 21, 28, 47}, + {4, 8, 20, 42, 43}, + {3, 12, 19, 22, 45}, + {7, 20, 31, 41, 48}, + {6, 25, 28, 42, 45}, + {0, 14, 15, 26, 31}, + {4, 11, 20, 31, 33}, + {3, 23, 29, 35, 38}, + {2, 6, 24, 43, 45}, + {7, 19, 26, 32, 38}, + {0, 4, 8, 19, 21}, + {5, 18, 29, 43, 48}, + {4, 9, 15, 18, 28}, + {2, 20, 33, 40, 50}, + {19, 27, 30, 42, 45}, + {4, 7, 17, 48, 51}, + {3, 11, 28, 29, 32}, + {3, 5, 10, 16, 45}, + {16, 18, 25, 29, 46}, + {9, 11, 17, 27, 43}, + {1, 2, 14, 42, 47}, + {12, 13, 22, 27, 39}, + {2, 7, 15, 34, 38}, + {2, 20, 33, 40, 51}, + {8, 11, 18, 30, 43}, + {0, 10, 14, 27, 38}, + {5, 7, 33, 42, 45}, + {3, 10, 16, 27, 39}, + {7, 15, 16, 29, 34}, + {0, 14, 21, 32, 45}, + {17, 22, 29, 34, 42}, + {5, 10, 28, 31, 42}, + {4, 5, 6, 15, 32}, + {29, 34, 43, 46, 47}, + {13, 14, 27, 45, 48}, + {3, 24, 29, 41, 49}, + {8, 13, 24, 34, 45}, + {18, 32, 41, 43, 45}, + {13, 18, 23, 26, 30}, + {3, 16, 31, 38, 49}, + {12, 17, 33, 43, 50}, + {3, 31, 34, 41, 47}, + {1, 12, 17, 30, 49}, + {13, 17, 29, 33, 43}, + {6, 16, 32, 33, 37}, + {15, 28, 32, 35, 47}, + {21, 25, 26, 27, 47}, + {1, 3, 22, 29, 32}, + {3, 33, 34, 41, 46}, + {1, 12, 27, 30, 49}, + {5, 23, 40, 44, 46}, + {1, 3, 23, 29, 41}, + {16, 26, 27, 35, 42}, + {5, 18, 21, 40, 43}, + {18, 19, 36, 38, 44}, + {1, 6, 19, 24, 41}, + {3, 16, 31, 43, 48}, + {0, 29, 42, 49, 50}, + {13, 15, 18, 30, 43}, + {3, 4, 14, 28, 30}, + {5, 8, 10, 18, 24}, + {4, 7, 19, 29, 32}, + {8, 21, 26, 31, 48}, + {5, 19, 41, 43, 45}, + {8, 26, 32, 34, 37}, + {4, 18, 25, 42, 43}, + {10, 19, 20, 25, 33}, + {3, 5, 26, 28, 42}, + {1, 2, 13, 16, 40}, + {4, 5, 17, 38, 49}, + {0, 11, 15, 22, 28}, + {1, 2, 16, 28, 44}, + {1, 18, 19, 27, 34}, + {10, 13, 20, 38, 46}, + {2, 3, 21, 28, 45}, + {4, 7, 18, 38, 43}, + {10, 15, 28, 42, 44}, + {0, 11, 15, 28, 45}, + {17, 31, 33, 42, 46}, + {11, 13, 32, 36, 39}, + {0, 15, 30, 41, 49}, + {1, 6, 12, 14, 49}, + {0, 2, 8, 13, 44}, + {0, 11, 20, 26, 27}, + {6, 13, 21, 38, 47}, + {4, 11, 13, 18, 43}, + {5, 28, 32, 40, 41}, + {7, 10, 19, 27, 45}, + {2, 18, 22, 26, 44}, + {0, 12, 39, 43, 48}, + {1, 15, 34, 40, 46}, + {24, 28, 41, 43, 49}, + {4, 11, 23, 30, 42}, + {0, 6, 8, 43, 45}, + {8, 12, 20, 21, 48}, + {2, 9, 16, 29, 31}, + {2, 3, 4, 6, 42}, + {2, 25, 27, 40, 48}, + {1, 9, 27, 36, 37}, + {12, 17, 30, 35, 50}, + {1, 34, 37, 39, 40}, + {15, 21, 32, 34, 43}, + {2, 23, 28, 38, 42}, + {0, 11, 14, 26, 31}, + {15, 20, 24, 28, 29}, + {10, 26, 29, 39, 41}, + {4, 9, 13, 23, 39}, + {17, 27, 28, 42, 43}, + {14, 20, 22, 39, 40}, + {0, 1, 17, 37, 39}, + {3, 19, 30, 32, 48}, + {4, 8, 10, 13, 34}, + {0, 39, 42, 44, 47}, + {4, 17, 18, 34, 46}, + {17, 18, 26, 43, 48}, + {6, 16, 21, 45, 50}, + {23, 26, 34, 38, 47}, + {8, 21, 38, 46, 50}, + {7, 26, 33, 38, 42}, + {0, 3, 5, 29, 32}, + {3, 4, 28, 31, 43}, + {1, 3, 17, 29, 39}, + {1, 14, 38, 42, 47}, + {4, 9, 29, 43, 50}, + {4, 10, 14, 26, 39}, + {4, 17, 27, 37, 41}, + {13, 26, 35, 38, 45}, + {0, 11, 16, 36, 42}, + {7, 13, 17, 43, 51}, + {0, 11, 21, 26, 49}, + {6, 22, 28, 32, 40}, + {6, 15, 22, 32, 42}, + {16, 22, 34, 42, 44}, + {8, 18, 25, 30, 43}, + {2, 6, 9, 32, 42}, + {13, 19, 39, 42, 49}, + {0, 8, 26, 29, 37}, + {17, 27, 38, 40, 46}, + {0, 5, 15, 25, 31}, + {4, 9, 43, 47, 50}, + {7, 18, 32, 33, 41}, + {16, 19, 29, 39, 40}, + {1, 16, 31, 42, 43}, + {1, 15, 16, 27, 49}, + {8, 21, 30, 44, 51}, + {2, 8, 20, 21, 50}, + {5, 20, 31, 37, 45}, + {14, 19, 21, 34, 39}, + {3, 11, 19, 29, 44}, + {0, 6, 13, 18, 36}, + {30, 38, 39, 42, 43}, + {8, 20, 23, 29, 33}, + {2, 3, 26, 31, 42}, + {8, 14, 26, 29, 34}, + {3, 6, 8, 31, 45}, + {20, 24, 33, 44, 51}, + {18, 19, 22, 23, 32}, + {4, 15, 30, 33, 51}, + {5, 15, 23, 28, 43}, + {11, 20, 25, 26, 33}, + {4, 17, 36, 39, 46}, + {0, 10, 14, 22, 27}, + {14, 15, 16, 34, 42}, + {3, 5, 7, 22, 44}, + {1, 13, 33, 43, 46}, + {3, 20, 21, 27, 40}, + {7, 8, 12, 17, 43}, + {0, 37, 39, 41, 42}, + {13, 17, 20, 31, 46}, + {12, 14, 37, 40, 46}, + {0, 12, 39, 43, 49}, + {1, 5, 14, 39, 45}, + {19, 20, 29, 31, 45}, + {0, 14, 31, 44, 45}, + {6, 15, 35, 43, 45}, + {18, 29, 31, 34, 48}, + {0, 10, 28, 33, 46}, + {18, 27, 29, 40, 45}, + {4, 5, 13, 34, 43}, + {29, 32, 40, 42, 44}, + {18, 22, 29, 31, 37}, + {19, 39, 42, 45, 51}, + {4, 21, 27, 34, 45}, + {0, 3, 16, 19, 36}, + {11, 26, 27, 35, 40}, + {1, 8, 15, 27, 33}, + {14, 15, 35, 40, 47}, + {0, 2, 8, 26, 38}, + {1, 5, 18, 41, 42}, + {2, 4, 28, 39, 51}, + {4, 20, 30, 31, 39}, + {0, 5, 44, 48, 51}, + {2, 5, 15, 32, 37}, + {9, 14, 15, 28, 47}, + {16, 36, 39, 40, 42}, + {9, 11, 26, 31, 44}, + {20, 26, 32, 35, 39}, + {3, 33, 39, 41, 46}, + {0, 7, 13, 18, 49}, + {0, 6, 32, 33, 34}, + {12, 21, 22, 34, 43}, + {21, 27, 34, 36, 37}, + {6, 32, 36, 42, 44}, + {1, 5, 17, 42, 43}, + {3, 7, 30, 34, 43}, + {10, 18, 33, 39, 46}, + {15, 28, 31, 34, 51}, + {3, 28, 31, 40, 41}, + {6, 14, 15, 28, 35}, + {10, 13, 26, 30, 47}, + {7, 21, 23, 47, 51}, + {2, 6, 28, 44, 51}, + {1, 4, 5, 28, 31}, + {4, 5, 7, 21, 30}, + {17, 24, 26, 33, 39}, + {4, 20, 28, 30, 48}, + {5, 13, 27, 40, 41}, + {21, 25, 27, 33, 34}, + {5, 31, 33, 41, 47}, + {2, 7, 43, 46, 50}, + {4, 7, 26, 46, 49}, + {3, 13, 25, 26, 45}, + {2, 11, 28, 30, 31}, + {17, 23, 26, 43, 45}, + {20, 27, 40, 41, 51}, + {9, 11, 18, 31, 39}, + {13, 28, 41, 42, 51}, + {1, 5, 20, 23, 40}, + {0, 1, 26, 38, 45}, + {2, 7, 20, 23, 44}, + {2, 15, 21, 35, 36}, + {3, 5, 15, 19, 42}, + {20, 33, 35, 41, 44}, + {3, 5, 13, 38, 42}, + {2, 12, 13, 19, 26}, + {9, 14, 15, 21, 47}, + {1, 3, 18, 22, 42}, + {3, 4, 5, 29, 36}, + {4, 10, 37, 42, 43}, + {12, 20, 21, 29, 47}, + {5, 15, 17, 27, 40}, + {4, 27, 35, 40, 49}, + {5, 14, 19, 27, 43}, + {15, 29, 34, 37, 42}, + {1, 15, 25, 40, 46}, + {13, 16, 22, 36, 39}, + {4, 5, 17, 20, 36}, + {8, 16, 25, 34, 40}, + {2, 27, 33, 40, 43}, + {3, 14, 32, 39, 42}, + {0, 12, 28, 36, 39}, + {1, 10, 13, 26, 38}, + {18, 26, 31, 48, 49}, + {7, 16, 24, 44, 46}, + {7, 18, 37, 46, 48}, + {0, 1, 8, 14, 31}, + {6, 13, 34, 37, 39}, + {0, 7, 39, 44, 50}, + {2, 5, 16, 27, 28}, + {1, 5, 24, 27, 29}, + {5, 14, 21, 30, 44}, + {10, 28, 34, 47, 50}, + {3, 6, 20, 32, 44}, + {11, 14, 20, 33, 43}, + {6, 19, 28, 31, 50}, + {13, 18, 20, 31, 50}, + {7, 16, 20, 27, 37}, + {2, 17, 27, 33, 40}, + {7, 11, 33, 38, 45}, + {6, 8, 21, 28, 42}, + {2, 7, 15, 29, 39}, + {5, 7, 13, 36, 46}, + {14, 22, 26, 27, 42}, + {2, 16, 23, 32, 41}, + {4, 26, 29, 31, 39}, + {7, 13, 27, 40, 48}, + {5, 15, 17, 18, 36}, + {13, 19, 28, 36, 45}, + {2, 6, 16, 41, 48}, + {1, 14, 20, 30, 31}, + {1, 8, 41, 46, 47}, + {1, 12, 17, 40, 48}, + {10, 17, 19, 25, 43}, + {1, 20, 34, 37, 47}, + {0, 4, 14, 32, 45}, + {2, 8, 16, 31, 44}, + {2, 15, 30, 35, 38}, + {21, 28, 29, 34, 44}, + {1, 22, 26, 38, 40}, + {4, 20, 24, 30, 47}, + {4, 17, 46, 47, 50}, + {1, 7, 10, 20, 35}, + {4, 18, 19, 23, 43}, + {8, 12, 21, 35, 41}, + {4, 13, 16, 18, 31}, + {27, 28, 34, 42, 47}, + {13, 17, 26, 42, 44}, + {3, 21, 27, 46, 47}, + {19, 24, 25, 32, 46}, + {2, 14, 16, 40, 49}, + {1, 20, 33, 41, 47}, + {8, 11, 13, 15, 26}, + {0, 29, 35, 39, 51}, + {2, 8, 15, 30, 48}, + {26, 33, 41, 46, 47}, + {8, 16, 23, 28, 47}, + {7, 31, 37, 45, 46}, + {5, 12, 20, 21, 33}, + {1, 15, 22, 24, 27}, + {3, 5, 40, 44, 45}, + {0, 13, 25, 33, 36}, + {5, 8, 20, 47, 51}, + {0, 8, 17, 34, 44}, + {0, 1, 2, 27, 35}, + {9, 18, 21, 25, 47}, + {0, 3, 4, 8, 21}, + {15, 20, 24, 32, 41}, + {10, 16, 41, 42, 51}, + {2, 4, 30, 42, 51}, + {1, 8, 19, 31, 45}, + {8, 19, 28, 31, 34}, + {1, 8, 9, 16, 42}, + {0, 14, 27, 41, 51}, + {3, 5, 23, 42, 48}, + {20, 24, 27, 40, 45}, + {1, 7, 19, 22, 40}, + {0, 10, 30, 35, 39}, + {8, 19, 24, 31, 34}, + {26, 32, 33, 46, 50}, + {0, 2, 3, 18, 28}, + {2, 7, 20, 40, 50}, + {15, 33, 39, 41, 50}, + {6, 7, 8, 31, 47}, + {6, 25, 32, 41, 50}, + {0, 2, 10, 15, 42}, + {5, 30, 36, 42, 43}, + {17, 25, 30, 39, 44}, + {5, 15, 22, 31, 43}, + {22, 29, 36, 42, 45}, + {14, 19, 28, 33, 45}, + {15, 24, 29, 31, 41}, + {2, 5, 12, 23, 44}, + {1, 6, 19, 24, 33}, + {1, 14, 21, 30, 45}, + {13, 18, 28, 30, 43}, + {5, 11, 27, 29, 31}, + {14, 15, 29, 42, 46}, + {6, 7, 24, 26, 45}, + {11, 13, 15, 41, 47}, + {12, 21, 28, 31, 34}, + {17, 23, 24, 34, 43}, + {16, 17, 34, 41, 47}, + {1, 2, 4, 24, 43}, + {8, 12, 22, 47, 50}, + {4, 12, 13, 17, 32}, + {5, 17, 18, 33, 48}, + {5, 8, 18, 29, 49}, + {8, 16, 25, 32, 34}, + {2, 13, 32, 43, 45}, + {9, 14, 16, 19, 29}, + {4, 21, 41, 45, 47}, + {8, 9, 31, 42, 47}, + {8, 34, 48, 50, 51}, + {7, 14, 15, 27, 34}, + {16, 41, 42, 45, 49}, + {1, 9, 12, 14, 34}, + {13, 25, 33, 37, 39}, + {8, 14, 38, 40, 43}, + {1, 7, 12, 29, 46}, + {27, 29, 32, 41, 45}, + {1, 10, 12, 13, 27}, + {1, 2, 18, 41, 49}, + {7, 21, 27, 40, 48}, + {5, 10, 18, 21, 39}, + {0, 28, 30, 43, 51}, + {27, 35, 39, 40, 44}, + {11, 26, 28, 40, 41}, + {27, 31, 36, 42, 44}, + {1, 9, 17, 23, 43}, + {20, 32, 33, 39, 49}, + {7, 30, 41, 42, 46}, + {8, 18, 29, 45, 47}, + {19, 21, 26, 36, 47}, + {3, 19, 28, 37, 42}, + {9, 12, 16, 17, 29}, + {0, 3, 4, 14, 26}, + {0, 9, 13, 15, 24}, + {2, 15, 20, 29, 51}, + {1, 20, 22, 27, 50}, + {5, 17, 18, 20, 49}, + {0, 6, 18, 25, 31}, + {5, 27, 31, 38, 49}, + {8, 17, 18, 21, 22}, + {3, 6, 25, 32, 34}, + {19, 20, 24, 25, 33}, + {20, 28, 41, 50, 51}, + {7, 13, 29, 43, 46}, + {12, 19, 23, 32, 47}, + {1, 27, 34, 37, 44}, + {8, 9, 14, 15, 28}, + {23, 24, 26, 39, 51}, + {4, 5, 6, 15, 45}, + {14, 29, 30, 40, 46}, + {10, 11, 28, 41, 43}, + {0, 6, 21, 36, 47}, + {2, 14, 24, 34, 40}, + {7, 18, 33, 43, 49}, + {0, 15, 16, 26, 33}, + {4, 27, 32, 40, 47}, + {3, 7, 16, 34, 37}, + {21, 28, 32, 44, 45}, + {3, 18, 25, 27, 40}, + {3, 15, 20, 41, 43}, + {8, 16, 20, 21, 38}, + {2, 5, 29, 31, 38}, + {15, 18, 26, 41, 46}, + {4, 8, 12, 30, 39}, + {1, 3, 8, 12, 16}, + {15, 20, 21, 30, 46}, + {5, 6, 32, 46, 47}, + {6, 32, 34, 36, 39}, + {8, 19, 20, 44, 46}, + {12, 20, 22, 29, 33}, + {6, 18, 20, 46, 51}, + {0, 7, 12, 13, 42}, + {16, 18, 39, 42, 47}, + {1, 29, 36, 42, 46}, + {16, 20, 42, 43, 49}, + {0, 9, 26, 27, 49}, + {13, 17, 32, 39, 48}, + {3, 21, 23, 32, 45}, + {9, 25, 26, 31, 44}, + {23, 32, 34, 46, 47}, + {5, 8, 29, 39, 44}, + {2, 8, 11, 16, 42}, + {7, 8, 14, 45, 46}, + {14, 15, 25, 29, 42}, + {0, 7, 34, 43, 47}, + {6, 32, 37, 39, 44}, + {6, 15, 32, 34, 35}, + {3, 12, 16, 21, 30}, + {1, 3, 13, 26, 48}, + {7, 12, 21, 34, 41}, + {11, 26, 31, 32, 39}, + {1, 23, 27, 42, 44}, + {1, 7, 11, 19, 33}, + {0, 5, 17, 28, 41}, + {1, 5, 7, 13, 44}, + {8, 19, 23, 43, 45}, + {2, 6, 39, 45, 51}, + {3, 7, 14, 15, 46}, + {1, 17, 24, 43, 46}, + {11, 19, 32, 33, 42}, + {13, 21, 25, 28, 39}, + {5, 25, 31, 43, 45}, + {2, 9, 13, 26, 43}, + {6, 19, 30, 48, 50}, + {5, 6, 25, 45, 46}, + {18, 19, 23, 26, 32}, + {1, 7, 25, 33, 47}, + {0, 1, 2, 16, 41}, + {2, 38, 41, 42, 48}, + {28, 34, 36, 42, 47}, + {3, 24, 27, 30, 40}, + {28, 32, 35, 41, 43}, + {13, 26, 33, 38, 48}, + {6, 16, 28, 45, 48}, + {29, 31, 40, 44, 51}, + {2, 13, 22, 28, 38}, + {0, 13, 17, 34, 51}, + {6, 10, 18, 45, 47}, + {0, 23, 28, 31, 41}, + {6, 10, 30, 37, 45}, + {2, 7, 28, 36, 51}, + {7, 26, 34, 36, 46}, + {6, 7, 13, 19, 34}, + {3, 15, 22, 29, 31}, + {5, 28, 33, 37, 46}, + {4, 9, 33, 44, 46}, + {15, 18, 21, 26, 47}, + {2, 16, 27, 28, 51}, + {1, 13, 16, 26, 41}, + {19, 30, 33, 41, 45}, + {15, 23, 27, 35, 41}, + {0, 5, 11, 41, 44}, + {18, 22, 31, 40, 43}, + {1, 2, 17, 32, 40}, + {2, 5, 17, 23, 41}, + {3, 5, 23, 29, 39}, + {15, 16, 17, 23, 42}, + {13, 20, 33, 42, 47}, + {1, 17, 28, 31, 44}, + {8, 17, 18, 30, 33}, + {14, 27, 28, 31, 38}, + {7, 18, 24, 39, 44}, + {1, 4, 13, 26, 42}, + {7, 26, 35, 39, 41}, + {0, 13, 15, 23, 32}, + {0, 17, 21, 35, 39}, + {1, 19, 27, 31, 36}, + {7, 12, 19, 33, 42}, + {16, 17, 21, 36, 42}, + {14, 19, 25, 43, 45}, + {4, 12, 15, 22, 28}, + {3, 14, 30, 34, 42}, + {4, 14, 19, 30, 41}, + {5, 29, 35, 37, 44}, + {24, 28, 29, 38, 41}, + {16, 18, 29, 34, 35}, + {14, 33, 36, 40, 48}, + {8, 12, 21, 26, 37}, + {2, 11, 15, 39, 47}, + {14, 16, 29, 41, 48}, + {6, 18, 34, 42, 45}, + {4, 21, 30, 49, 50}, + {14, 18, 19, 25, 32}, + {9, 14, 27, 37, 44}, + {0, 5, 6, 22, 31}, + {4, 17, 26, 37, 51}, + {6, 8, 15, 19, 23}, + {7, 8, 22, 31, 33}, + {1, 19, 27, 35, 50}, + {7, 9, 24, 45, 46}, + {3, 16, 22, 30, 34}, + {0, 7, 9, 26, 29}, + {4, 19, 26, 30, 48}, + {20, 33, 41, 43, 50}, + {11, 22, 32, 34, 47}, + {1, 10, 17, 27, 46}, + {11, 12, 27, 33, 46}, + {5, 13, 35, 39, 42}, + {6, 13, 32, 48, 51}, + {15, 19, 24, 30, 41}, + {4, 8, 29, 43, 49}, + {16, 20, 39, 42, 44}, + {8, 17, 26, 39, 51}, + {17, 19, 32, 34, 37}, + {6, 12, 21, 27, 47}, + {2, 10, 20, 34, 41}, + {25, 33, 34, 46, 49}, + {0, 4, 6, 30, 41}, + {1, 6, 14, 17, 18}, + {0, 3, 6, 28, 42}, + {12, 16, 37, 42, 43}, + {2, 8, 11, 30, 43}, + {4, 14, 15, 24, 30}, + {1, 7, 20, 34, 45}, + {4, 9, 12, 28, 30}, + {7, 12, 13, 18, 44}, + {5, 18, 23, 26, 50}, + {4, 14, 19, 27, 41}, + {6, 9, 31, 37, 44}, + {6, 24, 33, 44, 46}, + {3, 7, 16, 18, 50}, + {5, 9, 29, 41, 42}, + {4, 9, 17, 40, 50}, + {17, 21, 30, 31, 49}, + {0, 14, 30, 34, 39}, + {17, 28, 31, 41, 47}, + {0, 8, 16, 34, 51}, + {1, 11, 19, 43, 45}, + {18, 19, 22, 32, 43}, + {4, 14, 32, 35, 45}, + {1, 14, 19, 44, 49}, + {8, 12, 17, 44, 47}, + {1, 23, 28, 30, 41}, + {5, 19, 21, 31, 38}, + {5, 18, 27, 41, 47}, + {8, 16, 18, 42, 45}, + {1, 5, 9, 18, 19}, + {8, 26, 27, 36, 39}, + {4, 8, 20, 46, 49}, + {6, 14, 19, 28, 51}, + {2, 6, 14, 31, 40}, + {18, 40, 41, 44, 49}, + {0, 9, 26, 28, 47}, + {3, 7, 24, 30, 46}, + {15, 27, 28, 29, 35}, + {17, 21, 24, 30, 46}, + {5, 14, 24, 40, 51}, + {1, 3, 14, 21, 26}, + {6, 19, 26, 47, 48}, + {2, 8, 26, 34, 40}, + {7, 22, 24, 29, 42}, + {4, 6, 9, 12, 43}, + {13, 29, 32, 39, 49}, + {14, 18, 30, 40, 48}, + {3, 11, 29, 35, 39}, + {3, 8, 12, 47, 50}, + {14, 17, 18, 32, 43}, + {15, 28, 44, 45, 47}, + {0, 15, 28, 37, 43}, + {18, 26, 30, 34, 39}, + {20, 43, 46, 49, 51}, + {14, 27, 30, 34, 46}, + {8, 9, 25, 27, 47}, + {28, 39, 41, 42, 46}, + {5, 13, 14, 31, 41}, + {3, 6, 13, 27, 29}, + {20, 23, 27, 40, 47}, + {0, 5, 16, 37, 39}, + {1, 15, 20, 28, 43}, + {0, 2, 3, 8, 16}, + {4, 7, 17, 31, 41}, + {3, 7, 15, 29, 40}, + {4, 7, 24, 33, 40}, + {0, 1, 13, 18, 45}, + {8, 15, 28, 32, 33}, + {1, 8, 23, 40, 42}, + {5, 14, 34, 39, 44}, + {5, 14, 17, 27, 42}, + {3, 5, 16, 39, 51}, + {2, 8, 27, 41, 45}, + {2, 29, 41, 45, 50}, + {2, 19, 21, 47, 50}, + {7, 16, 23, 29, 31}, + {17, 25, 30, 42, 50}, + {15, 36, 40, 41, 46}, + {0, 4, 13, 33, 40}, + {18, 26, 31, 32, 34}, + {20, 31, 38, 41, 44}, + {1, 14, 16, 38, 45}, + {4, 9, 24, 29, 42}, + {6, 19, 35, 46, 50}, + {6, 10, 11, 43, 45}, + {25, 28, 29, 36, 42}, + {5, 16, 18, 30, 40}, + {4, 7, 16, 18, 31}, + {12, 15, 24, 41, 43}, + {3, 12, 16, 19, 28}, + {8, 22, 34, 38, 45}, + {7, 22, 32, 33, 43}, + {8, 20, 33, 36, 48}, + {8, 27, 30, 33, 34}, + {25, 29, 33, 42, 45}, + {6, 8, 47, 49, 51}, + {8, 10, 29, 44, 47}, + {3, 8, 29, 31, 50}, + {0, 27, 28, 30, 43}, + {0, 2, 11, 19, 32}, + {30, 34, 46, 47, 49}, + {5, 6, 18, 27, 42}, + {8, 14, 45, 47, 50}, + {12, 17, 35, 42, 43}, + {4, 21, 30, 46, 49}, + {2, 16, 23, 29, 48}, + {9, 17, 42, 43, 50}, + {7, 12, 21, 23, 33}, + {13, 14, 16, 27, 41}, + {8, 14, 30, 47, 48}, + {1, 22, 30, 34, 40}, + {4, 7, 13, 14, 39}, + {16, 36, 41, 42, 47}, + {2, 8, 10, 28, 37}, + {2, 16, 17, 18, 41}, + {7, 13, 15, 28, 50}, + {4, 19, 30, 31, 42}, + {4, 16, 28, 41, 51}, + {8, 31, 43, 44, 51}, + {10, 19, 34, 44, 45}, + {0, 18, 26, 35, 43}, + {4, 7, 18, 30, 39}, + {3, 7, 8, 21, 32}, + {5, 6, 32, 40, 43}, + {1, 10, 26, 27, 34}, + {5, 8, 12, 26, 47}, + {1, 17, 24, 30, 45}, + {0, 5, 18, 23, 46}, + {2, 22, 28, 29, 45}, + {18, 20, 28, 37, 44}, + {8, 10, 21, 26, 35}, + {17, 18, 26, 30, 50}, + {16, 34, 43, 47, 48}, + {0, 13, 15, 38, 49}, + {0, 11, 14, 27, 48}, + {1, 4, 9, 30, 34}, + {19, 32, 40, 42, 43}, + {0, 1, 7, 26, 34}, + {3, 4, 36, 43, 47}, + {3, 16, 37, 39, 41}, + {21, 28, 39, 41, 49}, + {12, 27, 29, 32, 40}, + {7, 11, 18, 45, 46}, + {1, 12, 17, 30, 50}, + {3, 10, 14, 15, 16}, + {7, 19, 27, 36, 40}, + {11, 20, 31, 46, 49}, + {8, 15, 19, 20, 21}, + {10, 17, 22, 32, 45}, + {4, 31, 43, 45, 49}, + {6, 18, 22, 34, 44}, + {3, 7, 11, 33, 40}, + {3, 11, 13, 26, 48}, + {7, 13, 17, 32, 39}, + {18, 42, 44, 48, 51}, + {13, 20, 29, 32, 45}, + {7, 11, 13, 22, 26}, + {5, 10, 31, 47, 50}, + {21, 28, 40, 42, 47}, + {3, 5, 12, 13, 44}, + {8, 15, 34, 40, 44}, + {7, 14, 15, 33, 47}, + {2, 16, 39, 41, 51}, + {4, 5, 13, 14, 44}, + {6, 27, 32, 36, 41}, + {2, 4, 24, 33, 43}, + {2, 10, 41, 45, 46}, + {1, 8, 10, 27, 44}, + {18, 26, 34, 45, 47}, + {18, 21, 38, 39, 47}, + {2, 15, 24, 25, 45}, + {15, 20, 24, 41, 43}, + {0, 1, 11, 16, 39}, + {2, 15, 30, 31, 46}, + {17, 30, 37, 42, 44}, + {18, 19, 32, 35, 46}, + {9, 15, 23, 37, 41}, + {0, 10, 26, 34, 38}, + {13, 35, 39, 50, 51}, + {25, 31, 39, 40, 44}, + {0, 7, 9, 25, 39}, + {3, 13, 19, 29, 40}, + {4, 6, 12, 13, 19}, + {2, 13, 19, 28, 49}, + {4, 7, 19, 28, 33}, + {20, 33, 38, 42, 43}, + {20, 22, 31, 38, 44}, + {0, 17, 22, 39, 45}, + {0, 15, 21, 22, 28}, + {0, 7, 34, 40, 46}, + {2, 8, 37, 47, 51}, + {2, 3, 8, 28, 51}, + {7, 29, 46, 47, 50}, + {19, 26, 32, 33, 47}, + {0, 37, 39, 40, 45}, + {0, 30, 39, 40, 51}, + {7, 23, 30, 32, 45}, + {14, 15, 27, 33, 42}, + {3, 14, 27, 48, 49}, + {14, 17, 22, 24, 27}, + {1, 6, 29, 31, 44}, + {0, 3, 15, 31, 44}, + {14, 18, 21, 34, 35}, + {4, 10, 14, 16, 29}, + {1, 3, 42, 47, 48}, + {2, 3, 20, 43, 46}, + {12, 18, 19, 32, 46}, + {1, 6, 19, 41, 46}, + {1, 24, 27, 32, 44}, + {0, 3, 11, 27, 39}, + {7, 15, 21, 27, 34}, + {8, 23, 32, 41, 45}, + {19, 27, 30, 33, 45}, + {6, 15, 26, 30, 45}, + {7, 14, 27, 41, 51}, + {7, 21, 28, 33, 51}, + {1, 7, 31, 32, 46}, + {16, 26, 29, 33, 41}, + {7, 19, 20, 30, 35}, + {17, 20, 24, 33, 44}, + {6, 8, 17, 21, 40}, + {15, 28, 30, 33, 42}, + {8, 10, 31, 44, 48}, + {1, 6, 9, 27, 47}, + {5, 7, 13, 31, 37}, + {23, 26, 29, 32, 42}, + {11, 14, 18, 35, 40}, + {2, 5, 6, 31, 49}, + {18, 20, 35, 36, 46}, + {6, 11, 26, 32, 40}, + {19, 30, 37, 45, 51}, + {1, 24, 29, 42, 51}, + {2, 27, 30, 33, 46}, + {2, 15, 27, 33, 36}, + {7, 10, 14, 27, 47}, + {5, 7, 25, 32, 33}, + {7, 10, 14, 20, 50}, + {4, 5, 7, 26, 30}, + {2, 12, 28, 33, 34}, + {5, 29, 32, 33, 45}, + {0, 2, 14, 15, 34}, + {4, 14, 20, 23, 27}, + {17, 30, 33, 50, 51}, + {13, 20, 23, 33, 35}, + {1, 16, 18, 39, 42}, + {7, 20, 22, 31, 39}, + {21, 26, 39, 42, 45}, + {3, 7, 15, 33, 39}, + {2, 16, 37, 41, 44}, + {1, 6, 28, 34, 40}, + {0, 2, 29, 41, 51}, + {2, 15, 22, 25, 40}, + {0, 2, 15, 37, 51}, + {0, 1, 8, 31, 47}, + {1, 19, 25, 27, 33}, + {9, 29, 41, 42, 46}, + {5, 6, 19, 34, 50}, + {10, 32, 38, 39, 45}, + {7, 9, 20, 25, 40}, + {5, 12, 19, 30, 45}, + {3, 4, 24, 42, 47}, + {0, 3, 27, 35, 40}, + {7, 8, 32, 34, 50}, + {8, 9, 12, 16, 42}, + {5, 11, 16, 26, 42}, + {21, 22, 23, 34, 37}, + {3, 19, 30, 31, 32}, + {3, 11, 15, 39, 42}, + {6, 8, 13, 43, 45}, + {2, 21, 23, 28, 37}, + {5, 14, 15, 18, 20}, + {5, 14, 28, 31, 36}, + {0, 13, 17, 29, 50}, + {1, 21, 29, 34, 46}, + {10, 14, 18, 40, 48}, + {2, 13, 16, 29, 31}, + {11, 16, 32, 45, 46}, + {0, 3, 12, 17, 26}, + {12, 13, 22, 24, 26}, + {16, 23, 25, 29, 40}, + {1, 9, 24, 40, 46}, + {8, 14, 30, 40, 49}, + {7, 9, 18, 33, 47}, + {1, 5, 21, 34, 45}, + {4, 20, 29, 31, 33}, + {14, 23, 29, 31, 42}, + {3, 10, 29, 31, 32}, + {12, 14, 15, 37, 41}, + {6, 13, 31, 34, 47}, + {13, 18, 29, 31, 46}, + {8, 21, 32, 36, 40}, + {5, 31, 37, 39, 45}, + {15, 20, 27, 33, 36}, + {18, 19, 31, 35, 51}, + {0, 19, 27, 34, 45}, + {8, 21, 22, 31, 37}, + {1, 13, 17, 27, 50}, + {5, 11, 21, 34, 46}, + {8, 9, 18, 34, 43}, + {0, 14, 27, 31, 37}, + {8, 20, 27, 29, 47}, + {3, 19, 28, 42, 46}, + {9, 14, 23, 28, 40}, + {4, 6, 32, 36, 46}, + {2, 28, 38, 46, 49}, + {4, 11, 14, 15, 27}, + {2, 15, 21, 27, 30}, + {0, 1, 13, 31, 43}, + {1, 2, 11, 25, 28}, + {6, 19, 22, 43, 51}, + {30, 31, 40, 44, 46}, + {5, 7, 25, 33, 35}, + {16, 18, 29, 30, 32}, + {14, 16, 21, 32, 42}, + {2, 5, 21, 39, 47}, + {27, 28, 36, 39, 40}, + {5, 25, 28, 32, 41}, + {6, 16, 32, 37, 40}, + {5, 7, 14, 20, 24}, + {3, 13, 22, 23, 42}, + {15, 17, 29, 43, 45}, + {1, 18, 24, 27, 38}, + {6, 35, 37, 45, 51}, + {1, 40, 41, 45, 49}, + {1, 37, 40, 43, 47}, + {4, 17, 26, 34, 38}, + {5, 7, 8, 34, 50}, + {7, 20, 22, 30, 38}, + {1, 28, 36, 41, 42}, + {2, 15, 16, 21, 33}, + {10, 15, 21, 41, 43}, + {15, 17, 30, 44, 51}, + {28, 34, 38, 40, 41}, + {0, 1, 5, 32, 40}, + {19, 31, 33, 35, 45}, + {10, 13, 17, 19, 45}, + {20, 22, 26, 33, 38}, + {17, 20, 31, 43, 48}, + {9, 17, 27, 31, 40}, + {4, 16, 33, 46, 50}, + {0, 8, 16, 20, 34}, + {12, 16, 20, 37, 46}, + {5, 23, 31, 40, 48}, + {0, 4, 7, 11, 13}, + {7, 18, 31, 43, 48}, + {6, 17, 19, 21, 31}, + {8, 12, 17, 29, 34}, + {18, 24, 31, 34, 48}, + {18, 25, 34, 39, 47}, + {5, 14, 16, 28, 29}, + {18, 20, 23, 33, 39}, + {3, 23, 25, 41, 42}, + {10, 11, 16, 26, 29}, + {7, 21, 25, 28, 34}, + {15, 20, 22, 44, 46}, + {2, 16, 39, 42, 45}, + {1, 2, 23, 29, 40}, + {3, 27, 32, 33, 40}, + {4, 26, 33, 36, 43}, + {8, 19, 32, 35, 42}, + {13, 25, 39, 40, 45}, + {7, 20, 22, 24, 25}, + {1, 13, 14, 23, 50}, + {4, 11, 19, 36, 45}, + {4, 10, 20, 43, 50}, + {2, 32, 36, 41, 42}, + {7, 24, 26, 33, 49}, + {15, 18, 22, 28, 42}, + {1, 6, 11, 45, 48}, + {2, 7, 10, 27, 40}, + {16, 23, 25, 29, 37}, + {1, 17, 19, 37, 43}, + {14, 16, 29, 31, 38}, + {1, 2, 18, 23, 31}, + {3, 4, 9, 29, 36}, + {4, 8, 18, 31, 42}, + {5, 17, 21, 22, 30}, + {7, 20, 29, 44, 51}, + {3, 8, 34, 48, 49}, + {5, 8, 10, 30, 43}, + {4, 14, 19, 31, 32}, + {4, 10, 13, 26, 38}, + {0, 2, 20, 41, 50}, + {0, 6, 10, 26, 41}, + {1, 5, 6, 38, 44}, + {2, 10, 14, 25, 28}, + {3, 13, 14, 29, 51}, + {5, 8, 31, 40, 51}, + {1, 6, 27, 36, 46}, + {4, 23, 24, 43, 48}, + {7, 36, 42, 43, 46}, + {9, 12, 17, 43, 45}, + {15, 22, 24, 34, 41}, + {0, 3, 10, 19, 39}, + {20, 27, 35, 46, 47}, + {14, 20, 27, 35, 47}, + {8, 15, 16, 40, 47}, + {1, 5, 23, 27, 32}, + {0, 2, 8, 13, 18}, + {6, 13, 24, 26, 33}, + {2, 3, 17, 34, 41}, + {10, 14, 30, 35, 43}, + {1, 20, 21, 41, 46}, + {6, 10, 19, 41, 51}, + {0, 5, 8, 13, 51}, + {0, 3, 8, 16, 20}, + {3, 9, 10, 34, 42}, + {4, 14, 17, 45, 46}, + {4, 6, 43, 44, 49}, + {3, 9, 25, 29, 41}, + {1, 3, 14, 19, 33}, + {1, 11, 38, 40, 47}, + {18, 25, 32, 44, 46}, + {1, 20, 35, 42, 46}, + {8, 9, 14, 16, 40}, + {16, 21, 24, 40, 42}, + {2, 8, 25, 28, 49}, + {9, 13, 20, 38, 46}, + {15, 31, 34, 35, 44}, + {4, 21, 29, 30, 36}, + {10, 13, 26, 30, 48}, + {6, 18, 20, 34, 46}, + {13, 17, 26, 38, 40}, + {14, 22, 27, 29, 43}, + {7, 15, 24, 28, 36}, + {17, 26, 30, 36, 50}, + {8, 12, 22, 29, 47}, + {2, 4, 25, 28, 46}, + {2, 24, 29, 34, 41}, + {10, 15, 18, 28, 50}, + {14, 15, 28, 29, 45}, + {6, 8, 21, 29, 41}, + {2, 13, 27, 31, 40}, + {4, 43, 44, 48, 51}, + {17, 25, 35, 43, 47}, + {11, 20, 22, 26, 46}, + {14, 34, 43, 47, 48}, + {9, 17, 25, 42, 43}, + {6, 13, 18, 20, 26}, + {17, 18, 20, 27, 43}, + {4, 21, 24, 34, 42}, + {1, 15, 18, 19, 44}, + {1, 15, 18, 36, 44}, + {8, 14, 19, 25, 32}, + {4, 30, 32, 40, 48}, + {0, 12, 15, 16, 39}, + {3, 5, 22, 31, 51}, + {0, 14, 20, 27, 31}, + {0, 18, 21, 30, 39}, + {23, 26, 39, 43, 48}, + {3, 7, 22, 29, 45}, + {0, 22, 24, 39, 44}, + {5, 23, 44, 46, 51}, + {1, 2, 5, 18, 19}, + {0, 3, 8, 21, 51}, + {3, 7, 11, 20, 23}, + {18, 19, 35, 39, 44}, + {15, 17, 28, 38, 49}, + {3, 8, 27, 40, 46}, + {9, 16, 29, 31, 45}, + {7, 13, 14, 31, 44}, + {1, 5, 10, 27, 41}, + {0, 10, 29, 34, 39}, + {4, 5, 24, 33, 43}, + {5, 13, 31, 34, 49}, + {3, 8, 13, 29, 36}, + {1, 11, 26, 34, 39}, + {6, 19, 23, 39, 41}, + {16, 18, 26, 28, 42}, + {0, 26, 27, 35, 47}, + {8, 15, 41, 48, 49}, + {2, 5, 15, 27, 32}, + {32, 37, 41, 45, 48}, + {5, 30, 31, 47, 51}, + {2, 23, 28, 40, 47}, + {6, 12, 20, 33, 50}, + {4, 17, 19, 24, 28}, + {14, 40, 41, 43, 49}, + {3, 15, 22, 41, 51}, + {11, 14, 17, 35, 40}, + {1, 2, 29, 30, 43}, + {3, 12, 36, 42, 47}, + {6, 7, 21, 32, 50}, + {0, 7, 14, 38, 46}, + {29, 30, 35, 36, 42}, + {12, 13, 17, 31, 39}, + {1, 2, 27, 33, 51}, + {4, 7, 21, 34, 37}, + {14, 29, 32, 40, 44}, + {5, 8, 21, 30, 40}, + {2, 6, 10, 45, 51}, + {2, 23, 31, 35, 44}, + {4, 14, 27, 38, 41}, + {2, 30, 43, 45, 46}, + {7, 22, 38, 41, 46}, + {2, 5, 6, 12, 15}, + {15, 32, 33, 45, 51}, + {6, 26, 35, 44, 45}, + {4, 6, 7, 20, 35}, + {3, 22, 25, 28, 41}, + {4, 5, 19, 44, 51}, + {9, 16, 19, 41, 45}, + {0, 7, 26, 40, 43}, + {1, 14, 30, 33, 47}, + {2, 6, 19, 35, 39}, + {7, 16, 19, 29, 40}, + {3, 21, 28, 29, 51}, + {5, 13, 15, 20, 41}, + {2, 15, 24, 36, 38}, + {14, 18, 21, 44, 48}, + {7, 9, 19, 33, 50}, + {9, 19, 29, 32, 41}, + {8, 14, 21, 46, 49}, + {6, 8, 27, 32, 36}, + {8, 18, 26, 29, 42}, + {27, 32, 40, 42, 46}, + {9, 18, 19, 29, 32}, + {4, 10, 13, 17, 42}, + {10, 11, 17, 21, 47}, + {8, 17, 35, 42, 43}, + {13, 32, 33, 39, 49}, + {4, 9, 34, 43, 44}, + {0, 1, 20, 21, 47}, + {11, 16, 22, 31, 44}, + {27, 40, 43, 44, 47}, + {2, 5, 8, 32, 45}, + {3, 21, 26, 39, 49}, + {4, 5, 10, 20, 30}, + {6, 15, 28, 46, 51}, + {10, 25, 30, 37, 43}, + {4, 7, 20, 31, 47}, + {3, 18, 25, 27, 44}, + {1, 2, 15, 25, 26}, + {6, 25, 26, 32, 48}, + {14, 17, 40, 42, 44}, + {5, 18, 19, 24, 49}, + {3, 10, 17, 18, 43}, + {1, 26, 39, 46, 49}, + {17, 30, 41, 50, 51}, + {18, 22, 32, 38, 45}, + {5, 14, 36, 44, 47}, + {17, 27, 33, 41, 43}, + {8, 11, 27, 40, 43}, + {6, 23, 28, 41, 48}, + {2, 12, 15, 43, 47}, + {8, 20, 33, 38, 49}, + {5, 11, 25, 31, 49}, + {4, 26, 37, 39, 47}, + {4, 9, 18, 25, 44}, + {20, 26, 27, 28, 40}, + {12, 19, 30, 42, 45}, + {18, 27, 34, 38, 47}, + {4, 38, 42, 43, 45}, + {9, 13, 26, 31, 49}, + {20, 34, 41, 43, 47}, + {0, 23, 26, 29, 33}, + {0, 1, 15, 40, 42}, + {14, 26, 35, 40, 47}, + {5, 24, 40, 44, 49}, + {7, 15, 20, 37, 43}, + {1, 4, 24, 31, 40}, + {5, 9, 14, 28, 44}, + {5, 18, 22, 36, 47}, + {4, 7, 11, 32, 33}, + {10, 13, 26, 27, 32}, + {1, 4, 15, 20, 27}, + {15, 28, 36, 44, 45}, + {2, 13, 21, 22, 26}, + {2, 3, 13, 34, 42}, + {0, 13, 14, 38, 46}, + {10, 14, 27, 37, 42}, + {12, 20, 28, 40, 41}, + {6, 15, 42, 45, 48}, + {0, 19, 20, 32, 49}, + {9, 10, 18, 31, 40}, + {1, 20, 30, 43, 50}, + {0, 28, 32, 37, 41}, + {2, 23, 39, 41, 50}, + {5, 15, 16, 44, 47}, + {5, 6, 36, 45, 51}, + {4, 36, 42, 43, 46}, + {12, 30, 33, 37, 43}, + {8, 36, 38, 44, 47}, + {1, 8, 13, 38, 40}, + {21, 34, 37, 42, 45}, + {7, 36, 39, 46, 47}, + {17, 26, 39, 47, 49}, + {2, 13, 24, 25, 39}, + {6, 25, 27, 28, 45}, + {2, 19, 32, 37, 46}, + {1, 21, 33, 40, 45}, + {0, 17, 19, 28, 43}, + {2, 8, 14, 29, 40}, + {20, 32, 37, 44, 46}, + {7, 20, 23, 29, 38}, + {7, 26, 29, 33, 34}, + {18, 19, 22, 30, 32}, + {6, 11, 17, 26, 39}, + {21, 30, 39, 43, 46}, + {0, 2, 15, 43, 50}, + {14, 27, 36, 41, 51}, + {7, 28, 41, 47, 49}, + {7, 8, 13, 25, 47}, + {8, 22, 25, 30, 47}, + {1, 5, 28, 30, 44}, + {1, 14, 15, 18, 22}, + {8, 16, 27, 40, 45}, + {5, 16, 20, 33, 43}, + {5, 7, 15, 18, 21}, + {2, 15, 33, 48, 50}, + {0, 11, 26, 34, 43}, + {2, 12, 18, 24, 41}, + {0, 13, 25, 34, 49}, + {8, 9, 20, 21, 23}, + {10, 14, 19, 21, 40}, + {3, 19, 26, 43, 45}, + {16, 19, 23, 27, 42}, + {8, 13, 18, 21, 28}, + {0, 4, 6, 15, 19}, + {17, 29, 36, 39, 43}, + {1, 23, 27, 34, 43}, + {2, 3, 21, 28, 48}, + {17, 27, 39, 42, 43}, + {23, 30, 31, 34, 43}, + {5, 19, 27, 31, 46}, + {12, 13, 22, 39, 47}, + {2, 4, 19, 30, 50}, + {5, 11, 28, 31, 48}, + {13, 15, 23, 31, 41}, + {8, 10, 28, 34, 38}, + {0, 3, 6, 10, 26}, + {4, 16, 19, 42, 48}, + {2, 13, 15, 17, 24}, + {7, 17, 25, 43, 47}, + {1, 7, 14, 29, 50}, + {6, 23, 32, 35, 42}, + {1, 2, 15, 24, 38}, + {10, 13, 19, 31, 39}, + {16, 19, 22, 44, 45}, + {0, 4, 15, 40, 41}, + {7, 20, 22, 34, 44}, + {8, 12, 13, 30, 43}, + {0, 14, 40, 47, 49}, + {7, 18, 20, 36, 41}, + {2, 21, 23, 28, 29}, + {4, 13, 26, 34, 48}, + {13, 33, 39, 45, 51}, + {7, 14, 21, 23, 46}, + {3, 13, 16, 30, 32}, + {9, 20, 28, 45, 46}, + {27, 28, 31, 44, 48}, + {7, 29, 33, 35, 38}, + {1, 13, 14, 17, 51}, + {16, 21, 25, 42, 48}, + {32, 38, 39, 44, 45}, + {1, 4, 5, 14, 49}, + {2, 16, 29, 32, 44}, + {25, 29, 31, 44, 45}, + {12, 14, 34, 47, 48}, + {5, 9, 17, 18, 45}, + {14, 17, 39, 43, 46}, + {1, 4, 8, 11, 17}, + {4, 18, 28, 34, 47}, + {7, 17, 29, 31, 44}, + {3, 6, 19, 21, 37}, + {12, 14, 18, 31, 46}, + {9, 20, 30, 45, 46}, + {19, 24, 33, 38, 46}, + {7, 14, 23, 29, 40}, + {2, 25, 40, 41, 42}, + {4, 18, 22, 36, 44}, + {14, 32, 36, 38, 40}, + {1, 15, 21, 34, 36}, + {3, 21, 22, 34, 37}, + {4, 8, 11, 12, 43}, + {9, 16, 20, 33, 41}, + {3, 16, 19, 26, 51}, + {18, 21, 41, 43, 47}, + {0, 4, 13, 34, 48}, + {3, 8, 11, 15, 29}, + {32, 38, 42, 44, 45}, + {3, 20, 46, 50, 51}, + {7, 24, 31, 33, 34}, + {3, 19, 20, 33, 50}, + {2, 28, 30, 38, 48}, + {2, 19, 29, 34, 41}, + {3, 24, 27, 29, 45}, + {5, 7, 20, 22, 23}, + {1, 5, 14, 17, 45}, + {1, 3, 14, 49, 50}, + {0, 3, 13, 33, 40}, + {15, 20, 21, 25, 41}, + {4, 12, 16, 17, 18}, + {5, 8, 36, 44, 45}, + {5, 30, 31, 33, 39}, + {4, 7, 14, 43, 50}, + {1, 5, 18, 33, 43}, + {5, 17, 18, 29, 47}, + {5, 17, 19, 31, 37}, + {7, 18, 21, 31, 42}, + {10, 12, 13, 26, 33}, + {4, 14, 38, 43, 47}, + {16, 27, 31, 38, 40}, + {8, 24, 32, 34, 39}, + {7, 12, 32, 45, 49}, + {19, 25, 32, 35, 42}, + {7, 11, 15, 33, 35}, + {5, 20, 23, 44, 45}, + {1, 9, 37, 40, 49}, + {7, 17, 21, 29, 43}, + {0, 7, 11, 33, 42}, + {4, 26, 35, 43, 44}, + {15, 27, 40, 42, 49}, + {2, 11, 13, 20, 39}, + {2, 3, 11, 17, 30}, + {5, 7, 14, 40, 47}, + {0, 6, 25, 27, 45}, + {0, 28, 31, 39, 50}, + {3, 4, 16, 21, 33}, + {2, 15, 19, 35, 47}, + {4, 18, 24, 39, 44}, + {1, 8, 10, 34, 38}, + {5, 27, 31, 34, 51}, + {13, 18, 30, 32, 43}, + {2, 5, 21, 31, 38}, + {9, 13, 15, 28, 40}, + {5, 10, 31, 32, 46}, + {7, 9, 21, 29, 42}, + {6, 7, 19, 25, 27}, + {31, 33, 34, 41, 47}, + {31, 39, 40, 44, 47}, + {10, 14, 16, 42, 45}, + {1, 32, 35, 40, 42}, + {23, 26, 27, 33, 40}, + {1, 18, 23, 24, 31}, + {16, 18, 42, 43, 51}, + {14, 27, 30, 39, 42}, + {7, 14, 23, 27, 38}, + {7, 13, 26, 30, 45}, + {4, 7, 13, 39, 42}, + {12, 14, 20, 21, 47}, + {7, 9, 14, 29, 46}, + {5, 8, 28, 31, 37}, + {12, 17, 20, 42, 43}, + {28, 37, 40, 41, 45}, + {5, 17, 24, 40, 44}, + {31, 37, 39, 40, 44}, + {4, 11, 21, 30, 45}, + {5, 6, 17, 19, 29}, + {2, 11, 12, 29, 42}, + {9, 13, 17, 43, 51}, + {10, 17, 19, 34, 43}, + {5, 18, 33, 45, 51}, + {2, 31, 44, 46, 47}, + {0, 4, 20, 33, 40}, + {6, 19, 20, 38, 42}, + {13, 23, 26, 28, 50}, + {1, 6, 8, 45, 50}, + {6, 8, 18, 23, 45}, + {7, 16, 20, 44, 45}, + {1, 6, 13, 26, 43}, + {0, 18, 30, 41, 44}, + {13, 14, 26, 37, 38}, + {5, 6, 22, 27, 45}, + {1, 11, 19, 44, 45}, + {11, 16, 29, 36, 40}, + {3, 13, 19, 23, 32}, + {1, 5, 31, 41, 51}, + {0, 5, 8, 28, 44}, + {9, 20, 34, 39, 46}, + {6, 16, 23, 45, 46}, + {1, 6, 11, 32, 41}, + {4, 6, 11, 14, 40}, + {0, 10, 13, 43, 45}, + {7, 16, 35, 37, 46}, + {0, 6, 15, 18, 32}, + {17, 27, 30, 49, 50}, + {8, 20, 33, 35, 42}, + {3, 25, 30, 40, 43}, + {1, 2, 10, 18, 28}, + {12, 18, 44, 45, 49}, + {7, 21, 34, 37, 38}, + {4, 13, 15, 29, 39}, + {0, 17, 18, 25, 31}, + {5, 25, 35, 39, 44}, + {6, 17, 19, 31, 39}, + {21, 27, 29, 34, 39}, + {0, 4, 9, 25, 30}, + {3, 19, 27, 41, 42}, + {1, 13, 17, 39, 44}, + {2, 5, 11, 31, 49}, + {0, 10, 26, 38, 43}, + {7, 10, 13, 26, 27}, + {5, 23, 24, 31, 39}, + {16, 17, 27, 33, 46}, + {20, 29, 35, 43, 46}, + {6, 17, 32, 47, 50}, + {7, 20, 23, 43, 50}, + {1, 13, 21, 33, 34}, + {3, 5, 8, 12, 47}, + {8, 26, 28, 41, 44}, + {1, 12, 17, 27, 42}, + {7, 11, 13, 20, 30}, + {5, 8, 24, 27, 44}, + {8, 10, 11, 32, 34}, + {2, 16, 17, 28, 48}, + {1, 5, 21, 39, 47}, + {3, 11, 14, 27, 30}, + {5, 11, 27, 28, 41}, + {0, 8, 32, 34, 46}, + {6, 21, 32, 41, 50}, + {0, 6, 10, 19, 28}, + {3, 4, 6, 20, 33}, + {5, 9, 32, 33, 46}, + {3, 14, 29, 35, 46}, + {13, 26, 40, 47, 48}, + {13, 33, 39, 45, 48}, + {4, 11, 30, 39, 48}, + {14, 18, 27, 39, 46}, + {0, 6, 13, 24, 27}, + {0, 12, 13, 15, 44}, + {8, 20, 34, 42, 48}, + {0, 16, 39, 41, 51}, + {9, 13, 16, 20, 29}, + {3, 16, 32, 43, 47}, + {17, 26, 31, 34, 39}, + {1, 13, 33, 36, 40}, + {9, 15, 19, 45, 50}, + {5, 27, 33, 44, 51}, + {4, 24, 28, 33, 41}, + {2, 14, 34, 39, 41}, + {11, 21, 39, 41, 47}, + {1, 6, 18, 31, 50}, + {1, 3, 5, 7, 44}, + {1, 8, 22, 27, 43}, + {3, 4, 15, 17, 19}, + {14, 16, 20, 28, 41}, + {7, 13, 20, 36, 45}, + {0, 5, 10, 28, 39}, + {9, 12, 14, 26, 27}, + {1, 8, 21, 25, 43}, + {1, 6, 18, 32, 42}, + {8, 14, 21, 41, 50}, + {13, 14, 22, 26, 43}, + {21, 25, 28, 34, 35}, + {1, 2, 14, 18, 35}, + {2, 21, 33, 36, 46}, + {0, 25, 26, 47, 50}, + {24, 28, 30, 33, 46}, + {3, 8, 20, 21, 30}, + {31, 32, 37, 41, 45}, + {4, 12, 23, 28, 41}, + {1, 15, 22, 30, 43}, + {1, 5, 29, 40, 49}, + {8, 13, 17, 30, 37}, + {2, 15, 19, 23, 24}, + {8, 17, 31, 34, 48}, + {18, 21, 23, 30, 47}, + {0, 13, 34, 36, 46}, + {9, 18, 37, 42, 44}, + {20, 27, 32, 40, 51}, + {1, 14, 18, 45, 48}, + {14, 19, 40, 44, 46}, + {0, 11, 23, 39, 44}, + {7, 16, 18, 21, 29}, + {2, 16, 27, 30, 43}, + {1, 7, 11, 20, 34}, + {2, 12, 41, 46, 49}, + {1, 5, 11, 21, 31}, + {5, 10, 13, 21, 47}, + {3, 4, 6, 18, 32}, + {0, 6, 21, 38, 47}, + {3, 9, 19, 32, 33}, + {2, 3, 15, 20, 40}, + {8, 16, 20, 34, 35}, + {5, 21, 39, 40, 47}, + {31, 38, 39, 43, 44}, + {12, 19, 30, 43, 46}, + {5, 20, 31, 38, 45}, + {4, 13, 17, 38, 49}, + {0, 16, 27, 33, 40}, + {8, 25, 37, 43, 47}, + {7, 13, 22, 33, 50}, + {6, 11, 20, 33, 35}, + {3, 6, 8, 32, 46}, + {15, 18, 31, 45, 48}, + {1, 14, 18, 33, 41}, + {2, 3, 21, 22, 42}, + {0, 30, 39, 42, 51}, + {2, 5, 13, 28, 43}, + {0, 1, 6, 33, 40}, + {0, 3, 5, 26, 49}, + {0, 22, 39, 46, 50}, + {13, 16, 22, 29, 31}, + {11, 12, 30, 41, 43}, + {8, 9, 12, 17, 43}, + {21, 24, 27, 34, 42}, + {13, 25, 27, 32, 39}, + {6, 11, 19, 25, 46}, + {8, 28, 29, 36, 47}, + {7, 8, 24, 28, 41}, + {2, 18, 44, 46, 48}, + {18, 21, 38, 42, 44}, + {0, 15, 21, 26, 49}, + {26, 30, 34, 35, 47}, + {9, 14, 18, 27, 38}, + {27, 30, 37, 40, 51}, + {13, 26, 33, 42, 51}, + {6, 15, 19, 20, 24}, + {0, 1, 16, 23, 29}, + {25, 28, 29, 32, 45}, + {3, 6, 15, 21, 29}, + {7, 8, 15, 19, 46}, + {14, 23, 25, 34, 40}, + {0, 2, 5, 12, 26}, + {2, 28, 33, 34, 45}, + {13, 26, 27, 28, 30}, + {16, 21, 31, 32, 42}, + {2, 15, 16, 19, 39}, + {4, 23, 35, 43, 46}, + {8, 14, 23, 37, 40}, + {2, 25, 41, 42, 45}, + {3, 14, 15, 20, 29}, + {9, 15, 16, 19, 28}, + {3, 6, 28, 29, 36}, + {7, 12, 20, 27, 42}, + {18, 21, 31, 42, 48}, + {0, 16, 34, 42, 48}, + {5, 25, 31, 34, 46}, + {7, 26, 30, 46, 50}, + {0, 2, 4, 21, 43}, + {0, 6, 8, 12, 32}, + {3, 6, 26, 29, 44}, + {10, 16, 27, 38, 42}, + {0, 13, 21, 22, 36}, + {7, 10, 17, 31, 33}, + {6, 7, 23, 35, 45}, + {16, 17, 30, 40, 48}, + {14, 32, 33, 43, 46}, + {2, 28, 36, 39, 47}, + {7, 11, 20, 38, 40}, + {10, 21, 27, 31, 40}, + {3, 4, 17, 35, 46}, + {16, 29, 36, 41, 47}, + {18, 26, 29, 42, 49}, + {7, 14, 25, 27, 42}, + {3, 7, 29, 49, 51}, + {5, 7, 20, 22, 50}, + {16, 29, 32, 33, 47}, + {1, 29, 40, 46, 51}, + {5, 21, 36, 40, 44}, + {2, 4, 34, 46, 47}, + {6, 20, 29, 34, 45}, + {26, 35, 39, 44, 50}, + {3, 7, 21, 33, 35}, + {2, 4, 31, 41, 48}, + {16, 17, 30, 34, 49}, + {2, 23, 24, 35, 41}, + {8, 16, 23, 24, 34}, + {1, 21, 30, 34, 41}, + {1, 15, 18, 27, 47}, + {1, 6, 14, 33, 38}, + {12, 16, 26, 42, 44}, + {0, 17, 18, 35, 44}, + {1, 14, 18, 38, 46}, + {6, 15, 19, 34, 40}, + {16, 17, 33, 39, 42}, + {8, 16, 21, 38, 46}, + {6, 30, 34, 43, 48}, + {2, 4, 26, 39, 45}, + {19, 20, 31, 32, 43}, + {4, 26, 34, 45, 47}, + {1, 2, 17, 40, 46}, + {7, 8, 13, 20, 42}, + {1, 27, 30, 47, 48}, + {1, 9, 16, 27, 38}, + {0, 25, 26, 41, 42}, + {4, 13, 18, 30, 46}, + {3, 14, 27, 43, 48}, + {1, 7, 11, 20, 35}, + {16, 18, 22, 44, 46}, + {2, 6, 21, 28, 37}, + {3, 6, 29, 31, 36}, + {2, 26, 34, 41, 51}, + {2, 3, 6, 10, 28}, + {8, 29, 33, 46, 48}, + {0, 7, 20, 24, 42}, + {5, 11, 19, 25, 45}, + {8, 18, 21, 38, 45}, + {19, 20, 23, 26, 46}, + {2, 10, 20, 32, 45}, + {1, 8, 34, 41, 51}, + {13, 15, 20, 33, 40}, + {3, 14, 17, 29, 44}, + {6, 7, 21, 46, 51}, + {18, 25, 28, 31, 49}, + {16, 21, 28, 40, 47}, + {4, 11, 30, 44, 51}, + {10, 25, 34, 42, 47}, + {0, 1, 11, 15, 40}, + {0, 30, 33, 37, 46}, + {4, 9, 13, 14, 27}, + {5, 10, 18, 27, 42}, + {4, 6, 12, 37, 43}, + {3, 10, 27, 37, 40}, + {3, 9, 27, 32, 45}, + {4, 16, 22, 42, 49}, + {17, 24, 25, 30, 41}, + {22, 25, 31, 34, 47}, + {4, 21, 30, 44, 48}, + {5, 7, 10, 20, 26}, + {4, 6, 7, 14, 27}, + {15, 20, 21, 22, 28}, + {16, 27, 35, 42, 45}, + {26, 30, 43, 44, 50}, + {1, 2, 8, 21, 35}, + {9, 19, 24, 32, 38}, + {14, 17, 35, 38, 43}, + {14, 19, 40, 41, 43}, + {13, 18, 23, 26, 27}, + {4, 12, 22, 33, 46}, + {2, 15, 27, 39, 50}, + {4, 10, 43, 48, 50}, + {0, 4, 6, 45, 48}, + {0, 1, 32, 33, 40}, + {5, 36, 37, 41, 44}, + {23, 24, 26, 39, 45}, + {18, 19, 41, 44, 51}, + {10, 14, 19, 32, 33}, + {1, 9, 28, 34, 40}, + {12, 16, 19, 26, 29}, + {7, 20, 25, 31, 47}, + {1, 25, 27, 35, 43}, + {10, 15, 20, 33, 39}, + {7, 12, 17, 40, 43}, + {3, 7, 16, 17, 24}, + {1, 2, 36, 40, 51}, + {23, 26, 35, 39, 42}, + {3, 4, 19, 28, 30}, + {5, 9, 14, 44, 47}, + {6, 13, 39, 46, 51}, + {3, 4, 5, 19, 45}, + {1, 7, 10, 27, 44}, + {29, 30, 40, 43, 47}, + {16, 22, 30, 34, 42}, + {0, 2, 17, 30, 51}, + {6, 16, 19, 33, 35}, + {20, 27, 35, 43, 46}, + {8, 17, 21, 35, 36}, + {18, 21, 26, 29, 47}, + {7, 28, 38, 41, 42}, + {5, 14, 18, 39, 49}, + {1, 5, 8, 43, 47}, + {26, 27, 30, 43, 50}, + {0, 6, 17, 32, 51}, + {1, 12, 36, 40, 48}, + {7, 14, 20, 31, 48}, + {0, 9, 16, 24, 42}, + {8, 16, 18, 34, 40}, + {14, 16, 38, 40, 48}, + {2, 8, 27, 28, 48}, + {18, 39, 40, 44, 51}, + {13, 22, 38, 39, 41}, + {5, 14, 30, 36, 44}, + {0, 3, 16, 48, 51}, + {9, 17, 36, 43, 45}, + {3, 32, 42, 43, 47}, + {0, 16, 20, 23, 46}, + {1, 4, 14, 25, 50}, + {7, 14, 26, 27, 30}, + {1, 11, 33, 38, 46}, + {5, 7, 10, 31, 41}, + {18, 20, 31, 45, 50}, + {1, 14, 20, 26, 37}, + {17, 19, 21, 26, 34}, + {4, 13, 19, 32, 41}, + {2, 15, 23, 32, 42}, + {11, 14, 15, 22, 41}, + {19, 21, 24, 42, 45}, + {3, 14, 15, 21, 34}, + {15, 17, 36, 38, 43}, + {0, 3, 6, 9, 29}, + {1, 2, 7, 14, 37}, + {6, 14, 19, 20, 36}, + {17, 20, 24, 29, 42}, + {7, 29, 31, 44, 50}, + {5, 20, 33, 42, 48}, + {2, 9, 15, 34, 51}, + {6, 24, 26, 45, 51}, + {3, 16, 39, 49, 50}, + {1, 10, 11, 33, 40}, + {1, 14, 20, 22, 31}, + {1, 15, 22, 30, 41}, + {3, 14, 25, 28, 41}, + {3, 15, 19, 34, 47}, + {0, 12, 30, 33, 46}, + {14, 22, 32, 45, 49}, + {12, 16, 26, 31, 44}, + {1, 16, 20, 31, 40}, + {4, 5, 19, 27, 44}, + {18, 26, 30, 39, 40}, + {6, 7, 12, 19, 34}, + {4, 30, 36, 44, 46}, + {5, 34, 36, 38, 47}, + {8, 13, 16, 21, 35}, + {0, 3, 20, 42, 48}, + {4, 12, 27, 30, 36}, + {1, 2, 14, 25, 29}, + {0, 15, 19, 30, 32}, + {4, 17, 24, 48, 49}, + {8, 16, 17, 27, 40}, + {2, 8, 21, 38, 43}, + {3, 18, 34, 44, 45}, + {5, 6, 20, 33, 49}, + {5, 11, 29, 44, 45}, + {4, 17, 31, 37, 42}, + {2, 6, 28, 30, 35}, + {7, 13, 34, 35, 39}, + {2, 10, 15, 34, 51}, + {13, 14, 15, 18, 44}, + {14, 25, 26, 27, 36}, + {18, 21, 24, 29, 47}, + {1, 18, 19, 31, 41}, + {5, 16, 29, 35, 51}, + {12, 13, 26, 41, 47}, + {7, 19, 21, 22, 46}, + {16, 33, 41, 42, 49}, + {1, 3, 4, 14, 20}, + {0, 1, 9, 26, 49}, + {7, 20, 21, 25, 27}, + {11, 13, 27, 39, 48}, + {5, 16, 21, 22, 44}, + {0, 22, 39, 43, 50}, + {0, 6, 11, 28, 32}, + {6, 7, 20, 28, 44}, + {2, 8, 21, 29, 38}, + {2, 9, 17, 28, 37}, + {8, 21, 28, 39, 44}, + {0, 26, 28, 45, 48}, + {15, 21, 23, 31, 41}, + {1, 8, 37, 40, 41}, + {11, 20, 25, 30, 33}, + {19, 21, 28, 29, 47}, + {0, 27, 29, 42, 45}, + {14, 32, 35, 40, 42}, + {2, 16, 25, 29, 37}, + {7, 14, 17, 27, 49}, + {7, 30, 35, 43, 45}, + {2, 4, 14, 17, 19}, + {16, 32, 34, 45, 46}, + {13, 21, 24, 30, 43}, + {3, 14, 15, 22, 27}, + {29, 30, 33, 40, 46}, + {13, 27, 34, 39, 44}, + {5, 8, 37, 46, 47}, + {5, 19, 21, 34, 39}, + {4, 5, 18, 29, 38}, + {4, 6, 7, 20, 49}, + {4, 18, 20, 39, 43}, + {3, 31, 33, 42, 47}, + {8, 10, 25, 33, 46}, + {8, 16, 21, 25, 37}, + {15, 28, 30, 31, 32}, + {5, 6, 11, 32, 33}, + {1, 16, 37, 40, 43}, + {5, 8, 27, 31, 35}, + {1, 21, 24, 34, 43}, + {26, 28, 29, 42, 43}, + {17, 20, 30, 35, 40}, + {12, 15, 28, 29, 36}, + {10, 20, 25, 28, 46}, + {8, 15, 26, 36, 47}, + {1, 11, 38, 40, 49}, + {1, 2, 28, 31, 48}, + {3, 5, 28, 31, 34}, + {5, 18, 29, 45, 48}, + {6, 8, 26, 44, 45}, + {6, 20, 21, 31, 45}, + {5, 20, 26, 44, 49}, + {8, 11, 20, 36, 46}, + {6, 15, 19, 20, 38}, + {11, 17, 31, 34, 43}, + {3, 14, 32, 40, 48}, + {0, 22, 26, 37, 43}, + {19, 21, 28, 30, 47}, + {5, 8, 18, 42, 48}, + {0, 22, 38, 39, 41}, + {4, 16, 17, 31, 49}, + {5, 12, 18, 36, 41}, + {8, 9, 19, 34, 42}, + {2, 18, 24, 41, 43}, + {7, 11, 18, 19, 44}, + {10, 19, 32, 37, 43}, + {3, 20, 21, 35, 42}, + {4, 30, 39, 47, 48}, + {5, 6, 10, 27, 32}, + {5, 18, 32, 39, 51}, + {20, 21, 41, 44, 47}, + {1, 8, 25, 28, 41}, + {4, 9, 20, 43, 45}, + {13, 14, 16, 27, 35}, + {0, 3, 12, 19, 32}, + {0, 1, 9, 13, 28}, + {22, 30, 32, 43, 50}, + {7, 28, 34, 41, 42}, + {1, 8, 16, 40, 44}, + {5, 9, 13, 21, 34}, + {1, 5, 13, 18, 51}, + {7, 14, 19, 33, 38}, + {1, 14, 16, 34, 43}, + {3, 6, 15, 45, 49}, + {5, 7, 17, 18, 40}, + {0, 3, 18, 29, 48}, + {3, 20, 26, 27, 29}, + {0, 30, 31, 39, 42}, + {3, 15, 24, 42, 45}, + {2, 5, 28, 32, 50}, + {4, 6, 13, 19, 40}, + {5, 8, 11, 47, 51}, + {0, 3, 7, 15, 39}, + {6, 34, 41, 45, 49}, + {14, 21, 32, 40, 41}, + {8, 19, 35, 42, 47}, + {2, 5, 13, 16, 42}, + {14, 24, 40, 46, 47}, + {3, 4, 30, 34, 51}, + {4, 13, 24, 32, 45}, +} diff --git a/gamerule/redvsblack/cardkind.go b/gamerule/redvsblack/cardkind.go new file mode 100644 index 0000000..4d6e96e --- /dev/null +++ b/gamerule/redvsblack/cardkind.go @@ -0,0 +1,219 @@ +package redvsblack + +import ( + "sort" +) + +const ( + CardsKind_Single int = iota //散牌 + CardsKind_Double //小对子2~8的对 + CardsKind_BigDouble //大对子9~A的对 + CardsKind_StraightA23 //顺子地龙 + CardsKind_Straight //顺子普通 + CardsKind_RoyalStraight //顺子天龙 + CardsKind_Flush //同花 + CardsKind_FlushStraightA23 //同花顺地龙 + CardsKind_FlushStraight //同花顺普通 + CardsKind_RoyalFlushStraight //同花顺天龙 + CardsKind_ThreeSame //豹子 + CardsKind_Max +) + +type CardKindCheckInteface func(cardsInfo *handCardsInfo) *KindOfCard + +var cardKindCheckList [CardsKind_Max]CardKindCheckInteface + +func registeCardCheckFunc(kind int, kindFunc CardKindCheckInteface) { + cardKindCheckList[kind] = kindFunc +} +func getCardCheckFunc(kind int) CardKindCheckInteface { + if kind > CardsKind_Single && kind < CardsKind_Max { + return cardKindCheckList[kind] + } + return nil +} + +func init() { + //1.CardsKind_Double + registeCardCheckFunc(CardsKind_Double, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 2 { + return nil + } + //对子放到前头 + if cardsInfo.cards2[0] != cardsInfo.cards2[1] { + cardsInfo.cards2[0], cardsInfo.cards2[1], cardsInfo.cards2[2] = cardsInfo.cards2[2], cardsInfo.cards2[1], cardsInfo.cards2[0] + } + if CardValueMap[cardsInfo.cards2[0]] < 8 { + return &KindOfCard{ + Kind: CardsKind_Double, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + //1.CardsKind_BigDouble + registeCardCheckFunc(CardsKind_BigDouble, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 2 { + return nil + } + //对子放到前头 + if cardsInfo.cards2[0] != cardsInfo.cards2[1] { + cardsInfo.cards2[0], cardsInfo.cards2[1], cardsInfo.cards2[2] = cardsInfo.cards2[2], cardsInfo.cards2[1], cardsInfo.cards2[0] + } + if CardValueMap[cardsInfo.cards2[0]] >= 8 { + return &KindOfCard{ + Kind: CardsKind_BigDouble, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + //2.顺子地龙 + registeCardCheckFunc(CardsKind_StraightA23, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 3 || cardsInfo.numColor < 2 { + return nil + } + if cardsInfo.cards2[0] == 0 && cardsInfo.cards2[1] == 1 && cardsInfo.cards2[2] == 2 { + return &KindOfCard{ + Kind: CardsKind_StraightA23, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } else { + return nil + } + }) + //3.顺子 + registeCardCheckFunc(CardsKind_Straight, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 3 || cardsInfo.numColor < 2 { + return nil + } + if IsRoundSlice(cardsInfo.cards2) && cardsInfo.cards2[0] != 0 { + return &KindOfCard{ + Kind: CardsKind_Straight, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + //4.皇家顺子 + registeCardCheckFunc(CardsKind_RoyalStraight, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 3 || cardsInfo.numColor < 2 { + return nil + } + if cardsInfo.cards2[0] == 0 && cardsInfo.cards2[1] == 11 && cardsInfo.cards2[2] == 12 { + return &KindOfCard{ + Kind: CardsKind_RoyalStraight, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + //5.同花 + registeCardCheckFunc(CardsKind_Flush, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numColor != 1 { + return nil + } + return &KindOfCard{ + Kind: CardsKind_Flush, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + }) + //6.同花顺地龙 + registeCardCheckFunc(CardsKind_FlushStraightA23, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numColor != 1 { + return nil + } + if cardsInfo.numValue != 3 { + return nil + } + if cardsInfo.cards2[0] == 0 && cardsInfo.cards2[1] == 1 && cardsInfo.cards2[2] == 2 { + return &KindOfCard{ + Kind: CardsKind_FlushStraightA23, + maxValue: cardsInfo.cards2[2], + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + //7.同花顺 + registeCardCheckFunc(CardsKind_FlushStraight, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numColor != 1 { + return nil + } + if cardsInfo.numValue != 3 { + return nil + } + if IsRoundSlice(cardsInfo.cards2) && cardsInfo.cards2[0] != 0 { + return &KindOfCard{ + Kind: CardsKind_FlushStraight, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } + return nil + }) + + //8.同花顺 QKA + registeCardCheckFunc(CardsKind_RoyalFlushStraight, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numColor != 1 { + return nil + } + if cardsInfo.numValue != 3 { + return nil + } + if cardsInfo.cards2[0] == 0 && cardsInfo.cards2[1] == 11 && cardsInfo.cards2[2] == 12 { + return &KindOfCard{ + Kind: CardsKind_RoyalFlushStraight, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + } else { + return nil + } + }) + //9.豹子 + registeCardCheckFunc(CardsKind_ThreeSame, func(cardsInfo *handCardsInfo) *KindOfCard { + if cardsInfo.numValue != 1 { + return nil + } + return &KindOfCard{ + Kind: CardsKind_ThreeSame, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + } + }) +} + +/* + * 查看一个切片是否是递增数组 + */ +func IsRoundSlice(sl []int) bool { + sort.Ints(sl) + if len(sl) != 3 { + return false + } + for i := 0; i < len(sl)-1; i++ { + if sl[i]+1 != sl[i+1] { + return false + } + } + return true +} + +/* + * 在一个切片中查找给定的数值是否存在 + */ +func InSlice(value int, sl []int) bool { + for _, v := range sl { + if v == value { + return true + } + } + return false +} diff --git a/gamerule/redvsblack/cardkindfigureup.go b/gamerule/redvsblack/cardkindfigureup.go new file mode 100644 index 0000000..5d0db38 --- /dev/null +++ b/gamerule/redvsblack/cardkindfigureup.go @@ -0,0 +1,359 @@ +package redvsblack + +import ( + "mongo.games.com/game/common" + "sort" +) + +var CardsKindFigureUpSington = &CardsKindFigureUp{} + +type handCardsInfo struct { + cards []int //考虑花色,从小到大排序,注意A的位置 + cards2 []int //不考虑花色,从小到大排序,注意A的位置 + numColor int //花形数量 + numValue int //牌型数量 + maxValue int //最大单牌 + maxColor int //最大单牌花色 +} + +type KindOfCard struct { + Kind int //牌型 + maxValue int //最大单牌 + maxColor int //最大单牌花色 + orgCards []int //原始牌 + nocCards []int //去掉花色的牌 + OrderCards []int //排序后的牌 +} + +func (this *KindOfCard) TidyCards() { + // var order []int + // if this.Kind == CardsKind_RoyalStraight || this.Kind == CardsKind_RoyalFlushStraight { + // order = []int{this.nocCards[1], this.nocCards[2], this.nocCards[0]} //把A放后面 + // } else { + // order = this.nocCards + // } + // orgCards := make([]int, Hand_CardNum, Hand_CardNum) + // copy(orgCards, this.orgCards) + // for _, c := range order { + // for i, oc := range orgCards { + // if c == oc%PER_CARD_COLOR_MAX { + // n := len(orgCards) + // this.OrderCards = append(this.OrderCards, oc) + // rest := orgCards[:i] + // if i < n-1 { + // rest = append(rest, orgCards[i+1:]...) + // } + // orgCards = rest + // break + // } + // } + // } + this.OrderCards = this.orgCards +} + +func CreateCardKind() *KindOfCard { + return &KindOfCard{ + Kind: CardsKind_Single, + maxValue: 0, + maxColor: 0, + } +} + +type CardsKindFigureUp struct { +} + +func createHandCardsInfo(cards []int) *handCardsInfo { + cardsInfo := &handCardsInfo{ + cards: make([]int, len(cards)), + } + copy(cardsInfo.cards, cards) + //先整理下牌 + sort.Ints(cardsInfo.cards) + colorMap := make(map[int]bool) + kindMap := make(map[int]bool) + maxValue := 1 + for i := 0; i < Hand_CardNum; i++ { + c := cardsInfo.cards[i] / PER_CARD_COLOR_MAX + k := cardsInfo.cards[i] % PER_CARD_COLOR_MAX + colorMap[c] = true + kindMap[k] = true + cardsInfo.cards2 = append(cardsInfo.cards2, k) + if CardValueMap[k] > CardValueMap[maxValue] { + maxValue = k + cardsInfo.maxValue = k + cardsInfo.maxColor = c + } else if CardValueMap[k] == CardValueMap[maxValue] { + if c > cardsInfo.maxColor { + cardsInfo.maxColor = c + } + } + } + sort.Ints(cardsInfo.cards2) + cardsInfo.numColor = len(colorMap) + cardsInfo.numValue = len(kindMap) + return cardsInfo +} + +func (this *CardsKindFigureUp) FigureUpByCard(cards []int) *KindOfCard { + cardsInfo := createHandCardsInfo(cards) + //处理检查的牌型 + cardKind := this.checkCardKind(cardsInfo) + if cardKind != nil { + cardKind.nocCards = cardsInfo.cards2 + } else { + cardKind = &KindOfCard{ + Kind: CardsKind_Single, + maxValue: cardsInfo.maxValue, + maxColor: cardsInfo.maxColor, + nocCards: cardsInfo.cards2, + } + } + cardKind.orgCards = cards + + return cardKind +} + +func (this *CardsKindFigureUp) checkCardKind(cardsInfo *handCardsInfo) *KindOfCard { + for i := CardsKind_Max - 1; i > 0; i-- { + kindFunc := getCardCheckFunc(i) + if kindFunc == nil { + continue + } + koc := kindFunc(cardsInfo) + if koc != nil { + return koc + } + } + return nil +} + +func CompareCards(l, r *KindOfCard) int { + if l.Kind == r.Kind { //牌型相同 + ret := compareCardsSameKindBySize(l, r) + if ret == 0 { + if l.maxColor > r.maxColor { + return 1 + } else { + return -1 + } + } else { + return ret + } + } + if l.Kind > r.Kind { + return 1 + } else { + return -1 + } +} + +// 比大小:同牌型最大单牌大小相同的情况下,依次比较剩余牌的大小 +func compareCardsSameKindBySize(l, r *KindOfCard) int { + if l.nocCards[0] == A_CARD && r.nocCards[0] != A_CARD { + return 1 + } + if l.nocCards[0] != A_CARD && r.nocCards[0] == A_CARD { + return -1 + } + //对子比较(优先比对) + if l.Kind == CardsKind_Double || l.Kind == CardsKind_BigDouble { + if CardValueMap[l.nocCards[0]] > CardValueMap[r.nocCards[0]] { + return 1 + } else if CardValueMap[l.nocCards[0]] < CardValueMap[r.nocCards[0]] { + return -1 + } + } + for i := 2; i >= 0; i-- { + if CardValueMap[l.nocCards[i]] > CardValueMap[r.nocCards[i]] { + return 1 + } else if CardValueMap[l.nocCards[i]] < CardValueMap[r.nocCards[i]] { + return -1 + } + } + //大小相等 + return 0 +} + +// 比花色:同牌型最大单牌大小相同的情况下,最大单牌比较花色,黑桃>红桃>梅花>方片 +func CompareCardsSameKindByColor(l, r *KindOfCard) int { + if l.maxColor > r.maxColor { + return 1 + } else { + return -1 + } +} + +// 根据牌型创建相应的牌 +func CreateCardByKind(cards [2][3]int, k int) []int { + //先将两幅手牌放入一个数组中 + //在两幅手牌中找到花色相同的牌 + temp := make([]int, 0) + for i := 0; i < 2; i++ { + for j := 0; j < 3; j++ { + temp = append(temp, cards[i][j]) + } + } + + //根据不同的类型调用对应的生成函数 + if k == CardsKind_BigDouble { + return CreateBigDouble(temp) + } else if k == CardsKind_Straight { + return CreateStraight(temp) + } else if k == CardsKind_Flush { + return CreateFlush(temp) + } else if k == CardsKind_ThreeSame { + return CreateThreeSame(temp) + } + return temp +} +func CreateBigDouble(temp []int) []int { + //先找出牌面值大于8的任一一张牌 + sameCard := -1 + for idx, v := range temp { + c := v % PER_CARD_COLOR_MAX + if CardValueMap[c] > 8 { + sameCard = v + temp[0], temp[idx] = temp[idx], temp[0] + break + } + } + + //现有手牌里查找是否有相同的 + for i := 1; i < len(temp); i++ { + if temp[i]%PER_CARD_COLOR_MAX == sameCard%PER_CARD_COLOR_MAX { + temp[1], temp[i] = temp[i], temp[1] + return temp + } + } + //没有相同的,创造一张相同的牌 + temp[1] = sameCard%PER_CARD_COLOR_MAX + common.RandInt(1, 4)*PER_CARD_COLOR_MAX + if temp[1] > POKER_CART_CNT { + temp[1] = temp[1] - POKER_CART_CNT + } + return temp +} +func CreateStraight(temp []int) []int { + firstCard := 0 + //找出第一张不是K的牌 + for k, v := range temp { + c := v % PER_CARD_COLOR_MAX + if CardValueMap[c] < 12 { + firstCard = c + temp[0], temp[k] = temp[k], temp[0] + break + } + } + + //从第二张开始查找,现有的牌能否凑成顺子,不能则创造一张 + n := 1 + for i := 1; i < len(temp); i++ { + c := temp[i] % PER_CARD_COLOR_MAX + if CardValueMap[c] == firstCard+1 { + temp[n], temp[i] = temp[i], temp[n] + firstCard++ + n++ + } + } + + if n < 3 { + for i := n; i < 3; i++ { + temp[i] = (firstCard + 1) + common.RandInt(4)*PER_CARD_COLOR_MAX + firstCard++ + } + } + + return temp +} +func CreateFlush(temp []int) []int { + colorMap := make(map[int]int) + maxColor := 0 + for _, v := range temp { + c := v / PER_CARD_COLOR_MAX + colorMap[c] = colorMap[c] + 1 + if colorMap[c] > colorMap[maxColor] { + maxColor = c + } + } + + //将maxColor花色的牌放最前边 + n := 0 + for i, v := range temp { + c := v / PER_CARD_COLOR_MAX + if c == maxColor { + if i != n { + temp[n], temp[i] = temp[i], temp[n] + } + n++ + if n == colorMap[maxColor] || n == 3 { + break + } + } + } + + //6张牌中至少有2张牌的花色一样,所以最多更换一张牌的花色 + if colorMap[maxColor] < 3 { + for i := colorMap[maxColor]; i < len(temp); i++ { + v := temp[i] + isNext := false + + for j := 0; j < len(temp); j++ { + if j != i && v%PER_CARD_COLOR_MAX == temp[j]%PER_CARD_COLOR_MAX { + isNext = true + break + } + } + + if !isNext { + temp[i] = temp[i]%PER_CARD_COLOR_MAX + maxColor*PER_CARD_COLOR_MAX + temp[colorMap[maxColor]], temp[i] = temp[i], temp[colorMap[maxColor]] + break + } + } + } + return temp +} +func CreateThreeSame(temp []int) []int { + valueMap := make(map[int]int) + valueArray := make([]int, 0) + sameCard := -1 + for _, v := range temp { + c := v % PER_CARD_COLOR_MAX + valueMap[c] = valueMap[c] + 1 + if valueMap[c] > valueMap[sameCard] { + sameCard = c + } + } + + if valueMap[sameCard] > 1 { + //将相同的牌放最前边 + n := 0 + for i, v := range temp { + c := v % PER_CARD_COLOR_MAX + if c == sameCard { + valueArray = append(valueArray, v) + if i != n { + temp[n], temp[i] = temp[i], temp[n] + } + n++ + if n == valueMap[sameCard] || n == 3 { + break + } + } + } + } + + if valueMap[sameCard] < 3 { + for i := valueMap[sameCard]; i < 3; i++ { //创造豹子 + for j := 0; j < 4; j++ { + card := j*PER_CARD_COLOR_MAX + sameCard + if !common.InSliceInt(valueArray, card) { + temp[i] = card + valueArray = append(valueArray, card) + break + } + } + } + } + + return temp +} diff --git a/gamerule/redvsblack/cardkindfigureup_test.go b/gamerule/redvsblack/cardkindfigureup_test.go new file mode 100644 index 0000000..fc69668 --- /dev/null +++ b/gamerule/redvsblack/cardkindfigureup_test.go @@ -0,0 +1,191 @@ +package redvsblack + +import ( + "testing" +) + +//牌序- K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +type TestCaseData struct { + c1 []int + c2 []int + expectResult int +} + +func TestCompareCards(t *testing.T) { + compareFunc := func(data []*TestCaseData) { + for i := 0; i < len(data); i++ { + tc := data[i] + cardsInfoO1 := CardsKindFigureUpSington.FigureUpByCard(tc.c1) + cardsInfoO2 := CardsKindFigureUpSington.FigureUpByCard(tc.c2) + result := CompareCards(cardsInfoO1, cardsInfoO2) + if result != tc.expectResult { + t.Log(tc) + t.Fatal(cardsInfoO1, "compare", cardsInfoO2, "expect result=", tc.expectResult, " but result=", result) + } else { + //t.Log(cardsInfoO1, "compare", cardsInfoO2, "result=", tc.expectResult) + } + } + } + testCases := []*TestCaseData{ + &TestCaseData{ //♦2♦3♦4 vs ♦6♦4♦3 牌型不同 比牌型 顺子>散牌 + c1: []int{1, 3, 2}, c2: []int{5, 3, 2}, expectResult: 1, + }, + &TestCaseData{ //♦3♦5♣2 vs ♦6♣6♥6 牌型不同 比牌型 顺子>散牌 + c1: []int{14, 4, 2}, c2: []int{5, 18, 31}, expectResult: -1, + }, + &TestCaseData{ //♦3♦5♣2 vs ♦6♣8♦10 牌型相同,比大小 + c1: []int{14, 4, 2}, c2: []int{5, 20, 9}, expectResult: -1, + }, + &TestCaseData{ //♦3♦5♣2 vs ♥2♦5♦3 牌型相同,比大小,大小相同比花色 + c1: []int{14, 4, 2}, c2: []int{27, 17, 2}, expectResult: -1, + }, + &TestCaseData{ //♦A♦Q♦K vs ♣A♣Q♣K 牌型相同,比大小,大小相同比花色 + c1: []int{0, 11, 12}, c2: []int{13, 24, 25}, expectResult: -1, + }, + &TestCaseData{ //♣A♣Q♣K vs ♦A♦Q♦K 牌型相同,比花色 + c1: []int{13, 24, 25}, c2: []int{0, 11, 12}, expectResult: 1, + }, + &TestCaseData{ //♣A♣Q♣K vs ♦A♦2♦3 牌型相同,比花色 + c1: []int{13, 24, 25}, c2: []int{0, 1, 2}, expectResult: 1, + }, + &TestCaseData{ //♣J♣Q♣K vs ♦A♦2♦3 牌型相同,比花色 + c1: []int{23, 24, 25}, c2: []int{0, 1, 2}, expectResult: 1, + }, + &TestCaseData{ //♦A♣A♥A vs ♦2♣2♥2 牌型相同,比大小 + c1: []int{0, 13, 26}, c2: []int{1, 14, 27}, expectResult: 1, + }, + &TestCaseData{ //♥K♠K♦A vs ♣A♥A♦J 牌型相同,比大小 + c1: []int{38, 51, 0}, c2: []int{13, 26, 10}, expectResult: -1, + }, + &TestCaseData{ //♦2♦6♣8 vs ♦3♦4♣A 牌型相同,比大小 + c1: []int{1, 5, 20}, c2: []int{2, 3, 13}, expectResult: -1, + }, + &TestCaseData{ //♦2♦6♥A vs ♦3♦4♣A 牌型相同,比大小,大小相同比花色 + c1: []int{1, 5, 26}, c2: []int{2, 3, 13}, expectResult: 1, + }, + &TestCaseData{ //♦2♦6♣2 vs ♥2♦4♠2 牌型相同,比单牌大小 + c1: []int{1, 5, 14}, c2: []int{27, 3, 40}, expectResult: 1, + }, + &TestCaseData{ //♦2♦6♣2 vs ♥2♣7♠2 牌型相同,比单牌大小,大小相同比花色 + c1: []int{1, 5, 14}, c2: []int{27, 19, 40}, expectResult: -1, + }, + &TestCaseData{ //♦2♦3♦4-♣2♣3♣4 牌型相同,大小相同,比花色 + c1: []int{1, 2, 3}, c2: []int{14, 15, 16}, expectResult: -1, + }, + &TestCaseData{ //♦2♦3♦4-♦6♦7♦8 牌型相同,比大小 + c1: []int{1, 2, 3}, c2: []int{5, 6, 7}, expectResult: -1, + }, + &TestCaseData{ //♥6♥7♥8-♦6♦7♦8 牌型相同,比花色 + c1: []int{31, 32, 33}, c2: []int{5, 6, 7}, expectResult: 1, + }, + &TestCaseData{ //♦2♦3♦4-♣2♣3♣4 牌型相同,大小相同,比花色♣>♦ + c1: []int{1, 2, 3}, c2: []int{14, 15, 16}, expectResult: -1, + }, + &TestCaseData{ //♦2♦3♦4-♣2♣3♣4 牌型相同,大小相同,比花色 + c1: []int{1, 2, 3}, c2: []int{14, 15, 16}, expectResult: -1, + }, + &TestCaseData{ //♦A♦2♦3 vs ♦J♦Q♦K 牌型不同,♦A♦2♦3为最小同花顺 + c1: []int{0, 1, 2}, c2: []int{10, 11, 12}, expectResult: -1, + }, + &TestCaseData{ //♦3♣3♥5 vs ♥3♠3♣5 对子相同,单牌相同,比花色 + c1: []int{2, 15, 30}, c2: []int{28, 41, 17}, expectResult: 1, + }, + &TestCaseData{ //♦3♣3♥5 vs ♥3♠3♣5 对子相同,单牌相同,全比 + c1: []int{2, 15, 30}, c2: []int{28, 41, 17}, expectResult: 1, + }, + //对子相同,单牌相同,对子大 + &TestCaseData{ //♦3♣3♥5 vs ♥3♠3♣5 对子相同,单牌相同,全比,比5的花色 + c1: []int{2, 15, 30}, c2: []int{28, 41, 17}, expectResult: 1, + }, + &TestCaseData{ //♦6♣6♥5 vs ♥6♠6♣5 对子相同,单牌相同,全比,比6的花色 + c1: []int{5, 18, 30}, c2: []int{31, 44, 17}, expectResult: -1, + }, + &TestCaseData{ //♦3♣3♥5 vs ♥3♠3♣6 对子相同,单牌不同 + c1: []int{2, 15, 30}, c2: []int{28, 41, 18}, expectResult: -1, + }, + //对子相同,单牌不同,对子大 + &TestCaseData{ //♦10♣10♥5 vs ♥10♠10♣6 对子相同,单牌不同 + c1: []int{9, 22, 30}, c2: []int{35, 48, 18}, expectResult: -1, + }, + //对子不同,单牌相同,单牌大 + &TestCaseData{ //♦10♣10♥J vs ♥3♠3♠J 对子不同,单牌相同 + c1: []int{9, 22, 36}, c2: []int{28, 41, 49}, expectResult: 1, + }, + //对子不同,单牌相同,对子大 + &TestCaseData{ //♦10♣10♦8 vs ♥3♠3♥8 对子不同,单牌相同 + c1: []int{9, 22, 7}, c2: []int{28, 41, 33}, expectResult: 1, + }, + //对子不同,单牌不同,单牌大 + &TestCaseData{ //♦10♣10♥J vs ♥3♠3♥8 对子不同,单牌不同 + c1: []int{9, 22, 36}, c2: []int{28, 41, 33}, expectResult: 1, + }, + //对子不同,单牌不同,对子大 + &TestCaseData{ //♦10♣10♦7 vs ♥3♠3♥8 对子不同,单牌不同 + c1: []int{9, 22, 6}, c2: []int{28, 41, 33}, expectResult: 1, + }, + &TestCaseData{ //♣A♣Q♣K vs ♦A♦2♦3 天龙 vs 地龙 + c1: []int{13, 24, 25}, c2: []int{0, 1, 2}, expectResult: 1, + }, + &TestCaseData{ //♥6♥7♥8 vs ♦A♦2♦3 牌型不同 + c1: []int{31, 32, 33}, c2: []int{0, 1, 2}, expectResult: 1, + }, + &TestCaseData{ //♥A♥2♥3 vs ♦A♦2♦3 牌型不同,比花色 + c1: []int{26, 27, 28}, c2: []int{0, 1, 2}, expectResult: 1, + }, + &TestCaseData{ //♥A♥2♥3 vs ♦4♦Q♥J 牌型不同 + c1: []int{26, 27, 28}, c2: []int{3, 11, 36}, expectResult: 1, + }, + &TestCaseData{ //♦A♦2♦3 vs ♣K♣A♣2 牌型不同 + c1: []int{0, 1, 2}, c2: []int{25, 13, 14}, expectResult: 1, + }, + } + + compareFunc(testCases) +} + +func TestFigureUp(t *testing.T) { + testFunc := func(testData []TestCaseData) { + for i := 0; i < len(testData); i++ { + data := testData[i] + cardsKind := CardsKindFigureUpSington.FigureUpByCard(data.c1) + if cardsKind.Kind != data.expectResult { + t.Log(data) + t.Log(cardsKind) + t.Fatal("is not expect") + } else { + cardsKind.TidyCards() + t.Log(cardsKind) + } + } + } + testData := []TestCaseData{ + //散牌 + {c1: []int{1, 15, 19}, expectResult: CardsKind_Single}, + {c1: []int{1, 4, 15}, expectResult: CardsKind_Single}, + {c1: []int{12, 13, 14}, expectResult: CardsKind_Single}, + //对子 + {c1: []int{1, 14, 15}, expectResult: CardsKind_Double}, + {c1: []int{43, 4, 22}, expectResult: CardsKind_Double}, //♠5♦5♣10 + {c1: []int{8, 21, 15}, expectResult: CardsKind_BigDouble}, + {c1: []int{0, 13, 15}, expectResult: CardsKind_BigDouble}, + //顺子 + {c1: []int{19, 5, 33}, expectResult: CardsKind_Straight}, + {c1: []int{33, 32, 8}, expectResult: CardsKind_Straight}, + {c1: []int{13, 1, 2}, expectResult: CardsKind_StraightA23}, //地龙 + //同花 + {c1: []int{13, 20, 23}, expectResult: CardsKind_Flush}, + //同花顺 + {c1: []int{1, 3, 2}, expectResult: CardsKind_FlushStraight}, + {c1: []int{0, 1, 2}, expectResult: CardsKind_FlushStraightA23}, //地龙 + {c1: []int{0, 11, 12}, expectResult: CardsKind_RoyalFlushStraight}, //天龙 + //豹子 ♦3♣3♥3 + {c1: []int{2, 15, 28}, expectResult: CardsKind_ThreeSame}, + } + testFunc(testData) + +} diff --git a/gamerule/redvsblack/constant.go b/gamerule/redvsblack/constant.go new file mode 100644 index 0000000..9506c79 --- /dev/null +++ b/gamerule/redvsblack/constant.go @@ -0,0 +1,29 @@ +package redvsblack + +import "time" + +const ( + RedVsBlackStakeAntTimeout = time.Second * 2 //准备押注 + RedVsBlackStakeTimeout = time.Second * 10 //押注 + RedVsBlackOpenCardAntTimeout = time.Second * 1 //准备开牌 + RedVsBlackOpenCardTimeout = time.Second * 6 //开牌 + RedVsBlackBilledTimeout = time.Millisecond * 3500 //结算 + RedVsBlackRecordTime = 5 //回收金币记录时间 + RedVsBlackBatchSendBetTimeout = time.Second * 1 //发送下注数据时间间隔 +) + +//场景状态 +const ( + RedVsBlackSceneStateStakeAnt int = iota //准备押注 + RedVsBlackSceneStateStake //押注 + RedVsBlackSceneStateOpenCardAnt //准备开牌 + RedVsBlackSceneStateOpenCard //开牌 + RedVsBlackSceneStateBilled //结算 + RedVsBlackSceneStateMax +) + +//玩家操作 +const ( + RedVsBlackPlayerOpBet int = iota //下注 + RedVsBlackPlayerOpGetOLList //获取在线列表 +) diff --git a/gamerule/redvsblack/poker.go b/gamerule/redvsblack/poker.go new file mode 100644 index 0000000..ad5c858 --- /dev/null +++ b/gamerule/redvsblack/poker.go @@ -0,0 +1,117 @@ +package redvsblack + +import ( + "math/rand" + "time" +) + +const ( + POKER_CART_CNT int = 52 + PER_CARD_COLOR_MAX = 13 + Hand_CardNum = 3 + A_CARD = 0 +) + +var cardSeed = time.Now().UnixNano() + +type Card int + +var CardValueMap = [PER_CARD_COLOR_MAX]int{13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} +var CardFaceMap = [PER_CARD_COLOR_MAX]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} + +type Poker struct { + buf [POKER_CART_CNT]Card + pos int +} + +func NewPoker() *Poker { + p := &Poker{} + p.init() + return p +} + +func (this *Poker) init() { + for i := 0; i < POKER_CART_CNT; i++ { + this.buf[i] = Card(i) + } + this.Shuffle() +} + +func (this *Poker) Shuffle() { + cardSeed++ + rand.Seed(cardSeed) + for i := 0; i < POKER_CART_CNT; i++ { + j := rand.Intn(i + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } + this.pos = 0 +} + +func (this *Poker) Next() Card { + if this.pos >= len(this.buf) { + return -1 + } + c := this.buf[this.pos] + this.pos++ + return c +} + +func (this *Poker) TryNextN(n int) Card { + if this.pos+n >= len(this.buf) { + return -1 + } + return this.buf[this.pos+n] +} + +func (this *Poker) ChangeNextN(n int, c Card) bool { + if this.pos+n >= len(this.buf) { + return false + } + this.buf[this.pos+n] = c + return true +} + +func (this *Poker) Count() int { + if len(this.buf) >= this.pos { + cnt := len(this.buf) - this.pos + if cnt < 0 { + cnt = 0 + } + return cnt + } + return 0 +} +func (this *Poker) TryCard(flag, kind int) ([]int, []int) { + this.Shuffle() + rCards := []int{} + bCards := []int{} + for i := 0; i < 6; i++ { + cards := [2][]int{} + for j := 0; j < Hand_CardNum; j++ { + cards[0] = append(cards[0], int(this.Next())) + cards[1] = append(cards[1], int(this.Next())) + } + cardsKind := [2]*KindOfCard{} + cardsKind[0] = CardsKindFigureUpSington.FigureUpByCard(cards[0]) + cardsKind[1] = CardsKindFigureUpSington.FigureUpByCard(cards[1]) + if flag == 0 { + if CompareCards(cardsKind[0], cardsKind[1]) == 1 && cardsKind[0].Kind == kind { + return cards[0], cards[1] + } + if CompareCards(cardsKind[0], cardsKind[1]) == 0 && cardsKind[1].Kind == kind { + return cards[1], cards[0] + } + } + if flag == 1 { + if CompareCards(cardsKind[0], cardsKind[1]) == 0 && cardsKind[1].Kind == kind { + return cards[0], cards[1] + } + if CompareCards(cardsKind[0], cardsKind[1]) == 1 && cardsKind[0].Kind == kind { + return cards[1], cards[0] + } + } + bCards = cards[0] + rCards = cards[1] + } + return bCards, rCards +} diff --git a/gamerule/redvsblack/poker_test.go b/gamerule/redvsblack/poker_test.go new file mode 100644 index 0000000..370f790 --- /dev/null +++ b/gamerule/redvsblack/poker_test.go @@ -0,0 +1,55 @@ +package redvsblack + +import ( + "testing" +) + +func TestPoker(t *testing.T) { + p := NewPoker() + dup := make(map[Card]bool) + for i := 0; i < POKER_CART_CNT; i++ { + card := p.Next() + if _, exist := dup[card]; exist { + t.Fatal("found dup card", card) + } + dup[card] = true + if card == -1 { + t.Fatal("not expect -1") + } else { + t.Log(card) + } + } +} + +//70% failed to try appoint card +func TestTryCard(t *testing.T) { + p := NewPoker() + failedCount := 0 + for i := 0; i < 10000; i++ { + bCard, rCard := p.TryCard(0, 1) + if p.Count() > 16 { + bKind := CardsKindFigureUpSington.FigureUpByCard(bCard) + rKind := CardsKindFigureUpSington.FigureUpByCard(rCard) + if CompareCards(bKind, rKind) != 1 { + t.Log(bCard, rCard) + t.Log(bKind, rKind) + t.Fatal() + } + } else { + failedCount++ + } + bCard, rCard = p.TryCard(1, 1) + if p.Count() > 16 { + bKind := CardsKindFigureUpSington.FigureUpByCard(bCard) + rKind := CardsKindFigureUpSington.FigureUpByCard(rCard) + if CompareCards(bKind, rKind) != -1 { + t.Log(bCard, rCard) + t.Log(bKind, rKind) + t.Fatal() + } + } else { + //failedCount++ + } + } + //fmt.Println(failedCount) +} diff --git a/gamerule/richblessed/1_test.go b/gamerule/richblessed/1_test.go new file mode 100644 index 0000000..5198976 --- /dev/null +++ b/gamerule/richblessed/1_test.go @@ -0,0 +1,89 @@ +package richblessed + +import ( + "fmt" + "testing" +) + +func TestName(t *testing.T) { + //eleLineAppearRate := []int32{926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481, 5000, 5000} + // 0万能元素 福字 Scatter 1 铜锣 GoldenPhoenix 2 金凤凰 Sailboat 3 //帆船GoldenTortoise 4金龟 + // GoldIngot 5 金元宝Copper 6 金钱币 A 7 A K 8 K Q 9 Q J 10 J Ten 11 10 Nine 12 9 + //eleLineAppearRate := []int32{100, 50, 60, 50, 50, 60, 50, 0, 0, 0, 0, 0, 0} + + //fileName := fmt.Sprintf("classic888-%v-%d.csv", 0, 0) + //file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + //defer file.Close() + //if err != nil { + // file, err = os.Create(fileName) + // if err != nil { + // return + // } + //} + //file.WriteString("随机倍率\n") + + for i := 0; i < 20; i++ { + var wls WinResult + // wls.CreateLine(eleLineAppearRate) + wls.Win(10, 10) + var rate int64 + for _, v := range wls.WinLine { + rate += v.Rate + fmt.Println(v.LineId, v.Lines, v.Poss) + } + fmt.Println(wls.WinLine, wls.AllRate, wls.FreeNum) + if len(wls.WinLine) > 0 { + break + } + //str := fmt.Sprintf("%v\r\n", rate) + //file.WriteString(str) + } + +} + +func TestWin(t *testing.T) { + //eleLineAppearRate := []int32{926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481, 5000, 5000} + //eleLineAppearRate := []int32{100, 50, 60, 50, 50, 60, 50, 0, 0, 0, 0, 0, 0} + + var wls WinResult + //wls.CreateLine(eleLineAppearRate) + wls.EleValue = []int32{1, 1, 1, 0, 5, 6, 2, 7, 7, 9, 4, 7, 4, 8, 7} + for i := 0; i != 1000; i++ { + if RandJACKPOT(1000, 1000) { + + fmt.Println("i:", i) + break + } + } + wls.Win(10, 10) + var rate int64 + for _, v := range wls.WinLine { + rate += v.Rate + fmt.Println(v.LineId, v.Lines, v.Poss) + } + fmt.Println(wls.WinLine, rate, wls.FreeNum) + +} + +func TestJackWin(t *testing.T) { + //eleLineAppearRate := []int32{926, 926, 556, 556, 741, 741, 741, 926, 1111, 1296, 1481, 5000, 5000} + //eleLineAppearRate := []int32{100, 50, 60, 50, 50, 60, 50, 0, 0, 0, 0, 0, 0} + + var wls WinResult + //wls.CreateLine(eleLineAppearRate) + wls.EleValue = []int32{1, 0, 1, 0, 5, 6, 2, 7, 7, 9, 4, 7, 4, 8, 7} + JACKPOTElementsParams := []int32{20, 30, 100, 150} + ret := wls.CanJACKPOT(1000, 1000) + if ret { + ele := wls.CreateJACKPOT(JACKPOTElementsParams) + fmt.Println(ret, ele) + } + wls.Win(10, 10) + var rate int64 + for _, v := range wls.WinLine { + rate += v.Rate + fmt.Println(v.LineId, v.Lines, v.Poss) + } + fmt.Println(wls.WinLine, rate, wls.FreeNum) + +} diff --git a/gamerule/richblessed/constants.go b/gamerule/richblessed/constants.go new file mode 100644 index 0000000..7ce539d --- /dev/null +++ b/gamerule/richblessed/constants.go @@ -0,0 +1,160 @@ +package richblessed + +// 房间类型 +const ( + RoomMode_Classic int = iota //经典 + RoomMode_Max +) + +// 场景状态 +const ( + RichBlessedStateStart int = iota //默认状态 + RichBlessedStateMax +) + +// 玩家操作 +const ( + RichBlessedPlayerOpStart int = iota + RichBlessedPlayerOpSwitch + RichBlessedPlayerOpJack +) +const NowByte int64 = 10000 +const ( + Normal = iota //正常 + FreeGame //免费游戏 + JackGame //Jack +) +const ( + Column = 3 + Row = 5 + LineNum = 88 +) +const ( + Wild int32 = iota //万能元素 福字 + Scatter //铜锣 + GoldenPhoenix //金凤凰 + Sailboat //帆船 + GoldenTortoise //金龟 + GoldIngot //金元宝 + Copper //金钱币 + A //A + K //K + Q //Q + J //J + Ten //10 + Nine //9 + EleMax +) + +const ( + GoldBoy int32 = iota //金色男孩 金娃 + GoldGirl //金色女孩 蓝娃 + BlueBoy //蓝色男孩 绿娃 + BlueGirl //蓝色女孩 紫娃 + JackMax +) + +// 元素对应数量的倍率 +var EleNumRate = make(map[int32]map[int]int64) + +var JkEleNumRate = [4]int64{1000000, 75000, 5000, 1500} + +var JKWeight = []int32{10, 20, 80, 400} + +type WinLine struct { + Lines []int32 + Poss []int32 + LineId int + Rate int64 +} +type WinResult struct { + EleValue []int32 + WinLine []WinLine //赢的线数 + // JackPotNum int //JackPot数量 按数量 翻倍 给奖池 + FreeNum int32 + AllRate int64 + + IsHaveScatter bool + + //JACKPOT游戏 + JackpotEle int32 //中奖元素 + JackpotRate int64 +} + +var EleWeight = [][]int32{ + {0, 996, 2350, 2380, 2460, 2680, 3450, 4650, 4650, 4650, 4650, 4650, 4650}, + {9530, 810, 1600, 2460, 3250, 3970, 5100, 7950, 7950, 7950, 7950, 7950, 7950}, + {9450, 500, 1850, 2200, 3600, 3850, 5390, 6760, 6760, 6760, 6760, 6760, 6760}, + {9350, 1050, 2000, 2600, 3909, 4540, 4700, 5750, 5750, 5750, 5750, 5750, 5750}, + {0, 970, 1390, 2580, 3180, 4400, 4800, 3090, 3000, 3000, 3000, 3000, 3000}, + + {0, 2900, 7800, 8700, 5850, 6150, 5500, 0, 0, 0, 0, 0, 0}, + {1810, 3900, 7800, 8800, 6000, 6250, 5150, 0, 0, 0, 0, 0, 0}, + {3050, 1780, 5810, 7200, 8000, 9500, 10000, 0, 0, 0, 0, 0, 0}, + {940, 400, 880, 1850, 4050, 7400, 9900, 0, 0, 0, 0, 0, 0}, + {0, 900, 158, 500, 1550, 4500, 9600, 0, 0, 0, 0, 0, 0}, + + {0, 0, 100, 20000}, +} + +func init() { + EleNumRate[Scatter] = make(map[int]int64) + EleNumRate[Scatter][3] = 5 * LineNum + EleNumRate[Scatter][4] = 10 * LineNum + EleNumRate[Scatter][5] = 50 * LineNum + + EleNumRate[GoldenPhoenix] = make(map[int]int64) + EleNumRate[GoldenPhoenix][3] = 100 + EleNumRate[GoldenPhoenix][4] = 200 + EleNumRate[GoldenPhoenix][5] = 1000 + + EleNumRate[Sailboat] = make(map[int]int64) + EleNumRate[Sailboat][3] = 50 + EleNumRate[Sailboat][4] = 100 + EleNumRate[Sailboat][5] = 500 + + EleNumRate[GoldenTortoise] = make(map[int]int64) + EleNumRate[GoldenTortoise][3] = 40 + EleNumRate[GoldenTortoise][4] = 80 + EleNumRate[GoldenTortoise][5] = 400 + + EleNumRate[GoldIngot] = make(map[int]int64) + EleNumRate[GoldIngot][3] = 25 + EleNumRate[GoldIngot][4] = 50 + EleNumRate[GoldIngot][5] = 250 + + EleNumRate[Copper] = make(map[int]int64) + EleNumRate[Copper][3] = 10 + EleNumRate[Copper][4] = 20 + EleNumRate[Copper][5] = 100 + + EleNumRate[A] = make(map[int]int64) + EleNumRate[A][3] = 5 + EleNumRate[A][4] = 10 + EleNumRate[A][5] = 50 + + EleNumRate[K] = make(map[int]int64) + EleNumRate[K][3] = 5 + EleNumRate[K][4] = 10 + EleNumRate[K][5] = 50 + + EleNumRate[Q] = make(map[int]int64) + EleNumRate[Q][3] = 5 + EleNumRate[Q][4] = 10 + EleNumRate[Q][5] = 50 + + EleNumRate[J] = make(map[int]int64) + EleNumRate[J][3] = 5 + EleNumRate[J][4] = 10 + EleNumRate[J][5] = 50 + + EleNumRate[Ten] = make(map[int]int64) + EleNumRate[Ten][3] = 5 + EleNumRate[Ten][4] = 10 + EleNumRate[Ten][5] = 50 + + EleNumRate[Nine] = make(map[int]int64) + EleNumRate[Nine][3] = 5 + EleNumRate[Nine][4] = 10 + EleNumRate[Nine][5] = 50 +} diff --git a/gamerule/richblessed/func.go b/gamerule/richblessed/func.go new file mode 100644 index 0000000..65f915a --- /dev/null +++ b/gamerule/richblessed/func.go @@ -0,0 +1,113 @@ +package richblessed + +import ( + "fmt" + "math/rand" + "strconv" +) + +func GetRate(ele int32, num int) int64 { + if data, ok := EleNumRate[ele]; ok { + if r, ok2 := data[num]; ok2 { + return r + } + } + return 0 +} + +func RandJACKPOT(bet int64, big int64) bool { // bet 用户在房间的单次下注/房间内最大下注 big 放大倍率 + ret := false + rate := float64(bet) / float64(big) * 0.003 + if rand.Float64() < rate { + ret = true + } + return ret +} + +func RandSliceInt32IndexByWightN(s1 []int32) int32 { + total := 0 + for _, v := range s1 { + total += int(v) + } + if total <= 0 { + return 0 + } + random := rand.Intn(total) + total = 0 + for i, v := range s1 { + total += int(v) + if random < total { + return int32(i) + } + } + return 0 +} +func PrintEle(idx int32) (str string) { + switch idx { + case Wild: + str += "福字 ," + case Scatter: + str += "铜锣 ," + case GoldenPhoenix: + str += "金凤凰 ," + case Sailboat: + str += "帆船 ," + case GoldenTortoise: + str += "金龟 ," + case GoldIngot: + str += "金元宝 ," + case Copper: + str += "金钱币 ," + case A: + str += "A ," + case K: + str += "K ," + case Q: + str += "Q ," + case J: + str += "J ," + case Ten: + str += "10 ," + case Nine: + str += "9 ," + } + return str +} +func Print(res []int32) { + fmt.Println(res) + str := "" + for k, ele := range res { + switch ele { + case Wild: + str += "福字 ," + case Scatter: + str += "铜锣 ," + case GoldenPhoenix: + str += "金凤凰 ," + case Sailboat: + str += "帆船 ," + case GoldenTortoise: + str += "金龟 ," + case GoldIngot: + str += "金元宝 ," + case Copper: + str += "金钱币 ," + case A: + str += "A ," + case K: + str += "K ," + case Q: + str += "Q ," + case J: + str += "J ," + case Ten: + str += "10 ," + case Nine: + str += "9 ," + } + if (k+1)%5 == 0 { + fmt.Println("第", strconv.Itoa((k+1)/5), "行 ", str) + str = "" + } + } +} diff --git a/gamerule/richblessed/richblessedAlgorithm.go b/gamerule/richblessed/richblessedAlgorithm.go new file mode 100644 index 0000000..67f556a --- /dev/null +++ b/gamerule/richblessed/richblessedAlgorithm.go @@ -0,0 +1,286 @@ +package richblessed + +func (w *WinResult) Init() { + w.EleValue = []int32{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1} // make([]int32, 15) + // w.JackPotNum = 0 + w.WinLine = nil + w.IsHaveScatter = false +} + +// 正常游戏 免费游戏 +func (w *WinResult) CreateLine(ele [][]int32) { + w.Init() + w.result(ele) + //Print(w.EleValue) +} + +// 离开免费游戏 +func (w *WinResult) CreateLevLine(ele [][]int32) { + w.Init() + w.levresult(ele) + //Print(w.EleValue) +} + +// JACKPOT游戏 +func (w *WinResult) InitJACKPOT() { + w.JackpotEle = -1 +} + +// JACKPOT游戏 +func (w *WinResult) CreateJACKPOT(ele []int32) int32 { + w.InitJACKPOT() + w.JackpotEle = RandSliceInt32IndexByWightN(ele) + return w.JackpotEle +} + +func (w *WinResult) CanJACKPOT(bet int64, big int64) (ret bool) { + if w.IsHaveScatter { + ret = RandJACKPOT(bet, big) + } + return +} + +// 奖池中奖 (当前下注 最大下注) 返回中的奖池索引 + +func (w *WinResult) WinJackPot(bet, maxBet int64) int { + if w.CanJACKPOT(bet, maxBet) { + idx := RandSliceInt32IndexByWightN(JKWeight) + return int(idx) + } + return -1 +} +func (w *WinResult) Win(bet, maxBet int64) { + w.getWinLineAndFree() + + // w.WinJackPot(bet, maxBet) +} + +func (w *WinResult) JACKPOTWin() { + if w.JackpotEle >= 0 { + w.JackpotRate = JkEleNumRate[int(w.JackpotEle)] + } +} + +func (w *WinResult) resultele(eles [][]int32) { + + for n, ele := range eles { // 每行元素有自己的概率 + + w.EleValue[n] = RandSliceInt32IndexByWightN(ele) + + } +} + +func (w *WinResult) levresult(ele [][]int32) { + n := 0 + for j := 0; j < Row; j++ { + gongnum := 0 + for i := 0; i < Column; i++ { + n = i*Row + j + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if (w.EleValue[n] == Wild || w.EleValue[n] == Scatter) && (j == 0 || j == Row-1) { // 最左或者最右不能为万能牌 砍掉免费次数机会 + + for ra := 0; ra != 100; ra++ { + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if w.EleValue[n] != Wild { + break + } + } + } + if w.EleValue[n] == Wild || w.EleValue[n] == Scatter { + gongnum++ + if gongnum > 1 { // 一列只能有一个铜锣或万能牌 + for ra := 0; ra != 100; ra++ { // 100次换不掉就是给的概率有问题 + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if w.EleValue[n] != Wild && w.EleValue[n] != Scatter { + break + } + } + } + } + if w.EleValue[n] == Wild { + w.IsHaveScatter = true + } + + } + } +} + +func (w *WinResult) result(ele [][]int32) { + n := 0 + for j := 0; j < Row; j++ { + gongnum := 0 + for i := 0; i < Column; i++ { + n = i*Row + j + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if w.EleValue[n] == Wild && (j == 0 || j == Row-1) { // 最左或者最右不能为万能牌 + + for ra := 0; ra != 100; ra++ { + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if w.EleValue[n] != Wild { + break + } + } + } + if w.EleValue[n] == Wild || w.EleValue[n] == Scatter { + gongnum++ + if gongnum > 1 { // 一列只能有一个铜锣或万能牌 + for ra := 0; ra != 100; ra++ { // 100次换不掉就是给的概率有问题 + w.EleValue[n] = RandSliceInt32IndexByWightN(ele[j]) + if w.EleValue[n] != Wild && w.EleValue[n] != Scatter { + break + } + } + } + } + if w.EleValue[n] == Wild { + w.IsHaveScatter = true + } + + } + } +} + +// 0 1 2 3 4 +// 5 6 7 8 9 +// 10 11 12 13 14 +func (w *WinResult) getWinLine() { + var ele [][]int + var count int + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + for m := 0; m < 3; m++ { + for n := 0; n < 3; n++ { + for x := 0; x < 3; x++ { + a1, a2, a3, a4, a5 := i*5, j*5+1, m*5+2, n*5+3, x*5+4 + el := []int{a1, a2, a3, a4, a5} + //fmt.Println(el) + ele = append(ele, el) + count++ + + var line []int32 + var pos []int32 + var num int + var flag = w.EleValue[a1] + for _, key := range el { + if flag == w.EleValue[key] { + line = append(line, w.EleValue[key]) + pos = append(pos, int32(key)) + num++ + } else { + if num >= 3 { + w.WinLine = append(w.WinLine, WinLine{ + Lines: line, + Poss: pos, + LineId: count, + Rate: GetRate(flag, num), + }) + } + break + } + } + } + } + } + } + } + //fmt.Println("lel:", len(ele)) +} + +/* +func (w *WinResult) getWinEleAndFree() { + var winret [15]int32 + var winnum [15]int32 + for i := 0; i != Column; i++ { //横 + winnum[w.EleValue[i*Row]]++ + } + for j := 1; j != Row; j++ { + ele1, ele2, ele3 := int32(0), int32(0), int32(0) + ele1 = w.EleValue[j] + ele2 = w.EleValue[1*Row+j] + ele3 = w.EleValue[2*Row+j] + for i := 0; i != Column; i++ { //横 + winnum[w.EleValue[i*Row]]++ + } + + } +}*/ + +func (w *WinResult) getWinLineAndFree() { + // var weles []WinLine + //Print(w.EleValue) + for i := 0; i != Column; i++ { //横 + var wele []WinLine + for k := 0; k != Column; k++ { + //wel + wel := WinLine{ + Lines: []int32{w.EleValue[i*Row]}, // 元素 + Poss: []int32{int32(i * Row)}, // 位置 + } + if w.EleValue[i*Row] == w.EleValue[k*Row+1] || w.EleValue[k*Row+1] == Wild { + + wel.Lines = append(wel.Lines, w.EleValue[k*Row+1]) + wel.Poss = append(wel.Poss, int32(k*Row+1)) + wele = append(wele, wel) + } + + } + if len(wele) == 0 { + continue + } + for j := 2; j != Row; j++ { + + del := append([]WinLine{}, wele...) + wele = wele[:0] // 长线代替短线 + for k := 0; k != Column; k++ { + if w.EleValue[i*Row] == w.EleValue[k*Row+j] || w.EleValue[k*Row+j] == Wild { + + for _, wel := range del { + var newwel WinLine + newwel.Lines = append(newwel.Lines, wel.Lines...) + newwel.Poss = append(newwel.Poss, wel.Poss...) + newwel.Lines = append(newwel.Lines, w.EleValue[k*Row+j]) + newwel.Poss = append(newwel.Poss, int32(k*Row+j)) + newwel.Rate = GetRate(newwel.Lines[0], len(newwel.Lines)) + wele = append(wele, newwel) + // fmt.Println("index: ", i*Row, w.EleValue[i*Row], w.EleValue[k*Row+j], wel, newwel, k*Row+j, k) + } + + } + } + + if len(wele) == 0 { + if j > 2 { + w.WinLine = append(w.WinLine, del...) + } + break + } else if j == Row-1 { + w.WinLine = append(w.WinLine, wele...) + } + + } + // weles = append(weles, wele...) + } + for _, v := range w.WinLine { + w.AllRate += v.Rate + } + fs := 0 + for j := 0; j < Row; j++ { // 前3列就可判断 + for i := 0; i < Column; i++ { + if w.EleValue[i*Row+j] == Scatter || w.EleValue[i*Row+j] == Wild { + fs++ + // fmt.Println("位置: ", j, i, fs, i*Row+j, w.EleValue[i*Row+j]) + break + } + } + if fs != j+1 { + break + } + if fs == 3 { + w.FreeNum += 10 //10 + break + } + } + + //fmt.Println("fs: ", fs) +} diff --git a/gamerule/rollcoin/rollcoin.go b/gamerule/rollcoin/rollcoin.go new file mode 100644 index 0000000..8e4d530 --- /dev/null +++ b/gamerule/rollcoin/rollcoin.go @@ -0,0 +1,34 @@ +package rollcoin + +import "time" + +const ( + RollCoinWaiteTimeout = time.Second * 0 //等待 + RollCoinStartTimeout = time.Second * 1 //开始 + RollCoinRollTimeout = time.Second * 18 //押注 + RollCoinCoinOverTimeout = time.Second * 1 //结束 + RollCoinGameTimeOut = time.Second * 14 //比赛 + + RollCoinBilledTimeout = time.Second * 5 //结算 +) + +//场景状态 +const ( + RollCoinSceneStateWait int = iota //等待状态 + RollCoinSceneStateStart //开始 + RollCoinSceneStateRoll //押注 + RollCoinSceneStateCoinOver //押注结束 + RollCoinSceneStateGame //比赛 + RollCoinSceneStateBilled //结算 + RollCoinSceneStateMax +) + +//玩家操作 +const ( + RollCoinPlayerOpPushCoin int = iota //押注 + RollCoinPlayerOpBanker //坐庄 + RollCoinPlayerOpBankerList //坐庄列表 + RollCoinPlayerList //玩家列表 + RollCoinPlayerOpDownBanker //下庄 + RollCoinPlayerOpCurDownBanker //当前庄下局下庄 +) diff --git a/gamerule/rollpoint/constants.go b/gamerule/rollpoint/constants.go new file mode 100644 index 0000000..9477d25 --- /dev/null +++ b/gamerule/rollpoint/constants.go @@ -0,0 +1 @@ +package rollpoint diff --git a/gamerule/rollpoint/point.go b/gamerule/rollpoint/point.go new file mode 100644 index 0000000..50dfbfd --- /dev/null +++ b/gamerule/rollpoint/point.go @@ -0,0 +1,173 @@ +package rollpoint + +import ( + "math/rand" + "time" +) + +const ( + RollNum = 3 +) +const ( + BetArea_1 int32 = iota + BetArea_2 + BetArea_3 + BetArea_4 + BetArea_5 + BetArea_6 //5 + BetArea_Sum4 //6 + BetArea_Sum5 + BetArea_Sum6 + BetArea_Sum7 + BetArea_Sum8 + BetArea_Sum9 + BetArea_Sum10 + BetArea_Sum11 + BetArea_Sum12 + BetArea_Sum13 + BetArea_Sum14 + BetArea_Sum15 + BetArea_Sum16 + BetArea_Sum17 + BetArea_Small //20 + BetArea_Big //21 + BetArea_Double1 //22 + BetArea_Double2 + BetArea_Double3 + BetArea_Double4 + BetArea_Double5 + BetArea_Double6 + BetArea_Boom //28 + BetArea_Boom1 //29 + BetArea_Boom2 + BetArea_Boom3 + BetArea_Boom4 + BetArea_Boom5 + BetArea_Boom6 //34 + BetArea_Max +) + +var rate = [BetArea_Max]int32{ + 1, 1, 1, 1, 1, 1, //Area1-6 + 60, 30, 20, 12, 8, 6, 6, 6, 6, 8, 12, 20, 30, 60, //Sum4-17 + 1, 1, //small,big + 10, 10, 10, 10, 10, 10, //Double1-6 + 30, //Boom + 200, 200, 200, 200, 200, 200, //Boom1-6 +} + +// 5 6 7 8 9 10 +// 9/10/11/12| 8/13| 7/14| 6/15| 5/16| 4/17 +var AreaIndex2MaxChipIndex = [BetArea_Max]int32{ + 1, 1, 1, 1, 1, 1, //Area1-6 + 10, 9, 8, 7, 6, 5, 5, 5, 5, 6, 7, 8, 9, 10, //Sum4-17 + 0, 0, //small,big + 2, 2, 2, 2, 2, 2, //Double1-6 + 3, //Boom + 4, 4, 4, 4, 4, 4, //Boom1-6 +} +var MaxChipColl = [BetArea_Max][]int32{} +var AllScore = [][BetArea_Max]int32{} +var AllRoll = [][RollNum]int32{} + +type BlackBox struct { + first *rand.Rand + second *rand.Rand + third *rand.Rand + count int + Point [RollNum]int32 + Score [BetArea_Max]int32 +} + +func CreateBlackBox() *BlackBox { + return &BlackBox{ + count: 0, + first: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + second: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + third: rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))), + Point: [RollNum]int32{}, + } +} +func (bb *BlackBox) Roll() { + if bb.count > 10 { + bb.first = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.second = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.third = rand.New(rand.NewSource(rand.Int63n(time.Now().UnixNano()))) + bb.count = 0 + } + bb.count++ + bb.Point[0] = bb.first.Int31n(6) + bb.Point[1] = bb.second.Int31n(6) + bb.Point[2] = bb.third.Int31n(6) + bb.Score = CalcPoint(bb.Point) +} +func CalcPoint(point [RollNum]int32) [BetArea_Max]int32 { + score := [BetArea_Max]int32{} + if point[0] == -1 || point[1] == -1 || point[2] == -1 { + point[0] = rand.Int31n(6) + point[1] = rand.Int31n(6) + point[2] = rand.Int31n(6) + } + sum := int32(0) + //Area1-6 + for _, value := range point { + score[value] += 1 + sum += (value + 1) + } + //Sum4-17 + if sum >= 4 && sum <= 17 { + index := (sum - 4) + BetArea_Sum4 + score[index] = rate[index] + } + //small + if sum >= 4 && sum <= 10 { + score[BetArea_Small] = rate[BetArea_Small] + } + //big + if sum >= 11 && sum <= 17 { + score[BetArea_Big] = rate[BetArea_Big] + } + //Double1-6 + if point[0] == point[1] { + index := point[0] + BetArea_Double1 + score[index] = rate[index] + } + if point[0] == point[2] { + index := point[0] + BetArea_Double1 + score[index] = rate[index] + } + if point[1] == point[2] { + index := point[1] + BetArea_Double1 + score[index] = rate[index] + } + //Boom Boom1-6 + if point[0] == point[1] && point[1] == point[2] { + //Boom + score[BetArea_Boom] += rate[BetArea_Boom] + //Boom1-6 + index := point[0] + BetArea_Boom1 + score[index] = rate[index] + score[BetArea_Small] = 0 + score[BetArea_Big] = 0 + } + return score +} +func init() { + for i := int32(0); i < 6; i++ { + for j := int32(0); j < 6; j++ { + for k := int32(0); k < 6; k++ { + AllScore = append(AllScore, CalcPoint([RollNum]int32{i, j, k})) + AllRoll = append(AllRoll, [RollNum]int32{i, j, k}) + } + } + } + for i := int32(0); i < BetArea_Max; i++ { + arr := []int32{} + for k := int32(0); k < BetArea_Max; k++ { + if AreaIndex2MaxChipIndex[k] == AreaIndex2MaxChipIndex[i] { + arr = append(arr, k) + } + } + MaxChipColl[i] = arr + } +} diff --git a/gamerule/rollpoint/point_test.go b/gamerule/rollpoint/point_test.go new file mode 100644 index 0000000..9ca0537 --- /dev/null +++ b/gamerule/rollpoint/point_test.go @@ -0,0 +1,110 @@ +package rollpoint + +import ( + "fmt" + "testing" +) + +func TestCreateBlackBox(t *testing.T) { + box := CreateBlackBox() + for i := 0; i < 50; i++ { + box.Roll() + //fmt.Println(box.Point) + //fmt.Println(CalcPoint(box.Point)) + } +} + +//var rate = [BetArea_Max]int32{ +// 1,1,1,1,1,1,//Area1-6 +// 60,30,20,12,8,6,6,6,6,8,12,20,30,60,//Sum4-17 +// 1,1,//small,big +// 10,10,10,10,10,10,//Double1-6 +// 30,//Boom +// 200,200,200,200,200,200,//Boom1-6 +//} +func TestCalcPoint(t *testing.T) { + type TestCase struct { + point [RollNum]int32 + score [BetArea_Max]int32 + } + testCase := []TestCase{ + { + point: [RollNum]int32{0, 1, 2}, + score: [BetArea_Max]int32{ + 1, 1, 1, 0, 0, 0, + 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, + 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0, 0, 0}, + }, { + point: [RollNum]int32{1, 1, 2}, + score: [BetArea_Max]int32{ + 0, 2, 1, 0, 0, 0, + 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, + 0, 10, 0, 0, 0, 0, + 0, + 0, 0, 0, 0, 0, 0}, + }, { + point: [RollNum]int32{4, 3, 2}, + score: [BetArea_Max]int32{ + 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, + 0, 1, + 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0, 0, 0}, + }, { + point: [RollNum]int32{5, 5, 5}, + score: [BetArea_Max]int32{ + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 10, + 30, + 0, 0, 0, 0, 0, 200}, + }, { + point: [RollNum]int32{1, 1, 1}, + score: [BetArea_Max]int32{ + 0, 3, 0, 0, 0, 0, + 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 10, 0, 0, 0, 0, + 30, + 0, 200, 0, 0, 0, 0}, + }, { + point: [RollNum]int32{3, 4, 5}, + score: [BetArea_Max]int32{ + 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, + 0, 1, + 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0, 0, 0}, + }, + } + for _, value := range testCase { + score := CalcPoint(value.point) + if !Int32SliceEqual(score[:], value.score[:]) { + fmt.Println(score) + fmt.Println(value.score) + t.Logf("Point %v calc score error:", value.point) + t.Fatal("TestCalcPoint test fetal.") + } + } +} +func Int32SliceEqual(left []int32, right []int32) bool { + if len(left) != len(right) { + return false + } + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} +func TestGlobalData(t *testing.T) { + fmt.Println(MaxChipColl) +} diff --git a/gamerule/roulette/1_test.go b/gamerule/roulette/1_test.go new file mode 100644 index 0000000..294397a --- /dev/null +++ b/gamerule/roulette/1_test.go @@ -0,0 +1,47 @@ +package roulette + +import ( + "fmt" + "testing" + "time" +) + +func Test1(t *testing.T) { + point := new(PointType) + point.Init() + max := 0 + maxi := 0 + min := 500 + mini := 0 + tim := time.Now() + t.Log("===============================11", tim) + for i := 0; i <= 36; i++ { + pointMapNums := point.PointMapNums[i] + betTypes := []int{} + for k, _ := range pointMapNums { + betTypes = append(betTypes, point.GetBetType(k)) + } + fmt.Println(i, "betTypes: ", betTypes) + rate := 0 + for _, v := range betTypes { + rate += point.RateMap[v] + } + if rate > max { + max = rate + maxi = i + } + if rate < min { + min = rate + mini = i + } + fmt.Println("==============rate: ", rate) + } + t.Log("===============================22", time.Now().Sub(tim)) + fmt.Println(max, maxi) + fmt.Println(min, mini) + + a := []int{10, 20, 30} + b := 1 + a = append(a[0:], b) + fmt.Println("Hello, World!", a) +} diff --git a/gamerule/roulette/roulette.go b/gamerule/roulette/roulette.go new file mode 100644 index 0000000..59b7a24 --- /dev/null +++ b/gamerule/roulette/roulette.go @@ -0,0 +1,308 @@ +package roulette + +import "time" + +//场景状态 +const ( + RouletteSceneStateWait int = iota //等待状态 + RouletteSceneStateBet //下注 + RouletteSceneStateOpenPrize //开奖 + RouletteSceneStateBilled //结算 + RouletteSceneStateStart //开始倒计时 + RouletteSceneStateMax +) + +//玩家操作 +const ( + RoulettePlayerOpReady int = iota //准备 + RoulettePlayerOpCancelReady //取消准备 + RoulettePlayerOpKickout //踢人 + RoulettePlayerOpBet //下注 + RoulettePlayerOpRecall //撤销 + RoulettePlayerOpPlayerList //玩家列表 + RoulettePlayerOpProceedBet //续投 +) +const ( + RouletteSceneWaitTimeout = time.Second * 2 //等待倒计时 + RouletteStartTimeout = time.Second * 6 //开始倒计时 + RouletteBetTimeout = time.Second * 15 //下注 + RouletteOpenPrizeTimeout = time.Second * 7 //开奖 + RouletteBilledTimeout = time.Second * 3 //结算 +) +const ( + Roulette_RICHTOP1 = 1 //富豪no.1 + Roulette_RICHTOP2 = 3 //富豪no.2 + Roulette_BESTWINPOS = 6 //神算子位置 + Roulette_SELFPOS = 7 //自己的位置 + Roulette_OLPOS = 8 //其他在线玩家的位置 +) +const ( + RoulettePlayerOpSuccess int32 = iota //成功 + RoulettePlayerOpBetCoinThanLimit //押注超过上限 + RoulettePlayerOpNotEnoughCoin //金币不足 + RoulettePlayerOpAlreadyProceedBet //已经续压 + RoulettePlayerOpNoBetCoinNotRecall //没有下注不能撤销 + RoulettePlayerOpError //失败 +) +const ( + BetTypeStraight int = iota //0.直接注(0-36个号码) + BetTypeSplit //1.分注 + BetTypeStreet //2.街注(1列3个数)/三个号码 + BetTypeCorner //3.角注(4个数位方框)/四个号码 + BetTypeLine //4.线注(2列6个数) + BetTypeThreeSide //5.三面 2to1 --- 1st12 2nd12 3rd12 + BetTypeTwoSide //6.双面 红 黑 单 双 高 低 + +//BetTypeStraight int = iota //直接注(0-36个号码) +//BetTypeSplit //分注(非0的相邻2个数) +//BetTypeStreet //街注(1列3个数) +//BetTypeThree //三数({0,1,2} {0,2,3}) +//BetTypeCorner //角注(4个数位方框) +//BetTypeFour //四个号码(0-3) +//BetTypeLine //线注(2列6个数) +//BetTypeColumn //直行注(第一行:3.+3.36 第二行:2.+3.35 第三行:1.+3.34) +//BetTypeDozen //打注(第一打:1-12 第二打:13-24 第三打:25-36) +//BetTypeBlack //黑注(0通杀) +//BetTypeRed //红注(0通杀) +//BetTypeOdd //奇数注(0通杀) +//BetTypeEven //偶数注(0通杀) +//BetTypeLow //低注(1-18 0通杀) +//BetTypeHi //高注(19-36 0通杀) +) +const ( + PointLow int = iota + 43 + PointHi + PointDouble + PointSingle + PointRed + PointBlack +) + +type PointType struct { + RateMap []int //倍率 + pointMap map[int][]int //位置详细值 0-157 0-36 每一个位置对应什么号码 + PointTypeMap map[int]map[int]bool //类型对应的位置 0-6类型 0-157位置 每一个类型对应什么位置 + PointMapNums map[int]map[int]bool //数字对应的位置号码 0-36 0-157 每一个号码对应什么位置 +} + +//四个下注区域初始化 +func InitBet() []int { + return make([]int, 4) +} +func (this *PointType) Init() { + this.RateMap = []int{35, 17, 11, 8, 5, 2, 1} + this.setPointMap() + this.setPointSlice() + this.setWinPointMap() +} + +//计算当前下注位置号码 是什么类型 key 为0-157 位置号码 +func (this *PointType) GetBetType(key int) (betType int) { + betType = -1 + //for k, v := range this.PointTypeMap { + // for _, n := range v { + // if key == n { + // betType = k + // return + // } + // } + //} + for k, v := range this.PointTypeMap { + if _, ok := v[key]; ok { + betType = k + break + } + } + return +} + +//设置中奖的位置号码集合 0-157 +func (this *PointType) setWinPointMap() { + this.PointMapNums = make(map[int]map[int]bool) + for n := 0; n <= 36; n++ { + pointMapNums := make(map[int]bool) + if n == 0 { + //pointMapNums = []int{0, 72, 73, 74, 97, 98, 99} + pointMapNums[0] = true + pointMapNums[72] = true + pointMapNums[73] = true + pointMapNums[74] = true + pointMapNums[97] = true + pointMapNums[98] = true + pointMapNums[99] = true + } else { + for pm, v := range this.pointMap { + pushBool := false + switch pm { + case 37: + //列 + if n%3 == 0 { + pushBool = true + } + case 38: + if n%3 == 2 { + pushBool = true + } + case 39: + if n%3 == 1 { + pushBool = true + } + case 40: + //打 + if n >= 1 && n <= 12 { + pushBool = true + } + case 41: + if n >= 13 && n <= 24 { + pushBool = true + } + case 42: + if n >= 25 && n <= 36 { + pushBool = true + } + case PointLow: //小 + if n >= 1 && n <= 18 { + pushBool = true + } + case PointHi: //大 + if n >= 19 && n <= 36 { + pushBool = true + } + case PointDouble: //双 + if n%2 == 0 { + pushBool = true + } + case PointSingle: //单 + if n%2 == 1 { + pushBool = true + } + } + if pm < 37 || pm > 46 { + for _, m := range v { + if m == n { + pushBool = true + break + } + } + } + if pushBool { + //pointMapNums = append(pointMapNums, pm) + pointMapNums[pm] = true + } + } + } + this.PointMapNums[n] = pointMapNums + } + +} +func (this *PointType) setPointSlice() { + this.PointTypeMap = make(map[int]map[int]bool) + for i := 0; i < 157; i++ { + betType := BetTypeStraight + if i >= 0 && i <= 36 { + //直接注 35 + betType = BetTypeStraight + } else if i >= 37 && i <= 42 { + //三面 2 + betType = BetTypeThreeSide + } else if i >= 43 && i <= 48 { + //双面 1 + betType = BetTypeTwoSide + } else if i >= 49 && i <= 59 { + //线注 5 + betType = BetTypeLine + } else if i >= 60 && i <= 73 { + //街注/3个号码 11 + betType = BetTypeStreet + } else if i >= 74 && i <= 96 { + //角注/4个号码 8 + betType = BetTypeCorner + } else if i >= 97 { + //分注 2个号码 17 + betType = BetTypeSplit + } + //this.PointTypeMap[betType] = append(this.PointTypeMap[betType], i) + if data, ok := this.PointTypeMap[betType]; ok { + data[i] = true + } else { + this.PointTypeMap[betType] = make(map[int]bool) + this.PointTypeMap[betType][i] = true + } + } +} +func (this *PointType) setPointMap() { + this.pointMap = make(map[int][]int) + //0-36 + for i := 0; i <= 36; i++ { + this.pointMap[i] = []int{i} + } + // 列 + this.pointMap[37] = []int{3 /*6, 9, 12, 15, 18, 21, 24, 27, 30, 33,*/, 36} //3 + this.pointMap[38] = []int{2 /*5, 8, 11, 14, 17, 20, 23, 26, 29, 32,*/, 35} //2 + this.pointMap[39] = []int{1 /*4, 7, 10, 13, 16, 19, 22, 25, 28, 31,*/, 34} //1 + // 打 + this.pointMap[40] = []int{1 /*2, 3, 4, 5, 6, 7, 8, 9, 10, 11,*/, 12} //1 + this.pointMap[41] = []int{13 /*14, 15, 16, 17, 18, 20, 21, 22, 23,*/, 24} //2 + this.pointMap[42] = []int{25 /*26, 27, 28, 29, 30, 31, 32, 33, 34, 35,*/, 36} //3 + + //<=18小 + this.pointMap[43] = []int{1 /*2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,*/, 18} + + //>=19大 + this.pointMap[44] = []int{19 /*20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,*/, 36} + + //双数 + this.pointMap[45] = []int{2 /*4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34,*/, 36} + //单数 + this.pointMap[46] = []int{1 /* 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33,*/, 35} + + //红 + this.pointMap[47] = []int{1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36} + //黑 + this.pointMap[48] = []int{2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35} + + //1 2 3 4 5 6 + for i := 1; i <= 11; i++ { + this.pointMap[48+i] = []int{1 + 3*(i-1), 2 + 3*(i-1), 3 + 3*(i-1), 4 + 3*(i-1), 5 + 3*(i-1), 6 + 3*(i-1)} + } + // 1 2 3 + for i := 1; i <= 12; i++ { + this.pointMap[59+i] = []int{1 + 3*(i-1), 2 + 3*(i-1), 3 + 3*(i-1)} + } + this.pointMap[72] = []int{0, 1, 2} + this.pointMap[73] = []int{0, 2, 3} + + //四 + //0 1 2 3 + this.pointMap[74] = []int{0, 1, 2, 3} + + //1 2 4 5 + for i := 1; i <= 11; i++ { + this.pointMap[74+i] = []int{1 + 3*(i-1), 2 + 3*(i-1), 4 + 3*(i-1), 5 + 3*(i-1)} + } + + for i := 1; i <= 11; i++ { + this.pointMap[85+i] = []int{2 + 3*(i-1), 3 + 3*(i-1), 5 + 3*(i-1), 6 + 3*(i-1)} + } + + //二 + this.pointMap[97] = []int{0, 1} + this.pointMap[98] = []int{0, 2} + this.pointMap[99] = []int{0, 3} + + for i := 1; i <= 12; i++ { + this.pointMap[99+i] = []int{1 + 3*(i-1), 2 + 3*(i-1)} + } + for i := 1; i <= 12; i++ { + this.pointMap[111+i] = []int{2 + 3*(i-1), 3 * i} + } + for i := 1; i <= 11; i++ { + this.pointMap[123+i] = []int{1 + 3*(i-1), 4 + 3*(i-1)} + } + for i := 1; i <= 11; i++ { + this.pointMap[134+i] = []int{2 + 3*(i-1), 5 + 3*(i-1)} + } + for i := 1; i <= 11; i++ { + this.pointMap[145+i] = []int{3 + 3*(i-1), 6 + 3*(i-1)} + } +} diff --git a/gamerule/samloc/card.go b/gamerule/samloc/card.go new file mode 100644 index 0000000..43e34f2 --- /dev/null +++ b/gamerule/samloc/card.go @@ -0,0 +1,945 @@ +package samloc + +import ( + "sort" +) + +const ( + Pass int = iota //0 nil + Single //1 单张 + Twin //2 对子 + Triple //3 三张 + Straight //4 顺子 + Four //5 四张 + TwinFour //6 两组四张 + StraightFiveTriple //7 五连对 + ThreeTriple //8 三个三张 + ColorSame //9 同色 + Four2 //10 四张二 + TenStraight //11 一条龙 +) + +const ( + POKER_3 int32 = iota // 0 + POKER_4 // 1 + POKER_5 // 2 + POKER_6 // 3 + POKER_7 // 4 + POKER_8 // 5 + POKER_9 // 6 + POKER_10 // 7 + POKER_J // 8 + POKER_Q // 9 + POKER_K // 10 + POKER_A // 11 + POKER_2 // 12 +) + +const ( + Club int = iota //0,黑桃 + Spade //1,梅花 + Diamond //2,方块 + Heart //3,红桃 +) + +const ( + Score2 int32 = 15 //4张 压2 + ScoreFour int32 = 20 //4张 压4张 + ScoreBaoSam int32 = 20 //baosam + ScoreLore int32 = 20 // 通杀 + Baopei2 int32 = 10 // 包赔 + Treo int32 = 15 // 结束游戏后而不打出任何牌 +) + +const ( + BaosamNot int = 1 // 0 非baosam 正常结算 + BaosamWin int = 2 // 1 baosam赢 + BaosamLose int = 4 // 2 baosam输 + SamLore int = 8 // 2 通杀 +) + +// KindOfCard 牌型 +type KindOfCard struct { + kind int //牌型 + // cards []int32 //出牌 + min int32 //最小牌 + max int32 //最大牌 + len int32 //牌的长度 + same int32 //同牌数量 33 -> 2 + num int32 //牌的数量 + ktype int //牌的权重 +} + +func (this *KindOfCard) GetKind() int { return this.kind } +func (this *KindOfCard) GetLen() int32 { return this.len } +func (this *KindOfCard) GetMin() int32 { return this.min } +func (this *KindOfCard) GetSame() int32 { return this.same } +func (this *KindOfCard) GetNum() int32 { return this.num } + +type Kds struct { + kd []*KindOfCard +} + +func (this *Kds) Len() int { return len(this.kd) } +func (this *Kds) Swap(i, j int) { this.kd[i], this.kd[j] = this.kd[j], this.kd[i] } +func (this *Kds) Less(i, j int) bool { + if this.kd[i].ktype > this.kd[j].ktype { // 大到小 + return true + } else if this.kd[i].ktype < this.kd[j].ktype { + return false + } + if this.kd[i].len > this.kd[j].len { + return true + } + if this.kd[i].min < this.kd[j].min { + if this.kd[i].min < 0 { // A 2做连 往后放 + return false + } + return true + } + return false // 小到大 +} + +//func (this *KindOfCard) GetCards() []int32 { return this.cards } +func (this *KindOfCard) OutPokers(poker []int32) []int32 { + pok := make([]int32, len(poker)) + copy(pok, poker) + var ret []int32 + if this != nil && this.num != 0 { + + for i := this.min; i <= this.max; { + va := i + if va < 0 { + va = va + POKER_2 + 1 + } + for j := 0; j != int(this.same); j++ { + for k, v := range pok { + if Value32(v) == va { + ret = append(ret, v) + pok = append(pok[:k], pok[k+1:]...) // del + break + } + } + } + if this.same > 3 && i != this.max { + i = this.max + } else { + i++ + } + } + + } + + return ret +} + +// SortInt32 []int32 +type SortInt32 []int32 + +func (a SortInt32) Len() int { return len(a) } +func (a SortInt32) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a SortInt32) Less(i, j int) bool { + + b := Value(a[i]) + c := Value(a[j]) + + if b == c { + return b < c + } + return b < c +} + +// Color 黑桃0 梅花1 方片2 紅桃3 +func Color(c int32) int { + return int(c) / PER_CARD_COLOR_MAX +} + +func RedColor(c int32) int { // Heart 红色 Club 黑色 + if (int(c) / PER_CARD_COLOR_MAX) > Spade { + return Heart + } + return Club +} + +func Value(c int32) int { + return int(c) % PER_CARD_COLOR_MAX +} + +func Value32(c int32) int32 { + return c % PER_CARD_COLOR_MAX +} + +func ValueStr(c int32) int { + if int(c+3)%PER_CARD_COLOR_MAX == 0 { + return PER_CARD_COLOR_MAX + } + return int(c+3) % PER_CARD_COLOR_MAX +} + +func IsSingle(cards []int32) bool { //单张 + if len(cards) == 1 { + return true + } + return false +} + +func IsContainPOKER2(cards []int32) bool { //全是2 + for _, v := range cards { + if Value32(v) == POKER_2 { + return true + } + } + return false +} + +func IsHavePOKER2(cards []int32) bool { //全是2 + for _, v := range cards { + if v == InvalideCard { + continue + } + if Value32(v) != POKER_2 { + return false + } + } + return true +} + +func DelOtherPOKER2(cards []int32, del []int32) bool { //全是2 + cpcards := append([]int32{}, cards...) + for _, v := range del { + for i, k := range cpcards { + if v == k { + cpcards[i] = InvalideCard + } + } + } + num := 0 + for _, v := range cpcards { + if v == InvalideCard { + num++ + continue + } + if Value32(v) != POKER_2 { + return false + } + } + if num == Hand_CardNum { + return false + } + return true +} + +func GetMaxCard(cards []int32) int32 { //单张 + max := int32(0) + for _, v := range cards { + if v != InvalideCard { + vv := Value32(v) + if vv > max { + max = vv + } + } + } + return max +} + +func IsTwin(cards []int32) bool { //对子 + if len(cards) == 2 { + if Value(cards[0]) == Value(cards[1]) { + return true + } + } + return false +} + +func IsValueStraight(tmpCards []int32) bool { //顺子 + if IsContainPOKER2(tmpCards) { // 包含2 单连要 + + for i := range tmpCards { + if tmpCards[i] > POKER_K { + tmpCards[i] = tmpCards[i] - POKER_2 - 1 + } + } + sort.Sort(SortInt32(tmpCards)) + } + if len(tmpCards) < 3 || len(tmpCards) > 12 { + return false + } + + for i := 0; i < len(tmpCards)-1; i++ { + card := tmpCards[i] + nextCard := tmpCards[i+1] + if nextCard-card != 1 { // 不相邻 + return false + } + } + + return true +} + +func IsTriple(cards []int32) bool { //三张 + if len(cards)%3 != 0 { + return false + } + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + return true +} + +func IsFour(cards []int32) bool { //四张 + if len(cards) != 4 { + return false + } + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + if Value(cards[2]) != Value(cards[3]) { + return false + } + return true +} + +func IsDouFour(cards []int32) bool { //两 四张 + if len(cards) != 8 { + return false + } + tmpCards := []int32{} + for _, card := range cards { + + tmpCards = append(tmpCards, int32(Value(card))) + } + sort.Sort(SortInt32(tmpCards)) + // fmt.Println(tmpCards[:4], tmpCards[4:], IsFour(tmpCards[:4]), IsFour(tmpCards[4:])) + if IsFour(tmpCards[:4]) && IsFour(tmpCards[4:]) { + return true + } + return false +} + +func IsColorSame(cards []int32) bool { // 同色 + + c := RedColor(cards[0]) + for _, card := range cards { + if c != RedColor(card) { + return false + } + } + return true +} + +func pt32m(cards []int32) map[int32]int32 { + m := make(map[int32]int32) + for _, v := range cards { + m[Value32(v)]++ + } + return m +} + +func ptstr32m(cards []int32) map[int32]int32 { + m := make(map[int32]int32) + for _, v := range cards { + vl := Value32(v) + m[vl]++ + } + if m[POKER_2] != 0 { + m[POKER_2-POKER_2-1] = m[POKER_2] + m[POKER_A-POKER_2-1] = m[POKER_A] + } + return m +} + +/*func findcardCommon(m map[int]int32, n int32) (res [][]int32) { + max := int(POKER_2) + + for i := 0; i <= max; i++ { + if m[i] >= n { + res = append(res, []int32{}...) + } + } + return +}*/ + +// 通杀 +func CardLore(cards []int32) (int, int32) { + + if int32(len(cards)) != HandCardNum { + return Pass, -3 + } + for _, v := range cards { + if v == -1 { + return Pass, -3 + } + } + max := POKER_2 + smap := pt32m(cards) + + tmpCards := []int32{} + for _, card := range cards { + + tmpCards = append(tmpCards, int32(Value(card))) + } + sort.Sort(SortInt32(tmpCards)) + + // 一条龙>四张2>10张颜色相同>三个三张>五连对 + + if IsValueStraight(tmpCards) { // 一条龙 + return TenStraight, tmpCards[HandCardNum-1] + } + + if smap[POKER_2] == 4 { // 四张 + return Four2, tmpCards[HandCardNum-1] + } + + if IsColorSame(cards) { // 同色 + return ColorSame, tmpCards[HandCardNum-1] + } + + threenum := 0 + for i := int32(0); i <= max; i++ { + v, _ := smap[i] + if v > 2 { + threenum++ + } + if threenum == 3 { + return ThreeTriple, int32(i) + } + + } + freenum := 0 + for i := int32(0); i <= max; i++ { + v, _ := smap[i] + if v == 0 && freenum == 0 { + continue + } + if v != 2 { + return Pass, -3 + } + if i == POKER_2 { + return Pass, -3 + } + freenum += 1 + if freenum == 5 { + return StraightFiveTriple, int32(i) + } + + } + + return Pass, -3 +} + +func GetCardKind(cards []int32) *KindOfCard { // cardType 类型 // 最小 + + count := int32(len(cards)) + ret := &KindOfCard{ + num: count, + kind: Pass, + //cards: append([]int32{}, cards...), + } + if count <= 0 { + return ret + } + tmpCards := []int32{} + for _, card := range cards { + + tmpCards = append(tmpCards, int32(Value(card))) + } + sort.Sort(SortInt32(tmpCards)) + + // fmt.Printf("GetCardKind %x %x\n", cards, tmpCards) + switch count { + case 1: //单牌 + ret.min = tmpCards[0] + ret.max = tmpCards[0] + ret.len = 1 + ret.same = 1 + ret.kind = Single + ret.ktype = GetCardType(ret) + return ret + case 2: //对牌 + if IsTwin(cards) { + ret.min = tmpCards[0] + ret.max = tmpCards[0] + ret.kind = Twin + ret.len = 1 + ret.same = 2 + ret.ktype = GetCardType(ret) + } + return ret + } + + return analysCardKind(cards, tmpCards, ret, count) +} + +func analysCardKind(cards []int32, tmpCards []int32, ret *KindOfCard, count int32) *KindOfCard { // cardType 类型 // 最小 + + switch count { + case 3: + if (tmpCards[0] == tmpCards[1]) && IsTriple(tmpCards) { + ret.min = tmpCards[0] + ret.max = tmpCards[0] + ret.kind = Triple + ret.len = 1 + ret.same = 3 + ret.ktype = GetCardType(ret) + return ret // 三张 + } + case 4: + if (tmpCards[0] == tmpCards[1]) && IsFour(tmpCards) { + ret.min = tmpCards[0] + ret.max = tmpCards[0] + ret.kind = Four + ret.len = 1 + ret.same = 4 + ret.ktype = GetCardType(ret) + return ret // 四张 + } + case 8: + if (tmpCards[0] == tmpCards[1]) && IsDouFour(tmpCards) { + ret.min = tmpCards[0] + ret.max = tmpCards[7] + ret.kind = TwinFour + ret.len = 2 + ret.same = 4 + ret.ktype = GetCardType(ret) + return ret //6 两组四张 + } + } + + if IsValueStraight(tmpCards) { + ret.min = tmpCards[0] + ret.max = tmpCards[len(tmpCards)-1] + ret.kind = Straight + ret.len = int32(len(tmpCards)) + ret.same = 1 + ret.ktype = GetCardType(ret) + return ret // 顺子 + } + return ret +} + +// 首出提示 +func HeadHintOut(card []int32) [][]int32 { // 按照单 双 三张 四张 顺子 双四出~ + + return nil +} + +// 压牌 +func PressOut(beforeKind *KindOfCard, cards []int32, f bool) (retkind *KindOfCard) { + p := pt32m(cards) + var kds []*KindOfCard + switch beforeKind.kind { + case Single: //1 单张 + retkind = GetSingle(cards, beforeKind, f) + if beforeKind.min == POKER_2 { + kds = findKindCommon(p, 4) + for _, kd := range kds { + if CompareCardKind(beforeKind, kd) { + retkind = kd + break + } + } + } + case Twin: //2 对子 + kds = findKindCommon(p, 2) + if beforeKind.min == POKER_2 { + kds = findKindTwinFour(p, 4) + } + for _, kd := range kds { + if CompareCardKind(beforeKind, kd) { + retkind = kd + break + } + } + case Straight: //4 顺子 + kds := findStraight(p, 1, beforeKind.num) + for _, kd := range kds { + if CompareCardKind(beforeKind, kd) { + retkind = kd + break + } + } + case Triple: //3 三张 + kds := findKindCommon(p, 3) + for _, kd := range kds { + if CompareCardKind(beforeKind, kd) { + retkind = kd + break + } + } + case Four: //5 四张 + kds := findKindCommon(p, 4) + for _, kd := range kds { + if CompareCardKind(beforeKind, kd) { + retkind = kd + break + } + } + case TwinFour: //6 两组四张 + } + return +} + +// CompareCardKind 对比 +func CompareCardKind(beforeKind *KindOfCard, kind *KindOfCard) bool { + + if beforeKind.kind == kind.kind && beforeKind.num == kind.num && beforeKind.kind != TwinFour { + + return beforeKind.min < kind.min + } + + if beforeKind.min == POKER_2 { + if beforeKind.kind == Single && kind.kind == Four { + return true + } else if beforeKind.kind == Twin && kind.kind == TwinFour { + return true + } + } + + return false +} + +// CompareCard 对比 +func CompareCard(beforeKind *KindOfCard, cards []int32) bool { // cards 大 返回true + cnum := int32(len(cards)) + tmpCards := []int32{} + for _, card := range cards { + + tmpCards = append(tmpCards, int32(Value(card))) + } + sort.Sort(SortInt32(tmpCards)) + switch beforeKind.kind { + case Single: //1 单张 + if cnum == 1 { + if beforeKind.min < tmpCards[0] { + return true + } + } else if beforeKind.min == POKER_2 && IsFour(tmpCards) { // 能压一个2 + return true + } + + case Twin: //2 对子 + if cnum == 2 { + if IsTwin(tmpCards) && beforeKind.min < tmpCards[0] { + return true + } + } else if beforeKind.min == POKER_2 && IsDouFour(tmpCards) { // 能压一对2 + return true + } + case Straight: //4 顺子 + //fmt.Println("IsValueStraight(tmpCards)", IsValueStraight(tmpCards), beforeKind.len, cnum, beforeKind.min, tmpCards[0]) + if beforeKind.len == cnum && IsValueStraight(tmpCards) && (beforeKind.min < tmpCards[0]) { + return true + } + case Triple: //3 三张 + if IsTriple(tmpCards) && (beforeKind.min < tmpCards[0]) { + return true + } + case Four: //5 四张 + if IsFour(tmpCards) && (beforeKind.min < tmpCards[0]) { + return true + } + case TwinFour: //6 两组四张 + } + return false +} + +// RemoveCard 删除扑克 +func RemoveCard(removePoker []int32, cardData *[]int32) bool { + + deleteCount := 0 + removeCount := len(removePoker) + pokerCount := len(*cardData) + tempPokerData := make([]int32, pokerCount) + if pokerCount > len(tempPokerData) { + return false + } + + copy(tempPokerData[0:], (*cardData)[0:pokerCount]) + + for i := 0; i < removeCount; i++ { + for j := 0; j < pokerCount; j++ { + if removePoker[i] == tempPokerData[j] { + deleteCount++ + // fmt.Printf("找到待删除扑克: %x\n", removePoker[i]) + tempPokerData[j] = -1 + break + } + } + } + if deleteCount != removeCount { + return false + } + + //清理扑克 + pos := 0 + for i := 0; i < pokerCount; i++ { + if tempPokerData[i] != -1 { + (*cardData)[pos] = tempPokerData[i] + pos++ + } + } + (*cardData) = (*cardData)[:pos] // 删除扑克 1244 delete 2 --> 144 + return true +} + +func GetCardType(kind *KindOfCard) int { + switch kind.same { + case 4: + // 四张 + if kind.num != 8 { + // 双四张 + return -1 // 不能首出 + } + if kind.min > POKER_10 { + return 1 + } + return 6 + case 3: + // 三牌 + if kind.min > POKER_10 { + return 1 + } + return 4 + case 2: + // 对牌 + return 3 + case 1: + + if kind.num >= 3 { + // 单连 + if kind.num > 5 { + return 8 + } + if kind.min < POKER_7 && kind.min >= POKER_3 { + return 7 + } + if kind.num == 3 && kind.min > POKER_10 { // 不先出 + return 1 + } + return 5 + } + } + // 单牌 + return 2 +} + +func MaxSingle(cpCards []int32, delCards []int32, f bool) (ret bool, retCards []int32) { + if DelOtherPOKER2(cpCards, delCards) || (f && len(delCards) == 1) { + for i := len(cpCards) - 1; i >= 0; i-- { + card := cpCards[i] + if card != InvalideCard { + retCards = append(retCards, card) + ret = true + break + } + } + } + return +} + +func GetSingle(cpCards []int32, kind *KindOfCard, f bool) (retkind *KindOfCard) { + var retCards []int32 + if kind == nil || kind.num == 0 { + for _, card := range cpCards { + if card != InvalideCard { + retCards = append(retCards, card) + break + } + } + if ret, del := MaxSingle(cpCards, retCards, f); ret { + retCards = del + } + + } else { + for _, card := range cpCards { + if card != InvalideCard && Value32(card) > kind.min { + retCards = append(retCards, card) + break + } + } + if ret, del := MaxSingle(cpCards, retCards, f); ret { + retCards = del + if Value32(retCards[0]) <= kind.min { + retCards = []int32{} + } + } + } + retkind = GetCardKind(retCards) + return +} + +func findKindCommon(m map[int32]int32, n int32) (res []*KindOfCard) { + + for i := int32(0); i <= POKER_2; i++ { + if m[i] >= n { + card := []int32{} + for num := int32(0); num < n; num++ { + card = append(card, i) + } + + res = append(res, GetCardKind(card)) + } + } + return +} + +// 双四 +func findKindTwinFour(m map[int32]int32, n int32) (res []*KindOfCard) { + card := []int32{} + k := 0 + for i := int32(0); i <= POKER_2; i++ { + if m[i] >= n { + for num := int32(0); num < n; num++ { + card = append(card, i) + } + k++ + } + if k == 2 { + res = append(res, GetCardKind(card)) + break + } + } + return +} + +func haveStraightLine(m map[int32]int32, num int32, len int32, min int32) bool { + if min+len > POKER_2 { // 2不能带 + return false + } + for i := min; i < min+len; i++ { + if m[i] < num { + return false + } + } + return true +} + +// 连牌 +func findStraight(m map[int32]int32, num int32, len int32) (res []*KindOfCard) { + + max := 11 - len + min := int32(0) + if m[-2] > 0 { + min = -2 + } else if m[-1] > 0 { // -1 ->2 + min = -1 + } + for ; min <= max; min++ { + if haveStraightLine(m, num, len, min) { + if min == -2 && len < 6 { // 没必要做连牌 + continue + } + if min == -1 && len < 5 { + continue + } + card := []int32{} + for num := int32(0); num < len; num++ { + card = append(card, min+num) + } + res = append(res, GetCardKind(card)) + } + } + + return +} + +func allfindStraight(m map[int32]int32, num int32, len int32) (res []*KindOfCard) { + for ilen := len; ; ilen++ { + var flag bool // 默认false 单联判断成功就继续 + max := 11 - ilen + min := int32(0) + + if m[-2] > 0 { + min = -2 + } else if m[-1] > 0 { // -1 ->2 + min = -1 + } + for ; min <= max; min++ { + if haveStraightLine(m, num, ilen, min) { + if min == -2 && len < 6 { // 没必要做连牌 + continue + } + if min == -1 && len < 5 { + continue + } + card := []int32{} + for num := int32(0); num < ilen; num++ { + card = append(card, min+num) + } + res = append(res, GetCardKind(card)) + flag = true + } + } + if !flag { + break + } + } + return +} + +// 超时出牌 TODO +func TickOut(cards []int32, kind *KindOfCard, f bool) (delCards []int32) { + cpCards := append([]int32{}, cards...) + + sort.Slice(cpCards, func(i, j int) bool { + v_i := Value(cpCards[i]) + v_j := Value(cpCards[j]) + c_i := Color(cpCards[i]) + c_j := Color(cpCards[j]) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + var retkind *KindOfCard + if kind == nil || kind.num == 0 { + retkind = GetSingle(cpCards, kind, f) + + } else { + retkind = PressOut(kind, cpCards, f) + } + delCards = retkind.OutPokers(cpCards) + return +} + +// 根据上家牌型是否需要额外延迟出牌时间(s) +func NeedExDelay(lastCards []int32) bool { + + return false +} + +// 计算输家输分数 +func GetLoseScore(cards [HandCardNum]int32, baopei bool) (loseScore int32) { + + //cpCards := []int32{} + num := int32(0) + flag := false + for _, card := range cards { + if card != InvalideCard { + if !flag && Value32(card) == POKER_2 { + flag = true + loseScore += Baopei2 + } + //cpCards = append(cpCards, card) + num++ + } + } + if num == HandCardNum { + loseScore += Treo + } else { + loseScore += num + } + if baopei { // 庄家赔付 + loseScore -= Baopei2 + } + + return +} diff --git a/gamerule/samloc/combine.go b/gamerule/samloc/combine.go new file mode 100644 index 0000000..24a5aad --- /dev/null +++ b/gamerule/samloc/combine.go @@ -0,0 +1,1378 @@ +package samloc + +import ( + "errors" + "sort" +) + +type line struct { + min int //最小牌 + num int // 主牌 333 + len int // 主牌数量 333444 -->2 + tks []*line +} + +func (l *line) incr(m map[int]int) { + for i := l.min; i < l.min+l.len; i++ { + m[i] += l.num + } +} + +func (l *line) decr(m map[int]int) { + for i := l.min; i < l.min+l.len; i++ { + m[i] -= l.num + } +} + +func (l *line) deepEqual(r *line) bool { + // 地址相等必相同 + if l == r { + return true + } + if l.min != r.min || l.num != r.num || l.len != r.len { + return false + } + if len(l.tks) != len(r.tks) { + return false + } + for i := 0; i < len(l.tks); i++ { + if !l.tks[i].deepEqual(r.tks[i]) { + return false + } + } + return true +} + +// 权重 +func (l *line) Type() int { + switch l.num { + case 4: + // 四张 + if len(l.tks) != 0 { + // 双四张 + return TwinFour + } + return Four + case 3: + // 三牌 + return Triple + case 2: + // 对牌 + return Twin + case 1: + + if l.len >= 3 { + // 单连 + return Straight + } + // 单牌 + return Single + } + return 0 +} + +// 三牌四牌带牌 +func (l *line) canTake() int { + if l.tks != nil { + return 0 + } + /*if l.num == 4 { + return 2 + } else if l.num == 3 { + return l.len + }*/ + return 0 +} + +// 能否出牌 +func (l *line) canOut(pL int) bool { + + return true +} + +func (l *line) isTake(b int) bool { + for i := 0; i < l.len; i++ { + if b == l.min+i { + return false + } + } + return true +} + +func (l *line) hintType() int { + switch l.num { + case 4: + if len(l.tks) == 0 { + if l.len == 2 { + // 双四张 + return TwinFour + } + return Four + } + + // return Pass + case 3: + + if len(l.tks) == 0 { + return Triple + } + + // return Pass + case 2: + + if l.len == 1 { + return Twin + } + // return Pass + case 1: + if l.len == 1 { + return Single + } + if l.len >= 3 { + // 单连 + return Straight + } + } + return Pass +} + +func (l *line) hintLen() (n int) { + for _, v := range l.tks { + n += v.hintLen() + } + n += int(l.num * l.len) + return +} + +// l > o 不同类型,小于等于为false 大于true +func (l *line) isBattle(o *line) bool { + tp := l.hintType() + ts := o.hintType() + tpl := l.hintLen() + tsl := o.hintLen() + if tp == ts { + if tpl == tsl { + return l.min > o.min + } + return false + } + if o.min == int(POKER_2) { + + if ts == Single && tp == Four { // 0 + return true + } else if ts == Twin && tp == TwinFour { + return true + } + + } + + return false +} + +func (l *line) isBigThan(lastShowCard *KindOfCard) bool { + if lastShowCard == nil { + return true + } + tp := l.hintType() + if tp == lastShowCard.GetKind() { + if l.hintLen() == int(lastShowCard.GetLen()*lastShowCard.GetSame()) { // 同类型,同数量 + + return l.min > int(lastShowCard.GetMin()) + } + return false + } + + if lastShowCard.GetMin() == POKER_2 { + + if lastShowCard.GetKind() == Single && tp == Four { + return true + } else if lastShowCard.GetKind() == Twin && tp == TwinFour { + return true + } + + } + return false +} + +func (l *line) outType(m int) int { + if l.len > 1 || (l.len > 1 && l.num == 3) { + if l.tks != nil && l.min <= m+0x8 { + return 5 // 飞机带 + } + if l.min <= m+0x8-l.num*2 { + return 4 // 成连 + } + } + if l.num > 2 && l.min <= m { + return 3 // 可带 + } + for _, v := range l.tks { + if v.outType(m) == 1 { + return 2 // 被带 + } + } + if l.min <= m { + return 1 // 普通 + } + return 0 +} + +func (l *line) firstTake() *line { + for _, v := range l.tks { + return v // 最小的 + } + return nil +} + +/* + func (l *line) Print(w io.Writer) { + var trans = map[int]string{ + 0: "3", + 1: "4", + 2: "5", + 3: "6", + 4: "7", + 5: "8", + 6: "9", + 7: "10", + 8: "J", + 9: "Q", + 10: "K", + 11: "A", + 12: "2", + } + + l.nestPrint(w, trans, "") + + if l != nil { + for _, v := range l.tks { + v.nestPrint(w, trans, " 带") + } + } + // fmt.Fprintln(w) + } + +func (l *line) nestPrint(w io.Writer, trans map[int]string, take string) { + + if l != nil { + switch l.Type() { + case TwinFour: + fmt.Fprintf(w, "两组四牌 ") + case Triple: + fmt.Fprint(w, "三牌: ") + case Four: + fmt.Fprint(w, "四牌: ") + case Straight: + fmt.Fprintf(w, "%d单连: ", l.len) + case Twin: + fmt.Fprintf(w, "%s对牌: ", take) + case Single: + fmt.Fprintf(w, "%s单牌: ", take) + } + + for i := l.min; i < l.min+l.len; i++ { + fmt.Fprint(w, trans[i], ", ") + } + } else { + fmt.Fprintf(w, "没有找到该出手牌 ") + } + } +*/ +func (l *line) outPokers(poker []int32) ([]int32, []int32) { + pok := make([]int32, len(poker)) + copy(pok, poker) + var ret []int32 + if l != nil { + for i := l.min; i != l.min+l.len; i++ { + for j := 0; j != l.num; j++ { + for k, v := range pok { + if realVle(v) == i { + ret = append(ret, v) + pok = append(pok[:k], pok[k+1:]...) // del + break + } + } + } + } + for _, ll := range l.tks { + for i := ll.min; i != ll.min+ll.len; i++ { + for j := 0; j != ll.num; j++ { + for k, v := range pok { + if realVle(v) == i { + ret = append(ret, v) + pok = append(pok[:k], pok[k+1:]...) // del + break + } + } + } + } + } + } + // fmt.Println(poker, pok) + return ret, pok +} + +type cutex struct { + l []*line + h int // 手数 + adv bool // 2属于绝对优势牌 h <1 先出2 + b int // 炸弹 + m []int // 哪几手非最大(方便测试人员,后在selectOut用于确认是否最大) + f bool // 四带标志(炸弹带) +} + +func (c *cutex) takeOther(max int) bool { + var n int + for k := len(c.l) - 1; k >= 0; k-- { + if c.l[k].len != 1 || c.l[k].num > 2 { + break + } + if c.l[k].min >= max { + continue + } + if n++; n >= 2 { + return true + } + } + return false +} + +/* +通过他人手牌推算出目前自己本手手牌是否为最大,最大不计入手数 +*/ +type enemy struct { + otherPokers [][]int32 +} + +func newEnemy(pokers [][]int32) *enemy { + var e enemy + e.otherPokers = make([][]int32, len(pokers)) + for i, v := range pokers { + e.otherPokers[i] = append(e.otherPokers[i], v...) + } + return &e +} + +func (e *enemy) isMax(ls *line) bool { + for _, v := range e.otherPokers { + m := ptm(v) + lines := findSameNum(m, 4) + tp := ls.hintType() + + if len(v) < ls.hintLen() { + continue + } + switch tp { + case Straight: + lines = findPlane(m, 1, ls.num) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + + case Triple: + lines = findSameNum(m, 3) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + case Four: + lines = findSameNum(m, 4) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + + default: + if ls.min == int(POKER_2) { + if tp == Twin { + lines = findTwoSameNum(m, 4, 2) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + } else if tp == Single { + lines = findSameNum(m, 4) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + } + } else { + lines = findSameType(m, ls.num) + for _, v1 := range lines { + if v1.isBattle(ls) { + return false + } + } + } + } + + } + return true +} + +type cutexSlice []*cutex + +func (c cutexSlice) Len() int { + return len(c) +} +func (c cutexSlice) Less(i, j int) bool { + if c[i].h != c[j].h { + return c[i].h < c[j].h // 少->多 + } + if c[i].b != c[j].b { + return c[i].b > c[j].b // 8888 7 6 全场最大时不带着出 + } + return len(c[i].l) < len(c[j].l) // 888 7 6 全场最大时带着出 +} +func (c cutexSlice) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func realVle(v int32) int { // 0-12 3~ A 2 + + return int(v) % PER_CARD_COLOR_MAX + +} + +func ptm(in []int32) map[int]int { + m := make(map[int]int) + for _, v := range in { + m[realVle(v)]++ + } + return m +} + +func haveLine(m map[int]int, num int, len int, min int) bool { + if min+len > int(POKER_2) { // 2不能带 + return false + } + for i := min; i < min+len; i++ { + if m[i] < num { + return false + } + } + return true +} + +func makeLine(m, n, l int, s ...*line) *line { + return &line{ + min: m, // 最小牌值 + num: n, // 单张数量 + len: l, // 长度 + tks: s, + } +} + +// 寻找相同个数的 +func findSameNum(m map[int]int, n int) (res []*line) { + maxcard := int(POKER_2) + for i := 0; i <= maxcard; i++ { + if m[i] >= n { + res = append(res, makeLine(i, m[i], 1)) + res[len(res)-1].decr(m) + } + } + + return +} + +// 寻找两4张 +func findTwoSameNum(m map[int]int, n int, l int) (res []*line) { + + re := makeLine(-1, -1, -1) + // re = makeLine(i, m[i], 1) + maxcard := int(POKER_2) + for i := 0; i <= maxcard; i++ { + + if m[i] >= n { + if re.min == -1 { + re.min = i + re.num = m[i] + re.len = 1 + } else { + re.tks = append(re.tks, makeLine(i, m[i], l)) + // re.max = i + re.len++ + } + if re.len == l { + res = append(res, re) + return + } + + } + + } + + return +} + +// 连牌 +func findPlane(m map[int]int, num int, len int) (res []*line) { + for i := len; ; i++ { + var flag bool // 默认false 单联判断成功就继续 + for j := 0; j <= 11; j++ { + if haveLine(m, num, i, j) { + res = append(res, makeLine(j, num, i)) + flag = true + } + } + if !flag { + break + } + } + return +} + +func findCommon(m map[int]int, n int) (res []*line) { + max := int(POKER_2) + + for i := 0; i <= max; i++ { + if m[i] >= n { + res = append(res, makeLine(i, m[i], 1)) + } + } + return +} + +func findSame(m map[int]int, n int) (res []*line) { + max := int(POKER_2) + + for i := 0; i <= max; i++ { + if m[i] >= n { + res = append(res, makeLine(i, n, 1)) + } + } + return +} + +func findSameType(m map[int]int, n int) (res []*line) { + maxcard := int(POKER_2) + for i := 0; i <= maxcard; i++ { + if m[i] >= n { + res = append(res, makeLine(i, n, 1)) + } + } + return +} + +// 遍历选定某连,找出与该连能同时存在的连,递归以上操作直至找不到,这些选定的连则是一种合法选择 +// 请给参数t指向的切片足够的容量,如此以来在递归调用中将不会因扩展容量而重新申请内存 +// 可以把递归想象成前序遍历树,由根向左,t用来记录由根到当前的所有节点 +// 参数l和局部变量ll用来表示当前节点的子节点 +// 参数m用来限制子节点的生成 +// 参数d用来记录深度 +func selectLine(m map[int]int, l []*line, t []*line, s *[][]*line, d int) { + for _, v := range l { + t = append(t[:d], v) + + v.decr(m) + + var ll []*line + for _, vv := range l { + // 不考虑自己(增加为压而拆后这里存在再找自己有效的情况,譬如对2被拆v是单2) + if v == vv { + continue + } + if haveLine(m, vv.num, vv.len, vv.min) { + ll = append(ll, vv) // 能匹配的向来不多,这里不事先make + } + } + + if len(ll) == 0 { + // 必须拷贝出来,因为参数t随着递归会变,前个合法选择会被下个覆盖 + tt := make([]*line, 0, len(t)) + tt = append(tt, t...) + *s = append(*s, tt) + } else { + selectLine(m, ll, t, s, d+1) + } + + v.incr(m) + } +} + +// 0: 相等, 1: 大于, -1: 小于 +type compareFunc func(p, q *line) int + +type multiSorter struct { + ls []*line + fs []compareFunc +} + +func (ms *multiSorter) Sort(ls []*line) { + ms.ls = ls + sort.Sort(ms) +} + +func (ms *multiSorter) Len() int { + return len(ms.ls) +} + +func (ms *multiSorter) Swap(i, j int) { + ms.ls[i], ms.ls[j] = ms.ls[j], ms.ls[i] +} + +// 小 -> 大 +func (ms *multiSorter) Less(i, j int) bool { + for k := 0; k < len(ms.fs); k++ { + if n := ms.fs[k](ms.ls[i], ms.ls[j]); n == 1 { + return false + } else if n == -1 { + return true + } + } + return false +} + +func orderedBy(fs ...compareFunc) *multiSorter { + return &multiSorter{ + fs: fs, + } +} + +// selectLine 后很容易[33 44][44 33]重复 +// 但[33]均属于相同实例,排序后可以通过地址快速判定是否相同 +// combineTake 后很容易带与被带均相同但由于带牌是拷贝而地址不等 +// 必要时可为 selectLine 后单独实现 fastEqualLine 里面仅比较地址 +func equalLine(l []*line, r []*line) bool { + if len(l) != len(r) { + return false + } + for i := 0; i < len(l); i++ { + if !l[i].deepEqual(r[i]) { + return false + } + } + return true +} + +func distinctLine(s [][]*line) [][]*line { + // 排序便于比较相同 + for _, v := range s { + orderedBy(rSortType, rSortMin, rSortLen).Sort(v) + } + //删除第二层重复的 + for p, lines := range s { + arr_len := len(lines) + for i := 0; i < arr_len; i++ { + for j := i + 1; j < arr_len; j++ { + l := lines[i] + r := lines[j] + if l.min != r.min || l.num != r.num || l.len != r.len { + continue + } + s[p] = append(s[p][:i], lines[i+1:]...) + arr_len-- + i-- + break + } + } + } + //删除第一层重复的 + arr_len := len(s) + for i := 0; i < arr_len; i++ { + for j := i + 1; j < arr_len; j++ { + l := s[i] + r := s[j] + CompareSlice := func(l, r []*line) bool { + if len(l) != len(r) { + return false + } + var n int + for i := 0; i < len(l); i++ { + for j := 0; j < len(r); j++ { + if l[i].min != r[j].min || l[i].num != r[j].num || l[i].len != r[j].len { + n++ + break + } + } + } + if n == len(r) { + return true + } + return false + } + if CompareSlice(l, r) { + continue + } + s = append(s[:i], s[i+1:]...) + arr_len-- + i-- + break + } + } + //for i := 0; i < len(s); i++ { + // for j := i + 1; j < len(s); j++ { + // if equalLine(s[i], s[j]) { + // s = append(s[:j], s[j+1:]...) + // j-- + // } + // } + //} + return s +} +func moreSplitTake(s *[][]*line, pL int, lastShowCard *KindOfCard) { + ls := len(*s) + + for i := 0; i < ls; i++ { + var ct int // 可带牌数量 + p := (*s)[i] // 始终指向即将拆对的 + pos := len(p) // 初始位置为长度预防无单对 + + for k, v := range p { + ct += v.canTake() + + if pos == len(p) && v.len == 1 && v.num <= 2 { + pos = k // 记录首个单对 + } + } + + // 单对不分类型按大小排序 + orderedBy(rSortMin).Sort(p[pos:]) + + var bt int // 被带牌数量 + var t []int // 记录对牌位置 + + for j := len(p) - 1; j >= 0; j-- { + if p[j].len != 1 || p[j].num > 2 { + break + } + + if bt++; bt >= ct { + break + } + + if p[j].num == 2 { + t = append(t, j) + bt++ // 对牌充当两单牌 + } + } + + // 从小到大拆对 + for _, v := range t { + n := make([]*line, 0, len(p)+1) + l := makeLine(p[v].min, 1, 1) + n = append(n, p[:v]...) + n = append(n, p[v+1:]...) + n = append(n, l, l) + + // 新增一种切牌 + *s = append(*s, n) + orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:]) + p = n // 下次以本次结果为基础 + } + orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:]) + } +} + +func combineTake(s *[][]*line, n int, pL int, lastShowCard *KindOfCard) { + ln := len(*s) + + for i := n; i < ln; i++ { + for j := 0; j < len((*s)[i]); j++ { + // 已没有可带牌 + if (*s)[i][j].num < 3 { + break + } + // 此牌已带过 + ct := (*s)[i][j].canTake() + // fmt.Println("Here", ct) + if ct == 0 { + continue + } + + // 记录被带单或对的位置 + is := make([]int, 0, ct) + id := make([]int, 0, ct) + for k := len((*s)[i]) - 1; k >= 0; k-- { + // 已没有单或对 + if (*s)[i][k].len != 1 || (*s)[i][k].num > 2 { + break + } + // 校验带牌与被带牌不能相同 + if !(*s)[i][j].isTake((*s)[i][k].min) { + continue + } + if (*s)[i][k].num == 1 && len(is) < int(ct) { + is = append(is, k) + } + if (*s)[i][k].num == 2 && len(id) < int(ct) { + id = append(id, k) + } + // 已找够单和对 + if len(is) == ct || len(id) == ct { + break + } + } + // fmt.Println("Here", ct, is, id) + if len(is) == ct { + *s = append(*s, pickupTake((*s)[i], j, is)) + } + if len(id) == ct { + *s = append(*s, pickupTake((*s)[i], j, id)) + } + } + } + + // 有新增则递归 + if len(*s) > ln { + combineTake(s, ln, pL, lastShowCard) + } +} + +// 带与被带之外为复制需考虑四点 +// [#1][带][#2][被带2][#3][被带1][#4] +// 为便于理解刻意举需要考虑#3的例子 +// [KKKAAA][555666][9][8][6][3] +// 当切成这样并考虑为56带38时 +// m = 1; t = [53] +func pickupTake(s []*line, m int, t []int) []*line { + r := make([]*line, 0, len(s)-len(t)) + r = append(r, s[:m]...) // #1 + tks := make([]*line, 0, len(t)) + for _, v := range t { + tks = append(tks, s[v]) + } + r = append(r, makeLine(s[m].min, s[m].num, s[m].len, tks...)) + for i := len(t) - 1; i >= 0; i-- { + if i == len(t)-1 { + r = append(r, s[m+1:t[i]]...) // #2 + } else { + r = append(r, s[t[i+1]+1:t[i]]...) // #3 + } + } + return append(r, s[t[0]+1:]...) // #4 +} + +func splitThree(lines []*line) bool { + min := -2 + flag := false + for _, v := range lines { + if v.num != 3 || v.min == 11 { + flag = false + break + } else { + if min == -2 { + min = v.min + } else if !flag && v.min-1 != min && v.min+1 != min { // 不是飞机的三带 + flag = true + } + } + } + return flag +} + +// cutPokers pokers 自己手牌 lastShowCard 上家出牌信息 otherPokers 别人剩余手牌 +func cutPokers(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32) []*cutex { + m := ptm(pokers) + pLen := len(pokers) + + var lines []*line + + // line 代表该名玩家能组成的所有牌型 + lines = append(lines, findPlane(m, 1, 3)...) // 单连, 未去重 + lines = append(lines, findCommon(m, 2)...) // 四牌 三牌 对牌 + lines = append(lines, findPress(m, lastShowCard)...) // 如果上家有牌就拆开 + + var s [][]*line + t := make([]*line, 0, len(lines)) + + selectLine(m, lines, t, &s, 0) + + // 去重 + s = distinctLine(s) + + for i := 0; i < len(s); i++ { + n := len(s[i]) + for j := 0; j < n; j++ { + s[i][j].decr(m) + } + // 拆四余三、拆三余对、拆对余单、原本的单牌 + s[i] = append(s[i], findCommon(m, 1)...) + for j := 0; j < n; j++ { + s[i][j].incr(m) + } + orderedBy(rSortType, rSortMin, rSortLen).Sort(s[i]) + } + + // 只有散牌 + if len(s) == 0 { + s = append(s, findCommon(m, 1)) + s = distinctLine(s) + } else { + + moreSplitTake(&s, pLen, lastShowCard) + + // 带牌 + combineTake(&s, 0, pLen, lastShowCard) + + s = distinctLine(s) + } + + cs := make([]*cutex, 0, len(s)) + enemy := newEnemy(otherPokers) + var isMaxFlag bool + if /*lastShowCard != nil ||*/ len(otherPokers) == 0 { // 由于首出牌敌人手牌传入为空,不限制会出错 + isMaxFlag = true + } + for _, v := range s { + c := &cutex{ + l: v, + h: len(v), + } + if m[int(POKER_2)] != 0 { + c.adv = true + } + for k, vv := range v { + if !isMaxFlag && enemy.isMax(vv) { + if lastShowCard != nil && vv.isBigThan(lastShowCard) { + c.h-- + continue + } else if lastShowCard == nil { + c.h-- + continue + } + } else if vv.min == int(POKER_2) { + c.h-- + continue + } + c.m = append(c.m, k) + continue + + } + + cs = append(cs, c) + } + sort.Sort(cutexSlice(cs)) + + return cs +} + +func headHintOut(pokers []int32, lastShowCard *KindOfCard) (lines []*line, retErr error) { // 提示 + defer func() { // 增加容错处理 + if err := recover(); err != nil { + retErr = errors.New("headHintOut is err") + lines = nil + } + }() + if lastShowCard != nil && lastShowCard.len == 0 { + //logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard) + lastShowCard = nil + } + //var lines []*line + m := ptm(pokers) + lines = append(lines, findPlane(m, 1, 3)...) // 单连, 未去重 + lines = append(lines, findSame(m, 4)...) // 四牌 + lines = append(lines, findSame(m, 3)...) // 三牌 + lines = append(lines, findSame(m, 2)...) // 对牌 + lines = append(lines, findSame(m, 1)...) // 单牌 + + return +} + +/* +outType: +暂时考虑... +0 各自为战 +1 给下个人喂牌,保证自己手数减少情况下管牌 +2 管牌,保证自己手数情况下压牌 +*/ +func selectOut(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool, outType int) (ls *line, retErr error) { // 选择手牌 + defer func() { // 增加容错处理 + if err := recover(); err != nil { + retErr = errors.New("selectOut is err") + ls = nil + } + }() + if lastShowCard != nil && lastShowCard.len == 0 { + //logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard) + lastShowCard = nil + } + cs := cutPokers(pokers, lastShowCard, otherPokers) + /*for _, v := range cs { + for _, v1 := range v.l { + fmt.Printf("CS: min:%v num:%v len:%v\n", v1.min, v1.num, v1.len) + for _, v2 := range v1.tks { + fmt.Printf(" tks:%v ", *v2) + } + v1.Print(os.Stdout) + fmt.Println() + } + fmt.Printf(" cs h:%v b:%v m:%v abv:%v \n\n", v.h, v.b, v.m, v.adv) + }*/ + // pL := len(pokers) + // var one *line + var offset int + // h==0 全是最大,找到直接出 + // h==1 先找最大,并记录首个非最大,若没有最大则出非最大,此时会赢 + for _, c := range cs { + if c.h > 1 { // 不能一手出完就跳过 + break + } + for k, l := range c.l { + if !l.isBigThan(lastShowCard) { + continue + } + // 没有非最大或这个不是非最大,说明这个是最大 考虑如果剩下一手情况下 + if len(c.m) == 0 || c.m[0] != k { // 后续考虑代码优化 + if len(c.l) > 2 { //真实手数超过两手 + for i, v := range c.l { + if !v.isBigThan(lastShowCard) { + continue + } + if len(c.m) == 0 { + if i == k { + continue + } + } else { + if i == k || i == c.m[0] { + continue + } + } + ls = v + // ls = calcOut(ls, pokers, lastShowCard, otherPokers) + return + } + } + ls = l + // ls = calcOut(ls, pokers, lastShowCard, otherPokers) + return + } + // 记录非最大 + if ls == nil { + if outType == 0 { // 压牌 + ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn) + } else { + ls = l + } + } + } + offset++ + } + + // 出非最大 + if ls != nil { + // ls = calcOut(ls, pokers, lastShowCard, otherPokers) + return //one, ret + } + + if len(cs[offset:]) != 0 { + cs = cs[offset:] + } + + // 首出 + // var ls *line + if lastShowCard == nil { + ls, retErr = headOut(cs, pokers) + } else { + ls = pressOut(cs, lastShowCard, outType) + } + if outType == 0 { // 压牌 + ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn) + } + return //ls, ret //pressOut(cs, lastShowCard) +} + +// 这里主要限制AAA带牌问题 +func containBoom(ls []*line) bool { + if len(ls) == 0 { + return false + } + orderedBy(sortMin, sortTks).Sort(ls) + if ls[0].num == 3 && ls[0].min == 13 { + return true + } + return false +} + +func pressOut(cs []*cutex, lastShowCard *KindOfCard, outType int) *line { + var h int + var ls []*line + + // 首个能压手数中可出牌 + // 本身就是根据h排序过的 + for _, c := range cs { + if h != 0 && c.h > h { + /*if containBoom(ls) { // 判断管牌有没有包含 + ls = ls[:0] + } else {*/ + break // continue + //} + } + for _, l := range c.l { + + if !l.isBigThan(lastShowCard) { + // fmt.Printf("cs: l%v type:%v\n", *l, l.hintType()) + continue + } + + ls = append(ls, l) + h = c.h + } + } + if len(ls) == 0 { + return nil + } + + if outType != 0 { + if h > cs[0].h+2 { + return nil + } + } + + /*// 无牌可出 + if len(ls) == 0 { + return nil + }*/ + + // 小 -> 小带 + orderedBy(sortMin, sortTks).Sort(ls) + return ls[0] +} + +func calcOut(ls *line, pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool) *line { + + if (warn || len(otherPokers) != 0 && len(otherPokers[0]) == 1) && ls != nil { // 报单出最大 + if ls.hintType() != Single { + return ls + } + m := ptm(pokers) + if lastShowCard == nil { // 自动出牌出对牌 + for i := 0; i <= 11; i++ { + if m[i] == 2 { + return makeLine(i, m[i], 1) + } + } + } + // 没有对牌,出大单 + + maxcard := int(POKER_2) + for i := maxcard; i >= 0; i-- { // ls不为nil且为单牌 肯定能压牌 + if m[i] != 0 { + return makeLine(i, 1, 1) + } + } + } + + return ls +} + +func headOut(cs []*cutex, pokers []int32) (l *line, retErr error) { + defer func() { // 增加容错处理 + if err := recover(); err != nil { + retErr = errors.New("headOut is err") + l = nil + } + }() + tps := make(map[int][]*line) + + // 最小逻辑牌值 + min := int(POKER_2) + for _, v := range pokers { + if n := realVle(v); n < min { + min = n + } + } + + // 从最少手数中选出携带最小牌值的出牌按牌型记录 + for _, c := range cs { + if c.h > cs[0].h { + break + } + for _, l := range c.l { + t := l.outType(min) // 暂不修改 + if t == 0 { // 不携带最小牌值 + continue + } + if t == 2 && c.takeOther(l.min) { + // 最小牌属于被带且当前切牌有别的被带可选 + tps[t] = append(tps[t], l.firstTake()) + } else { + tps[t] = append(tps[t], l) + } + } + } + + var i int + var ls []*line + + // 飞机带 -> 成连 -> 可带 -> 被带 -> 普通 + for i = 5; i > 0; i-- { + if v, ok := tps[i]; ok { + ls = v + break + } + } + + lLen := len(ls) + if lLen == 0 { // 自动出牌判断出错,使用老处理逻辑重新判断 + l = nil + retErr = errors.New("headOut is err") + return + } + + switch i { + case 5: + // 小 -> 长 -> 小带 + orderedBy(sortMin, rSortLen, sortTks).Sort(ls) + case 4: + // 小 -> 宽 -> 短 + orderedBy(sortMin, rSortNum, sortLen).Sort(ls) + case 3: + // 小带 + orderedBy(sortTks).Sort(ls) + case 2: + // 带 -> 小 -> 长 + orderedBy(rSortTake, sortMin, rSortLen).Sort(ls) + case 1: + // 宽 + orderedBy(rSortNum).Sort(ls) + } + + l = ls[0] + //retErr = errors.New("headOut is err") // 返回错误,使用老处理逻辑 + return // ls[10], ret +} + +func findPress(m map[int]int, lastShowCard *KindOfCard) []*line { + if lastShowCard == nil { + return nil + } + n := 4 + var ret []*line + switch lastShowCard.GetKind() { + case Single: + n = 1 + if lastShowCard.GetMin() == POKER_2 { + n = 4 + } + case Twin: + n = 2 + if lastShowCard.GetMin() == POKER_2 { + n = 4 + } + case Triple: + n = 3 + case Four: + n = 4 + } + + for i := int(POKER_2); i > 0; i-- { + if i <= int(lastShowCard.GetMin()) { + break + } + if m[i] >= n { + ret = append(ret, makeLine(i, n, 1)) + // return []*line{makeLine(i, n, 1)} + } + } + + return ret +} + +var rSortLen = func(p, q *line) int { + return sortLen(q, p) +} +var sortLen = func(p, q *line) int { + if p.len == q.len { + return 0 + } + if p.len > q.len { + return 1 + } + return -1 +} + +var rSortNum = func(p, q *line) int { + return sortNum(q, p) +} +var sortNum = func(p, q *line) int { + if p.num == q.num { + return 0 + } + if p.num > q.num { + return 1 + } + return -1 +} + +var rSortMin = func(p, q *line) int { + return sortMin(q, p) +} +var sortMin = func(p, q *line) int { + if p.min == q.min { + return 0 + } + if p.min > q.min { + return 1 + } + return -1 +} + +var rSortTks = func(p, q *line) int { + return sortTks(q, p) +} +var sortTks = func(p, q *line) int { + + pL := len(p.tks) + qL := len(q.tks) + if pL != qL { // 带对牌跟两张单牌。只需考虑对牌需不需要拆开 + if pL == 0 { + return 1 + } else if qL == 0 { + return -1 + } + if p.tks[0].min < q.tks[0].min { + return -1 + } + if p.tks[0].min > q.tks[0].min { + return 1 + } + if pL > qL { + return 1 + } + return -1 + } + for i := 0; i < pL; i++ { + if p.tks[i].min < q.tks[i].min { + return -1 + } + if p.tks[i].min > q.tks[i].min { + return 1 + } + } + return 0 +} + +var rSortType = func(p, q *line) int { + return sortType(q, p) +} +var sortType = func(p, q *line) int { + if tp, tq := p.Type(), q.Type(); tp == tq { + return 0 + } else if tp > tq { + return 1 + } + return -1 +} + +var rSortTake = func(p, q *line) int { + return sortTake(q, p) +} +var sortTake = func(p, q *line) int { + if p.tks != nil { + if q.tks == nil { + return 1 + } + } else if q.tks != nil { + return -1 + } + return 0 +} diff --git a/gamerule/samloc/constants.go b/gamerule/samloc/constants.go new file mode 100644 index 0000000..c1cb8f2 --- /dev/null +++ b/gamerule/samloc/constants.go @@ -0,0 +1,58 @@ +package samloc + +import "time" + +//////////////////////////////////////////////////////////////////////////////// +//tala +//////////////////////////////////////////////////////////////////////////////// + +const ( + MaxNumOfPlayer int = 5 //最多人数 + HandCardNum int32 = 10 //手牌数量 + InvalideCard int32 = -1 //默认牌 + InvalidePos int32 = -1 +) + +const ( + TestOpen bool = false //测试开关 + POKER_CNT = 52 + Hand_CardNum = 10 //手牌 + PER_CARD_COLOR_MAX = 13 +) + +const ( + RobotGameTimesMin int32 = 5 //机器人参与游戏次数下限 + RobotGameTimesMax int32 = 10 //机器人参与游戏次数上限 + DelayCanOp int = 3 //根据上家牌型额外延迟下家出牌时间 +) + +const ( + SamLocWaitStartTimeout = time.Second * 10 //人数够开启游戏, 延迟X秒开始游戏 + SamLocHandCardTimeout = time.Second * 5 //发牌 + SamLocBaoSamTimeout = time.Second * 11 //baosam + SamLocPlayerOpTimeout = time.Second * 16 //出牌(玩家操作阶段) + SamLocBilledTimeout = time.Second * 8 //结算 + SamLocPlayerOpTimeout1 = time.Second * 11 //托管1状态 + SamLocPlayerOpTimeout2 = time.Second * 6 //托管2状态 +) + +// 场景状态 +const ( + SamLocSceneStateWaitPlayer int = iota //0 等待玩家 + SamLocSceneStateWaitStart //1 延迟X秒开始游戏 + SamLocSceneStateHandCard //2 发牌 + SamLocSceneStateBaoSam //3 baosam + SamLocSceneStatePlayerOp //4 出牌(玩家操作阶段) + SamLocSceneStateBilled //5 结算 + SamLocSceneStateMax +) + +// 玩家操作 +const ( + SamLocPlayerOpNull int32 = iota // 初始值 + SamLocPlayerOpPlay // 出牌 + SamLocPlayerOpPass // 过牌 + SamLocPlayerOpShow // 提示 + SamLocPlayerOpStart // 房主开始游戏 + SamLocPlayerOpBaoSam // baosam +) diff --git a/gamerule/samloc/poker.go b/gamerule/samloc/poker.go new file mode 100644 index 0000000..aaec302 --- /dev/null +++ b/gamerule/samloc/poker.go @@ -0,0 +1,60 @@ +package samloc + +import ( + "math/rand" + "time" +) + +//牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 +//红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +type Card int + +type Poker struct { + buf [POKER_CNT]Card +} + +func (this *Poker) GetPokerBuf() [POKER_CNT]Card { + return this.buf +} + +func NewPoker() *Poker { + p := &Poker{} + p.init() + return p +} + +func (this *Poker) init() { + for i := int32(0); i < POKER_CNT; i++ { + this.buf[i] = Card(i) + } + rand.Seed(time.Now().UnixNano()) + this.Shuffle() +} + +func (this *Poker) Shuffle() { + for i := int32(0); i < POKER_CNT; i++ { + j := rand.Intn(int(i) + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} + +// 牌型算分~ +func GetCardsGrade(cards []int32) int { + return 0 +} + +func SelectOut(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool, outType int) (ret []int32, lcards []int32, err error) { // 选择手牌 + var v *line + v, err = selectOut(pokers, lastShowCard, + otherPokers, warn, 0) + + if err == nil { + ret, lcards = v.outPokers(pokers) + //v.Print(os.Stdout) + } + return +} diff --git a/gamerule/samloc/poker_test.go b/gamerule/samloc/poker_test.go new file mode 100644 index 0000000..3214ca4 --- /dev/null +++ b/gamerule/samloc/poker_test.go @@ -0,0 +1,146 @@ +package samloc + +import ( + "fmt" + "log" + "sort" + "testing" +) + +func TestPoker(t *testing.T) { + // 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 一条龙 + // + card := []int32{12, 11, 7, 6, 5, 4, 3, 2, 1, 0} + ty, lc := CardLore(card) + fmt.Println(ty, lc) +} + +func TestGetPokerType(t *testing.T) { + + cards := []int32{2, 2, 2, 2, 4, 4, 4, 4} + cards1 := []int32{0, 1, 2} + kind := GetCardKind(cards) + kind1 := GetCardKind(cards1) + aards := []int32{12, 1, 0, 24, 11} + log.Println("####################4 ", kind) + ret := kind.OutPokers(aards) + fmt.Println(ret) + fmt.Println(kind.ktype, kind.min, kind.max, CompareCard(kind, cards1), CompareCardKind(kind, kind1)) +} + +func TestRemoveCard(t *testing.T) { + + recards := []int32{9, 8, 7, 6, 19} + cards := []int32{10, 9, 8, 7, 6, 1, 19, 22, 51} + flag := RemoveCard(recards, &cards) + fmt.Println(flag, cards) +} + +func TestHeadHintOut(t *testing.T) { + lastKind := &KindOfCard{} + lastKind.kind = Single + lastKind.min = 3 + lastKind.len = 1 + cards := []int32{12, 51, 11} + _, err := headHintOut(cards, nil) + log.Println("####################", err) + +} + +func TestDelOtherPOKER2(t *testing.T) { + + cards := []int32{26, 2, 15, 19, 34, 22, 48, 10, 11, 24} + ret := DelOtherPOKER2(cards, []int32{11}) + log.Println("####################", ret, cards) + +} + +func TestTickOut(t *testing.T) { + lcards := []int32{2, 1, 0} //{33, 46} + lastKind := GetCardKind(lcards) + cards := []int32{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} //{0, 2, 15, 28, 41, 4, 17, 30, 43} + ret := TickOut(cards, lastKind, false) + log.Println("####################", ret, cards) + +} +func TestALL(t *testing.T) { + cards := []int32{12, 11, 8, 7, 43, 30, 3, 2, 1, 0} + p := ptstr32m(cards) + log.Println("####################", p) + ret := &Kds{ + //kd: ret, + } + ret.kd = append(ret.kd, allfindStraight(p, 1, 3)...) + ret.kd = append(ret.kd, findKindCommon(p, 2)...) + ret.kd = append(ret.kd, findKindCommon(p, 3)...) + ret.kd = append(ret.kd, findKindCommon(p, 4)...) + + sort.Sort(ret) + // log.Println("####################", len(ret)) + for _, v := range ret.kd { + log.Println("#################### ret1", v, v.OutPokers(cards)) + } +} + +func TestStraight(t *testing.T) { + cards := []int32{12, 11, 8, 7, 43, 30, 3, 2, 1, 0} + p := ptstr32m(cards) + log.Println("####################", p) + ret := findStraight(p, 1, 3) + ret[0], ret[1] = ret[1], ret[0] + for _, v := range ret { + log.Println("####################", v, v.OutPokers(cards)) + } + ret1 := &Kds{ + kd: ret, + } + sort.Sort(ret1) + // log.Println("####################", len(ret)) + for _, v := range ret1.kd { + log.Println("#################### ret1", v, v.OutPokers(cards)) + } + + ret = allfindStraight(p, 1, 3) + // log.Println("####################", len(ret)) + for _, v := range ret { + log.Println("####################", v, v.OutPokers(cards)) + } + +} + +func TestCommon(t *testing.T) { + cards := []int32{26, 2, 15, 28, 41, 22, 48, 10, 11, 24} + p := pt32m(cards) + ret := findKindCommon(p, 4) + // log.Println("####################", len(ret)) + for _, v := range ret { + log.Println("####################", v, v.OutPokers(cards)) + } + +} + +//牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 +//红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +func TestAlls(t *testing.T) { + + lcards := []int32{} //{33, 46} + lastKind := GetCardKind(lcards) + fmt.Println(lastKind) + // 4, 0, 1, 2, 9, 22, 35 + cards := []int32{0, 13, 24, 37} //10, 36, 9, 22, 12, 25 // 2, 3, 16, 30, 31, 8, 47, 9, 22, 11 + + /*v, err := selectOut(cards, nil, + [][]int32{}, 1) + ret, lcards := v.outPokers(cards)*/ + warn := false + ret, lcards, err := SelectOut(cards, lastKind, [][]int32{{1, 14}}, warn, 0) + log.Println("####################", err) + fmt.Println(ret, lcards, cards) + + //v.Print(os.Stdout) + +} diff --git a/gamerule/smallrocket/constants.go b/gamerule/smallrocket/constants.go new file mode 100644 index 0000000..dab7505 --- /dev/null +++ b/gamerule/smallrocket/constants.go @@ -0,0 +1,37 @@ +package smallrocket + +import "time" + +// 场景状态 +const ( + SmallRocketSceneStateWait int = iota //等待状态 + SmallRocketSceneStateStart //开始倒计时 + SmallRocketSceneStatePlayGame //发射游戏中 + SmallRocketSceneStateBilled //结算 + SmallRocketSceneStateMax +) + +const ( + SmallRocketSceneWaitTimeout = time.Second * 2 //等待倒计时 + SmallRocketSceneStartTimeout = time.Second * 6 //开始倒计时 + SmallRocketSceneBilledTimeout = time.Second * 2 //结算 +) + +const ( + SmallRocketBombRandomMulMaxE = 100000000 // 随机最大值 + SmallRocketPlayerTakeMulMax = 100 // 玩家下注倍数 为了满足浮点数放大100倍 + SmallRocketPlayerTransDataMul = 10000 + SmallRocketBombRoomMulMaxE = 1000 // 每局房间里随机的最大倍数 +) + +// 玩家操作 +const ( + SmallRocketPlayerOpBet = 0 // 下注 + SmallRocketPlayerOpCancelBet = 1 // 取消下注 + SmallRocketPlayerOpTakeReward = 2 // 下车收取奖励 + SmallRocketPlayerOpSetAutoBet = 3 // 设置自动下注 + SmallRocketPlayerOpSetAutoTakeGain = 4 // 设置自动领取 + SmallRocketPlayerOpSResumeGetPlayerInfo = 5 // 中断后回到游戏请求玩家信息 + SmallRocketPlayerOpStopAutoBet = 6 // 自动下注停止提示信息 + SmallRocketPlayerOpAutoSubCoin = 7 // 自动下注收取提前扣除金币 +) diff --git a/gamerule/smallrocket/logic.go b/gamerule/smallrocket/logic.go new file mode 100644 index 0000000..e719999 --- /dev/null +++ b/gamerule/smallrocket/logic.go @@ -0,0 +1,277 @@ +package smallrocket + +import ( + "fmt" + "math" + "math/rand" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "strconv" + "time" +) + +type Logic struct { + BoomMul float64 + + BombTimeToMulArr []*BombTimeToMul + BombMulToTimeArr []*BombMulToTime +} + +func (this *Logic) init() { + if this.BombTimeToMulArr == nil { + this.BombTimeToMulArr = make([]*BombTimeToMul, 0) + } + + if this.BombMulToTimeArr == nil { + this.BombMulToTimeArr = make([]*BombMulToTime, 0) + } +} + +// 重置下注信息 +func (this *Logic) ResetBetData(betData []*BetDataEx) { + if len(betData) >= 2 { + for i := 0; i < len(betData); i++ { + betData[i].Reset() + } + } +} + +// 火箭爆炸倍数 +func (l *Logic) CalBoomMul(e int) (float64, int) { + h := rand.Intn(e) + + fh := float64(h) + fe := float64(e) + + return math.Floor((96*fe)/(fe-fh)) / 100.00, h +} + +// 火箭爆炸倍数 +func (l *Logic) CalBoomTime(p int) float64 { + fp := float64(p) + //q := 100000000.00 + pp := 400000.00 + ppp := 20000.00 + score := 0.0 + for { + if pp+ppp > float64(SmallRocketBombRandomMulMaxE) { + pp += ppp * (fp - pp) / fp + score = fp / (fp - pp) + + break + } + } + logger.Logger.Trace("score=", score) + return score +} + +// 本局是否下注 +func (l *Logic) IsCurRoundBet(betData []*BetDataEx) bool { + if len(betData) >= 2 { + if betData[0].IsCurBet || betData[1].IsCurBet { + return true + } else { + return false + } + } + return false +} + +// 本局的子下注位置否下注 +func (l *Logic) IsCurRoundBetPos(betData []*BetDataEx, betPos int64) bool { + if len(betData) >= 2 { + return betData[betPos].IsCurBet + } + return false +} + +// 下一局位置否下注 +func (l *Logic) IsNextRoundBet(betData []*BetDataEx) bool { + if len(betData) >= 2 { + if betData[0].IsNextBet || betData[1].IsNextBet { + return true + } else { + return false + } + } + return false +} + +// 下一局的子下注位置否下注 +func (l *Logic) IsNextRoundBetPos(betData []*BetDataEx, betPos int64) bool { + if len(betData) >= 2 && betPos <= 1 { + return betData[betPos].IsNextBet + } + return false +} + +// 获取本局下注总金额 +func (l *Logic) GetRoundBetCoin(betData []*BetDataEx) float64 { + if !l.IsCurRoundBet(betData) { + return 0 + } + + var retBetCoin float64 = 0 + if len(betData) >= 2 { + if betData[0].IsCurBet { + retBetCoin += float64(betData[0].BetVal) + } + + if betData[1].IsCurBet { + retBetCoin += float64(betData[1].BetVal) + } + } + + return retBetCoin +} + +// 本局金币收益 +func (l *Logic) GetRoundGainCoin(betData []*BetDataEx, bombMul float64) float64 { + if !l.IsCurRoundBet(betData) { + return 0 + } + + var retGainCoin float64 = 0 + if len(betData) >= 2 { + if betData[0].IsCurBet && !betData[0].IsTakeGain { + retGainCoin += float64(betData[0].BetVal) * (bombMul - 1.0) + } + + if betData[1].IsCurBet && !betData[1].IsTakeGain { + retGainCoin += float64(betData[1].BetVal) * (bombMul - 1.0) + } + } + + return retGainCoin +} + +// 本局是否所有奖励都已领取 +func (this *Logic) IsExistRoundTakenGain(betData []*BetDataEx) bool { + + if len(betData) >= 2 { + if betData[0].IsCurBet && !betData[0].IsTakeGain { + return false + } + + if betData[1].IsCurBet && !betData[1].IsTakeGain { + return false + } + } + return true +} + +// 加载火箭时间和倍数数据 +func (this *Logic) LoadCrashSearchData() { + + dbItemArr := srvdata.PBDB_CrashSearchMgr.Datas.Arr + + logger.Logger.Trace("(this *Logic) LoadCrashSearchData: dbItemArr.Len: ", len(dbItemArr)) + if dbItemArr != nil { + if this.BombTimeToMulArr == nil { + this.BombTimeToMulArr = make([]*BombTimeToMul, 0, 5120) + } + + if this.BombMulToTimeArr == nil { + this.BombMulToTimeArr = make([]*BombMulToTime, 0, 5120) + } + + preTime := 0 + preMul := 0 + for _, dbItem := range dbItemArr { + + this.BombTimeToMulArr = append(this.BombTimeToMulArr, &BombTimeToMul{int32(preTime), dbItem.GetTime(), dbItem.GetPrice()}) + + this.BombMulToTimeArr = append(this.BombMulToTimeArr, &BombMulToTime{int32(preMul), dbItem.GetPrice(), dbItem.GetTime()}) + + preTime = int(dbItem.GetTime()) + preMul = int(dbItem.GetPrice()) + + } + + logger.Logger.Trace("(this *Logic) LoadCrashSearchData: BombTimeToMulArr.Len: ", len(this.BombTimeToMulArr)) + logger.Logger.Trace("(this *Logic) LoadCrashSearchData: BombMulToTimeArr.Len: ", len(this.BombMulToTimeArr)) + } +} + +// 通过火箭倍数找到火箭爆炸时间 +func (this *Logic) GetBombTimeByBombMul(bombMulSearch int32) int32 { + + //logger.Logger.Trace("(this *Logic) GetBombTimeByBombMul: In , bombMulSearch:", bombMulSearch) + + if this.BombMulToTimeArr == nil { + return 0 + } + + //for i := 0; i <= len(this.BombMulToTimeArr); i++ { + // //if BombMulToTimeArr + // if bombMulSearch > mulItem.MinBombMul && bombMulSearch <= mulItem.MaxBombMul { + // + // } + //} + + //logger.Logger.Trace("(this *Logic) GetBombTimeByBombMul: BombMulToTimeArr.Len: ", len(this.BombMulToTimeArr)) + + for _, mulItem := range this.BombMulToTimeArr { + if bombMulSearch > mulItem.MinBombMul && bombMulSearch <= mulItem.MaxBombMul { + return mulItem.BombTime + } + } + + logger.Logger.Trace("(this *Logic) GetBombTimeByBombMul: Not Found ") + return 0 +} + +// 通过火箭爆炸时间找到火箭倍数 +func (this *Logic) GetBombMulByBombTime(BombTime int32) int32 { + if this.BombTimeToMulArr == nil { + return 0 + } + + for _, mulItem := range this.BombTimeToMulArr { + if BombTime > mulItem.MinBombTime && BombTime <= mulItem.MaxBombTime { + return mulItem.BombMul + } + } + + return 0 +} + +// 下注信息 +type BetDataEx struct { + BetVal int64 // 下注金额 + TakeMul float64 // 下注领取倍数 + IsCurBet bool // 本局是否下注 + IsNextBet bool // 下一局是否下注 + IsTakeGain bool // 是否收取过奖励 + IsAutoBetAndTake bool // 是否自动下注收取 + GainSearchTime time.Duration // 自动收取时间 + GainCoinMul float64 // 最终赢取倍数 +} + +// 重置 +func (b *BetDataEx) Reset() { + b.BetVal = 1000 + b.TakeMul, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", 1.20), 64) + b.IsCurBet = false + b.IsTakeGain = false + b.IsNextBet = false + b.IsAutoBetAndTake = false + b.GainCoinMul = 0 +} + +// 新一局 设置数据 +func (b *BetDataEx) ReStartGame() { + b.IsCurBet = false + b.IsTakeGain = false + + if b.IsNextBet { + b.IsCurBet = true + } + + if b.IsAutoBetAndTake { + b.IsCurBet = true + } + + b.IsNextBet = false + b.GainCoinMul = 0 +} diff --git a/gamerule/smallrocket/smallrocket.go b/gamerule/smallrocket/smallrocket.go new file mode 100644 index 0000000..ee5c7ab --- /dev/null +++ b/gamerule/smallrocket/smallrocket.go @@ -0,0 +1,137 @@ +package smallrocket + +import "fmt" + +type SequentialQueue struct { + Items []any // 队列元素 + Length int32 // 队列元素个数 + Cap int32 // 队列容量 +} + +// NewSequentialQueue 初始化队列 +func NewSequentialQueue(cap int32) *SequentialQueue { + return &SequentialQueue{ + Items: make([]any, 0, cap), + Length: 0, + Cap: cap, + } +} + +// Clear 清空队列 +func (queue *SequentialQueue) Clear() { + queue.Items = []any{} +} + +// IsEmpty 判断队列是否为空 +func (queue *SequentialQueue) IsEmpty() bool { + return queue.Length == 0 +} + +// IsFull 判断队列是否为满 +func (queue *SequentialQueue) IsFull() bool { + return queue.Length == queue.Cap +} + +// Push 将元素添加到该队列末尾 +func (queue *SequentialQueue) Push(item any) { + if queue.IsFull() { + queue.Items = queue.Items[1:] + queue.Length-- + } + + queue.Items = append(queue.Items, item) + queue.Length++ +} + +// Pop 将该队列首元素弹出并返回 +func (queue *SequentialQueue) Pop() any { + if queue.IsEmpty() { + fmt.Println("queue empty") + return nil + } + item := queue.Items[0] + queue.Items = queue.Items[1:] + queue.Length-- + return item +} + +// Peek 获取该队列首元素但不出队 +func (queue *SequentialQueue) Peek() any { + if queue.IsEmpty() { + fmt.Println("queue empty") + return nil + } + return queue.Items[0] +} + +// Back 获取该队列尾元素 +func (queue *SequentialQueue) Back() any { + if queue.IsEmpty() { + fmt.Println("queue empty") + return nil + } + return queue.Items[queue.Length-1] +} + +func (queue *SequentialQueue) GetItems() []any { + return queue.Items +} + +func (q *SequentialQueue) GetFloat64Items() []float64 { + var retItems []float64 + + for i := q.Length - 1; i >= 0; i-- { + + switch s := q.Items[i].(type) { + case float64: + retItems = append(retItems, s) + } + } + + return retItems +} + +func (q *SequentialQueue) GetFloat32Items() []float32 { + var retItems []float32 + + for i := q.Length - 1; i >= 0; i-- { + + switch s := q.Items[i].(type) { + case float32: + retItems = append(retItems, s) + } + } + + return retItems +} + +func (q *SequentialQueue) ShowQueue() { + + for i := q.Length - 1; i >= 0; i-- { + + switch s := q.Items[i].(type) { + case float64: + //strconv.FormatFloat(s, 'f', -1, 64) + fmt.Printf(" queue[%d] = %f \t", i, s) + case float32: + fmt.Printf(" queue[%d] = %f \t", i, s) + default: + + } + + } + + fmt.Println() +} + +type BombTimeToMul struct { + MinBombTime int32 + MaxBombTime int32 + BombMul int32 +} + +type BombMulToTime struct { + MinBombMul int32 + MaxBombMul int32 + BombTime int32 +} diff --git a/gamerule/tala/card.go b/gamerule/tala/card.go new file mode 100644 index 0000000..d2e9fd5 --- /dev/null +++ b/gamerule/tala/card.go @@ -0,0 +1,477 @@ +package tala + +import ( + "sort" + "strconv" +) + +//牌序- K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3, 2, A +//方片- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +const ( + TaLaNil int = iota //nil + ColorStraight //1 同花顺 + Triple //2 三张 +) + +// Color 黑桃0 梅花1 紅桃2 方片3 +func Color(c int32) int { + return int(c) / PER_CARD_COLOR_MAX +} + +func Value(c int32) int { + value := int(c+1) % PER_CARD_COLOR_MAX + if value == 0 { + value = PER_CARD_COLOR_MAX + } + return value +} + +func ValueStr(c int32) string { + if c == InvalideCard { + return "" + } + str := "" + value := strconv.Itoa(Value(c)) + switch Color(c) { + case 0: + str = "黑桃" + case 1: + str = "梅花" + case 2: + str = "紅桃" + case 3: + str = "方片" + } + return str + value +} + +func IsStraight(cards []int32) bool { //顺子 + if len(cards) < 3 || len(cards) > HandCardNum { + return false + } + tmpCards := []int{} + for _, card := range cards { + tmpCards = append(tmpCards, Value(card)) + } + sort.Ints(tmpCards) + for i := 0; i < len(tmpCards)-1; i++ { + card := tmpCards[i] + nextCard := tmpCards[i+1] + if nextCard-card != 1 { //不相邻 + return false + } + } + return true +} + +func IsColorStraight(cards []int32) bool { //同花顺子 + // 先是顺子 + if !IsStraight(cards) { + return false + } + c := Color(cards[0]) + for _, card := range cards { + if c != Color(card) { + return false + } + } + return true +} + +func IsTriple(cards []int32) bool { //三张 + if len(cards) < 3 || len(cards) > 4 { // 3张或者4张 + return false + } + switch len(cards) { + case 3: + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + return true + case 4: + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + if Value(cards[2]) != Value(cards[3]) { + return false + } + return true + } + return false +} + +func CanChi(cards []int32, chiCard int32, excludeCards []int32) (bool, []int32) { //可吃 + chi2pPhom := []int32{chiCard} + if chiCard == InvalideCard { + return false, chi2pPhom + } + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + if len(cpCards) == 0 { + return false, chi2pPhom + } + n := 0 + tmp := []int32{} + for _, card := range cpCards { + if Value(chiCard) == Value(card) && !InSliceInt32(excludeCards, card) { + n++ + tmp = append(tmp, card) + } + } + if n >= 2 { //找到两张以上牌值相同的,可以吃 + for _, card := range tmp { + chi2pPhom = append(chi2pPhom, card) + } + return true, chi2pPhom + } + chi2pPhom = []int32{chiCard} + min1 := false + min2 := false + minPhom := []int32{} + mid1 := false + mid2 := false + midPhom := []int32{} + max1 := false + max2 := false + maxPhom := []int32{} + + for _, card := range cpCards { + if Color(chiCard) == Color(card) && !InSliceInt32(excludeCards, card) { + //最小 + if chiCard+1 == card { + minPhom = append(minPhom, card) + min1 = true + } + if chiCard+2 == card { + minPhom = append(minPhom, card) + min2 = true + } + if min1 && min2 { + for _, c := range minPhom { + chi2pPhom = append(chi2pPhom, c) + } + return true, chi2pPhom + } + //中间 + if chiCard+1 == card { + midPhom = append(midPhom, card) + mid1 = true + } + if chiCard-1 == card { + midPhom = append(midPhom, card) + mid2 = true + } + if mid1 && mid2 { + for _, c := range midPhom { + chi2pPhom = append(chi2pPhom, c) + } + return true, chi2pPhom + } + //最大 + if chiCard-1 == card { + maxPhom = append(maxPhom, card) + max1 = true + } + if chiCard-2 == card { + maxPhom = append(maxPhom, card) + max2 = true + } + if max1 && max2 { + for _, c := range maxPhom { + chi2pPhom = append(chi2pPhom, c) + } + return true, chi2pPhom + } + } + } + return false, chi2pPhom +} + +func GetCardsValue(cards []int32) int { + cardValue := 0 + for _, card := range cards { + if card != InvalideCard { + cardValue += Value(card) + } + } + return cardValue +} + +// 尝试组合phom +func TryPhom(cards, chiCards []int32) ([]int32, [][]int32) { //胡 + cpCards := []int32{} + phomCards := [][]int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + if len(cpCards) == 0 { + return cpCards, phomCards + } + cardValue := GetCardsValue(cards) + tmpPhoms := [][]int32{} + var TryOpPhom func([]int32) + + TryOpPhom = func(cs []int32) { + if len(cs) == 0 { + return + } + cp := []int32{} + for _, i2 := range cs { + if i2 != InvalideCard { + cp = append(cp, i2) + } + } + //fmt.Println("===2===", cp) + phoms := needPhom(cp) + if len(phoms) != 0 { + //fmt.Println("===4===", phoms) + for _, phom := range phoms { + for _, card := range phom { + for i := 0; i < len(cp); i++ { + if cp[i] == card { + cp = append(cp[:i], cp[i+1:]...) + i-- + } + } + } + tmpCp := []int32{} + for _, tmp := range cp { + tmpCp = append(tmpCp, tmp) + } + tmpPhoms = append(tmpPhoms, phom) + + TryOpPhom(tmpCp) + + //fmt.Println("===3===", tmpCp, tmpPhoms) + if GetCardsValue(tmpCp) < cardValue && !InSliceASliceB(tmpCp, chiCards) { //是不是最优解 //吃过的牌不能留在手牌里 + cardValue = GetCardsValue(cp) + phomCards = [][]int32{} + for _, tmpPhom := range tmpPhoms { + if len(tmpPhom) > 0 { + tmp := []int32{} + for _, c := range tmpPhom { + tmp = append(tmp, c) + } + phomCards = append(phomCards, tmp) + } + } + } + cp = []int32{} //回退下 next + for _, card := range cs { + cp = append(cp, card) + } + tmpPhoms = append(tmpPhoms[:len(tmpPhoms)-1]) + } + } + return + } + + TryOpPhom(cpCards) + //fmt.Println("===1===", phomCards) + for _, phom := range phomCards { + for _, card := range phom { + for i, cpcard := range cpCards { + if card == cpcard { + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + break + } + } + } + } + return cpCards, phomCards +} + +// b中元素是否在a里面 +func InSliceASliceB(a, b []int32) bool { + for _, bi := range b { + if InSliceInt32(a, bi) { + return true + } + } + return false +} + +// 有所有的同花顺组合 或者 有所有的点数相同的组合 +func needPhom(cards []int32) [][]int32 { //胡 + tmps := [][]int32{} + haveColorStraights, colorStraights := needColorStraight(cards) + haveTriples, triples := needTriple(cards) + if haveColorStraights || haveTriples { //有所有的同花顺组合 或者 有所有的点数相同的组合 + if haveColorStraights { + for _, straight := range colorStraights { + tmps = append(tmps, straight) + } + } + if haveTriples { + for _, triple := range triples { + tmps = append(tmps, triple) + } + } + } + return tmps +} + +// 找所有相同点数的三张 +func needTriple(cards []int32) (bool, [][]int32) { + haveNeed := false + needCards := [][]int32{} + if len(cards) != 0 { + mapTriple := make(map[int32][]int32) + for _, card := range cards { + if card != InvalideCard { + mapTriple[int32(Value(card))] = append(mapTriple[int32(Value(card))], card) + } + } + if len(mapTriple) > 0 { + for _, tripleCards := range mapTriple { + if len(tripleCards) == 3 { + haveNeed = true + needCards = append(needCards, tripleCards) + } + if len(tripleCards) == 4 { + haveNeed = true + needCards = append(needCards, tripleCards) + for _, card := range tripleCards { + tmps := []int32{} + for _, tmp := range tripleCards { + if card != tmp { + tmps = append(tmps, tmp) + } + } + if len(tmps) > 0 { + needCards = append(needCards, tmps) + } + } + } + } + } + } + return haveNeed, needCards +} + +// 所有的同花顺 +func needColorStraight(cards []int32) (bool, [][]int32) { + haveNeed := false + needCards := [][]int32{} + if len(cards) != 0 { + sliceCS := [4][]int32{} //color-card + for _, card := range cards { + if card != InvalideCard { + sliceCS[Color(card)] = append(sliceCS[Color(card)], card) + } + } + for _, colorCards := range sliceCS { + if len(colorCards) > 0 { + sort.Slice(colorCards, func(i, j int) bool { + if Value(colorCards[i]) > Value(colorCards[j]) { + return false + } + return true + }) + for i := len(colorCards); i > 2; i-- { + tmps := FindStraightWithWidth(i, colorCards) + if len(tmps) > 0 { + haveNeed = true + for _, tmp := range tmps { + needCards = append(needCards, tmp) + } + } + } + } + } + } + return haveNeed, needCards +} + +// 找一个固定长度的顺子 (2,3,4,5,6) 3-> [2,3,4] +func FindOneStraightWithWidth(n int, pairs []int32) []int32 { + if len(pairs) == 0 { + return nil + } + lastKey := pairs[0] + tempPair := []int32{lastKey} + if n == 1 { + return tempPair + } + for i := 1; i < len(pairs); i++ { + if pairs[i]-lastKey == 1 { + tempPair = append(tempPair, pairs[i]) + } else { + tempPair = []int32{pairs[i]} + } + if len(tempPair) == n { + break + } + lastKey = pairs[i] + } + if len(tempPair) == n { + return tempPair + } + return nil +} + +// 找多个固定长度的顺子(2,3,4,5,6) 3-> [[2,3,4][3,4,5][4,5,6]] +func FindStraightWithWidth(n int, pairs []int32) [][]int32 { + if len(pairs) == 0 || n < 2 { + return nil + } + var tempPairs [][]int32 + lastKey := pairs[0] + tempPair := []int32{lastKey} + for i := 1; i < len(pairs); i++ { + if pairs[i]-lastKey == 1 { + tempPair = append(tempPair, pairs[i]) + } else { + tempPair = []int32{pairs[i]} + } + if len(tempPair) == n { + tempPairs = append(tempPairs, tempPair) + tempPair = []int32{} + for j := n - 1; j > 0; j-- { + tempPair = append(tempPair, pairs[i-j+1]) + } + } + lastKey = pairs[i] + } + return tempPairs +} + +func DelSliceInt32(sl []int32, v int32) []int32 { + index := -1 + for key, value := range sl { + if value == v { + index = key + break + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl +} + +func InSliceInt32(sl []int32, v int32) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} diff --git a/gamerule/tala/constants.go b/gamerule/tala/constants.go new file mode 100644 index 0000000..7113d18 --- /dev/null +++ b/gamerule/tala/constants.go @@ -0,0 +1,42 @@ +package tala + +import "time" + +//////////////////////////////////////////////////////////////////////////////// +//tala +//////////////////////////////////////////////////////////////////////////////// + +const ( + MaxNumOfPlayer int = 4 //最多人数 + HandCardNum int = 10 //手牌数量 + InvalideCard int32 = -1 //默认牌 + InvalidePos int32 = -1 +) + +const ( + TaLaWaitStartTimeout = time.Second * 5 //人数够开启游戏, 延迟X秒开始游戏 + TaLaHandCardTimeout = time.Second * 3 //发牌 + TaLaPlayerOpTimeout = time.Second * 30 //出牌(玩家操作阶段) + TaLaBilledTimeout = time.Second * 5 //结算 +) + +// 场景状态 +const ( + TaLaSceneStateWaitPlayer int = iota //0 等待玩家 + TaLaSceneStateWaitStart //1 延迟X秒开始游戏 + TaLaSceneStateHandCard //2 发牌 + TaLaSceneStatePlayerOp //3 出牌(玩家操作阶段) + TaLaSceneStateBilled //4 结算 + TaLaSceneStateMax +) + +// 玩家操作 +const ( + TaLaPlayerOpNull int32 = iota //初始值 + TaLaPlayerOpChi //吃住 + TaLaPlayerOpChou //抽牌 + TaLaPlayerOpPlay //出牌 + TaLaPlayerOpPhom //组phom + TaLaPlayerOpShowPhom //亮phom + TaLaPlayerOpStart //房主开始游戏 +) diff --git a/gamerule/tala/poker.go b/gamerule/tala/poker.go new file mode 100644 index 0000000..f134e2f --- /dev/null +++ b/gamerule/tala/poker.go @@ -0,0 +1,42 @@ +package tala + +import ( + "math/rand" + "time" +) + +const ( + POKER_CNT = 52 + PER_CARD_COLOR_MAX = 13 +) + +type Card int + +type Poker struct { + buf [POKER_CNT]Card +} + +func (this *Poker) GetPokerBuf() [POKER_CNT]Card { + return this.buf +} + +func NewPoker() *Poker { + p := &Poker{} + p.init() + return p +} + +func (this *Poker) init() { + for i := int32(0); i < POKER_CNT; i++ { + this.buf[i] = Card(i) + } + rand.Seed(time.Now().UnixNano()) + this.Shuffle() +} + +func (this *Poker) Shuffle() { + for i := int32(0); i < POKER_CNT; i++ { + j := rand.Intn(int(i) + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} diff --git a/gamerule/tala/poker_test.go b/gamerule/tala/poker_test.go new file mode 100644 index 0000000..65e4783 --- /dev/null +++ b/gamerule/tala/poker_test.go @@ -0,0 +1,32 @@ +package tala + +import ( + "fmt" + "testing" +) + +//牌序- K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3, 2, A +//方片- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +func TestPoker(t *testing.T) { + cards := []int32{7, 1, 31, 33, 43, 32, 4, 17, 34, 20} + chiCards := []int32{20, 32, 43} + + for _, card := range cards { + fmt.Println("card :", ValueStr(card)) + } + + for _, chi := range chiCards { + fmt.Println("chi :", ValueStr(chi)) + } + + cpCards, phomCards := TryPhom(cards, chiCards) + fmt.Println("cpCards :", cpCards) + fmt.Println("phomCards :", phomCards) + //for _, tmp := range tmps { + // fmt.Println("tmp :", tmp) + //} +} diff --git a/gamerule/tamquoc/constants.go b/gamerule/tamquoc/constants.go new file mode 100644 index 0000000..84c59ab --- /dev/null +++ b/gamerule/tamquoc/constants.go @@ -0,0 +1,1346 @@ +package tamquoc + +// 二次元 +const ( + Element_BONUS int = iota + 1 //1 Bonus + Element_FREESPIN //2 FreeSpin + Element_JACKPOT //3 Jackpot + Element_NGUA //4 圣旨 + Element_NHAN //5 紫钻石 + Element_AO //6 戒指 + Element_VUKHI //7 跨服 + Element_Max +) + +var Element_NAME_MAP = map[int]string{ + Element_BONUS: "[ BONUS ]", + Element_FREESPIN: "[ FREE ]", + Element_JACKPOT: "[JACKPOT]", + Element_NGUA: "[ 圣旨 ]", + Element_NHAN: "[ 紫钻石 ]", + Element_AO: "[ 戒指 ]", + Element_VUKHI: "[ 跨服 ]", + -1: "[ - ]", +} + +/* 所有线条数组 + *0 1 2 3 4 + *5 6 7 8 9 + *10 11 12 13 14 + */ +var AllLineMatrix = [][]int{ + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条4 + + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条5 + + ///////////////////// + // *---*-------*---* + // \ / + // --------*-------- + // + // ----------------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + }, //线条6 + + ///////////////////// + // ----------------- + // + // --------*-------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + }, //线条7 + + ///////////////////// + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条8 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条9 + + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条10 + + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条11 + + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条12 + + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条13 + + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条14 + + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条15 + + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条16 + + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条17 + + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条18 + + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条19 + + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条20 +} +var AllLineArray = [][]int{ + {5, 6, 7, 8, 9}, //线条1 + {0, 1, 2, 3, 4}, //线条2 + {10, 11, 12, 13, 14}, //线条3 + {5, 6, 2, 8, 9}, //线条4 + {5, 6, 12, 8, 9}, //线条5 + {0, 1, 7, 3, 4}, //线条6 + {10, 11, 7, 13, 14}, //线条7 + {0, 11, 2, 13, 4}, //线条8 + {10, 1, 12, 3, 14}, //线条9 + {5, 1, 12, 3, 9}, //线条10 + {10, 6, 2, 8, 14}, //线条11 + {0, 6, 12, 8, 4}, //线条12 + {5, 11, 7, 3, 9}, //线条13 + {5, 1, 7, 13, 9}, //线条14 + {10, 6, 7, 8, 14}, //线条15 + {0, 6, 7, 8, 4}, //线条16 + {5, 11, 12, 13, 9}, //线条17 + {5, 1, 2, 3, 9}, //线条18 + {10, 11, 7, 3, 4}, //线条19 + {0, 1, 7, 13, 14}, //线条20 +} + +var AllLineDraw = []string{ + ` + ///////////////////// + // ----------------- + // + // *---*---*---*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + }, //线条1 + `, + + ` + ///////////////////// + // *---*---*---*---* + // + // ----------------- + // + // ----------------- + ///////////////////// + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + }, //线条2 + `, + + ` + ///////////////////// + // ----------------- + // + // ----------------- + // + // *---*---*---*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, //线条3 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // *---*-------*---* + // + // ----------------- + ///////////////////// + { + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 0, 0, 0, + }, //线条4 + `, + + ` + ///////////////////// + // ----------------- + // + // *---*-------*---* + // \ / + // --------*-------- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + }, //线条5 + `, + + ` + ///////////////////// + // *---*-------*---* + // \ / + // --------*-------- + // + // ----------------- + ///////////////////// + { + 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + }, //线条6 + `, + + ` + ///////////////////// + // ----------------- + // + // --------*-------- + // / \ + // *---*-------*---* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, + }, //线条7 + `, + + ` + ///////////////////// + // *-------*-------* + // \ / \ / + // ----------------- + // \ / \ / + // ----*-------*---- + ///////////////////// + { + 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + }, //线条8 + `, + + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // ----------------- + // / \ / \ + // *-------*-------* + ///////////////////// + { + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, + }, //线条9 + `, + + ` + ///////////////////// + // ----*-------*---- + // / \ / \ + // *---------------* + // \ / + // --------*-------- + ///////////////////// + { + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, + }, //线条10 + `, + + ` + ///////////////////// + // --------*-------- + // / \ + // ----*-------*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + }, //线条11 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*-------*---- + // \ / + // --------*-------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + }, //线条12 + `, + + ` + ///////////////////// + // ------------*---- + // / \ + // *-------*-------* + // \ / + // ----*------------ + ///////////////////// + { + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, + }, //线条13 + `, + + ` + ///////////////////// + // ----*------------ + // / \ + // *-------*-------* + // \ / + // ------------*---- + ///////////////////// + { + 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, + }, //线条14 + `, + + ` + ///////////////////// + // ----------------- + // + // ----*---*---*---- + // / \ + // *---------------* + ///////////////////// + { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + }, //线条15 + `, + + ` + ///////////////////// + // *---------------* + // \ / + // ----*---*---*---- + // + // ----------------- + ///////////////////// + { + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, + }, //线条16 + `, + + ` + ///////////////////// + // ----*---*---*---- + // / \ + // *---------------* + // + // ----------------- + ///////////////////// + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + }, //线条17 + `, + + ` + ///////////////////// + // ----------------- + // + // *---------------* + // \ / + // ----*---*---*---- + ///////////////////// + { + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, //线条18 + `, + + ` + ///////////////////// + // ------------*---* + // / + // --------*-------- + // / + // *---*------------ + ///////////////////// + { + 0, 0, 0, 1, 1, + 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, + }, //线条19 + `, + + ` + ///////////////////// + // *---*------------ + // \ + // --------*------- + // \ + // ------------*---* + ///////////////////// + { + 1, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, + }, //线条20 + `, +} + +const LINE_ROW int = 3 //行数 +const LINE_CELL int = 5 //列数 +const LINENUM int = 20 //线条数 +const ELEMENT_TOTAL = LINE_ROW * LINE_CELL + +// 所有元素对应的赔率 +var LineScore = [Element_Max][LINE_CELL]int{ + {0, 0, 0, 0, 0}, //占位 + {0, 0, 1, 5, 8000}, //1 Bonus + {0, 0, 0, 0, 0}, //2 免费旋转 + {0, 0, 5, 30, 0}, //3 Jackpot + {0, 0, 4, 20, 500}, //4 圣旨 + {0, 0, 3, 15, 200}, //5 紫钻石 + {0, 0, 2, 10, 75}, //6 戒指 + {0, 0, 0, 6, 30}, //7 跨服 +} + +var AllBetLines = []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + +// 免费次数奖励 +var FreeSpinTimesRate = [LINE_CELL]int{0, 0, 1, 5, 15} + +const BonusStepNum = 12 + +// ver 2 +var symbol1 = []int{ + 1, + 2, + 3, 3, + 4, 4, 4, + 5, 5, 5, 5, + 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, +} +var symbol2 = []int{ + 1, 1, + 2, 2, + 3, 3, + 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, +} + +// jack params +const ( + TAMQUOC_JACKPOT_InitJackpot int = iota //初始化奖池倍率 + TAMQUOC_JACKPOT_LIMITWIN_PRIZELOW //现金池不足时 最多赚取投注的多少倍 + TAMQUOC_JACKPOT_LIMITWIN_PRIZEHIGH //现金池充足时 最多赚取投注的多少倍 + TAMQUOC_JACKPOT_BonusMinTimeInterval_Low //小游戏触发的最小时间间隔 秒 + TAMQUOC_JACKPOT_BonusMinTimeInterval_High //小游戏触发的最小时间间隔 秒 +) + +// 小游戏数值倍率(-1 表示未中奖,1 表示高倍率) +var bonusNormalRate = []int{-1, -1, -1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4} + +// 小游戏高倍率 +var bonusHighRate = []int{10, 10, 15, 15, 20} +var bonusValueRateEx = [][]int{ // 下标0 权重比 1 倍率 + {200, 1}, + {200, 1}, + {200, 1}, + {200, 1}, + {210, 1}, + {210, 1}, + {210, 1}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, + {220, 4}, +} + +var bonusPictureRateEx = [][]int{ + {201, 10}, + {201, 10}, + {202, 15}, + {202, 15}, + {203, 20}, +} + +var defaultRoomModel = [][]int{ + {1, 2, 4, 4, 2, 1, 3, 4, 5, 6, 6, 7, 7, 6, 5}, + {4, 1, 2, 3, 3, 3, 4, 5, 6, 1, 7, 2, 4, 6, 2}, + {2, 2, 7, 5, 7, 5, 6, 7, 4, 4, 7, 6, 3, 4, 1}, + {1, 2, 4, 4, 2, 1, 3, 4, 5, 6, 6, 7, 7, 6, 5}, + {2, 2, 7, 5, 7, 5, 6, 7, 4, 4, 7, 6, 3, 4, 1}, + {7, 7, 6, 6, 3, 4, 6, 6, 1, 4, 4, 2, 6, 6, 4}, + {7, 7, 5, 2, 1, 3, 7, 5, 1, 5, 6, 6, 5, 7, 4}, + {4, 4, 7, 5, 1, 4, 7, 6, 7, 5, 3, 7, 3, 5, 5}, + {5, 5, 6, 7, 3, 7, 7, 3, 2, 7, 6, 4, 6, 4, 3}, + {5, 6, 7, 3, 6, 1, 3, 4, 7, 1, 5, 3, 2, 5, 4}, + {6, 5, 3, 7, 2, 2, 5, 1, 7, 4, 1, 7, 6, 7, 7}, + {5, 6, 7, 3, 6, 1, 3, 4, 7, 1, 5, 3, 2, 5, 4}, + {5, 7, 7, 5, 7, 6, 4, 6, 1, 2, 6, 5, 7, 4, 3}, + {7, 7, 6, 4, 5, 7, 7, 5, 7, 2, 7, 6, 6, 7, 2}, + {7, 7, 7, 6, 5, 6, 3, 7, 5, 7, 7, 5, 7, 2, 6}, + {7, 4, 7, 2, 6, 5, 2, 6, 4, 6, 7, 5, 6, 1, 6}, + {3, 7, 5, 5, 7, 7, 7, 7, 5, 6, 3, 7, 5, 3, 6}, + {5, 5, 6, 3, 7, 4, 7, 2, 5, 3, 7, 5, 6, 7, 7}, + {5, 6, 7, 3, 7, 5, 7, 7, 6, 2, 4, 3, 7, 7, 7}, + {7, 7, 5, 4, 2, 3, 3, 6, 7, 5, 5, 7, 4, 4, 7}, + {7, 1, 4, 7, 1, 4, 5, 7, 6, 3, 7, 6, 6, 5, 5}, + {4, 7, 7, 6, 6, 5, 6, 1, 7, 2, 7, 7, 7, 3, 4}, + {5, 7, 5, 6, 1, 5, 6, 5, 4, 6, 5, 2, 5, 1, 3}, + {7, 4, 6, 3, 7, 7, 4, 6, 5, 7, 6, 7, 5, 1, 4}, +} +var roomMode1 = [][]int{ // 第一个场 + {3, 6, 3, 5, 6, 6, 7, 7, 4, 6, 6, 7, 3, 4, 4}, + {5, 3, 5, 2, 7, 7, 5, 4, 4, 1, 7, 1, 5, 7, 7}, + {6, 1, 7, 7, 3, 7, 7, 7, 2, 4, 6, 4, 4, 3, 7}, + {7, 7, 5, 7, 6, 5, 4, 6, 6, 2, 2, 3, 7, 5, 7}, + {6, 6, 7, 4, 5, 4, 2, 6, 2, 3, 7, 3, 3, 2, 6}, + {1, 7, 3, 3, 7, 5, 4, 6, 4, 3, 7, 5, 6, 7, 4}, + {5, 6, 6, 5, 7, 6, 3, 6, 3, 5, 6, 6, 4, 7, 5}, + {5, 7, 2, 2, 5, 7, 4, 4, 5, 2, 5, 4, 7, 6, 5}, + {1, 3, 2, 4, 7, 7, 4, 7, 7, 7, 6, 7, 1, 4, 2}, + {7, 7, 6, 6, 7, 2, 7, 6, 6, 7, 6, 3, 7, 7, 5}, + {1, 7, 6, 5, 6, 7, 2, 6, 4, 5, 3, 7, 5, 5, 6}, + {7, 6, 7, 6, 6, 5, 7, 2, 7, 5, 6, 2, 6, 7, 5}, + {6, 5, 6, 7, 7, 7, 4, 7, 6, 1, 5, 5, 5, 7, 6}, + {2, 7, 1, 7, 6, 4, 6, 7, 7, 7, 7, 2, 6, 1, 5}, + {7, 4, 7, 7, 6, 3, 3, 2, 6, 4, 3, 2, 3, 4, 7}, + {7, 7, 6, 6, 5, 2, 5, 6, 5, 2, 5, 5, 7, 7, 3}, + {4, 4, 2, 5, 1, 6, 6, 6, 7, 7, 7, 7, 1, 6, 5}, + {7, 7, 4, 4, 6, 4, 5, 6, 3, 7, 5, 3, 7, 7, 7}, + {7, 4, 5, 4, 4, 7, 7, 7, 6, 5, 5, 3, 7, 7, 5}, + {4, 4, 3, 7, 5, 7, 6, 4, 5, 7, 6, 4, 5, 3, 7}, + {6, 6, 7, 7, 3, 5, 7, 1, 5, 4, 7, 6, 4, 6, 6}, + {7, 1, 7, 5, 6, 3, 7, 7, 4, 7, 6, 6, 7, 1, 4}, + {5, 6, 6, 3, 4, 7, 5, 7, 4, 6, 6, 6, 5, 6, 5}, + {4, 5, 3, 3, 6, 6, 4, 5, 3, 5, 7, 3, 7, 7, 7}, + {6, 4, 5, 6, 4, 7, 7, 1, 6, 5, 5, 2, 6, 4, 6}, + {5, 1, 5, 5, 6, 7, 7, 6, 2, 4, 6, 5, 6, 3, 1}, + {5, 1, 6, 7, 6, 3, 7, 5, 7, 7, 6, 5, 5, 4, 6}, + {7, 6, 6, 7, 5, 4, 5, 7, 6, 6, 5, 5, 1, 7, 3}, + {7, 3, 7, 5, 7, 7, 6, 6, 4, 6, 3, 3, 7, 6, 4}, + {6, 7, 2, 1, 4, 7, 3, 5, 5, 3, 3, 5, 7, 4, 5}, + {7, 4, 4, 6, 5, 5, 6, 7, 5, 4, 6, 4, 7, 5, 7}, + {7, 1, 7, 4, 1, 7, 7, 4, 7, 2, 5, 7, 5, 7, 6}, + {1, 3, 5, 6, 7, 7, 2, 7, 6, 7, 5, 4, 7, 7, 6}, + {6, 7, 7, 3, 5, 6, 6, 5, 5, 5, 7, 7, 4, 3, 6}, + {1, 7, 6, 6, 7, 1, 6, 2, 7, 6, 4, 5, 6, 1, 5}, + {6, 5, 6, 6, 3, 5, 4, 6, 7, 4, 2, 2, 7, 5, 6}, + {7, 5, 5, 7, 2, 6, 3, 4, 6, 7, 5, 5, 5, 7, 1}, + {7, 7, 5, 2, 1, 7, 6, 6, 7, 4, 3, 6, 4, 3, 6}, + {7, 6, 7, 4, 4, 6, 4, 1, 6, 5, 2, 7, 4, 7, 5}, + {4, 5, 6, 5, 6, 4, 7, 5, 3, 2, 7, 5, 6, 7, 7}, + {7, 4, 6, 6, 7, 4, 3, 3, 6, 6, 3, 7, 7, 1, 1}, + {5, 7, 4, 6, 6, 4, 7, 7, 3, 6, 5, 5, 5, 7, 3}, + {7, 7, 7, 7, 4, 5, 6, 3, 6, 4, 3, 7, 2, 5, 4}, + {5, 7, 7, 7, 7, 1, 6, 7, 3, 6, 2, 6, 4, 7, 7}, + {5, 6, 7, 6, 5, 5, 4, 7, 4, 1, 5, 1, 6, 5, 6}, + {6, 2, 5, 3, 3, 6, 1, 5, 5, 7, 5, 3, 6, 7, 7}, + {5, 7, 2, 3, 6, 7, 6, 1, 6, 6, 4, 5, 7, 7, 4}, + {4, 6, 7, 7, 7, 6, 4, 3, 6, 7, 7, 7, 7, 5, 6}, + {4, 7, 5, 6, 3, 6, 7, 3, 1, 7, 7, 3, 4, 3, 4}, + {7, 3, 1, 5, 6, 7, 7, 4, 1, 4, 6, 6, 7, 7, 5}, + {5, 6, 7, 7, 6, 7, 6, 5, 7, 5, 4, 5, 3, 5, 4}, + {6, 4, 1, 6, 6, 6, 4, 7, 3, 7, 7, 3, 6, 7, 2}, + {1, 5, 6, 7, 1, 5, 5, 6, 5, 7, 5, 3, 7, 7, 6}, + {3, 7, 7, 4, 7, 6, 7, 6, 5, 6, 7, 3, 2, 5, 6}, + {7, 6, 5, 6, 7, 5, 6, 4, 7, 6, 4, 7, 7, 4, 7}, + {7, 4, 5, 5, 1, 1, 6, 4, 3, 5, 3, 7, 6, 3, 5}, + {7, 7, 5, 5, 4, 7, 6, 3, 1, 5, 3, 7, 6, 6, 1}, + {4, 6, 6, 1, 5, 2, 6, 7, 6, 7, 5, 7, 4, 6, 6}, + {7, 6, 6, 2, 6, 6, 7, 7, 5, 7, 5, 5, 7, 7, 7}, + {7, 7, 6, 5, 5, 7, 4, 7, 5, 7, 5, 6, 3, 5, 6}, + {7, 5, 7, 5, 4, 5, 4, 3, 3, 6, 6, 7, 4, 7, 6}, + {5, 7, 7, 1, 7, 1, 6, 6, 5, 7, 6, 5, 4, 5, 7}, + {6, 7, 5, 7, 1, 6, 7, 5, 6, 5, 7, 3, 5, 5, 6}, + {7, 6, 3, 7, 6, 6, 4, 6, 5, 1, 5, 5, 7, 6, 4}, + {5, 5, 4, 5, 2, 7, 3, 7, 6, 7, 7, 4, 6, 5, 3}, + {6, 7, 5, 6, 7, 7, 6, 5, 6, 5, 2, 5, 5, 6, 3}, + {7, 6, 7, 6, 4, 4, 3, 3, 6, 4, 6, 5, 6, 5, 7}, + {4, 4, 1, 6, 6, 6, 7, 5, 7, 1, 5, 4, 6, 7, 7}, + {6, 3, 3, 5, 7, 5, 3, 5, 6, 6, 3, 4, 7, 5, 7}, + {5, 4, 3, 7, 2, 7, 5, 6, 5, 1, 6, 7, 7, 6, 1}, + {1, 5, 7, 6, 2, 3, 7, 6, 5, 4, 6, 3, 5, 7, 6}, + {5, 4, 6, 3, 5, 5, 6, 5, 3, 7, 7, 7, 7, 2, 4}, + {5, 7, 5, 3, 5, 6, 7, 3, 7, 3, 5, 6, 7, 6, 6}, + {7, 4, 4, 5, 4, 6, 4, 3, 7, 2, 1, 7, 6, 3, 6}, + {4, 3, 5, 6, 3, 7, 7, 1, 3, 4, 5, 5, 3, 7, 7}, + {6, 7, 5, 6, 5, 7, 4, 4, 5, 7, 6, 5, 7, 6, 5}, + {1, 3, 7, 3, 4, 3, 6, 7, 4, 6, 3, 5, 7, 4, 5}, + {6, 4, 2, 3, 3, 5, 6, 3, 7, 7, 6, 6, 1, 7, 6}, + {6, 6, 3, 3, 2, 7, 5, 2, 3, 3, 7, 7, 2, 5, 6}, + {7, 5, 6, 4, 7, 5, 7, 3, 1, 6, 6, 1, 7, 3, 4}, + {4, 7, 4, 3, 7, 7, 7, 3, 5, 5, 5, 6, 7, 6, 6}, + {7, 7, 7, 5, 5, 7, 5, 7, 6, 6, 5, 3, 2, 5, 7}, + {5, 7, 7, 5, 7, 7, 5, 7, 5, 4, 5, 7, 3, 5, 4}, + {5, 7, 4, 6, 6, 6, 5, 6, 5, 1, 2, 5, 2, 5, 3}, + {7, 7, 5, 7, 4, 4, 6, 6, 7, 6, 4, 3, 4, 7, 6}, + {6, 3, 6, 5, 4, 5, 7, 5, 7, 4, 5, 7, 1, 7, 3}, + {6, 7, 6, 1, 7, 6, 4, 4, 7, 6, 6, 5, 3, 6, 7}, + {5, 3, 3, 7, 7, 3, 5, 7, 5, 4, 4, 6, 7, 4, 4}, + {7, 7, 5, 6, 6, 7, 7, 6, 7, 3, 5, 7, 6, 6, 6}, + {3, 7, 6, 5, 7, 6, 2, 5, 5, 3, 4, 7, 5, 3, 5}, + {4, 3, 5, 6, 5, 1, 5, 7, 7, 7, 6, 5, 6, 4, 5}, + {4, 4, 5, 7, 7, 3, 7, 1, 1, 4, 7, 7, 4, 6, 4}, + {5, 5, 6, 7, 6, 7, 7, 3, 6, 5, 6, 6, 5, 7, 4}, + {4, 7, 4, 7, 6, 7, 2, 6, 7, 7, 5, 2, 5, 7, 7}, + {1, 7, 6, 7, 2, 4, 6, 3, 7, 2, 7, 5, 6, 6, 6}, + {5, 4, 7, 6, 5, 5, 6, 7, 6, 5, 5, 7, 6, 7, 7}, + {7, 6, 5, 3, 5, 6, 3, 2, 7, 3, 6, 6, 3, 7, 7}, + {4, 3, 2, 6, 5, 5, 4, 6, 6, 3, 2, 6, 7, 5, 5}, + {3, 6, 5, 7, 4, 7, 7, 3, 4, 6, 2, 5, 7, 5, 3}, + {1, 7, 5, 6, 5, 6, 4, 5, 2, 7, 3, 5, 3, 6, 4}, + {7, 7, 7, 6, 6, 6, 5, 6, 3, 5, 7, 6, 3, 1, 6}, + {5, 5, 2, 4, 7, 4, 7, 7, 7, 4, 1, 6, 7, 6, 6}, + {6, 5, 5, 6, 5, 7, 5, 6, 6, 6, 6, 4, 5, 6, 7}, + {7, 6, 6, 1, 4, 6, 1, 5, 4, 6, 7, 7, 4, 6, 3}, + {7, 6, 3, 4, 4, 6, 3, 6, 4, 1, 1, 7, 4, 6, 6}, + {1, 7, 3, 3, 7, 5, 4, 6, 4, 3, 7, 5, 6, 7, 4}, + {5, 7, 2, 2, 5, 7, 4, 4, 5, 2, 5, 4, 7, 6, 5}, + {1, 3, 2, 4, 7, 7, 4, 7, 7, 7, 6, 7, 1, 4, 2}, + {7, 7, 6, 6, 7, 2, 7, 6, 6, 7, 6, 3, 7, 7, 5}, + {7, 7, 6, 6, 5, 2, 5, 6, 5, 2, 5, 5, 7, 7, 3}, + {7, 4, 5, 4, 4, 7, 7, 7, 6, 5, 5, 3, 7, 7, 5}, + {4, 4, 3, 7, 5, 7, 6, 4, 5, 7, 6, 4, 5, 3, 7}, + {5, 6, 6, 3, 4, 7, 5, 7, 4, 6, 6, 6, 5, 6, 5}, + {7, 1, 7, 4, 1, 7, 7, 4, 7, 2, 5, 7, 5, 7, 6}, + {1, 3, 5, 6, 7, 7, 2, 7, 6, 7, 5, 4, 7, 7, 6}, + {5, 6, 7, 6, 5, 5, 4, 7, 4, 1, 5, 1, 6, 5, 6}, + {6, 2, 5, 3, 3, 6, 1, 5, 5, 7, 5, 3, 6, 7, 7}, + {4, 7, 5, 6, 3, 6, 7, 3, 1, 7, 7, 3, 4, 3, 4}, + {1, 5, 6, 7, 1, 5, 5, 6, 5, 7, 5, 3, 7, 7, 6}, + {7, 6, 5, 6, 7, 5, 6, 4, 7, 6, 4, 7, 7, 4, 7}, + {7, 6, 3, 7, 6, 6, 4, 6, 5, 1, 5, 5, 7, 6, 4}, + {7, 6, 7, 6, 4, 4, 3, 3, 6, 4, 6, 5, 6, 5, 7}, + {6, 3, 3, 5, 7, 5, 3, 5, 6, 6, 3, 4, 7, 5, 7}, + {6, 7, 5, 6, 5, 7, 4, 4, 5, 7, 6, 5, 7, 6, 5}, + {1, 3, 7, 3, 4, 3, 6, 7, 4, 6, 3, 5, 7, 4, 5}, + {7, 7, 7, 6, 6, 6, 5, 6, 3, 5, 7, 6, 3, 1, 6}, + {6, 5, 5, 6, 5, 7, 5, 6, 6, 6, 6, 4, 5, 6, 7}, + {4, 5, 2, 7, 7, 6, 6, 7, 6, 5, 5, 7, 7, 7, 7}, + {5, 5, 4, 7, 6, 7, 1, 7, 7, 5, 1, 7, 7, 5, 5}, + {7, 5, 6, 5, 6, 4, 7, 6, 5, 6, 5, 6, 2, 6, 4}, + {7, 4, 3, 7, 7, 6, 6, 7, 6, 5, 4, 7, 5, 6, 4}, + {5, 3, 3, 6, 4, 6, 7, 5, 6, 6, 5, 6, 3, 7, 6}, + {6, 5, 4, 3, 6, 7, 6, 2, 6, 7, 3, 4, 7, 4, 5}, + {7, 7, 6, 3, 3, 5, 6, 3, 4, 3, 6, 4, 4, 6, 4}, + {7, 2, 5, 7, 7, 7, 5, 6, 5, 3, 5, 6, 6, 1, 7}, + {7, 7, 7, 6, 6, 6, 5, 6, 3, 5, 7, 6, 3, 1, 6}, + {6, 5, 5, 6, 5, 7, 5, 6, 6, 6, 6, 4, 5, 6, 7}, + {4, 5, 2, 7, 7, 6, 6, 7, 6, 5, 5, 7, 7, 7, 7}, + {5, 5, 4, 7, 6, 7, 1, 7, 7, 5, 1, 7, 7, 5, 5}, + {7, 5, 6, 5, 6, 4, 7, 6, 5, 6, 5, 6, 2, 6, 4}, + {7, 4, 3, 7, 7, 6, 6, 7, 6, 5, 4, 7, 5, 6, 4}, + {5, 3, 3, 6, 4, 6, 7, 5, 6, 6, 5, 6, 3, 7, 6}, + {6, 5, 4, 3, 6, 7, 6, 2, 6, 7, 3, 4, 7, 4, 5}, + {7, 7, 6, 3, 3, 5, 6, 3, 4, 3, 6, 4, 4, 6, 4}, + {7, 2, 5, 7, 7, 7, 5, 6, 5, 3, 5, 6, 6, 1, 7}, +} +var roomMode2 = [][]int{ // 第二个场 + {7, 6, 6, 6, 5, 7, 5, 6, 4, 7, 7, 6, 4, 7, 7}, + {4, 3, 3, 5, 3, 7, 5, 7, 4, 4, 7, 5, 6, 6, 7}, + {5, 3, 7, 6, 2, 7, 4, 4, 6, 4, 2, 7, 1, 5, 7}, + {5, 7, 2, 7, 4, 6, 7, 1, 5, 6, 6, 3, 5, 3, 6}, + {5, 6, 7, 5, 6, 3, 7, 3, 3, 6, 5, 4, 6, 7, 2}, + {1, 4, 5, 4, 3, 7, 5, 6, 7, 3, 3, 7, 4, 2, 6}, + {5, 2, 7, 6, 4, 4, 4, 4, 1, 7, 5, 5, 6, 6, 7}, + {6, 3, 1, 5, 6, 6, 5, 7, 7, 3, 3, 6, 6, 4, 7}, + {7, 7, 2, 5, 6, 7, 2, 6, 2, 6, 4, 7, 7, 6, 2}, + {3, 2, 1, 7, 4, 7, 7, 2, 5, 7, 6, 7, 4, 5, 7}, + {3, 4, 5, 6, 6, 6, 3, 6, 5, 1, 5, 3, 4, 4, 7}, + {2, 7, 7, 5, 5, 5, 7, 7, 6, 1, 6, 5, 7, 6, 3}, + {6, 7, 5, 2, 7, 4, 3, 4, 2, 6, 4, 3, 5, 6, 6}, + {1, 7, 6, 7, 5, 7, 3, 6, 6, 6, 4, 4, 5, 1, 3}, + {3, 7, 3, 1, 5, 3, 4, 6, 5, 6, 7, 4, 5, 5, 7}, + {4, 7, 4, 7, 7, 6, 4, 6, 6, 3, 7, 6, 7, 3, 5}, + {1, 5, 6, 7, 6, 7, 4, 3, 7, 5, 7, 3, 4, 6, 4}, + {5, 7, 5, 4, 7, 7, 3, 7, 4, 5, 7, 7, 2, 2, 6}, + {6, 5, 2, 5, 3, 4, 4, 7, 3, 7, 7, 1, 3, 4, 4}, + {7, 7, 5, 5, 3, 7, 3, 4, 7, 3, 4, 6, 3, 5, 6}, + {4, 7, 6, 2, 6, 3, 3, 7, 6, 6, 4, 3, 6, 4, 6}, + {6, 4, 5, 2, 7, 1, 6, 3, 6, 6, 5, 4, 7, 7, 2}, + {5, 7, 4, 6, 5, 7, 4, 1, 7, 7, 6, 6, 3, 7, 7}, + {3, 5, 4, 5, 6, 7, 2, 6, 5, 6, 6, 1, 6, 7, 7}, + {6, 4, 7, 5, 2, 4, 7, 6, 4, 5, 3, 7, 4, 6, 3}, + {7, 6, 5, 4, 6, 7, 6, 6, 3, 4, 5, 4, 5, 2, 4}, + {7, 5, 7, 6, 6, 6, 5, 6, 4, 4, 3, 5, 7, 4, 2}, + {7, 4, 6, 6, 4, 4, 5, 1, 6, 7, 2, 1, 3, 5, 6}, + {5, 7, 6, 2, 6, 2, 3, 6, 5, 7, 1, 7, 6, 6, 7}, + {6, 7, 5, 3, 5, 7, 6, 1, 7, 3, 5, 3, 6, 7, 7}, + {3, 4, 6, 5, 7, 4, 1, 3, 7, 5, 6, 7, 6, 6, 7}, + {7, 7, 6, 6, 7, 7, 6, 6, 3, 4, 7, 7, 5, 4, 6}, + {4, 3, 6, 6, 7, 3, 1, 3, 3, 4, 4, 4, 6, 7, 5}, + {6, 3, 6, 6, 7, 2, 7, 5, 7, 6, 7, 1, 5, 7, 4}, + {5, 6, 4, 6, 2, 7, 4, 7, 7, 2, 7, 5, 4, 4, 4}, + {6, 4, 5, 3, 4, 4, 7, 5, 5, 3, 6, 3, 7, 5, 2}, + {7, 2, 7, 5, 7, 6, 4, 7, 3, 6, 6, 7, 2, 4, 1}, + {6, 5, 6, 7, 7, 6, 7, 6, 3, 5, 4, 7, 5, 2, 7}, + {7, 5, 6, 5, 6, 7, 7, 4, 6, 6, 7, 3, 5, 7, 6}, + {6, 3, 3, 6, 7, 1, 7, 7, 5, 7, 7, 6, 6, 4, 6}, + {7, 5, 6, 5, 6, 6, 6, 7, 1, 5, 1, 3, 7, 6, 7}, + {7, 5, 3, 3, 7, 5, 5, 4, 7, 7, 3, 6, 7, 7, 7}, + {6, 4, 7, 5, 7, 7, 7, 7, 6, 3, 4, 5, 6, 4, 6}, + {6, 7, 5, 6, 7, 4, 7, 7, 7, 4, 4, 7, 6, 5, 4}, + {6, 5, 6, 6, 7, 5, 5, 4, 6, 3, 7, 3, 7, 3, 2}, + {4, 7, 5, 6, 6, 7, 6, 4, 6, 4, 6, 5, 3, 6, 7}, + {7, 6, 7, 7, 4, 4, 3, 5, 4, 6, 4, 3, 5, 6, 5}, + {5, 6, 5, 6, 7, 7, 4, 4, 1, 6, 7, 5, 5, 7, 5}, + {7, 4, 5, 7, 4, 6, 7, 3, 7, 4, 4, 3, 6, 6, 2}, + {4, 2, 4, 7, 5, 7, 1, 4, 6, 3, 5, 4, 7, 7, 3}, + {6, 6, 5, 5, 6, 7, 6, 7, 5, 6, 4, 7, 5, 7, 3}, + {7, 2, 6, 3, 1, 7, 5, 4, 7, 4, 5, 4, 6, 2, 4}, + {3, 6, 7, 7, 7, 5, 4, 7, 7, 5, 4, 6, 5, 6, 7}, + {4, 7, 6, 5, 6, 6, 5, 5, 1, 7, 4, 6, 5, 7, 5}, + {7, 5, 4, 6, 7, 6, 6, 6, 7, 7, 4, 5, 7, 1, 7}, + {4, 5, 7, 6, 4, 4, 7, 3, 2, 2, 7, 5, 5, 6, 7}, + {6, 3, 2, 6, 4, 7, 3, 1, 4, 7, 5, 7, 6, 5, 5}, + {7, 6, 5, 5, 6, 7, 5, 2, 3, 3, 6, 3, 7, 2, 6}, + {7, 3, 3, 6, 7, 6, 7, 4, 4, 2, 1, 3, 6, 7, 4}, + {5, 3, 4, 1, 6, 5, 5, 7, 7, 5, 7, 4, 4, 7, 3}, + {1, 6, 6, 5, 2, 5, 5, 7, 4, 6, 3, 4, 7, 7, 5}, + {4, 7, 4, 5, 7, 3, 6, 3, 5, 4, 7, 6, 7, 5, 7}, + {5, 7, 6, 6, 6, 5, 7, 7, 1, 3, 1, 7, 7, 5, 7}, + {6, 2, 7, 7, 7, 6, 1, 5, 4, 5, 6, 6, 6, 4, 7}, + {6, 6, 1, 2, 7, 7, 7, 6, 5, 1, 5, 2, 2, 7, 4}, + {5, 7, 4, 6, 3, 2, 7, 6, 7, 3, 4, 6, 5, 7, 4}, + {3, 7, 6, 5, 3, 7, 6, 7, 2, 6, 7, 5, 7, 6, 1}, + {4, 4, 5, 2, 7, 4, 3, 7, 7, 7, 5, 6, 5, 4, 7}, + {4, 6, 5, 4, 2, 3, 5, 5, 7, 6, 1, 7, 1, 6, 4}, + {2, 5, 7, 3, 1, 5, 4, 5, 3, 4, 6, 7, 7, 6, 4}, + {4, 5, 6, 5, 3, 1, 7, 4, 5, 4, 7, 6, 7, 4, 3}, + {6, 7, 7, 4, 6, 4, 2, 3, 3, 6, 4, 3, 6, 7, 4}, + {7, 7, 2, 4, 5, 5, 2, 3, 6, 2, 6, 3, 4, 7, 4}, + {5, 4, 7, 4, 6, 5, 7, 3, 7, 5, 6, 6, 6, 3, 2}, + {5, 7, 7, 5, 4, 3, 6, 5, 7, 3, 6, 5, 5, 7, 4}, + {3, 1, 7, 5, 1, 6, 4, 6, 7, 5, 6, 3, 6, 6, 4}, + {5, 6, 4, 7, 2, 7, 5, 4, 7, 5, 6, 6, 4, 6, 7}, + {6, 7, 6, 2, 5, 2, 7, 4, 3, 4, 6, 7, 6, 4, 7}, + {7, 1, 7, 4, 4, 6, 7, 6, 3, 5, 3, 7, 5, 6, 6}, + {5, 7, 4, 5, 2, 6, 7, 6, 3, 6, 1, 4, 3, 4, 5}, + {5, 7, 7, 6, 1, 7, 2, 5, 4, 2, 6, 7, 3, 1, 6}, + {7, 7, 6, 6, 3, 4, 5, 2, 5, 6, 2, 5, 6, 4, 7}, + {7, 2, 4, 2, 6, 4, 2, 6, 6, 6, 4, 6, 7, 4, 7}, + {5, 4, 6, 7, 6, 7, 6, 2, 6, 7, 3, 5, 5, 7, 7}, + {6, 3, 5, 7, 7, 5, 7, 6, 5, 1, 7, 5, 3, 4, 5}, + {6, 7, 3, 5, 7, 5, 4, 5, 7, 1, 5, 4, 6, 6, 7}, + {7, 7, 6, 5, 5, 5, 5, 4, 7, 4, 2, 5, 6, 7, 3}, + {5, 7, 5, 6, 4, 7, 4, 7, 7, 3, 5, 3, 5, 3, 2}, + {2, 3, 7, 7, 6, 5, 5, 7, 4, 7, 1, 7, 7, 5, 5}, + {6, 2, 4, 3, 5, 7, 6, 5, 1, 3, 7, 4, 3, 6, 3}, + {6, 7, 7, 6, 5, 5, 5, 4, 7, 6, 4, 5, 6, 4, 7}, + {6, 7, 7, 7, 6, 7, 5, 7, 7, 5, 6, 5, 4, 3, 7}, + {5, 6, 1, 5, 6, 6, 6, 5, 4, 3, 7, 3, 4, 1, 5}, + {6, 6, 4, 6, 2, 7, 4, 5, 5, 7, 7, 7, 6, 1, 7}, + {6, 4, 7, 6, 1, 7, 7, 4, 5, 7, 6, 4, 5, 7, 7}, + {7, 5, 5, 7, 4, 7, 1, 6, 2, 7, 7, 5, 7, 5, 1}, + {3, 5, 5, 4, 6, 1, 4, 6, 7, 6, 7, 6, 5, 7, 4}, + {2, 7, 4, 4, 1, 6, 5, 3, 3, 7, 7, 5, 4, 5, 4}, + {6, 4, 5, 5, 7, 5, 2, 2, 7, 3, 3, 5, 6, 6, 6}, + {3, 1, 4, 6, 3, 7, 5, 6, 6, 6, 5, 5, 7, 7, 6}, + {4, 3, 3, 5, 3, 7, 5, 7, 4, 4, 7, 5, 6, 6, 7}, + {5, 3, 7, 6, 2, 7, 4, 4, 6, 4, 2, 7, 1, 5, 7}, + {5, 7, 2, 7, 4, 6, 7, 1, 5, 6, 6, 3, 5, 3, 6}, + {7, 7, 2, 5, 6, 7, 2, 6, 2, 6, 4, 7, 7, 6, 2}, + {6, 7, 5, 2, 7, 4, 3, 4, 2, 6, 4, 3, 5, 6, 6}, + {6, 6, 7, 4, 7, 2, 3, 7, 6, 5, 4, 3, 7, 1, 5}, + {5, 7, 7, 4, 3, 1, 7, 5, 7, 1, 3, 7, 6, 3, 6}, + {7, 4, 3, 7, 6, 6, 7, 3, 6, 7, 5, 4, 5, 6, 4}, + {6, 6, 4, 3, 3, 6, 4, 2, 5, 7, 6, 5, 7, 4, 2}, + {7, 3, 7, 3, 7, 4, 1, 6, 7, 5, 5, 6, 6, 1, 5}, + + {5, 4, 5, 7, 6, 7, 2, 5, 7, 7, 3, 7, 3, 7, 3}, + {5, 2, 7, 5, 4, 2, 5, 4, 5, 7, 6, 7, 5, 7, 4}, + {7, 4, 6, 7, 6, 6, 7, 6, 2, 6, 2, 6, 4, 7, 7}, + {7, 1, 6, 7, 5, 4, 3, 5, 5, 5, 6, 6, 6, 4, 6}, + {7, 1, 7, 6, 5, 6, 5, 4, 6, 6, 4, 4, 3, 6, 7}, + {7, 3, 7, 3, 4, 5, 1, 3, 4, 7, 4, 6, 7, 4, 4}, + {5, 6, 5, 7, 6, 5, 7, 7, 5, 5, 3, 6, 7, 7, 7}, + {6, 6, 6, 3, 6, 4, 7, 5, 4, 7, 4, 5, 3, 5, 3}, + {7, 7, 4, 5, 5, 5, 5, 4, 5, 7, 5, 1, 7, 5, 4}, + {4, 6, 4, 2, 6, 1, 4, 6, 3, 6, 4, 5, 5, 6, 5}, + {7, 5, 7, 5, 6, 5, 5, 1, 5, 3, 6, 7, 1, 2, 1}, + {7, 3, 6, 5, 5, 6, 5, 3, 4, 5, 7, 7, 5, 5, 5}, + {7, 7, 5, 5, 7, 5, 7, 7, 5, 7, 1, 1, 6, 6, 6}, + {7, 4, 7, 4, 7, 6, 4, 6, 4, 1, 6, 4, 5, 6, 6}, + {6, 5, 5, 6, 6, 4, 7, 5, 6, 4, 5, 6, 5, 7, 3}, + {3, 7, 6, 5, 3, 6, 2, 7, 3, 4, 2, 3, 5, 1, 7}, + {6, 6, 5, 5, 6, 5, 6, 2, 5, 6, 5, 4, 7, 7, 4}, + {4, 5, 7, 5, 6, 4, 4, 3, 7, 7, 5, 7, 2, 4, 5}, + {7, 4, 6, 2, 3, 7, 3, 5, 6, 4, 6, 7, 7, 7, 6}, + {3, 5, 7, 4, 6, 7, 7, 6, 2, 7, 5, 7, 7, 1, 6}, +} +var roomMode3 = [][]int{ // 第三个场 + {4, 7, 7, 6, 7, 2, 3, 7, 2, 6, 4, 7, 6, 6, 5}, + {5, 7, 7, 7, 5, 6, 6, 3, 7, 5, 6, 5, 7, 7, 5}, + {6, 5, 7, 5, 6, 3, 7, 4, 4, 6, 7, 7, 4, 5, 5}, + {2, 5, 6, 5, 6, 7, 5, 4, 2, 6, 4, 3, 6, 4, 7}, + {7, 2, 7, 4, 4, 3, 7, 4, 3, 7, 6, 6, 7, 4, 7}, + {6, 6, 7, 4, 7, 4, 2, 7, 7, 7, 6, 6, 5, 6, 7}, + {7, 4, 5, 5, 7, 6, 7, 6, 6, 3, 7, 4, 3, 3, 4}, + {6, 4, 2, 3, 7, 7, 3, 3, 4, 7, 6, 3, 5, 5, 1}, + {7, 4, 4, 7, 6, 3, 4, 5, 3, 7, 6, 5, 7, 6, 5}, + {7, 7, 6, 7, 3, 7, 1, 6, 7, 4, 1, 4, 7, 6, 3}, + {2, 7, 6, 4, 6, 7, 5, 5, 6, 6, 5, 7, 4, 5, 2}, + {5, 3, 3, 7, 7, 7, 4, 5, 2, 4, 6, 7, 6, 5, 7}, + {6, 3, 7, 4, 4, 1, 5, 3, 7, 5, 3, 5, 6, 5, 5}, + {3, 7, 7, 7, 4, 7, 4, 3, 1, 2, 7, 3, 6, 1, 6}, + {7, 6, 1, 1, 7, 7, 1, 6, 6, 6, 7, 3, 6, 7, 4}, + {3, 7, 6, 5, 3, 7, 7, 5, 2, 4, 4, 3, 4, 7, 5}, + {7, 5, 6, 2, 5, 5, 6, 4, 7, 7, 6, 6, 2, 5, 5}, + {6, 5, 6, 1, 7, 7, 7, 6, 6, 6, 7, 4, 4, 7, 7}, + {7, 4, 7, 2, 6, 6, 4, 3, 5, 7, 3, 6, 1, 6, 7}, + {5, 4, 6, 3, 7, 2, 7, 6, 6, 5, 6, 7, 2, 7, 4}, + {5, 1, 7, 7, 5, 4, 6, 3, 6, 7, 6, 7, 5, 6, 2}, + {6, 7, 3, 3, 4, 6, 1, 6, 7, 7, 6, 7, 3, 6, 7}, + {7, 5, 1, 7, 7, 4, 5, 6, 6, 3, 7, 4, 6, 7, 6}, + {5, 3, 6, 4, 6, 7, 5, 5, 7, 1, 4, 6, 6, 1, 7}, + {5, 6, 5, 2, 4, 7, 1, 6, 6, 4, 4, 6, 7, 6, 4}, + {6, 7, 5, 6, 5, 1, 7, 7, 1, 2, 5, 4, 7, 5, 7}, + {4, 3, 6, 1, 6, 6, 3, 5, 6, 5, 7, 5, 3, 6, 5}, + {5, 7, 6, 6, 6, 7, 7, 4, 6, 5, 5, 1, 7, 3, 3}, + {2, 5, 4, 7, 4, 4, 6, 7, 2, 4, 4, 7, 6, 7, 3}, + {6, 2, 5, 6, 7, 6, 1, 3, 2, 6, 4, 5, 7, 6, 7}, + {5, 6, 7, 5, 6, 7, 3, 6, 1, 5, 7, 6, 5, 4, 5}, + {4, 2, 7, 4, 3, 3, 3, 7, 7, 4, 6, 6, 4, 7, 4}, + {4, 2, 7, 7, 5, 7, 2, 4, 5, 7, 6, 5, 5, 3, 6}, + {3, 6, 4, 3, 1, 7, 6, 7, 7, 6, 7, 6, 7, 6, 7}, + {6, 6, 7, 7, 7, 2, 6, 7, 5, 4, 6, 4, 4, 6, 3}, + {5, 7, 4, 6, 6, 2, 4, 1, 2, 7, 6, 1, 4, 4, 4}, + {6, 5, 7, 7, 5, 4, 6, 6, 4, 7, 7, 6, 7, 2, 5}, + {4, 5, 7, 6, 4, 7, 5, 7, 3, 3, 6, 1, 4, 6, 7}, + {4, 3, 1, 5, 6, 7, 7, 7, 5, 3, 7, 6, 7, 3, 6}, + {6, 7, 5, 5, 3, 3, 4, 7, 6, 6, 3, 1, 6, 4, 7}, + {4, 1, 6, 6, 7, 7, 6, 5, 7, 7, 7, 4, 7, 6, 7}, + {6, 5, 7, 6, 3, 7, 4, 5, 5, 7, 7, 7, 4, 4, 5}, + {3, 4, 5, 3, 3, 7, 5, 6, 4, 3, 2, 5, 5, 2, 6}, + {7, 1, 6, 7, 5, 6, 6, 7, 7, 3, 1, 2, 7, 7, 6}, + {6, 4, 5, 4, 1, 6, 1, 6, 7, 7, 6, 6, 7, 7, 5}, + {5, 7, 3, 3, 7, 6, 7, 3, 7, 5, 7, 1, 7, 7, 4}, + {3, 6, 7, 4, 6, 4, 3, 6, 1, 3, 4, 6, 7, 7, 7}, + {4, 7, 3, 6, 7, 6, 7, 6, 7, 5, 4, 5, 1, 6, 6}, + {5, 3, 4, 4, 5, 6, 4, 7, 5, 6, 7, 5, 4, 3, 5}, + {7, 7, 4, 5, 5, 7, 1, 2, 6, 2, 6, 6, 5, 4, 6}, + {6, 7, 7, 5, 6, 1, 7, 7, 6, 7, 4, 2, 5, 4, 6}, + {2, 4, 5, 6, 5, 5, 3, 6, 4, 3, 7, 6, 4, 3, 6}, + {6, 5, 7, 6, 5, 1, 6, 3, 4, 3, 5, 5, 7, 3, 7}, + {6, 3, 7, 6, 4, 7, 5, 4, 7, 7, 5, 7, 3, 6, 6}, + {5, 5, 7, 6, 7, 7, 6, 7, 5, 7, 3, 6, 4, 7, 1}, + {7, 6, 5, 5, 4, 2, 2, 6, 4, 6, 7, 7, 5, 4, 4}, + {1, 2, 5, 5, 7, 6, 7, 2, 1, 6, 6, 6, 5, 7, 4}, + {7, 7, 7, 6, 4, 7, 6, 7, 4, 3, 6, 5, 7, 5, 5}, + {5, 7, 4, 7, 7, 6, 7, 6, 5, 6, 6, 7, 3, 4, 3}, + {6, 7, 4, 6, 3, 4, 6, 5, 7, 3, 5, 5, 7, 4, 2}, + {4, 4, 5, 4, 7, 1, 6, 7, 7, 6, 7, 1, 5, 7, 5}, + {7, 4, 5, 6, 2, 1, 7, 4, 4, 4, 7, 7, 6, 7, 5}, + {6, 7, 7, 6, 6, 4, 5, 4, 6, 5, 6, 7, 7, 3, 7}, + {5, 7, 6, 6, 2, 5, 6, 5, 7, 7, 6, 3, 7, 5, 7}, + {6, 3, 7, 6, 6, 2, 1, 7, 7, 4, 7, 6, 7, 5, 2}, + {4, 5, 3, 7, 7, 4, 3, 7, 5, 7, 7, 6, 7, 5, 2}, + {7, 4, 1, 6, 6, 6, 7, 2, 4, 6, 6, 7, 5, 7, 6}, + {3, 6, 6, 6, 7, 7, 3, 7, 6, 5, 3, 4, 7, 7, 6}, + {2, 7, 6, 1, 4, 6, 3, 7, 4, 6, 6, 7, 7, 4, 5}, + {7, 1, 5, 7, 4, 4, 5, 5, 7, 6, 3, 6, 6, 1, 3}, + {6, 4, 5, 7, 7, 6, 6, 6, 3, 5, 4, 1, 6, 7, 5}, + {5, 6, 7, 4, 1, 7, 5, 6, 7, 1, 7, 5, 6, 6, 4}, + {5, 7, 6, 4, 4, 1, 5, 6, 4, 7, 7, 4, 6, 2, 5}, + {5, 4, 5, 4, 6, 6, 4, 7, 6, 7, 6, 6, 2, 7, 3}, + {3, 7, 4, 3, 3, 5, 7, 5, 7, 4, 7, 7, 7, 6, 6}, + {4, 7, 3, 2, 4, 7, 2, 6, 6, 7, 5, 5, 3, 7, 2}, + {7, 3, 7, 4, 3, 7, 4, 3, 7, 3, 6, 4, 6, 4, 5}, + {4, 4, 4, 7, 6, 5, 3, 1, 7, 6, 4, 7, 7, 5, 6}, + {5, 6, 7, 6, 1, 5, 7, 6, 7, 4, 5, 5, 2, 7, 6}, + {5, 3, 4, 7, 7, 7, 6, 3, 7, 7, 5, 5, 5, 4, 3}, + {6, 4, 3, 4, 7, 7, 3, 1, 7, 7, 3, 6, 5, 2, 7}, + {4, 7, 2, 4, 5, 3, 6, 7, 1, 7, 5, 5, 6, 6, 5}, + {5, 7, 6, 3, 5, 5, 7, 7, 5, 7, 5, 3, 3, 4, 4}, + {1, 4, 3, 6, 4, 7, 7, 7, 4, 7, 3, 5, 6, 4, 5}, + {6, 4, 5, 4, 7, 3, 6, 3, 5, 7, 5, 7, 7, 6, 1}, + {4, 4, 7, 7, 6, 1, 3, 7, 3, 5, 7, 3, 4, 6, 4}, + {5, 7, 4, 6, 5, 5, 2, 6, 4, 7, 7, 7, 6, 1, 4}, + {3, 4, 5, 5, 7, 6, 5, 2, 7, 7, 5, 5, 4, 4, 7}, + {3, 6, 5, 6, 6, 3, 7, 4, 6, 5, 5, 6, 5, 6, 7}, + {7, 6, 5, 7, 6, 6, 2, 1, 3, 5, 7, 7, 5, 5, 4}, + {6, 7, 2, 5, 7, 7, 4, 7, 5, 6, 7, 7, 3, 3, 6}, + {5, 7, 5, 7, 7, 7, 7, 2, 7, 6, 6, 5, 5, 6, 6}, + {7, 6, 7, 6, 4, 1, 3, 7, 6, 2, 7, 6, 3, 6, 6}, + {3, 1, 3, 7, 7, 4, 6, 6, 6, 5, 3, 7, 3, 6, 4}, + {5, 7, 3, 7, 7, 5, 5, 3, 7, 7, 3, 5, 7, 4, 5}, + {5, 5, 1, 7, 6, 2, 7, 7, 6, 7, 6, 6, 7, 7, 6}, + {6, 7, 7, 5, 3, 3, 5, 5, 5, 1, 4, 1, 3, 6, 4}, + {7, 6, 7, 5, 4, 4, 7, 5, 5, 7, 6, 7, 5, 7, 6}, + {4, 7, 5, 6, 3, 5, 4, 7, 3, 7, 7, 6, 4, 5, 6}, + {1, 7, 7, 5, 6, 5, 7, 2, 7, 4, 5, 6, 6, 7, 7}, + {4, 7, 7, 6, 7, 2, 3, 7, 2, 6, 4, 7, 6, 6, 5}, + {5, 7, 7, 7, 5, 6, 6, 3, 7, 5, 6, 5, 7, 7, 5}, + {6, 5, 7, 5, 6, 3, 7, 4, 4, 6, 7, 7, 4, 5, 5}, + {2, 5, 6, 5, 6, 7, 5, 4, 2, 6, 4, 3, 6, 4, 7}, + {7, 4, 4, 7, 6, 3, 4, 5, 3, 7, 6, 5, 7, 6, 5}, + {7, 7, 6, 7, 3, 7, 1, 6, 7, 4, 1, 4, 7, 6, 3}, + {5, 3, 3, 7, 7, 7, 4, 5, 2, 4, 6, 7, 6, 5, 7}, + {3, 7, 7, 7, 4, 7, 4, 3, 1, 2, 7, 3, 6, 1, 6}, + {3, 7, 6, 5, 3, 7, 7, 5, 2, 4, 4, 3, 4, 7, 5}, + {6, 5, 6, 1, 7, 7, 7, 6, 6, 6, 7, 4, 4, 7, 7}, + {7, 4, 7, 2, 6, 6, 4, 3, 5, 7, 3, 6, 1, 6, 7}, + {5, 4, 6, 3, 7, 2, 7, 6, 6, 5, 6, 7, 2, 7, 4}, + {6, 7, 3, 3, 4, 6, 1, 6, 7, 7, 6, 7, 3, 6, 7}, + {7, 5, 1, 7, 7, 4, 5, 6, 6, 3, 7, 4, 6, 7, 6}, + {5, 3, 6, 4, 6, 7, 5, 5, 7, 1, 4, 6, 6, 1, 7}, + {5, 6, 5, 2, 4, 7, 1, 6, 6, 4, 4, 6, 7, 6, 4}, + {6, 7, 5, 6, 5, 1, 7, 7, 1, 2, 5, 4, 7, 5, 7}, + {5, 7, 6, 6, 6, 7, 7, 4, 6, 5, 5, 1, 7, 3, 3}, + {6, 6, 7, 7, 7, 2, 6, 7, 5, 4, 6, 4, 4, 6, 3}, + {6, 5, 7, 7, 5, 4, 6, 6, 4, 7, 7, 6, 7, 2, 5}, + {4, 5, 7, 6, 4, 7, 5, 7, 3, 3, 6, 1, 4, 6, 7}, + {4, 3, 1, 5, 6, 7, 7, 7, 5, 3, 7, 6, 7, 3, 6}, + {6, 7, 5, 5, 3, 3, 4, 7, 6, 6, 3, 1, 6, 4, 7}, + {4, 1, 6, 6, 7, 7, 6, 5, 7, 7, 7, 4, 7, 6, 7}, + {6, 5, 7, 6, 3, 7, 4, 5, 5, 7, 7, 7, 4, 4, 5}, + {7, 1, 6, 7, 5, 6, 6, 7, 7, 3, 1, 2, 7, 7, 6}, + {6, 4, 5, 4, 1, 6, 1, 6, 7, 7, 6, 6, 7, 7, 5}, + {5, 7, 3, 3, 7, 6, 7, 3, 7, 5, 7, 1, 7, 7, 4}, + {3, 6, 7, 4, 6, 4, 3, 6, 1, 3, 4, 6, 7, 7, 7}, + {6, 7, 7, 5, 6, 1, 7, 7, 6, 7, 4, 2, 5, 4, 6}, + {2, 4, 5, 6, 5, 5, 3, 6, 4, 3, 7, 6, 4, 3, 6}, + {5, 5, 7, 6, 7, 7, 6, 7, 5, 7, 3, 6, 4, 7, 1}, + {7, 6, 5, 5, 4, 2, 2, 6, 4, 6, 7, 7, 5, 4, 4}, + {1, 2, 5, 5, 7, 6, 7, 2, 1, 6, 6, 6, 5, 7, 4}, + {7, 7, 7, 6, 4, 7, 6, 7, 4, 3, 6, 5, 7, 5, 5}, + {5, 7, 4, 7, 7, 6, 7, 6, 5, 6, 6, 7, 3, 4, 3}, + {6, 7, 4, 6, 3, 4, 6, 5, 7, 3, 5, 5, 7, 4, 2}, + {4, 5, 3, 7, 7, 4, 3, 7, 5, 7, 7, 6, 7, 5, 2}, + {2, 7, 6, 1, 4, 6, 3, 7, 4, 6, 6, 7, 7, 4, 5}, + {7, 1, 5, 7, 4, 4, 5, 5, 7, 6, 3, 6, 6, 1, 3}, + {5, 6, 7, 4, 1, 7, 5, 6, 7, 1, 7, 5, 6, 6, 4}, + {5, 7, 6, 4, 4, 1, 5, 6, 4, 7, 7, 4, 6, 2, 5}, + {5, 4, 5, 4, 6, 6, 4, 7, 6, 7, 6, 6, 2, 7, 3}, + {4, 7, 3, 2, 4, 7, 2, 6, 6, 7, 5, 5, 3, 7, 2}, + {4, 4, 4, 7, 6, 5, 3, 1, 7, 6, 4, 7, 7, 5, 6}, + {5, 6, 7, 6, 1, 5, 7, 6, 7, 4, 5, 5, 2, 7, 6}, + {6, 4, 3, 4, 7, 7, 3, 1, 7, 7, 3, 6, 5, 2, 7}, + {5, 7, 6, 3, 5, 5, 7, 7, 5, 7, 5, 3, 3, 4, 4}, + {1, 4, 3, 6, 4, 7, 7, 7, 4, 7, 3, 5, 6, 4, 5}, + {6, 4, 5, 4, 7, 3, 6, 3, 5, 7, 5, 7, 7, 6, 1}, + {7, 5, 1, 7, 7, 4, 5, 6, 6, 3, 7, 4, 6, 7, 6}, + {5, 7, 6, 6, 6, 7, 7, 4, 6, 5, 5, 1, 7, 3, 3}, + {6, 5, 7, 7, 5, 4, 6, 6, 4, 7, 7, 6, 7, 2, 5}, + {7, 6, 5, 5, 4, 2, 2, 6, 4, 6, 7, 7, 5, 4, 4}, + {7, 1, 5, 7, 4, 4, 5, 5, 7, 6, 3, 6, 6, 1, 3}, + {5, 4, 5, 4, 6, 6, 4, 7, 6, 7, 6, 6, 2, 7, 3}, + {6, 7, 4, 5, 6, 6, 7, 4, 7, 3, 6, 7, 7, 6, 7}, + {6, 6, 5, 3, 5, 6, 3, 7, 4, 3, 7, 4, 1, 3, 6}, + {5, 6, 6, 1, 4, 7, 3, 5, 4, 2, 1, 6, 3, 6, 7}, + {1, 7, 2, 7, 6, 4, 7, 6, 4, 2, 6, 5, 1, 7, 5}, + {4, 7, 6, 3, 6, 3, 4, 6, 6, 5, 4, 7, 5, 7, 7}, + {4, 6, 6, 3, 1, 7, 7, 1, 7, 6, 3, 6, 4, 4, 5}, + {7, 6, 6, 5, 2, 6, 5, 2, 2, 5, 6, 7, 4, 1, 1}, + {6, 3, 3, 2, 6, 6, 7, 5, 4, 4, 7, 6, 7, 4, 5}, + {4, 5, 5, 6, 6, 4, 6, 3, 4, 7, 5, 6, 7, 7, 7}, + {7, 6, 7, 7, 6, 6, 7, 7, 2, 3, 6, 5, 5, 6, 7}, + {3, 4, 7, 6, 4, 7, 7, 1, 3, 1, 6, 6, 5, 7, 3}, + {1, 7, 4, 7, 4, 3, 6, 6, 5, 4, 3, 6, 5, 6, 7}, + {7, 3, 4, 7, 6, 7, 5, 6, 4, 3, 5, 6, 6, 7, 1}, + {5, 7, 6, 7, 6, 7, 7, 4, 7, 4, 5, 7, 4, 6, 5}, + {3, 7, 5, 7, 7, 4, 6, 5, 1, 1, 6, 5, 6, 7, 7}, + {2, 4, 7, 6, 3, 7, 6, 7, 5, 3, 3, 7, 5, 5, 6}, + {7, 2, 5, 4, 5, 1, 4, 3, 7, 6, 7, 2, 7, 2, 6}, + {5, 6, 5, 3, 4, 4, 7, 7, 5, 4, 7, 6, 6, 7, 1}, + {3, 6, 7, 4, 7, 3, 6, 4, 7, 5, 7, 5, 4, 7, 6}, + {6, 7, 3, 7, 4, 7, 5, 4, 5, 4, 7, 6, 6, 6, 7}, + {4, 4, 4, 5, 6, 6, 6, 3, 4, 4, 6, 5, 7, 6, 3}, + {6, 5, 7, 5, 5, 2, 5, 4, 4, 5, 6, 7, 6, 7, 6}, + {5, 6, 4, 6, 6, 1, 7, 7, 3, 5, 6, 4, 4, 7, 6}, + {5, 7, 6, 6, 4, 7, 7, 5, 6, 7, 5, 2, 7, 6, 7}, + {6, 7, 3, 7, 4, 7, 5, 4, 5, 4, 7, 6, 6, 6, 7}, + {4, 4, 4, 5, 6, 6, 6, 3, 4, 4, 6, 5, 7, 6, 3}, + {7, 2, 7, 4, 4, 3, 7, 4, 3, 7, 6, 6, 7, 4, 7}, + {6, 6, 7, 4, 7, 4, 2, 7, 7, 7, 6, 6, 5, 6, 7}, + {7, 4, 5, 5, 7, 6, 7, 6, 6, 3, 7, 4, 3, 3, 4}, + {6, 4, 2, 3, 7, 7, 3, 3, 4, 7, 6, 3, 5, 5, 1}, + {2, 7, 6, 4, 6, 7, 5, 5, 6, 6, 5, 7, 4, 5, 2}, + {5, 7, 6, 6, 4, 7, 7, 5, 6, 7, 5, 2, 7, 6, 7}, + {6, 3, 7, 4, 4, 1, 5, 3, 7, 5, 3, 5, 6, 5, 5}, + {7, 6, 1, 1, 7, 7, 1, 6, 6, 6, 7, 3, 6, 7, 4}, + {5, 4, 4, 7, 7, 5, 4, 5, 7, 7, 5, 5, 6, 6, 6}, + {7, 7, 7, 6, 6, 5, 3, 5, 6, 6, 5, 7, 7, 7, 5}, + {7, 5, 6, 2, 5, 5, 6, 4, 7, 7, 6, 6, 2, 5, 5}, + {5, 1, 7, 7, 5, 4, 6, 3, 6, 7, 6, 7, 5, 6, 2}, + {3, 6, 4, 2, 2, 5, 4, 6, 5, 6, 1, 7, 5, 5, 5}, + {4, 3, 6, 1, 6, 6, 3, 5, 6, 5, 7, 5, 3, 6, 5}, + {2, 5, 4, 7, 4, 4, 6, 7, 2, 4, 4, 7, 6, 7, 3}, + {6, 2, 5, 6, 7, 6, 1, 3, 2, 6, 4, 5, 7, 6, 7}, + {5, 7, 3, 6, 2, 3, 2, 6, 3, 7, 3, 5, 6, 7, 6}, + {5, 6, 7, 5, 6, 7, 3, 6, 1, 5, 7, 6, 5, 4, 5}, + {6, 5, 7, 6, 6, 5, 6, 5, 4, 6, 5, 3, 4, 5, 7}, + {7, 5, 5, 1, 1, 5, 7, 6, 5, 7, 5, 6, 7, 1, 5}, + {7, 5, 2, 5, 4, 4, 7, 6, 2, 3, 5, 5, 6, 7, 5}, + {5, 4, 6, 5, 7, 5, 6, 7, 6, 7, 1, 5, 2, 5, 5}, + {7, 7, 2, 5, 7, 6, 7, 7, 3, 4, 6, 7, 7, 7, 6}, + {6, 5, 7, 6, 6, 5, 6, 5, 4, 6, 5, 3, 4, 5, 7}, + {7, 5, 5, 1, 1, 5, 7, 6, 5, 7, 5, 6, 7, 1, 5}, + {7, 5, 2, 5, 4, 4, 7, 6, 2, 3, 5, 5, 6, 7, 5}, + {5, 4, 6, 5, 7, 5, 6, 7, 6, 7, 1, 5, 2, 5, 5}, + {7, 7, 2, 5, 7, 6, 7, 7, 3, 4, 6, 7, 7, 7, 6}, + {6, 5, 7, 6, 6, 5, 6, 5, 4, 6, 5, 3, 4, 5, 7}, + {7, 5, 5, 1, 1, 5, 7, 6, 5, 7, 5, 6, 7, 1, 5}, + {7, 5, 2, 5, 4, 4, 7, 6, 2, 3, 5, 5, 6, 7, 5}, + {5, 4, 6, 5, 7, 5, 6, 7, 6, 7, 1, 5, 2, 5, 5}, + {7, 7, 2, 5, 7, 6, 7, 7, 3, 4, 6, 7, 7, 7, 6}, +} + +var roomMode4 = [][]int{ // 第四个场 + {7, 4, 6, 3, 3, 6, 6, 6, 3, 7, 6, 2, 4, 6, 2}, + {7, 5, 7, 7, 6, 7, 3, 5, 5, 7, 6, 4, 7, 2, 7}, + {6, 7, 5, 4, 4, 6, 3, 6, 4, 3, 5, 5, 7, 6, 5}, + {6, 7, 1, 3, 6, 7, 4, 4, 4, 7, 7, 2, 1, 4, 5}, + {7, 1, 4, 4, 7, 2, 4, 6, 4, 6, 6, 7, 1, 7, 7}, + {6, 7, 4, 5, 2, 4, 7, 7, 6, 7, 5, 4, 7, 7, 7}, + {5, 5, 7, 7, 1, 3, 7, 7, 5, 4, 7, 5, 2, 6, 7}, + {2, 6, 7, 6, 3, 5, 3, 6, 7, 7, 7, 4, 7, 7, 7}, + {5, 3, 4, 4, 1, 4, 7, 6, 6, 6, 4, 7, 7, 7, 4}, + {2, 6, 5, 7, 7, 6, 5, 3, 5, 7, 7, 3, 6, 6, 7}, + {7, 4, 2, 4, 2, 5, 7, 6, 5, 4, 5, 1, 3, 3, 5}, + {7, 7, 6, 4, 2, 6, 4, 5, 6, 3, 6, 7, 6, 7, 7}, + {6, 5, 3, 6, 7, 6, 2, 6, 7, 5, 5, 6, 7, 1, 7}, + {5, 6, 6, 7, 4, 3, 4, 7, 6, 7, 6, 7, 4, 6, 3}, + {6, 7, 7, 7, 5, 3, 6, 6, 3, 7, 1, 4, 5, 4, 6}, + {6, 6, 6, 1, 5, 3, 5, 7, 6, 3, 7, 3, 6, 5, 5}, + {4, 2, 7, 5, 5, 7, 7, 5, 5, 1, 6, 7, 7, 7, 6}, + {4, 4, 3, 2, 3, 5, 5, 1, 7, 6, 5, 6, 5, 5, 4}, + {4, 6, 5, 4, 5, 3, 7, 4, 6, 3, 4, 6, 7, 5, 6}, + {6, 6, 7, 7, 6, 5, 5, 7, 1, 7, 5, 7, 6, 3, 7}, + {4, 7, 6, 5, 7, 5, 6, 2, 4, 5, 5, 3, 7, 6, 3}, + {6, 7, 6, 6, 5, 7, 6, 4, 7, 7, 3, 3, 4, 7, 7}, + {5, 5, 5, 2, 7, 6, 5, 4, 7, 7, 5, 5, 7, 7, 7}, + {6, 5, 7, 6, 7, 7, 4, 5, 4, 5, 5, 7, 6, 7, 7}, + {4, 7, 3, 7, 6, 1, 5, 6, 7, 5, 4, 6, 4, 6, 5}, + {6, 6, 1, 7, 5, 7, 3, 7, 5, 7, 4, 5, 3, 6, 7}, + {7, 6, 7, 6, 3, 4, 3, 6, 7, 6, 7, 7, 2, 5, 5}, + {4, 5, 5, 3, 4, 6, 7, 7, 6, 5, 7, 6, 7, 3, 7}, + {3, 6, 6, 5, 4, 7, 6, 7, 4, 7, 3, 1, 3, 7, 6}, + {3, 5, 2, 7, 3, 2, 7, 7, 6, 6, 7, 7, 7, 5, 4}, + {3, 4, 7, 3, 5, 6, 7, 6, 6, 4, 7, 6, 6, 1, 4}, + {7, 5, 4, 6, 5, 5, 3, 7, 4, 5, 7, 6, 7, 4, 6}, + {7, 7, 7, 7, 6, 6, 4, 5, 7, 4, 1, 5, 5, 7, 3}, + {1, 6, 6, 3, 3, 1, 7, 6, 7, 5, 5, 5, 5, 7, 7}, + {7, 1, 7, 3, 6, 7, 5, 5, 5, 3, 3, 7, 6, 5, 4}, + {6, 4, 5, 7, 6, 7, 7, 6, 5, 3, 3, 6, 5, 5, 4}, + {4, 4, 6, 5, 7, 2, 7, 5, 7, 6, 5, 3, 7, 7, 3}, + {1, 2, 7, 4, 7, 3, 7, 6, 5, 3, 2, 5, 7, 3, 1}, + {6, 5, 6, 4, 6, 7, 5, 2, 2, 4, 6, 3, 4, 3, 1}, + {5, 6, 5, 7, 6, 6, 7, 6, 4, 7, 6, 7, 5, 1, 7}, + {7, 7, 4, 6, 7, 5, 5, 4, 6, 7, 6, 5, 1, 5, 6}, + {7, 5, 7, 6, 7, 7, 6, 7, 4, 6, 5, 7, 7, 4, 7}, + {4, 5, 7, 2, 4, 6, 7, 6, 5, 4, 7, 7, 4, 5, 7}, + {6, 3, 7, 5, 4, 7, 6, 5, 6, 2, 5, 6, 1, 2, 4}, + {5, 1, 7, 4, 6, 4, 1, 4, 4, 3, 7, 6, 5, 7, 7}, + {7, 7, 5, 7, 4, 5, 4, 2, 6, 6, 4, 5, 3, 5, 7}, + {3, 7, 7, 7, 7, 5, 6, 5, 4, 6, 4, 7, 5, 6, 6}, + {5, 1, 7, 6, 3, 4, 7, 1, 1, 6, 5, 6, 6, 7, 7}, + {5, 3, 4, 4, 3, 3, 3, 7, 5, 5, 3, 7, 5, 2, 7}, + {1, 5, 2, 4, 4, 4, 7, 6, 4, 5, 1, 4, 6, 7, 6}, + {5, 7, 7, 6, 5, 2, 1, 5, 7, 7, 3, 2, 3, 7, 5}, + {5, 7, 4, 6, 7, 7, 4, 7, 3, 6, 6, 7, 6, 7, 5}, + {7, 6, 7, 5, 6, 4, 5, 4, 6, 6, 3, 5, 6, 6, 3}, + {2, 7, 5, 1, 7, 1, 5, 6, 2, 5, 6, 7, 7, 7, 7}, + {7, 6, 5, 7, 7, 6, 3, 3, 4, 3, 2, 2, 5, 3, 7}, + {1, 7, 7, 7, 4, 7, 5, 2, 7, 1, 3, 2, 3, 4, 2}, + {5, 7, 7, 1, 4, 1, 3, 5, 5, 6, 7, 7, 7, 5, 4}, + {4, 4, 6, 7, 7, 6, 6, 1, 7, 7, 4, 7, 4, 6, 5}, + {6, 4, 4, 1, 7, 5, 7, 7, 7, 3, 4, 4, 5, 3, 3}, + {7, 5, 7, 6, 6, 3, 5, 6, 6, 7, 5, 5, 2, 2, 6}, + {4, 7, 7, 7, 7, 5, 6, 6, 7, 6, 4, 3, 3, 6, 4}, + {7, 7, 4, 5, 6, 6, 6, 5, 4, 6, 6, 7, 4, 6, 7}, + {7, 6, 3, 7, 5, 4, 7, 7, 7, 6, 5, 6, 6, 4, 2}, + {3, 6, 7, 7, 6, 3, 6, 7, 7, 3, 5, 7, 7, 3, 7}, + {1, 7, 7, 1, 6, 6, 7, 5, 5, 5, 5, 3, 4, 7, 7}, + {2, 6, 5, 7, 1, 7, 6, 6, 5, 7, 1, 4, 7, 2, 4}, + {4, 5, 7, 3, 4, 7, 4, 7, 6, 1, 1, 7, 5, 2, 7}, + {6, 6, 5, 7, 4, 7, 6, 1, 4, 3, 5, 6, 6, 1, 6}, + {6, 6, 5, 3, 6, 7, 4, 5, 4, 5, 5, 6, 6, 2, 3}, + {1, 7, 7, 6, 7, 7, 6, 6, 6, 5, 2, 6, 7, 4, 4}, + {1, 7, 5, 6, 3, 7, 6, 3, 5, 2, 6, 5, 6, 3, 5}, + {6, 1, 3, 5, 7, 7, 7, 7, 6, 5, 6, 2, 5, 4, 7}, + {3, 6, 4, 5, 5, 4, 5, 6, 6, 7, 5, 4, 6, 5, 7}, + {6, 5, 7, 2, 6, 5, 5, 3, 2, 7, 3, 7, 7, 3, 5}, + {7, 3, 6, 1, 6, 5, 7, 5, 7, 6, 4, 5, 4, 6, 2}, + {5, 7, 2, 7, 6, 4, 2, 2, 6, 4, 7, 6, 6, 5, 4}, + {4, 3, 3, 6, 7, 7, 3, 7, 5, 5, 2, 4, 5, 6, 4}, + {4, 5, 7, 1, 6, 6, 6, 2, 4, 7, 5, 4, 6, 2, 1}, + {4, 6, 6, 3, 6, 7, 6, 4, 7, 1, 7, 5, 4, 7, 5}, + {7, 5, 6, 5, 6, 7, 4, 6, 7, 6, 6, 1, 3, 5, 4}, + {5, 6, 5, 4, 2, 7, 2, 7, 5, 6, 7, 3, 7, 5, 4}, + {5, 7, 4, 7, 3, 6, 7, 6, 3, 7, 7, 4, 5, 4, 1}, + {7, 7, 4, 4, 5, 1, 3, 5, 3, 6, 5, 4, 2, 6, 7}, + {6, 7, 2, 5, 3, 3, 7, 6, 7, 5, 7, 7, 5, 1, 6}, + {6, 3, 5, 7, 4, 7, 7, 6, 6, 5, 5, 7, 3, 6, 7}, + {4, 5, 3, 7, 2, 6, 5, 3, 6, 5, 7, 4, 6, 6, 2}, + {3, 1, 7, 6, 4, 5, 6, 6, 4, 6, 4, 7, 3, 5, 3}, + {1, 7, 6, 7, 6, 2, 5, 4, 3, 3, 6, 5, 6, 6, 2}, + {6, 5, 5, 4, 7, 4, 6, 3, 6, 7, 7, 7, 1, 4, 7}, + {6, 5, 7, 6, 6, 3, 4, 7, 7, 7, 3, 7, 7, 4, 6}, + {2, 6, 5, 4, 7, 6, 6, 3, 4, 7, 6, 7, 7, 5, 5}, + {6, 5, 7, 7, 5, 4, 5, 4, 6, 7, 5, 3, 7, 5, 7}, + {3, 5, 6, 7, 4, 4, 6, 6, 4, 1, 7, 7, 5, 4, 7}, + {7, 7, 5, 6, 5, 7, 3, 3, 7, 5, 7, 7, 3, 7, 6}, + {7, 1, 4, 5, 3, 4, 1, 7, 6, 7, 4, 5, 7, 1, 2}, + {7, 7, 7, 7, 4, 6, 7, 7, 2, 4, 4, 5, 7, 4, 7}, + {7, 1, 7, 3, 7, 6, 7, 7, 5, 5, 7, 5, 5, 4, 7}, + {7, 6, 6, 4, 4, 6, 4, 7, 3, 6, 7, 5, 5, 5, 7}, + {5, 6, 6, 5, 5, 6, 4, 6, 5, 3, 7, 6, 6, 1, 5}, + {1, 7, 7, 1, 6, 7, 7, 7, 7, 4, 6, 5, 6, 5, 7}, + {7, 7, 7, 7, 4, 6, 7, 7, 2, 4, 4, 5, 7, 4, 7}, + {7, 1, 7, 3, 7, 6, 7, 7, 5, 5, 7, 5, 5, 4, 7}, + {7, 6, 6, 4, 4, 6, 4, 7, 3, 6, 7, 5, 5, 5, 7}, + {5, 6, 6, 5, 5, 6, 4, 6, 5, 3, 7, 6, 6, 1, 5}, + {1, 7, 7, 1, 6, 7, 7, 7, 7, 4, 6, 5, 6, 5, 7}, +} diff --git a/gamerule/tamquoc/tamquoc.go b/gamerule/tamquoc/tamquoc.go new file mode 100644 index 0000000..5178c48 --- /dev/null +++ b/gamerule/tamquoc/tamquoc.go @@ -0,0 +1,193 @@ +package tamquoc + +import ( + "fmt" + "math/rand" + "time" +) + +var SpinID int64 = 100000 // todo + +// 小游戏点击次数(起始次数) +var bonusGameNum = 10 + +type LineData struct { + Index int + Element int + Count int + Score int + Position []int32 + FreeElementCnt int // 免费元素个数 + BonusElementCnt int // Bonus元素个数 +} + +//图案*3+连线数量=赔率索引,返回元素值和数量 +func isLine(data []int) (e int, count int) { + tempData := make([]int, LINE_CELL) + copy(tempData, data) + dataMap := make(map[int]int, LINE_CELL) + + for _, v := range tempData { + if _, ok := dataMap[v]; !ok { + dataMap[v] = 1 + } else { + dataMap[v]++ + } + } + for _, v := range tempData { + if dataMap[v] >= 3 { + if dataMap[v] == 3 && v == Element_VUKHI { // 跨服3线不计算 + continue + } + return v, dataMap[v] + } + } + return -1, -1 +} + +func CalcLine(data []int, betLines []int64) (lines []LineData) { + for _, lineNum := range betLines { + index := int(lineNum) + lineTemplate := AllLineArray[index-1] + edata := []int{} + epos := []int32{} + for _, pos := range lineTemplate { + edata = append(edata, data[pos]) + } + if len(edata) == LINE_CELL { + head, count := isLine(edata) + for _, pos := range lineTemplate { + if data[pos] == head { + epos = append(epos, int32(pos+1)) + } + } + if head >= 0 && count >= 3 && len(epos) == count { + var freeElementCnt, bonusElementCnt int // 免费元素个数/Bonus元素个数 + if head == Element_FREESPIN { + freeElementCnt = count + } + if head == Element_BONUS && count >= 3 { + bonusElementCnt = count + } + lines = append(lines, LineData{index, head, count, LineScore[head][count-1], epos, freeElementCnt, bonusElementCnt}) + } + } + } + return +} + +type CaclResult struct { + WinLines [][]LineData //多次中奖的线 + IsJackpot bool //是否爆奖 + AllScore int //总中奖倍率 + BonusScore int //小游戏奖励 + SpinFreeTimes int //免费旋转次数 +} + +func PrintHuman(data []int) { + var l = len(data) + if l != ELEMENT_TOTAL { + return + } + for r := 0; r < LINE_ROW; r++ { + for c := 0; c < LINE_CELL; c++ { + fmt.Printf("%5s", Element_NAME_MAP[data[r*LINE_CELL+c]]) + } + fmt.Println() + } +} + +type Symbol int + +const ( + SYMBOL1 Symbol = iota + 1 + SYMBOL2 +) +const ( + DEFALUTROOMMODEL int = iota + ROOMMODE1 + ROOMMODE2 + ROOMMODE3 +) + +func GenerateSlotsData_v2(s Symbol) []int { + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0, ELEMENT_TOTAL) + for i := 0; i < ELEMENT_TOTAL; i++ { + if s == SYMBOL1 { + slotsData = append(slotsData, symbol1[rand.Intn(len(symbol1))]) + } else if s == SYMBOL2 { + slotsData = append(slotsData, symbol2[rand.Intn(len(symbol2))]) + } + } + return slotsData +} +func GenerateSlotsData_v3(roomMode int) []int { + gSeedV++ + rand.Seed(gSeedV) + var slotsData = make([]int, 0) + switch roomMode { + case ROOMMODE1: + slotsData = roomMode1[rand.Intn(len(roomMode1))] + case ROOMMODE2: + slotsData = roomMode2[rand.Intn(len(roomMode2))] + case ROOMMODE3: + slotsData = roomMode3[rand.Intn(len(roomMode3))] + case DEFALUTROOMMODEL: + slotsData = defaultRoomModel[rand.Intn(len(defaultRoomModel))] + } + return slotsData +} + +type BonusGameResult struct { + BonusData []int64 //每次点击的显示奖金,有几个就点击几次,最后一次点击是0 + DataMultiplier int64 //第一级界面小游戏总和 + Mutiplier int //最终小游戏的倍率 + TotalPrizeValue int64 //最终小游戏的奖励 +} + +var gSeedV = time.Now().UnixNano() + +func GenerateBonusGame(betValue int, startBonus int) BonusGameResult { + if betValue <= 0 || startBonus <= 0 { + return BonusGameResult{ + BonusData: nil, + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: 0, + } + } + var bg = BonusGameResult{ + BonusData: make([]int64, 0), + DataMultiplier: 0, + Mutiplier: 0, + TotalPrizeValue: int64(0), + } + gSeedV++ + rand.Seed(gSeedV) + for i := 0; i < bonusGameNum; i++ { + var rate int + rate = bonusNormalRate[rand.Intn(len(bonusNormalRate))] + if rate == -1 { + i-- + } + switch rate { + case -1: // 未中奖(客户端显示图片) + rate = 0 + case 1: // 中高倍率 + rate = bonusHighRate[rand.Intn(len(bonusHighRate))] + default: // 中低倍率 + } + prizeValue := int64(rate) * int64(betValue) + bg.TotalPrizeValue += prizeValue + bg.DataMultiplier += prizeValue + bg.BonusData = append(bg.BonusData, prizeValue) + if bonusGameNum >= 24 { + break + } + } + bg.Mutiplier = startBonus + bg.TotalPrizeValue *= int64(bg.Mutiplier) + return bg +} diff --git a/gamerule/tamquoc/tamquoc_test.go b/gamerule/tamquoc/tamquoc_test.go new file mode 100644 index 0000000..72bc78e --- /dev/null +++ b/gamerule/tamquoc/tamquoc_test.go @@ -0,0 +1,149 @@ +package tamquoc + +import ( + "testing" +) + +func TestIsLine(t *testing.T) { + type TestData struct { + data []int + line int + } + testData := []TestData{ + {data: []int{0, 0, 0, 1, 1}, line: 3}, + {data: []int{1, 0, 0, 0, 1}, line: 3}, + {data: []int{1, 1, 0, 0, 0}, line: 3}, + + {data: []int{0, 1, 0, 0, 1}, line: 3}, + {data: []int{0, 1, 1, 0, 0}, line: 3}, + + {data: []int{0, 0, 1, 0, 1}, line: 3}, + {data: []int{0, 0, 1, 1, 0}, line: 3}, + + {data: []int{0, 0, 0, 0, 1}, line: 4}, + {data: []int{1, 0, 0, 0, 0}, line: 4}, + {data: []int{0, 1, 0, 0, 0}, line: 4}, + {data: []int{0, 0, 1, 0, 0}, line: 4}, + {data: []int{0, 0, 0, 1, 0}, line: 4}, + + {data: []int{0, 0, 0, 0, 0}, line: 5}, + } + for _, value := range testData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error line data:", value) + t.Fatal("TestIsLine") + } + } + errorData := []TestData{ + {data: []int{1, 2, 0, 0, 1}, line: -1}, + {data: []int{1, 2, 2, 0, 1}, line: -1}, + {data: []int{1, 2, 2, 4, 1}, line: -1}, + {data: []int{1, 2, 2, 1, 3}, line: -1}, + } + for _, value := range errorData { + if _, count := isLine(value.data); count != value.line { + t.Error(isLine(value.data)) + t.Error("Error data:", value) + t.Fatal("TestIsLine") + } + } +} + +func TestCalcLine(t *testing.T) { + type TestData struct { + data []int + line int64 + } + testData := []TestData{ + {data: []int{5, 6, 1, 7, 8, 4, 4, 4, 4, 4, 1, 0, 8, 1, 7}, line: 1}, + {data: []int{5, 6, 1, 7, 8, 4, 4, 0, 0, 4, 1, 0, 8, 1, 7}, line: 1}, + } + for _, value := range testData { + lines := CalcLine(value.data, []int64{value.line}) + if int64(lines[0].Index) != value.line { + t.Log("lines:", lines) + t.Log("Error line data:", value.data) + t.Fatal("TestIsLine") + } + } + +} +func TestGenerateBonusGame(t *testing.T) { + type args struct { + totalBet int + startBonus int + } + tests := []struct { + name string + args args + want BonusGameResult + }{ + { + name: "A", + args: args{ + totalBet: 100, + startBonus: 1, + }, + want: BonusGameResult{}, + }, + { + name: "B", + args: args{ + totalBet: 250, + startBonus: 5, + }, + want: BonusGameResult{}, + }, + { + name: "C", + args: args{ + totalBet: 250, + startBonus: 8000, + }, + want: BonusGameResult{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GenerateBonusGame(tt.args.totalBet, tt.args.startBonus) + t.Logf("GenerateBonusGame() = %v", got) + }) + } +} + +func TestCalcLine1(t *testing.T) { + type args struct { + data []int + betLines []int64 + } + tests := []struct { + name string + args args + wantLines []LineData + }{ + { + name: "A", + args: args{ + data: []int{6, 7, 2, 1, 4, 7, 3, 5, 5, 3, 3, 5, 7, 4, 5}, + betLines: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + }, + wantLines: nil, + }, + { + name: "B", + args: args{ + data: []int{4, 3, 2, 6, 5, 5, 4, 6, 6, 3, 2, 6, 7, 5, 5}, + betLines: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + }, + wantLines: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotLines := CalcLine(tt.args.data, tt.args.betLines) + PrintHuman(tt.args.data) + t.Logf("CalcLine() = %v", gotLines) + }) + } +} diff --git a/gamerule/thirteen/constants.go b/gamerule/thirteen/constants.go new file mode 100644 index 0000000..3cbdadd --- /dev/null +++ b/gamerule/thirteen/constants.go @@ -0,0 +1,71 @@ +package thirteen + +import "time" + +var ( + SpecialScore = []int64{0, 108, 36, 0, 20, 0, 0, 0, 10, 0, 0, 4, 4, 3} +) + +// 房间的参数 +const ( + ParamMaxPlayerNum = 0 // 最大人数 + ParamAuto = 1 // 自动摆牌 + ParamBaiPai = 2 // 摆牌时间 + ParamBaseScore = 3 // 游戏底分 + ParamLaiZi = 4 // 是否有癞子 +) + +// 场景状态 +const ( + ThirteenWaterSceneStateWait int = iota //等待状态 + ThirteenWaterSceneStateStart //开始倒计时 + ThirteenWaterSceneStateSendCards //开始发牌 + ThirteenWaterSceneStateOptCard //选牌理牌 + ThirteenWaterSceneStateShowCards //轮流看牌 + ThirteenWaterSceneStateHit //打枪 + ThirteenWaterSceneStateBilled //结算 + ThirteenWaterSceneStateMax +) + +// 玩家操作 +const ( + ThirteenWaterPlayerOpMS = 1 //确定牌 + ThirteenWaterPlayerOpStandup = 2 //站起标记 + ThirteenWaterPlayerOpTest = 3 // test + ThirteenWaterPlayerOpReset = 4 // 重新选牌 +) +const ( + ThirteenWaterSceneWaitTimeout = time.Second * 2 //等待倒计时 + ThirteenWaterStartTimeout = time.Second * 3 //开始倒计时 + ThirteenWaterSendCardsTimeout = time.Second * 3 //开始发牌 + ThirteenWaterOptCardTimeout = time.Second * 30 //30选牌换牌 + ThirteenWaterShowCardsTimeout = time.Second * 1 //轮流看牌时间 + ThirteenWaterHitTimeout = time.Second * 2 //打枪 + ThirteenWaterBilledTimeout = time.Second * 5 //结算 +) + +func GetMaxCard(allGroup map[int]*Group) *Group { + var ret *Group + max := -1000 + for k, v := range allGroup { + sum := 0 + if k >= 1000000 { + ret = v + break + } + a := k / 10000 + b := (k - a*10000) / 100 + c := k - a*10000 - b*100 + s := []int{a, b, c} + for _, m := range s { + if m >= 1 && m <= 20 { + sum += 20 - m + } + } + if max <= sum { + max = sum + ret = v + } + } + return ret +} diff --git a/gamerule/thirteen/find.go b/gamerule/thirteen/find.go new file mode 100644 index 0000000..8ae794c --- /dev/null +++ b/gamerule/thirteen/find.go @@ -0,0 +1,599 @@ +package thirteen + +import ( + "sort" +) + +// DelCards 从cards里边删除arr +func DelCards(cards []int, arr []int) []int { + c := make([]int, len(cards)) + s := make([]int, len(arr)) + copy(c, cards) + copy(s, arr) + for i := 0; i < len(arr); i++ { + for k, v := range c { + a := false + for m, n := range s { + if v == n { + a = true + c = append(c[:k], c[k+1:]...) + s = append(s[:m], s[m+1:]...) + break + } + } + if a { + break + } + } + if len(s) == 0 { + break + } + } + return c +} + +func getLaiZiCards(cards []int, lai []int) []int { + var ret []int + for k, v := range cards { + if v < 0 { + continue + } + for _, vv := range lai { + if vv < 0 { + continue + } + if v == vv { + ret = append(ret, v) + cards[k] = -1 + break + } + } + } + return ret +} + +func remainCards(cards, lai []int) (remainCount int, cardsLaiZi []int, cardsCount [20]int) { + ret := [20]int{} + count := 0 + cardsLaiZi = getLaiZiCards(cards, lai) + for _, v := range cards { + if v < 0 { + continue + } + count++ + ret[ToLogic(v)]++ + } + return count, cardsLaiZi, ret +} + +func findCardByLogic(cards []int, logicValue, count int) []int { + var ret []int + for k, v := range cards { + if v < 0 { + continue + } + if ToLogic(v) == logicValue { + ret = append(ret, v) + cards[k] = -1 + } + if len(ret) >= count { + break + } + } + return ret +} + +func findCardByLogic2(cards []int, logicValue, count int) []int { + var ret []int + for _, v := range cards { + if v < 0 { + continue + } + if ToLogic(v) == logicValue { + ret = append(ret, v) + //cards[k] = -1 + } + if len(ret) >= count { + break + } + } + return ret +} + +// FindMaxPairs 找最大对子 +func FindMaxPairs(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 0; i-- { + switch cardsCount[i] { + case 0: + if len(cardsLaiZi) >= 2 { + ret = cardsLaiZi[:2] + change = []int{51, 51} // AA + return + } + case 1: + if len(cardsLaiZi) > 0 { + ret = append(findCardByLogic(cs, i, 1), cardsLaiZi[0]) + change = []int{ret[0], i + 3*13} + return + } + default: + ret = findCardByLogic(cs, i, 2) + change = []int{ret[0], ret[1]} + return + } + } + return +} + +// FindMaxThreeAAA 找最大三条 +func FindMaxThreeAAA(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 0; i-- { + switch cardsCount[i] { + case 0: + if len(cardsLaiZi) >= 3 { + ret = cardsLaiZi[:3] + change = []int{51, 51, 51} //AAA + return + } + case 1: + if len(cardsLaiZi) > 1 { + ret = append(findCardByLogic(cs, i, 1), cardsLaiZi[:2]...) + change = []int{ret[0], i + 3*13, i + 3*13} + return + } + case 2: + if len(cardsLaiZi) > 0 { + ret = append(findCardByLogic(cs, i, 2), cardsLaiZi[0]) + change = []int{ret[0], ret[1], i + 3*13} + return + } + default: + ret = findCardByLogic(cs, i, 3) + change = []int{ret[0], ret[1], ret[2]} + return + } + } + return +} + +// FindMaxFlush 找最大顺子 +// 逆序返回,癞子填充 +func FindMaxFlush(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 4; i-- { // 起始顺子逻辑值 + var a, b int + for j := 0; j < 5; j++ { // 顺子都是5个 + if cardsCount[i-j] > 0 { + a++ + } else { + b++ + } + } + if b <= len(cardsLaiZi) { // 癞子足够 + r := [5]int{} + r2 := [5]int{} + l := 0 + for j := 0; j < 5; j++ { + if cardsCount[i-j] > 0 { + r[j] = findCardByLogic(cs, i-j, 1)[0] + r2[j] = r[j] + } else { + r[j] = cardsLaiZi[l] + l++ + r2[j] = i - j + 3*13 + } + } + ret = r[:] + change = r2[:] + return + } + } + + // 找 A,2,3... + var a, b int + for i := 0; i < 4; i++ { + if cardsCount[i] > 0 { + a++ + } else { + b++ + } + } + if cardsCount[12] > 0 { + a++ + } else { + b++ + } + + if b <= len(cardsLaiZi) { // 癞子足够 + r := [5]int{} + r2 := [5]int{} + l := 0 + + for i := 3; i >= 0; i-- { + if cardsCount[i] > 0 { + r[3-i] = findCardByLogic(cs, i, 1)[0] + r2[3-i] = r[3-i] + } else { + r[3-i] = cardsLaiZi[l] + l++ + r2[3-i] = i + 3*13 + } + } + + if cardsCount[12] > 0 { + r[4] = findCardByLogic(cs, 12, 1)[0] + r2[4] = r[4] + } else { + r[4] = cardsLaiZi[l] + l++ + r2[4] = 12 + 3*13 + } + ret = r[:] + change = r2[:] + return + } + + return +} + +// FindMaxSameColors 找最大同花 +// 逆序返回,癞子追加 +func FindMaxSameColors(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + //_, cardsLaiZi, _ := remainCards(cs, lai) + cardsLaiZi := getLaiZiCards(cs, lai) + m := [4][]int{} + for _, v := range cs { + if v >= 0 { + m[ToColor(v)] = append(m[ToColor(v)], v) + } + } + for i := 3; i >= 0; i-- { + if len(m[i])+len(cardsLaiZi) >= 5 { + if len(m[i]) >= 5 { + ret = m[i][:5] + change = make([]int, 5) + copy(change, ret) + return + } + ret = append(m[i], cardsLaiZi[:5-len(m[i])]...) + change = make([]int, 5) + copy(change, m[i]) + for j := len(m[i]); j < 5; j++ { + change[j] = 12 + i*13 + } + return + } + } + return +} + +// FindMaxGourdCards 找最大葫芦 +// 三条在前对子在后 +func FindMaxGourdCards(cards, lai []int) (ret []int, change []int) { + // 找最大三条,再找最大对子 + r, c := FindMaxThreeAAA(cards, lai) + if len(r) == 0 { + return + } + rr, cc := FindMaxPairs(DelCards(cards, r), lai) + if len(rr) == 0 { + return + } + return append(r, rr...), append(c, cc...) +} + +// FindMaxFourAAAA 找最大铁支 +func FindMaxFourAAAA(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 0; i-- { + switch cardsCount[i] { + case 0: + if len(cardsLaiZi) >= 4 { + ret = cardsLaiZi[:4] + change = []int{51, 51, 51, 51} + return + } + case 1: + if len(cardsLaiZi) >= 3 { + ret = append(findCardByLogic(cs, i, 1), cardsLaiZi[:3]...) + change = []int{ret[0], i + 3*13, i + 3*13, i + 3*13} + return + } + case 2: + if len(cardsLaiZi) > 1 { + ret = append(findCardByLogic(cs, i, 2), cardsLaiZi[:2]...) + change = []int{ret[0], ret[1], i + 3*13, i + 3*13} + return + } + case 3: + if len(cardsLaiZi) > 0 { + ret = append(findCardByLogic(cs, i, 3), cardsLaiZi[0]) + change = []int{ret[0], ret[1], ret[2], i + 3*13} + return + } + default: + ret = findCardByLogic(cs, i, 4) + change = make([]int, 4) + copy(change, ret) + return + } + } + return +} + +// FindMaxSameColorFlush 找最大同花顺 +func FindMaxSameColorFlush(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, _ := remainCards(cs, lai) + m := [4][]int{} + for _, v := range cs { + if v >= 0 { + m[ToColor(v)] = append(m[ToColor(v)], v) + } + } + for i := 3; i >= 0; i-- { + a := append(m[i], cardsLaiZi...) + r, c := FindMaxFlush(a, lai) + if len(r) > 0 { + // 修改一下花色 + for j := 0; j < 5; j++ { + c[j] = ToLogic(c[j]) + i*13 + } + return r, c + } + } + return +} + +// FindAllFlush 找所有顺子 +func FindAllFlush(cards []int, lai []int) [][]int { + var ret [][]int + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 4; i-- { // 起始顺子逻辑值 + var a, b int + for j := 0; j < 5; j++ { // 顺子都是5个 + if cardsCount[i-j] > 0 { + a++ + } else { + b++ + } + } + if b <= len(cardsLaiZi) { // 癞子足够 + r := [5]int{} + //r2 := [5]int{} + l := 0 + for j := 0; j < 5; j++ { + if cardsCount[i-j] > 0 { + r[j] = findCardByLogic2(cs, i-j, 1)[0] + //r2[j] = r[j] + } else { + r[j] = cardsLaiZi[l] + l++ + //r2[j] = i - j + 3*13 + } + } + ret = append(ret, r[:]) + } + } + + // 找 A,2,3... + var a, b int + for i := 0; i < 4; i++ { + if cardsCount[i] > 0 { + a++ + } else { + b++ + } + } + if cardsCount[12] > 0 { + a++ + } else { + b++ + } + + if b <= len(cardsLaiZi) { // 癞子足够 + r := [5]int{} + //r2 := [5]int{} + l := 0 + + for i := 3; i >= 0; i-- { + if cardsCount[i] > 0 { + r[3-i] = findCardByLogic2(cs, i, 1)[0] + //r2[3-i] = r[3-i] + } else { + r[3-i] = cardsLaiZi[l] + l++ + //r2[3-i] = i + 3*13 + } + } + + if cardsCount[12] > 0 { + r[4] = findCardByLogic2(cs, 12, 1)[0] + //r2[4] = r[4] + } else { + r[4] = cardsLaiZi[l] + l++ + //r2[4] = 12 + 3*13 + } + ret = append(ret, r[:]) + } + + return ret +} + +// FindAllSameColorFlush 找所有同花顺 +func FindAllSameColorFlush(cards []int, lai []int) [][]int { + var ret [][]int + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, _ := remainCards(cs, lai) + m := [4][]int{} + for _, v := range cs { + if v >= 0 { + m[ToColor(v)] = append(m[ToColor(v)], v) + } + } + for i := 3; i >= 0; i-- { + a := append(m[i], cardsLaiZi...) + r := FindAllFlush(a, lai) + ret = append(ret, r...) + } + return ret +} + +// FindMaxFive 找最大五同 +func FindMaxFive(cards, lai []int) (ret []int, change []int) { + cs := make([]int, len(cards)) + copy(cs, cards) + sort.Sort(sort.Reverse(sort.IntSlice(cs))) + _, cardsLaiZi, cardsCount := remainCards(cs, lai) + for i := 12; i >= 0; i-- { + switch cardsCount[i] { + case 0: + if len(cardsLaiZi) >= 5 { + ret = cardsLaiZi[:5] + change = []int{51, 51, 51, 51} + return + } + case 1: + if len(cardsLaiZi) >= 4 { + ret = append(findCardByLogic(cs, i, 1), cardsLaiZi[:4]...) + change = []int{ret[0], i + 3*13, i + 3*13, i + 3*13, i + 3*13} + return + } + case 2: + if len(cardsLaiZi) >= 3 { + ret = append(findCardByLogic(cs, i, 2), cardsLaiZi[:3]...) + change = []int{ret[0], ret[1], i + 3*13, i + 3*13, i + 3*13} + return + } + case 3: + if len(cardsLaiZi) >= 2 { + ret = append(findCardByLogic(cs, i, 3), cardsLaiZi[:2]...) + change = []int{ret[0], ret[1], ret[2], i + 3*13, i + 3*13} + return + } + case 4: + if len(cardsLaiZi) >= 1 { + ret = append(findCardByLogic(cs, i, 3), cardsLaiZi[0]) + change = []int{ret[0], ret[1], ret[2], ret[3], i + 3*13} + return + } + + default: + ret = findCardByLogic(cs, i, 5) + change = make([]int, 5) + copy(change, ret) + return + } + } + return +} + +func isType(cards []int, lai []int, n int) bool { + if len(cards) < 3 { + return false + } + switch n { + case PokersTypeFive: + //找五同 + card, _ := FindMaxFive(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeStraightFlush: + //找同花顺 + card, _ := FindMaxSameColorFlush(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeFour: + //找铁支 + card, _ := FindMaxFourAAAA(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeFullHouse: + //找葫芦 + card, _ := FindMaxGourdCards(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeFlush: + //找同花 + card, _ := FindMaxSameColors(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeStraight: + //找顺子 + card, _ := FindMaxFlush(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeThree: + //找三条 + card, _ := FindMaxThreeAAA(cards, lai) + if len(card) != 0 { + return true + } + case PokersTypeTwoPairs: + //找两对子 + if len(cards) == 3 { + return false + } + card, _ := FindMaxPairs(cards, lai) + if len(card) != 0 { + card1, _ := FindMaxPairs(DelCards(cards, card), lai) + if len(card1) != 0 { + return true + } + } + case PokersTypePair: + //找对子 + card, _ := FindMaxPairs(cards, lai) + if len(card) != 0 { + return true + } + } + return false +} + +func GetType(cards []int, lai []int) int { + if len(cards) < 3 { + return 0 + } + b := PokersTypeFive + if len(cards) == 3 { + b = PokersTypeThree + } + for i := b; i < PokersTypeMax; i++ { + if isType(cards, lai, i) { + return i + } + } + return PokersTypeOne +} diff --git a/gamerule/thirteen/find_test.go b/gamerule/thirteen/find_test.go new file mode 100644 index 0000000..5e01393 --- /dev/null +++ b/gamerule/thirteen/find_test.go @@ -0,0 +1,346 @@ +package thirteen + +import ( + "fmt" + "strings" + "testing" +) + +// 牌序- A, K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2 +// 黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +// 红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +// 梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +// 方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +var pokersTest = []int{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 38, + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39} + +var pokers2Test = append(pokersTest, pokersTest...) + +func arrEqual(a, b [][]int) bool { + if len(a) != len(b) { + return false + } + + for i := 0; i < len(a); i++ { + if len(a[i]) != len(b[i]) { + return false + } + + cp := make([]int, len(a[i])) + copy(cp, a[i]) + for _, v := range b[i] { + cp = DelCards(cp, []int{v}) + } + if len(cp) > 0 { + return false + } + } + return true +} + +type item struct { + cards []int + lai []int + target []int +} + +func goResult(cards []int) string { + buf := strings.Builder{} + buf.WriteString("[]int{") + + for m, n := range cards { + buf.WriteString(fmt.Sprint(n)) + if m < len(cards)-1 { + buf.WriteString(",") + } + } + + buf.WriteString("}") + return buf.String() +} + +func printTest(t *testing.T, cards []int, target, result, change []int) { + // 牌应该都在原牌堆中存在,这里检查一下 + for _, vv := range result { + has := false + for _, c := range cards { + if vv == c { + has = true + } + } + if !has { + t.Logf("%v,%v 不存在\n", PokerName(vv), vv) + } + } + + t.Logf("goResult: %s", goResult(result)) + t.Fatal() +} + +func findFuncTest(t *testing.T, data []item, find func([]int, []int) ([]int, []int)) { + for _, v := range data { + result, change := find(v.cards, v.lai) + t.Logf(" cards: %s\n", PokersShow(v.cards)) + t.Logf(" cards: %v\n", v.cards) + t.Logf("target: %s %v\n", PokersShow(v.target), v.target) + t.Logf("result: %s %v \n", PokersShow(result), result) + t.Logf("change: %s %v \n\n", PokersShow(change), change) + if !arrEqual([][]int{v.target}, [][]int{result}) { + printTest(t, v.cards, v.target, result, change) + } + } +} + +func TestFindMaxPairs(t *testing.T) { + data := []item{ + { + cards: []int{9, 8, 7, 6, 5, 20, 19, 18, 17, 16, 32, 31, 30, 29, 28, 27, 26, 38}, + target: []int{20, 7}, + }, + { + cards: []int{5, 4, 3, 2, 1, 0, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13}, + target: []int{18, 5}, + }, + { + cards: pokers2Test, + target: []int{51, 51}, + }, + { + cards: pokers2Test, + lai: []int{52, 53}, + target: []int{51, 51}, + }, + { + cards: []int{51, 0, 2, 3, 52, 53}, + lai: []int{52, 53}, + target: []int{51, 53}, + }, + { + cards: []int{1, 0, 2, 3, 52, 53}, + lai: []int{52, 53}, + target: []int{53, 52}, + }, + } + findFuncTest(t, data, FindMaxPairs) +} + +func TestFindMaxThreeAAA(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{25, 38, 51}, + }, + { + cards: pokers2Test, + target: []int{51, 51, 38}, + }, + { + cards: []int{51, 0, 2, 3, 52, 53}, + lai: []int{52, 53}, + target: []int{51, 53, 52}, + }, + { + cards: []int{1, 0, 2, 3, 52, 53}, + lai: []int{52, 53}, + target: []int{3, 53, 52}, + }, + { + cards: []int{1, 0, 2, 3, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{53, 52, 52}, + }, + } + findFuncTest(t, data, FindMaxThreeAAA) +} + +func TestFindMaxFlush(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{47, 48, 49, 50, 51}, + }, + { + cards: []int{12, 3, 2, 1, 0}, + target: []int{12, 0, 1, 2, 3}, + }, + { + cards: pokers2Test, + target: []int{47, 48, 49, 50, 51}, + }, + { + cards: []int{0, 1, 2, 3, 4, 5}, + lai: []int{52, 53}, + target: []int{5, 4, 3, 2, 1}, + }, + { + cards: []int{0, 2, 4, 5, 7, 8, 52, 53}, + lai: []int{52, 53}, + target: []int{53, 8, 7, 52, 5}, + }, + { + cards: []int{0, 2, 4, 8, 9, 10, 52, 53}, + lai: []int{52, 53}, + target: []int{53, 52, 10, 9, 8}, + }, + { + cards: []int{51, 0, 1, 2, 4, 6, 52, 53}, + lai: []int{52, 53}, + target: []int{6, 53, 4, 52, 2}, + }, + { + cards: []int{51, 0, 1, 2, 8, 9, 52, 53}, + lai: []int{52, 53}, + target: []int{51, 53, 52, 9, 8}, + }, + { + cards: []int{51, 0, 1, 2, 8, 52}, + lai: []int{52, 53}, + target: []int{52, 2, 1, 0, 51}, + }, + } + findFuncTest(t, data, FindMaxFlush) +} + +func TestFindMaxSameColors(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{47, 48, 49, 50, 51}, + }, + { + cards: pokers2Test, + target: []int{49, 50, 50, 51, 51}, + }, + { + cards: []int{39, 38, 34, 33, 30}, + target: []int{}, + }, + { + cards: []int{51, 0, 1, 2, 8, 52}, + lai: []int{52, 53}, + target: []int{8, 2, 1, 0, 52}, + }, + { + cards: []int{51, 0, 1, 2, 22, 52, 53}, + lai: []int{52, 53}, + target: []int{2, 1, 0, 53, 52}, + }, + { + cards: []int{51, 50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{51, 50, 53, 52, 52}, + }, + } + findFuncTest(t, data, FindMaxSameColors) +} + +func TestFindMaxGourdCards(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{25, 37, 38, 50, 51}, + }, + { + cards: pokers2Test, + target: []int{51, 51, 38, 38, 25}, + }, + { + cards: []int{51, 50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{51, 53, 52, 50, 52}, + }, + { + cards: []int{51, 12, 50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{51, 12, 53, 52, 52}, + }, + } + findFuncTest(t, data, FindMaxGourdCards) +} + +func TestFindMaxFourAAAA(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{12, 25, 38, 51}, + }, + { + cards: pokers2Test, + target: []int{51, 51, 38, 38}, + }, + { + cards: []int{51, 12, 50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{51, 12, 53, 52}, + }, + { + cards: []int{12, 50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{12, 53, 52, 52}, + }, + { + cards: []int{50, 1, 2, 22, 52, 53, 52, 52}, + lai: []int{52, 53}, + target: []int{53, 52, 52, 52}, + }, + { + cards: []int{50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{50, 53, 52, 52}, + }, + } + findFuncTest(t, data, FindMaxFourAAAA) +} + +func TestFindMaxSameColorFlush(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{47, 48, 49, 50, 51}, + }, + { + cards: pokers2Test, + target: []int{47, 48, 49, 50, 51}, + }, + { + cards: []int{50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{53, 52, 52, 2, 1}, + }, + { + cards: []int{0, 1, 2, 4, 9, 52}, + lai: []int{52, 53}, + target: []int{4, 52, 2, 1, 0}, + }, + { + cards: []int{12, 1, 2, 3, 9, 52}, + lai: []int{52, 53}, + target: []int{3, 2, 1, 52, 12}, + }, + } + findFuncTest(t, data, FindMaxSameColorFlush) +} + +func TestFindMaxFive(t *testing.T) { + data := []item{ + { + cards: pokersTest, + target: []int{}, + }, + { + cards: pokers2Test, + target: []int{51, 51, 38, 38, 25}, + }, + { + cards: []int{50, 1, 2, 22, 52, 53, 52}, + lai: []int{52, 53}, + target: []int{}, + }, + { + cards: []int{51, 51, 51, 51, 12, 52}, + lai: []int{52, 53}, + target: []int{51, 51, 51, 51, 12}, + }, + } + findFuncTest(t, data, FindMaxFive) +} diff --git a/gamerule/thirteen/findspecial.go b/gamerule/thirteen/findspecial.go new file mode 100644 index 0000000..893857b --- /dev/null +++ b/gamerule/thirteen/findspecial.go @@ -0,0 +1,485 @@ +package thirteen + +import ( + "fmt" + "sort" +) + +const ( + SpecialType_0 int = iota + CyanLongType // 清龙 + ALongType // 一条龙 + TwelveKingType //十二皇族 + ThreeColorFlushType // 三同花顺 + ThreeTheWorldType //三分天下 + AllBigType //全大 + AllSmallType //全小 + SameColorType // 凑一色 + FourThreeType //四套三条 + FivePairsThreeType //五对三条 + SixPairsHalfType // 六对半 + ThreeContinuityType // 三顺子 + ThreeColorsType //三同花 + SpecialTypeMax +) + +var SpecialTypeName = map[int]string{ + SpecialType_0: "-", + CyanLongType: "清龙", + ALongType: "一条龙", + TwelveKingType: "十二皇族", + ThreeColorFlushType: "三同花顺", + ThreeTheWorldType: "三分天下", + AllBigType: "全大", + AllSmallType: "全小", + SameColorType: "凑一色", + FourThreeType: "四套三条", + FivePairsThreeType: "五对三条", + SixPairsHalfType: "六对半", + ThreeContinuityType: "三顺子", + ThreeColorsType: "三同花", +} + +func SpecialName(n int) string { + if n <= 0 { + return "-" + } + return fmt.Sprintf("%v,%v,%v,%v", SpecialTypeName[n/1000000], PokersTypeName[n%1000000/10000], PokersTypeName[n%10000/100], PokersTypeName[n%100]) +} + +// CyanLong 找清龙 +// 13张同花色 +func CyanLong(cards [13]int, lai []int) bool { + getLaiZiCards(cards[:], lai) // 去掉癞子牌 + // 找一个花色 + color := -1 + for _, v := range cards { + if v < 0 { + continue + } + color = ToColor(v) + break + } + if color < 0 { + return false + } + // 除癞子牌,其余牌同花色 + for _, v := range cards { + if v < 0 { + continue + } + if ToColor(v) != color { + return false + } + } + return true +} + +// ALong 找一条龙 +// 13张顺子 +func ALong(cards [13]int, lai []int) bool { + _, laiZiCards, cardsCount := remainCards(cards[:], lai) + var n int + for i := 0; i < 13; i++ { + if cardsCount[i] == 0 { + n++ + } + } + return n <= len(laiZiCards) +} + +// 是不是顺子 +// 没有牌的用癞子填充 +func isFlush(cards []int) bool { + //fmt.Printf("isFlush: %v\n", PokersShow(cards)) + if len(cards) != 3 && len(cards) != 5 { + return false + } + laiZiCount := 0 + cardsCount := [20]int{} + for _, v := range cards { + if v < 0 { + laiZiCount++ + continue + } + cardsCount[ToLogic(v)]++ + } + //fmt.Printf("isFlush cardsCount: %v\n", cardsCount) + if len(cards) == 3 { + for i := 0; i < 10; i++ { // 排除QKA + var n int + for j := i; j < i+3; j++ { + if cardsCount[j] == 0 { + n++ + } + } + if n <= laiZiCount { + return true + } + } + // 找 A 2 3 + n := 0 + if cardsCount[0] == 0 { + n++ + } + if cardsCount[1] == 0 { + n++ + } + if cardsCount[12] == 0 { + n++ + } + if n <= laiZiCount { + return true + } + } else { + for i := 0; i < 9; i++ { + var n int + for j := i; j < i+5; j++ { + if cardsCount[j] == 0 { + n++ + } + } + if n <= laiZiCount { + return true + } + } + // 找 A 2 3 4 5 + n := 0 + if cardsCount[0] == 0 { + n++ + } + if cardsCount[1] == 0 { + n++ + } + if cardsCount[2] == 0 { + n++ + } + if cardsCount[3] == 0 { + n++ + } + if cardsCount[12] == 0 { + n++ + } + if n <= laiZiCount { + return true + } + } + return false +} + +// 是不是同花 +// 没有牌的用癞子填充 +func isSameColor(cards []int) bool { + color := -1 + for _, v := range cards { + if v < 0 { + continue + } + if color < 0 { + color = ToColor(v) + } else { + if ToColor(v) != color { + return false + } + } + } + return true +} + +// ThreeColorFlush 找三同花顺 +func ThreeColorFlush(cards [13]int, lai []int) bool { + l := FindAllSameColorFlush(cards[:], lai) + for _, v := range l { + c1 := DelCards(cards[:], v) + l1 := FindAllSameColorFlush(c1, lai) + //fmt.Printf("第1层:%v--%v\n", PokersShow(v), PokersShow(c1)) + for _, vv := range l1 { + //fmt.Printf("第2层:%v\n", PokersShow(vv)) + c2 := DelCards(c1, vv) + //fmt.Printf("第3层:%v\n", PokersShow(c2)) + getLaiZiCards(c2, lai) + if isFlush(c2) && isSameColor(c2) { + return true + } + } + } + return false +} + +// SameColor 找凑一色 +// 13张牌都是黑色或红色 +func SameColor(cards [13]int, lai []int) bool { + getLaiZiCards(cards[:], lai) + + color := -1 // 黑色0 红色1 + for _, v := range cards { + if v < 0 { + continue + } + if ToColor(v) == 3 || ToColor(v) == 1 { + color = 0 + } else { + color = 1 + } + break + } + if color < 0 { + return false + } + + for _, v := range cards { + if v < 0 { + continue + } + if ToColor(v) == 3 || ToColor(v) == 1 { + if color == 1 { + return false + } + } else { + if color == 0 { + return false + } + } + } + return true +} + +// SixPairsHalf 找六对半 +// 有六个对子就算 +func SixPairsHalf(cards [13]int, lai []int) bool { + _, cardsLaiZi, cardsCount := remainCards(cards[:], lai) + n := 0 + for k, v := range cardsCount { // 找对子 + if v >= 2 { + n += v / 2 + cardsCount[k] = v % 2 + } + } + p := 0 + for _, v := range cardsCount { // 找单张 + if v > 0 { + p++ + } + } + ln := len(cardsLaiZi) + if ln >= p { + ln -= p + n += p + p = 0 + } else { + n += ln + p -= ln + ln = 0 + } + n += ln / 2 + if n == 6 { + return true + } + return false +} + +// ThreeContinuity 找三顺子 +func ThreeContinuity(cards [13]int, lai []int) bool { + l := FindAllFlush(cards[:], lai) + for _, v := range l { + c := DelCards(cards[:], v) + l = FindAllFlush(c, lai) + for _, vv := range l { + c = DelCards(c, vv) + getLaiZiCards(c, lai) + if isFlush(c) { + return true + } + } + } + return false +} + +// ThreeColors 找三同花 +func ThreeColors(cards [13]int, lai []int) bool { + //sort.Sort(sort.Reverse(sort.IntSlice(cards[:]))) + getLaiZiCards(cards[:], lai) + colorMap := make(map[int]int) + for _, v := range cards { + if v >= 0 { + colorMap[ToColor(v)]++ + } + } + switch len(colorMap) { + case 0: + return false + case 1: + return false + case 2: + var c []int + for _, v := range colorMap { + c = append(c, v) + } + sort.Ints(c) + if c[0] <= 3 && c[1] <= 10 { + return true + } + if c[0] <= 5 && c[1] <= 8 { + return true + } + + case 3: + var c []int + for _, v := range colorMap { + c = append(c, v) + } + sort.Ints(c) + if c[0] <= 3 && c[1] <= 5 && c[2] <= 5 { + return true + } + } + return false +} + +// 找五对三条 +//func FivePairsThree(cards [13]int) bool { +// valueMap := make(map[int]int) +// for _, v := range cards { +// valueMap[v%13]++ +// } +// pairs := 0 +// three := 0 +// for _, v := range valueMap { +// if v == 2 { +// pairs++ +// } else if v == 3 { +// three++ +// } +// } +// if pairs == 5 && three == 1 { +// return true +// } +// return false +//} + +// 找四套三条 +//func FourThree(cards [13]int) bool { +// valueMap := make(map[int]int) +// for _, v := range cards { +// valueMap[v%13]++ +// } +// three := 0 +// for _, v := range valueMap { +// if v == 3 { +// three++ +// } +// } +// if three == 4 { +// return true +// } +// return false +//} + +// 找全小 +//func AllSmall(cards [13]int) bool { +// for _, v := range cards { +// if v%13 > 6 { +// return false +// } +// } +// return true +//} + +// 找全大 +//func AllBig(cards [13]int) bool { +// for _, v := range cards { +// if v%13 < 6 { +// return false +// } +// } +// return true +//} + +// 找三分天下 +//func ThreeTheWorld(cards [13]int) bool { +// fourMap := make(map[int]int) +// for _, v := range cards { +// fourMap[v%13]++ +// } +// i := 0 +// for _, v := range fourMap { +// if v == 4 { +// i++ +// } +// } +// if i == 3 { +// return true +// } +// return false +//} + +// 找十二皇族 +//func TwelveKing(cards [13]int) bool { +// for _, v := range cards { +// if v%13 < 9 { +// return false +// } +// } +// return true +//} + +// GetSpecialType 找特殊牌型 +// lai 癞子牌 +func GetSpecialType(cards [13]int, lai []int) int { + // 清龙判定 + if CyanLong(cards, lai) { + return CyanLongType + } + // 找一条龙 + if ALong(cards, lai) { + return ALongType + } + ////找十二皇族 + //if TwelveKing(cards) { + // return TwelveKingType + //} + //找三同花顺 + if ThreeColorFlush(cards, lai) { + return ThreeColorFlushType + } + ////找三分天下 + //if ThreeTheWorld(cards) { + // return ThreeTheWorldType + //} + ////找全大 + //if AllBig(cards) { + // return AllBigType + //} + ////找全小 + //if AllSmall(cards) { + // return AllSmallType + //} + // 找凑一色 + if SameColor(cards, lai) { + return SameColorType + } + ////找四套三条 + //if FourThree(cards) { + // return FourThreeType + //} + ////找五对三条 + //if FivePairsThree(cards) { + // return FivePairsThreeType + //} + + // 找六对半 + if SixPairsHalf(cards, lai) { + return SixPairsHalfType + } + + // 找三顺子 + if ThreeContinuity(cards, lai) { + return ThreeContinuityType + } + + // 找三同花 + if ThreeColors(cards, lai) { + return ThreeColorsType + } + return 0 +} diff --git a/gamerule/thirteen/findspecial_test.go b/gamerule/thirteen/findspecial_test.go new file mode 100644 index 0000000..c53db23 --- /dev/null +++ b/gamerule/thirteen/findspecial_test.go @@ -0,0 +1,131 @@ +package thirteen + +import "testing" + +func TestFindSpecial(t *testing.T) { + type item struct { + cards [13]int + lai []int + target int // 牌型 + } + + data := []item{ + // 青龙 + { + cards: [13]int{39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}, + lai: []int{52, 53}, + target: CyanLongType, + }, + { + cards: [13]int{39, 40, 41, 42, 43, 44, 44, 46, 47, 48, 49, 53, 52}, + lai: []int{52, 53}, + target: CyanLongType, + }, + //一条龙 + { + cards: [13]int{13, 14, 28, 29, 30, 44, 45, 7, 34, 9, 23, 11, 51}, + lai: []int{52, 53}, + target: ALongType, + }, + { + cards: [13]int{13, 14, 28, 29, 52, 44, 45, 7, 34, 9, 23, 11, 51}, + lai: []int{52, 53}, + target: ALongType, + }, + { + cards: [13]int{13, 13, 28, 29, 52, 44, 45, 7, 34, 9, 23, 11, 51}, + lai: []int{52, 53}, + target: 0, + }, + //三同花顺 + { + cards: [13]int{1, 2, 3, 4, 5, 15, 16, 17, 18, 19, 27, 28, 29}, + lai: []int{52, 53}, + target: ThreeColorFlushType, + }, + { + cards: [13]int{1, 2, 3, 4, 5, 15, 16, 17, 52, 19, 27, 28, 52}, + lai: []int{52, 53}, + target: ThreeColorFlushType, + }, + //凑一色 + { + cards: [13]int{13, 14, 40, 16, 42, 18, 44, 19, 46, 47, 23, 49, 25}, + lai: []int{52, 53}, + target: SameColorType, + }, + { + cards: [13]int{0, 1, 2, 28, 3, 29, 5, 31, 8, 10, 11, 12, 37}, + lai: []int{52, 53}, + target: SameColorType, + }, + { + cards: [13]int{39, 40, 17, 18, 44, 45, 21, 48, 49, 24, 50, 25, 51}, + lai: []int{52, 53}, + target: SameColorType, + }, + { + cards: [13]int{52, 40, 17, 18, 44, 45, 21, 48, 49, 24, 50, 25, 51}, + lai: []int{52, 53}, + target: SameColorType, + }, + //六对半 + { + cards: [13]int{17, 30, 5, 31, 6, 32, 8, 47, 23, 36, 38, 51, 39}, + lai: []int{52, 53}, + target: SixPairsHalfType, + }, + { + cards: [13]int{26, 39, 15, 28, 16, 42, 4, 17, 18, 31, 34, 47, 22}, + lai: []int{52, 53}, + target: SixPairsHalfType, + }, + { + cards: [13]int{26, 52, 15, 28, 53, 42, 4, 17, 18, 31, 34, 47, 22}, + lai: []int{52, 53}, + target: SixPairsHalfType, + }, + { + cards: [13]int{0, 13, 3, 3, 4, 30, 4, 4, 7, 7, 8, 0, 52}, + lai: []int{52, 53}, + target: SixPairsHalfType, + }, + //三顺子 + { + cards: [13]int{28, 16, 30, 5, 32, 44, 6, 7, 21, 35, 47, 22, 23}, + lai: []int{52, 53}, + target: ThreeContinuityType, + }, + { + cards: [13]int{1, 28, 16, 17, 18, 30, 31, 45, 20, 8, 36, 50, 12}, + lai: []int{52, 53}, + target: 0, + }, + { + cards: [13]int{52, 28, 16, 17, 18, 30, 31, 45, 20, 8, 36, 50, 52}, + lai: []int{52, 53}, + target: ThreeContinuityType, + }, + //三同花 + { + cards: [13]int{43, 42, 46, 26, 29, 33, 35, 34, 2, 1, 0, 5, 52}, + lai: []int{52, 53}, + target: ThreeColorsType, + }, + { + cards: [13]int{43, 42, 46, 26, 29, 33, 35, 34, 2, 1, 0, 5, 11}, + lai: []int{52, 53}, + target: ThreeColorsType, + }, + } + + for _, v := range data { + r := GetSpecialType(v.cards, v.lai) + t.Logf("cards: %v", PokersShow(v.cards[:])) + if r != v.target { + t.Errorf("判断错误:结果:%v 正确结果:%v\n", SpecialTypeName[r], SpecialTypeName[v.target]) + } else { + t.Logf("判断日志:结果:%v 正确结果:%v\n", SpecialTypeName[r], SpecialTypeName[v.target]) + } + } +} diff --git a/gamerule/thirteen/grade.go b/gamerule/thirteen/grade.go new file mode 100644 index 0000000..dc7c5d2 --- /dev/null +++ b/gamerule/thirteen/grade.go @@ -0,0 +1,26 @@ +package thirteen + +import "math" + +func GetCardsGrade(cards [13]int, allGroup map[int]*Group) int { + m := math.MinInt + for k := range allGroup { + sum := 0 + if k >= 1000000 { + return k + } + a := k / 10000 + b := (k - a*10000) / 100 + c := k - a*10000 - b*100 + for _, m := range []int{a, b, c} { + if m > PokersTypeZero && m <= PokersTypeOne { + sum += PokersTypeOne - m + } + } + if m <= sum { + m = sum + } + } + + return m +} diff --git a/gamerule/thirteen/logic.go b/gamerule/thirteen/logic.go new file mode 100644 index 0000000..e0cd97a --- /dev/null +++ b/gamerule/thirteen/logic.go @@ -0,0 +1,1189 @@ +package thirteen + +import ( + "fmt" + "sort" + "strings" +) + +func getIndex(a []int, e int) int { + for k, v := range a { + if v < 0 { + continue + } + if v == e { + return k + } + } + return -1 +} + +func compareLine(a, b []int, getFunc, cmpFunc func(int) int) (int, int, int) { + ac := make([]int, len(a)) + copy(ac, a) + sort.Sort(sort.Reverse(sort.IntSlice(ac))) + bc := make([]int, len(b)) + copy(bc, b) + sort.Sort(sort.Reverse(sort.IntSlice(bc))) + var maxA, maxB int + for i := 0; i < 15; i++ { + if len(ac) == 0 || len(bc) == 0 { + return 0, -1, -1 + } + maxA = -1 + for _, v := range ac { + if v < 0 { + continue + } + if getFunc(v) > getFunc(maxA) { + maxA = v + } + } + if maxA < 0 { + break + } + + maxB = -1 + for _, v := range bc { + if v < 0 { + continue + } + if getFunc(v) > getFunc(maxB) { + maxB = v + } + } + if maxB < 0 { + break + } + + ca, cb := cmpFunc(maxA), cmpFunc(maxB) + if ca > cb { + return 1, getIndex(a, maxA), getIndex(b, maxB) + } + if ca < cb { + return -1, getIndex(a, maxA), getIndex(b, maxB) + } + ac = DelCards(ac, []int{maxA}) + bc = DelCards(bc, []int{maxB}) + } + return 0, -1, -1 +} + +// CompareColorByLogic 依次比最大逻辑点数牌的花色大小 +func CompareColorByLogic(a, b []int) int { + n, _, _ := compareLine(a, b, ToLogic, ToColor) + return n +} + +func IndexCompareColorByLogic(a, b []int) (int, int, int) { + return compareLine(a, b, ToLogic, ToColor) +} + +// CompareLogic 依次比逻辑点数大小 +func CompareLogic(a, b []int) int { + n, _, _ := compareLine(a, b, ToLogic, ToLogic) + return n +} + +func IndexCompareLogic(a, b []int) (int, int, int) { + return compareLine(a, b, ToLogic, ToLogic) +} + +func CompareFirstLogic(a, b []int) int { + if len(a) != 0 && len(b) != 0 { + if ToLogic(a[0]) > ToLogic(b[0]) { + return 1 + } else if ToLogic(a[0]) < ToLogic(b[0]) { + return -1 + } + } + return 0 +} + +// Group 出牌组合 +type Group struct { + Head [3]int + Mid [5]int + End [5]int + PokerType int +} + +func (p *Group) String() string { + buf := strings.Builder{} + buf.WriteString("\tHead:") + buf.WriteString(PokerName(p.Head[0])) + for _, v := range p.Head[1:] { + buf.WriteString(",") + buf.WriteString(PokerName(v)) + } + buf.WriteString("\tMid:") + buf.WriteString(PokerName(p.Mid[0])) + for _, v := range p.Mid[1:] { + buf.WriteString(",") + buf.WriteString(PokerName(v)) + } + buf.WriteString("\tEnd:") + buf.WriteString(PokerName(p.End[0])) + for _, v := range p.End[1:] { + buf.WriteString(",") + buf.WriteString(PokerName(v)) + } + buf.WriteString(fmt.Sprintf("\tPokerType:%v", SpecialTypeName[p.PokerType])) + return buf.String() +} + +func thanT(c, cs []int) bool { + num := 0 + for k, v := range c { + if cs[k] == v { + num++ + } + } + if len(c) == 3 && len(cs) == 3 && num == 3 { + return true + } + if len(c) == 5 && len(cs) == 5 && num == 5 { + return true + } + return false +} + +type Logic struct { + LaiZi []int +} + +// GetType 获取牌型 +// cards 牌 +func (l *Logic) GetType(cards []int) int { + return GetType(cards, l.LaiZi) +} + +func (l *Logic) isLaiZi(c int) bool { + if c < 0 { + return false + } + for _, v := range l.LaiZi { + if v < 0 { + continue + } + if v == c { + return true + } + } + return false +} + +func (l *Logic) SortMidAndEnd(cards *Group) *Group { + if cards.Head[0] == -1 { + return nil + } + a := l.GetType(cards.Head[:]) + b := l.GetType(cards.Mid[:]) + c := l.GetType(cards.End[:]) + if a < b || a < c { + return nil // 牌型错了就不要了 + } + + if a == b { + switch a { + case PokersTypeThree: + // 有癞子,癞子一定在三条中 + r1, c1 := FindMaxThreeAAA(cards.Head[:], l.LaiZi) + r2, c2 := FindMaxThreeAAA(cards.Mid[:], l.LaiZi) + if len(c1) > 0 && len(c2) > 0 { + // 比三条 + n := CompareFirstLogic(c1, c2) + if n == 1 { + // 交换三条,结束 + var cp1 []int + for k, v := range cards.Head { + for i, n := range r1 { + if v == n { + cards.Head[k] = -1 + r1[i] = -1 + cp1 = append(cp1, n) + } + } + } + r1 = cp1 + var cp2 []int + for k, v := range cards.Mid { + for i, n := range r2 { + if v == n { + cards.Mid[k] = -1 + r2[i] = -1 + cp2 = append(cp2, n) + } + } + } + r2 = cp2 + for k, v := range cards.Head { + if v == -1 { + for m, n := range r2 { + if n != -1 { + cards.Head[k] = n + r2[m] = -1 + } + } + } + } + for k, v := range cards.Mid { + if v == -1 { + for m, n := range r1 { + if n != -1 { + cards.Mid[k] = n + r1[m] = -1 + } + } + } + } + } else if n == 0 { // 三条点数相同 + // 比逻辑点数 + changeMid := append(DelCards(cards.Mid[:], r2), c2...) + n = CompareLogic(c1, changeMid) + if n == 1 { + // 不可能 + } else if n == 0 { // 逻辑点数相同 + // 比花色,比三条就行了 + //n, index1, index2 := IndexCompareColorByLogic(c1, c2) + n = 0 + if n == 1 { + // 花色交换,结束 + //if index1 >= 0 && index2 >= 0 { + // for k, v := range cards.Head { + // if v == r1[index1] { + // cards.Head[k] = -1 + // break + // } + // } + // for k, v := range cards.Mid { + // if v == r2[index2] { + // cards.Mid[k] = -1 + // break + // } + // } + // for k, v := range cards.Head { + // if v == -1 && r2[index2] >= 0 { + // cards.Head[k] = r2[index2] + // break + // } + // } + // for k, v := range cards.Mid { + // if v == -1 && r1[index1] >= 0 { + // cards.Mid[k] = r1[index1] + // break + // } + // } + //} + } else if n == 0 { + // 比癞子数 + if l.LaiZiCount(r1) < l.LaiZiCount(r2) { + // 中墩三条中的癞子和头墩三条中的非癞子牌交换一张,结束 + indexMid := -1 + indexHead := -1 + for k, v := range cards.Mid { + if l.isLaiZi(v) { + indexMid = k + break + } + } + for k, v := range cards.Head { + if v >= 0 && !l.isLaiZi(v) { + indexHead = k + break + } + } + if indexMid >= 0 && indexHead >= 0 { + cards.Head[indexHead], cards.Mid[indexMid] = cards.Mid[indexMid], cards.Head[indexHead] + } + } + } + } + } + } + + case PokersTypePair: // 最多有一个癞子,并且癞子一定在对子中 + r1, c1 := FindMaxPairs(cards.Head[:], l.LaiZi) + r2, c2 := FindMaxPairs(cards.Mid[:], l.LaiZi) + if len(c1) > 0 && len(c2) > 0 { + n := CompareFirstLogic(c1, c2) + if n == 1 { + // 交换对子,结束 + var cp1 []int + for k, v := range cards.Head { + for i, n := range r1 { + if v == n { + cards.Head[k] = -1 + r1[i] = -1 + cp1 = append(cp1, n) + } + } + } + r1 = cp1 + var cp2 []int + for k, v := range cards.Mid { + for i, n := range r2 { + if v == n { + cards.Mid[k] = -1 + r2[i] = -1 + cp2 = append(cp2, n) + } + } + } + r2 = cp2 + for k, v := range cards.Head { + if v == -1 { + for m, n := range r2 { + if n != -1 { + cards.Head[k] = n + r2[m] = -1 + } + } + } + } + for k, v := range cards.Mid { + if v == -1 { + for m, n := range r1 { + if n != -1 { + cards.Mid[k] = n + r1[m] = -1 + } + } + } + } + } else if n == 0 { + // 比点数 + nHead := cards.Head + nMid := cards.Mid + for k, v := range nHead { + if v < 0 { + continue + } + if l.isLaiZi(v) { + nHead[k] = ToLogic(c1[0]) + 13*3 + break + } + } + for k, v := range nMid { + if v < 0 { + continue + } + if l.isLaiZi(v) { + nHead[k] = ToLogic(c2[0]) + 13*3 + break + } + } + n, index1, index2 := IndexCompareLogic(nHead[:], nMid[:]) + if n == 1 { + // 最大逻辑点数牌交换,结束 + if index1 >= 0 && index2 >= 0 { + cards.Head[index1], cards.Mid[index2] = cards.Mid[index2], cards.Head[index1] + } + } else if n == 0 { // 点数相同 + // 比花色 + //n, index1, index2 := IndexCompareColorByLogic(nHead[:], nMid[:]) + n = 0 + if n == 1 { + // 交换花色,结束 + //if index1 >= 0 && index2 >= 0 { + // cards.Head[index1], cards.Mid[index2] = cards.Mid[index2], cards.Head[index1] + //} + } else if n == 0 { + // 比癞子多少 + if l.LaiZiCount(r1) < l.LaiZiCount(r2) { + // 中墩对子中有一个癞子,头墩没有癞子 + // 中墩对中的癞子和头墩对子中的非癞子牌交换一张,结束 + indexMid := -1 + indexHead := -1 + for k, v := range cards.Mid { + if l.isLaiZi(v) { + indexMid = k + break + } + } + for k, v := range cards.Head { + if v >= 0 && v == r1[0] { + indexHead = k + break + } + } + if indexMid >= 0 && indexHead >= 0 { + cards.Head[indexHead], cards.Mid[indexMid] = cards.Mid[indexMid], cards.Head[indexHead] + } + } + } + } + } + } + + case PokersTypeOne: + // 都不会有癞子 + n, index1, index2 := IndexCompareLogic(cards.Head[:], cards.Mid[:]) + if n == 1 { + // 交换最大逻辑点数的牌 + if index1 >= 0 && index2 >= 0 { + cards.Head[index1], cards.Mid[index2] = cards.Mid[index2], cards.Head[index1] + } + } else if n == 0 { + // 点数相同比花色 + //n, index1, index2 := IndexCompareColorByLogic(cards.Head[:], cards.Mid[:]) + //if n == 1 { + // // 交换最大花色的牌 + // if index1 >= 0 && index2 >= 0 { + // cards.Head[index1], cards.Mid[index2] = cards.Mid[index2], cards.Head[index1] + // } + //} + } + } + } + + //换牌之后重新识别 + a = l.GetType(cards.Head[:]) + b = l.GetType(cards.Mid[:]) + c = l.GetType(cards.End[:]) + if a < b || a < c { + return nil + } + + if b < c { + cards.Mid, cards.End = cards.End, cards.Mid + } else if c == b { + n := l.CompareFive(cards.Mid, cards.End) + if n == 1 { + cards.Mid, cards.End = cards.End, cards.Mid + } + } + return cards +} + +func (l *Logic) delPokers(poker []*Group) []*Group { + if len(poker) == 0 { + return nil + } + //去重 + for _, v := range poker { + num := 0 + s := 0 + for i := 0; i < len(poker); i++ { + if poker[i].Head[0] == -1 { + continue + } + if thanT(poker[i].Head[:], v.Head[:]) { + num++ + } + if thanT(poker[i].Mid[:], v.Mid[:]) { + num++ + } + if thanT(poker[i].End[:], v.End[:]) { + num++ + } + if num == 3 { + s++ + if s > 1 { + poker[i] = &Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}} + } + } + num = 0 + } + } + //修正 + for k, v := range poker { + if v.Head[0] != -1 { + a := l.SortMidAndEnd(v) + if a != nil { + poker[k] = a + } else { + poker[k] = &Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}} + } + } + } + return poker +} + +// 查找牌型 返回 (是否找到,找到的牌,找到牌之后剩余的牌) +func (l *Logic) getPokerTypeCards(cards []int, n int) (b bool, card, remainCards []int) { + if len(cards) < 3 { + return + } + switch n { + case PokersTypeFive: + card, _ := FindMaxFive(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeStraightFlush: + //找同花顺 + card, _ := FindMaxSameColorFlush(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeFour: + //找铁支 + card, _ := FindMaxFourAAAA(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeFullHouse: + //找葫芦 + card, _ := FindMaxGourdCards(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeFlush: + //找同花 + card, _ := FindMaxSameColors(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeStraight: + //找顺子 + card, _ := FindMaxFlush(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeThree: + //找三条 + card, _ := FindMaxThreeAAA(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + case PokersTypeTwoPairs: + //找对子 + card, _ := FindMaxPairs(cards, l.LaiZi) + if len(card) != 0 { + return true, card, DelCards(cards, card) + } + } + + return false, card, remainCards +} + +func (l *Logic) findAllCombine(cards [13]int) (pokers []*Group) { + copyCards := make([]int, 13) + copy(copyCards, cards[:]) + for i := 0; i < 8; i++ { + pokers = append(pokers, &Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}}) + } + poker := &Group{} + index := PokersTypeFive + for zz := 0; zz < 10; zz++ { + for _, v := range pokers { + if v != nil && v.Head[0] == -1 { + poker = v + break + } + } + // 从尾墩往前,从大到小牌型顺序获取组合 + for i := index; i < PokersTypePair; i++ { + b, card, cards2 := l.getPokerTypeCards(copyCards, i) + if b { + //fmt.Printf("找到牌型%v----%v\n", PokersShow(card), PokersShow(cards2)) + copyCards = cards2 + if poker.End[0] == -1 { + if i != PokersTypeTwoPairs { + copy(poker.End[:], card) + } else { + if len(copyCards) == 11 { // 对子 + copy(poker.End[2:], card) + } else if len(copyCards) == 9 { // 两对 + copy(poker.End[:], card) + } else { + copyCards = append(copyCards, card...) + i++ + } + } + i-- + } else if poker.Mid[0] == -1 { + if i == PokersTypeFive || i == PokersTypeStraightFlush || i == PokersTypeFullHouse || i == PokersTypeFlush || i == PokersTypeStraight { + copy(poker.Mid[:], card) + } else if i == PokersTypeFour { + if len(copyCards) != 1 { + copy(poker.Mid[:], card) + } else { + copyCards = append(copyCards, card...) + i++ + } + } else if i == PokersTypeThree { + al := len(copyCards) + if al == 5 || al == 6 || al == 7 { + copy(poker.Mid[:], card) + } else { + copyCards = append(copyCards, card...) + i++ + } + } else if i == PokersTypeTwoPairs { + al := len(copyCards) + if al == 7 || al == 6 { + copy(poker.Mid[2:], card) + } else if al == 5 || al == 4 { + copy(poker.Mid[:], card) + } else if al == 8 { + copy(poker.Mid[:], card) + } else { + copyCards = append(copyCards, card...) + i++ + } + } else { + i++ + } + i-- + } else { + if i == PokersTypeTwoPairs { + if len(copyCards) == 2 || len(copyCards) == 1 { + copy(poker.Head[:], card) + } else if len(copyCards) == 4 || len(copyCards) == 5 { + copy(poker.Head[:], card) + copyCards = DelCards(copyCards, card[:]) + } else { + copyCards = append(copyCards, card...) + i++ + } + } else { + copyCards = append(copyCards, card...) + i++ + } + i-- + } + //fmt.Printf("填充后%v\n", poker) + } + } + //fmt.Println("-->", poker) + if poker.End[2] == -1 { + for k, v := range pokers { + if v.End[2] == -1 { + pokers[k] = &Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}} + } + } + pokers = l.delPokers(pokers) + return + } + //fmt.Println(poker) + // 填充剩余位置的牌 + ci := 0 + for m, n := range poker.End { + if n == -1 && ci < len(copyCards) { + poker.End[m] = copyCards[ci] + ci++ + } + } + for m, n := range poker.Mid { + if n == -1 && ci < len(copyCards) { + poker.Mid[m] = copyCards[ci] + ci++ + } + } + for m, n := range poker.Head { + if n == -1 && ci < len(copyCards) { + poker.Head[m] = copyCards[ci] + ci++ + } + } + //fmt.Println(poker) + //fmt.Println() + if index < PokersTypePair { + index = l.GetType(poker.End[:]) + 1 + copyCards = make([]int, 13) + copy(copyCards, cards[:]) + for k, v := range pokers { + if l.GetType(v.End[:]) == 9 { + pokers[k] = &Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}} + break + } + } + } else { + break + } + } + pokers = l.delPokers(pokers) + return +} + +// GetAllCombine 获取所有组合 +// 特殊组合的 PokerType > 0,无牌数据,数据为 -1 +// 普通组合的 PokerType = 0,有牌数据 +func (l *Logic) GetAllCombine(cards [13]int) (pokers []*Group) { + tp := l.GetSpecialType(cards) + if tp != 0 { + poker := Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}} + poker.PokerType = tp + pokers = append(pokers, &poker) + } + aps := l.findAllCombine(cards) + if len(aps) != 0 { + for _, v := range aps { + pokers = append(pokers, v) + } + } + return +} + +// Suggest 获取所有组合,带牌型标记 +func (l *Logic) Suggest(cards [13]int) map[int]*Group { + m := make(map[int]*Group) + pokers := l.GetAllCombine(cards) + if pokers != nil { + for _, v := range pokers { + num := 0 + if v != nil && v.PokerType != 0 { + m[v.PokerType*1000000] = v + } + if v != nil && v.PokerType == 0 && v.Head[0] != -1 && v.Head[1] != -1 && v.Head[2] != -1 { + num += l.GetType(v.Head[:]) * 10000 + num += l.GetType(v.Mid[:]) * 100 + num += l.GetType(v.End[:]) + m[num] = v + } + } + } + return m +} + +func (l *Logic) GetSpecialType(cards [13]int) int { + return GetSpecialType(cards, l.LaiZi) +} + +func (l *Logic) LaiZiCount(cards []int) int { + var ret int + for _, v := range cards { + if v < 0 { + continue + } + for _, vv := range l.LaiZi { + if vv < 0 { + continue + } + if v == vv { + ret++ + break + } + } + } + return ret +} + +// 将癞子牌变成 黑桃A +func (l *Logic) laiZiTo51(card []int) { + for k, v := range card { + if v < 0 { + continue + } + for _, vv := range l.LaiZi { + if vv < 0 { + continue + } + if v == vv { + card[k] = 51 + break + } + } + } +} + +func (l *Logic) CompareHead(c, cs [3]int) int { + a := l.GetType(c[:]) + b := l.GetType(cs[:]) + if a < b { + return 1 + } else if a > b { + return -1 + } + + var r1, r2 []int + var c1, c2 []int + switch a { + case PokersTypeThree: + r1, c1 = FindMaxThreeAAA(c[:], l.LaiZi) + r1, c2 = FindMaxThreeAAA(cs[:], l.LaiZi) + + case PokersTypePair: + r1, c1 = FindMaxPairs(c[:], l.LaiZi) + r2, c2 = FindMaxPairs(cs[:], l.LaiZi) + + case PokersTypeOne: + // 乌龙肯定不会有癞子 + n := CompareLogic(c[:], cs[:]) + if n != 0 { + return n + } + n = CompareColorByLogic(c[:], cs[:]) + if n != 0 { + return n + } + return 0 + } + + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + re1 := DelCards(c[:], r1) + l.laiZiTo51(re1) + c1 = append(c1, re1...) + re2 := DelCards(cs[:], r2) + l.laiZiTo51(re2) + c2 = append(c2, re2...) + n = CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + // 比癞子数 + z1, z2 := l.LaiZiCount(c[:]), l.LaiZiCount(cs[:]) + if z1 < z2 { + return 1 + } + if z1 > z2 { + return -1 + } + return 0 +} + +func (l *Logic) CompareFive(c, cs [5]int) int { + a := l.GetType(c[:]) + b := l.GetType(cs[:]) + if a < b { + return 1 + } else if a > b { + return -1 + } + + // 牌型相同,五张牌相比 + switch a { + case PokersTypeFive: + _, c1 := FindMaxFive(c[:], l.LaiZi) + _, c2 := FindMaxFive(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeStraightFlush: + _, c1 := FindMaxSameColorFlush(c[:], l.LaiZi) + _, c2 := FindMaxSameColorFlush(cs[:], l.LaiZi) + // 比点数 + // 5 4 3 2 A + ch1, ch2 := false, false + if ToPoint(c1[0]) == 5 { + ch1 = true + } + if ToPoint(c2[0]) == 5 { + ch2 = true + } + if ch1 && ch2 { + // 比花色 + //n := CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + } else if !ch1 && !ch2 { + n := CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + } else { + if ch1 { + return -1 + } + return 1 + } + + case PokersTypeFour: + r1, c1 := FindMaxFourAAAA(c[:], l.LaiZi) + r2, c2 := FindMaxFourAAAA(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + re1 := DelCards(c[:], r1) + l.laiZiTo51(re1) + c1 = append(c1, re1...) + re2 := DelCards(cs[:], r2) + l.laiZiTo51(re2) + c2 = append(c2, re2...) + n = CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeFullHouse: + _, c1 := FindMaxGourdCards(c[:], l.LaiZi) + _, c2 := FindMaxGourdCards(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + n = CompareFirstLogic(c1[3:], c2[3:]) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeFlush: + _, c1 := FindMaxSameColors(c[:], l.LaiZi) + _, c2 := FindMaxSameColors(cs[:], l.LaiZi) + // 比点数 + n := CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeStraight: + _, c1 := FindMaxFlush(c[:], l.LaiZi) + _, c2 := FindMaxFlush(cs[:], l.LaiZi) + // 比点数 + // 5 4 3 2 A + ch1, ch2 := false, false + if ToPoint(c1[0]) == 5 { + ch1 = true + } + if ToPoint(c2[0]) == 5 { + ch2 = true + } + if ch1 && ch2 { + // 比花色 + //n := CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + } else if !ch1 && !ch2 { + n := CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + } else { + if ch1 { + return -1 + } + return 1 + } + + case PokersTypeThree: + r1, c1 := FindMaxThreeAAA(c[:], l.LaiZi) + r2, c2 := FindMaxThreeAAA(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + re1 := DelCards(c[:], r1) + l.laiZiTo51(re1) + c1 = append(c1, re1...) + re2 := DelCards(cs[:], r2) + l.laiZiTo51(re2) + c2 = append(c2, re2...) + n = CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeTwoPairs: + r1, c1 := FindMaxPairs(c[:], l.LaiZi) + r2, c2 := FindMaxPairs(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + r12, c12 := FindMaxPairs(DelCards(c[:], r1), l.LaiZi) + r22, c22 := FindMaxPairs(DelCards(cs[:], r2), l.LaiZi) + n = CompareFirstLogic(c12, c22) + if n != 0 { + return n + } + + c1 = append(c1, c12...) + c2 = append(c2, c22...) + + re1 := DelCards(c[:], append(r1, r12...)) + l.laiZiTo51(re1) + c1 = append(c1, re1...) + re2 := DelCards(cs[:], append(r2, r22...)) + l.laiZiTo51(re2) + c2 = append(c2, re2...) + n = CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypePair: + r1, c1 := FindMaxPairs(c[:], l.LaiZi) + r2, c2 := FindMaxPairs(cs[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n + } + re1 := DelCards(c[:], r1) + l.laiZiTo51(re1) + c1 = append(c1, re1...) + re2 := DelCards(cs[:], r2) + l.laiZiTo51(re2) + c2 = append(c2, re2...) + n = CompareLogic(c1, c2) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c1, c2) + //if n != 0 { + // return n + //} + + case PokersTypeOne: + // 乌龙不会有癞子 + // 比点数 + n := CompareLogic(c[:], cs[:]) + if n != 0 { + return n + } + // 比花色 + //n = CompareColorByLogic(c[:], cs[:]) + //if n != 0 { + // return n + //} + return 0 + } + + // 比癞子数 + z1, z2 := l.LaiZiCount(c[:]), l.LaiZiCount(cs[:]) + if z1 < z2 { + return 1 + } + if z1 > z2 { + return -1 + } + return 0 +} + +func (l *Logic) IsDP(head [3]int, mid, end [5]int) bool { + if l.CompareFive(mid, end) > 0 { + return true + } + + h := l.GetType(head[:]) + m := l.GetType(mid[:]) + if h < m { + return true + } + + if h == m { // 牌型相同,比点数,比癞子 + switch h { + case PokersTypeThree: // 如果有癞子,癞子一定在三条里 + r1, c1 := FindMaxThreeAAA(head[:], l.LaiZi) + r2, c2 := FindMaxThreeAAA(mid[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n == 1 + } + // 比最大牌点数 + changeMid := append(DelCards(mid[:], r2), c2...) + n = CompareLogic(c1, changeMid) + if n != 0 { + return n == 1 + } + // 比花色 + //n = CompareColorByLogic(c1, changeMid) + //if n != 0 { + // return n == 1 + //} + // 比癞子数 + z1, z2 := l.LaiZiCount(r1), l.LaiZiCount(r2) + if z1 < z2 { + return true + } + if z1 > z2 { + return false + } + + case PokersTypePair: // 如果有癞子,癞子一定在对子中 + r1, c1 := FindMaxPairs(head[:], l.LaiZi) + r2, c2 := FindMaxPairs(mid[:], l.LaiZi) + // 比点数 + n := CompareFirstLogic(c1, c2) + if n != 0 { + return n == 1 + } + // 比最大牌点数 + changeMid := append(DelCards(mid[:], r2), c2...) + n = CompareLogic(c1, changeMid) + if n != 0 { + return n == 1 + } + // 比花色 + //n = CompareColorByLogic(c1, changeMid) + //if n != 0 { + // return n == 1 + //} + // 比癞子数 + z1, z2 := l.LaiZiCount(r1), l.LaiZiCount(r2) + if z1 < z2 { + return true + } + if z1 > z2 { + return false + } + + case PokersTypeOne: // 乌龙不会有癞子 + // 比点数 + n := CompareLogic(head[:], mid[:]) + if n != 0 { + return n == 1 + } + // 比花色 + //n = CompareColorByLogic(head[:], mid[:]) + //if n != 0 { + // return n == 1 + //} + } + } + + return false +} diff --git a/gamerule/thirteen/logic_test.go b/gamerule/thirteen/logic_test.go new file mode 100644 index 0000000..729d406 --- /dev/null +++ b/gamerule/thirteen/logic_test.go @@ -0,0 +1,270 @@ +package thirteen + +import ( + "fmt" + "math/rand" + "sort" + "strings" + "testing" +) + +func TestFindAllPokers(t *testing.T) { + l := new(Logic) + l.LaiZi = []int{52, 53} + data := [][13]int{ + {31, 3, 2, 8, 7, 15, 44, 49, 9, 16, 10, 19, 6}, + {13, 23, 24, 36, 37, 38, 39, 51, 51, 50, 10, 11, 12}, + {6 + 39, 7 + 39, 8 + 39, 2 + 13, 3 + 13, 4 + 13, 5 + 13, 6 + 13, 4 + 26, 5 + 26, 6 + 26, 7 + 26, 8 + 26}, + {27, 28, 34, 12, 36, 39, 35, 46, 7, 2, 6, 24, 50}, + {27, 51, 33, 29, 45, 7, 34, 31, 36, 38, 44, 10, 30}, + {1, 2, 3, 8, 9, 0, 20, 32, 12, 45, 12}, + {18, 5, 11, 20, 19, 36, 42, 17, 21, 25, 43, 34, 1}, + {3, 9, 45, 31, 38, 21, 33, 36, 41, 20, 7, 28, 32}, + + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + {0 + 13, 1 + 13, 2 + 13, 3 + 13, 4 + 13, 5 + 13, 6 + 13, 7 + 13, 8 + 13, 9 + 13, 10 + 13, 11 + 13, 12 + 13}, + {0, 14, 28, 3, 17, 31, 45, 7, 21, 22, 36, 50, 25}, + {0, 13, 26, 39, 12, 25, 38, 11, 37, 50, 10, 23, 49}, + {1, 2, 3, 4, 5, 14, 15, 16, 17, 18, 27, 28, 29}, + {0, 13, 26, 39, 1, 14, 27, 40, 3, 16, 29, 42, 50}, + {7, 20, 21, 34, 35, 48, 10, 12, 24, 50, 12, 25, 0}, + {1, 15, 29, 43, 5, 32, 46, 14, 28, 30, 31, 45, 7}, + {0, 26, 1, 2, 28, 4, 30, 6, 32, 9, 35, 11, 37}, + {0, 13, 26, 3, 16, 29, 5, 18, 31, 9, 22, 48, 12}, + {0, 26, 2, 15, 4, 30, 6, 19, 8, 21, 11, 24, 50}, + {0, 26, 2, 15, 4, 30, 6, 19, 8, 21, 10, 23, 24}, + {0, 1, 15, 3, 17, 29, 43, 31, 19, 7, 36, 24, 12}, + {0, 3, 6, 9, 12, 13, 19, 20, 21, 23, 26, 32, 38}, + + //三同花 + {15, 17, 19, 23, 25, 28, 30, 33, 36, 38, 44, 47, 48}, + {26, 27, 29, 31, 32, 44, 46, 48, 49, 51, 0, 8, 12}, + {39, 42, 44, 45, 47, 0, 1, 7, 9, 11, 13, 17, 21}, + {43, 42, 46, 26, 29, 33, 35, 34, 2, 1, 0, 5, 52}, + + //三顺子 + {28, 16, 30, 5, 32, 44, 6, 7, 21, 35, 47, 22, 23}, + {1, 28, 16, 17, 18, 30, 31, 45, 20, 8, 36, 50, 12}, + + //六对半 + {17, 30, 5, 31, 6, 32, 8, 47, 23, 36, 38, 51, 39}, + {26, 39, 15, 28, 16, 42, 4, 17, 18, 31, 34, 47, 22}, + {7, 20, 21, 34, 10, 23, 24, 37, 38, 51, 15, 28, 43}, + + //五对三条 + {17, 30, 5, 31, 6, 32, 8, 47, 23, 36, 12, 25, 38}, + {26, 39, 15, 28, 16, 42, 4, 17, 18, 31, 9, 22, 35}, + {7, 20, 21, 34, 10, 23, 24, 37, 38, 51, 1, 14, 40}, + + //四套三条 + {5, 18, 44, 6, 19, 32, 7, 33, 46, 10, 23, 36, 50}, + {1, 27, 40, 4, 17, 30, 6, 19, 32, 11, 37, 50, 51}, + {16, 29, 42, 18, 31, 44, 8, 21, 47, 10, 23, 36, 25}, + + //凑一色 + {13, 14, 40, 16, 42, 18, 44, 19, 46, 47, 23, 49, 25}, + {0, 1, 2, 28, 3, 29, 5, 31, 8, 10, 11, 12, 37}, + {39, 40, 17, 18, 44, 45, 21, 48, 49, 24, 50, 25, 51}, + + //全小 + {2, 15, 28, 41, 4, 17, 30, 43, 5, 18, 6, 45, 6}, + {3, 16, 42, 5, 18, 31, 6, 19, 32, 5, 18, 30, 45}, + {14, 27, 40, 15, 28, 41, 4, 17, 5, 18, 6, 19, 6}, + + //全大 + {8, 21, 47, 9, 22, 35, 48, 10, 23, 36, 12, 25, 51}, + {7, 20, 33, 8, 21, 34, 12, 25, 38, 12, 25, 38, 10}, + {7, 21, 35, 23, 11, 38, 25, 33, 48, 49, 50, 12, 25}, + + //三分天下 + {4, 17, 30, 43, 5, 18, 31, 44, 7, 20, 33, 46, 51}, + {6, 19, 32, 45, 7, 20, 33, 46, 9, 22, 35, 48, 49}, + {10, 23, 36, 49, 11, 24, 37, 50, 12, 25, 38, 51, 13}, + + // + {24, 18, 42, 25, 13, 15, 2, 7, 39, 38, 34, 33, 30}, + {51, 51, 51, 51, 12, 52, 12, 11, 10, 9, 8, 7, 6}, + {46, 35, 30, 50, 34, 9, 25, 41, 3, 26, 2, 6, 45}, + } + + for _, v := range data { + cards := v + fmt.Println(PokersShow(cards[:])) + for k, v := range l.Suggest(cards) { + fmt.Println(k, SpecialName(k), v) + + if k >= 1000000 { + continue + } + cs := make([]int, 0, 13) + for _, v := range v.Head { + if v >= 0 { + cs = append(cs, v) + } + } + for _, v := range v.Mid { + if v >= 0 { + cs = append(cs, v) + } + } + for _, v := range v.End { + if v >= 0 { + cs = append(cs, v) + } + } + sort.Ints(cards[:]) + sort.Ints(cs) + if !arrEqual([][]int{cards[:]}, [][]int{cs}) { + fmt.Println("牌不对") + t.Fatal() + } + if l.IsDP(v.Head, v.Mid, v.End) { + fmt.Println("倒排") + t.Fatal() + } + } + } +} + +func TestC(t *testing.T) { + for i := 0; i < 10; i++ { + rand.Shuffle(len(pokers2Test), func(i, j int) { + pokers2Test[i], pokers2Test[j] = pokers2Test[j], pokers2Test[i] + }) + + v := pokers2Test[:10] + + buf := strings.Builder{} + buf.WriteString("{") + + for m, n := range v { + buf.WriteString(fmt.Sprint(n)) + if m < len(v)-1 { + buf.WriteString(",") + } + } + + buf.WriteString(",-2},") + fmt.Println(buf.String()) + } +} + +func TestLogic_CompareHead(t *testing.T) { + l := new(Logic) + l.LaiZi = []int{52, 53} + data := [][]int{ + // 比花色的 + //{1, 27, 49, 50, 43, 42, 1}, + //{47, 20, 23, 29, 36, 42, -1}, + //{0, 2, 18, 31, 21, 29, -1}, + //{45, 7, 18, 13, 15, 29, 1}, + //{14, 4, 46, 9, 1, 11, -1}, + //{7, 3, 24, 43, 50, 44, 1}, + //{5, 4, 31, 15, 33, 14, 1}, + //{21, 21, 32, 31, 38, 28, 1}, + //{47, 38, 49, 30, 34, 23, 1}, + //{14, 10, 16, 29, 30, 35, 1}, + //// + //{0, 52, 8, 7, 8, 9, 1}, + //{0, 52, 52, 7, 8, 9, 1}, + //{0, 52, 52, 7, 51, 53, 1}, + //{0, 52, 51, 7, 51, 53, -1}, + //{8, 52, 51, 7, 51, 53, 1}, + //{7, 52, 51, 7, 51, 53, 0}, + //{7, 9, 22, 22, 7, 53, -1}, + //{7, 48, 22, 22, 7, 53, 1}, + //{7, 48, 22, 22, 6, 53, 1}, + //{7, 48, 22, 22, 20, 53, -1}, + + // 不比花色 + {1, 27, 49, 50, 43, 42, 1}, + {47, 20, 23, 29, 36, 42, -1}, + {0, 2, 18, 31, 21, 29, -1}, + {45, 7, 18, 13, 15, 29, 1}, + {14, 4, 46, 9, 1, 11, -1}, + {7, 3, 24, 43, 50, 44, 1}, + {5, 4, 31, 15, 33, 14, 1}, + {21, 21, 32, 31, 38, 28, 1}, + {47, 38, 49, 30, 34, 23, 1}, + {14, 10, 16, 29, 30, 35, 1}, + // + {0, 52, 8, 7, 8, 9, 1}, + {0, 52, 52, 7, 8, 9, 1}, + {0, 52, 52, 7, 51, 53, 1}, + {0, 52, 51, 7, 51, 53, -1}, + {8, 52, 51, 7, 51, 53, 1}, + {7, 52, 51, 7, 51, 53, 0}, + {7, 9, 22, 22, 7, 53, 1}, + {7, 48, 22, 22, 7, 53, 1}, + {7, 48, 22, 22, 6, 53, 1}, + {7, 48, 22, 22, 20, 53, 1}, + } + + for _, v := range data { + a := [3]int{} + b := [3]int{} + copy(a[:], v[:3]) + copy(b[:], v[3:6]) + R := -2 + if len(v) > 6 { + R = v[6] + } + r := l.CompareHead(a, b) + if r != R { + t.Fatalf("比牌错误:%v and %v 结果%v 正确结果%v\n", PokersShow(a[:]), PokersShow(b[:]), r, R) + } else { + t.Logf("比牌日志:%v and %v 结果%v 正确结果%v\n", PokersShow(a[:]), PokersShow(b[:]), r, R) + } + } +} + +func TestLogic_CompareFive(t *testing.T) { + l := new(Logic) + l.LaiZi = []int{52, 53} + data := [][]int{ + {5, 23, 10, 6, 1, 45, 41, 20, 1, 13, 1}, + {29, 31, 26, 1, 39, 31, 44, 17, 9, 34, -1}, + {24, 26, 9, 10, 0, 16, 30, 7, 37, 3, -1}, + {31, 30, 11, 33, 7, 18, 1, 36, 9, 3, 1}, + // 牌型不同 + {2, 15, 28, 0, 6, 12, 0, 1, 2, 3, -1}, + // 点数不同 + {2, 15, 28, 0, 6, 2, 15, 28, 0, 6, 0}, + {2, 15, 28, 0, 6, 2, 15, 28, 0, 7, -1}, + {0, 13, 4, 17, 6, 0, 13, 4, 17, 5, 1}, + {0, 13, 5, 18, 6, 0, 13, 4, 17, 6, 1}, + {1, 14, 4, 17, 6, 0, 13, 4, 17, 5, 1}, + // 花色不同 + {2, 15, 28, 0, 6, 2, 15, 28, 0, 19, 0}, + {41, 15, 28, 0, 6, 2, 15, 28, 0, 6, 0}, + {2, 15, 28, 0, 6, 2, 15, 28, 13, 6, 0}, + {3, 4, 5, 6, 52, 3, 52, 5, 6, 53, 1}, + {3, 17, 5, 6, 52, 3, 52, 5, 6, 53, -1}, + {3, 4, 5, 6, 7, 16, 52, 18, 19, 53, 1}, + {3, 52, 5, 6, 20, 3, 17, 18, 19, 53, 0}, + {3, 52, 5, 6, 46, 3, 17, 18, 19, 53, 0}, + // 癞子数不同 + {52, 52, 7, 20, 12, 52, 7, 20, 33, 25, -1}, + {52, 33, 7, 20, 12, 52, 7, 20, 33, 25, 0}, + } + + for _, v := range data { + a := [5]int{} + b := [5]int{} + copy(a[:], v[:5]) + copy(b[:], v[5:10]) + R := -2 + if len(v) > 10 { + R = v[10] + } + r := l.CompareFive(a, b) + if r != R { + t.Fatalf("比牌错误:%v and %v 结果%v 正确结果%v\n", PokersShow(a[:]), PokersShow(b[:]), r, R) + } else { + t.Logf("比牌日志:%v and %v 结果%v 正确结果%v\n", PokersShow(a[:]), PokersShow(b[:]), r, R) + } + } +} + +func TestLogic_IsDP(t *testing.T) { +} diff --git a/gamerule/thirteen/poker.go b/gamerule/thirteen/poker.go new file mode 100644 index 0000000..6b04cb6 --- /dev/null +++ b/gamerule/thirteen/poker.go @@ -0,0 +1,194 @@ +package thirteen + +import ( + "fmt" + "math/rand" + "strings" +) + +//牌序- A, K, Q, J,10, 9, 8, 7, 6, 5, 4, 3, 2 +//黑桃-51,50,49,48,47,46,45,44,43,42,41,40,39 +//红桃-38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花-25,24,23,22,21,20,19,18,17,16,15,14,13 +//方片-12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +const ( + PokersTypeZero = 0 + PokersTypeFive = 1 //五同 + PokersTypeStraightFlush = 2 //同花顺 + PokersTypeFour = 3 //铁支 + PokersTypeFullHouse = 4 //葫芦 + PokersTypeFlush = 5 //同花 + PokersTypeStraight = 6 //顺子 + PokersTypeThree = 7 //三条 + PokersTypeTwoPairs = 8 //两对 + PokersTypePair = 9 //对子 + PokersTypeOne = 10 //乌龙 + PokersTypeMax = 11 +) + +var PokersTypeName = map[int]string{ + PokersTypeZero: "-", + PokersTypeFive: "五同", + PokersTypeStraightFlush: "同花顺", + PokersTypeFour: "铁支", + PokersTypeFullHouse: "葫芦", + PokersTypeFlush: "同花", + PokersTypeStraight: "顺子", + PokersTypeThree: "三条", + PokersTypeTwoPairs: "两对", + PokersTypePair: "对子", + PokersTypeOne: "乌龙", +} + +// ToPoint 获取牌点数 +func ToPoint(c int) int { + if c > 53 || c < 0 { + return -1 + } + if c == 52 { + return 14 + } + if c == 53 { + return 15 + } + r := c % 13 + if r < 12 { + return r + 2 + } + if r == 12 { + return 1 + } + return r +} + +// ToLogic 获取逻辑点数 +func ToLogic(c int) int { + if c > 53 || c < 0 { + return -1 + } + if c == 52 { + return 13 + } + if c == 53 { + return 14 + } + return c % 13 +} + +// ToColor 获取花色 +func ToColor(c int) int { + if c > 51 || c < 0 { + return -1 + } + return c / 13 +} + +func PokerName(n int) string { + var AIRecordMap = map[int]string{ + 0: "2", + 1: "3", + 2: "4", + 3: "5", + 4: "6", + 5: "7", + 6: "8", + 7: "9", + 8: "T", + 9: "J", + 10: "Q", + 11: "K", + 12: "A", + 52: "w", // 小王 + 53: "W", // 大王 + } + + if n == -1 { + return "-1" + } + color := "" + switch n / 13 { + case 0: + color = "♦" + case 1: + color = "♣" + case 2: + color = "♥" + case 3: + color = "♠" + } + + if n > 51 { + return fmt.Sprint(AIRecordMap[n]) + } + + return fmt.Sprint(color, AIRecordMap[n%13]) +} + +func PokersShow(cards []int) string { + buf := strings.Builder{} + for _, v := range cards { + buf.WriteString(PokerName(v)) + buf.WriteString(",") + } + return buf.String() +} + +// Pokers 牌堆 +type Pokers struct { + N int // 几副牌 + HasKing bool // 有没有大小王 + AllCards []int +} + +func NewPokers(n int, hasKing bool) *Pokers { + p := &Pokers{ + N: n, + HasKing: hasKing, + } + count := n * 52 + if hasKing { + count += n * 2 + } + p.AllCards = make([]int, 0, count) + p.Init() + return p +} + +// Init 初始化牌 +func (this *Pokers) Init() { + this.AllCards = this.AllCards[:0] + num := 52 // 一副牌几张牌 + if this.HasKing { + num += 2 + } + for k := 0; k < this.N; k++ { + for i := 0; i < num; i++ { + this.AllCards = append(this.AllCards, i) + } + } + rand.Shuffle(len(this.AllCards), func(i, j int) { + this.AllCards[i], this.AllCards[j] = this.AllCards[j], this.AllCards[i] + }) +} + +// Get13Crads 单人获取13张牌 +func (this *Pokers) Get13Crads() (cards [13]int) { + if len(this.AllCards) < 13 { + this.Init() + } + for i := 0; i < 13; i++ { + cards[i] = this.Next() + } + return +} + +// Next 获取单张牌 +func (this *Pokers) Next() int { + if len(this.AllCards) == 0 { + this.Init() + } + c := this.AllCards[len(this.AllCards)-1] + this.AllCards = this.AllCards[:len(this.AllCards)-1] + return c +} diff --git a/gamerule/tienlen/card.go b/gamerule/tienlen/card.go new file mode 100644 index 0000000..36cb2cc --- /dev/null +++ b/gamerule/tienlen/card.go @@ -0,0 +1,2989 @@ +package tienlen + +import ( + "math/rand" + "slices" + "sort" +) + +const ( + Tienlen_Pass int = iota //nil + Single //1 单张 + Twin //2 对子 + Straight //3 顺子 + ColorStraight //4 同花顺 + Straight_Twin //5 连对(双顺 + Triple //6 三张 + Four_Bomb //7 炸弹 + Straight_Triple //8三顺 (333,444) + Plane_Single //9三带单 444 555 6 7 8 9带4张牌 最后一首可以带小于等于4张牌 + Plane_Twin //10三带双 +) + +// 不同牌分值 +const ( + Score1 int32 = 1 + Score5 int32 = 5 + Score10 int32 = 10 + Score15 int32 = 15 + Score20 int32 = 20 + Score25 int32 = 25 +) + +// 打到底不同牌分值百分比 +const ( + Score2End5 int32 = 50 + Score2End10 int32 = 100 + Score2End15 int32 = 150 + Score2End20 int32 = 200 + Score2End25 int32 = 250 +) + +// Color 黑桃0 梅花1 方片2 紅桃3 +func Color(c int32) int { + return int(c) / PER_CARD_COLOR_MAX +} + +func Value(c int32) int { + return int(c) % PER_CARD_COLOR_MAX +} + +func ValueStr(c int32) int { + if int(c+3)%PER_CARD_COLOR_MAX == 0 { + return PER_CARD_COLOR_MAX + } + return int(c+3) % PER_CARD_COLOR_MAX +} + +// CmpCard 比逻辑大小 +func CmpCard(a, b int32) bool { + if Value(a) > Value(b) { + return true + } else if Value(a) == Value(b) { + if Color(a) > Color(b) { + return true + } + } + return false +} + +func IsSingle(cards []int32) bool { //单张 + if len(cards) == 1 { + return true + } + return false +} + +func IsTwin(cards []int32) bool { //对子 + if len(cards) == 2 { + if Value(cards[0]) == Value(cards[1]) { + return true + } + } + return false +} + +// IsStraight 顺子 +// 至少3张,且不带2 +func IsStraight(cards []int32) bool { //顺子,至少3张 + if len(cards) < 3 || len(cards) > 12 { + return false + } + tmpCards := []int{} + for _, card := range cards { + if Value(card) == Card_Value_2 { + return false + } + tmpCards = append(tmpCards, Value(card)) + } + sort.Ints(tmpCards) + for i := 0; i < len(tmpCards)-1; i++ { + card := tmpCards[i] + nextCard := tmpCards[i+1] + if nextCard-card != 1 { //不相邻 + return false + } + } + //fmt.Println("顺子排序:", tmpCards) + return true +} + +// IsColorStraight 同花顺 +// 是顺子又是同花 +func IsColorStraight(cards []int32) bool { + // 先是顺子 + if !IsStraight(cards) { + return false + } + c := Color(cards[0]) + for _, card := range cards { + if c != Color(card) { + return false + } + } + return true +} + +// IsStraightTwin 双顺 +// 最少两连,且不带2 +func IsStraightTwin(cards []int32) bool { + if len(cards) < 4 || len(cards)%2 != 0 { + return false + } + tmpCards := []int{} + for _, card := range cards { + if Value(card) == Card_Value_2 { + return false + } + tmpCards = append(tmpCards, Value(card)) + } + sort.Ints(tmpCards) + for i := 0; i < len(tmpCards); i += 2 { + card := tmpCards[i] + nextCard := tmpCards[i+1] + if card != nextCard { + return false + } + if i < len(cards)-2 { + if tmpCards[i+2]-card != 1 { + return false + } + if card == Card_Value_2 || nextCard == Card_Value_2 { //2不能加入顺子中 + return false + } + } + } + return true +} + +// IsTriple 三张 +func IsTriple(cards []int32) bool { + if len(cards)%3 != 0 { + return false + } + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + return true +} + +// IsFourBomb 炸弹 +// 4张相同点数的牌 +func IsFourBomb(cards []int32) bool { + if len(cards) != 4 { + return false + } + if Value(cards[0]) != Value(cards[1]) { + return false + } + if Value(cards[1]) != Value(cards[2]) { + return false + } + if Value(cards[2]) != Value(cards[3]) { + return false + } + return true +} + +// IsStraightTriple 三顺 +// 至少两连,顺子不带2 +func IsStraightTriple(cards []int32) bool { //三顺 + if len(cards) == 0 { + return false + } + if len(cards)%3 != 0 { + return false + } + mapSTriple := make(map[int32]int32) + for _, card := range cards { + if Value(card) == Card_Value_2 { + return false + } + mapSTriple[int32(Value(card))]++ + } + if len(mapSTriple) > 0 { + valueSTriple := []int32{} + for card, num := range mapSTriple { + if num == 3 { + valueSTriple = append(valueSTriple, card) + } + } + if len(valueSTriple) != len(cards)/3 { + return false + } + if len(valueSTriple) > 0 { + sort.Slice(valueSTriple, func(i, j int) bool { + if valueSTriple[i] > valueSTriple[j] { + return false + } + return true + }) + tmpPairs := FindOneStraightWithWidth(len(valueSTriple), valueSTriple) + if len(tmpPairs) > 0 { + return true + } + } + } + + return false +} + +// IsPlaneSingle 飞机带单包括三带一(只能最后一手出牌) +func IsPlaneSingle(cards []int32) bool { + if len(cards) < 4 { + return false + } + mapSTriple := make(map[int32]int32) + if len(cards) == 4 { + for _, card := range cards { + mapSTriple[int32(Value(card))]++ + } + } else { + for _, card := range cards { + if Value(card) != Card_Value_2 { + mapSTriple[int32(Value(card))]++ + } + } + } + + if len(mapSTriple) > 0 { + valueSTriple := []int32{} + for card, num := range mapSTriple { + if num == 3 { + valueSTriple = append(valueSTriple, card) + } + if num == 4 { //有炸弹不让出 + return false + } + } + if len(valueSTriple) > 0 { + sort.Slice(valueSTriple, func(i, j int) bool { + if valueSTriple[i] > valueSTriple[j] { + return false + } + return true + }) + for i := len(valueSTriple); i > 0; i-- { + tmpPairs := FindOneStraightWithWidth(i, valueSTriple) + if len(tmpPairs) > 0 && len(tmpPairs)*5 > len(cards) { + return true + } + } + } + } + return false +} + +func IsPlaneTwin(cards []int32) bool { //飞机带双包括三带二 + if len(cards) == 0 { + return false + } + if len(cards)%5 != 0 { + return false + } + mapSTriple := make(map[int32]int32) + if len(cards) == 5 { + for _, card := range cards { + mapSTriple[int32(Value(card))]++ + } + } else { + for _, card := range cards { + if Value(card) != Card_Value_2 { + mapSTriple[int32(Value(card))]++ + } + } + } + + if len(mapSTriple) > 0 { + valueSTriple := []int32{} + for card, num := range mapSTriple { + if num == 3 { + valueSTriple = append(valueSTriple, card) + } + if num == 4 { //有炸弹不让出 + return false + } + } + if len(valueSTriple) > 0 { + sort.Slice(valueSTriple, func(i, j int) bool { + if valueSTriple[i] > valueSTriple[j] { + return false + } + return true + }) + for i := len(valueSTriple); i > 0; i-- { + tmpPairs := FindOneStraightWithWidth(i, valueSTriple) + if len(tmpPairs) > 0 && len(tmpPairs)*5 == len(cards) { + return true + } + } + } + } + return false +} + +// 飞机带双 找头 长度 +func FindPlaneTwinHead(cards []int32) (int32, int) { + if len(cards) == 0 { + return InvalideCard, 0 + } + if len(cards)%5 != 0 { + return InvalideCard, 0 + } + mapSTriple := make(map[int32]int32) + for _, card := range cards { + mapSTriple[int32(Value(card))]++ + } + if len(mapSTriple) > 0 { + valueSTriple := []int32{} + for card, num := range mapSTriple { + if num == 3 { + valueSTriple = append(valueSTriple, card) + } + if num == 4 { //有炸弹不让出 + return InvalideCard, 0 + } + } + if len(valueSTriple) > 0 { + sort.Slice(valueSTriple, func(i, j int) bool { + if valueSTriple[i] > valueSTriple[j] { + return false + } + return true + }) + for i := len(valueSTriple); i > 0; i-- { + tmpPairs := FindOneStraightWithWidth(i, valueSTriple) + if len(tmpPairs) > 0 && len(tmpPairs)*5 == len(cards) { + return tmpPairs[len(tmpPairs)-1], len(tmpPairs) + } + } + } + } + return InvalideCard, 0 +} + +// 飞机带单 找头 长度 +func FindPlaneSingleHead(cards []int32) (int32, int) { + if len(cards) == 0 { + return InvalideCard, 0 + } + mapSTriple := make(map[int32]int32) + for _, card := range cards { + mapSTriple[int32(Value(card))]++ + } + if len(mapSTriple) > 0 { + valueSTriple := []int32{} + for card, num := range mapSTriple { + if num == 3 { + valueSTriple = append(valueSTriple, card) + } + if num == 4 { //有炸弹不让出 + return InvalideCard, 0 + } + } + if len(valueSTriple) > 0 { + sort.Slice(valueSTriple, func(i, j int) bool { + if valueSTriple[i] > valueSTriple[j] { + return false + } + return true + }) + for i := len(valueSTriple); i > 0; i-- { + tmpPairs := FindOneStraightWithWidth(i, valueSTriple) + if len(tmpPairs) > 0 && len(tmpPairs)*5 > len(cards) { + return tmpPairs[len(tmpPairs)-1], len(tmpPairs) + } + } + } + } + return InvalideCard, 0 +} + +// RulePopEnable 牌型判断 +// bool:是否找到牌型 +// int:牌型 +func RulePopEnable(cards []int32) (bool, int) { + isRule := false + ruleType := Tienlen_Pass + switch len(cards) { + case 1: + isRule = true + ruleType = Single + case 2: + if IsTwin(cards) { + isRule = true + ruleType = Twin + } + case 3: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsTriple(cards) { //三张 + isRule = true + ruleType = Triple + } + case 4: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsFourBomb(cards) { //炸弹 + isRule = true + ruleType = Four_Bomb + } else if IsStraightTwin(cards) { //连对(双顺) + isRule = true + ruleType = Straight_Twin + } + default: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsStraightTwin(cards) { //连对(双顺) + isRule = true + ruleType = Straight_Twin + } + } + return isRule, ruleType +} + +// RulePopEnable_yl 娱乐版牌型判断 +// 多了三顺,飞机 +func RulePopEnable_yl(cards []int32) (bool, int) { //娱乐场 + isRule := false + ruleType := Tienlen_Pass + switch len(cards) { + case 1: + isRule = true + ruleType = Single + case 2: + if IsTwin(cards) { + isRule = true + ruleType = Twin + } + case 3: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsTriple(cards) { //三张 + isRule = true + ruleType = Triple + } + case 4: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsFourBomb(cards) { //炸弹 + isRule = true + ruleType = Four_Bomb + } else if IsStraightTwin(cards) { //连对(双顺) + isRule = true + ruleType = Straight_Twin + } else if IsPlaneSingle(cards) { //飞机带单 + isRule = true + ruleType = Plane_Single + } + break + default: + if IsColorStraight(cards) { //同花顺 + isRule = true + ruleType = ColorStraight + } else if IsStraight(cards) { //顺子 + isRule = true + ruleType = Straight + } else if IsStraightTwin(cards) { //连对(双顺) + isRule = true + ruleType = Straight_Twin + } else if IsStraightTriple(cards) { //三顺 + isRule = true + ruleType = Straight_Triple + } else if IsPlaneTwin(cards) { //飞机带双 + isRule = true + ruleType = Plane_Twin + } else if IsPlaneSingle(cards) { //飞机带单 + isRule = true + ruleType = Plane_Single + } + break + } + return isRule, ruleType +} + +// CanDel 跟牌 +// lastCards 上家出牌 +// cards 当前出牌 +// toEnd 打到底 +// return :可压住,是否炸,炸多少分 +func CanDel(lastCards, cards []int32, toEnd bool) (bool, bool, int32) { + isBomb := false + bombScore := int32(0) + if len(lastCards) == 0 || len(cards) == 0 { + return false, isBomb, bombScore + } + for _, card := range lastCards { + if card == InvalideCard { + return false, isBomb, bombScore + } + } + for _, card := range cards { + if card == InvalideCard { + return false, isBomb, bombScore + } + } + sort.Slice(lastCards, func(i, j int) bool { + if Value(lastCards[i]) > Value(lastCards[j]) { + return false + } else if Value(lastCards[i]) == Value(lastCards[j]) { + return Color(lastCards[i]) < Color(lastCards[j]) + } + return true + }) + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) > Value(cards[j]) { + return false + } else if Value(cards[i]) == Value(cards[j]) { + return Color(cards[i]) < Color(cards[j]) + } + return true + }) + lastIsRule, lastRuleType := RulePopEnable(lastCards) + isRule, ruleType := RulePopEnable(cards) + //fmt.Println("isRule:", isRule, " ruleType:", ruleType) + if isRule && lastIsRule { + switch ruleType { + case Single: //单张只能压单张 + if lastRuleType == Single { + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Twin: //对子只能压对子 + if lastRuleType == Twin { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Straight: //非同花顺子只能压非同花顺子 + if len(cards) == len(lastCards) && lastRuleType == Straight { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case ColorStraight: //同花顺子可压: 同花顺、非同花顺子 + if len(cards) == len(lastCards) && (lastRuleType == ColorStraight || lastRuleType == Straight) { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Straight_Twin: //连对特殊处理下!!! + twinNum := len(cards) + switch twinNum { + case 4: //二连对只能压二连对 + if len(cards) == len(lastCards) && lastRuleType == Straight_Twin { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case 6: //三连对可压:三连对、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + } + return true, true, score + } + case 8: //四连对可压:四连对、三连对、对子2、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 8 { //四连对 + score := Score20 + if toEnd { + score = Score2End20 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } else if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + return true, true, score + } + } else if lastRuleType == Twin && Value(lastCard) == Card_Value_2 { //对子2 + tmpScore := int32(0) + for _, card := range lastCards { + if card == 51 || card == 38 { //红2 10分 + if toEnd { + tmpScore += Score2End10 + } else { + tmpScore += Score10 + } + } else if card == 25 || card == 12 { //黑2 5分 + if toEnd { + tmpScore += Score2End5 + } else { + tmpScore += Score5 + } + } + } + return true, true, tmpScore + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + return true, true, score + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + return true, true, score + } + } + case 10: //五连对可压:五连对、四连对、三连对、对子2、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 10 { //五连对 + score := Score25 + if toEnd { + score = Score2End25 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } else if len(lastCards) == 8 { //四连对 + score := Score20 + if toEnd { + score = Score2End20 + } + return true, true, score + } else if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + return true, true, score + } + } else if lastRuleType == Twin && Value(lastCard) == Card_Value_2 { //对子2 + tmpScore := int32(0) + for _, card := range lastCards { + if card == 51 || card == 38 { //红2 10分 + if toEnd { + tmpScore += Score2End10 + } else { + tmpScore += Score10 + } + } else if card == 25 || card == 12 { //黑2 5分 + if toEnd { + tmpScore += Score2End5 + } else { + tmpScore += Score5 + } + } + } + return true, true, tmpScore + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + return true, true, score + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + return true, true, score + } + } + } + case Triple: //三张只能压三张 + if lastRuleType == Triple { + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, false, 0 + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), false, 0 + } + } + case Four_Bomb: //炸弹可压:炸弹、三连对、对子2、单2 + score := Score2End20 + if lastRuleType == Four_Bomb { //炸弹 + if !toEnd { + score = Score20 + } + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, true, score + } + } else if lastRuleType == Straight_Twin { + if len(lastCards) == 6 { //三连对 + if !toEnd { + score = Score15 + } + return true, true, score + } + } else if lastRuleType == Twin && Value(lastCards[0]) == Card_Value_2 { + if !toEnd { + tmpScore := int32(0) + for _, card := range lastCards { + if card == 51 || card == 38 { //红2 10分 + tmpScore += Score10 + } else if card == 25 || card == 12 { //黑2 5分 + tmpScore += Score5 + } + } + return true, true, tmpScore + } + return true, true, score + } else if lastRuleType == Single && Value(lastCards[0]) == Card_Value_2 { + if lastCards[0] == 51 || lastCards[0] == 38 { //红2 10分 + if !toEnd { + score = Score10 + } + return true, true, score + } else if lastCards[0] == 25 || lastCards[0] == 12 { //黑2 5分 + if !toEnd { + score = Score5 + } + return true, true, score + } + } + } + } + return false, false, 0 +} + +// return :可压住,是否炸,炸多少分 +func CanDel_yl(lastCards, cards []int32, toEnd bool) (bool, bool, int32) { + isBomb := false + bombScore := int32(0) + if len(lastCards) == 0 || len(cards) == 0 { + return false, isBomb, bombScore + } + for _, card := range lastCards { + if card == InvalideCard { + return false, isBomb, bombScore + } + } + for _, card := range cards { + if card == InvalideCard { + return false, isBomb, bombScore + } + } + sort.Slice(lastCards, func(i, j int) bool { + if Value(lastCards[i]) > Value(lastCards[j]) { + return false + } else if Value(lastCards[i]) == Value(lastCards[j]) { + return Color(lastCards[i]) < Color(lastCards[j]) + } + return true + }) + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) > Value(cards[j]) { + return false + } else if Value(cards[i]) == Value(cards[j]) { + return Color(cards[i]) < Color(cards[j]) + } + return true + }) + lastIsRule, lastRuleType := RulePopEnable_yl(lastCards) + isRule, ruleType := RulePopEnable_yl(cards) + //fmt.Println("CanDel_yl: isRule:", isRule, " ruleType:", ruleType) + if isRule && lastIsRule { + switch ruleType { + case Single: //单张只能压单张 + if lastRuleType == Single { + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Twin: //对子只能压对子 + if lastRuleType == Twin { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Straight: //非同花顺子只能压非同花顺子 + if len(cards) == len(lastCards) && lastRuleType == Straight { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case ColorStraight: //同花顺子可压: 同花顺、非同花顺子 + if len(cards) == len(lastCards) && (lastRuleType == ColorStraight || lastRuleType == Straight) { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case Straight_Twin: //连对特殊处理下!!! + twinNum := len(cards) + switch twinNum { + case 4: //二连对只能压二连对 + if len(cards) == len(lastCards) && lastRuleType == Straight_Twin { + lastCard := lastCards[len(lastCards)-1] + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, isBomb, bombScore + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), isBomb, bombScore + } + } + case 6: //三连对可压:三连对、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + return true, true, score + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + return true, true, score + } + } + case 8: //四连对可压:四连对、三连对、对子2、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 8 { //四连对 + score := Score20 + if toEnd { + score = Score2End20 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } else if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + return true, true, score + } + } else if lastRuleType == Twin && Value(lastCard) == Card_Value_2 { //对子2 + tmpScore := int32(0) + for _, card := range lastCards { + if card == 51 || card == 38 { //红2 10分 + if toEnd { + tmpScore += Score2End10 + } else { + tmpScore += Score10 + } + } else if card == 25 || card == 12 { //黑2 5分 + if toEnd { + tmpScore += Score2End5 + } else { + tmpScore += Score5 + } + } + } + return true, true, tmpScore + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + return true, true, score + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + return true, true, score + } + } + case 10: //五连对可压:五连对、四连对、三连对、对子2、单2 + lastCard := lastCards[len(lastCards)-1] + if lastRuleType == Straight_Twin { + if len(lastCards) == 10 { //五连对 + score := Score25 + if toEnd { + score = Score2End25 + } + card := cards[len(cards)-1] + if Value(card) > Value(lastCard) { + return true, true, score + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), true, score + } + } else if len(lastCards) == 8 { //四连对 + score := Score20 + if toEnd { + score = Score2End20 + } + return true, true, score + } else if len(lastCards) == 6 { //三连对 + score := Score15 + if toEnd { + score = Score2End15 + } + return true, true, score + } + } else if lastRuleType == Twin && Value(lastCard) == Card_Value_2 { //对子2 + tmpScore := int32(0) + for _, card := range lastCards { + if card == 51 || card == 38 { //红2 10分 + if toEnd { + tmpScore += Score2End10 + } else { + tmpScore += Score10 + } + } else if card == 25 || card == 12 { //黑2 5分 + if toEnd { + tmpScore += Score2End5 + } else { + tmpScore += Score5 + } + } + } + return true, true, tmpScore + } else if lastRuleType == Single && Value(lastCard) == Card_Value_2 { //单2 + score := Score10 + if lastCard == 51 || lastCard == 38 { //红2 10分 + if toEnd { + score = Score2End10 + } + return true, true, score + } else if lastCard == 25 || lastCard == 12 { //黑2 5分 + score = Score5 + if toEnd { + score = Score2End5 + } + return true, true, score + } + } + } + case Triple: //三张只能压三张 + if lastRuleType == Triple { + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, false, 0 + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), false, 0 + } + } + case Straight_Triple: //三顺只能压三顺,或者张数相同的飞机带单飞机带双 + if len(lastCards) == len(cards) { + if lastRuleType == Straight_Triple { + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, false, 0 + } else if Value(card) == Value(lastCard) { + return Color(card) > Color(lastCard), false, 0 + } + } else if lastRuleType == Plane_Single || lastRuleType == Plane_Twin { + lastCardValue, lastPlaneLen := FindPlaneSingleHead(lastCards) + cardValue, planeLen := FindPlaneSingleHead(cards) + if lastCardValue != InvalideCard && cardValue != InvalideCard && lastPlaneLen <= planeLen && cardValue > lastCardValue { + return true, false, 0 + } else { + return false, false, 0 + } + } + } + case Plane_Twin: //飞机带双只能压飞机带双 + if lastRuleType == Plane_Twin || lastRuleType == Plane_Single { + lastCardValue, lastPlaneLen := FindPlaneTwinHead(lastCards) + cardValue, planeLen := FindPlaneTwinHead(cards) + if lastCardValue != InvalideCard && cardValue != InvalideCard && len(lastCards) == len(cards) && lastPlaneLen <= planeLen && cardValue > lastCardValue { + return true, false, 0 + } else { + return false, false, 0 + } + } + case Plane_Single: //飞机带单只能压飞机带单 + if lastRuleType == Plane_Single || lastRuleType == Plane_Twin { + lastCardValue, lastPlaneLen := FindPlaneSingleHead(lastCards) + cardValue, planeLen := FindPlaneSingleHead(cards) + if lastCardValue != InvalideCard && cardValue != InvalideCard && len(lastCards) == len(cards) && lastPlaneLen <= planeLen && cardValue > lastCardValue { + return true, false, 0 + } else { + return false, false, 0 + } + } + case Four_Bomb: //炸弹可压:炸弹、飞机带双,飞机带单、三顺、三张、顺子、三连对以下的对子、单张 + score := Score20 + if toEnd { + score = Score2End20 + } + switch lastRuleType { + case Four_Bomb: + lastCard := lastCards[0] + card := cards[0] + if Value(card) > Value(lastCard) { + return true, true, score + } + case Plane_Twin, Plane_Single, Straight_Triple, Triple, Straight, ColorStraight, Twin, Single: + return true, true, score + case Straight_Twin: + if len(lastCards) == 6 || len(lastCards) == 4 { // 三连对、二连对 + return true, true, score + } + } + } + } + return false, false, 0 +} + +func DelSliceInt32(sl []int32, v int32) []int32 { + index := -1 + for key, value := range sl { + if value == v { + index = key + break + } + } + if index != -1 { + sl = append(sl[:index], sl[index+1:]...) + } + return sl +} + +// 计算输家输分数百分比 +func GetLoseScore(cards [HandCardNum]int32, toEnd bool) int32 { + loseScore := int32(0) + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + + //找单2 + find2Num := 0 + for i := 0; i < len(cpCards); i++ { + card := cpCards[i] + switch card { + case HongTao2, FangPian2: + if toEnd { + loseScore += Score2End10 + } else { + loseScore += Score10 + } + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + find2Num++ + case MeiHua2, HeiTao2: + if toEnd { + loseScore += Score2End5 + } else { + loseScore += Score5 + } + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + find2Num++ + } + } + if find2Num >= 4 { //找到4个2?? + return 0 + } + + // 找炸弹 + mapBomb := make(map[int32]int) + for _, card := range cpCards { + mapBomb[int32(Value(card))]++ + } + bombValue := []int32{} + for card, num := range mapBomb { + if num == 4 { + bombValue = append(bombValue, card) + } + } + if len(bombValue) > 0 { + score := Score20 + if toEnd { + score = Score2End20 + } + for _, card := range bombValue { + for i := int32(0); i < 4; i++ { + cpCards = DelSliceInt32(cpCards, card+(i*PER_CARD_COLOR_MAX)) + } + } + loseScore += int32(len(bombValue)) * score + } + + //找五连 + if len(cpCards) >= 10 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map5STwin := make(map[int32]int32) + for _, card := range cpCards { + map5STwin[int32(Value(card))]++ + } + Value5STwin := []int32{} + for card, num := range map5STwin { + if num >= 2 { + Value5STwin = append(Value5STwin, card) + } + } + sort.Slice(Value5STwin, func(i, j int) bool { + if Value5STwin[i] > Value5STwin[j] { + return false + } + return true + }) + if len(Value5STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(5, Value5STwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + score := Score25 + if toEnd { + score = Score2End25 + } + loseScore += score + } + } + } + + //找四连 + if len(cpCards) >= 8 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map4STwin := make(map[int32]int32) + for _, card := range cpCards { + map4STwin[int32(Value(card))]++ + } + Value4STwin := []int32{} + for card, num := range map4STwin { + if num >= 2 { + Value4STwin = append(Value4STwin, card) + } + } + sort.Slice(Value4STwin, func(i, j int) bool { + if Value4STwin[i] > Value4STwin[j] { + return false + } + return true + }) + if len(Value4STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(4, Value4STwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] != InvalideCard { + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + score := Score20 + if toEnd { + score = Score2End20 + } + loseScore += score + } + } + } + + //找三连 + if len(cpCards) >= 6 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map3STwin := make(map[int32]int32) + for _, card := range cpCards { + map3STwin[int32(Value(card))]++ + } + Value3STwin := []int32{} + for card, num := range map3STwin { + if num >= 2 { + Value3STwin = append(Value3STwin, card) + } + } + sort.Slice(Value3STwin, func(i, j int) bool { + if Value3STwin[i] > Value3STwin[j] { + return false + } + return true + }) + if len(Value3STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(3, Value3STwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + score := Score15 + if toEnd { + score = Score2End15 + } + loseScore += score + } + } + } + + //再找三连 + if len(cpCards) >= 6 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map3STwin := make(map[int32]int32) + for _, card := range cpCards { + map3STwin[int32(Value(card))]++ + } + Value3STwin := []int32{} + for card, num := range map3STwin { + if num >= 2 { + Value3STwin = append(Value3STwin, card) + } + } + sort.Slice(Value3STwin, func(i, j int) bool { + if Value3STwin[i] > Value3STwin[j] { + return false + } + return true + }) + if len(Value3STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(3, Value3STwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + score := Score15 + if toEnd { + score = Score2End15 + } + loseScore += score + } + } + } + if !toEnd { + //找单张 + for _, card := range cpCards { + if card != InvalideCard { + loseScore++ + } + } + } + return loseScore +} + +// 找一个固定长度的顺子 (2,3,4,5,6) 3-> [2,3,4] +func FindOneStraightWithWidth(n int, pairs []int32) []int32 { + if len(pairs) == 0 { + return nil + } + lastKey := pairs[0] + tempPair := []int32{lastKey} + if n == 1 { + return tempPair + } + for i := 1; i < len(pairs); i++ { + if pairs[i]-lastKey == 1 { + tempPair = append(tempPair, pairs[i]) + } else { + tempPair = []int32{pairs[i]} + } + if len(tempPair) == n { + break + } + lastKey = pairs[i] + } + if len(tempPair) == n { + return tempPair + } + return nil +} + +// 找多个固定长度的顺子(2,3,4,5,6) 3-> [[2,3,4][3,4,5][4,5,6]] +func FindStraightWithWidth(n int, pairs []int32) [][]int32 { + if len(pairs) == 0 || n < 2 { + return nil + } + var tempPairs [][]int32 + lastKey := pairs[0] + tempPair := []int32{lastKey} + for i := 1; i < len(pairs); i++ { + if pairs[i]-lastKey == 1 { + tempPair = append(tempPair, pairs[i]) + } else { + tempPair = []int32{pairs[i]} + } + if len(tempPair) == n { + tempPairs = append(tempPairs, tempPair) + tempPair = []int32{} + for j := n - 1; j > 0; j-- { + tempPair = append(tempPair, pairs[i-j+1]) + } + } + lastKey = pairs[i] + } + return tempPairs +} + +func FindCardsFromCardsValue(cards, valueCards []int32, count int) []int32 { + var ret []int32 + for _, card := range valueCards { + delC := 0 + for i := 0; i < len(cards); i++ { + if int32(Value(cards[i])) == card { //删除count次 + delC++ + ret = append(ret, cards[i]) + } + if delC >= count { + break + } + } + } + return ret +} + +// ///////////////////////////(天胡牌型)/////////////////////// +// 判断13张牌里面是否有4个2炸弹 +func Have2FourBomb(cards []int32) bool { + if len(cards) == Hand_CardNum { + for i := 0; i < len(cards); i++ { + if Value(cards[i]) == Card_Value_2 { + tmpInt := 0 + for j := 0; j < len(cards); j++ { + if Value(cards[i]) == Value(cards[j]) { + tmpInt++ + } + } + if tmpInt == 4 { + return true + } + } + } + } + return false +} + +// 判断13张牌里面是否有6连对(单牌2不能带到连牌里) +func Have6StraightTwin(cards []int32) bool { + cpCards := []int32{} + for _, card := range cards { + if int32(Value(card)) != InvalideCard { + cpCards = append(cpCards, card) + } + } + if len(cpCards) == Hand_CardNum { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map6STwin := make(map[int32]int32) + for _, card := range cpCards { + if Value(card) != Card_Value_2 { + map6STwin[int32(Value(card))]++ + } + } + if len(map6STwin) > 0 { + Value6STwin := []int32{} + for card, num := range map6STwin { + if num >= 2 { + Value6STwin = append(Value6STwin, card) + } + } + if len(Value6STwin) > 0 { + sort.Slice(Value6STwin, func(i, j int) bool { + if Value6STwin[i] > Value6STwin[j] { + return false + } + return true + }) + tmpPairs := FindOneStraightWithWidth(6, Value6STwin) + if len(tmpPairs) == 6 { + return true + } + } + } + } + return false +} + +// 判断13张牌里面是否有12顺(单牌2不能带到连牌里) +func Have12Straight(cards []int32) bool { + if len(cards) == Hand_CardNum { + mapCard := make(map[int32]int) + for _, card := range cards { + if Value(card) != Card_Value_2 { + mapCard[int32(Value(card))]++ + } + } + if len(mapCard) == 12 { + return true + } + } + return false +} + +// 随机获取一个天胡牌型 +func GetTianHu() ([]int32, int) { + var ret []int32 + n := rand.Intn(3) + switch n { + case 0: // 4个2炸弹 + ret = append(ret, []int32{51, 38, 25, 12}...) + for i := 0; i < Hand_CardNum-4; i++ { + for j := 0; j < 52; j++ { + if !slices.Contains(ret, int32(j)) { + ret = append(ret, int32(i)) + break + } + } + } + + case 1: // 6连对 + i := rand.Int31n(7) + n := i + 6 + for ; i < n; i++ { + cs := rand.Perm(4) + ret = append(ret, []int32{int32(cs[0])*13 + i, int32(cs[1])*13 + i}...) + } + for i := 0; i < 52; i++ { + if !slices.Contains(ret, int32(i)) { + ret = append(ret, int32(i)) + break + } + } + + case 2: // 12顺 + for i := 0; i < 12; i++ { + color := rand.Int31n(4) + ret = append(ret, color*13+int32(i)) + } + for i := 0; i < 52; i++ { + if !slices.Contains(ret, int32(i)) { + ret = append(ret, int32(i)) + break + } + } + } + return ret, n +} + +// 根据最小牌值去推荐出牌 +/*func RecommendCardsWithMinCard(cards []int32) []int32 { + recmCards := []int32{} + //排序 + sort.Slice(cards, func(i, j int) bool { + v_i := Value(cards[i]) + v_j := Value(cards[j]) + c_i := Color(cards[i]) + c_j := Color(cards[j]) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + //取手牌 + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + //取最小牌值 + minCard := cpCards[0] + for _, card := range cpCards { + minCard = card + break + } + //找顺子 + recmCards = []int32{} + findNum := 0 + for _, card := range cpCards { + if Value(card) == Value(minCard)+findNum && Value(card) != Card_Value_2 { + recmCards = append(recmCards, card) + findNum++ + } else { + break + } + } + if len(recmCards) < 3 { + //找二连对 + recmCards = []int32{} + find1 := 0 + find2 := 0 + for _, card := range cpCards { + if Value(card) == Value(minCard) && Value(card) != Card_Value_2 && find1 < 2 { + recmCards = append(recmCards, card) + find1++ + } + if Value(card) == Value(minCard)+1 && Value(card) != Card_Value_2 && find2 < 2 { + recmCards = append(recmCards, card) + find2++ + } + } + if len(recmCards) != 4 { + //找三张 + recmCards = []int32{} + find3 := 0 + for _, card := range cpCards { + if Value(card) == Value(minCard) && Value(card) != Card_Value_2 && find3 < 3 { + recmCards = append(recmCards, card) + find3++ + } + } + if len(recmCards) != 3 { + //找对子 + recmCards = []int32{} + find4 := 0 + for _, card := range cpCards { + if Value(card) == Value(minCard) && Value(card) != Card_Value_2 && find4 < 2 { + recmCards = append(recmCards, card) + find4++ + } + } + if len(recmCards) != 2 { + // 找单张 + recmCards = []int32{} + recmCards = append(recmCards, minCard) + } + } + } + } + if len(recmCards) == 0 { + recmCards = append(recmCards, minCard) + } + return recmCards +}*/ + +// 根据牌型牌数量最多去推荐出牌 +/*func RecommendCardsWithCards(cards []int32) []int32 { + recmCards := []int32{} + //排序 + sort.Slice(cards, func(i, j int) bool { + v_i := Value(cards[i]) + v_j := Value(cards[j]) + c_i := Color(cards[i]) + c_j := Color(cards[j]) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + //取手牌 + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + //取最小牌值 + minCard := cpCards[0] + for _, card := range cpCards { + minCard = card + break + } + if IsStraight(cpCards) { + //5顺直接返回 + for _, card := range cpCards { + recmCards = append(recmCards, card) + } + } else { + if len(cpCards) >= 4 { + //找4顺 + map4Straight := make(map[int32]int32) + for _, card := range cpCards { + if Value(card) == Card_Value_2 { + continue + } + map4Straight[int32(Value(card))]++ + } + if len(map4Straight) > 0 { + Value4Straight := []int32{} + for cardV, num := range map4Straight { + if num >= 1 { + Value4Straight = append(Value4Straight, cardV) + } + } + sort.Slice(Value4Straight, func(i, j int) bool { + if Value4Straight[i] > Value4Straight[j] { + return false + } + return true + }) + if len(Value4Straight) > 0 { + tmpPairs := FindOneStraightWithWidth(4, Value4Straight) + if len(tmpPairs) > 0 { + for _, cardV := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if int32(Value(cpCards[i])) == cardV && (delC < 1) { //找1次 + delC++ + recmCards = append(recmCards, cpCards[i]) + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + } + } + } + if len(cpCards) >= 4 { + //找炸弹 + mapBomb := make(map[int32]int) + for _, card := range cpCards { + mapBomb[int32(Value(card))]++ + } + if len(mapBomb) > 0 { + bombValue := []int32{} + for card, num := range mapBomb { + if num == 4 { + bombValue = append(bombValue, card) + break //找一个炸弹就行 + } + } + if len(bombValue) > 0 { + for _, card := range bombValue { + for i := 0; i < len(cpCards); i++ { + cpCard := cpCards[i] + if int32(Value(cpCard)) == card { + recmCards = append(recmCards, cpCard) + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + } + } + } + } + } + } + if len(cpCards) >= 4 { + //找二连对 + map2STwin := make(map[int32]int32) + for _, card := range cpCards { + if Value(card) == Card_Value_2 { + continue + } + map2STwin[int32(Value(card))]++ + } + if len(map2STwin) > 0 { + Value2STwin := []int32{} + for card, num := range map2STwin { + if num >= 2 { + Value2STwin = append(Value2STwin, card) + } + } + if len(Value2STwin) > 0 { + sort.Slice(Value2STwin, func(i, j int) bool { + if Value2STwin[i] > Value2STwin[j] { + return false + } + return true + }) + tmpPairs := FindOneStraightWithWidth(2, Value2STwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if int32(Value(cpCards[i])) == card && (delC < 2) { //找2次 + delC++ + recmCards = append(recmCards, cpCards[i]) + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + } + } + } + if len(cpCards) >= 3 { + //找3顺 + map3Straight := make(map[int32]int32) + for _, card := range cpCards { + if Value(card) == Card_Value_2 { + continue + } + map3Straight[int32(Value(card))]++ + } + if len(map3Straight) > 0 { + Value3Straight := []int32{} + for cardV, num := range map3Straight { + if num >= 1 { + Value3Straight = append(Value3Straight, cardV) + } + } + sort.Slice(Value3Straight, func(i, j int) bool { + if Value3Straight[i] > Value3Straight[j] { + return false + } + return true + }) + if len(Value3Straight) > 0 { + tmpPairs := FindOneStraightWithWidth(3, Value3Straight) + if len(tmpPairs) > 0 { + for _, cardV := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if int32(Value(cpCards[i])) == cardV && (delC < 1) { //找1次 + delC++ + recmCards = append(recmCards, cpCards[i]) + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + } + } + } + if len(cpCards) >= 3 { + //找3张 + mapTriple := make(map[int32]int32) + for _, card := range cpCards { + mapTriple[int32(Value(card))]++ + } + if len(mapTriple) > 0 { + valueTriple := []int32{} + for cardV, num := range mapTriple { + if num >= 3 { + valueTriple = append(valueTriple, cardV) + } + } + if len(valueTriple) > 0 { + tripleValue := valueTriple[0] + delC := 0 + for i := 0; i < len(cpCards); i++ { + if int32(Value(cpCards[i])) == tripleValue && (delC < 3) { //找3次 + delC++ + recmCards = append(recmCards, cpCards[i]) + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + } + if len(cpCards) >= 2 { + //找对子 + mapTwin := make(map[int32]int32) + for _, card := range cpCards { + if Value(card) == Card_Value_2 { + continue + } + mapTwin[int32(Value(card))]++ + } + if len(mapTwin) > 0 { + ValueTwin := []int32{} + for card, num := range mapTwin { + if num >= 2 { + ValueTwin = append(ValueTwin, card) + } + } + if len(ValueTwin) > 0 { + sort.Slice(ValueTwin, func(i, j int) bool { + if ValueTwin[i] > ValueTwin[j] { + return false + } + return true + }) + vTwin := ValueTwin[0] + recmCount := 0 + for i := 0; i < len(cpCards); i++ { + card := cpCards[i] + if vTwin == int32(Value(card)) && recmCount < 2 { + recmCards = append(recmCards, card) + recmCount++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + } + } + } + } + } + } + if len(recmCards) == 0 { + recmCards = append(recmCards, minCard) + } + return recmCards +}*/ + +/*// 根据牌型牌数量最多去推荐出牌 +func RecommendCardsWithCards_yl(cards []int32) []int32 { + recmCards := []int32{} + allTypeCards := map[int][]int32{} + maxType := 0 + maxNum := 0 + //排序 + sort.Slice(cards, func(i, j int) bool { + v_i := Value(cards[i]) + v_j := Value(cards[j]) + c_i := Color(cards[i]) + c_j := Color(cards[j]) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + //取手牌 + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + //取最小牌值 + minCard := cpCards[0] + for _, card := range cpCards { + minCard = card + break + } + if IsStraight(cpCards) { + //顺直接返回 + for _, card := range cpCards { + recmCards = append(recmCards, card) + } + maxType = Straight + maxNum = len(recmCards) + allTypeCards[maxType] = recmCards + } else { + isHave := true + var tCards []int32 + isHave, tCards = needMaxStraightTripleRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Straight_Triple + } + allTypeCards[Straight_Triple] = tCards + } + + isHave, tCards = needMaxPlaneSingleRandom(cpCards) + if isHave { + //只有最后一手牌才能出 + if len(tCards) == len(cpCards) { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Plane_Single + } + allTypeCards[Plane_Single] = tCards + } + } + + isHave, tCards = needMaxPlaneTwinRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Plane_Twin + } + allTypeCards[Plane_Twin] = tCards + } + + isHave, tCards = needMaxStraightTwinRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Straight_Twin + } + allTypeCards[Straight_Twin] = tCards + } + isHave, tCards = needMaxBoomRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Four_Bomb + } + allTypeCards[Four_Bomb] = tCards + } + isHave, tCards = needMaxTripleRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Triple + } + allTypeCards[Triple] = tCards + } + isHave, tCards = needMaxStraightRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Straight + } + allTypeCards[Straight] = tCards + } + + isHave, tCards = needMaxTwinRandom(cpCards) + if isHave { + if len(tCards) > maxNum { + maxNum = len(tCards) + maxType = Twin + } + allTypeCards[Twin] = tCards + } + + } + + if maxType == 0 { + recmCards = append(recmCards, minCard) + } else { + recmCards = allTypeCards[maxType] + } + return recmCards +}*/ + +// 根据上家牌去推荐出牌 压牌 +func RecommendCardsWithLastCards(lastCards, cards []int32) []int32 { + recmCards := []int32{} + isHave := false + isRule, ruleType := RulePopEnable(lastCards) + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + if isRule { + switch ruleType { + case Single: + isHave, recmCards = needSingle(lastCards, cpCards) + if !isHave { + + } + case Twin: + isHave, recmCards = needTwin(lastCards, cpCards) + if !isHave { + + } + case Straight: + isHave, recmCards = needStraight(lastCards, cpCards) + if !isHave { + + } + case ColorStraight: + isHave, recmCards = needColorStraight(lastCards, cpCards) + if !isHave { + + } + case Straight_Twin: + isHave, recmCards = needStraightTwin(lastCards, cpCards) + if !isHave { + + } + case Triple: + isHave, recmCards = needTriple(lastCards, cpCards) + if !isHave { + + } + case Four_Bomb: + isHave, recmCards = needFourBomb(lastCards, cpCards) + if !isHave { + + } + } + } + return recmCards +} + +// 根据上家牌去推荐出牌 +func RecommendCardsWithLastCards_yl(lastCards, cards []int32) []int32 { + recmCards := []int32{} + isHave := false + isRule, ruleType := RulePopEnable_yl(lastCards) + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + } + } + if isRule { + switch ruleType { + case Single: + isHave, recmCards = needSingle(lastCards, cpCards) + if !isHave { + + } + case Twin: + isHave, recmCards = needTwin(lastCards, cpCards) + if !isHave { + + } + case Straight: + isHave, recmCards = needStraight(lastCards, cpCards) + if !isHave { + + } + case ColorStraight: + isHave, recmCards = needColorStraight(lastCards, cpCards) + if !isHave { + + } + case Straight_Twin: + isHave, recmCards = needStraightTwin(lastCards, cpCards) + if !isHave { + + } + case Triple: + isHave, recmCards = needTriple(lastCards, cpCards) + if !isHave { + + } + case Four_Bomb: + isHave, recmCards = needFourBomb(lastCards, cpCards) + if !isHave { + + } + case Straight_Triple: + isHave, recmCards = needStraightTriple(lastCards, cpCards) + if !isHave { + + } + case Plane_Single: + + case Plane_Twin: + + } + } + return recmCards +} + +// 需要单张 +func needSingle(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsSingle(lastCards) { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) > Value(cards[j]) { + return false + } + return true + }) + for _, card := range cards { + if card != InvalideCard { + if Value(card) < Value(lastCards[0]) { + continue + } else { + if Value(card) == Value(lastCards[0]) { + if Color(card) > Color(lastCards[0]) { + haveNeed = true + needCards = append(needCards, card) + break + } + } else if Value(card) > Value(lastCards[0]) { + haveNeed = true + needCards = append(needCards, card) + break + } + } + } + } + } + return haveNeed, needCards +} + +// 需要对子 +func needTwin(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsTwin(lastCards) { + mapTwin := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapTwin[int32(Value(card))]++ + } + } + if len(mapTwin) > 0 { + ValueTwin := []int32{} + for card, num := range mapTwin { + if num >= 2 { + ValueTwin = append(ValueTwin, card) + } + } + if len(ValueTwin) > 0 { + sort.Slice(ValueTwin, func(i, j int) bool { + if ValueTwin[i] > ValueTwin[j] { + return false + } + return true + }) + sort.Slice(lastCards, func(i, j int) bool { + if lastCards[i] > lastCards[j] { + return false + } + return true + }) + needValue := -1 + for _, card := range ValueTwin { + if int(card) < Value(lastCards[1]) { + continue + } else { + if int(card) == Value(lastCards[1]) { + if Color(card) > Color(lastCards[1]) { + haveNeed = true + needValue = int(card) + break + } + } else if int(card) > Value(lastCards[1]) { + haveNeed = true + needValue = int(card) + break + } + } + } + if needValue != -1 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + needNum := 0 + for i := len(cards) - 1; i >= 0; i-- { + if Value(cards[i]) == needValue && needNum < 2 { + needNum++ + needCards = append(needCards, cards[i]) + } + } + } + } + } + } + return haveNeed, needCards +} + +// 需要顺子 +func needStraight(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsStraight(lastCards) { + sort.Slice(lastCards, func(i, j int) bool { + if lastCards[i] > lastCards[j] { + return false + } + return true + }) + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) > Value(cards[j]) { + return false + } + return true + }) + mapS := make(map[int]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapS[Value(card)]++ + } + } + if len(mapS) > 0 { + ValueS := []int32{} + for card, num := range mapS { + if num >= 1 { + ValueS = append(ValueS, int32(card)) + } + } + idx := len(lastCards) + if len(ValueS) > 0 { + sort.Slice(ValueS, func(i, j int) bool { + if Value(ValueS[i]) > Value(ValueS[j]) { + return false + } + return true + }) + tmpSMap := FindStraightWithWidth(idx, ValueS) + if len(tmpSMap) != 0 { + needCard := int32(-1) + for _, sCards := range tmpSMap { + maxSCard := sCards[len(sCards)-1] + for i := len(cards) - 1; i >= 0; i-- { + if int32(Value(cards[i])) == maxSCard { + card := cards[i] + lastCard := lastCards[len(lastCards)-1] + if Value(card) < Value(lastCard) { + continue + } else { + if Value(card) == Value(lastCard) { + if Color(card) > Color(lastCard) { + haveNeed = true + needCard = cards[i] + } + } else if Value(card) > Value(lastCards[1]) { + haveNeed = true + needCard = cards[i] + } + } + } + if haveNeed && needCard != -1 { + break + } + } + if haveNeed && needCard != -1 { + break + } + } + if haveNeed && needCard != -1 { + needValue := Value(needCard) + need := 0 + for j := len(cards) - 1; j >= 0; j-- { + if needValue >= 0 && Value(cards[j]) == needValue && need < idx { + needCards = append(needCards, cards[j]) + needValue-- + need++ + } + } + } + } + } + } + } + return haveNeed, needCards +} + +func needColorStraight(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsColorStraight(lastCards) { + sliceCS := [4][]int32{} //color-card + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + sliceCS[Color(card)] = append(sliceCS[Color(card)], card) + } + } + for _, colorCards := range sliceCS { + if len(colorCards) > 0 && len(colorCards) >= len(lastCards) { + sort.Slice(colorCards, func(i, j int) bool { + if Value(colorCards[i]) > Value(colorCards[j]) { + return false + } + return true + }) + tmpS := FindOneStraightWithWidth(len(lastCards), colorCards) + if len(tmpS) > 0 { + card := tmpS[len(tmpS)-1] + lastCard := lastCards[len(lastCards)-1] + if Value(card) > Value(lastCard) { + haveNeed = true + copy(needCards, tmpS) + } else if Value(card) == Value(lastCard) { + if Color(card) > Color(lastCard) { + haveNeed = true + copy(needCards, tmpS) + } + } + } + } + } + } + return haveNeed, needCards +} + +func needStraightTwin(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsStraightTwin(lastCards) { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + mapSTwin := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapSTwin[int32(Value(card))]++ + } + } + if len(mapSTwin) > 0 { + valueSTwin := []int32{} + for card, num := range mapSTwin { + if num >= 2 { + valueSTwin = append(valueSTwin, card) + } + } + if len(valueSTwin) > 0 { + sort.Slice(valueSTwin, func(i, j int) bool { + if valueSTwin[i] > valueSTwin[j] { + return false + } + return true + }) + tmpPairs := FindOneStraightWithWidth(len(lastCards)/2, valueSTwin) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cards); i++ { + if int32(Value(cards[i])) == card && (delC < 2) { //删除2次 + delC++ + haveNeed = true + needCards = append(needCards, cards[i]) + continue + } + } + } + } + } + } + } + return haveNeed, needCards +} + +func needTriple(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsTriple(lastCards) { + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + lastCard := lastCards[0] + for _, triple := range ValueTriple { + if triple > lastCard { + for _, card := range cards { + if int32(Value(card)) == triple { + haveNeed = true + needCards = append(needCards, card) + } + } + } + } + } + } + } + return haveNeed, needCards +} + +func needStraightTriple(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsStraightTriple(lastCards) { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + + tmpPairs := FindOneStraightWithWidth(len(lastCards)/3, ValueTriple) + if len(tmpPairs) > 0 { + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cards); i++ { + if int32(Value(cards[i])) == card && (delC < 3) { //删除3次 + delC++ + haveNeed = true + needCards = append(needCards, cards[i]) + continue + } + } + } + } + + } + } + } + return haveNeed, needCards +} + +func needFourBomb(lastCards, cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 && IsFourBomb(lastCards) { + mapBomb := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapBomb[int32(Value(card))]++ + } + } + if len(mapBomb) > 0 { + ValueBomb := []int32{} + for card, num := range mapBomb { + if num >= 4 { + ValueBomb = append(ValueBomb, card) + } + } + if len(ValueBomb) > 0 { + lastCard := lastCards[0] + for _, bomb := range ValueBomb { + if bomb > lastCard { + for _, card := range cards { + if int32(Value(card)) == bomb { + haveNeed = true + needCards = append(needCards, card) + } + } + } + } + } + } + } + return haveNeed, needCards +} + +func needMaxPlaneSingleRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + for start := len(ValueTriple) - 1; start >= 1; start-- { + tmpPairs := FindOneStraightWithWidth(start, ValueTriple) + if len(tmpPairs) > 0 { + haveNeed = true + needCards = FindCardsFromCardsValue(cards, tmpPairs, 3) + break + } + } + //找到3顺后,寻找配牌. 3-1可以是3-x,不匹配3-2 + if haveNeed { + maxSLen := len(needCards)*2/3 - 1 + tCards := GetASliceInt32NotInB(cards, needCards) + for i := 0; i < len(tCards) && i < maxSLen; i++ { + needCards = append(needCards, tCards[i]) + } + } + } + } + } + return haveNeed, needCards +} + +func needMaxPlaneTwinRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + for start := len(ValueTriple) - 1; start >= 1; start-- { + tmpPairs := FindOneStraightWithWidth(start, ValueTriple) + if len(tmpPairs) > 0 { + //需要判断剩余牌张数 + if len(cards)-len(tmpPairs)*3 < len(tmpPairs)*2 { + continue + } + haveNeed = true + needCards = FindCardsFromCardsValue(cards, tmpPairs, 3) + //找到3顺后,寻找配牌. + if haveNeed { + maxSLen := len(needCards) * 2 / 3 + tCards := GetASliceInt32NotInB(cards, needCards) + for i := 0; i < len(tCards) && i < maxSLen; i++ { + needCards = append(needCards, tCards[i]) + } + } + break + } + } + + } + } + } + return haveNeed, needCards +} + +func needMaxStraightTripleRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + for start := len(ValueTriple) - 1; start >= 2; start-- { + tmpPairs := FindOneStraightWithWidth(start, ValueTriple) + if len(tmpPairs) > 0 { + haveNeed = true + needCards = FindCardsFromCardsValue(cards, tmpPairs, 3) + break + } + } + } + } + } + return haveNeed, needCards +} + +func needMaxStraightTwinRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 2 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + for start := len(ValueTriple) - 1; start >= 2; start-- { + tmpPairs := FindOneStraightWithWidth(start, ValueTriple) + if len(tmpPairs) > 0 { + haveNeed = true + needCards = FindCardsFromCardsValue(cards, tmpPairs, 2) + break + } + } + } + } + } + return haveNeed, needCards +} + +func needMaxStraightRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 1 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + sort.Slice(ValueTriple, func(i, j int) bool { + if ValueTriple[i] > ValueTriple[j] { + return false + } + return true + }) + for start := len(ValueTriple) - 1; start >= 3; start-- { + tmpPairs := FindOneStraightWithWidth(start, ValueTriple) + if len(tmpPairs) > 0 { + haveNeed = true + needCards = FindCardsFromCardsValue(cards, tmpPairs, 1) + break + } + } + } + } + } + return haveNeed, needCards +} + +func needMaxTwinRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 2 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + card := ValueTriple[0] + haveNeed = true + needCards = FindCardsFromCardsValue(cards, []int32{card}, 2) + } + } + } + return haveNeed, needCards +} + +func needMaxBoomRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 4 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + card := ValueTriple[0] + haveNeed = true + needCards = FindCardsFromCardsValue(cards, []int32{card}, 4) + } + } + } + return haveNeed, needCards +} + +func needMaxTripleRandom(cards []int32) (bool, []int32) { + haveNeed := false + needCards := []int32{} + if len(cards) != 0 { + sort.Slice(cards, func(i, j int) bool { + if cards[i] > cards[j] { + return false + } + return true + }) + + mapTriple := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard { + mapTriple[int32(Value(card))]++ + } + } + if len(mapTriple) > 0 { + ValueTriple := []int32{} + for card, num := range mapTriple { + if num >= 3 { + ValueTriple = append(ValueTriple, card) + } + } + if len(ValueTriple) > 0 { + card := ValueTriple[0] + haveNeed = true + needCards = FindCardsFromCardsValue(cards, []int32{card}, 3) + } + } + } + return haveNeed, needCards +} + +// 根据上家牌型是否需要额外延迟出牌时间(s) +func NeedExDelay(lastCards []int32) bool { + if IsStraightTwin(lastCards) && len(lastCards) >= 6 { //三连对、四连对、五连对 + return true + } + if IsFourBomb(lastCards) { //炸弹 + return true + } + //if IsStraight(lastCards) { //5张以上的顺子 + // return true + //} + if IsPlaneTwin(lastCards) && len(lastCards) >= 10 { //飞机 + return true + } + return false +} + +func GetASliceInt32NotInB(a []int32, b []int32) []int32 { + var c []int32 + temp := map[int32]struct{}{} + for _, val := range b { + if _, ok := temp[val]; !ok { + temp[val] = struct{}{} // 空struct 不占内存空间 + } + } + + for _, val := range a { + if _, ok := temp[val]; !ok { + c = append(c, val) + } + } + + return c +} diff --git a/gamerule/tienlen/cardAi.go b/gamerule/tienlen/cardAi.go new file mode 100644 index 0000000..9899e0d --- /dev/null +++ b/gamerule/tienlen/cardAi.go @@ -0,0 +1,3058 @@ +package tienlen + +import ( + "math" + tienlenApi "mongo.games.com/game/api3th/smart/tienlen" + "mongo.games.com/goserver/core/logger" + "sort" +) + +// 新tienlenAI 获取牌型 +func GetCardsType(myCards []int32, isYuLe bool) map[int]map[int32][]int32 { + cards := make([]int32, len(myCards)) + copy(cards, myCards) + cardsTypeMap := make(map[int]map[int32][]int32) + //先找炸弹 + bombMap := GetBomb(cards) + if len(bombMap) > 0 { + cardsTypeMap[Four_Bomb] = bombMap + } + cards = DelCards(cards, bombMap) + //找连对(5连对>4连对>3连队>2连队) + pair, pairMap := GetPair(cards) + //再找连对 + pairStraightMap := GetConsecutivePairs(cards, pair) + if len(pairStraightMap) > 0 { + cardsTypeMap[Straight_Twin] = pairStraightMap + } + for i, pairStraight := range pairStraightMap { + if len(pairStraight)/2 <= 2 { + //先删除2连对 去找顺子和三条 + delete(cardsTypeMap[Straight_Twin], i) + } + } + //先排除2连对 + //排除3连对以上的连对 + for _, pairStraight := range pairStraightMap { + if isYuLe { + //娱乐模式 如果3连对以上,则删除3连对以上的连对 + if len(pairStraight)/2 >= 3 { + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = pairStraight + cards = DelCards(cards, delMap) + } + } else { + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = pairStraight + cards = DelCards(cards, delMap) + } + } + //娱乐模式先找三张 + //连队和三条有冲突 既能组成连对 也是三条 这个时候要判断是不是娱乐模式 娱乐模式保留三条拆连对,不是娱乐模式保留连对 拆三条 + if isYuLe { + threeMap := GetThreeOfkind(cards) + if len(threeMap) > 0 { + cardsTypeMap[Triple] = threeMap + } + //娱乐模式三张不拆 删除掉三张的牌找顺子 + cards = DelCards(cards, threeMap) + //判断获取到的三条在不在连对中 + for _, three := range threeMap { + for i, pairStraight := range pairStraightMap { + if len(pairStraight)/2 >= 3 { + continue + } + for _, data := range pairStraight { + if Value(data) == Value(three[0]) { + //删除连对 + delete(cardsTypeMap[Straight_Twin], i) + break + } + } + + } + } + cards = DelCards(cards, threeMap) + } + //找顺子(同花顺>杂色顺子) + straightMap := findStraights(cards) + if len(straightMap) > 0 { + cardsTypeMap[Straight] = straightMap + cards = DelCards(cards, straightMap) + } + //找三张 + if !isYuLe { + threeMap := GetThreeOfkind(cards) + if len(threeMap) > 0 { + cardsTypeMap[Triple] = threeMap + } + //不是娱乐模式删除三条 + for i, three := range threeMap { + for _, pairStraight := range pairStraightMap { + if len(pairStraight)/2 >= 3 { + continue + } + for _, data := range pairStraight { + if Value(data) == Value(three[0]) { + //删除连对 + delete(cardsTypeMap[Triple], i) + break + } + } + + } + } + cards = DelCards(cards, threeMap) + } + + //找三顺 + var arr []int32 + for _, three := range cardsTypeMap[Triple] { + for _, value := range three { + arr = append(arr, value) + } + } + StraightTripleMap := GetStraightTriple(arr) + if len(StraightTripleMap) > 0 { + cardsTypeMap[Straight_Triple] = StraightTripleMap + } + //剩余的牌再找对子 + pair, pairMap = GetPair(cards) + //再找连队 + pairStraightMap = GetConsecutivePairs(cards, pair) + for _, pairStraight := range pairStraightMap { + cardsTypeMap[Straight_Twin][int32(len(cardsTypeMap[Straight_Twin])+1)] = pairStraight + + } + cards = DelCards(cards, pairStraightMap) + pair, pairMap = GetPair(cards) + if len(pairMap) > 0 { + cardsTypeMap[Twin] = pairMap + } + //找单张 移除上面所有牌型的牌,剩余的牌就是单张 + cards = DelCards(cards, pairMap) + if len(cards) > 0 { + cardsTypeMap[Single] = make(map[int32][]int32) + for i := 0; i < len(cards); i++ { + cardsTypeMap[Single][int32(i)] = []int32{cards[i]} + } + } + return cardsTypeMap +} + +// 找炸弹 +func GetBomb(cards []int32) map[int32][]int32 { + cardCount := make(map[int32]int) + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + + } + + // 查找炸弹组合 + bombMap := make(map[int32][]int32) + for card, count := range cardCount { + if count == 4 { + bombMap[card] = cardMap[card] + } + } + return bombMap +} + +// 找连对 +func GetConsecutivePairs(cards, pair []int32) map[int32][]int32 { + // 对切片进行排序 + sort.Slice(pair, func(i, j int) bool { + return pair[i] < pair[j] + }) + + // 存储顺子的 map + straightMap := make(map[int32][]int32) + for i := 0; i < len(pair); i++ { + // 如果当前数字与前一个数字相同,则跳过 + if i > 0 && pair[i] == pair[i-1] { + continue + } + + // 初始值为当前数字 + current := pair[i] + straight := []int32{current} + + // 检查是否连续递增 + num := i + for j := i + 1; j < len(pair); j++ { + if pair[j] == current+1 && pair[j] < 12 { + straight = append(straight, pair[j]) + current = pair[j] + num = j + } + } + i = num + if len(straight) >= 2 { + straightMap[int32(len(straightMap)+1)] = straight + } + } + //找原牌 + cardsMap := make(map[int32][]int32) + for i, v := range straightMap { + for _, v1 := range v { + num := 0 + for _, v2 := range cards { + if v1 == int32(Value(v2)) { + num += 1 + cardsMap[i] = append(cardsMap[i], v2) + if num == 2 { + break + } + } + } + } + } + return cardsMap + +} + +// 找顺子 +func GetAllStraight(cards []int32, length int) map[int32][]int32 { + // 对切片进行排序 + sort.Slice(cards, func(i, j int) bool { + return cards[i] < cards[j] + }) + + // 存储顺子的 map + straightMap := make(map[int32][]int32) + for i := 0; i < len(cards); i++ { + // 如果当前数字与前一个数字相同,则跳过 + if i > 0 && Value(cards[i]) == Value(cards[i-1]) { + continue + } + + // 初始值为当前数字 + current := cards[i] + straight := []int32{current} + + // 检查是否连续递增 + for j := i + 1; j < len(cards); j++ { + if Value(cards[j]) == Value(current)+1 && Value(cards[j]) < 12 { + straight = append(straight, cards[j]) + current = cards[j] + if len(straight) >= length { + straightMap[int32(len(straightMap)+1)] = straight + } + } + } + } + return straightMap +} + +// 1.先找到所有长度为3的顺子 +// 2.剩余的牌再和已经取出来的顺子组合,知道组合成更大的牌, +// 3.合并顺子 +// 4.最后再确定单顺,剩余的牌和三条连对进行组合 看看能不能组成新的顺子(娱乐版不拆三条)拆连队只能拆长度等于2的连对 +// 找出所有长度为3的顺子 +func findStraights(cards []int32) map[int32][]int32 { + straights := [][]int32{} + length := len(cards) + if length < 3 { + return make(map[int32][]int32) + } + + sort.Slice(cards, func(i, j int) bool { + return Value(cards[i]) < Value(cards[j]) + }) + + for i := 0; i <= length-3; { + if Value(cards[i]) == Value(cards[i+1]) || Value(cards[i]) >= 10 { + i++ + continue + } + if i+2 < length { + arr := []int32{} + value := cards[i] + arr = append(arr, cards[i]) + for j := i + 1; j <= length-1; j++ { + if Value(cards[j]) == Value(value) || Value(cards[j]) >= 12 { + continue + } + arr = append(arr, cards[j]) + value = cards[j] + if len(arr) == 3 { + break + } + } + if isStraight(arr) { + straights = append(straights, arr) + // 从牌组中移除当前顺子的牌 + cards = removeCards(cards, arr) + length = len(cards) // 更新牌组长度 + } else { + i++ + } + } else { + break + } + } + sort.Slice(straights, func(i, j int) bool { + return Value(straights[i][0]) < Value(straights[j][0]) + }) + //2.剩余的牌再和已经取出来的顺子组合,知道组合成更大的牌, + removeCard := []int32{} + for _, card := range cards { + for i, straight := range straights { + if Value(card) == Value(straight[len(straight)-1]+1) && Value(card) < 12 { + straight = append(straight, card) + removeCard = append(removeCard, card) + straights[i] = straight + break + } + } + } + cards = removeCards(cards, removeCard) + //3.合并顺子 + for i := 0; i < len(straights)-1; i++ { + if Value(straights[i][len(straights[i])-1]) == Value(straights[i+1][0])-1 { + straights[i] = append(straights[i], straights[i+1]...) + straights = append(straights[:i+1], straights[i+2:]...) + i -= 1 + } + } + //转正map + straightsMap := make(map[int32][]int32) + for i, straight := range straights { + straightsMap[int32(i)] = straight + } + return straightsMap +} + +// 暂时不做 现在的找牌顺序可以兼容这种 拆三条 二连对组顺子 +func TwoFindStraights(cards []int32, isYuLe bool, cardsType map[int]map[int32][]int32, straights [][]int32) { + //4.剩余的牌和三条连对进行组合 看看能不能组成新的顺子(娱乐版不拆三条)拆连队只能拆长度等于2的连对 + //找相邻的两张牌 + twoCards := [][]int32{} + for i := 0; i < len(cards)-1; i++ { + if cards[i] == cards[i+1]-1 || cards[i] == cards[i+1]-2 { + twoCards = append(twoCards, []int32{cards[i], cards[i+1]}) + i += 1 + } + } + if len(twoCards) > 0 { + indexesToRemove := []int{} + for _, twoCard := range twoCards { + value := twoCard + //添加三条 和连对的牌进去再找顺子 + if !isYuLe { + threeCards := cardsType[Triple] + for i, three := range threeCards { + //logger.Logger.Tracef("开始拆三条组顺子 three = %d \n", three) + value = append(value, three[0]) + sort.Slice(value, func(i, j int) bool { + return value[i] < value[j] + }) + //判断是不是顺子 + if isStraight(value) { + //找到顺子了 移除三张 + cards = removeCards(cards, value) + delete(cardsType[Triple], i) + straights = append(straights, value) + indexesToRemove = append(indexesToRemove, int(i)) + cards = append(cards, three[1], three[2]) + break + } + } + } + + } + // 根据索引从切片中删除元素 + for i := len(indexesToRemove) - 1; i >= 0; i-- { + twoCards = append(twoCards[:indexesToRemove[i]], twoCards[indexesToRemove[i]+1:]...) + } + //添加二连对组顺子 + + //最后在合并一下顺子 + for i := 0; i < len(straights)-1; i++ { + if straights[i][len(straights[i])-1] == straights[i+1][0]-1 { + straights[i] = append(straights[i], straights[i+1]...) + straights = append(straights[:i+1], straights[i+2:]...) + i -= 1 + } + } + } +} + +func removeCards(cards []int32, toRemove []int32) []int32 { + newCards := []int32{} + removed := make([]bool, len(cards)) + + for _, card := range toRemove { + for i, c := range cards { + if c == card && !removed[i] { + removed[i] = true + break + } + } + } + + for i, card := range cards { + if !removed[i] { + newCards = append(newCards, card) + } + } + + return newCards +} + +func isStraight(cards []int32) bool { + if len(cards) < 3 { + return false + } + return Value(cards[1])-Value(cards[0]) == 1 && Value(cards[2])-Value(cards[1]) == 1 +} + +// 找三张 +func GetThreeOfkind(cards []int32) map[int32][]int32 { + cardCount := make(map[int32]int) + var three []int32 + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + } + // 查找炸弹组合 + threeMap := make(map[int32][]int32) + for card, count := range cardCount { + if count >= 3 { + three = append(three, card) + threeMap[card] = cardMap[card] + } + } + return threeMap +} + +// 找三顺 +func GetStraightTriple(cards []int32) map[int32][]int32 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + result := make(map[int32][]int32) + for i := 0; i < len(cards)-3; { + num := 1 + for j := i + 3; j < len(cards); { + if Value(cards[i]) == Value(cards[j])-num && Value(cards[j]) < 12 { + num += 1 + } + j += 3 + } + if num >= 2 { + arr := cards[i : i+num*3] + result[int32(Value(arr[len(arr)-1]))] = arr + } + i += 3 + + } + return result +} + +// 找对子 +func GetPair(cards []int32) ([]int32, map[int32][]int32) { + cardCount := make(map[int32]int) + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + } + // 查找对子组合 + pairMap := make(map[int32][]int32) + var pair []int32 + for card, count := range cardCount { + if count >= 2 { + //logger.Logger.Tracef("\n找到所有的对子:%d \n", card) + pair = append(pair, card) + pairMap[card] = cardMap[card] + } + } + return pair, pairMap +} + +// 牌型转换 +func CardsChange(cards []int32) []int { + //map key-相同牌数量 value-key: + var cardChange []int + for _, card := range cards { + cardChange = append(cardChange, Value(card)) + } + sort.Ints(cardChange) + return cardChange +} + +// 删除牌 +func DelCards(cards []int32, delCards map[int32][]int32) []int32 { + // 创建一个新的切片,用于存储不需要删除的元素 + newCards := make([]int32, 0, len(cards)) + + // 遍历 cards 中的元素 + for _, card := range cards { + // 检查当前元素是否需要删除 + shouldDelete := false + for _, delCard := range delCards { + for _, del := range delCard { + if card == del { + shouldDelete = true + break + } + } + } + + // 如果不需要删除,则将元素添加到新的切片中 + if !shouldDelete { + newCards = append(newCards, card) + } + } + + return newCards +} + +// 获取牌型分计算 +func GetCardTypeScore(cardsType map[int]map[int32][]int32, isYuLe bool, cards []int32) (int, int) { + //计算每张牌的牌值 + cardValues := []int{ + -120, -100, -80, -60, -40, -20, 0, 20, 50, 90, 140, 200, 300, + -115, -95, -75, -55, -35, -15, 5, 25, 55, 95, 145, 205, 325, -110, -90, -70, -50, -30, -10, 10, 30, 60, 100, 150, 210, 350, -105, -85, -65, -45, -25, -5, 15, 35, 65, 105, 155, 215, 400, + } + //logger.Logger.Trace("计算牌型分 cardsType = ", cardsType) + //计算牌型分值 + var score int + //炸弹 + if len(cardsType[Four_Bomb]) > 0 { + if isYuLe { + score += len(cardsType[Four_Bomb]) * 1000 + } else { + score += len(cardsType[Four_Bomb]) * 700 + } + } + //连队 + if len(cardsType[Straight_Twin]) > 0 { + for _, twin := range cardsType[Straight_Twin] { + switch len(twin) / 2 { + case 5: + score += 500 + case 4: + score += 400 + case 3: + score += 300 + case 2: + score += 200 + } + } + } + //顺子 + if len(cardsType[Straight]) > 0 { + for _, straight := range cardsType[Straight] { + score += len(straight)*100 + (len(straight)-3)*(cardValues[straight[len(straight)-1]]/3) + } + + } + //三张 + if len(cardsType[Triple]) > 0 { + for _, triples := range cardsType[Triple] { + if Value(triples[0]) < 7 { + score += int(math.Abs(float64(250 + cardValues[triples[2]]))) + } else { + score += cardValues[triples[0]] + cardValues[triples[1]] + cardValues[triples[2]] + } + } + if isYuLe { + score += len(cardsType[Triple]) * 300 + } + } + //两对 + if len(cardsType[Twin]) > 0 { + for _, twins := range cardsType[Twin] { + if Value(twins[0]) < 7 { + score += cardValues[twins[1]] / 2 + } else { + score += cardValues[twins[0]] + cardValues[twins[1]] + } + } + } + + //删除三连对以上且三连对小于10的牌 + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + copyCards = DelCards(copyCards, cardsType[Four_Bomb]) + for _, straightTwin := range cardsType[Straight_Twin] { + delMap := make(map[int32][]int32) + if len(straightTwin)/2 >= 3 && Value(straightTwin[len(straightTwin)-1]) < 10 { + delMap[int32(len(delMap)+1)] = straightTwin + copyCards = DelCards(copyCards, delMap) + } + } + + for _, card := range copyCards { + score += cardValues[card] + } + handCardnum := len(cardsType[Four_Bomb]) + len(cardsType[Straight_Twin]) + len(cardsType[Straight]) + len(cardsType[Triple]) + len(cardsType[Twin]) + len(cardsType[Single]) + if isYuLe { + handCardnum -= (len(cardsType[Triple]) * 2) + } + if handCardnum <= 1 { + handCardnum = 1 + if isYuLe && handCardnum == 1 { + if len(cardsType[Triple]) > 0 { + if len(cards) > 5 { + handCardnum = 2 + } + } + } + } + if Have6StraightTwin(cards) || + Have12Straight(cards) || + Have2FourBomb(cards) { + return 9999, 1 + } + return score - (handCardnum-5)*150, handCardnum +} + +// 压牌 isWin-是否胜利 +func GetPressCards(cards, lastCards []int32, data *tienlenApi.PredictRequest, pos int32) []int32 { + logger.Logger.Tracef("---------------压牌开始-------------------") + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + isWin := data.IsWin + //测试代码 + //isWin = false + logger.Logger.Trace("压牌 调控输赢 isWin = ", isWin) + outCards := []int32{} + //压牌测试代码 + // cards = []int32{6, 19, 2, 28, 41, 43, 12, 25} + // lastCards = []int32{14, 27} + // pos = 0 + // data.Cards_left_2 = []int32{45, 32} + // data.Cards_left_3 = nil + // data.Cards_left_1 = nil + // data.Last_pos = 2 + + lastPos := data.Last_pos //上家出牌人的位置 + logger.Logger.Tracef(" 上家出牌人的位置%d,出的牌:%v\n", lastPos, lastCards) + isYuLe := data.IsTienLenYule + isEnd := data.IsEnd + winSnids := data.WinSnids + logger.Logger.Trace("当前模式是否是打到底%v\n,当前获胜玩家:%v\n", isEnd, winSnids) + //获取牌型 + cardsTypeMap := GetCardsType(cards, isYuLe) + score, handCardnum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + logger.Logger.Tracef("压牌 获取牌型:%v\n,分值:%d\n手牌:%d、n,我的位置:%d\n", cardsTypeMap, score, handCardnum, pos) + //获取出牌人的 手数 + otherCards := getOtherPlayerCards(pos, lastPos, data) + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + lastScore, lastHandCardnum := GetCardTypeScore(otherCardsTypeMap, isYuLe, otherCards) + logger.Logger.Tracef("压牌,获取出牌玩家的剩余牌的牌型:", otherCardsTypeMap, "分值:", lastScore, "手牌:", lastHandCardnum, "\n") + //获取下一家的位置和剩余的牌 + nextPos, nextCards := GetNextPos(pos, data) + logger.Logger.Tracef("我的位置:%d\n,获取下家牌和位置:nextPos = %d\n,nextCards := %v\n", pos, nextPos, nextCards) + isRule, ruleType := RulePopEnable_yl(lastCards) + if isRule { + switch ruleType { + case Single: + //单张 + cardsMap := cardsTypeMap[Single] + //转成数组 + singleArr := []int32{} + for _, single := range cardsMap { + singleArr = append(singleArr, single[0]) + } + if isEnd && !isYuLe { + sort.Slice(singleArr, func(i, j int) bool { + if Value(singleArr[i]) == Value(singleArr[j]) { + return singleArr[i] < singleArr[j] + } + return Value(singleArr[i]) < Value(singleArr[j]) + }) + } + //如果我的下一家剩余一张牌 出比他大的牌 + if len(singleArr) > 0 { + if len(singleArr) == 2 && len(cards) == 2 { + for _, mycard := range singleArr { + if getMaxCardTypeNum(Single, []int32{mycard}, pos, data) { + outCards = []int32{mycard} + } + } + } else { + for _, card := range singleArr { + if Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) < Color(lastCards[0]) { + continue + } + } + + if len(nextCards) == 1 { + if Value(card) >= Value(nextCards[0]) { + if Value(card) == Value(nextCards[0]) { + if Color(card) > Color(nextCards[0]) { + outCards = []int32{card} + break + } + } else { + outCards = []int32{card} + break + } + } + } else { + outCards = []int32{card} + break + } + } + } + } + } else { + //找顺子交集 返回值[][]int32 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + for _, arr := range result { + for _, card := range arr { + if Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card) >= Value(nextCards[0]) { + if Value(card) == Value(nextCards[0]) { + if Color(card) > Color(nextCards[0]) { + outCards = []int32{card} + break + //return card + } + } else { + outCards = []int32{card} + break + } + } + } else { + outCards = []int32{card} + break + } + } + } + if len(outCards) > 0 { + break + } + } + } + } + if isWin { + //拆牌 先找顺子的交集 拆对子 拆2 拆长度大于3的顺子 最后找大于这张出牌的牌除了炸弹 出牌这家只剩一首牌了 + if len(outCards) == 0 && len(cardsTypeMap[Twin]) > 0 { + logger.Logger.Trace("拆对子") + twinMap := cardsTypeMap[Twin] + //拆小于2的对子 + for _, card := range twinMap { + if Value(card[0]) == 12 { + //排除掉对2 + continue + } + //先拆小于2的对子 + if Value(card[0]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[0]} + break + } + } else { + outCards = []int32{card[0]} + break + } + } + } else { + //玩家出完这张牌 还剩一张 剩余的牌比我现在出的牌大 + if len(otherCards) == 1 && Value(otherCards[0]) >= Value(card[0]) { + continue + } + outCards = []int32{card[0]} + break + } + } + } + //判断一下下一家剩1张牌比我的大的情况 + if len(outCards) > 0 { + break + } + } + //直接拆2上 + if len(outCards) == 0 && + (lastHandCardnum <= 4 || handCardnum <= 3 || + (len(nextCards) == 1 && Value(nextCards[0]) >= Value(lastCards[0]))) { + for _, card := range cards { + if Value(card) == 12 && Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) > Color(lastCards[0]) { + outCards = []int32{card} + break + } else { + continue + } + } else { + outCards = []int32{card} + break + } + + } + } + if len(outCards) > 0 && len(otherCards) == 1 && Value(otherCards[0]) >= Value(outCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } else { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } + } + + //拆连对 map[int32][]int32 + if len(outCards) == 0 && len(cardsTypeMap[Straight_Twin]) > 0 { + for _, straightTwin := range cardsTypeMap[Straight_Twin] { + if len(straightTwin)/2 == 2 || lastHandCardnum == 1 { + for _, twin := range straightTwin { + if Value(twin) > Value(lastCards[0]) { + outCards = []int32{twin} + break + } + } + if len(outCards) > 0 { + break + } + } + } + } + + if !isYuLe { + //拆三条 + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 { + tripleMap := cardsTypeMap[Triple] + for _, card := range tripleMap { + if Value(card[0]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[0]} + break + } + } else { + outCards = []int32{card[0]} + break + } + } + } else { + outCards = []int32{card[0]} + break + } + } + } + } + } + //拆长度>3的顺子 + if len(outCards) == 0 && len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for _, card := range straightMap { + if len(card) <= 3 && lastHandCardnum != 1 { + continue + } + if Value(card[len(card)-1]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[len(card)-1]} + break + } + } else { + outCards = []int32{card[len(card)-1]} + break + } + } + } else { + outCards = []int32{card[len(card)-1]} + break + } + } + } + } + } + if len(outCards) == 0 && Value(lastCards[0]) == 12 { + //找三连对 >四连对 >五连对 > 炸弹 + straightTwinMap := cardsTypeMap[Straight_Twin] + //找最小长度的key + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 3 { + outCards = straightTwin + logger.Logger.Trace("-----------------单张压牌 找到连对!", outCards) + break + //return straightTwin + } + } + } + //找炸弹 + if !isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && Value(lastCards[0]) == 12 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && (lastHandCardnum <= 2 || handCardnum <= 2) { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if len(outCards) > 0 { + if Value(lastCards[0]) == 12 && Value(outCards[0]) == 12 && lastHandCardnum != 1 { + //如果出牌玩家剩余手数大于2 或者我的剩余手数小于等于3 + //存在一种情况 上家剩余1张牌 其他人出单张 上家不要 这个时候我执行了这个判断 没有出2去接牌 && 我的上家单张数量>=2 ||上家剩余1张牌了 我要跑牌了 + _, myLastCards := GetLastPos(pos, data) + if ((lastHandCardnum > 2 && len(otherCardsTypeMap[Single]) > 2) || (handCardnum > 3 && len(cardsTypeMap[Single]) >= 2)) && len(myLastCards) >= 2 { + if lastHandCardnum > 3 { + logger.Logger.Tracef("上家出2剩余的手数:%d,我的手数:%d\n,让一手", lastHandCardnum, handCardnum) + outCards = []int32{} + } + } + } + } + if len(outCards) == 0 { + if len(nextCards) == 1 && Value(lastCards[0]) < Value(nextCards[0]) { + if Value(cards[len(cards)-1]) >= Value(nextCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } + //强压 + if len(outCards) == 0 && (len(lastCards) == 1 || len(nextCards) == 1) { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + + } + //打到底 最后一首牌判断 + if len(otherCards) == 1 && (!isYuLe && isEnd) && (winSnids == nil || len(winSnids) == 0) && len(outCards) != 0 && nextPos != lastPos { + if Value(outCards[0]) == Value(cards[len(cards)-1]) && handCardnum > 2 { + if Value(outCards[0]) <= Value(otherCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + logger.Logger.Tracef("判断我的最大牌小于上家剩余的牌,不出!") + outCards = []int32{} + } + } else { + logger.Logger.Trace("打到底最后一手牌牌,让一手!") + outCards = []int32{} + } + } + } + } + + if isEnd && len(otherCards) == 0 && len(outCards) > 0 { + if len(nextCards) == 1 { + //在这加一道防线 + if Value(outCards[0]) < Value(nextCards[0]) { + //出最大牌 + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) && Value(cards[len(cards)-1]) >= Value(nextCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + + } + } + } + if len(outCards) == 0 && len(lastCards) == 1 && nextPos == lastPos { + //出最大的牌 + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Single) { + logger.Logger.Trace("出牌人打完牌了,我过一手!!!!", lastCards) + outCards = []int32{} + } + logger.Logger.Tracef("--------------单张压牌完毕,出的牌:", outCards) + return outCards + case Twin: + //对子 + cardsMap := cardsTypeMap[Twin] + if len(cardsMap) > 0 { + for _, card := range cardsMap { + if lastHandCardnum == 1 && len(otherCards) == 2 && Value(otherCards[len(otherCards)-1]) >= Value(card[1]) { + if Value(otherCards[len(otherCards)-1]) == Value(card[1]) { + if Color(otherCards[len(otherCards)-1]) > Color(card[1]) { + continue + } + } else { + continue + } + } + if Value(card[1]) >= Value(lastCards[1]) { + if Value(card[1]) == 12 { + //判断能不能直接出对2 + if handCardnum > 3 && lastHandCardnum > 3 { + continue + } + } + if Value(card[1]) == Value(lastCards[1]) { + //判断花色 + if Color(card[1]) > Color(lastCards[1]) { + outCards = card + break + } + } else { + outCards = card + break + } + } + } + //再找一遍 + if len(outCards) == 0 { + for _, card := range cardsMap { + if Value(card[1]) >= Value(lastCards[1]) { + if Value(card[1]) == 12 { + //判断能不能直接出对2 + if handCardnum > 3 && lastHandCardnum > 3 { + continue + } + } + if Value(card[1]) == Value(lastCards[1]) { + //判断花色 + if Color(card[1]) > Color(lastCards[1]) { + outCards = card + break + } + } else { + outCards = card + break + } + } + } + } + } + logger.Logger.Trace("找到对子-------------------outCards =", outCards) + //拆连对 + if isWin { + straightTwinMap := cardsTypeMap[Straight_Twin] + if len(outCards) == 0 { + if Value(lastCards[0]) == 12 { + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 4 { + outCards = straightTwin + break + } + } + } + } else { + //拆连对 + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 2 { + //判断第一个和最后一位 + if Value(straightTwin[0]) >= Value(lastCards[0]) { + if Color(straightTwin[1]) > Color(lastCards[1]) { + outCards = []int32{straightTwin[0], straightTwin[1]} + break + } + } else { + //判断最后一位 + if Value(straightTwin[3]) >= Value(lastCards[0]) { + if Color(straightTwin[3]) > Color(lastCards[1]) { + outCards = []int32{straightTwin[2], straightTwin[3]} + break + } + } + } + } + } + } + } + } + //拆三条 + //找最大的三条拆 + if !isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 { + for _, triple := range cardsTypeMap[Triple] { + if Value(triple[0]) > Value(lastCards[0]) { + outCards = []int32{triple[0], triple[1]} + logger.Logger.Trace("拆三条压对子 outCards = ", outCards) + break + + } + } + + } + } else { + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 && (lastHandCardnum <= 2 || handCardnum <= 3) { + for _, triple := range cardsTypeMap[Triple] { + if Value(triple[0]) > Value(lastCards[0]) { + outCards = []int32{triple[0], triple[1]} + logger.Logger.Trace("拆三条压对子 outCards = ", outCards) + break + } + } + + } + } + } + //找炸弹 + if isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && (lastHandCardnum <= 2 || handCardnum <= 2) { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } else { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && Value(lastCards[0]) == 12 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if len(outCards) > 0 { + if Value(lastCards[0]) == 12 && Value(outCards[0]) == 12 { + //如果出牌玩家剩余手数大于2 或者我的剩余手数小于等于3 + if lastHandCardnum > 3 && (len(otherCardsTypeMap[Twin]) > 2 || len(otherCardsTypeMap[Single]) > 2) || (handCardnum > 3 && len(cardsTypeMap[Single]) > 2) { + if lastHandCardnum > 1 { + logger.Logger.Tracef("上家出2剩余的手数:%d,我的手数:%d\n,让一手", lastHandCardnum, handCardnum) + outCards = []int32{} + } + } + } + } + if len(outCards) == 0 && lastHandCardnum <= 2 { + //重组对子 找大对子 + _, pairMap := GetPair(cards) + if len(pairMap) > 0 { + maxkey := int32(-1) + for key, pairs := range pairMap { + if Value(pairs[0]) >= Value(lastCards[0]) { + if key > maxkey { + maxkey = key + } + } + } + if maxkey != -1 { + outCards = append(outCards, pairMap[maxkey][0], pairMap[maxkey][1]) + } + } + } + if len(otherCards) == 2 && lastHandCardnum == 1 && (!isYuLe && isEnd) && (winSnids == nil || len(winSnids) == 0) && len(outCards) != 0 && nextPos != lastPos { + if Value(outCards[0]) == Value(cards[len(cards)-1]) && handCardnum > 2 { + if Value(outCards[0]) <= Value(otherCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + outCards = []int32{} + } + } else { + outCards = []int32{} + } + } + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Twin) { + outCards = []int32{} + } + logger.Logger.Trace("--------------压牌完毕,出的牌:", outCards) + return outCards + case Straight: + //顺子 + cardsMap := cardsTypeMap[Straight] + //获取顺子长度 + + for _, card := range cardsMap { + if len(card) == len(lastCards) { + if Value(card[len(card)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(card[len(card)-1]) == Value(lastCards[len(lastCards)-1]) { + //比较花色 + if Color(card[len(card)-1]) > Color(lastCards[len(lastCards)-1]) { + outCards = card + break + //return card + } + } else { + outCards = card + break + //return card + } + } + } else if len(card) > len(lastCards) && Value(card[len(card)-1]) >= Value(lastCards[len(lastCards)-1]) && (lastHandCardnum <= 2 || isEnd) { + //拆顺子 + if Value(card[len(card)-1]) == Value(lastCards[len(lastCards)-1]) { + //比较花色 + if Color(card[len(card)-1]) < Color(lastCards[len(lastCards)-1]) { + //截取 + //return []int32{} + } + } + outCards = card[len(card)-len(lastCards):] + logger.Logger.Tracef("拆顺子 outCards = %d\n,lastCard = %d\n", outCards, lastCards) + } + } + // + if len(outCards) == 0 && lastHandCardnum <= 2 { + //找顺子 + allStraight := GetAllStraight(cards, len(lastCards)) + logger.Logger.Trace("allStraight = ", allStraight) + if len(allStraight) > 0 { + for _, straight := range allStraight { + if len(straight) >= len(lastCards) { + if Value(straight[len(straight)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(straight[len(straight)-1]) == Value(lastCards[len(lastCards)-1]) { + if Color(straight[len(straight)-1]) > Color(lastCards[len(lastCards)-1]) { + outCards = straight[len(straight)-len(lastCards):] + break + } + } else { + outCards = straight[len(straight)-len(lastCards):] + break + } + logger.Logger.Tracef("找所有顺子来压牌 outCards = ", outCards) + } + } + } + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight) { + outCards = []int32{} + } + logger.Logger.Trace("--------------顺子压牌完毕,出的牌:", outCards) + return outCards + case ColorStraight: + //同花顺 + if len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for _, straight := range straightMap { + if len(straight) >= len(lastCards) { + //判断是不是同花顺 + if IsColorStraight(straight) { + if Value(straight[len(straight)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(straight[len(straight)-1]) == Value(lastCards[len(lastCards)-1]) { + if Color(straight[len(straight)-1]) > Color(lastCards[len(lastCards)-1]) { + return straight[len(straight)-len(lastCards):] + } + } else { + return straight[len(straight)-len(lastCards):] + } + } + } + } + } + } + + //找炸弹 + if isYuLe && ((len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2) || handCardnum <= 2) { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + logger.Logger.Trace("--------------同花顺压牌完毕,出的牌:", outCards) + return cardsTypeMap[Four_Bomb][minKey] + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, ColorStraight) { + outCards = []int32{} + } + case Straight_Twin: + //连对 + //获取连对长度 + lastCardNum := len(lastCards) / 2 + lastMaxCard := Value(lastCards[len(lastCards)-1]) + cardsMap := cardsTypeMap[Straight_Twin] + for _, card := range cardsMap { + if len(card) >= len(lastCards) { + if len(card) == len(lastCards) { + if Value(card[len(card)-1]) >= lastMaxCard { + if Value(card[len(card)-1]) == lastMaxCard { + //比较花色 + if Color(card[len(card)-1]) > Color(lastCards[len(lastCards)-1]) { + //截取 + outCards = card + break + } + } else { + outCards = card + break + } + } + } else { + outCards = card + break + } + } + + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && (len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 && lastCardNum <= 3) || handCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } else { + if lastCardNum == 3 { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + } + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight_Twin) { + outCards = []int32{} + } + logger.Logger.Tracef("--------------连对压牌完毕,出的牌:", outCards) + return outCards + case Triple: + //三张 + cardsMap := cardsTypeMap[Triple] + for _, card := range cardsMap { + if Value(card[len(card)-1]) > Value(lastCards[len(lastCards)-1]) { + if Value(card[0]) == 12 && lastHandCardnum > 2 { + continue + } + outCards = card + break + //return card + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && ((len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2) || handCardnum <= 2) { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Triple) { + outCards = []int32{} + } + logger.Logger.Trace("--------------三张压牌完毕,出的牌:", outCards) + return outCards + case Four_Bomb: + //炸弹 + cardsMap := cardsTypeMap[Four_Bomb] + for _, card := range cardsMap { + if Value(card[0]) > Value(lastCards[0]) { + logger.Logger.Trace("--------------炸弹压牌,找到的牌:", outCards) + if isYuLe && (lastHandCardnum <= 2 || handCardnum <= 2) { + return card + } + if !isYuLe { + return card + } + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Four_Bomb) { + outCards = []int32{} + } + case Straight_Triple: + //三顺 + cardsMap := cardsTypeMap[Straight_Triple] + if len(cardsMap) > 0 { + for _, card := range cardsMap { + if Value(card[0]) > Value(lastCards[0]) && len(card) == len(lastCards) { + outCards = card + break + } + } + } + if isYuLe { + //找炸弹 + if (len(outCards) == 0 || len(outCards) != len(lastCards)) && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight_Triple) { + outCards = []int32{} + } + logger.Logger.Trace("--------------三顺压牌完毕,出的牌:", outCards) + return outCards + case Plane_Single: + //三代单 --不用考虑 只有最后一首才会出三代单 + singleHand, singleLen := FindPlaneSingleHead(lastCards) + logger.Logger.Trace("飞机找带双 singleHand = ", singleHand, "singleLen = ", singleLen) + if singleLen > 1 && len(cards) >= len(lastCards) { + //333 444 5 6 7 8 + //找相同长度三顺 + straightTripleMap := cardsTypeMap[Straight_Triple] + if len(straightTripleMap) > 0 { + for _, straightTriple := range straightTripleMap { + if len(straightTriple)/3 >= singleLen { + if Value(straightTriple[len(straightTriple)-1]) > int(singleHand) { + outCards = straightTriple[len(straightTriple)-singleLen*3:] + } + } + } + } + } + if len(outCards) != 0 { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + delCard := make(map[int32][]int32) + delCard[0] = outCards + copyCards = DelCards(copyCards, delCard) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + num := len(cards) - len(lastCards) + if num == 1 { + //留最大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + outCards = []int32{} + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + outCards = []int32{} + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-3:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + for _, value := range cards { + status := true + for _, straight := range outCards { + if value == straight || Value(value) == Value(straight) { + status = false + break + } + } + if status { + outCards = append(outCards, value) + } + if len(outCards) == len(lastCards) { + break + } + } + } + } + if isYuLe { + //找炸弹 + if (len(outCards) == 0 || len(outCards) != len(lastCards)) && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + return cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Plane_Single) { + outCards = []int32{} + } + logger.Logger.Trace("--------------飞机带单压牌完毕,出的牌:", outCards) + return outCards + case Plane_Twin: + outCards = []int32{} + twinHand, twinLen := FindPlaneTwinHead(lastCards) + logger.Logger.Trace("飞机找带双 twinHand = ", twinHand, "twinLen = ", twinLen) + if twinLen == 1 { + //三代双 + cardsMap := cardsTypeMap[Triple] + key := int32(999) + for i, data1 := range cardsMap { + if Value(data1[len(data1)-1]) > int(twinHand) { + if i < key { + //card = data1 + key = i + } + break + } + } + if key != 999 { + outCards = cardsTypeMap[Triple][key] + } + if len(outCards) > 0 { + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + if len(cardsTypeMap[Single]) >= 2 && Value(cardsTypeMap[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsTypeMap[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + //logger.Logger.Trace("找出最小两张单排的值:", minValue) + outCards = append(outCards, minValue[0], minValue[1]) + } + } else if len(cardsTypeMap[Twin]) >= 1 && Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 { + //三代一对 + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + //logger.Logger.Trace("压牌,三代一对: ", card) + } else { + //直接找最小的两张牌 + if len(cards) >= 5 { + for _, value := range cards { + if Value(value) != Value(outCards[0]) { + outCards = append(outCards, value) + if len(outCards) == 5 { + return outCards + } + } + } + } + } + } + } else { + //333 444 555 + //找相同长度三顺 + if len(cards) >= len(lastCards) { + straightTripleMap := cardsTypeMap[Straight_Triple] + if len(straightTripleMap) > 0 { + for _, straightTriple := range straightTripleMap { + if len(straightTriple)/3 >= twinLen { + if Value(straightTriple[len(straightTriple)-1]) > int(twinHand) { + outCards = straightTriple[len(straightTriple)-twinLen*3:] + } + } + } + } + + if len(outCards) != 0 { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + delCard := make(map[int32][]int32) + delCard[0] = outCards + copyCards = DelCards(copyCards, delCard) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + num := len(cards) - len(lastCards) + if num == 1 { + //留最大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + outCards = []int32{} + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + outCards = []int32{} + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + maxCard := copyCards[len(copyCards)-3:] + outCards = []int32{} + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + for _, value := range cards { + status := true + for _, straight := range outCards { + if value == straight || Value(value) == Value(straight) { + status = false + break + } + } + if status { + outCards = append(outCards, value) + } + if len(outCards) == len(lastCards) { + break + } + } + } + } + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && (lastHandCardnum <= 2 || handCardnum <= 2) { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + return cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Plane_Twin) { + outCards = []int32{} + } + + logger.Logger.Trace("--------------三代双压牌完毕,出的牌:", outCards) + return outCards + default: + logger.Logger.Trace("--------------default压牌完毕,出的牌:", outCards) + return outCards + } + } + + logger.Logger.Trace("--------------压牌完毕,出的牌:", outCards) + return outCards +} + +// 出牌 isMin是否先手出最小的牌 +func GetOutCards(cards []int32, data *tienlenApi.PredictRequest, pos int32) []int32 { + logger.Logger.Trace("---------------出牌开始-------------------") + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + isWin := data.IsWin + //测试代码 + //isWin = false + logger.Logger.Trace("出牌 调控输赢 isWin = ", isWin) + //测试代码 + //cards = []int32{51, 4, 5} + //pos = 0 + //data.Cards_left_2 = []int32{0, 1, 2, 3} + //data.Cards_left_3 = nil + //data.Cards_left_1 = nil + //data.Last_pos = 2 + + isYuLe := data.IsTienLenYule + isMin := data.IsFirstHand + isEnd := data.IsEnd + winSnids := data.WinSnids + //获取牌型 + cardsTypeMap := GetCardsType(cards, isYuLe) //map[int]map[int32][]int32 + cardsTypeMap = reloadcardsType(cardsTypeMap, pos, data, isYuLe) + logger.Logger.Trace("出牌 获取我的牌型:", cardsTypeMap, "\n") + score, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + logger.Logger.Tracef("出牌 获取当前手牌的牌型分数:%v, 手牌数量:%v\n,我的位置:%d \n", score, handNum, pos) + if handNum <= 1 { + logger.Logger.Trace("只剩最后一手牌 直接全出了", cards) + return cards + } + //出牌逻辑 + outCards := []int32{} + cardValue := -1 + if isMin { + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + logger.Logger.Trace("先出第一手,当前出牌只能出最小的牌!", outCards) + return outCards + } + //判断其他玩家剩余的牌的牌型是否能压过当前出牌的牌型 + //出牌顺序 + //出单张 判断下一家剩余1张牌且剩余的牌比我出的牌大 不出 + otherMinHandNum, otherMinCardsNum, otherPos, otherCardsType := getOtherMinCards(pos, data, isYuLe) + + logger.Logger.Tracef("玩家出牌,获取其他玩家最小剩余手数 和剩余牌数及位置剩余的牌型 otherMinHandNum=%d otherMinCardsNum=%d otherPos=%d cardsType=%d", otherMinHandNum, otherMinCardsNum, otherPos, otherCardsType) + //优先出三张 如果是娱乐模式 直接带单张 或者最小的对子 + if isYuLe { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + logger.Logger.Tracef("出三张 cardValue = %d ,outCards = %d", cardValue, outCards) + } + //出单张 + if len(outCards) == 0 && handNum > 2 { + straightNum := len(cardsTypeMap[Straight]) + //测试一下 + //if isYuLe { + if len(cardsTypeMap[Straight]) > 0 { + straightNum = len(cardsTypeMap[Straight])*2 + 1 + } + //} + if len(cardsTypeMap[Single]) > straightNum && len(cardsTypeMap[Single]) > (len(cardsTypeMap[Twin])+len(cardsTypeMap[Straight_Twin])) { + outCards = getSingle(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum <= 1 && otherCardsType == Single { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if Value(cards[0]) < Value(otherCards[0]) { + logger.Logger.Trace("出单张,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } else if Value(cards[0]) == Value(otherCards[0]) { + //判断花色 + if Color(cards[0]) < Color(otherCards[0]) { + logger.Logger.Trace("出单张,牌值相同,花色小,不出!outCards = ", outCards) + outCards = []int32{} + } + } + + otherCardsMap := getOtherCards(pos, data) + + for _, single := range cardsTypeMap[Single] { + status := true + for _, other := range otherCardsMap { + if len(other) == 1 { + if Value(single[0]) <= Value(other[0]) { + if Value(single[0]) == Value(other[0]) && Color(single[0]) > Color(other[0]) { + continue + } + status = false + } + } + } + if status { + outCards = single + break + } + } + } + //如果是全场最大的单张 不出 + if len(outCards) > 0 && (otherMinCardsNum != 1) { + if Value(outCards[0]) == 12 && handNum > 2 { + outCards = []int32{} + } + } + } + } + } + + //出对子 判断下一家剩余2张牌且剩余的牌比我出的牌大 不出 + if len(outCards) == 0 { + straightNum := len(cardsTypeMap[Straight]) + if isYuLe { + //straightNum = len(cardsTypeMap[Straight]) + if len(cardsTypeMap[Straight]) > 0 { + straightNum = len(cardsTypeMap[Straight])*2 + 1 + } + } + if len(cardsTypeMap[Twin]) > straightNum { + outCards = getTwin(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + logger.Logger.Tracef("------首牌出对子 outcards = %d\n,所有的对子:%d\n", outCards, cardsTypeMap[Twin]) + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if otherMinHandNum == 1 && otherMinCardsNum == 2 && otherCardsType == Twin { + + if Value(outCards[1]) < Value(otherCards[1]) { + logger.Logger.Trace("出对子,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } else if Value(outCards[1]) == Value(otherCards[1]) { + //判断花色 + if Color(outCards[1]) < Color(otherCards[1]) { + logger.Logger.Trace("出对子,牌值相同,花色小,不出!outCards = ", outCards) + outCards = []int32{} + } + } + } + + otherCardsMap := getOtherCards(pos, data) + + for _, twin := range cardsTypeMap[Twin] { + if Value(twin[0]) == 12 && handNum > 2 { + continue + } + for _, other := range otherCardsMap { + _, otherRuleType := RulePopEnable(other) + if len(other) == 2 && otherRuleType == Twin { + if Value(twin[1]) >= Value(other[1]) { + if Value(twin[1]) == Value(other[1]) { + if Color(twin[0]) > Color(other[0]) { + outCards = twin + break + } else { + continue + } + } + outCards = twin + break + + } + } + } + + } + } + } + } + + //出三张 + if isYuLe { + if len(outCards) == 0 { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 { + if otherMinHandNum == 1 && otherMinCardsNum >= 3 && otherCardsType == Triple { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + //获取牌型 + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + otherTriple := otherCardsTypeMap[Triple] + //找最小key + if len(otherTriple) > 0 { + otherMinKey := findMinKey(otherTriple) + if cardValue < Value(otherTriple[otherMinKey][0]) { + logger.Logger.Trace("出三张,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } + } + } + } + } + } + + //顺子 + if len(outCards) == 0 { + outCards = getStraight(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum == len(outCards) && otherCardsType == Straight { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + + if Value(outCards[len(outCards)-1]) < Value(otherCards[len(otherCards)-1]) { + logger.Logger.Trace("出顺子,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } else if Value(outCards[len(outCards)-1]) == Value(otherCards[len(otherCards)-1]) { + //判断花色 + if Color(outCards[len(outCards)-1]) < Color(otherCards[len(otherCards)-1]) { + logger.Logger.Trace("出顺子,牌值相同,花色小,不出!outCards = ", outCards) + outCards = []int32{} + } + } + } + //如果其他玩家剩余牌全是顺子 并且比我的大 我不出顺子了 (后续没牌型可出了!!!! + } + } + if !isYuLe { + if len(outCards) == 0 { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum >= 3 && otherCardsType == Triple { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + //获取牌型 + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + otherTriple := otherCardsTypeMap[Triple] + //找最小key + if len(otherTriple) > 0 { + otherMinKey := findMinKey(otherTriple) + if cardValue < Value(otherTriple[otherMinKey][0]) { + logger.Logger.Trace("出三张,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } + } + } + } + } + } + //连对 + if len(outCards) == 0 { + outCards = getStraightTwin(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum == len(outCards) && otherCardsType == Straight_Twin { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if Value(outCards[len(outCards)-1]) < Value(otherCards[len(otherCards)-1]) { + logger.Logger.Trace("出连对,其他玩家只剩一首牌 且剩余的牌比我出的牌大 不出!outCards = ", outCards) + outCards = []int32{} + } else if Value(outCards[len(outCards)-1]) == Value(otherCards[len(otherCards)-1]) { + //判断花色 + if Color(outCards[len(outCards)-1]) < Color(otherCards[len(otherCards)-1]) { + logger.Logger.Trace("出连队,牌值相同,花色小,不出!outCards = ", outCards) + outCards = []int32{} + } + } + } + } + if len(outCards)/2 >= 3 && handNum > 2 { + outCards = []int32{} + } + } + //炸弹 这个就不用检查了 + if len(outCards) == 0 && handNum <= 2 { + if isYuLe { + if len(cardsTypeMap[Triple]) < 2 && len(cards) < 13 { + outCards = getBomb(cardsTypeMap, cards, isYuLe) + } + } else { + //出炸弹 + outCards = getBomb(cardsTypeMap, cards, isYuLe) + } + } + //额外判断 + //获取下一家的位置和剩余的牌 + if isWin { + if handNum <= 2 { + //判断我的剩余牌型中有没有全场最大的牌型 + for cardType, myCards := range cardsTypeMap { //map[int]map[int32][]int32 + for _, mycard := range myCards { + if getMaxCardTypeNum(cardType, mycard, pos, data) { + logger.Logger.Trace("最后两手牌,找到最大牌型,mycard:", mycard) + if cardType == Triple { + //出三代二 + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 { + return outCards + } else { + continue + } + } + return mycard + } + } + } + } + if (handNum <= 3 && isYuLe && len(cardsTypeMap[Triple]) == 0) || (handNum <= 3 && !isYuLe) { + myMaxCardStatus, maxCardNum, maxCardsArr := getMaxCards(cards, data, pos) + if len(outCards) == 1 { + //判断当前最大牌在不在我手 + if !myMaxCardStatus { + //找最长的牌 + for cardType, myCards := range cardsTypeMap { //map[int]map[int32][]int32 + for _, mycard := range myCards { + if (cardType == Straight_Twin && len(mycard)/2 >= 3) || cardType == Four_Bomb { + //手里有连炸 + continue + } + if len(mycard) > len(outCards) { + if cardType == Triple { + //出三代二 + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 { + return outCards + } else { + break + } + } + if cardType != Single && cardType != Twin { + outCards = mycard + } else { + if Value(mycard[0]) != Value(cards[len(cards)-1]) { + outCards = mycard + } + } + } + } + } + } + } + //获取我最大牌的 + if myMaxCardStatus { + if otherMinCardsNum > 1 { + //这个时候我应该出单牌 + //找单张 - 对子- 和最大牌数量相等的顺子长度+1的顺子 + status := false + if len(cardsTypeMap[Single]) > 0 { + card := getSingle(cardsTypeMap, cards, isYuLe) + if card[0] != maxCardsArr[len(maxCardsArr)-1] { + status = true + } + } + if len(cardsTypeMap[Single]) > 0 && status { + card := getSingle(cardsTypeMap, cards, isYuLe) + if len(card) > 0 { + outCards = card + } + } else if len(cardsTypeMap[Twin]) > 0 { + minKey := findMinKey(cardsTypeMap[Twin]) + //map[int]map[int32][]int32 + if cardsTypeMap[Twin][minKey] != nil { + outCards = []int32{cardsTypeMap[Twin][minKey][0]} + } + logger.Logger.Trace("对子拆单张出!") + } else { + if maxCardNum > 1 { + //先找顺子的交集 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + if len(result) > 0 { + for _, arr := range result { + outCards = arr + break + } + } + } else if len(cardsTypeMap[Straight]) > 0 { + minLength := 13 + minKey := int32(0) + for i, straight := range cardsTypeMap[Straight] { + if len(straight) < minLength { + minKey = i + } + } + if len(cardsTypeMap[Straight][minKey]) <= maxCardNum+1 { + outCards = []int32{cardsTypeMap[Straight][minKey][0]} + logger.Logger.Trace("拆顺子,出单张!outCards = ", outCards) + } + } + } + } + } + } + } + } + //最后直接出最小牌型的牌 + if len(outCards) == 0 { + //直接出最大的单牌 + if otherMinHandNum == 1 { + //先找其他牌型 + for cardType, card := range cardsTypeMap { + if len(card) > 0 { + if cardType != Single { + minKey := findMinKey(card) + outCards = card[minKey] + logger.Logger.Trace("最后直接先出其他牌型的牌 outCards = ", outCards) + break + } else if cardType == Triple && isYuLe { + //找两张单张 + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + if len(cardsTypeMap[Single]) >= 2 && Value(cardsTypeMap[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsTypeMap[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + outCards = append(outCards, minValue[0], minValue[1]) + } + } else if len(cardsTypeMap[Twin]) >= 1 && Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 { + //三代一对 + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + } + } + } + } + if len(outCards) == 0 { + nextPos, nextCards := GetNextPos(pos, data) + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if nextPos != int32(otherPos) && Value(nextCards[len(nextCards)-1]) > Value(otherCards[0]) { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + logger.Logger.Trace("最后直接找最小牌型的牌,让下一家顶上 outCards = ", outCards) + } else { + lastPos, _ := GetLastPos(pos, data) + if isEnd && len(winSnids) == 0 && nextPos != lastPos { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + logger.Logger.Trace("打到底模式 最后直接找最小牌型的牌, outCards = ", outCards) + } else { + if otherMinHandNum > 2 { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + logger.Logger.Trace("打到底模式 最后直接找最小牌型的牌, outCards = ", outCards) + } else { + outCards = []int32{cards[len(cards)-1]} + logger.Logger.Trace("最后直接出最大的牌 outCards = ", outCards) + } + } + + } + } + } else { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + logger.Logger.Trace("最后直接找最小牌型的牌 outCards = ", outCards) + } + } + logger.Logger.Trace("---------------------------出牌结束 出的牌:", outCards) + return outCards +} + +// 判断牌型是否全场最大 +func getMaxCardTypeNum(cardsType int, cards []int32, pos int32, data *tienlenApi.PredictRequest) bool { + //map[int][]int32 + status := true + otherCardsMap := getOtherCards(pos, data) + for _, otherCards := range otherCardsMap { + if len(otherCards) == 0 { + continue + } + sort.Slice(otherCards, func(i, j int) bool { + if Value(otherCards[i]) == Value(otherCards[j]) { + return otherCards[i] < otherCards[j] + } + return Value(otherCards[i]) < Value(otherCards[j]) + }) + if cardsType == Single { + if len(otherCards) > 0 && len(cards) > 0 { + if Value(otherCards[len(otherCards)-1]) >= Value(cards[0]) { + if Value(otherCards[len(otherCards)-1]) == Value(cards[0]) { + if Color(otherCards[len(otherCards)-1]) > Color(cards[0]) { + return false + } + } else { + return false + } + } + } + } else if cardsType == Twin { + _, pairMap := GetPair(otherCards) + if len(pairMap) > 0 { + for _, pair := range pairMap { + if Value(pair[len(pair)-1]) >= Value(cards[0]) { + if Value(pair[len(pair)-1]) == Value(cards[0]) { + if Color(pair[len(pair)-1]) > Color(cards[0]) { + return false + } + } else { + return false + } + } + } + } + } + otherCardsTypeMap := GetCardsType(otherCards, data.IsTienLenYule) //map[int]map[int32][]int32 + if otherCardsTypeMap[cardsType] != nil && len(otherCardsTypeMap[cardsType]) > 0 { + for _, cardsArr := range otherCardsTypeMap[cardsType] { + if len(cardsArr) < len(cards) { + continue + } + if Value(cardsArr[len(cardsArr)-1]) > Value(cards[len(cards)-1]) { + status = false + break + } else if Value(cardsArr[len(cardsArr)-1]) == Value(cards[len(cards)-1]) { + if Color(cardsArr[len(cardsArr)-1]) > Color(cards[len(cards)-1]) { + status = false + break + } + } + } + if !status { + break + } + } + } + return status +} + +// 出单张 +func getSingle(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + if isYuLe { + if len(cardsTypeMap[Triple]) == 0 { + minKey := findMinKey(cardsTypeMap[Single]) + outCards = cardsTypeMap[Single][minKey] + + } + } else { + minKey := findMinKey(cardsTypeMap[Single]) + outCards = cardsTypeMap[Single][minKey] + } + return outCards +} + +// 出对子 +func getTwin(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if len(cardsTypeMap[Twin]) > len(cardsTypeMap[Straight]) { + minKey := findMinKey(cardsTypeMap[Twin]) + if isYuLe { + if len(cardsTypeMap[Triple]) == 0 { + if Value(cardsTypeMap[Twin][minKey][0]) < 11 || (handNum <= 2 && len(cardsTypeMap[Triple]) == 0) { + outCards = cardsTypeMap[Twin][minKey] + } + } + } else { + if Value(cardsTypeMap[Twin][minKey][0]) < 11 || (handNum <= 2 && len(cardsTypeMap[Triple]) == 0) { + outCards = cardsTypeMap[Twin][minKey] + } + + } + + } + return outCards +} + +// 出三张 cardValue三张的值 value(card) +func getTriple(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool, otherMinHandNum int, mypos, otherPos int32, data *tienlenApi.PredictRequest) ([]int32, int) { + outCards := []int32{} + cardValue := 0 + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if isYuLe && len(cardsTypeMap[Straight_Triple]) > 0 { + //AI机器人三顺的长度只能是2 大于2的直接拆成连对炸弹了 + outCards = []int32{} + if len(cards) <= 10 && len(cardsTypeMap[Four_Bomb]) == 0 { + return cards, cardValue + } else { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + copyCards = DelCards(copyCards, cardsTypeMap[Straight_Triple]) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + sort.Slice(copyCards, func(i, j int) bool { + if Value(copyCards[i]) == Value(copyCards[j]) { + return copyCards[i] < copyCards[j] + } + return Value(copyCards[i]) < Value(copyCards[j]) + }) + num := len(cards) - 10 + if num == 1 { + //留最大牌 + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + maxCard := copyCards[len(copyCards)-3:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + //直接出三顺 + straightTripleMap := cardsTypeMap[Straight_Triple] + for _, straightTriple := range straightTripleMap { + outCards = straightTriple + break + } + } + } + logger.Logger.Trace("-----------出三顺--------------------", outCards) + return outCards, cardValue + + } + + if len(cardsTypeMap[Triple]) > 0 { + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + minTripleKey := findMinKey(cardsTypeMap[Triple]) + if len(cardsTypeMap[Triple]) == 2 && len(cards) <= 9 { + //先出大的三条 + minTripleKey = findMaxKey(cardsTypeMap[Triple]) + } + if len(cardsTypeMap[Triple]) >= 2 && len(cards) <= 8 && isYuLe { + //判断单张数量 + if len(cardsTypeMap[Single]) < 2 { + //判断是不是三顺 + var arr []int32 + for _, three := range cardsTypeMap[Triple] { + for _, value := range three { + arr = append(arr, value) + } + } + StraightTripleMap := GetStraightTriple(arr) + if len(StraightTripleMap) > 0 { + //娱乐场 小于11张牌可以一首扔 + return cards, cardValue + } + } + } + if isYuLe { + //出三代2 或三带一小对 单张小于12 三顺小于等于7 + otherCard := getOtherPlayerCards(mypos, otherPos, data) + if len(cardsTypeMap[Single]) >= 2 && (Value(cardsTypeMap[Single][minSingleKey][0]) < 12 || handNum <= 2 || (otherMinHandNum == 1 && len(otherCard) == 1 && len(cardsTypeMap[Single]) >= 3)) { + //三代2单 + if Value(cardsTypeMap[Triple][minTripleKey][0]) <= 9 || handNum <= 2 || (otherMinHandNum == 1 && len(otherCard) == 1 && len(cardsTypeMap[Single]) >= 3) { + outCards = cardsTypeMap[Triple][minTripleKey] + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + if len(minValue) < 2 { + logger.Logger.Tracef("找出最小两张单排的值,没找出来!!!!!!!!!!!!!!!!!!!!!!!!!minValue = %v,cardsTypeMap[Single]= %v", minValue, cardsTypeMap[Single]) + } else { + outCards = append(outCards, minValue[0], minValue[1]) + } + + } + } else if len(cardsTypeMap[Twin]) >= 1 && (Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 || handNum <= 3) { + //三代一对 + outCards = cardsTypeMap[Triple][minTripleKey] + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + } else { + if len(cards) > 5 { + outCards = cardsTypeMap[Triple][minTripleKey] + if len(cardsTypeMap[Single]) > 0 { + for _, single := range cardsTypeMap[Single] { + outCards = append(outCards, single[0]) + } + } + + if len(outCards) < 5 { + //找顺子交集 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + } + for _, cardArr := range result { + for _, card := range cardArr { + outCards = append(outCards, card) + if len(outCards) == 5 { + break + } + } + if len(outCards) == 5 { + break + } + } + //找顺子 + if len(outCards) < 5 { + if len(cardsTypeMap[Straight]) > 0 { + for _, straights := range cardsTypeMap[Straight] { + //剩余俩个三张 一共8张牌 + if len(straights)-(5-len(outCards)) >= 3 || (len(cards) == 10 && len(cardsTypeMap[Triple]) == 2) { + for i := 0; i < 5-len(outCards); i++ { + outCards = append(outCards, straights[i]) + if len(outCards) == 5 { + break + } + } + if len(outCards) == 5 { + break + } + } + } + } + } + } + if len(outCards) != 5 { + //直接出三张 + outCards = cardsTypeMap[Triple][minTripleKey] + } + } else { + //直接出三张 + outCards = cardsTypeMap[Triple][minTripleKey] + } + } + //出三张拦截 下面三手牌会判断牌型出 这里直接不让出三个2 + if Value(cardsTypeMap[Triple][minTripleKey][0]) == 12 && handNum <= 3 && handNum != 1 && len(cards) >= 10 { + logger.Logger.Trace("出三张 拦截 三个2 !!!!!!!!!!!!!!!") + outCards = []int32{} + return outCards, 0 + } + } else { + outCards = cardsTypeMap[Triple][minTripleKey] + if isYuLe { + if len(cards) <= 5 { + //最后4张牌 出三带1 + outCards = cards + } + } + } + cardValue = Value(cardsTypeMap[Triple][minTripleKey][0]) + } + return outCards, cardValue +} + +// 出顺子 +func getStraight(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if len(cardsTypeMap[Straight]) >= 1 { + //出顺子 + //如果只剩两手牌 先出最长的顺子 + if handNum <= 2 { + //找长度最大的顺子 + maxLength := 0 + maxKey := int32(0) + for i, straight := range cardsTypeMap[Straight] { + if len(straight) > maxLength { + maxKey = i + } + } + outCards = cardsTypeMap[Straight][maxKey] + } else { + //找小顺子 + outCards = cardsTypeMap[Straight][0] + } + } + return outCards +} + +// 出连对 +func getStraightTwin(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + if len(cardsTypeMap[Straight_Twin]) > 0 { + //找长度最小的连对 + length := 0 + key := int32(0) + for i, straight := range cardsTypeMap[Straight_Twin] { + if len(straight) < length && len(straight) > 0 { + key = i + } + } + outCards = cardsTypeMap[Straight_Twin][key] + } + return outCards +} + +// 出炸弹 +func getBomb(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + //炸弹 + if len(cardsTypeMap[Four_Bomb]) > 0 { + //出炸弹 + minTripleKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minTripleKey] + } + return outCards +} + +// ---------------------------------------------// + +// 找手里最小的牌的组合 +func GetMinCardType(minCard int32, cardsType map[int]map[int32][]int32, isYuLe bool) []int32 { + for cardType, cards := range cardsType { + for _, cardArr := range cards { + for i, card := range cardArr { + if Value(card) == Value(minCard) { + if card == minCard { + if cardType == Triple && isYuLe { + //找两张单张 + minSingleKey := findMinKey(cardsType[Single]) + minTwinKey := findMinKey(cardsType[Twin]) + if len(cardsType[Single]) >= 2 && Value(cardsType[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsType[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsType[Single]) + cardArr = append(cardArr, minValue[0], minValue[1]) + } + } else if len(cardsType[Twin]) >= 1 && Value(cardsType[Twin][minTwinKey][0]) < 11 { + //三代一对 + cardArr = append(cardArr, cardsType[Twin][minTwinKey][0], cardsType[Twin][minTwinKey][1]) + } + } + return cardArr + } else { + //替换牌 + cardArr[i] = minCard + if cardType == Triple && isYuLe { + //找两张单张 变成三代二 + minSingleKey := findMinKey(cardsType[Single]) + minTwinKey := findMinKey(cardsType[Twin]) + if len(cardsType[Single]) >= 2 && Value(cardsType[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsType[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsType[Single]) + cardArr = append(cardArr, minValue[0], minValue[1]) + } + } else if len(cardsType[Twin]) >= 1 && Value(cardsType[Twin][minTwinKey][0]) < 11 { + //三代一对 + cardArr = append(cardArr, cardsType[Twin][minTwinKey][0], cardsType[Twin][minTwinKey][1]) + } + } + return cardArr + } + + } + } + } + } + //直接返回单张 + return []int32{minCard} +} + +// 获取下一家的位置和剩余的牌 +func GetNextPos(pos int32, data *tienlenApi.PredictRequest) (int32, []int32) { + for i := 1; i < 4; i++ { + nextPos := pos + int32(i) + if nextPos > 3 { + nextPos -= 4 + } + //判断这个位置有没有剩余牌 + if nextPos == 0 && len(data.Cards_left_0) > 0 { + return nextPos, data.Cards_left_0 + } else if nextPos == 1 && len(data.Cards_left_1) > 0 { + return nextPos, data.Cards_left_1 + } else if nextPos == 2 && len(data.Cards_left_2) > 0 { + return nextPos, data.Cards_left_2 + } else if nextPos == 3 && len(data.Cards_left_3) > 0 { + return nextPos, data.Cards_left_3 + } + } + return -1, nil +} + +// 获取上家的位置和剩余的牌 +func GetLastPos(pos int32, data *tienlenApi.PredictRequest) (int32, []int32) { + for i := 1; i < 4; i++ { + nextPos := (pos - int32(i) + 4) % 4 + //判断这个位置有没有剩余牌 + if nextPos == 0 && len(data.Cards_left_0) > 0 { + return nextPos, data.Cards_left_0 + } else if nextPos == 1 && len(data.Cards_left_1) > 0 { + return nextPos, data.Cards_left_1 + } else if nextPos == 2 && len(data.Cards_left_2) > 0 { + return nextPos, data.Cards_left_2 + } else if nextPos == 3 && len(data.Cards_left_3) > 0 { + return nextPos, data.Cards_left_3 + } + } + return -1, nil +} + +// 获取指定玩家的牌 +func getOtherPlayerCards(mypos, otherPos int32, data *tienlenApi.PredictRequest) []int32 { + otherCardsMap := getOtherCards(mypos, data) + sort.Slice(otherCardsMap[int(otherPos)], func(i, j int) bool { + if otherCardsMap[int(otherPos)][i] == otherCardsMap[int(otherPos)][j] { + return otherCardsMap[int(otherPos)][i] < otherCardsMap[int(otherPos)][j] + } + return Value(otherCardsMap[int(otherPos)][i]) < Value(otherCardsMap[int(otherPos)][j]) + }) + return otherCardsMap[int(otherPos)] +} + +// 判断自己手里有没有最大的牌 +func getMaxCards(myCards []int32, data *tienlenApi.PredictRequest, mypos int32) (bool, int, []int32) { + sort.Slice(myCards, func(i, j int) bool { + if Value(myCards[i]) == Value(myCards[j]) { + return myCards[i] < myCards[j] + } + return Value(myCards[i]) < Value(myCards[j]) + }) + //getOtherCards() + otherCardsMap := getOtherCards(mypos, data) + var otherCards []int32 + for _, otherCard := range otherCardsMap { + otherCards = append(otherCards, otherCard...) + } + + sort.Slice(otherCards, func(i, j int) bool { + if Value(otherCards[i]) == Value(otherCards[j]) { + return otherCards[i] < otherCards[j] + } + return Value(otherCards[i]) < Value(otherCards[j]) + }) + maxCardNum := 0 + status := false + maxCardsArr := []int32{} + for _, mycard := range myCards { + if Value(mycard) >= Value(otherCards[len(otherCards)-1]) { + if Value(mycard) == Value(otherCards[len(otherCards)-1]) { + if Color(mycard) < Color(otherCards[len(otherCards)-1]) { + continue + } + } + maxCardNum += 1 + status = true + maxCardsArr = append(maxCardsArr, mycard) + } + } + //排序返回 + sort.Slice(maxCardsArr, func(i, j int) bool { + if Value(maxCardsArr[i]) == Value(maxCardsArr[j]) { + return maxCardsArr[i] < maxCardsArr[j] + } + return Value(maxCardsArr[i]) < Value(maxCardsArr[j]) + }) + return status, maxCardNum, maxCardsArr +} +func findMinKey(data map[int32][]int32) int32 { + minValue := int32(math.MaxInt32) + minKey := int32(-1) + for i, value := range data { + if int32(Value(value[0])) < minValue { + minValue = int32(Value(value[0])) + minKey = i + } + } + return minKey +} +func findMaxKey(data map[int32][]int32) int32 { + maxKey := int32(-1) + + for key := range data { + if key > maxKey { + maxKey = key + } + } + + return maxKey +} + +// 获取其他玩家的牌 +func getOtherCards(myPos int32, data *tienlenApi.PredictRequest) map[int][]int32 { + otherCards := make(map[int][]int32) + for i := 0; i < 4; i++ { + if i == int(myPos) { + continue + } + if i == 0 { + otherCards[i] = data.Cards_left_0 + } else if i == 1 { + otherCards[i] = data.Cards_left_1 + } else if i == 2 { + otherCards[i] = data.Cards_left_2 + } else if i == 3 { + otherCards[i] = data.Cards_left_3 + } + } + return otherCards +} + +// 获取其他玩家最小剩余手数 和剩余牌数及位置剩余的牌型 +func getOtherMinCards(myPos int32, data *tienlenApi.PredictRequest, isYuLe bool) (int, int, int, int) { + otherCards := getOtherCards(myPos, data) + otherMinHandNum := 100 + otherMinCardsNum := 13 + otherPos := -1 + cardsType := -1 + maxCards := []int32{} + for i, other := range otherCards { + //获取牌型 + if len(other) > 0 { + cardsTypeMap := GetCardsType(other, isYuLe) + //剩余手数 + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, other) + if handNum <= otherMinHandNum && len(other) <= otherMinCardsNum { + if handNum == otherMinHandNum && len(other) <= otherMinHandNum { + _, ruleType := RulePopEnable(other) + if ruleType == cardsType { + //相同牌型找最大的牌 + if Value(other[len(other)-1]) < Value(maxCards[len(maxCards)-1]) { + continue + } + } + } + otherMinHandNum = handNum + otherMinCardsNum = len(other) + otherPos = i + maxCards = other + if handNum <= 1 { + isRule, ruleType := RulePopEnable(other) + if isRule { + cardsType = ruleType + } else { + //三张 三带一 三带二 + cardsType = Triple + } + + } + } + } + } + return otherMinHandNum, otherMinCardsNum, otherPos, cardsType +} + +// 找到最小的两个值 +func FindSmallestTwoValues(m map[int32][]int32) []int32 { + // 创建一个切片存储map的键值对 + pairs := make([][2]int32, 0) + + // 将map的键值对存储在切片中 + for key := range m { + pairs = append(pairs, [2]int32{key, key}) + } + + // 对切片按照键值进行排序 + sort.Slice(pairs, func(i, j int) bool { + return pairs[i][0] < pairs[j][0] + }) + + // 取出最小的两个值 + var smallestTwo []int32 + if len(pairs) >= 2 { + smallestTwo = append(smallestTwo, m[pairs[0][0]]...) + if len(m[pairs[1][0]]) > 0 { + smallestTwo = append(smallestTwo, m[pairs[1][0]]...) + } + } else if len(pairs) == 1 { + smallestTwo = m[pairs[0][0]] + } + return smallestTwo +} + +// 找交集 +func FindIntersection(arrays map[int32][]int32) [][]int32 { + var result [][]int32 + + // 获取 map 中的键,并按顺序进行迭代 + keys := make([]int32, 0, len(arrays)) + for key := range arrays { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + + // 检查相邻的数组之间的交集 + for i := 0; i < len(keys)-1; i++ { + currentKey := keys[i] + nextKey := keys[i+1] + + currentArray := arrays[currentKey] + nextArray := arrays[nextKey] + + intersection := GetArrayIntersection(currentArray, nextArray) + if intersection != nil { + result = append(result, intersection) + } + } + + return result +} + +// 获取两个切片的交集 +func GetArrayIntersection(arr1, arr2 []int32) []int32 { + // 创建一个 map 用于存储第一个切片中的元素 + elements := make(map[int32]bool) + for _, num := range arr1 { + elements[num] = true + } + + // 检查第二个切片中的元素是否在第一个切片中出现,如果是,则添加到结果中 + var result []int32 + for _, num := range arr2 { + if elements[num] { + result = append(result, num) + } + } + + return result +} + +// 是否压牌 +func IsPressed(isYuLe, isEnd bool, otherCards, lastCards []int32, data *tienlenApi.PredictRequest, pos, lastPos, nextPos int32, handCardnum, cardsType int) bool { + if (!isYuLe && isEnd) && len(otherCards) == 0 { + lastNextPos, _ := GetNextPos(lastPos, data) + if lastNextPos == pos && nextPos != lastPos { + if getMaxCardTypeNum(cardsType, lastCards, pos, data) && handCardnum > 2 { + return false + } + if len(lastCards) > 0 { + if Value(lastCards[0]) == 2 && handCardnum > 2 { + return false + } + } + } + + } + + return true +} + +// 重组牌型 +func reloadcardsType(cardsTypeMap map[int]map[int32][]int32, pos int32, data *tienlenApi.PredictRequest, isYuLe bool) map[int]map[int32][]int32 { + //其他玩家只剩一张牌了 + otherMinHandNum, otherMinCardsNum, otherPos, otherCardsType := getOtherMinCards(pos, data, isYuLe) + //otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + + //剩余两手 全是单牌 + status := false + allOtherCards := getOtherCards(pos, data) + for _, otherCards := range allOtherCards { + if len(otherCards) == 2 { + otherType := GetCardsType(otherCards, isYuLe) + //剩余手数 + _, handNum := GetCardTypeScore(otherType, isYuLe, otherCards) + if handNum == 2 { + otherMaxCard := otherCards[0] + if Value(otherCards[0]) < Value(otherCards[1]) { + otherMaxCard = otherCards[1] + } + if getMaxCardTypeNum(Single, []int32{otherMaxCard}, pos, data) { + logger.Logger.Trace("玩家剩余2张牌且有一张全场最大的牌,重组牌型!otherCards = ", otherCards) + status = true + } + } + } + } + + if (otherMinHandNum == 1 && otherMinCardsNum <= 1 && otherCardsType == Single) || status { + //lastPos, _ := GetLastPos(pos, data) + //nextPos, _ := GetNextPos(pos, data) + //只考虑剩2人的情况下 + //if nextPos == int32(otherPos) { + //判断我手里的单牌 组对子 + if len(cardsTypeMap[Single]) >= 2 { + //判断如果单牌有2张以上小于其他玩家的牌,开始组对子 + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + minCards := map[int32]int32{} + for i, card := range cardsTypeMap[Single] { + if Value(card[0]) <= Value(otherCards[0]) { + //判断花色 + if Value(card[0]) == Value(otherCards[0]) { + if Color(card[0]) < Color(otherCards[0]) { + //minCards = append(minCards, card[0]) + minCards[i] = card[0] + continue + } + } else { + //minCards = append(minCards, card[0]) + minCards[i] = card[0] + } + } + } + //有超过两张的单牌小于其他玩家的牌,开始组对子 + if len(minCards) >= 2 { + //开始拆顺子 组对子 + if len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for delKey, minCard := range minCards { + for straightKey, straight := range straightMap { + if len(straight) <= 3 { + continue + } + //先找最小的牌 + if Value(straight[0]) == Value(minCard) { + if cardsTypeMap[Twin] == nil { + cardsTypeMap[Twin] = map[int32][]int32{} + } + cardsTypeMap[Twin][int32(Value(minCard))] = append(cardsTypeMap[Twin][int32(Value(minCard))], minCard, straight[0]) + + cardsTypeMap[Straight][straightKey] = cardsTypeMap[Straight][straightKey][1:] + //删除单张 + delete(cardsTypeMap[Single], delKey) + break + } else if Value(straight[len(straight)-1]) == Value(minCard) { + if cardsTypeMap[Twin] == nil { + cardsTypeMap[Twin] = map[int32][]int32{} + } + cardsTypeMap[Twin][int32(Value(minCard))] = append(cardsTypeMap[Twin][int32(Value(minCard))], minCard, straight[len(straight)-1]) + cardsTypeMap[Straight][straightKey] = cardsTypeMap[Straight][straightKey][:len(straight)-1] + //删除单张 + delete(cardsTypeMap[Single], delKey) + break + } else { + //找中间 暂时不做 情况太复杂了 需要考虑的太多 + } + } + } + } + } + } + //} + if len(cardsTypeMap[Single]) == 0 { + delete(cardsTypeMap, Single) + } + if len(cardsTypeMap[Straight]) == 0 { + delete(cardsTypeMap, Straight) + } + } + return cardsTypeMap +} + +// 获取所有玩家剩余的牌型 +func GetAllOtherCardsType(data *tienlenApi.PredictRequest, pos int32, isYuLe bool) map[int]map[int]map[int32][]int32 { + allOtherCardsType := map[int]map[int]map[int32][]int32{} + for i := 1; i < 4; i++ { + if i == int(pos) { + continue + } + //判断这个位置有没有剩余牌 + if i == 0 && len(data.Cards_left_0) > 0 { + cardsTypeMap0 := GetCardsType(data.Cards_left_0, isYuLe) + allOtherCardsType[i] = cardsTypeMap0 + } else if i == 1 && len(data.Cards_left_1) > 0 { + cardsTypeMap1 := GetCardsType(data.Cards_left_1, isYuLe) + allOtherCardsType[i] = cardsTypeMap1 + } else if i == 2 && len(data.Cards_left_2) > 0 { + cardsTypeMap2 := GetCardsType(data.Cards_left_2, isYuLe) + allOtherCardsType[i] = cardsTypeMap2 + } else if i == 3 && len(data.Cards_left_3) > 0 { + cardsTypeMap3 := GetCardsType(data.Cards_left_3, isYuLe) + allOtherCardsType[i] = cardsTypeMap3 + } + } + return allOtherCardsType +} + +// 判断能不能出顺子 +func IsOutStraight(data *tienlenApi.PredictRequest, pos int32, isYuLe bool, mycards []int32) (bool, int) { + allOtherCardsType := GetAllOtherCardsType(data, pos, isYuLe) + copyMap := make(map[int]map[int]map[int32][]int32) + for k1, v1 := range allOtherCardsType { + copyMap[k1] = make(map[int]map[int32][]int32) + for k2, v2 := range v1 { + copyMap[k1][k2] = make(map[int32][]int32) + for k3, v3 := range v2 { + copyMap[k1][k2][k3] = make([]int32, len(v3)) + copy(copyMap[k1][k2][k3], v3) + } + } + } + + for i, OtherCardsType := range copyMap { + if len(OtherCardsType[Straight]) > 0 { + for _, otherStraight := range OtherCardsType[Straight] { + if len(otherStraight) >= len(mycards) { + if len(otherStraight)-len(mycards) <= 2 { + //说明要拆牌多出来2张,我可以出 + } + if Value(otherStraight[len(otherStraight)-1]) <= Value(mycards[len(mycards)-1]) { + //判断花色 + if Value(otherStraight[len(otherStraight)-1]) == Value(mycards[len(mycards)-1]) { + if Color(otherStraight[len(otherStraight)-1]) < Color(mycards[len(mycards)-1]) { + continue + } + } else { + continue + } + } + } + } + + //判断剩余的牌是不是全是顺子 + delete(OtherCardsType, Straight) + if len(OtherCardsType) <= 1 { + logger.Logger.Trace("全是顺子啊,还比我的大!!!!不能打顺子!") + return false, i + } + } + } + return true, -1 +} diff --git a/gamerule/tienlen/card_test.go b/gamerule/tienlen/card_test.go new file mode 100644 index 0000000..6ecf344 --- /dev/null +++ b/gamerule/tienlen/card_test.go @@ -0,0 +1,3366 @@ +package tienlen + +/* +import ( + "encoding/json" + "fmt" + "github.com/tealeg-new/xlsx" + "math" + "math/rand" + "sort" + "strconv" + "testing" +) + +func TestCardsKu(t *testing.T) { + fmt.Printf("生成牌库!!!!\n") + file := xlsx.NewFile() + sheet, err := file.AddSheet("Sheet1") + if err != nil { + fmt.Printf("Error creating sheet: %s\n", err) + return + } + // 表头数据 + header := []string{ + "Id", + "Card1", + "Card1Score", + "Card1HandNum", + "Change1Cards", + "Card2", + "Card2Score", + "Card2HandNum", + "Change2Cards", + "Card3", + "Card3Score", + "Card3HandNum", + "Change3Cards", + "Card4", + "Card4Score", + "Card4HandNum", + "Change4Cards", + } + // 创建表头行 + headerRow := sheet.AddRow() + for _, column := range header { + cell := headerRow.AddCell() + cell.Value = column + } + allCards := map[int32][][]int32{} + for { + slice := []int32{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39} + // 使用 Fisher-Yates 算法对切片进行乱序处理 + for i := len(slice) - 1; i > 0; i-- { + j := rand.Intn(i + 1) + slice[i], slice[j] = slice[j], slice[i] + } + + // 确保切片长度为 52,以便均分为 4 份 + if len(slice) != 52 { + fmt.Println("切片长度不正确") + return + } + + // 将切片分成 4 份,每份 13 张牌 + partSize := 13 + parts := make([][]int32, 4) + for i := 0; i < 4; i++ { + startIndex := i * partSize + endIndex := startIndex + partSize + parts[i] = slice[startIndex:endIndex] + } + isYuLe := true + // 打印分割后的每份牌 + for i, part := range parts { + //获取手数 和分数 + cardsTypeMap := GetCardsType(part, isYuLe) + score, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, part) + num := 3000 + handSum := 6 + if isYuLe { + num = 4000 + handSum = 4 + } + if score < num || handNum > handSum || score == 9999 { + continue + } + + fmt.Printf("生成第%d份牌,分数:%d,手数:%d\n", i+1, score, handNum) + //写如到xlsx文件中 + cards1TypeMap := GetCardsType(parts[0], true) + score1, handNum1 := GetCardTypeScore(cards1TypeMap, true, part) + cards2TypeMap := GetCardsType(parts[1], true) + score2, handNum2 := GetCardTypeScore(cards2TypeMap, true, part) + cards3TypeMap := GetCardsType(parts[2], true) + score3, handNum3 := GetCardTypeScore(cards3TypeMap, true, part) + cards4TypeMap := GetCardsType(parts[3], true) + score4, handNum4 := GetCardTypeScore(cards4TypeMap, true, part) + json1Data, _ := json.Marshal(parts[0]) + json2Data, _ := json.Marshal(parts[1]) + json3Data, _ := json.Marshal(parts[2]) + json4Data, _ := json.Marshal(parts[3]) + change1, _ := json.Marshal(CardsChange(parts[0])) + change2, _ := json.Marshal(CardsChange(parts[1])) + change3, _ := json.Marshal(CardsChange(parts[2])) + change4, _ := json.Marshal(CardsChange(parts[3])) + if score1 == 9999 || score2 == 9999 || score3 == 9999 || score4 == 9999 { + break + } + scoreNum := 1000 + if isYuLe { + scoreNum = 2000 + } + if !areSlicesEqual(part, parts[0]) && score-score1 < scoreNum { + break + } + if !areSlicesEqual(part, parts[1]) && score-score2 < scoreNum { + break + } + if !areSlicesEqual(part, parts[2]) && score-score3 < scoreNum { + break + } + if !areSlicesEqual(part, parts[3]) && score-score4 < scoreNum { + break + } + if !areSlicesEqual(part, parts[0]) && handNum1 < 5 { + break + } + if !areSlicesEqual(part, parts[0]) && handNum2 < 5 { + break + } + if !areSlicesEqual(part, parts[0]) && handNum3 < 5 { + break + } + if !areSlicesEqual(part, parts[0]) && handNum4 < 5 { + break + } + //判断炸弹 + if !areSlicesEqual(part, parts[0]) && len(cards1TypeMap[Four_Bomb]) > 0 { + break + } + if !areSlicesEqual(part, parts[0]) && len(cards2TypeMap[Four_Bomb]) > 0 { + break + } + if !areSlicesEqual(part, parts[0]) && len(cards3TypeMap[Four_Bomb]) > 0 { + break + } + if !areSlicesEqual(part, parts[0]) && len(cards4TypeMap[Four_Bomb]) > 0 { + break + } + //判断连对 + if !areSlicesEqual(part, parts[0]) && len(cards1TypeMap[Straight_Twin]) > 0 { + status := false + for _, straight := range cards1TypeMap[Straight_Twin] { + if len(straight)/2 >= 3 { + status = true + break + } + } + if status { + break + } + } + if !areSlicesEqual(part, parts[0]) && len(cards2TypeMap[Straight_Twin]) > 0 { + status := false + for _, straight := range cards2TypeMap[Straight_Twin] { + if len(straight)/2 >= 3 { + status = true + break + } + } + if status { + break + } + } + if !areSlicesEqual(part, parts[0]) && len(cards3TypeMap[Straight_Twin]) > 0 { + status := false + for _, straight := range cards3TypeMap[Straight_Twin] { + if len(straight)/2 >= 3 { + status = true + break + } + } + if status { + break + } + } + if !areSlicesEqual(part, parts[0]) && len(cards4TypeMap[Straight_Twin]) > 0 { + status := false + for _, straight := range cards4TypeMap[Straight_Twin] { + if len(straight)/2 >= 3 { + status = true + break + } + } + if status { + break + } + } + //机器人打一局 看看谁赢了 + winId := RobotPlay(parts, isYuLe, true, true) + if winId != i { + fmt.Printf("这手牌没赢!!!winId = %d,i = %d\n", winId, i) + break + } + //找最小的牌 先手 + + allCards[int32(len(allCards)+1)] = parts + data := []map[string]string{ + { + "Id": strconv.Itoa(len(allCards)), + "Card1": string(json1Data), + "Card1Score": strconv.Itoa(score1), + "Card1HandNum": strconv.Itoa(handNum1), + "Change1Cards": string(change1), + "Card2": string(json2Data), + "Card2Score": strconv.Itoa(score2), + "Card2HandNum": strconv.Itoa(handNum2), + "Change2Cards": string(change2), + "Card3": string(json3Data), + "Card3Score": strconv.Itoa(score3), + "Card3HandNum": strconv.Itoa(handNum3), + "Change3Cards": string(change3), + "Card4": string(json4Data), + "Card4Score": strconv.Itoa(score4), + "Card4HandNum": strconv.Itoa(handNum4), + "Change4Cards": string(change4), + }, + } + + // 写入数据 + for _, rowData := range data { + row := sheet.AddRow() + for _, column := range header { + cell := row.AddCell() + value := rowData[column] // 直接访问映射中的值 + cell.Value = value + } + } + break + } + if len(allCards) >= 10000 { + break + } + fmt.Printf("当前生成的牌库条数:%d 条\n", len(allCards)) + } + // 保存文件 + err = file.Save("output.xlsx") + if err != nil { + fmt.Printf("Error saving file: %s\n", err) + return + } + fmt.Print("File saved successfully.\n") + fmt.Print("生成牌库完毕!!!!!\n") +} +func areSlicesEqual(slice1, slice2 []int32) bool { + if len(slice1) != len(slice2) { + return false + } + + for i := 0; i < len(slice1); i++ { + if slice1[i] != slice2[i] { + return false + } + } + + return true +} + +// 机器人打牌 +func RobotPlay(cards [][]int32, isYuLe, isFirstHand, isOut bool) int { + //4个机器人打牌 + winId := 0 + data := &PredictRequest{ + IsWin: true, + IsEnd: false, + IsTienLenYule: isYuLe, + IsFirstHand: isFirstHand, + Cards_left_0: cards[0], + Cards_left_1: cards[1], + Cards_left_2: cards[2], + Cards_left_3: cards[3], + } + + //手牌ID + oneId := -1 + for id, card := range cards { + for _, value := range card { + if value == 0 { + oneId = id + break + } + } + if oneId != -1 { + break + } + } + + fmt.Printf("出手牌的玩家ID = %d\n", oneId) + lastCards := []int32{} + count := 0 + for pos := 0; pos < 4; pos++ { + if data.IsFirstHand { + if pos != oneId { + continue + } + } + card := cards[pos] + //出首牌 + + if isOut { + outCards := GetOutCards(card, data, int32(pos)) + fmt.Printf("出手牌 outCards= %v\n", outCards) + data.IsFirstHand = false + lastCards = outCards + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = outCards + card = DelCards(card, delMap) + cards[pos] = card + + if pos == 0 { + data.Cards_left_0 = card + } else if pos == 1 { + data.Cards_left_1 = card + } else if pos == 2 { + data.Cards_left_2 = card + } else if pos == 3 { + data.Cards_left_3 = card + } + + if len(card) == 0 { + winId = pos + fmt.Printf("最后一手牌 outCards= %v\n", outCards) + return winId + } + isOut = false + } else { + //压牌 + outCards := GetPressCards(card, lastCards, data, int32(pos)) + fmt.Printf("压牌 outCards= %v\n", outCards) + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = outCards + card = DelCards(card, delMap) + cards[pos] = card + if len(outCards) > 0 { + lastCards = outCards + count = 0 + if pos == 0 { + data.Cards_left_0 = card + } else if pos == 1 { + data.Cards_left_1 = card + } else if pos == 2 { + data.Cards_left_2 = card + } else if pos == 3 { + data.Cards_left_3 = card + } + + } else { + count += 1 + } + if len(card) == 0 { + winId = pos + fmt.Printf("压牌出完了 outCards= %v\n", outCards) + return winId + } + } + if count == 3 { + isOut = true + count = 0 + } + if pos == 3 { + pos = -1 + } + } + return winId +} + +// 新tienlenAI 获取牌型 +func GetCardsType(myCards []int32, isYuLe bool) map[int]map[int32][]int32 { + cards := make([]int32, len(myCards)) + copy(cards, myCards) + cardsTypeMap := make(map[int]map[int32][]int32) + //先找炸弹 + bombMap := GetBomb(cards) + if len(bombMap) > 0 { + cardsTypeMap[Four_Bomb] = bombMap + } + cards = DelCards(cards, bombMap) + //找连对(5连对>4连对>3连队>2连队) + pair, pairMap := GetPair(cards) + //再找连对 + pairStraightMap := GetConsecutivePairs(cards, pair) + if len(pairStraightMap) > 0 { + cardsTypeMap[Straight_Twin] = pairStraightMap + } + for i, pairStraight := range pairStraightMap { + if len(pairStraight)/2 <= 2 { + //先删除2连对 去找顺子和三条 + delete(cardsTypeMap[Straight_Twin], i) + } + } + //先排除2连对 + //排除3连对以上的连对 + for _, pairStraight := range pairStraightMap { + if isYuLe { + //娱乐模式 如果3连对以上,则删除3连对以上的连对 + if len(pairStraight)/2 >= 3 { + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = pairStraight + cards = DelCards(cards, delMap) + } + } else { + delMap := make(map[int32][]int32) + delMap[int32(len(delMap)+1)] = pairStraight + cards = DelCards(cards, delMap) + } + } + //娱乐模式先找三张 + //连队和三条有冲突 既能组成连对 也是三条 这个时候要判断是不是娱乐模式 娱乐模式保留三条拆连对,不是娱乐模式保留连对 拆三条 + if isYuLe { + threeMap := GetThreeOfkind(cards) + if len(threeMap) > 0 { + cardsTypeMap[Triple] = threeMap + } + //娱乐模式三张不拆 删除掉三张的牌找顺子 + cards = DelCards(cards, threeMap) + //判断获取到的三条在不在连对中 + for _, three := range threeMap { + for i, pairStraight := range pairStraightMap { + if len(pairStraight)/2 >= 3 { + continue + } + for _, data := range pairStraight { + if Value(data) == Value(three[0]) { + //删除连对 + delete(cardsTypeMap[Straight_Twin], i) + break + } + } + + } + } + cards = DelCards(cards, threeMap) + } + //找顺子(同花顺>杂色顺子) + straightMap := findStraights(cards) + if len(straightMap) > 0 { + cardsTypeMap[Straight] = straightMap + cards = DelCards(cards, straightMap) + } + //找三张 + if !isYuLe { + threeMap := GetThreeOfkind(cards) + if len(threeMap) > 0 { + cardsTypeMap[Triple] = threeMap + } + //不是娱乐模式删除三条 + for i, three := range threeMap { + for _, pairStraight := range pairStraightMap { + if len(pairStraight)/2 >= 3 { + continue + } + for _, data := range pairStraight { + if Value(data) == Value(three[0]) { + //删除连对 + delete(cardsTypeMap[Triple], i) + break + } + } + + } + } + cards = DelCards(cards, threeMap) + } + + //找三顺 + var arr []int32 + for _, three := range cardsTypeMap[Triple] { + for _, value := range three { + arr = append(arr, value) + } + } + StraightTripleMap := GetStraightTriple(arr) + if len(StraightTripleMap) > 0 { + cardsTypeMap[Straight_Triple] = StraightTripleMap + } + //剩余的牌再找对子 + pair, pairMap = GetPair(cards) + //再找连队 + pairStraightMap = GetConsecutivePairs(cards, pair) + for _, pairStraight := range pairStraightMap { + cardsTypeMap[Straight_Twin][int32(len(cardsTypeMap[Straight_Twin])+1)] = pairStraight + + } + cards = DelCards(cards, pairStraightMap) + pair, pairMap = GetPair(cards) + if len(pairMap) > 0 { + cardsTypeMap[Twin] = pairMap + } + //找单张 移除上面所有牌型的牌,剩余的牌就是单张 + cards = DelCards(cards, pairMap) + if len(cards) > 0 { + cardsTypeMap[Single] = make(map[int32][]int32) + for i := 0; i < len(cards); i++ { + cardsTypeMap[Single][int32(i)] = []int32{cards[i]} + } + } + return cardsTypeMap +} + +// 找炸弹 +func GetBomb(cards []int32) map[int32][]int32 { + cardCount := make(map[int32]int) + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + + } + + // 查找炸弹组合 + bombMap := make(map[int32][]int32) + for card, count := range cardCount { + if count == 4 { + bombMap[card] = cardMap[card] + } + } + return bombMap +} + +// 找连对 +func GetConsecutivePairs(cards, pair []int32) map[int32][]int32 { + // 对切片进行排序 + sort.Slice(pair, func(i, j int) bool { + return pair[i] < pair[j] + }) + + // 存储顺子的 map + straightMap := make(map[int32][]int32) + for i := 0; i < len(pair); i++ { + // 如果当前数字与前一个数字相同,则跳过 + if i > 0 && pair[i] == pair[i-1] { + continue + } + + // 初始值为当前数字 + current := pair[i] + straight := []int32{current} + + // 检查是否连续递增 + num := i + for j := i + 1; j < len(pair); j++ { + if pair[j] == current+1 && pair[j] < 12 { + straight = append(straight, pair[j]) + current = pair[j] + num = j + } + } + i = num + if len(straight) >= 2 { + straightMap[int32(len(straightMap)+1)] = straight + } + } + //找原牌 + cardsMap := make(map[int32][]int32) + for i, v := range straightMap { + for _, v1 := range v { + num := 0 + for _, v2 := range cards { + if v1 == int32(Value(v2)) { + num += 1 + cardsMap[i] = append(cardsMap[i], v2) + if num == 2 { + break + } + } + } + } + } + return cardsMap + +} + +// 找顺子 +func GetAllStraight(cards []int32, length int) map[int32][]int32 { + // 对切片进行排序 + sort.Slice(cards, func(i, j int) bool { + return cards[i] < cards[j] + }) + + // 存储顺子的 map + straightMap := make(map[int32][]int32) + for i := 0; i < len(cards); i++ { + // 如果当前数字与前一个数字相同,则跳过 + if i > 0 && Value(cards[i]) == Value(cards[i-1]) { + continue + } + + // 初始值为当前数字 + current := cards[i] + straight := []int32{current} + + // 检查是否连续递增 + for j := i + 1; j < len(cards); j++ { + if Value(cards[j]) == Value(current)+1 && Value(cards[j]) < 12 { + straight = append(straight, cards[j]) + current = cards[j] + if len(straight) >= length { + straightMap[int32(len(straightMap)+1)] = straight + } + } + } + } + return straightMap +} + +// 1.先找到所有长度为3的顺子 +// 2.剩余的牌再和已经取出来的顺子组合,知道组合成更大的牌, +// 3.合并顺子 +// 4.最后再确定单顺,剩余的牌和三条连对进行组合 看看能不能组成新的顺子(娱乐版不拆三条)拆连队只能拆长度等于2的连对 +// 找出所有长度为3的顺子 +func findStraights(cards []int32) map[int32][]int32 { + straights := [][]int32{} + length := len(cards) + if length < 3 { + return make(map[int32][]int32) + } + + sort.Slice(cards, func(i, j int) bool { + return Value(cards[i]) < Value(cards[j]) + }) + + for i := 0; i <= length-3; { + if Value(cards[i]) == Value(cards[i+1]) || Value(cards[i]) >= 10 { + i++ + continue + } + if i+2 < length { + arr := []int32{} + value := cards[i] + arr = append(arr, cards[i]) + for j := i + 1; j <= length-1; j++ { + if Value(cards[j]) == Value(value) || Value(cards[j]) >= 12 { + continue + } + arr = append(arr, cards[j]) + value = cards[j] + if len(arr) == 3 { + break + } + } + if isStraight(arr) { + straights = append(straights, arr) + // 从牌组中移除当前顺子的牌 + cards = removeCards(cards, arr) + length = len(cards) // 更新牌组长度 + } else { + i++ + } + } else { + break + } + } + sort.Slice(straights, func(i, j int) bool { + return Value(straights[i][0]) < Value(straights[j][0]) + }) + //2.剩余的牌再和已经取出来的顺子组合,知道组合成更大的牌, + removeCard := []int32{} + for _, card := range cards { + for i, straight := range straights { + if Value(card) == Value(straight[len(straight)-1]+1) && Value(card) < 12 { + straight = append(straight, card) + removeCard = append(removeCard, card) + straights[i] = straight + break + } + } + } + cards = removeCards(cards, removeCard) + //3.合并顺子 + for i := 0; i < len(straights)-1; i++ { + if Value(straights[i][len(straights[i])-1]) == Value(straights[i+1][0])-1 { + straights[i] = append(straights[i], straights[i+1]...) + straights = append(straights[:i+1], straights[i+2:]...) + i -= 1 + } + } + //转正map + straightsMap := make(map[int32][]int32) + for i, straight := range straights { + straightsMap[int32(i)] = straight + } + return straightsMap +} + +// 暂时不做 现在的找牌顺序可以兼容这种 拆三条 二连对组顺子 +func TwoFindStraights(cards []int32, isYuLe bool, cardsType map[int]map[int32][]int32, straights [][]int32) { + //4.剩余的牌和三条连对进行组合 看看能不能组成新的顺子(娱乐版不拆三条)拆连队只能拆长度等于2的连对 + //找相邻的两张牌 + twoCards := [][]int32{} + for i := 0; i < len(cards)-1; i++ { + if cards[i] == cards[i+1]-1 || cards[i] == cards[i+1]-2 { + twoCards = append(twoCards, []int32{cards[i], cards[i+1]}) + i += 1 + } + } + if len(twoCards) > 0 { + indexesToRemove := []int{} + for _, twoCard := range twoCards { + value := twoCard + //添加三条 和连对的牌进去再找顺子 + if !isYuLe { + threeCards := cardsType[Triple] + for i, three := range threeCards { + value = append(value, three[0]) + sort.Slice(value, func(i, j int) bool { + return value[i] < value[j] + }) + //判断是不是顺子 + if isStraight(value) { + //找到顺子了 移除三张 + cards = removeCards(cards, value) + delete(cardsType[Triple], i) + straights = append(straights, value) + indexesToRemove = append(indexesToRemove, int(i)) + cards = append(cards, three[1], three[2]) + break + } + } + } + + } + // 根据索引从切片中删除元素 + for i := len(indexesToRemove) - 1; i >= 0; i-- { + twoCards = append(twoCards[:indexesToRemove[i]], twoCards[indexesToRemove[i]+1:]...) + } + //添加二连对组顺子 + + //最后在合并一下顺子 + for i := 0; i < len(straights)-1; i++ { + if straights[i][len(straights[i])-1] == straights[i+1][0]-1 { + straights[i] = append(straights[i], straights[i+1]...) + straights = append(straights[:i+1], straights[i+2:]...) + i -= 1 + } + } + } +} + +func removeCards(cards []int32, toRemove []int32) []int32 { + newCards := []int32{} + removed := make([]bool, len(cards)) + + for _, card := range toRemove { + for i, c := range cards { + if c == card && !removed[i] { + removed[i] = true + break + } + } + } + + for i, card := range cards { + if !removed[i] { + newCards = append(newCards, card) + } + } + + return newCards +} + +func isStraight(cards []int32) bool { + if len(cards) < 3 { + return false + } + return Value(cards[1])-Value(cards[0]) == 1 && Value(cards[2])-Value(cards[1]) == 1 +} + +// 找三张 +func GetThreeOfkind(cards []int32) map[int32][]int32 { + cardCount := make(map[int32]int) + var three []int32 + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + } + // 查找炸弹组合 + threeMap := make(map[int32][]int32) + for card, count := range cardCount { + if count >= 3 { + three = append(three, card) + threeMap[card] = cardMap[card] + } + } + return threeMap +} + +// 找三顺 +func GetStraightTriple(cards []int32) map[int32][]int32 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + result := make(map[int32][]int32) + for i := 0; i < len(cards)-3; { + num := 1 + for j := i + 3; j < len(cards); { + if Value(cards[i]) == Value(cards[j])-num { + num += 1 + } + j += 3 + } + if num >= 2 { + arr := cards[i : i+num*3] + result[int32(Value(arr[len(arr)-1]))] = arr + } + i += 3 + + } + return result +} + +// 找对子 +func GetPair(cards []int32) ([]int32, map[int32][]int32) { + cardCount := make(map[int32]int) + cardMap := make(map[int32][]int32) + // 统计每张牌的数量 + for _, card := range cards { + cardCount[int32(Value(card))]++ + cardMap[int32(Value(card))] = append(cardMap[int32(Value(card))], card) + } + // 查找对子组合 + pairMap := make(map[int32][]int32) + var pair []int32 + for card, count := range cardCount { + if count >= 2 { + pair = append(pair, card) + pairMap[card] = cardMap[card] + } + } + return pair, pairMap +} + +// 转换 +func CardsChange(cards []int32) []string { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return Color(cards[i]) > Color(cards[j]) + } + return Value(cards[i]) > Value(cards[j]) + }) + var cardName = []string{ + "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♠", "2♠", // 0-12 黑桃 + "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♣", "2♣", // 13-25 梅花 + "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦", "A♦", "2♦", // 26-38 方片 + "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♥", "2♥", // 39-51 红桃 + } + change := []string{} + for _, card := range cards { + change = append(change, cardName[card]) + } + + return change +} + +// 删除牌 +func DelCards(cards []int32, delCards map[int32][]int32) []int32 { + // 创建一个新的切片,用于存储不需要删除的元素 + newCards := make([]int32, 0, len(cards)) + + // 遍历 cards 中的元素 + for _, card := range cards { + // 检查当前元素是否需要删除 + shouldDelete := false + for _, delCard := range delCards { + for _, del := range delCard { + if card == del { + shouldDelete = true + break + } + } + } + + // 如果不需要删除,则将元素添加到新的切片中 + if !shouldDelete { + newCards = append(newCards, card) + } + } + + return newCards +} + +// 获取牌型分计算 +func GetCardTypeScore(cardsType map[int]map[int32][]int32, isYuLe bool, cards []int32) (int, int) { + //计算每张牌的牌值 + cardValues := []int{ + -120, -100, -80, -60, -40, -20, 0, 20, 50, 90, 140, 200, 300, + -115, -95, -75, -55, -35, -15, 5, 25, 55, 95, 145, 205, 325, -110, -90, -70, -50, -30, -10, 10, 30, 60, 100, 150, 210, 350, -105, -85, -65, -45, -25, -5, 15, 35, 65, 105, 155, 215, 400, + } + //计算牌型分值 + var score int + //炸弹 + if len(cardsType[Four_Bomb]) > 0 { + if isYuLe { + score += len(cardsType[Four_Bomb]) * 1000 + } else { + score += len(cardsType[Four_Bomb]) * 700 + } + } + //连队 + if len(cardsType[Straight_Twin]) > 0 { + for _, twin := range cardsType[Straight_Twin] { + switch len(twin) / 2 { + case 5: + score += 500 + case 4: + score += 400 + case 3: + score += 300 + case 2: + score += 200 + } + } + } + //顺子 + if len(cardsType[Straight]) > 0 { + for _, straight := range cardsType[Straight] { + score += len(straight)*100 + (len(straight)-3)*(cardValues[straight[len(straight)-1]]/3) + } + + } + //三张 + if len(cardsType[Triple]) > 0 { + for _, triples := range cardsType[Triple] { + if Value(triples[0]) < 7 { + score += int(math.Abs(float64(250 + cardValues[triples[2]]))) + } else { + score += cardValues[triples[0]] + cardValues[triples[1]] + cardValues[triples[2]] + } + } + if isYuLe { + score += len(cardsType[Triple]) * 300 + } + } + //两对 + if len(cardsType[Twin]) > 0 { + for _, twins := range cardsType[Twin] { + if Value(twins[0]) < 7 { + score += cardValues[twins[1]] / 2 + } else { + score += cardValues[twins[0]] + cardValues[twins[1]] + } + } + } + + //删除三连对以上且三连对小于10的牌 + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + copyCards = DelCards(copyCards, cardsType[Four_Bomb]) + for _, straightTwin := range cardsType[Straight_Twin] { + delMap := make(map[int32][]int32) + if len(straightTwin)/2 >= 3 && Value(straightTwin[len(straightTwin)-1]) < 10 { + delMap[int32(len(delMap)+1)] = straightTwin + copyCards = DelCards(copyCards, delMap) + } + } + + for _, card := range copyCards { + score += cardValues[card] + } + handCardnum := len(cardsType[Four_Bomb]) + len(cardsType[Straight_Twin]) + len(cardsType[Straight]) + len(cardsType[Triple]) + len(cardsType[Twin]) + len(cardsType[Single]) + if isYuLe { + handCardnum -= (len(cardsType[Triple]) * 2) + } + if handCardnum <= 1 { + handCardnum = 1 + if isYuLe && handCardnum == 1 { + if len(cardsType[Triple]) > 0 { + if len(cards) > 5 { + handCardnum = 2 + } + } + } + } + if Have6StraightTwin(cards) || + Have12Straight(cards) || + Have2FourBomb(cards) { + return 9999, 1 + } + return score - (handCardnum-5)*150, handCardnum +} + +// 压牌 isWin-是否胜利 +func GetPressCards(cards, lastCards []int32, data *PredictRequest, pos int32) []int32 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + isWin := data.IsWin + //测试代码 + //isWin = false + outCards := []int32{} + + lastPos := data.Last_pos //上家出牌人的位置 + isYuLe := data.IsTienLenYule + isEnd := data.IsEnd + winSnids := data.WinSnids + //获取牌型 + cardsTypeMap := GetCardsType(cards, isYuLe) + _, handCardnum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + //获取出牌人的 手数 + otherCards := getOtherPlayerCards(pos, lastPos, data) + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + _, lastHandCardnum := GetCardTypeScore(otherCardsTypeMap, isYuLe, otherCards) + //获取下一家的位置和剩余的牌 + nextPos, nextCards := GetNextPos(pos, data) + isRule, ruleType := RulePopEnable_yl(lastCards) + if isRule { + switch ruleType { + case Single: + //单张 + cardsMap := cardsTypeMap[Single] + //转成数组 + singleArr := []int32{} + for _, single := range cardsMap { + singleArr = append(singleArr, single[0]) + } + if isEnd && !isYuLe { + sort.Slice(singleArr, func(i, j int) bool { + if Value(singleArr[i]) == Value(singleArr[j]) { + return singleArr[i] < singleArr[j] + } + return Value(singleArr[i]) < Value(singleArr[j]) + }) + } + //如果我的下一家剩余一张牌 出比他大的牌 + if len(singleArr) > 0 { + if len(singleArr) == 2 && len(cards) == 2 { + for _, mycard := range singleArr { + if getMaxCardTypeNum(Single, []int32{mycard}, pos, data) { + outCards = []int32{mycard} + } + } + } else { + for _, card := range singleArr { + if Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) < Color(lastCards[0]) { + continue + } + } + + if len(nextCards) == 1 { + if Value(card) >= Value(nextCards[0]) { + if Value(card) == Value(nextCards[0]) { + if Color(card) > Color(nextCards[0]) { + outCards = []int32{card} + break + } + } else { + outCards = []int32{card} + break + } + } + } else { + outCards = []int32{card} + break + } + } + } + } + } else { + //找顺子交集 返回值[][]int32 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + for _, arr := range result { + for _, card := range arr { + if Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card) >= Value(nextCards[0]) { + if Value(card) == Value(nextCards[0]) { + if Color(card) > Color(nextCards[0]) { + outCards = []int32{card} + break + //return card + } + } else { + outCards = []int32{card} + break + } + } + } else { + outCards = []int32{card} + break + } + } + } + if len(outCards) > 0 { + break + } + } + } + } + if isWin { + //拆牌 先找顺子的交集 拆对子 拆2 拆长度大于3的顺子 最后找大于这张出牌的牌除了炸弹 出牌这家只剩一首牌了 + if len(outCards) == 0 && len(cardsTypeMap[Twin]) > 0 { + twinMap := cardsTypeMap[Twin] + //拆小于2的对子 + for _, card := range twinMap { + if Value(card[0]) == 12 { + //排除掉对2 + continue + } + //先拆小于2的对子 + if Value(card[0]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[0]} + break + } + } else { + outCards = []int32{card[0]} + break + } + } + } else { + //玩家出完这张牌 还剩一张 剩余的牌比我现在出的牌大 + if len(otherCards) == 1 && Value(otherCards[0]) >= Value(card[0]) { + continue + } + outCards = []int32{card[0]} + break + } + } + } + //判断一下下一家剩1张牌比我的大的情况 + if len(outCards) > 0 { + break + } + } + //直接拆2上 + if len(outCards) == 0 && + (lastHandCardnum <= 4 || handCardnum <= 3 || + (len(nextCards) == 1 && Value(nextCards[0]) >= Value(lastCards[0]))) { + for _, card := range cards { + if Value(card) == 12 && Value(card) >= Value(lastCards[0]) { + if Value(card) == Value(lastCards[0]) { + if Color(card) > Color(lastCards[0]) { + outCards = []int32{card} + break + } else { + continue + } + } else { + outCards = []int32{card} + break + } + + } + } + if len(outCards) > 0 && len(otherCards) == 1 && Value(otherCards[0]) >= Value(outCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } else { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } + } + + //拆连对 map[int32][]int32 + if len(outCards) == 0 && len(cardsTypeMap[Straight_Twin]) > 0 { + for _, straightTwin := range cardsTypeMap[Straight_Twin] { + if len(straightTwin)/2 == 2 || lastHandCardnum == 1 { + for _, twin := range straightTwin { + if Value(twin) > Value(lastCards[0]) { + outCards = []int32{twin} + break + } + } + if len(outCards) > 0 { + break + } + } + } + } + + if !isYuLe { + //拆三条 + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 { + tripleMap := cardsTypeMap[Triple] + for _, card := range tripleMap { + if Value(card[0]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[0]} + break + } + } else { + outCards = []int32{card[0]} + break + } + } + } else { + outCards = []int32{card[0]} + break + } + } + } + } + } + //拆长度>3的顺子 + if len(outCards) == 0 && len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for _, card := range straightMap { + if len(card) <= 3 && lastHandCardnum != 1 { + continue + } + if Value(card[len(card)-1]) >= Value(lastCards[0]) { + if Value(card[0]) == Value(lastCards[0]) { + if Color(card[0]) < Color(lastCards[0]) { + continue + } + } + if len(nextCards) == 1 { + if Value(card[0]) >= Value(nextCards[0]) { + if Value(card[0]) == Value(nextCards[0]) { + if Color(card[0]) > Color(nextCards[0]) { + outCards = []int32{card[len(card)-1]} + break + } + } else { + outCards = []int32{card[len(card)-1]} + break + } + } + } else { + outCards = []int32{card[len(card)-1]} + break + } + } + } + } + } + if len(outCards) == 0 && Value(lastCards[0]) == 12 { + //找三连对 >四连对 >五连对 > 炸弹 + straightTwinMap := cardsTypeMap[Straight_Twin] + //找最小长度的key + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 3 { + outCards = straightTwin + break + //return straightTwin + } + } + } + //找炸弹 + if !isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && Value(lastCards[0]) == 12 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if len(outCards) > 0 { + if Value(lastCards[0]) == 12 && Value(outCards[0]) == 12 && lastHandCardnum != 1 { + //如果出牌玩家剩余手数大于2 或者我的剩余手数小于等于3 + //存在一种情况 上家剩余1张牌 其他人出单张 上家不要 这个时候我执行了这个判断 没有出2去接牌 && 我的上家单张数量>=2 ||上家剩余1张牌了 我要跑牌了 + _, myLastCards := GetLastPos(pos, data) + if ((lastHandCardnum > 2 && len(otherCardsTypeMap[Single]) > 2) || (handCardnum > 3 && len(cardsTypeMap[Single]) >= 2)) && len(myLastCards) >= 2 { + if lastHandCardnum > 3 { + outCards = []int32{} + } + } + } + } + if len(outCards) == 0 { + if len(nextCards) == 1 && Value(lastCards[0]) < Value(nextCards[0]) { + if Value(cards[len(cards)-1]) >= Value(nextCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + } + //强压 + if len(outCards) == 0 && (len(lastCards) == 1 || len(nextCards) == 1) { + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + + } + //打到底 最后一首牌判断 + if len(otherCards) == 1 && (!isYuLe && isEnd) && (winSnids == nil || len(winSnids) == 0) && len(outCards) != 0 && nextPos != lastPos { + if Value(outCards[0]) == Value(cards[len(cards)-1]) && handCardnum > 2 { + if Value(outCards[0]) <= Value(otherCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + outCards = []int32{} + } + } else { + outCards = []int32{} + } + } + } + } + + if isEnd && len(otherCards) == 0 && len(outCards) > 0 { + if len(nextCards) == 1 { + //在这加一道防线 + if Value(outCards[0]) < Value(nextCards[0]) { + //出最大牌 + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) && Value(cards[len(cards)-1]) >= Value(nextCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + + } + } + } + if len(outCards) == 0 && len(lastCards) == 1 && nextPos == lastPos { + //出最大的牌 + if Value(cards[len(cards)-1]) >= Value(lastCards[0]) { + outCards = []int32{cards[len(cards)-1]} + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Single) { + outCards = []int32{} + } + return outCards + case Twin: + //对子 + cardsMap := cardsTypeMap[Twin] + if len(cardsMap) > 0 { + for _, card := range cardsMap { + if lastHandCardnum == 1 && len(otherCards) == 2 && Value(otherCards[len(otherCards)-1]) >= Value(card[1]) { + if Value(otherCards[len(otherCards)-1]) == Value(card[1]) { + if Color(otherCards[len(otherCards)-1]) > Color(card[1]) { + continue + } + } else { + continue + } + } + if Value(card[1]) >= Value(lastCards[1]) { + if Value(card[1]) == 12 { + //判断能不能直接出对2 + if handCardnum > 3 && lastHandCardnum > 3 { + continue + } + } + if Value(card[1]) == Value(lastCards[1]) { + //判断花色 + if Color(card[1]) > Color(lastCards[1]) { + outCards = card + break + } + } else { + outCards = card + break + } + } + } + //再找一遍 + if len(outCards) == 0 { + for _, card := range cardsMap { + if Value(card[1]) >= Value(lastCards[1]) { + if Value(card[1]) == 12 { + //判断能不能直接出对2 + if handCardnum > 3 && lastHandCardnum > 3 { + continue + } + } + if Value(card[1]) == Value(lastCards[1]) { + //判断花色 + if Color(card[1]) > Color(lastCards[1]) { + outCards = card + break + } + } else { + outCards = card + break + } + } + } + } + } + //拆连对 + if isWin { + straightTwinMap := cardsTypeMap[Straight_Twin] + if len(outCards) == 0 { + if Value(lastCards[0]) == 12 { + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 4 { + outCards = straightTwin + break + } + } + } + } else { + //拆连对 + if len(straightTwinMap) > 0 { + for _, straightTwin := range straightTwinMap { + if len(straightTwin)/2 >= 2 { + //判断第一个和最后一位 + if Value(straightTwin[0]) >= Value(lastCards[0]) { + if Color(straightTwin[1]) > Color(lastCards[1]) { + outCards = []int32{straightTwin[0], straightTwin[1]} + break + } + } else { + //判断最后一位 + if Value(straightTwin[3]) >= Value(lastCards[0]) { + if Color(straightTwin[3]) > Color(lastCards[1]) { + outCards = []int32{straightTwin[2], straightTwin[3]} + break + } + } + } + } + } + } + } + } + //拆三条 + //找最大的三条拆 + if !isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 { + for _, triple := range cardsTypeMap[Triple] { + if Value(triple[0]) > Value(lastCards[0]) { + outCards = []int32{triple[0], triple[1]} + break + + } + } + + } + } else { + if len(outCards) == 0 && len(cardsTypeMap[Triple]) > 0 && (lastHandCardnum <= 2 || handCardnum <= 3) { + for _, triple := range cardsTypeMap[Triple] { + if Value(triple[0]) > Value(lastCards[0]) { + outCards = []int32{triple[0], triple[1]} + break + } + } + + } + } + } + //找炸弹 + if isYuLe { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } else { + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && Value(lastCards[0]) == 12 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if len(outCards) > 0 { + if Value(lastCards[0]) == 12 && Value(outCards[0]) == 12 { + //如果出牌玩家剩余手数大于2 或者我的剩余手数小于等于3 + if lastHandCardnum > 3 && (len(otherCardsTypeMap[Twin]) > 2 || len(otherCardsTypeMap[Single]) > 2) || (handCardnum > 3 && len(cardsTypeMap[Single]) > 2) { + if lastHandCardnum > 1 { + outCards = []int32{} + } + } + } + } + if len(outCards) == 0 && lastHandCardnum <= 2 { + //重组对子 找大对子 + _, pairMap := GetPair(cards) + if len(pairMap) > 0 { + maxkey := int32(-1) + for key, pairs := range pairMap { + if Value(pairs[0]) >= Value(lastCards[0]) { + if key > maxkey { + maxkey = key + } + } + } + if maxkey != -1 { + outCards = append(outCards, pairMap[maxkey][0], pairMap[maxkey][1]) + } + } + } + if len(otherCards) == 2 && lastHandCardnum == 1 && (!isYuLe && isEnd) && (winSnids == nil || len(winSnids) == 0) && len(outCards) != 0 && nextPos != lastPos { + if Value(outCards[0]) == Value(cards[len(cards)-1]) && handCardnum > 2 { + if Value(outCards[0]) <= Value(otherCards[0]) { + if Value(outCards[0]) == Value(otherCards[0]) { + if Color(outCards[0]) < Color(otherCards[0]) { + outCards = []int32{} + } + } else { + outCards = []int32{} + } + } + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Twin) { + outCards = []int32{} + } + return outCards + case Straight: + //顺子 + cardsMap := cardsTypeMap[Straight] + //获取顺子长度 + + for _, card := range cardsMap { + if len(card) == len(lastCards) { + if Value(card[len(card)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(card[len(card)-1]) == Value(lastCards[len(lastCards)-1]) { + //比较花色 + if Color(card[len(card)-1]) > Color(lastCards[len(lastCards)-1]) { + outCards = card + break + //return card + } + } else { + outCards = card + break + //return card + } + } + } else if len(card) > len(lastCards) && Value(card[len(card)-1]) >= Value(lastCards[len(lastCards)-1]) && (lastHandCardnum <= 2 || isEnd) { + //拆顺子 + if Value(card[len(card)-1]) == Value(lastCards[len(lastCards)-1]) { + //比较花色 + if Color(card[len(card)-1]) < Color(lastCards[len(lastCards)-1]) { + //截取 + //return []int32{} + } + } + outCards = card[len(card)-len(lastCards):] + } + } + // + if len(outCards) == 0 && lastHandCardnum <= 2 { + //找顺子 + allStraight := GetAllStraight(cards, len(lastCards)) + if len(allStraight) > 0 { + for _, straight := range allStraight { + if len(straight) >= len(lastCards) { + if Value(straight[len(straight)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(straight[len(straight)-1]) == Value(lastCards[len(lastCards)-1]) { + if Color(straight[len(straight)-1]) > Color(lastCards[len(lastCards)-1]) { + outCards = straight[len(straight)-len(lastCards):] + break + } + } else { + outCards = straight[len(straight)-len(lastCards):] + break + } + } + } + } + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight) { + outCards = []int32{} + } + return outCards + case ColorStraight: + //同花顺 + if len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for _, straight := range straightMap { + if len(straight) >= len(lastCards) { + //判断是不是同花顺 + if IsColorStraight(straight) { + if Value(straight[len(straight)-1]) >= Value(lastCards[len(lastCards)-1]) { + if Value(straight[len(straight)-1]) == Value(lastCards[len(lastCards)-1]) { + if Color(straight[len(straight)-1]) > Color(lastCards[len(lastCards)-1]) { + return straight[len(straight)-len(lastCards):] + } + } else { + return straight[len(straight)-len(lastCards):] + } + } + } + } + } + } + + //找炸弹 + if isYuLe && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + return cardsTypeMap[Four_Bomb][minKey] + } + //打到底 出牌人打完了 + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, ColorStraight) { + outCards = []int32{} + } + case Straight_Twin: + //连对 + //获取连对长度 + lastCardNum := len(lastCards) / 2 + lastMaxCard := Value(lastCards[len(lastCards)-1]) + cardsMap := cardsTypeMap[Straight_Twin] + for _, card := range cardsMap { + if len(card) >= len(lastCards) { + if len(card) == len(lastCards) { + if Value(card[len(card)-1]) >= lastMaxCard { + if Value(card[len(card)-1]) == lastMaxCard { + //比较花色 + if Color(card[len(card)-1]) > Color(lastCards[len(lastCards)-1]) { + //截取 + outCards = card + break + } + } else { + outCards = card + break + } + } + } else { + outCards = card + break + } + } + + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 && lastCardNum <= 3 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } else { + if lastCardNum == 3 { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + } + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight_Twin) { + outCards = []int32{} + } + return outCards + case Triple: + //三张 + cardsMap := cardsTypeMap[Triple] + for _, card := range cardsMap { + if Value(card[len(card)-1]) > Value(lastCards[len(lastCards)-1]) { + if Value(card[0]) == 12 && lastHandCardnum > 2 { + continue + } + outCards = card + break + //return card + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Triple) { + outCards = []int32{} + } + return outCards + case Four_Bomb: + //炸弹 + cardsMap := cardsTypeMap[Four_Bomb] + for _, card := range cardsMap { + if Value(card[0]) > Value(lastCards[0]) { + if isYuLe && (lastHandCardnum <= 2 || handCardnum <= 2) { + return card + } + if !isYuLe { + return card + } + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Four_Bomb) { + outCards = []int32{} + } + case Straight_Triple: + //三顺 + cardsMap := cardsTypeMap[Straight_Triple] + if len(cardsMap) > 0 { + for _, card := range cardsMap { + if Value(card[0]) > Value(lastCards[0]) && len(card) == len(lastCards) { + outCards = card + break + } + } + } + if isYuLe { + //找炸弹 + if (len(outCards) == 0 || len(outCards) != len(lastCards)) && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Straight_Triple) { + outCards = []int32{} + } + return outCards + case Plane_Single: + //三代单 --不用考虑 只有最后一首才会出三代单 + singleHand, singleLen := FindPlaneSingleHead(lastCards) + if singleLen > 1 && len(cards) >= len(lastCards) { + //333 444 5 6 7 8 + //找相同长度三顺 + straightTripleMap := cardsTypeMap[Straight_Triple] + if len(straightTripleMap) > 0 { + for _, straightTriple := range straightTripleMap { + if len(straightTriple)/3 >= singleLen { + if Value(straightTriple[len(straightTriple)-1]) > int(singleHand) { + outCards = straightTriple[len(straightTriple)-singleLen*3:] + } + } + } + } + } + if len(outCards) != 0 { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + delCard := make(map[int32][]int32) + delCard[0] = outCards + copyCards = DelCards(copyCards, delCard) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + num := len(cards) - len(lastCards) + if num == 1 { + //留最大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + outCards = []int32{} + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + outCards = []int32{} + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-3:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + for _, value := range cards { + status := true + for _, straight := range outCards { + if value == straight || Value(value) == Value(straight) { + status = false + break + } + } + if status { + outCards = append(outCards, value) + } + if len(outCards) == len(lastCards) { + break + } + } + } + } + if isYuLe { + //找炸弹 + if (len(outCards) == 0 || len(outCards) != len(lastCards)) && len(cardsTypeMap[Four_Bomb]) > 0 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + return cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Plane_Single) { + outCards = []int32{} + } + return outCards + case Plane_Twin: + outCards = []int32{} + twinHand, twinLen := FindPlaneTwinHead(lastCards) + if twinLen == 1 { + //三代双 + cardsMap := cardsTypeMap[Triple] + key := int32(999) + for i, data1 := range cardsMap { + if Value(data1[len(data1)-1]) > int(twinHand) { + if i < key { + //card = data1 + key = i + } + break + } + } + if key != 999 { + outCards = cardsTypeMap[Triple][key] + } + if len(outCards) > 0 { + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + if len(cardsTypeMap[Single]) >= 2 && Value(cardsTypeMap[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsTypeMap[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + outCards = append(outCards, minValue[0], minValue[1]) + } + } else if len(cardsTypeMap[Twin]) >= 1 && Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 { + //三代一对 + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + } else { + //直接找最小的两张牌 + if len(cards) >= 5 { + for _, value := range cards { + if Value(value) != Value(outCards[0]) { + outCards = append(outCards, value) + if len(outCards) == 5 { + return outCards + } + } + } + } + } + } + } else { + //333 444 555 + //找相同长度三顺 + if len(cards) >= len(lastCards) { + straightTripleMap := cardsTypeMap[Straight_Triple] + if len(straightTripleMap) > 0 { + for _, straightTriple := range straightTripleMap { + if len(straightTriple)/3 >= twinLen { + if Value(straightTriple[len(straightTriple)-1]) > int(twinHand) { + outCards = straightTriple[len(straightTriple)-twinLen*3:] + } + } + } + } + + if len(outCards) != 0 { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + delCard := make(map[int32][]int32) + delCard[0] = outCards + copyCards = DelCards(copyCards, delCard) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + num := len(cards) - len(lastCards) + if num == 1 { + //留最大牌 + outCards = []int32{} + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + outCards = []int32{} + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + outCards = []int32{} + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + maxCard := copyCards[len(copyCards)-3:] + outCards = []int32{} + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + for _, value := range cards { + status := true + for _, straight := range outCards { + if value == straight || Value(value) == Value(straight) { + status = false + break + } + } + if status { + outCards = append(outCards, value) + } + if len(outCards) == len(lastCards) { + break + } + } + } + } + } + } + if isYuLe { + //找炸弹 + if len(outCards) == 0 && len(cardsTypeMap[Four_Bomb]) > 0 && lastHandCardnum <= 2 { + minKey := findMinKey(cardsTypeMap[Four_Bomb]) + return cardsTypeMap[Four_Bomb][minKey] + } + } + + if !IsPressed(isYuLe, isEnd, otherCards, lastCards, data, pos, lastPos, nextPos, handCardnum, Plane_Twin) { + outCards = []int32{} + } + + return outCards + default: + return outCards + } + } + + return outCards +} + +// 出牌 isMin是否先手出最小的牌 +func GetOutCards(cards []int32, data *PredictRequest, pos int32) []int32 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + isWin := data.IsWin + + isYuLe := data.IsTienLenYule + isMin := data.IsFirstHand + isEnd := data.IsEnd + winSnids := data.WinSnids + //获取牌型 + cardsTypeMap := GetCardsType(cards, isYuLe) //map[int]map[int32][]int32 + cardsTypeMap = reloadcardsType(cardsTypeMap, pos, data, isYuLe) + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if handNum <= 1 { + return cards + } + //出牌逻辑 + outCards := []int32{} + cardValue := -1 + if isMin { + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + return outCards + } + //判断其他玩家剩余的牌的牌型是否能压过当前出牌的牌型 + //出牌顺序 + //出单张 判断下一家剩余1张牌且剩余的牌比我出的牌大 不出 + otherMinHandNum, otherMinCardsNum, otherPos, otherCardsType := getOtherMinCards(pos, data, isYuLe) + + //优先出三张 如果是娱乐模式 直接带单张 或者最小的对子 + if isYuLe { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + } + //出单张 + if len(outCards) == 0 && handNum > 2 { + straightNum := len(cardsTypeMap[Straight]) + //测试一下 + //if isYuLe { + if len(cardsTypeMap[Straight]) > 0 { + straightNum = len(cardsTypeMap[Straight])*2 + 1 + } + //} + if len(cardsTypeMap[Single]) > straightNum && len(cardsTypeMap[Single]) > (len(cardsTypeMap[Twin])+len(cardsTypeMap[Straight_Twin])) { + outCards = getSingle(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum <= 1 && otherCardsType == Single { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if Value(cards[0]) < Value(otherCards[0]) { + outCards = []int32{} + } else if Value(cards[0]) == Value(otherCards[0]) { + //判断花色 + if Color(cards[0]) < Color(otherCards[0]) { + outCards = []int32{} + } + } + + otherCardsMap := getOtherCards(pos, data) + + for _, single := range cardsTypeMap[Single] { + status := true + for _, other := range otherCardsMap { + if len(other) == 1 { + if Value(single[0]) <= Value(other[0]) { + if Value(single[0]) == Value(other[0]) && Color(single[0]) > Color(other[0]) { + continue + } + status = false + } + } + } + if status { + outCards = single + break + } + } + } + //如果是全场最大的单张 不出 + if len(outCards) > 0 && (otherMinCardsNum != 1) { + if Value(outCards[0]) == 12 && handNum > 2 { + outCards = []int32{} + } + } + } + } + } + + //出对子 判断下一家剩余2张牌且剩余的牌比我出的牌大 不出 + if len(outCards) == 0 { + straightNum := len(cardsTypeMap[Straight]) + if isYuLe { + //straightNum = len(cardsTypeMap[Straight]) + if len(cardsTypeMap[Straight]) > 0 { + straightNum = len(cardsTypeMap[Straight])*2 + 1 + } + } + if len(cardsTypeMap[Twin]) > straightNum { + outCards = getTwin(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if otherMinHandNum == 1 && otherMinCardsNum == 2 && otherCardsType == Twin { + + if Value(outCards[1]) < Value(otherCards[1]) { + outCards = []int32{} + } else if Value(outCards[1]) == Value(otherCards[1]) { + //判断花色 + if Color(outCards[1]) < Color(otherCards[1]) { + outCards = []int32{} + } + } + } + + otherCardsMap := getOtherCards(pos, data) + + for _, twin := range cardsTypeMap[Twin] { + if Value(twin[0]) == 12 && handNum > 2 { + continue + } + for _, other := range otherCardsMap { + _, otherRuleType := RulePopEnable(other) + if len(other) == 2 && otherRuleType == Twin { + if Value(twin[1]) >= Value(other[1]) { + if Value(twin[1]) == Value(other[1]) { + if Color(twin[0]) > Color(other[0]) { + outCards = twin + break + } else { + continue + } + } + outCards = twin + break + + } + } + } + + } + } + } + } + + //出三张 + if isYuLe { + if len(outCards) == 0 { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 { + if otherMinHandNum == 1 && otherMinCardsNum >= 3 && otherCardsType == Triple { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + //获取牌型 + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + otherTriple := otherCardsTypeMap[Triple] + //找最小key + if len(otherTriple) > 0 { + otherMinKey := findMinKey(otherTriple) + if cardValue < Value(otherTriple[otherMinKey][0]) { + outCards = []int32{} + } + } + } + } + } + } + + //顺子 + if len(outCards) == 0 { + outCards = getStraight(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum == len(outCards) && otherCardsType == Straight { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + + if Value(outCards[len(outCards)-1]) < Value(otherCards[len(otherCards)-1]) { + outCards = []int32{} + } else if Value(outCards[len(outCards)-1]) == Value(otherCards[len(otherCards)-1]) { + //判断花色 + if Color(outCards[len(outCards)-1]) < Color(otherCards[len(otherCards)-1]) { + outCards = []int32{} + } + } + } + //如果其他玩家剩余牌全是顺子 并且比我的大 我不出顺子了 (后续没牌型可出了!!!! + } + } + if !isYuLe { + if len(outCards) == 0 { + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum >= 3 && otherCardsType == Triple { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + //获取牌型 + otherCardsTypeMap := GetCardsType(otherCards, isYuLe) + otherTriple := otherCardsTypeMap[Triple] + //找最小key + if len(otherTriple) > 0 { + otherMinKey := findMinKey(otherTriple) + if cardValue < Value(otherTriple[otherMinKey][0]) { + outCards = []int32{} + } + } + } + } + } + } + //连对 + if len(outCards) == 0 { + outCards = getStraightTwin(cardsTypeMap, cards, isYuLe) + if len(outCards) > 0 && isWin { + if otherMinHandNum == 1 && otherMinCardsNum == len(outCards) && otherCardsType == Straight_Twin { + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if Value(outCards[len(outCards)-1]) < Value(otherCards[len(otherCards)-1]) { + outCards = []int32{} + } else if Value(outCards[len(outCards)-1]) == Value(otherCards[len(otherCards)-1]) { + //判断花色 + if Color(outCards[len(outCards)-1]) < Color(otherCards[len(otherCards)-1]) { + outCards = []int32{} + } + } + } + } + if len(outCards)/2 >= 3 && handNum > 2 { + outCards = []int32{} + } + } + //炸弹 这个就不用检查了 + if len(outCards) == 0 && handNum <= 2 { + //出炸弹 + outCards = getBomb(cardsTypeMap, cards, isYuLe) + } + //额外判断 + //获取下一家的位置和剩余的牌 + if isWin { + if handNum <= 2 { + //判断我的剩余牌型中有没有全场最大的牌型 + for cardType, myCards := range cardsTypeMap { //map[int]map[int32][]int32 + for _, mycard := range myCards { + if getMaxCardTypeNum(cardType, mycard, pos, data) { + if cardType == Triple { + //出三代二 + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + if len(outCards) > 0 { + return outCards + } else { + break + } + } else { + return mycard + } + } + } + } + } + if handNum <= 3 { + myMaxCardStatus, maxCardNum, maxCardsArr := getMaxCards(cards, data, pos) + if len(outCards) == 1 { + //判断当前最大牌在不在我手 + if !myMaxCardStatus { + //找最长的牌 + for cardType, myCards := range cardsTypeMap { //map[int]map[int32][]int32 + for _, mycard := range myCards { + if (cardType == Straight_Twin && len(mycard)/2 >= 3) || cardType == Four_Bomb { + //手里有连炸 + continue + } + if len(mycard) > len(outCards) { + if cardType == Triple { + //出三代二 + outCards, cardValue = getTriple(cardsTypeMap, cards, isYuLe, otherMinHandNum, pos, int32(otherPos), data) + return outCards + } + if cardType != Single && cardType != Twin { + outCards = mycard + } else { + if Value(mycard[0]) != Value(cards[len(cards)-1]) { + outCards = mycard + } + } + } + } + } + } + } + //获取我最大牌的 + if myMaxCardStatus { + if otherMinCardsNum > 1 { + //这个时候我应该出单牌 + //找单张 - 对子- 和最大牌数量相等的顺子长度+1的顺子 + status := true + if len(cardsTypeMap[Single]) > 0 { + card := getSingle(cardsTypeMap, cards, isYuLe) + if len(card) > 0 { + for _, maxCard := range maxCardsArr { + if maxCard == card[0] { + status = false + break + } + } + } + } + if len(cardsTypeMap[Single]) > 0 && status { + card := getSingle(cardsTypeMap, cards, isYuLe) + if len(card) > 0 { + outCards = card + } + } else if len(cardsTypeMap[Twin]) > 0 { + minKey := findMinKey(cardsTypeMap[Twin]) + //map[int]map[int32][]int32 + if cardsTypeMap[Twin][minKey] != nil { + outCards = []int32{cardsTypeMap[Twin][minKey][0]} + } + } else { + if maxCardNum > 1 { + //先找顺子的交集 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + if len(result) > 0 { + for _, arr := range result { + outCards = arr + break + } + } + } else if len(cardsTypeMap[Straight]) > 0 { + minLength := 13 + minKey := int32(0) + for i, straight := range cardsTypeMap[Straight] { + if len(straight) < minLength { + minKey = i + } + } + if len(cardsTypeMap[Straight][minKey]) <= maxCardNum+1 { + outCards = []int32{cardsTypeMap[Straight][minKey][0]} + } + } + } + } + } + } + } + } + //最后直接出最小牌型的牌 + if len(outCards) == 0 { + //直接出最大的单牌 + if otherMinHandNum == 1 { + //先找其他牌型 + for cardType, card := range cardsTypeMap { + if len(card) > 0 { + if cardType != Single { + minKey := findMinKey(card) + outCards = card[minKey] + break + } else if cardType == Triple && isYuLe { + //找两张单张 + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + if len(cardsTypeMap[Single]) >= 2 && Value(cardsTypeMap[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsTypeMap[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + outCards = append(outCards, minValue[0], minValue[1]) + } + } else if len(cardsTypeMap[Twin]) >= 1 && Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 { + //三代一对 + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + } + } + } + } + if len(outCards) == 0 { + nextPos, nextCards := GetNextPos(pos, data) + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + if nextPos != int32(otherPos) && Value(nextCards[len(nextCards)-1]) > Value(otherCards[0]) { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + } else { + lastPos, _ := GetLastPos(pos, data) + if isEnd && len(winSnids) == 0 && nextPos != lastPos { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + } else { + if otherMinHandNum > 2 { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + } else { + outCards = []int32{cards[len(cards)-1]} + } + } + + } + } + } else { + //找最小牌型的牌 + outCards = GetMinCardType(cards[0], cardsTypeMap, isYuLe) + } + } + return outCards +} + +// 判断牌型是否全场最大 +func getMaxCardTypeNum(cardsType int, cards []int32, pos int32, data *PredictRequest) bool { + //map[int][]int32 + status := true + otherCardsMap := getOtherCards(pos, data) + for _, otherCards := range otherCardsMap { + if len(otherCards) == 0 { + continue + } + sort.Slice(otherCards, func(i, j int) bool { + if Value(otherCards[i]) == Value(otherCards[j]) { + return otherCards[i] < otherCards[j] + } + return Value(otherCards[i]) < Value(otherCards[j]) + }) + if cardsType == Single { + if len(otherCards) > 0 && len(cards) > 0 { + if Value(otherCards[len(otherCards)-1]) >= Value(cards[0]) { + if Value(otherCards[len(otherCards)-1]) == Value(cards[0]) { + if Color(otherCards[len(otherCards)-1]) > Color(cards[0]) { + return false + } + } else { + return false + } + } + } + } else if cardsType == Twin { + _, pairMap := GetPair(otherCards) + if len(pairMap) > 0 { + for _, pair := range pairMap { + if Value(pair[len(pair)-1]) >= Value(cards[0]) { + if Value(pair[len(pair)-1]) == Value(cards[0]) { + if Color(pair[len(pair)-1]) > Color(cards[0]) { + return false + } + } else { + return false + } + } + } + } + } + otherCardsTypeMap := GetCardsType(otherCards, data.IsTienLenYule) //map[int]map[int32][]int32 + if otherCardsTypeMap[cardsType] != nil && len(otherCardsTypeMap[cardsType]) > 0 { + for _, cardsArr := range otherCardsTypeMap[cardsType] { + if len(cardsArr) < len(cards) { + continue + } + if Value(cardsArr[len(cardsArr)-1]) > Value(cards[len(cards)-1]) { + status = false + break + } else if Value(cardsArr[len(cardsArr)-1]) == Value(cards[len(cards)-1]) { + if Color(cardsArr[len(cardsArr)-1]) > Color(cards[len(cards)-1]) { + status = false + break + } + } + } + if !status { + break + } + } + } + return status +} + +// 出单张 +func getSingle(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + if isYuLe { + if len(cardsTypeMap[Triple]) == 0 { + minKey := findMinKey(cardsTypeMap[Single]) + outCards = cardsTypeMap[Single][minKey] + + } + } else { + minKey := findMinKey(cardsTypeMap[Single]) + outCards = cardsTypeMap[Single][minKey] + } + return outCards +} + +// 出对子 +func getTwin(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if len(cardsTypeMap[Twin]) > len(cardsTypeMap[Straight]) { + minKey := findMinKey(cardsTypeMap[Twin]) + if isYuLe { + if len(cardsTypeMap[Triple]) == 0 { + if Value(cardsTypeMap[Twin][minKey][0]) < 11 || (handNum <= 2 && len(cardsTypeMap[Triple]) == 0) { + outCards = cardsTypeMap[Twin][minKey] + } + } + } else { + if Value(cardsTypeMap[Twin][minKey][0]) < 11 || (handNum <= 2 && len(cardsTypeMap[Triple]) == 0) { + outCards = cardsTypeMap[Twin][minKey] + } + + } + + } + return outCards +} + +// 出三张 cardValue三张的值 value(card) +func getTriple(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool, otherMinHandNum int, mypos, otherPos int32, data *PredictRequest) ([]int32, int) { + outCards := []int32{} + cardValue := 0 + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if isYuLe && len(cardsTypeMap[Straight_Triple]) > 0 { + //AI机器人三顺的长度只能是2 大于2的直接拆成连对炸弹了 + outCards = []int32{} + if len(cards) <= 10 && len(cardsTypeMap[Four_Bomb]) == 0 { + return cards, cardValue + } else { + copyCards := make([]int32, len(cards)) + copy(copyCards, cards) + copyCards = DelCards(copyCards, cardsTypeMap[Straight_Triple]) + copyCards = DelCards(copyCards, cardsTypeMap[Four_Bomb]) + sort.Slice(copyCards, func(i, j int) bool { + if Value(copyCards[i]) == Value(copyCards[j]) { + return copyCards[i] < copyCards[j] + } + return Value(copyCards[i]) < Value(copyCards[j]) + }) + num := len(cards) - 10 + if num == 1 { + //留最大牌 + maxCard := copyCards[len(copyCards)-1:] + for _, card := range cards { + if card == maxCard[0] { + continue + } + outCards = append(outCards, card) + } + } else if num == 2 { + //留对子 + _, pairMap := GetPair(copyCards) + if len(pairMap) > 0 { + maxPair := -1 + maxKey := int32(0) + for i, pair := range pairMap { + if Value(pair[0]) > maxPair { + maxPair = Value(pair[0]) + maxKey = i + } + } + for _, card := range cards { + if card == pairMap[maxKey][0] || card == pairMap[maxKey][1] { + continue + } + outCards = append(outCards, card) + } + } else { + //留最后两张大牌 + maxCard := copyCards[len(copyCards)-2:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] { + continue + } + outCards = append(outCards, card) + } + + } + } else if num == 3 { + //留顺子 + straights := cardsTypeMap[Straight] + if len(straights) > 0 { + straight := straights[int32(len(straights)-1)] + saveCards := straight[len(straight)-3:] + for _, card := range cards { + if card == saveCards[0] || card == saveCards[1] || card == saveCards[2] { + continue + } + outCards = append(outCards, card) + } + + } else { + //留最后三张大牌 + maxCard := copyCards[len(copyCards)-3:] + for _, card := range cards { + if card == maxCard[0] || card == maxCard[1] || card == maxCard[2] { + continue + } + outCards = append(outCards, card) + } + } + } else { + //直接出三顺 + straightTripleMap := cardsTypeMap[Straight_Triple] + for _, straightTriple := range straightTripleMap { + outCards = straightTriple + break + } + } + } + return outCards, cardValue + + } + + if len(cardsTypeMap[Triple]) > 0 { + minSingleKey := findMinKey(cardsTypeMap[Single]) + minTwinKey := findMinKey(cardsTypeMap[Twin]) + minTripleKey := findMinKey(cardsTypeMap[Triple]) + if len(cardsTypeMap[Triple]) == 2 && len(cards) <= 9 { + //先出大的三条 + minTripleKey = findMaxKey(cardsTypeMap[Triple]) + } + if len(cardsTypeMap[Triple]) >= 2 && len(cards) <= 8 && isYuLe { + //判断单张数量 + if len(cardsTypeMap[Single]) < 2 { + //判断是不是三顺 + var arr []int32 + for _, three := range cardsTypeMap[Triple] { + for _, value := range three { + arr = append(arr, value) + } + } + StraightTripleMap := GetStraightTriple(arr) + if len(StraightTripleMap) > 0 { + //娱乐场 小于11张牌可以一首扔 + return cards, cardValue + } + } + } + if isYuLe { + //出三代2 或三带一小对 单张小于12 三顺小于等于7 + otherCard := getOtherPlayerCards(mypos, otherPos, data) + if len(cardsTypeMap[Single]) >= 2 && (Value(cardsTypeMap[Single][minSingleKey][0]) < 12 || handNum <= 2 || (otherMinHandNum == 1 && len(otherCard) == 1 && len(cardsTypeMap[Single]) >= 3)) { + //三代2单 + if Value(cardsTypeMap[Triple][minTripleKey][0]) <= 9 || handNum <= 2 || (otherMinHandNum == 1 && len(otherCard) == 1 && len(cardsTypeMap[Single]) >= 3) { + outCards = cardsTypeMap[Triple][minTripleKey] + minValue := FindSmallestTwoValues(cardsTypeMap[Single]) + if len(minValue) < 2 { + } else { + outCards = append(outCards, minValue[0], minValue[1]) + } + + } + } else if len(cardsTypeMap[Twin]) >= 1 && (Value(cardsTypeMap[Twin][minTwinKey][0]) < 11 || handNum <= 3) { + //三代一对 + outCards = cardsTypeMap[Triple][minTripleKey] + outCards = append(outCards, cardsTypeMap[Twin][minTwinKey][0], cardsTypeMap[Twin][minTwinKey][1]) + } else { + if len(cards) > 5 { + outCards = cardsTypeMap[Triple][minTripleKey] + if len(cardsTypeMap[Single]) > 0 { + for _, single := range cardsTypeMap[Single] { + outCards = append(outCards, single[0]) + } + } + + if len(outCards) < 5 { + //找顺子交集 + result := FindIntersection(cardsTypeMap[Straight]) + if len(result) > 0 { + sort.Slice(cards, func(i, j int) bool { + if Value(cards[i]) == Value(cards[j]) { + return cards[i] < cards[j] + } + return Value(cards[i]) < Value(cards[j]) + }) + } + for _, cardArr := range result { + for _, card := range cardArr { + outCards = append(outCards, card) + if len(outCards) == 5 { + break + } + } + if len(outCards) == 5 { + break + } + } + //找顺子 + if len(outCards) < 5 { + if len(cardsTypeMap[Straight]) > 0 { + for _, straights := range cardsTypeMap[Straight] { + //剩余俩个三张 一共8张牌 + if len(straights)-(5-len(outCards)) >= 3 || (len(cards) == 10 && len(cardsTypeMap[Triple]) == 2) { + for i := 0; i < 5-len(outCards); i++ { + outCards = append(outCards, straights[i]) + if len(outCards) == 5 { + break + } + } + if len(outCards) == 5 { + break + } + } + } + } + } + } + if len(outCards) != 5 { + //直接出三张 + outCards = cardsTypeMap[Triple][minTripleKey] + } + } else { + //直接出三张 + outCards = cardsTypeMap[Triple][minTripleKey] + } + } + //出三张拦截 下面三手牌会判断牌型出 这里直接不让出三个2 + if Value(cardsTypeMap[Triple][minTripleKey][0]) == 12 && handNum >= 3 && len(cards) >= 10 { + outCards = []int32{} + return outCards, 0 + } + } else { + outCards = cardsTypeMap[Triple][minTripleKey] + if isYuLe { + if len(cards) <= 5 { + //最后4张牌 出三带1 + outCards = cards + } + } + } + cardValue = Value(cardsTypeMap[Triple][minTripleKey][0]) + } + return outCards, cardValue +} + +// 出顺子 +func getStraight(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, cards) + if len(cardsTypeMap[Straight]) >= 1 { + //出顺子 + //如果只剩两手牌 先出最长的顺子 + if handNum <= 2 { + //找长度最大的顺子 + maxLength := 0 + maxKey := int32(0) + for i, straight := range cardsTypeMap[Straight] { + if len(straight) > maxLength { + maxKey = i + } + } + outCards = cardsTypeMap[Straight][maxKey] + } else { + //找小顺子 + outCards = cardsTypeMap[Straight][0] + } + } + return outCards +} + +// 出连对 +func getStraightTwin(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + if len(cardsTypeMap[Straight_Twin]) > 0 { + //找长度最小的连对 + length := 0 + key := int32(0) + for i, straight := range cardsTypeMap[Straight_Twin] { + if len(straight) < length && len(straight) > 0 { + key = i + } + } + outCards = cardsTypeMap[Straight_Twin][key] + } + return outCards +} + +// 出炸弹 +func getBomb(cardsTypeMap map[int]map[int32][]int32, cards []int32, isYuLe bool) []int32 { + outCards := []int32{} + //炸弹 + if len(cardsTypeMap[Four_Bomb]) > 0 { + //出炸弹 + minTripleKey := findMinKey(cardsTypeMap[Four_Bomb]) + outCards = cardsTypeMap[Four_Bomb][minTripleKey] + } + return outCards +} + +// ---------------------------------------------// + +// 找手里最小的牌的组合 +func GetMinCardType(minCard int32, cardsType map[int]map[int32][]int32, isYuLe bool) []int32 { + for cardType, cards := range cardsType { + for _, cardArr := range cards { + for i, card := range cardArr { + if Value(card) == Value(minCard) { + if card == minCard { + if cardType == Triple && isYuLe { + //找两张单张 + minSingleKey := findMinKey(cardsType[Single]) + minTwinKey := findMinKey(cardsType[Twin]) + if len(cardsType[Single]) >= 2 && Value(cardsType[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsType[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsType[Single]) + cardArr = append(cardArr, minValue[0], minValue[1]) + } + } else if len(cardsType[Twin]) >= 1 && Value(cardsType[Twin][minTwinKey][0]) < 11 { + //三代一对 + cardArr = append(cardArr, cardsType[Twin][minTwinKey][0], cardsType[Twin][minTwinKey][1]) + } + } + return cardArr + } else { + //替换牌 + cardArr[i] = minCard + if cardType == Triple && isYuLe { + //找两张单张 变成三代二 + minSingleKey := findMinKey(cardsType[Single]) + minTwinKey := findMinKey(cardsType[Twin]) + if len(cardsType[Single]) >= 2 && Value(cardsType[Single][minSingleKey][0]) < 12 { + //三代2单 + if len(cardsType[Single]) >= 2 { + minValue := FindSmallestTwoValues(cardsType[Single]) + cardArr = append(cardArr, minValue[0], minValue[1]) + } + } else if len(cardsType[Twin]) >= 1 && Value(cardsType[Twin][minTwinKey][0]) < 11 { + //三代一对 + cardArr = append(cardArr, cardsType[Twin][minTwinKey][0], cardsType[Twin][minTwinKey][1]) + } + } + return cardArr + } + + } + } + } + } + //直接返回单张 + return []int32{minCard} +} + +// 获取下一家的位置和剩余的牌 +func GetNextPos(pos int32, data *PredictRequest) (int32, []int32) { + for i := 1; i < 4; i++ { + nextPos := pos + int32(i) + if nextPos > 3 { + nextPos -= 4 + } + //判断这个位置有没有剩余牌 + if nextPos == 0 && len(data.Cards_left_0) > 0 { + return nextPos, data.Cards_left_0 + } else if nextPos == 1 && len(data.Cards_left_1) > 0 { + return nextPos, data.Cards_left_1 + } else if nextPos == 2 && len(data.Cards_left_2) > 0 { + return nextPos, data.Cards_left_2 + } else if nextPos == 3 && len(data.Cards_left_3) > 0 { + return nextPos, data.Cards_left_3 + } + } + return -1, nil +} + +// 获取上家的位置和剩余的牌 +func GetLastPos(pos int32, data *PredictRequest) (int32, []int32) { + for i := 1; i < 4; i++ { + nextPos := (pos - int32(i) + 4) % 4 + //判断这个位置有没有剩余牌 + if nextPos == 0 && len(data.Cards_left_0) > 0 { + return nextPos, data.Cards_left_0 + } else if nextPos == 1 && len(data.Cards_left_1) > 0 { + return nextPos, data.Cards_left_1 + } else if nextPos == 2 && len(data.Cards_left_2) > 0 { + return nextPos, data.Cards_left_2 + } else if nextPos == 3 && len(data.Cards_left_3) > 0 { + return nextPos, data.Cards_left_3 + } + } + return -1, nil +} + +// 获取指定玩家的牌 +func getOtherPlayerCards(mypos, otherPos int32, data *PredictRequest) []int32 { + otherCardsMap := getOtherCards(mypos, data) + sort.Slice(otherCardsMap[int(otherPos)], func(i, j int) bool { + if otherCardsMap[int(otherPos)][i] == otherCardsMap[int(otherPos)][j] { + return otherCardsMap[int(otherPos)][i] < otherCardsMap[int(otherPos)][j] + } + return Value(otherCardsMap[int(otherPos)][i]) < Value(otherCardsMap[int(otherPos)][j]) + }) + return otherCardsMap[int(otherPos)] +} + +// 判断自己手里有没有最大的牌 +func getMaxCards(myCards []int32, data *PredictRequest, mypos int32) (bool, int, []int32) { + sort.Slice(myCards, func(i, j int) bool { + if Value(myCards[i]) == Value(myCards[j]) { + return myCards[i] < myCards[j] + } + return Value(myCards[i]) < Value(myCards[j]) + }) + //getOtherCards() + otherCardsMap := getOtherCards(mypos, data) + var otherCards []int32 + for _, otherCard := range otherCardsMap { + otherCards = append(otherCards, otherCard...) + } + + sort.Slice(otherCards, func(i, j int) bool { + if Value(otherCards[i]) == Value(otherCards[j]) { + return otherCards[i] < otherCards[j] + } + return Value(otherCards[i]) < Value(otherCards[j]) + }) + maxCardNum := 0 + status := false + maxCardsArr := []int32{} + for _, mycard := range myCards { + if Value(mycard) >= Value(otherCards[len(otherCards)-1]) { + if Value(mycard) == Value(otherCards[len(otherCards)-1]) { + if Color(mycard) < Color(otherCards[len(otherCards)-1]) { + continue + } + } + maxCardNum += 1 + status = true + maxCardsArr = append(maxCardsArr, mycard) + } + } + return status, maxCardNum, maxCardsArr +} +func findMinKey(data map[int32][]int32) int32 { + minValue := int32(math.MaxInt32) + minKey := int32(-1) + for i, value := range data { + if int32(Value(value[0])) < minValue { + minValue = int32(Value(value[0])) + minKey = i + } + } + return minKey +} +func findMaxKey(data map[int32][]int32) int32 { + maxKey := int32(-1) + + for key := range data { + if key > maxKey { + maxKey = key + } + } + + return maxKey +} + +// 获取其他玩家的牌 +func getOtherCards(myPos int32, data *PredictRequest) map[int][]int32 { + otherCards := make(map[int][]int32) + for i := 0; i < 4; i++ { + if i == int(myPos) { + continue + } + if i == 0 { + otherCards[i] = data.Cards_left_0 + } else if i == 1 { + otherCards[i] = data.Cards_left_1 + } else if i == 2 { + otherCards[i] = data.Cards_left_2 + } else if i == 3 { + otherCards[i] = data.Cards_left_3 + } + } + return otherCards +} + +// 获取其他玩家最小剩余手数 和剩余牌数及位置剩余的牌型 +func getOtherMinCards(myPos int32, data *PredictRequest, isYuLe bool) (int, int, int, int) { + otherCards := getOtherCards(myPos, data) + otherMinHandNum := 100 + otherMinCardsNum := 13 + otherPos := -1 + cardsType := -1 + maxCards := []int32{} + for i, other := range otherCards { + //获取牌型 + if len(other) > 0 { + cardsTypeMap := GetCardsType(other, isYuLe) + //剩余手数 + _, handNum := GetCardTypeScore(cardsTypeMap, isYuLe, other) + if handNum <= otherMinHandNum && len(other) <= otherMinCardsNum { + if handNum == otherMinHandNum && len(other) <= otherMinHandNum { + _, ruleType := RulePopEnable(other) + if ruleType == cardsType { + //相同牌型找最大的牌 + if Value(other[len(other)-1]) < Value(maxCards[len(maxCards)-1]) { + continue + } + } + } + otherMinHandNum = handNum + otherMinCardsNum = len(other) + otherPos = i + maxCards = other + if handNum <= 1 { + isRule, ruleType := RulePopEnable(other) + if isRule { + cardsType = ruleType + } else { + //三张 三带一 三带二 + cardsType = Triple + } + + } + } + } + } + return otherMinHandNum, otherMinCardsNum, otherPos, cardsType +} + +// 找到最小的两个值 +func FindSmallestTwoValues(m map[int32][]int32) []int32 { + // 创建一个切片存储map的键值对 + pairs := make([][2]int32, 0) + + // 将map的键值对存储在切片中 + for key := range m { + pairs = append(pairs, [2]int32{key, key}) + } + + // 对切片按照键值进行排序 + sort.Slice(pairs, func(i, j int) bool { + return pairs[i][0] < pairs[j][0] + }) + + // 取出最小的两个值 + var smallestTwo []int32 + if len(pairs) >= 2 { + smallestTwo = append(smallestTwo, m[pairs[0][0]]...) + if len(m[pairs[1][0]]) > 0 { + smallestTwo = append(smallestTwo, m[pairs[1][0]]...) + } + } else if len(pairs) == 1 { + smallestTwo = m[pairs[0][0]] + } + return smallestTwo +} + +// 找交集 +func FindIntersection(arrays map[int32][]int32) [][]int32 { + var result [][]int32 + + // 获取 map 中的键,并按顺序进行迭代 + keys := make([]int32, 0, len(arrays)) + for key := range arrays { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + + // 检查相邻的数组之间的交集 + for i := 0; i < len(keys)-1; i++ { + currentKey := keys[i] + nextKey := keys[i+1] + + currentArray := arrays[currentKey] + nextArray := arrays[nextKey] + + intersection := GetArrayIntersection(currentArray, nextArray) + if intersection != nil { + result = append(result, intersection) + } + } + + return result +} + +// 获取两个切片的交集 +func GetArrayIntersection(arr1, arr2 []int32) []int32 { + // 创建一个 map 用于存储第一个切片中的元素 + elements := make(map[int32]bool) + for _, num := range arr1 { + elements[num] = true + } + + // 检查第二个切片中的元素是否在第一个切片中出现,如果是,则添加到结果中 + var result []int32 + for _, num := range arr2 { + if elements[num] { + result = append(result, num) + } + } + + return result +} + +// 是否压牌 +func IsPressed(isYuLe, isEnd bool, otherCards, lastCards []int32, data *PredictRequest, pos, lastPos, nextPos int32, handCardnum, cardsType int) bool { + if (!isYuLe && isEnd) && len(otherCards) == 0 { + lastNextPos, _ := GetNextPos(lastPos, data) + if lastNextPos == pos && nextPos != lastPos { + if getMaxCardTypeNum(cardsType, lastCards, pos, data) && handCardnum > 2 { + return false + } + if len(lastCards) > 0 { + if Value(lastCards[0]) == 2 && handCardnum > 2 { + return false + } + } + } + + } + + return true +} + +// 重组牌型 +func reloadcardsType(cardsTypeMap map[int]map[int32][]int32, pos int32, data *PredictRequest, isYuLe bool) map[int]map[int32][]int32 { + //其他玩家只剩一张牌了 + otherMinHandNum, otherMinCardsNum, otherPos, otherCardsType := getOtherMinCards(pos, data, isYuLe) + //otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + + //剩余两手 全是单牌 + status := false + allOtherCards := getOtherCards(pos, data) + for _, otherCards := range allOtherCards { + if len(otherCards) == 2 { + otherType := GetCardsType(otherCards, isYuLe) + //剩余手数 + _, handNum := GetCardTypeScore(otherType, isYuLe, otherCards) + if handNum == 2 { + otherMaxCard := otherCards[0] + if Value(otherCards[0]) < Value(otherCards[1]) { + otherMaxCard = otherCards[1] + } + if getMaxCardTypeNum(Single, []int32{otherMaxCard}, pos, data) { + status = true + } + } + } + } + + if (otherMinHandNum == 1 && otherMinCardsNum <= 1 && otherCardsType == Single) || status { + //lastPos, _ := GetLastPos(pos, data) + //nextPos, _ := GetNextPos(pos, data) + //只考虑剩2人的情况下 + //if nextPos == int32(otherPos) { + //判断我手里的单牌 组对子 + if len(cardsTypeMap[Single]) >= 2 { + //判断如果单牌有2张以上小于其他玩家的牌,开始组对子 + otherCards := getOtherPlayerCards(pos, int32(otherPos), data) + minCards := map[int32]int32{} + for i, card := range cardsTypeMap[Single] { + if Value(card[0]) <= Value(otherCards[0]) { + //判断花色 + if Value(card[0]) == Value(otherCards[0]) { + if Color(card[0]) < Color(otherCards[0]) { + //minCards = append(minCards, card[0]) + minCards[i] = card[0] + continue + } + } else { + //minCards = append(minCards, card[0]) + minCards[i] = card[0] + } + } + } + //有超过两张的单牌小于其他玩家的牌,开始组对子 + if len(minCards) >= 2 { + //开始拆顺子 组对子 + if len(cardsTypeMap[Straight]) > 0 { + straightMap := cardsTypeMap[Straight] + for delKey, minCard := range minCards { + for straightKey, straight := range straightMap { + if len(straight) <= 3 { + continue + } + //先找最小的牌 + if Value(straight[0]) == Value(minCard) { + if cardsTypeMap[Twin] == nil { + cardsTypeMap[Twin] = map[int32][]int32{} + } + cardsTypeMap[Twin][int32(Value(minCard))] = append(cardsTypeMap[Twin][int32(Value(minCard))], minCard, straight[0]) + + cardsTypeMap[Straight][straightKey] = cardsTypeMap[Straight][straightKey][1:] + //删除单张 + delete(cardsTypeMap[Single], delKey) + break + } else if Value(straight[len(straight)-1]) == Value(minCard) { + if cardsTypeMap[Twin] == nil { + cardsTypeMap[Twin] = map[int32][]int32{} + } + cardsTypeMap[Twin][int32(Value(minCard))] = append(cardsTypeMap[Twin][int32(Value(minCard))], minCard, straight[len(straight)-1]) + cardsTypeMap[Straight][straightKey] = cardsTypeMap[Straight][straightKey][:len(straight)-1] + //删除单张 + delete(cardsTypeMap[Single], delKey) + break + } else { + //找中间 暂时不做 情况太复杂了 需要考虑的太多 + } + } + } + } + } + } + //} + if len(cardsTypeMap[Single]) == 0 { + delete(cardsTypeMap, Single) + } + if len(cardsTypeMap[Straight]) == 0 { + delete(cardsTypeMap, Straight) + } + } + return cardsTypeMap +} + +// 获取所有玩家剩余的牌型 +func GetAllOtherCardsType(data *PredictRequest, pos int32, isYuLe bool) map[int]map[int]map[int32][]int32 { + allOtherCardsType := map[int]map[int]map[int32][]int32{} + for i := 1; i < 4; i++ { + if i == int(pos) { + continue + } + //判断这个位置有没有剩余牌 + if i == 0 && len(data.Cards_left_0) > 0 { + cardsTypeMap0 := GetCardsType(data.Cards_left_0, isYuLe) + allOtherCardsType[i] = cardsTypeMap0 + } else if i == 1 && len(data.Cards_left_1) > 0 { + cardsTypeMap1 := GetCardsType(data.Cards_left_1, isYuLe) + allOtherCardsType[i] = cardsTypeMap1 + } else if i == 2 && len(data.Cards_left_2) > 0 { + cardsTypeMap2 := GetCardsType(data.Cards_left_2, isYuLe) + allOtherCardsType[i] = cardsTypeMap2 + } else if i == 3 && len(data.Cards_left_3) > 0 { + cardsTypeMap3 := GetCardsType(data.Cards_left_3, isYuLe) + allOtherCardsType[i] = cardsTypeMap3 + } + } + return allOtherCardsType +} + +// 判断能不能出顺子 +func IsOutStraight(data *PredictRequest, pos int32, isYuLe bool, mycards []int32) (bool, int) { + allOtherCardsType := GetAllOtherCardsType(data, pos, isYuLe) + copyMap := make(map[int]map[int]map[int32][]int32) + for k1, v1 := range allOtherCardsType { + copyMap[k1] = make(map[int]map[int32][]int32) + for k2, v2 := range v1 { + copyMap[k1][k2] = make(map[int32][]int32) + for k3, v3 := range v2 { + copyMap[k1][k2][k3] = make([]int32, len(v3)) + copy(copyMap[k1][k2][k3], v3) + } + } + } + + for i, OtherCardsType := range copyMap { + if len(OtherCardsType[Straight]) > 0 { + for _, otherStraight := range OtherCardsType[Straight] { + if len(otherStraight) >= len(mycards) { + if len(otherStraight)-len(mycards) <= 2 { + //说明要拆牌多出来2张,我可以出 + } + if Value(otherStraight[len(otherStraight)-1]) <= Value(mycards[len(mycards)-1]) { + //判断花色 + if Value(otherStraight[len(otherStraight)-1]) == Value(mycards[len(mycards)-1]) { + if Color(otherStraight[len(otherStraight)-1]) < Color(mycards[len(mycards)-1]) { + continue + } + } else { + continue + } + } + } + } + + //判断剩余的牌是不是全是顺子 + delete(OtherCardsType, Straight) + if len(OtherCardsType) <= 1 { + return false, i + } + } + } + return true, -1 +} + +type PredictRequest struct { + Bomb_num int `url:"bomb_num" form:"bomb_num"` + Card_play_action_seq string `url:"card_play_action_seq" form:"card_play_action_seq"` + Last_move_0 string `url:"last_move_0" form:"last_move_0"` + Last_move_1 string `url:"last_move_1" form:"last_move_1"` + Last_move_2 string `url:"last_move_2" form:"last_move_2"` + Last_move_3 string `url:"last_move_3" form:"last_move_3"` + Num_cards_left_0 int `url:"num_cards_left_0" form:"num_cards_left_0"` + Num_cards_left_1 int `url:"num_cards_left_1" form:"num_cards_left_1"` + Num_cards_left_2 int `url:"num_cards_left_2" form:"num_cards_left_2"` + Num_cards_left_3 int `url:"num_cards_left_3" form:"num_cards_left_3"` + Other_hand_cards string `url:"other_hand_cards" form:"other_hand_cards"` + Played_cards_0 string `url:"played_cards_0" form:"played_cards_0"` + Played_cards_1 string `url:"played_cards_1" form:"played_cards_1"` + Played_cards_2 string `url:"played_cards_2" form:"played_cards_2"` + Played_cards_3 string `url:"played_cards_3" form:"played_cards_3"` + Player_hand_cards string `url:"player_hand_cards" form:"player_hand_cards"` + Player_position int `url:"player_position" form:"player_position"` + IsTienLenYule bool `url:"isTienLenYule" form:"isTienLenYule"` + IsFirstHand bool `url:"isFirstHand" form:"isFirstHand"` + Cards_left_0 []int32 `url:"cards_left_0" form:"cards_left_0"` + Cards_left_1 []int32 `url:"cards_left_1" form:"cards_left_1"` + Cards_left_2 []int32 `url:"cards_left_2" form:"cards_left_2"` + Cards_left_3 []int32 `url:"cards_left_3" form:"cards_left_3"` + Last_pos int32 `url:"last_pos" form:"last_pos"` + IsEnd bool `url:"isend" form:"isend"` + WinSnids []int32 `url:"winsnids" form:"winsnids"` + IsWin bool `url:"iswin" form:"iswin"` +} +*/ diff --git a/gamerule/tienlen/constants.go b/gamerule/tienlen/constants.go new file mode 100644 index 0000000..24ff3bc --- /dev/null +++ b/gamerule/tienlen/constants.go @@ -0,0 +1,85 @@ +package tienlen + +import "time" + +//////////////////////////////////////////////////////////////////////////////// +//tienlen +//////////////////////////////////////////////////////////////////////////////// + +const ( + TestOpen bool = false //测试开关 + MaxNumOfPlayer int = 4 //最多人数 + HandCardNum int32 = 13 //手牌数量 + InvalideCard int32 = -1 //默认牌 + InvalidePos int32 = -1 + RobotGameTimesMin int32 = 5 //机器人参与游戏次数下限 + RobotGameTimesMax int32 = 5 //机器人参与游戏次数上限 + DelayCanOp int = 3 //根据上家牌型额外延迟下家出牌时间 + RankBaseScore int64 = 10 // 排位基础分 + RankBaseScoreToEnd int64 = 100 // 排位打到底基础分 + RankType = 1 // 排位类型,表示tienlen游戏 +) + +const ( + TienLenWaitPlayerTimeout = time.Second * 1 + TienLenWaitStartTimeout = time.Second * 10 //人数够开启游戏, 延迟X秒开始游戏 + TienLenHandCardTimeout = time.Second * 3 //发牌 + TienLenPlayerOpTimeout = time.Second * 15 //出牌(玩家操作阶段) + TienLenBilledTimeout = time.Second * 5 //结算 + TIenLenTianhuTimeout = time.Second * 2 // 天胡动画时长 + TienLenHandNotExceedTimeLimit = time.Second * 3 //玩家没有牌可以接上家的牌,出牌时间上限 + TienLenHandAutoStateTimeOut = time.Second * 1 //玩家托管出牌时间上限 +) + +// 场景状态 +const ( + TienLenSceneStateWaitPlayer int = iota //0 人数不够开启游戏,等待玩家上线 + TienLenSceneStateWaitStart //1 人数够开启游戏, 延迟X秒开始游戏 + TienLenSceneStateHandCard //2 发牌 + TienLenSceneStatePlayerOp //3 出牌(玩家操作阶段) + TienLenSceneStateBilled //4 结算 + TienLenSceneStateMax +) + +// 玩家操作 +const ( + TienLenPlayerOpNull int32 = iota //0,初始值 + TienLenPlayerOpPlay //1,出牌 + TienLenPlayerOpPass //2,过牌 + TienLenPlayerOpStart //3,房主开始游戏 + TienLenPlayerOpTest //4,调试发牌,发指定的牌 + TienLenPlayerOpAutoHandCard //5,托管状态下出牌 + TienLenPlayerOpCanelAuto //6,取消托管状态 + TienLenPlayerClientHintCards //7,客户端提示出牌 +) + +type ThingLongTime struct { + Min int32 + Max int32 + Time time.Duration +} + +var Think_Long_Sct_MAP = map[int]int{ + 1: 10, + 2: 10, + 3: 7, + 4: 7, +} + +var ( + ThinkLongSct = []ThingLongTime{ + ThingLongTime{0, 1, 15 * time.Second}, + ThingLongTime{2, 3, 10 * time.Second}, + ThingLongTime{4, 999999, 7 * time.Second}, + } +) + +const ( + TianHu2Four = 1 // 4个2 + TianHu6StraightTwin = 2 // 6连对 + TianHu12Straight = 3 // 12顺 +) + +const ( + StaticsTianHuTimes = "tienlen_tianhu_times" +) diff --git a/gamerule/tienlen/poker.go b/gamerule/tienlen/poker.go new file mode 100644 index 0000000..2c11101 --- /dev/null +++ b/gamerule/tienlen/poker.go @@ -0,0 +1,651 @@ +package tienlen + +import ( + "math/rand" + "sort" + "time" +) + +//牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 +//红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +const ( + POKER_CNT = 52 + PER_CARD_COLOR_MAX = 13 + Hand_CardNum = 13 //手牌 + Card_Value_2 = 12 //特殊牌值2 + Player_Num_Max = 4 //桌上最大人數 + HongTao2 = 51 + FangPian2 = 38 + MeiHua2 = 25 + HeiTao2 = 12 +) + +// 不同牌型分值 +const ( + hei2 = 0 + fang2 = 3 + hong2 = 5 + zhadan = 20 + liandui5 = 21 + liandui4 = 18 + liandui3 = 15 + shunzi8 = 15 + shunzi7 = 10 + shunzi6 = 5 + sanzhang = 3 +) + +const ( + yl_hei2 = 0 + yl_fang2 = 3 + yl_hong2 = 5 + yl_zhadan = 30 + yl_liandui5 = 21 + yl_liandui4 = 18 + yl_liandui3 = 15 + yl_shunzi8 = 15 + yl_shunzi7 = 10 + yl_shunzi6 = 5 + yl_sanzhang = 15 +) + +type Card int + +type Poker struct { + buf [POKER_CNT]Card + copyBuf [POKER_CNT]Card + tianhuBuf [POKER_CNT]Card + isTianhu bool +} + +func (this *Poker) GetPokerBuf() [POKER_CNT]Card { + return this.buf +} + +func (this *Poker) CopyPokerBuf() { + for i := 0; i < len(this.buf); i++ { + this.copyBuf[i] = this.buf[i] + } +} + +func (this *Poker) UncopyPokerBuf() { + for i := 0; i < len(this.copyBuf); i++ { + this.buf[i] = this.copyBuf[i] + this.copyBuf[i] = Card(i) + } +} + +func (this *Poker) TianhuPokerBuf() { + for i := 0; i < len(this.buf); i++ { + this.tianhuBuf[i] = this.buf[i] + } + this.isTianhu = true +} + +func (this *Poker) UnTianhuPokerBuf() { + for i := 0; i < len(this.tianhuBuf); i++ { + this.buf[i] = this.tianhuBuf[i] + this.tianhuBuf[i] = Card(i) + } + this.isTianhu = false +} + +func (this *Poker) IsTianhuPoker() bool { + return this.isTianhu +} + +func NewPoker() *Poker { + p := &Poker{} + p.init() + return p +} + +func (this *Poker) init() { + for i := int32(0); i < POKER_CNT; i++ { + this.buf[i] = Card(i) + } + rand.Seed(time.Now().UnixNano()) + this.Shuffle() +} + +func (this *Poker) Shuffle() { + for i := int32(0); i < POKER_CNT; i++ { + j := rand.Intn(int(i) + 1) + this.buf[i], this.buf[j] = this.buf[j], this.buf[i] + } +} + +// GetCardsGrade 给手牌打分 +func GetCardsGrade(cards []int32, yl bool) int { + grade := 0 + cpCards := []int32{} + for _, card := range cards { + if card != InvalideCard { + cpCards = append(cpCards, card) + grade += Value(card) + 1 // 基础分 + } + } + + if Have2FourBomb(cpCards) || Have6StraightTwin(cpCards) || Have12Straight(cpCards) { + return 9999 + } + + //找单2 + for i := 0; i < len(cpCards); i++ { + card := cpCards[i] + switch card { + case HongTao2: + if yl { + grade += yl_hong2 + } else { + grade += hong2 + } + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + case FangPian2: + if yl { + grade += yl_fang2 + } else { + grade += fang2 + } + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + case MeiHua2, HeiTao2: + if yl { + grade += yl_hei2 + } else { + grade += hei2 + } + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + } + } + + // 找炸弹 + if len(cpCards) >= 4 { + mapBomb := make(map[int32]int) + for _, card := range cpCards { + mapBomb[int32(Value(card))]++ + } + bombValue := []int32{} + for card, num := range mapBomb { + if num == 4 { + bombValue = append(bombValue, card) + } + } + if len(bombValue) > 0 { + findCards := []int32{} + for _, card := range bombValue { + for i := int32(0); i < 4; i++ { + findCards = append(findCards, card+(i*PER_CARD_COLOR_MAX)) + cpCards = DelSliceInt32(cpCards, card+(i*PER_CARD_COLOR_MAX)) + } + } + if yl { + grade += len(bombValue) * yl_zhadan + } else { + grade += len(bombValue) * zhadan + } + } + } + + //找五连 + if len(cpCards) >= 10 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map5STwin := make(map[int32]int32) + for _, card := range cpCards { + map5STwin[int32(Value(card))]++ + } + Value5STwin := []int32{} + for card, num := range map5STwin { + if num >= 2 { + Value5STwin = append(Value5STwin, card) + } + } + sort.Slice(Value5STwin, func(i, j int) bool { + if Value5STwin[i] > Value5STwin[j] { + return false + } + return true + }) + if len(Value5STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(5, Value5STwin) + if len(tmpPairs) > 0 { + findCards := []int32{} + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + findCards = append(findCards, card) + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + if yl { + grade += yl_liandui5 + } else { + grade += liandui5 + } + } + } + } + + //找四连 + if len(cpCards) >= 8 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map4STwin := make(map[int32]int32) + for _, card := range cpCards { + map4STwin[int32(Value(card))]++ + } + Value4STwin := []int32{} + for card, num := range map4STwin { + if num >= 2 { + Value4STwin = append(Value4STwin, card) + } + } + sort.Slice(Value4STwin, func(i, j int) bool { + if Value4STwin[i] > Value4STwin[j] { + return false + } + return true + }) + if len(Value4STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(4, Value4STwin) + if len(tmpPairs) > 0 { + findCards := []int32{} + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] != InvalideCard { + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + findCards = append(findCards, card) + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + if yl { + grade += yl_liandui4 + } else { + grade += liandui4 + } + } + } + } + + //找三连 + if len(cpCards) >= 6 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map3STwin := make(map[int32]int32) + for _, card := range cpCards { + map3STwin[int32(Value(card))]++ + } + Value3STwin := []int32{} + for card, num := range map3STwin { + if num >= 2 { + Value3STwin = append(Value3STwin, card) + } + } + sort.Slice(Value3STwin, func(i, j int) bool { + if Value3STwin[i] > Value3STwin[j] { + return false + } + return true + }) + if len(Value3STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(3, Value3STwin) + if len(tmpPairs) > 0 { + findCards := []int32{} + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + findCards = append(findCards, card) + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + if yl { + grade += yl_liandui3 + } else { + grade += liandui3 + } + } + } + } + + //再找三连 + if len(cpCards) >= 6 { + sort.Slice(cpCards, func(i, j int) bool { + if cpCards[i] > cpCards[j] { + return false + } + return true + }) + map3STwin := make(map[int32]int32) + for _, card := range cpCards { + map3STwin[int32(Value(card))]++ + } + Value3STwin := []int32{} + for card, num := range map3STwin { + if num >= 2 { + Value3STwin = append(Value3STwin, card) + } + } + sort.Slice(Value3STwin, func(i, j int) bool { + if Value3STwin[i] > Value3STwin[j] { + return false + } + return true + }) + if len(Value3STwin) > 0 { + tmpPairs := FindOneStraightWithWidth(3, Value3STwin) + if len(tmpPairs) > 0 { + findCards := []int32{} + for _, card := range tmpPairs { + delC := 0 + for i := 0; i < len(cpCards); i++ { + if cpCards[i] == InvalideCard { + continue + } + if int32(Value(cpCards[i])) == card && (delC < 2) { //删除2次 + findCards = append(findCards, card) + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + if yl { + grade += yl_liandui3 + } else { + grade += liandui3 + } + } + } + } + //找8顺子 + if len(cpCards) >= 8 { + del, tmpCards, _ := delStraight(cpCards, 8) + if del { + if yl { + grade += yl_shunzi8 + } else { + grade += shunzi8 + } + cpCards = tmpCards + } + } + // 找三张 + if len(cpCards) >= 3 { + mapTriple := make(map[int32]int) + for _, card := range cpCards { + mapTriple[int32(Value(card))]++ + } + tripleValues := []int32{} + for card, num := range mapTriple { + if num == 3 { + tripleValues = append(tripleValues, card) + } + } + if len(tripleValues) > 0 { + findCards := []int32{} + for _, value := range tripleValues { + delC := 0 + for i := 0; i < len(cpCards); i++ { + card := cpCards[i] + if card != InvalideCard { + if Value(card) == int(value) && (delC < 3) { //删除3次 + findCards = append(findCards, card) + delC++ + cpCards = append(cpCards[:i], cpCards[i+1:]...) + i-- + continue + } + } + } + } + if yl { + grade += len(tripleValues) * yl_sanzhang + } else { + grade += len(tripleValues) * sanzhang + } + } + } + //找7顺子 + if len(cpCards) >= 7 { + del, tmpCards, _ := delStraight(cpCards, 7) + if del { + if yl { + grade += yl_shunzi7 + } else { + grade += shunzi7 + } + cpCards = tmpCards + } + } + //找6顺子 + if len(cpCards) >= 6 { + del, tmpCards, _ := delStraight(cpCards, 6) + if del { + if yl { + grade += yl_shunzi6 + } else { + grade += shunzi6 + } + cpCards = tmpCards + } + } + //再找6顺子 + if len(cpCards) >= 6 { + del, tmpCards, _ := delStraight(cpCards, 6) + if del { + if yl { + grade += yl_shunzi6 + } else { + grade += shunzi6 + } + cpCards = tmpCards + } + } + return grade +} + +// 发顺子(不包括2 最少3顺子) +func delStraight(cards []int32, value int) (bool, []int32, []int32) { + haveNeed := false + needCards := []int32{} + + if cards == nil || len(cards) < value { + return haveNeed, cards, needCards + } + + mapStraights := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapStraights[int32(Value(card))]++ + } + } + valueStraights := []int32{} + for cardValue, _ := range mapStraights { + valueStraights = append(valueStraights, cardValue) + } + if len(valueStraights) > 0 { + sort.Slice(valueStraights, func(i, j int) bool { + if valueStraights[i] > valueStraights[j] { + return false + } + return true + }) + straights := FindStraightWithWidth(value, valueStraights) + if len(straights) > 0 { + randStraight := straights[rand.Intn(len(straights))] //随机一条顺子 + for _, cardValue := range randStraight { + delC := 0 + for i := 0; i < len(cards); i++ { + card := cards[i] + if card == InvalideCard { + continue + } + if int32(Value(card)) == cardValue && (delC < 1) { //删除1次 + delC++ + needCards = append(needCards, card) + cards = append(cards[:i], cards[i+1:]...) + i-- + continue + } + } + } + haveNeed = true + } + } + + return haveNeed, cards, needCards +} + +// 发连对(不包括2 最少2顺子) +func delStraightTwin(cards []int32, value int) (bool, []int32, []int32) { + haveNeed := false + needCards := []int32{} + + if cards == nil || len(cards) < value { + return haveNeed, cards, needCards + } + + mapStraights := make(map[int32]int32) + for _, card := range cards { + if card != InvalideCard && Value(card) != Card_Value_2 { + mapStraights[int32(Value(card))]++ + } + } + valueStraights := []int32{} + for cardValue, count := range mapStraights { + if count >= 2 /*&& !common.InSliceInt32(valueStraights, cardValue)*/ { + valueStraights = append(valueStraights, cardValue) + } + } + if len(valueStraights) > 0 { + sort.Slice(valueStraights, func(i, j int) bool { + if valueStraights[i] > valueStraights[j] { + return false + } + return true + }) + straights := FindStraightWithWidth(value, valueStraights) + if len(straights) > 0 { + randStraight := straights[rand.Intn(len(straights))] //随机一条顺子 + for _, cardValue := range randStraight { + delC := 0 + for i := 0; i < len(cards); i++ { + card := cards[i] + if card == InvalideCard { + continue + } + if int32(Value(card)) == cardValue && (delC < 2) { //删除2次 + delC++ + needCards = append(needCards, card) + cards = append(cards[:i], cards[i+1:]...) + i-- + continue + } + } + } + haveNeed = true + } + } + + return haveNeed, cards, needCards +} + +func SendHandCard(poker *Poker) []int { + poker.Shuffle() + buf := poker.GetPokerBuf() + + cardss := map[int][]int32{} + for i, card := range buf { + if int32(card) != InvalideCard { + index := i / 13 + cardss[index] = append(cardss[index], int32(card)) + } + } + + type gradeInfo struct { + id int + grade int + cards []int32 + } + grades := []gradeInfo{} + for i, card13 := range cardss { + cardTmp := make([]int32, 13) + copy(cardTmp, card13) + grade := GetCardsGrade(cardTmp, false) + gi := gradeInfo{ + id: i, + grade: grade, + cards: card13, + } + grades = append(grades, gi) + } + + sort.Slice(grades, func(i, j int) bool { + return grades[i].grade > grades[j].grade + }) + + //fmt.Println("grades: ", grades) + + sortGrades := []int{} + for _, grade := range grades { + sortGrades = append(sortGrades, grade.grade) + } + return sortGrades +} + +func MinSortCards(cs []int32) { + sort.Slice(cs, func(i, j int) bool { + vI := Value(cs[i]) + vJ := Value(cs[j]) + cI := Color(cs[i]) + cJ := Color(cs[j]) + if vI > vJ { + return false + } else if vI == vJ { + return cI < cJ + } + return true + }) +} diff --git a/gamerule/tienlen/poker_test.go b/gamerule/tienlen/poker_test.go new file mode 100644 index 0000000..1aff398 --- /dev/null +++ b/gamerule/tienlen/poker_test.go @@ -0,0 +1,95 @@ +package tienlen + +import ( + "fmt" + "math/rand" + "sort" + "testing" +) + +//牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 +//红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 +//方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 +//梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 +//黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +func TestPoker(t *testing.T) { + poker := NewPoker() + + gradess := [][]int{} + gradess1 := []int{} + gradess2 := []int{} + gradess3 := []int{} + gradess4 := []int{} + + for i := 0; i < 1000; i++ { + grade4s := SendHandCard(poker) + //fmt.Println("局数: ", i+1) + //for _, grade := range grades { + // fmt.Println(" grade: ", grade) + //} + gradess = append(gradess, grade4s) + } + + fmt.Println("gradess: ", gradess) + for _, grades := range gradess { + for i, grade := range grades { + if i == 0 { + gradess1 = append(gradess1, grade) + } + if i == 1 { + gradess2 = append(gradess2, grade) + } + if i == 2 { + gradess3 = append(gradess3, grade) + } + if i == 3 { + gradess4 = append(gradess4, grade) + } + } + } + fmt.Println("==========================1") + for _, g1 := range gradess1 { + fmt.Println(g1) + } + fmt.Println("==========================2") + for _, g2 := range gradess2 { + fmt.Println(g2) + } + fmt.Println("==========================3") + for _, g3 := range gradess3 { + fmt.Println(g3) + } + fmt.Println("==========================4") + for _, g4 := range gradess4 { + fmt.Println(g4) + } +} + +func TestGetCardsGrade(t *testing.T) { + var n int + for i := 0; i < 100; i++ { + var score []int + var allCards []int32 + for i := 0; i < POKER_CNT; i++ { + allCards = append(allCards, int32(i)) + } + rand.Shuffle(len(allCards), func(i, j int) { + allCards[i], allCards[j] = allCards[j], allCards[i] + }) + for i := 0; i < 4; i++ { + ok := Have6StraightTwin(allCards[:13]) || Have12Straight(allCards[:13]) || Have2FourBomb(allCards[:13]) + if ok { + t.Log("ttttttttttttttttttttttttttttttttttt") + } + score = append(score, GetCardsGrade(allCards[:13], false)) + allCards = allCards[13:] + } + sort.Ints(score) + fmt.Println(score) + if score[len(score)-1]-score[0] > 30 && score[len(score)-2]-score[0] > 30 { + n++ + } + } + fmt.Println(n) +} diff --git a/gamesrv/FishLogger.xml b/gamesrv/FishLogger.xml new file mode 100644 index 0000000..d58d21c --- /dev/null +++ b/gamesrv/FishLogger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gamesrv/action/action_game.go b/gamesrv/action/action_game.go new file mode 100644 index 0000000..76c9900 --- /dev/null +++ b/gamesrv/action/action_game.go @@ -0,0 +1,324 @@ +package action + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/srvdata" +) + +type CSDestroyRoomPacketFactory struct { +} +type CSDestroyRoomHandler struct { +} + +func (this *CSDestroyRoomPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSDestroyRoom{} + return pack +} + +func (this *CSDestroyRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSDestroyRoomHandler Process recv ", data) + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSDestroyRoomHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSDestroyRoomHandler p.GetScene() == nil") + return nil + } + if !scene.HasPlayer(p) { + return nil + } + if scene.Creator != p.SnId { + logger.Logger.Warn("CSDestroyRoomHandler s.creator != p.AccountId") + return nil + } + scene.Destroy(true) + return nil +} + +type CSLeaveRoomPacketFactory struct { +} +type CSLeaveRoomHandler struct { +} + +func (this *CSLeaveRoomPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSLeaveRoom{} + return pack +} + +func (this *CSLeaveRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSLeaveRoomHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSLeaveRoom); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSLeaveRoomHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warnf("CSLeaveRoomHandler[%v] p.GetScene() == nil", p.SnId) + pack := &gamehall.SCLeaveRoom{ + OpRetCode: gamehall.OpResultCode_Game_OPRC_RoomNotExit, + Mode: msg.Mode, + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + return nil + } + if msg.GetMode() == 0 && !scene.CanChangeCoinScene(p) { + logger.Logger.Warnf("CSLeaveRoomHandler[%v][%v] scene.gaming==true", scene.SceneId, p.SnId) + pack := &gamehall.SCLeaveRoom{ + OpRetCode: gamehall.OpResultCode_Game_OPRC_YourAreGamingCannotLeave_Game, + RoomId: proto.Int(scene.SceneId), + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + return nil + } + if msg.GetMode() == 0 { // 0退出 1暂离 + if scene.HasAudience(p) { + scene.AudienceLeave(p, common.PlayerLeaveReason_Normal) + } else if scene.HasPlayer(p) { + scene.PlayerLeave(p, common.PlayerLeaveReason_Normal, false) + } + } else { + pack := &gamehall.SCLeaveRoom{ + Reason: proto.Int(0), + OpRetCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + Mode: msg.Mode, + RoomId: proto.Int(scene.SceneId), + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + scene.PlayerDropLine(p.SnId) + + //标记上暂离状态 + p.ActiveLeave = true + p.MarkFlag(base.PlayerState_Online) + p.MarkFlag(base.PlayerState_Leave) + p.SyncFlag() + } + } + return nil +} + +type CSAudienceLeaveRoomHandler struct { +} + +func (this *CSAudienceLeaveRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSAudienceLeaveRoomHandler Process recv ", data) + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSAudienceLeaveRoomHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSAudienceLeaveRoomHandler p.GetScene() == nil") + return nil + } + if !scene.HasAudience(p) { + return nil + } + scene.AudienceLeave(p, common.PlayerLeaveReason_Normal) + return nil +} + +type CSForceStartPacketFactory struct { +} +type CSForceStartHandler struct { +} + +func (this *CSForceStartPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSForceStart{} + return pack +} + +func (this *CSForceStartHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSForceStartHandler Process recv ", data) + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSForceStartHandler p == nil") + return nil + } + if p.GetScene() == nil { + logger.Logger.Warn("CSForceStartHandler p.GetScene() == nil") + return nil + } + if p.Pos != 0 /*p.GetScene().creator != p.SnId*/ { //第1个进房间的玩家 + logger.Logger.Warn("CSForceStartHandler p.GetScene().creator != p.SnId") + return nil + } + if p.GetScene().Gaming { + logger.Logger.Warn("CSForceStartHandler p.GetScene().gaming==true") + return nil + } + if !p.GetScene().GetScenePolicy().IsCanForceStart(p.GetScene()) { + logger.Logger.Warn("CSForceStartHandler !p.GetScene().sp.IsCanForceStart(p.GetScene())") + return nil + } + //强制开始 + p.GetScene().GetScenePolicy().ForceStart(p.GetScene()) + + p.GetScene().NotifySceneRoundStart(1) + + packClient := &gamehall.SCForceStart{} + proto.SetDefaults(packClient) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_FORCESTART), packClient) + return nil +} + +//type CSPlayerSwitchFlagPacketFactory struct { +//} +//type CSPlayerSwitchFlagHandler struct { +//} +// +//func (this *CSPlayerSwitchFlagPacketFactory) CreatePacket() interface{} { +// pack := &gamehall.CSPlayerSwithFlag{} +// return pack +//} +// +//func (this *CSPlayerSwitchFlagHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSPlayerSwitchFlagHandler Process recv ", data) +// if msg, ok := data.(*gamehall.CSPlayerSwithFlag); ok { +// p := base.PlayerMgrSington.GetPlayer(sid) +// if p == nil { +// logger.Logger.Warn("CSPlayerSwitchFlagHandler p == nil") +// return nil +// } +// flag := int(msg.GetFlag()) +// //flag = 1 << uint(flag) +// //if msg.Mark != nil { +// logger.Logger.Trace("CSPlayerSwitchFlagHandler Process recv SnId(%v) Mark is %v", p.SnId, msg.GetMark()) +// if msg.GetMark() == 0 { //0取消状态 1设置状态 +// oldFlag := p.GetFlag() +// if p.IsMarkFlag(flag) { +// p.UnmarkFlag(flag) +// } +// if flag == base.PlayerState_Leave { +// if p.GetScene() != nil { +// //重置下房间状态 +// p.GetScene().PlayerReturn(p, true) +// } +// } +// if oldFlag != p.GetFlag() { +// p.SyncFlag() +// } +// } else { //设置状态 +// if flag == base.PlayerState_Leave { +// p.ActiveLeave = false //被动暂离 +// if p.GetScene() != nil { +// //todo dev fish 字游戏暂时被删除了 所以这里先注释掉 +// //if p.GetScene().gameId == common.GameId_HFishing || p.GetScene().gameId == common.GameId_LFishing || +// // p.GetScene().gameId == common.GameId_RFishing || p.GetScene().gameId == common.GameId_DFishing || +// // p.GetScene().gameId == common.GameId_NFishing || p.GetScene().gameId == common.GameId_TFishing { +// // p.GetScene().sp.OnPlayerOp(p.GetScene(), p, FishingPlayerOpLeave, []int64{}) +// //} +// } +// } +// if !p.IsMarkFlag(flag) { +// p.MarkFlag(flag) +// p.SyncFlag() +// } +// } +// //} +// } +// return nil +//} + +func CSRoomEvent(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSRoomEvent %v", data) + msg, ok := data.(*gamehall.CSRoomEvent) + if !ok { + return nil + } + + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + scene := p.GetScene() + if scene == nil { + return nil + } + + if !scene.HasPlayer(p) { + return nil + } + + pack := &gamehall.SCRoomEvent{ + OpCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + Tp: msg.GetTp(), + Pos: int32(p.GetPos()), + Content: msg.GetContent(), + Param: msg.GetParam(), + Ts: time.Now().Unix(), + } + + switch msg.GetTp() { + case 1: // 普通消息 + + case 2: // 互动表情 + var totalCoin int64 + for _, v := range msg.GetParam() { + item := srvdata.PBDB_GameItemMgr.GetData(v) + switch item.GetType() { + case common.ItemTypeInteract: + totalCoin += item.GetNum() + } + } + if p.GetCoin() >= totalCoin { + for _, v := range msg.GetParam() { + item := srvdata.PBDB_GameItemMgr.GetData(v) + if item.GetNum() > 0 { + p.AddCoin(-item.GetNum(), common.GainWay_Interact, base.SyncFlag_ToClient|base.SyncFlag_Broadcast, "system", "互动表情") + } + } + } else { + pack.OpCode = gamehall.OpResultCode_Game_OPRC_CoinNotEnough_Game + } + + default: + return nil + } + + scene.Broadcast(int(gamehall.GameHallPacketID_PACKET_SCRoomEvent), pack, 0) + logger.Logger.Tracef("SCRoomEvent %v", pack) + return nil +} + +func init() { + // 房间创建者解散房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_DESTROYROOM), &CSDestroyRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_DESTROYROOM), &CSDestroyRoomPacketFactory{}) + + // 离开或暂离房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_LEAVEROOM), &CSLeaveRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_LEAVEROOM), &CSLeaveRoomPacketFactory{}) + + // 观众离开房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCE_LEAVEROOM), &CSAudienceLeaveRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCE_LEAVEROOM), &CSLeaveRoomPacketFactory{}) + + // 第一个进房间的玩家强制开始游戏 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_FORCESTART), &CSForceStartHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_FORCESTART), &CSForceStartPacketFactory{}) + + // 同步玩家状态 + //common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_PLAYER_SWITCHFLAG), &CSPlayerSwitchFlagHandler{}) + //netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_PLAYER_SWITCHFLAG), &CSPlayerSwitchFlagPacketFactory{}) + + // 房间事件 + common.Register(int(gamehall.GameHallPacketID_PACKET_CSRoomEvent), gamehall.CSRoomEvent{}, CSRoomEvent) +} diff --git a/gamesrv/action/action_server.go b/gamesrv/action/action_server.go new file mode 100644 index 0000000..40f0893 --- /dev/null +++ b/gamesrv/action/action_server.go @@ -0,0 +1,968 @@ +package action + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + //创建场景 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_CREATESCENE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGCreateScene{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_CREATESCENE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGCreateScene:", pack) + if msg, ok := pack.(*server.WGCreateScene); ok { + sceneId := int(msg.GetSceneId()) + gameMode := int(msg.GetGameMode()) + sceneMode := int(msg.GetSceneMode()) + gameId := int(msg.GetGameId()) + paramsEx := msg.GetParamsEx() + hallId := msg.GetHallId() + groupId := msg.GetGroupId() + dbGameFree := msg.GetDBGameFree() + bEnterAfterStart := msg.GetEnterAfterStart() + totalOfGames := msg.GetTotalOfGames() + baseScore := msg.GetBaseScore() + playerNum := int(msg.GetPlayerNum()) + scene := base.SceneMgrSington.CreateScene(s, sceneId, gameMode, sceneMode, gameId, msg.GetPlatform(), msg.GetParams(), + msg.GetAgentor(), msg.GetCreator(), msg.GetReplayCode(), hallId, groupId, totalOfGames, dbGameFree, + bEnterAfterStart, baseScore, playerNum, msg.GetChessRank(), paramsEx...) + if scene != nil { + if scene.IsMatchScene() { + if len(scene.Params) > 0 { + scene.MatchId = scene.Params[0] + } + if len(scene.Params) > 1 { + scene.MatchFinals = scene.Params[1] == 1 + } + if len(scene.Params) > 2 { + scene.MatchRound = scene.Params[2] + } + if len(scene.Params) > 3 { + scene.MatchCurPlayerNum = scene.Params[3] + } + if len(scene.Params) > 4 { + scene.MatchNextNeed = scene.Params[4] + } + if len(scene.Params) > 5 { + scene.MatchType = scene.Params[5] + } + } + scene.ClubId = msg.GetClub() + scene.RoomId = msg.GetClubRoomId() + scene.RoomPos = msg.GetClubRoomPos() + scene.PumpCoin = msg.GetClubRate() + scene.RealCtrl = msg.RealCtrl + } + } + return nil + })) + + //删除场景 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_DESTROYSCENE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGDestroyScene{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_DESTROYSCENE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGDestroyScene:", pack) + if msg, ok := pack.(*server.WGDestroyScene); ok { + sceneId := int(msg.GetSceneId()) + s := base.SceneMgrSington.GetScene(sceneId) + if s != nil { + if gameScene, ok := s.ExtraData.(base.GameScene); ok { + gameScene.SceneDestroy(true) + } + } + } + return nil + })) + + //删除场景 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGGraceDestroyScene{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGGraceDestroyScene:", pack) + if msg, ok := pack.(*server.WGGraceDestroyScene); ok { + ids := msg.GetIds() + for _, id := range ids { + s := base.SceneMgrSington.GetScene(int(id)) + if s != nil { + if s.IsHundredScene() || s.Gaming { + s.SetGraceDestroy(true) + } else { + if s.IsMatchScene() { + s.SetGraceDestroy(true) + } + if gameScene, ok := s.ExtraData.(base.GameScene); ok { + gameScene.SceneDestroy(true) + } + } + } + } + } + return nil + })) + //玩家进入 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PLAYERENTER), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerEnter{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PLAYERENTER), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerEnter") + if msg, ok := pack.(*server.WGPlayerEnter); ok { + sceneId := int(msg.GetSceneId()) + sid := msg.GetSid() + data := msg.GetPlayerData() + gateSid := msg.GetGateSid() + isload := msg.GetIsLoaded() + IsQM := msg.GetIsQM() + sendLeave := func(reason int) { + pack := &server.GWPlayerLeave{ + RoomId: msg.SceneId, + PlayerId: msg.SnId, + ReturnCoin: msg.TakeCoin, + Reason: proto.Int(reason), + } + proto.SetDefaults(pack) + s.Send(int(server.SSPacketID_PACKET_GW_PLAYERLEAVE), pack) + } + scene := base.SceneMgrSington.GetScene(sceneId) + p := base.NewPlayer(sid, data, nil, nil) + if p == nil && !scene.IsMatchScene() { + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + p.UnmarshalIParam(msg.GetIParams()) + p.UnmarshalSParam(msg.GetSParams()) + p.UnmarshalCParam(msg.GetCParams()) + p.AgentCode = msg.GetAgentCode() + p.Coin = msg.GetTakeCoin() + p.Pos = int(msg.GetPos()) + p.MatchParams = msg.GetMatchParams() + for id, num := range msg.Items { + p.Items[id] = num + } + for k, v := range msg.RankScore { + p.RankScore[k] = v + } + p.SetTakeCoin(msg.GetTakeCoin()) + //p.StartCoin = msg.GetTakeCoin() + //机器人用 + p.ExpectGameTime = msg.GetExpectGameTimes() + p.ExpectLeaveCoin = msg.GetExpectLeaveCoin() + //当局游戏结束后剩余金额 起始设置 + p.SetCurrentCoin(msg.GetTakeCoin()) + + p.LastSyncCoin = p.Coin + p.IsQM = IsQM + + if sid == 0 && (scene != nil && !scene.IsMatchScene()) { + logger.Logger.Warnf("when WGPlayerEnter (sid == 0)") + //进入房间失败 + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + + if scene == nil { + logger.Logger.Warn("when WGPlayerEnter (scene == nil)") + //进入房间失败 + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + + isQuit := p.GetIParam(common.PlayerIParam_IsQuit) + logger.Logger.Tracef("WGPlayerEnter scene.IsMatchScene()=%v p.GetIParam(common.PlayerIParam_IsQuit)=%v", scene.IsMatchScene(), isQuit) + if scene.IsMatchScene() && isQuit == 1 { //比赛场退赛 + p.MarkFlag(base.PlayerState_MatchQuit) + p.MarkFlag(base.PlayerState_Auto) + p.MarkFlag(base.PlayerState_Leave) + } + + var sessionId srvlib.SessionId + sessionId.Set(gateSid) + gateSess := srvlib.ServerSessionMgrSington.GetSession(int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + logger.Logger.Tracef("WGPlayerEnter, AreaId=%v, SrvType=%v, SrvId=%v, GateSess=%v", int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId()), gateSess) + if gateSess == nil && !scene.IsMatchScene() { + logger.Logger.Warnf("WGPlayerEnter, AreaId=%v, SrvType=%v, SrvId=%v, GateSess=", int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + //进入房间失败 + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + + p.SetGateSess(gateSess) + p.SetWorldSess(s) + + if gateSess != nil { + pack := &server.GGPlayerSessionBind{ + Sid: proto.Int64(sid), + } + if !p.IsRob { + pack.SnId = proto.Int32(p.SnId) + pack.Vip = proto.Int32(p.VIP) + pack.CoinPayTotal = proto.Int64(p.CoinPayTotal) + pack.Ip = proto.String(p.Ip) + pack.Platform = proto.String(p.Platform) + } + proto.SetDefaults(pack) + gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONBIND), pack) + } + + if scene.Testing { + p.Coin = int64(scene.DbGameFree.GetTestTakeCoin()) + } + base.PlayerMgrSington.ManagePlayer(p) + scene.PlayerEnter(p, isload) + //进场失败 + if p.IsMarkFlag(base.PlayerState_EnterSceneFailed) { + scene.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + } else { + // 进入成功 + if !p.IsRobot() && !scene.Testing && !scene.IsMatchScene() { + //base.LogChannelSingleton.WriteMQData(model.GenerateEnterEvent(scene.GetRecordId(), p.SnId, p.Platform, + // p.DeviceOS, scene.GameId, scene.GameMode, scene.GetGameFreeId())) + } + } + } + return nil + })) + + //观众进入 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_AUDIENCEENTER), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerEnter{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_AUDIENCEENTER), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive PACKET_WG_AUDIENCEENTER WGPlayerEnter") + if msg, ok := pack.(*server.WGPlayerEnter); ok { + sceneId := int(msg.GetSceneId()) + sid := msg.GetSid() + data := msg.GetPlayerData() + gateSid := msg.GetGateSid() + isload := msg.GetIsLoaded() + IsQM := msg.GetIsQM() + var sessionId srvlib.SessionId + sessionId.Set(gateSid) + sendLeave := func(reason int) { + pack := &server.GWPlayerLeave{ + RoomId: msg.SceneId, + PlayerId: msg.SnId, + ReturnCoin: msg.TakeCoin, + Reason: proto.Int(reason), + } + proto.SetDefaults(pack) + s.Send(int(server.SSPacketID_PACKET_GW_AUDIENCELEAVE), pack) + } + scene := base.SceneMgrSington.GetScene(sceneId) + if scene == nil || sid == 0 { + if sid == 0 { + logger.Logger.Warnf("when WGAUPlayerEnter (sid == 0)") + } + + //进入房间失败 + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + + gateSess := srvlib.ServerSessionMgrSington.GetSession(int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + logger.Logger.Tracef("PACKET_WG_AUDIENCEENTER WGPlayerEnter, AreaId=%v, SrvType=%v, SrvId=%v, GateSess=%v", int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId()), gateSess) + if gateSess != nil { + pack := &server.GGPlayerSessionBind{ + Sid: proto.Int64(sid), + } + proto.SetDefaults(pack) + gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONBIND), pack) + } else { + //进入房间失败 + logger.Logger.Warnf("PACKET_WG_AUDIENCEENTER WGPlayerEnter, AreaId=%v, SrvType=%v, SrvId=%v, GateSess=", int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + + // 自建房检查观众人数上限 + if scene.IsPreCreateScene() { + if len(scene.GetAudiences()) >= model.GameParamData.MaxAudienceNum { + sendLeave(common.PlayerLeaveReason_RoomFull) + return nil + } + } + + p := base.PlayerMgrSington.AddPlayer(sid, data, s, gateSess) + if p == nil { + //进入房间失败 + sendLeave(common.PlayerLeaveReason_OnDestroy) + return nil + } + p.UnmarshalIParam(msg.GetIParams()) + p.UnmarshalSParam(msg.GetSParams()) + p.UnmarshalCParam(msg.GetCParams()) + p.Coin = msg.GetTakeCoin() + p.SetTakeCoin(msg.GetTakeCoin()) + p.LastSyncCoin = p.Coin + p.IsQM = IsQM + if scene != nil { + scene.AudienceEnter(p, isload) + if !p.IsRobot() && !scene.Testing { + //base.LogChannelSingleton.WriteMQData(model.GenerateEnterEvent(scene.GetRecordId(), p.SnId, p.Platform, + // p.DeviceOS, scene.GameId, scene.GameMode, scene.GetGameFreeId())) + } + } + } + return nil + })) + + //观众坐下 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_AUDIENCESIT), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGAudienceSit{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_AUDIENCESIT), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive PACKET_WG_AUDIENCESIT WGAudienceSit", pack) + if msg, ok := pack.(*server.WGAudienceSit); ok { + + p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if p != nil { + scene := p.GetScene() + if scene != nil { + p.Pos = int(msg.GetPos()) + //p.coin = msg.GetTakeCoin() + //p.takeCoin = msg.GetTakeCoin() + if scene.Testing { + p.Coin = int64(scene.DbGameFree.GetTestTakeCoin()) + } + p.LastSyncCoin = p.Coin + scene.AudienceSit(p) + } + } else { + leavePack := &server.GWPlayerLeave{ + RoomId: msg.SceneId, + PlayerId: msg.SnId, + Reason: proto.Int(common.PlayerLeaveReason_Bekickout), + ReturnCoin: msg.TakeCoin, + } + proto.SetDefaults(leavePack) + s.Send(int(server.SSPacketID_PACKET_GW_AUDIENCELEAVE), leavePack) + } + } + return nil + })) + + //玩家返回房间 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PLAYERRETURN), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerReturn{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PLAYERRETURN), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerReturn") + if msg, ok := pack.(*server.WGPlayerReturn); ok { + playerId := msg.GetPlayerId() + p := base.PlayerMgrSington.GetPlayerBySnId(playerId) + if p != nil { + oldFlag := p.GetFlag() + if !p.IsOnLine() { + p.MarkFlag(base.PlayerState_Online) + } + if p.IsMarkFlag(base.PlayerState_Leave) { + p.UnmarkFlag(base.PlayerState_Leave) + } + if p.GetFlag() != oldFlag { + p.SyncFlag() + } + if p.GetScene() != nil { + p.GetScene().PlayerReturn(p, msg.GetIsLoaded()) + } else { + logger.Logger.Warnf("whern (%v) WGPlayerReturn p.scene == nil", playerId) + } + } else { + logger.Logger.Warnf("WGPlayerReturn found player:%v not exist", playerId) + scene := base.SceneMgrSington.GetScene(int(msg.GetRoomId())) + if scene != nil { + p := scene.GetPlayer(msg.GetPlayerId()) + if p != nil { + logger.Logger.Warnf("WGPlayerReturn found player:%v not exist but in scene:%v gameid:%v", playerId, scene.SceneId, scene.GameId) + } + } + //TODO try leave from room + pack := &server.GWPlayerForceLeave{ + RoomId: msg.RoomId, + PlayerId: msg.PlayerId, + Reason: proto.Int(common.PlayerLeaveReason_Bekickout), + EnterTs: msg.EnterTs, + } + proto.SetDefaults(pack) + s.Send(int(server.SSPacketID_PACKET_GW_PLAYERFORCELEAVE), pack) + } + } + return nil + })) + + //玩家掉线 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PLAYERDROPLINE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerDropLine{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PLAYERDROPLINE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerDropLine:", pack) + if msg, ok := pack.(*server.WGPlayerDropLine); ok { + sceneId := int(msg.GetSceneId()) + scene := base.SceneMgrSington.GetScene(sceneId) + if scene != nil { + scene.PlayerDropLine(msg.GetId()) + } + } + return nil + })) + + //玩家重连 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PLAYERREHOLD), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerRehold{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PLAYERREHOLD), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerRehold") + if msg, ok := pack.(*server.WGPlayerRehold); ok { + sceneId := int(msg.GetSceneId()) + scene := base.SceneMgrSington.GetScene(sceneId) + if scene != nil { + var sessionId srvlib.SessionId + sessionId.Set(msg.GetGateSid()) + gateSess := srvlib.ServerSessionMgrSington.GetSession(int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + logger.Logger.Tracef("WGPlayerRehold, AreaId=%v, SrvType=%v, SrvId=%v, SessionId=%v", int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId()), int64(sessionId)) + if gateSess != nil { + pack := &server.GGPlayerSessionBind{ + Sid: msg.Sid, + } + proto.SetDefaults(pack) + gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONBIND), pack) + } + p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetId()) + if p != nil { + base.PlayerMgrSington.ReholdPlayer(p.GetSid(), msg.GetSid(), gateSess) + scene.PlayerRehold(msg.GetId(), msg.GetSid(), gateSess) + } + } + } + return nil + })) + + //玩家充值 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_RECHARGE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGHundredOp{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_RECHARGE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("WGHundredOp Process recv ", pack) + if wgHundredOp, ok := pack.(*server.WGHundredOp); ok { + if wgHundredOp.GetOpCode() == 1 { + snid := wgHundredOp.GetSnid() + param := wgHundredOp.GetParams() + p := base.PlayerMgrSington.GetPlayerBySnId(snid) + + if p == nil { + logger.Logger.Warn("WGHundredOp p == nil") + return nil + } + + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("WGHundredOp p.scene == nil") + return nil + } + + if !scene.HasPlayer(p) { + return nil + } + //同步用户的充值累加额 + if len(param) > 0 { + p.CoinPayTotal += param[0] + if p.TodayGameData != nil { + p.TodayGameData.RechargeCoin += param[0] + } + } + //第2个参数是vip + if len(param) > 1 && p.VIP < int32(param[1]) { + p.VIP = int32(param[1]) + } + + scene.GetScenePolicy().OnPlayerEvent(scene, p, base.PlayerEventRecharge, param) + return nil + } + return nil + } + return nil + })) + + //同步水池设置 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_COINPOOLSETTING), netlib.PacketFactoryWrapper(func() interface{} { + return &webapi.CoinPoolSetting{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_COINPOOLSETTING), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("CoinPoolSetting Process recv ", pack) + if wgCoinPoolSetting, ok := pack.(*webapi.CoinPoolSetting); ok { + base.CoinPoolMgr.UpdateCoinPoolSetting(wgCoinPoolSetting) + return nil + } + return nil + })) + + // 重置水池水位 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_RESETCOINPOOL), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGResetCoinPool{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_RESETCOINPOOL), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("WGResetCoinPool Process recv ", pack) + if wgResetCoinPool, ok := pack.(*server.WGResetCoinPool); ok { + base.CoinPoolMgr.ResetCoinPool(wgResetCoinPool) + return nil + } + return nil + })) + + //设置玩家黑白名单 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_SETPLAYERBLACKLEVEL), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGSetPlayerBlackLevel{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_SETPLAYERBLACKLEVEL), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("WGSetPlayerBlackLevel Process recv ", pack) + if wgSetPlayerBlackLevel, ok := pack.(*server.WGSetPlayerBlackLevel); ok { + p := base.PlayerMgrSington.GetPlayerBySnId(wgSetPlayerBlackLevel.GetSnId()) + if p != nil { + p.WBLevel = wgSetPlayerBlackLevel.GetWBLevel() + if p.WBLevel > 0 { + p.WhiteLevel = p.WBLevel + } else if p.WBLevel < 0 { + p.BlackLevel = -p.WBLevel + } else { + p.WhiteLevel = 0 + p.BlackLevel = 0 + } + p.WBCoinLimit = wgSetPlayerBlackLevel.GetWBCoinLimit() + p.WBMaxNum = wgSetPlayerBlackLevel.GetMaxNum() + p.WBState = wgSetPlayerBlackLevel.GetState() + if wgSetPlayerBlackLevel.GetResetTotalCoin() { + p.WBCoinTotalIn = 0 + p.WBCoinTotalOut = 0 + } + } + return nil + } + return nil + })) + + //同步游戏状态 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_SERVER_STATE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.ServerState{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_SERVER_STATE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("PACKET_WG_SERVER_STATE Process recv ", pack) + if srvState, ok := pack.(*server.ServerState); ok { + base.ServerStateMgr.SetState(common.GameSessState(srvState.GetSrvState())) + return nil + } + return nil + })) + + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_DTRoomInfo), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGDTRoomInfo{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_DTRoomInfo), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("SSPacketID_PACKET_WG_DTRoomInfo Process recv ", pack) + // if msg, ok := pack.(*server.WGDTRoomInfo); ok { + // scene := base.SceneMgrSington.GetScene(int(msg.GetRoomId())) + // if scene != nil { + // data := scene.GetScenePolicy().PacketGameData(scene) + // if pack, ok := data.(*server.GWDTRoomInfo); ok { + // pack.DataKey = proto.String(msg.GetDataKey()) + // pack.RoomId = proto.Int32(msg.GetRoomId()) + // } else { + // logger.Logger.Warn("Covert DT scene packet game data error.") + // } + // scene.SendToWorld(int(server.SSPacketID_PACKET_GW_DTRoomInfo), data) + // } + // return nil + // } + // return nil + //})) + // + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_DTRoomFlag), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGDTRoomFlag{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_DTRoomFlag), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("SSPacketID_PACKET_WG_DTRoomFlag Process recv ", pack) + // if msg, ok := pack.(*server.WGDTRoomFlag); ok { + // scene := base.SceneMgrSington.GetScene(int(msg.GetRoomId())) + // if scene != nil { + // data := base.InterventionData{ + // Webuser: msg.GetWebuser(), + // Flag: msg.GetFlag(), + // NumOfGames: msg.GetNumGames(), + // } + // scene.GetScenePolicy().InterventionGame(scene, data) + // } + // return nil + // } + // return nil + //})) + // + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_DTRoomResults), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGRoomResults{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_DTRoomResults), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("SSPacketID_PACKET_WG_DTRoomResults Process recv:", pack) + // if msg, ok := pack.(*server.WGRoomResults); ok { + // scene := base.SceneMgrSington.GetScene(int(msg.GetRoomId())) + // if scene != nil { + // data := base.InterventionResults{ + // Key: msg.GetDataKey(), + // Webuser: msg.GetWebuser(), + // Results: msg.GetResults(), + // } + // ret := scene.GetScenePolicy().InterventionGame(scene, data) + // if pack, ok := ret.(*server.GWRoomResults); ok { + // pack.DataKey = proto.String(msg.GetDataKey()) + // } else { + // logger.Logger.Warn("Covert DTRoomResults scene packet game data error.") + // } + // scene.SendToWorld(int(server.SSPacketID_PACKET_GW_DTRoomResults), ret) + // } + // return nil + // } + // return nil + //})) + + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PlayerOnGameCount), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGPayerOnGameCount{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PlayerOnGameCount), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("SSPacketID_PACKET_WG_PlayerOnGameCount Process recv ", pack) + // if msg, ok := pack.(*server.WGPayerOnGameCount); ok { + // base.CoinPoolMgr.LastDayDtCount = nil + // for _, value := range msg.GetDTCount() { + // base.CoinPoolMgr.LastDayDtCount = append(base.CoinPoolMgr.LastDayDtCount, int(value)) + // } + // return nil + // } + // return nil + //})) + + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_SyncPlayerSafeBoxCoin), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGSyncPlayerSafeBoxCoin{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_SyncPlayerSafeBoxCoin), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("WGSyncPlayerSafeBoxCoin Process recv ", pack) + // if msg, ok := pack.(*server.WGSyncPlayerSafeBoxCoin); ok { + // p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + // if p != nil { + // p.SafeBoxCoin = msg.GetSafeBoxCoin() + // } + // return nil + // } + // return nil + //})) + + //更新俱乐部房间配置 + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_CLUB_MESSAGE), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGClubMessage{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_CLUB_MESSAGE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + // logger.Logger.Trace("receive WGClubMessage:", pack) + // if msg, ok := pack.(*server.WGClubMessage); ok { + // sceneIds := msg.GetSceneIds() + // for _, id := range sceneIds { + // s := base.SceneMgrSington.GetScene(int(id)) + // if s != nil { + // if msg.GetPumpCoin() > 0 { + // s.PumpCoin = int32(msg.GetPumpCoin()) + // } + // if msg.GetDBGameFree() != nil { + // s.DbGameFree = msg.GetDBGameFree() + // } + // } + // } + // } + // return nil + //})) + + //更新NiceId + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_GW_NICEIDREBIND), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGNiceIdRebind{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_GW_NICEIDREBIND), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("receive WGNiceIdRebind:", pack) + // if msg, ok := pack.(*server.WGNiceIdRebind); ok { + // player := base.PlayerMgrSington.GetPlayerBySnId(msg.GetUser()) + // if player != nil { + // player.NiceId = msg.GetNewId() + // } + // } + // return nil + //})) + + // + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_INVITEROBENTERCOINSCENEQUEUE), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGInviteRobEnterCoinSceneQueue{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_INVITEROBENTERCOINSCENEQUEUE), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("receive WGInviteRobEnterCoinSceneQueue:", pack) + // if msg, ok := pack.(*server.WGInviteRobEnterCoinSceneQueue); ok { + // base.NpcServerAgentSingleton.QueueInvite(msg.GetGameFreeId(), msg.GetPlatform(), msg.GetRobNum()) + // } + // return nil + //})) + // + + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_GAMEFORCESTART), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGGameForceStart{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_GAMEFORCESTART), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGGameForceStart:", pack) + if msg, ok := pack.(*server.WGGameForceStart); ok { + scene := base.SceneMgrSington.GetScene(int(msg.GetSceneId())) + if scene != nil { + scene.GetScenePolicy().ForceStart(scene) + scene.NotifySceneRoundStart(1) + } + } + return nil + })) + + //邀请机器人进比赛 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_INVITEMATCHROB), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGInviteMatchRob{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_INVITEMATCHROB), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + //logger.Logger.Trace("receive WGInviteMatchRob:", pack) + if msg, ok := pack.(*server.WGInviteMatchRob); ok { + base.NpcServerAgentSingleton.MatchInvite(msg.GetRoomId(), msg.GetMatchId(), msg.GetPlatform(), msg.GetRobNum(), msg.GetNeedAwait()) + } + return nil + })) + + //比赛场底分变化 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_SCENEMATCHBASECHANGE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGSceneMatchBaseChange{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_SCENEMATCHBASECHANGE), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("WGSceneMatchBaseChange Process recv ", pack) + if msg, ok := pack.(*server.WGSceneMatchBaseChange); ok { + ids := msg.GetSceneIds() + for _, id := range ids { + s := base.SceneMgrSington.GetScene(int(id)) + if s != nil { + if s.GetMatchChgData() == nil { + s.SetMatchChgData(&base.SceneMatchChgData{}) + } + if s.GetMatchChgData() != nil { + s.GetMatchChgData().NextBaseScore = msg.GetBaseScore() + s.GetMatchChgData().NextOutScore = msg.GetOutScore() + } + } + } + } + return nil + })) + + //玩家退赛 + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PLAYERQUITMATCH), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGPlayerQuitMatch{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PLAYERQUITMATCH), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("WGPlayerQuitMatch Process recv ", pack) + // if msg, ok := pack.(*server.WGPlayerQuitMatch); ok { + // p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + // if p == nil { + // return nil + // } + // scene := base.SceneMgrSington.GetScene(int(msg.GetSceneId())) + // if scene == nil { + // return nil + // } + // if scene.GetParamEx(common.PARAMEX_MATCH_COPYID) != msg.GetMatchId() { + // return nil + // } + // //if scene.mp != nil { + // // if scene.mp.OnMatchBreak(scene, p.pos) { + // // //base.PlayerMgrSington.DelPlayerBySnId(p.SnId) + // // //p.gateSess = nil + // // //p.worldSess = nil + // // //p.gateSid = 0 + // // //p.sid = 0 + // // p.SetIParam(common.PlayerIParam_IsQuit, 1) + // // p.MarkFlag(base.PlayerState_Leave) + // // p.MarkFlag(PlayerState_Auto) + // // p.MarkFlag(PlayerState_MatchQuit) + // // p.SyncFlag() + // // } + // //} + // } + // return nil + //})) + + //玩家中转消息 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_SS_REDIRECTTOPLAYER), netlib.PacketFactoryWrapper(func() interface{} { + return &server.SSRedirectToPlayer{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_SS_REDIRECTTOPLAYER), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("SSRedirectToPlayer Process recv ", pack) + if msg, ok := pack.(*server.SSRedirectToPlayer); ok { + p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if p == nil { + logger.Logger.Trace("SSRedirectToPlayer Process recv p == nil ", msg.GetSnId()) + return nil + } + p.SendToClient(int(msg.GetPacketId()), msg.GetData()) + } + return nil + })) + + ////同步玩家排名信息 + //netlib.RegisterFactory(int(match.MatchPacketID_PACKET_SS_MATCH_PLAYERDATA), netlib.PacketFactoryWrapper(func() interface{} { + // return &match.SSMatchPlayerData{} + //})) + //netlib.RegisterHandler(int(match.MatchPacketID_PACKET_SS_MATCH_PLAYERDATA), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("SSMatchPlayerData Process recv ", pack) + // if msg, ok := pack.(*match.SSMatchPlayerData); ok { + // scene := base.SceneMgrSington.GetScene(int(msg.GetSceneId())) + // if scene == nil { + // return nil + // } + // if !scene.IsMatchScene() { + // return nil + // } + // for _, mp := range msg.GetMatchPlayerData() { + // if data, ok := scene.Players[mp.GetSnId()]; ok { + // data.Iparams[common.PlayerIParam_MatchRank] = int64(mp.GetRank()) + // } + // } + // } + // return nil + //})) + + //由worldsrv通知gamesrv向玩家发送奖池信息 + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_GAMEJACKPOT), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGGameJackpot{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_GAMEJACKPOT), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("WGGameJackpot Process recv ", pack) + // if msg, ok := pack.(*server.WGGameJackpot); ok { + // sid := msg.GetSid() + // gateSid := msg.GetGateSid() + // platform := msg.GetPlatform() + // info := msg.GetInfo() + // + // var sessionId srvlib.SessionId + // sessionId.Set(gateSid) + // gateSess := srvlib.ServerSessionMgrSington.GetSession(int(sessionId.AreaId()), int(sessionId.SrvType()), int(sessionId.SrvId())) + // pack := &gamehall.SCHundredSceneGetGameJackpot{} + // for _, v := range info { + // if common.InSliceInt(base.BroadJackpotGame, int(v.GameId)) { //不是小游戏且需要广播游戏奖池 + // jpfi := &gamehall.GameJackpotFundInfo{ + // GameFreeId: proto.Int32(int32(v.GameFreeId)), + // } + // + // // + // str := base.XSlotsPoolMgr.GetPool(v.GetGameFreeId(), platform) + // if str != "" { + // jackpot := &base.XSlotJackpotPool{} + // err := json.Unmarshal([]byte(str), jackpot) + // if err == nil { + // jpfi.JackPotFund = jackpot.JackpotFund + // } + // } + // + // //初始化奖池金额 + // if jpfi.JackPotFund == 0 { + // dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(v.GameFreeId) + // if dbGameFree != nil { + // params := dbGameFree.GetJackpot() + // jpfi.JackPotFund = int64(params[0] * dbGameFree.GetBaseScore()) + // } + // } + // pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) + // } + // } + // + // proto.SetDefaults(pack) + // common.SendToGate(sid, int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, gateSess) + // logger.Logger.Trace("SCHundredSceneGetGameJackpot:", pack) + // } + // return nil + //})) + + //单控 + //netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_SINGLEADJUST), netlib.PacketFactoryWrapper(func() interface{} { + // return &server.WGSingleAdjust{} + //})) + //netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_SINGLEADJUST), netlib.HandlerWrapper(func(s *netlib.Session, + // packetid int, pack interface{}) error { + // logger.Logger.Trace("WGSingleAdjust Process recv ", pack) + // if msg, ok := pack.(*server.WGSingleAdjust); ok { + // //修改内存 + // sa := model.UnmarshalSingleAdjust(msg.PlayerSingleAdjust) + // if sa == nil { + // logger.Logger.Warn("WGSingleAdjust sa == nil") + // return nil + // } + // p := base.PlayerMgrSington.GetPlayerBySnId(sa.SnId) + // if p == nil { + // logger.Logger.Warn("WGSingleAdjust p == nil") + // return nil + // } + // switch msg.Option { + // case 1, 2: + // p.UpsertSingleAdjust(sa) + // case 3: + // p.DeleteSingleAdjust(sa.Platform, sa.GameFreeId) + // } + // } + // return nil + //})) + + //玩家离开 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_PlayerLEAVE), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WGPlayerLeave{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_PlayerLEAVE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerLeaveGame") + if msg, ok := pack.(*server.WGPlayerLeave); ok { + p := base.PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if p != nil { + scene := p.GetScene() + if scene != nil { + scene.PlayerLeave(p, common.PlayerLeaveReason_DropLine, false) + } + } + } + return nil + })) + + //黑白名单开关 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WG_WBCtrlCfg), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WbCtrlCfg{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WG_WBCtrlCfg), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGPlayerLeaveGame") + if msg, ok := pack.(*server.WbCtrlCfg); ok { + for _, id := range msg.GameIds { + ss := base.SceneMgrSington.GetSceneByGameId(msg.Platform, id) + for _, scene := range ss { + scene.RealCtrl = msg.RealCtrl + scene.Novice = msg.Novice + scene.Welfare = msg.Welfare + scene.KillPoints = msg.KillPoints + } + } + } + return nil + })) +} diff --git a/gamesrv/base/ai.go b/gamesrv/base/ai.go new file mode 100644 index 0000000..fec62fb --- /dev/null +++ b/gamesrv/base/ai.go @@ -0,0 +1,179 @@ +package base + +type AI interface { + // SetOwner 挂载玩家 + SetOwner(*Player) + // GetOwner 获取挂载玩家 + GetOwner() *Player + // GetAttribute 获取属性 + GetAttribute(key interface{}) (interface{}, bool) + // SetAttribute 设置属性 + SetAttribute(key, val interface{}) + // OnStart 开启事件 + OnStart() + // OnStop 停止事件 + OnStop() + // OnTick 心跳事件 + OnTick(s *Scene) + // OnSelfEnter 自己进入事件 + OnSelfEnter(s *Scene, p *Player) + // OnSelfLeave 自己离开事件 + OnSelfLeave(s *Scene, p *Player, reason int) + // OnPlayerEnter 其他玩家进入事件 + OnPlayerEnter(s *Scene, p *Player) + // OnPlayerLeave 其他玩家离开事件 + OnPlayerLeave(s *Scene, p *Player, reason int) + // OnPlayerDropLine 其他玩家掉线事件 + OnPlayerDropLine(s *Scene, p *Player) + // OnPlayerRehold 其他玩家重连事件 + OnPlayerRehold(s *Scene, p *Player) + // OnPlayerReturn 其他玩家返回房间事件 + OnPlayerReturn(s *Scene, p *Player) + // OnPlayerOp 其他玩家操作事件 + OnPlayerOp(s *Scene, p *Player, opcode int, params []int64) bool + // OnPlayerOperate 其他玩家操作事件 + OnPlayerOperate(s *Scene, p *Player, params interface{}) bool + // OnPlayerEvent 其他玩家事件 + OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) + // OnAudienceEnter 其他观众进入事件 + OnAudienceEnter(s *Scene, p *Player) + // OnAudienceLeave 其他观众离开事件 + OnAudienceLeave(s *Scene, p *Player, reason int) + // OnAudienceSit 其他观众坐下事件 + OnAudienceSit(s *Scene, p *Player) + // OnAudienceDropLine 其他观众掉线事件 + OnAudienceDropLine(s *Scene, p *Player) + // OnChangeSceneState 房间状态变化事件 + OnChangeSceneState(s *Scene, oldstate, newstate int) +} + +type BaseAI struct { + owner *Player + attribute map[interface{}]interface{} +} + +//挂载玩家 +func (b *BaseAI) SetOwner(p *Player) { + b.owner = p +} + +//获取挂载玩家 +func (b *BaseAI) GetOwner() *Player { + return b.owner +} + +//获取属性 +func (b *BaseAI) GetAttribute(key interface{}) (interface{}, bool) { + if b.attribute != nil { + v, ok := b.attribute[key] + return v, ok + } + return nil, false +} + +//设置属性 +func (b *BaseAI) SetAttribute(key, val interface{}) { + if b.attribute != nil { + b.attribute[key] = val + } +} + +//开启事件 +func (b *BaseAI) OnStart() { + +} + +//关闭事件 +func (b *BaseAI) OnStop() { + +} + +//心跳事件 +func (b *BaseAI) OnTick(s *Scene) { + +} + +//自己进入事件 +func (b *BaseAI) OnSelfEnter(s *Scene, p *Player) { + if !p.IsLocal { + return + } + takeCoin, leaveCoin, gameTimes := s.RandTakeCoin(p) + p.Coin = takeCoin + p.SetTakeCoin(takeCoin) + p.ExpectGameTime = int32(gameTimes) + p.ExpectLeaveCoin = leaveCoin + //当局游戏结束后剩余金额 起始设置 + p.SetCurrentCoin(takeCoin) + p.LastSyncCoin = p.Coin +} + +//自己离开事件 +func (b *BaseAI) OnSelfLeave(s *Scene, p *Player, reason int) { + +} + +//其他玩家进入事件 +func (b *BaseAI) OnPlayerEnter(s *Scene, p *Player) { + +} + +//其他玩家离开事件 +func (b *BaseAI) OnPlayerLeave(s *Scene, p *Player, reason int) { + +} + +//其他玩家掉线 +func (b *BaseAI) OnPlayerDropLine(s *Scene, p *Player) { + +} + +//其他玩家重连 +func (b *BaseAI) OnPlayerRehold(s *Scene, p *Player) { + +} + +//其他玩家 返回房间 +func (b *BaseAI) OnPlayerReturn(s *Scene, p *Player) { + +} + +//其他玩家操作事件 +func (b *BaseAI) OnPlayerOp(s *Scene, p *Player, opcode int, params []int64) bool { + return true +} + +//其他玩家操作事件 +func (b *BaseAI) OnPlayerOperate(s *Scene, p *Player, params interface{}) bool { + return true +} + +//其他玩家事件 +func (b *BaseAI) OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) { + +} + +//观众进入事件 +func (b *BaseAI) OnAudienceEnter(s *Scene, p *Player) { + +} + +//观众离开事件 +func (b *BaseAI) OnAudienceLeave(s *Scene, p *Player, reason int) { + +} + +//观众坐下事件 +func (b *BaseAI) OnAudienceSit(s *Scene, p *Player) { + +} + +//观众掉线事件 +func (b *BaseAI) OnAudienceDropLine(s *Scene, p *Player) { + +} + +//房间状态变化事件 +func (b *BaseAI) OnChangeSceneState(s *Scene, oldstate, newstate int) { + +} diff --git a/gamesrv/base/aimgr.go b/gamesrv/base/aimgr.go new file mode 100644 index 0000000..db61909 --- /dev/null +++ b/gamesrv/base/aimgr.go @@ -0,0 +1,16 @@ +package base + +type AIMgr interface { + // GetAttribute 获取属性 + GetAttribute(key interface{}) (interface{}, bool) + // SetAttribute 设置属性 + SetAttribute(key, val interface{}) + // OnStart 房间启动 + OnStart(s *Scene) + // OnTick 房间心跳 + OnTick(s *Scene) + // OnStop 房间停止 + OnStop(s *Scene) + // OnChangeState 房间状态变化 + OnChangeState(s *Scene, oldstate, newstate int) +} diff --git a/gamesrv/base/broadcasthandler.go b/gamesrv/base/broadcasthandler.go new file mode 100644 index 0000000..afebcee --- /dev/null +++ b/gamesrv/base/broadcasthandler.go @@ -0,0 +1,61 @@ +package base + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + BroadcastMaker = &BroadcastPacketFactory{} +) + +type BroadcastPacketFactory struct { +} + +type BroadcastHandler struct { +} + +func init() { + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), &BroadcastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), BroadcastMaker) +} + +func (this *BroadcastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketBroadcast{} + return pack +} + +func (this *BroadcastPacketFactory) CreateBroadcastPacket(sp *protocol.BCSessionUnion, packetid int, data interface{}) (rawproto.Message, error) { + pack := &protocol.SSPacketBroadcast{ + SessParam: sp, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Warn("BroadcastPacketFactory.CreateBroadcastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *BroadcastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if bp, ok := data.(*protocol.SSPacketBroadcast); ok { + pd := bp.GetData() + sp := bp.GetSessParam() + if bcss := sp.GetBcss(); bcss != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(bp.GetPacketId()), pd, int(bcss.GetSArea()), int(bcss.GetSType())) + } + } + return nil +} diff --git a/gamesrv/base/coinpoolmanager.go b/gamesrv/base/coinpoolmanager.go new file mode 100644 index 0000000..9004b26 --- /dev/null +++ b/gamesrv/base/coinpoolmanager.go @@ -0,0 +1,738 @@ +package base + +import ( + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "sync" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" +) + +const ( + CoinPoolStatus_Normal = iota //库存下限 < 库存值 < 库存上限 + CoinPoolStatus_Low //库存值 < 库存下限 + CoinPoolStatus_High //库存上限 < 库存值 < 库存上限+偏移量 + CoinPoolStatus_TooHigh //库存>库存上限+偏移量 +) + +var CoinPoolMgr = &CoinPoolManager{ + CoinPool: new(sync.Map), + ProfitPool: new(sync.Map), + CoinPoolSetting: make(map[string]*webapi.CoinPoolSetting), + curRunTime: make(map[string]int64), + publicPond: new(sync.Map), + bossPond: new(sync.Map), +} + +func GetCoinPoolMgr() *CoinPoolManager { + return CoinPoolMgr +} + +const CoinPoolKeyToken = "-" +const CoinPoolKeyTokenGroup = "+" + +type CoinPoolListener interface { + OnSettingUpdate(oldSetting, newSetting *webapi.CoinPoolSetting) +} + +type CoinPoolManager struct { + //调控池 + CoinPoolDBKey string + CoinPool *sync.Map + //收益池 + ProfitPoolDBKey string + ProfitPool *sync.Map + + dirty bool + + // 水池配置 + CoinPoolSetting map[string]*webapi.CoinPoolSetting //初始化值需要配置数据表DB_GameCoinPool + // 重置水池的时间,水池索引:时间戳 + curRunTime map[string]int64 + + listener []CoinPoolListener // 捕鱼 + + publicPond *sync.Map //公共池 + bossPond *sync.Map //BOSS池 +} + +func (this *CoinPoolManager) RegisterListener(l CoinPoolListener) { + this.listener = append(this.listener, l) +} + +func (this *CoinPoolManager) GenKey(gameFreeId int32, platform string, groupId int32) string { + var key string + if groupId != 0 { + key = fmt.Sprintf("%v%v%v", gameFreeId, CoinPoolKeyTokenGroup, groupId) + } else { + key = fmt.Sprintf("%v%v%v", gameFreeId, CoinPoolKeyToken, platform) + } + return key +} + +func (this *CoinPoolManager) SplitKey(key string) (gameFreeId, groupId int32, platform string, ok bool) { + if strings.Contains(key, CoinPoolKeyToken) { + datas := strings.Split(key, CoinPoolKeyToken) + if len(datas) >= 2 { + id, err := strconv.Atoi(datas[0]) + if err == nil { + gameFreeId = int32(id) + } + platform = datas[1] + ok = true + return + } + } else { + datas := strings.Split(key, CoinPoolKeyTokenGroup) + if len(datas) >= 2 { + id, err := strconv.Atoi(datas[0]) + if err == nil { + gameFreeId = int32(id) + } + id, err = strconv.Atoi(datas[1]) + if err == nil { + groupId = int32(id) + } + ok = true + return + } + } + return +} + +// AddProfitPool 增加收益池 +func (this *CoinPoolManager) AddProfitPool(key string, coin int64) bool { + if this.getCoinPoolSetting(key).GetSwitch() == 1 { + return false + } + + poolValue, ok := this.ProfitPool.Load(key) + if !ok { + this.ProfitPool.Store(key, coin) + this.dirty = true + return true + } + poolCoin, ok := poolValue.(int64) + if !ok { + return false + } + + this.ProfitPool.Store(key, poolCoin+coin) + this.dirty = true + return true +} + +// GetProfitPoolCoin 获取收益池水位 +func (this *CoinPoolManager) GetProfitPoolCoin(gameFreeId int32, platform string, groupId int32) int64 { + key := this.GenKey(gameFreeId, platform, groupId) + poolValue, ok := this.ProfitPool.Load(key) + if !ok { + return 0 + } + poolCoin, ok := poolValue.(int64) + if !ok { + logger.Logger.Errorf("Error type convert in coinpoolmanager GetProfitPoolCoin:%v-%v", key, poolValue) + return 0 + } + return poolCoin +} + +// GetProfitRate 获取营收比例 +func (this *CoinPoolManager) GetProfitRate(setting *webapi.CoinPoolSetting) int32 { + if setting == nil { + return 0 + } + + // 营收比例 + rate := setting.GetProfitRate() + if rate != 0 { + return rate + } + return 0 +} + +// 获取调控池水位 +func (this *CoinPoolManager) getCoinPoolCoin(key string) int64 { + poolValue, ok := this.CoinPool.Load(key) + if !ok { + return 0 + } + poolCoin, ok := poolValue.(int64) + if !ok { + logger.Logger.Errorf("Error type convert in coinpoolmanager GetCoin:%v-%v", key, poolValue) + return 0 + } + return poolCoin +} + +// 减少调控池水位 +// 返回当前水位 +func (this *CoinPoolManager) popCoin(key string, coin int64) (bool, int64) { + if this.getCoinPoolSetting(key).GetSwitch() == 1 { + return false, 0 + } + + poolValue, ok := this.CoinPool.Load(key) + if !ok { + return false, 0 + } + poolCoin, ok := poolValue.(int64) + if !ok { + logger.Logger.Errorf("Error type convert in coinpoolmanager popcoin:%v-%v", key, poolValue) + return false, 0 + } + + //if poolCoin < coin { + // return false + //} + + curValue := poolCoin - coin + if curValue < -99999999 { + curValue = -99999999 + } + + this.CoinPool.Store(key, curValue) + this.dirty = true + logger.Logger.Infof("$$$$$$$$金币池 %v 取出 %v 金币,现有金币 %v.$$$$$$$$", key, coin, curValue) + return true, curValue +} + +// 增加调控池水位 +// 返回当前水位 +func (this *CoinPoolManager) pushCoin(key string, coin int64) int64 { + if this.getCoinPoolSetting(key).GetSwitch() == 1 { + return 0 + } + + poolValue, ok := this.CoinPool.Load(key) + if !ok { + this.CoinPool.Store(key, coin) + this.dirty = true + logger.Logger.Infof("$$$$$$$$金币池 %v 放入 %v 金币,现有金币 %v.$$$$$$$$", key, coin, coin) + return coin + } + poolCoin, ok := poolValue.(int64) + if !ok { + logger.Logger.Errorf("Coin pool push error type value:%v-%v", key, poolValue) + return 0 + } + poolCoin += coin + this.CoinPool.Store(key, poolCoin) + this.dirty = true + logger.Logger.Infof("$$$$$$$$金币池 %v 放入 %v 金币,现有金币 %v.$$$$$$$$", key, coin, poolCoin) + return poolCoin +} + +// PopCoin 减少调控池水位 +func (this *CoinPoolManager) PopCoin(gameFreeId, groupId int32, platform string, coin int64) bool { + if coin == 0 { + return true + } + + key := this.GenKey(gameFreeId, platform, groupId) + poolVal := coin + if coin < 0 { //负值为收益 + setting := this.getCoinPoolSetting(key) + if setting != nil && setting.GetSwitch() == 0 { + rate := this.GetProfitRate(setting) + if rate != 0 { //负值也生效??? + profit := coin * int64(rate) / 1000 + this.AddProfitPool(key, -profit) + poolVal = coin - profit + } + } + } + ok, _ := this.popCoin(key, poolVal) + if ok { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(gameFreeId) + if dbGameFree != nil && dbGameFree.GetSceneType() != -1 { //非试玩场 + //保存数据 + //this.ReportCoinPoolRecEvent(gameFreeId, groupId, platform, -coin, curPoolVal, false) + setting := this.GetCoinPoolSetting(platform, gameFreeId, groupId) + if setting != nil { + poolValue, ok := this.CoinPool.Load(key) + if ok { + poolCoin, ok := poolValue.(int64) + if ok { + if int64(setting.GetLowerLimit()) > poolCoin { + WarningCoinPool(Warning_CoinPoolLow, gameFreeId) + } + + if poolCoin < 0 { + WarningCoinPool(Warning_CoinPoolZero, gameFreeId) + } + } + } + } + } + return true + } + return false +} + +// PushCoin 增加调控池水位 +func (this *CoinPoolManager) PushCoin(gameFreeId, groupId int32, platform string, coin int64) { + if coin == 0 { + return + } + key := this.GenKey(gameFreeId, platform, groupId) + poolVal := coin + if coin > 0 { //处理收益池 + setting := this.getCoinPoolSetting(key) + if setting != nil && setting.GetSwitch() == 0 { + rate := this.GetProfitRate(setting) + if rate != 0 { //负值也生效??? + profit := coin * int64(rate) / 1000 + this.AddProfitPool(key, profit) + poolVal = coin - profit + } + } + } + + this.pushCoin(key, poolVal) + return +} + +// GetCoin 获取调控池水位 +func (this *CoinPoolManager) GetCoin(gameFreeId int32, platform string, groupId int32) int64 { + key := this.GenKey(gameFreeId, platform, groupId) + return this.getCoinPoolCoin(key) +} + +// UpdateCoinPoolSetting 更新或添加水池配置 +// worldsrv创建房间时发送,或后台修改水池后worldsrv发送给gamesrv +func (this *CoinPoolManager) UpdateCoinPoolSetting(setting *webapi.CoinPoolSetting) { + if setting != nil { + key := this.GenKey(setting.GetGameFreeId(), setting.GetPlatform(), setting.GetGroupId()) + // ResetTime + if setting.GetResetTime() != 0 { + runTime := time.Now().Unix() + setting.GetResetTime() + this.curRunTime[key] = runTime + } + + old, _ := this.CoinPoolSetting[key] + this.CoinPoolSetting[key] = setting + for _, v := range this.listener { + v.OnSettingUpdate(old, setting) + } + + // Switch + if old.GetSwitch() != setting.GetSwitch() { + + } + + // 调控池初始化 + initValue := setting.GetInitValue() + if initValue != 0 { //初始化水池 + _, ok := this.CoinPool.Load(key) + if !ok { + this.pushCoin(key, int64(initValue)) + } + } + } +} + +// ResetCoinPool 水位重置 +// 后台请求worldsrv,worldsrv发送给gamesrv +func (this *CoinPoolManager) ResetCoinPool(wgRcp *server.WGResetCoinPool) { + if wgRcp != nil { + key := this.GenKey(wgRcp.GetGameFreeId(), wgRcp.GetPlatform(), wgRcp.GetGroupId()) + if setting, exist := this.CoinPoolSetting[key]; exist { + switch wgRcp.GetPoolType() { + case 1: //水池 + value := int64(wgRcp.GetValue()) + if value == -1 { + initValue := setting.GetInitValue() + if initValue != 0 { //初始化水池 + value = int64(initValue) + } + } + this.CoinPool.Store(key, value) + logger.Logger.Infof("$$$$$$$$金币池 %v 重置金币 %v.$$$$$$$$", key, value) + case 2: //营收池 + value := int64(wgRcp.GetValue()) + if value == -1 { + value = 0 + } + this.ProfitPool.Store(key, value) + logger.Logger.Infof("$$$$$$$$营收池 %v 重置金币 %v.$$$$$$$$", key, value) + case 3: //水池&营收池 + value := int64(wgRcp.GetValue()) + if value == -1 { + initValue := setting.GetInitValue() + if initValue != 0 { //初始化水池 + value = int64(initValue) + } + } + this.CoinPool.Store(key, value) + logger.Logger.Infof("$$$$$$$$金币池 %v 重置金币 %v.$$$$$$$$", key, value) + + value = int64(wgRcp.GetValue()) + if value == -1 { + value = 0 + } + this.ProfitPool.Store(key, value) + logger.Logger.Infof("$$$$$$$$营收池 %v 重置金币 %v.$$$$$$$$", key, value) + } + } + } +} + +func (this *CoinPoolManager) getCoinPoolSetting(key string) *webapi.CoinPoolSetting { + if setting, exist := this.CoinPoolSetting[key]; exist { + return setting + } + return nil +} + +// GetCoinPoolSetting 获取水池配置 +func (this *CoinPoolManager) GetCoinPoolSetting(platform string, gamefreeid, groupId int32) *webapi.CoinPoolSetting { + key := this.GenKey(gamefreeid, platform, groupId) + return this.getCoinPoolSetting(key) +} + +// GetCoinPoolSettingByGame 获取水池配置 +func (this *CoinPoolManager) GetCoinPoolSettingByGame(platform string, gameId, gameMode, groupId int32) (settings []*webapi.CoinPoolSetting) { + ids, _ := srvdata.DataMgr.GetGameFreeIds(gameId, gameMode) + for _, id := range ids { + setting := this.GetCoinPoolSetting(platform, id, groupId) + if setting != nil { + s := &webapi.CoinPoolSetting{ + Platform: setting.GetPlatform(), + GameFreeId: setting.GetGameFreeId(), + ServerId: setting.GetServerId(), + GroupId: setting.GetGroupId(), + InitValue: setting.GetInitValue(), + LowerLimit: setting.GetLowerLimit(), + UpperLimit: setting.GetUpperLimit(), + QuDu: setting.GetQuDu(), + UpperOdds: setting.GetUpperOdds(), + UpperOddsMax: setting.GetUpperOddsMax(), + LowerOdds: setting.GetLowerOdds(), + LowerOddsMax: setting.GetLowerOddsMax(), + ProfitRate: setting.GetProfitRate(), + + ResetTime: setting.GetResetTime(), + Switch: setting.GetSwitch(), + + CoinValue: this.GetCoin(id, platform, groupId), // 当前水位 + ProfitPool: this.GetProfitPoolCoin(id, platform, groupId), // 收益池水位 + } + settings = append(settings, s) + } + } + return +} + +func (this *CoinPoolManager) GetCoinPoolOdds(platform string, gamefreeid, groupId int32) int32 { + var coin int64 + var odds int32 + key := this.GenKey(gamefreeid, platform, groupId) + setting := this.getCoinPoolSetting(key) + if setting != nil { + coin = this.getCoinPoolCoin(key) + if coin > setting.GetUpperLimit() { + v := common.SliceMinValue([]int{800, int(float64(coin-setting.GetUpperLimit())/float64(setting.GetQuDu())* + float64(setting.GetUpperOddsMax()-setting.GetUpperOdds()) + float64(setting.GetUpperOdds()))}) + odds = int32(common.SliceMaxValue([]int{100, v})) + } else if coin < setting.GetLowerLimit() { + v := common.SliceMaxValue([]int{-800, int(float64(coin-setting.GetLowerLimit())/float64(setting.GetQuDu())* + float64(setting.GetLowerOdds()-setting.GetLowerOddsMax()) + float64(setting.GetLowerOdds()))}) + odds = int32(common.SliceMinValue([]int{-100, v})) + } + } + logger.Logger.Tracef("TienLenSceneData 场次水池调控 platform:%v gamefreeid:%v groupId:%v Coin:%v, config:%v 概率:%v", + platform, gamefreeid, groupId, coin, setting, odds) + return odds +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *CoinPoolManager) ModuleName() string { + return "CoinPoolManager" +} + +func (this *CoinPoolManager) Init() { + // 调控池 + this.CoinPoolDBKey = fmt.Sprintf("CoinPoolManager_Srv%v", common.GetSelfSrvId()) + data := model.GetStrKVGameData(this.CoinPoolDBKey) + coinPool := make(map[string]int64) + err := json.Unmarshal([]byte(data), &coinPool) + if err == nil { + for key, value := range coinPool { + this.CoinPool.Store(key, value) + } + } else { + logger.Logger.Error("Unmarshal coin pool error:", err) + } + // 收益池 + this.ProfitPoolDBKey = fmt.Sprintf("ProfitPoolManager_Srv%v", common.GetSelfSrvId()) + data = model.GetStrKVGameData(this.ProfitPoolDBKey) + profitPool := make(map[string]int64) + err = json.Unmarshal([]byte(data), &profitPool) + if err == nil { + for key, value := range profitPool { + this.ProfitPool.Store(key, value) + } + } else { + logger.Logger.Error("Unmarshal profit pool error:", err) + } +} + +func (this *CoinPoolManager) Update() { + if this.dirty { + this.dirty = false + this.Save(true) + } + +} + +func (this *CoinPoolManager) Shutdown() { + this.Save(false) + module.UnregisteModule(this) +} + +func (this *CoinPoolManager) Save(asyn bool) { + // 调控池 + coinPool := make(map[string]int64) + this.CoinPool.Range(func(key, value interface{}) bool { + coinPool[key.(string)] = value.(int64) + return true + }) + buff, err := json.Marshal(coinPool) + if err == nil { + if asyn { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UptStrKVGameData(this.CoinPoolDBKey, string(buff)) + }), nil, "UptStrKVGameData").Start() + } else { + model.UptStrKVGameData(this.CoinPoolDBKey, string(buff)) + } + } else { + logger.Logger.Error("Marshal coin pool error:", err) + } + // 收益池 + profitPool := make(map[string]int64) + this.ProfitPool.Range(func(key, value interface{}) bool { + profitPool[key.(string)] = value.(int64) + return true + }) + buffProfit, err := json.Marshal(profitPool) + if err == nil { + if asyn { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UptStrKVGameData(this.ProfitPoolDBKey, string(buffProfit)) + }), nil, "UptStrKVGameData").Start() + } else { + model.UptStrKVGameData(this.ProfitPoolDBKey, string(buffProfit)) + } + } else { + logger.Logger.Error("Marshal profit pool error:", err) + } +} + +func (this *CoinPoolManager) OnMiniTimer() { + +} + +func (this *CoinPoolManager) OnHourTimer() { + +} + +func (this *CoinPoolManager) OnDayTimer() { + logger.Logger.Info("(this *CoinPoolManager) OnDayTimer") +} + +func (this *CoinPoolManager) OnWeekTimer() { + logger.Logger.Info("(this *CoinPoolManager) OnWeekTimer") +} + +func (this *CoinPoolManager) OnMonthTimer() { + logger.Logger.Info("(this *CoinPoolManager) OnMonthTimer") + // 每月1号清空营收池 + //this.ClearProfitPool() +} + +func (this *CoinPoolManager) GetTax() float64 { + return 0 +} +func (this *CoinPoolManager) SetTax(tax float64) { + +} + +func init() { + module.RegisteModule(CoinPoolMgr, time.Minute, 0) + CoinPoolMgr.RegisterListener(new(CoinPoolFishListener)) +} + +// GetCoinPoolStatesByPlatform 获取水池状态 +func (this *CoinPoolManager) GetCoinPoolStatesByPlatform(platform string, games []*common.GamesIndex) (info map[int32]*common.CoinPoolStatesInfo) { + info = make(map[int32]*common.CoinPoolStatesInfo) + for _, g := range games { + gameId := srvdata.PBDB_GameFreeMgr.GetData(g.GameFreeId).GetGameId() + key := this.GenKey(g.GameFreeId, platform, g.GroupId) + pool := this.CoinPoolSetting[key] + if pool != nil { + //在当前水池找到了 + state, _ := this.GetCoinPoolStatus(platform, g.GameFreeId, pool.GetGroupId()) + nowsate := int32(1) + switch state { + case CoinPoolStatus_Normal: + nowsate = 1 + case CoinPoolStatus_Low: + nowsate = 2 + case CoinPoolStatus_High: + nowsate = 3 + case CoinPoolStatus_TooHigh: + nowsate = 4 + } + //当前水池的值 + nowpool := &common.CoinPoolStatesInfo{} + nowpool.GameId = gameId + nowpool.GameFreeId = g.GameFreeId + nowpool.CoinValue = int32(this.GetCoin(g.GameFreeId, platform, g.GroupId)) + nowpool.States = nowsate + info[g.GameFreeId] = nowpool + } else { + //在当前水池没有找到并且已经开启的游戏 给默认值 + //def := srvdata.PBDB_GameCoinPoolMgr.GetData(g.GameFreeId) + nowpool := &common.CoinPoolStatesInfo{} + nowpool.GameId = gameId + nowpool.GameFreeId = g.GameFreeId + nowpool.States = 1 + info[g.GameFreeId] = nowpool + } + } + return +} + +func (this *CoinPoolManager) pushCoinNovice(key string, coin int64) { +} + +func (this *CoinPoolManager) popCoinNovice(key string, coin int64) { + +} + +// PushCoinNovice 增加新手池水位 +func (this *CoinPoolManager) PushCoinNovice(gameFreeId, groupId int32, platform string, coin int64) { + if coin == 0 { + return + } + key := this.GenKey(gameFreeId, platform, groupId) + this.pushCoinNovice(key, coin) +} + +// PopCoinNovice 减少新手池水位 +func (this *CoinPoolManager) PopCoinNovice(gameFreeId, groupId int32, platform string, coin int64) { +} + +// GetNoviceCoin 获取新手池水位 +func (this *CoinPoolManager) GetNoviceCoin(gameFreeId int32, platform string, groupId int32) int64 { + return 0 +} + +// IsMaxOutHaveEnough 判定是否满足出分,out 为负值是出分,正值为收分 +func (this *CoinPoolManager) IsMaxOutHaveEnough(platform string, gameFreeId int32, groupId int32, out int64) bool { + return false +} + +// GetCoinPoolCtx 获取水池状态 +func (this *CoinPoolManager) GetCoinPoolCtx(platform string, gamefreeid, groupId int32) model.CoinPoolCtx { + setting := this.GetCoinPoolSetting(platform, gamefreeid, groupId) + if setting == nil { + return model.CoinPoolCtx{} + } + curCoin := this.GetCoin(gamefreeid, platform, groupId) + state := CoinPoolStatus_Normal + //switch { + //case curCoin < int64(setting.GetLowerLimit()): + // state = CoinPoolStatus_Low + //case curCoin > int64(setting.GetUpperLimit()) && curCoin < int64(setting.GetUpperLimit()+setting.GetUpperOffsetLimit()): + // state = CoinPoolStatus_High + //case curCoin > int64(setting.GetUpperLimit()+setting.GetUpperOffsetLimit()): + // state = CoinPoolStatus_TooHigh + //} + ctx := model.CoinPoolCtx{CurrValue: int32(curCoin), CurrMode: int32(state)} + ctx.LowerLimit = setting.GetLowerLimit() + ctx.UpperLimit = setting.GetUpperLimit() + return ctx +} + +func (this *CoinPoolManager) getCoinPoolStatus(platform string, gamefreeid, groupId int32, value int64) (int, int) { + curCoin := this.GetCoin(gamefreeid, platform, groupId) + if value < 0 { + curCoin += value + } + setting := this.GetCoinPoolSetting(platform, gamefreeid, groupId) + if setting == nil { + return CoinPoolStatus_Normal, 10000 + } + return CoinPoolStatus_Normal, 10000 +} + +// GetCoinPoolStatus 获取水池水位状态 +func (this *CoinPoolManager) GetCoinPoolStatus(platform string, gamefreeid, groupId int32) (int, int) { + return this.getCoinPoolStatus(platform, gamefreeid, groupId, 0) +} + +// GetCoinPoolStatus2 获取减少水位后的水位状态 +// value 减少值,需要带负号 +func (this *CoinPoolManager) GetCoinPoolStatus2(platform string, gamefreeid, groupId int32, value int64) (int, int) { + return this.getCoinPoolStatus(platform, gamefreeid, groupId, value) +} + +// 增加BOSS池 +func (this *CoinPoolManager) AddBossPond(sceneType int32, score int64) { + value, ok := this.bossPond.Load(sceneType) + if ok { + sumValue := value.(int64) + score + if sumValue > math.MaxInt64 { + sumValue = math.MaxInt64 + } + this.bossPond.Store(sceneType, sumValue) + } else { + this.bossPond.Store(sceneType, score) + } +} + +// 获取当前BOSS池 +func (this *CoinPoolManager) GetBossPond(sceneType int32) int64 { + value, ok := this.bossPond.Load(sceneType) + if ok { + return value.(int64) + } + return 0 +} + +// 增加公共奖池 暂时无用 +func (this *CoinPoolManager) AddPublicPond(roomId int32, score int64) { + value, ok := this.publicPond.Load(roomId) + if ok { + this.publicPond.Store(roomId, value.(int64)+score) + } else { + this.publicPond.Store(roomId, score) + } +} + +// 获取公共奖池 +func (this *CoinPoolManager) GetPublicPond(roomId int32) int64 { + value, ok := this.publicPond.Load(roomId) + if ok { + return value.(int64) + } + return 0 +} diff --git a/gamesrv/base/config.go b/gamesrv/base/config.go new file mode 100644 index 0000000..b402665 --- /dev/null +++ b/gamesrv/base/config.go @@ -0,0 +1,29 @@ +package base + +import ( + "mongo.games.com/goserver/core" +) + +var ( + ForwardConfig = ForwardConfiguration{} +) + +type ForwardConfiguration struct { + ForwardMakeCards bool +} + +func (this *ForwardConfiguration) Name() string { + return "forward" +} + +func (this *ForwardConfiguration) Init() error { + return nil +} + +func (this *ForwardConfiguration) Close() error { + return nil +} + +func init() { + core.RegistePackage(&ForwardConfig) +} diff --git a/gamesrv/base/constant.go b/gamesrv/base/constant.go new file mode 100644 index 0000000..7617082 --- /dev/null +++ b/gamesrv/base/constant.go @@ -0,0 +1,13 @@ +package base + +import ( + "mongo.games.com/game/common" +) + +var BroadJackpotGame = []int{ + common.GameId_IceAge, + common.GameId_TamQuoc, + common.GameId_CaiShen, + common.GameId_Avengers, + common.GameId_EasterIsland, +} diff --git a/gamesrv/base/data.go b/gamesrv/base/data.go new file mode 100644 index 0000000..d4b8c8b --- /dev/null +++ b/gamesrv/base/data.go @@ -0,0 +1,13 @@ +package base + +type InterventionData struct { + Webuser string + Flag int32 + NumOfGames int32 +} + +type InterventionResults struct { + Key string + Webuser string + Results string +} diff --git a/gamesrv/base/divisionsystem.go b/gamesrv/base/divisionsystem.go new file mode 100644 index 0000000..b016caf --- /dev/null +++ b/gamesrv/base/divisionsystem.go @@ -0,0 +1,6 @@ +package base + +// 提供税收和流水,根据代理需求后台进行分账 +func ProfitDistribution(p *Player, tax, taxex, validFlow int64) { + //LogChannelSingleton.WriteMQData(model.GenerateTaxDivide(p.SnId, p.Platform, p.Channel, p.BeUnderAgentCode, p.PackageID, tax, taxex, validFlow, p.scene.GameId, p.scene.GameMode, p.scene.DbGameFree.GetId(), p.PromoterTree)) +} diff --git a/gamesrv/base/elementproducer.go b/gamesrv/base/elementproducer.go new file mode 100644 index 0000000..eea2f8f --- /dev/null +++ b/gamesrv/base/elementproducer.go @@ -0,0 +1,145 @@ +package base + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/utils" + "sync/atomic" + "time" +) + +type ElementPool struct { + key int32 + pool chan interface{} +} + +type ElementProducer struct { + times int64 + cnt int32 + max int32 + pool []*ElementPool + nf ProduceFunc //正常的生产函数 + bf ProduceFunc //保底的生产函数 + quit bool +} + +type ProduceFunc func() (interface{}, int32) + +type ElementProducerMgr struct { + producer map[int32]*ElementProducer +} + +var ElementProducerMgrSington = &ElementProducerMgr{ + producer: make(map[int32]*ElementProducer), +} + +func CreateElementProducer(max int32, nf, bf ProduceFunc) *ElementProducer { + ep := &ElementProducer{ + max: max, + nf: nf, + bf: bf, + pool: make([]*ElementPool, max, max), + } + return ep +} + +func (ep *ElementProducer) Start() { + go _start_produce_routine(ep) +} + +func (ep *ElementProducer) Stop() { + ep.quit = true +} + +func (ep *ElementProducer) FetchOneElement(key int32) interface{} { + if key < 0 || key >= int32(len(ep.pool)) { + return nil + } + pool := ep.pool[key] + if pool != nil { + select { + case d, ok := <-pool.pool: + if ok { + atomic.AddInt32(&ep.cnt, -1) + return d + } else { + //向下取一个结果 + for i := key - 1; i >= 0; i-- { + if ep.pool[i] != nil && len(ep.pool[i].pool) != 0 { + return ep.FetchOneElement(i) + } + } + d, _ := ep.bf() + return d + } + default: + } + } + //向下取一个结果 + for i := key - 1; i >= 0; i-- { + if ep.pool[i] != nil && len(ep.pool[i].pool) != 0 { + return ep.FetchOneElement(i) + } + } + d, _ := ep.bf() + return d +} + +func (ep *ElementProducer) Dump() { + logger.Logger.Tracef("[[[[[[[[[[[[[[[total=%v, calcu=%v]]]]]]]]]]]]]]]", ep.cnt, ep.times) + for i := 0; i < len(ep.pool); i++ { + epp := ep.pool[i] + if epp != nil && len(epp.pool) != 0 { + logger.Logger.Tracef("[key=%v] [count=%v]", i, len(epp.pool)) + } + } +} + +func _start_produce_routine(ep *ElementProducer) { + defer utils.DumpStackIfPanic("_start_produce_routine") + var idle = 0 + for !ep.quit { + val, key := ep.nf() + atomic.AddInt64(&ep.times, 1) + if key >= 0 && key < int32(len(ep.pool)) { + epp := ep.pool[key] + if epp == nil { + epp = &ElementPool{key: key, pool: make(chan interface{}, 128)} + ep.pool[key] = epp + } + select { + case epp.pool <- val: + atomic.AddInt32(&ep.cnt, 1) + case <-time.After(time.Millisecond): + idle++ + } + } + if idle >= 10000 { + idle = 0 + //ep.Dump() + } + } +} + +func (this *ElementProducerMgr) GetProducer(id int32) *ElementProducer { + if p, exist := this.producer[id]; exist { + return p + } + return nil +} + +func (this *ElementProducerMgr) SetProducer(id int32, p *ElementProducer) { + if op, exist := this.producer[id]; exist { + if op != nil { + op.Stop() + } + } + this.producer[id] = p +} + +func (this *ElementProducerMgr) StopAll() { + for _, p := range this.producer { + if p != nil { + p.Stop() + } + } +} diff --git a/gamesrv/base/etcd.go b/gamesrv/base/etcd.go new file mode 100644 index 0000000..a27f38a --- /dev/null +++ b/gamesrv/base/etcd.go @@ -0,0 +1,22 @@ +package base + +import ( + "go.etcd.io/etcd/client/v3" + + "mongo.games.com/game/etcd" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + // 个人水池 + etcd.Register(etcd.ETCDKEY_PLAYERPOOL, webapi.PlayerPool{}, PlatformConfigEtcd) + // 游戏配置 + etcd.Register(etcd.ETCDKEY_GAME_CONFIG, webapi.GameConfig{}, PlatformConfigEtcd) +} + +func PlatformConfigEtcd(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + PlatformConfigSingleton.Update(data) +} diff --git a/gamesrv/base/gamever.go b/gamesrv/base/gamever.go new file mode 100644 index 0000000..0cb74a6 --- /dev/null +++ b/gamesrv/base/gamever.go @@ -0,0 +1,15 @@ +package base + +import ( + "mongo.games.com/game/common" +) + +//TODO 后续优化改为生成当先监控文件的快照,在游戏部署时对比两个快照文件,修改变动文件对应游戏的版本号,由程序自动完成,避免手动修改 + +var GameDetailedVer = map[int]int32{} +var ReplayRecorderVer = map[int]int32{} + +func init() { + //需要改动游戏详情版本号,在这里修改 + GameDetailedVer[common.GameId_Unknow] = 1 +} diff --git a/gamesrv/base/gamewarning.go b/gamesrv/base/gamewarning.go new file mode 100644 index 0000000..4a0ddbe --- /dev/null +++ b/gamesrv/base/gamewarning.go @@ -0,0 +1,120 @@ +package base + +import ( + "encoding/json" + "fmt" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" +) + +const ( + Warning_LoseCoinLimit = 1 //1. 单次输分超过设定值,默认值100000分 + Warning_CoinPoolZero = 2 //2. 水位线低于0 + Warning_CoinPoolLow = 3 //3. 水位线低于水位下限 + Warning_BlackPlayer = 4 //4. 黑名单玩家加入游戏 + Warning_BetCoinMax = 5 //5. 单个玩家下注超过5000元,可配置,默认值5000 + Warning_WinRate = 6 //6. 实时赔率 = 玩家产出+1/玩家投入+1 ≥ 5 的玩家加入游戏,可配置,默认值5 + Warning_WinCoin = 7 //7. 玩家获利 = 玩家产出 - 玩家投入 ≥ 5000元 加入游戏,可配置,默认值5000 +) + +type GameWarningParam struct { + WarningType int + WarningGame int + WarningScene int + WarningSnid int + WarningBet int + WarningCoin int + WarningRate int + WinCoin int64 + LoseCoin int64 +} + +var EmailTitle = "Game server warning!" + +func NewGameWarning(param string) { + timeNow := time.Now() + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + gwParam := GameWarningParam{} + err := json.Unmarshal([]byte(param), &gwParam) + if err != nil { + logger.Logger.Error("Unmarshal game warning param error:", err) + return err + } + var emailContent string + switch gwParam.WarningType { + case Warning_LoseCoinLimit: + case Warning_CoinPoolZero: + emailContent = fmt.Sprintf("Game %v coin pool drop to zero level.", gwParam.WarningGame) + case Warning_CoinPoolLow: + emailContent = fmt.Sprintf("Game %v coin pool drop to low level.", gwParam.WarningGame) + case Warning_BlackPlayer: + emailContent = fmt.Sprintf("Player %v where in black list enter %v game.", gwParam.WarningSnid, gwParam.WarningGame) + case Warning_BetCoinMax: + emailContent = fmt.Sprintf("Player %v where in %v scene,bet big coin in %v game.", + gwParam.WarningSnid, gwParam.WarningScene, gwParam.WarningGame) + case Warning_WinRate: + case Warning_WinCoin: + default: + return nil + } + emailContent = emailContent + "\n" + fmt.Sprintf("Time:%v", timeNow) + println(EmailTitle, emailContent) + //TODO send email + //email.InitGmailFrom() + return nil + }), nil, "NewGameWarning").Start() +} + +// func WarningLoseCoin(gameFreeId int32, snid int32, loseCoin int64) { +// if model.GameParamData.WarningLoseLimit == 0 { +// return +// } +// if loseCoin < model.GameParamData.WarningLoseLimit { +// return +// } +// NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningGame":%v,"WarningSnid":%v,"LoseCoin":%v}`, +// Warning_LoseCoinLimit, gameFreeId, snid, loseCoin)) +// + +//func WarningBetCoinCheck(sceneId, gameFreeId int32, snid int32, betCoin int64) { +// if model.GameParamData.WarningBetMax == 0 { +// return +// } +// if betCoin > model.GameParamData.WarningBetMax { +// NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningSnid":%v,"WarningGame":%v,"WarningScene":%v}`, +// Warning_BetCoinMax, snid, gameFreeId, sceneId)) +// } +//} + +func WarningCoinPool(warnType int, gameFreeId int32) { + NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningGame":%v}`, + warnType, gameFreeId)) +} +func WarningBlackPlayer(snid, gameFreeId int32) { + NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningSnid":%v,"WarningGame":%v}`, + Warning_BlackPlayer, snid, gameFreeId)) +} + +//func WarningWinnerRate(snid int32, winCoin, loseCoin int64) { +// if model.GameParamData.WarningWinRate == 0 { +// return +// } +// if (winCoin+1)/(loseCoin+1) < model.GameParamData.WarningWinRate { +// return +// } +// NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningSnid":%v,"WarningRate":%v,"WinCoin":%v,"LoseCoin":%v}`, +// Warning_WinRate, snid, (winCoin+1)/(loseCoin+1), winCoin, loseCoin)) +//} +//func WarningWinnerCoin(snid int32, winCoin, loseCoin int64) { +// if model.GameParamData.WarningWinMoney == 0 { +// return +// } +// if (winCoin - loseCoin) < model.GameParamData.WarningWinMoney { +// return +// } +// NewGameWarning(fmt.Sprintf(`{"WarningType":%v,"WarningSnid":%v,"WarningCoin":%v,"WinCoin":%v,"LoseCoin":%v}`, +// Warning_WinCoin, snid, (winCoin - loseCoin), winCoin, loseCoin)) +//} diff --git a/gamesrv/base/handredJacklistmgr.go b/gamesrv/base/handredJacklistmgr.go new file mode 100644 index 0000000..e444859 --- /dev/null +++ b/gamesrv/base/handredJacklistmgr.go @@ -0,0 +1,300 @@ +package base + +import ( + "fmt" + "strconv" + "strings" + "time" + + "mongo.games.com/game/model" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" +) + +// HundredJackListManager 排行榜 key: platform+gamefreeId +type HundredJackListManager struct { + HundredJackTsList map[string][]*HundredJackInfo + HundredJackSortList map[string][]*HundredJackInfo +} + +// HundredJackListMgr 实例化 +var HundredJackListMgr = &HundredJackListManager{ + HundredJackTsList: make(map[string][]*HundredJackInfo), + HundredJackSortList: make(map[string][]*HundredJackInfo), +} + +// HundredJackInfo 数据结构 +type HundredJackInfo struct { + model.HundredjackpotLog + linkSnids []int32 //点赞人数 +} + +// ModuleName . +func (hm *HundredJackListManager) ModuleName() string { + return "HundredJackListManager" +} + +// Init . +func (hm *HundredJackListManager) Init() { + //data := model. +} + +// Update . +func (hm *HundredJackListManager) Update() { +} + +// Shutdown . +func (hm *HundredJackListManager) Shutdown() { + module.UnregisteModule(hm) +} + +// ISInitJackInfo 仅初始化一次 +var ISInitJackInfo bool + +// InitTsJackInfo 初始化TsJackInfo +func (hm *HundredJackListManager) InitTsJackInfo(platform string, freeID int32) { + key := fmt.Sprintf("%v-%v", platform, freeID) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + datas, err := model.GetHundredjackpotLogTsByPlatformAndGameFreeID(platform, freeID) + if err != nil { + logger.Logger.Error("HundredJackListManager DelOneJackInfo ", err) + return nil + } + return datas + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + datas := data.([]model.HundredjackpotLog) + if data != nil && datas != nil { + for i := range datas { + if i == model.HundredjackpotLogMaxLimitPerQuery { + break + } + data := &HundredJackInfo{ + HundredjackpotLog: datas[i], + } + strlikeSnids := strings.Split(datas[i].LinkeSnids, "|") + for _, v := range strlikeSnids { + if v == "" { + break + } + snid, err := strconv.Atoi(v) + if err == nil { + data.linkSnids = append(data.linkSnids, int32(snid)) + } + } + hm.HundredJackTsList[key] = append(hm.HundredJackTsList[key], data) + } + // logger.Logger.Warnf("InitTsJackInfo data:%v", datas) + } else { + hm.HundredJackTsList[key] = []*HundredJackInfo{} + } + return + }), "InitTsJackInfo").Start() +} + +// InitSortJackInfo 初始化SortJackInfo +func (hm *HundredJackListManager) InitSortJackInfo(platform string, freeID int32) { + + key := fmt.Sprintf("%v-%v", platform, freeID) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + datas, err := model.GetHundredjackpotLogCoinByPlatformAndGameFreeID(platform, freeID) + if err != nil { + logger.Logger.Error("HundredJackListManager DelOneJackInfo ", err) + return nil + } + return datas + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + datas := data.([]model.HundredjackpotLog) + if data != nil && datas != nil { + for i := range datas { + if i == model.HundredjackpotLogMaxLimitPerQuery { + break + } + data := &HundredJackInfo{ + HundredjackpotLog: datas[i], + } + strlikeSnids := strings.Split(datas[i].LinkeSnids, "|") + for _, v := range strlikeSnids { + snid, err := strconv.Atoi(v) + if err == nil { + data.linkSnids = append(data.linkSnids, int32(snid)) + } + } + hm.HundredJackSortList[key] = append(hm.HundredJackSortList[key], data) + } + // logger.Logger.Warnf("InitSortJackInfo data:%v", datas) + } else { + hm.HundredJackSortList[key] = []*HundredJackInfo{} + } + return + }), "InitSortJackInfo").Start() +} + +// InitHundredJackListInfo 初始化 HundredJackListInfo +func (hm *HundredJackListManager) InitHundredJackListInfo(platform string, freeID int32) { + if ISInitJackInfo { + return + } + key := fmt.Sprintf("%v-%v", platform, freeID) + if _, exist := hm.HundredJackTsList[key]; !exist { + hm.InitTsJackInfo(platform, freeID) + } + if _, exist := hm.HundredJackSortList[key]; !exist { + hm.InitSortJackInfo(platform, freeID) + } + ISInitJackInfo = true + return +} + +// GetJackTsInfo 返回TsInfo +func (hm *HundredJackListManager) GetJackTsInfo(platform string, freeID int32) []*HundredJackInfo { + key := fmt.Sprintf("%v-%v", platform, freeID) + if _, exist := hm.HundredJackTsList[key]; !exist { // 玩家进入scene 已经初始化 + hm.InitTsJackInfo(platform, freeID) + } + return hm.HundredJackTsList[key] +} + +// GetJackSortInfo 返回SortInfo +func (hm *HundredJackListManager) GetJackSortInfo(platform string, freeID int32) []*HundredJackInfo { + key := fmt.Sprintf("%v-%v", platform, freeID) + if _, exist := hm.HundredJackSortList[key]; !exist { + hm.InitSortJackInfo(platform, freeID) + } + return hm.HundredJackSortList[key] +} + +// Insert 插入 +func (hm *HundredJackListManager) Insert(coin, turncoin int64, snid, roomid, jackType, inGame, vip int32, platform, channel, name string, gamedata []string) { + key := fmt.Sprintf("%v-%v", platform, roomid) + log := model.NewHundredjackpotLogEx(snid, coin, turncoin, roomid, jackType, inGame, vip, platform, channel, name, gamedata) + ///////////////////实际不走这里 + if _, exist := hm.HundredJackTsList[key]; !exist { + hm.InitTsJackInfo(platform, roomid) + } + if _, exist := hm.HundredJackSortList[key]; !exist { + hm.InitSortJackInfo(platform, roomid) + } + ///////////////////// + hm.InsertLog(log) + data := &HundredJackInfo{ + HundredjackpotLog: *log, + } + /*logger.Logger.Trace("HundredJackListManager log 1 ", log.SnID, log.LogID, data.GameData) + for _, v := range hm.HundredJackTsList[key] { + logger.Logger.Trace("HundredJackListManager log 2 ", v.SnID, v.LogID, v.GameData) + }*/ + for i, v := range hm.HundredJackSortList[key] { // 插入 + if v.Coin < log.Coin { + d1 := append([]*HundredJackInfo{}, hm.HundredJackSortList[key][i:]...) + hm.HundredJackSortList[key] = append(hm.HundredJackSortList[key][:i], data) + hm.HundredJackSortList[key] = append(hm.HundredJackSortList[key], d1...) + goto Exit + } + } + if len(hm.HundredJackSortList[key]) < model.HundredjackpotLogMaxLimitPerQuery { + hm.HundredJackSortList[key] = append(hm.HundredJackSortList[key], data) + } +Exit: + d1 := append([]*HundredJackInfo{}, hm.HundredJackTsList[key][0:]...) + hm.HundredJackTsList[key] = append(hm.HundredJackTsList[key][:0], data) + hm.HundredJackTsList[key] = append(hm.HundredJackTsList[key], d1...) + var delList []*HundredJackInfo + if len(hm.HundredJackTsList[key]) > model.HundredjackpotLogMaxLimitPerQuery { + delList = append(delList, hm.HundredJackTsList[key][model.HundredjackpotLogMaxLimitPerQuery:]...) + hm.HundredJackTsList[key] = hm.HundredJackTsList[key][:model.HundredjackpotLogMaxLimitPerQuery] + } + if len(hm.HundredJackSortList[key]) > model.HundredjackpotLogMaxLimitPerQuery { + delList = append(delList, hm.HundredJackSortList[key][model.HundredjackpotLogMaxLimitPerQuery:]...) + hm.HundredJackSortList[key] = hm.HundredJackSortList[key][:model.HundredjackpotLogMaxLimitPerQuery] + } + /*for _, v := range hm.HundredJackTsList[key] { + logger.Logger.Trace("HundredJackListManager log 3 ", v.SnID, v.LogID, v.GameData) + }*/ + for _, v := range delList { + if hm.IsCanDel(v, hm.HundredJackTsList[key], hm.HundredJackSortList[key]) { // 两个排行帮都不包含 + logger.Logger.Info("HundredJackListManager DelOneJackInfo ", v.LogID) + hm.DelOneJackInfo(v.Platform, v.LogID) + } + } +} + +// IsCanDel 能否删除 +func (hm *HundredJackListManager) IsCanDel(deldata *HundredJackInfo, tsList, sortList []*HundredJackInfo) bool { + for _, v := range tsList { + if v.LogID == deldata.LogID { + return false + } + } + for _, v := range sortList { + if v.LogID == deldata.LogID { + return false + } + } + return true +} + +// InsertLog insert db +func (hm *HundredJackListManager) InsertLog(log *model.HundredjackpotLog) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertHundredjackpotLog(log) + if err != nil { + logger.Logger.Error("HundredJackListManager Insert ", err) + } + return err + }), nil, "InsertHundredJack").Start() +} + +// UpdateLikeNum updata likenum +func (hm *HundredJackListManager) UpdateLikeNum(plt string, gid bson.ObjectId, like int32, likesnids string) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.UpdateLikeNum(plt, gid, like, likesnids) + if err != nil { + logger.Logger.Error("HundredJackListManager UpdateHundredLikeNum ", err) + } + return err + }), nil, "UpdateHundredLikeNum").Start() +} + +// UpdatePlayBlackNum updata playblacknum +func (hm *HundredJackListManager) UpdatePlayBlackNum(plt string, gid bson.ObjectId, playblack int32) []string { + var ret []string + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + data, err := model.UpdatePlayBlackNum(plt, gid, playblack) + if err != nil { + logger.Logger.Error("HundredJackListManager DelOneJackInfo ", err) + return nil + } + return data + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + ret = data.([]string) + logger.Logger.Warnf("UpdatePlayBlackNum data:%v", ret) + } + return + }), "UpdatePlayBlackNum").Start() + + logger.Logger.Error("HundredJackListManager UpdatePlayBlackNum ", ret) + if len(ret) == 0 { + return ret + } + return nil +} + +// DelOneJackInfo del +func (hm *HundredJackListManager) DelOneJackInfo(plt string, gid bson.ObjectId) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.RemoveHundredjackpotLogOne(plt, gid) + if err != nil { + logger.Logger.Error("HundredJackListManager DelOneJackInfo ", err) + } + return err + }), nil, "DelOneJackInfo").Start() +} + +func init() { + module.RegisteModule(HundredJackListMgr, time.Hour, 0) +} diff --git a/gamesrv/base/init.go b/gamesrv/base/init.go new file mode 100644 index 0000000..1863b63 --- /dev/null +++ b/gamesrv/base/init.go @@ -0,0 +1,45 @@ +package base + +import ( + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/broker/rabbitmq" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +var RabbitMQPublisher *mq.RabbitMQPublisher + +func init() { + model.InitGameParam() + model.InitFishingParam() + model.InitNormalParam() + model.InitGMAC() + + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + model.StartupRPClient(common.CustomConfig.GetString("MgoRpcCliNet"), common.CustomConfig.GetString("MgoRpcCliAddr"), time.Duration(common.CustomConfig.GetInt("MgoRpcCliReconnInterV"))*time.Second) + model.InitGameKVData() + + //rabbitmq打开链接 + RabbitMQPublisher = mq.NewRabbitMQPublisher(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}, common.CustomConfig.GetInt("RMQPublishBacklog")) + if RabbitMQPublisher != nil { + err := RabbitMQPublisher.Start() + if err != nil { + panic(err) + } + } + + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + //关闭rabbitmq连接 + if RabbitMQPublisher != nil { + RabbitMQPublisher.Stop() + } + return nil + }) +} diff --git a/gamesrv/base/logchannel.go b/gamesrv/base/logchannel.go new file mode 100644 index 0000000..63173f6 --- /dev/null +++ b/gamesrv/base/logchannel.go @@ -0,0 +1,56 @@ +package base + +import ( + "reflect" + + "mongo.games.com/game/model" +) + +// LogChannelSingleton 日志记录器 +var LogChannelSingleton = &LogChannel{ + cName: make(map[reflect.Type]string), +} + +type LogChannel struct { + cName map[reflect.Type]string +} + +func (c *LogChannel) RegisterLogCName(cname string, log interface{}) { + t := c.getLogType(log) + c.cName[t] = cname +} + +func (c *LogChannel) getLogType(log interface{}) reflect.Type { + return reflect.Indirect(reflect.ValueOf(log)).Type() +} + +func (c *LogChannel) getLogCName(log interface{}) string { + t := c.getLogType(log) + if name, exist := c.cName[t]; exist { + return name + } + return "" +} + +func (c *LogChannel) WriteLog(log interface{}) { + cname := c.getLogCName(log) + if cname == "" { + cname = "_null_" + } + RabbitMQPublisher.Send(cname, log) +} + +func (c *LogChannel) WriteMQData(data *model.RabbitMQData) { + RabbitMQPublisher.Send(data.MQName, data.Data) +} + +func init() { + LogChannelSingleton.RegisterLogCName(model.LoginLogCollName, &model.LoginLog{}) + LogChannelSingleton.RegisterLogCName(model.CoinGiveLogCollName, &model.CoinGiveLog{}) + LogChannelSingleton.RegisterLogCName(model.CoinLogCollName, &model.CoinLog{}) + LogChannelSingleton.RegisterLogCName(model.SceneCoinLogCollName, &model.SceneCoinLog{}) + LogChannelSingleton.RegisterLogCName(model.GameDetailedLogCollName, &model.GameDetailedLog{}) + LogChannelSingleton.RegisterLogCName(model.GamePlayerListLogCollName, &model.GamePlayerListLog{}) + LogChannelSingleton.RegisterLogCName(model.FriendRecordLogCollName, &model.FriendRecord{}) + LogChannelSingleton.RegisterLogCName(model.ItemLogCollName, &model.ItemLog{}) +} diff --git a/gamesrv/base/monitormgr.go b/gamesrv/base/monitormgr.go new file mode 100644 index 0000000..b2e3e39 --- /dev/null +++ b/gamesrv/base/monitormgr.go @@ -0,0 +1,133 @@ +package base + +import ( + "encoding/gob" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/profile" + "mongo.games.com/goserver/core/schedule" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" + "time" +) + +var MonitorMgrSington = &MonitorMgr{} + +type MonitorMgr struct { +} + +func (this *MonitorMgr) ModuleName() string { + return "MonitorMgr" +} + +func (this *MonitorMgr) Init() { + +} + +func (this *MonitorMgr) Update() { + //logic stats + logicStats := profile.GetStats() + if len(logicStats) > 0 { + logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats) + if logLogic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("logic", logLogic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //net session stats + netStats := netlib.Stats() + if len(netStats) > 0 { + logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats) + if logNet != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("net", logNet) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //schedule stats + jobStats := schedule.Stats() + if len(jobStats) > 0 { + logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats) + if logJob != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("job", logJob) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //trans stats + transStats := transact.Stats() + if len(transStats) > 0 { + logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats) + if logTrans != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("transact", logTrans) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //panic stats + panicStats := utils.GetPanicStats() + if len(panicStats) > 0 { + for key, stats := range panicStats { + logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats) + if logPanic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("panic", logPanic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + } + + //object command quene stats + objStats := core.AppCtx.GetStats() + if len(objStats) > 0 { + logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats) + if logCmd != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("cmdque", logCmd) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //gorouting count, eg. system info + runtimeStats := utils.StatsRuntime() + logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats) + if logRuntime != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertMonitorData("runtime", logRuntime) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } +} + +func (this *MonitorMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + //gob registe + gob.Register(profile.TimeElement{}) + gob.Register(map[string]profile.TimeElement{}) + gob.Register(netlib.ServiceStats{}) + gob.Register(map[int]netlib.ServiceStats{}) + gob.Register(schedule.TaskStats{}) + gob.Register(map[string]schedule.TaskStats{}) + gob.Register(transact.TransStats{}) + gob.Register(map[int]transact.TransStats{}) + gob.Register(utils.PanicStackInfo{}) + gob.Register(map[string]utils.PanicStackInfo{}) + gob.Register(basic.CmdStats{}) + gob.Register(map[string]basic.CmdStats{}) + gob.Register(utils.RuntimeStats{}) + //gob registe + + module.RegisteModule(MonitorMgrSington, time.Minute*5, 0) +} diff --git a/gamesrv/base/multicasthandler.go b/gamesrv/base/multicasthandler.go new file mode 100644 index 0000000..6a670d7 --- /dev/null +++ b/gamesrv/base/multicasthandler.go @@ -0,0 +1,67 @@ +package base + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + MulticastMaker = &MulticastPacketFactory{} +) + +type MulticastPacketFactory struct { +} + +type MulticastHandler struct { +} + +func init() { + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), &MulticastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), MulticastMaker) +} + +func (this *MulticastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketMulticast{} + return pack +} + +func (this *MulticastPacketFactory) CreateMulticastPacket(packetid int, data interface{}, sis ...*protocol.MCSessionUnion) (rawproto.Message, error) { + pack := &protocol.SSPacketMulticast{ + Sessions: sis, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Info("MulticastPacketFactory.CreateMulticastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *MulticastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if mp, ok := data.(*protocol.SSPacketMulticast); ok { + pd := mp.GetData() + sis := mp.GetSessions() + for _, si := range sis { + ss := si.GetMcss() + if ss != nil { + ns := srvlib.ServerSessionMgrSington.GetSession(int(ss.GetSArea()), int(ss.GetSType()), int(ss.GetSId())) + if ns != nil { + ns.Send(int(mp.GetPacketId()), pd /*, s.GetSessionConfig().IsInnerLink*/) + } + } + } + } + return nil +} diff --git a/gamesrv/base/platform.go b/gamesrv/base/platform.go new file mode 100644 index 0000000..b161108 --- /dev/null +++ b/gamesrv/base/platform.go @@ -0,0 +1,45 @@ +package base + +import ( + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" +) + +var PlatformConfigSingleton = &PlatformConfigMgr{ + Platforms: make(map[string]*PlatformConfig), +} + +type PlatformConfig struct { + PlayerPool *webapi.PlayerPool + GameConfig *webapi.GameConfig +} + +type PlatformConfigMgr struct { + Platforms map[string]*PlatformConfig //key 是平台标记 +} + +func (this *PlatformConfigMgr) Get(plt string) *PlatformConfig { + ret, ok := this.Platforms[plt] + if !ok { + ret = new(PlatformConfig) + this.Platforms[plt] = ret + } + return ret +} + +func (this *PlatformConfigMgr) Update(config any) { + if config == nil { + return + } + + switch v := config.(type) { + case *webapi.PlayerPool: + this.Get(v.GetPlatform()).PlayerPool = v + + case *webapi.GameConfig: + this.Get(v.GetPlatform()).GameConfig = v + + default: + logger.Logger.Error("Update config type error") + } +} diff --git a/gamesrv/base/player.go b/gamesrv/base/player.go new file mode 100644 index 0000000..deb8947 --- /dev/null +++ b/gamesrv/base/player.go @@ -0,0 +1,1488 @@ +package base + +import ( + "fmt" + rawproto "google.golang.org/protobuf/proto" + "math" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/player" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + + //rawproto "github.com/golang/protobuf/proto" + "math/rand" + "strconv" + "time" +) + +// 对应到客户端的一个玩家对象. + +const ( + PlayerState_Online int = 1 << iota //在线标记 1 + PlayerState_Ready //准备标记 2 + PlayerState_SceneOwner //房主标记 3 + PlayerState_Choke //呛标记 被复用于金花,是否被动弃牌 4 + PlayerState_Ting //听牌标记 5 金花复用,标记最后押注时,是否看牌 + PlayerState_NoisyBanker //闹庄标记 6 金花复用,标记allin时,是否看牌 + PlayerState_WaitOp //等待操作标记 7 + PlayerState_Auto //托管状态 8 + PlayerState_Check //已看牌状态 9 + PlayerState_Fold //弃牌状态 10 + PlayerState_Lose //输状态 11 + PlayerState_Win //赢状态 12 + PlayerState_WaitNext //等待下一局游戏 13 + PlayerState_GameBreak //不能继续游戏 14 + PlayerState_Leave //暂离状态 15 + PlayerState_Audience //观众标记 16 + PlayerState_AllIn //allin标记 17 + PlayerState_FinalAllIn //最后一圈,最后一个人allin标记 18 + PlayerState_Show //亮牌标记 19 + PlayerState_EnterSceneFailed //进场失败 20 + PlayerState_PKLost //发起Pk,失败 21 + PlayerState_IsChangeCard //牛牛标识是否换牌 22 + PlayerState_IsPayChangeCard //牛牛标识是否充值换牌 23 + PlayerState_Bankruptcy //玩家破产 24 + PlayerState_MatchQuit //退赛标记 25 + PlayerState_AllFollow //跟到底状态 26 + PlayerState_SAdjust //单控状态 27 + PlayerState_Max +) + +// 玩家事件 +const ( + PlayerEventEnter int = iota //进入事件 + PlayerEventLeave //离开事件 + PlayerEventDropLine //掉线 + PlayerEventRehold //重连 + PlayerEventReturn //返回房间 gs 添加 + PlayerEventRecharge //冲值事件 + PlayerEventAddCoin //其他加减币事件(例如:小游戏) + AudienceEventEnter //观众进入事件 + AudienceEventLeave //观众离开事件 + AudienceEventDropLine //观众掉线 + AudienceEventRehold //观众重连 +) + +type Player struct { + model.PlayerData //po 持久化对象 + ExtraData interface{} //扩展接口 + gateSess *netlib.Session //所在GateServer的session + worldSess *netlib.Session //所在WorldServer的session + scene *Scene //当前所在个Scene + ai AI //ai接口 + sid int64 //对应客户端的sessionId + gateSid int64 //对应网关的sessionId + Longitude int32 //经纬度 + Latitude int32 //经纬度 + city string //城市 + flag int //状态标记 + Pos int //当前位置 + dirty bool //脏标记 + Billed bool //是否已经结算过了 + AgentCode string //代理商编号 + Coin int64 //金币 + serviceFee int64 //服务费|税收 + TotalBet int64 //总下注额(从进房间开始,包含多局游戏的下注) + disbandGen int //第几次解散申请 + hAuto timer.TimerHandle //托管handle + GameTimes int32 //游戏次数 + winTimes int //胜利次数 + lostTimes int //失败次数 + ActiveLeave bool //主动暂离 + OpCode player.OpResultCode //错误码 + takeCoin int64 //携带金币 + ExpectLeaveCoin int64 //期望离场时的金币[机器人用] + ExpectGameTime int32 //期望进行的局数[机器人用] + CurIsWin int64 //当局输赢 负数:输 0:平局 正数:赢 + currentCoin int64 //本局结束后剩余 + CurrentBet int64 //本局下注额 + CurrentTax int64 //本局税收 + StartCoin int64 //本局开始金币 + LastSyncCoin int64 // + IsQM bool //是否是全民推广用户 + LastOPTimer time.Time //玩家最后一次操作时间 + Trusteeship int32 //玩家托管了几局 + ValidCacheBetTotal int64 //有效下注缓存 + isFightRobot bool //测试机器人,这种机器人可以用作记录水池数据,方便模拟用户输赢 + DropTime time.Time //掉线时间 + cparams map[string]string //平台登陆数据 + Iparams map[int]int64 //整形参数 + sparams map[int]string //字符参数 + WhiteLevel int32 //todo 使用WBLevel + BlackLevel int32 //todo 使用WBLevel + SingleAdjust *model.PlayerSingleAdjust + IsLocal bool //是否本地player + Items map[int32]int32 //背包数据 + MatchParams []int32 //比赛参数 排名、段位、假snid、假角色 + MatchRobotGrades []MatchRobotGrade + TestLog []string // 调试日志 + RankScore map[int32]int64 // 段位积分 +} + +type MatchRobotGrade struct { + CopySnid int32 + Grade int32 +} + +func NewPlayer(sid int64, data []byte, ws, gs *netlib.Session) *Player { + p := &Player{ + sid: sid, + worldSess: ws, + gateSess: gs, + flag: PlayerState_Online, + Pos: -1, + Longitude: -1, + Latitude: -1, + cparams: make(map[string]string), //平台登陆数据 + Iparams: make(map[int]int64), //整形参数 + sparams: make(map[int]string), //字符参数 + Items: make(map[int32]int32), + RankScore: make(map[int32]int64), + } + + // 需要make的,统一在这里初始化默认值,别的地方就不用再初始化了 + p.PlayerData = model.PlayerData{ + TotalGameData: make(map[int][]*model.PlayerGameTotal), + GDatas: make(map[string]*model.PlayerGameInfo), + ShopTotal: make(map[int32]*model.ShopTotal), + ShopLastLookTime: make(map[int32]int64), + IsFoolPlayer: make(map[string]bool), + } + + if p.init(data) { + return p + } + + return nil +} + +func (this *Player) init(data []byte) bool { + if !this.UnmarshalData(data) { + return false + } + if this.GMLevel > 2 { + this.Longitude = rand.Int31n(114103930-113216260) + 113216260 + this.Latitude = rand.Int31n(34963671-34592702) + 34592702 + } + this.city = this.City + this.LastOPTimer = time.Now() + if this.GDatas == nil { + this.GDatas = make(map[string]*model.PlayerGameInfo) + } + if this.WBLevel > 0 { + this.WhiteLevel = this.WBLevel + } else if this.WBLevel < 0 { + this.BlackLevel = -this.WBLevel + } + return true +} + +func (this *Player) MarkFlag(flag int) { + this.flag |= flag + switch flag { + case PlayerState_Online, PlayerState_Ready, PlayerState_Leave: + this.SyncFlagToWorld() + } +} + +func (this *Player) UnmarkFlag(flag int) { + this.flag &= ^flag + switch flag { + case PlayerState_Online, PlayerState_Ready, PlayerState_Leave: + this.SyncFlagToWorld() + } +} + +func (this *Player) IsMarkFlag(flag int) bool { + if (this.flag & flag) != 0 { + return true + } + return false +} + +func (this *Player) IsOnLine() bool { + return this.IsMarkFlag(PlayerState_Online) +} + +func (this *Player) IsReady() bool { + return this.IsMarkFlag(PlayerState_Ready) +} + +func (this *Player) IsSceneOwner() bool { + return this.IsMarkFlag(PlayerState_SceneOwner) +} + +func (this *Player) IsAuto() bool { + return this.IsMarkFlag(PlayerState_Auto) +} + +func (this *Player) IsGameing() bool { + return !this.IsMarkFlag(PlayerState_WaitNext) && !this.IsMarkFlag(PlayerState_GameBreak) && !this.IsMarkFlag(PlayerState_Bankruptcy) && !this.IsMarkFlag(PlayerState_Audience) +} + +func (this *Player) IsAllFollow() bool { + return this.IsMarkFlag(PlayerState_AllFollow) +} + +func (this *Player) SyncFlag(onlyMyself ...bool) { + if this.IsLocal { + return + } + pack := &player.SCPlayerFlag{ + PlayerId: proto.Int32(this.SnId), + Flag: proto.Int(this.flag), + } + proto.SetDefaults(pack) + if len(onlyMyself) != 0 && onlyMyself[0] { + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERFLAG), pack) + } else { + this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERFLAG), pack, 0) + } + //logger.Logger.Trace("SyncFlag:", pack) +} + +func (this *Player) SyncFlagToWorld() { + if this.IsLocal { + return + } + if this.scene == nil || this.scene.IsCoinScene() || this.scene.IsMatchScene() || this.scene.IsHundredScene() { + return + } + pack := &server.GWPlayerFlag{ + SnId: proto.Int32(this.SnId), + RoomId: proto.Int(this.scene.SceneId), + Flag: proto.Int(this.flag), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_PLAYERSTATE), pack) + logger.Logger.Trace("SyncFlag to world:", pack) +} + +func (this *Player) SendToClient(packetid int, rawpack interface{}, forceIgnore ...bool) bool { + if this.IsLocal { + return true + } + if !this.scene.Testing && this.scene.Gaming && this.scene.rr != nil && this.Pos != -1 && len(forceIgnore) == 0 && !this.scene.IsHundredScene() { + this.scene.rr.Record(this.Pos, -1, packetid, rawpack) + } + if this.gateSess == nil { + logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] gatesess == nil ", this.SnId, packetid) + return false + } + if rawpack == nil { + logger.Logger.Tracef("(this *Player) SendToClient [snid:%v packetid:%v] rawpack == nil ", this.SnId, packetid) + return false + } + if !this.IsOnLine() { + logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] Player if offline.", this.SnId, packetid) + return false + } + if this.IsMarkFlag(PlayerState_Leave) { + logger.Logger.Warnf("(this *Player) SendToClient [snid:%v packetid:%v] Player if leave.", this.SnId, packetid) + return false + } + //logger.Logger.Trace("Send to player's packet:", packetid) + return common.SendToGate(this.sid, packetid, rawpack, this.gateSess) +} + +func (this *Player) Broadcast(packetid int, rawpack interface{}, excludeSid int64) bool { + if this.scene != nil { + this.scene.Broadcast(packetid, rawpack.(rawproto.Message), excludeSid) + return true + } + return false +} + +func (this *Player) SendToWorld(packetid int, rawpack interface{}) bool { + if this.IsLocal { + return true + } + if this.worldSess == nil { + logger.Logger.Tracef("(this *Player) SendToWorld [%v] worldsess == nil ", this.Name) + return false + } + if rawpack == nil { + logger.Logger.Trace("(this *Player) SendToWorld rawpack == nil ") + return false + } + + this.scene.SendToWorld(packetid, rawpack) + return true +} + +func (this *Player) OnEnter(s *Scene) { + this.scene = s + this.TestLog = this.TestLog[:0] + //标记房主 + if this.SnId == s.Creator { + this.MarkFlag(PlayerState_SceneOwner) + } +} + +func (this *Player) OnAudienceEnter(s *Scene) { + this.scene = s +} + +func (this *Player) OnRehold(newSid int64, newSess *netlib.Session) { + this.sid = newSid + this.gateSess = newSess + this.MarkFlag(PlayerState_Online) + // 2018-4-25 + // 这里先注释掉,暂离的状态在LeaveRoom和ReturnRoom的消息中设置 + // 如果这里清除暂离,那么在棋牌馆中,离开房间,用的状态更新为暂离状态,断线重连以后的话,在棋牌馆大厅界面看到的用户为非暂离状态, + // 所以这里就先注释掉,让暂离的状态在离开和返回房间的消息中成对出现 + this.UnmarkFlag(PlayerState_Leave) + this.SyncFlag() +} + +func (this *Player) OnDropLine() { + this.UnmarkFlag(PlayerState_Online) + if !this.scene.Gaming && this.IsReady() && !this.scene.IsMatchScene() { + this.UnmarkFlag(PlayerState_Ready) + } + this.SyncFlag() + + //存在假掉线的可能吗? + //this.gateSess = nil + //this.sid = 0 +} + +func (this *Player) OnAudienceDropLine() { + this.gateSess = nil +} + +func (this *Player) OnLeave(reason int) { + unbindGateSess := true + PlayerMgrSington.DelPlayerBySnId(this.SnId) + //解绑gamesession + if unbindGateSess && this.gateSess != nil { + pack := &server.GGPlayerSessionUnBind{ + Sid: proto.Int64(this.sid), + } + proto.SetDefaults(pack) + this.gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), pack) + } +} + +func (this *Player) OnAudienceLeave(reason int) { + PlayerMgrSington.DelPlayerBySnId(this.SnId) + //解绑gamesession + if this.gateSess != nil { + pack := &server.GGPlayerSessionUnBind{ + Sid: proto.Int64(this.sid), + } + proto.SetDefaults(pack) + this.gateSess.Send(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), pack) + } +} + +func (this *Player) MarshalData(gameid int) (d []byte, e error) { + d, e = netlib.Gob.Marshal(&this.PlayerData) + logger.Logger.Trace("(this *Player) MarshalData(gameid int)") + return +} + +func (this *Player) UnmarshalData(data []byte) bool { + if len(data) == 0 { + return true + } + err := netlib.Gob.Unmarshal(data, &this.PlayerData) + if err == nil { + this.dirty = true + return true + } else { + logger.Logger.Warn("Player.SyncData err:", err) + } + return false +} + +func (this *Player) OnSecTimer() { +} + +func (this *Player) OnMiniTimer() { +} + +func (this *Player) OnHourTimer() { +} + +func (this *Player) OnDayTimer() { + //在线跨天 数据给昨天,今天置为空 + this.YesterdayGameData = this.TodayGameData + this.TodayGameData = model.NewPlayerGameCtrlData() + /* + for k, v := range this.YesterdayGameData.CtrlData { + t := &model.PlayerGameStatics{} + t.AvgBetCoin = v.AvgBetCoin + this.TodayGameData.CtrlData[k] = t + } + */ +} + +func (this *Player) OnMonthTimer() { +} + +func (this *Player) OnWeekTimer() { +} + +func (this *Player) GetName() string { + return this.Name +} + +func (this *Player) MarkDirty() { + this.dirty = true +} + +const ( + SyncFlag_ToClient = 1 << iota //同步给客户端 + SyncFlag_ToWorld //同步给服务端 + SyncFlag_Broadcast //广播给房间内的用户 +) + +func (this *Player) AddCoin(num int64, gainWay int32, syncFlag int, oper, remark string) { + if num == 0 { + return + } + this.Coin += num + if this.scene != nil { + if !this.IsRob && !this.scene.Testing { //机器人log排除掉 + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: this.Platform, + SnID: this.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: num, + RemainNum: this.Coin, + Add: 0, + LogType: gainWay, + GameID: int64(int32(this.scene.GetGameId())), + GameFreeID: int64(this.scene.GetGameFreeId()), + BaseCoin: int64(this.scene.GetBaseScore()), + Operator: oper, + Remark: remark, + }) + if log != nil { + this.GameCoinTs = log.Time.UnixNano() + this.dirty = true + LogChannelSingleton.WriteLog(log) + } + } + //确保金币场金币数量不小于0 + if this.Coin < 0 { + this.Coin = 0 + } + if this.scene.IsHundredScene() { + this.scene.NewBigCoinNotice(this, int64(num), 5) + } + } + if (syncFlag & SyncFlag_ToClient) != 0 { + pack := &player.SCPlayerCoinChange{ + SnId: proto.Int32(this.SnId), + AddCoin: proto.Int64(num), + RestCoin: proto.Int64(this.Coin), + } + proto.SetDefaults(pack) + if (syncFlag & SyncFlag_Broadcast) != 0 { + this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) + } else { + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) + } + logger.Logger.Trace("(this *Player) AddCoin SCPlayerCoinChange:", pack) + } +} + +func (this *Player) AddCoinNoLog(num int64, syncFlag int) { + if num == 0 { + return + } + this.Coin += num + if this.scene != nil { + if !this.IsRob && !this.scene.Testing { //机器人log排除掉 + this.dirty = true + } + } + if (syncFlag & SyncFlag_ToClient) != 0 { + pack := &player.SCPlayerCoinChange{ + SnId: proto.Int32(this.SnId), + AddCoin: proto.Int64(num), + RestCoin: proto.Int64(this.Coin), + } + proto.SetDefaults(pack) + if (syncFlag & SyncFlag_Broadcast) != 0 { + this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) + } else { + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) + } + logger.Logger.Trace("(this *Player) AddCoinNoLog SCPlayerCoinChange:", pack) + } +} + +func (this *Player) AddCoinAsync(num int64, gainWay int32, notifyC, broadcast bool, oper, remark string, writeLog bool) { + if num == 0 { + return + } + this.Coin += num + if this.scene != nil { + if !this.IsRob && !this.scene.Testing && writeLog { //机器人log排除掉 + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: this.Platform, + SnID: this.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: num, + RemainNum: this.Coin, + Add: 0, + LogType: gainWay, + GameID: int64(this.scene.GetGameId()), + GameFreeID: int64(this.scene.GetGameFreeId()), + BaseCoin: int64(this.scene.GetBaseScore()), + Operator: oper, + Remark: remark, + }) + if log != nil { + this.GameCoinTs = log.Time.UnixNano() + this.dirty = true + LogChannelSingleton.WriteLog(log) + } + } + //确保金币场金币数量不小于0 + if this.Coin < 0 { + this.Coin = 0 + } + } + if notifyC { + pack := &player.SCPlayerCoinChange{ + SnId: proto.Int32(this.SnId), + AddCoin: proto.Int64(num), + RestCoin: proto.Int64(this.Coin), + } + proto.SetDefaults(pack) + if broadcast { + this.Broadcast(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack, 0) + } else { + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) + } + } +} + +func (this *Player) AddChessGrade(num int64) { + if num == 0 { + return + } + this.ChessGrade += num + if this.ChessGrade < 0 { + this.ChessGrade = 0 + } +} + +func (this *Player) AddRankScore(rankType int32, num int64) { + if num == 0 { + return + } + oldScore := this.RankScore[rankType] + + var lessScore int64 + switch rankType { + case 1: + lessScore = 10001 + default: + lessScore = 10001 + } + + if oldScore < lessScore && num < 0 { + return + } + + this.RankScore[rankType] += num + + if this.RankScore[rankType] < 0 { + this.RankScore[rankType] = 0 + } + + if oldScore >= lessScore && this.RankScore[rankType] < lessScore { + this.RankScore[rankType] = lessScore + } +} + +// 保存金币变动日志 +// 数据用途: 个人房间内牌局账变记录,后台部分报表使用,确保数据计算无误,否则可能影响月底对账 +// takeCoin: 牌局结算前玩家身上的金币 +// changecoin: 本局玩家输赢的钱,注意是税后 +// coin: 结算后玩家当前身上的金币余额 +// totalbet: 总下注额 +// taxcoin: 本局该玩家产生的税收,这里要包含俱乐部的税 +// wincoin: 本局赢取的金币,含税 wincoin==changecoin+taxcoin +// jackpotWinCoin: 从奖池中赢取的金币(拉霸类游戏) +// smallGameWinCoin: 小游戏赢取的金币(拉霸类游戏) +func (this *Player) SaveSceneCoinLog(takeCoin, changecoin, coin, totalbet, taxcoin, wincoin int64, jackpotWinCoin int64, smallGameWinCoin int64) { + if this.scene != nil { + if !this.IsRob && !this.scene.Testing && !this.scene.IsMatchScene() { //机器人log排除掉 + var eventType int64 //输赢事件值 默认值为0 + if coin-takeCoin > 0 { + eventType = 1 + } else if coin-takeCoin < 0 { + eventType = -1 + } + log := model.NewSceneCoinLogEx(this.SnId, changecoin, takeCoin, coin, eventType, + int64(this.scene.DbGameFree.GetBaseScore()), totalbet, int32(this.scene.GameId), this.PlayerData.Ip, + this.scene.paramsEx[0], this.Pos, this.Platform, this.Channel, this.BeUnderAgentCode, int32(this.scene.SceneId), + this.scene.DbGameFree.GetGameMode(), this.scene.GetGameFreeId(), taxcoin, wincoin, + jackpotWinCoin, smallGameWinCoin, this.PackageID) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } +} + +// 需要关照 +func (this *Player) IsNeedCare() bool { + return false +} + +// 需要削弱 +func (this *Player) IsNeedWeaken() bool { + return false +} + +func (this *Player) GetCoinOverPercent() int32 { + return 0 +} + +func (this *Player) SyncCoin() { + pack := &player.SCPlayerCoinChange{ + SnId: proto.Int32(this.SnId), + AddCoin: proto.Int64(0), + RestCoin: proto.Int64(this.Coin), + } + proto.SetDefaults(pack) + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PLAYERCOINCHANGE), pack) + logger.Logger.Trace("(this *Player) SyncCoin SCPlayerCoinChange:", pack) +} + +func (this *Player) ReportGameEvent(tax, taxex, changeCoin, validbet, validFlow, in, out int64) { + // 记录玩家 首次参与该场次的游戏时间 游戏次数 + var gameFirstTime, gameFreeFirstTime time.Time + var gameTimes, gameFreeTimes int64 + data, ok := this.GDatas[this.scene.KeyGamefreeId] + if ok { + gameFirstTime = data.FirstTime + gameTimes = data.Statics.GameTimes + } + + // 记录玩家 首次参与该游戏时间 游戏次数(不区分场次) + dataGame, ok := this.GDatas[this.scene.KeyGameId] + if ok { + gameFreeFirstTime = dataGame.FirstTime + gameFreeTimes = dataGame.Statics.GameTimes + } + + gamingTime := int32(time.Now().Sub(this.scene.GameNowTime).Seconds()) + LogChannelSingleton.WriteMQData(model.GenerateGameEvent(model.CreatePlayerGameRecEvent(this.SnId, tax, taxex, changeCoin, validbet, validFlow, in, out, + int32(this.scene.GameId), this.scene.DbGameFree.GetId(), int32(this.scene.GameMode), + this.scene.GetRecordId(), this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS, + this.CreateTime, gamingTime, gameFirstTime, gameFreeFirstTime, gameTimes, gameFreeTimes, this.LastLoginTime, + this.TelephonePromoter, this.DeviceId))) +} + +// 破产事件 +func (this *Player) ReportBankRuptcy(gameId, gameMode, gameFreeId int32) { + //if !this.IsRob { + // d, e := model.MarshalBankruptcyEvent(2, this.SnId, this.TelephonePromoter, this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.CreateTime, gameId, gameMode, gameFreeId) + // if e == nil { + // rmd := model.NewInfluxDBData("hj.player_bankruptcy", d) + // if rmd != nil { + // InfluxDBDataChannelSington.Write(rmd) + // } + // } + //} +} + +// 汇总玩家该次游戏总产生的税收 +// 数据用途: 平台和推广间分账用,确保数据计算无误, +// 注意:该税收不包含俱乐部的抽水 +// tax:游戏税收 +func (this *Player) AddServiceFee(tax int64) { + if this.scene == nil || this.scene.Testing || this.scene.IsMatchScene() { //测试场不统计 + return + } + if tax > 0 && !this.IsRob { + this.serviceFee += tax + } +} + +func (this *Player) GetStaticsData(gameDiff string) (winCoin int64, lostCoin int64) { + if this.PlayerData.GDatas != nil { + if data, ok := this.PlayerData.GDatas[gameDiff]; ok { + winCoin, lostCoin = data.Statics.TotalOut, data.Statics.TotalIn + return + } + } + return +} +func (this *Player) SaveReportForm(showId, sceneMode int, keyGameId string, profitCoin, flow int64, validBet int64) { + //个人报表统计 + if this.TotalGameData == nil { + this.TotalGameData = make(map[int][]*model.PlayerGameTotal) + } + if this.TotalGameData[showId] == nil { + this.TotalGameData[showId] = []*model.PlayerGameTotal{new(model.PlayerGameTotal)} + } + td := this.TotalGameData[showId][len(this.TotalGameData[showId])-1] + td.ProfitCoin += profitCoin + td.BetCoin += validBet + td.FlowCoin += flow + ///////////////最多盈利 + if pgs, exist := this.GDatas[keyGameId]; exist { + if pgs.Statics.MaxSysOut < profitCoin { + pgs.Statics.MaxSysOut = profitCoin + } + } else { + gs := model.NewPlayerGameStatics() + gs.MaxSysOut = profitCoin + this.GDatas[keyGameId] = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: *gs} + } +} + +// Statics 弃用,使用 Scene.Statistics 方法 +// 个人投入产出汇总,以游戏id为key存储 +// 数据用途:计算玩家赔率用,数据确保计算无误,否则可能影响玩家手牌的调控 +// key: 游戏ID对应的字符串,牛牛目前用的是同一个ID,这块有待优化 +// gain:输赢额,注意如果是[正值]这里一定要用税前数据,否则玩家会有数值调控优势 +// 如果需要汇总gameid today的数据,可以使用game scene的GetTotalTodayDaliyGameData +// Deprecated: Use Scene.Statistics instead. +func (this *Player) Statics(keyGameId string, keyGameFreeId string, gain int64, isAddNum bool) { + //if this.scene == nil || this.scene.Testing { //测试场|自建房和机器人不统计 + // return + //} + // + //if this.IsRob && !this.scene.IsRobFightGame() { + // return + //} + // + //if this.TodayGameData == nil { + // this.TodayGameData = &model.PlayerGameCtrlData{} + //} + //if this.TodayGameData.CtrlData == nil { + // this.TodayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) + //} + // + //var totalIn int64 + //var totalOut int64 + //if gain > 0 { + // totalOut = gain + //} else { + // totalIn = -gain + //} + // + //statics := make([]*model.PlayerGameStatics, 0, 4) + ////当天数据统计 + ////按场次分 + //if data, ok := this.TodayGameData.CtrlData[keyGameFreeId]; ok { + // statics = append(statics, data) + //} else { + // gs := &model.PlayerGameStatics{} + // this.TodayGameData.CtrlData[keyGameFreeId] = gs + // statics = append(statics, gs) + //} + ////按游戏分 + //if data, ok := this.TodayGameData.CtrlData[keyGameId]; ok { + // statics = append(statics, data) + //} else { + // data = &model.PlayerGameStatics{} + // this.TodayGameData.CtrlData[keyGameId] = data + // statics = append(statics, data) + //} + // + ////按游戏场次进行的统计 + //if data, ok := this.GDatas[keyGameFreeId]; ok { + // statics = append(statics, &data.Statics) + //} else { + // data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: model.PlayerGameStatics{}} + // this.GDatas[keyGameFreeId] = data + // statics = append(statics, &data.Statics) + //} + //if data, ok := this.GDatas[keyGameId]; ok { + // statics = append(statics, &data.Statics) + //} else { + // data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: model.PlayerGameStatics{}} + // this.GDatas[keyGameId] = data + // statics = append(statics, &data.Statics) + //} + // + ////if !this.scene.IsPrivateScene() { + //// //增加黑白名单、GM过滤,因为黑白名单过后,会导致玩家体验急剧变化 + //// needStatic := this.WhiteLevel == 0 && this.WhiteFlag == 0 && this.BlackLevel == 0 && this.GMLevel == 0 + //// //增加黑白名单过滤,因为黑白名单后,会导致数据出现补偿 + //// if needStatic { + //for _, data := range statics { + // if data != nil { + // data.TotalIn += totalIn + // data.TotalOut += totalOut + // if isAddNum { + // data.GameTimes++ + // if gain > 0 { + // data.WinGameTimes++ + // data.WinGameTimesNum++ + // data.LoseGameTimesNum = 0 + // } else if gain < 0 { + // data.LoseGameTimes++ + // data.LoseGameTimesNum++ + // data.WinGameTimesNum = 0 + // } else { + // data.DrawGameTimes++ + // data.WinGameTimesNum = 0 + // data.LoseGameTimesNum = 0 + // } + // } + // } + //} + // + //// 黑白名单,新手,不统计到个人赔率 + // + ////玩家身上元数据 + //this.GameTimes++ + //if gain > 0 { + // this.winTimes++ + // this.WinTimes++ + // this.WinCoin += totalOut + //} else if gain < 0 { + // this.lostTimes++ + // this.FailTimes++ + // this.FailCoin += totalIn + //} else { + // this.DrawTimes++ + //} + //// } + ////} +} + +// 拉霸游戏 一局一条 +func (this *Player) StaticsLaba(keyGameId string, keyGameFreeId string, totalIn, totalOut int64) { + if this.scene == nil || this.scene.Testing { //测试场|自建房和机器人不统计 + return + } + if totalIn < 0 || totalOut < 0 { + return + } + + if this.IsRob && !this.scene.IsRobFightGame() { + return + } + + if this.TodayGameData == nil { + this.TodayGameData = &model.PlayerGameCtrlData{} + } + if this.TodayGameData.CtrlData == nil { + this.TodayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) + } + + statics := make([]*model.PlayerGameStatics, 0, 4) + //当天数据统计 + //按场次分 + if data, ok := this.TodayGameData.CtrlData[keyGameFreeId]; ok { + statics = append(statics, data) + } else { + gs := model.NewPlayerGameStatics() + this.TodayGameData.CtrlData[keyGameFreeId] = gs + statics = append(statics, gs) + } + //按游戏分 + if data, ok := this.TodayGameData.CtrlData[keyGameId]; ok { + statics = append(statics, data) + } else { + data = model.NewPlayerGameStatics() + this.TodayGameData.CtrlData[keyGameId] = data + statics = append(statics, data) + } + + //按游戏场次进行的统计 + if data, ok := this.GDatas[keyGameFreeId]; ok { + statics = append(statics, &data.Statics) + } else { + data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: *model.NewPlayerGameStatics()} + this.GDatas[keyGameFreeId] = data + statics = append(statics, &data.Statics) + } + if data, ok := this.GDatas[keyGameId]; ok { + statics = append(statics, &data.Statics) + } else { + data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: *model.NewPlayerGameStatics()} + this.GDatas[keyGameId] = data + statics = append(statics, &data.Statics) + } + gain := totalOut - totalIn + for _, data := range statics { + if data != nil { + data.TotalIn += totalIn + data.TotalOut += totalOut + data.GameTimes++ + if gain > 0 { + data.WinGameTimes++ + } else if gain < 0 { + data.LoseGameTimes++ + } else { + data.DrawGameTimes++ + } + } + } + //玩家身上元数据 + this.GameTimes++ + if gain > 0 { + this.winTimes++ + this.WinTimes++ + } else if gain < 0 { + this.lostTimes++ + this.FailTimes++ + } else { + this.DrawTimes++ + } +} + +//func (this *Player) CheckType(gamefreeId, gameId int32) *server.DB_PlayerType { +// types := srvdata.PlayerTypeMgrSington.GetPlayerType(gamefreeId) +// cnt := len(types) +// if cnt > 0 { +// var pgs *model.PlayerGameStatics +// if this.GDatas != nil { +// if d, exist := this.GDatas[strconv.Itoa(int(gameId))]; exist { +// pgs = &d.Statics +// } +// } +// +// //赔率 产出/投入 万分比 +// odds := int64(float64(float64(pgs.TotalOut+1)/float64(pgs.TotalIn+1)) * 10000) +// if odds > 10000000 { +// odds = 10000000 +// } +// for i := 0; i < cnt; i++ { +// t := types[i] +// if t != nil { +// if this.CoinPayTotal >= int64(t.GetPayLowerLimit()) && this.CoinPayTotal <= int64(t.GetPayUpperLimit()) && +// pgs.GameTimes >= int64(t.GetGameTimeLowerLimit()) && pgs.GameTimes <= int64(t.GetGameTimeUpperLimit()) && +// pgs.TotalIn >= int64(t.GetTotalInLowerLimit()) && pgs.TotalIn <= int64(t.GetTotalInUpperLimit()) && +// odds >= int64(t.GetOddsLowerLimit()) && odds <= int64(t.GetOddsUpperLimit()) { +// return t +// } +// } +// } +// } +// return nil +//} + +// 计算玩家赔率 产出/投入 +func (this *Player) LoseRate(gamefreeId, gameId int32) (rate float64) { + rate = -1 + if this.GDatas != nil { + if d, exist := this.GDatas[strconv.Itoa(int(gameId))]; exist { + rate = float64(float64(d.Statics.TotalOut+1) / float64(d.Statics.TotalIn+1)) + return rate + } + } + + return rate +} + +// 计算玩家赔率 产出/投入 +func (this *Player) LoseRateKeyGameid(gameKeyId string) (rate float64) { + rate = -1 + var pgs *model.PlayerGameStatics + if this.GDatas != nil { + if d, exist := this.GDatas[gameKeyId]; exist { + pgs = &d.Statics + } + } + + rate = float64(float64(pgs.TotalOut+1) / float64(pgs.TotalIn+1)) + return +} + +// 是否是新手判定 +// +// func (this *Player) IsFoolPlayerBy(gameId string) { +// if this.IsRob || this.GDatas == nil { +// return +// } +// if this.GDatas[gameId] == nil { +// return +// } +// if this.IsFoolPlayer == nil { +// this.IsFoolPlayer = make(map[string]bool) +// } +// if model.GameParamData.BirdPlayerFlag == false { +// this.IsFoolPlayer[gameId] = false +// return +// } +// playerDate := this.GDatas[gameId] +// //金花游戏局数小于10局并且总产出<100000并且总产出/(总投入+10000)<=2的玩家定义为新手玩家 +// if playerDate.Statics.GameTimes < 10 && playerDate.Statics.TotalOut < 100000 && +// playerDate.Statics.TotalOut/(playerDate.Statics.TotalIn+10000) <= 2 { +// this.IsFoolPlayer[gameId] = true +// } else { +// this.IsFoolPlayer[gameId] = false +// } +// } + +//func (this *Player) PlayerGameNewCheck(gameDiff string) bool { +// if this.IsRob { +// return false +// } +// if this.GDatas == nil { +// return true +// } +// gameId := gameDiff +// if this.GDatas[gameId] == nil { +// return true +// } +// playerDate := this.GDatas[gameId] +// if playerDate.Statics.GameTimes > int64(model.GameParamData.GamePlayerCheckNum) { +// return true +// } else { +// return false +// } +//} + +func (this *Player) SendTrusteeshipTips() { + pack := &player.SCTrusteeshipTips{ + Trusteeship: proto.Int32(this.Trusteeship), + TotalNum: proto.Int32(model.GameParamData.PlayerWatchNum), + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCTrusteeshipTips: ", pack) + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_TRUSTEESHIPTIPS), pack) +} + +func (this *Player) MarshalIParam() []*server.PlayerIParam { + var params []*server.PlayerIParam + for i, v := range this.Iparams { + params = append(params, &server.PlayerIParam{ + ParamId: proto.Int(i), + IntVal: proto.Int64(v), + }) + } + return params +} + +func (this *Player) UnmarshalIParam(params []*server.PlayerIParam) { + for _, p := range params { + this.Iparams[int(p.GetParamId())] = p.GetIntVal() + } +} + +func (this *Player) MarshalSParam() []*server.PlayerSParam { + var params []*server.PlayerSParam + for i, v := range this.sparams { + params = append(params, &server.PlayerSParam{ + ParamId: proto.Int(i), + StrVal: proto.String(v), + }) + } + return params +} + +func (this *Player) UnmarshalSParam(params []*server.PlayerSParam) { + for _, p := range params { + this.sparams[int(p.GetParamId())] = p.GetStrVal() + } +} + +func (this *Player) MarshalCParam() []*server.PlayerCParam { + var params []*server.PlayerCParam + for k, v := range this.cparams { + params = append(params, &server.PlayerCParam{ + StrKey: proto.String(k), + StrVal: proto.String(v), + }) + } + return params +} + +func (this *Player) UnmarshalCParam(params []*server.PlayerCParam) { + for _, p := range params { + this.cparams[p.GetStrKey()] = p.GetStrVal() + } + logger.Logger.Trace("(this *Player) UnmarshalCParam ", this.cparams) +} + +func (this *Player) GetIParam(k int) int64 { + if v, exist := this.Iparams[k]; exist { + return v + } + return 0 +} + +func (this *Player) SetIParam(k int, v int64) { + this.Iparams[k] = v +} + +func (this *Player) GetSParam(k int) string { + if v, exist := this.sparams[k]; exist { + return v + } + return "" +} + +func (this *Player) SetSParam(k int, v string) { + this.sparams[k] = v +} + +func (this *Player) GetCoin() int64 { + return this.Coin +} +func (this *Player) SetCoin(coin int64) { + this.Coin = coin +} + +func (this *Player) GetRankScore(rankType int32) int64 { + return this.RankScore[rankType] +} + +func (this *Player) SetRankScore(rankType int32, score int64) { + this.RankScore[rankType] = score +} + +// func (this *Player) GetStartCoin() int64 { +// return this.StartCoin +// } +// +// func (this *Player) SetStartCoin(startCoin int64) { +// this.StartCoin = startCoin +// } +func (this *Player) GetExtraData() interface{} { + return this.ExtraData +} +func (this *Player) SetExtraData(data interface{}) { + this.ExtraData = data +} +func (this *Player) GetLastOPTimer() time.Time { + return this.LastOPTimer +} +func (this *Player) SetLastOPTimer(lastOPTimer time.Time) { + this.LastOPTimer = lastOPTimer +} +func (this *Player) GetPos() int { + return this.Pos +} +func (this *Player) SetPos(pos int) { + this.Pos = pos +} +func (this *Player) GetGameTimes() int32 { + return this.GameTimes +} +func (this *Player) SetGameTimes(gameTimes int32) { + this.GameTimes = gameTimes +} +func (this *Player) GetWinTimes() int { + return this.winTimes +} +func (this *Player) SetWinTimes(winTimes int) { + this.winTimes = winTimes +} +func (this *Player) GetLostTimes() int { + return this.lostTimes +} +func (this *Player) SetLostTimes(lostTimes int) { + this.lostTimes = lostTimes +} +func (this *Player) GetTotalBet() int64 { + return this.TotalBet +} +func (this *Player) SetTotalBet(totalBet int64) { + this.TotalBet = totalBet +} +func (this *Player) GetCurrentBet() int64 { + return this.CurrentBet +} +func (this *Player) SetCurrentBet(currentBet int64) { + this.CurrentBet = currentBet +} +func (this *Player) GetCurrentTax() int64 { + return this.CurrentTax +} +func (this *Player) SetCurrentTax(currentTax int64) { + this.CurrentTax = currentTax +} +func (this *Player) GetSid() int64 { + return this.sid +} +func (this *Player) SetSid(sid int64) { + this.sid = sid +} +func (this *Player) GetScene() *Scene { + return this.scene +} +func (this *Player) GetGateSess() *netlib.Session { + return this.gateSess +} +func (this *Player) SetGateSess(gateSess *netlib.Session) { + this.gateSess = gateSess +} +func (this *Player) GetWorldSess() *netlib.Session { + return this.worldSess +} +func (this *Player) SetWorldSess(worldSess *netlib.Session) { + this.worldSess = worldSess +} +func (this *Player) GetCurrentCoin() int64 { + return this.currentCoin +} +func (this *Player) SetCurrentCoin(currentCoin int64) { + this.currentCoin = currentCoin +} +func (this *Player) GetTakeCoin() int64 { + return this.takeCoin +} +func (this *Player) SetTakeCoin(takeCoin int64) { + this.takeCoin = takeCoin +} +func (this *Player) GetFlag() int { + return this.flag +} +func (this *Player) SetFlag(flag int) { + this.flag = flag +} +func (this *Player) GetCity() string { + return this.city +} +func (this *Player) SetCity(city string) { + this.city = city +} + +// 附加ai +func (this *Player) AttachAI(ai AI) { + this.ai = ai + ai.SetOwner(this) + ai.OnStart() +} + +// 解除ai +func (this *Player) UnattachAI() { + ai := this.ai + this.ai = nil + ai.SetOwner(nil) + ai.OnStop() +} + +func (this *Player) RobotRandName() { + if this.IsRob { + //if rand.Int31n(100) < 60 { //随机昵称库里的名字 + pool := srvdata.PBDB_NameMgr.Datas.GetArr() + cnt := int32(len(pool)) + if cnt > 0 { + this.Name = pool[rand.Int31n(cnt)].GetName() + } + //} else { + // this.Name = "Guest" + //} + } + return +} + +func (this *Player) RobRandVip() { + if this.IsRob { + dbvip := srvdata.PBDB_VIPMgr.GetData(this.VIP) + if dbvip != nil { + outlines := dbvip.GetRewardOutlineID() + n := len(outlines) + this.HeadOutLine = outlines[rand.Intn(n)] + logger.Logger.Tracef("(this *Player) RobRandVip() %d HeadOutLine=%d", this.SnId, this.HeadOutLine) + this.dirty = true + } + this.Head = rand.Int31n(common.HeadRange) + //0:男 1:女 + this.Sex = (this.Head%2 + 1) % 2 + } +} + +// BlackWhiteOdds 黑白名单调控概率 +func (this *Player) BlackWhiteOdds(gameId int) (int32, bool) { + if this.WBLevel == 0 { + return 0, false + } + data := srvdata.PBDB_BlackWhiteMgr.GetData(int32(gameId)) + if data == nil { + return 0, false + } + var odds int32 + var b bool + if this.WBLevel > 0 && len(data.GetWhiteOdds()) > 0 { + if int(this.WBLevel) > len(data.GetWhiteOdds()) { + odds = data.GetWhiteOdds()[len(data.GetWhiteOdds())-1] + b = true + } else if this.WBLevel-1 >= 0 { + odds = data.GetWhiteOdds()[this.WBLevel-1] + b = true + } + } + if this.WBLevel < 0 && len(data.GetBlackOdds()) > 0 { + if int(-this.WBLevel) > len(data.GetBlackOdds()) { + odds = data.GetBlackOdds()[len(data.GetBlackOdds())-1] + b = true + } else if -this.WBLevel-1 >= 0 { + odds = data.GetBlackOdds()[-this.WBLevel-1] + b = true + } + } + //logger.Logger.Tracef("TienLenSceneData snid(%v) 黑白名单调控 是否启用:%v WBLevel:%v config:%v 概率:%v", this.SnId, b, this.WBLevel, data, odds) + this.TestLog = append(this.TestLog, fmt.Sprintf("黑白名单调控 是否启用:%v WBTotalOut:%v WBTotalIn:%v WBCoinLimit:%v WBLevel:%v config:%v 概率:%v", b, this.WBCoinTotalOut, this.WBCoinTotalIn, this.WBCoinLimit, this.WBLevel, data, odds)) + return odds, b +} + +// NoviceOdds 新手补偿概率 +func (this *Player) NoviceOdds(gameId int) (int32, bool) { + data := srvdata.PBDB_NewPlayerMgr.GetData(int32(gameId)) + if data == nil { + return 0, false + } + + if model.GameParamData.CloseNovice || common.InSliceInt(model.GameParamData.CloseNoviceGame, gameId) { + return 0, false + } + + keyNoviceGameId := common.GetKeyNoviceGameId(gameId) + + var b1, b2 bool + var times, targetTimes int64 + var winCoin, targetWinCoin int64 + switch data.GetCondition1() { + case 1: // 游戏次数 + m := this.GDatas[keyNoviceGameId] + if m == nil { + b1 = true + } else { + b1 = m.Statics.GameTimes < data.GetConditionValue1() + times = m.Statics.GameTimes + } + targetTimes = data.GetConditionValue1() + case 2: // gameId输赢金额 + m := this.GDatas[keyNoviceGameId] + if m == nil { + b1 = true + } else { + b1 = m.Statics.TotalOut-m.Statics.Tax-m.Statics.TotalIn < data.GetConditionValue1() + winCoin = m.Statics.TotalOut - m.Statics.TotalIn + } + targetWinCoin = data.GetConditionValue1() + } + + switch data.GetCondition2() { + case 1: // 游戏次数 + m := this.GDatas[keyNoviceGameId] + if m == nil { + b2 = true + } else { + b2 = m.Statics.GameTimes < data.GetConditionValue2() + times = m.Statics.GameTimes + } + targetTimes = data.GetConditionValue2() + case 2: // gameId输赢金额 + m := this.GDatas[keyNoviceGameId] + if m == nil { + b2 = true + } else { + b2 = m.Statics.TotalOut-m.Statics.Tax-m.Statics.TotalIn < data.GetConditionValue2() + winCoin = m.Statics.TotalOut - m.Statics.TotalIn + } + targetWinCoin = data.GetConditionValue2() + } + + switch data.GetBond() { + case 1: // or + b1 = b1 || b2 + case 2: // and + b1 = b1 && b2 + } + + var odds int + if b1 { + switch data.GetAddType() { + case 1: // 游戏次数 + odds = common.SliceMaxValue([]int{int(data.GetAddMin()), + int(data.GetAddMax() - int64(float64(times)/float64(targetTimes)*float64(data.GetAddMax()-data.GetAddMin())))}) + case 2: // gameId输赢金额 + odds = common.SliceMaxValue([]int{int(data.GetAddMin()), + int(data.GetAddMax() - int64(float64(winCoin)/float64(targetWinCoin)*float64(data.GetAddMax()-data.GetAddMin())))}) + } + odds = common.SliceMinValue([]int{int(data.GetAddMax()), odds}) + } + //logger.Logger.Tracef("TienLenSceneData snid(%v) 新手调控 是否启用:%v times:%v winCoin:%v config:%v 概率:%v", this.SnId, b1, times, winCoin, data, odds) + this.TestLog = append(this.TestLog, fmt.Sprintf("新手调控 是否启用:%v times:%v winCoin:%v config:%v 概率:%v", b1, times, winCoin, data, odds)) + return int32(odds), b1 +} + +/*// 设置玩家捕鱼等级 +func (this *Player) SetFishLevel(level int64) { + data := srvdata.PBDB_PlayerExpMgr.GetData(int32(level)) + if data == nil { + logger.Logger.Errorf("设置玩家等级错误!snid = %v, lvel = %v", this.SnId, level) + return + } + this.FishLevel = level + this.FishExp = int64(data.Exp) +}*/ + +// 增加捕鱼经验 +func (this *Player) AddPlayerExp(exp int64) bool { + this.FishExp += exp + if this.FishExp >= int64(math.MaxInt64) { + this.FishExp = int64(math.MaxInt64) + } + maxExp := srvdata.PBDB_PlayerExpMgr.Datas.Arr[len(srvdata.PBDB_PlayerExpMgr.Datas.Arr)-1].Exp + if this.FishExp > int64(maxExp) { + this.FishExp = int64(maxExp) + } + + oldLevel := this.FishLevel + //获取等级 + for _, playerExp := range srvdata.PBDB_PlayerExpMgr.Datas.Arr { + if this.FishExp >= int64(playerExp.Exp) { + this.FishLevel = int64(playerExp.Id) + } else { + break + } + } + //升级 + if this.FishLevel != oldLevel { + ////通知客户端 + pack := &player.SCPlayerUpLevel{ + Level: this.FishLevel, + Exp: this.FishExp, + } + //通知客户端玩家提升等级 + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUpLevel), pack) + return true + } + return false +} + +// 解锁炮倍 +func (this *Player) UnPlayerPowerEx(power int64) { + logger.Logger.Tracef("解锁炮倍 当前最大解锁炮倍:%v,要解锁的炮倍:%v", this.UnMaxPower, power) + if this.UnPlayerPower(power) { + pack := &player.SCPlayerUnPower{ + UnMaxpower: power, + } + this.UnMaxPower = power + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUnPower), pack) + logger.Logger.Tracef("通知客户端解锁最大炮倍,snid = %v,power = %v", this.SnId, power) + } +} + +// 解锁炮台 +func (this *Player) UnPlayerPowerListEx(powerId int32) { + data := srvdata.PBDB_ArtillerySkinMgr.GetData(powerId) + if data == nil { + return + } + if this.UnPlayerPowerList(powerId) { + //通知客户端解锁炮台 + pack := &player.SCPlayerUnPowerList{ + UnPowerList: powerId, + } + this.SendToClient(int(player.PlayerPacketID_PACKET_SC_PlayerUnPowerList), pack) + logger.Logger.Trace("通知客户端解锁炮台 snid = %v,解锁的炮台:%v,当前已有的炮台 = %v", this.SnId, powerId, this.PowerList) + } + return +} diff --git a/gamesrv/base/playermgr.go b/gamesrv/base/playermgr.go new file mode 100644 index 0000000..5a4db1e --- /dev/null +++ b/gamesrv/base/playermgr.go @@ -0,0 +1,244 @@ +package base + +import ( + server_proto "mongo.games.com/game/protocol/server" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var PlayerMgrSington = &PlayerMgr{ + playerMap: make(map[int64]*Player), + playerSnMap: make(map[int32]*Player), + playerAccountMap: make(map[string]*Player), + // playerNameMap: make(map[string]*Player), +} + +type PlayerMgr struct { + playerMap map[int64]*Player // sid + playerSnMap map[int32]*Player // snid + playerAccountMap map[string]*Player // accoundid + // playerNameMap map[string]*Player +} + +func (this *PlayerMgr) ManagePlayer(player *Player) { + if old, ok := this.playerMap[player.sid]; ok && old != nil { + logger.Logger.Warnf("(this *PlayerMgr) ManagePlayer [playerMap] found sid=%v player exist snid=%v, mysnid=%v", player.sid, old.SnId, player.SnId) + } + this.playerMap[player.sid] = player + + if old, ok := this.playerSnMap[player.SnId]; ok && old != nil { + logger.Logger.Warnf("(this *PlayerMgr) ManagePlayer [playerSnMap] found player exist snid=%v, mysnid=%v", old.SnId, player.SnId) + } + this.playerSnMap[player.SnId] = player + + if old, ok := this.playerAccountMap[player.AccountId]; ok && old != nil { + logger.Logger.Warnf("(this *PlayerMgr) ManagePlayer [playerAccountMap] found player exist snid=%v, mysnid=%v", old.SnId, player.SnId) + } + this.playerAccountMap[player.AccountId] = player + + // if old, ok := this.playerNameMap[player.Name]; ok && old != nil { + // logger.Logger.Warnf("(this *PlayerMgr) ManagePlayer [playerNameMap] found player exist snid=%v, mysnid=%v", old.SnId, player.SnId) + // } + // this.playerNameMap[player.Name] = player +} + +func (this *PlayerMgr) AddPlayer(id int64, data []byte, ws, gs *netlib.Session) *Player { + oldPlayer := this.GetPlayer(id) + testFlag := false + if oldPlayer != nil { + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer found id=%v player exist snid=%v", id, oldPlayer.SnId) + testFlag = true + if oldPlayer.scene != nil { + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer found snid=%v in sceneid=%v", id, oldPlayer.SnId, oldPlayer.scene.SceneId) + if SceneMgrSington.GetScene(oldPlayer.scene.SceneId) != nil { + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer found snid=%v in sceneid=%v SceneMgrSington.GetScene(oldPlayer.scene.sceneId) != nil", id, oldPlayer.SnId, oldPlayer.scene.SceneId) + } + } + this.DelPlayer(id) + } + player := NewPlayer(id, data, ws, gs) + if player == nil { + logger.Logger.Warn("(this *PlayerMgr) NewPlayer player == nil") + return nil + } + if testFlag == true { + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer new snid=%v", player.SnId) + } + //logger.Logger.Trace("(this *PlayerMgr) NewPlayer player = ", player) + this.playerMap[id] = player + this.playerSnMap[player.SnId] = player + this.playerAccountMap[player.AccountId] = player + // this.playerNameMap[player.Name] = player + logger.Logger.Tracef("(this *PlayerMgr) AddPlayer snid:%v ", player.SnId) + return player +} + +func (this *PlayerMgr) DelPlayer(id int64) bool { + player := this.GetPlayer(id) + if player != nil { + delete(this.playerMap, id) + delete(this.playerSnMap, player.SnId) + delete(this.playerAccountMap, player.AccountId) + // delete(this.playerNameMap, player.Name) + logger.Logger.Tracef("(this *PlayerMgr) DelPlayer snid:%v ", player.SnId) + return true + } + return false +} + +func (this *PlayerMgr) DeletePlayers(ids ...int64) { + for _, sid := range ids { + if p, ok := this.playerMap[sid]; ok { + delete(this.playerMap, sid) + if p != nil { + delete(this.playerSnMap, p.SnId) + delete(this.playerAccountMap, p.AccountId) + // delete(this.playerNameMap, p.Name) + } + } + } +} + +func (this *PlayerMgr) DelPlayerBySnId(snid int32) bool { + player := this.GetPlayerBySnId(snid) + if player != nil { + delete(this.playerMap, player.sid) + delete(this.playerSnMap, player.SnId) + delete(this.playerAccountMap, player.AccountId) + // delete(this.playerNameMap, player.Name) + logger.Logger.Tracef("(this *PlayerMgr) DelPlayerBySnId snid:%v ", player.SnId) + return true + } + return false +} + +func (this *PlayerMgr) ReholdPlayer(oldSid, newSid int64, newSess *netlib.Session) { + p := this.GetPlayer(oldSid) + if p != nil { + delete(this.playerMap, oldSid) + this.playerMap[newSid] = p + } +} + +func (this *PlayerMgr) GetPlayer(id int64) *Player { + if pi, ok := this.playerMap[id]; ok { + return pi + } + return nil +} + +func (this *PlayerMgr) GetPlayerBySnId(id int32) *Player { + if pi, ok := this.playerSnMap[id]; ok { + return pi + } + return nil +} + +func (this *PlayerMgr) GetPlayerByAccount(acc string) *Player { + if p, ok := this.playerAccountMap[acc]; ok { + return p + } + return nil +} + +//func (this *PlayerMgr) GetPlayerByName(name string) *Player { +// if p, ok := this.playerNameMap[name]; ok { +// return p +// } +// return nil +//} + +func (this *PlayerMgr) BroadcastMessage(packetid int, rawpack interface{}) bool { + sc := &protocol.BCSessionUnion{ + Bccs: &protocol.BCClientSession{}, + } + pack, err := BroadcastMaker.CreateBroadcastPacket(sc, packetid, rawpack) + if err == nil && pack != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), pack, common.GetSelfAreaId(), srvlib.GateServerType) + return true + } + return false +} + +func (this *PlayerMgr) BroadcastMessageToGroup(packetid int, rawpack interface{}, tags []string) bool { + pack := &server_proto.SSCustomTagMulticast{ + Tags: tags, + } + if byteData, ok := rawpack.([]byte); ok { + pack.RawData = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, rawpack) + if err == nil { + pack.RawData = byteData + } else { + logger.Logger.Info("PlayerMgr.BroadcastMessageToGroup err:", err) + return false + } + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_SS_CUSTOMTAG_MULTICAST), pack, common.GetSelfAreaId(), srvlib.GateServerType) + return true +} + +func (this *PlayerMgr) RebindPlayerSnId(oldSnId, newSnId int32) { + if p, exist := this.playerSnMap[oldSnId]; exist { + delete(this.playerSnMap, oldSnId) + this.playerSnMap[newSnId] = p + } +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *PlayerMgr) ModuleName() string { + return "playermgr" +} + +func (this *PlayerMgr) Init() { +} + +func (this *PlayerMgr) Update() { +} + +func (this *PlayerMgr) Shutdown() { + for _, p := range this.playerMap { + if p.scene != nil { + p.scene.Pause() + // if p.dirty { + // data, err := p.MarshalData(p.scene.gameId) + // if err == nil { + // msgSync := &protocol2.GWPlayerDataSync{ + // Sid: proto.Int64(p.sid), + // PlayerData: data, + // IParams: p.MarshalIParam(), + // SParams: p.MarshalSParam(), + // } + // proto.SetDefaults(msgSync) + // p.worldSess.Send(int(protocol2.MmoPacketID_PACKET_GW_PLAYERDATASYNC), msgSync) + // } + // } + } + } + module.UnregisteModule(this) +} +func (this *PlayerMgr) OnDayTimer() { + //在线跨天 数据给昨天,今天置为空 + for _, p := range this.playerMap { + if p != nil && !p.IsRob { + p.OnDayTimer() + } + } +} + +//////////////////////////////////////////////////////////////////// +/// Module Implement [end] +//////////////////////////////////////////////////////////////////// + +func init() { + module.RegisteModule(PlayerMgrSington, time.Second, 0) +} diff --git a/gamesrv/base/replay_recorder.go b/gamesrv/base/replay_recorder.go new file mode 100644 index 0000000..3c2f777 --- /dev/null +++ b/gamesrv/base/replay_recorder.go @@ -0,0 +1,174 @@ +package base + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "time" + + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +const ( + ReplayServerType int = 8 + ReplayServerId = 801 +) + +var _replayIgnorePacketIds = map[int]bool{} + +type ReplayRecorder struct { + rs *server.ReplaySequene + id string + Logid string + has bool + start bool + needRec bool +} + +func RegisteReplayIgnorePacketId(ids ...int) { + for _, id := range ids { + _replayIgnorePacketIds[id] = true + } +} + +func NewReplayRecorder(id string) *ReplayRecorder { + rr := &ReplayRecorder{ + rs: &server.ReplaySequene{}, + id: id, + } + return rr +} + +func (this *ReplayRecorder) Reset(needRec bool) { + this.needRec = needRec + this.rs.Sequenes = nil +} + +func (this *ReplayRecorder) Init(packid int, pack interface{}) { + this.Record(-1, -1, packid, pack) + this.start = true +} + +func (this *ReplayRecorder) Record(pos, excludePos int, packid int, pack interface{}, force ...bool) { + //纯机器人对战可能不需要记录录像 + if !this.needRec { + return + } + if this.start { + //过滤掉中间玩家掉线重新上线的消息 + if _, exist := _replayIgnorePacketIds[packid]; exist { + if len(force) == 0 || force[0] == false { + return + } + } + } + var data []byte + var err error + if model.GameParamData.ReplayDataUseJson { + data, err = json.Marshal(pack) + } else { + data, err = netlib.Gpb.Marshal(pack) + } + if err == nil { + rr := &server.ReplayRecord{ + TimeStamp: proto.Int64(time.Now().Unix()), + Pos: proto.Int(pos), + ExcludePos: proto.Int(excludePos), + PacketId: proto.Int(packid), + } + this.rs.Sequenes = append(this.rs.Sequenes, rr) + if model.GameParamData.ReplayDataUseJson { + rr.StrData = proto.String(string(data[:])) + } else { + rr.BinData = data + } + this.has = true + } +} + +func (this *ReplayRecorder) Fini(s *Scene) { + if this.has && len(this.rs.Sequenes) > 10 { + pack := &server.GRReplaySequene{ + Name: proto.String(this.id), + // todo dev + //Rec: this.rs, + LogId: proto.String(this.Logid), + GameId: proto.Int32(s.DbGameFree.GetGameId()), + RoomMode: proto.Int32(s.DbGameFree.GetGameMode()), + NumOfGames: proto.Int(s.NumOfGames), + Platform: proto.String(s.Platform), + DatasVer: proto.Int32(s.rrVer), + GameFreeid: proto.Int32(s.GetGameFreeId()), + RoomId: proto.Int(s.SceneId), + } + if s.ClubId != 0 { + pack.ClubId = proto.Int32(s.ClubId) + pack.ClubRoom = proto.String(s.RoomId) + } + for _, player := range s.Players { + if player != nil { + pack.Channel = proto.String(player.Channel) + pack.Promoter = proto.String(player.BeUnderAgentCode) + break + } + } + LogChannelSingleton.WriteMQData(&model.RabbitMQData{MQName: "log_playback", Data: pack}) + } +} + +func (this *ReplayRecorder) GetId() string { + return this.id +} + +// //////////////////////////////////////////////// +// 内存落地 +type ReplayRecorderPO struct { + RS *server.ReplaySequene + Id string + LogId string + Has bool + Start bool +} + +// 序列化 +func (this *ReplayRecorder) Marshal() ([]byte, error) { + po := ReplayRecorderPO{ + RS: this.rs, + Id: this.id, + LogId: this.Logid, + Has: this.has, + Start: this.start, + } + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(&po) + if err != nil { + logger.Logger.Warnf("(this *ReplayRecorder) Marshal() %v gob.Encode err:%v", this.id, err) + return nil, err + } + return buf.Bytes(), nil +} + +// 反序列化 +func (this *ReplayRecorder) Unmarshal(data []byte) error { + po := &ReplayRecorderPO{} + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + err := dec.Decode(po) + if err != nil { + logger.Logger.Warnf("(this *ReplayRecorder) Unmarshal gob.Decode err:%v", err) + return err + } + this.rs = po.RS + this.id = po.Id + this.Logid = po.LogId + this.has = po.Has + this.start = po.Start + return nil +} + +////////////////////////////////////////////////// diff --git a/gamesrv/base/robotagent.go b/gamesrv/base/robotagent.go new file mode 100644 index 0000000..5ea4833 --- /dev/null +++ b/gamesrv/base/robotagent.go @@ -0,0 +1,96 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" +) + +/* + 给机器人服务发消息 +*/ + +var NpcServerAgentSingleton = &NpcServerAgent{} + +type NpcServerAgent struct { +} + +func (nsa *NpcServerAgent) OnConnected() { +} + +func (nsa *NpcServerAgent) OnDisconnected() { + RobotSceneDBGameFreeSync = make(map[int]bool) +} + +func (nsa *NpcServerAgent) OnPlayerEnterScene(s *Scene, p *Player) { + logger.Logger.Trace("(nsa *NpcServerAgent) OnPlayerEnterScene") +} + +func (nsa *NpcServerAgent) OnPlayerLeaveScene(s *Scene, p *Player) { + logger.Logger.Trace("(nsa *NpcServerAgent) OnPlayerLeaveScene") +} + +func (nsa *NpcServerAgent) OnSceneClose(s *Scene) { + logger.Logger.Trace("(nsa *NpcServerAgent) OnSceneClose") +} + +func (nsa *NpcServerAgent) GetSession() *netlib.Session { + return srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), common.RobotServerType, common.RobotServerId) +} + +func (nsa *NpcServerAgent) sendPacket(packetId int, pack interface{}) bool { + s := nsa.GetSession() + if s != nil { + s.Send(packetId, pack) + return true + } + return false +} + +func (nsa *NpcServerAgent) SyncDBGameFree(roomId int, DBGameFree *server.DB_GameFree) { + if !RobotSceneDBGameFreeSync[roomId] { + pack := &server.GRGameFreeData{ + RoomId: proto.Int(roomId), + DBGameFree: DBGameFree, + } + if nsa.sendPacket(int(server.SSPacketID_PACKET_GR_GameFreeData), pack) { + RobotSceneDBGameFreeSync[roomId] = true + } + } +} + +// Invite 邀请机器人 +func (nsa *NpcServerAgent) Invite(roomId, cnt int, gameFreeId int32) bool { + //logger.Logger.Trace("(nsa *NpcServerAgent) Invite", roomId, cnt, isAgent, gameFreeId) + s := nsa.GetSession() + if s == nil { + return false + } + pack := &server.WRInviteRobot{ + RoomId: proto.Int(roomId), + MatchId: proto.Int32(gameFreeId), + Cnt: proto.Int(cnt), + } + return s.Send(int(server.SSPacketID_PACKET_WR_INVITEROBOT), pack) +} + +// MatchInvite 比赛场邀请机器人 +func (nsa *NpcServerAgent) MatchInvite(roomId, matchId int32, platform string, cnt int32, needAwait bool) bool { + //logger.Logger.Trace("(nsa *NpcServerAgent) MatchInvite", roomId, matchId, platform, cnt, needAwait) + s := nsa.GetSession() + if s == nil { + return false + } + pack := &server.WRInviteRobot{ + RoomId: proto.Int32(roomId), + MatchId: proto.Int32(matchId), + Cnt: proto.Int32(cnt), + Platform: proto.String(platform), + IsMatch: proto.Bool(true), + NeedAwait: proto.Bool(needAwait), + } + return s.Send(int(server.SSPacketID_PACKET_WR_INVITEROBOT), pack) +} diff --git a/gamesrv/base/scene.go b/gamesrv/base/scene.go new file mode 100644 index 0000000..85ef62b --- /dev/null +++ b/gamesrv/base/scene.go @@ -0,0 +1,2776 @@ +package base + +import ( + "bytes" + "fmt" + "math/rand" + "strconv" + "strings" + "time" + + rawproto "google.golang.org/protobuf/proto" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + "mongo.games.com/goserver/core/utils" + srvlibproto "mongo.games.com/goserver/srvlib/protocol" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/player" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +const ( + SCENE_STATE_INITED int = iota + SCENE_STATE_RUNNING + SCENE_STATE_OVER +) + +const ReplayIdTf = "20060102150405" + +var sceneRandSeed = time.Now().UnixNano() +var RobotSceneDBGameFreeSync = make(map[int]bool) + +type GameScene interface { + SceneDestroy(force bool) +} + +type CanRebindSnId interface { + RebindPlayerSnId(oldSnId, newSnId int32) +} + +// 房间比赛数据变化 +type SceneMatchChgData struct { + NextBaseScore int32 //底分 + NextOutScore int32 //淘汰分 +} + +type Scene struct { + ws *netlib.Session // 大厅服 + Rand *rand.Rand // 随机数生成器 + ExtraData interface{} // 房间数据 + matchData interface{} // 比赛房数据 + aiMgr AIMgr // + WithLocalAI bool // + SceneId int // 房间id + GameId int // 游戏模式id + GameMode int // 弃用了,都是0 + SceneMode int // 房间模式,如:公共房间 common.SceneMode_Public + SceneType int // 场次,新手场,中级场... + Platform string // 平台id + Params []int32 + paramsEx []int32 + Creator int32 + agentor int32 + hallId int32 + replayCode string + disbandGen int //第几次解散申请 + disbandParam []int64 //解散参数 + disbandPos int32 //发起解散的玩家位置 + disbandTs int64 //解散发起时间戳 + playerNum int //游戏人数 + realPlayerNum int //真实玩家人数 + robotNum int //机器人数量 + robotLimit int //最大限制机器人数量 + robotNumLastInvite int //上次邀请机器人时的数量 + TotalOfGames int //游戏总局数 + NumOfGames int //局数 + Players map[int32]*Player //参与者 + audiences map[int32]*Player //观众 + sp ScenePolicy //场景游戏策略 + //mp MatchPolicy //场景比赛策略 + rr *ReplayRecorder //回放记录器 + rrVer int32 //录像的协议版本号 + DbGameFree *server.DB_GameFree //自由场数据 + SceneState SceneState //场景状态 + hDisband timer.TimerHandle //解散handle + StateStartTime time.Time //状态开始时间 + stateEndTime time.Time //状态结束时间 + GameStartTime time.Time //游戏开始计时时间 + GameNowTime time.Time //当局游戏开始时间 + nextInviteTime time.Time //下次邀请机器人时间 + inviteInterval int64 //邀请间隔 + pause bool + Gaming bool + destroyed bool + completed bool + Testing bool //是否为测试场 + graceDestroy bool //等待销毁 + replayAddId int32 + KeyGameId string //游戏类型唯一ID + KeyGamefreeId string //游戏场次唯一id + GroupId int32 //分组id + bEnterAfterStart bool //是否允许中途加入 + ClubId int32 + RoomId string //俱乐部那个包间 + RoomPos int32 //房间桌号 + PumpCoin int32 //抽水比例,同一个俱乐部下面的抽水比例是一定的,百分比 + DealyTime int64 //结算延时时间 + CpCtx model.CoinPoolCtx //水池环境 + CpControlled bool //被水池控制了 + timerRandomRobot int64 + nogDismiss int //检查机器人离场时的局数(同一局只检查一次) + //playerStatement map[int32]*webapi.PlayerStatement //玩家流水记录 + SystemCoinOut int64 //本局游戏机器人营收 机器人赢:正值 机器人输:负值 + matchChgData *SceneMatchChgData //比赛变化数据 + ChessRank []int32 + + LoopNum int // 循环计数 + results []int // 本局游戏结果 + WebUser string // 操作人 + resultHistory [][]int // 记录数 [控制结果,局数...] + BaseScore int32 //tienlen游戏底分 + MatchId int32 //标记本次比赛的id,并不是后台id + MatchFinals bool //比赛场决赛 + MatchRound int32 + MatchCurPlayerNum int32 + MatchNextNeed int32 + MatchType int32 // 0.普通场 1.锦标赛 2.冠军赛 3.vip专属 + MatchStop bool + RealCtrl bool + Novice bool + Welfare bool + KillPoints bool +} + +func NewScene(ws *netlib.Session, sceneId, gameMode, sceneMode, gameId int, platform string, params []int32, + agentor, creator int32, replayCode string, hallId, groupId, totalOfGames int32, dbGameFree *server.DB_GameFree, bEnterAfterStart bool, baseScore int32, playerNum int, cherank []int32, paramsEx ...int32) *Scene { + sp := GetScenePolicy(gameId, gameMode) + if sp == nil { + logger.Logger.Errorf("Game id %v not register in ScenePolicyPool.", gameId) + return nil + } + tNow := time.Now() + s := &Scene{ + ws: ws, + SceneId: sceneId, + GameId: gameId, + GameMode: gameMode, + SceneMode: sceneMode, + SceneType: int(dbGameFree.GetSceneType()), + Params: params, + paramsEx: paramsEx, + Creator: creator, + agentor: agentor, + replayCode: replayCode, + Players: make(map[int32]*Player), + audiences: make(map[int32]*Player), + sp: sp, + hDisband: timer.TimerHandle(0), + GameStartTime: tNow, + hallId: hallId, + Platform: platform, + DbGameFree: dbGameFree, + inviteInterval: model.GameParamData.RobotInviteInitInterval, + GroupId: groupId, + bEnterAfterStart: bEnterAfterStart, + TotalOfGames: int(totalOfGames), + results: make([]int, common.MaxLoopNum), + BaseScore: baseScore, + playerNum: playerNum, + ChessRank: cherank, + } + if s != nil && s.init() { + logger.Logger.Trace("NewScene init success.") + if !s.Testing { + s.rrVer = ReplayRecorderVer[gameId] + s.RecordReplayStart() + } + return s + } else { + logger.Logger.Trace("NewScene init failed.") + return nil + } +} + +//func (this *Scene) BindAIMgr(aimgr AIMgr) { +// this.aiMgr = aimgr +//} + +// 根据gamedifid,转为gameid,然后返回所有的相同gameid的数据 +func (this *Scene) GetTotalTodayDaliyGameData(keyGameId string, pd *Player) *model.PlayerGameStatics { + todayData := model.NewPlayerGameStatics() + + if pd.TodayGameData == nil { + return todayData + } + + if pd.TodayGameData.CtrlData == nil { + return todayData + } + + if info, ok := pd.TodayGameData.CtrlData[keyGameId]; ok { + todayData.TotalIn += info.TotalIn + todayData.TotalOut += info.TotalOut + } + + return todayData +} + +func (this *Scene) RebindPlayerSnId(oldSnId, newSnId int32) { + if p, exist := this.Players[oldSnId]; exist { + delete(this.Players, oldSnId) + this.Players[newSnId] = p + } + if p, exist := this.audiences[oldSnId]; exist { + delete(this.audiences, oldSnId) + this.audiences[newSnId] = p + } + if rebind, ok := this.ExtraData.(CanRebindSnId); ok { + rebind.RebindPlayerSnId(oldSnId, newSnId) + } +} + +func (this *Scene) GetInit() bool { + return this.init() +} +func (this *Scene) init() bool { + tNow := time.Now() + sceneRandSeed++ + this.Rand = rand.New(rand.NewSource(sceneRandSeed)) + this.nextInviteTime = tNow.Add(time.Second * time.Duration(this.Rand.Int63n(model.GameParamData.RobotInviteInitInterval))) + this.RandRobotCnt() + + if len(this.paramsEx) != 0 { + if this.IsMatchScene() { + //this.mp = GetMatchPolicy(this.gameId) + baseScore := this.GetParamEx(common.PARAMEX_MATCH_BASESCORE) + this.DbGameFree.BaseScore = proto.Int32(baseScore) + } else { + if this.DbGameFree.GetSceneType() == -1 { + this.Testing = true + } else { + this.Testing = false + } + } + + //this.keyGameId = strconv.Itoa(int(this.dbGameFree.GetGameId())) + this.KeyGameId = this.DbGameFree.GetGameDif() + this.KeyGamefreeId = strconv.Itoa(int(this.DbGameFree.GetId())) + } + // test + //for i := 0; i < 100; i++ { + // n := this.rand.Intn(10) + // r := this.rand.Intn(3) + 1 + // str := fmt.Sprint(this.rand.Intn(1000), ":", r) + // for j := 0; j < n; j++ { + // str += fmt.Sprint(",", this.rand.Intn(1000), ":", r) + // } + // logger.Logger.Trace("--> str ", str) + // this.ParserResults1(str, "") + //} + // test + return true +} + +func (this *Scene) GetParam(idx int) int32 { + if idx < 0 || idx >= len(this.Params) { + return -1 + } + + return this.Params[idx] +} + +func (this *Scene) GetBetMap() []int64 { + return this.DbGameFree.GetOtherIntParams() +} + +func (this *Scene) IsDisbanding() bool { + return this.hDisband != timer.TimerHandle(0) +} + +func (this *Scene) GetParamEx(idx int) int32 { + if idx < 0 || idx > len(this.paramsEx) { + return -1 + } + + return this.paramsEx[idx] +} + +func (this *Scene) SetParamEx(idx int, val int32) { + cnt := len(this.paramsEx) + if idx >= 0 && idx < cnt { + this.paramsEx[idx] = val + } +} + +func (this *Scene) GetMatchTotalOfGame() int { + return int(this.GetParamEx(common.PARAMEX_MATCH_NUMOFGAME)) +} + +func (this *Scene) GetMatchBaseScore() int32 { + return this.GetParamEx(common.PARAMEX_MATCH_BASESCORE) +} + +func (this *Scene) GetGameFreeId() int32 { + return this.DbGameFree.Id +} +func (this *Scene) GetDBGameFree() *server.DB_GameFree { + return this.DbGameFree +} +func (this *Scene) GetPlatform() string { + return this.Platform +} +func (this *Scene) GetKeyGameId() string { + return this.KeyGameId +} +func (this *Scene) SetKeyGameId(keyGameId string) { + this.KeyGameId = keyGameId +} +func (this *Scene) GetSceneId() int { + return this.SceneId +} +func (this *Scene) SetSceneId(sceneId int) { + this.SceneId = sceneId +} +func (this *Scene) GetGroupId() int32 { + return this.GroupId +} +func (this *Scene) SetGroupId(groupId int32) { + this.GroupId = groupId +} +func (this *Scene) GetExtraData() interface{} { + return this.ExtraData +} +func (this *Scene) SetExtraData(data interface{}) { + this.ExtraData = data +} +func (this *Scene) GetSceneState() SceneState { + return this.SceneState +} +func (this *Scene) SetSceneState(state SceneState) { + this.SceneState = state +} +func (this *Scene) GetGameId() int { + return this.GameId +} +func (this *Scene) SetGameId(gameId int) { + this.GameId = gameId +} +func (this *Scene) GetPlayerNum() int { + return this.playerNum +} +func (this *Scene) SetPlayerNum(playerNum int) { + this.playerNum = playerNum +} +func (this *Scene) GetGameMode() int { + return this.GameMode +} +func (this *Scene) SetGameMode(gameMode int) { + this.GameMode = gameMode +} +func (this *Scene) GetGaming() bool { + return this.Gaming +} +func (this *Scene) SetGaming(gaming bool) { + this.Gaming = gaming +} +func (this *Scene) GetTesting() bool { + return this.Testing +} +func (this *Scene) SetTesting(testing bool) { + this.Testing = testing +} +func (this *Scene) GetCreator() int32 { + return this.Creator +} +func (this *Scene) SetCreator(creator int32) { + this.Creator = creator +} +func (this *Scene) GetSceneMode() int { + return this.SceneMode +} +func (this *Scene) SetSceneMode(sceneMode int) { + this.SceneMode = sceneMode +} +func (this *Scene) GetParams() []int32 { + return this.Params +} +func (this *Scene) SetParams(params []int32) { + this.Params = params +} +func (this *Scene) GetParamsEx() []int32 { + return this.paramsEx +} +func (this *Scene) SetParamsEx(paramsEx []int32) { + this.paramsEx = paramsEx +} +func (this *Scene) GetStateStartTime() time.Time { + return this.StateStartTime +} +func (this *Scene) SetStateStartTime(stateStartTime time.Time) { + this.StateStartTime = stateStartTime +} +func (this *Scene) GetGameStartTime() time.Time { + return this.GameStartTime +} +func (this *Scene) SetGameStartTime(gameStartTime time.Time) { + this.GameStartTime = gameStartTime +} +func (this *Scene) GetGameNowTime() time.Time { + return this.GameNowTime +} +func (this *Scene) SetGameNowTime(gameNowTime time.Time) { + this.GameNowTime = gameNowTime +} +func (this *Scene) GetNumOfGames() int { + return this.NumOfGames +} +func (this *Scene) SetNumOfGames(numOfGames int) { + this.NumOfGames = numOfGames +} +func (this *Scene) GetCpCtx() model.CoinPoolCtx { + return this.CpCtx +} +func (this *Scene) SetCpCtx(cpCtx model.CoinPoolCtx) { + this.CpCtx = cpCtx +} +func (this *Scene) GetAudiences() map[int32]*Player { + return this.audiences +} +func (this *Scene) GetAgentor() int32 { + return this.agentor +} +func (this *Scene) SetAgentor(agentor int32) { + this.agentor = agentor +} +func (this *Scene) GetDisbandGen() int { + return this.disbandGen +} +func (this *Scene) SetDisbandGen(disbandGen int) { + this.disbandGen = disbandGen +} +func (this *Scene) GetScenePolicy() ScenePolicy { + return this.sp +} +func (this *Scene) SetScenePolicy(sp ScenePolicy) { + this.sp = sp +} +func (this *Scene) GetGraceDestroy() bool { + return this.graceDestroy +} +func (this *Scene) SetGraceDestroy(graceDestroy bool) { + this.graceDestroy = graceDestroy +} +func (this *Scene) GetMatchChgData() *SceneMatchChgData { + return this.matchChgData +} +func (this *Scene) SetMatchChgData(matchChgData *SceneMatchChgData) { + this.matchChgData = matchChgData +} + +func (this *Scene) GetCpControlled() bool { + return this.CpControlled +} +func (this *Scene) SetCpControlled(cpControlled bool) { + this.CpControlled = cpControlled +} + +func (this *Scene) GetSystemCoinOut() int64 { + return this.SystemCoinOut +} +func (this *Scene) SetSystemCoinOut(systemCoinOut int64) { + this.SystemCoinOut = systemCoinOut +} + +func (this *Scene) GetBEnterAfterStart() bool { + return this.bEnterAfterStart +} +func (this *Scene) SetBEnterAfterStart(bEnterAfterStart bool) { + this.bEnterAfterStart = bEnterAfterStart +} + +func (this *Scene) GetTimerRandomRobot() int64 { + return this.timerRandomRobot +} +func (this *Scene) SetTimerRandomRobot(timerRandomRobot int64) { + this.timerRandomRobot = timerRandomRobot +} + +func (this *Scene) GetDestroyed() bool { + return this.destroyed +} +func (this *Scene) SetDestroyed(destroyed bool) { + this.destroyed = destroyed +} + +// //////////////////////////////////////////////// +func (this *Scene) OnStart() { + logger.Logger.Trace("Scene on start.") + this.sp.OnStart(this) + this.TryInviteRobot() +} + +func (this *Scene) OnStop() { + logger.Logger.Trace("Scene on stop.") + this.sp.OnStop(this) +} + +func (this *Scene) OnTick() { + if !this.pause { + this.TryInviteRobot() + this.sp.OnTick(this) + } +} +func (this *Scene) SendRoomType(p *Player) { + //通知客户端 当前房间类型 + //RoomSign := &protocol.SCClubRoomSign{ + // ClubId: proto.Int64(this.ClubId), + //} + //proto.SetDefaults(RoomSign) + //logger.Logger.Trace("RoomSign: ", RoomSign) + //p.SendToClient(int(protocol.MmoPacketID_PACKET_SC_CLUB_ROOMSIGN), RoomSign) +} + +////////////////////////////////////////////////// + +func (this *Scene) PlayerEnter(p *Player, isLoaded bool) { + logger.Logger.Trace("(this *Scene) PlayerEnter:", p.SnId, isLoaded, this.SceneId, p.GetName()) + this.Players[p.SnId] = p + p.scene = this + + pack := &gamehall.SCEnterRoom{ + GameId: proto.Int(this.GameId), + ModeType: proto.Int(this.GameMode), + RoomId: proto.Int(this.SceneId), + OpRetCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + Params: []int32{}, + ClubId: proto.Int32(this.ClubId), + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERROOM), pack) + + if p.IsRob { + this.robotNum++ + logger.Logger.Tracef("(this *Scene) PlayerEnter(%v) robot(%v) robotlimit(%v)", this.DbGameFree.GetName()+this.DbGameFree.GetTitle(), this.robotNum, this.robotLimit) + } else { + p.Trusteeship = 0 + p.ValidCacheBetTotal = 0 + this.realPlayerNum++ + this.RandRobotCnt() + } + + p.OnEnter(this) + p.SyncFlagToWorld() + if !isLoaded && !p.IsRob { //等待玩家加载 + p.MarkFlag(PlayerState_Leave) + } + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnPlayerEnter(this, p) }) + + if p.BlackLevel > 0 { + WarningBlackPlayer(p.SnId, this.DbGameFree.Id) + } + + this.ResetNextInviteTime() +} + +func (this *Scene) PlayerLeave(p *Player, reason int, isBill bool) { + logger.Logger.Trace("===(this *Scene) PlayerLeave ", p.SnId, reason, isBill) + //logger.Logger.Trace(utils.GetCallStack()) + if _, exist := this.Players[p.SnId]; !exist { + logger.Logger.Warnf("(this *Scene) PlayerLeave(%v) no found in scene(%v)", p.SnId, this.SceneId) + return + } + + //当前状态不能离场 + if !this.CanChangeCoinScene(p) && !this.destroyed { + pack := &gamehall.SCLeaveRoom{ + //OpRetCode: p.opCode, //protocol.OpResultCode_OPRC_Hundred_YouHadBetCannotLeave, + OpRetCode: gamehall.OpResultCode_Game(p.OpCode), + RoomId: proto.Int(this.SceneId), + } + if pack.GetOpRetCode() == gamehall.OpResultCode_Game_OPRC_Sucess_Game { + //不能这么做,机器人有特殊判定 + //pack.OpRetCode = gamehall.OpResultCode_OPRC_Error + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + logger.Logger.Tracef("(this *Scene) Cant PlayerLeave(%v) no found in scene(%v)", p.SnId, this.SceneId) + return + } + + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnPlayerLeave(this, p, reason) }) + + p.OnLeave(reason) + delete(this.Players, p.SnId) + isBill = true + + //send world离开房间 + pack := &server.GWPlayerLeave{ + RoomId: proto.Int(this.SceneId), + PlayerId: proto.Int32(p.SnId), + Reason: proto.Int(reason), + ServiceFee: proto.Int64(p.serviceFee), + GameTimes: proto.Int32(p.GameTimes), + BetCoin: proto.Int64(p.TotalBet), + WinTimes: proto.Int(p.winTimes), + LostTimes: proto.Int(p.lostTimes), + TotalConvertibleFlow: proto.Int64(p.TotalConvertibleFlow), + ValidCacheBetTotal: proto.Int64(p.ValidCacheBetTotal), + MatchId: proto.Int32(this.MatchId), + CurIsWin: proto.Int64(p.CurIsWin), // 负数:输 0:平局 正数:赢 + } + matchRobotGrades := p.MatchRobotGrades + if matchRobotGrades != nil { + pack.MatchRobotGrades = make(map[int32]int32) + for _, gradeInfo := range matchRobotGrades { + pack.MatchRobotGrades[gradeInfo.CopySnid] = proto.Int32(gradeInfo.Grade) + } + p.MatchRobotGrades = nil + } + + pack.ReturnCoin = proto.Int64(p.Coin) + if this.Testing { + pack.ReturnCoin = proto.Int64(p.takeCoin) + } + pack.GameCoinTs = proto.Int64(p.GameCoinTs) + if !p.IsLocal { + data, err := p.MarshalData(this.GameId) + if err == nil { + pack.PlayerData = data + } + } + + pack.Items = make(map[int32]int32) + for id, num := range p.Items { + pack.Items[id] = proto.Int32(num) + } + pack.RankScore = make(map[int32]int64) + for k, v := range p.RankScore { + pack.RankScore[k] = v + } + + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_PLAYERLEAVE), pack) + logger.Logger.Tracef("(this *Scene) PlayerLeave(%v) success reason %v", p.SnId, reason) + + if p.IsRob { + this.robotNum-- + if this.robotNum != len(this.Players) { + var num int + for _, p2 := range this.Players { + if p2.IsRob { + num++ + } + } + this.robotNum = num + } + logger.Logger.Tracef("(this *Scene) PlayerLeave(%v) robot(%v) robotlimit(%v)", this.DbGameFree.GetName()+this.DbGameFree.GetTitle(), this.robotNum, this.robotLimit) + } else { + this.realPlayerNum-- + this.RandRobotCnt() + } + + this.ResetNextInviteTime() +} + +func (this *Scene) AudienceEnter(p *Player, isload bool) { + logger.Logger.Trace("(this *Scene) AudienceEnter") + this.audiences[p.SnId] = p + p.scene = this + p.MarkFlag(PlayerState_Audience) + pack := &gamehall.SCEnterRoom{ + GameId: proto.Int(this.GameId), + ModeType: proto.Int(this.GameMode), + RoomId: proto.Int(this.SceneId), + OpRetCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERROOM), pack) + + p.OnAudienceEnter(this) + if !isload && !p.IsRob { + p.MarkFlag(PlayerState_Leave) + } + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceEnter(this, p) }) +} + +func (this *Scene) AudienceLeave(p *Player, reason int) { + logger.Logger.Trace("(this *Scene) AudienceLeave") + //当前状态不能离场 + if !this.CanChangeCoinScene(p) { + pack := &gamehall.SCLeaveRoom{ + OpRetCode: (gamehall.OpResultCode_Game(p.OpCode)), //protocol.OpResultCode_OPRC_Hundred_YouHadBetCannotLeave, + RoomId: proto.Int(this.SceneId), + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + return + } + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceLeave(this, p, reason) }) + p.OnAudienceLeave(reason) + delete(this.audiences, p.SnId) + //send world离开房间 + pack := &server.GWPlayerLeave{ + RoomId: proto.Int(this.SceneId), + PlayerId: proto.Int32(p.SnId), + Reason: proto.Int(reason), + TotalConvertibleFlow: proto.Int64(p.TotalConvertibleFlow), + ValidCacheBetTotal: proto.Int64(p.ValidCacheBetTotal), + } + pack.ReturnCoin = proto.Int64(p.Coin) + if this.Testing { + pack.ReturnCoin = proto.Int64(p.takeCoin) + } + pack.GameCoinTs = proto.Int64(p.GameCoinTs) + // if p.dirty { + // data, err := p.MarshalData(this.gameId) + // if err == nil { + // pack.PlayerData = data + // } + // } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_AUDIENCELEAVE), pack) +} + +func (this *Scene) AudienceSit(p *Player) { + logger.Logger.Trace("(this *Scene) AudienceSit") + if _, exist := this.audiences[p.SnId]; exist { + delete(this.audiences, p.SnId) + + this.Players[p.SnId] = p + p.scene = this + + p.OnEnter(this) + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceSit(this, p) }) + } +} + +func (this *Scene) HasPlayer(p *Player) bool { + if p == nil { + return false + } + + if pp, ok := this.Players[p.SnId]; ok && pp == p { + return true + } + return false +} + +func (this *Scene) HasAudience(p *Player) bool { + if p == nil { + return false + } + + if pp, ok := this.audiences[p.SnId]; ok && pp == p { + return true + } + return false +} + +func (this *Scene) GetPlayer(id int32) *Player { + if p, exist := this.Players[id]; exist { + return p + } + return nil +} + +func (this *Scene) GetPlayerByPos(pos int) *Player { + for _, p := range this.Players { + if p.Pos == pos { + return p + } + } + return nil +} + +func (this *Scene) PlayerDropLine(snid int32) { + logger.Logger.Trace("(this *Scene) PlayerDropLine") + if p, exist := this.Players[snid]; exist { + p.OnDropLine() + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnPlayerDropLine(this, p) }) + } else if p, exist := this.audiences[snid]; exist { + p.OnAudienceDropLine() + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceDropLine(this, p) }) + } +} + +func (this *Scene) PlayerRehold(snid int32, sid int64, gs *netlib.Session) { + logger.Logger.Trace("(this *Scene) PlayerRehold") + if p, exist := this.Players[snid]; exist { + p.OnRehold(sid, gs) + //if !p.IsRob { + // p.trusteeship = 0 + //} + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnPlayerRehold(this, p) }) + } else if p, exist := this.audiences[snid]; exist { + p.OnRehold(sid, gs) + //if !p.IsRob { + // p.trusteeship = 0 + //} + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceEnter(this, p) }) + } +} + +func (this *Scene) PlayerReturn(p *Player, isLoaded bool) { + logger.Logger.Trace("(this *Scene) PlayerReturn") + pack := &gamehall.SCReturnRoom{ + RoomId: proto.Int(this.SceneId), + GameId: proto.Int(this.GameId), + ModeType: proto.Int(this.GameMode), + Params: this.Params, + HallId: proto.Int32(this.hallId), + IsLoaded: proto.Bool(isLoaded), + OpRetCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + ClubId: proto.Int32(this.ClubId), + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_RETURNROOM), pack) + logger.Logger.Tracef("Scene.PlayerReturn %v", pack) + //if !p.IsRob { + // p.trusteeship = 0 + //} + if this.HasPlayer(p) { + //避免游戏接口异常 + //utils.RunPanicless(func() { this.sp.OnPlayerRehold(this, p) }) + //这里应该调用 return消息 因为在上面rehold的消息已经处理过了 + utils.RunPanicless(func() { this.sp.OnPlayerReturn(this, p) }) + } else if this.HasAudience(p) { + //避免游戏接口异常 + utils.RunPanicless(func() { this.sp.OnAudienceEnter(this, p) }) + } + if !p.IsRob { //等待玩家加载 + if isLoaded { + p.UnmarkFlag(PlayerState_Leave) + } else { + p.MarkFlag(PlayerState_Leave) + } + } + if this.IsMatchScene() { + p.SetIParam(common.PlayerIParam_IsQuit, 0) + p.UnmarkFlag(PlayerState_MatchQuit) + } +} + +// 广播消息 +func (this *Scene) Broadcast(packetid int, msg rawproto.Message, excludeSid int64, includeOffline ...bool) { + excludePos := -1 + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range this.Players { + if p != nil { + if p.sid != excludeSid { + if (p.gateSess != nil && p.IsOnLine() && !p.IsMarkFlag(PlayerState_Leave)) || len(includeOffline) != 0 { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + if p.sid == excludeSid { + excludePos = p.Pos + } + } + } + for _, p := range this.audiences { + if p != nil && p.sid != excludeSid { + if (p.gateSess != nil && p.IsOnLine() && !p.IsMarkFlag(PlayerState_Leave)) || len(includeOffline) != 0 { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + } + if this.rr != nil && !this.Testing && this.Gaming && !this.IsHundredScene() && !this.IsMatchScene() { + this.rr.Record(-1, excludePos, packetid, msg) + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, msg, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} + +func (this *Scene) RobotBroadcast(packetid int, msg rawproto.Message) { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range this.Players { + if p != nil && p.IsRob { + if p.gateSess != nil && p.IsOnLine() && !p.IsMarkFlag(PlayerState_Leave) { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, msg, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} +func (this *Scene) BroadcastToAudience(packetid int, msg rawproto.Message) { + if len(this.audiences) > 0 { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range this.audiences { + if p != nil { + if p.gateSess != nil && p.IsOnLine() { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, msg, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } + } +} + +func (this *Scene) GetAudiencesNum() int { + if this.audiences != nil { + return len(this.audiences) + } + return 0 +} + +func (this *Scene) ChangeSceneState(stateid int) { + if this.destroyed { + return + } + state := this.sp.GetSceneState(this, stateid) + if state == nil { + return + } + oldState := -1 + if this.SceneState != nil { + oldState = this.SceneState.GetState() + if this.SceneState.CanChangeTo(state) { + logger.Logger.Tracef("(this *Scene) [%v] ChangeSceneState %v -> %v", this.SceneId, this.SceneState.GetState(), state.GetState()) + + this.SceneState.OnLeave(this) + this.SceneState = state + this.SceneState.OnEnter(this) + this.sp.NotifyGameState(this) + } else { + logger.Logger.Tracef("(this *Scene) [%v] ChangeSceneState failed %v -> %v", this.SceneId, this.SceneState.GetState(), state.GetState()) + } + } else { + logger.Logger.Tracef("(this *Scene) [%v] ChangeSceneState -> %v", this.SceneId, state.GetState()) + this.SceneState = state + this.SceneState.OnEnter(this) + //this.NotifySceneState(stateid) + } + + if this.aiMgr != nil { + this.aiMgr.OnChangeState(this, oldState, stateid) + } +} + +func (this *Scene) SendToWorld(packetid int, pack interface{}) { + if this.ws != nil { + this.ws.Send(packetid, pack) + } +} + +func (this *Scene) FirePlayerEvent(p *Player, evtcode int, params []int64) { + if this.SceneState != nil { + this.SceneState.OnPlayerEvent(this, p, evtcode, params) + } + ////比赛事件 + //if this.mp != nil { + // this.mp.OnPlayerEvent(this, p, evtcode, params) + //} +} + +func (this *Scene) Pause() { + this.pause = true +} + +// RankMatchDestroy 排位解散房间 +func (this *Scene) RankMatchDestroy() { + if this.IsRankMatch() { + this.Destroy(true) + } +} + +func (this *Scene) Destroy(force bool) { + + this.destroyed = true + this.pause = true + + if !this.IsMatchScene() { + for _, p := range this.Players { + this.PlayerLeave(p, common.PlayerLeaveReason_OnDestroy, true) + } + for _, p := range this.audiences { + this.AudienceLeave(p, common.PlayerLeaveReason_OnDestroy) + } + } else { + for _, p := range this.Players { + this.PlayerLeave(p, common.PlayerLeaveReason_OnBilled, true) + } + } + + for _, p := range this.Players { + PlayerMgrSington.DelPlayerBySnId(p.SnId) + } + + for _, p := range this.audiences { + PlayerMgrSington.DelPlayerBySnId(p.SnId) + } + + isCompleted := this.sp.IsCompleted(this) || this.completed + SceneMgrSington.DestroyScene(this.SceneId) + pack := &server.GWDestroyScene{ + SceneId: proto.Int(this.SceneId), + IsCompleted: proto.Bool(isCompleted), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_DESTROYSCENE), pack) + logger.Logger.Trace("(this *Scene) Destroy(force bool) isCompleted", isCompleted) +} + +func (this *Scene) IsPrivateScene() bool { + return this.SceneId >= common.PrivateSceneStartId && this.SceneId <= common.PrivateSceneMaxId || this.SceneMode == common.SceneMode_Private +} + +// IsFreePublic 自由桌 +func (this *Scene) IsFreePublic() bool { + return this.GetDBGameFree().GetFreeMode() == 1 +} + +// IsRankMatch 排位赛 +func (this *Scene) IsRankMatch() bool { + return this.GetDBGameFree().GetRankType() > 0 +} + +// IsMatchScene 比赛场 +func (this *Scene) IsMatchScene() bool { + return this.SceneId >= common.MatchSceneStartId && this.SceneId <= common.MatchSceneMaxId +} + +func (this *Scene) IsFull() bool { + return len(this.Players) >= this.playerNum +} + +// 大厅场 +func (this *Scene) IsHallScene() bool { + return this.SceneId >= common.HallSceneStartId && this.SceneId <= common.HallSceneMaxId +} + +// 金豆自由场 +func (this *Scene) IsCoinScene() bool { + return this.SceneId >= common.CoinSceneStartId && this.SceneId <= common.CoinSceneMaxId +} + +// 百人场 +func (this *Scene) IsHundredScene() bool { + return this.SceneId >= common.HundredSceneStartId && this.SceneId <= common.HundredSceneMaxId +} + +func (this *Scene) GetCoinSceneLowerThanKick() int64 { + if this.DbGameFree != nil { + return this.DbGameFree.GetLowerThanKick() + } + return 0 +} + +func (this *Scene) GetCoinSceneMaxCoinLimit() int64 { + if this.DbGameFree != nil { + return this.DbGameFree.GetMaxCoinLimit() + } + return 0 +} + +// CoinInLimitLocal 本地游戏入场限额检查 +func (this *Scene) CoinInLimitLocal(coin int64) bool { + minCoin := this.GetLimitCoin() + if minCoin != 0 && coin < minCoin { + return false + } + if coin <= 0 { + return false + } + return true +} + +// NotCoinInLimitType 金额超出入场限额,返回踢出类型 +func (this *Scene) NotCoinInLimitType(coin int64) int { + if common.IsLocalGame(this.GameId) { + minCoin := this.GetLimitCoin() + if minCoin != 0 && coin < minCoin { + return common.PlayerLeaveReason_Bekickout + } + if coin <= 0 { + return common.PlayerLeaveReason_Bekickout + } + return common.PlayerLeaveReason_Normal + } + + minCoin := int(this.GetCoinSceneLowerThanKick()) + maxCoin := int(this.GetCoinSceneMaxCoinLimit()) + if minCoin != 0 && coin < int64(minCoin) { + return common.PlayerLeaveReason_Bekickout + } + if maxCoin != 0 && coin > int64(maxCoin) { + return common.PlayerLeaveReason_RoomMaxCoin + } + if coin <= 0 { + return common.PlayerLeaveReason_Bekickout + } + return common.PlayerLeaveReason_Normal +} + +// CoinInLimit 单入场限额检查 +func (this *Scene) CoinInLimit(coin int64) bool { + if common.IsLocalGame(this.GameId) { + return this.CoinInLimitLocal(coin) + } + + minCoin := int(this.GetCoinSceneLowerThanKick()) + maxCoin := int(this.GetCoinSceneMaxCoinLimit()) + if minCoin != 0 && coin < int64(minCoin) { + return false + } + if maxCoin != 0 && coin > int64(maxCoin) { + return false + } + if coin <= 0 { + return false + } + return true +} + +// 根据底注去取createroom表里面的最小携带金额 +func (this *Scene) GetLimitCoin() int64 { + limitCoin := int64(0) + tmpIds := []int32{} + for _, data := range srvdata.PBDB_CreateroomMgr.Datas.GetArr() { + if int(data.GameId) == this.GameId && int(data.GameSite) == this.SceneType { + betRange := data.GetBetRange() + if len(betRange) == 0 { + continue + } + for j := 0; j < len(betRange); j++ { + if betRange[j] == this.BaseScore && len(data.GetGoldRange()) > 0 && data.GetGoldRange()[0] != 0 { + tmpIds = append(tmpIds, data.GetId()) + break + } + } + } + } + if len(tmpIds) > 0 { + goldRange := srvdata.PBDB_CreateroomMgr.GetData(tmpIds[0]).GetGoldRange() + if len(goldRange) != 0 && goldRange[0] != 0 { + limitCoin = int64(goldRange[0]) + } + if limitCoin != 0 { + for _, id := range tmpIds { + tmp := srvdata.PBDB_CreateroomMgr.GetData(id).GetGoldRange() + if int64(tmp[0]) < limitCoin && tmp[0] != 0 { + limitCoin = int64(tmp[0]) + } + } + } + } + return limitCoin +} + +func (this *Scene) CoinOverMaxLimit(coin int64, p *Player) bool { + if this.Testing { + return false + } + if coin < 0 { + return false + } + + if p.ExpectLeaveCoin != 0 && this.IsCoinScene() { //暂只对对战场生效 + if p.ExpectLeaveCoin < p.takeCoin { //期望输的时候离场 + if coin <= p.ExpectLeaveCoin { + return true + } + } else { //期望赢的时候离场 + if coin >= p.ExpectLeaveCoin { + return true + } + } + } else { + if this.DbGameFree != nil { + limit := this.DbGameFree.GetRobotLimitCoin() + if len(limit) >= 2 { + comp := common.RandInt(int(limit[0]), int(limit[1])) + if coin > int64(comp) { + return true + } + } + } + } + + return false +} + +func (this *Scene) CorrectBillCoin(coin, limit1, limit2 int64) int64 { + if coin > limit1 { + coin = limit1 + } + if coin > limit2 { + coin = limit2 + } + return coin +} + +func (this *Scene) GetCoinSceneServiceFee() int32 { + if this.DbGameFree != nil { + return this.DbGameFree.GetServiceFee() + } + return 0 +} + +func (this *Scene) GetCoinSceneTypeId() int32 { + if this.DbGameFree != nil { + return this.DbGameFree.Id + } + return 0 +} + +func (this *Scene) GetCoinSceneName() string { + if this.DbGameFree != nil { + return this.DbGameFree.GetName() + this.DbGameFree.GetTitle() + } + return "" +} + +func (this *Scene) GetHundredSceneName() string { + if this.IsHundredScene() && this.DbGameFree != nil { + if this.DbGameFree.GetName() == this.DbGameFree.GetTitle() { + return this.DbGameFree.GetTitle() + } else { + return this.DbGameFree.GetName() + this.DbGameFree.GetTitle() + } + } + return "" +} + +func (this *Scene) GetSceneName() string { + if this.IsCoinScene() { + return this.GetCoinSceneName() + } else if this.IsHundredScene() { + return this.GetHundredSceneName() + } + return "" +} + +func (this *Scene) CanChangeCoinScene(p *Player) bool { + //if p.IsMarkFlag(PlayerState_Audience) { + // if this.drp != nil { + // return this.drp.CanChangeCoinScene(this, p) + // } + //} + //if this.mp != nil { + // if !this.mp.IsMatchEnd(this) { + // return false + // } + //} + if this.sp != nil { + return this.sp.CanChangeCoinScene(this, p) + } + return false +} + +func (this *Scene) SyncPlayerCoin() { + //if this.Testing { + // return + //} + //pack := &server.GWSyncPlayerCoin{ + // SceneId: proto.Int(this.SceneId), + //} + //switch this.GameId { + //case common.GameId_HFishing, common.GameId_TFishing: + // for _, value := range this.Players { + // if value.IsRob { + // continue + // } + // //todo dev 捕鱼的逻辑暂时不用 开发的时候再增加 + // //if exData, ok := value.extraData.(*FishingPlayerData); ok { + // // if exData.CoinCache != value.LastSyncCoin { + // // pack.PlayerCoins = append(pack.PlayerCoins, int64(value.SnId)) + // // pack.PlayerCoins = append(pack.PlayerCoins, exData.CoinCache) + // // value.LastSyncCoin = exData.CoinCache + // // } + // //} + // } + //default: + // for _, value := range this.Players { + // if value.Coin != value.LastSyncCoin && !value.IsRob { + // pack.PlayerCoins = append(pack.PlayerCoins, int64(value.SnId)) + // pack.PlayerCoins = append(pack.PlayerCoins, value.Coin) + // value.LastSyncCoin = value.Coin + // } + // } + //} + //if len(pack.PlayerCoins) > 0 { + // proto.SetDefaults(pack) + // this.SendToWorld(int(server.SSPacketID_PACKET_GW_SYNCPLAYERCOIN), pack) + //} +} +func (this *Scene) NotifySceneStateFishing(state int) { + pack := &server.GWSceneState{ + RoomId: proto.Int(this.SceneId), + Fishing: proto.Int32(int32(state)), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_SCENESTATE), pack) +} +func (this *Scene) NotifySceneRoundStart(round int) { + pack := &server.GWSceneStart{ + RoomId: proto.Int(this.SceneId), + CurrRound: proto.Int(round), + Start: proto.Bool(true), + MaxRound: proto.Int(this.TotalOfGames), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_SCENESTART), pack) +} + +func (this *Scene) NotifySceneRoundPause() { + pack := &server.GWSceneStart{ + RoomId: proto.Int(this.SceneId), + Start: proto.Bool(false), + CurrRound: proto.Int(this.NumOfGames), + MaxRound: proto.Int(this.TotalOfGames), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_SCENESTART), pack) +} +func (this *Scene) SyncGameState(sec, bl int) { + if this.SceneState != nil { + pack := &server.GWGameState{ + SceneId: proto.Int(this.SceneId), + State: proto.Int(this.SceneState.GetState()), + Ts: proto.Int64(time.Now().Unix()), + Sec: proto.Int(sec), + BankerListNum: proto.Int(bl), + } + proto.SetDefaults(pack) + this.SendToWorld(int(server.SSPacketID_PACKET_GW_GAMESTATE), pack) + } +} + +// 游戏开始的时候同步防伙牌数据 +func (this *Scene) SyncScenePlayer() { + pack := &server.GWScenePlayerLog{ + GameId: proto.Int(this.GameId), + GameFreeId: proto.Int32(this.DbGameFree.GetId()), + } + for _, value := range this.Players { + if value.IsRob || !value.IsGameing() { + continue + } + pack.Snids = append(pack.Snids, value.SnId) + pack.IsGameing = append(pack.IsGameing, value.IsGameing()) + } + this.SendToWorld(int(server.SSPacketID_PACKET_GW_SCENEPLAYERLOG), pack) +} + +// 防伙牌换桌 +func (this *Scene) ChangeSceneEvent() { + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + if this.DbGameFree.GetMatchMode() == 1 { + return true + } + this.SendToWorld(int(server.SSPacketID_PACKET_GW_CHANGESCENEEVENT), &server.GWChangeSceneEvent{ + SceneId: proto.Int(this.SceneId), + }) + return true + }), nil, time.Second*3, 1) +} +func (this *Scene) RecordReplayStart() { + if !this.IsHundredScene() && !this.IsMatchScene() { + logger.Logger.Trace("RecordReplayStart-----", this.replayCode, this.NumOfGames, this.replayAddId) + id := fmt.Sprintf("%d%d%v%d", this.GameId, this.SceneId, this.GameNowTime.Format(ReplayIdTf), this.replayAddId) + this.rr = NewReplayRecorder(id) + } +} + +func (this *Scene) RecordReplayOver() { + if !this.Testing && !this.IsHundredScene() && !this.IsMatchScene() { + logger.Logger.Trace("RecordReplayOver-----", this.replayCode, this.NumOfGames, this.replayAddId) + this.replayAddId++ + this.rr.Fini(this) + + this.RecordReplayStart() + } +} + +// TryDismissRob 尝试机器人离场 +func (this *Scene) TryDismissRob(params ...int) { + if this.IsMatchScene() { + return + } + if this.IsCoinScene() { + allRobot := true + for _, p := range this.Players { + if !p.IsRob { + allRobot = false + break + } + } + //一次离开一个 + hasLeave := false + if allRobot && !this.IsPreCreateScene() { + for _, p := range this.Players { + if p.IsRob { + this.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + hasLeave = true + } + } + } + + //当局已经检查过了 + if this.nogDismiss == this.NumOfGames { + return + } + this.nogDismiss = this.NumOfGames + + //如果是满桌并且是禁止匹配真人,那么保持满桌几局 + if this.DbGameFree.GetMatchTrueMan() == common.MatchTrueMan_Forbid && this.IsFull() && rand.Intn(4) == 1 { + hasLeave = true + } + + if !hasLeave && !this.Testing { + for _, p := range this.Players { + rands := this.Rand.Int63n(20) + 20 + a := float64(p.Coin) / float64(p.takeCoin) + if p != nil && p.IsRob && a >= float64(rands)/10 { + this.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + hasLeave = true + break + } + } + } + + if !hasLeave && this.DbGameFree.GetMatchTrueMan() != common.MatchTrueMan_Forbid && len(params) > 0 && + params[0] == 1 && this.IsFull() && common.RandInt(10000) < 4000 { + for _, r := range this.Players { + if r.IsRob { + this.PlayerLeave(r, common.PlayerLeaveReason_Normal, true) + hasLeave = true + break + } + } + } + + if !hasLeave { + for _, r := range this.Players { + if r.IsRob { + if !r.IsGameing() { //5%的概率,不玩游戏直接离场 + if rand.Intn(100) < 5 { + this.PlayerLeave(r, common.PlayerLeaveReason_Normal, true) + hasLeave = true + break + } + } else { //玩游戏的,玩几局有概率离场 + expectTimes := 5 + rand.Int31n(20) + if r.GameTimes >= expectTimes { + this.PlayerLeave(r, common.PlayerLeaveReason_Normal, true) + hasLeave = true + break + } + } + } + } + } + + //如果当局有机器人离开,适当延长下下次邀请的时间 + if hasLeave { + tNow := time.Now() + if this.nextInviteTime.Sub(tNow) < time.Second { + this.nextInviteTime = tNow.Add(time.Second * time.Duration(rand.Int31n(3)+1)) + } + } + } +} + +func (this *Scene) CreateGameRecPacket() *server.GWGameRec { + return &server.GWGameRec{ + RoomId: proto.Int(this.SceneId), + NumOfGames: proto.Int(this.NumOfGames), + GameTime: proto.Int(int(time.Now().Sub(this.GameStartTime) / time.Second)), + } +} +func (this *Scene) IsAllReady() bool { + for _, p := range this.Players { + if !p.IsOnLine() || !p.IsReady() { + return false + } + } + return true +} + +func (this *Scene) GetOnlineCnt() int { + cnt := 0 + for _, p := range this.Players { + if p.IsOnLine() && !p.IsMarkFlag(PlayerState_Leave) { + cnt++ + } + } + return cnt +} + +func (this *Scene) GetRealPlayerCnt() int { + cnt := 0 + for _, p := range this.Players { + if !p.IsRob { + cnt++ + } + } + return cnt +} + +func (this *Scene) GetGameingPlayerCnt() int { + cnt := 0 + for _, p := range this.Players { + if p != nil && p.IsGameing() { + cnt += 1 + } + } + return cnt +} + +func (this *Scene) GetGameingRealPlayerCnt() int { + cnt := 0 + for _, p := range this.Players { + if p != nil && p.IsGameing() && !p.IsRob { + cnt += 1 + } + } + return cnt +} + +func (this *Scene) GetRandomRobotPlayer() *Player { + robotArray := []*Player{} + for _, p := range this.Players { + if p != nil && p.IsGameing() && p.IsRob { + robotArray = append(robotArray, p) + } + } + if len(robotArray) > 0 { + return robotArray[common.RandInt(0, len(robotArray))] + } + + return nil +} + +func (this *Scene) CoinPoolCanOut() bool { + return true + /* 暂时屏蔽 + noRobotPlayerCount := this.GetRealPlayerCnt() + setting := coinPoolMgr.GetCoinPoolSetting(this.platform, this.gamefreeId, this.groupId) + if setting != nil { + return int32(noRobotPlayerCount) >= setting.GetMinOutPlayerNum() + } + return int32(noRobotPlayerCount) >= this.dbGameFree.GetMinOutPlayerNum() + */ +} +func (this *Scene) ClearAutoPlayer() { + for _, p := range this.Players { + if p.IsAuto() { + p.UnmarkFlag(PlayerState_Auto) + p.SyncFlag() + } + } +} + +func (this *Scene) NewBigCoinNotice(player *Player, num int64, msgType int64) { + //if !this.Testing && !this.IsMatchScene() { + // if num < model.GameParamData.NoticeCoinMin || model.GameParamData.NoticeCoinMax < num { + // return + // } + // start := time.Now().Add(time.Second * 30).Unix() + // content := fmt.Sprintf("%v|%v|%v", player.GetName(), this.GetHundredSceneName(), num) + // pack := &server.GWNewNotice{ + // Ch: proto.String(""), + // Content: proto.String(content), + // Start: proto.Int64(start), + // Interval: proto.Int64(0), + // Count: proto.Int64(1), + // Msgtype: proto.Int64(msgType), + // Platform: proto.String(player.Platform), + // Isrob: proto.Bool(player.IsRob), + // Priority: proto.Int32(int32(num)), + // } + // if common.HorseRaceLampPriority_Rand == model.GameParamData.NoticePolicy { + // pack.Priority = proto.Int32(rand.Int31n(100)) + // } + // this.SendToWorld(int(server.SSPacketID_PACKET_GW_NEWNOTICE), pack) + //} +} + +type GameDetailedParam struct { + Trend20Lately string //最近20局开奖结果 +} + +// 保存详细游戏日志 +func (this *Scene) SaveGameDetailedLog(logid string, gamedetailednote string, gameDetailedParam *GameDetailedParam) { + if this != nil { + if !this.Testing { //测试场屏蔽掉 + trend20Lately := gameDetailedParam.Trend20Lately + baseScore := this.DbGameFree.GetBaseScore() + if common.IsLocalGame(this.GameId) { + baseScore = this.BaseScore + } + if this.IsCoinScene() { + mapPlatform := make(map[string]bool) + for _, p := range this.Players { + if _, ok := mapPlatform[p.Platform]; !ok { + mapPlatform[p.Platform] = true + log := model.NewGameDetailedLogEx(logid, int32(this.GameId), int32(this.SceneId), + this.DbGameFree.GetGameMode(), this.DbGameFree.Id, int32(len(this.Players)), + int32(time.Now().Unix()-this.GameNowTime.Unix()), baseScore, + gamedetailednote, p.Platform, this.ClubId, this.RoomId, this.CpCtx, GameDetailedVer[this.GameId], trend20Lately) + if log != nil { + if this.IsMatchScene() { + log.MatchId = this.MatchId + } + LogChannelSingleton.WriteLog(log) + } + } + } + } else { + log := model.NewGameDetailedLogEx(logid, int32(this.GameId), int32(this.SceneId), + this.DbGameFree.GetGameMode(), this.DbGameFree.Id, int32(len(this.Players)), + int32(time.Now().Unix()-this.GameNowTime.Unix()), baseScore, + gamedetailednote, this.Platform, this.ClubId, this.RoomId, this.CpCtx, GameDetailedVer[this.GameId], trend20Lately) + if log != nil { + if this.IsMatchScene() { + log.MatchId = this.MatchId + } + newLog := new(model.GameDetailedLog) + *newLog = *log + LogChannelSingleton.WriteLog(log) + } + } + } + } +} + +type SaveGamePlayerListLogParam struct { + Platform string //平台 + Channel string //渠道 + Promoter string //推广员 + PackageTag string //包标识 + InviterId int32 //邀请人 + LogId string //日志id + TotalIn int64 //总投入 + TotalOut int64 //总产出 + TaxCoin int64 //总税收 + ClubPumpCoin int64 //俱乐部抽水 + BetAmount int64 //下注量 + WinAmountNoAnyTax int64 //税后赢取额(净利润,正负值) + ValidBet int64 //有效下注 + ValidFlow int64 //有效流水 + IsFirstGame bool //是否第一次游戏 + IsLeave bool //是否中途离开,用于金花,德州可以中途离开游戏使用 + IsFree bool //拉霸专用 是否免费 + WinSmallGame int64 //拉霸专用 小游戏奖励 + WinTotal int64 //拉霸专用 本局输赢 + PlayerName string //玩家名字 +} + +func GetSaveGamePlayerListLogParam(platform, channel, promoter, packageTag, logid string, + inviterId int32, totalin, totalout, taxCoin, clubPumpCoin, betAmount, winAmountNoAnyTax, validBet, validFlow int64, + isFirstGame, isLeave bool) *SaveGamePlayerListLogParam { + return &SaveGamePlayerListLogParam{ + Platform: platform, + Channel: channel, + Promoter: promoter, + PackageTag: packageTag, + InviterId: inviterId, + LogId: logid, + TotalIn: totalin, + TotalOut: totalout, + TaxCoin: taxCoin, + ClubPumpCoin: clubPumpCoin, + BetAmount: betAmount, + WinAmountNoAnyTax: winAmountNoAnyTax, + ValidBet: validBet, + ValidFlow: validFlow, + IsFirstGame: isFirstGame, + IsLeave: isLeave, + } +} + +func IsFishGame(gameId int) bool { + if gameId == common.GameId_HFishing || gameId == common.GameId_TFishing { + return true + } + + return false +} +func (this *Scene) SaveFriendRecord(snid int32, isWin int32, billCoin int64, baseScore int32) { + if this.SceneMode == common.SceneMode_Private { + return + } + log := model.NewFriendRecordLogEx(this.Platform, snid, isWin, int32(this.GameId), baseScore, billCoin, this.MatchType) + if log != nil { + LogChannelSingleton.WriteLog(log) + } +} + +// 保存玩家和GameDetailedLog的映射表 +func (this *Scene) SaveGamePlayerListLog(snid int32, param *SaveGamePlayerListLogParam) { + if this != nil { + if !this.Testing { //测试场屏蔽掉 龙虎两边都压,totalin和totalout都=0,这个条件去掉 + //统计流水值 + playerEx := this.GetPlayer(snid) + //有些结算的时候,玩家已经退场,不要用是否在游戏,0709,修改为扣税后数值 + if playerEx != nil && !param.IsLeave && !playerEx.IsRob && (param.IsFree || param.TotalIn != 0 || param.TotalOut != 0) { + totalFlow := param.ValidFlow * int64(this.DbGameFree.GetBetWaterRate()) / 100 + playerEx.TotalConvertibleFlow += totalFlow + playerEx.TotalFlow += totalFlow + playerEx.ValidCacheBetTotal += param.ValidBet + //报表统计 + playerEx.SaveReportForm(int(this.DbGameFree.GetGameClass()), this.SceneMode, this.KeyGameId, + param.WinAmountNoAnyTax, totalFlow, param.ValidBet) + //分配利润 + ProfitDistribution(playerEx, param.TaxCoin, param.ClubPumpCoin, totalFlow) + //上报游戏事件 + playerEx.ReportGameEvent(param.TaxCoin, param.ClubPumpCoin, param.WinAmountNoAnyTax, param.ValidBet, totalFlow, param.TotalIn, param.TotalOut) + } + + roomType := int32(this.SceneMode) + if this.GameId == common.GameId_Avengers || + this.GameId == common.GameId_CaiShen || + this.GameId == common.GameId_EasterIsland || + this.GameId == common.GameId_IceAge || + this.GameId == common.GameId_TamQuoc { //复仇者联盟强制为0,所有场次操作记录放一起 + roomType = 0 + } + baseScore := this.DbGameFree.GetBaseScore() + if common.IsLocalGame(this.GameId) { + baseScore = this.BaseScore + } + + Name := param.PlayerName + if playerEx != nil { + Name = param.PlayerName + } + + log := model.NewGamePlayerListLogEx(snid, param.LogId, param.Platform, param.Channel, param.Promoter, param.PackageTag, + int32(this.GameId), baseScore, int32(this.SceneId), this.DbGameFree.GetGameMode(), + this.GetGameFreeId(), param.TotalIn, param.TotalOut, this.ClubId, this.RoomId, param.TaxCoin, param.ClubPumpCoin, roomType, + param.BetAmount, param.WinAmountNoAnyTax, this.KeyGameId, Name, this.DbGameFree.GetGameClass(), + param.IsFirstGame, this.MatchId, this.MatchType, param.IsFree, param.WinSmallGame, param.WinTotal) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } +} + +func (this *Scene) IsPlayerFirst(p *Player) bool { + if p == nil { + return false + } + if p.GDatas != nil { + if data, ok := p.GDatas[this.KeyGameId]; ok { + if data.Statics.GameTimes <= 1 { + return true + } + return false + } + return true + } + return false +} + +func (this *Scene) RobotIsLimit() bool { + if this.robotLimit != 0 { + if this.IsCoinScene() { + if this.robotLimit <= this.robotNum { + return true + } + // 房间需要给真人留一个空位 + //if this.DbGameFree.GetMatchTrueMan() == common.MatchTrueMan_Priority && this.playerNum-this.realPlayerNum-1 <= this.robotNum { + // 没有真人的房间需要给真人留一个空位 + if this.DbGameFree.GetMatchTrueMan() == common.MatchTrueMan_Priority && this.playerNum-1 <= this.robotNum { + return true + } + } else if this.IsHundredScene() { + if this.robotNum >= this.robotLimit { + return true + } + } + } + return false +} + +func (this *Scene) RandRobotCnt() { + if this.DbGameFree != nil { + if this.DbGameFree.GetMatchMode() == 1 { + return + } + numrng := this.DbGameFree.GetRobotNumRng() + if len(numrng) >= 2 { + if numrng[1] == numrng[0] { + this.robotLimit = int(numrng[0]) + } else { + if numrng[1] < numrng[0] { + numrng[1], numrng[0] = numrng[0], numrng[1] + } + this.robotLimit = int(numrng[0] + this.Rand.Int31n(numrng[1]-numrng[0]+1)) + } + } + //logger.Logger.Tracef("===(this *Scene) RandRobotCnt() sceneid:%v gameid:%v mode:%v robotLimit:%v robotNum:%v", this.SceneId, this.GameId, this.GameMode, this.robotLimit, this.robotNum) + } +} +func (this *Scene) GetRobotTime() int64 { + l := int64(common.RandInt(model.NormalParamData.RobotRandomTimeMin, model.NormalParamData.RobotRandomTimeMax)) + return l + time.Now().Unix() +} + +func (this *Scene) IsPreCreateScene() bool { + return this.DbGameFree.GetCreateRoomNum() > 0 +} + +func (this *Scene) TryInviteRobot() { + if this.aiMgr != nil { + return + } + // 游戏配置错误 + if this.DbGameFree == nil { + return + } + // 私有房间不邀请机器人,比赛场部邀请机器人 + if this.IsPrivateScene() || this.IsMatchScene() { + return + } + // 队列匹配不邀请机器人 + if this.DbGameFree.GetMatchMode() == 1 { + return + } + // 不使用机器人 + if this.DbGameFree.GetBot() == 0 { //机器人不进的场 + return + } + + // 分组模式下机器人是否使用(默认不使用) + if !model.GameParamData.GameConfigGroupUseRobot && this.GroupId != 0 { + return + } + + // 对战场有真实玩家的情况才需要机器人匹配 + if !this.IsRobFightGame() && this.realPlayerNum <= 0 && !this.IsHundredScene() && !this.IsPreCreateScene() { //预创建房间的对战场可以优先进机器人,如:21点 判断依据:CreateRoomNum + return + } + + switch this.DbGameFree.GetGameType() { + case common.GameType_Fishing: + if this.robotNum >= this.robotLimit { + return + } + } + + tNow := time.Now() + if tNow.Before(this.nextInviteTime) { + return + } + if model.GameParamData.EnterAfterStartSwitch && this.IsCoinScene() && this.Gaming && !this.bEnterAfterStart { + return + } + + if this.robotNumLastInvite == this.robotNum { + this.inviteInterval = this.inviteInterval + 1 + if this.inviteInterval > model.GameParamData.RobotInviteIntervalMax { + this.inviteInterval = model.GameParamData.RobotInviteIntervalMax + } + } else { + this.inviteInterval = model.GameParamData.RobotInviteInitInterval + } + + this.ResetNextInviteTime() + this.robotNumLastInvite = this.robotNum + + if !this.RobotIsLimit() { + var robCnt int + if this.robotLimit != 0 { + if this.IsCoinScene() { + if this.robotNum >= this.robotLimit { //机器人数量已达上限 + return + } + hadCnt := len(this.Players) + robCnt = this.robotLimit - this.robotNum + if robCnt > this.playerNum-hadCnt { + robCnt = this.playerNum - hadCnt + } + } else if this.IsHundredScene() { + robCnt = this.robotLimit - this.robotNum + } + } else { + if this.IsCoinScene() { + if this.IsFull() { + return + } + hadCnt := len(this.Players) + robCnt = this.playerNum - hadCnt + if this.realPlayerNum == 0 { //一个真人都没有,不让机器人坐满房间 + robCnt-- + } + } + } + if robCnt > 0 { + var num int32 + if this.DbGameFree.GameId == common.GameId_ChesstitiansCambodianRobot { + num = int32(robCnt) + } else { + num = this.Rand.Int31n(int32(robCnt + 1)) + } + if num > 0 { + if this.IsCoinScene() /* && this.gaming*/ { //如果牌局正在进行中,一个一个进 + num = 1 + } + //logger.Logger.Tracef("(this *Scene)(groupid:%v sceneid:%v) TryInviteRobot(%v) current robot(%v+%v) robotlimit(%v)", this.groupId, this.sceneId, this.dbGameFree.GetName()+this.dbGameFree.GetTitle(), this.robotNum, num, this.robotLimit) + //同步下房间里的参数' + NpcServerAgentSingleton.SyncDBGameFree(this.SceneId, this.GetDBGameFree()) + //然后再邀请 + NpcServerAgentSingleton.Invite(this.SceneId, int(num), this.DbGameFree.Id) + } + } + } +} + +func (this *Scene) ResetNextInviteTime() { + this.nextInviteTime = time.Now().Add(time.Second * (time.Duration(this.Rand.Int63n(2+this.inviteInterval)) + 1)) +} + +// 是否有真人参与游戏 +func (this *Scene) IsRealInGame() bool { + for _, player := range this.Players { + if player != nil && player.IsGameing() && !player.IsRob { + return true + } + } + return false +} + +// 是否都是真人 +func (this *Scene) IsAllRealInGame() bool { + for _, player := range this.Players { + if player != nil && player.IsGameing() && player.IsRob { + return false + } + } + return true +} + +// 是否开启机器人对战游戏 +func (this *Scene) IsRobFightGame() bool { + if this.DbGameFree == nil { + return false + } + if this.DbGameFree.GetAi()[0] == 1 && model.GameParamData.IsRobFightTest == true { + return true + } + return false +} + +// 百人场机器人离场规则 +func (this *Scene) RobotLeaveHundred() { + for _, p := range this.Players { + if p != nil { + leave := false + var reason int + //if p.trusteeship >= 5 { + // leave = true + // reason = common.PlayerLeaveReason_LongTimeNoOp + //} + if !leave && !p.IsOnLine() && time.Now().Sub(p.DropTime) > 30*time.Second { + leave = true + reason = common.PlayerLeaveReason_DropLine + } + if !leave && p.IsRob { + if !this.CoinInLimit(p.Coin) { + //钱少 + leave = true + reason = common.PlayerLeaveReason_Bekickout + } else if this.CoinOverMaxLimit(p.Coin, p) { + //钱多 + leave = true + reason = common.PlayerLeaveReason_Normal + } else if p.Coin < int64(this.DbGameFree.GetBetLimit()) { + //少于下注限额 + leave = true + reason = common.PlayerLeaveReason_Normal + } + } + if leave { + this.PlayerLeave(p, reason, leave) + } + } + } +} +func (this *Scene) RandInt(args ...int) int { + switch len(args) { + case 0: + return this.Rand.Int() + case 1: + if args[0] != 0 { + return this.Rand.Intn(args[0]) + } else { + return 0 + } + default: + l := args[0] + u := args[1] + switch { + case l == u: + { + return l + } + case l > u: + { + return u + this.Rand.Intn(l-u) + } + default: + { + return l + this.Rand.Intn(u-l) + } + } + } +} + +func (this *Scene) CheckNeedDestroy() bool { + //if common.IsLocalGame(this.GameId) { + return ServerStateMgr.GetState() == common.GAME_SESS_STATE_OFF || this.graceDestroy + //} else { + // return (ServerStateMgr.GetState() == common.GAME_SESS_STATE_OFF || this.graceDestroy) || (this.IsPrivateScene() && this.NumOfGames >= this.TotalOfGames) + //} +} + +func (this *Scene) GetRecordId() string { + if this.rr != nil { + return this.rr.id + } + return fmt.Sprintf("%d%d%v%d", this.GameId, this.SceneId, this.GameNowTime.Format(ReplayIdTf), this.NumOfGames) +} + +//func (this *Scene) TryUseMatchNextBaseData() { +// data := this.matchChgData +// if data != nil { +// this.matchChgData = nil +// this.SetParamEx(common.PARAMEX_MATCH_BASESCORE, data.NextBaseScore) +// this.SetParamEx(common.PARAMEX_MATCH_OUTSCORE, data.NextOutScore) +// this.DbGameFree.BaseScore = proto.Int32(data.NextBaseScore) +// //同步给玩家 +// pack := &match.SCMatchBaseScoreChange{ +// MatchId: proto.Int32(this.GetParamEx(common.PARAMEX_MATCH_COPYID)), +// BaseScore: proto.Int32(data.NextBaseScore), +// OutScore: proto.Int32(data.NextOutScore), +// } +// proto.SetDefaults(pack) +// this.Broadcast(int(match.MatchPacketID_PACKET_SC_MATCH_BASESCORECHANGE), pack, 0) +// } +//} + +// /////////////////////////////////////////////////////////////////// +func (this *Scene) resultHistoryRemove(n int) { + var i int + for ; i < len(this.resultHistory); i++ { + for j := 1; j < len(this.resultHistory[i]); j++ { + if this.resultHistory[i][j] == n { + this.resultHistory[i] = append(this.resultHistory[i][:j], this.resultHistory[i][j+1:]...) + if len(this.resultHistory[i]) <= 1 { + this.resultHistory = append(this.resultHistory[:i], this.resultHistory[i+1:]...) + } + return + } + } + } +} + +func (this *Scene) GetResultHistoryResult(n int) int { + for i := 0; i < len(this.resultHistory); i++ { + for j := 1; j < len(this.resultHistory[i]); j++ { + if this.resultHistory[i][j] == n { + return this.resultHistory[i][0] + } + } + } + return -1 +} +func (this *Scene) GetResult() int { + return this.results[this.LoopNum] +} +func (this *Scene) resultCheck() { + logger.Logger.Tracef("历史记录: %v\n调控配置: %v", this.resultHistory, this.results) + for i := 0; i < len(this.resultHistory); i++ { + m := this.resultHistory[i][0] + for j := 1; j < len(this.resultHistory[i]); j++ { + if this.results[this.resultHistory[i][j]] != m { + logger.Logger.Errorf("不匹配 局数:%d 配置结果:%d 历史记录的结果:%d", + this.resultHistory[i][j], this.results[this.resultHistory[i][j]], m) + } + } + } +} + +func (this *Scene) AddLoopNum() { + //defer this.resultCheck() + // 维护 resultHistory + this.resultHistoryRemove(this.LoopNum) + + this.results[this.LoopNum] = common.DefaultResult + this.LoopNum++ + if this.LoopNum == common.MaxLoopNum { + this.LoopNum = 0 + } +} +func (this *Scene) ProtoResults() []*server.EResult { + ret := []*server.EResult{} + for i := 0; i < len(this.resultHistory); i++ { + if len(this.resultHistory[i]) <= 1 { + continue + } + temp := new(server.EResult) + temp.Result = proto.Int(this.resultHistory[i][0]) + buf := bytes.NewBufferString("") + buf.WriteString(fmt.Sprint(this.resultHistory[i][1])) + for j := 2; j < len(this.resultHistory[i]); j++ { + buf.WriteString(fmt.Sprint(",", this.resultHistory[i][j])) + } + temp.Index = proto.String(buf.String()) + ret = append(ret, temp) + } + return ret +} + +func (this *Scene) ParserResults(str, user string) { + for _, v := range strings.Split(strings.TrimSpace(str), ",") { + ns := strings.Split(v, ":") + if len(ns) != 2 { + continue + } + i, err := strconv.Atoi(ns[0]) + if err != nil { + continue + } + n, err := strconv.Atoi(ns[1]) + if err != nil { + continue + } + if n < 0 || n >= len(this.results) { + continue + } + this.results[i] = n + } + this.WebUser = user +} + +// ParserResults1 只能解析一种控制结果的设置 +func (this *Scene) ParserResults1(str, user string) *server.GWRoomResults { + // 维护 resultHistory + ret := &server.GWRoomResults{ + Code: proto.Int(0), + } + + arr := strings.Split(strings.TrimSpace(str), ",") + if len(arr) == 0 { + ret.Code = proto.Int(3) + ret.Msg = proto.String("参数错误") + return ret + } + ns := strings.Split(arr[0], ":") + if len(ns) != 2 { + ret.Code = proto.Int(3) + ret.Msg = proto.String("参数错误") + return ret + } + n, err := strconv.Atoi(ns[1]) + if err != nil { + ret.Code = proto.Int(3) + ret.Msg = proto.String("参数错误") + return ret + } + + //logger.Logger.Tracef("控牌设置历史 %s", str) + //defer this.resultCheck() + + var is []int + if n == common.DefaultResult { + for _, v := range arr { + ns := strings.Split(v, ":") + if len(ns) != 2 { + continue + } + // 局数 + i, err := strconv.Atoi(ns[0]) + if err != nil { + continue + } + if i < 0 || i >= len(this.results) { + ret.Code = proto.Int(2) + ret.Msg = proto.String(fmt.Sprintf("局数错误 第%d局", i)) + return ret + } + is = append(is, i) + } + for _, v := range is { + this.resultHistoryRemove(v) + this.results[v] = common.DefaultResult + } + this.WebUser = user + return ret + } + + // 设置 + indexes := []int{n} + for _, v := range arr { + ns := strings.Split(v, ":") + if len(ns) != 2 { + continue + } + // 局数 + i, err := strconv.Atoi(ns[0]) + if err != nil { + continue + } + if i < 0 || i >= len(this.results) { + ret.Code = proto.Int(2) + ret.Msg = proto.String(fmt.Sprintf("局数错误 第%d局", i)) + return ret + } + if this.results[i] != common.DefaultResult { + ret.Code = proto.Int(1) + ret.Msg = proto.String(fmt.Sprintf("重复设置第%d局", i)) + return ret + } + is = append(is, i) + } + + for _, v := range is { + this.results[v] = n + indexes = append(indexes, v) + } + this.resultHistory = append(this.resultHistory, indexes) + this.WebUser = user + return ret +} + +func (this *Scene) RandTakeCoin(p *Player) (takeCoin, leaveCoin, gameTimes int64) { + if p.IsRob && p.IsLocal { + dbGameFree := this.DbGameFree + takerng := dbGameFree.GetRobotTakeCoin() + if len(takerng) >= 2 && takerng[1] > takerng[0] { + if takerng[0] < dbGameFree.GetLimitCoin() { + takerng[0] = dbGameFree.GetLimitCoin() + } + takeCoin = int64(common.RandInt(int(takerng[0]), int(takerng[1]))) + } else { + maxlimit := int64(dbGameFree.GetMaxCoinLimit()) + if maxlimit != 0 && p.Coin > maxlimit { + logger.Logger.Trace("Player coin:", p.Coin) + //在下限和上限之间随机,并对其的100的整数倍 + takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit))) + logger.Logger.Trace("Take coin:", takeCoin) + } + if maxlimit == 0 && this.IsCoinScene() { + maxlimit = int64(common.RandInt(10, 50)) * int64(dbGameFree.GetLimitCoin()) + takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit))) + logger.Logger.Trace("Take coin:", takeCoin) + } + } + takeCoin = takeCoin / 100 * 100 + //离场金币 + leaverng := dbGameFree.GetRobotLimitCoin() + if len(leaverng) >= 2 { + leaveCoin = int64(leaverng[0] + rand.Int63n(leaverng[1]-leaverng[0])) + } + } + return +} + +// TryBillExGameDrop 掉落道具 +func (this *Scene) TryBillExGameDrop(p *Player) { + if p.IsRob { + return + } + baseScore := this.DbGameFree.BaseScore + if common.IsLocalGame(this.GameId) { + baseScore = this.BaseScore + } + if baseScore == 0 { + return + } + dropInfo := srvdata.GameDropMgrSington.GetDropInfoByBaseScore(int32(this.GameId), baseScore) + if dropInfo != nil && len(dropInfo) != 0 && p.Items != nil { + realDrop := make(map[int32]int32) + for _, drop := range dropInfo { + if _, ok := p.Items[drop.ItemId]; ok { + //概率 + randTmp := rand.Int31n(10000) + if randTmp < drop.Rate { + //个数 + num := drop.MinAmount + if drop.MaxAmount > drop.MinAmount { + num = rand.Int31n(drop.MaxAmount-drop.MinAmount+1) + drop.MinAmount + } + + p.Items[drop.ItemId] += num + realDrop[drop.ItemId] = num + } + } else { + logger.Logger.Error("itemid not exist! ", drop.ItemId) + } + } + if realDrop != nil && len(realDrop) != 0 { + //通知客户端游戏内额外掉落 + pack := &player.SCGameExDropItems{} + pack.Items = make(map[int32]int32) + for id, num := range realDrop { + pack.Items[id] = proto.Int32(num) + itemData := srvdata.PBDB_GameItemMgr.GetData(id) + if itemData != nil { + //logType 0获得 1消耗 + log := model.NewItemLogEx(p.Platform, p.SnId, 0, itemData.Id, itemData.Name, int64(num), "tienlen游戏掉落") + if log != nil { + logger.Logger.Trace("WriteLog: ", log) + LogChannelSingleton.WriteLog(log) + } + } + } + if pack != nil && pack.Items != nil && len(pack.Items) != 0 { + p.SendToClient(int(player.PlayerPacketID_PACKET_SCGAMEEXDROPITEMS), pack) + logger.Logger.Trace("SCGAMEEXDROPITEMS ", pack) + } + } + } +} + +// 生成名字 +func (this *Scene) RandNickName() string { + //if rand.Int31n(100) < 60 { + pool := srvdata.PBDB_NameMgr.Datas.GetArr() + cnt := int32(len(pool)) + if cnt > 0 { + return pool[rand.Int31n(cnt)].GetName() + } + //} + return "Guest" +} + +func (this *Scene) GetRobotNum() int { + return this.robotNum +} + +func (this *Scene) GetRealPlayerNum() int { + return this.realPlayerNum +} + +func (this *Scene) GetBaseScore() int32 { + if this.BaseScore > 0 { + return this.BaseScore + } + return this.GetDBGameFree().GetBaseScore() +} + +func (this *Scene) PlayerPoolOdds(p *Player) int32 { + config := PlatformConfigSingleton.Get(p.Platform).PlayerPool + if config == nil { + return 0 + } + + if !config.GetPlayerPoolSwitch() || p == nil { + return 0 + } + + pCoin := p.TotalOut - p.PlayerTax - p.TotalIn + var gameCoin int64 + var upLine, lowLine int64 + + lowLine = p.GetPoolLower(config) + upLine = p.GetPoolUpper(config) + + odds := int32(p.GetPoolOdds(config)) + + p.TestLog = append(p.TestLog, fmt.Sprintf("个人水池调控 gameID:%v 兑换金币:%v 玩家初始值:%v, 游戏类型:%v, gameCoin:%v, 上线:%v, 下线:%v 概率:%v", + this.GameId, p.DiamondToCoin, pCoin, this.GetDBGameFree().GetGameType(), gameCoin, upLine, lowLine, odds)) + + return odds +} + +// GetPlayerOdds 获取玩家发牌调控概率 +func (this *Scene) GetPlayerOdds(p *Player, gameId int, hasRobot bool) int32 { + if !this.IsControl(hasRobot) { + return 0 + } + + if p == nil { + return 0 + } + // 黑白名单概率 + odds, ok := p.BlackWhiteOdds(gameId) + if ok { + return odds + } + + // 新手补偿概率 + odds, ok = p.NoviceOdds(gameId) + if ok { + return odds + } + + var f, g, t, ret int32 + // 个人水池概率 + g = this.PlayerPoolOdds(p) + + setting := CoinPoolMgr.GetCoinPoolSetting(this.Platform, this.GetGameFreeId(), this.GroupId) + if setting != nil && setting.GetSwitch() == 0 { + // 场次水池概率 + f = CoinPoolMgr.GetCoinPoolOdds(this.Platform, this.GetGameFreeId(), this.GroupId) + + cfg := PlatformConfigSingleton.Get(p.Platform).PlayerPool + if cfg != nil && cfg.PlayerPoolSwitch { + if g > 0 { + t = f + (1000-f)*g/1000 + ret = t + } else if g < 0 { + t = f + (1000+f)*g/1000 + ret = t + } else { + ret = f + } + } else { + ret = f + } + //logger.Logger.Tracef("TienLenSceneData snid(%v) 水池配置 水位:%v 配置:%+v", + // p.SnId, CoinPoolMgr.GetCoin(this.GetGameFreeId(), this.Platform, this.GroupId), *setting) + p.TestLog = append(p.TestLog, fmt.Sprintf("水位:%v 水池配置:%+v", + CoinPoolMgr.GetCoin(this.GetGameFreeId(), this.Platform, this.GroupId), *setting)) + } else { + ret = g + } + + //logger.Logger.Tracef("TienLenSceneData snid(%v) 个人及场次水池调控 hasRobot:%v 个人水池:%v 场次水池:%v 概率:%v", p.SnId, hasRobot, g, f, ret) + p.TestLog = append(p.TestLog, fmt.Sprintf("个人及场次水池调控 有机器人:%v 个人水池:%v 场次水池:%v 概率:%v", hasRobot, g, f, ret)) + return ret +} + +type PlayerDataParam struct { + HasRobotGaming bool + Data *server.GWPlayerData +} + +// SyncPlayerDatas 同步玩家游戏数据到worldsrv +// hasRobotGaming 本局是否有机器人参与游戏 +// 返回 溢出金额 +func (this *Scene) SyncPlayerDatas(param *PlayerDataParam) int64 { + if param == nil || param.Data == nil { + return 0 + } + var n int64 + // 比赛场,私人房,公共房无机器人不统计黑白名单输赢金额 + if this.IsControl(param.HasRobotGaming) { + for _, v := range param.Data.Datas { + if v.WBGain == 0 { + v.WBGain = v.Gain + } + if v.WBGain != 0 { + if p := this.GetPlayer(v.SnId); p != nil { + n = p.WBUpdate(v.WBGain) + if p.WBLevel == 0 { + p.BlackLevel = 0 + p.WhiteLevel = 0 + } + } + } + } + } + this.SendToWorld(int(server.SSPacketID_PACKET_GW_PLAYERDATA), param.Data) + logger.Logger.Trace("Send PlayerData ===>", param.Data) + return n +} + +type StaticParam struct { + SnId int32 // 玩家id + Gain int64 // 赢取金币(税后) + GainTax int64 // 赢取金币时的税收 + IsAddTimes bool // 是否记录游戏次数 + HasRobotGaming bool // 是否有机器人玩本局游戏 + WinState int32 // 输赢状态 1 赢 2 输 3 和 +} + +// IsControl 是否调控 +func (this *Scene) IsControl(hasRobotGaming bool) bool { + return !this.IsMatchScene() && !this.IsPrivateScene() && !(this.IsFreePublic() && !hasRobotGaming) +} + +// Statistics 玩家游戏数据统计 +func (this *Scene) Statistics(param *StaticParam) { + if param == nil { + return + } + p := this.GetPlayer(param.SnId) + if p == nil || p.IsRob { + return + } + + _, isNovice := p.NoviceOdds(this.GameId) + isControl := this.IsControl(param.HasRobotGaming) // 需要调控的房间 + + var wbLevel = p.WBLevel // 原来的黑白名单等级; 注意SyncPlayerDatas会修改WBLevel + var addGain int64 // 玩家溢出金额 + // 黑白名单 + // 解除黑白名单,记录黑白名单投入产出 + addGain += this.SyncPlayerDatas(&PlayerDataParam{ + HasRobotGaming: param.HasRobotGaming, + Data: &server.GWPlayerData{ + Datas: []*server.PlayerData{ + { + SnId: param.SnId, + Gain: param.Gain, + Tax: param.GainTax, + Coin: p.Coin, + GameCoinTs: p.GameCoinTs, + WinState: param.WinState, + }, + }, + GameFreeId: this.GetGameFreeId(), + SceneId: int32(this.SceneId), + }, + }) + + logger.Logger.Tracef("Statistics gameId:%v wbLevel:%v gain:%v addGain:%v", this.GameId, wbLevel, param.Gain, addGain) + + // 比赛场,私人房不统计 + if this.IsMatchScene() || this.IsPrivateScene() { + return + } + + var totalIn int64 + var totalOut int64 + now := time.Now() + if param.Gain > 0 { + totalOut = param.Gain + param.GainTax + } else { + totalIn = -param.Gain + } + + var statics []*model.PlayerGameStatics + keyGameId := strconv.Itoa(this.GetGameId()) + keyGameFreeId := strconv.Itoa(int(this.GetGameFreeId())) + // 当天数据统计 + // 按场次分 + if data, ok := p.TodayGameData.CtrlData[keyGameFreeId]; ok { + statics = append(statics, data) + } else { + gs := model.NewPlayerGameStatics() + p.TodayGameData.CtrlData[keyGameFreeId] = gs + statics = append(statics, gs) + } + // 按游戏分 + if data, ok := p.TodayGameData.CtrlData[keyGameId]; ok { + statics = append(statics, data) + } else { + data = model.NewPlayerGameStatics() + p.TodayGameData.CtrlData[keyGameId] = data + statics = append(statics, data) + } + + // 按场次分 + if data, ok := p.GDatas[keyGameFreeId]; ok { + if data.FirstTime.IsZero() { + data.FirstTime = now + } + statics = append(statics, &data.Statics) + } else { + data = &model.PlayerGameInfo{FirstTime: now} + p.GDatas[keyGameFreeId] = data + statics = append(statics, &data.Statics) + } + // 按游戏分 + if data, ok := p.GDatas[keyGameId]; ok { + if data.FirstTime.IsZero() { + data.FirstTime = now + } + statics = append(statics, &data.Statics) + } else { + data = &model.PlayerGameInfo{FirstTime: now} + p.GDatas[keyGameId] = data + statics = append(statics, &data.Statics) + } + + // 新手输赢统计 + if !model.GameParamData.CloseNovice && !common.InSliceInt(model.GameParamData.CloseNoviceGame, this.GameId) && isControl && wbLevel == 0 && isNovice { + keyNoviceGameId := common.GetKeyNoviceGameId(this.GameId) + var gs *model.PlayerGameStatics + if data, ok := p.GDatas[keyNoviceGameId]; ok { + statics = append(statics, &data.Statics) + gs = &data.Statics + } else { + data = &model.PlayerGameInfo{FirstTime: time.Now(), Statics: *model.NewPlayerGameStatics()} + p.GDatas[keyNoviceGameId] = data + statics = append(statics, &data.Statics) + gs = &data.Statics + } + // 溢出 + data := srvdata.PBDB_NewPlayerMgr.GetData(int32(this.GameId)) + if data != nil { + switch data.GetCondition1() { + case 2: // gameId输赢金额 + if gs != nil { + out := gs.TotalOut + totalOut + in := gs.TotalIn + totalIn + tax := gs.Tax + param.GainTax + cur := out - tax - in + if cur > data.GetConditionValue1() { + addGain += cur - data.GetConditionValue1() + } + } + } + switch data.GetCondition2() { + case 2: // gameId输赢金额 + if gs != nil { + out := gs.TotalOut + totalOut + in := gs.TotalIn + totalIn + tax := gs.Tax + param.GainTax + cur := out - tax - in + if cur > data.GetConditionValue2() { + addGain += cur - data.GetConditionValue2() + } + } + } + } + } + logger.Logger.Tracef("Statistics Novice gameId:%v wbLevel:%v gain:%v addGain:%v", this.GameId, wbLevel, param.Gain, addGain) + + // 个人输赢统计条件(个人水池) + // 黑白名单和新手调控,溢出金币统计到个人水池 + cfg := PlatformConfigSingleton.Get(p.Platform).PlayerPool + isPlayerPool := cfg != nil && cfg.PlayerPoolSwitch && isControl && ((wbLevel == 0 && !isNovice) || addGain > 0) + if isPlayerPool { + keyGameType := common.GetKeyGameType(int(this.GetDBGameFree().GetGameType())) + gs, ok := p.GDatas[keyGameType] + if !ok { + gs = &model.PlayerGameInfo{FirstTime: time.Now()} + p.GDatas[keyGameType] = gs + } + if addGain == 0 { + // 正常统计 + // 游戏类型输赢统计(个人水池调控使用) + statics = append(statics, &gs.Statics) + } else { + // 溢出统计;只有多赢的值,多输的不计算 + if wbLevel >= 0 { + gs.Statics.TotalOut += addGain + } + } + logger.Logger.Tracef("Statistics PlayerPool gameId:%v wbLevel:%v gain:%v addGain:%v", this.GameId, wbLevel, param.Gain, addGain) + } + + for _, data := range statics { + if data != nil { + data.TotalIn += totalIn + data.TotalOut += totalOut + data.Tax += param.GainTax + if param.IsAddTimes { + data.GameTimes++ + if param.Gain > 0 { + data.WinGameTimes++ + data.WinGameTimesNum++ + data.LoseGameTimesNum = 0 + } else if param.Gain < 0 { + data.LoseGameTimes++ + data.LoseGameTimesNum++ + data.WinGameTimesNum = 0 + } else { + data.DrawGameTimes++ + data.WinGameTimesNum = 0 + data.LoseGameTimesNum = 0 + } + } + } + } + + // 玩家身上元数据 + p.GameTimes++ + if param.Gain > 0 { + p.WinTimes++ + p.WinCoin += totalOut + p.TaxCoin += param.GainTax + if isPlayerPool && common.IsPlayerPool(this.GameId) { + p.TotalOut += totalOut + p.PlayerTax += param.GainTax + } + } else if param.Gain < 0 { + p.FailTimes++ + p.FailCoin += totalIn + if isPlayerPool && common.IsPlayerPool(this.GameId) { + p.TotalIn += totalIn + } + } else { + p.DrawTimes++ + } + + // 水池统计 + if isControl && ((wbLevel == 0 && !isNovice) || addGain > 0) { + if addGain == 0 { + CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, -param.Gain) + } else { + // 溢出统计;只有多赢的值,多输的不计算 + if wbLevel >= 0 { + CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, -addGain) + } + } + logger.Logger.Tracef("Statisticsis CoinPool gameId:%v wbLevel:%v gain:%v addGain:%v", this.GameId, wbLevel, param.Gain, addGain) + } +} + +func (this *Scene) TryRelease() { + if !this.IsMatchScene() && this.realPlayerNum == 0 { + this.Destroy(true) + } +} + +//type PlayerGamePoolParam struct { +// SnId int32 +// HasRobotGaming bool // 是否有机器人玩本局游戏 +// Gain int64 // 普通非黑白,非新手,非机器人的玩家输赢总额,需要扣税后的值(赢分扣税) +//} + +// PushGamePool 将普通非黑白,新手,机器人的玩家输赢金额放入场次水池 +//func (this *Scene) PushGamePool(param *PlayerGamePoolParam) { +// if this.IsMatchScene() || this.IsPrivateScene() || (this.IsFreePublic() && !param.HasRobotGaming) { +// return +// } +// p := this.GetPlayer(param.SnId) +// _, isNovice := p.NoviceOdds(this.GameId) +// if p == nil || p.IsRob || p.WBLevel != 0 || isNovice { +// return +// } +// CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, -param.Gain) +//} diff --git a/gamesrv/base/scene_event.go b/gamesrv/base/scene_event.go new file mode 100644 index 0000000..9de7194 --- /dev/null +++ b/gamesrv/base/scene_event.go @@ -0,0 +1,37 @@ +package base + +import ( + "mongo.games.com/goserver/core/basic" +) + +type SceneEventHandler interface { + Handler(*Scene, interface{}) +} + +type SceneEventHandlerWrapper func(*Scene, interface{}) + +func (sehw SceneEventHandlerWrapper) Handler(s *Scene, d interface{}) { + sehw(s, d) +} + +type SceneEvent struct { + d interface{} + h SceneEventHandler +} + +type SceneEventCommand struct { + *Scene + *SceneEvent +} + +func (this *SceneEventCommand) Done(o *basic.Object) error { + defer o.ProcessSeqnum() + this.SceneEvent.h.Handler(this.Scene, this.SceneEvent.d) + return nil +} + +type SceneTimerHandlerWrapper func(*Scene, interface{}) +type SceneTimer struct { + d interface{} + c int32 +} diff --git a/gamesrv/base/scene_mgr.go b/gamesrv/base/scene_mgr.go new file mode 100644 index 0000000..ebbb1f3 --- /dev/null +++ b/gamesrv/base/scene_mgr.go @@ -0,0 +1,296 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/server" + srvlibproto "mongo.games.com/goserver/srvlib/protocol" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" +) + +var SceneMgrSington = &SceneMgr{ + scenes: make(map[int]*Scene), + scenesByGame: make(map[int]map[int]*Scene), + scenesByGameFree: make(map[int32]map[int]*Scene), + PlatformScene: make(map[string]bool), +} + +type SceneMgr struct { + scenes map[int]*Scene + scenesByGame map[int]map[int]*Scene + scenesByGameFree map[int32]map[int]*Scene + lastSendJackPot time.Time + PlatformScene map[string]bool +} + +func (this *SceneMgr) makeKey(gameid, gamemode int) int { + return int(gameid*10000 + gamemode) +} + +func (this *SceneMgr) CreateScene(s *netlib.Session, sceneId, gameMode, sceneMode, gameId int, platform string, + params []int32, agentor, creator int32, replayCode string, hallId, groupId, totalOfGames int32, + dbGameFree *server.DB_GameFree, bEnterAfterStart bool, baseScore int32, playerNum int, chessRank []int32, paramsEx ...int32) *Scene { + scene := NewScene(s, sceneId, gameMode, sceneMode, gameId, platform, params, agentor, creator, replayCode, + hallId, groupId, totalOfGames, dbGameFree, bEnterAfterStart, baseScore, playerNum, chessRank, paramsEx...) + if scene == nil { + logger.Logger.Error("(this *SceneMgr) CreateScene, scene == nil") + return nil + } + this.scenes[scene.SceneId] = scene + if _, ok := this.PlatformScene[platform]; !ok { + this.PlatformScene[platform] = true + } + // + key := this.makeKey(gameId, gameMode) + if ss, exist := this.scenesByGame[key]; exist { + ss[scene.SceneId] = scene + } else { + ss = make(map[int]*Scene) + ss[scene.SceneId] = scene + this.scenesByGame[key] = ss + } + // + if ss, exist := this.scenesByGameFree[dbGameFree.GetId()]; exist { + ss[scene.SceneId] = scene + } else { + ss = make(map[int]*Scene) + ss[scene.SceneId] = scene + this.scenesByGameFree[dbGameFree.GetId()] = ss + } + scene.OnStart() + logger.Logger.Infof("(this *SceneMgr) CreateScene,New scene,id:[%d] replaycode:[%v]", scene.SceneId, replayCode) + return scene +} + +func (this *SceneMgr) DestroyScene(sceneId int) { + if scene, exist := this.scenes[sceneId]; exist { + scene.OnStop() + // + key := this.makeKey(scene.GameId, scene.GameMode) + if ss, exist := this.scenesByGame[key]; exist { + delete(ss, scene.SceneId) + } + // + if ss, exist := this.scenesByGameFree[scene.GetGameFreeId()]; exist { + delete(ss, scene.SceneId) + } + delete(this.scenes, sceneId) + logger.Logger.Infof("(this *SceneMgr) DestroyScene, sceneid = %v", sceneId) + } +} + +func (this *SceneMgr) GetPlayerNumByGameFree(platform string, gamefreeid, groupId int32) int32 { + var num int32 + if ss, exist := SceneMgrSington.scenesByGameFree[gamefreeid]; exist { + for _, scene := range ss { + if groupId != 0 { + if scene.GroupId == groupId { + cnt := scene.GetRealPlayerCnt() + num += int32(cnt) + } + } else { + if scene.Platform == platform { + cnt := scene.GetRealPlayerCnt() + num += int32(cnt) + } + } + } + } + return num +} + +func (this *SceneMgr) GetPlayerNumByGame(platform string, gameid, gamemode, groupId int32) map[int32]int32 { + nums := make(map[int32]int32) + key := this.makeKey(int(gameid), int(gamemode)) + if ss, exist := SceneMgrSington.scenesByGame[key]; exist { + for _, scene := range ss { + if groupId != 0 { + if scene.GroupId == groupId { + cnt := scene.GetRealPlayerCnt() + nums[scene.GetGameFreeId()] = nums[scene.GetGameFreeId()] + int32(cnt) + } + } else { + if scene.Platform == platform { + cnt := scene.GetRealPlayerCnt() + nums[scene.GetGameFreeId()] = nums[scene.GetGameFreeId()] + int32(cnt) + } + } + } + } + return nums +} + +func (this *SceneMgr) GetPlayersByGameFree(platform string, gamefreeid int32) []*Player { + players := make([]*Player, 0) + if ss, exist := SceneMgrSington.scenesByGameFree[gamefreeid]; exist { + for _, scene := range ss { + if scene.Platform == platform { + for _, p := range scene.Players { + if p != nil { + players = append(players, p) + } + } + } + } + } + return players +} + +func (this *SceneMgr) GetScene(sceneId int) *Scene { + if s, exist := this.scenes[sceneId]; exist { + return s + } + return nil +} +func (this *SceneMgr) GetSceneByGameId(platform string, gameId int32) []*Scene { + key := this.makeKey(int(gameId), 0) + var ss []*Scene + if data, ok := this.scenesByGame[key]; ok { + for _, scene := range data { + if scene.Platform == platform { + ss = append(ss, scene) + } + } + } + return ss +} +func (this *SceneMgr) JackPotSync(platform string, gameIds ...int32) { + for _, gameId := range gameIds { + ss := this.GetSceneByGameId(platform, gameId) + if ss != nil { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + pack := &gamehall.SCHundredSceneGetGameJackpot{} + for _, s := range ss { + val := s.sp.GetJackPotVal(s) + if val > 0 { + jpfi := &gamehall.GameJackpotFundInfo{ + GameFreeId: proto.Int32(s.DbGameFree.Id), + JackPotFund: proto.Int64(val), + } + pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) + } + for _, p := range s.Players { + if p != nil && p.gateSess != nil && p.IsOnLine() { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + } + if len(pack.GameJackpotFund) == 0 || len(mgs) == 0 { + continue + } + proto.SetDefaults(pack) + //以平台为标识向该平台内所有玩家广播奖池变动消息,游戏内外的玩家可监听该消息,减少由gamesrv向worldsrv转发这一步 + //tags := []string{platform} + //logger.Logger.Trace("jackpot avengers", pack) + //PlayerMgrSington.BroadcastMessageToGroup(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, tags) + + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + cPack, err := MulticastMaker.CreateMulticastPacket(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, v...) + if err == nil { + proto.SetDefaults(cPack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), cPack) + } + } + } + } + } +} +func (this *SceneMgr) OnMiniTimer() { + for _, scene := range this.scenes { + scene.SyncPlayerCoin() + } +} + +func (this *SceneMgr) OnHourTimer() { + // for _, scene := range this.scenes { + // scene.OnHourTimer() + // } +} + +func (this *SceneMgr) OnDayTimer() { + logger.Logger.Info("(this *SceneMgr) OnDayTimer") + // for _, scene := range this.scenes { + // scene.OnDayTimer() + // } + PlayerMgrSington.OnDayTimer() +} + +func (this *SceneMgr) OnWeekTimer() { + logger.Logger.Info("(this *SceneMgr) OnWeekTimer") + // for _, scene := range this.scenes { + // scene.OnWeekTimer() + // } +} + +func (this *SceneMgr) OnMonthTimer() { + logger.Logger.Info("(this *SceneMgr) OnMonthTimer") + // for _, scene := range this.scenes { + // scene.OnMonthTimer() + // } +} + +func (this *SceneMgr) RebindPlayerSnId(oldSnId, newSnId int32) { + for _, s := range this.scenes { + s.RebindPlayerSnId(oldSnId, newSnId) + } +} + +func (this *SceneMgr) DestoryAllScene() { + for _, s := range this.scenes { + s.Destroy(true) + } +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *SceneMgr) ModuleName() string { + return "SceneMgr" +} + +func (this *SceneMgr) Init() { + //RestoreMDump(dumpFileName) + this.lastSendJackPot = time.Now() +} + +func (this *SceneMgr) Update() { + for _, s := range this.scenes { + s.OnTick() + } + if time.Now().Sub(this.lastSendJackPot) > 0 { + this.lastSendJackPot = time.Now().Add(time.Second * 3) + for platform, _ := range this.PlatformScene { + if platform != common.Platform_Sys { + this.JackPotSync(platform, + common.GameId_Avengers, + common.GameId_IceAge, + common.GameId_EasterIsland, + common.GameId_CaiShen, + common.GameId_TamQuoc) + } + } + } +} + +func (this *SceneMgr) Shutdown() { + //WriteMDump(dumpFileName) + for _, s := range this.scenes { + s.Destroy(true) + } + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(SceneMgrSington, time.Millisecond*50, 0) + //RegisteDayTimeChangeListener(SceneMgrSington) +} diff --git a/gamesrv/base/scene_policy.go b/gamesrv/base/scene_policy.go new file mode 100644 index 0000000..9b2ee48 --- /dev/null +++ b/gamesrv/base/scene_policy.go @@ -0,0 +1,275 @@ +package base + +import ( + "time" +) + +// 根据不同的房间模式,选择不同的房间业务逻辑 +var ScenePolicyPool = make(map[int]map[int]ScenePolicy) // gameId:gameMode + +type ScenePolicy interface { + //心跳间隔 + GetHeartBeatInterval() time.Duration + //场景开启事件 + OnStart(s *Scene) + //场景关闭事件 + OnStop(s *Scene) + //场景心跳事件 + OnTick(s *Scene) + //玩家进入事件 + OnPlayerEnter(s *Scene, p *Player) + //玩家离开事件 + OnPlayerLeave(s *Scene, p *Player, reason int) + //玩家掉线 + OnPlayerDropLine(s *Scene, p *Player) + //玩家重连 + OnPlayerRehold(s *Scene, p *Player) + //玩家 返回房间 + OnPlayerReturn(s *Scene, p *Player) + //玩家操作 + OnPlayerOp(s *Scene, p *Player, opcode int, params []int64) bool + //玩家操作 + OnPlayerOperate(s *Scene, p *Player, params interface{}) bool + //玩家事件 + OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) + //观众进入事件 + OnAudienceEnter(s *Scene, p *Player) + //观众离开事件 + OnAudienceLeave(s *Scene, p *Player, reason int) + //观众坐下事件 + OnAudienceSit(s *Scene, p *Player) + //观众掉线 + OnAudienceDropLine(s *Scene, p *Player) + // + GetSceneState(s *Scene, stateid int) SceneState + //是否完成了整个牌局 + IsCompleted(s *Scene) bool + //是否可以强制开始 + IsCanForceStart(s *Scene) bool + //强制开始 + ForceStart(s *Scene) + //当前状态能否加币 + CanAddCoin(s *Scene, p *Player, val int64) bool + //当前状态能否换桌 + CanChangeCoinScene(s *Scene, p *Player) bool + //创建场景扩展数据 + CreateSceneExData(s *Scene) interface{} + //创建玩家扩展数据 + CreatePlayerExData(s *Scene, p *Player) interface{} + // + PacketGameData(s *Scene) interface{} + InterventionGame(s *Scene, data interface{}) interface{} + //通知分场状态 + NotifyGameState(s *Scene) + + GetJackPotVal(s *Scene) int64 +} + +// 场景状态接口 +type SceneState interface { + GetState() int //状态ID + CanChangeTo(s SceneState) bool //切换到指定状态 + CanChangeCoinScene(s *Scene, p *Player) bool //当前状态能否换桌 + GetTimeout(s *Scene) int //超时时间 + OnEnter(s *Scene) //状态进入时 + OnLeave(s *Scene) //状态离开时 + OnTick(s *Scene) //状态tick + OnPlayerOp(s *Scene, p *Player, opcode int, params []int64) bool //玩家操作 + OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) //玩家事件 +} + +type SceneStateOperate interface { + OnPlayerOperate(s *Scene, p *Player, params interface{}) bool +} + +func GetScenePolicy(gameId, mode int) ScenePolicy { + if g, exist := ScenePolicyPool[gameId]; exist { + if p, exist := g[mode]; exist { + return p + } + } + return nil +} + +func RegisteScenePolicy(gameId, mode int, sp ScenePolicy) { + pool := ScenePolicyPool[gameId] + if pool == nil { + pool = make(map[int]ScenePolicy) + ScenePolicyPool[gameId] = pool + } + if pool != nil { + pool[mode] = sp + } +} + +type BaseScenePolicy struct { +} + +func (bsp *BaseScenePolicy) GetHeartBeatInterval() time.Duration { return time.Second } +func (bsp *BaseScenePolicy) OnStart(s *Scene) { + if s.aiMgr != nil { + s.aiMgr.OnStart(s) + } +} +func (bsp *BaseScenePolicy) OnStop(s *Scene) { + if s.aiMgr != nil { + s.aiMgr.OnStop(s) + } +} +func (bsp *BaseScenePolicy) OnTick(s *Scene) { + if s.aiMgr != nil { + s.aiMgr.OnTick(s) + } +} +func (bsp *BaseScenePolicy) OnPlayerEnter(s *Scene, p *Player) { + if s.WithLocalAI { + if p.ai != nil && p.IsLocal { + p.ai.OnSelfEnter(s, p) + } + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerEnter(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) OnPlayerLeave(s *Scene, p *Player, reason int) { + if s.WithLocalAI { + if p.ai != nil && p.IsLocal { + p.ai.OnSelfLeave(s, p, reason) + } + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerLeave(s, p, reason) + } + } + } +} +func (bsp *BaseScenePolicy) OnPlayerDropLine(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerDropLine(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) OnPlayerRehold(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerRehold(s, p) + } + } + } +} + +// 玩家返回房间 +func (bsp *BaseScenePolicy) OnPlayerReturn(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerReturn(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) OnPlayerOp(s *Scene, p *Player, op int, params []int64) bool { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerOp(s, p, op, params) + } + } + } + return false +} +func (bsp *BaseScenePolicy) OnPlayerOperate(s *Scene, p *Player, params interface{}) bool { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerOperate(s, p, params) + } + } + } + return false +} +func (bsp *BaseScenePolicy) OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnPlayerEvent(s, p, evtcode, params) + } + } + } + switch evtcode { + case PlayerEventRecharge, PlayerEventAddCoin: + p.SetTakeCoin(p.GetTakeCoin() + params[0]) // 为了保持游戏事件统计中的输赢分计算正确 + } +} + +func (bsp *BaseScenePolicy) OnAudienceEnter(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnAudienceEnter(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) OnAudienceLeave(s *Scene, p *Player, reason int) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnAudienceLeave(s, p, reason) + } + } + } +} +func (bsp *BaseScenePolicy) OnAudienceSit(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnAudienceSit(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) OnAudienceDropLine(s *Scene, p *Player) { + if s.WithLocalAI { + for _, pp := range s.Players { + if pp.ai != nil && pp != p { + pp.ai.OnAudienceDropLine(s, p) + } + } + } +} +func (bsp *BaseScenePolicy) GetSceneState(s *Scene, stateid int) SceneState { return G_BaseSceneState } +func (bsp *BaseScenePolicy) IsCompleted(s *Scene) bool { return false } +func (bsp *BaseScenePolicy) IsCanForceStart(s *Scene) bool { return false } +func (bsp *BaseScenePolicy) ForceStart(s *Scene) {} +func (bsp *BaseScenePolicy) CanAddCoin(s *Scene, p *Player, val int64) bool { return true } /*百人牛牛,百人金华多倍结算,且当前是减币的情况下,需要判断*/ +func (bsp *BaseScenePolicy) CanChangeCoinScene(s *Scene, p *Player) bool { return false } +func (bsp *BaseScenePolicy) CreateSceneExData(s *Scene) interface{} { return false } +func (bsp *BaseScenePolicy) CreatePlayerExData(s *Scene, p *Player) interface{} { return false } +func (bsp *BaseScenePolicy) PacketGameData(s *Scene) interface{} { return nil } +func (bsp *BaseScenePolicy) InterventionGame(s *Scene, data interface{}) interface{} { return nil } +func (bsp *BaseScenePolicy) NotifyGameState(s *Scene) {} +func (bsp *BaseScenePolicy) GetJackPotVal(s *Scene) int64 { return 0 } + +var G_BaseSceneState = &BaseSceneState{} + +type BaseSceneState struct { +} + +func (bst *BaseSceneState) GetState() int { return -1 } +func (bst *BaseSceneState) CanChangeTo(s SceneState) bool { return false } +func (bst *BaseSceneState) CanChangeCoinScene(s *Scene, p *Player) bool { return false } +func (bst *BaseSceneState) GetTimeout(s *Scene) int { return 0 } +func (bst *BaseSceneState) OnEnter(s *Scene) {} +func (bst *BaseSceneState) OnLeave(s *Scene) {} +func (bst *BaseSceneState) OnTick(s *Scene) {} +func (bst *BaseSceneState) OnPlayerOp(s *Scene, p *Player, opcode int, params []int64) bool { + return false +} +func (bst *BaseSceneState) OnPlayerEvent(s *Scene, p *Player, evtcode int, params []int64) {} diff --git a/gamesrv/base/serverctrl.go b/gamesrv/base/serverctrl.go new file mode 100644 index 0000000..1b2c5cf --- /dev/null +++ b/gamesrv/base/serverctrl.go @@ -0,0 +1,25 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/mongo" + "mongo.games.com/goserver/srvlib" +) + +func init() { + common.RegisteServerCtrlCallback(func(code int32) { + switch code { + case common.SrvCtrlStateSwitchCode: + pack := &server.ServerStateSwitch{ + SrvType: proto.Int(common.GetSelfSrvType()), + SrvId: proto.Int(common.GetSelfSrvId()), + } + proto.SetDefaults(pack) + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_GB_STATE_SWITCH), pack, common.GetSelfAreaId(), srvlib.WorldServerType) + case common.SrvCtrlResetMgoSession: + mongo.ResetAllSession() + } + }) +} diff --git a/gamesrv/base/serverstate.go b/gamesrv/base/serverstate.go new file mode 100644 index 0000000..9d375dd --- /dev/null +++ b/gamesrv/base/serverstate.go @@ -0,0 +1,33 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" +) + +var ServerStateMgr = &ServerStateManager{ + State: common.GAME_SESS_STATE_ON, +} + +type ServerStateManager struct { + State common.GameSessState +} + +func (this *ServerStateManager) Init() { + this.State = common.GAME_SESS_STATE_ON +} + +func (this *ServerStateManager) SetState(state common.GameSessState) { + this.State = state +} + +func (this *ServerStateManager) GetState() common.GameSessState { + return this.State +} + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + ServerStateMgr.Init() + return nil + }) +} diff --git a/gamesrv/base/slotjackpotpool.go b/gamesrv/base/slotjackpotpool.go new file mode 100644 index 0000000..d8a1e69 --- /dev/null +++ b/gamesrv/base/slotjackpotpool.go @@ -0,0 +1,160 @@ +package base + +type SlotJackpotPool struct { + Grand int64 //玩家巨奖池 + Big int64 //玩家大奖池 + Middle int64 //玩家中奖池 + Small int64 //玩家小奖池 + + GrandRob int64 //机器人巨奖池 + BigRob int64 //机器人大奖池 + MiddleRob int64 //机器人中奖池 + SmallRob int64 //机器人小奖池 + + Normal []int64 //玩家普通奖池 + NormalRob []int64 //机器人普通奖池 + SysAllOutput int64 + BetTotal int64 + //金鼓齐鸣 等游戏奖池根据玩家下注金额而变化 + SmallPools []int64 //小奖固定额度 + MiddlePools []int64 + BigPools []int64 + HugePools []int64 + BigRobPools []int64 + HugeRobPools []int64 + + VirtualJK int64 //虚拟奖池 +} + +func (this *SlotJackpotPool) GetTotalBig() int64 { + return this.Big + this.BigRob +} + +func (this *SlotJackpotPool) GetTotalGrand() int64 { + return this.Grand + this.GrandRob +} + +func (this *SlotJackpotPool) GetTotalMiddle() int64 { + return this.Middle + this.MiddleRob +} + +func (this *SlotJackpotPool) GetTotalSmall() int64 { + return this.Small + this.SmallRob +} +func (this *SlotJackpotPool) GetNormal(idx int) int64 { + return this.Normal[idx] +} + +func (this *SlotJackpotPool) AddToGrand(isRob bool, coin int64) { + if isRob { + this.GrandRob += coin + } else { + this.Grand += coin + } +} + +func (this *SlotJackpotPool) AddToBig(isRob bool, coin int64) { + if isRob { + this.BigRob += coin + } else { + this.Big += coin + } +} + +func (this *SlotJackpotPool) AddToMiddle(isRob bool, coin int64) { + if isRob { + this.MiddleRob += coin + } else { + this.Middle += coin + } +} + +func (this *SlotJackpotPool) AddToSmall(isRob bool, coin int64) { + if isRob { + this.SmallRob += coin + } else { + this.Small += coin + } +} + +func (this *SlotJackpotPool) NormalIsEnough(isRob bool, idx int, coin int64) bool { + if isRob { + if idx >= 0 && idx < len(this.NormalRob) { + return this.NormalRob[idx] >= coin + } + } else { + if idx >= 0 && idx < len(this.Normal) { + return this.Normal[idx] >= coin + } + } + return false +} + +func (this *SlotJackpotPool) AddToNormal(isRob bool, idx int, coin int64) { + if isRob { + if idx >= 0 && idx < len(this.NormalRob) { + this.NormalRob[idx] += coin + } + } else { + if idx >= 0 && idx < len(this.Normal) { + this.Normal[idx] += coin + } + } +} + +func (this *SlotJackpotPool) AddToBetTotal(isRob bool, coin int64) { + if !isRob { + this.BetTotal += coin + } +} + +func (this *SlotJackpotPool) AddToSystemOut(isRob bool, coin int64) { + if !isRob { + this.SysAllOutput += coin + } +} + +func (this *SlotJackpotPool) AddToBigPoolByIdx(isRob bool, idx int32, coin int64) { + //logger.Logger.Tracef("向大奖池放入金币 ===>%d", coin) + if isRob { + this.BigRobPools[idx] += coin + } else { + this.BigPools[idx] += coin + } +} + +func (this *SlotJackpotPool) AddToHugePoolByIdx(isRob bool, idx int32, coin int64) { + //logger.Logger.Tracef("向巨奖池放入金币 ===>%d", coin) + if isRob { + this.HugeRobPools[idx] += coin + } else { + this.HugePools[idx] += coin + } +} + +func (this *SlotJackpotPool) AddToPoolsByIdx(isRob bool, idx int32, coin int64) { + this.AddToBigPoolByIdx(isRob, idx, coin) + this.AddToHugePoolByIdx(isRob, idx, coin) +} + +func (this *SlotJackpotPool) GetSmallPools(idx int) int64 { + return this.SmallPools[idx] +} +func (this *SlotJackpotPool) GetMiddlePools(idx int) int64 { + return this.MiddlePools[idx] +} + +func (this *SlotJackpotPool) GetBigPools(isRob bool, idx int) int64 { + if isRob { + return this.BigRobPools[idx] + } else { + return this.BigPools[idx] + } +} +func (this *SlotJackpotPool) GetHugePools(isRob bool, idx int) int64 { + if isRob { + return this.HugeRobPools[idx] + } else { + return this.HugePools[idx] + } +} diff --git a/gamesrv/base/slotspoolmanager.go b/gamesrv/base/slotspoolmanager.go new file mode 100644 index 0000000..26c1f03 --- /dev/null +++ b/gamesrv/base/slotspoolmanager.go @@ -0,0 +1,93 @@ +package base + +import ( + "encoding/json" + "fmt" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" +) + +var SlotsPoolMgr = &SlotsPoolManager{ + SlotsPoolData: make(map[string]interface{}), + SlotsPoolStr: make(map[string]string), +} + +type SlotsPoolManager struct { + SlotsPoolData map[string]interface{} + SlotsPoolStr map[string]string + SlotsPoolDBKey string +} + +func (this *SlotsPoolManager) GetPool(gamefreeId int32, platform string) string { + key := fmt.Sprintf("%v-%v", gamefreeId, platform) + if str, exist := this.SlotsPoolStr[key]; exist { + return str + } + return "" +} + +func (this *SlotsPoolManager) SetPool(gamefreeId int32, platform string, pool interface{}) { + key := fmt.Sprintf("%v-%v", gamefreeId, platform) + this.SlotsPoolData[key] = pool +} + +//func (this *SlotsPoolManager) GetSlotsPoolData(gamefreeId int32, platform string) interface{} { +// key := fmt.Sprintf("%v-%v", gamefreeId, platform) +// return this.SlotsPoolData[key] +//} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *SlotsPoolManager) ModuleName() string { + return "SlotsPoolManager" +} + +func (this *SlotsPoolManager) Init() { + this.SlotsPoolDBKey = fmt.Sprintf("SlotsPoolManager_Srv%v", common.GetSelfSrvId()) + data := model.GetStrKVGameData(this.SlotsPoolDBKey) + err := json.Unmarshal([]byte(data), &this.SlotsPoolStr) + if err != nil { + logger.Logger.Error("Unmarshal slots pool error:", err) + } +} + +func (this *SlotsPoolManager) Update() { + this.Save() +} + +func (this *SlotsPoolManager) Shutdown() { + this.Save() + module.UnregisteModule(this) +} + +func (this *SlotsPoolManager) Save() { + //数据先整合 + for k, v := range this.SlotsPoolData { + data, err := json.Marshal(v) + if err == nil { + this.SlotsPoolStr[k] = string(data) + } + } + + //再保存 + buff, err := json.Marshal(this.SlotsPoolStr) + if err == nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UptStrKVGameData(this.SlotsPoolDBKey, string(buff)) + }), nil, "SaveActGoldCome").StartByFixExecutor("UptStrKVGameData") + } else { + logger.Logger.Error("Marshal coin pool error:", err) + } +} + +func init() { + module.RegisteModule(SlotsPoolMgr, time.Minute, 0) +} diff --git a/gamesrv/base/srvdatamgrex.go b/gamesrv/base/srvdatamgrex.go new file mode 100644 index 0000000..9759469 --- /dev/null +++ b/gamesrv/base/srvdatamgrex.go @@ -0,0 +1,197 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" +) + +var SrvDataMgrEx = &SrvDataManagerEx{ + DataReloader: make(map[string]SrvDataReloadInterface), +} + +type SrvDataManagerEx struct { + DataReloader map[string]SrvDataReloadInterface +} + +func RegisterDataReloader(fileName string, sdri SrvDataReloadInterface) { + SrvDataMgrEx.DataReloader[fileName] = sdri +} + +type SrvDataReloadInterface interface { + Reload() +} + +// 奔驰宝马表格参数 +var SystemChanceMgrEx = &PBDB_SystemChanceMgrEx{} + +type PBDB_SystemChanceMgrEx struct { + RollCoinRate []int32 //赔率数组 + RollCoinChance []int64 //概率数组 + RollCoinIds []int32 +} + +func (this *PBDB_SystemChanceMgrEx) Reload() { + this.RollCoinIds = nil + this.RollCoinRate = nil + this.RollCoinChance = nil + for _, value := range srvdata.PBDB_SystemChanceMgr.Datas.Arr { + this.RollCoinIds = append(this.RollCoinIds, value.GetId()) + this.RollCoinRate = append(this.RollCoinRate, value.GetCoin()) + this.RollCoinChance = append(this.RollCoinChance, int64(value.GetId()), int64(value.GetRate())) + } + logger.Logger.Trace(this) +} +func (this *PBDB_SystemChanceMgrEx) GetRollCoinIds() []int32 { + var rollCoinIds = []int32{} + for _, value := range SystemChanceMgrEx.RollCoinIds { + rollCoinIds = append(rollCoinIds, value) + } + for i := 0; i < len(rollCoinIds); i++ { + rollIndex := common.RandInt(len(rollCoinIds)) + rollCoinIds[i], rollCoinIds[rollIndex] = rollCoinIds[rollIndex], rollCoinIds[i] + } + return rollCoinIds +} + +////飞禽走兽表格参数 +//var AnimalsChanceMgrEx = &PBDB_AnimalsChanceMgrEx{} +// +//type PBDB_AnimalsChanceMgrEx struct { +// RollAnimalsIds []int64 +// //RollAnimalsOdds []int64 //赔率数组 +// RollAnimalsOdds map[int64]int64 +// RollAnimalsRate []int64 //概率数组 (id,rate) +//} +// +//func (this *PBDB_AnimalsChanceMgrEx) Reload() { +// this.RollAnimalsIds = nil +// this.RollAnimalsOdds = make(map[int64]int64) +// this.RollAnimalsRate = nil +// for _, value := range srvdata.PBDB_AnimalsChanceMgr.Datas.Arr { +// this.RollAnimalsIds = append(this.RollAnimalsIds, int64(value.GetId())) +// this.RollAnimalsOdds[int64(value.GetId())] = int64(value.GetCoin()[0]) +// this.RollAnimalsRate = append(this.RollAnimalsRate, int64(value.GetId()), int64(value.GetRateA())) +// if int64(value.GetId()) == int64(rollanimals.RollAnimals_Shark) { +// this.RollAnimalsOdds[int64(rollanimals.RollAnimals_Big_Shark)] = int64(value.GetCoin()[1]) +// } +// } +// this.RollAnimalsOdds[int64(rollanimals.RollAnimals_Bird)] = int64(rollanimals.RollAnimals_BirdOdds) +// this.RollAnimalsOdds[int64(rollanimals.RollAnimals_Beast)] = int64(rollanimals.RollAnimals_BeastOdds) +// +// logger.Logger.Trace(this) +//} + +// 深林舞会表格参数 +var AnimalColorMgrEx = &DB_AnimalColorMgrEx{make(map[int32][]int32)} + +type DB_AnimalColorMgrEx struct { + RollColorRate map[int32][]int32 //赔率数组 +} + +func (this *DB_AnimalColorMgrEx) Reload() { + for _, value := range srvdata.PBDB_AnimalColorMgr.Datas.Arr { + this.RollColorRate[value.GetId()] = value.GetColorChance() + } + for key, value := range this.RollColorRate { + if len(value) != 13 { + logger.Logger.Error("Animal color data reload error.") + this.RollColorRate[key] = []int32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + } + } +} + +var FishTemplateEx = &DB_FishMgrEx{FishPool: make(map[int32]*FishTemplate)} + +// 初始化鱼模板 +type FishTemplate struct { + ID int32 //鱼的模板ID + Name string //鱼的昵称 + Exp int32 //鱼的基础经验 + DropCoin []int32 //鱼掉落的基础金币数 + Boss int32 //BOSS + + Speed int32 //速度 + HP []int32 //血量 + Ratio int32 //底分系数 + Ratio1 []int32 //激光炮掉血比例 + FishType int32 //鱼本身的分类 + RandomCoin string //掉落金币的倍数权重 + Rate int32 //概率 大闹天宫尝试 + Jackpot int32 //奖金鱼概率(天天捕鱼) + DealRate int32 //致命一击概率(天天捕鱼) + IsBoss int32 //是否是BOSS鱼 + Gold []int32 //金币 + EXP int32 //经验 +} +type DB_FishMgrEx struct { + FishPool map[int32]*FishTemplate // 鱼id:鱼配置文件 +} + +func (this *DB_FishMgrEx) Reload() { + for _, value := range srvdata.PBDB_FishMgr.Datas.Arr { + ft := &FishTemplate{} + ft.ID = value.GetId() + ft.Name = value.GetName() + ft.Exp = value.GetExp() + ft.Boss = value.GetIsBoss() + ft.Speed = value.GetSpeed() + ft.FishType = value.GetFishType() + ft.RandomCoin = value.GetRandomCoin() + ft.Rate = value.GetRate() + ft.IsBoss = value.GetIsBoss() + ft.Gold = value.Gold + ft.EXP = value.Exp + this.FishPool[value.GetId()] = ft + } +} + +var FishOutEx = &DB_FishOutMgrEx{FishOutPool: make(map[int32]*AppearFish)} + +// 初始化鱼模板 +type AppearFish struct { + FishId int32 //对应fish表中的fishType + Name string //鱼的名字 + Exp int32 //经验值 + Multiple []int32 //倍数 + Prob float64 //死亡概率 目前不用 看后期设计 每个场次单独控制 还是整体控制 + BaseRates float64 //基础概率 目前不用 + Path []int32 //出鱼路径 + FishNum []int32 //出鱼数量 + Interval int32 //出鱼间隔 + SceneType int32 + Event int32 //鱼事件 +} + +type DB_FishOutMgrEx struct { + FishOutPool map[int32]*AppearFish // 鱼id:鱼配置文件 +} + +// 出鱼策略初始化 +func (this *DB_FishOutMgrEx) InitFishAppear() { + this.FishOutPool = make(map[int32]*AppearFish) + for _, v := range srvdata.PBDB_FishOutMgr.Datas.Arr { + logger.Logger.Tracef("初始化房间出鱼列表 fishId = %v", v.Id) + this.FishOutPool[v.Id] = &AppearFish{ + FishId: v.Id, + Exp: v.Exp, + Multiple: v.Multiple, + Path: v.Path, + Name: v.Name, + Interval: v.RefreshInterval, + FishNum: v.Count, + SceneType: v.SceneType, + Event: v.Event, + } + } +} + +// 初始化在线奖励系统 +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + logger.Logger.Info("初始化牌库[S]") + defer logger.Logger.Info("初始化牌库[E]") + return nil + }) +} diff --git a/gamesrv/base/srvsessmgr.go b/gamesrv/base/srvsessmgr.go new file mode 100644 index 0000000..335ee56 --- /dev/null +++ b/gamesrv/base/srvsessmgr.go @@ -0,0 +1,56 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var SrvSessMgrSington = &SrvSessMgr{} + +type SrvSessMgr struct { +} + +// 注册事件 +func (this *SrvSessMgr) OnRegiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.WorldServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnRegiste (WorldSrv):", s) + } else if srvInfo.GetType() == srvlib.GateServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnRegiste (GateSrv):", s) + } else if srvInfo.GetType() == srvlib.RankServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnRegiste (RankSrv):", s) + } else if srvInfo.GetType() == int32(common.RobotServerType) { + logger.Logger.Warn("(this *SrvSessMgr) OnRegiste (RobotSrv):", s) + NpcServerAgentSingleton.OnConnected() + } + } + } +} + +// 注销事件 +func (this *SrvSessMgr) OnUnregiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.WorldServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnUnregiste (WorldSrv):", s) + } else if srvInfo.GetType() == srvlib.GateServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnUnregiste (GateSrv):", s) + } else if srvInfo.GetType() == srvlib.RankServerType { + logger.Logger.Warn("(this *SrvSessMgr) OnUnregiste (GateSrv):", s) + } else if srvInfo.GetType() == int32(common.RobotServerType) { + logger.Logger.Warn("(this *SrvSessMgr) OnUnregiste (RobotSrv):", s) + NpcServerAgentSingleton.OnDisconnected() + } + } + } +} + +func init() { + srvlib.ServerSessionMgrSington.AddListener(SrvSessMgrSington) +} diff --git a/gamesrv/base/sysprofitcoinmgr.go b/gamesrv/base/sysprofitcoinmgr.go new file mode 100644 index 0000000..f342de3 --- /dev/null +++ b/gamesrv/base/sysprofitcoinmgr.go @@ -0,0 +1,117 @@ +package base + +import ( + "fmt" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + module.RegisteModule(SysProfitCoinMgr, time.Hour, 0) +} + +var SysProfitCoinMgr = &SysProfitCoinManager{} + +// SysProfitCoinManager 存储 +// 目前只有捕鱼用到 +type SysProfitCoinManager struct { + SysPfCoin *model.SysProfitCoin +} + +func (this *SysProfitCoinManager) GetBetAndSysOut(key string) (int64, int64) { + if data, exist := this.SysPfCoin.ProfitCoin[key]; exist { + return data.PlaysBet, data.SysPushCoin + } + return 1, 0 +} + +// PushBetAndSysOut 增加玩家总投入,系统总产出 +func (this *SysProfitCoinManager) PushBetAndSysOut(key string, bet int64, systemOut int64) { + data, ok := this.SysPfCoin.ProfitCoin[key] + if !ok { + data = new(model.SysCoin) + this.SysPfCoin.ProfitCoin[key] = data + } + data.PlaysBet += bet + data.SysPushCoin += systemOut +} + +func (this *SysProfitCoinManager) Get(key string) *model.SysCoin { + data, ok := this.SysPfCoin.ProfitCoin[key] + if !ok { + data = new(model.SysCoin) + this.SysPfCoin.ProfitCoin[key] = data + } + return data +} + +func (this *SysProfitCoinManager) Del(key string) { + if _, ok := this.SysPfCoin.ProfitCoin[key]; ok { + delete(this.SysPfCoin.ProfitCoin, key) + } +} + +func (this *SysProfitCoinManager) ModuleName() string { + return "SysProfitCoinManager" +} + +func (this *SysProfitCoinManager) Init() { + this.SysPfCoin = model.InitSysProfitCoinData(fmt.Sprintf("%d", common.GetSelfSrvId())) +} + +func (this *SysProfitCoinManager) Update() { + this.Save() +} + +func (this *SysProfitCoinManager) Shutdown() { + this.Save() + module.UnregisteModule(this) +} + +func (this *SysProfitCoinManager) Save() { + data := &model.SysProfitCoin{ // + ProfitCoin: make(map[string]*model.SysCoin), + LogId: this.SysPfCoin.LogId, + Key: this.SysPfCoin.Key, + } + for i, v := range this.SysPfCoin.ProfitCoin { + v1 := &model.SysCoin{ // + PlaysBet: v.PlaysBet, + SysPushCoin: v.SysPushCoin, + //CommonPool: v.CommonPool, + //Version: v.Version, + } + data.ProfitCoin[i] = v1 + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.SaveSysProfitCoin(data /*this.SysPfCoin*/) // fatal error: concurrent map iteration and map write + if err != nil { + logger.Logger.Errorf("SaveSysProfitCoin err:%v ", err) + } + return err + }), nil, "SaveSysProfitCoin").Start() +} + +type CoinPoolFishListener struct { +} + +func (f *CoinPoolFishListener) OnSettingUpdate(oldSetting, newSetting *webapi.CoinPoolSetting) { + //GameFree := srvdata.PBDB_GameFreeMgr.GetData(newSetting.GetGameFreeId()) + //if GameFree != nil { + // gameId := int(GameFree.GetGameId()) + // if gameId == common.GameId_HFishing || gameId == common.GameId_TFishing { + // if oldSetting == nil || oldSetting.GetCtroRate() != newSetting.GetCtroRate() { + // keyPf := fmt.Sprintf("%v_%v", newSetting.GetPlatform(), newSetting.GetGameFreeId()) + // SysProfitCoinMgr.Del(keyPf) + // } + // } + //} +} diff --git a/gamesrv/base/waitquene.go b/gamesrv/base/waitquene.go new file mode 100644 index 0000000..4fe5234 --- /dev/null +++ b/gamesrv/base/waitquene.go @@ -0,0 +1,41 @@ +package base + +type WaitOpPlayer struct { + pos int + flag int +} + +type WaitQuene struct { + quene [8]WaitOpPlayer + cnt int +} + +func (wq *WaitQuene) Compress() { + if wq.cnt < 2 { + return + } + var flag int + //for i := 0; i < wq.cnt-1; { + // if wq.quene[i].pos == wq.quene[i+1].pos { + // flag = wq.quene[i].flag | wq.quene[i+1].flag + // copy(wq.quene[i:], wq.quene[i+1:wq.cnt]) + // wq.quene[i].flag = flag + // wq.cnt-- + // wq.quene[i+1].flag = 0 + // } else { + // i++ + // } + //} + for i := 0; i < wq.cnt; i++ { + for j := i + 1; j < wq.cnt; j++ { + if wq.quene[i].flag != 0 && wq.quene[i].pos == wq.quene[j].pos && wq.quene[j].flag != 0 { + flag = wq.quene[i].flag | wq.quene[j].flag + copy(wq.quene[i:], wq.quene[j:wq.cnt]) + wq.quene[i].flag = flag + wq.cnt-- + wq.quene[j].flag = 0 + } + } + } + +} diff --git a/gamesrv/base/worldsessmgr.go b/gamesrv/base/worldsessmgr.go new file mode 100644 index 0000000..ae2e5c6 --- /dev/null +++ b/gamesrv/base/worldsessmgr.go @@ -0,0 +1,40 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var WorldSessMgrSington = &WorldSessMgr{} + +type WorldSessMgr struct { +} + +func (this *WorldSessMgr) OnRegiste(s *netlib.Session) { + +} + +// 注册事件 +func (this *WorldSessMgr) SendToMessage(packetid int, rawpack interface{}) { + ss := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), srvlib.WorldServerType, common.GetWorldSrvId()) + ss.Send(packetid, rawpack) +} + +// 注销事件 +func (this *WorldSessMgr) OnUnregiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.WorldServiceType { + logger.Logger.Warn("(this *WorldSessMgr) OnUnregiste (Srv):", s) + } + } + } +} + +func init() { + srvlib.ServerSessionMgrSington.AddListener(WorldSessMgrSington) +} diff --git a/gamesrv/base/xslotjackpotpool.go b/gamesrv/base/xslotjackpotpool.go new file mode 100644 index 0000000..c3dda37 --- /dev/null +++ b/gamesrv/base/xslotjackpotpool.go @@ -0,0 +1,10 @@ +package base + +type XSlotJackpotPool struct { + //PrizeFund int64 + JackpotFund int64 +} + +func (this *XSlotJackpotPool) GetJackPotFund() int64 { + return this.JackpotFund +} diff --git a/gamesrv/base/xslotspoolmanager.go b/gamesrv/base/xslotspoolmanager.go new file mode 100644 index 0000000..ba266f4 --- /dev/null +++ b/gamesrv/base/xslotspoolmanager.go @@ -0,0 +1,94 @@ +package base + +//import ( +// "encoding/json" +// "fmt" +// +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/module" +// +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +//) +// +//var XSlotsPoolMgr = &XSlotsPoolManager{ +// //SlotsPoolData: make(map[string]interface{}), +// SlotsPoolStr: make(map[string]string), +//} +// +//type XSlotsPoolManager struct { +// //SlotsPoolData map[string]interface{} +// SlotsPoolStr map[string]string +// SlotsPoolDBKey string +//} +// +//func (this *XSlotsPoolManager) GetPool(gamefreeId int32, platform string) string { +// key := fmt.Sprintf("%v-%v", gamefreeId, platform) +// if str, exist := this.SlotsPoolStr[key]; exist { +// return str +// } +// return "" +//} +// +////func (this *XSlotsPoolManager) SetPool(gamefreeId int32, platform string, pool interface{}) { +//// key := fmt.Sprintf("%v-%v", gamefreeId, platform) +//// this.SlotsPoolData[key] = pool +////} +// +////func (this *XSlotsPoolManager) GetPoolByPlatform(platform string) map[string]interface{} { +//// slots := make(map[string]interface{}) +//// for k, v := range this.SlotsPoolData { +//// str := strings.Split(k, "-") +//// if len(str) == 2 && str[1] == platform { +//// idStr := str[0] // gamefreeid +//// slots[idStr] = v +//// } +//// } +//// return slots +////} +// +//// ////////////////////////////////////////////////////////////////// +//// / Module Implement [beg] +//// ////////////////////////////////////////////////////////////////// +//func (this *XSlotsPoolManager) ModuleName() string { +// return "XSlotsPoolManager" +//} +// +//func (this *XSlotsPoolManager) Init() { +// this.SlotsPoolDBKey = fmt.Sprintf("XSlotsPoolManager_Srv%v", common.GetSelfSrvId()) +// data := model.GetStrKVGameData(this.SlotsPoolDBKey) +// err := json.Unmarshal([]byte(data), &this.SlotsPoolStr) +// if err != nil { +// logger.Logger.Error("Unmarshal slots pool error:", err) +// } +//} +// +//func (this *XSlotsPoolManager) Update() { +// this.Save() +//} +// +//func (this *XSlotsPoolManager) Shutdown() { +// this.Save() +// module.UnregisteModule(this) +//} +// +//func (this *XSlotsPoolManager) Save() { +// //数据先整合 +// //for k, v := range this.SlotsPoolData { +// // data, err := json.Marshal(v) +// // if err == nil { +// // this.SlotsPoolStr[k] = string(data) +// // } +// //} +// //再保存 +// buff, err := json.Marshal(this.SlotsPoolStr) +// if err == nil { +// model.UptStrKVGameData(this.SlotsPoolDBKey, string(buff)) +// } else { +// logger.Logger.Error("Marshal coin pool error:", err) +// } +//} +// +//func init() { +// //module.RegisteModule(XSlotsPoolMgr, time.Minute, 0) +//} diff --git a/gamesrv/chess/action.go b/gamesrv/chess/action.go new file mode 100644 index 0000000..7108586 --- /dev/null +++ b/gamesrv/chess/action.go @@ -0,0 +1,98 @@ +package chesstitians + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSPlayerOpPacketFactory struct { +} +type CSPlayerOpHandler struct { +} + +func (f *CSPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &chesstitians.CSChesstitiansPlayerOp{} + return pack +} +func (h *CSPlayerOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerOpHandler Process recv ", data) + if msg, ok := data.(*chesstitians.CSChesstitiansPlayerOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSPlayerOpHandler p.scene == nil") + return nil + } + if !common.IsChess(scene.GetGameId()) { + logger.Logger.Error("CSPlayerOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + sp := scene.GetScenePolicy() + if sp != nil { + sp.OnPlayerOp(scene, p, int(msg.GetOpCode()), msg.GetOpParam()) + } + return nil + } + return nil +} + +// dev + +type CSPlayerDevOpPacketFactory struct { +} +type CSPlayerDevOpHandler struct { +} + +func (f *CSPlayerDevOpPacketFactory) CreatePacket() interface{} { + pack := &chesstitians.CSChesstitiansPlayerDevOp{} + return pack +} + +func (h *CSPlayerDevOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerDevOpHandler Process recv ", data) + if msg, ok := data.(*chesstitians.CSChesstitiansPlayerDevOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerDevOpHandler p == nil") + return nil + } + scene := p.GetScene() + sp := scene.GetScenePolicy() + if scene == nil { + logger.Logger.Warn("CSPlayerDevOpHandler p.scene == nil") + return nil + } + if !common.IsChess(scene.GetGameId()) { + logger.Logger.Error("CSPlayerDevOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + if sp != nil { + sp.(*ScenePolicyEntity).OnPlayerDevOp(scene, p, int(msg.GetOpCode()), msg.GetOpParamStr(), int64(msg.GetCurOpIdx())) + //logger.Logger.Info("msg", msg) + } + return nil + } + return nil +} + +func init() { + common.RegisterHandler(int(chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), &CSPlayerOpHandler{}) + netlib.RegisterFactory(int(chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), &CSPlayerOpPacketFactory{}) + // dev + common.RegisterHandler(int(chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerDevOp), &CSPlayerDevOpHandler{}) + netlib.RegisterFactory(int(chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerDevOp), &CSPlayerDevOpPacketFactory{}) + +} diff --git a/gamesrv/chess/player.go b/gamesrv/chess/player.go new file mode 100644 index 0000000..bd910e2 --- /dev/null +++ b/gamesrv/chess/player.go @@ -0,0 +1,88 @@ +package chesstitians + +import ( + rule "mongo.games.com/game/gamerule/chess" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "time" +) + +// PlayerEx 玩家身上的额外数据 +type PlayerEx struct { + *base.Player // 玩家信息 + isBlack bool // 黑棋(默认是白棋先手) + nextPlay bool // 是否再来一局 + + winCoin int64 // 本局赢的钱 + taxCoin int64 // 本局税收 + isWin int32 // 本局输赢 1赢 2输 0平 + winTimes int32 // 连赢次数 + otherScore int32 // 额外积分 + nextRank int32 // 再赢几局晋级 + winScore int32 // 本局积分变化 + oldGrade int64 // 原来的积分 + totalTime int64 // 下棋总时长 + lastTime time.Time +} + +func (p *PlayerEx) init() { + p.winCoin = 0 + p.isBlack = false + p.nextPlay = false + p.oldGrade = 0 + p.totalTime = 0 + p.lastTime = time.Time{} + if !p.GetScene().IsMatchScene() && !p.GetScene().IsPrivateScene() { //非比赛场 非私有房间 + p.LoadPlayerGameInfo(p.GetScene().KeyGameId) + } +} + +func (p *PlayerEx) Clear() { + p.winCoin = 0 + p.isBlack = false + p.nextPlay = false + p.oldGrade = 0 + p.totalTime = 0 + p.lastTime = time.Time{} + p.MarkFlag(base.PlayerState_WaitNext) +} + +func (p *PlayerEx) CanOp() bool { + if p.IsGameing() { + return true + } + return false +} + +func (p *PlayerEx) LoadPlayerGameInfo(keyGameId string) { + if p.IsRob { + return + } + if p.GDatas == nil { + p.GDatas = make(map[string]*model.PlayerGameInfo) + } + if data, exist := p.GDatas[keyGameId]; !exist { + data = new(model.PlayerGameInfo) + data.Statics.TotalOut = 920000 + data.Statics.TotalIn = 1000000 + p.GDatas[keyGameId] = data + } +} + +// GetTotalTime 获取玩家总下棋消耗的操作时长,单位秒 +func (p *PlayerEx) GetTotalTime() int32 { + if s := p.GetScene(); s != nil && s.GetSceneState().GetState() == rule.SceneStatePlayerOp && p.IsGameing() { + if ex, ok := s.ExtraData.(*SceneEx); ok { + if ex.GetCurOpPos() == p.Pos { + return p.getTotalTime() + } else { + return int32(p.totalTime) + } + } + } + return 0 +} + +func (p *PlayerEx) getTotalTime() int32 { + return int32(p.totalTime + int64(time.Now().Sub(p.lastTime).Seconds())) +} diff --git a/gamesrv/chess/scene.go b/gamesrv/chess/scene.go new file mode 100644 index 0000000..f65d018 --- /dev/null +++ b/gamesrv/chess/scene.go @@ -0,0 +1,783 @@ +package chesstitians + +import ( + "fmt" + "math" + "sort" + "strings" + "time" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/chess" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +// SceneEx 房间上的额外数据 +type SceneEx struct { + *base.Scene // 场景 + players map[int32]*PlayerEx // 玩家信息 + seats [rule.MaxNumOfPlayer]*PlayerEx // 玩家 + opPos int // 当前等待出牌的玩家位置 + winSnId int32 // 赢家id + masterSnId int32 // 房主 + isAllRob bool // 是否是纯AI场 + chess rule.Logic // 棋盘对象 + lastMove []int32 // 上一步的行动 + variant int // 棋类变种 对应fairy-stockfish + stepSnId int32 // 计步玩家SnId + stepNum int // 计步回合数 + stepLimit int // 计步最大回合数 + stepKing int // 孤王计步回合数 + drawSnId int32 // 求和的玩家SnId + nextSnId int32 // 再来一局玩家SnId +} + +func getChessVariant(gameId int) int { + var variant int + switch gameId { + case common.GameId_Chesstitians: + variant = rule.ChessVarChess + case common.GameId_ChesstitiansMakruk: + variant = rule.ChessVarMakruk + case common.GameId_ChesstitiansCambodian, common.GameId_ChesstitiansCambodianRobot: + variant = rule.ChessVarCambodian + } + return variant +} + +func NewSceneEx(s *base.Scene) *SceneEx { + variant := getChessVariant(s.GameId) + chess := rule.NewChess(variant) + chess.Init() + sceneEx := &SceneEx{ + Scene: s, + chess: chess, + players: make(map[int32]*PlayerEx), + variant: variant, + } + return sceneEx +} + +func (e *SceneEx) init() bool { + // 初始配置 + e.SetPlayerNum(rule.MaxNumOfPlayer) + return true +} + +func (e *SceneEx) Clear() { + // 重置 + e.chess.Init() + e.opPos = rule.InvalidPos + e.isAllRob = false + e.winSnId = 0 + e.nextSnId = 0 + for _, player := range e.players { + if player != nil { + player.UnmarkFlag(base.PlayerState_WaitNext) + } + } + e.lastMove = e.lastMove[:0] + e.stepSnId = 0 + e.stepNum = 0 + e.stepLimit = 0 + e.drawSnId = 0 +} + +func (e *SceneEx) CanStart() bool { + if e.GetGaming() { + return false + } + + if e.GetPlayerCnt() == rule.MaxNumOfPlayer { + return true + } + return false +} + +func (e *SceneEx) GetPlayerCnt() int { + var cnt int + for i := 0; i < e.GetPlayerNum(); i++ { + playerEx := e.seats[i] + if playerEx == nil { + continue + } + cnt++ + } + return cnt +} + +func (e *SceneEx) GetGamingPlayerCnt() int { + var cnt int + for i := 0; i < e.GetPlayerNum(); i++ { + playerEx := e.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsGameing() { + cnt++ + } + } + return cnt +} + +func (e *SceneEx) GetRobotCnt() int { + var cnt int + for i := 0; i < e.GetPlayerNum(); i++ { + playerEx := e.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsRob { + cnt++ + } + } + return cnt +} + +func (e *SceneEx) GetGamingRobotCnt() int { + var cnt int + for i := 0; i < e.GetPlayerNum(); i++ { + playerEx := e.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsRob && playerEx.IsGameing() { + cnt++ + } + } + return cnt +} + +func (e *SceneEx) GetSeatPlayerCnt() int { + var cnt int + for i := 0; i < e.GetPlayerNum(); i++ { + playerEx := e.seats[i] + if playerEx == nil { + continue + } + cnt++ + } + return cnt +} + +func (e *SceneEx) delPlayer(p *base.Player) { + if p, exist := e.players[p.SnId]; exist { + e.seats[p.GetPos()] = nil + delete(e.players, p.SnId) + } +} + +func (e *SceneEx) OnPlayerLeave(p *base.Player, reason int) { + e.delPlayer(p) + e.BroadcastPlayerLeave(p, reason) +} + +func (e *SceneEx) SceneDestroy(force bool) { + //销毁房间 + e.Scene.Destroy(force) +} + +func (e *SceneEx) BroadcastPlayerLeave(p *base.Player, reason int) { + scLeavePack := &chesstitians.SCChesstitiansPlayerLeave{ + Pos: proto.Int(p.GetPos()), + } + proto.SetDefaults(scLeavePack) + logger.Logger.Trace("SCChesstitiansPlayerLeave: ", p.SnId, " scLeavePack: ", scLeavePack) + e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), scLeavePack, p.GetSid()) +} + +func (e *SceneEx) BroadcastAudienceNum(p *base.Player) { + pack := &chesstitians.SCChesstitiansUpdateAudienceNum{ + AudienceNum: proto.Int(e.GetAudiencesNum()), + } + proto.SetDefaults(pack) + e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateAudienceNum), pack, p.GetSid()) +} + +// BroadcastUpdateMasterSnId 广播房主更换 +func (e *SceneEx) BroadcastUpdateMasterSnId(changeState bool) { + pack := &chesstitians.SCChesstitiansUpdateMasterSnid{ + MasterSnid: proto.Int32(e.masterSnId), + } + proto.SetDefaults(pack) + e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), pack, 0) + //logger.Logger.Trace("BroadcastUpdateMasterSnId ", pack) + + //重置开始倒计时 + if changeState && e.CanStart() { + e.Scene.ChangeSceneState(rule.SceneStateWaitStart) + } +} + +func (e *SceneEx) playerOpPack(snId int32, opcode int, opRetCode chesstitians.OpResultCode, params []int64) *chesstitians.SCChesstitiansPlayerOp { + pack := &chesstitians.SCChesstitiansPlayerOp{ + OpCode: proto.Int(opcode), + OpParam: params, + SnId: proto.Int32(snId), + OpRetCode: opRetCode, + Check: e.chess.GetCheck(), + Checkmate: e.chess.GetCheckmate(), + StepSnId: e.stepSnId, + StepNum: int64(proto.Int(e.stepNum)), + StepLimit: int64(e.stepLimit), + } + p := e.GetPlayer(snId) + if p != nil { + if ex, ok := p.ExtraData.(*PlayerEx); ok { + pack.TotalTime = append(pack.TotalTime, ex.GetTotalTime()) + for _, v := range e.seats { + if v != nil && v.Pos != ex.Pos { + pack.TotalTime = append(pack.TotalTime, v.GetTotalTime()) + break + } + } + } + } + + proto.SetDefaults(pack) + return pack +} + +// OnPlayerSCOp 发送玩家操作情况 +func (e *SceneEx) OnPlayerSCOp(p *base.Player, opcode int, opRetCode chesstitians.OpResultCode, params []int64) { + pack := e.playerOpPack(p.SnId, opcode, opRetCode, params) + p.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), pack) + logger.Logger.Tracef("OnPlayerSCOp %s", pack) +} + +// BroadcastPlayerSCOp 广播发送玩家操作情况 +func (e *SceneEx) BroadcastPlayerSCOp(snid int32, opcode int, opRetCode chesstitians.OpResultCode, params []int64) { + pack := e.playerOpPack(snid, opcode, opRetCode, params) + e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), pack, 0) + logger.Logger.Tracef("BroadcastPlayerSCOp %s", pack) +} + +// FindOnePos 逆时针找一个空位 +func (e *SceneEx) FindOnePos() int { + for i := 0; i < e.GetPlayerNum(); i++ { + if e.seats[i] == nil { + return i + } + } + return rule.InvalidPos +} + +func (e *SceneEx) SystemCoinOut() int64 { + systemGain := int64(0) + for i := 0; i < e.GetPlayerNum(); i++ { + playerData := e.seats[i] + + if playerData != nil && playerData.IsGameing() && playerData.IsRob { + systemGain += playerData.winCoin + } + } + return systemGain +} + +func (e *SceneEx) AllPlayerEnterGame() { + for i := 0; i < e.GetPlayerNum(); i++ { + if e.seats[i] == nil { + continue + } + + if e.seats[i].IsMarkFlag(base.PlayerState_GameBreak) == false { + e.seats[i].UnmarkFlag(base.PlayerState_WaitNext) + e.seats[i].SyncFlag() + } + } +} + +func (e *SceneEx) SetCurOpPos(pos int) { + e.opPos = pos + e.seats[pos].lastTime = time.Now() +} + +func (e *SceneEx) GetCurOpPos() int { + return e.opPos +} + +func (e *SceneEx) AddChessGrade(p *PlayerEx, num int64) { + if p == nil { + return + } + + // 人机对战没有积分 + if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { + p.AddChessGrade(num) + } +} + +func (e *SceneEx) AddCoin(p *PlayerEx, num int64, gainWay int32, syncFlag int, oper, remark string) { + if p == nil { + return + } + + // 人机对战没有金币结算 + if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { + p.AddCoin(num, gainWay, syncFlag, oper, remark) + } +} + +func (e *SceneEx) GetSeatByCKey(cKey string) *PlayerEx { + _cKey := strings.ToLower(cKey) + for _, seat := range e.seats { + if _cKey == "w" && !seat.isBlack || _cKey == "b" && seat.isBlack { + return seat + } + } + return nil +} + +// ChessInit 棋盘初始化 +func (e *SceneEx) ChessInit() { + e.chess.Init() + e.lastMove = []int32{} + // 空结构 + // 随机 + firstIdx := e.RandInt(2) + fmt.Println("@@firstIdx", firstIdx) + for idx, seat := range e.seats { + if seat != nil { + if idx == firstIdx { + // 默认seats中的第一个是白棋,先走 + seat.isBlack = false + } else { + seat.isBlack = true + } + seat.oldGrade = seat.ChessGrade + pack := &chesstitians.SCChesstitiansCard{} + // 通过cards[0] 传递isBlack + if seat.isBlack { + pack.Cards = append(pack.Cards, int32(1)) + } else { + pack.Cards = append(pack.Cards, int32(0)) + } + // 生成棋数据 + pack.Chess = e.chess.GetChess() + pack.Act = e.chess.GetAct() + pack.Castling = e.chess.GetCastling() + pack.CambodianMove = e.chess.GetCambodianMove() + pack.Enpassant = int32(e.chess.GetEnPassant()) + pack.ChessRound = int32(e.chess.GetRound()) + pack.Check = e.chess.GetCheck() + pack.Checkmate = e.chess.GetCheckmate() + proto.SetDefaults(pack) + seat.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), pack) + } + } +} + +// PlayChess 判断客户端发来的行棋操作,返回是否合法 +func (e *SceneEx) PlayChess(fromTo []int64) bool { + fromIdx, toIdx := int(fromTo[0]), int(fromTo[1]) + // 判断该谁走 + // 检查棋子本身能不能走 + if !e.chess.CanMove(fromIdx, toIdx) { + logger.Logger.Errorf("非法行棋(棋子本身的路线错误) %v %v %v", fromIdx, toIdx, e.chess.GetPiece(fromIdx)) + return false + } + // 王被吃 + toPiece := e.chess.GetPiece(toIdx) + if toPiece == rule.WK { // 白王被吃,标记黑方获胜 + for _, seat := range e.seats { + if seat != nil { + if seat.isBlack { + e.winSnId = seat.GetSnId() + } + } + } + } else if toPiece == rule.BK { // 黑王被吃,标记白方获胜 + for _, seat := range e.seats { + if !seat.isBlack { + e.winSnId = seat.GetSnId() + } + } + } + + c := e.chess.GetPiece(fromIdx) + e.chess.Move(fromIdx, toIdx) + e.lastMove = []int32{int32(fromIdx), int32(toIdx)} + + // 将死 + if rule.IsWhite(c) && e.chess.IsCheckmate(true) { // 白方胜 + e.winSnId = e.GetSeatByCKey("w").GetSnId() + } else if !rule.IsWhite(c) && e.chess.IsCheckmate(false) { // 黑方胜 + e.winSnId = e.GetSeatByCKey("b").GetSnId() + } + + // 无子可动平局 + if rule.IsWhite(c) && e.chess.IsStop(false) { + e.ChangeSceneState(rule.SceneStateBilled) + } else if rule.IsBlack(c) && e.chess.IsStop(true) { + e.ChangeSceneState(rule.SceneStateBilled) + } + return true +} + +// NextOpPos 轮换到下一个操作的玩家 +func (e *SceneEx) NextOpPos() { + lastPlayer := e.seats[e.opPos] + if !lastPlayer.lastTime.IsZero() { + lastPlayer.totalTime += int64(time.Now().Sub(lastPlayer.lastTime).Seconds()) + } + + e.opPos = (e.opPos + 1) % rule.MaxNumOfPlayer + e.chess.NextAct() + e.chess.PrintChess() + s := e.chess.GenFen() + println(s) + + // 计步 + if e.IsStep() { + curPlayer := e.seats[e.opPos] + if curPlayer.SnId == e.stepSnId { + e.stepNum++ + } + if e.IsKingStep() { + e.stepKing++ + if e.stepKing == 2 { + e.stepNum++ + e.stepKing = 0 + } + } + + if e.stepNum > e.stepLimit { + // 计步结束,平局 + e.ChangeSceneState(rule.SceneStateBilled) + return + } + } + + // 检查孤王计步 + // 任何一方只剩一个王时,开始计步;取消残局计步;确定回合限制 + if !e.IsKingStep() { + if e.chess.Has(rule.WK) && e.chess.GetWhiteNum() == 1 || + e.chess.Has(rule.BK) && e.chess.GetBlackNum() == 1 { + if e.IsStep() { + e.cancelStep() + } + e.StartKingStep() + } + } + // 记录操作开始时间 + e.seats[e.opPos].lastTime = time.Now() +} + +// IsStep 是否计步中 +func (e *SceneEx) IsStep() bool { + if e.stepLimit > 0 { + return true + } + return false +} + +func (e *SceneEx) IsKingStep() bool { + if e.stepSnId == 0 && e.stepLimit > 0 { + return true + } + return false +} + +// CanStep 是否可以计步 +func (e *SceneEx) CanStep() bool { + if e.IsStep() { + return false + } + // 双方都没有兵 + if e.chess.GetChessNum(rule.WP) == 0 && e.chess.GetChessNum(rule.BP) == 0 { + return true + } + return false +} + +// StartStep 玩家开始计步 +func (e *SceneEx) StartStep(p *PlayerEx) bool { + if !e.CanStep() { + return false + } + + e.stepSnId = p.GetSnId() + e.stepNum = 1 + e.stepLimit = rule.PlayerStepLimit + return true +} + +// StartKingStep 开始孤王计步 +func (e *SceneEx) StartKingStep() { + e.stepKing = 0 + c := rule.White + if e.chess.Has(rule.WK) && e.chess.GetWhiteNum() == 1 { + c = rule.Black + } + + //双车:8回合 + //单车:16回合 + //双象:22回合 + //双马:32回合 + //单象:44回合 + //单马:64回合 + //其他情况:64回合 + r := e.chess.GetChessNum(rule.ToPiece(c, rule.R)) + if r == 2 { + e.stepLimit = 8 + } else if r == 1 { + e.stepLimit = 16 + } + + if e.stepLimit == 0 { + s := e.chess.GetChessNum(rule.ToPiece(c, rule.S)) + if s == 2 { + e.stepLimit = 22 + } else { + n := e.chess.GetChessNum(rule.ToPiece(c, rule.N)) + if n == 2 { + e.stepLimit = 32 + } else if s == 1 { + e.stepLimit = 44 + } else if n == 1 { + e.stepLimit = 64 + } + } + } + + if e.stepLimit == 0 { + e.stepLimit = 64 + } + + // 起始回合数 + //e.stepNum = e.chess.GetWhiteNum() + e.chess.GetBlackNum() + 1 + e.stepNum = 1 +} + +// cancelStep 取消计步 +func (e *SceneEx) cancelStep() { + e.stepSnId = 0 + e.stepNum = 0 + e.stepLimit = 0 +} + +// CancelStep 玩家取消计步 +func (e *SceneEx) CancelStep(p *PlayerEx) bool { + if !e.IsStep() { + return false + } + if e.stepSnId != p.GetSnId() { + return false + } + e.cancelStep() + return true +} + +// RequestDraw 玩家求和 +func (e *SceneEx) RequestDraw(p *PlayerEx) bool { + if e.drawSnId > 0 { + return false + } + e.drawSnId = p.GetSnId() + return true +} + +// RefuseDraw 玩家拒绝求和 +func (e *SceneEx) RefuseDraw(p *PlayerEx) bool { + if e.drawSnId == 0 { + return false + } + if e.drawSnId == p.GetSnId() { + return false + } + e.drawSnId = 0 + return true +} + +// AgreeDraw 玩家同意求和 +func (e *SceneEx) AgreeDraw(p *PlayerEx) bool { + if e.drawSnId == 0 { + return false + } + if e.drawSnId == p.GetSnId() { + return false + } + e.drawSnId = 0 + e.ChangeSceneState(rule.SceneStateBilled) + return true +} + +// CancelDraw 撤销求和 +func (e *SceneEx) CancelDraw(p *PlayerEx) bool { + if e.drawSnId == 0 { + return false + } + if e.drawSnId != p.GetSnId() { + return false + } + e.drawSnId = 0 + return true +} + +func (e *SceneEx) GetChessRank(p *PlayerEx) int32 { + i := sort.Search(len(e.ChessRank), func(i int) bool { + return int64(e.ChessRank[i]) > p.ChessGrade + }) + if i == len(e.ChessRank) { + return e.ChessRank[i-1] + } + i = i - 1 + if i < 0 { + i = 0 + } + if i < len(e.ChessRank) { + return e.ChessRank[i] + } + return 0 +} + +func (e *SceneEx) GetChessRankNext(p *PlayerEx) int32 { + i := sort.Search(len(e.ChessRank), func(i int) bool { + return int64(e.ChessRank[i]) > p.ChessGrade + }) + if i == len(e.ChessRank) { + return e.ChessRank[i-1] + } + return e.ChessRank[i] +} + +func (e *SceneEx) ToBilled(p1, p2 *PlayerEx, isWin int) (*chesstitians.ChesstitiansPlayerGameBilled, *model.ChesstitiansPerson) { + // 获取结算规则 + var typeId int32 + var billedRule, billedRuleDraw *server.DB_ChessBilledRules + + // 人机对战不结算 + if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { + winRank := e.GetChessRank(p1) + loseRank := e.GetChessRank(p2) + if winRank > loseRank { + typeId = rule.BilledTypeGT + } else if winRank < loseRank { + typeId = rule.BilledTypeLT + } else { + typeId = rule.BilledTypeEQ + } + + for _, v := range srvdata.PBDB_ChessBilledRulesMgr.Datas.Arr { + if v.GetTypeId() == typeId { + billedRule = v + } + if v.GetTypeId() == rule.BilledTypeEQ { + billedRuleDraw = v + } + } + } + + p1.isWin = int32(isWin) + p1.winCoin = 0 + p1.taxCoin = 0 + p1.winTimes = 0 + p1.winScore = 0 + p1.otherScore = 0 + p1.nextRank = 0 + + switch isWin { + case rule.Win: + // 金币和税收 + gainScore := int64(float64(e.GetBaseScore()) * float64(10000-e.GetDBGameFree().GetTaxRate()) / 10000.0) + p1.winCoin = gainScore + p1.taxCoin = int64(e.GetBaseScore()) - gainScore + + // 连赢次数 + winTimes := p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.WinGameTimesNum + e.AddCoin(p1, gainScore, common.GainWay_CoinSceneWin, 0, "system", e.GetSceneName()) + p1.winTimes = int32(winTimes + 1) + + var winScore int32 + if billedRule != nil { + // 连赢积分 + if p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.GetInt(rule.StaticsChessTime)+1 >= int(billedRule.GetWinTimes()) { + p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) + p1.AddChessGrade(int64(billedRule.OtherScore)) + p1.otherScore = billedRule.OtherScore + winScore += billedRule.OtherScore + } else { + p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.Incr(rule.StaticsChessTime) + } + // 总积分 + e.AddChessGrade(p1, int64(billedRule.GetWinScore())) + // 本局积分 + winScore += billedRule.GetWinScore() + p1.winScore = winScore + } + + case rule.Lose: + e.AddCoin(p1, int64(-e.GetDBGameFree().GetBaseScore()), common.GainWay_CoinSceneLost, 0, "system", e.GetSceneName()) + p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) + p1.winCoin = int64(-e.GetDBGameFree().GetBaseScore()) + + if billedRule != nil { + // 总积分 + e.AddChessGrade(p1, int64(billedRule.GetLoseScore())) + // 本局积分 + p1.winScore = proto.Int32(billedRule.GetLoseScore()) + } + + case rule.Draw: + p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) + if billedRule != nil { + // 总积分 + e.AddChessGrade(p1, int64(billedRule.GetDrawScore())) + // 本局积分 + p1.winScore = proto.Int32(billedRule.GetDrawScore()) + } + } + + if billedRuleDraw != nil { + // 还有几局晋级 + n := math.Ceil(float64(int64(e.GetChessRankNext(p1))-p1.ChessGrade) / float64(billedRuleDraw.GetWinScore())) + p1.nextRank = int32(n) + } + + // 人机对战,没有输赢分,没有积分变化 + if e.GetGameId() == common.GameId_ChesstitiansCambodianRobot { + p1.winCoin = 0 + p1.taxCoin = 0 + p1.winScore = 0 + p1.otherScore = 0 + } + + billData := &chesstitians.ChesstitiansPlayerGameBilled{ + SnId: proto.Int32(p1.SnId), + WinCoin: proto.Int64(p1.winCoin), + GameCoin: proto.Int64(p1.GetCoin()), + IsWin: proto.Int32(p1.isWin), + WinTimes: proto.Int32(p1.winTimes), + OtherScore: proto.Int32(p1.otherScore), + NextRank: proto.Int32(p1.nextRank), + Score: proto.Int32(int32(p1.ChessGrade)), + WinScore: proto.Int32(p1.winScore), + OldChessGrade: proto.Int64(p1.oldGrade), + } + + chessPerson := &model.ChesstitiansPerson{ + UserId: p1.SnId, + UserIcon: p1.Head, + Platform: p1.Platform, + Channel: p1.Channel, + Promoter: p1.BeUnderAgentCode, + PackageTag: p1.PackageID, + InviterId: p1.InviterId, + WBLevel: p1.WBLevel, + IsRob: p1.IsRob, + IsFirst: e.IsPlayerFirst(e.GetPlayer(p1.SnId)), + IsLeave: false, + IsWin: p1.isWin, + Seat: p1.GetPos(), + GainCoin: p1.winCoin, + GainTaxCoin: p1.taxCoin, + } + return billData, chessPerson +} diff --git a/gamesrv/chess/scenepolicy.go b/gamesrv/chess/scenepolicy.go new file mode 100644 index 0000000..f39d27c --- /dev/null +++ b/gamesrv/chess/scenepolicy.go @@ -0,0 +1,1504 @@ +package chesstitians + +import ( + "math/rand" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/chess" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/chesstitians" +) + +var ScenePolicyEntitySingleton = &ScenePolicyEntity{} + +type ScenePolicyEntity struct { + base.BaseScenePolicy + states [rule.SceneStateMax]base.SceneState +} + +// 创建场景扩展数据 +func (this *ScenePolicyEntity) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewSceneEx(s) + if sceneEx != nil { + if sceneEx.init() { + sceneEx.Clear() + s.SetExtraData(sceneEx) + } + } + return sceneEx +} + +// 创建玩家扩展数据 +func (this *ScenePolicyEntity) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &PlayerEx{Player: p} + if playerEx != nil { + playerEx.init() + p.SetExtraData(playerEx) + } + return playerEx +} + +// 场景开启事件 +func (this *ScenePolicyEntity) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyEntity) OnStart, GetSceneId()=", s.GetSceneId()) + + sceneEx := NewSceneEx(s) + if sceneEx != nil { + if sceneEx.init() { + sceneEx.Clear() + s.SetExtraData(sceneEx) + s.ChangeSceneState(rule.SceneStateWaitPlayer) + } + } +} + +// 场景心跳事件 +func (this *ScenePolicyEntity) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnTick(s) + } +} + +// 玩家进入事件 +func (this *ScenePolicyEntity) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerEnter, GetSceneId()=", s.GetSceneId(), " player=", p.GetName()) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + + //自动带入金币 + pos := sceneEx.FindOnePos() + if p.Pos != rule.InvalidPos && sceneEx.seats[p.Pos] == nil { + pos = p.Pos + } + if pos < 0 || pos > 1 || sceneEx.seats[pos] != nil { + p.MarkFlag(base.PlayerState_EnterSceneFailed) + cnt := len(sceneEx.players) + logger.Logger.Warnf("ScenePolicyEntity.OnPlayerEnter(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames) + return + } + + playerEx := &PlayerEx{Player: p} + playerEx.init() + playerEx.SetPos(pos) + if sceneEx.GetGaming() { + playerEx.MarkFlag(base.PlayerState_WaitNext) + } + p.SetExtraData(playerEx) + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + + if sceneEx.GetSeatPlayerCnt() == 1 { + sceneEx.masterSnId = playerEx.SnId + sceneEx.BroadcastUpdateMasterSnId(false) + } + + //广播个人信息 + playerData := CreatePlayerData(p) + if sceneEx.IsMatchScene() && p.IsRob { + if len(p.MatchParams) > 2 { + if p.MatchParams[2] != 0 { + playerData.CopySnid = p.MatchParams[2] + } + } + if len(p.MatchParams) > 3 { + if p.MatchParams[3] != 0 { + playerData.CopyRoleId = p.MatchParams[3] + } + } + } + pack := &chesstitians.SCChesstitiansPlayerEnter{ + Data: playerData, + } + proto.SetDefaults(pack) + s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), pack, p.GetSid()) + + //给自己发送房间信息 + SendRoomInfo(s, p, sceneEx, playerEx) + + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } +} + +// 玩家离开事件 +func (this *ScenePolicyEntity) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerLeave, GetSceneId()=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if playerEx, ok := p.GetExtraData().(*PlayerEx); ok { + if this.CanChangeCoinScene(s, p) { + LeavePlayerSnid := playerEx.SnId + + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, []int64{int64(reason)}) + + if LeavePlayerSnid == sceneEx.masterSnId { + findOne := false + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerExSeat := sceneEx.seats[i] + if playerExSeat != nil && playerExSeat.SnId != sceneEx.masterSnId { + sceneEx.masterSnId = playerExSeat.SnId + sceneEx.BroadcastUpdateMasterSnId(true) + findOne = true + break + } + } + if !findOne { + sceneEx.masterSnId = 0 + sceneEx.BroadcastUpdateMasterSnId(false) + } + } + + if !playerEx.IsRob { //真人离开 + hadSeat := false + for _, seat := range sceneEx.seats { + if seat != nil { + hadSeat = true + break + } + } + if !hadSeat { //座位上没有人了,把观众踢出去 + audiences := sceneEx.GetAudiences() + for _, audience := range audiences { + if audience != nil { + s.AudienceLeave(audience, common.PlayerLeaveReason_RoomClose) + } + } + } + } + } + } + + } +} + +// 玩家掉线 +func (this *ScenePolicyEntity) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerDropLine, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if sceneEx.GetGaming() { + if s.IsMatchScene() || s.IsCoinScene() { + p.MarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } + return + } + s.PlayerLeave(p, common.PlayerLeaveReason_DropLine, true) + } +} + +// 玩家重连 +func (this *ScenePolicyEntity) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerRehold, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if playerEx, ok := p.GetExtraData().(*PlayerEx); ok { + if p.IsMarkFlag(base.PlayerState_Auto) { + p.UnmarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } + + //发送房间信息给自己 + SendRoomInfo(s, p, sceneEx, playerEx) + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +// 返回房间 +func (this *ScenePolicyEntity) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerReturn, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if playerEx, ok := p.GetExtraData().(*PlayerEx); ok { + if p.IsMarkFlag(base.PlayerState_Auto) { + p.UnmarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } + + //发送房间信息给自己 + SendRoomInfo(s, p, sceneEx, playerEx) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func (this *ScenePolicyEntity) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerOp, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " opcode=", opcode, " params=", params) + if s.GetSceneState() != nil { + if s.GetSceneState().OnPlayerOp(s, p, opcode, params) { + p.LastOPTimer = time.Now() + return true + } + return false + } + return true +} + +func (this *ScenePolicyEntity) OnPlayerDevOp(s *base.Scene, p *base.Player, opcode int, paramStr []string, param int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerDevOp, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " opcode=", opcode, " paramsStr=", paramStr, " param=", param) + sceneEx, _ := s.GetExtraData().(*SceneEx) + sceneEx.chess.SetChess(paramStr) + //sceneEx.opPos = int32(param) + + snid := int32(param) + + for idx, seat := range sceneEx.seats { + if seat.GetSnId() == snid { + sceneEx.opPos = idx + break + } + } + + sceneEx.lastMove = []int32{} + + //playerEx, _ := p.GetExtraData().(*PlayerEx) + + //pack := CreateRoomInfoPacket(s, p, sceneEx, playerEx) + //s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack, 0) + + for _, seat := range sceneEx.seats { + if seat != nil { + pack := CreateRoomInfoPacket(s, seat.Player, sceneEx, seat) + ok := seat.Player.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack) + logger.Logger.Trace("SCChesstitiansSendRoomInfo isok : ", ok, ",pack", pack) + } + } + + return true +} + +func (this *ScenePolicyEntity) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " eventcode=", evtcode, " params=", params) + if s.GetSceneState() != nil { + s.GetSceneState().OnPlayerEvent(s, p, evtcode, params) + } +} + +func (this *ScenePolicyEntity) OnAudienceEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyEntity OnAudienceEnter, sceneId=", s.SceneId, " player=", p.SnId) + this.BaseScenePolicy.OnAudienceEnter(s, p) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + //给自己发送房间信息 + p.UnmarkFlag(base.PlayerState_Leave) + SendRoomInfo(s, p, sceneEx, nil) + sceneEx.BroadcastAudienceNum(p) + } +} + +func (this *ScenePolicyEntity) OnAudienceLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyEntity OnAudienceLeave, sceneId=", s.SceneId, " player=", p.SnId, " reason=", reason) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + sceneEx.BroadcastAudienceNum(p) + } +} + +func (this *ScenePolicyEntity) OnAudienceSit(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyEntity OnAudienceSit, sceneId=", s.SceneId, " player=", p.SnId) + sceneEx, ok := s.GetExtraData().(*SceneEx) + if !ok { + return + } + if s.Gaming && !s.GetBEnterAfterStart() { + return + } + + //自动带入金币 + pos := sceneEx.FindOnePos() + if pos < 0 || pos > 3 || sceneEx.seats[pos] != nil { + p.MarkFlag(base.PlayerState_EnterSceneFailed) + cnt := len(sceneEx.players) + logger.Logger.Warnf("ScenePolicyEntity.OnAudienceSit(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames) + return + } + + playerEx := &PlayerEx{Player: p} + playerEx.init() + playerEx.SetPos(pos) + p.UnmarkFlag(base.PlayerState_Audience) + p.UnmarkFlag(base.PlayerState_Leave) + if sceneEx.GetGaming() { + playerEx.MarkFlag(base.PlayerState_WaitNext) + p.SyncFlag() + } + + p.SetExtraData(playerEx) + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + //logger.Logger.Trace("广播个人信息,curSeatsNum: ", sceneEx.GetSeatPlayerCnt()) + if sceneEx.GetSeatPlayerCnt() == 1 { + sceneEx.masterSnId = playerEx.SnId + sceneEx.BroadcastUpdateMasterSnId(false) + } + + //广播个人信息 + playerData := CreatePlayerData(p) + pack := &chesstitians.SCChesstitiansPlayerEnter{ + Data: playerData, + } + proto.SetDefaults(pack) + s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), pack, p.GetSid()) + + //给自己发送房间信息 + SendRoomInfo(s, p, sceneEx, playerEx) + sceneEx.BroadcastAudienceNum(p) + + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + +} + +func (this *ScenePolicyEntity) OnAudienceDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyEntity OnAudienceDropLine, sceneId=", s.SceneId, " player=", p.SnId) + s.AudienceLeave(p, common.PlayerLeaveReason_DropLine) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + sceneEx.BroadcastAudienceNum(p) + } +} + +// 是否完成了整个牌局 +func (this *ScenePolicyEntity) IsCompleted(s *base.Scene) bool { + if s == nil { + return false + } + return false +} + +// 是否可以强制开始 +func (this *ScenePolicyEntity) IsCanForceStart(s *base.Scene) bool { + return false +} + +// 当前状态能否换桌 +func (this *ScenePolicyEntity) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + return true +} + +func (this *ScenePolicyEntity) ForceStart(s *base.Scene) { + s.ChangeSceneState(rule.SceneStateWaitStart) +} + +func CreatePlayerData(p *base.Player) *chesstitians.ChesstitiansPlayerData { + pd := &chesstitians.ChesstitiansPlayerData{ + Name: proto.String(p.Name), + SnId: proto.Int32(p.SnId), + Head: proto.Int32(p.Head), + Sex: proto.Int32(p.Sex), + Coin: proto.Int64(p.GetCoin()), + Flag: proto.Int(p.GetFlag()), + Longitude: proto.Int32(p.Longitude), + Latitude: proto.Int32(p.Latitude), + City: proto.String(p.City), + VIP: proto.Int32(p.VIP), + HeadOutLine: proto.Int32(p.HeadOutLine), + NiceId: proto.Int32(p.NiceId), + Pos: proto.Int(p.GetPos()), + ChessGrade: proto.Int64(p.ChessGrade), + IsRob: proto.Bool(p.IsRob), + } + ex, ok := p.GetExtraData().(*PlayerEx) + if ok { + pd.IsWin = ex.isWin + pd.WinTimes = ex.winTimes + pd.OtherScore = ex.otherScore + pd.NextRank = ex.nextRank + pd.WinScore = ex.winScore + pd.WinCoin = ex.winCoin + pd.OldChessGrade = ex.oldGrade + pd.TotalTime = ex.GetTotalTime() + } + if p.Roles != nil { + pd.RoleId = proto.Int32(p.Roles.ModId) + } + if p.Items != nil { + pd.Items = make(map[int32]int32) + for id, num := range p.Items { + pd.Items[id] = proto.Int32(num) + } + } + if len(p.MatchParams) > 0 { + pd.MatchRankId = p.MatchParams[0] + } + if len(p.MatchParams) > 1 { + pd.Lv = p.MatchParams[1] + } + logger.Logger.Trace("ChesstitiansCreatePlayerData pd : ", pd) + + return pd +} + +func CreateRoomInfoPacket(s *base.Scene, p *base.Player, sceneEx *SceneEx, playerEx *PlayerEx) *chesstitians.SCChesstitiansRoomInfo { + pack := &chesstitians.SCChesstitiansRoomInfo{ + RoomId: proto.Int(s.GetSceneId()), + Creator: proto.Int32(s.GetCreator()), + GameId: proto.Int(s.GetGameId()), + RoomMode: proto.Int(s.GetSceneMode()), + Params: s.GetParams(), + State: proto.Int32(int32(s.GetSceneState().GetState())), + TimeOut: proto.Int(s.GetSceneState().GetTimeout(s)), + NumOfGames: proto.Int(sceneEx.NumOfGames), + TotalOfGames: proto.Int(sceneEx.TotalOfGames), + CurOpIdx: proto.Int(-1), + MasterSnid: proto.Int32(sceneEx.masterSnId), + AudienceNum: proto.Int(s.GetAudiencesNum()), + BaseScore: proto.Int32(s.BaseScore), + MaxPlayerNum: proto.Int(s.GetPlayerNum()), + GameFreeId: proto.Int32(s.GetDBGameFree().GetId()), + SceneType: proto.Int32(s.GetDBGameFree().GetSceneType()), + // 比赛场相关 + Round: proto.Int32(s.MatchRound), + CurPlayerNum: proto.Int32(s.MatchCurPlayerNum), + NextNeed: proto.Int32(s.MatchNextNeed), + // 计步 + StepSnId: proto.Int32(sceneEx.stepSnId), + StepNum: int64(sceneEx.stepNum), + StepLimit: int64(sceneEx.stepLimit), + // 求和 + DrawSnId: proto.Int32(sceneEx.drawSnId), + // 发起再来一局的玩家 + NextSnId: proto.Int32(sceneEx.nextSnId), + } + + pack.Variant = int32(sceneEx.chess.GetVariant()) + + // 生成棋数据 + pack.Chess = sceneEx.chess.GetChess() + pack.Act = sceneEx.chess.GetAct() + + pack.Castling = sceneEx.chess.GetCastling() + pack.CambodianMove = sceneEx.chess.GetCambodianMove() + + pack.Enpassant = int32(sceneEx.chess.GetEnPassant()) + pack.ChessRound = int32(sceneEx.chess.GetRound()) + + pack.Check = sceneEx.chess.GetCheck() + pack.Checkmate = sceneEx.chess.GetCheckmate() + + for _, v := range sceneEx.lastMove { + pack.LastMove = append(pack.LastMove, int32(v)) + } + + pack.IsMatch = int32(0) + // 0.普通场 1.锦标赛 2.冠军赛 3.vip专属 + if s.IsMatchScene() { + pack.IsMatch = s.MatchType + } + pack.MatchFinals = 0 + if s.MatchFinals { + pack.MatchFinals = 1 + if s.NumOfGames >= 2 { + pack.MatchFinals = 2 + } + } + if s.GetSceneState().GetState() == rule.SceneStateBilled { + sceneEx.opPos = -1 + pack.CurOpIdx = proto.Int32(-1) + } + if s.GetSceneState().GetState() >= rule.SceneStatePlayerOp { + pack.CurOpIdx = proto.Int32(int32(sceneEx.opPos)) + } + //玩家信息.第一个必然是自己 + pd := CreatePlayerData(p) + if playerEx != nil { + // 是黑棋的标记?1黑棋,0白棋 + if playerEx.isBlack { + pd.IsBlack = proto.Int32(1) + } else { + pd.IsBlack = proto.Int32(0) + } + } + pack.Players = append(pack.Players, pd) + + //剩下的按座位排序 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + nowPlayer := sceneEx.seats[i] + if nowPlayer != nil { + if nowPlayer.SnId != p.SnId { + pd1 := CreatePlayerData(nowPlayer.Player) + if sceneEx.IsMatchScene() && nowPlayer.IsRob { + if len(nowPlayer.MatchParams) > 2 { + if nowPlayer.MatchParams[2] != 0 { + pd1.CopySnid = nowPlayer.MatchParams[2] + } + } + if len(nowPlayer.MatchParams) > 3 { + if nowPlayer.MatchParams[3] != 0 { + pd1.CopyRoleId = nowPlayer.MatchParams[3] + } + } + } + // 是黑棋的标记?1黑棋,0白棋 + if nowPlayer.isBlack { + pd1.IsBlack = proto.Int32(1) + } else { + pd1.IsBlack = proto.Int32(0) + } + pack.Players = append(pack.Players, pd1) + } + } + } + proto.SetDefaults(pack) + return pack +} + +func SendRoomInfo(s *base.Scene, p *base.Player, sceneEx *SceneEx, playerEx *PlayerEx) { + pack := CreateRoomInfoPacket(s, p, sceneEx, playerEx) + ok := p.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack) + logger.Logger.Trace("SCChesstitiansSendRoomInfo isok : ", ok, ",pack", pack) +} + +//===================================== +// SceneStateBase 状态基类 +//===================================== + +type SceneStateBase struct { +} + +func (this *SceneStateBase) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + return int(time.Now().Sub(sceneEx.StateStartTime) / time.Second) + } + return 0 +} + +func (this *SceneStateBase) CanChangeTo(s base.SceneState) bool { + return true +} + +func (this *SceneStateBase) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + return !p.IsGameing() || s.GetDestroyed() +} + +func (this *SceneStateBase) OnEnter(s *base.Scene) { + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + sceneEx.StateStartTime = time.Now() + } +} + +func (this *SceneStateBase) OnLeave(s *base.Scene) {} + +func (this *SceneStateBase) OnTick(s *base.Scene) { +} + +func (this *SceneStateBase) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + logger.Logger.Trace("SceneBaseStateChesstitians.", " s.GetSceneId() : ", s.GetSceneId(), " p.SnId : ", p.SnId, " opcode : ", opcode, " params ", params) + + sceneEx, _ := s.GetExtraData().(*SceneEx) + if sceneEx == nil { + return true + } + playerEx, _ := p.GetExtraData().(*PlayerEx) + if playerEx == nil { + return true + } + return false +} + +func (this *SceneStateBase) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { +} + +// BroadcastRoomState 广播房间状态 +func (this *SceneStateBase) BroadcastRoomState(s *base.Scene, state int, params ...int64) { + pack := &chesstitians.SCChesstitiansRoomState{ + State: proto.Int(state), + Params: params, + } + proto.SetDefaults(pack) + s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), pack, 0) +} + +//===================================== +// SceneStateWaitPlayer 等待玩家进入 +//===================================== + +type SceneStateWaitPlayer struct { + SceneStateBase +} + +func (this *SceneStateWaitPlayer) GetState() int { + return rule.SceneStateWaitPlayer +} + +func (this *SceneStateWaitPlayer) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SceneStateWaitStart: + return true + default: + return false + } +} + +func (this *SceneStateWaitPlayer) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s.IsMatchScene() { + return false + } + return true +} + +func (this *SceneStateWaitPlayer) OnEnter(s *base.Scene) { + this.SceneStateBase.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + sceneEx.Clear() + sceneEx.SetGaming(false) + this.BroadcastRoomState(s, this.GetState()) + } +} + +// 状态离开时 +func (this *SceneStateWaitPlayer) OnLeave(s *base.Scene) { + this.SceneStateBase.OnLeave(s) + logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneStateWaitPlayer) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) { + return true + } + return true +} + +// 玩家事件 +func (this *SceneStateWaitPlayer) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneWaitPlayerStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params) + + if _, ok := p.GetExtraData().(*PlayerEx); ok { + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + switch evtcode { + case base.PlayerEventEnter: + // 段位等级没有设置不能玩游戏,不应该有这个错误的 + if len(sceneEx.ChessRank) == 0 && s.GetGameId() != common.GameId_ChesstitiansCambodianRobot { + logger.Logger.Errorf("ChessRank not found Platform:%v", sceneEx.Platform) + sceneEx.SceneDestroy(true) + return + } + + //如果有人进入, 检查在线人是否能够开启游戏,够的话,切换到延迟开启状态 + if sceneEx.CanStart() { + logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnPlayerEvent s.ChangeSceneState(SceneStateWaitStart) %v", s.GetSceneId()) + s.ChangeSceneState(rule.SceneStateWaitStart) + } + } + } + } +} + +func (this *SceneStateWaitPlayer) OnTick(s *base.Scene) { + this.SceneStateBase.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if s.CheckNeedDestroy() { + sceneEx.SceneDestroy(true) + return + } + if sceneEx.CanStart() { + logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnTick s.ChangeSceneState(SceneStateWaitStart) %v", s.GetSceneId()) + s.ChangeSceneState(rule.SceneStateWaitStart) + } else if !s.GetGaming() && s.GetRobotNum() == 1 { + tNow := time.Now() + for _, p := range s.Players { + if p.IsRob && tNow.Sub(p.GetLastOPTimer()) > time.Second*time.Duration(30+rand.Int63n(60)) { + //p.LastOPTimer = tNow.Add(time.Minute * 30) + s.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + } + } + } + } +} + +//===================================== +// SceneStateWaitStart 开始游戏 +//===================================== + +type SceneStateWaitStart struct { + SceneStateBase +} + +func (this *SceneStateWaitStart) GetState() int { + return rule.SceneStateWaitStart +} + +func (this *SceneStateWaitStart) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SceneStateWaitPlayer, rule.SceneStateWaitStart, rule.SceneStateChessInit: + return true + default: + return false + } +} + +// 当前状态能否换桌 +func (this *SceneStateWaitStart) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s.IsMatchScene() { + return false + } + return true +} + +func (this *SceneStateWaitStart) OnEnter(s *base.Scene) { + this.SceneStateBase.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + sceneEx.Clear() + sceneEx.SetGaming(false) + this.BroadcastRoomState(s, this.GetState()) + logger.Logger.Trace("(this *SceneWaitStartStateChesstitians) OnEnter", this.GetState()) + } +} + +// 状态离开时 +func (this *SceneStateWaitStart) OnLeave(s *base.Scene) { + this.SceneStateBase.OnLeave(s) + logger.Logger.Tracef("(this *SceneWaitStartStateChesstitians) OnLeave", this.GetState()) +} + +// 玩家操作 +func (this *SceneStateWaitStart) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) { + return true + } + sceneEx, _ := s.GetExtraData().(*SceneEx) + if sceneEx != nil { + playerEx, _ := p.GetExtraData().(*PlayerEx) + if playerEx != nil { + opRetCode := chesstitians.OpResultCode_OPRC_Error + if playerEx.SnId == sceneEx.masterSnId && this.GetState() == rule.SceneStateWaitStart { + if sceneEx.IsMatchScene() { + return false + } + switch int32(opcode) { + case rule.PlayerOpStart: //房主开始游戏 + if sceneEx.CanStart() == false { + s.ChangeSceneState(rule.SceneStateWaitPlayer) + } else { + opRetCode = chesstitians.OpResultCode_OPRC_Sucess + s.ChangeSceneState(rule.SceneStateChessInit) + } + } + } + if opRetCode == chesstitians.OpResultCode_OPRC_Sucess { + sceneEx.OnPlayerSCOp(p, opcode, opRetCode, params) + } else { + logger.Logger.Tracef("SceneWaitStartStateChesstitians OnPlayerOp snid:%v, masterSnId:%v", playerEx.SnId, sceneEx.masterSnId) + } + } + } + return true +} + +// 玩家事件 +func (this *SceneStateWaitStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneWaitStartStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params) + + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + //如果有人退出, 检查在线人是否能够开启游戏,不够的话,切换到等待状态 + if !sceneEx.CanStart() { + s.ChangeSceneState(rule.SceneStateWaitPlayer) + } + } + } +} + +func (this *SceneStateWaitStart) OnTick(s *base.Scene) { + this.SceneStateBase.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if sceneEx.IsMatchScene() { + delayT := time.Second * 2 + if sceneEx.MatchRound != 1 { //第一轮延迟2s,其他延迟3s 配合客户端播放动画 + delayT = time.Second * 4 + } + if time.Now().Sub(sceneEx.StateStartTime) > delayT { + s.ChangeSceneState(rule.SceneStateChessInit) // 比赛场直接发牌 + return + } + } + + if time.Now().Sub(sceneEx.StateStartTime) > rule.WaitStartTimeout { + //开始前再次检查开始条件 + if sceneEx.CanStart() { + s.ChangeSceneState(rule.SceneStateChessInit) + } else { + s.ChangeSceneState(rule.SceneStateWaitPlayer) + } + } + } +} + +//===================================== +// SceneStateChessInit 棋盘初始化 +//===================================== + +type SceneStateChessInit struct { + SceneStateBase +} + +func (this *SceneStateChessInit) GetState() int { + return rule.SceneStateChessInit +} + +func (this *SceneStateChessInit) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.SceneStatePlayerOp { + return true + } + return false +} + +func (this *SceneStateChessInit) OnEnter(s *base.Scene) { + this.SceneStateBase.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + sceneEx.Clear() + sceneEx.SetGaming(true) + sceneEx.GameNowTime = time.Now() + sceneEx.AllPlayerEnterGame() + + //参与游戏次数 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerEx := sceneEx.seats[i] + if playerEx != nil { + if playerEx.IsGameing() { + playerEx.GameTimes++ + } + } + } + + s.NumOfGames++ + s.NotifySceneRoundStart(s.NumOfGames) + this.BroadcastRoomState(s, this.GetState(), int64(s.NumOfGames)) + + //同步防伙牌数据 + sceneEx.SyncScenePlayer() + //发牌 + sceneEx.ChessInit() + + } +} + +// 状态离开时 +func (this *SceneStateChessInit) OnLeave(s *base.Scene) { + this.SceneStateBase.OnLeave(s) + + logger.Logger.Tracef("(this *SceneHandCardStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneStateChessInit) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) { + return true + } + + return true +} + +// 玩家事件 +func (this *SceneStateChessInit) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneHandCardStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params) +} + +func (this *SceneStateChessInit) OnTick(s *base.Scene) { + this.SceneStateBase.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.HandCardTimeout { + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + seat := sceneEx.seats[i] + if !seat.isBlack { + sceneEx.SetCurOpPos(seat.GetPos()) + } + } + s.ChangeSceneState(rule.SceneStatePlayerOp) + } + } +} + +//===================================== +// SceneStatePlayerOp 玩家操作状态 +//===================================== + +type SceneStatePlayerOp struct { + SceneStateBase +} + +func (this *SceneStatePlayerOp) GetState() int { + return rule.SceneStatePlayerOp +} + +func (this *SceneStatePlayerOp) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.SceneStateBilled { + return true + } + return false +} + +func (this *SceneStatePlayerOp) OnEnter(s *base.Scene) { + this.SceneStateBase.OnEnter(s) + this.BroadcastRoomState(s, this.GetState()) +} + +// 状态离开时 +func (this *SceneStatePlayerOp) OnLeave(s *base.Scene) { + this.SceneStateBase.OnLeave(s) + + logger.Logger.Tracef("(this *SceneHandCardStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneStatePlayerOp) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) { + return true + } + + this.OnPlayerOpNormal(s, p, opcode, params) + this.OnPlayerOpAI(s, p, opcode, params) + return true +} + +func (this *SceneStatePlayerOp) OnPlayerOpNormal(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + sceneEx, ok := s.GetExtraData().(*SceneEx) + if !ok { + return true + } + if sceneEx == nil { + return true + } + playerEx, ok := p.GetExtraData().(*PlayerEx) + if !ok { + return true + } + if playerEx == nil { + return true + } + + returnFunc := func(code chesstitians.OpResultCode) { + if code == chesstitians.OpResultCode_OPRC_Sucess { + // 可以成功行棋,则同步给所有的玩家 + sceneEx.BroadcastPlayerSCOp(playerEx.SnId, opcode, code, params) + + if sceneEx.winSnId != 0 { + sceneEx.ChangeSceneState(rule.SceneStateBilled) + } + } else { + // 若不能成功操作,仅发给操作的玩家 + sceneEx.OnPlayerSCOp(p, opcode, code, params) + } + } + + switch int32(opcode) { + case rule.PlayerOpPlay: // 玩家行棋 + if sceneEx.GetCurOpPos() != playerEx.GetPos() { + return false + } + + if len(params) != 2 { + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + } + + if sceneEx.PlayChess(params) { + // 轮换当前操作标记 + sceneEx.NextOpPos() + sceneEx.StateStartTime = time.Now() + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + + case rule.PlayerOpSurrender: // 投降 + for _, seat := range sceneEx.seats { + if seat != nil { + if seat.GetSnId() != playerEx.GetSnId() { + sceneEx.winSnId = seat.GetSnId() + break + } + } + } + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + + case rule.PlayerOpDraw: // 求和 + if len(params) == 0 { + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + } + + switch params[0] { + case rule.RequestDraw: + if sceneEx.RequestDraw(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + + case rule.CancelDraw: + if sceneEx.CancelDraw(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + + case rule.RefuseDraw: + if sceneEx.RefuseDraw(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + + case rule.AgreeDraw: + if sceneEx.AgreeDraw(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + } + + case rule.PlayerOpStep: // 计步操作 + // 参数校验 + if len(params) == 0 { + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + } + + switch params[0] { + case rule.StepStart: // 开始计步 + if sceneEx.StartStep(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + + case rule.StepCancel: // 取消计步 + if sceneEx.CancelStep(playerEx) { + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + returnFunc(chesstitians.OpResultCode_OPRC_Error) + return true + } + + case rule.PlayerOpTest: // 测试 强制同步给所有的玩家 + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + return true + } + return true +} + +func (this *SceneStatePlayerOp) OnPlayerOpAI(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + return true +} + +// 玩家事件 +func (this *SceneStatePlayerOp) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneHandCardStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params) +} + +func (this *SceneStatePlayerOp) OnTick(s *base.Scene) { + this.SceneStateBase.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + now := time.Now() + + f := func() { + // 玩家操作超时,输 + for k, v := range sceneEx.seats { + if k != sceneEx.GetCurOpPos() && v != nil { + sceneEx.winSnId = v.GetSnId() + sceneEx.ChangeSceneState(rule.SceneStateBilled) + break + } + } + } + + if now.Sub(sceneEx.StateStartTime) > rule.PlayerOpTimeout { + f() + } + + curPlayer := sceneEx.seats[sceneEx.GetCurOpPos()] + if time.Duration(curPlayer.getTotalTime())*time.Second >= rule.PlayerTotalTime { + f() + } + } +} + +//===================================== +// SceneStateBilled 结算 +//===================================== + +type SceneStateBilled struct { + SceneStateBase +} + +func (this *SceneStateBilled) GetState() int { + return rule.SceneStateBilled +} + +func (this *SceneStateBilled) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SceneStateWaitPlayer, rule.SceneStateWaitStart: + return true + default: + return false + } +} + +func (this *SceneStateBilled) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + return true +} + +func (this *SceneStateBilled) OnEnter(s *base.Scene) { + this.SceneStateBase.OnEnter(s) + this.BroadcastRoomState(s, this.GetState()) + + sceneEx, ok := s.GetExtraData().(*SceneEx) + if !ok { + return + } + + pack := &chesstitians.SCChesstitiansGameBilled{} + chessType := model.ChesstitiansType{ + GameId: sceneEx.GameId, + RoomId: int32(sceneEx.GetSceneId()), + RoomType: int32(sceneEx.Scene.SceneType), + NumOfGames: int32(sceneEx.Scene.NumOfGames), + BankId: sceneEx.masterSnId, + PlayerCount: rule.MaxNumOfPlayer, + BaseScore: s.BaseScore, + TaxRate: s.DbGameFree.GetTaxRate(), + RoomMode: s.GetSceneMode(), + } + + var winPlayer *PlayerEx + var losePlayer *PlayerEx + var draw bool + + for _, seat := range sceneEx.seats { + if seat != nil { + if sceneEx.winSnId == seat.GetSnId() { + winPlayer = seat + } else { + losePlayer = seat + } + } + } + + if winPlayer == nil { + draw = true + winPlayer = sceneEx.seats[0] + losePlayer = sceneEx.seats[1] + } + + if draw { // 平局 + billData, chessPerson := sceneEx.ToBilled(winPlayer, losePlayer, rule.Draw) + pack.Datas = append(pack.Datas, billData) + chessType.PlayerData = append(chessType.PlayerData, chessPerson) + + //玩家2 + billData1, chessPerson1 := sceneEx.ToBilled(losePlayer, winPlayer, rule.Draw) + pack.Datas = append(pack.Datas, billData1) + chessType.PlayerData = append(chessType.PlayerData, chessPerson1) + } else { // 有输赢 + billData, chessPerson := sceneEx.ToBilled(winPlayer, losePlayer, rule.Win) + pack.Datas = append(pack.Datas, billData) + chessType.PlayerData = append(chessType.PlayerData, chessPerson) + + // 输家 + billData1, chessPerson1 := sceneEx.ToBilled(losePlayer, winPlayer, rule.Lose) + pack.Datas = append(pack.Datas, billData1) + chessType.PlayerData = append(chessType.PlayerData, chessPerson1) + } + + proto.SetDefaults(pack) + s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), pack, 0) + logger.Logger.Trace("ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled gameFreeId:", sceneEx.GetGameFreeId(), ";pack:", pack) + + info, err := model.MarshalGameNoteByFIGHT(&chessType) + if err == nil { + isSave := false + var logid string + for _, o_player := range chessType.PlayerData { + if !sceneEx.Testing && !o_player.IsRob { + if logid == "" { + logid, _ = model.AutoIncGameLogId() + } + var totalin, totalout int64 + + if o_player.GainCoin < 0 { + totalin -= (o_player.GainCoin + o_player.GainTaxCoin) + } else { + totalout += (o_player.GainCoin + o_player.GainTaxCoin) + } + + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + sceneEx.SaveFriendRecord(o_player.UserId, o_player.IsWin, o_player.GainCoin, sceneEx.GetBaseScore()) + + // 游戏数据统计 + sceneEx.Statistics(&base.StaticParam{ + SnId: o_player.UserId, + Gain: o_player.GainCoin, + GainTax: o_player.GainTaxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.GetGamingRobotCnt() > 0, + }) + + // 玩家游戏记录 + sceneEx.SaveGamePlayerListLog(o_player.UserId, + base.GetSaveGamePlayerListLogParam(o_player.Platform, o_player.Channel, o_player.Promoter, + o_player.PackageTag, logid, o_player.InviterId, totalin, totalout, o_player.GainTaxCoin, + 0, 0, o_player.GainCoin, validBet, validFlow, o_player.IsFirst, o_player.IsLeave)) + isSave = true + } + } + if isSave { + // 牌局记录 + sceneEx.SaveGameDetailedLog(logid, info, &base.GameDetailedParam{}) + if !sceneEx.IsMatchScene() { + sceneEx.SetSystemCoinOut(sceneEx.SystemCoinOut()) + base.CoinPoolMgr.PushCoin(sceneEx.GetCoinSceneTypeId(), sceneEx.GetGroupId(), sceneEx.GetPlatform(), sceneEx.GetSystemCoinOut()) + } + } + } + sceneEx.NotifySceneRoundPause() +} + +// 玩家操作 +func (this *SceneStateBilled) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) { + return true + } + + sceneEx, ok := s.GetExtraData().(*SceneEx) + if !ok { + return true + } + if sceneEx == nil { + return true + } + playerEx, ok := p.GetExtraData().(*PlayerEx) + if !ok { + return true + } + if playerEx == nil { + return true + } + + returnFunc := func(code chesstitians.OpResultCode) { + if code == chesstitians.OpResultCode_OPRC_Sucess { + sceneEx.BroadcastPlayerSCOp(playerEx.SnId, opcode, code, params) + } else { + // 若不能成功操作,仅发给操作的玩家 + sceneEx.OnPlayerSCOp(p, opcode, code, params) + } + } + + switch int32(opcode) { + case rule.PlayerOpNextPlay: // 再来一局 + if playerEx.nextPlay { + return false + } + playerEx.nextPlay = true + returnFunc(chesstitians.OpResultCode_OPRC_Sucess) + + if sceneEx.nextSnId == 0 { + sceneEx.nextSnId = playerEx.GetSnId() + } + + if sceneEx.seats[0].nextPlay && sceneEx.seats[1].nextPlay || + sceneEx.GetGameId() == common.GameId_ChesstitiansCambodianRobot { + s.ChangeSceneState(rule.SceneStateWaitStart) + } + + return true + } + + return true +} + +// 玩家事件 +func (this *SceneStateBilled) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params) + + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + // 有人离开,解散房间 + for _, v := range sceneEx.seats { + if v != nil { + sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true) + } + } + } + } +} + +func (this *SceneStateBilled) OnTick(s *base.Scene) { + this.SceneStateBase.OnTick(s) + + sceneEx, ok := s.GetExtraData().(*SceneEx) + if !ok { + return + } + + // 有机器人,让机器人离开 + for _, v := range sceneEx.seats { + if v != nil && v.IsRob { + sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true) + } + } + + if sceneEx.GetRealPlayerNum() == 1 { + for _, v := range sceneEx.seats { + if v != nil { + sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true) + } + } + } + + if sceneEx.GetPlayerCnt() == 0 { + sceneEx.SceneDestroy(true) + } +} + +// 状态离开时 +func (this *SceneStateBilled) OnLeave(s *base.Scene) { + this.SceneStateBase.OnLeave(s) + if sceneEx, ok := s.GetExtraData().(*SceneEx); ok { + sceneEx.SetGaming(false) + + switch sceneEx.GetGameId() { + case common.GameId_Chesstitians, common.GameId_ChesstitiansMakruk, common.GameId_ChesstitiansCambodian: + hasLeave := false + //剔除下线玩家 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerEx := sceneEx.seats[i] + if playerEx == nil { + continue + } + playerEx.Clear() + if sceneEx.IsMatchScene() { + continue + } + if !playerEx.IsOnLine() { + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_DropLine, true) + hasLeave = true + continue + } + // 机器人金币离场检查 + if playerEx.IsRob { + if s.CoinOverMaxLimit(playerEx.GetCoin(), playerEx.Player) { + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_Normal, true) + hasLeave = true + continue + } + } + // 低于进场限额 + if !s.CoinInLimit(playerEx.GetCoin()) { + sceneEx.PlayerLeave(playerEx.Player, s.NotCoinInLimitType(playerEx.GetCoin()), true) + hasLeave = true + continue + } + } + + if !hasLeave && !sceneEx.IsRobFightGame() && !sceneEx.IsMatchScene() { + s.TryDismissRob() + } + case common.GameId_ChesstitiansCambodianRobot: + sceneEx.SceneDestroy(true) + return + } + + if s.CheckNeedDestroy() || (s.IsMatchScene() && (!s.MatchFinals || (s.MatchFinals && s.NumOfGames >= 2))) { // 非决赛打一场 决赛打两场 + sceneEx.SceneDestroy(true) + } + s.TryRelease() + } +} + +func (this *ScenePolicyEntity) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateId := state.GetState() + if stateId < 0 || stateId >= rule.SceneStateMax { + return + } + this.states[stateId] = state +} + +func (this *ScenePolicyEntity) GetSceneState(s *base.Scene, stateId int) base.SceneState { + if stateId >= 0 && stateId < rule.SceneStateMax { + return this.states[stateId] + } + return nil +} + +func init() { + ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateWaitPlayer{}) + ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateWaitStart{}) + ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateChessInit{}) + ScenePolicyEntitySingleton.RegisteSceneState(&SceneStatePlayerOp{}) + ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateBilled{}) + + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameId_Chesstitians, 0, ScenePolicyEntitySingleton) + base.RegisteScenePolicy(common.GameId_ChesstitiansMakruk, 0, ScenePolicyEntitySingleton) + base.RegisteScenePolicy(common.GameId_ChesstitiansCambodian, 0, ScenePolicyEntitySingleton) + base.RegisteScenePolicy(common.GameId_ChesstitiansCambodianRobot, 0, ScenePolicyEntitySingleton) + return nil + }) +} diff --git a/gamesrv/config.json b/gamesrv/config.json new file mode 100644 index 0000000..03b8858 --- /dev/null +++ b/gamesrv/config.json @@ -0,0 +1,174 @@ +{ + "netlib": { + "SrvInfo": { + "Name": "GameServer", + "Type": 7, + "Id": 777, + "AreaID": 1, + "Data": "", + "Banner": [ + "=================", + "game server", + "=================" + ] + }, + "IoServices": [ + { + "Id": 777, + "Type": 7, + "AreaId": 1, + "Name": "GameService", + "Ip": "127.0.0.1", + "Port": 0, + "Protocol": "tcp", + "Path": "/", + "MaxDone": 400000, + "MaxPend": 2000000, + "MaxPacket": 65535, + "MaxConn": 10000, + "RcvBuff": 4096000, + "SndBuff": 10240000, + "WriteTimeout": 30, + "ReadTimeout": 30, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-trace", + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "handler-game-close" + ] + }, + { + "Id": 501, + "Type": 5, + "AreaId": 1, + "Name": "ManagerService", + "Ip": "127.0.0.1", + "Port": 3002, + "MaxDone": 200, + "MaxPend": 200, + "MaxPacket": 65535, + "MaxConn": 10, + "RcvBuff": 8192, + "SndBuff": 8192, + "WriteTimeout": 30, + "ReadTimeout": 30, + "IsInnerLink": true, + "NoDelay": true, + "IsClient": true, + "IsAutoReconn": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler" + ] + }, + { + "Id": 901, + "Type": 9, + "AreaId": 1, + "Name": "RobotService", + "Ip": "127.0.0.1", + "Port": 9003, + "MaxDone": 20000, + "MaxPend": 20000, + "MaxPacket": 65535, + "MaxConn": 1, + "RcvBuff": 819200, + "SndBuff": 819200, + "WriteTimeout": 300, + "ReadTimeout": 300, + "IsInnerLink": true, + "NoDelay": true, + "IsClient": true, + "IsAutoReconn": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler" + ] + } + ] + }, + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal": { + "SupportSignal": true + }, + "common": { + "AppId": "5c56d1644966f078bfb90c71", + "IsDevMode": true + }, + "profile": { + "SlowMS": 500 + }, + "costum": { + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog": 1024, + "etcdurl": [ + "127.0.0.1:2379" + ], + "etcduser": "", + "etcdpwd": "" + }, + "webapi": { + "GameApiURL": "http://127.0.0.1:8000/api/game_srv" + }, + "data": { + "RootPath": "../data", + "LoadLib": true + }, + "i18n": { + "RootPath": "../data/i18n" + } +} \ No newline at end of file diff --git a/gamesrv/fishing/action_fish.go b/gamesrv/fishing/action_fish.go new file mode 100644 index 0000000..0afcde3 --- /dev/null +++ b/gamesrv/fishing/action_fish.go @@ -0,0 +1,637 @@ +package fishing + +import ( + "fmt" + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + fishing_proto "mongo.games.com/game/protocol/fishing" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "strconv" + "strings" +) + +type ErrorString struct { + code string +} + +func (this *ErrorString) Error() string { + return this.code +} + +// 装载完毕 +type CSLoadCompletePacketFactory struct { +} +type CSLoadCompleteHandler struct { +} + +func (this *CSLoadCompletePacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSLoadComplete{} + return pack +} +func (this *CSLoadCompleteHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + _, ok := data.(*fishing_proto.CSLoadComplete) + if ok == false { + return &ErrorString{code: "CSLoadComplete serialize error."} + } + player := base.PlayerMgrSington.GetPlayer(sid) + if player != nil { + if player.GetScene() != nil { + //fsd := player.GetScene().GetExtraData().(*FishingSceneData) + //if fsd != nil { + // fsd.SyncFish(player) + //} + } + } + return nil +} + +// 捕鱼 +type CSFishingOpPacketFactory struct { +} +type CSFishingOpHandler struct { +} + +func (this *CSFishingOpPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFishingOp{} + return pack +} +func (this *CSFishingOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + //logger.Logger.Trace("CSFishingOpHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSFishingOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSFishingOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSFishingOpHandler p.GetScene() == nil") + return nil + } + if !scene.HasPlayer(p) { + return nil + } + if scene.GetScenePolicy() != nil { + scene.GetScenePolicy().OnPlayerOp(scene, p, int(msg.GetOpCode()), msg.GetParams()) + } + return nil + } + return nil +} + +// 捕鱼 +type CSFishViewPacketFactory struct { +} +type CSFishViewHandler struct { +} + +func (this *CSFishViewPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFishView{} + return pack +} +func (this *CSFishViewHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishViewHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSFishView); ok { + var player *base.Player + snid := msg.GetSnId() + if snid == 0 { + player = base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishViewHandler GetPlayer p == nil") + return nil + } + } else { + player = base.PlayerMgrSington.GetPlayerBySnId(snid) + if player == nil { + logger.Logger.Warn("CSFishViewHandler GetPlayerBySnId p == nil") + return nil + } + if !player.IsRob && player != base.PlayerMgrSington.GetPlayer(sid) { + logger.Logger.Warn("CSFishViewHandler !player.IsRob && player!=base.PlayerMgrSington.GetPlayer(sid)", player.SnId) + return nil + } + } + + if player.GetScene() != nil { + if fsd, ok := player.GetScene().GetExtraData().(*FishingSceneData); ok && fsd != nil { + fp := fsd.players[player.SnId] + if fp != nil { + fsd.PushEventFish(fp, msg.GetSign(), msg.GetFishs(), msg.GetEventFish()) + } + } + } + return nil + } + return nil +} + +// 瞄准 +/*type CSFishTargetPacketFactory struct { +} +type CSFishTargetHandler struct { +} + +func (this *CSFishTargetPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFishTarget{} + return pack +}*/ + +/*func (this *CSFishTargetHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishTargetHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSFishTarget); ok { + var player *base.Player + if msg.GetRobotId() != 0 { + player = base.PlayerMgrSington.GetPlayerBySnId(msg.GetRobotId()) + } + if player == nil { + logger.Logger.Warn("CSFishViewHandler robot == nil") + return nil + //player = base.PlayerMgrSington.GetPlayer(sid) + } + if player == nil { + logger.Logger.Warn("CSFishViewHandler p == nil") + return nil + } + if player.GetScene() != nil { + if playerEx, ok := player.GetExtraData().(*FishingPlayerData); ok { + playerEx.TargetFish = msg.GetFishId() + } + pack := &fishing_proto.SCFishTarget{ + FishId: proto.Int32(msg.GetFishId()), + SnId: proto.Int32(player.SnId), + } + player.GetScene().Broadcast(int(fishing_proto.FIPacketID_FISHING_SC_FISHTARGET), pack, 0) + } + return nil + } + return nil +}*/ + +// 查看打死鱼 +type CSFishLookLockPacketFactory struct { +} +type CSFishLookLockHandler struct { +} + +func (this *CSFishLookLockPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSLookLockFish{} + return pack +} +func (this *CSFishLookLockHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishLookLockHandler Process recv ", data) + if _, ok := data.(*fishing_proto.CSLookLockFish); ok { + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishLookLockHandler p == nil") + return nil + } + if player.GetScene() != nil { + if playerEx, ok := player.GetExtraData().(*FishingPlayerData); ok { + + pack := &fishing_proto.SCLookLockFish{} + for id, v := range playerEx.lockFishCount { + pack.FishId = append(pack.FishId, id) + pack.FishIdNum = append(pack.FishIdNum, v) + } + logger.Logger.Trace("CSFishLookLockHandler Pb ", pack) + playerEx.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_LOOKLOCKFISH), pack) + } + } + return nil + } + return nil +} + +// 放炮台 +type CSFishReadyPranaPacketFactory struct { +} +type CSFishReadyPranaHandler struct { +} + +func (this *CSFishReadyPranaPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSReadyPrana{} + return pack +} +func (this *CSFishReadyPranaHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishReadyPranaHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSReadyPrana); ok { + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishReadyPranaHandler p == nil") + return nil + } + if player.GetScene() != nil { + if playerEx, ok := player.GetExtraData().(*FishingPlayerData); ok { + + pack := &fishing_proto.SCReadyPrana{ + SnId: proto.Int32(playerEx.SnId), + X: proto.Int32(msg.GetX()), + Y: proto.Int32(msg.GetY()), + } + if playerEx.PranaPercent == 100 { + pack.OpRetCode = fishing_proto.OpResultCode_OPRC_Sucess + } else { + pack.OpRetCode = fishing_proto.OpResultCode_OPRC_Error // 操作失败 + } + + logger.Logger.Trace("CSFishReadyPranaHandler Pb ", pack) + playerEx.GetScene().Broadcast(int(fishing_proto.FIPacketID_FISHING_SC_REALYPRANA), pack, 0) + } + } + return nil + } + return nil +} + +// 发射能量炮 +type CSFishFirePranaPacketFactory struct { +} +type CSFishFirePranaHandler struct { +} + +func (this *CSFishFirePranaPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFirePrana{} + return pack +} + +/*func (this *CSFishFirePranaHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishFirePranaHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSFirePrana); ok { + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishFirePranaHandler p == nil") + return nil + } + if player.GetScene() != nil { + if sx, ok := player.GetScene().GetExtraData().(*HPFishingSceneData); ok && sx != nil { + if playerEx, ok := player.GetExtraData().(*FishingPlayerData); ok { + + pack := &fishing_proto.SCReadyPrana{ + SnId: proto.Int32(playerEx.SnId), + X: proto.Int32(msg.GetX()), + Y: proto.Int32(msg.GetY()), + } + if playerEx.PranaPercent == 100 { + pack.OpRetCode = fishing_proto.OpResultCode_OPRC_Sucess + var fishs []int + for _, v := range msg.FishIds { + fish := sx.fish_list[v] + if fish != nil { + fish.OnHit(0) + fishs = append(fishs, int(v)) + } + } + // TODO 死鱼操作 + sx.firePranaDel(playerEx, fishs, int32(playerEx.Prana)) + playerEx.Prana = 0.0 + playerEx.PranaPercent = 0 + } else { + pack.OpRetCode = fishing_proto.OpResultCode_OPRC_Error // 操作失败 + } + + logger.Logger.Trace("CSFishFirePranaHandler Pb ", pack) + playerEx.GetScene().Broadcast(int(fishing_proto.FIPacketID_FISHING_SC_FIREPRANA), pack, 0) + } + } + } + return nil + } + return nil +}*/ +//玩家使用技能 +type CSFishSkillUseReqPacketFactory struct { +} +type CSFishSkillUseReqHandler struct { +} + +func (this *CSFishSkillUseReqPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFishSkillUseReq{} + return pack +} +func (this *CSFishSkillUseReqHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishSkillUseReqHandler Process recv ", data) + if msg, ok := data.(*fishing_proto.CSFishSkillUseReq); ok { + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishReadyPranaHandler p == nil") + return nil + } + if player.GetScene() != nil { + if playerEx, ok := player.GetExtraData().(*FishingPlayerData); ok { + skillId := msg.GetSkillId() + pack := &fishing_proto.SCFishSkillUseResp{ + Result: 0, + SkillId: skillId, + Status: fishing_proto.SCFishSkillUseResp_USABLE, + } + + //获取技能配置表 + skillTemp := srvdata.PBDB_FishSkillMgr.GetData(skillId) + if skillTemp == nil { + fishlogger.Errorf("没有找到技能模版 skillId = %v,snid = %v,playerEx = %v", skillId, player.SnId, playerEx) + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_NOT_EXISTS + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + //判断技能使用条件 + //vip等级限制 + if player.VIP < skillTemp.GetVip() { + fishlogger.Errorf("VIP等级不足,无法使用此技能!") + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_VIP_LIMIT + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + //判断消耗物品 + if skillTemp.GetConsume() != 0 { + item := skillTemp.Item + itemId := item[0] + itemNum := item[1] + //获取背包道具数量 + num, ok := player.Items[item[0]] + if !ok || num < item[1] { + fishlogger.Errorf("道具数量不在 itemId = %v,itemNum = %v,背包数量 = %v", item[0], item[1], num) + //判断其他消耗其他物品 + otherItem := skillTemp.OtherConsumer + otherItemId := otherItem[0] + otherItemNum := otherItem[1] + fishlogger.Tracef("道具不足,转换道具 otherItemId = %v,otherItemNum = %v", otherItemId, otherItemNum) + //判断背包道具是否足够 + if otherItemId == 0 || otherItemNum == 0 { + fishlogger.Tracef("转换道具为0,不支持用其他道具转换!!!!") + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_TOOL_LIMIT + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + if otherItemId == common.ItemIDDiamond { + if player.Diamond < int64(itemNum) { + fishlogger.Tracef("钻石数量不足,无法使用技能!") + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_TOOL_LIMIT + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + player.Diamond -= int64(otherItemNum) + } else { + if player.Items[otherItemId] < otherItemNum { + fishlogger.Tracef("转换物品是其他物品时,物品数量不足!") + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_TOOL_LIMIT + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + player.Items[otherItemId] = player.Items[otherItemId] - otherItemNum + } + fishlogger.Tracef("玩家使用技能当前道具不足 转化物品消耗 skillid = %v,snid = %v,otherItemId = %v,otherItemNum = %v", skillId, player.SnId, otherItemId, otherItemNum) + } else { + player.Items[itemId] = player.Items[itemId] - itemNum + //记录道具消耗日志 + fishlogger.Tracef("玩家使用技能,消耗道具!snod = %v, skill = %v,itemId =%v,itemNum = %v", player.SnId, skillId, itemId, itemNum) + } + } + if int64(skillTemp.GetMultiple()) > player.UnMaxPower { + fishlogger.Trace("解锁炮倍不足,无法使用此技能!") + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_UN_POWER + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + //判断当前房间是否可以使用此技能 + strSlice := strings.Split(skillTemp.Hidden, ",") + status := false + for _, s := range strSlice { + num, _ := strconv.Atoi(s) + if num == player.GetScene().SceneType { + status = true + } + } + if !status { + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_ROOM_DISALLOW + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + fishlogger.Trace("当前房间不允许使用此技能 skillId = %v,sceneType = %v", skillId, player.GetScene().SceneType) + return nil + } + //判断当前技能在不在CD中 + if playerEx, ok := player.GetScene().GetExtraData().(*FishingPlayerData); ok { + if !playerEx.GetSkillCD(skillId, skillTemp.SkillGroups) { + fishlogger.Tracef("当前技能正在CD中,无法使用此技能!!!!snid = %v,skill = %v", player.SnId, skillId) + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_CD + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + } else { + return nil + } + //当前场景存在世界boss是否可用 + //获取当前场景中的世界BOSS + if skillTemp.GetLimit() == 0 { + //判断当前场景中是否存在世界BOSS + if fsd, ok := player.GetScene().GetExtraData().(*FishingSceneData); ok && fsd != nil { + if fsd.GetWorldBoss() { + fishlogger.Tracef("当前场景存在世界BOSS,当前技能无法使用!snid = %v, skillId =%v ", player.SnId, skillId) + pack.Result = 1 + pack.Status = fishing_proto.SCFishSkillUseResp_WORLD_BOSS + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + return nil + } + } + } + //狂暴VIP等级限制 服务端在发炮的时候拦截检测 这里不检测 + if skillTemp.Fury > 0 { + + } + fishType := 0 + switch skillId { + case FishSill_Lock: //锁定 + break + case FishSkill_Freeze: //冰冻 + //scene冰冻 定屏 + //获取定屏时间 + SkillCDTime := skillTemp.Cooldown //秒 + fishlogger.Tracef("玩家使用定屏技能 定屏时间skillCDTime = %v", SkillCDTime) + //判断当前屏幕是否定屏 + if sceneData, ok := player.GetScene().GetExtraData().(*FishingSceneData); ok && sceneData != nil { + if sceneData.frozenTick == 0 { + sceneData.Freeze(int64(SkillCDTime)) + } else { + sceneData.Freeze(int64(SkillCDTime - sceneData.frozenTick)) + } + sceneData.frozenTick = SkillCDTime + } + break + case FishSkill_Speed: //狂暴 有2倍狂暴 和4倍狂暴 + break + case FishSkill_MagicLamp: + break + case FishSkill_MysticalHorn: + break + } + //记录技能CD 和公共CD cd延长1秒 + playerEx.AddSkillCD(skillId, skillTemp.SkillGroups, skillTemp.GetCooldown()+1, skillTemp.GCD+1) + //客户端返回消息 + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + //广播给其他人 FISHING_SC_SKILLUSEBEGIN + pack1 := &fishing_proto.SCFishSkillUseBegin{ + SkillId: skillId, + SnId: player.SnId, + FishID: 0, + FishType: int32(fishType), + Pos: int32(player.Pos), + } + player.GetScene().Broadcast(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSEBEGIN), pack1, 0) + } + } + return nil + } + return nil +} + +// 获取房间技能列表 +type CSSkillListReqPacketFactory struct { +} +type CSSkillListReqHandler struct { +} + +func (this *CSSkillListReqPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSFishSkillUseReq{} + return pack +} +func (this *CSSkillListReqHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + fishlogger.Trace("玩家请求房间技能列表!") + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSSkillListReqHandler p == nil") + return nil + } + if player.GetScene() == nil { + return nil + } + pack := &fishing_proto.SCSkillListResp{} + for _, skill := range srvdata.PBDB_FishSkillMgr.Datas.Arr { + //获取房间类型 + sceneType := player.GetScene().SceneType + str := skill.Hidden + num, err := strconv.Atoi(str) + status := true + if err != nil { + //解析错误 是数组 + numbers := strings.Split(str, ",") // 将字符串按逗号分割成单独的数字 + var arr []int32 // 声明一个空的 []int32 切片 + for _, numStr := range numbers { + num, err := strconv.ParseInt(numStr, 10, 32) // 将字符串转换为 int32 类型 + if err != nil { + fmt.Println("转换错误:", err) + return nil + } + arr = append(arr, int32(num)) // 将转换后的 int32 添加到切片中 + } + for _, value := range arr { + if value == int32(sceneType) { + status = false + break + } + } + } else { + if num != 0 { + status = false + } + } + if status { + skillInfo := &fishing_proto.SkillInfo{ + Id: skill.Id, + Name: skill.Name, + Vip: skill.Vip, + Consume: skill.Consume, + Item: string(skill.Item), + OtherConsumer: string(skill.OtherConsumer), + Duration: skill.Duration, + SkillGroup: skill.SkillGroups, + Gcd: skill.GCD, + Cd: skill.Cooldown, + HiddenPoses: skill.Hidden, + Describe: skill.Describe, + OpenCanon: skill.Multiple, + SkillType: skill.Type, + Fury: skill.Fury, + Limit: skill.Limit, + } + fishlogger.Tracef("skillInfo = %v", skillInfo) + pack.Items = append(pack.Items, skillInfo) + } + } + + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SKILLUSERESP), pack) + + return nil + +} + +// 客户端请求帧同步 +type CSSyncTimePointPacketFactory struct { +} +type CSSyncTimePointHandler struct { +} + +func (this *CSSyncTimePointPacketFactory) CreatePacket() interface{} { + pack := &fishing_proto.CSSyncTimePoint{} + return pack +} +func (this *CSSyncTimePointHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + fishlogger.Trace("客户端请求帧同步!!!!") + player := base.PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSSyncTimePointHandler p == nil") + return nil + } + if sceneData, ok := player.GetScene().GetExtraData().(*FishingSceneData); ok && sceneData != nil { + pack := &fishing_proto.SCSyncTimePoint{ + TimePoint: sceneData.TimePoint, + } + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SYNCTIMEPOINT), pack) + } + return nil +} + +func init() { + // 装载完毕,同步存活的鱼 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_LOADCOMPLETE), &CSLoadCompleteHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_LOADCOMPLETE), &CSLoadCompletePacketFactory{}) + // 捕鱼操作 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_OP), &CSFishingOpHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_OP), &CSFishingOpPacketFactory{}) + // 客户端触发事件 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_FISHVIEW), &CSFishViewHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_FISHVIEW), &CSFishViewPacketFactory{}) + //瞄准 + /* common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_FISHTARGET), &CSFishTargetHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_FISHTARGET), &CSFishTargetPacketFactory{})*/ + //查看打死鱼 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_LOOKLOCKFISH), &CSFishLookLockHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_LOOKLOCKFISH), &CSFishLookLockPacketFactory{}) + //放置炮台 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_REALYPRANA), &CSFishReadyPranaHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_REALYPRANA), &CSFishReadyPranaPacketFactory{}) + //发射能量炮 + //common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_FIREPRANA), &CSFishFirePranaHandler{}) + //netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_FIREPRANA), &CSFishFirePranaPacketFactory{}) + + //查询中奖 + //common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_JACKPOTLIST), &CSFishJackpotHandler{}) + //netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_JACKPOTLIST), &CSFishJackpotPacketFactory{}) + //使用技能 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_SKILLUSEREQ), &CSFishSkillUseReqHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_SKILLUSEREQ), &CSFishSkillUseReqPacketFactory{}) + //获取房间列表 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_SKILLLISTREQ), &CSSkillListReqHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_SKILLLISTREQ), &CSFishSkillUseReqPacketFactory{}) + //帧同步 + common.RegisterHandler(int(fishing_proto.FIPacketID_FISHING_CS_SYNCTIMEPOINT), &CSSkillListReqHandler{}) + netlib.RegisterFactory(int(fishing_proto.FIPacketID_FISHING_CS_SYNCTIMEPOINT), &CSFishSkillUseReqPacketFactory{}) +} diff --git a/gamesrv/fishing/constant.go b/gamesrv/fishing/constant.go new file mode 100644 index 0000000..70cb303 --- /dev/null +++ b/gamesrv/fishing/constant.go @@ -0,0 +1,77 @@ +package fishing + +import "time" + +const BULLETLIMIT = 2048 +const WINDOW_SIZE = 10 + +// 玩家的炮台类型 +const ( + NormalPowerType = iota // 普通炮台 + FreePowerType // 免费炮台 + BitPowerType // 钻头贝炮台 +) + +// 玩家数据索引 +const ( + GDATAS_HPFISHING_PRANA int = iota //聚能炮能量 + GDATAS_HPFISHING_ALLBET //总下注 + GDATAS_HPFISHING_ALLBET64 //总下注 高字节 + GDATAS_HPFISHING_CHANGEBET //每一局 金币变动总额 + GDATAS_FISHING_SELVIP //VIP 炮等级 + GDATAS_HPFISHING_MAX +) +const ( + FishDrop_Rate = 10000 + CountSaveNums = 300 +) + +const ( + FishingSceneAniTimeout = time.Second * 3 + FishArrayInterval = time.Second * 60 * 5 //鱼阵每五分钟直行一次 + PlayerLongTimeNoOp = time.Minute * 5 //玩家5分钟未发炮 踢出房间 +) +const ( + FishingSceneStateStart int = iota + FishingSceneStateClear + FishingSceneStateMax +) +const ( + FishingPlayerOpFire int = iota // 开炮 + FishingPlayerOpHitFish // 命中 + FishingPlayerOpSetPower // 设置炮台倍率 + FishingPlayerOpSelVip // 设置vip炮台 + FishingPlayerOpRobotFire // 机器人开炮 + FishingPlayerOpRobotHitFish // 机器人命中 + FishingPlayerOpLeave // + FishingPlayerOpEnter // + FishingPlayerOpAuto // 自动 + FishingPlayerOpSelTarget // 瞄准 + FishingPlayerOpFireRate // 开炮频率 + FishingRobotOpAuto // 机器人自动 + FishingRobotOpSetPower // 机器人设置炮台倍率 + FishingPlayerHangup // + FishingRobotWantLeave // +) + +const ( + FishingRobotBehaviorCode_StopFire int32 = iota //停止开炮 +) +const ( + ExtraAppearTime = 5 //额外出现时间 +) +const ( + FishArrayOne = 1 +) +const ( + ChangeSceneIdMax = 5 //变化场景最大id +) + +// 技能 +const ( + FishSill_Lock = 101 //锁定 + FishSkill_Freeze = 102 //冰冻 + FishSkill_Speed = 103 //狂暴 + FishSkill_MagicLamp = 104 //神灯 + FishSkill_MysticalHorn = 105 //神秘号角 +) diff --git a/gamesrv/fishing/playerdata_fishing.go b/gamesrv/fishing/playerdata_fishing.go new file mode 100644 index 0000000..b45893b --- /dev/null +++ b/gamesrv/fishing/playerdata_fishing.go @@ -0,0 +1,515 @@ +package fishing + +import ( + "fmt" + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fishing" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + "os" + "strconv" + "time" +) + +type FishingPlayerEvent struct { + FishId int32 // 唯一标识 PolicyId*1000000 + int32(value.GetId())*100 + int32(index+1) + Event int32 // 鱼触发的事件id + Power int32 // 炮台倍率 + Ts int64 // 时间戳 + DropCoin int32 // 鱼掉落金币 + ExtFishId []int32 // 相关鱼的鱼标识 +} + +type FishingPlayerData struct { + *base.Player + scene *base.Scene // 玩家当前所在场景 + LastFireTime time.Time // 用来计时,3分钟不在线踢出房间 + power int32 // 炮台倍率 + CoinCache int64 // 实时余额 + bullet map[int32]*Bullet // 子弹 + BulletLimit [BULLETLIMIT]int64 // 检测开枪频率过大 + fishEvent map[string]*FishingPlayerEvent // 事件标识:事件 + LostCoin int64 // 进场之后消耗的金币(子弹消耗) + LostCoinCache int64 // 保存游戏记录时的消耗金币,保存一次刷新一次,用来计算上次保存记录后消耗了多少金币 + AgentParam int32 // 机器人该字段表示代理玩家的id;玩家该字段表示其代理的其中一个机器人的id + RobotSnIds []int32 // 真实玩家绑定的机器人数组 + AutoFishing int32 // 自动标记 + SelTarget int32 // 瞄准数据? + TargetFish int32 // 瞄准的鱼标识? + FireRate int32 // 开炮频率 + taxCoin float64 // 开炮税收 + sTaxCoin float64 // 开炮税收(保存游戏记录时清零) + enterTime time.Time // 玩家入场时间 + SelVip int32 // 玩家选择的vip炮等级 + powerType int32 // 玩家炮台的类型 + FreePowerNum int32 // 免费炮台炮弹的数量 + winCoin int64 // 入场后总赢分 + realOdds int // 玩家的实际赔率? + logBulletHitNums int32 // 命中计数,保存记录时清零 + logFishCount map[int64]*model.FishCoinNum // key:鱼id和子弹倍率;游戏记录 + EnterCoin int64 // 进场金币 统计使用 + + lockFishCount map[int32]int32 // 鱼id对应打死的数量(天天捕鱼) + jackpotCoin float64 // 用来收集不足1的金币(天天捕鱼) + Prana float64 // 蓄能能量(天天捕鱼) + PranaPercent int32 // 蓄能能量百分比(天天捕鱼) + MaxCoin int64 // 游戏场最大金币(天天捕鱼) + MinCoin int64 // 游戏场最少金币(天天捕鱼) + ExtraCoin int64 // 除捕鱼外额外获得的金币(天天捕鱼) + TestHitNum int64 // 测试碰撞次数(天天捕鱼) + SkillCd map[int32]int64 //技能CD + SkillGCD map[int32]int64 //技能公共CD + //SkillMutex sync.Mutex +} + +/* +更新免费炮台相关状态 +*/ +func (this *FishingPlayerData) UpdateFreePowerState() { + this.powerType = FreePowerType + this.FreePowerNum = 100 +} + +/* +更新成普通炮台相关状态 +*/ +func (this *FishingPlayerData) UpdateNormalPowerState() { + this.powerType = NormalPowerType + this.FreePowerNum = 0 +} + +/* +更新成钻头炮台相关状态 +*/ +func (this *FishingPlayerData) UpdateBitPowerState() { + this.powerType = BitPowerType +} + +func (this *FishingPlayerData) MakeLogKey(fishTemplateId, power int32) int64 { + return common.MakeI64(fishTemplateId, power) +} + +func (this *FishingPlayerData) SplitLogKey(key int64) (int32, int32) { + return common.LowI32(key), common.HighI32(key) +} + +func (this *FishingPlayerData) init(s *base.Scene) { + //this.Player.extraData = this + this.Player.SetExtraData(this) + this.LostCoin = 0 + this.LostCoinCache = 0 + this.power = s.GetDBGameFree().GetIntuseCannonMin() + this.AgentParam = 0 + this.bullet = make(map[int32]*Bullet) + this.BulletLimit = [BULLETLIMIT]int64{} + this.fishEvent = make(map[string]*FishingPlayerEvent) + this.logBulletHitNums = 0 + this.lockFishCount = make(map[int32]int32) + this.logFishCount = make(map[int64]*model.FishCoinNum) + this.jackpotCoin = 0.0 + this.TestHitNum = 0 + this.scene = s + this.SkillGCD = make(map[int32]int64) + this.SkillCd = make(map[int32]int64) + this.LastFireTime = time.Now() + if this.GDatas == nil { + this.GDatas = make(map[string]*model.PlayerGameInfo) + } + if !s.GetTesting() && !this.IsRob { + key := s.KeyGamefreeId + var pgd *model.PlayerGameInfo + if data, exist := this.GDatas[key]; !exist { + pgd = new(model.PlayerGameInfo) + this.GDatas[key] = pgd + } else { + pgd = data + } + if pgd != nil { + //参数确保 + for i := len(pgd.Data); i < GDATAS_HPFISHING_MAX; i++ { + pgd.Data = append(pgd.Data, 0) + } + this.Prana = float64(pgd.Data[GDATAS_HPFISHING_PRANA]) + //this.SelVip = int32(pgd.Data[GDATAS_FISHING_SELVIP]) + } + //////////////////////// + var npgd *model.PlayerGameInfo + if ndata, exist := this.GDatas[s.KeyGameId]; !exist { + npgd = new(model.PlayerGameInfo) + this.GDatas[s.KeyGameId] = npgd + } else { + npgd = ndata + } + if npgd != nil { + //参数确保 + for i := len(npgd.Data); i < GDATAS_HPFISHING_MAX; i++ { + npgd.Data = append(npgd.Data, 0) + } + this.SelVip = int32(npgd.Data[GDATAS_FISHING_SELVIP]) + } + } + this.Clean() +} + +func (this *FishingPlayerData) SetSelVip(keyGameId string) { + if pgd, ok := this.GDatas[keyGameId]; ok { + pgd.Data[GDATAS_FISHING_SELVIP] = int64(this.SelVip) + } +} + +func (this *FishingPlayerData) Clean() { + this.bullet = make(map[int32]*Bullet) + this.BulletLimit = [BULLETLIMIT]int64{} + this.fishEvent = make(map[string]*FishingPlayerEvent) + this.logBulletHitNums = 0 + this.bullet = make(map[int32]*Bullet) + this.logFishCount = make(map[int64]*model.FishCoinNum) +} + +func (this *FishingPlayerData) CoinCheck(power int32) bool { + return this.CoinCache >= int64(power) +} + +func (this *FishingPlayerData) CurrentCoin() int64 { + return this.CoinCache +} + +func (this *FishingPlayerData) GetTodayGameData(gameId string) *model.PlayerGameStatics { + if this.TodayGameData == nil { + this.TodayGameData = model.NewPlayerGameCtrlData() + } + if this.TodayGameData.CtrlData == nil { + this.TodayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) + } + if _, ok := this.TodayGameData.CtrlData[gameId]; !ok { + this.TodayGameData.CtrlData[gameId] = model.NewPlayerGameStatics() + } + return this.TodayGameData.CtrlData[gameId] +} + +/* +设置当天数据 +*/ +func (this *FishingPlayerData) SetTodayGameDate(gameId string, playerGameStatics *model.PlayerGameStatics) { + this.TodayGameData.CtrlData[gameId] = playerGameStatics +} + +/* +获取昨日得当天数据集合 +*/ +func (this *FishingPlayerData) GetYestDayGameData(gameId string) *model.PlayerGameStatics { + if this.YesterdayGameData == nil { + this.YesterdayGameData = &model.PlayerGameCtrlData{} + } + if this.YesterdayGameData.CtrlData == nil { + this.YesterdayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) + } + if _, ok := this.YesterdayGameData.CtrlData[gameId]; !ok { + this.YesterdayGameData.CtrlData[gameId] = model.NewPlayerGameStatics() + } + return this.YesterdayGameData.CtrlData[gameId] +} + +func (this *FishingPlayerData) SaveDetailedLog(s *base.Scene) { + if len(this.logFishCount) == 0 { + return + } + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + if sceneEx.GetTesting() && this.IsRob { + this.logFishCount = make(map[int64]*model.FishCoinNum) + return + } + totalin := int64(0) + totalout := int64(0) + var fd model.FishDetiel + logBulletCount := make(map[int32]int32) + fcn := make([]model.FishCoinNum, 0) + for k, v := range this.logFishCount { + FishTemplateId, power := this.SplitLogKey(k) + fcn = append(fcn, model.FishCoinNum{ID: FishTemplateId, Power: power, Num: v.Num, Coin: v.Coin, HitNum: v.HitNum}) + totalout += int64(v.Coin) + logBulletCount[power] += v.HitNum + } + fd.HitInfo = &fcn + bt := make([]model.BulletLevelTimes, 0) + for k, v := range logBulletCount { + bt = append(bt, model.BulletLevelTimes{Level: k, Times: v}) + totalin += int64(k * v) + } + fd.BulletInfo = &bt + fp := &model.FishPlayerData{ + UserId: this.SnId, + UserIcon: this.Head, + TotalIn: totalin, + TotalOut: totalout, + CurrCoin: this.CoinCache, + } + // 捕鱼不需要个人信息里的战绩 + //win := totalout - totalin + //var isWin int32 + //if win > 0 { + // isWin = 1 + //} else if win < 0 { + // isWin = -1 + //} + //sceneEx.SaveFriendRecord(this.SnId, isWin) + fd.PlayData = fp + info, err := model.MarshalGameNoteByFISH(&fd) + if err == nil { + logid, _ := model.AutoIncGameLogId() + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + param := base.GetSaveGamePlayerListLogParam(this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, logid, + this.InviterId, totalin, totalout, int64(this.sTaxCoin), 0, totalin, + totalout, validFlow, validBet, sceneEx.IsPlayerFirst(this.Player), false) + sceneEx.SaveGamePlayerListLog(this.SnId, param) + sceneEx.SaveGameDetailedLog(logid, info, &base.GameDetailedParam{}) + } + + pack := &server_proto.GWFishRecord{ + GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), + SnId: proto.Int32(this.SnId), + } + for _, v := range this.logFishCount { + fishRecord := &server_proto.FishRecord{ + FishId: proto.Int32(v.ID), + Count: proto.Int32(v.Num), + } + pack.FishRecords = append(pack.FishRecords, fishRecord) + } + if len(pack.FishRecords) > 0 { + this.SendToWorld(int(server_proto.SSPacketID_PACKET_GW_FISHRECORD), pack) + } + + diffLostCoin := this.LostCoin - this.LostCoinCache + this.LostCoinCache = this.LostCoin + gain := totalout - totalin + this.Statics(s.KeyGameId, s.KeyGamefreeId, gain, true) + if diffLostCoin > 0 { + playerBet := &server_proto.PlayerData{ + SnId: proto.Int32(this.SnId), + Bet: proto.Int64(totalin), + Gain: proto.Int64(gain), + Tax: proto.Int64(int64(this.sTaxCoin)), + Coin: proto.Int64(this.Coin), + GameCoinTs: proto.Int64(this.GameCoinTs), + } + gwPlayerData := &server_proto.GWPlayerData{ + SceneId: proto.Int(sceneEx.SceneId), + GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), + } + gwPlayerData.Datas = append(gwPlayerData.Datas, playerBet) + proto.SetDefaults(gwPlayerData) + sceneEx.SendToWorld(int(server_proto.SSPacketID_PACKET_GW_PLAYERDATA), gwPlayerData) + } + } + this.sTaxCoin = 0 + this.logBulletHitNums = 0 + this.logFishCount = make(map[int64]*model.FishCoinNum) +} + +func (this *FishingPlayerData) SetMaxCoin() { + if this.CoinCache > this.MaxCoin { + this.MaxCoin = this.CoinCache + } +} + +func (this *FishingPlayerData) SetMinCoin() { + if this.CoinCache < this.MinCoin || this.MinCoin == 0 { + this.MinCoin = this.CoinCache + } +} + +func (this *FishingPlayerData) SaveFishingLog(curCoin int64, gameid string) { + data := this.GDatas[gameid] + log := fmt.Sprintf("%v,%v,%v,%v,%v\n", this.CurrentCoin(), data.Statics.TotalIn, data.Statics.TotalOut, curCoin, base.GetCoinPoolMgr().GetTax()) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + fileName := fmt.Sprintf("fishdata-%v.csv", this.SnId) + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + defer file.Close() + if err != nil { + file, err = os.Create(fileName) + if err != nil { + return err + } + } + file.WriteString(log) + return nil + }), nil, "SaveFishingLog").StartByFixExecutor("SaveFishingLog") +} + +// NewStatics .b +func (this *FishingPlayerData) NewStatics(betCoin, gain int64) { + if this.scene == nil || this.scene.GetTesting() || this.IsRob { //测试场和机器人不统计 + return + } + if betCoin == 0 && gain == 0 { + return + } + // start 如果当前处于免费炮的情况,NewStatics 记录消费为0 + if this.powerType == FreePowerType { + betCoin = 0 + } + // end + + //黑白名单不参与投入产出统计,影响自己和他人体验 + if this.WhiteLevel != 0 || this.WhiteFlag != 0 || this.BlackLevel != 0 || this.GMLevel > 0 { + return + } + + fishlogger.Tracef("============snid=%v betCoin=%v gain=%v ", this.SnId, betCoin, gain) + + keyGlobal := fmt.Sprintf("%v_%v", this.scene.GetPlatform(), this.scene.GetGameFreeId()) + if base.SysProfitCoinMgr.SysPfCoin != nil { + if base.SysProfitCoinMgr.SysPfCoin.ProfitCoin == nil { + base.SysProfitCoinMgr.SysPfCoin.ProfitCoin = make(map[string]*model.SysCoin) + } + var syscoin *model.SysCoin + if data, exist := base.SysProfitCoinMgr.SysPfCoin.ProfitCoin[keyGlobal]; !exist { + syscoin = new(model.SysCoin) + base.SysProfitCoinMgr.SysPfCoin.ProfitCoin[keyGlobal] = syscoin + } else { + syscoin = data + } + syscoin.PlaysBet += betCoin + syscoin.SysPushCoin += gain + fishlogger.Tracef("============SysProfitCoinMgr key=%v PlaysBet:= %v SysPushCoin= %v ", keyGlobal, syscoin.PlaysBet, syscoin.SysPushCoin) + } + + keyPlayer := strconv.Itoa(int(this.scene.GetGameFreeId())) + var pgd *model.PlayerGameInfo + if d, exist := this.GDatas[keyPlayer]; exist { + FishGDataLen(len(d.Data), d) + pgd = d + } else { + pgd = &model.PlayerGameInfo{ + Data: make([]int64, GDATAS_HPFISHING_MAX, GDATAS_HPFISHING_MAX), + } + this.GDatas[keyPlayer] = pgd + } + + low := pgd.Data[GDATAS_HPFISHING_ALLBET] + high := pgd.Data[GDATAS_HPFISHING_ALLBET64] + allBet := common.MakeI64(int32(low), int32(high)) + betCoin + + pgd.Data[GDATAS_HPFISHING_ALLBET], pgd.Data[GDATAS_HPFISHING_ALLBET64] = common.LowAndHighI64(allBet) + pgd.Data[GDATAS_HPFISHING_CHANGEBET] += int64(gain) + + fishlogger.Tracef("============snid=%v total fish betCoin:= %v gain=%v ", this.SnId, allBet, + pgd.Data[GDATAS_HPFISHING_CHANGEBET]) +} + +// GetAllBet . +func (this *FishingPlayerData) GetAllBet(key string) int64 { + ret := int64(1) + if d, exist := this.GDatas[key]; exist { + FishGDataLen(len(d.Data), d) + ret = common.MakeI64(int32(d.Data[GDATAS_HPFISHING_ALLBET]), int32(d.Data[GDATAS_HPFISHING_ALLBET64])) + } + return ret +} + +// GetAllChangeBet . +func (this *FishingPlayerData) GetAllChangeBet(key string) int64 { + ret := int64(0) + if d, exist := this.GDatas[key]; exist { + FishGDataLen(len(d.Data), d) + ret = int64(d.Data[GDATAS_HPFISHING_CHANGEBET]) + } + return ret +} + +// 计算个人赔率和个人限制系数 +func (this *FishingPlayerData) GetPlayerOdds(gameid string, ctroRate int32, fishlevel int32) (float64, float64) { + if data, ok := this.GDatas[gameid]; ok { + //总产出初始值 = 100*(1-调节频率)*100 (分) 初级场 + //总产出初始值 = 1000*(1-调节频率)*100 (分) 中级场 + //总产出初始值 = 10000*(1-调节频率)*100 (分) 高级场 + //总投入初始值 = 100*100 (分) 初级场 + //总投入初始值 = 1000*100 (分) 中级场 + //总投入初始值 = 10000*100 (分) 高级场 + initBaseValue := int64(10000) //1万分 + if fishlevel == 2 { + initBaseValue = 100000 + } else if fishlevel == 3 { + initBaseValue = 1000000 + } + totalInValue := initBaseValue + data.Statics.TotalIn + totalOutValue := initBaseValue*(10000-int64(ctroRate))/10000 + data.Statics.TotalOut + + //个人限制系数 + ratio := 1.0 + if fishlevel == 1 && totalOutValue-totalInValue >= 20000 { + ratio = 0.5 + } else if fishlevel == 2 && totalOutValue-totalInValue >= 100000 { + ratio = 0.5 + } else if fishlevel == 3 && totalOutValue-totalInValue >= 500000 { + ratio = 0.5 + } + + return float64(totalOutValue) / float64(totalInValue), ratio + } else { + fishlogger.Errorf("player.GDatas[%v] is %v", gameid, this.GDatas[gameid]) + return 0, 0 + } + +} + +var FishGDataLen = func(flen int, pgd *model.PlayerGameInfo) { + if flen < GDATAS_HPFISHING_MAX { + for i := flen; i < GDATAS_HPFISHING_MAX; i++ { + pgd.Data = append(pgd.Data, 0) + } + } +} + +// 增加技能CD +func (this *FishingPlayerData) AddSkillCD(skillId, skillType, cdTime, gcdTime int32) { + /* this.SkillMutex.Lock() + defer this.SkillMutex.Unlock()*/ + this.SkillCd[skillId] = time.Now().UnixNano()/int64(time.Millisecond) + int64(cdTime*1000) + this.SkillGCD[skillType] = time.Now().UnixNano()/int64(time.Millisecond) + int64(gcdTime*1000) +} + +// 判断技能在不在CD中 +func (this *FishingPlayerData) GetSkillCD(skillId, skillType int32) bool { + _, exists := this.SkillCd[skillId] + if exists { + return false + } + _, exists = this.SkillGCD[skillType] + if exists { + return false + } + return true +} + +// 玩家升级解锁炮倍 +func (this *FishingPlayerData) PlayerEvent(eventType, id int64) { + switch eventType { + case fishing.Event_Player_UpLevel: + //获取解锁的炮倍 + power := srvdata.PlayerExpMgr.GetUnPower(int32(id)) + if power != 0 { + this.UnPlayerPowerEx(int64(power)) + } + break + case fishing.Event_Player_UnMaxPower: + //id 直接解锁炮倍 后期用 + this.UnPlayerPowerEx(id) + break + case fishing.Event_Player_UnPowerList: + //id 解锁的炮台ID + this.UnPlayerPowerListEx(int32(id)) + break + default: + fishlogger.Errorf("未处理的玩家事件 eventType = %v", eventType) + break + } + +} diff --git a/gamesrv/fishing/scenedata_fishing.go b/gamesrv/fishing/scenedata_fishing.go new file mode 100644 index 0000000..aa76cd2 --- /dev/null +++ b/gamesrv/fishing/scenedata_fishing.go @@ -0,0 +1,1563 @@ +package fishing + +import ( + "fmt" + "github.com/cihub/seelog" + "math" + "math/rand" + "mongo.games.com/game/gamesrv/base" + fishing_proto "mongo.games.com/game/protocol/fishing" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/timer" + "strconv" + "strings" + "sync" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fishing" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/netlib" + srvlibproto "mongo.games.com/goserver/srvlib/protocol" +) + +var fishlogger seelog.LoggerInterface + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + fishlogger = common.GetLoggerInstanceByName("FishLogger") + return nil + }) +} + +type FishBattle struct { + SnId int32 // 玩家id + Bullet int32 // 子弹id + Power int32 // 炮台倍率 + FishsId []int // 被打鱼id + ExtFishis []int32 // 关联鱼id + LockFish int32 // 用来提交捕捉到的指定选鱼(天天捕鱼) + Multiple int32 //子弹倍数 +} +type Bullet struct { + Id int32 + Power int32 + SrcId int32 + LifeTime int32 + Multiple int32 +} +type FishingSceneData struct { + *base.Scene + players map[int32]*FishingPlayerData + seats [fishing.MaxPlayer]*FishingPlayerData + BattleBuff chan *FishBattle + TimePoint int32 // 帧同步中的帧序号,这里一帧是100毫秒 + NextTime int64 // 下次模式切换的时间点 + fish_list map[int32]*Fish // 鱼标识 存活的鱼 + delFish_list map[int32]int32 // 鱼标识:1 打死的鱼 + fish_Event map[int32][]int32 // 事件id:鱼标识;鱼消失时要同步维护 + fishLevel int32 // 场次 + frozenTick int32 // 冰冻计时 + lastTick int64 // 鱼同步计时 + remainder int64 // 鱼同步计时 + lastBossTime int64 // boss 上一次出现的时间 + lastLittleBossTime int64 // 小boss 上一次出现的时间 + BossId int32 //当前场景的BOSS + BossTag int32 //当前场景BOSS是否 + LastID int32 //当前场景鱼ID生成器 + hDestroy timer.TimerHandle + fishsMutex sync.Mutex //同步鱼 + af AppearFish // 出鱼管理 + platform string + gameId int + sceneType int + sceneMode int + keyGameId string //游戏ID + testing bool + gamefreeId int32 + groupId int32 + agentor int32 + fluctuateMaxMap map[string]float64 + ChangeSceneId int32 //切换场景Id 客户端用 +} + +func NewFishingSceneData(s *base.Scene) *FishingSceneData { + return &FishingSceneData{ + Scene: s, + players: make(map[int32]*FishingPlayerData), + BattleBuff: make(chan *FishBattle, 1000), + fish_list: make(map[int32]*Fish), + fish_Event: make(map[int32][]int32), + delFish_list: make(map[int32]int32), + fluctuateMaxMap: make(map[string]float64), + } +} +func (this *FishingSceneData) RebindPlayerSnId(oldSnId, newSnId int32) { + if p, exist := this.players[oldSnId]; exist { + delete(this.players, oldSnId) + this.players[newSnId] = p + } +} + +func (this *FishingSceneData) init() bool { + if this.GetDBGameFree() != nil { + this.fishLevel = this.GetDBGameFree().GetSceneType() // 更新当前的场次 + } + this.SetPlayerNum(4) + this.gameId = this.GetGameId() + this.platform = this.GetPlatform() + this.sceneType = int(this.DbGameFree.GetSceneType()) + this.keyGameId = this.GetKeyGameId() + this.testing = this.GetTesting() + this.gamefreeId = this.GetGameFreeId() + this.groupId = this.GetGroupId() + this.agentor = this.GetAgentor() + this.sceneMode = this.GetSceneMode() + this.TimePoint = 0 + this.lastLittleBossTime = time.Now().Unix() + this.lastBossTime = time.Now().Unix() + /* //随机一个初始点 + start := rand.Int31n(this.MaxTick * 4 / 5) + for i := int32(0); i < start; i++ { + this.fishFactory() + }*/ + this.af.SceneEx = this + //this.NextTime -= int64(time.Millisecond * time.Duration(start*100)) + fluctuateMaxStr := this.GetDBGameFree().FluctuateMax + if len(fluctuateMaxStr) > 0 { + pairs := strings.Split(fluctuateMaxStr, ";") + for _, pair := range pairs { + keyValue := strings.Split(pair, ",") + key := keyValue[0] + value, _ := strconv.ParseFloat(keyValue[1], 64) + this.fluctuateMaxMap[key] = value + } + } + //this.af.InitFishAppear() + this.af.InitFishPath() + this.af.Start() + return true +} + +func (this *FishingSceneData) Clean() { + for _, p := range this.players { + //5款捕鱼保持一致 + fishID, coin, taxc := int32(0), int32(0), int64(0) + for _, v := range p.bullet { + coin += v.Power + taxc += int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(v.Power)) + } + this.RetBulletCoin(p, fishID, coin, taxc, false) // 合并后发送 + p.bullet = make(map[int32]*Bullet) + p.BulletLimit = [BULLETLIMIT]int64{} + p.fishEvent = make(map[string]*FishingPlayerEvent) + p.logFishCount = make(map[int64]*model.FishCoinNum) + } + this.fish_list = make(map[int32]*Fish) + this.delFish_list = make(map[int32]int32) + this.fish_Event = make(map[int32][]int32) +} +func (this *FishingSceneData) BroadcastPlayerLeave(p *base.Player, reason int) { +} +func (this *FishingSceneData) SceneDestroy(force bool) { + this.Scene.Destroy(force) +} + +// RetBulletCoin 返还子弹消耗的金币 +// 子弹碰撞的鱼已经死了或者过期了,返还 +func (this *FishingSceneData) RetBulletCoin(player *FishingPlayerData, fishID, coin int32, taxc int64, flag bool) { + if player.IsRob && !model.GameParamData.IsRobFightTest { + return + } + pack := &fishing_proto.SCFishDel{ + FishId: proto.Int32(int32(fishID)), + Coin: proto.Int32(coin), + CurrentPlayerCoin: proto.Int64(player.CoinCache), + } + proto.SetDefaults(pack) + if !this.GetTesting() && !player.IsRob { //所有捕鱼统一 + player.NewStatics(int64(-coin), 0) + } + //taxc := int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(coin)) + player.LostCoin -= int64(coin) + player.CoinCache += int64(coin) + player.SetTotalBet(player.GetTotalBet() - int64(coin)) + //player.Statics(this.KeyGameId, this.KeyGamefreeId, int64(coin), false) + player.LastRechargeWinCoin -= int64(coin) + + if !this.GetTesting() { + player.taxCoin -= float64(taxc) + player.sTaxCoin -= float64(taxc) + } + fishlogger.Trace("RetBulletCoin : ", fishID, player.IsRob, coin, player.LostCoin, player.CoinCache, player.GetTotalBet()) + //base.GetCoinPoolMgr().PopCoin(this.GetGameFreeId(), this.GetGroupId(), this.GetPlatform(), int64(coin)-taxc) + tax := base.GetCoinPoolMgr().GetTax() + base.GetCoinPoolMgr().SetTax(tax - float64(taxc)) + pack.CurrentPlayerCoin = proto.Int64(player.CoinCache) + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FISHDEL), pack) +} + +/* + * 玩家相关 + */ +func (this *FishingSceneData) EnterPlayer(player *FishingPlayerData) bool { + pos := -1 + emptyPos := []int{} + for i := 0; i < fishing.MaxPlayer; i++ { + if this.seats[i] == nil { + emptyPos = append(emptyPos, i) + } + } + if len(emptyPos) > 0 { + pos = emptyPos[common.RandInt(len(emptyPos))] + } else { + fishlogger.Error("Fishing enter player find pos error.") + } + player.SetPos(pos) + player.CoinCache = player.GetCoin() + player.LostCoin = 0 + player.LostCoinCache = 0 + //player.FishPoolKey = fmt.Sprintf("%v-%v", this.GetGameFreeId(), player.Platform) + this.players[player.SnId] = player + this.seats[pos] = player + this.OnEnterPlayer(player) + return true +} +func (this *FishingSceneData) OnEnterPlayer(player *FishingPlayerData) { + /* + 如果进场的是机器人,寻找场内负载最小的玩家进行绑定 + 如果进场的是玩家,寻找场内没有代理的机器人 + */ + var tempPlayer *FishingPlayerData //新逻辑 + if player.IsRob { + for i := 0; i < fishing.MaxPlayer; i++ { + curSeatPlayer := this.seats[i] + if curSeatPlayer != nil && !curSeatPlayer.IsRob { + //新逻辑 + if tempPlayer == nil { + tempPlayer = curSeatPlayer + } else if len(tempPlayer.RobotSnIds) > len(curSeatPlayer.RobotSnIds) { + tempPlayer = curSeatPlayer + } + + //老逻辑 + if curSeatPlayer.AgentParam == 0 { + curSeatPlayer.AgentParam = player.SnId + player.AgentParam = curSeatPlayer.SnId + curSeatPlayer.RobotSnIds = append(curSeatPlayer.RobotSnIds, player.SnId) + break + } + } + } + + //新逻辑 设置机器人的代理情况 + if tempPlayer != nil && player.AgentParam == 0 { + player.AgentParam = tempPlayer.SnId + tempPlayer.AgentParam = player.SnId + tempPlayer.RobotSnIds = append(tempPlayer.RobotSnIds, player.SnId) + } + + } else { + for i := 0; i < fishing.MaxPlayer; i++ { + curSeatRobot := this.seats[i] + if curSeatRobot != nil && curSeatRobot.IsRob { + if curSeatRobot.AgentParam == 0 { //无主机器人 + curSeatRobot.AgentParam = player.SnId + player.AgentParam = curSeatRobot.SnId + player.RobotSnIds = append(player.RobotSnIds, curSeatRobot.SnId) //新逻辑 + } + } + } + if len(player.RobotSnIds) == 0 { + for i := 0; i < fishing.MaxPlayer; i++ { + curSeatRobot := this.seats[i] + if curSeatRobot != nil && curSeatRobot.IsRob { + if curSeatRobot.AgentParam != 0 { + //需要重新平衡下机器人负载 + for j := 0; j < fishing.MaxPlayer; j++ { + curSeatPlayer := this.seats[j] + if curSeatPlayer != nil && !curSeatPlayer.IsRob && curSeatPlayer.SnId == curSeatRobot.AgentParam && len(curSeatPlayer.RobotSnIds) > 1 { + //bind agent + curSeatRobot.AgentParam = player.SnId + player.AgentParam = curSeatRobot.SnId + player.RobotSnIds = append(player.RobotSnIds, curSeatRobot.SnId) + //unbind agent + curSeatPlayer.RobotSnIds = common.DelSliceInt32(curSeatPlayer.RobotSnIds, curSeatRobot.SnId) + curSeatPlayer.AgentParam = curSeatPlayer.RobotSnIds[0] + break + } + } + } + } + //分担一个就可以了 + if len(player.RobotSnIds) != 0 { + break + } + } + } + } + return +} +func (this *FishingSceneData) QuitPlayer(player *FishingPlayerData, reason int) bool { + if _, ok := this.players[player.SnId]; ok { + player.SetSelVip(this.KeyGameId) + player.SaveDetailedLog(this.Scene) + delete(this.players, player.SnId) + this.seats[player.GetPos()] = nil + this.BroadcastPlayerLeave(player.Player, 0) + diffCoin := player.CoinCache - player.GetCoin() + player.AddCoin(diffCoin, common.GainWay_Fishing, base.SyncFlag_ToClient, "system", this.GetSceneName()) + if diffCoin != 0 || player.LostCoin != 0 { + if !player.IsRob && !this.GetTesting() { + + player.AddServiceFee(int64(player.taxCoin)) + } + player.SetGameTimes(player.GetGameTimes() + 1) + if diffCoin > 0 { + //player.winTimes++ + player.SetWinTimes(player.GetWinTimes() + 1) + } else { + //player.lostTimes++ + player.SetLostTimes(player.GetLostTimes() + 1) + } + } + this.OnQuitPlayer(player, reason) + return true + } else { + return false + } +} +func (this *FishingSceneData) OnQuitPlayer(player *FishingPlayerData, reason int) { + /* + 如果离场的是机器人,从代理玩家身上将其删除 + 如果离场的是玩家,为玩家身上代理的机器人寻找其他代理 + */ + if player.IsRob { //机器人离场 + for i := 0; i < fishing.MaxPlayer; i++ { + curSeatPlayer := this.seats[i] + if curSeatPlayer != nil && !curSeatPlayer.IsRob { + //老逻辑,不改动 + if curSeatPlayer.AgentParam == player.SnId { + curSeatPlayer.AgentParam = 0 + player.AgentParam = 0 + } + //新逻辑 + if common.InSliceInt32(curSeatPlayer.RobotSnIds, player.SnId) { + curSeatPlayer.RobotSnIds = common.DelSliceInt32(curSeatPlayer.RobotSnIds, player.SnId) + player.AgentParam = 0 + if len(curSeatPlayer.RobotSnIds) > 0 { + curSeatPlayer.AgentParam = curSeatPlayer.RobotSnIds[0] + } else { + curSeatPlayer.AgentParam = 0 + } + } + } + } + } else { //玩家离场 + for i := 0; i < fishing.MaxPlayer; i++ { + curSeatRobot := this.seats[i] + if curSeatRobot != nil && curSeatRobot.IsRob { + if curSeatRobot.AgentParam == player.SnId { + curSeatRobot.AgentParam = 0 + player.AgentParam = 0 + + var tempPlayer *FishingPlayerData //新逻辑 负载最小的玩家 + for j := 0; j < fishing.MaxPlayer; j++ { + curSeatPlayer := this.seats[j] + if curSeatPlayer != nil && !curSeatPlayer.IsRob && curSeatPlayer != player { + if curSeatPlayer.AgentParam == 0 { + curSeatPlayer.AgentParam = curSeatRobot.SnId + curSeatRobot.AgentParam = curSeatPlayer.SnId + curSeatPlayer.RobotSnIds = append(curSeatPlayer.RobotSnIds, curSeatRobot.SnId) //新逻辑 + pack := &fishing_proto.SCReBindAgent{ + PlayerSnid: proto.Int32(curSeatPlayer.SnId), + RobSnid: proto.Int32(curSeatRobot.SnId), + } + curSeatPlayer.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_REBINDAGENT), pack) + break + } else { + if tempPlayer == nil { + tempPlayer = curSeatPlayer + } else if len(tempPlayer.RobotSnIds) > len(curSeatPlayer.RobotSnIds) { + tempPlayer = curSeatPlayer + } + } + } + } + + //新逻辑 + if tempPlayer != nil && curSeatRobot.AgentParam == 0 { + curSeatRobot.AgentParam = tempPlayer.SnId + tempPlayer.AgentParam = curSeatRobot.SnId + tempPlayer.RobotSnIds = append(tempPlayer.RobotSnIds, curSeatRobot.SnId) + pack := &fishing_proto.SCReBindAgent{ + PlayerSnid: proto.Int32(tempPlayer.SnId), + RobSnid: proto.Int32(curSeatRobot.SnId), + } + tempPlayer.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_REBINDAGENT), pack) + } + } + } + } + player.RobotSnIds = nil + } +} +func (this *FishingSceneData) OnTick() { + if this.TimePoint%10 == 0 { // TimePoint单位是百毫秒,所以是每秒执行一次 fishTimeOut() + if this.frozenTick > 0 { + this.frozenTick -= 1 + fishlogger.Trace("当前屏幕冰冻剩余时间:", this.frozenTick) + } + this.FishTimeOut() + //检测技能CD + this.CheckSkillCD() + } +} + +func (this *FishingSceneData) CheckSkillCD() { + for _, player := range this.players { + /* player.SkillMutex.Lock() + defer player.SkillMutex.Unlock()*/ + delSkillCD := []int32{} + for skillId, endTime := range player.SkillCd { + if time.Now().UnixNano()/int64(time.Millisecond) >= endTime { + delSkillCD = append(delSkillCD, skillId) + } + } + //删除技能cd + for _, key := range delSkillCD { + delete(player.SkillCd, key) + } + delSkillGCD := []int32{} + for skillId, endTime := range player.SkillGCD { + if time.Now().UnixNano()/int64(time.Millisecond) >= endTime { + delSkillGCD = append(delSkillCD, skillId) + } + } + //删除公共CD + for _, key := range delSkillGCD { + delete(player.SkillGCD, key) + } + } +} + +// 获取当前场景是否存在世界BOSS +func (this *FishingSceneData) GetWorldBoss() bool { + for _, value := range this.fish_list { + if value.FishType == fishing.WorldBoss { + return true + } + } + return false +} + +// fishTimeOut 清除过期的鱼 +func (this *FishingSceneData) FishTimeOut() { + //设置鱼的创建帧和结束帧 + this.fishsMutex.Lock() + defer this.fishsMutex.Unlock() + for key, value := range this.fish_list { + if value == nil { + delete(this.fish_list, key) + fishlogger.Tracef("[fishTimeOut]1 delete fish:[%v]", key) + continue + } + if value.LiveTick <= this.TimePoint { + delete(this.fish_list, key) + fishlogger.Tracef("[fishTimeOut]2 delete fish:[%v]", key) + if value.Event != 0 { + this.fish_Event[value.Event] = common.DelSliceInt32(this.fish_Event[value.Event], value.FishID) + } + } + } +} + +func (this *FishingSceneData) AddFish(fishs []*Fish, duration int32) { + //设置鱼的创建帧和结束帧 + this.fishsMutex.Lock() + defer this.fishsMutex.Unlock() + for _, fish := range fishs { + fish.BirthTick = this.TimePoint + path := this.af.fishPath[fish.Path] + if duration == 0 { + fish.InitCrashDetect(0, path) + } + fish.LiveTick += this.TimePoint + fishlogger.Tracef("计算鱼的结束帧 liveTick = %v,当前场景帧:%v", fish.LiveTick, this.TimePoint) + this.fish_list[fish.FishID] = fish + if fish.Event != 0 { + eventFishs := this.fish_Event[fish.Event] + if eventFishs == nil { + eventFishs = []int32{} + } + this.fish_Event[fish.Event] = append(eventFishs, fish.FishID) + fishlogger.Trace("出鱼 添加鱼事件!!! fishType = %v,fishId = %v", fish.FishType, fish.FishID) + } + } +} +func (this *FishingSceneData) DelFish(id int32) { + //加锁管理 + this.fishsMutex.Lock() + defer this.fishsMutex.Unlock() + + if _, exist := this.fish_list[id]; exist { + delete(this.fish_list, id) + //fishlogger.Tracef("[DelFish] delete fish:[%v]", id) + this.delFish_list[id] = 1 + } +} + +// notifyAppearFish 通知出鱼 +func (this *FishingSceneData) NotifyAppearFish(fishs []*Fish) { + //创建队列 + array := make([]*fishing_proto.FishInfo, 0, len(fishs)) + + //遍历出鱼 + for _, v := range fishs { + fish := &fishing_proto.FishInfo{ + FishID: v.FishID, + FishType: v.FishType, + FishPath: v.Path, + FishSpeed: v.Speed, + BirthTick: v.BirthTick, + LiveTick: v.LiveTick, + FishChild: v.Child, + } + + //加入队列 + array = append(array, fish) + } + + //构造数据 + appearFish := &fishing_proto.SCNotifyAppearFish{ + OutFishType: 1, + Fishs: array, + } + //发送数据 + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_NOTIFYAPPEARFISH), appearFish, 0) + fishlogger.Trace("通知客户端出鱼!!!!!!!!!!!!!!!!!appearFish = ", appearFish) +} + +// 通知客户端切换场景 +func (this *FishingSceneData) NotifyChangeScene() { + this.ChangeSceneId += 1 + if this.ChangeSceneId >= ChangeSceneIdMax { + this.ChangeSceneId = 0 + } + date := &fishing_proto.SCNotifyChangeScene{ + SceneId: this.ChangeSceneId, + } + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_NOTIFYCHANGESCENE), date, 0) + fishlogger.Trace("通知客户端切换场景并清空数据!!!!!!") +} + +/* + * 捕鱼相关 + */ +func (this *FishingSceneData) fishBattle() { + select { + case data := <-this.BattleBuff: + player := this.players[data.SnId] + if player == nil { + fishlogger.Tracef("Bullet %v owner %v offline.", data.Bullet, data.SnId) + return + } + delete(player.bullet, data.Bullet) // 二次清楚 防止没有删掉 + var count = len(data.FishsId) + if count > 0 && data.Power > 0 { + this.fishProcess(player, data.FishsId, data.Power, data.Multiple, data.ExtFishis) + } + default: + break + } +} + +// fishProcess 捕鱼击中的标准处理逻辑 +// fishIds 被击中的鱼(目前只会是一条鱼) +// power 炮倍率 +// extfishis 关联鱼 +func (this *FishingSceneData) fishProcess(player *FishingPlayerData, fishIds []int, power, multiple int32, extfishis []int32) { + if len(fishIds) == 0 { + return + } + + //调试辅助 + sendMiss := func(fishid, rate int32) { + if player.GMLevel > 0 { + pack := &fishing_proto.SCFireMiss{ + FishId: proto.Int32(fishid), + Rate: proto.Int32(rate), + } + proto.SetDefaults(pack) + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FIREMISS), pack) + } + } + + // 去重,检查存活 + extFishMap := make(map[int32]struct{}) + dropcoinext := int32(0) // 掉落金币 + for _, v := range extfishis { + if _, exist := extFishMap[v]; !exist { + extFishMap[v] = struct{}{} + var extfish = this.fish_list[v] + if extfish == nil { + continue + } + dropcoinext += extfish.DropCoin + } + } + + var killRate int32 + var death bool + var robot = player.IsRob + var ts = time.Now().Unix() + hitFishMap := make(map[int32]struct{}) + for _, id := range fishIds { + hitFishMap[int32(id)] = struct{}{} + } + for value, _ := range hitFishMap { + var fish = this.fish_list[value] // 取出对应的鱼的概率 + // 判断当前的鱼是否有效 + if fish == nil { + fishlogger.Tracef("[fishProcess] Be hit fish [%v] is disappear.", value) + taxc := int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(power)) + if player.powerType != FreePowerType { + // 不是免费炮期间返还 + this.RetBulletCoin(player, int32(value), power, taxc, true) + } + //} + //鱼不存在 + sendMiss(int32(value), -1) + continue + } + + //特殊鱼处理 + if fish.TemplateID == fishing.Fish_CaiShen && fish.DropCoin < fish.MaxDropCoin { + fish.DropCoin++ + this.syncFishCoin(fish) + } + + //同组的鱼(一网打尽) + var groupcoinex int32 + if fish.Event > 0 && fish.Event <= fishing.Event_Group_Max { + groupFishs := this.fish_Event[fish.Event] + for _, fishId := range groupFishs { + if fishId == value { //去重 + continue + } + if _, exist := extFishMap[value]; exist { //去重 + continue + } + fishg := this.fish_list[fishId] + if fishg == nil { + continue + } + if fishg.IsDeath(this.TimePoint) { + continue + } + if !fishg.IsBirth(this.TimePoint) { + continue + } + groupcoinex += fishg.DropCoin + } + } + //判断鱼是不是boss鱼 1 普通BOSS 2 世界BOSS + if fish.IsBoss == fishing.Boss { + //增加BOSS奖池 + this.AddBossPond(player, fish.TemplateID, power) + } + + death = false + if (robot && !model.GameParamData.IsRobFightTest) || this.GetTesting() { + //体验场概率稍有提升 + killRate = 10000 / (fish.DropCoin + dropcoinext + groupcoinex) + if this.GetTesting() { + killRate *= 2 + } + if rand.Int31n(10000) < killRate { + death = true + } + // todo 强制设置机器人 鱼死不 (测试使用) + //death = false + } else { //欢乐捕鱼|李逵劈鱼...其他捕鱼都走这个算法 + // start 记录相关鱼的击中次数 + key := player.MakeLogKey(fish.TemplateID, power) + if v, ok := player.logFishCount[key]; ok { + v.HitNum++ + } else { + player.logFishCount[key] = &model.FishCoinNum{ + HitNum: 1, + ID: fish.TemplateID, + Power: power, + } + } + //击中计数 + player.logBulletHitNums++ + //判断鱼是否死亡 + dto := this.isDead(player, value, power, multiple, fish.DropCoin, 0, false) + death = dto.IsResult + } + + if !death { + //鱼没死 + sendMiss(int32(value), killRate) + continue + } + // 判断当前是否是 特殊鱼 + deathFishs := this.fishEvent(fish, player, power, ts, extfishis) + if len(deathFishs) == 0 { + //鱼没死 + sendMiss(int32(value), killRate) + continue + } + + this.fishSettlements(deathFishs, player, power, fish.Event, ts, 0, 0) + } + + //写条记录 + if player.logBulletHitNums >= CountSaveNums { + player.SaveDetailedLog(this.Scene) + } +} + +type GameSettleDto struct { + HitRate float64 + Suiji float64 + IsResult bool + Rate interface{} +} + +// 计算鱼死亡 +func (this *FishingSceneData) isDead(player *FishingPlayerData, fishType int32, power int32, multiple int32, fishmu int32, totalfishProb float64, flag bool) *GameSettleDto { + /** + * 取反正切函数: ATAN(个人金币池的绝对值/(用户目前炮倍*炮数波动参数))/3.14*2*当前场次波动上限+1 + */ + //判断鱼死亡 + // 获取鱼的配置信息和波动上限信息 + var fish = this.fish_list[fishType] // 取出对应的鱼的概率 + // 判断是否满足波动上限条件 + glp := this.checkIsmeet(this.fluctuateMaxMap, fishmu) + + var fishProb float64 + if !flag { + // 如果不是标志位,使用鱼的默认概率 + fishProb = float64(fish.Rate) / 10000.0 + } else { + // 否则使用总概率 + fishProb = totalfishProb + } + + // 计算炮的加成概率 + canPro := float64(float64(power) * float64(this.GetDBGameFree().Fluctuate)) + + // 获取捕鱼最大负面影响、最大命中率上限值和个人金币池的绝对值 + negativemax := float64(this.GetDBGameFree().NegativeMax) / 1000.0 + catchFishratioMax := float64(this.GetDBGameFree().RatioMax) / 1000.0 + scorepool := player.MoneyPond + + // 使用反正切函数计算命中率的变化率 + glp1, error := strconv.ParseFloat(glp, 64) + if error != nil { + dto := &GameSettleDto{ + IsResult: false, + } + return dto + } + rate := math.Atan(math.Abs(float64(scorepool))/canPro)/3.14*2*glp1 + 1 + var hitRate float64 + + if scorepool > 0 { + // 如果个人金币池大于0,则命中率为鱼的概率乘以变化率 + hitRate = fishProb * rate + } else { + if rate > negativemax { + // 如果变化率超过了捕鱼最大负面影响,将变化率限制为最大负面影响值 + rate = negativemax + } + // 命中率为鱼的概率除以变化率,但不能超过最大命中率上限值 + hitRate = fishProb / rate + if hitRate > catchFishratioMax { + hitRate = catchFishratioMax + } + } + + // 根据倍数随机判断命中次数 + var suiji float64 + isresult := false + num := multiple + + // 如果玩家有特殊状态,则命中次数为2次 + if player.powerType != 0 { + num = 2 + } + + for i := 0; i < int(num); i++ { + // 使用随机数判断命中结果 + suiji = rand.Float64() + isresult = hitRate >= suiji + if isresult { + break + } + } + // 构造游戏结算信息 + dto := &GameSettleDto{ + HitRate: hitRate, + Suiji: suiji, + IsResult: isresult, + Rate: rate, + } + fishlogger.Infof("玩家:{%v}金币池为:{%v}炮倍:{%v}波动上限glp:{%v}鱼的ID为:{%v}=鱼的概率为:{%v}=suiji值获取:{%v}=hitRate值:{%v}=rate值:{%v},鱼id = {%v}", player.GetSnId(), player.MoneyPond, power, glp, fish.FishID, fishProb, dto.Suiji, dto.HitRate, dto.Rate, fish.TemplateID) + return dto +} + +func (data *FishingSceneData) checkIsmeet(fluctuateMaxMap map[string]float64, fishmu int32) string { + glc := "" + for key, value := range fluctuateMaxMap { + if len(key) == 0 { + continue + } + str := strings.Split(key, "-") + start, _ := strconv.Atoi(str[0]) + end, _ := strconv.Atoi(str[1]) + if fishmu >= int32(start) && fishmu <= int32(end) { + glc = strconv.FormatFloat(value, 'f', -1, 64) + break + } + } + + if fishmu > 10000 { + glc = strconv.FormatFloat(getMaxValue(fluctuateMaxMap), 'f', -1, 64) + } + + return glc +} + +func getMaxValue(fluctuateMaxMap map[string]float64) float64 { + maxValue := math.Inf(-1) + for _, value := range fluctuateMaxMap { + if value > maxValue { + maxValue = value + } + } + return maxValue +} + +/* +event 对应得事件ID +*/ +func (this *FishingSceneData) EventTreasureChestSettlements(power int32) (int32, []int32) { + // 计算龙王多播相关得额外收益 + var totalWeight int32 + var eventCoin int32 + for _, weight := range fishing.TreasureChestWeight { + totalWeight = totalWeight + weight + } + NowWeight := rand.Int31n(totalWeight) + var cumulativeWeight int32 + for index, weight := range fishing.TreasureChestWeight { + cumulativeWeight = cumulativeWeight + weight + if NowWeight <= cumulativeWeight { + treasureChestReward := fishing.TreasureChestReward[index] + for _, value := range treasureChestReward { + eventCoin = eventCoin + power*value + } + return eventCoin, treasureChestReward + } + } + return 0, []int32{} +} + +// fishSettlements 计算得分 +// fishs 打死的鱼 +// power 炮倍率 +// event 事件 +// ts 时间戳 +// eventFishId 事件鱼标识 +// eventFishCoin 事件鱼倍率 +func (this *FishingSceneData) fishSettlements(fishs []*Fish, player *FishingPlayerData, power int32, event int32, + ts int64, eventFishId int32, eventFishCoin int32) { + var coin int64 // 鱼死亡本身得金币计算 + var treasureChestReward []int32 + pack := &fishing_proto.SCFireHit{ + Snid: proto.Int32(player.SnId), + Ts: proto.Int64(ts), + EventFish: proto.Int32(eventFishId), + EventCoin: proto.Int32(eventFishCoin * power), + Power: proto.Int32(power), + Event: proto.Int32(event), + } + var sumExp int32 = 0 + for _, value := range fishs { + var dropCoin int32 + if value.Event == fishing.Event_Bit { + dropCoin = 0 + } else if value.Event == fishing.Event_TreasureChest { + dropCoin, treasureChestReward = this.EventTreasureChestSettlements(power) + fishlogger.Infof("Event_TreasureChest eventCoin %v treasureChestReward %v", dropCoin, treasureChestReward) + } else if value.Event == fishing.Event_NewBoom { + dropCoin = 0 + } else if value.Event == fishing.Event_FreePower { + dropCoin = 0 + } else { + dropCoin = value.DropCoin * power + } + //BOSS鱼死亡 更新BOSS池和个人池 + if value.IsBoss == fishing.Boss { + bossPond := base.GetCoinPoolMgr().GetBossPond(this.DbGameFree.SceneType) + this.isBossDie(player, int64(dropCoin), bossPond) + } + + pack.FishId = append(pack.FishId, value.FishID) + pack.Coin = append(pack.Coin, dropCoin) + key := player.MakeLogKey(value.TemplateID, power) + if v, ok := player.logFishCount[key]; ok { + v.Coin += dropCoin + v.Num++ + } else { + player.logFishCount[key] = &model.FishCoinNum{ + Coin: dropCoin, + Num: 1, + Power: power, + } + } + fishlogger.Infof("logFishCount %v,%v,%v,%v", value.TemplateID, power, player.logFishCount[key].Coin, player.logFishCount[key].HitNum) + coin = coin + int64(dropCoin) + this.DelFish(value.FishID) + value.SetDeath() + //打死鱼增加玩家经验 + sumExp += value.Exp + } + if event == fishing.Event_FreePower { + player.UpdateFreePowerState() + fishlogger.Infof("snid %v 更新为免费炮台", player.SnId) + } + if !this.GetTesting() && !player.IsRob { //5款捕鱼保持统一 + player.NewStatics(0, coin) + } + player.winCoin += coin + player.CoinCache += coin + player.MoneyPond -= coin + fishlogger.Infof("玩家:%v ,鱼死亡扣除金币池: %v ,当前金币池剩余:%v", player.SnId, coin, player.MoneyPond) + fishlogger.Infof("fishSettlements player %v coin %v dropCoin %v , moneyPond = %v", player.SnId, player.CoinCache, coin, player.MoneyPond) + pack.AddExp = sumExp + oldLevel := player.FishLevel + player.AddPlayerExp(int64(sumExp)) + if oldLevel != player.FishLevel { + //触发炮倍解锁事件 + player.PlayerEvent(fishing.Event_Player_UpLevel, player.FishLevel) + } + pack.CurrentPlayerCoin = proto.Int64(player.CoinCache) + proto.SetDefaults(pack) + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIREHIT), pack, 0) + + // 连续开宝箱 + if event == fishing.Event_TreasureChest { + eventTreasureChestPack := &fishing_proto.SCTreasureChestEvent{ + Snid: proto.Int32(player.SnId), + Reward: treasureChestReward, + CurrentPlayerCoin: proto.Int64(player.CoinCache), + } + proto.SetDefaults(eventTreasureChestPack) + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_TREASURECHESTEVENT), eventTreasureChestPack, 0) + fishlogger.Infof("Event_TreasureChest BroadCastMessage %v", fishing_proto.FIPacketID_FISHING_SC_TREASURECHESTEVENT) + } + /* if !player.IsRob || model.GameParamData.IsRobFightTest { + base.GetCoinPoolMgr().PopCoin(this.GetGameFreeId(), this.GetGroupId(), this.GetPlatform(), int64(coin)) + } + */ +} +func (this *FishingSceneData) PushBattle(player *FishingPlayerData, bulletid int32, fishs []int32, extfishis []int32) { + bullet := player.bullet[bulletid] + fishlogger.Infof("PushBattle player %v bullet %v fishs %v extfishis %v", player.SnId, bulletid, fishs, extfishis) + if bullet == nil { + fishlogger.Infof("%v not find in %v bullet buff. PushBattle player %v", bulletid, player.GetName(), player.SnId) + return + } + + battleData := &FishBattle{ + SnId: bullet.SrcId, + Bullet: bullet.Id, + Power: bullet.Power, + ExtFishis: extfishis, + Multiple: bullet.Multiple, + } + if len(fishs) > 0 { + battleData.FishsId = append(battleData.FishsId, int(fishs[0])) + } + + select { + case this.BattleBuff <- battleData: + { + delete(player.bullet, battleData.Bullet) + } + default: + { + delete(player.bullet, battleData.Bullet) + fishlogger.Error("Player battle buff full.") + } + } +} + +/* +向前端推送 绑定机器人对应的行为 +behaviorCode 0 是 机器人 静默行为 +*/ +func (this *FishingSceneData) SCRobotBehavior(snid, robotId int32, behaviorCode int32) { + player := this.players[snid] + if player == nil { + fishlogger.Errorf("SCRobotBehavior player %v is empty,bullet will be droped.", snid) + return + } + pack := &fishing_proto.SCRobotBehavior{ + Code: proto.Int32(behaviorCode), + RobotId: proto.Int32(robotId), + } + proto.SetDefaults(pack) + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SCROBOTBEHAVIOR), pack) +} + +// PushBullet 开炮 +// 玩家开炮,记录税收和消息广播 +func (this *FishingSceneData) PushBullet(snid, x, y, id, power, fishId, multiple int32) fishing_proto.OpResultCode { + player := this.players[snid] + if player == nil { + fishlogger.Errorf("player %v is empty,bullet will be droped.", snid) + return fishing_proto.OpResultCode_OPRC_Error + } + + if power <= 0 || power != player.power { + fishlogger.Tracef("[%v %v] power is invalid(%v) currpower(%v).", player.GetName(), player.SnId, power, player.power) + return fishing_proto.OpResultCode_OPRC_Error + } + //判断玩家发炮倍数 + if multiple == 1 || multiple == 2 || multiple == 4 { + if multiple == 4 { + skillTemp := srvdata.PBDB_FishSkillMgr.GetData(103) + if skillTemp == nil { + return fishing_proto.OpResultCode_OPRC_Error + } + if player.VIP < skillTemp.Fury { + fishlogger.Errorf("4倍炮弹,VIP等级不足 SnId = %v", player.SnId) + return fishing_proto.OpResultCode_OPRC_Error + } + } + } else { + fishlogger.Errorf("炮弹倍数错误,SnId = %v,multiple = %v", player.SnId, multiple) + return fishing_proto.OpResultCode_OPRC_Error + } + if (multiple == 2 || multiple == 4) && player.powerType != 0 { + //狂暴下已经打死特属鱼了,再发狂暴子弹 直接return + return fishing_proto.OpResultCode_OPRC_Error + } + + // 检测当前玩家的的金币数够不够 + if !player.CoinCheck(power * multiple) { + fishlogger.Tracef("%v no enough coin to fishing.", player.GetName()) + return fishing_proto.OpResultCode_OPRC_CoinNotEnough + } + + curTime := time.Now().Unix() % BULLETLIMIT + sbl := player.BulletLimit[curTime] + // 子弹的数量限制,统一按最高倍速处理,配合10秒窗口期 + bulletCountLimit := int64(12) + // start 玩家的开火率 》 0 的时候 子弹的数量限制 设置为 10 + //if player.FireRate > 0 { + // bulletCountLimit = 10 + //} + // 判断子弹的数量 是否过大 + if sbl > bulletCountLimit { + //10秒的窗口期,避免堆包误判 + total := sbl + for i := 1; i < WINDOW_SIZE; i++ { + total += player.BulletLimit[(int(curTime)-i+BULLETLIMIT)%BULLETLIMIT] + } + if total/WINDOW_SIZE > bulletCountLimit { + fishlogger.Infof("Player bullet too fast.") + //子弹打飞机了~~~ + key := player.MakeLogKey(0, power) + if v, ok := player.logFishCount[key]; ok { + v.HitNum++ + } else { + player.logFishCount[key] = &model.FishCoinNum{ + HitNum: 1, + ID: 0, //飞机??? + Power: power, + } + } + return fishing_proto.OpResultCode_OPRC_Error + } + } else { + player.BulletLimit[curTime] = sbl + 1 + } + + player.bullet[id] = &Bullet{ + Id: id, + Power: power, + SrcId: player.SnId, + LifeTime: 0, + Multiple: multiple, + } + + if !this.GetTesting() && !player.IsRob { //5款捕鱼保持统一 + player.NewStatics(int64(power*multiple), 0) + } + + // start 对玩家身上的金币变化进行变更 + if player.powerType != FreePowerType { // 只有当前炮台不是免费炮台的时候,才计算相关金额 + player.LostCoin += int64(power * multiple) + player.CoinCache -= int64(power * multiple) + //fishlogger.Infof("player %v coin %v", player.SnId, player.CoinCache) + player.SetTotalBet(player.GetTotalBet() + int64(power*multiple)) + //if this.GetDBGameFree().GetGameId() != int32(common.GameId_NFishing) { + //player.Statics(this.KeyGameId, this.KeyGamefreeId, -int64(power), true) + player.LastRechargeWinCoin += int64(power * multiple) + if _, ok := player.TodayGameData.CtrlData[this.GetKeyGameId()]; !ok { + player.TodayGameData.CtrlData[this.GetKeyGameId()] = model.NewPlayerGameStatics() + } + player.TodayGameData.CtrlData[this.GetKeyGameId()].TotalIn += int64(power * multiple) // 添加每日写入数据 + } else { + player.FreePowerNum-- + if player.FreePowerNum == 0 { + player.UpdateNormalPowerState() // 状态变更 + fishlogger.Infof("snid %v , 更新为普通炮台", player.SnId) + } + } + //更新玩家最后发炮时间 + player.LastFireTime = time.Now() + // start 根据税收调整对应的比例 , 并且调整整个水池的逻辑 + //if !player.IsRob { + //增加个人金币池 + //玩家发炮增加金币池数值 + //var multiple int32 = 1 //当前默认的倍数只有一倍 后期增加了再改 + var moneyScore = int64(float64(power) * float64(multiple) * float64(1.0-this.GetDraw()/1000)) + //var moneyScore = int64(float64(power) * float64(1.0-this.GetDraw()/1000)) + //普通跑增加个人金币池 其他状态的炮不增加 + if moneyScore > 0 && player.powerType == 0 { + player.MoneyPond += moneyScore + } + fishlogger.Infof("玩家发炮snid= %v, 当前玩家金币池:%v,本次增加的金币moneyScore = %v", player.SnId, player.MoneyPond, moneyScore) + //公共池 + //publicPond := int64(float64(power) * float64((this.GetPublicPondRatio()))) + playerCount := len(this.Players) + roomCount := 1 + if math.Ceil(float64(playerCount/4)) != 0 { + roomCount = int(math.Ceil(float64(playerCount / 4))) + } + fmt.Println("roomCount = ", roomCount) + //增加公共奖池 暂时不要 + //base.GetCoinPoolMgr().AddPublicPond(l(this.gameId), publicPond) + + pack := &fishing_proto.SCFire{ + Snid: proto.Int32(player.SnId), + X: proto.Int32(x), + Y: proto.Int32(y), + Bulletid: proto.Int32(id), + Power: proto.Int32(power), + CurrentPlayerCoin: proto.Int64(player.CoinCache), + FishId: proto.Int32(fishId), + Multiple: proto.Int32(multiple), + } + proto.SetDefaults(pack) + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIRE), pack, 0) + //if player.GameData[this.GetKeyGameId()].GameTimes%15 == 0 { + // curCoin := coinPoolMgr.GetCoin(this.GetGameFreeId(), this.GetPlatform(), this.GetGroupId()) + // player.SaveFishingLog(curCoin, this.GetKeyGameId()) + //} + return fishing_proto.OpResultCode_OPRC_Sucess +} + +// 同步屏幕存在的鱼 +func (this *FishingSceneData) SyncFish(player *base.Player) { + var cnt int + var fishes []*fishing_proto.FishInfo + syncFish := false + pack := &fishing_proto.SCSyncFishCoin{} + for _, fish := range this.fish_list { + if fish.IsDeath(this.TimePoint) { + continue + } + + fishes = append(fishes, &fishing_proto.FishInfo{ + FishID: fish.FishID, + FishType: fish.FishType, + FishPath: fish.Path, + FishSpeed: fish.Speed, + BirthTick: fish.BirthTick, + LiveTick: fish.LiveTick, + FishChild: fish.Child, + }) + cnt++ + if fish.TemplateID == fishing.Fish_CaiShen { + pack.FishId = proto.Int32(fish.FishID) + pack.Coin = proto.Int64(int64(fish.DropCoin)) + syncFish = true + } + } + fishlogger.Trace("Current fish list count:", cnt) + fishlogger.Trace("Current timePoint :", this.TimePoint) + packFishes := &fishing_proto.SCFishesEnter{ + //PolicyId: proto.Int32(this.PolicyId), + Fishes: fishes, + IceSec: proto.Int32(int32(0)), + TimeTick: proto.Int32(this.TimePoint), + } + proto.SetDefaults(packFishes) + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FISHERENTER), packFishes) + + if syncFish { + player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SCSYNCFISHCOIN), pack) + } +} + +// fishEvent 记录鱼触发的事件 +func (this *FishingSceneData) fishEvent(fish *Fish, player *FishingPlayerData, power int32, ts int64, extfishs []int32) []*Fish { + var deathFishs []*Fish + if fish.Event == 0 { + deathFishs = append(deathFishs, fish) + return deathFishs + } + + // 随机事件 + if fish.Event == fishing.Event_Rand { + fish.Event = common.RandInt32Slice([]int32{int32(fishing.Event_Booms), int32(fishing.Event_Boom), int32(fishing.Event_Ring)}) + } + + // 普通事件 + switch fish.Event { + case fishing.Event_Ring: + { + //冰冻鱼事件 后续做 + /* deathFishs = append(deathFishs, fish) + for _, value := range this.fish_list { + value.LiveTick += 100 + if value.BirthTick >= this.TimePoint { + value.BirthTick += 100 + } + } + this.NextTime += time.Second.Nanoseconds() * 10 + //冰冻鱼的冰冻时间 暂时设置为1秒 + this.frozenTick = 1 + pack := &fishing_proto.SCFreeze{SnId: proto.Int32(player.SnId), FishId: proto.Int32(fish.FishID)} + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FREEZE), pack, 0)*/ + } + case fishing.Event_Booms, fishing.Event_Boom, fishing.Event_Lightning, fishing.Event_Same, fishing.Event_TreasureChest: + { + fishlogger.Tracef("Event fish %v-%v.", fish.TemplateID, fish.Event) + fishlogger.Trace("Event ts:", ts) + sign := fmt.Sprintf("%v;%v;%v;%v;", fish.Event, fish.FishID, ts, player.SnId) + sign = common.MakeMd5String(sign) + player.fishEvent[sign] = &FishingPlayerEvent{ + FishId: fish.FishID, + Event: fish.Event, + Power: power, + DropCoin: fish.DropCoin, + Ts: ts, + ExtFishId: extfishs, + } + fishlogger.Trace("Event sign:", sign) + deathFishs = append(deathFishs, fish) + } + case fishing.Event_NewBoom, fishing.Event_Bit, fishing.Event_FreePower: + { + fishlogger.Tracef("Event fish %v-%v.", fish.TemplateID, fish.Event) + fishlogger.Trace("Event ts:", ts) + sign := fmt.Sprintf("%v;%v;%v;%v;", fish.Event, fish.FishID, ts, player.SnId) + sign = common.MakeMd5String(sign) + fishlogger.Infof("playerID %v ,sign %v , fishId %v , eventId %v", player.SnId, sign, fish.FishID, fish.Event) + player.fishEvent[sign] = &FishingPlayerEvent{ + FishId: fish.FishID, + Event: fish.Event, + Power: power, + Ts: ts, + ExtFishId: extfishs, + } + fishlogger.Trace("Event sign:", sign) + deathFishs = append(deathFishs, fish) + } + default: //一网打尽(同组的鱼) + { + fishlogger.Trace("Event fish:", fish) + eventFishs := this.fish_Event[fish.Event] + fishlogger.Trace("eventFishs:", eventFishs) + for _, fishId := range eventFishs { + fishLink := this.fish_list[fishId] + if fishLink == nil { + fishlogger.Trace("Event link fish is null:", fishId) + continue + } + if fishLink.IsDeath(this.TimePoint) { + fishlogger.Tracef("Event link fish is death:%v-%v", fishLink.FishID, fishId) + continue + } + if !fishLink.IsBirth(this.TimePoint) { + fishlogger.Tracef("Event link fish is not birth:%v-%v", fishLink.FishID, fishId) + continue + } + fishlogger.Trace("Event link fish:", fishLink) + fishlogger.Trace("Drop coin:", fishLink.DropCoin*power) + deathFishs = append(deathFishs, fishLink) + } + } + } + return deathFishs +} + +// PushEventFish 客户端触发鱼事件 +// sign 事件唯一标识 +// fishs 事件关联的鱼标识 +// eventFish 产生事件的鱼标识 +func (this *FishingSceneData) PushEventFish(player *FishingPlayerData, sign string, fishs []int32, eventFish int32) bool { + // 获取事件 + fishEvent := player.fishEvent[sign] // 根据 Sign 确定触发鱼本身的事件 + if fishEvent == nil { + fishlogger.Error("Recive event fish sign error.") + fishlogger.Trace("Event sign:", sign) + return false + } else { + fishlogger.Infof("PushEventFish fishEvent %v, %v, sign %v", fishEvent.Event, fishEvent.Ts, sign) + } + + // 接收 Fish 的 相关事件 太晚了 + var timeout time.Duration + if fishEvent.Event == fishing.Event_Bit { + timeout = 15 + } else { + timeout = 10 + } + if fishEvent.Ts < time.Now().Add(-time.Second*timeout).Unix() { + fishlogger.Error("Recive event fish list to late.") + fishlogger.Infof("Event ts: %v", fishEvent.Ts) + fishlogger.Infof("Event event:%v", fishEvent.Event) + delete(player.fishEvent, sign) // 消费掉对应的sign + return false + } + + fishlogger.Infof("PushEventFish(client): %v", fishs) + fishlogger.Infof("PushEventFish(srv): %v", fishEvent.ExtFishId) + + // selFishs 获取关联的所有鱼 + var selFishs []*Fish + // start 获取当前场景 中的 Fish 对象 + for _, id := range fishEvent.ExtFishId { //用碰撞时带上来的id,防作弊 + fish := this.fish_list[id] + if fish == nil || fish.IsDeath(this.TimePoint) { + continue + } + if fishEvent.Event == fishing.Event_Bit && (fish.FishType == 7 || fish.FishType == 8 || fish.FishType == 6) { + fishlogger.Infof("钻头贝事件,屏蔽的鱼 %v", fish.TemplateID) + continue + } + selFishs = append(selFishs, fish) + } + if fishEvent.Event == fishing.Event_Bit { + for _, id := range fishs { //用碰撞时带上来的id,防作弊 + fish := this.fish_list[id] + if fish == nil || fish.IsDeath(this.TimePoint) { + continue + } + if fish.FishType == 7 || fish.FishType == 8 || fish.FishType == 6 { + fishlogger.Infof("钻头贝事件,屏蔽的鱼 %v", fish.TemplateID) + continue + } + selFishs = append(selFishs, fish) + } + } + // end + if fishEvent.Event == fishing.Event_Bit { + if len(fishs) == 0 { + fishlogger.Errorf(" Event_Bit Event fish die all.") + delete(player.fishEvent, sign) // 消费掉对应的sign + this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, 0) + return false + } + } else { + if len(selFishs) == 0 { + fishlogger.Errorf("Event fish die all.") + delete(player.fishEvent, sign) // 消费掉对应的sign + this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, fishEvent.DropCoin) + return false + } + } + + if fishEvent.Event == fishing.Event_Bit && (len(selFishs) > 1) { + delete(player.fishEvent, sign) // 消费掉对应的sign + } else if fishEvent.Event != fishing.Event_Bit { + delete(player.fishEvent, sign) // 消费掉对应的sign + } + // start 根据不同的场景截取 结算的 Fish + if fishEvent.Event == fishing.Event_Boom && len(selFishs) > 15 { + selFishs = selFishs[:15] + } + if fishEvent.Event == fishing.Event_Lightning && len(selFishs) > 35 { + selFishs = selFishs[:35] + } + if fishEvent.Event == fishing.Event_Booms && len(selFishs) > 30 { + selFishs = selFishs[:30] + } + if fishEvent.Event == fishing.Event_Same && len(selFishs) > 30 { + selFishs = selFishs[:30] + } + // 新事件鱼代码处理相关 + if fishEvent.Event == fishing.Event_Bit && len(selFishs) > 50 { + selFishs = selFishs[:50] + } + if fishEvent.Event == fishing.Event_NewBoom && len(selFishs) > 50 { + selFishs = selFishs[:50] + } + // end + // 进行 鱼类结算 + if fishEvent.Event == fishing.Event_Bit { + if len(selFishs) == 1 { + } else { + this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, 0) + } + } else { + this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, fishEvent.DropCoin) + } + return true +} + +func (this *FishingSceneData) BroadCastMessage(packetid int, msg proto.Message, excludeSid int64) { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range this.players { + if p == nil || p.GetGateSess() == nil { + continue + } + if !p.IsOnLine() || p.IsMarkFlag(base.PlayerState_Leave) { + continue + } + if p.GetSid() == excludeSid { + continue + } + mgs[p.GetGateSess()] = append(mgs[p.GetGateSess()], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.GetSid()), + }, + }) + } + audiences := this.GetAudiences() + for _, p := range audiences { + if p == nil || p.GetGateSess() == nil { + continue + } + if !p.IsOnLine() || p.IsMarkFlag(base.PlayerState_Leave) { + continue + } + if p.GetSid() == excludeSid { + continue + } + mgs[p.GetGateSess()] = append(mgs[p.GetGateSess()], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: proto.Int64(p.GetSid()), + }, + }) + } + for gateSess, v := range mgs { + if gateSess == nil || len(v) == 0 { + continue + } + pack, err := base.MulticastMaker.CreateMulticastPacket(packetid, msg, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } +} + +func (this *FishingSceneData) syncFishCoin(fish *Fish) { + pack := &fishing_proto.SCSyncFishCoin{ + FishId: proto.Int32(fish.FishID), + Coin: proto.Int64(int64(fish.DropCoin)), + } + proto.SetDefaults(pack) + this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_SCSYNCFISHCOIN), pack, 0) +} + +// GetDraw 获取金币池抽水百分比 +func (this *FishingSceneData) GetDraw() float64 { + draw := this.GetDBGameFree().GetDraw() + return float64(draw / 100.0) +} + +// GetBossDrainageBet 获取BOSS池抽水百分比 +func (this *FishingSceneData) GetBossDrainageBet() float64 { + bossDraw := this.GetDBGameFree().GetBossDrainageBet() + return float64(bossDraw / 100.0) +} + +// 获取公共池抽水百分比 +func (this *FishingSceneData) GetPublicPondRatio() float64 { + ratio := this.GetDBGameFree().GetRatio() + return float64(ratio / 1000.0) +} + +// 增加boss池 +func (this *FishingSceneData) AddBossPond(player *FishingPlayerData, fishtype int32, bulletM int32) int64 { + bossPond := int64(0) + + // boss抽水比例 + bossRatio := this.GetBossDrainageBet() + score := int64(float64(bulletM) * bossRatio) + // 打食人鱼BOSS + if fishtype == fishing.SUNWUKONG { + //bossPond = publicPandService.AddPiranhaBossPond(TableConfig.GetInc().GetServiceId(), tableInfo.TableId, score) + } else { + fishlogger.Infof("玩家:%v, 鱼的ID为:%v, Boss抽水比例:%v, Boss池增加的数值:%v\n", player.SnId, fishtype, bossRatio, score) + // 减掉个人池数值 + if score > 0 { + player.MoneyPond -= score + base.GetCoinPoolMgr().AddBossPond(this.DbGameFree.SceneType, score) + } + } + + return bossPond +} + +// Boss死亡 更新各种池 +func (this *FishingSceneData) isBossDie(player *FishingPlayerData, score int64, bossPond int64) { + minNum := score + if minNum > bossPond { + minNum = bossPond + } + player.MoneyPond += minNum + base.GetCoinPoolMgr().AddBossPond(this.DbGameFree.SceneType, -minNum) + fishlogger.Infof("玩家:%v,Boss奖池剩余金币数量:%v\n", player.SnId, bossPond) +} + +// PathUsable 路径可用 +func (this *FishingSceneData) PathUsable(path int32) bool { + // 加锁管理 + /* this.fishsMutex.Lock() + defer this.fishsMutex.Unlock() + // 当前时间 + now := time.Now().Unix() + // 遍历鱼 + for _, v := range this.fish_list { + if v.Path == path { + //相同路径 比较出鱼时间 小于2秒 不让用 + if now-v.InitTime <= 2 { + return false + } + } + }*/ + return true +} + +// 暂停出鱼 +func (this *FishingSceneData) Freeze(sec int64) { + this.af.Pause(sec * 1000) + // 增加普通出鱼生命周期 + offset := int32(sec * 1000 / 100) + for _, f := range this.fish_list { + f.AddTime(offset) + } +} + +// 停止出鱼 +func (this *FishingSceneData) Stop() bool { + //停止出鱼 + this.af.Stop() + return true +} diff --git a/gamesrv/fishing/scenedata_fishing_fish.go b/gamesrv/fishing/scenedata_fishing_fish.go new file mode 100644 index 0000000..45e439d --- /dev/null +++ b/gamesrv/fishing/scenedata_fishing_fish.go @@ -0,0 +1,74 @@ +package fishing + +import ( + "math" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core" + "time" +) + +type Fish struct { + FishID int32 // 唯一标识 PolicyId*1000000 + int32(value.GetId())*100 + int32(index+1) + TemplateID int32 // 鱼id + DropCoin int32 // 掉落倍率 + MaxDropCoin int32 //最大掉落倍率 + Path int32 // 鱼路径id + BirthTick int32 // 出生帧 + LiveTick int32 // 存活帧(最多活到第几帧) + Event int32 // 事件标签,用于记录当前 鱼所触发的状态 + InitTime int64 // 创建时间 + Death bool // 是否死亡 + Speed int32 // 速度修正参数 + IsBoss int32 // 是否boss + IsJackpot bool //(天天捕鱼) + DealRate int32 //(天天捕鱼) + FishType int32 // 当前鱼是属于 第几类鱼 目前一共有 8 类(天天捕鱼) + Rate int32 //鱼死亡概率 + Name string //名字 + Gold int32 //金币 + Exp int32 //经验 + Child []int32 //孩子 +} + +func (this *Fish) IsBirth(timePoint int32) bool { + return this.BirthTick <= timePoint && timePoint <= this.LiveTick +} +func (this *Fish) SetDeath() { + this.Death = true + this.LiveTick = 0 +} +func (this *Fish) IsDeath(timePoint int32) bool { + if this.Death { + return true + } + //return this.BirthTick > timePoint && timePoint > this.LiveTick + return this.BirthTick < timePoint && timePoint > this.LiveTick +} + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.FishTemplateEx.Reload() + base.FishOutEx.InitFishAppear() + return nil + }) +} + +// AddTime 增加帧 +func (this *Fish) AddTime(offset int32) { + this.BirthTick += offset + this.LiveTick += offset + fishlogger.Tracef("冰冻-暂停出鱼,增加鱼的补偿帧 offset = %v,fishType = %v,当前鱼的出生帧:%v,结束帧:%v", offset, this.FishType, this.BirthTick, this.LiveTick) +} +func (this *Fish) InitCrashDetect(offset int, path *FishPath) { + // 修正 串串鱼需要补偿时间 暂时不需要 + //if f.appear != AppearFishString { + // offset = 0 + //} + // 初始化 + this.InitTime = time.Now().Unix() + this.LiveTick = int32(math.Ceil(float64(path.DisappearTime*100)/float64(this.Speed) + float64(offset))) + + // 修正消失时间 + this.LiveTick += ExtraAppearTime + fishlogger.Infof("初始化碰撞检测 修正后的鱼的出现帧:%v,鱼的消失帧:%v", this.BirthTick, this.LiveTick) +} diff --git a/gamesrv/fishing/scenedata_fishing_path.go b/gamesrv/fishing/scenedata_fishing_path.go new file mode 100644 index 0000000..c92f54b --- /dev/null +++ b/gamesrv/fishing/scenedata_fishing_path.go @@ -0,0 +1,536 @@ +package fishing + +import ( + "math/rand" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/srvdata" + "sync" + "time" +) + +// FishArray 鱼阵 +type FishArray struct { + Type uint32 //鱼阵类型 + Speed uint32 //鱼阵速度 + Duration uint32 //持续时间 + Fishs []*Fish //鱼阵中鱼 + + //Actions []*FishArrayAction //鱼阵动作 +} + +// AppearFish 出鱼 +type AppearFish struct { + id uint64 //出鱼编号 + aid uint32 //鱼阵编号 + //mutex sync.Mutex //编号同步 + start bool //是否出鱼 + pause int64 //暂停时间 + resumeTimer *time.Timer //恢复定时器 + SceneEx *FishingSceneData + appearFishs map[int32]*AppearFishConfig //出鱼策略 + fishPath map[int32]*FishPath //出鱼路径配置 + mutex sync.Mutex //编号同步 +} + +type FishPath struct { + PathId int32 + AppearTime int32 + DisappearTime int32 +} + +type AppearFishConfig struct { + fishId int32 //对应fish表中的fishType + name string //鱼的名字 + exp int32 //经验值 + multiple []int32 //倍数 + prob float64 //死亡概率 目前不用 看后期设计 每个场次单独控制 还是整体控制 + baseRates float64 //基础概率 目前不用 + path []int32 //出鱼路径 + fishNum []int32 //出鱼数量 + interval int32 //出鱼间隔 + +} + +// Start 开始 +func (a *AppearFish) Start() { + fishlogger.Trace("newFish 开始出鱼!!!!!!!!!!!!") + a.aid = FishArrayOne //鱼阵起始标记 + a.start = true + //最短间隔 + minInterval := int32(100000) + for _, v := range base.FishOutEx.FishOutPool { + if a.SceneEx.GetDBGameFree().GetSceneType() == v.SceneType && v.Interval < minInterval { + minInterval = v.Interval + } + } + + //间隔差值 开始出鱼时候出鱼比较慢 让玩家早点看到屏幕鱼 + var diffInterval int32 + if minInterval > 1000 { + diffInterval = minInterval - 1000 + } + + //遍历出鱼 + for _, v := range base.FishOutEx.FishOutPool { + if a.SceneEx.GetDBGameFree().GetSceneType() == v.SceneType { + tmp := v + //定时出鱼 + time.AfterFunc(time.Duration(tmp.Interval-diffInterval)*time.Millisecond, func() { + a.do(tmp) + }) + } + } + /* //定时鱼阵 + time.AfterFunc(time.Duration(FishArrayInterval)*time.Millisecond, func() { + a.doArray() + })*/ + + //定时切换场景 + time.AfterFunc(FishArrayInterval, func() { + a.cleanData() + }) +} + +// Stop 停止 +func (a *AppearFish) Stop() { + a.start = false +} + +// Pause 暂停 +func (a *AppearFish) Pause(t int64) { + //暂停时间 + a.pause = time.Now().Unix() + a.pause += (t / 1000) + + //定时暂停 + if a.resumeTimer != nil { + a.resumeTimer.Stop() + } + a.resumeTimer = time.AfterFunc(time.Duration(t)*time.Millisecond, func() { + a.resume() + }) +} + +// resume 恢复 +func (a *AppearFish) resume() { + a.pause = 0 + fishlogger.Trace("暂停结束,恢复出鱼!") +} + +// do 执行 +func (a *AppearFish) do(afc *base.AppearFish) { + if !a.start { + return + } + startTime := time.Now().UnixNano() / int64(time.Millisecond) + //定时出鱼 + if a.pause != 0 { + //不能出就不出不延迟至鱼阵结束 + time.AfterFunc(time.Duration(afc.Interval)*time.Millisecond, func() { + a.do(afc) + }) + + return + } + + //出鱼数量 + quantity := afc.FishNum[0] + if afc.FishNum[0] != afc.FishNum[1] { + //随机出鱼数量 + quantity += rand.Int31n(int32(afc.FishNum[1] - afc.FishNum[0] + 1)) + } + //创建出鱼切片 + fishs := make([]*Fish, 0, quantity) + + for i := 0; i < int(quantity); i++ { + //遍历鱼类 + v := afc.FishId + + //获取鱼的配置 + //conf := a.fishConfig[afc.fishId] + conf := base.FishTemplateEx.FishPool[afc.FishId] + if conf == nil { + fishlogger.Errorf("出鱼 没有找到鱼的配置!!!!fishId = %v", afc.FishId) + continue + } + + //出鱼路径 出多条一样的鱼 路径尽量不重复 + pathTime := time.Now().UnixNano() / int64(time.Millisecond) + path := afc.Path[0] + var num = 0 + for { + num++ + if afc.Path[0] != afc.Path[1] { + path += rand.Int31n(int32(afc.Path[1] - afc.Path[0] + 1)) + } + + if a.PathUsable(path, fishs) && a.SceneEx.PathUsable(path) { + break + } + + path = afc.Path[0] + } + fishlogger.Tracef("寻找路径耗时:%v,循环了多少次:%v", time.Now().UnixNano()/int64(time.Millisecond)-pathTime, num) + //创建出鱼 + fish := new(Fish) + + //设置属性 + fish.TemplateID = v + fish.Path = path + fish.Exp = conf.EXP + //fish.SetrAppea(afc.FishAppearType) + + //计算倍数 待确认怎么随机 + //策划让直接用鱼谱里的GOLD值 + multiple := conf.Gold[0] + if conf.Gold[0] != conf.Gold[1] { + multiple += rand.Int31n(conf.Gold[1] - conf.Gold[0] + 1) + } + + //设置属性 + fish.DropCoin = multiple + //这个参数是用于财神鱼 要不要待定 + fish.MaxDropCoin = conf.Gold[1] + fish.Speed = conf.Speed + fish.Rate = conf.Rate + fish.Name = conf.Name + //设置出鱼编号 + fish.FishID = int32(a.GetFishID()) + fish.Event = afc.Event + //不指定能打死自己的玩家 + //fish.SetPlayer(InvalidPlayer) + fishlogger.Infof("newfish 出鱼 fish = %v", fish) + //加入切片 + fishs = append(fishs, fish) + } + //桌子增加鱼 + a.SceneEx.AddFish(fishs, 0) + fishlogger.Trace("出鱼耗时:", time.Now().UnixNano()/int64(time.Millisecond)-startTime) + //定时出鱼 + time.AfterFunc(time.Duration(afc.Interval)*time.Millisecond, func() { + a.do(afc) + }) + //通知客户端出鱼 + a.SceneEx.NotifyAppearFish(fishs) + +} + +func (a *AppearFish) doPreArray() { + if !a.start { + return + } + //通知客户端 鱼潮来临 + //a.pfacb() +} + +// 鱼阵 doArray 执行 +func (a *AppearFish) doArray() { + /* + if !a.start { + return + } + + //定时鱼阵 + if a.pause != 0 { + time.AfterFunc(time.Duration(a.GetPauseTime())*time.Second, func() { + a.doArray() + }) + + return + } + + //最大持续时间 + var maxDuration uint32 + + //创建鱼阵切片 + var fishArrays []FishArray + + //子鱼阵编号 + ids := CMIns().FishArrayConf[a.aid-1] + a.aid = a.aid%FishArraySeven + 1 + + //遍历子鱼阵 + for _, v := range ids { + //创建鱼阵 + var fishArray FishArray + + //获取鱼阵配置 + fishArrayConfig := CMIns().GetFishArrayConfig(v) + + //遍历鱼阵组 + for _, i := range fishArrayConfig.FishArrayGroups { + //获取鱼类配置 + conf := CMIns().GetFishConfig(i.FishType) + if conf == nil { + continue + } + + //遍历鱼阵鱼 + for _, j := range i.FishArrayFishs { + //创建出鱼 + fish := new(Fish) + + //计算倍数 + multiple := conf.FishMinMultiple + if conf.FishMinMultiple != conf.FishMaxMultiple { + multiple += uint32(rand.Int31n(int32(conf.FishMaxMultiple - conf.FishMinMultiple + 1))) + } + + //设置属性 + fish.SetType(i.FishType) + fish.SetMultiple(multiple) + fish.SetProbability(conf.FishProbability) + fish.SetLotteryProbability(conf.LotteryProbability) + fish.SetName(conf.FishName) + fish.SetAngle(j.FishAngle) + fish.SetX(j.FishX) + fish.SetY(j.FishY) + + //设置出鱼编号 + fish.SetID(a.GetFishID()) + + //不指定能打死自己的玩家 + fish.SetPlayer(InvalidPlayer) + + //加入切片 + fishArray.Fishs = append(fishArray.Fishs, fish) + } + } + + //设置鱼阵属性 + fishArray.Type = fishArrayConfig.FishArrayType + fishArray.Speed = fishArrayConfig.FishArraySpeed + fishArray.Duration = fishArrayConfig.FishArrayDuration + fishArray.Actions = fishArrayConfig.FishArrayActions + + //最高持续时间 + if maxDuration < fishArrayConfig.FishArrayDuration { + maxDuration = fishArrayConfig.FishArrayDuration + } + + //加入鱼阵切片 + fishArrays = append(fishArrays, fishArray) + } + + //清空鱼 + a.table.ClearFish() + + //暂停出鱼 + a.Pause(int64(maxDuration + 5000)) + + //增加鱼阵 + a.table.AddFishArray(fishArrays) + + //定时鱼阵 + time.AfterFunc(time.Duration(CMIns().FishArrayInterval)*time.Millisecond, func() { + a.doArray() + }) + //time.AfterFunc(time.Duration(CMIns().FishArrayInterval-15)*time.Millisecond, func() { + // a.doPreArray() + //}) + + //设置标识 + a.table.SetFishArray(true) + + //鱼阵回调 //通知客户端 + a.facb(0, fishArrays) + */ + +} + +/*// DeadFishArray 死鱼鱼阵 +func (a *AppearFish) DeadFishArray(tp uint32, fid uint64) { + //获取配置 + conf := CMIns().GetFishConfig(tp) + if conf == nil { + return + } + + //校验鱼类 + if conf.DeadFish == 0 { + return + } + + //确定鱼类 + fishType := conf.DeadFish + redFishType := conf.DeadRedFish + + //获取鱼类配置 + fishconfig := CMIns().GetFishConfig(fishType) + if fishconfig == nil { + return + } + + //创建鱼阵切片 + var fishArrays []FishArray + + //随机红鱼位置 + index := int(rand.Int31n(int32(NumberPreCircle))) + + //获取红鱼配置 + redFishConfig := CMIns().GetFishConfig(redFishType) + + //发散三圈 + for i := 0; i < CircleNumber; i++ { + //创建鱼阵 + var fishArray FishArray + for j := 0; j < NumberPreCircle; j++ { + //创建出鱼 + fish := new(Fish) + + //红鱼属性 + if i == 2 && j == index && redFishConfig != nil { + //计算倍数 + multiple := redFishConfig.FishMinMultiple + if redFishConfig.FishMinMultiple != redFishConfig.FishMaxMultiple { + multiple += uint32(rand.Int31n(int32(redFishConfig.FishMaxMultiple - redFishConfig.FishMinMultiple + 1))) + } + + //设置属性 + fish.SetType(redFishType) + fish.SetMultiple(multiple) + fish.SetProbability(redFishConfig.FishProbability) + fish.SetLotteryProbability(redFishConfig.LotteryProbability) + fish.SetName(redFishConfig.FishName) + } else { + //计算倍数 + multiple := fishconfig.FishMinMultiple + if fishconfig.FishMinMultiple != fishconfig.FishMaxMultiple { + multiple += uint32(rand.Int31n(int32(fishconfig.FishMaxMultiple - fishconfig.FishMinMultiple + 1))) + } + + //设置属性 + fish.SetType(fishType) + fish.SetMultiple(multiple) + fish.SetProbability(fishconfig.FishProbability) + fish.SetLotteryProbability(fishconfig.LotteryProbability) + fish.SetName(fishconfig.FishName) + } + + fish.SetAngle((float32(360) / float32(NumberPreCircle)) * float32(j)) + + //设置出鱼编号 + fish.SetID(a.GetFishID()) + + //不指定能打死自己的玩家 + fish.SetPlayer(InvalidPlayer) + + //加入切片 + fishArray.Fishs = append(fishArray.Fishs, fish) + } + + //设置鱼阵属性 + fishArray.Type = FishArrayDisperse + fishArray.Speed = CMIns().FishArrays[17+i].FishArraySpeed + fishArray.Duration = CMIns().FishArrays[17+i].FishArrayDuration + //fishArray.Actions = CMIns().FishArrays[17+i].FishArrayActions + + //加快死鱼鱼阵出圈速度 + fishArrayAction := &FishArrayAction{ + Path: CMIns().FishArrays[17+i].FishArrayActions[0].Path, + Time: CMIns().FishArrays[17+i].FishArrayActions[0].Time / 2, + } + + fishArray.Actions = append(fishArray.Actions, fishArrayAction) + fishArray.Actions = append(fishArray.Actions, CMIns().FishArrays[17+i].FishArrayActions[1]) + + //加入鱼阵切片 + fishArrays = append(fishArrays, fishArray) + } + + //增加鱼阵 + a.table.AddFishArray(fishArrays) + + //通知客户端鱼阵消息 + a.facb(fid, fishArrays) +} +*/ +//==================== +//simple interface +//==================== + +// GetID 出鱼编号 +func (a *AppearFish) GetID() uint64 { + return a.id +} + +// GetAID 鱼阵编号 +func (a *AppearFish) GetAID() uint32 { + return a.aid +} + +// IsStart 是否开始 +func (a *AppearFish) IsStart() bool { + return a.start +} + +// IsPause 是否暂停 +func (a *AppearFish) IsPause() bool { + return a.pause != 0 +} + +// GetFishID 获取出鱼编号 +func (a *AppearFish) GetFishID() uint64 { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.id++ + return a.id +} + +// GetPauseTime 获取暂停时间 +func (a *AppearFish) GetPauseTime() int64 { + surplus := a.pause - time.Now().Unix() + if surplus <= 0 { + surplus = 1 + } + + return surplus +} + +// PathUsable 路径可用 +func (a *AppearFish) PathUsable(path int32, fishs []*Fish) bool { + for _, v := range fishs { + if v.Path == path { + return false + } + } + + return true +} + +// 鱼潮策略初始化 +func (a *AppearFish) InitFishArray() { + //后续再做 +} + +// 初始化鱼的路径 +func (a *AppearFish) InitFishPath() { + a.fishPath = make(map[int32]*FishPath) + for _, v := range srvdata.PBDB_FishPathMgr.Datas.Arr { + a.fishPath[v.Id] = &FishPath{ + PathId: v.Id, + AppearTime: v.AppearTime, + DisappearTime: v.DisappearTime, + } + } +} + +// 清理数据 +func (a *AppearFish) cleanData() { + //重置出鱼编号 + a.id = 0 + //重置场景里的鱼 + a.SceneEx.fish_list = make(map[int32]*Fish) // 鱼标识 存活的鱼 + //重置逻辑帧 + a.SceneEx.TimePoint = 0 + fishlogger.Trace("定时切换场景,清理数据!!!!!!!!") + //通知客户端切换场景 + a.SceneEx.NotifyChangeScene() + //定时切换场景 + time.AfterFunc(FishArrayInterval, func() { + if a.start { + a.cleanData() + } + }) +} diff --git a/gamesrv/fishing/scenepolicy_fishing.go b/gamesrv/fishing/scenepolicy_fishing.go new file mode 100644 index 0000000..2120f18 --- /dev/null +++ b/gamesrv/fishing/scenepolicy_fishing.go @@ -0,0 +1,771 @@ +package fishing + +import ( + "math" + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fishing" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + fishing_proto "mongo.games.com/game/protocol/fishing" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/timer" + "time" +) + +var ScenePolicyFishingSington = &ScenePolicyFishing{} + +type ScenePolicyFishing struct { + base.BaseScenePolicy + states [FishingSceneStateMax]base.SceneState +} + +func (this *ScenePolicyFishing) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewFishingSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.SetExtraData(sceneEx) + } + } + return sceneEx +} + +func (this *ScenePolicyFishing) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &FishingPlayerData{Player: p} + if playerEx != nil { + p.SetExtraData(playerEx) + } + return playerEx +} + +func (this *ScenePolicyFishing) OnStart(s *base.Scene) { + fishlogger.Trace("(this *ScenePolicyFishing) OnStart, sceneId=", s.GetSceneId()) + sceneEx := NewFishingSceneData(s) // 初始化当前场景的数据接口 + if sceneEx != nil { + if sceneEx.init() { + s.SetExtraData(sceneEx) + s.ChangeSceneState(FishingSceneStateStart) + } + } +} + +func (this *ScenePolicyFishing) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnTick(s) + } +} + +func (this *ScenePolicyFishing) GetPlayerNum(s *base.Scene) int32 { + if s == nil { + return 0 + } + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + return int32(len(sceneEx.players)) + } + return 0 +} + +func (this *ScenePolicyFishing) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + fishlogger.Trace("(this *ScenePolicyFishing) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + sceneEx.SetGameNowTime(time.Now()) + playerEx := &FishingPlayerData{Player: p} + playerEx.init(s) + playerEx.taxCoin = 0 + playerEx.sTaxCoin = 0 + playerEx.winCoin = 0 + playerEx.enterTime = time.Now() + sceneEx.EnterPlayer(playerEx) + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } +} + +func (this *ScenePolicyFishing) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + fishlogger.Trace("(this *ScenePolicyFishing) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + if this.CanChangeCoinScene(s, p) { + if playerEx, ok := p.GetExtraData().(*FishingPlayerData); ok { + playerEx.SaveDetailedLog(s) + sceneEx.QuitPlayer(playerEx, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, []int64{int64(reason)}) + if playerEx.GetCurrentCoin() == 0 { + playerEx.SetCurrentCoin(playerEx.GetTakeCoin()) + } + playerEx.SaveSceneCoinLog(playerEx.GetCurrentCoin(), int64(playerEx.CoinCache-playerEx.GetCurrentCoin()), + playerEx.GetCoin(), 0, int64(math.Floor(playerEx.taxCoin+0.5)), playerEx.winCoin, 0, 0) + playerEx.SetCurrentCoin(playerEx.GetCoin()) + } + } + //检测房间人数 + if len(sceneEx.players) == 0 { + sceneEx.Stop() + } + } +} + +func (this *ScenePolicyFishing) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + fishlogger.Trace("(this *ScenePolicyFishing) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.Name) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) + if _, ok := s.GetExtraData().(*FishingSceneData); ok { + if _, ok := p.GetExtraData().(*FishingPlayerData); ok { + } + } +} + +func (this *ScenePolicyFishing) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + fishlogger.Trace("(this *ScenePolicyFishing) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if _, ok := s.GetExtraData().(*FishingSceneData); ok { + if _, ok := p.GetExtraData().(*FishingPlayerData); ok { + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +func (this *ScenePolicyFishing) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + fishlogger.Trace("(this *ScenePolicyFishing) OnPlayerReturn, sceneId=", s.GetSceneId(), " player=", p.SnId) + if _, ok := s.GetExtraData().(*FishingSceneData); ok { + if _, ok := p.GetExtraData().(*FishingPlayerData); ok { + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func (this *ScenePolicyFishing) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().OnPlayerOp(s, p, opcode, params) + } + return true +} + +func (this *ScenePolicyFishing) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnPlayerEvent(s, p, evtcode, params) + } +} + +func (this *ScenePolicyFishing) IsCompleted(s *base.Scene) bool { return false } +func (this *ScenePolicyFishing) IsCanForceStart(s *base.Scene) bool { return true } +func (this *ScenePolicyFishing) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return true + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + return true +} + +func FishingSendRoomInfo(p *base.Player, sceneEx *FishingSceneData) { + pack := &fishing_proto.SCFishingRoomInfo{ + RoomId: proto.Int(sceneEx.GetSceneId()), + Creator: proto.Int32(sceneEx.GetCreator()), + GameId: proto.Int(sceneEx.gameId), + RoomMode: proto.Int(sceneEx.sceneMode), + AgentId: proto.Int32(sceneEx.agentor), + SceneType: proto.Int(sceneEx.sceneType), + Params: sceneEx.GetParams(), + NumOfGames: proto.Int(sceneEx.GetNumOfGames()), + State: proto.Int(sceneEx.GetSceneState().GetState()), + TimeOut: proto.Int(sceneEx.GetSceneState().GetTimeout(sceneEx.Scene)), + DisbandGen: proto.Int(sceneEx.GetDisbandGen()), + GameFreeId: proto.Int32(sceneEx.gamefreeId), + FrozenTick: proto.Int32(sceneEx.frozenTick), + MinPower: proto.Int32(sceneEx.GetDBGameFree().GetIntuseCannonMin()), + MaxPower: proto.Int32(sceneEx.GetDBGameFree().GetIntuseCannonMax()), + PowerList: sceneEx.GetDBGameFree().GetOtherIntParams(), + ChangeSceneId: proto.Int32(sceneEx.ChangeSceneId), + } + for _, pp := range sceneEx.players { + pd := &fishing_proto.FishingPlayerData{ + SnId: proto.Int32(pp.SnId), + Name: proto.String(pp.Name), + Head: proto.Int32(pp.Head), + Sex: proto.Int32(pp.Sex), + Coin: proto.Int64(pp.CoinCache), + Pos: proto.Int(pp.GetPos()), + Flag: proto.Int(pp.GetFlag()), + City: proto.String(pp.GetCity()), + Params: proto.String(pp.Params), + HeadOutLine: proto.Int32(pp.HeadOutLine), + VIP: proto.Int32(pp.VIP), + SelVip: proto.Int32(pp.SelVip), + Power: proto.Int32(pp.power), + AgentParam: proto.Int32(pp.AgentParam), + TargetSel: proto.Int32(pp.SelTarget), + AutoFishing: proto.Int32(pp.AutoFishing), + FireRate: proto.Int32(pp.FireRate), + IsRobot: proto.Bool(pp.IsRob), + NiceId: proto.Int32(pp.NiceId), + UnMaxPower: proto.Int64(pp.UnMaxPower), + UnPowerList: pp.PowerList, + FishLevel: proto.Int64(pp.FishLevel), + FishExp: proto.Int64(pp.FishExp), + } + for _, v := range pp.RobotSnIds { + pd.RobotSnIds = append(pd.RobotSnIds, v) + } + pack.Players = append(pack.Players, pd) + } + proto.SetDefaults(pack) + p.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_ROOMINFO), pack) +} + +type SceneBaseStateFishing struct { + base.BaseScenePolicy +} + +func (this *SceneBaseStateFishing) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + return int(time.Now().Sub(sceneEx.GetStateStartTime()) / time.Second) + } + return 0 +} + +func (this *SceneBaseStateFishing) CanChangeTo(s base.SceneState) bool { + return true +} + +func (this *SceneBaseStateFishing) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if _, ok := p.GetExtraData().(*FishingPlayerData); ok { + return true + } + return true +} + +func (this *SceneBaseStateFishing) OnEnter(s *base.Scene) { + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + sceneEx.SetStateStartTime(time.Now()) + } +} + +func (this *SceneBaseStateFishing) OnLeave(s *base.Scene) {} +func (this *SceneBaseStateFishing) OnTick(s *base.Scene) { + //玩家超时5分钟未发炮 踢出房间 + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + for _, p := range sceneEx.players { + if time.Now().Sub(p.LastFireTime) > PlayerLongTimeNoOp { + sceneEx.PlayerLeave(p.Player, common.PlayerLeaveReason_LongTimeNoOp, true) + fishlogger.Tracef("玩家超过5分钟未开炮,踢出房间!snid = %v", p.SnId) + } + + } + } + +} + +func (this *SceneBaseStateFishing) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if playerEx, ok := p.GetExtraData().(*FishingPlayerData); ok { + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + // start 检测当前房间是否处于基本的关闭状态 + if s.CheckNeedDestroy() { + if sceneEx.hDestroy == timer.InvalidTimerHandle { + if hNext, ok := common.DelayInvake(func() { + sceneEx.hDestroy = timer.InvalidTimerHandle + sceneEx.SceneDestroy(true) + }, nil, time.Second*10, 1); ok { + sceneEx.hDestroy = hNext + } + } + // + if opcode == FishingPlayerOpFire || opcode == FishingPlayerOpRobotFire { //房间开始关闭,拦截掉发炮数据 + return true + } + } + // end + switch opcode { + case FishingPlayerOpFire: + // 真人玩家的开火倍数 + if len(params) >= 4 { + opRetCode := sceneEx.PushBullet(playerEx.SnId, int32(params[0]), int32(params[1]), + int32(params[2]), int32(params[3]), int32(params[4]), int32(params[5])) + // start 异常处理的时候 发统一的 处理逻辑 + if opRetCode == fishing_proto.OpResultCode_OPRC_CoinNotEnough { + this.OnPlayerSToCOp(s, p, opcode, opRetCode, params) + } + // end + } + case FishingPlayerOpRobotFire: + if len(params) >= 5 { + robotPlayer := sceneEx.GetPlayer(int32(params[0])) + if robotPlayer != nil && robotPlayer.IsRob == true && p.IsRob == false { + opRetCode := sceneEx.PushBullet(robotPlayer.SnId, int32(params[1]), int32(params[2]), + int32(params[3]), int32(params[4]), int32(params[4]), int32(params[5])) + if opRetCode == fishing_proto.OpResultCode_OPRC_CoinNotEnough { + //sceneEx.PlayerLeave(robotPlayer, common.PlayerLeaveReason_Bekickout, true) + if !robotPlayer.IsMarkFlag(base.PlayerState_GameBreak) { + robotPlayer.MarkFlag(base.PlayerState_GameBreak) + sceneEx.SCRobotBehavior(p.SnId, robotPlayer.SnId, FishingRobotBehaviorCode_StopFire) + this.OnPlayerSToCOp(s, robotPlayer, opcode, opRetCode, params) + } + } + if robotPlayerEx, ok := robotPlayer.GetExtraData().(*FishingPlayerData); ok { + fishlogger.Tracef("playerId snid %v robotPlayer sid %v snid %v coin %v name %v leaveCoin %v ", p.SnId, robotPlayer.GetSid(), robotPlayer.SnId, robotPlayerEx.CoinCache, robotPlayer.Name, robotPlayer.ExpectLeaveCoin) + if robotPlayerEx.CoinCheck(robotPlayerEx.power) == false { + //sceneEx.PlayerLeave(robotPlayerEx.Player, common.PlayerLeaveReason_Bekickout, true) + fishlogger.Tracef("robotId sid %v snid %v CoinCheck ", robotPlayer.GetSid(), robotPlayerEx.SnId) + if !robotPlayer.IsMarkFlag(base.PlayerState_GameBreak) { + robotPlayer.MarkFlag(base.PlayerState_GameBreak) + sceneEx.SCRobotBehavior(p.SnId, robotPlayer.SnId, FishingRobotBehaviorCode_StopFire) + this.OnPlayerSToCOp(s, robotPlayer, opcode, opRetCode, params) + } + } + if !s.CoinInLimit(robotPlayerEx.CoinCache) { + //sceneEx.PlayerLeave(robotPlayerEx.Player, common.PlayerLeaveReason_Bekickout, true) + fishlogger.Tracef("robotId sid %v snid %v CoinInLimit ", robotPlayer.GetSid(), robotPlayerEx.SnId) + if !robotPlayer.IsMarkFlag(base.PlayerState_GameBreak) { + robotPlayer.MarkFlag(base.PlayerState_GameBreak) + sceneEx.SCRobotBehavior(p.SnId, robotPlayer.SnId, FishingRobotBehaviorCode_StopFire) + this.OnPlayerSToCOp(s, robotPlayer, opcode, opRetCode, params) + } + } + if s.CoinOverMaxLimit(robotPlayerEx.CoinCache, robotPlayer) { + //sceneEx.PlayerLeave(robotPlayerEx.Player, common.PlayerLeaveReason_Normal, true) + fishlogger.Tracef("robotId sid %v snid %v CoinOverMaxLimit ", robotPlayer.GetSid(), robotPlayerEx.SnId) + if !robotPlayer.IsMarkFlag(base.PlayerState_GameBreak) { + robotPlayer.MarkFlag(base.PlayerState_GameBreak) + sceneEx.SCRobotBehavior(p.SnId, robotPlayer.SnId, FishingRobotBehaviorCode_StopFire) + this.OnPlayerSToCOp(s, robotPlayer, opcode, opRetCode, params) + } + } + } + } + } + case FishingPlayerOpHitFish: + if len(params) >= 2 { + fishId := []int32{int32(params[1])} + extfishis := []int32{} + for i := 2; i < len(params); i++ { + extfishis = append(extfishis, int32(params[i])) + } + sceneEx.PushBattle(playerEx, int32(params[0]), fishId, extfishis) + } + case FishingPlayerOpRobotHitFish: + if len(params) >= 3 { + robotPlayer := sceneEx.GetPlayer(int32(params[0])) + if robotPlayer != nil && robotPlayer.IsRob == true && p.IsRob == false { + fishId := []int32{int32(params[2])} + extfishis := []int32{} + for i := 3; i < len(params); i++ { + extfishis = append(extfishis, int32(params[i])) + } + if robotPlayerEx, ok := robotPlayer.GetExtraData().(*FishingPlayerData); ok { + sceneEx.PushBattle(robotPlayerEx, int32(params[1]), fishId, extfishis) + } + } + } + case FishingPlayerOpSetPower: + if len(params) >= 1 { + fishlogger.Tracef("%v set power to %v.", playerEx.SnId, params[0]) + powers := sceneEx.GetDBGameFree().GetOtherIntParams() // power的配置信息 + // 当前如果是免费炮时期,后端直接屏蔽协议 + if playerEx.powerType == FreePowerType { + fishlogger.Tracef("player %v 正处于免费炮阶段.", playerEx.SnId) + return true + } + int32Slice := make([]int32, len(powers)) + for i, v := range powers { + int32Slice[i] = int32(v) + } + if common.InSliceInt32(int32Slice, int32(params[0])) { + playerEx.power = int32(params[0]) + } else { + fishlogger.Errorf("Can't not switch power to %v.", params[0]) + fishlogger.Errorf("%v scene power list:%v.", sceneEx.gamefreeId, powers) + } + pack := &fishing_proto.SCFirePower{ + Snid: proto.Int32(playerEx.SnId), + Power: proto.Int32(playerEx.power), + } + + if playerEx.IsRobot() && len(params) >= 2 { + pack.TargetPower = proto.Int32(int32(params[1])) + pack.RobitFire = proto.Bool(int32(params[1]) == playerEx.power) + } + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIREPOWER), pack, 0) + if playerEx.IsRobot() { + fishlogger.Tracef("机器人%v设置倍率:当前倍率%v 目标倍率%v 能否发泡%v:", playerEx.SnId, playerEx.power, params[1], pack.RobitFire) + } + fishlogger.Trace("SCFirePower:", pack) + if playerEx.IsRob { + if playerEx.CoinCheck(playerEx.power) == false { + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_Bekickout, true) + } + } + } + case FishingRobotOpSetPower: + if len(params) >= 2 { + robotPlayer := sceneEx.GetPlayer(int32(params[0])) + if robotPlayer != nil && robotPlayer.IsRob == true && p.IsRob == false { + if robotPlayerEx, ok := robotPlayer.GetExtraData().(*FishingPlayerData); ok { + fishlogger.Tracef("RobotID %v set power to %v.", params[0], params[1]) + powers := sceneEx.GetDBGameFree().GetOtherIntParams() // power的配置信息 + int32Slice := make([]int32, len(powers)) + for i, v := range powers { + int32Slice[i] = int32(v) + } + if common.InSliceInt32(int32Slice, int32(params[1])) { + robotPlayerEx.power = int32(params[1]) + } else { + fishlogger.Errorf("Can't not switch power to %v.", params[0]) + fishlogger.Errorf("%v scene power list:%v.", sceneEx.gamefreeId, powers) + } + pack := &fishing_proto.SCFirePower{ + Snid: proto.Int32(robotPlayerEx.SnId), + Power: proto.Int32(robotPlayerEx.power), + } + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIREPOWER), pack, 0) + } + } + } + + case FishingPlayerOpSelVip: + if len(params) > 0 { + //if p.VIP >= int32(params[0]) { + playerEx.SelVip = int32(params[0]) + pack := &fishing_proto.SCSelVip{ + Snid: proto.Int32(playerEx.SnId), + Vip: proto.Int32(playerEx.SelVip), + } + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_SELVIP), pack, 0) + //} + } + case FishingPlayerOpLeave: + if playerEx.AgentParam != 0 { + robot := base.PlayerMgrSington.GetPlayerBySnId(playerEx.AgentParam) + if robot != nil { + sceneEx.PlayerLeave(robot, common.PlayerLeaveReason_Bekickout, true) + } + } + /* case FishingPlayerOpAuto: + { + if len(params) > 0 { + playerEx.AutoFishing = int32(params[0]) + this.OnPlayerSToCOp(s, p, opcode, fishing_proto.OpResultCode_OPRC_Sucess, params) + } + }*/ + /* case FishingPlayerOpSelTarget: + { + if len(params) > 0 { + playerEx.SelTarget = int32(params[0]) + pack := &fishing_proto.SCFishingOp{ + OpCode: proto.Int(opcode), + Params: params, + OpRetCode: fishing_proto.OpResultCode_OPRC_Sucess, + SnId: proto.Int32(playerEx.SnId), + } + proto.SetDefaults(pack) + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_OP), pack, 0) + } + }*/ + /* case FishingPlayerOpFireRate: + { + if len(params) > 0 { + playerEx.FireRate = int32(params[0]) + pack := &fishing_proto.SCFishingOp{ + OpCode: proto.Int(opcode), + Params: params, + OpRetCode: fishing_proto.OpResultCode_OPRC_Sucess, + SnId: proto.Int32(playerEx.SnId), + } + proto.SetDefaults(pack) + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_OP), pack, 0) + } + }*/ + case FishingPlayerHangup: + { + //客户端辅助,原路返回 + this.OnPlayerSToCOp(s, p, opcode, fishing_proto.OpResultCode_OPRC_Sucess, params) + } + case FishingRobotWantLeave: + { + //机器人想要离开 + if len(params) >= 1 { + robotPlayer := sceneEx.GetPlayer(int32(params[0])) + if robotPlayer != nil && robotPlayer.IsRob == true && p.IsRob == false { + this.OnPlayerSToCOp(s, robotPlayer, opcode, fishing_proto.OpResultCode_OPRC_Sucess, params) + } + } + } + } + } + } + return false +} + +/*// SyncFishingTarget 同步其他玩家的瞄准数据 +func SyncFishingTarget(sceneEx *FishingSceneData, playerEx *FishingPlayerData) { + for _, value := range sceneEx.players { + if value.SnId != playerEx.SnId { + continue + } + if value.TargetFish == 0 || value.SelTarget == 0 { + continue + } + pack := &fishing_proto.SCFishTarget{ + FishId: proto.Int32(value.TargetFish), + SnId: proto.Int32(value.SnId), + } + playerEx.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FISHTARGET), pack) + } +}*/ + +/* +同步当前玩家得炮台状态 +*/ +//func SyncFishingPowerState(sceneEx *FishingSceneData, playerEx *FishingPlayerData) { +// playerPowerState := []*fishing_proto.PlayerPowerState{} +// for _, value := range sceneEx.players { +// pps := &fishing_proto.PlayerPowerState{ +// Snid: proto.Int32(value.SnId), +// State: proto.Int32(value.powerType), +// Num: proto.Int32(value.FreePowerNum), +// } +// playerPowerState = append(playerPowerState, pps) +// } +// pack := &fishing_proto.SCPowerState{ +// PowerState: playerPowerState, +// } +// //sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_POWERSTATE), pack,0) +// playerEx.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_POWERSTATE), pack) +//} + +func (this *SceneBaseStateFishing) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + this.BaseScenePolicy.OnPlayerEvent(s, p, evtcode, params) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + if playerEx, ok := p.GetExtraData().(*FishingPlayerData); ok { + switch evtcode { + case base.PlayerEventRehold: + fishlogger.Tracef("用户 %v PlayerEventRehold", playerEx.SnId) + case base.PlayerEventEnter: + fallthrough + case base.PlayerEventReturn: + FishingSendRoomInfo(p, sceneEx) + pack := &fishing_proto.SCfishingEnter{ + Data: &fishing_proto.FishingPlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.CoinCache), + Pos: proto.Int(playerEx.GetPos()), + Flag: proto.Int(playerEx.GetFlag()), + City: proto.String(playerEx.GetCity()), + Params: proto.String(playerEx.Params), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + SelVip: proto.Int32(playerEx.SelVip), + Power: proto.Int32(playerEx.power), + AgentParam: proto.Int32(playerEx.AgentParam), + TargetSel: proto.Int32(playerEx.SelTarget), + AutoFishing: proto.Int32(playerEx.AutoFishing), + FireRate: proto.Int32(playerEx.FireRate), + IsRobot: proto.Bool(playerEx.IsRob), + NiceId: proto.Int32(playerEx.NiceId), + UnMaxPower: proto.Int64(playerEx.UnMaxPower), + UnPowerList: playerEx.PowerList, + FishLevel: proto.Int64(playerEx.FishLevel), + FishExp: proto.Int64(playerEx.FishExp), + }, + } + for _, v := range playerEx.RobotSnIds { + pack.Data.RobotSnIds = append(pack.Data.RobotSnIds, v) + } + fishlogger.Tracef("用户 %v 本身的VIP %v selVip %v playerEx.RobotSnIds %v", playerEx.SnId, playerEx.VIP, playerEx.SelVip, playerEx.RobotSnIds) + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_ENTER), pack, int64(playerEx.GetSid())) + //SyncFishingTarget(sceneEx, playerEx) + sceneEx.SyncFish(p) + case base.PlayerEventLeave: + pack := &fishing_proto.SCfishingLeave{ + SnId: proto.Int32(playerEx.SnId), + } + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_LEAVE), pack, int64(playerEx.GetSid())) + case base.PlayerEventRecharge, base.PlayerEventAddCoin: + playerEx.CoinCache += params[0] + } + } + } +} + +// Op协议包的统一发送 +func (this *SceneBaseStateFishing) OnPlayerSToCOp(s *base.Scene, p *base.Player, opcode int, opRetCode fishing_proto.OpResultCode, params []int64) { + pack := &fishing_proto.SCFishingOp{ + OpCode: proto.Int(opcode), + Params: params, + OpRetCode: opRetCode, + SnId: proto.Int32(p.SnId), + } + proto.SetDefaults(pack) + p.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_OP), pack) +} + +type SceneStateFishingStart struct { + SceneBaseStateFishing +} + +func (this *SceneStateFishingStart) GetState() int { return FishingSceneStateStart } +func (this *SceneStateFishingStart) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateFishing.OnPlayerOp(s, p, opcode, params) { + return true + } + return true +} +func (this *SceneStateFishingStart) OnEnter(s *base.Scene) { + this.SceneBaseStateFishing.OnEnter(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + fishlogger.Tracef("(this *Scene) [%v] 场景状态进入 %v", s.GetSceneId(), len(sceneEx.players)) + pack := &fishing_proto.SCFishingRoomState{ + State: proto.Int(this.GetState()), + } + proto.SetDefaults(pack) + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_ROOMSTATE), pack, 0) + sceneEx.lastTick = 0 + sceneEx.remainder = 0 + } +} +func (this *SceneStateFishingStart) OnTick(s *base.Scene) { + this.SceneBaseStateFishing.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + if sceneEx.lastTick == 0 { + sceneEx.lastTick = time.Now().UnixNano() + } else { + diff := time.Now().UnixNano() - sceneEx.lastTick + diff = diff / time.Millisecond.Nanoseconds() + sceneEx.lastTick = time.Now().UnixNano() + diff += sceneEx.remainder + for i := int64(0); i < diff/100; i++ { + // 每一百毫秒执行一次 + sceneEx.TimePoint++ + } + sceneEx.remainder = diff % 100 + + } + sceneEx.fishBattle() // 子弹鱼碰撞处理 + sceneEx.OnTick() + } +} + +func (this *SceneStateFishingStart) OnLeave(s *base.Scene) { + this.SceneBaseStateFishing.OnLeave(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + for _, p := range sceneEx.players { + if p != nil && p.IsGameing() && !p.IsRob { + p.SaveDetailedLog(s) + } + } + } +} + +type SceneStateFishingClear struct { + SceneBaseStateFishing +} + +func (this *SceneStateFishingClear) GetState() int { return FishingSceneStateClear } +func (this *SceneStateFishingClear) CanChangeTo(s base.SceneState) bool { return true } +func (this *SceneStateFishingClear) OnEnter(s *base.Scene) { + this.SceneBaseStateFishing.OnEnter(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + fishlogger.Tracef("(this *Scene) [%v] 场景状态进入 %v", s.GetSceneId(), len(sceneEx.players)) + sceneEx.Clean() + //sceneEx.ChangeFlushFish() + pack := &fishing_proto.SCFishingRoomState{ + State: proto.Int(this.GetState()), + Params: []int32{int32(common.RandInt(5) + 1)}, + } + proto.SetDefaults(pack) + sceneEx.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_ROOMSTATE), pack, 0) + } +} +func (this *SceneStateFishingClear) OnTick(s *base.Scene) { + this.SceneBaseStateFishing.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + if time.Now().Sub(sceneEx.GetStateStartTime()) > FishingSceneAniTimeout { + sceneEx.OnTick() + sceneEx.ChangeSceneState(FishingSceneStateStart) + } + } +} +func (this *SceneStateFishingClear) OnLeave(s *base.Scene) { + this.SceneBaseStateFishing.OnLeave(s) + if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { + for _, p := range sceneEx.players { + leave := false + var reason int + if p.IsRob { + if s.CoinOverMaxLimit(p.CoinCache, p.Player) { + leave = true + reason = common.PlayerLeaveReason_Normal + } + } else { + if !p.IsOnLine() { + leave = true + reason = common.PlayerLeaveReason_DropLine + } else if !s.CoinInLimit(p.CoinCache) { + leave = true + reason = common.PlayerLeaveReason_Bekickout + } + } + todayGamefreeIDSceneData, _ := p.GetDaliyGameData(int(sceneEx.GetDBGameFree().GetId())) + if !p.IsRob && + todayGamefreeIDSceneData != nil && + sceneEx.GetDBGameFree().GetPlayNumLimit() != 0 && + todayGamefreeIDSceneData.GameTimes >= int64(sceneEx.GetDBGameFree().GetPlayNumLimit()) { + leave = true + reason = common.PlayerLeaveReason_GameTimes + } + if leave { + s.PlayerLeave(p.Player, reason, true) + } + } + } +} +func (this *ScenePolicyFishing) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + if stateid < 0 || stateid >= FishingSceneStateMax { + return + } + this.states[stateid] = state +} +func (this *ScenePolicyFishing) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < FishingSceneStateMax { + return ScenePolicyFishingSington.states[stateid] + } + return nil +} + +// func (this *ScenePolicyFishing) GetGameSubState(s *base.Scene, stateid int) base.SceneGamingSubState { +// return nil +// } +func init() { + ScenePolicyFishingSington.RegisteSceneState(&SceneStateFishingStart{}) + ScenePolicyFishingSington.RegisteSceneState(&SceneStateFishingClear{}) + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameId_HFishing, fishing.RoomMode_HuanLe, ScenePolicyFishingSington) + return nil + }) +} diff --git a/gamesrv/fruits/action_fruits.go b/gamesrv/fruits/action_fruits.go new file mode 100644 index 0000000..cffa6f7 --- /dev/null +++ b/gamesrv/fruits/action_fruits.go @@ -0,0 +1,48 @@ +package fruits + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/fruits" +) + +type CSFruitsOpPacketFactory struct { +} +type CSFruitsOpHandler struct { +} + +func (this *CSFruitsOpPacketFactory) CreatePacket() interface{} { + pack := &fruits.CSFruitsOp{} + return pack +} + +func (this *CSFruitsOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + if op, ok := data.(*fruits.CSFruitsOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSFruitsOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSFruitsOpHandler p.scene == nil") + return nil + } + if !scene.HasPlayer(p) { + return nil + } + if scene.GetScenePolicy() != nil { + scene.GetScenePolicy().OnPlayerOp(scene, p, int(op.GetOpCode()), op.GetParams()) + } + return nil + } + return nil +} +func init() { + //水果拉霸 + common.RegisterHandler(int(fruits.FruitsPID_PACKET_FRUITS_CSFruitsOp), &CSFruitsOpHandler{}) + netlib.RegisterFactory(int(fruits.FruitsPID_PACKET_FRUITS_CSFruitsOp), &CSFruitsOpPacketFactory{}) +} diff --git a/gamesrv/fruits/playerdata_fruits.go b/gamesrv/fruits/playerdata_fruits.go new file mode 100644 index 0000000..0a1d098 --- /dev/null +++ b/gamesrv/fruits/playerdata_fruits.go @@ -0,0 +1,333 @@ +package fruits + +import ( + "math" + + "mongo.games.com/game/gamerule/fruits" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" +) + +var ElementsParams = [][]int32{} + +type FruitsPlayerData struct { + *base.Player + roomid int32 //房间ID + result *fruits.WinResult + betIdx int //下注索引 + betCoin int64 //下注金额 + oneBetCoin int64 //单注 + cpCtx model.CoinPoolCtx //水池环境 + leaveTime int32 //离开时间 + winCoin int64 //本局输赢 + freeTimes int //免费游戏 + maryFreeTimes int //玛丽游戏 + gameState int //当前游戏模式 + ///当局数值 + winLineRate int64 //线倍率 + JackPotRate int64 //获取奖池的百分比 5% 10% 15% + JackPot7Rate int64 //元素jackpot的倍率 + freeWinTotalRate int64 //免费游戏赢的倍率 + maryWinTotalRate int64 //玛丽游戏赢的倍率 + winNowJackPotCoin int64 //当局奖池爆奖 + winNowAllRate int64 //当局赢得倍率 + ////////////////////////// + //统计数值 + winLineCoin int64 //线赢金额 + winJackPotCoin int64 //爆奖 + winEle777Coin int64 //元素数量 + winFreeCoin int64 //免费游戏 + //winMaryCoin int64 //玛丽游戏 + winOutMaryCoin int64 //玛丽游戏外圈 + winMidMaryCoin int64 //玛丽游戏内圈 + winFreeTimes int //当局获得的免费次数 + //log + nowFreeTimes int //当前第几轮免费游戏 + nowMaryTimes int //当前第几轮小玛丽游戏 + wl []model.FruitsWinLine + maryInNum int //玛丽游戏连续数量 + taxCoin int64 + //调控 + preWinRate int64 + preNeedRate int64 + pre30WinRate int64 + preEleVal []int32 + //mary + preMaryOutSide int32 + preMaryMidArray []int32 + noWinTimes int + // + startCoin int64 + testNum int + testIdx int + isf bool + freeTotal int + weightPos int32 //当局权重 +} + +func (p *FruitsPlayerData) init() { + p.roomid = 0 + p.result = new(fruits.WinResult) +} +func (p *FruitsPlayerData) Clear() { + p.gameState = fruits.Normal + p.startCoin = p.Coin + p.winCoin = 0 + p.JackPotRate = 0 + p.JackPot7Rate = 0 + p.winLineRate = 0 + p.maryWinTotalRate = 0 + p.freeWinTotalRate = 0 + p.winNowAllRate = 0 + p.winNowJackPotCoin = 0 + p.winFreeTimes = 0 + p.maryInNum = 0 + p.taxCoin = 0 + if p.freeTimes == 0 { + p.winLineCoin = 0 + p.winFreeCoin = 0 + p.winJackPotCoin = 0 + p.winEle777Coin = 0 + p.nowFreeTimes = 0 + } + if p.maryFreeTimes == 0 { + //p.winMaryCoin = 0 + p.winOutMaryCoin = 0 + p.winMidMaryCoin = 0 + p.nowMaryTimes = 0 + p.winOutMaryCoin = 0 + p.winMidMaryCoin = 0 + } + p.weightPos = 0 +} +func (p *FruitsPlayerData) TestCode(eleLineAppearRate [][]int32, sceneEx *FruitsSceneData) bool { + //if sceneEx.DbGameFree.GetId() == 3060004 { + // p.result.CreateLine(eleLineAppearRate, p.gameState == fruits.FreeGame) + // if p.testIdx == 1 { + // //test mary + // if p.testNum%5 == 0 && p.gameState != fruits.FreeGame { + // for i := 0; i < 5; i++ { + // p.result.EleValue[i] = fruits.Wild + // } + // p.testIdx = 2 + // } + // p.testNum++ + // if p.testNum >= 100000 { + // p.testNum = 0 + // } + // if p.gameState == fruits.FreeGame && !p.isf { + // p.isf = true + // for i := 0; i < 5; i++ { + // p.result.EleValue[i] = fruits.Bonus + // } + // } + // } else if p.testIdx == 0 { + // //test free + // if p.testNum%5 == 0 && p.gameState == fruits.Normal { + // for i := 0; i < 5; i++ { + // p.result.EleValue[i] = fruits.Bonus + // } + // p.testIdx = 1 + // p.isf = false + // } + // if p.gameState == fruits.Normal { + // p.testNum++ + // if p.testNum >= 100000 { + // p.testNum = 0 + // } + // } + // } else { + // if p.testNum%5 == 0 && p.gameState == fruits.Normal { + // for i := 0; i < 5; i++ { + // p.result.EleValue[i] = fruits.Bonus + // p.result.EleValue[i+5] = fruits.Wild + // } + // p.testIdx = 0 + // } + // if p.gameState == fruits.Normal { + // p.testNum++ + // if p.testNum >= 100000 { + // p.testNum = 0 + // } + // } + // } + // p.result.Win() + // return true + //} + return false +} + +// 正常游戏 免费游戏 +func (p *FruitsPlayerData) CreateResult(eleLineAppearRate [][]int32, sceneEx *FruitsSceneData) { + //if len(p.preEleVal) > 0 { + // p.result.EleValue = make([]int32, 15) + // copy(p.result.EleValue, p.preEleVal) + // p.preEleVal = nil + //} + + if !p.TestCode(eleLineAppearRate, sceneEx) { + for i := 0; i < 10; i++ { + p.result.CreateLine(eleLineAppearRate, p.gameState == fruits.FreeGame) + p.result.Win() + var isScatter bool + var isNeed bool = true + for _, wl := range p.result.WinLine { + flag := wl.Lines[0] + if flag == fruits.Scatter { + isScatter = true + JackPotVal := sceneEx.jackpot.GetTotalSmall() + if JackPotVal > 0 { + var winjackpot int64 + switch len(wl.Poss) { + case 3: + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.1 / float64(fruits.NowByte))) + case 4: + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.2 / float64(fruits.NowByte))) + case 5: + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.4 / float64(fruits.NowByte))) + } + if winjackpot < int64(sceneEx.DbGameFree.GetJackpotMin())*fruits.NowByte { + isNeed = false + break + } + } + } + } + if !isScatter || (isScatter && isNeed) { + break + } + } + } + //线倍率 + var rate int64 + for _, wl := range p.result.WinLine { + //Wild 元素 按线的连续给玛丽游戏 + flag := wl.Lines[0] + var NowWildNum int + var MaxLinXu int + for _, n := range wl.Lines { + if flag != n && flag == fruits.Wild { + flag = n + } + if n == fruits.Wild { + NowWildNum++ + if NowWildNum > MaxLinXu { + MaxLinXu = NowWildNum + } + } else { + NowWildNum = 0 + } + } + if MaxLinXu >= 3 { + newM := model.FruitsWinLine{ + Id: wl.LineId, + EleValue: fruits.Wild, + Num: MaxLinXu, + Rate: 0, + WinCoin: 0, + } + if MaxLinXu == 3 { + p.maryFreeTimes += 1 + newM.WinFreeGame = 0 + } else if MaxLinXu == 4 { + p.maryFreeTimes += 2 + newM.WinFreeGame = 1 + } else if MaxLinXu >= 5 { + p.maryFreeTimes += 3 + newM.WinFreeGame = 2 + } + p.wl = append(p.wl, newM) + } + + //统计线倍率 + rate += wl.Rate + + //Bonus 元素 按线给倍率 按线给免费 + if flag == fruits.Bonus { + n := len(wl.Lines) + newL := model.FruitsWinLine{ + Id: wl.LineId, + EleValue: fruits.Bonus, + Num: n, + Rate: wl.Rate, + WinCoin: p.oneBetCoin * wl.Rate, + } + if n == 3 { + p.freeTimes += 5 + p.winFreeTimes += 5 + newL.WinFreeGame = 3 + } else if n == 4 { + p.freeTimes += 10 + p.winFreeTimes += 10 + newL.WinFreeGame = 4 + } else if n == 5 { + p.freeTimes += 20 + p.winFreeTimes += 20 + newL.WinFreeGame = 5 + } + p.wl = append(p.wl, newL) + } else if flag == fruits.Scatter { + JackPotVal := sceneEx.jackpot.GetTotalSmall() + if JackPotVal > 0 { + var winjackpot int64 + switch len(wl.Poss) { + case 3: + p.JackPotRate += 10 + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.1 / float64(fruits.NowByte))) + case 4: + p.JackPotRate += 20 + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.2 / float64(fruits.NowByte))) + case 5: + p.JackPotRate += 40 + winjackpot = int64(math.Ceil(float64(JackPotVal) * 0.4 / float64(fruits.NowByte))) + } + p.wl = append(p.wl, model.FruitsWinLine{ + Id: wl.LineId, + EleValue: fruits.Scatter, + Num: len(wl.Poss), + Rate: wl.Rate, + WinCoin: winjackpot, + WinFreeGame: -1, + }) + p.JackPot7Rate += wl.Rate + p.winNowJackPotCoin += winjackpot + } + } + + } + //赢的线钱 + p.winLineCoin += p.oneBetCoin * rate + p.winLineRate = rate + //元素输赢 + p.winEle777Coin += p.oneBetCoin * p.JackPot7Rate +} + +// 玛丽游戏 +func (p *FruitsPlayerData) CreateMary() { + //if len(p.preMaryMidArray) > 0 { + // p.result.MaryMidArray = make([]int32, 4) + // copy(p.result.MaryMidArray, p.preMaryMidArray) + // p.result.MaryOutSide = p.preMaryOutSide + // p.preMaryMidArray = nil + //} + + //idx := p.result.MaryOutSide + //maryOutSide := fruits.MaryEleArray[idx] + + //if maryOutSide == fruits.Bomb { + // p.maryFreeTimes = 0 + // rate = 0 + //} + + if p.result.MaryOutSide == fruits.Bomb { + p.maryFreeTimes-- + if p.maryFreeTimes < 0 { + p.maryFreeTimes = 0 + } + } + + p.maryWinTotalRate = p.result.MaryOutRate + p.result.MaryMidRate + p.winOutMaryCoin += p.oneBetCoin * p.result.MaryOutRate + p.winMidMaryCoin += p.oneBetCoin * p.result.MaryMidRate + //p.winMaryCoin += p.winOutMaryCoin + p.winMidMaryCoin +} diff --git a/gamesrv/fruits/scenedata_fruits.go b/gamesrv/fruits/scenedata_fruits.go new file mode 100644 index 0000000..35ac3d6 --- /dev/null +++ b/gamesrv/fruits/scenedata_fruits.go @@ -0,0 +1,646 @@ +package fruits + +import ( + "encoding/json" + "fmt" + "math" + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fruits" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + protocol "mongo.games.com/game/protocol/fruits" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "time" +) + +type FruitsSceneData struct { + *base.Scene //场景 + players map[int32]*FruitsPlayerData //玩家信息 + jackpot *base.SlotJackpotPool //奖池 + levelRate [][]int32 //调控概率区间 + //slotRateWeight []int32 //调控权重 + //slotRateWeightTotal [][]int32 //总调控权重 + sysProfitCoinKey string + mqLogTime time.Time +} + +func NewFruitsSceneData(s *base.Scene) *FruitsSceneData { + sceneEx := &FruitsSceneData{ + Scene: s, + players: make(map[int32]*FruitsPlayerData), + } + sceneEx.Init() + return sceneEx +} +func (s *FruitsSceneData) Init() { + s.LoadJackPotData() + //for _, data := range srvdata.PBDB_SlotRateWeightMgr.Datas.Arr { + // if data.Id == s.DbGameFree.Id { + // //s.levelRate = append(s.levelRate, data.EleWeight1) + // //s.slotRateWeightTotal = append(s.slotRateWeightTotal, data.EleWeight1, data.EleWeight2, data.EleWeight3, data.EleWeight4, data.EleWeight5) + // } + //} + s.sysProfitCoinKey = fmt.Sprintf("%v_%v", s.Platform, s.GetGameFreeId()) + s.mqLogTime = time.Now() +} + +func (s *FruitsSceneData) Clear() { + //应该是水池变一次就判断修改一次 + //s.slotRateWeight = s.slotRateWeightTotal[0] +} +func (s *FruitsSceneData) SceneDestroy(force bool) { + //销毁房间 + s.Scene.Destroy(force) +} +func (s *FruitsSceneData) AddPrizeCoin(playerEx *FruitsPlayerData) { + val := playerEx.betCoin + tax := int64(math.Ceil(float64(val) * float64(s.DbGameFree.GetTaxRate()) / 10000)) + //playerEx.taxCoin = tax + //playerEx.AddServiceFee(tax) + val -= tax + + addPrizeCoin := int64(math.Floor(float64(val*fruits.NowByte*int64(s.DbGameFree.GetJackpotRatio())) / 1000)) //扩大10000倍 + s.jackpot.AddToSmall(playerEx.IsRob, addPrizeCoin) + logger.Logger.Tracef("奖池增加...AddPrizeCoin... %f", float64(addPrizeCoin)/float64(fruits.NowByte)) + base.SlotsPoolMgr.SetPool(s.GetGameFreeId(), s.Platform, s.jackpot) + + if !s.Testing { + base.CoinPoolMgr.PushCoin(s.GetGameFreeId(), s.GroupId, s.Platform, val*fruits.NowByte-addPrizeCoin) + } +} +func (s *FruitsSceneData) DelPrizeCoin(isRob bool, win int64) { + if win > 0 { + s.jackpot.AddToSmall(isRob, -win*fruits.NowByte) + logger.Logger.Tracef("奖池减少...AddPrizeCoin... %d", win) + base.SlotsPoolMgr.SetPool(s.GetGameFreeId(), s.Platform, s.jackpot) + } +} +func (s *FruitsSceneData) delPlayer(SnId int32) { + if _, exist := s.players[SnId]; exist { + delete(s.players, SnId) + } +} +func (s *FruitsSceneData) OnPlayerLeave(p *base.Player, reason int) { + if playerEx, ok := p.ExtraData.(*FruitsPlayerData); ok { + if playerEx.freeTimes > 0 || playerEx.maryFreeTimes > 0 { + //免费 + playerEx.winFreeCoin = playerEx.winLineCoin + playerEx.winJackPotCoin + playerEx.winEle777Coin + //小玛丽 + playerEx.winCoin = playerEx.winOutMaryCoin + playerEx.winMidMaryCoin + playerEx.winFreeCoin + if playerEx.winCoin != 0 { + //SysProfitCoinMgr.PushBetAndSysOut(s.sysProfitCoinKey, 0, playerEx.winCoin) + p.Statics(s.KeyGameId, s.KeyGamefreeId, playerEx.winCoin, false) + //tax := int64(math.Ceil(float64(playerEx.winCoin) * float64(s.DbGameFree.GetTaxRate()) / 10000)) + //playerEx.taxCoin = tax + //playerEx.winCoin -= tax + //p.AddServiceFee(tax) + p.AddCoin(playerEx.winCoin, common.GainWay_HundredSceneWin, 0, "system", s.GetSceneName()) + if !s.Testing { + base.CoinPoolMgr.PushCoin(s.GetGameFreeId(), s.GroupId, s.Platform, -(playerEx.winCoin)*fruits.NowByte) + } + //p.isReportGameEvent = true + //p.ReportGameEventParam() + s.SaveLog(playerEx, 1) + } + } + } + s.delPlayer(p.SnId) +} +func (s *FruitsSceneData) Win(p *FruitsPlayerData) { + if p.gameState != fruits.MaryGame && p.JackPotRate > 0 { + //奖池 + s.DelPrizeCoin(p.IsRob, p.winNowJackPotCoin) + p.winJackPotCoin += p.winNowJackPotCoin + } + isBilled := false + if p.gameState == fruits.Normal { + isBilled = true + p.winCoin = p.winLineCoin + p.winJackPotCoin + p.winEle777Coin + p.winNowAllRate = p.winLineRate + p.JackPot7Rate + } else if p.gameState == fruits.FreeGame { + p.winFreeCoin = p.winLineCoin + p.winJackPotCoin + p.winEle777Coin + p.winNowAllRate = p.winLineRate + p.JackPot7Rate + if p.freeTimes == 0 { + isBilled = true + p.winCoin = p.winFreeCoin + } + } else { + p.winNowAllRate = p.maryWinTotalRate + if p.maryFreeTimes == 0 { + isBilled = true + p.winCoin = p.winOutMaryCoin + p.winMidMaryCoin + } + } + if isBilled && p.winCoin != 0 { + p.noWinTimes = 0 + //SysProfitCoinMgr.PushBetAndSysOut(s.sysProfitCoinKey, 0, p.winCoin) + p.Statics(s.KeyGameId, s.KeyGamefreeId, p.winCoin, false) + //tax := int64(math.Ceil(float64(p.winCoin) * float64(s.DbGameFree.GetTaxRate()) / 10000)) + //p.taxCoin = tax + //p.winCoin -= tax + //p.AddServiceFee(tax) + + p.AddCoin(p.winCoin, common.GainWay_HundredSceneWin, 0, "system", s.GetSceneName()) + if !s.Testing && p.winCoin != 0 { + base.CoinPoolMgr.PushCoin(s.GetGameFreeId(), s.GroupId, s.Platform, -(p.winCoin)*fruits.NowByte) + } + //p.isReportGameEvent = true + } + //p.ReportGameEventParam() +} +func (s *FruitsSceneData) SendBilled(p *FruitsPlayerData) { + if p.gameState != fruits.MaryGame { + //正常游戏 免费游戏 + var wl []*protocol.FruitsWinLine + for _, r := range p.result.WinLine { + wl = append(wl, &protocol.FruitsWinLine{ + Poss: r.Poss, + LineId: proto.Int(r.LineId), + }) + } + pack := &protocol.SCFruitsBilled{ + NowGameState: proto.Int(p.gameState), + BetIdx: proto.Int(p.betIdx), + Coin: proto.Int64(p.Coin), + FreeTotalWin: proto.Int64(p.winFreeCoin), + Jackpot: proto.Int64(s.jackpot.GetTotalSmall() / 10000), + WinJackpot: proto.Int64(p.winNowJackPotCoin), + WinEle777Coin: proto.Int64(p.oneBetCoin * p.JackPot7Rate), + WinRate: proto.Int64(p.winLineRate + p.JackPot7Rate), + Cards: p.result.EleValue, + WinLines: wl, + FreeTimes: proto.Int(p.freeTimes), + MaryFreeTimes: proto.Int(p.maryFreeTimes), + WinFreeTimes: proto.Int(p.winFreeTimes), + WinLineCoin: proto.Int64(p.oneBetCoin * p.winLineRate), + } + logger.Logger.Trace("SCFruitsBilled:", pack) + p.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsBilled), pack) + } else { + //玛丽游戏 + pack := &protocol.SCFruitsMaryBilled{ + Coin: proto.Int64(p.Coin), + FreeTotalWin: proto.Int64(p.winFreeCoin), + MaryOutTotalWin: proto.Int64(p.winOutMaryCoin), + MaryMidTotalWin: proto.Int64(p.winMidMaryCoin), + MaryWinCoin: proto.Int64(p.oneBetCoin * p.maryWinTotalRate), + MaryWinId: proto.Int32(p.result.MaryOutSide), + MaryCards: p.result.MaryMidArray, + MaryFreeTimes: proto.Int(p.maryFreeTimes), + } + logger.Logger.Trace("SCFruitsMaryBilled:", pack) + p.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsMaryBilled), pack) + } +} +func (s *FruitsSceneData) LoadJackPotData() { + str := base.SlotsPoolMgr.GetPool(s.GetGameFreeId(), s.Platform) + if str != "" { + jackpot := &base.SlotJackpotPool{} + err := json.Unmarshal([]byte(str), jackpot) + if err == nil { + s.jackpot = jackpot + } + } + if s.jackpot != nil { + base.SlotsPoolMgr.SetPool(s.GetGameFreeId(), s.Platform, s.jackpot) + } else { + s.jackpot = &base.SlotJackpotPool{} + jp := s.DbGameFree.GetJackpot() + if len(jp) > 0 { + s.jackpot.Small += int64(jp[0] * 10000) + } + } +} +func (s *FruitsSceneData) SaveLog(p *FruitsPlayerData, isOffline int) { + if s.Testing { + return + } + s.SendPlayerBet(p) + var betCoin int64 + var nowNRound int + var nowGetCoin int64 + var isF bool + + if p.gameState == fruits.Normal { + betCoin = p.betCoin + nowGetCoin = p.winCoin + isF = true + } else if p.gameState == fruits.FreeGame { + nowNRound = p.nowFreeTimes + nowGetCoin = p.winFreeCoin + if p.freeTimes == 0 { + isF = true + } + } else { + nowNRound = p.nowMaryTimes + nowGetCoin = p.winOutMaryCoin + p.winMidMaryCoin + if p.maryFreeTimes == 0 { + isF = true + } + } + FruitsType := model.FruitsType{ + RoomId: s.SceneId, + BasicScore: int32(p.oneBetCoin), + PlayerSnId: p.SnId, + BeforeCoin: p.startCoin, + AfterCoin: p.Coin, + ChangeCoin: p.Coin - p.startCoin, + TotalBetCoin: betCoin, + TotalLine: 9, + TotalWinCoin: nowGetCoin, + NowGameState: p.gameState, + NowNRound: nowNRound, + IsOffline: isOffline, + FirstFreeTimes: p.freeTimes, + MaryFreeTimes: p.maryFreeTimes, + //WhiteLevel: p.WhiteLevel, + //BlackLevel: p.BlackLevel, + TaxCoin: p.taxCoin, + WBLevel: p.WBLevel, + RealCtrl: s.RealCtrl, + WBState: p.WBState, + WeightKey: p.weightPos + 1, + } + //if p.result.JackPotNum >= 3 { + //Classic777Type.JackPotNum = p.result.JackPotNum + FruitsType.JackPotWinCoin = p.oneBetCoin * p.JackPot7Rate + //} + var winLine []model.FruitsWinLine + if p.gameState == fruits.MaryGame { + FruitsType.MaryOutSide = fruits.MaryEleArray[p.result.MaryOutSide] + FruitsType.MaryMidCards = p.result.MaryMidArray + winLine = append(winLine, model.FruitsWinLine{ + Id: -1, + EleValue: FruitsType.MaryOutSide, + Num: p.maryInNum, + Rate: p.maryWinTotalRate, + WinCoin: p.oneBetCoin * p.maryWinTotalRate, + WinFreeGame: -1, + }) + + } else { + FruitsType.Cards = p.result.EleValue + FruitsType.HitPrizePool = p.winNowJackPotCoin + n := 0 + for _, v := range p.result.WinLine { + if v.Rate == 0 { + n++ + } + } + FruitsType.WinLineNum = len(p.result.WinLine) - n + FruitsType.WinLineRate = p.winLineRate + FruitsType.WinLineCoin = p.winLineRate * p.oneBetCoin + for _, line := range p.result.WinLine { + if line.LineId > 9 { + continue + } + flag := line.Lines[0] + if flag == fruits.Wild { + for _, v := range line.Lines { + if flag != v { + flag = v + break + } + } + } else if flag == fruits.Bonus { + continue + } + fw := model.FruitsWinLine{ + Id: line.LineId, + EleValue: flag, + Num: len(line.Lines), + Rate: line.Rate, + WinCoin: line.Rate * p.oneBetCoin, + WinFreeGame: -1, + } + winLine = append(winLine, fw) + } + } + winLine = append(winLine, p.wl...) + FruitsType.WinLine = winLine + info, err := model.MarshalGameNoteByROLL(&FruitsType) + if err == nil { + logId, _ := model.AutoIncGameLogId() + s.SaveGameDetailedLog(logId, info, &base.GameDetailedParam{}) + //水池上下文环境s + s.CpCtx = p.cpCtx + var totalIn, totalOut int64 + if betCoin > 0 { + totalIn = betCoin + } + if nowGetCoin > 0 && isF { + totalOut = p.Coin - p.startCoin + betCoin /*+ p.taxCoin*/ + } + s.SaveGamePlayerListLog(p.SnId, + &base.SaveGamePlayerListLogParam{ + Platform: p.Platform, + Channel: p.Channel, + Promoter: p.BeUnderAgentCode, + PackageTag: p.PackageID, + InviterId: p.InviterId, + LogId: logId, + TotalIn: totalIn, + TotalOut: totalOut, + TaxCoin: p.taxCoin, + ClubPumpCoin: 0, + BetAmount: totalIn, + WinAmountNoAnyTax: p.Coin - p.startCoin, + IsFirstGame: s.IsPlayerFirst(p.Player), + }) + } + s.GameNowTime = time.Now() + if s.CheckNeedDestroy() && p.freeTimes == 0 && p.maryFreeTimes == 0 { + s.PlayerLeave(p.Player, common.PlayerLeaveReason_OnDestroy, true) + } +} +func (s *FruitsSceneData) SendPlayerBet(p *FruitsPlayerData) { + //统计输下注金币数 + if !p.IsRob && !s.Testing { + betCoin := p.betCoin + if p.gameState != fruits.Normal { + betCoin = 0 + } + + s.SyncPlayerDatas(&base.PlayerDataParam{ + HasRobotGaming: false, + Data: &server.GWPlayerData{ + Datas: []*server.PlayerData{ + { + SnId: p.SnId, + Bet: betCoin, + Gain: p.Coin - p.startCoin, + Tax: p.taxCoin, + Coin: p.Coin, + GameCoinTs: p.GameCoinTs, + }, + }, + GameFreeId: s.GetGameFreeId(), + SceneId: int32(s.SceneId), + }, + }) + } +} +func (s *FruitsSceneData) Regulation(p *FruitsPlayerData, n int) { + //if len(s.levelRate) == 0 || len(s.slotRateWeight) == 0 { + // return + //} + //if p.gameState != fruits.Normal { + // state, _ := base.CoinPoolMgr.GetCoinPoolStatus2(s.Platform, s.GetGameFreeId(), s.GroupId, -p.preWinRate*p.oneBetCoin) + // var isLow bool + // if state == base.CoinPoolStatus_Low { + // isLow = true + // if p.preWinRate > 9 { + // p.preWinRate = rand.Int63n(9) + 1 + // } + // } + // s.InFreeGame(p, isLow) + // return + //} + //setting := base.CoinPoolMgr.GetCoinPoolSetting(s.Platform, s.GetGameFreeId(), s.GroupId) + //state, _ := base.CoinPoolMgr.GetCoinPoolStatus(s.Platform, s.GetGameFreeId(), s.GroupId) + //isLow := false + //if state == base.CoinPoolStatus_Low { + // //test code + // isLow = true + //} + //var levelMaxOut = -1 + //var level = -1 + //if setting != nil { + // for k, v := range s.levelRate { + // if setting.GetMaxOutValue() != 0 && int64(v[1]) <= int64(setting.GetMaxOutValue())/p.oneBetCoin { + // levelMaxOut = k + // } + // if isLow { + // if int64(v[1]) <= 9 { + // level = k + // } + // } + // } + //} + //if !isLow && levelMaxOut > level { + // level = levelMaxOut + //} + ////调控Y值 + //var regY int32 = -1 + //if p.WhiteLevel > 0 || p.BlackLevel > 0 { + // if p.WhiteLevel > 0 { + // regY = s.slotRateWeight[0] * (10 - p.WhiteLevel) + // } else if p.BlackLevel > 0 { + // regY = s.slotRateWeight[0] * (10 + p.BlackLevel*5) / 10 + // } + //} else { + // if d, exist := p.GDatas[s.KeyGameId]; exist { + // if d.Statics.TotalIn < 20000 { + // regY = s.slotRateWeight[0] * 50 / 100 + // } + // if d.Statics.TotalIn != 0 && d.Statics.TotalOut*100/d.Statics.TotalIn >= 150 && regY == -1 { + // regY = s.slotRateWeight[0] * 4 + // } + // } + // if regY == -1 { + // if setting != nil { + // poolValue := base.CoinPoolMgr.GetCoin(s.GetGameFreeId(), s.Platform, s.GroupId) + // w := float64(s.slotRateWeight[0]) * (1 - float64(int32(poolValue)-setting.GetLowerLimit())/float64(setting.GetUpperLimit()-setting.GetLowerLimit()+1)) + // if w < 0 { + // regY = 0 + // } else { + // regY = int32(w) + // } + // } + // } + // if p.noWinTimes > 0 && regY == -1 { + // if rand.Intn(100) < 10*p.noWinTimes { + // //要中奖 + // regY = s.slotRateWeight[0] * (10 - int32(p.noWinTimes)) * 10 / 100 + // } + // } + //} + //if regY == -1 { + // regY = s.slotRateWeight[0] + //} + //slotRateWeight := make([]int32, level+1) + //copy(slotRateWeight, s.slotRateWeight[:level+1]) + //slotRateWeight[0] = regY + //rIdx := fruits.RandSliceInt32IndexByWightN(slotRateWeight) + //parsec := s.levelRate[rIdx] + //var needRate int32 + //if rIdx != 0 { + // needRate = rand.Int31n(parsec[1]-parsec[0]) + parsec[0] + //} + //state, _ = base.CoinPoolMgr.GetCoinPoolStatus2(s.Platform, s.GetGameFreeId(), s.GroupId, -int64(needRate)*p.oneBetCoin) + //if n >= 5 { + // needRate = rand.Int31n(9) + //} + ////test code + ////if state == base.CoinPoolStatus_Low && needRate > 9 { + //// s.Regulation(p, n+1) + //// return + ////} + //p.preWinRate = int64(needRate) + //p.preNeedRate = int64(needRate) + //if p.preNeedRate > 1000 { + // p.pre30WinRate = int64(needRate) * 30 / 100 + //} + //s.ShowGameState(int64(needRate), p, isLow) +} +func (s *FruitsSceneData) InMaryGame(p *FruitsPlayerData) { + ele := []int32{fruits.Watermelon, fruits.Grape, fruits.Lemon, fruits.Cherry, fruits.Banana, fruits.Bonus, fruits.Pineapple} + var eleVal int32 = -1 + var lianxuEle = 0 + if p.preWinRate > 550*9 { + //四连 + rate := []int64{700 * 9, 600 * 9, 570 * 9, 550 * 9, 520 * 9, 510 * 9, 505 * 9} + for k, v := range rate { + if p.preWinRate > v { + ele = ele[k:] + break + } + } + lianxuEle = 4 + } else if p.preWinRate > 80*9 { + //三连 + rate := []int64{220 * 9, 120 * 9, 90 * 9, 70 * 9, 40 * 9, 30 * 9, 25 * 9} + for k, v := range rate { + if p.preWinRate > v { + ele = ele[k:] + break + } + } + lianxuEle = 3 + } else if p.preWinRate > 5*9 { + //单个 + rate := []int64{200 * 9, 100 * 9, 70 * 9, 50 * 9, 20 * 9, 10 * 9, 5 * 9} + for k, v := range rate { + if p.preWinRate > v { + ele = ele[k:] + break + } + } + lianxuEle = 1 + } + p.preMaryMidArray = make([]int32, 4) + MaryMidEleRate := make([]int32, len(fruits.MaryMidEleRate)) + copy(MaryMidEleRate, fruits.MaryMidEleRate) + for i := 0; i < 4; i++ { + eler := fruits.RandSliceInt32IndexByWightN(MaryMidEleRate) + p.preMaryMidArray[i] = eler + } + if lianxuEle > 0 { + //需要中奖 + idx := rand.Intn(len(ele)) + eleVal = ele[idx] + ele = append(ele[:idx], ele[idx+1:]...) + for k, v := range p.preMaryMidArray { + if v == eleVal { + p.preMaryMidArray[k] = ele[rand.Intn(len(ele))] + } + } + if lianxuEle > 1 { + for i := 0; i < lianxuEle; i++ { + p.preMaryMidArray[i] = eleVal + } + } else { + for k := range p.preMaryMidArray { + if rand.Intn(300) <= (k+1)*100 { + p.preMaryMidArray[k] = eleVal + break + } + } + } + for k, v := range fruits.MaryEleArray { + if v == eleVal && rand.Intn(300) <= (k+1)*100 { + p.preMaryOutSide = int32(k) + break + } + } + var rate int32 + switch eleVal { + case fruits.Cherry: + rate += 50 + case fruits.Pineapple: + rate += 5 + case fruits.Grape: + rate += 100 + case fruits.Lemon: + rate += 70 + case fruits.Watermelon: + rate += 200 + case fruits.Banana: + rate += 20 + case fruits.Apple: + rate += 10 + } + if lianxuEle == 3 { + rate += 20 + } else if lianxuEle == 4 { + rate += 500 + } + p.preWinRate -= int64(rate) * 9 + } else { + //不需要中 + for _, v := range p.preMaryMidArray { + for m, n := range ele { + if n == v { + ele = append(ele[:m], ele[m+1:]...) + break + } + } + } + idxEle := ele[rand.Intn(len(ele))] + for k, v := range fruits.MaryEleArray { + if v == idxEle && rand.Intn(100) > 50 { + p.preMaryOutSide = int32(k) + break + } + } + } +} +func (s *FruitsSceneData) GetEleWeight(needpos int32) (norms, frees, marys [][]int32, key int32) { + if needpos < 0 || needpos > 7 || !s.RealCtrl { + needpos = 0 + } + if needpos == 0 { + curCoin := base.CoinPoolMgr.GetCoin(s.GetGameFreeId(), s.Platform, s.GroupId) + curCoin = int64(math.Floor(float64(curCoin) / float64(fruits.NowByte))) + + for i := len(s.DbGameFree.BalanceLine) - 1; i >= 0; i-- { + balance := s.DbGameFree.BalanceLine[i] + if curCoin >= int64(balance) { + key = int32(i) + break + } + } + } else { + key = needpos - 1 + } + for _, ele := range srvdata.PBDB_SlotRateWeightMgr.Datas.GetArr() { + if ele.GameFreeId == s.GetGameFreeId() && ele.Pos == key { + norms = append(norms, ele.NormCol1) + norms = append(norms, ele.NormCol2) + norms = append(norms, ele.NormCol3) + norms = append(norms, ele.NormCol4) + norms = append(norms, ele.NormCol5) + + frees = append(frees, ele.FreeCol1) + frees = append(frees, ele.FreeCol2) + frees = append(frees, ele.FreeCol3) + frees = append(frees, ele.FreeCol4) + frees = append(frees, ele.FreeCol5) + + marys = append(marys, ele.MaryOut) + marys = append(marys, ele.MaryMid) + break + } + } + if norms == nil { + norms = fruits.EleWeight[:5] + frees = fruits.EleWeight[5:10] + marys = fruits.EleWeight[10:] + key = 0 + } + return +} diff --git a/gamesrv/fruits/scenepolicy_fruits.go b/gamesrv/fruits/scenepolicy_fruits.go new file mode 100644 index 0000000..50e69a2 --- /dev/null +++ b/gamesrv/fruits/scenepolicy_fruits.go @@ -0,0 +1,549 @@ +package fruits + +import ( + "math" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fruits" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + protocol "mongo.games.com/game/protocol/fruits" +) + +// //////////////////////////////////////////////////////////// +var ScenePolicyFruitsSington = &ScenePolicyFruits{} + +type ScenePolicyFruits struct { + base.BaseScenePolicy + states [fruits.FruitsStateMax]base.SceneState +} + +// 创建场景扩展数据 +func (this *ScenePolicyFruits) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewFruitsSceneData(s) + if sceneEx != nil { + if sceneEx.GetInit() { + s.SetExtraData(sceneEx) + } + } + return sceneEx +} + +// 创建玩家扩展数据 +func (this *ScenePolicyFruits) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &FruitsPlayerData{Player: p} + p.SetExtraData(playerEx) + return playerEx +} + +// 场景开启事件 +func (this *ScenePolicyFruits) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyFruits) OnStart, sceneId=", s.GetSceneId()) + sceneEx := NewFruitsSceneData(s) + if sceneEx != nil { + if sceneEx.GetInit() { + s.SetExtraData(sceneEx) + s.ChangeSceneState(fruits.FruitsStateStart) + } + } +} + +// 场景关闭事件 +func (this *ScenePolicyFruits) OnStop(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyFruits) OnStop , sceneId=", s.GetSceneId()) +} + +// 场景心跳事件 +func (this *ScenePolicyFruits) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnTick(s) + } +} + +// 玩家进入事件 +func (this *ScenePolicyFruits) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + playerEx := &FruitsPlayerData{Player: p} + playerEx.init() + playerEx.Clear() + + sceneEx.players[p.SnId] = playerEx + + p.SetExtraData(playerEx) + FruitsSendRoomInfo(s, sceneEx, playerEx) + + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } +} + +// 玩家离开事件 +func (this *ScenePolicyFruits) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*FruitsSceneData); ok { + s.FirePlayerEvent(p, base.PlayerEventLeave, nil) + sceneEx.OnPlayerLeave(p, reason) + } +} + +// 玩家掉线 +func (this *ScenePolicyFruits) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) +} + +// 玩家重连 +func (this *ScenePolicyFruits) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + if playerEx, ok := p.GetExtraData().(*FruitsPlayerData); ok { + FruitsSendRoomInfo(s, sceneEx, playerEx) + } + } +} + +// 返回房间 +func (this *ScenePolicyFruits) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerReturn, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + if playerEx, ok := p.GetExtraData().(*FruitsPlayerData); ok { + //if p.IsMarkFlag(base.PlayerState_Auto) { + // p.UnmarkFlag(base.PlayerState_Auto) + // p.SyncFlag() + //} + //发送房间信息给自己 + FruitsSendRoomInfo(s, sceneEx, playerEx) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func FruitsSendRoomInfo(s *base.Scene, sceneEx *FruitsSceneData, playerEx *FruitsPlayerData) { + pack := FruitsCreateRoomInfoPacket(s, sceneEx, playerEx) + logger.Logger.Trace("RoomInfo: ", pack) + playerEx.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsRoomInfo), pack) +} +func FruitsCreateRoomInfoPacket(s *base.Scene, sceneEx *FruitsSceneData, playerEx *FruitsPlayerData) interface{} { + //房间信息 + pack := &protocol.SCFruitsRoomInfo{ + RoomId: proto.Int(s.SceneId), + GameId: proto.Int(s.GameId), + RoomMode: proto.Int(s.SceneMode), + SceneType: proto.Int(s.SceneType), + Params: s.Params, + NumOfGames: proto.Int(sceneEx.NumOfGames), + State: proto.Int(s.SceneState.GetState()), + ParamsEx: s.DbGameFree.OtherIntParams, + GameFreeId: proto.Int32(s.DbGameFree.Id), + //BetLimit: s.DbGameFree.BetLimit, + } + + //自己的信息 + if playerEx != nil { + pd := &protocol.FruitsPlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.Coin), + Pos: proto.Int(playerEx.Pos), + Flag: proto.Int(playerEx.GetFlag()), + City: proto.String(playerEx.City), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + } + pack.Player = pd + pack.NowGameState = proto.Int(playerEx.gameState) + pack.BetIdx = proto.Int(playerEx.betIdx) + pack.FreeTimes = proto.Int(playerEx.freeTimes) + pack.MaryFreeTimes = proto.Int(playerEx.maryFreeTimes) + //玛丽 + pack.MaryTotalWin = proto.Int64(playerEx.winOutMaryCoin + playerEx.winMidMaryCoin) + pack.Cards = playerEx.result.EleValue + if playerEx.maryFreeTimes > 0 && playerEx.result != nil { + pack.MaryWinId = proto.Int32(playerEx.result.MaryOutSide) + pack.MaryCards = playerEx.result.MaryMidArray + } + //免费 当局的 + if playerEx.freeTimes > 0 && playerEx.result != nil { + pack.Cards = playerEx.result.EleValue + } + pack.WinLineCoin = proto.Int64(playerEx.winLineCoin) + pack.WinJackpot = proto.Int64(playerEx.winNowJackPotCoin) + pack.WinEle777Coin = proto.Int64(playerEx.winEle777Coin) + pack.FreeTotalWin = proto.Int64(playerEx.winFreeCoin) + pack.WinRate = proto.Int64(playerEx.winLineRate + playerEx.JackPot7Rate) + pack.WinFreeTimes = proto.Int(playerEx.winFreeTimes) + + var wl []*protocol.FruitsWinLine + for _, r := range playerEx.result.WinLine { + wl = append(wl, &protocol.FruitsWinLine{ + Poss: r.Poss, + LineId: proto.Int(r.LineId), + }) + } + pack.WinLines = wl + } + proto.SetDefaults(pack) + return pack +} +func (this *ScenePolicyFruits) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerOp, sceneId=", s.GetSceneId(), " player=", p.SnId, " opcode=", opcode, " params=", params) + if s.GetSceneState() != nil { + if s.GetSceneState().OnPlayerOp(s, p, opcode, params) { + p.SetLastOPTimer(time.Now()) + return true + } + return false + } + return true +} + +func (this *ScenePolicyFruits) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyFruits) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " eventcode=", evtcode, " params=", params) + if s.GetSceneState() != nil { + s.GetSceneState().OnPlayerEvent(s, p, evtcode, params) + } +} + +// 当前状态能否换桌 +func (this *ScenePolicyFruits) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + return false +} + +// 状态基类 +type SceneBaseStateFruits struct { +} + +func (this *SceneBaseStateFruits) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + return int(time.Now().Sub(sceneEx.GetStateStartTime()) / time.Second) + } + return 0 +} + +func (this *SceneBaseStateFruits) CanChangeTo(s base.SceneState) bool { + return true +} + +// 当前状态能否换桌 +func (this *SceneBaseStateFruits) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + return true +} +func (this *SceneBaseStateFruits) OnEnter(s *base.Scene) { + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + sceneEx.SetStateStartTime(time.Now()) + } +} + +func (this *SceneBaseStateFruits) OnLeave(s *base.Scene) {} +func (this *SceneBaseStateFruits) OnTick(s *base.Scene) { + if time.Now().Sub(s.GameStartTime) > time.Second*3 { + if sceneEx, ok := s.ExtraData.(*FruitsSceneData); ok { + prizePool := int64(math.Floor(float64(sceneEx.jackpot.GetTotalSmall()) / 10000)) + pack := &protocol.SCFruitsPrize{ + PrizePool: proto.Int64(prizePool), + } + proto.SetDefaults(pack) + //logger.Logger.Trace("SCFruitsPrize: ", pack) + s.Broadcast(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsPrize), pack, 0) + for _, p := range sceneEx.players { + if p.IsOnLine() { + p.leaveTime = 0 + continue + } + p.leaveTime++ + if p.leaveTime < 60*2 { + continue + } + //踢出玩家 + sceneEx.PlayerLeave(p.Player, common.PlayerLeaveReason_LongTimeNoOp, true) + } + } + s.GameStartTime = time.Now() + } + if sceneEx, ok := s.ExtraData.(*FruitsSceneData); ok { + //for _, p := range sceneEx.players { + // //游戏次数达到目标值 + // todayGamefreeIDSceneData, _ := p.GetDaliyGameData(int(sceneEx.DbGameFree.GetId())) + // if !p.IsRob && + // todayGamefreeIDSceneData != nil && + // sceneEx.DbGameFree.GetPlayNumLimit() != 0 && + // todayGamefreeIDSceneData.GameTimes >= int64(sceneEx.DbGameFree.GetPlayNumLimit()) { + // s.PlayerLeave(p.Player, common.PlayerLeaveReason_GameTimes, true) + // } + //} + if sceneEx.CheckNeedDestroy() { + for _, player := range sceneEx.players { + if !player.IsRob { + if player.freeTimes == 0 && player.maryFreeTimes == 0 { + //离开有统计 + sceneEx.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true) + } + } + } + if s.GetRealPlayerCnt() == 0 { + sceneEx.SceneDestroy(true) + } + } + } + //if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + // if time.Now().Sub(sceneEx.mqLogTime) >= time.Minute*1 { + // now := time.Now() + // sceneEx.mqLogTime = now.Add(-time.Second * time.Duration(now.Second())) + // + // waterVal := base.CoinPoolMgr.GetCoin(s.GetGameFreeId(), s.Platform, s.GroupId) + // waterVal = int64(math.Floor(float64(waterVal) / float64(fruits.NowByte))) + // + // jkVal1 := int64(math.Floor(float64(sceneEx.jackpot.GetTotalSmall()) / float64(fruits.NowByte))) + // base.JackPotMgr.WJackPotUpdate(sceneEx.GetGameFreeId(), sceneEx.Platform, waterVal, jkVal1, 0, 0, 0) + // } + //} +} +func (this *SceneBaseStateFruits) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + return false +} +func (this *SceneBaseStateFruits) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { +} + +// //////////////////////////////////////////////////////////// +// 开始状态 +// //////////////////////////////////////////////////////////// +type SceneStateStartFruits struct { + SceneBaseStateFruits +} + +func (this *SceneStateStartFruits) GetState() int { + return fruits.FruitsStateStart +} + +func (this *SceneStateStartFruits) CanChangeTo(s base.SceneState) bool { + return false +} + +// 当前状态能否换桌 +func (this *SceneStateStartFruits) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if playerEx, ok := p.GetExtraData().(*FruitsPlayerData); ok { + if playerEx.IsOnLine() && (playerEx.freeTimes > 0 || playerEx.maryFreeTimes > 0) { + return false + } + } + return true +} + +func (this *SceneStateStartFruits) GetTimeout(s *base.Scene) int { + return 0 +} + +func (this *SceneStateStartFruits) OnEnter(s *base.Scene) { + this.SceneBaseStateFruits.OnEnter(s) + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + sceneEx.SetGameNowTime(time.Now()) + } +} + +// 状态离开时 +func (this *SceneStateStartFruits) OnLeave(s *base.Scene) { + this.SceneBaseStateFruits.OnLeave(s) + logger.Logger.Tracef("(this *SceneStateStartFruits) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneStateStartFruits) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + logger.Logger.Tracef("(this *SceneStateStartFruits) OnPlayerOp, sceneid=%v params=%v", s.GetSceneId(), params) + if this.SceneBaseStateFruits.OnPlayerOp(s, p, opcode, params) { + return true + } + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + if playerEx, ok := p.GetExtraData().(*FruitsPlayerData); ok { + switch opcode { + case fruits.FruitsPlayerOpStart: + playerEx.Clear() + if len(params) > 0 { + //只有开始算操作 + p.LastOPTimer = time.Now() + idx := int(params[0]) + if len(sceneEx.DbGameFree.GetOtherIntParams()) <= idx { + pack := &protocol.SCFruitsOp{ + OpCode: proto.Int(opcode), + OpRetCode: proto.Int(3), + } + playerEx.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsOp), pack) + return false + } + if playerEx.maryFreeTimes > 0 { + playerEx.gameState = fruits.MaryGame + playerEx.maryFreeTimes-- + playerEx.nowMaryTimes++ + } else if playerEx.freeTimes > 0 { + playerEx.gameState = fruits.FreeGame + playerEx.freeTimes-- + playerEx.nowFreeTimes++ + } + //水池上下文环境 + playerEx.cpCtx = base.CoinPoolMgr.GetCoinPoolCtx(sceneEx.Platform, sceneEx.GetGameFreeId(), sceneEx.GroupId) + if playerEx.gameState == fruits.Normal { + playerEx.freeTotal = 0 + playerEx.betIdx = idx + playerEx.betCoin = int64(sceneEx.DbGameFree.GetOtherIntParams()[idx]) + playerEx.oneBetCoin = playerEx.betCoin / 9 + //playerEx.isReportGameEvent = true + playerEx.noWinTimes++ + + if playerEx.Coin < int64(s.DbGameFree.GetBetLimit()) { + //押注限制(低于该值不能押注) + pack := &protocol.SCFruitsOp{ + OpCode: proto.Int(opcode), + OpRetCode: proto.Int(2), + } + playerEx.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsOp), pack) + return false + } + if playerEx.betCoin > playerEx.Coin { + //金币不足 + pack := &protocol.SCFruitsOp{ + OpCode: proto.Int(opcode), + OpRetCode: proto.Int(1), + } + playerEx.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsOp), pack) + return false + } + playerEx.CurrentBet = playerEx.betCoin + playerEx.CurrentTax = 0 + //SysProfitCoinMgr.PushBetAndSysOut(sceneEx.sysProfitCoinKey, playerEx.betCoin, -playerEx.betCoin) + //没有免费次数 扣钱 + p.Statics(s.KeyGameId, s.KeyGamefreeId, -playerEx.betCoin, false) + playerEx.AddCoin(-playerEx.betCoin, common.GainWay_HundredSceneLost, 0, "system", s.GetSceneName()) + sceneEx.AddPrizeCoin(playerEx) + } + if !s.Testing { + //sceneEx.Regulation(playerEx, 0) + } + norms, frees, marys, key := sceneEx.GetEleWeight(playerEx.WBState) + playerEx.weightPos = key + if playerEx.gameState == fruits.MaryGame { + playerEx.result.CreateMary(marys) + playerEx.CreateMary() + } else if playerEx.gameState == fruits.FreeGame { + playerEx.freeTotal++ + playerEx.CreateResult(frees, sceneEx) + } else { + playerEx.CreateResult(norms, sceneEx) + } + + sceneEx.Win(playerEx) + + if playerEx.gameState == fruits.FreeGame && playerEx.freeTotal >= 100 { + playerEx.freeTotal = 0 + playerEx.freeTimes = 0 + } + //发送结算 + sceneEx.SendBilled(playerEx) + + sceneEx.SaveLog(playerEx, 0) + + if playerEx.gameState == fruits.Normal { + playerEx.winJackPotCoin = 0 + playerEx.winEle777Coin = 0 + playerEx.winLineCoin = 0 + } + playerEx.wl = nil + } + case fruits.FruitsPlayerOpSwitch: + if len(params) > 0 && playerEx.freeTimes == 0 && playerEx.maryFreeTimes == 0 { + idx := int(params[0]) + if len(sceneEx.DbGameFree.GetOtherIntParams()) > idx { + playerEx.betIdx = idx + playerEx.betCoin = int64(sceneEx.DbGameFree.GetOtherIntParams()[idx]) + playerEx.oneBetCoin = playerEx.betCoin / 9 + } + } + } + } + } + return true +} + +// 玩家事件 +func (this *SceneStateStartFruits) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneStateStartFruits) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode) + this.SceneBaseStateFruits.OnPlayerEvent(s, p, evtcode, params) + if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok { + switch evtcode { + case base.PlayerEventEnter: + prizePool := int64(math.Floor(float64(sceneEx.jackpot.GetTotalSmall()) / 10000)) + pack := &protocol.SCFruitsPrize{ + PrizePool: proto.Int64(prizePool), + } + proto.SetDefaults(pack) + //logger.Logger.Trace("SCFruitsPrize: ", pack) + p.SendToClient(int(protocol.FruitsPID_PACKET_FRUITS_SCFruitsPrize), pack) + } + } +} + +func (this *SceneStateStartFruits) OnTick(s *base.Scene) { + this.SceneBaseStateFruits.OnTick(s) +} + +// ////////////////////////////////////////////////////////////////////////////// +func (this *ScenePolicyFruits) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + if stateid < 0 || stateid >= fruits.FruitsStateMax { + return + } + this.states[stateid] = state +} + +func (this *ScenePolicyFruits) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < fruits.FruitsStateMax { + return this.states[stateid] + } + return nil +} + +func init() { + //主状态 + ScenePolicyFruitsSington.RegisteSceneState(&SceneStateStartFruits{}) + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameId_Fruits, fruits.RoomMode_Classic, ScenePolicyFruitsSington) + return nil + }) +} diff --git a/gamesrv/iceage/action_iceage.go b/gamesrv/iceage/action_iceage.go new file mode 100644 index 0000000..fb02b54 --- /dev/null +++ b/gamesrv/iceage/action_iceage.go @@ -0,0 +1,54 @@ +package iceage + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/iceage" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +// 冰河世纪的操作 +type CSIceAgeOpPacketFactory struct { +} +type CSIceAgeOpHandler struct { +} + +func (this *CSIceAgeOpPacketFactory) CreatePacket() interface{} { + pack := &iceage.CSIceAgeOp{} + return pack +} +func (this *CSIceAgeOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSIceAgeOpHandler Process recv ", data) + if csIceAgeOp, ok := data.(*iceage.CSIceAgeOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSIceAgeOpHandler p == nil") + return nil + } + scene := p.GetScene() + sp := scene.GetScenePolicy() + if scene == nil { + logger.Logger.Warn("CSIceAgeOpHandler p.scene == nil") + return nil + } + if scene.GameId != common.GameId_IceAge { + logger.Logger.Error("CSIceAgeOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + if sp != nil { + sp.OnPlayerOp(scene, p, int(csIceAgeOp.GetOpCode()), csIceAgeOp.GetParams()) + } + return nil + } + return nil +} + +func init() { + // 冰河世纪的操作 + common.RegisterHandler(int(iceage.IceAgePacketID_PACKET_CS_ICEAGE_PLAYEROP), &CSIceAgeOpHandler{}) + netlib.RegisterFactory(int(iceage.IceAgePacketID_PACKET_CS_ICEAGE_PLAYEROP), &CSIceAgeOpPacketFactory{}) +} diff --git a/gamesrv/iceage/constant.go b/gamesrv/iceage/constant.go new file mode 100644 index 0000000..df0aace --- /dev/null +++ b/gamesrv/iceage/constant.go @@ -0,0 +1,90 @@ +package iceage + +import ( + "encoding/json" + "mongo.games.com/game/model" + "time" +) + +// 玩家游戏数据索引 +const ( + IAFreeTimes int = iota //0 当前剩余免费次数 + IAIndexMax +) + +// 通知奖池变化的时间间隔 +const jackpotNoticeInterval = time.Second + +// 场景状态 +const ( + IceAgeSceneStateStart int = iota //开始游戏 + IceAgeSceneStateMax +) + +// 玩家操作 +const ( + IceAgePlayerOpStart int = iota //游戏 + IceAgePlayerHistory // 游戏记录 + IceAgeBurstHistory // 大奖记录 (已废弃) + IceAgeBonusGame // 小游戏结束 + IceAgeBonusGameStart // 小游戏开始 +) + +// 小游戏超时时间 +const IceAgeBonusGameTimeout = time.Second * 15 + +type GameResultLog struct { + BaseResult *model.SlotBaseResultType + AllLine int32 //线路数 + UserName string //昵称 + BetLines []int64 //下注的线 + Cards [][]int32 // 消除前后的牌(消除前15张,消除后15张...) + WinLines [][]int // 赢分的线 +} + +// 冰河世纪解析的数据 +type IceAgeGameNoteData struct { + Source int32 + Data *GameResultLog +} + +// 冰河世纪游戏记录 +func UnMarshalIceAgeGameNote(data string) (roll interface{}, err error) { + gnd := &IceAgeGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 游戏测试数据 小游戏 +var DebugBonusData = []int{ + 7, 5, 7, 5, 4, + 3, 4, 4, 7, 6, + 1, 1, 1, 4, 6, +} + +var DebugData = [][]int{ + {1, 1, 1, 4, 5, + 2, 3, 5, 4, 6, + 1, 1, 1, 4, 3}, + {2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4}, + {5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5}, + {6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6}, + {7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7}, +} diff --git a/gamesrv/iceage/playerdata_iceage.go b/gamesrv/iceage/playerdata_iceage.go new file mode 100644 index 0000000..acba5cf --- /dev/null +++ b/gamesrv/iceage/playerdata_iceage.go @@ -0,0 +1,153 @@ +package iceage + +import ( + "encoding/json" + "math/rand" + rule "mongo.games.com/game/gamerule/iceage" + base "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/iceage" + "mongo.games.com/goserver/core/timer" + "strconv" + //"mongo.games.com/game/common" +) + +type IceAgePlayerData struct { + *base.Player + spinID int64 //当前旋转ID + score int32 //单线押注数 + freeTimes int32 //免费转动次数 + cards []*iceage.IceAgeCards //15张牌 + totalPriceBonus int64 //小游戏奖金 + bonusTimerHandle timer.TimerHandle //托管handle + //RollGameType *model.IceAgeType //记录信息 + RollGameType *GameResultLog //记录信息 + enterGameCoin int64 //玩家进入初始金币 + betLines []int64 //下注的选线 + taxCoin int64 //本局税收 + winCoin int64 //本局收税前赢的钱 + linesWinCoin int64 //本局中奖线赢得钱 + jackpotWinCoin int64 //本局奖池赢的钱 + smallGameWinCoin int64 //本局小游戏赢的钱 + currentLogId string //爆奖玩家logid + leavetime int32 //用户离开时间 + billedData *iceage.GameBilledData //上一局结算信息 + BonusLineIdx int64 //bonus中奖线索引 + DebugBonus bool //测试进入bonus + TestNum int +} + +// 玩家初始化 +func (this *IceAgePlayerData) init(s *base.Scene) { + this.Clean() + this.score = 0 + this.freeTimes = 0 + //this.RollGameType = &model.IceAgeType{} + this.RollGameType = &GameResultLog{} + this.RollGameType.BaseResult = &model.SlotBaseResultType{} + this.enterGameCoin = this.GetCoin() + this.currentLogId = "" + this.DebugBonus = true + this.TestNum = 0 + this.billedData = &iceage.GameBilledData{} + + initCards := getSlotsDataByGroupName(DefaultData_v1) + cards := make([]int32, 0) + for _, card := range initCards { + cards = append(cards, int32(card)) + } + this.cards = []*iceage.IceAgeCards{ + { + Card: cards, + }, + } + + // 加载玩家游戏数据 + if this.GDatas == nil { + this.GDatas = make(map[string]*model.PlayerGameInfo) + } + gameFreeID := strconv.Itoa(int(s.GetGameFreeId())) + if d, exist := this.GDatas[gameFreeID]; exist { + gLen := len(d.Data) + if gLen < IAIndexMax { + for i := gLen; i < IAIndexMax; i++ { + d.Data = append(d.Data, 0) + } + } + } else { + pgd := &model.PlayerGameInfo{ + Data: make([]int64, IAIndexMax, IAIndexMax), + } + this.GDatas[gameFreeID] = pgd + } + this.LoadPlayerGameData(gameFreeID) + + if len(this.betLines) == 0 { + this.betLines = make([]int64, rule.LINENUM) + for i := range this.betLines { + this.betLines[i] = int64(i + 1) + } + } +} + +// 玩家清理数据 +func (this *IceAgePlayerData) Clean() { + for i := 0; i < len(this.cards); i++ { + this.cards[i] = nil + } + this.winCoin = 0 + this.taxCoin = 0 + this.linesWinCoin = 0 + this.jackpotWinCoin = 0 + this.smallGameWinCoin = 0 +} + +// 加载玩家游戏数据 +func (this *IceAgePlayerData) LoadPlayerGameData(gameFreeId string) { + if d, exist := this.GDatas[gameFreeId]; exist { + this.freeTimes = int32(d.Data[IAFreeTimes]) + if this.freeTimes > 0 && len(d.DataEx) != 0 { + json.Unmarshal(d.DataEx, &this.betLines) + } + } +} + +// 存储玩家游戏数据 +func (this *IceAgePlayerData) SavePlayerGameData(gameFreeId string) { + if d, exist := this.GDatas[gameFreeId]; exist { + d.Data[IAFreeTimes] = int64(this.freeTimes) + d.DataEx, _ = json.Marshal(this.betLines) + } +} + +// 黑白名单的限制是否生效 +func (this *IceAgePlayerData) CheckBlackWriteList(isWin bool) bool { + if isWin && this.BlackLevel > 0 && this.BlackLevel <= 10 { + if rand.Int31n(100) < this.BlackLevel*10 { + return true + } + } else if !isWin && this.WhiteLevel > 0 && this.WhiteLevel <= 10 { + if rand.Int31n(100) < this.WhiteLevel*10 { + return true + } + } + return false +} + +// 玩家是否是新手 +func (this *IceAgePlayerData) IsFNovice(keyGameId string) { + if f, ok := this.IsFoolPlayer[keyGameId]; ok { + if !f { + return + } + } else { + this.IsFoolPlayer[keyGameId] = true + return + } + if data, ok := this.GDatas[keyGameId]; ok { + if data.Statics.GameTimes < 100 && data.Statics.TotalOut-data.Statics.TotalIn <= 2000000 { + return + } + this.IsFoolPlayer[keyGameId] = false + } +} diff --git a/gamesrv/iceage/scenedata_iceage.go b/gamesrv/iceage/scenedata_iceage.go new file mode 100644 index 0000000..d1d9a1e --- /dev/null +++ b/gamesrv/iceage/scenedata_iceage.go @@ -0,0 +1,389 @@ +package iceage + +import ( + "encoding/json" + "math" + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/iceage" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + "time" + + rule "mongo.games.com/game/gamerule/iceage" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" +) + +type IceAgeJackpot struct { + createdTime time.Time + userName string + priceValue int64 + roomID int64 + spinID string +} + +type IceAgeSceneData struct { + *base.Scene //房间信息 + players map[int32]*IceAgePlayerData //玩家信息 + jackpot *base.SlotJackpotPool //奖池 + lastJackpotValue int64 //上一次奖池变化时的值 + lastJackPot time.Time //增加奖池时间 + lastBurstJackPot map[int32]time.Time //爆池时间 +} + +func NewIceAgeSceneData(s *base.Scene) *IceAgeSceneData { + return &IceAgeSceneData{ + Scene: s, + players: make(map[int32]*IceAgePlayerData), + } +} + +func (this *IceAgeSceneData) SaveData(force bool) { +} + +func (this *IceAgeSceneData) OnPlayerLeave(p *base.Player, reason int) { + if p, exist := this.players[p.SnId]; exist { + delete(this.players, p.SnId) + } +} + +func (this *IceAgeSceneData) SceneDestroy(force bool) { + //销毁房间 + this.Scene.Destroy(force) +} + +func (this *IceAgeSceneData) init() bool { + if this.GetDBGameFree() == nil { + return false + } + params := this.GetDBGameFree().GetJackpot() + this.jackpot = &base.SlotJackpotPool{} + if this.jackpot.Small <= 0 { + this.jackpot.Small = 0 + this.jackpot.VirtualJK = int64(params[rule.ICEAGE_JACKPOT_InitJackpot] * this.DbGameFree.GetBaseScore()) + } + + str := base.SlotsPoolMgr.GetPool(this.GetGameFreeId(), this.GetPlatform()) + if str != "" { + jackpot := &base.SlotJackpotPool{} + err := json.Unmarshal([]byte(str), jackpot) + if err == nil { + this.jackpot = jackpot + } + } + + if this.jackpot != nil { + base.SlotsPoolMgr.SetPool(this.GetGameFreeId(), this.GetPlatform(), this.jackpot) + } + this.lastJackPot = time.Now() + this.lastBurstJackPot = make(map[int32]time.Time) + this.SetLastBurstJackPot() + return true +} + +const ( + SlotsData = "SlotsData" + SlotsData_V2 = "SlotsData_V2" + BonusData = "BonusData" + BonusData_V2 = "BonusData_V2" + DefaultData = "DefaultData" + DefaultData_v1 = "DefaultData_v1" +) + +func getElementDistributionByName(name string) *server.DB_IceAgeElementRate { + for i := range srvdata.PBDB_IceAgeElementRateMgr.Datas.Arr { + item := srvdata.PBDB_IceAgeElementRateMgr.GetData(int32(i + 1)) + if item.GetModeName() == name { + return item + } + } + return nil +} +func getSlotsDataByElementDistribution(data []int32) []int { + var t = make([]int, rule.ELEMENT_TOTAL) + for i, _ := range t { + t[i] = int(data[rand.Intn(len(data))]) + } + return t +} +func getSlotsDataByGroupName(name string) []int { + cardLib := rule.MissData[name] + if len(cardLib) > 0 { + return cardLib[rand.Intn(len(cardLib))] + } + return nil +} + +type SpinResult struct { + LinesInfo []*iceage.IceAgeLinesInfo + SlotsData []*iceage.IceAgeCards + TotalPrizeLine int64 // 线条总金额+爆奖 + TotalPrizeBonus int64 // 小游戏总金额 + BonusGameCnt int32 // 小游戏次数 + TotalPrizeJackpot int64 // 爆奖总金额 + JackpotCnt int // 爆奖的次数 + AddFreeTimes int32 // 新增免费次数 + IsJackpot bool // 是否爆奖 + TotalWinRate int32 // 中奖总倍率 + TotalTaxScore int64 // 税收 + WinLines [][]int // 赢分的线 +} + +func (this *IceAgeSceneData) CalcSpinsPrize(cards []int, betLines []int64, distributionData []int32, bonusParams []int32, betValue int64, taxRate int32) (spinRes SpinResult) { + + tmpCards := make([]int, len(cards)) + copy(tmpCards, cards) + + calcTaxScore := func(score int64, taxScore *int64) int64 { + tmpTaxScore := int64(float64(score) * float64(taxRate) / 10000.0) + *taxScore += tmpTaxScore + return score - tmpTaxScore + } + + for loopIndex := 0; loopIndex < 99; loopIndex++ { // 避免死循环 + var winLines []int + slotData := &iceage.IceAgeCards{} + for _, card := range tmpCards { + slotData.Card = append(slotData.Card, int32(card)) + } + spinRes.SlotsData = append(spinRes.SlotsData, slotData) + + lineCount := 0 + for _, lineNum := range betLines { + lineTemplate := rule.AllLineArray[int(lineNum)-1] + edata := []int{} + epos := []int32{} + for _, pos := range lineTemplate { + edata = append(edata, tmpCards[pos]) + epos = append(epos, int32(pos)+1) + } + head, count := rule.IsLine(edata) + if head == 0 || count <= 0 { + continue + } + + var spinFree, prizeJackpot, bonus int64 + var prizesBonus = make([]int64, 0) + if head == rule.Element_FREESPIN { + spinFree = int64(rule.FreeSpinTimesRate[count-1]) + } else if head == rule.Element_JACKPOT && count == 5 { + spinRes.IsJackpot = true + spinRes.JackpotCnt++ + if spinRes.TotalPrizeJackpot == 0 { // 第一个爆奖 获取当前奖池所有 + prizeJackpot = this.jackpot.VirtualJK + } else { // 之后的爆奖 奖励为奖池初值 + prizeJackpot = int64(this.GetDBGameFree().GetJackpot()[rule.ICEAGE_JACKPOT_InitJackpot] * this.GetDBGameFree().GetBaseScore()) + } + prizeJackpot = calcTaxScore(prizeJackpot, &spinRes.TotalTaxScore) + spinRes.TotalPrizeJackpot += prizeJackpot + } else if head == rule.Element_BONUS && count >= 3 { + bonus = betValue * int64(len(betLines)) * int64(bonusParams[rand.Intn(len(bonusParams))]) + bonus = calcTaxScore(bonus, &spinRes.TotalTaxScore) + prizesBonus = []int64{bonus, bonus, bonus} // len大于0即可 + spinRes.BonusGameCnt++ + } + + curScore := int64(rule.LineScore[head][count-1]) * betValue + curScore = calcTaxScore(curScore, &spinRes.TotalTaxScore) + spinRes.TotalWinRate += int32(rule.LineScore[head][count-1]) + spinRes.TotalPrizeLine += curScore + prizeJackpot + spinRes.TotalPrizeBonus += bonus + spinRes.AddFreeTimes += int32(spinFree) + + line := &iceage.IceAgeLinesInfo{ + LineID: proto.Int32(int32(lineNum)), + Turn: proto.Int32(int32(loopIndex + 1)), + PrizeValue: proto.Int64(curScore + prizeJackpot), + PrizesFreespin: proto.Int64(spinFree), + PrizesJackport: proto.Int64(prizeJackpot), + PrizesBonus: prizesBonus, + Items: epos[:count], + RoleID: proto.Int32(int32(head)), + } + spinRes.LinesInfo = append(spinRes.LinesInfo, line) + winLines = append(winLines, int(line.GetLineID())) + lineCount++ + } + if winLines != nil { + spinRes.WinLines = append(spinRes.WinLines, winLines) + } + + if loopIndex == 98 { + logger.Logger.Error("IceAgeSpinTimes99", cards, betLines, distributionData, bonusParams, betValue, taxRate) + } + + // 没有匹配的线条 不用消除处理 + if lineCount == 0 { + break + } + + // 获取消除后的新线 + tmpCards = rule.MakePlan(tmpCards, distributionData, betLines) + if len(tmpCards) == 0 { + break + } + } + return +} + +func (this *IceAgeSceneData) BroadcastJackpot(sync bool) { + if this.lastJackpotValue != this.jackpot.VirtualJK || sync { + this.lastJackpotValue = this.jackpot.VirtualJK + pack := &gamehall.SCHundredSceneGetGameJackpot{} + jpfi := &gamehall.GameJackpotFundInfo{ + GameFreeId: proto.Int32(this.DbGameFree.Id), + JackPotFund: proto.Int64(this.jackpot.VirtualJK), + } + pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) + proto.SetDefaults(pack) + //以平台为标识向该平台内所有玩家广播奖池变动消息,游戏内外的玩家可监听该消息,减少由gamesrv向worldsrv转发这一步 + tags := []string{this.Platform} + logger.Logger.Trace("jackpot avengers", pack) + base.PlayerMgrSington.BroadcastMessageToGroup(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, tags) + } +} +func (this *IceAgeSceneData) PushCoinPool(prizeFundAdd int64, IsNovice bool) { + if IsNovice { + base.CoinPoolMgr.PushCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd) + } else { + base.CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd) + } +} +func (this *IceAgeSceneData) PopCoinPool(winCoin int64, IsNovice bool) { + if IsNovice { + base.CoinPoolMgr.PopCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin) + } else { + base.CoinPoolMgr.PopCoin(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin) + } +} +func (this *IceAgeSceneData) RecordBurstLog(name string, wincoin, totalbet int64) { + log := model.NewBurstJackpotLog(this.Platform, this.DbGameFree.GameId, this.GetGameFreeId(), name, wincoin, totalbet) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertBurstJackpotLogs(log) + }), nil, "InsertBurstJackpotLogs").Start() +} + +func (this *IceAgeSceneData) BurstHistory(player *IceAgePlayerData) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.GetBurstJackpotLog(this.Platform, this.DbGameFree.GameId) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + var logsp []*iceage.IceAgeBurstHistoryInfo + if data != nil { + logs := data.([]model.BurstJackpotLog) + if len(logs) > 0 { + for _, log := range logs { + logsp = append(logsp, &iceage.IceAgeBurstHistoryInfo{ + UserName: log.Name, + PriceValue: log.WinCoin, + TotalBet: log.TotalBet, + Ts: log.Ts, + }) + } + } + } + pack := &iceage.SCIceAgeBurstHistoryInfo{ + BurstLog: logsp, + } + logger.Logger.Trace("SCIceAgeBurstHistoryInfo:", pack) + player.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_BURSTHISTORY), pack) + }), "BurstHistory").Start() +} +func (this *IceAgeSceneData) GetLastBurstJackPot() time.Time { + return this.lastBurstJackPot[this.GetGameFreeId()] +} +func (this *IceAgeSceneData) SetLastBurstJackPot() { + var randT = rand.Intn(25200-7200+1) + 7200 + switch this.DbGameFree.SceneType { + case 1: + randT = rand.Intn(25200-7200+1) + 7200 + case 2: + randT = rand.Intn(108000-72000+1) + 72000 + case 3: + randT = rand.Intn(180000-108000+1) + 108000 + } + this.lastBurstJackPot[this.GetGameFreeId()] = time.Now().Add(time.Second * time.Duration(randT)) +} +func (this *IceAgeSceneData) AIAddJackPot() { + if time.Now().Sub(this.lastJackPot) > 0 { + var randT = rand.Intn(3) + 1 + switch this.DbGameFree.SceneType { + case 1: + randT = rand.Intn(3) + 1 + case 2: + randT = rand.Intn(12-5) + 6 + case 3: + randT = rand.Intn(20-9) + 10 + default: + randT = rand.Intn(3) + 1 + } + this.lastJackPot = time.Now().Add(time.Second * time.Duration(randT)) + //AI机器人 + setting := base.CoinPoolMgr.GetCoinPoolSetting(this.Platform, this.GetGameFreeId(), this.GroupId) + var ctroRate int32 + if setting != nil { + ctroRate = 9800 + } + var val int64 + val = int64(math.Floor(float64(this.DbGameFree.GetBaseScore()) * float64(rule.LINENUM) * float64(ctroRate) / 10000)) + this.jackpot.VirtualJK += val + } +} +func (this *IceAgeSceneData) AIBurstJackPot() { + if time.Now().Sub(this.GetLastBurstJackPot()) > 0 { + this.SetLastBurstJackPot() + jackpotParams := this.DbGameFree.GetJackpot() + var jackpotInit = int64(jackpotParams[rule.ICEAGE_JACKPOT_InitJackpot] * this.DbGameFree.GetBaseScore()) //奖池初始值 + + //AI机器人爆奖 + val := this.jackpot.VirtualJK + this.jackpot.VirtualJK = jackpotInit + bet := this.DbGameFree.GetBaseScore() * int32(rule.LINENUM) + this.RecordBurstLog(this.RandNickName(), val, int64(bet)) + } +} +func (this *IceAgeSceneData) KickPlayerByTime() { + if time.Now().Sub(this.GameStartTime) > time.Second*3 { + this.GameStartTime = time.Now() + for _, p := range this.players { + if p.IsOnLine() { + p.leavetime = 0 + continue + } + p.leavetime++ + if p.leavetime < 60 { + continue + } + //踢出玩家 + this.PlayerLeave(p.Player, common.PlayerLeaveReason_LongTimeNoOp, true) + } + //for _, p := range this.players { + // //游戏次数达到目标值 + // todayGamefreeIDSceneData, _ := p.GetDaliyGameData(int(this.DbGameFree.GetId())) + // if !p.IsRob && + // todayGamefreeIDSceneData != nil && + // this.DbGameFree.GetPlayNumLimit() != 0 && + // todayGamefreeIDSceneData.GameTimes >= int64(this.DbGameFree.GetPlayNumLimit()) { + // this.PlayerLeave(p.Player, common.PlayerLeaveReason_GameTimes, true) + // } + //} + if this.CheckNeedDestroy() { + for _, player := range this.players { + if !player.IsRob { + if time.Now().Sub(player.LastOPTimer) > 10*time.Second { + //离开有统计 + this.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true) + } + } + } + if this.GetRealPlayerCnt() == 0 { + this.SceneDestroy(true) + } + } + } +} diff --git a/gamesrv/iceage/scenepolicy_iceage.go b/gamesrv/iceage/scenepolicy_iceage.go new file mode 100644 index 0000000..5561bcc --- /dev/null +++ b/gamesrv/iceage/scenepolicy_iceage.go @@ -0,0 +1,1004 @@ +package iceage + +import ( + "fmt" + "math" + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/iceage" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/iceage" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/timer" + "os" + "strconv" + "sync" + "time" +) + +//////////////////////////////////////////////////////////////////////////////// +//冰河世纪 +//////////////////////////////////////////////////////////////////////////////// + +// 房间内主要逻辑 +var ScenePolicyIceAgeSington = &ScenePolicyIceAge{} + +type ScenePolicyIceAge struct { + base.BaseScenePolicy + states [IceAgeSceneStateMax]base.SceneState +} + +// 创建场景扩展数据 +func (this *ScenePolicyIceAge) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewIceAgeSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.SetExtraData(sceneEx) + } + } + return sceneEx +} + +// 创建玩家扩展数据 +func (this *ScenePolicyIceAge) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &IceAgePlayerData{Player: p} + if playerEx != nil { + p.SetExtraData(playerEx) + } + return playerEx +} + +// 场景开启事件 +func (this *ScenePolicyIceAge) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyIceAge) OnStart, sceneId=", s.GetSceneId()) + sceneEx := NewIceAgeSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.SetExtraData(sceneEx) + //sceneEx.BroadcastJackpot() + s.ChangeSceneState(IceAgeSceneStateStart) //改变当前的玩家状态 + } + } +} + +// 场景关闭事件 +func (this *ScenePolicyIceAge) OnStop(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyIceAge) OnStop , sceneId=", s.GetSceneId()) + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + sceneEx.SaveData(true) + } +} + +// 场景心跳事件 +func (this *ScenePolicyIceAge) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnTick(s) + } +} + +// 玩家进入事件 +func (this *ScenePolicyIceAge) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyIceAge) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.SnId, s.GetGameId()) + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + playerEx := &IceAgePlayerData{Player: p} + playerEx.init(s) // 玩家当前信息初始化 + playerEx.score = sceneEx.GetDBGameFree().GetBaseScore() // 底注初始化 + sceneEx.players[p.SnId] = playerEx + p.SetExtraData(playerEx) + IceAgeSendRoomInfo(s, p, sceneEx, playerEx, nil) + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) //回调会调取 onPlayerEvent事件 + } +} + +// 玩家离开事件 +func (this *ScenePolicyIceAge) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyIceAge) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + if this.CanChangeCoinScene(s, p) { + if playerEx, ok := p.GetExtraData().(*IceAgePlayerData); ok { + playerEx.SavePlayerGameData(strconv.Itoa(int(s.GetGameFreeId()))) + } + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, []int64{int64(reason)}) + } + } +} + +// 玩家掉线 +func (this *ScenePolicyIceAge) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyIceAge) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + if sceneEx.GetGaming() { + return + } + } +} + +// 玩家重连 +func (this *ScenePolicyIceAge) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyIceAge) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.Name) + //gs 玩家rehold的时候不再发送 RoomInfo消息 + if _, ok := s.GetExtraData().(*IceAgeSceneData); ok { + if _, ok := p.GetExtraData().(*IceAgePlayerData); ok { + //发送房间信息给自己 + //IceAgeSendRoomInfo(s, p, sceneEx, playerEx) + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +// 玩家返回房间 gs添加 +func (this *ScenePolicyIceAge) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyIceAge) OnPlayerReturn,sceneId =", s.GetSceneId(), " player= ", p.Name) + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + if playerEx, ok := p.GetExtraData().(*IceAgePlayerData); ok { + IceAgeSendRoomInfo(s, p, sceneEx, playerEx, playerEx.billedData) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +// 玩家操作 +func (this *ScenePolicyIceAge) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().OnPlayerOp(s, p, opcode, params) + } + return true +} + +// 玩家事件 +func (this *ScenePolicyIceAge) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnPlayerEvent(s, p, evtcode, params) + } +} + +// 是否完成了整个牌局 +func (this *ScenePolicyIceAge) IsCompleted(s *base.Scene) bool { return false } + +// 是否可以强制开始 +func (this *ScenePolicyIceAge) IsCanForceStart(s *base.Scene) bool { return true } + +// 当前状态能否换桌 +func (this *ScenePolicyIceAge) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return true + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + return true +} + +func (this *ScenePolicyIceAge) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + if stateid < 0 || stateid >= IceAgeSceneStateMax { + return + } + this.states[stateid] = state +} + +func (this *ScenePolicyIceAge) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < IceAgeSceneStateMax { + return ScenePolicyIceAgeSington.states[stateid] + } + return nil +} +func (this *ScenePolicyIceAge) GetJackPotVal(s *base.Scene) int64 { + if sceneEx, ok := s.ExtraData.(*IceAgeSceneData); ok { + if sceneEx.lastJackpotValue != sceneEx.jackpot.VirtualJK { + return sceneEx.jackpot.VirtualJK + } + } + return 0 +} +func IceAgeSendRoomInfo(s *base.Scene, p *base.Player, sceneEx *IceAgeSceneData, playerEx *IceAgePlayerData, data *iceage.GameBilledData) { + logger.Logger.Trace("-------------------发送房间消息 ", s.RoomId, p.SnId) + pack := &iceage.SCIceAgeRoomInfo{ + RoomId: proto.Int(s.GetSceneId()), + Creator: proto.Int32(s.GetCreator()), + GameId: proto.Int(s.GetGameId()), + RoomMode: proto.Int(s.GetSceneMode()), + Params: s.GetParams(), + State: proto.Int(s.GetSceneState().GetState()), + Jackpot: proto.Int64(sceneEx.jackpot.VirtualJK), + GameFreeId: proto.Int32(s.GetDBGameFree().GetId()), + BilledData: data, + } + if playerEx != nil { + pd := &iceage.IceAgePlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.GetCoin()), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + } + pack.Players = append(pack.Players, pd) + pack.BetLines = playerEx.betLines + pack.FreeTimes = proto.Int32(playerEx.freeTimes) + pack.Chip = proto.Int32(s.DbGameFree.BaseScore) + pack.TotalPriceBonus = proto.Int64(playerEx.totalPriceBonus) + pack.SpinID = proto.Int64(playerEx.spinID) + } + proto.SetDefaults(pack) + p.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_ROOMINFO), pack) +} + +type SceneStateIceAgeStart struct { +} + +// 获取当前场景状态 +func (this *SceneStateIceAgeStart) GetState() int { return IceAgeSceneStateStart } + +// 是否可以切换状态到 +func (this *SceneStateIceAgeStart) CanChangeTo(s base.SceneState) bool { return true } + +// 当前状态能否换桌 +func (this *SceneStateIceAgeStart) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + return true +} + +func (this *SceneStateIceAgeStart) GetTimeout(s *base.Scene) int { return 0 } + +func (this *SceneStateIceAgeStart) OnEnter(s *base.Scene) { + if sceneEx, ok := s.GetExtraData().(*IceAgeSceneData); ok { + logger.Logger.Tracef("(this *base.Scene) [%v] 场景状态进入 %v", s.GetSceneId(), len(sceneEx.players)) + //sceneEx.stateStartTime = time.Now() + sceneEx.SetStateStartTime(time.Now()) + pack := &iceage.SCIceAgeRoomState{ + State: proto.Int(this.GetState()), + } + proto.SetDefaults(pack) + s.Broadcast(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_ROOMSTATE), pack, 0) + } +} + +func (this *SceneStateIceAgeStart) OnLeave(s *base.Scene) {} + +func (this *SceneStateIceAgeStart) OnTick(s *base.Scene) { + if sceneEx, ok := s.ExtraData.(*IceAgeSceneData); ok { + sceneEx.AIAddJackPot() + sceneEx.AIBurstJackPot() + sceneEx.KickPlayerByTime() + } +} + +func (this *SceneStateIceAgeStart) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + playerEx, ok := p.GetExtraData().(*IceAgePlayerData) + if !ok { + return false + } + sceneEx, ok := s.GetExtraData().(*IceAgeSceneData) + if !ok { + return false + } + if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 { + //离开有统计 + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true) + return false + } + switch opcode { + case IceAgePlayerOpStart: //开始 + //if !iceAgeBenchTest { + // iceAgeBenchTest = true + // for i := 0; i < 10; i++ { + // //this.BenchTest(s, p) + // this.WinTargetBenchTest(s, p) + // } + // return true + //} + + //params 参数0底注,后面跟客户端选择的线n条线(1<=n<=20),客户端线是从1开始算起1~20条线 + //参数是否合法 + if len(params) < 2 || len(params) > rule.LINENUM+1 { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + + //// 小游戏未结束 不能进行下一次旋转 + //if playerEx.totalPriceBonus > 0 { + // this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + // return false + //} + + //先做底注校验,看是否在指定参数内 + if sceneEx.GetDBGameFree().GetBaseScore() != int32(params[0]) { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + playerEx.score = int32(params[0]) + + //判断线条是否重复,是否合法 + lineFlag := make(map[int64]bool) + lineParams := make([]int64, 0) + for i := 1; i < len(params); i++ { + lineNum := params[i] + if lineNum >= 1 && lineNum <= int64(rule.LINENUM) && !lineFlag[lineNum] { + lineParams = append(lineParams, lineNum) + lineFlag[lineNum] = true + } else { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + } + //没有选线参数 + if len(lineParams) == 0 { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + + //获取总投注金额(所有线的总投注) | 校验玩家余额是否足够 + totalBetValue := (int64(len(lineParams))) * params[0] + if playerEx.freeTimes <= 0 && totalBetValue > playerEx.GetCoin() { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_CoinNotEnough, params) + return false + } else if playerEx.freeTimes <= 0 && int64(sceneEx.GetDBGameFree().GetBetLimit()) > playerEx.GetCoin() { //押注限制 + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_CoinNotEnough, params) + return false + } + + //p.lastOPTimer = time.Now() + //sceneEx.gameNowTime = time.Now() + //sceneEx.numOfGames++ + //p.gameTimes++ + //playerEx.startCoin = playerEx.GetCoin() + p.SetLastOPTimer(time.Now()) + sceneEx.SetGameNowTime(time.Now()) + p.SetGameTimes(p.GetGameTimes() + 1) + sceneEx.SetNumOfGames(sceneEx.GetNumOfGames() + 1) + //playerEx.SetStartCoin(playerEx.GetCoin()) + + //获取当前水池的上下文环境 + cpCtx := base.GetCoinPoolMgr().GetCoinPoolCtx(sceneEx.GetPlatform(), sceneEx.GetGameFreeId(), sceneEx.GetGroupId()) + sceneEx.SetCpCtx(cpCtx) + //税收比例 + taxRate := sceneEx.GetDBGameFree().GetTaxRate() + if taxRate < 0 || taxRate > 10000 { + logger.Logger.Warnf("IceAgeErrorTaxRate [%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, playerEx.spinID, taxRate) + taxRate = 500 + } + //水池设置 + coinPoolSetting := base.GetCoinPoolMgr().GetCoinPoolSetting(sceneEx.GetPlatform(), sceneEx.GetGameFreeId(), sceneEx.GetGroupId()) + //baseRate := coinPoolSetting.GetBaseRate() //基础赔率 + ctroRate := 9800 //调节赔率 暗税系数 + //if baseRate >= 10000 || baseRate <= 0 || ctroRate < 0 || ctroRate >= 1000 || baseRate+ctroRate > 9900 { + // logger.Logger.Warnf("IceAgeErrorBaseRate [%v][%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, playerEx.spinID, baseRate, ctroRate) + // baseRate = 9700 + // ctroRate = 200 + //} + //jackpotRate := 10000 - (baseRate + ctroRate) //奖池系数 + jackpotRate := ctroRate //奖池系数 + logger.Logger.Tracef("IceAgeRates [%v][%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, playerEx.spinID, taxRate, ctroRate) + + playerEx.IsFNovice(sceneEx.KeyGameId) + isFoolPlayer := playerEx.IsFoolPlayer[sceneEx.KeyGameId] + var gamePoolCoin int64 + if isFoolPlayer { + gamePoolCoin = base.CoinPoolMgr.GetNoviceCoin(sceneEx.GetGameFreeId(), sceneEx.Platform, sceneEx.GroupId) // 当前水池金额 + } else { + gamePoolCoin = base.CoinPoolMgr.GetCoin(sceneEx.GetGameFreeId(), sceneEx.Platform, sceneEx.GroupId) // 当前水池金额 + } + prizeFund := gamePoolCoin - sceneEx.jackpot.VirtualJK // 除去奖池的水池剩余金额 + + var jackpotParam = sceneEx.GetDBGameFree().GetJackpot() // 奖池参数 + var jackpotInit = int64(jackpotParam[rule.ICEAGE_JACKPOT_InitJackpot] * sceneEx.GetDBGameFree().GetBaseScore()) + var jackpotFundAdd, prizeFundAdd int64 //奖池/水池增量 + if playerEx.freeTimes <= 0 { //正常模式才能记录用户的押注变化,免费模式不能改变押注 + playerEx.betLines = lineParams // 选线记录 + jackpotFundAdd = int64(math.Floor(float64(totalBetValue) * (float64(jackpotRate) / 10000.0))) + prizeFundAdd = totalBetValue - jackpotFundAdd + //playerEx.totalBet += totalBetValue //总下注额(从进房间开始,包含多局游戏的下注) + //playerEx.SetTotalBet(playerEx.GetTotalBet() + totalBetValue) + + p.AddCoin(-totalBetValue, common.GainWay_HundredSceneLost, base.SyncFlag_ToClient, "system", s.GetSceneName()) + //p.Statics(sceneEx.GetKeyGameId(), sceneEx.KeyGamefreeId, -totalBetValue, true) + if !p.IsRob && !sceneEx.GetTesting() { + // 推送金币 + sceneEx.PushCoinPool(prizeFundAdd, isFoolPlayer) + //base.GetCoinPoolMgr().PushCoin(sceneEx.GetGameFreeId(), sceneEx.GetGroupId(), sceneEx.GetPlatform(), int64(float64(totalBetValue)*float64(10000-ctroRate)/10000.0)) + } + + ////统计参与游戏次数 + //if !sceneEx.GetTesting() && !playerEx.IsRob { + // pack := &server.GWSceneEnd{ + // GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), + // Players: []*server.PlayerCtx{&server.PlayerCtx{SnId: proto.Int32(playerEx.SnId), Coin: proto.Int64(playerEx.GetCoin())}}, + // } + // proto.SetDefaults(pack) + // sceneEx.SendToWorld(int(server.SSPacketID_PACKET_GW_SCENEEND), pack) + //} + } else { //免费次数时,不能改线改底注 + totalBetValue = 0 + } + + var distributionParams, bonusDistributionParams []int32 + //奖池模式 + var slotMode = 0 + //if prizeFund <= int64(jackpotParam[iceage.ICEAGE_JACKPOT_RoomPrizeFundLimit]) { //奖池不足 + if gamePoolCoin <= int64(coinPoolSetting.GetLowerLimit()) { //奖池不足 + distributionParams = getElementDistributionByName(SlotsData).GetParams() + bonusDistributionParams = getElementDistributionByName(BonusData).GetParams() + } else { + slotMode = 1 + distributionParams = getElementDistributionByName(SlotsData_V2).GetParams() + bonusDistributionParams = getElementDistributionByName(BonusData_V2).GetParams() + } + logger.Logger.Tracef("IceAgePlayerOpStart prizeFund:%v distributionParams:%v bonusDistributionParams:%v", prizeFund, distributionParams, bonusDistributionParams) + + writeBlackTryTimes := 0 + WriteBlack: + slotData := make([]int, 0) + var spinRes SpinResult + loopCount := 0 + + for { + loopCount++ + if loopCount > 3 { + break + } + slotData = getSlotsDataByElementDistribution(distributionParams) + + spinRes = sceneEx.CalcSpinsPrize(slotData, playerEx.betLines, getElementDistributionByName(SlotsData).GetParams(), bonusDistributionParams, params[0], taxRate) + logger.Logger.Tracef("CalcSpinsPrize:%v", spinRes) + + //不允许有免费旋转同时还出大奖 + if playerEx.freeTimes > 0 && spinRes.IsJackpot { + continue + } + //不允许出现两次小游戏 + if spinRes.BonusGameCnt > 1 { + continue + } + + if spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus > 0 { + if spinRes.IsJackpot { + if sceneEx.jackpot.Small < sceneEx.jackpot.VirtualJK { + continue + } + } + // 现金池不够补充 奖池初值 和 玩家奖金 时,不允许出现爆奖 + spinCondition := prizeFund + prizeFundAdd - (spinRes.TotalPrizeLine + spinRes.TotalPrizeBonus) + spinCondition += jackpotFundAdd + sceneEx.jackpot.VirtualJK - jackpotInit + win := spinRes.TotalPrizeLine + spinRes.TotalPrizeBonus + if spinCondition < 0 && win > totalBetValue { + if !spinRes.IsJackpot { // 非爆奖 水池不足 不再进行黑白名单调控 + writeBlackTryTimes = 999 + } + continue + } + + var limitWin = int32(0) + switch slotMode { + case 0: + limitWin = jackpotParam[rule.ICEAGE_JACKPOT_LIMITWIN_PRIZELOW] + case 1: + limitWin = jackpotParam[rule.ICEAGE_JACKPOT_LIMITWIN_PRIZEHIGH] + } + + if totalBetValue > 0 && !spinRes.IsJackpot && spinRes.TotalPrizeLine > totalBetValue*int64(limitWin) { + continue + } + //if !spinRes.IsJackpot && spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus > gamePoolCoin { + // // 水池不足 不再进行黑白名单调控 + // writeBlackTryTimes = 999 + // continue + //} + } + break + } + + limitWinScore := int64(coinPoolSetting.GetLowerLimit()) + // 多次尝试后 没有选好牌 + if loopCount > 3 { + logger.Logger.Warnf("CalcSpinsPrize at loopCount>3 :%v", spinRes) + if sceneEx.GetDBGameFree().GetSceneType() > 1 { // IF(@_RoomID > 1) + slotData = getSlotsDataByGroupName(DefaultData) + } else if gamePoolCoin < limitWinScore || playerEx.freeTimes > 0 { + slotData = getSlotsDataByGroupName(DefaultData) + } else { + slotData = getSlotsDataByGroupName(DefaultData_v1) + } + spinRes = sceneEx.CalcSpinsPrize(slotData, playerEx.betLines, getElementDistributionByName(SlotsData).GetParams(), bonusDistributionParams, params[0], taxRate) + } + + // 黑白名单调控 防止异常循环,添加上限次数 + if writeBlackTryTimes < 100 && playerEx.CheckBlackWriteList(spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus > totalBetValue) { + writeBlackTryTimes++ + goto WriteBlack + } else if writeBlackTryTimes >= 100 && writeBlackTryTimes != 999 { + logger.Logger.Warnf("IceAgeWriteBlackTryTimesOver [%v][%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, gamePoolCoin, playerEx.BlackLevel, playerEx.WhiteLevel) + } + + ///////////测试游戏数据 开始////////// + //if playerEx.DebugBonus && sceneEx.SceneType == 1 { + // if playerEx.TestNum >= len(DebugData) { + // playerEx.TestNum = 0 + // } + // slotData = DebugData[playerEx.TestNum] + // spinRes = sceneEx.CalcSpinsPrize(slotData, playerEx.betLines, getElementDistributionByName(SlotsData).GetParams(), bonusDistributionParams, params[0], taxRate) + // //playerEx.DebugBonus = false + // playerEx.TestNum++ + //} + ///////////测试游戏数据 结束////////// + + if spinRes.IsJackpot { + sceneEx.jackpot.Small -= sceneEx.jackpot.VirtualJK + if sceneEx.jackpot.Small < 0 { + sceneEx.jackpot.Small = 0 + } + sceneEx.jackpot.VirtualJK = jackpotInit + } else { + sceneEx.jackpot.Small += jackpotFundAdd + sceneEx.jackpot.VirtualJK += jackpotFundAdd + } + + if spinRes.TotalPrizeLine > 0 || spinRes.TotalPrizeBonus > 0 { + p.AddCoin(spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus, common.GainWay_HundredSceneWin, 0, "system", s.GetSceneName()) + //p.Statics(sceneEx.GetKeyGameId(), sceneEx.KeyGamefreeId, spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus+spinRes.TotalTaxScore, true) + if !p.IsRob && !sceneEx.GetTesting() { + sceneEx.PopCoinPool(spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus+spinRes.TotalTaxScore, isFoolPlayer) + //base.GetCoinPoolMgr().PopCoin(sceneEx.GetGameFreeId(), sceneEx.GetGroupId(), sceneEx.GetPlatform(), spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus+spinRes.TotalTaxScore) + } + playerEx.taxCoin = spinRes.TotalTaxScore + playerEx.AddServiceFee(playerEx.taxCoin) + } + p.StaticsLaba(sceneEx.KeyGameId, sceneEx.KeyGamefreeId, totalBetValue, spinRes.TotalPrizeLine+spinRes.TotalPrizeBonus+spinRes.TotalTaxScore) + + var isFreeFlag bool + //免费次数 + if playerEx.freeTimes > 0 { + playerEx.freeTimes-- + isFreeFlag = true + } + playerEx.freeTimes += spinRes.AddFreeTimes + + rule.SpinID++ + playerEx.spinID = rule.SpinID + if spinRes.TotalPrizeBonus > 0 { // 小游戏开始 + logger.Logger.Tracef("BonusGame Start [%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, playerEx.spinID, spinRes.TotalPrizeBonus) + playerEx.totalPriceBonus = spinRes.TotalPrizeBonus + playerEx.BonusLineIdx = 0 + //// 小游戏超时处理 + //playerEx.bonusTimerHandle, _ = timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + // this.OnPlayerOp(s, p, IceAgeBonusGame, []int64{playerEx.spinID}) + // return true + //}), nil, IceAgeBonusGameTimeout, 1) + } + playerEx.cards = spinRes.SlotsData + playerEx.winCoin = spinRes.TotalPrizeLine + spinRes.TotalPrizeJackpot + spinRes.TotalPrizeBonus + playerEx.taxCoin + playerEx.linesWinCoin = spinRes.TotalPrizeLine + playerEx.jackpotWinCoin = spinRes.TotalPrizeJackpot + playerEx.smallGameWinCoin = spinRes.TotalPrizeBonus + //playerEx.currentBet = totalBetValue + //playerEx.currentTax = playerEx.taxCoin + playerEx.SetCurrentBet(totalBetValue) + playerEx.SetCurrentTax(playerEx.taxCoin) + + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Sucess, append(params[:1], playerEx.betLines...)) + playerEx.billedData = &iceage.GameBilledData{ + SpinID: proto.Int64(playerEx.spinID), + TotalBetValue: proto.Int64(totalBetValue), + TotalPriceValue: proto.Int64(spinRes.TotalPrizeLine), + IsJackpot: proto.Bool(spinRes.IsJackpot), + Jackpot: proto.Int64(sceneEx.jackpot.VirtualJK), + Balance: proto.Int64(playerEx.GetCoin()), + TotalFreeSpin: proto.Int32(playerEx.freeTimes), + TotalPriceBonus: proto.Int64(spinRes.TotalPrizeBonus), + TotalJackpot: proto.Int64(spinRes.TotalPrizeJackpot), + PrizesData: spinRes.LinesInfo, + SlotsData: spinRes.SlotsData, + } + pack := &iceage.SCIceAgeGameBilled{ + BilledData: playerEx.billedData, + } + proto.SetDefaults(pack) + logger.Logger.Infof("IceAgePlayerOpStart %v", pack) + p.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_GAMEBILLED), pack) + + // 记录本次操作 + var allCards = make([][]int32, 0) + for _, v := range pack.BilledData.SlotsData { + allCards = append(allCards, v.Card) + } + playerEx.RollGameType.BaseResult.WinTotal = pack.BilledData.GetTotalPriceValue() + pack.BilledData.GetTotalPriceBonus() + playerEx.RollGameType.BaseResult.IsFree = isFreeFlag + playerEx.RollGameType.BaseResult.WinSmallGame = pack.BilledData.GetTotalPriceBonus() + playerEx.RollGameType.BaseResult.AllWinNum = int32(len(pack.BilledData.PrizesData)) + playerEx.RollGameType.BaseResult.WinRate = spinRes.TotalWinRate + playerEx.RollGameType.Cards = allCards + playerEx.RollGameType.WinLines = spinRes.WinLines + playerEx.RollGameType.BaseResult.WinJackpot = pack.BilledData.GetTotalJackpot() + playerEx.RollGameType.BaseResult.WinLineScore = pack.BilledData.TotalPriceValue + playerEx.RollGameType.BaseResult.IsFoolPlayer = isFoolPlayer + IceAgeCheckAndSaveLog(sceneEx, playerEx) + + // 广播奖池 + if totalBetValue == 0 && !spinRes.IsJackpot { // 没改变奖池 + return true + } + // 添加进开奖记录里面 + if spinRes.IsJackpot { + //spinid := strconv.FormatInt(int64(playerEx.SnId), 10) + // 推送最新开奖记录到world + //msg := &server.GWGameNewBigWinHistory{ + // SceneId: proto.Int32(int32(sceneEx.GetSceneId())), + // BigWinHistory: &server.BigWinHistoryInfo{ + // SpinID: proto.String(spinid), + // CreatedTime: proto.Int64(time.Now().Unix()), + // BaseBet: proto.Int64(int64(playerEx.score)), + // TotalBet: proto.Int64(int64(playerEx.CurrentBet)), + // PriceValue: proto.Int64(pack.BilledData.GetTotalJackpot()), + // UserName: proto.String(playerEx.Name), + // }, + //} + //proto.SetDefaults(msg) + //logger.Logger.Infof("GWGameNewBigWinHistory gameFreeID %v %v", sceneEx.GetDBGameFree().GetId(), msg) + //sceneEx.SendToWorld(int(server.SSPacketID_PACKET_GW_GAMENEWBIGWINHISTORY), msg) + } + logger.Logger.Tracef("---iceage---当前奖池:真人[%v] 虚拟[%v]", sceneEx.jackpot.Small, sceneEx.jackpot.VirtualJK) + case IceAgePlayerHistory: + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + spinid := strconv.FormatInt(int64(playerEx.SnId), 10) + gpl := model.GetPlayerListByHallEx(p.SnId, p.Platform, 0, 80, 0, 0, 0, s.DbGameFree.GetGameClass(), s.GetGameId()) + pack := &iceage.SCIceAgePlayerHistory{} + for _, v := range gpl.Data { + //if v.GameDetailedLogId == "" { + // logger.Logger.Error("IceAgePlayerHistory GameDetailedLogId is nil") + // break + //} + //gdl := model.GetPlayerHistory(p.Platform, v.GameDetailedLogId) + //if gdl == nil { + // logger.Logger.Error("IceAgePlayerHistory gdl is nil") + // continue + //} + //data, err := UnMarshalIceAgeGameNote(gdl.GameDetailedNote) + //if err != nil { + // logger.Logger.Errorf("UnMarshalIceAgeGameNote error:%v", err) + //} + //gnd := data.(*GameResultLog) + player := &iceage.IceAgePlayerHistoryInfo{ + SpinID: proto.String(spinid), + CreatedTime: proto.Int64(int64(v.Ts)), + TotalBetValue: proto.Int64(v.BetAmount), + TotalPriceValue: proto.Int64(v.WinTotal), + IsFree: proto.Bool(v.IsFree), + TotalBonusValue: proto.Int64(v.WinSmallGame), + } + pack.PlayerHistory = append(pack.PlayerHistory, player) + } + proto.SetDefaults(pack) + logger.Logger.Info("IceAgePlayerHistory: ", pack) + return pack + + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + logger.Logger.Error("IceAgePlayerHistory data is nil") + return + } + p.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_PLAYERHISTORY), data) + }), "CSGetIceAgePlayerHistoryHandler").Start() + case IceAgeBurstHistory: + sceneEx.BurstHistory(playerEx) + case IceAgeBonusGame: + //params 参数0 spinID + //参数是否合法 + if len(params) < 2 { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + if playerEx.spinID != params[0] || playerEx.totalPriceBonus <= 0 || params[1] != playerEx.BonusLineIdx { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + idx := params[1] + logger.Logger.Tracef("BonusGame End [%v][%v][%v][%v][%v]", sceneEx.GetGameFreeId(), playerEx.SnId, playerEx.spinID, playerEx.totalPriceBonus, idx) + + if playerEx.bonusTimerHandle != timer.TimerHandle(0) { + timer.StopTimer(playerEx.bonusTimerHandle) + playerEx.bonusTimerHandle = timer.TimerHandle(0) + } + + //p.AddCoin(playerEx.totalPriceBonus, common.GainWay_HundredSceneWin, false, "system", s.GetSceneName()) + //if !p.IsRob && !sceneEx.GetTesting() { + // GetCoinPoolMgr().PopCoin(sceneEx.GetGameFreeId(), sceneEx.GetGroupId(), sceneEx.GetPlatform(), playerEx.totalPriceBonus) + // p.Statics(sceneEx.GetKeyGameId(), sceneEx.GetGameFreeId(), playerEx.totalPriceBonus, true) + //} + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Sucess, params) + //playerEx.totalPriceBonus = 0 + case IceAgeBonusGameStart: + logger.Logger.Tracef("params:%v", params) + if len(params) < 2 || params[1] < 0 || int(params[1]) > len(playerEx.billedData.PrizesData) { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Error, params) + return false + } + idx := params[1] + if idx > int64(playerEx.BonusLineIdx) || playerEx.BonusLineIdx == 0 { + // 小游戏超时处理 + playerEx.BonusLineIdx = idx + if playerEx.bonusTimerHandle != timer.TimerHandle(0) { + timer.StopTimer(playerEx.bonusTimerHandle) + playerEx.bonusTimerHandle = timer.TimerHandle(0) + } + playerEx.bonusTimerHandle, _ = timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + this.OnPlayerOp(s, p, IceAgeBonusGame, []int64{playerEx.spinID, playerEx.BonusLineIdx}) + return true + }), nil, IceAgeBonusGameTimeout, 1) + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, iceage.OpResultCode_OPRC_Sucess, params) + } + + } + return true +} + +func (this *SceneStateIceAgeStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { +} + +// 发送玩家操作情况 +func (this *SceneStateIceAgeStart) OnPlayerSToCOp(s *base.Scene, p *base.Player, pos int, opcode int, + opRetCode iceage.OpResultCode, params []int64) { + pack := &iceage.SCIceAgeOp{ + SnId: proto.Int32(p.SnId), + OpCode: proto.Int(opcode), + OpRetCode: opRetCode, + Params: params, + } + proto.SetDefaults(pack) + p.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_PLAYEROP), pack) +} + +var iceAgeBenchTest bool +var iceAgeBenchTestTimes int + +func (this *SceneStateIceAgeStart) BenchTest(s *base.Scene, p *base.Player) { + const BENCH_CNT = 10000 + setting := base.GetCoinPoolMgr().GetCoinPoolSetting(s.GetPlatform(), s.GetGameFreeId(), s.GetGroupId()) + oldPoolCoin := base.GetCoinPoolMgr().GetCoin(s.GetGameFreeId(), s.GetPlatform(), s.GetGroupId()) + if iceAgeBenchTestTimes == 0 { + defaultVal := int64(setting.GetLowerLimit()) + if oldPoolCoin != defaultVal { + base.GetCoinPoolMgr().PushCoin(s.GetGameFreeId(), s.GetGroupId(), s.GetPlatform(), defaultVal-oldPoolCoin) + } + } + iceAgeBenchTestTimes++ + + fileName := fmt.Sprintf("iceage-%v-%d.csv", p.SnId, iceAgeBenchTestTimes) + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + defer file.Close() + if err != nil { + file, err = os.Create(fileName) + if err != nil { + return + } + } + file.WriteString("玩家id,当前水位,之前余额,之后余额,投入,产出,税收,小游戏,爆奖,中线倍数,中线数,剩余免费次数\r\n") + + oldCoin := p.GetCoin() + //p.coin = int64(5000 * s.GetDBGameFree().GetBaseScore()) + p.SetCoin(int64(5000 * s.GetDBGameFree().GetBaseScore())) + if playerEx, ok := p.GetExtraData().(*IceAgePlayerData); ok { + for i := 0; i < BENCH_CNT; i++ { + startCoin := p.GetCoin() + freeTimes := playerEx.freeTimes + poolCoin := base.GetCoinPoolMgr().GetCoin(s.GetGameFreeId(), s.GetPlatform(), s.GetGroupId()) + playerEx.UnmarkFlag(base.PlayerState_GameBreak) + suc := this.OnPlayerOp(s, p, IceAgePlayerOpStart, append([]int64{int64(playerEx.score)}, rule.AllBetLines...)) + inCoin := int64(playerEx.RollGameType.BaseResult.TotalBet) + outCoin := playerEx.RollGameType.BaseResult.ChangeCoin + inCoin + taxCoin := playerEx.RollGameType.BaseResult.Tax + lineScore := float64(playerEx.RollGameType.BaseResult.WinRate*s.GetDBGameFree().GetBaseScore()) * float64(10000.0-s.GetDBGameFree().GetTaxRate()) / 10000.0 + jackpotScore := outCoin - playerEx.RollGameType.BaseResult.WinSmallGame - int64(lineScore+0.00001) + + str := fmt.Sprintf("%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v\r\n", p.SnId, poolCoin, startCoin, p.GetCoin(), inCoin, outCoin, taxCoin, + playerEx.RollGameType.BaseResult.WinSmallGame, jackpotScore, playerEx.RollGameType.BaseResult.WinRate, playerEx.RollGameType.BaseResult.AllWinNum, freeTimes) + file.WriteString(str) + if !suc { + break + } + + if playerEx.totalPriceBonus > 0 { + this.OnPlayerOp(s, p, IceAgeBonusGame, []int64{playerEx.spinID}) + } + } + } + p.SetCoin(oldCoin) +} +func (this *SceneStateIceAgeStart) WinTargetBenchTest(s *base.Scene, p *base.Player) { + const BENCH_CNT = 10000 + var once = sync.Once{} + once.Do(func() { + setting := base.GetCoinPoolMgr().GetCoinPoolSetting(s.GetPlatform(), s.GetGameFreeId(), s.GetGroupId()) + oldPoolCoin := base.GetCoinPoolMgr().GetCoin(s.GetGameFreeId(), s.GetPlatform(), s.GetGroupId()) + if iceAgeBenchTestTimes == 0 { + defaultVal := int64(setting.GetLowerLimit()) + if oldPoolCoin != defaultVal { + base.GetCoinPoolMgr().PushCoin(s.GetGameFreeId(), s.GetGroupId(), s.GetPlatform(), defaultVal-oldPoolCoin) + } + } + }) + iceAgeBenchTestTimes++ + + fileName := fmt.Sprintf("iceage-%v-%d.csv", p.SnId, iceAgeBenchTestTimes) + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) + defer file.Close() + if err != nil { + file, err = os.Create(fileName) + if err != nil { + return + } + } + file.WriteString("玩家id,当前水位,之前余额,之后余额,投入,产出,税收,小游戏,爆奖,中线倍数,中线数,剩余免费次数\r\n") + oldCoin := p.GetCoin() + switch s.GetDBGameFree().GetSceneType() { + case 1: + p.SetCoin(100000) + case 2: + p.SetCoin(500000) + case 3: + p.SetCoin(1000000) + default: + p.SetCoin(100000) + } + var targetCoin = p.GetCoin() + p.GetCoin()/10 + if playerEx, ok := p.GetExtraData().(*IceAgePlayerData); ok { + for i := 0; p.GetCoin() < targetCoin; i++ { + startCoin := p.GetCoin() + freeTimes := playerEx.freeTimes + poolCoin := base.GetCoinPoolMgr().GetCoin(s.GetGameFreeId(), s.GetPlatform(), s.GetGroupId()) + playerEx.UnmarkFlag(base.PlayerState_GameBreak) + suc := this.OnPlayerOp(s, p, IceAgePlayerOpStart, append([]int64{int64(playerEx.score)}, rule.AllBetLines...)) + inCoin := int64(playerEx.RollGameType.BaseResult.TotalBet) + outCoin := playerEx.RollGameType.BaseResult.ChangeCoin + inCoin + taxCoin := playerEx.RollGameType.BaseResult.Tax + lineScore := float64(playerEx.RollGameType.BaseResult.WinRate*s.GetDBGameFree().GetBaseScore()) * float64(10000.0-s.GetDBGameFree().GetTaxRate()) / 10000.0 + jackpotScore := outCoin - playerEx.RollGameType.BaseResult.WinSmallGame - int64(lineScore+0.00001) + + str := fmt.Sprintf("%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v\r\n", p.SnId, poolCoin, startCoin, p.GetCoin(), inCoin, outCoin, taxCoin, + playerEx.RollGameType.BaseResult.WinSmallGame, jackpotScore, playerEx.RollGameType.BaseResult.WinRate, playerEx.RollGameType.BaseResult.AllWinNum, freeTimes) + file.WriteString(str) + if !suc { + break + } + + if playerEx.totalPriceBonus > 0 { + this.OnPlayerOp(s, p, IceAgeBonusGame, []int64{playerEx.spinID}) + } + if i > BENCH_CNT { + break + } + } + } + p.SetCoin(oldCoin) +} +func IceAgeCheckAndSaveLog(sceneEx *IceAgeSceneData, playerEx *IceAgePlayerData) { + //统计金币变动 + //log1 + logger.Logger.Trace("IceAgeCheckAndSaveLog Save ", playerEx.SnId) + //changeCoin := playerEx.GetCoin() - playerEx.GetStartCoin() + changeCoin := playerEx.winCoin - playerEx.taxCoin - playerEx.CurrentBet + startCoin := playerEx.Coin - changeCoin + playerEx.SaveSceneCoinLog(startCoin, changeCoin, + playerEx.GetCoin(), playerEx.GetCurrentBet(), playerEx.taxCoin, playerEx.winCoin, playerEx.jackpotWinCoin, playerEx.smallGameWinCoin) + + //log2 + playerEx.RollGameType.BaseResult.ChangeCoin = changeCoin + playerEx.RollGameType.BaseResult.BasicBet = sceneEx.GetDBGameFree().GetBaseScore() + playerEx.RollGameType.BaseResult.RoomId = int32(sceneEx.GetSceneId()) + playerEx.RollGameType.BaseResult.AfterCoin = playerEx.GetCoin() + playerEx.RollGameType.BaseResult.BeforeCoin = startCoin + playerEx.RollGameType.BaseResult.IsFirst = sceneEx.IsPlayerFirst(playerEx.Player) + playerEx.RollGameType.BaseResult.PlayerSnid = playerEx.SnId + playerEx.RollGameType.BaseResult.TotalBet = int32(playerEx.GetCurrentBet()) + playerEx.RollGameType.AllLine = int32(len(playerEx.betLines)) + playerEx.RollGameType.BaseResult.FreeTimes = playerEx.freeTimes + playerEx.RollGameType.UserName = playerEx.Name + playerEx.RollGameType.BetLines = playerEx.betLines + playerEx.RollGameType.BaseResult.Tax = playerEx.taxCoin + playerEx.RollGameType.BaseResult.WBLevel = sceneEx.players[playerEx.SnId].WBLevel + if playerEx.score > 0 { + if !playerEx.IsRob { + info, err := model.MarshalGameNoteByROLL(playerEx.RollGameType) + if err == nil { + logid, _ := model.AutoIncGameLogId() + playerEx.currentLogId = logid + sceneEx.SaveGameDetailedLog(logid, info, &base.GameDetailedParam{}) + totalin := int64(playerEx.RollGameType.BaseResult.TotalBet) + totalout := playerEx.RollGameType.BaseResult.ChangeCoin + playerEx.taxCoin + totalin + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + logParam := &base.SaveGamePlayerListLogParam{ + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + LogId: logid, + TotalIn: totalin, + TotalOut: totalout, + TaxCoin: playerEx.taxCoin, + BetAmount: int64(playerEx.RollGameType.BaseResult.TotalBet), + WinAmountNoAnyTax: playerEx.RollGameType.BaseResult.ChangeCoin, + ValidBet: validBet, + ValidFlow: validFlow, + IsFirstGame: sceneEx.IsPlayerFirst(playerEx.Player), + IsFree: playerEx.RollGameType.BaseResult.IsFree, + WinSmallGame: playerEx.RollGameType.BaseResult.WinSmallGame, + WinTotal: playerEx.RollGameType.BaseResult.WinTotal, + } + sceneEx.SaveGamePlayerListLog(playerEx.SnId, logParam) + } + } + } + + //统计输下注金币数 + if !sceneEx.GetTesting() && !playerEx.IsRob { + playerBet := &server.PlayerData{ + SnId: proto.Int32(playerEx.SnId), + Bet: proto.Int64(playerEx.GetCurrentBet()), + Gain: proto.Int64(playerEx.RollGameType.BaseResult.ChangeCoin), + Tax: proto.Int64(playerEx.taxCoin), + Coin: proto.Int64(playerEx.GetCoin()), + GameCoinTs: proto.Int64(playerEx.GameCoinTs), + } + gwPlayerBet := &server.GWPlayerData{ + SceneId: proto.Int(sceneEx.SceneId), + GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), + } + gwPlayerBet.Datas = append(gwPlayerBet.Datas, playerBet) + proto.SetDefaults(gwPlayerBet) + sceneEx.SendToWorld(int(server.SSPacketID_PACKET_GW_PLAYERDATA), gwPlayerBet) + logger.Logger.Trace("Send msg gwPlayerBet ===>", gwPlayerBet) + } + + playerEx.taxCoin = 0 + playerEx.winCoin = 0 + playerEx.linesWinCoin = 0 + playerEx.jackpotWinCoin = 0 + playerEx.smallGameWinCoin = 0 + + if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 { + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true) + } +} + +func init() { + ScenePolicyIceAgeSington.RegisteSceneState(&SceneStateIceAgeStart{}) + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameId_IceAge, 0, ScenePolicyIceAgeSington) + return nil + }) +} diff --git a/gamesrv/logger.xml b/gamesrv/logger.xml new file mode 100644 index 0000000..f6eb37b --- /dev/null +++ b/gamesrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gamesrv/main.go b/gamesrv/main.go new file mode 100644 index 0000000..171d8fe --- /dev/null +++ b/gamesrv/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "mongo.games.com/goserver/core" + _ "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/module" + + _ "games.yol.com/win88" + "mongo.games.com/game/common" + _ "mongo.games.com/game/srvdata" + + _ "mongo.games.com/game/gamesrv/action" + _ "mongo.games.com/game/gamesrv/base" + _ "mongo.games.com/game/gamesrv/transact" + + // game + _ "mongo.games.com/game/gamesrv/chess" + _ "mongo.games.com/game/gamesrv/fishing" + _ "mongo.games.com/game/gamesrv/smallrocket" + _ "mongo.games.com/game/gamesrv/thirteen" + _ "mongo.games.com/game/gamesrv/tienlen" + + _ "mongo.games.com/game/gamesrv/fruits" + _ "mongo.games.com/game/gamesrv/iceage" +) + +func main() { + core.RegisterConfigEncryptor(common.ConfigFE) + defer core.ClosePackages() + core.LoadPackages("config.json") + + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/gamesrv/robotaccount.json b/gamesrv/robotaccount.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/gamesrv/robotaccount.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/gamesrv/smallrocket/action.go b/gamesrv/smallrocket/action.go new file mode 100644 index 0000000..167c26f --- /dev/null +++ b/gamesrv/smallrocket/action.go @@ -0,0 +1,55 @@ +package smallrocket + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/smallrocket" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSPlayerOpPacketFactory struct { +} + +type CSPlayerOpHandler struct { +} + +func (f *CSPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &smallrocket.CSSmallRocketOp{} + return pack +} + +func (h *CSPlayerOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerOpHandler Process recv ", data) + if msg, ok := data.(*smallrocket.CSSmallRocketOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSPlayerOpHandler p.scene == nil") + return nil + } + + if !common.IsSmallRocket(scene.GetGameId()) { + logger.Logger.Error("CSPlayerOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + sp := scene.GetScenePolicy() + if sp != nil { + sp.OnPlayerOp(scene, p, int(msg.GetOpCode()), msg.GetParams()) + } + return nil + } + return nil +} + +func init() { + common.RegisterHandler(int(smallrocket.SmallRocketPacketID_PACKET_CS_SMALLROCKET_PLAYEROP), &CSPlayerOpHandler{}) + netlib.RegisterFactory(int(smallrocket.SmallRocketPacketID_PACKET_CS_SMALLROCKET_PLAYEROP), &CSPlayerOpPacketFactory{}) +} diff --git a/gamesrv/smallrocket/player.go b/gamesrv/smallrocket/player.go new file mode 100644 index 0000000..51f62cb --- /dev/null +++ b/gamesrv/smallrocket/player.go @@ -0,0 +1,410 @@ +package smallrocket + +import ( + "fmt" + rule "mongo.games.com/game/gamerule/smallrocket" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/smallrocket" + "mongo.games.com/goserver/core/logger" + "strconv" +) + +type PlayerEx struct { + *base.Player //玩家信息 + + betData []*rule.BetDataEx // 下注信息 + CurBetData []*rule.BetDataEx // 本局的下注信息 + gainCoin int64 // 本局赢的金币 + taxCoin int64 // 本局税收 + odds int32 +} + +func (this *PlayerEx) Clear(baseScore int32) { + this.UnmarkFlag(base.PlayerState_WaitNext) + this.UnmarkFlag(base.PlayerState_GameBreak) + this.MarkFlag(base.PlayerState_Ready) + + this.gainCoin = 0 + this.taxCoin = 0 + this.odds = 0 + + this.betData = make([]*rule.BetDataEx, 2) + this.CurBetData = make([]*rule.BetDataEx, 2) + + this.InitBetData(baseScore) +} + +func (this *PlayerEx) CanOp(sceneEx *SceneEx) bool { + if !this.IsGameing() { + logger.Logger.Trace("(this *PlayerEx) CanOp return false ", this.SnId) + return false + } + return true +} + +// 按下注位置检查能否下注 +func (this *PlayerEx) CanBetCoinByPos(betPos int, betVal int64) bool { + if len(this.betData) >= 2 && betPos <= 1 { + if this.GetCoin() >= betVal { + return true + } + } + + return false +} + +// 检查金币能否够下注 +func (this *PlayerEx) CanBetCoin() bool { + if len(this.betData) >= 2 { + var betCoins int64 = 0 + for i := 0; i <= len(this.betData); i++ { + betCoins += this.betData[i].BetVal + } + + if this.Coin >= betCoins { + return true + } + } + + return false +} + +// 游戏新一局 设置数据 +func (this *PlayerEx) ReStartGame() { + this.ReBetDataStartGame() + this.gainCoin = 0 + this.taxCoin = 0 + this.odds = 0 +} + +// 金币不够停止下注 +func (this *PlayerEx) StopBetCoinByPos(betPos int) { + + logger.Logger.Trace("(this *PlayerEx) StopBetCoinByPos SnId: ", this.SnId, " | betPos:", betPos) + + if len(this.betData) >= 2 && betPos <= 1 { + + if this.betData[betPos].IsAutoBetAndTake { + + params := make([]int64, 10) + params[0] = int64(betPos) + pack := &smallrocket.SCSmallRocketOp{ + SnId: proto.Int32(this.SnId), + OpCode: proto.Int32(int32(rule.SmallRocketPlayerOpStopAutoBet)), + Params: params, + OpRetCode: smallrocket.OpResultCode_OPRC_CoinNotEnough, + } + + if this.betData != nil { + for i := 0; i < len(this.betData); i++ { + + tempBetEml := &smallrocket.BetDataInfo{ + BetVal: proto.Int64(this.betData[i].BetVal), + TakeMul: proto.Float32(float32(this.betData[i].TakeMul)), + IsCurBet: proto.Bool(false), + IsNextBet: proto.Bool(false), + IsTakeGain: proto.Bool(false), + IsAutoBetAndTake: proto.Bool(false), + } + + pack.BetDataArr = append(pack.BetDataArr, tempBetEml) + } + } + + this.SendToClient(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_PLAYEROP), pack) + } + + this.betData[betPos].IsCurBet = false + this.betData[betPos].IsNextBet = false + this.betData[betPos].IsAutoBetAndTake = false + this.betData[betPos].IsTakeGain = false + } +} + +// 停止不能下注的 +func (this *PlayerEx) StopBetCoin() (betPos0 bool, betPos1 bool) { + + if len(this.betData) >= 2 { + BetCoin0Val := this.betData[0].BetVal + BetCoin1Val := this.betData[1].BetVal + + logger.Logger.Trace("(this *PlayerEx) StopBetCoin SnId:", this.SnId, " | Coin:", this.Coin, " | BetCoin0Val:", BetCoin0Val, " | BetCoin1Val:", BetCoin1Val) + + if this.betData[0].IsCurBet && this.betData[1].IsCurBet { + if this.GetCoin() <= 0 { + this.StopBetCoinByPos(0) + this.StopBetCoinByPos(1) + return true, true + } + + // 两个押注都够,什么也不做 + if (BetCoin0Val + BetCoin1Val) <= this.Coin { + return false, false + } + + // 两个押注都不够,全部停止投注 + if BetCoin0Val > this.Coin && BetCoin1Val > this.Coin { + this.StopBetCoinByPos(0) + this.StopBetCoinByPos(1) + return true, true + } + + // 两个押注之和大于 玩家所拥有金币数 + if (BetCoin0Val + BetCoin1Val) > this.Coin { + if BetCoin0Val < this.Coin && BetCoin1Val < this.Coin { + if BetCoin0Val >= BetCoin1Val { + this.StopBetCoinByPos(1) + betPos0 = false + betPos1 = true + } else { + this.StopBetCoinByPos(0) + betPos0 = true + betPos1 = false + } + return + } + + if BetCoin0Val <= this.Coin || BetCoin1Val <= this.Coin { + if BetCoin0Val > this.Coin { + this.StopBetCoinByPos(0) + betPos0 = false + betPos1 = true + } + + if BetCoin1Val > this.Coin { + this.StopBetCoinByPos(1) + betPos0 = false + betPos1 = true + } + + return betPos0, betPos1 + } + } + } else if this.betData[0].IsCurBet && !this.betData[1].IsCurBet { + if BetCoin0Val > this.Coin { + this.StopBetCoinByPos(0) + } + } else if !this.betData[0].IsCurBet && this.betData[1].IsCurBet { + if BetCoin1Val > this.Coin { + this.StopBetCoinByPos(1) + } + } + } + + return false, false +} + +// 不在线的玩家停止自动下注 +func (this *PlayerEx) StopLineAutoBetCoin() { + if this.IsOnLine() { + return + } + + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + if this.betData[i].IsAutoBetAndTake || this.betData[i].IsCurBet { + this.betData[i].IsAutoBetAndTake = false + this.betData[i].IsCurBet = false + this.betData[i].IsNextBet = false + + this.betData[i].ReStartGame() + logger.Logger.Trace("(this *PlayerEx) StopLineAutoBetCoin SnId:", this.SnId, " betPos:", i) + } + } + } +} + +// 初始化下注数据 +func (this *PlayerEx) InitBetData(baseScore int32) { + + for i := 0; i < len(this.betData); i++ { + + TakeMul, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 1.20), 64) + this.betData[i] = &rule.BetDataEx{ + BetVal: int64(baseScore), + TakeMul: TakeMul, + IsCurBet: false, + IsNextBet: false, + IsTakeGain: false, + IsAutoBetAndTake: false, + } + } + + for i := 0; i < len(this.betData); i++ { + + TakeMul, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 1.20), 64) + this.CurBetData[i] = &rule.BetDataEx{ + BetVal: int64(baseScore), + TakeMul: TakeMul, + IsCurBet: false, + IsNextBet: false, + IsTakeGain: false, + IsAutoBetAndTake: false, + } + } +} + +func (this *PlayerEx) CopyToCurBetData() { + + for i := 0; i < len(this.betData); i++ { + this.CurBetData[i].BetVal = this.betData[i].BetVal + this.CurBetData[i].TakeMul = this.betData[i].TakeMul + this.CurBetData[i].IsCurBet = this.betData[i].IsCurBet + this.CurBetData[i].IsNextBet = this.betData[i].IsNextBet + this.CurBetData[i].IsTakeGain = this.betData[i].IsTakeGain + this.CurBetData[i].IsAutoBetAndTake = this.betData[i].IsAutoBetAndTake + this.CurBetData[i].GainSearchTime = this.betData[i].GainSearchTime + this.CurBetData[i].GainCoinMul = this.betData[i].GainCoinMul + + } +} + +// 重置下注数据 +func (this *PlayerEx) ResetBetData() { + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + this.betData[i].Reset() + } + } + + if len(this.CurBetData) >= 2 { + for i := 0; i < len(this.CurBetData); i++ { + this.CurBetData[i].Reset() + } + } +} + +// 游戏新一局 设置下注数据 +func (this *PlayerEx) ReBetDataStartGame() { + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + this.betData[i].ReStartGame() + } + } + + if len(this.CurBetData) >= 2 { + this.CopyToCurBetData() + } +} + +// 玩家设置自动 +func (this *PlayerEx) SetAutoBetAndTake(isAutoBet int64, betPos int64) { + if len(this.betData) >= 2 && betPos <= 1 { + + if isAutoBet == 1 { + this.betData[betPos].IsAutoBetAndTake = true + } else if isAutoBet == 0 { + this.betData[betPos].IsAutoBetAndTake = false + } + } +} + +func (this *PlayerEx) GetAutoBetAndTake(betPos int64) bool { + if len(this.betData) >= 2 && betPos <= 1 { + return this.betData[betPos].IsAutoBetAndTake + } + + return false +} + +func (this *PlayerEx) IsAutoBetAndTakeByPos(betPos int64) bool { + if len(this.betData) >= 2 && betPos <= 1 { + return this.betData[betPos].IsAutoBetAndTake + } + + return false +} + +func (this *PlayerEx) IsAutoBetAndTake() bool { + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + if this.betData[i].IsAutoBetAndTake { + return true + } + } + } + + return false +} + +func (this *PlayerEx) CanPlayerOpInState(sceneState int, betPos int) bool { + if len(this.betData) < 2 || betPos > 1 { + return false + } + + return false +} + +// 玩家自动下注 +func (this *PlayerEx) PlayerAutoBet(sceneState int) { + + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + if sceneState == rule.SmallRocketSceneStateStart { + if this.betData[i].IsAutoBetAndTake { + this.betData[i].IsCurBet = true + this.betData[i].IsTakeGain = false + } + } else if sceneState == rule.SmallRocketSceneStatePlayGame { + if this.betData[i].IsAutoBetAndTake { + this.betData[i].IsNextBet = true + } + } + } + } +} + +// 能否退出游戏 +func (this *PlayerEx) CanLeaveScene(sceneState int) bool { + + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + if sceneState == rule.SmallRocketSceneStateStart { + if this.betData[i].IsCurBet && !this.betData[i].IsTakeGain { + return false + } + } else if sceneState == rule.SmallRocketSceneStatePlayGame { + if this.betData[i].IsCurBet && !this.betData[i].IsTakeGain { + return false + } + } + } + } + + return true +} + +// 本局是否领取过押注奖励 +func (this *PlayerEx) IsFinishTakeBet(sceneState int) bool { + + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + if sceneState == rule.SmallRocketSceneStatePlayGame { + if this.betData[i].IsCurBet && this.betData[i].IsTakeGain { + return true + } + } + } + } + + return false +} + +func (this *PlayerEx) PrintBetData(sceneState int) { + if sceneState == rule.SmallRocketSceneStateStart { + logger.Logger.Trace("StateStart: PrintBetData") + } else if sceneState == rule.SmallRocketSceneStatePlayGame { + logger.Logger.Trace("PlayGame: PrintBetData") + } else if sceneState == rule.SmallRocketSceneStateBilled { + logger.Logger.Trace("StateBilled: PrintBetData") + } + + if len(this.betData) >= 2 { + for i := 0; i < len(this.betData); i++ { + logger.Logger.Trace("SnId:", this.SnId, " Name: ", this.Name, "PlayerEx-----betData[", i, "]:", *this.betData[i]) + // logger.Logger.Trace("SnId:", this.SnId, " PlayerEx-----betData: ", *this.betData[i]) + } + + } +} diff --git a/gamesrv/smallrocket/scene.go b/gamesrv/smallrocket/scene.go new file mode 100644 index 0000000..e732167 --- /dev/null +++ b/gamesrv/smallrocket/scene.go @@ -0,0 +1,345 @@ +package smallrocket + +import ( + "time" + + rule "mongo.games.com/game/gamerule/smallrocket" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/smallrocket" + + "mongo.games.com/goserver/core/logger" +) + +type PlayerData struct { + SnId int32 + Head int32 //头像框 + VIP int32 //VIP帐号 等级 + Name string //名字 + Sex int32 //性别 + IsRob bool + + Coin int64 + gainCoin int64 //本局赢的金币 + taxCoin int64 //本局税收 + isBilled bool //是否结算 + CurIsWin int64 //当局输赢 负数:输 正数:赢 + + InviterId int32 //邀请人Id + BeUnderAgentCode string //隶属经销商(推广人) + CurBetData []*rule.BetDataEx // 本局的下注信息 + IsPlayerFirst bool + + Platform string //平台 + Channel string //渠道信息 + PackageID string //推广包标识 对应客户端的packagetag + flag int +} + +type SceneEx struct { + *base.Scene // 场景 + logic *rule.Logic // 小火箭算法 + players map[int32]*PlayerEx // 玩家信息 + PlayerBackup map[int32]*PlayerData // 本局离场玩家数据备份 + seats []*PlayerEx // 本局游戏中的玩家状态数据 + + TotalBetPlayer int // 本局下注人数 + NextTotalBetPlayers int // 下局投注人数 + gamePlayerNum int // 参与游戏人数 + BoomMul float64 // 本局火箭飞行爆炸倍数 + BombRandomNum int // 本局火箭飞行随机数 + startTime time.Duration // 本局开始时间 + bombTime time.Duration // 本局火箭爆炸时间 + + HistoryBombMul *rule.SequentialQueue // 房间历史爆炸倍数记录 + + RoundId int // 局数,第几局 + robotNum int // 参与游戏的机器人数量 + logid string +} + +// 游戏是否能开始 +func (this *SceneEx) CanStart() bool { + //人数>=1自动开始 + if len(this.players) >= 0 && (this.GetRealPlayerNum() >= 0 || this.IsPreCreateScene()) { + return true + } + return false +} + +// 从房间删除玩家 +func (this *SceneEx) delPlayer(p *base.Player) { + if p, exist := this.players[p.SnId]; exist { + this.seats[p.GetPos()] = nil + delete(this.players, p.SnId) + } +} + +// 广播玩家离开 +func (this *SceneEx) BroadcastPlayerLeave(p *base.Player, reason int) { + scLeavePack := &smallrocket.SCSmallRocketPlayerLeave{ + Pos: proto.Int(p.GetPos()), + } + proto.SetDefaults(scLeavePack) + + this.Broadcast(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_PlayerLeave), scLeavePack, p.GetSid()) +} + +// 玩家离开事件 +func (this *SceneEx) OnPlayerLeave(p *base.Player, reason int) { + this.delPlayer(p) + this.BroadcastPlayerLeave(p, reason) +} +func (this *SceneEx) SceneDestroy(force bool) { + //销毁房间 + this.Scene.Destroy(force) +} + +func (e *SceneEx) playerOpPack(snId int32, opcode int, opRetCode smallrocket.OpResultCode, params []int64, betDataInfo []*rule.BetDataEx) *smallrocket.SCSmallRocketOp { + pack := &smallrocket.SCSmallRocketOp{ + SnId: proto.Int32(snId), + OpCode: proto.Int32(int32(opcode)), + Params: params, + OpRetCode: smallrocket.OpResultCode_OPRC_Success, + } + + if betDataInfo != nil { + for i := 0; i < len(betDataInfo); i++ { + + tempBetEml := &smallrocket.BetDataInfo{ + BetVal: proto.Int64(betDataInfo[i].BetVal), + TakeMul: proto.Float32(float32(betDataInfo[i].TakeMul)), + IsCurBet: proto.Bool(betDataInfo[i].IsCurBet), + IsNextBet: proto.Bool(betDataInfo[i].IsNextBet), + IsTakeGain: proto.Bool(betDataInfo[i].IsTakeGain), + IsAutoBetAndTake: proto.Bool(betDataInfo[i].IsAutoBetAndTake), + } + + pack.BetDataArr = append(pack.BetDataArr, tempBetEml) + } + } + proto.SetDefaults(pack) + return pack +} + +// OnPlayerSCOp 发送玩家操作情况 +func (e *SceneEx) OnPlayerSCOp(p *base.Player, opcode int, opRetCode smallrocket.OpResultCode, params []int64, betDataInfo []*rule.BetDataEx) { + pack := e.playerOpPack(p.SnId, opcode, opRetCode, params, betDataInfo) + p.SendToClient(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_PLAYEROP), pack) + logger.Logger.Tracef("OnPlayerSCOp %s", pack) +} + +func (this *SceneEx) SubBetPlayerNum() { + this.TotalBetPlayer-- + if this.TotalBetPlayer < 0 { + this.TotalBetPlayer = 0 + } +} + +// 房间信息打包 +func (this *SceneEx) SamllRocketCreateRoomInfoPacket(s *base.Scene, p *base.Player) interface{} { + pack := &smallrocket.SCSmallRocketRoomInfo{ + RoomId: proto.Int(s.GetSceneId()), + GameId: proto.Int(s.GetGameId()), + RoomMode: proto.Int(s.GetSceneMode()), + Params: this.Params, + State: proto.Int(s.GetSceneState().GetState()), + TimeOut: proto.Int(s.GetSceneState().GetTimeout(s)), + BombMul: proto.Float32(float32(0)), + BombRandomNum: proto.Int(0), + TotalPlayer: proto.Int(len(this.players)), + RoundId: proto.Int(this.RoundId), + TotalBetPlayer: proto.Int(this.TotalBetPlayer), + ParamsEx: nil, + GameFreeId: 0, + BaseScore: proto.Int32(this.GetBaseScore()), + } + + // 历史积分添加 + if this.HistoryBombMul != nil { + hisBoomMul := this.HistoryBombMul.GetFloat64Items() + for i := 0; i < len(hisBoomMul); i++ { + pack.RoundBoomMuHistory = append(pack.RoundBoomMuHistory, float32(hisBoomMul[i])) + } + } + + // 玩家信息 + for _, playerEx := range this.players { + + if p.SnId == playerEx.SnId { + pd := &smallrocket.SmallRocketPlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.Coin), + Flag: proto.Int(playerEx.GetFlag()), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + + WinCoin: proto.Int64(playerEx.gainCoin), + } + + if playerEx.betData != nil { + for i := 0; i < len(playerEx.betData); i++ { + + tempBetEml := &smallrocket.BetDataInfo{ + BetVal: proto.Int64(playerEx.betData[i].BetVal), + TakeMul: proto.Float32(float32(playerEx.betData[i].TakeMul)), + IsCurBet: proto.Bool(playerEx.betData[i].IsCurBet), + IsNextBet: proto.Bool(playerEx.betData[i].IsNextBet), + IsTakeGain: proto.Bool(playerEx.betData[i].IsTakeGain), + IsAutoBetAndTake: proto.Bool(playerEx.betData[i].IsAutoBetAndTake), + } + + pd.BetDataArr = append(pd.BetDataArr, tempBetEml) + } + } + pack.Players = append(pack.Players, pd) + } + } + + //// 备份的玩家信息 + //for _, playerEx := range this.PlayerBackup { + // if playerEx != nil { + // continue + // } + // + // pd := &smallrocket.SmallRocketPlayerData{ + // SnId: proto.Int32(playerEx.SnId), + // Name: proto.String(playerEx.Name), + // Head: proto.Int32(playerEx.Head), + // Sex: proto.Int32(playerEx.Sex), + // Coin: proto.Int64(playerEx.Coin), + // Flag: proto.Int(playerEx.flag), + // VIP: proto.Int32(playerEx.VIP), + // + // WinCoin: proto.Int64(playerEx.gainCoin), + // } + // + // if s.SceneState.GetState() == rule.SmallRocketSceneStateBilled { + // pd.WinCoin = playerEx.gainCoin + // } + // + // // 下注信息 + // if playerEx.betData != nil { + // for i := 0; i < len(playerEx.betData); i++ { + // + // tempBetEml := &smallrocket.BetDataInfo{ + // BetVal: proto.Int64(playerEx.betData[i].BetVal), + // TakeMul: proto.Float32(float32(playerEx.betData[i].TakeMul)), + // IsCurBet: proto.Bool(playerEx.betData[i].IsCurBet), + // IsNextBet: proto.Bool(playerEx.betData[i].IsNextBet), + // IsTakeGain: proto.Bool(playerEx.betData[i].IsTakeGain), + // IsAutoBetAndTake: proto.Bool(playerEx.betData[i].IsAutoBetAndTake), + // } + // + // pd.BetDataArr = append(pd.BetDataArr, tempBetEml) + // } + // } + // + // pack.Players = append(pack.Players, pd) + //} + + proto.SetDefaults(pack) + if p != nil { + p.SyncFlag() + } + + logger.Logger.Trace("SCSmallRocketRoomInfo:", pack) + return pack +} + +func NewSmallRocketSceneData(s *base.Scene) *SceneEx { + sceneEx := &SceneEx{ + Scene: s, + logic: new(rule.Logic), + players: make(map[int32]*PlayerEx), + seats: make([]*PlayerEx, s.GetPlayerNum()), + PlayerBackup: make(map[int32]*PlayerData), + HistoryBombMul: rule.NewSequentialQueue(5), + } + + return sceneEx +} + +func (this *SceneEx) init() bool { + this.Clear() + //this.logic.CalBoomTime(rule.SmallRocketBombMulMaxE) + return true +} + +func (this *SceneEx) GetBaseScore() int32 { //游戏底分 + if this.DbGameFree != nil { + return this.DbGameFree.GetBaseScore() + } + return 1 +} + +func (this *SceneEx) GetBetMaxCoin() int32 { //游戏底分 + if this.DbGameFree != nil { + return this.DbGameFree.GetBaseScore() * 10000 + } + return 1 * 10000 +} + +// 检查下注是否合法 +func (this *SceneEx) CheckBetOp(betVal int64, takeMul int64) bool { //游戏底分 + + if betVal <= 0 || takeMul < rule.SmallRocketPlayerTakeMulMax { + return false + } + + if betVal < int64(this.GetBaseScore()) || betVal > int64(this.GetBetMaxCoin()) { + return false + } + + return true +} + +func (this *SceneEx) Clear() { + this.gamePlayerNum = 0 + this.robotNum = 0 + this.BoomMul = 0 + + this.bombTime = 0 + this.BombRandomNum = rule.SmallRocketPlayerTransDataMul + this.startTime = rule.SmallRocketSceneStartTimeout + + this.PlayerBackup = make(map[int32]*PlayerData) + + this.TotalBetPlayer = 0 + this.NextTotalBetPlayers = 0 + this.HistoryBombMul.Clear() + this.RoundId = 0 + + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] != nil { + this.seats[i].Clear(this.GetBaseScore()) + } + } +} + +func (this *SceneEx) BackupPlayer(p *PlayerEx, isBilled bool) { + this.PlayerBackup[p.SnId] = &PlayerData{ + SnId: p.SnId, + gainCoin: p.gainCoin, + taxCoin: p.taxCoin, + isBilled: isBilled, + IsRob: p.IsRob, + Coin: p.Coin, + Head: p.Head, + flag: p.GetFlag(), + Platform: p.Platform, + Channel: p.Channel, + PackageID: p.PackageID, + CurIsWin: p.CurIsWin, + Name: p.Name, + Sex: p.Sex, + VIP: p.VIP, + InviterId: p.InviterId, + IsPlayerFirst: this.IsPlayerFirst(p.Player), + BeUnderAgentCode: p.BeUnderAgentCode, + CurBetData: p.CurBetData, + } +} diff --git a/gamesrv/smallrocket/scenepolicy.go b/gamesrv/smallrocket/scenepolicy.go new file mode 100644 index 0000000..0203250 --- /dev/null +++ b/gamesrv/smallrocket/scenepolicy.go @@ -0,0 +1,1364 @@ +package smallrocket + +import ( + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/smallrocket" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/smallrocket" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" +) + +var PolicySmallRocketSingleton = &PolicySmallRocket{} + +type PolicySmallRocket struct { + base.BaseScenePolicy + states [rule.SmallRocketSceneStateMax]base.SceneState +} + +func (this *PolicySmallRocket) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewSmallRocketSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.ExtraData = sceneEx + } + } + return sceneEx +} + +func (this *PolicySmallRocket) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &PlayerEx{Player: p} + if playerEx != nil { + p.ExtraData = playerEx + } + return playerEx +} + +func (this *PolicySmallRocket) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *PolicySmallRocket) OnStart, sceneId=", s.GetSceneId()) + + sceneEx := NewSmallRocketSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.ExtraData = sceneEx + s.ChangeSceneState(rule.SmallRocketSceneStateWait) + } + } +} + +func (this *PolicySmallRocket) OnStop(s *base.Scene) { + logger.Logger.Trace("(this *PolicyThirteen) OnStop , sceneId=", s.GetSceneId()) +} + +func (this *PolicySmallRocket) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.SceneState != nil { + s.SceneState.OnTick(s) + } +} + +func (this *PolicySmallRocket) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.SnId) + + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + pos := -1 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + if sceneEx.seats[i] == nil { + pos = i + break + } + } + if pos != -1 { + playerEx := &PlayerEx{Player: p} + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + + baseScore := sceneEx.GetBaseScore() + + p.Pos = pos + p.ExtraData = playerEx + playerEx.Clear(baseScore) + + if sceneEx.Gaming { + p.MarkFlag(base.PlayerState_WaitNext) + p.UnmarkFlag(base.PlayerState_Ready) + } + + //给自己发送房间信息 + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } + } +} + +func (this *PolicySmallRocket) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.SnId) + if s == nil || p == nil { + return + } + if !this.CanChangeCoinScene(s, p) { + return + } + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return + } + + isBilled := false + + // 游戏已开始,玩家离开,备份玩家数据 + if sceneEx.Gaming { + isBilled = playerEx.IsFinishTakeBet(sceneEx.GetSceneState().GetState()) + sceneEx.BackupPlayer(playerEx, isBilled) + } + + // 清理玩家数据 + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, nil) +} + +func (this *PolicySmallRocket) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) +} + +func (this *PolicySmallRocket) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if _, ok := p.ExtraData.(*PlayerEx); ok { + //发送房间信息给自己 + if p.IsGameing() { + p.MarkFlag(base.PlayerState_Ready) + } + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +func (this *PolicySmallRocket) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if _, ok := p.ExtraData.(*PlayerEx); ok { + //发送房间信息给自己 + if p.IsGameing() { + p.MarkFlag(base.PlayerState_Ready) + } + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func (this *PolicySmallRocket) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerOp, sceneId=", s.GetSceneId(), " player=", p.SnId, " opcode=", opcode, " params=", params) + if s.SceneState != nil { + p.LastOPTimer = time.Now() + p.Trusteeship = 0 + return s.SceneState.OnPlayerOp(s, p, opcode, params) + } + return true +} + +func (this *PolicySmallRocket) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicySmallRocket) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " eventcode=", evtcode, " params=", params) + if s.SceneState != nil { + s.SceneState.OnPlayerEvent(s, p, evtcode, params) + } +} + +func (this *PolicySmallRocket) IsCompleted(s *base.Scene) bool { + if s == nil { + return false + } + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return !sceneEx.Gaming + } + return false +} + +func (this *PolicySmallRocket) IsCanForceStart(s *base.Scene) bool { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return len(s.Players) >= 2 && !sceneEx.Gaming + } + return false +} + +func (this *PolicySmallRocket) ForceStart(s *base.Scene) { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if sceneEx.SceneState.GetState() == rule.SmallRocketSceneStateWait { + s.ChangeSceneState(rule.SmallRocketSceneStateStart) + } + } +} + +// 当前状态能否退出游戏 +func (this *PolicySmallRocket) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + + return true +} + +func (this *PolicySmallRocket) SendRoomInfo(s *base.Scene, p *base.Player, sceneEx *SceneEx) { + pack := sceneEx.SamllRocketCreateRoomInfoPacket(s, p) + p.SendToClient(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_ROOMINFO), pack) +} + +// 广播房间状态 params[0]: bombMul params[1]: bombRandomVal +func SmallRocketBroadcastRoomState(s *base.Scene, params ...float32) { + pack := &smallrocket.SCSmallRocketRoomState{ + State: proto.Int(s.SceneState.GetState()), + Params: params, + } + s.Broadcast(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_ROOMSTATE), pack, 0) +} + +// 广播下注人数 +func SmallRocketBroadcastRoomBetPlayerNum(s *base.Scene, params ...int32) { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + logger.Logger.Trace("SmallRocketBroadcastRoomBetPlayerNum, sceneId=", s.GetSceneId(), " | sceneEx.TotalBetPlayer=", sceneEx.TotalBetPlayer) + + if sceneEx.TotalBetPlayer < 0 { + sceneEx.TotalBetPlayer = 0 + } + + pack := &smallrocket.SCSmallRocketBetPlayerChange{ + TotalBetPlayerCnt: proto.Int(sceneEx.TotalBetPlayer), + } + s.Broadcast(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_ROOMPLAYERBETNUM), pack, 0) + } +} + +// 广播有玩家领取收益跳伞 +func SmallRocketBroadcastRoomPlayerTakeGain(s *base.Scene, snId int32, LeaveTime float64) { + + if _, ok := s.ExtraData.(*SceneEx); ok { + pack := &smallrocket.SCSmallRocketPlayerTakeGain{ + SnId: proto.Int32(snId), + LeaveTime: proto.Float32(float32(LeaveTime)), + } + s.Broadcast(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_PLAYERTAKEGAIN), pack, 0) + } +} + +// 玩家信息变化 +func SmallRocketSendPlayerInfo(s *base.Scene, p *PlayerEx, betDataInfo []*rule.BetDataEx, GainCoinBet1 int64, GainCoinBet2 int64) { + + if _, ok := s.ExtraData.(*SceneEx); ok { + pack := &smallrocket.SCSmallRocketPlayerInfo{ + SnId: proto.Int32(p.SnId), + Coin: proto.Int64(p.Coin), + GainCoin: proto.Int64(p.gainCoin), + GainCoinBet1: GainCoinBet1, + GainCoinBet2: GainCoinBet2, + } + + if betDataInfo != nil { + for i := 0; i < len(betDataInfo); i++ { + + tempBetEml := &smallrocket.BetDataInfo{ + BetVal: proto.Int64(betDataInfo[i].BetVal), + TakeMul: proto.Float32(float32(betDataInfo[i].TakeMul)), + IsCurBet: proto.Bool(betDataInfo[i].IsCurBet), + IsNextBet: proto.Bool(betDataInfo[i].IsNextBet), + IsTakeGain: proto.Bool(betDataInfo[i].IsTakeGain), + IsAutoBetAndTake: proto.Bool(betDataInfo[i].IsAutoBetAndTake), + } + + pack.BetDataArr = append(pack.BetDataArr, tempBetEml) + } + } + + p.SendToClient(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_PLAYERINFO), pack) + } +} + +//===================================== +// BaseState 状态基类 +//===================================== + +type BaseState struct { +} + +func (this *BaseState) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return int(time.Now().Sub(sceneEx.StateStartTime) / time.Second) + } + return 0 +} + +func (this *BaseState) CanChangeTo(s base.SceneState) bool { + return true +} + +func (this *BaseState) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + + //playerEx, ok := p.ExtraData.(*PlayerEx) + //if !ok { + // return false + //} + // + //if !playerEx.CanLeaveScene(s.GetSceneState().GetState()) { + // return false + //} + + return true +} + +func (this *BaseState) OnEnter(s *base.Scene) { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + sceneEx.StateStartTime = time.Now() + } +} + +func (this *BaseState) OnLeave(s *base.Scene) {} + +func (this *BaseState) OnTick(s *base.Scene) { +} + +func (this *BaseState) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + switch opcode { + case rule.SmallRocketPlayerOpSResumeGetPlayerInfo: // + if len(params) >= 1 { + + params = append(params, playerEx.Coin) + params = append(params, int64(this.GetTimeout(s))) + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + } + } + return false +} + +func (this *BaseState) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { +} + +//===================================== +// StateWait 等待中 +//===================================== + +type StateWait struct { + BaseState +} + +func (this *StateWait) GetState() int { + return rule.SmallRocketSceneStateWait +} + +func (this *StateWait) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.SmallRocketSceneStateStart { + return true + } + return false +} + +func (this *StateWait) GetTimeout(s *base.Scene) int { + + return this.BaseState.GetTimeout(s) +} + +func (this *StateWait) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if s.Gaming { + s.NotifySceneRoundPause() + } + s.Gaming = false + + sceneEx.logic.LoadCrashSearchData() + + SmallRocketBroadcastRoomState(s, float32(0), float32(0)) + + if sceneEx.CanStart() { + s.ChangeSceneState(rule.SmallRocketSceneStateStart) + } + } +} + +// 玩家事件 +func (this *StateWait) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *StateWait) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode) + this.BaseState.OnPlayerEvent(s, p, evtcode, params) + if _, ok := s.ExtraData.(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + case base.PlayerEventEnter: + if !p.IsReady() { + p.MarkFlag(base.PlayerState_Ready) + p.SyncFlag() + } + } + } +} + +func (this *StateWait) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if s.CheckNeedDestroy() { + sceneEx.SceneDestroy(true) + return + } + if time.Now().Sub(sceneEx.StateStartTime) > rule.SmallRocketSceneWaitTimeout { + //切换到准备开局状态 + if sceneEx.CanStart() { + s.ChangeSceneState(rule.SmallRocketSceneStateStart) + } + } + } +} + +//===================================== +// StateStart 开始倒计时 +//===================================== + +type StateStart struct { + BaseState +} + +func (this *StateStart) GetState() int { + return rule.SmallRocketSceneStateStart +} + +func (this *StateStart) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SmallRocketSceneStatePlayGame: + return true + } + return false +} + +func (this *StateStart) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + SmallRocketBroadcastRoomState(s, float32(0), float32(0)) + s.Gaming = false + sceneEx.GameNowTime = time.Now() + sceneEx.NumOfGames++ + + sceneEx.TotalBetPlayer = 0 + sceneEx.BoomMul, sceneEx.BombRandomNum = sceneEx.logic.CalBoomMul(int(rule.SmallRocketBombRandomMulMaxE)) + + sceneEx.logid, _ = model.AutoIncGameLogId() + + if sceneEx.BoomMul >= rule.SmallRocketBombRoomMulMaxE { + sceneEx.BoomMul = rule.SmallRocketBombRoomMulMaxE + } + + BoomTime := sceneEx.logic.GetBombTimeByBombMul(int32(sceneEx.BoomMul * rule.SmallRocketPlayerTransDataMul)) + if BoomTime < rule.SmallRocketPlayerTransDataMul { + BoomTime = rule.SmallRocketPlayerTransDataMul + } + + sceneEx.bombTime = time.Duration(float64(BoomTime) * float64(time.Second) / rule.SmallRocketPlayerTransDataMul) + + logger.Logger.Trace("StateStart------OnEnter: sceneEx.BombRandomNum: ", sceneEx.BombRandomNum, " | sceneEx.BoomMul: ", sceneEx.BoomMul, " | sceneEx.bombTime: ", sceneEx.bombTime) + + for _, playerEx := range sceneEx.players { + if playerEx != nil { + + playerEx.PlayerAutoBet(this.GetState()) + + playerEx.StopBetCoin() + + SmallRocketSendPlayerInfo(s, playerEx, playerEx.betData, 0, 0) + + for i := 0; i < len(playerEx.betData); i++ { + if playerEx.betData[i].IsCurBet && !playerEx.betData[i].IsTakeGain { + var gainSearchTime = float64(sceneEx.logic.GetBombTimeByBombMul(int32(playerEx.betData[i].TakeMul * float64(rule.SmallRocketPlayerTransDataMul)))) + if gainSearchTime < rule.SmallRocketPlayerTransDataMul { + gainSearchTime = rule.SmallRocketPlayerTransDataMul + } + + playerEx.betData[i].GainSearchTime = time.Duration((gainSearchTime / rule.SmallRocketPlayerTransDataMul) * float64(time.Second)) + logger.Logger.Trace("StateStart------OnEnter: SnId=", playerEx.SnId, " | betData[", i, "]: ", " | GainSearchTime: ", playerEx.betData[i].GainSearchTime) + + // 没有自动的才扣钱 + if !playerEx.IsAutoBetAndTakeByPos(int64(i)) { + if playerEx.betData[i].BetVal > 0 { + playerEx.AddCoin(-playerEx.betData[i].BetVal, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } else if playerEx.betData[i].BetVal < 0 { + logger.Logger.Error("(this *StateStart) OnEnter, sceneid:", s.GetSceneId(), " | Player SnId:", playerEx.SnId, " | BetCoinVal:", playerEx.betData[i].BetVal) + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + } + } + } + } + + playerEx.CopyToCurBetData() + + if sceneEx.logic.IsCurRoundBet(playerEx.betData) { + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + sceneEx.TotalBetPlayer++ + } + } + } + + SmallRocketBroadcastRoomBetPlayerNum(s) + } +} + +func (this *StateStart) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.SmallRocketSceneStartTimeout { + //切换到等待操作状态 + if sceneEx.CanStart() { + logger.Logger.Trace("StateStart------SmallRocketSceneStartTimeout: ", rule.SmallRocketSceneStartTimeout) + s.ChangeSceneState(rule.SmallRocketSceneStatePlayGame) + } else { + s.ChangeSceneState(rule.SmallRocketSceneStateWait) + } + } + } +} + +func (this *StateStart) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.BaseState.OnPlayerOp(s, p, opcode, params) { + return true + } + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + //if !playerEx.IsGameing() { + // //returnFunc(thirteen.OpResultCode_OPRC_Error) + // return true + //} + + logger.Logger.Trace("StateStart OnPlayerOp-----SnId:", playerEx.SnId, " opcode: ", opcode, " params:", params) + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + + switch opcode { + case rule.SmallRocketPlayerOpBet: // 下注 + if len(params) >= 3 { + betPos := params[0] + betVal := params[1] + takeMul := params[2] + + if !sceneEx.CheckBetOp(betVal, takeMul) { + return false + } + + if len(playerEx.betData) >= 2 && (betPos >= 0 && betPos <= 1) { + + // 当前位置已经下注不允许再次下注 + if sceneEx.logic.IsCurRoundBetPos(playerEx.betData, betPos) && !playerEx.betData[betPos].IsAutoBetAndTake { + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_PosAlReadyBet, params, playerEx.betData) + return false + } + + // 检测能否下注 + if !playerEx.CanBetCoinByPos(int(betPos), betVal) { + return false + } + + // 之前没有下过注 下注人数增加, 通知服务器 + if !sceneEx.logic.IsCurRoundBet(playerEx.betData) { + sceneEx.TotalBetPlayer++ + SmallRocketBroadcastRoomBetPlayerNum(s) + } + + playerEx.betData[betPos].BetVal = betVal + playerEx.betData[betPos].TakeMul = float64(takeMul) / 100.00 + playerEx.betData[betPos].IsCurBet = true + playerEx.betData[betPos].IsNextBet = false + playerEx.betData[betPos].IsTakeGain = false + + if playerEx.betData[betPos].IsCurBet { + var gainSearchTime = float64(sceneEx.logic.GetBombTimeByBombMul(int32(playerEx.betData[betPos].TakeMul * float64(rule.SmallRocketPlayerTransDataMul)))) + if gainSearchTime < rule.SmallRocketPlayerTransDataMul { + gainSearchTime = rule.SmallRocketPlayerTransDataMul + } + + playerEx.betData[betPos].GainSearchTime = time.Duration((gainSearchTime / rule.SmallRocketPlayerTransDataMul) * float64(time.Second)) + + if playerEx.betData[betPos].BetVal > 0 { + playerEx.AddCoin(-playerEx.betData[betPos].BetVal, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } else if playerEx.betData[betPos].BetVal < 0 { + logger.Logger.Error("(this *StateStart) OnPlayerOp, sceneid:", s.GetSceneId(), " | Player SnId:", playerEx.SnId, " | betData[", betPos, "]: ", " | BetCoinVal:", playerEx.betData[betPos].BetVal) + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + } + + logger.Logger.Trace("StateStart SmallRocketPlayerOpBet------SnId:", playerEx.SnId, " | betData[", betPos, "]: ", " | BetVal: ", playerEx.betData[betPos].BetVal) + } + + params = append(params, playerEx.Coin) + + playerEx.CopyToCurBetData() + + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + } + } + case rule.SmallRocketPlayerOpSetAutoBet, rule.SmallRocketPlayerOpSetAutoTakeGain: // 设置自动下注状态 + if len(params) >= 4 { + isAutoTakeGain := params[0] + betPos := params[1] + betVal := params[2] + takeMul := params[3] + + if !sceneEx.CheckBetOp(betVal, takeMul) { + return false + } + + if len(playerEx.betData) >= 2 && (betPos >= 0 && betPos <= 1) { + + // 检测能否下注 + if !playerEx.CanBetCoinByPos(int(betPos), betVal) { + return false + } + + // 下一局已下注 + if playerEx.betData[betPos].IsNextBet { + return false + } + + // 本局还在下注状态下 + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + return false + } + + playerEx.betData[betPos].BetVal = betVal + playerEx.betData[betPos].TakeMul = float64(takeMul) / 100.00 + playerEx.betData[betPos].IsCurBet = true + + playerEx.SetAutoBetAndTake(isAutoTakeGain, betPos) + + // 自动 下注未领取奖励 + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + var gainSearchTime = float64(sceneEx.logic.GetBombTimeByBombMul(int32(playerEx.betData[betPos].TakeMul * float64(rule.SmallRocketPlayerTransDataMul)))) + if gainSearchTime < rule.SmallRocketPlayerTransDataMul { + gainSearchTime = rule.SmallRocketPlayerTransDataMul + } + + playerEx.betData[betPos].GainSearchTime = time.Duration((gainSearchTime / rule.SmallRocketPlayerTransDataMul) * float64(time.Second)) + + if playerEx.betData[betPos].BetVal > 0 { + playerEx.AddCoin(-playerEx.betData[betPos].BetVal, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } else if playerEx.betData[betPos].BetVal < 0 { + logger.Logger.Error("(this *StateStart) OnPlayerOp 4 , sceneid:", s.GetSceneId(), " | Player SnId:", playerEx.SnId, " | BetCoinVal:", playerEx.betData[betPos].BetVal) + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + } + } + + params = append(params, playerEx.Coin) + + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + + playerEx.CopyToCurBetData() + } else { + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Error, params, playerEx.betData) + } + + } + case rule.SmallRocketPlayerOpAutoSubCoin: + if len(params) >= 8 { + //isAutoTakeGain := params[0] + betPos0 := params[1] + betVal0 := params[2] + takeMul0 := params[3] + + betPos1 := params[5] + betVal1 := params[6] + takeMul1 := params[7] + + if !sceneEx.CheckBetOp(betVal0, takeMul0) { + return false + } + + if !sceneEx.CheckBetOp(betVal1, takeMul1) { + return false + } + + if len(playerEx.betData) >= 2 && (betPos0 >= 0 && betPos0 <= 1) && (betPos1 >= 0 && betPos1 <= 1) { + + playerEx.betData[betPos0].BetVal = betVal0 + playerEx.betData[betPos0].TakeMul = float64(takeMul0) / 100.00 + + playerEx.betData[betPos1].BetVal = betVal1 + playerEx.betData[betPos1].TakeMul = float64(takeMul1) / 100.00 + + playerEx.StopBetCoin() + + for i := 0; i < len(playerEx.betData); i++ { + // 自动下注领取 扣除金币 + if playerEx.betData[i].IsCurBet && playerEx.betData[i].IsAutoBetAndTake { + var gainSearchTime = float64(sceneEx.logic.GetBombTimeByBombMul(int32(playerEx.betData[i].TakeMul * float64(rule.SmallRocketPlayerTransDataMul)))) + if gainSearchTime < rule.SmallRocketPlayerTransDataMul { + gainSearchTime = rule.SmallRocketPlayerTransDataMul + } + + playerEx.betData[i].GainSearchTime = time.Duration((gainSearchTime / rule.SmallRocketPlayerTransDataMul) * float64(time.Second)) + + if playerEx.betData[i].BetVal > 0 { + playerEx.AddCoin(-playerEx.betData[i].BetVal, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } else if playerEx.betData[i].BetVal <= 0 { + logger.Logger.Error("(this *StateStart) OnPlayerOp 7, sceneid:", s.GetSceneId(), " | Player SnId:", playerEx.SnId, " | BetCoinVal:", playerEx.betData[i].BetVal) + playerEx.PrintBetData(rule.SmallRocketSceneStateStart) + } + + //params = append(params, playerEx.Coin) + // + //sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + } + } + + playerEx.CopyToCurBetData() + } else { + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Error, params, playerEx.betData) + } + } + default: + return false + } + return false +} + +func (this *StateStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *StateStart) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode) + this.BaseState.OnPlayerEvent(s, p, evtcode, params) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + if !sceneEx.CanStart() { + logger.Logger.Trace("(this *StateStart) OnPlayerEvent s.ChangeSceneState(SmallRocketSceneStateWait):", s.GetSceneId()) + s.ChangeSceneState(rule.SmallRocketSceneStateWait) + } + case base.PlayerEventEnter: + if !p.IsReady() { + p.MarkFlag(base.PlayerState_Ready) + p.SyncFlag() + } + } + } +} + +// ===================================== +// PlayGame 游戏中 +// ===================================== +type PlayGame struct { + BaseState +} + +func (this *PlayGame) GetState() int { + return rule.SmallRocketSceneStatePlayGame +} + +func (this *PlayGame) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SmallRocketSceneStateBilled: + return true + } + return false +} + +func (this *PlayGame) OnEnter(s *base.Scene) { + logger.Logger.Trace("(this *PlayGame) OnEnter, sceneid=", s.GetSceneId()) + + this.BaseState.OnEnter(s) + + s.Gaming = true + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return + } + + for _, playerEx := range sceneEx.players { + if playerEx != nil { + SmallRocketSendPlayerInfo(s, playerEx, playerEx.betData, 0, 0) + } + } + + SmallRocketBroadcastRoomState(s, float32(0), float32(0)) + SmallRocketBroadcastRoomBetPlayerNum(s) +} + +func (this *PlayGame) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + + logger.Logger.Trace("StatePlayGame OnPlayerOp-----SnId:", p.SnId, " opcode: ", opcode, " params:", params) + + if this.BaseState.OnPlayerOp(s, p, opcode, params) { + return true + } + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + //if !playerEx.IsGameing() { + // //returnFunc(thirteen.OpResultCode_OPRC_Error) + // return true + //} + + // playerEx.PrintBetData(rule.SmallRocketSceneStatePlayGame) + + switch opcode { + case rule.SmallRocketPlayerOpBet: // 为下一局下注 + if len(params) >= 3 { + betPos := params[0] + betVal := params[1] + takeMul := params[2] + + if !sceneEx.CheckBetOp(betVal, takeMul) { + return false + } + + if len(playerEx.betData) >= 2 && (betPos >= 0 && betPos <= 1) { + + // 检测能否下注 + if !playerEx.CanBetCoinByPos(int(betPos), betVal) { + return false + } + + // 下一局已下注 + if playerEx.betData[betPos].IsNextBet { + return false + } + + // 本局还在下注状态下 + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + return false + } + + playerEx.betData[betPos].BetVal = betVal + playerEx.betData[betPos].TakeMul = float64(takeMul) / 100.00 + playerEx.betData[betPos].IsNextBet = true + + params = append(params, playerEx.Coin) + + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + } + } + case rule.SmallRocketPlayerOpCancelBet: //取消下一局下注 + if len(params) >= 1 { + betPos := params[0] + if len(playerEx.betData) >= 2 && betPos <= 1 { + + // 没有下注 + if !playerEx.betData[betPos].IsNextBet { + return false + } + + // 本局还在下注状态下 + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + return false + } + + playerEx.betData[betPos].IsNextBet = false + + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + } + } + + case rule.SmallRocketPlayerOpTakeReward: // 下车收取奖励 + if len(params) >= 1 { + betPos := params[0] + + if len(playerEx.betData) >= 2 && (betPos >= 0 && betPos <= 1) { + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + + //var startTimeDur = time.Now().Sub(sceneEx.StateStartTime) + //logger.Logger.Trace("StatePlayGame OnPlayerOp OpTakeReward:-----SnId:", playerEx.SnId, " startTimeDur: ", startTimeDur, " GainSearchTime:", playerEx.betData[betPos].GainSearchTime) + + gainSearchMul := sceneEx.logic.GetBombMulByBombTime(int32(this.GetTimeout(s) * rule.SmallRocketPlayerTransDataMul)) + + gainCoinMul := float64(gainSearchMul) / rule.SmallRocketPlayerTransDataMul + gainCoin := int64(float64(playerEx.betData[betPos].BetVal) * gainCoinMul) + playerEx.gainCoin += gainCoin + + if gainCoin > 0 { + playerEx.AddCoin(gainCoin, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = 1 + playerEx.betData[betPos].GainCoinMul = gainCoinMul + } + + playerEx.betData[betPos].IsTakeGain = true + + params = append(params, gainCoin) + params = append(params, playerEx.Coin) + + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + + SmallRocketBroadcastRoomPlayerTakeGain(s, playerEx.SnId, float64(this.GetTimeout(s))) + + if sceneEx.logic.IsExistRoundTakenGain(playerEx.betData) { + sceneEx.SubBetPlayerNum() + SmallRocketBroadcastRoomBetPlayerNum(s) + } + + playerEx.CopyToCurBetData() + } else { + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Error, params, playerEx.betData) + } + } + + } + case rule.SmallRocketPlayerOpSetAutoBet, rule.SmallRocketPlayerOpSetAutoTakeGain: // 设置自动下注状态 + if len(params) >= 4 { + isAutoTakeGain := params[0] + betPos := params[1] + betVal := params[2] + takeMul := params[3] + + if !sceneEx.CheckBetOp(betVal, takeMul) { + return false + } + + if len(playerEx.betData) >= 2 && (betPos >= 0 && betPos <= 1) { + + // 检测能否下注 + if !playerEx.CanBetCoinByPos(int(betPos), betVal) { + return false + } + + // 下一局已下注 + if playerEx.betData[betPos].IsNextBet { + return false + } + + // 本局还在下注状态下 + if playerEx.betData[betPos].IsCurBet && !playerEx.betData[betPos].IsTakeGain { + return false + } + + playerEx.betData[betPos].BetVal = betVal + playerEx.betData[betPos].TakeMul = float64(takeMul) / 100.00 + + playerEx.SetAutoBetAndTake(isAutoTakeGain, betPos) + + params = append(params, playerEx.Coin) + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Success, params, playerEx.betData) + + } else { + sceneEx.OnPlayerSCOp(p, opcode, smallrocket.OpResultCode_OPRC_Error, params, playerEx.betData) + } + + } + default: + return false + } + return false +} + +func (this *PlayGame) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + + if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.bombTime { + logger.Logger.Trace("PlayGame------sceneEx.bombTime: ", sceneEx.bombTime) + s.ChangeSceneState(rule.SmallRocketSceneStateBilled) + } else { + // 玩家自动收取功能 + for _, playerEx := range sceneEx.players { + if playerEx != nil && playerEx.IsAutoBetAndTake() { + for i := 0; i < len(playerEx.betData); i++ { + if playerEx.betData[i].IsAutoBetAndTake && playerEx.betData[i].IsCurBet && !playerEx.betData[i].IsTakeGain { + + if time.Now().Sub(sceneEx.StateStartTime) >= playerEx.betData[i].GainSearchTime { + + gainCoin := int64(float64(playerEx.betData[i].BetVal) * playerEx.betData[i].TakeMul) + playerEx.gainCoin += gainCoin + logger.Logger.Trace("(this *PlayGame) OnTick: SnId:", playerEx.SnId, " | timeOut: ", time.Now().Sub(sceneEx.StateStartTime), " gainSearchTime: ", playerEx.betData[i].GainSearchTime) + + if gainCoin > 0 { + playerEx.AddCoin(gainCoin, common.GainWay_SmallRocket, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = 1 + + playerEx.betData[i].GainCoinMul = playerEx.betData[i].TakeMul + + if playerEx.betData[i].IsCurBet { + playerEx.betData[i].IsTakeGain = true + } + + logger.Logger.Trace("(this *PlayGame) OnTick------SnId:", playerEx.SnId, " | playerEx.gainCoin: ", gainCoin) + + GainCoinBet1 := int64(0) + GainCoinBet2 := int64(0) + if i == 0 { + GainCoinBet1 = gainCoin + GainCoinBet2 = 0 + } else if i == 1 { + GainCoinBet1 = 0 + GainCoinBet2 = gainCoin + } + + SmallRocketSendPlayerInfo(s, playerEx, playerEx.betData, GainCoinBet1, GainCoinBet2) + + //playerEx.PrintBetData(rule.SmallRocketSceneStatePlayGame) + + SmallRocketBroadcastRoomPlayerTakeGain(s, playerEx.SnId, float64(this.GetTimeout(s))) + + if sceneEx.logic.IsExistRoundTakenGain(playerEx.betData) { + //sceneEx.TotalBetPlayer-- + sceneEx.SubBetPlayerNum() + SmallRocketBroadcastRoomBetPlayerNum(s) + } + } + } + + } + } + + playerEx.CopyToCurBetData() + } + } + } + } +} + +//===================================== +// StateBilled 结算 +//===================================== + +type StateBilled struct { + BaseState +} + +func (this *StateBilled) GetState() int { + return rule.SmallRocketSceneStateBilled +} + +func (this *StateBilled) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.SmallRocketSceneStateStart: + return true + } + return false +} + +func (this *StateBilled) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + return false +} + +func (this *StateBilled) OnEnter(s *base.Scene) { + logger.Logger.Trace("(this *StateBilled) OnEnter, sceneid=", s.GetSceneId()) + this.BaseState.OnEnter(s) + + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + + boomMul := sceneEx.BoomMul + if boomMul < 1 { + boomMul = 1 + } + + // 添加历史爆炸倍数记录 + sceneEx.HistoryBombMul.Push(boomMul) + + SmallRocketBroadcastRoomState(s, float32(0), float32(0)) + + // 没离开,剩余玩家结算 + for _, playerEx := range sceneEx.players { + if playerEx != nil { + pack := &smallrocket.SCSmallRocketRoundGameBilled{ + RoundId: proto.Int32(int32(sceneEx.RoundId)), + BombMul: proto.Float32(float32(boomMul)), + Award: proto.Int64(playerEx.gainCoin), + Balance: proto.Int64(playerEx.Coin), + } + + if sceneEx.HistoryBombMul != nil { + hisBoomMul := sceneEx.HistoryBombMul.GetFloat64Items() + for i := 0; i < len(hisBoomMul); i++ { + pack.RoundBoomMuHistory = append(pack.RoundBoomMuHistory, float32(hisBoomMul[i])) + } + } + + // logger.Logger.Trace("SCSmallRocketRoundGameBilled is pack: ", pack) + proto.SetDefaults(pack) + + playerEx.SendToClient(int(smallrocket.SmallRocketPacketID_PACKET_SC_SMALLROCKET_GAMEBILLED), pack) + + // 本局下注才记录 + if sceneEx.logic.IsCurRoundBet(playerEx.CurBetData) { + sceneEx.Statistics(&base.StaticParam{ + SnId: playerEx.SnId, + Gain: playerEx.gainCoin, + GainTax: playerEx.taxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.robotNum > 0, + }) + + logger.Logger.Trace("SmallRocketSaveLog Save ", playerEx.SnId) + + TotalBetValue := sceneEx.logic.GetRoundBetCoin(playerEx.CurBetData) + + // 统计金币变动 + changeCoin := playerEx.gainCoin - playerEx.taxCoin - int64(TotalBetValue) + startCoin := playerEx.Coin - changeCoin + + playerEx.SaveSceneCoinLog(startCoin, changeCoin, playerEx.GetCoin(), int64(TotalBetValue), playerEx.taxCoin, playerEx.gainCoin, 0, 0) + + //GainTotalBetVal := sceneEx.logic.GetRoundGainCoin(playerEx.betData, ) + LogBaseResult := model.SmallRocketBaseResultType{} + LogBaseResult.RoomId = int32(sceneEx.GetSceneId()) + LogBaseResult.TotalBetVal = int64(TotalBetValue) + LogBaseResult.BeforeCoin = startCoin + LogBaseResult.AfterCoin = playerEx.GetCoin() + LogBaseResult.WinCoin = playerEx.gainCoin + LogBaseResult.PlayerSnid = playerEx.SnId + + if len(playerEx.CurBetData) >= 2 { + LogBaseResult.BetCoin1 = playerEx.CurBetData[0].BetVal + LogBaseResult.BetMul1 = playerEx.CurBetData[0].TakeMul + LogBaseResult.IsAutoBetAndTake1 = playerEx.CurBetData[0].IsAutoBetAndTake + LogBaseResult.TakeBetMul1 = playerEx.CurBetData[0].GainCoinMul + + LogBaseResult.BetCoin2 = playerEx.CurBetData[1].BetVal + LogBaseResult.BetMul2 = playerEx.CurBetData[1].TakeMul + LogBaseResult.IsAutoBetAndTake2 = playerEx.CurBetData[1].IsAutoBetAndTake + LogBaseResult.TakeBetMul2 = playerEx.CurBetData[1].GainCoinMul + } + + if !playerEx.IsRob { + info, err := model.MarshalGameNoteByHUNDRED(LogBaseResult) + if err == nil { + sceneEx.SaveGameDetailedLog(sceneEx.logid, info, &base.GameDetailedParam{}) + } + + totalin := int64(TotalBetValue) + totalout := playerEx.gainCoin + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + logParam := &base.SaveGamePlayerListLogParam{ + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + LogId: sceneEx.logid, + TotalIn: totalin, + TotalOut: totalout, + TaxCoin: playerEx.taxCoin, + BetAmount: int64(TotalBetValue), + WinAmountNoAnyTax: playerEx.gainCoin, + ValidBet: validBet, + ValidFlow: validFlow, + IsFirstGame: sceneEx.IsPlayerFirst(playerEx.Player), + IsFree: false, + WinSmallGame: 0, + WinTotal: 0, + } + sceneEx.SaveGamePlayerListLog(playerEx.SnId, logParam) + } + } + + //playerEx.PrintBetData(rule.SmallRocketSceneStateBilled) + + playerEx.ReStartGame() + + //playerEx.StopBetCoin() + + SmallRocketSendPlayerInfo(s, playerEx, playerEx.betData, 0, 0) + } + } + + // 提前离开的 + for _, playerEx := range sceneEx.PlayerBackup { + if playerEx != nil && playerEx.isBilled { + if sceneEx.logic.IsCurRoundBet(playerEx.CurBetData) { + sceneEx.Statistics(&base.StaticParam{ + SnId: playerEx.SnId, + Gain: playerEx.gainCoin, + GainTax: playerEx.taxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.robotNum > 0, + }) + } + + TotalBetValue := sceneEx.logic.GetRoundBetCoin(playerEx.CurBetData) + + // 统计金币变动 + changeCoin := playerEx.gainCoin - playerEx.taxCoin - int64(TotalBetValue) + startCoin := playerEx.Coin - changeCoin + + // playerEx.SaveSceneCoinLog(startCoin, changeCoin, playerEx.Coin, int64(TotalBetValue), playerEx.taxCoin, playerEx.gainCoin, 0, 0) + + LogBaseResult := model.SmallRocketBaseResultType{} + LogBaseResult.RoomId = int32(sceneEx.GetSceneId()) + LogBaseResult.TotalBetVal = int64(TotalBetValue) + LogBaseResult.BeforeCoin = startCoin + LogBaseResult.AfterCoin = playerEx.Coin + LogBaseResult.WinCoin = playerEx.gainCoin + LogBaseResult.PlayerSnid = playerEx.SnId + + if len(playerEx.CurBetData) >= 2 { + LogBaseResult.BetCoin1 = playerEx.CurBetData[0].BetVal + LogBaseResult.BetMul1 = playerEx.CurBetData[0].TakeMul + LogBaseResult.IsAutoBetAndTake1 = playerEx.CurBetData[0].IsAutoBetAndTake + LogBaseResult.TakeBetMul1 = playerEx.CurBetData[0].GainCoinMul + + LogBaseResult.BetCoin2 = playerEx.CurBetData[1].BetVal + LogBaseResult.BetMul2 = playerEx.CurBetData[1].TakeMul + LogBaseResult.IsAutoBetAndTake2 = playerEx.CurBetData[1].IsAutoBetAndTake + LogBaseResult.TakeBetMul2 = playerEx.CurBetData[1].GainCoinMul + } + + if !playerEx.IsRob { + info, err := model.MarshalGameNoteByHUNDRED(LogBaseResult) + if err == nil { + sceneEx.SaveGameDetailedLog(sceneEx.logid, info, &base.GameDetailedParam{}) + } + + totalin := int64(TotalBetValue) + totalout := playerEx.gainCoin + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + logParam := &base.SaveGamePlayerListLogParam{ + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + LogId: sceneEx.logid, + TotalIn: totalin, + TotalOut: totalout, + TaxCoin: playerEx.taxCoin, + BetAmount: int64(TotalBetValue), + WinAmountNoAnyTax: playerEx.gainCoin, + ValidBet: validBet, + ValidFlow: validFlow, + IsFirstGame: playerEx.IsPlayerFirst, + IsFree: false, + WinSmallGame: 0, + WinTotal: 0, + PlayerName: playerEx.Name, + } + sceneEx.SaveGamePlayerListLog(playerEx.SnId, logParam) + } + } + } + + //通知客户端结算结果 + sceneEx.TotalBetPlayer = 0 + //sceneEx.BoomMul = 0 + sceneEx.RoundId++ + + s.Gaming = false + } +} + +func (this *StateBilled) OnLeave(s *base.Scene) { + logger.Logger.Trace("(this *StateBilled) OnLeave, sceneid=", s.GetSceneId()) + this.BaseState.OnLeave(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + + for _, playerEx := range sceneEx.players { + if playerEx != nil { + // 不在线停止自动下注 + if !playerEx.IsOnLine() { + playerEx.StopLineAutoBetCoin() + sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_DropLine, true) + } + } + } + + if s.CheckNeedDestroy() { + sceneEx.SceneDestroy(true) + } + } +} + +func (this *StateBilled) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.SmallRocketSceneBilledTimeout { + if sceneEx.CanStart() { + + logger.Logger.Trace("StateStart------SmallRocketSceneBilledTimeout: ", rule.SmallRocketSceneBilledTimeout) + s.ChangeSceneState(rule.SmallRocketSceneStateStart) + } else { + s.ChangeSceneState(rule.SmallRocketSceneStateWait) + } + return + } + } +} + +// // ////////////////////////////////////////////////////////////////////////////// +func (this *PolicySmallRocket) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + + if stateid < 0 || stateid >= rule.SmallRocketSceneStateMax { + return + } + this.states[stateid] = state +} + +func (this *PolicySmallRocket) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < rule.SmallRocketSceneStateMax { + return this.states[stateid] + } + return nil +} + +func init() { + PolicySmallRocketSingleton.RegisteSceneState(&StateWait{}) + PolicySmallRocketSingleton.RegisteSceneState(&StateStart{}) + PolicySmallRocketSingleton.RegisteSceneState(&PlayGame{}) + PolicySmallRocketSingleton.RegisteSceneState(&StateBilled{}) + + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameId_SmallRoket, 0, PolicySmallRocketSingleton) + + return nil + }) +} diff --git a/gamesrv/start_gamesrv.sh b/gamesrv/start_gamesrv.sh new file mode 100644 index 0000000..35d1987 --- /dev/null +++ b/gamesrv/start_gamesrv.sh @@ -0,0 +1,98 @@ +#!/bin/bash +#set -x +PRO=gamesrv + +function USAGE(){ + echo "Usage: $0 [Control]. Such as $0 start" + # echo -e "\tProcessName: [ QPHallServer | QPGateWay ]" + echo -e "\tControl: [ start | stop | force_stop | restart | force_restart ]" + exit 1 +} + +function START(){ + rm ./scroll_all.log + nohup ./$PRO & + sleep 5 + ps -ef | grep -v 'grep' | grep -v 'start' | grep ${PRO} + if [ $? == 0 ] + then + echo "${PRO} Started!" + else + echo "Start ${PRO} failed!" + exit 1 + fi +} + +function STOP(){ + rm ./scroll_all.log + pids=$(ps aux | grep $PRO | grep -v grep | awk -F " " '{print $2}') + arr=(${pids//' + '/ }) + #echo $arr + #echo ${#arr[@]} + for ((i=0;i<${#arr[@]};i++)) + do + kill -s 2 ${arr[$i]} > /dev/null 2>&1 + done + echo "${PRO} has been stopped!" +} + +function FORCE_STOP(){ + rm ./scroll_all.log + pids=$(ps aux | grep $PRO | grep -v grep | awk -F " " '{print $2}') + arr=(${pids//' + '/ }) + for ((i=0;i<${#arr[@]};i++)) + do + kill -9 ${arr[$i]} > /dev/null 2>&1 + done + echo "${PRO} has been shutdown!" +} + +function RELOAD() { + pids=$(ps aux | grep $PRO | grep -v grep | awk -F " " '{print $2}') + arr=(${pids//' + '/ }) + for ((i=0;i<${#arr[@]};i++)) + do + kill -1 ${arr[$i]} > /dev/null 2>&1 + done + echo "${PRO} has been RELOADED!" +} + +main(){ + case $1 in + start) + START ; + ;; + stop) + STOP ; + ;; + force_stop) + FORCE_STOP ; + ;; + restart) + STOP ; + START ; + ;; +# reload) +# RELOAD ; +# ;; + force_restart) + FORCE_STOP ; + START ; + esac +} + + +if [ -z $1 ] +then + USAGE +fi + + +main $1 + + + + diff --git a/gamesrv/thirteen/action.go b/gamesrv/thirteen/action.go new file mode 100644 index 0000000..7cccb71 --- /dev/null +++ b/gamesrv/thirteen/action.go @@ -0,0 +1,52 @@ +package thirteen + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/thirteen" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSPlayerOpPacketFactory struct { +} +type CSPlayerOpHandler struct { +} + +func (f *CSPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &thirteen.CSThirteenPlayerOp{} + return pack +} +func (h *CSPlayerOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerOpHandler Process recv ", data) + if msg, ok := data.(*thirteen.CSThirteenPlayerOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSPlayerOpHandler p.scene == nil") + return nil + } + if !common.IsThirteen(scene.GetGameId()) { + logger.Logger.Error("CSPlayerOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + sp := scene.GetScenePolicy() + if sp != nil { + sp.OnPlayerOp(scene, p, int(msg.GetOpCode()), msg.GetOpParam()) + } + return nil + } + return nil +} + +func init() { + common.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_CSThirteenPlayerOp), &CSPlayerOpHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_CSThirteenPlayerOp), &CSPlayerOpPacketFactory{}) +} diff --git a/gamesrv/thirteen/player.go b/gamesrv/thirteen/player.go new file mode 100644 index 0000000..302474e --- /dev/null +++ b/gamesrv/thirteen/player.go @@ -0,0 +1,58 @@ +package thirteen + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/gamerule/thirteen" + "mongo.games.com/game/gamesrv/base" +) + +type PlayerEx struct { + *base.Player //玩家信息 + cards [13]int //手牌信息 + allGroup map[int]*thirteen.Group //玩家所有牌型 + cardsO *thirteen.Group //确定的牌型信息 + isDP bool // 是否倒排 + gainCoin int64 //本局赢的金币 + taxCoin int64 //本局税收 + deterMine bool //玩家是否确定牌 + score [7]int64 //玩家每墩的得分 0:头 1中 2尾 3:特殊牌型分数 4打枪 5全垒打 6离场补偿分 + winThreePos map[int]int64 //只包含打枪玩家位置和总赢分 + winAllPlayers map[int]int64 //玩家位置和输赢分 + isStand bool //玩家站起 true打开 本局结束后自动离开房间 false未打开 + tableScore [6]int64 + odds int32 + totalScore int64 + defGroup *thirteen.Group +} + +func (this *PlayerEx) Clear() { + this.UnmarkFlag(base.PlayerState_WaitNext) + this.UnmarkFlag(base.PlayerState_GameBreak) + this.MarkFlag(base.PlayerState_Ready) + for i := 0; i < 13; i++ { + this.cards[i] = -1 + } + this.allGroup = make(map[int]*thirteen.Group) + this.cardsO = &thirteen.Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}, PokerType: -1} + this.isDP = false + this.gainCoin = 0 + this.taxCoin = 0 + this.deterMine = false + this.score = [7]int64{0, 0, 0, 0, 0, 0, 0} + this.tableScore = [6]int64{} + this.winThreePos = make(map[int]int64) + this.winAllPlayers = make(map[int]int64) + this.odds = 0 + this.totalScore = 0 + this.defGroup = nil + this.TestLog = this.TestLog[:0] +} + +func (this *PlayerEx) CanOp(sceneEx *SceneEx) bool { + if !this.IsGameing() { + logger.Logger.Trace("(this *PlayerEx) CanOp return false ", this.SnId) + return false + } + return true +} diff --git a/gamesrv/thirteen/scene.go b/gamesrv/thirteen/scene.go new file mode 100644 index 0000000..32d37e9 --- /dev/null +++ b/gamesrv/thirteen/scene.go @@ -0,0 +1,1433 @@ +package thirteen + +import ( + "fmt" + "math" + "math/rand" + "sort" + "strings" + "time" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/thirteen" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/thirteen" +) + +type PlayerData struct { + SnId int32 + cards [13]int //手牌信息 + cardsO *rule.Group //确定的牌型信息 + isDP bool // 是否倒排 + gainCoin int64 //本局赢的金币 + taxCoin int64 //本局税收 + clubPump int64 //俱乐部抽水 + deterMine bool //玩家是否确定牌 + score [7]int64 //玩家每墩的得分 0:头 1中 2尾 3:特殊牌型分数 4打枪 5全垒打 6离场补偿分 + tableScore [6]int64 // 客户端展示 + winThreePos map[int]int64 //只包含打枪玩家位置和总赢分 + winAllPlayers map[int]int64 //玩家位置和输赢分 + isStand bool //玩家站起 true打开 本局结束后自动离开房间 false未打开 + isBilled bool // 是否结算 + IsRob bool + Pos int + Coin int64 + StartCoin int64 + Head int32 //头像 + flag int + Platform string //平台 + Channel string //渠道信息 + PackageID string //推广包标识 对应客户端的packagetag + PromoterTree int32 + InviterId int32 //邀请人Id + WBLevel int32 //黑白名单 白:[1,10] 黑:[-1,-10] + CurIsWin int64 //当局输赢 负数:输 0:平局 正数:赢 + BeUnderAgentCode string //隶属经销商(推广人) + Name string //名字 + Sex int32 //性别 + City string //城市 + Longitude int32 //经纬度 + Latitude int32 //经纬度 + AgentCode string //代理商编号 + HeadOutLine int32 //头像框 + VIP int32 //VIP帐号 等级 + allGroup map[int]*rule.Group + TestLog []string + RoleId int32 +} + +type SceneEx struct { + *base.Scene //场景 + poker *rule.Pokers // 扑克牌对象 + logic *rule.Logic // 十三张算法 + players map[int32]*PlayerEx // 玩家信息 + seats []*PlayerEx // 本局游戏中的玩家状态数据 + isCanAllHitPos int // 全垒打玩家坐标 =4人 并且没有特殊牌型 + currOpPos int // 当前操作的玩家 + hitTime time.Duration // 打枪阶段按打枪人数加时间 + specialTime time.Duration // 亮牌阶段有特殊牌型增加时间 + specialTypeNum int // 玩家有特殊牌的个数 + entryHitState bool // 是否可以进入打枪阶段 + robotNum int // 参与游戏的机器人数量 + gamePlayerNum int // 参与游戏的人数量 + + r int // 体验场测试数据 + cardsSlice [][13]int // 从大到小排列四副牌 + nowMaxCardsIsIn bool // 现在最大牌是否还在 + PlayerBackup map[int32]*PlayerData // 本局离场玩家数据备份 + LeaveNum int // 离场扣分人数 + testPokers []int64 // 测试牌堆 + logid string +} + +func NewThirteenWaterSceneData(s *base.Scene) *SceneEx { + sceneEx := &SceneEx{ + Scene: s, + logic: new(rule.Logic), + players: make(map[int32]*PlayerEx), + seats: make([]*PlayerEx, s.GetPlayerNum()), + PlayerBackup: make(map[int32]*PlayerData), + } + if s.GetPlayerNum() > 4 { // 两幅牌 + sceneEx.poker = rule.NewPokers(2, sceneEx.HasLaiZi()) + } else { + sceneEx.poker = rule.NewPokers(1, sceneEx.HasLaiZi()) + } + if sceneEx.HasLaiZi() { + sceneEx.logic.LaiZi = []int{52, 53} // 目前只有大小王为癞子 + } + return sceneEx +} + +func (this *SceneEx) init() bool { + this.Clear() + return true +} + +func (this *SceneEx) Clear() { + this.poker.Init() + this.isCanAllHitPos = -1 + this.currOpPos = -1 + this.hitTime = rule.ThirteenWaterHitTimeout + this.specialTypeNum = 0 + this.entryHitState = false + this.robotNum = 0 + this.gamePlayerNum = 0 + this.r = 1 + this.cardsSlice = nil + this.nowMaxCardsIsIn = true + this.PlayerBackup = make(map[int32]*PlayerData) + this.LeaveNum = 0 + + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] != nil { + this.seats[i].Clear() + } + } +} + +func (this *SceneEx) delPlayer(p *base.Player) { + if p, exist := this.players[p.SnId]; exist { + this.seats[p.GetPos()] = nil + delete(this.players, p.SnId) + } +} + +func (this *SceneEx) BroadcastPlayerLeave(p *base.Player, reason int) { + scLeavePack := &thirteen.SCThirteenPlayerLeave{ + Pos: proto.Int(p.GetPos()), + } + proto.SetDefaults(scLeavePack) + + this.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerLeave), scLeavePack, p.GetSid()) +} + +func (this *SceneEx) OnPlayerLeave(p *base.Player, reason int) { + this.delPlayer(p) + this.BroadcastPlayerLeave(p, reason) +} + +func (this *SceneEx) SceneDestroy(force bool) { + //销毁房间 + this.Scene.Destroy(force) +} + +func (this *SceneEx) CanStart() bool { + //房间人数>=2自动开始,并且有真人或者是预创建房间 + if len(this.players) >= 2 && (this.GetRealPlayerNum() > 0 || this.IsPreCreateScene()) { + return true + } + return false +} + +func (this *SceneEx) ThirteenWaterCreateRoomInfoPacket(s *base.Scene, p *base.Player) interface{} { + pack := &thirteen.SCThirteenRoomInfo{ + RoomId: proto.Int(s.GetSceneId()), + Creator: proto.Int32(s.GetCreator()), + GameId: proto.Int(s.GetGameId()), + RoomMode: proto.Int(s.GetSceneMode()), + AgentId: proto.Int32(s.GetAgentor()), + SceneType: s.GetDBGameFree().SceneType, + State: proto.Int(s.GetSceneState().GetState()), + TimeOut: proto.Int(s.GetSceneState().GetTimeout(s)), + DisbandGen: proto.Int(this.GetDisbandGen()), + BaseScore: proto.Int32(this.GetBaseScore()), + LeaveDeduct: this.GetDBGameFree().GetLeaveDeduct(), + LeaveCombat: this.GetDBGameFree().GetLeaveCombat(), + Params: this.Params, + } + // 玩家信息 + for _, playerEx := range this.players { + pd := &thirteen.ThirteenPlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.Coin), + Pos: proto.Int(playerEx.Pos), + Flag: proto.Int(playerEx.GetFlag()), + Longitude: proto.Int32(playerEx.Longitude), + Latitude: proto.Int32(playerEx.Latitude), + City: proto.String(playerEx.City), + AgentCode: proto.String(playerEx.AgentCode), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + IsStand: proto.Bool(playerEx.isStand), + IsConfirm: proto.Bool(playerEx.deterMine), + RoleId: playerEx.PlayerData.GetRoleId(), + } + ppp := &thirteen.Poker{} + ppp.IndexType = -1 + pd.CardsO = ppp + if playerEx.cardsO != nil { + if playerEx.cardsO.PokerType > 0 { + //确定之后为特殊牌型 + ppp := &thirteen.Poker{} + ppp.Head = common.CopySliceIntToInt32(playerEx.cards[:3]) + ppp.Mid = common.CopySliceIntToInt32(playerEx.cards[3:8]) + ppp.End = common.CopySliceIntToInt32(playerEx.cards[8:]) + ppp.IndexType = proto.Int32(int32(playerEx.cardsO.PokerType * 1000000)) + pd.CardsO = ppp + pd.IsDP = playerEx.isDP + } else if playerEx.cardsO.PokerType == 0 { + //确定之后为普通牌型 + ppp := &thirteen.Poker{} + ppp.Head = common.CopySliceIntToInt32(playerEx.cardsO.Head[:]) + ppp.Mid = common.CopySliceIntToInt32(playerEx.cardsO.Mid[:]) + ppp.End = common.CopySliceIntToInt32(playerEx.cardsO.End[:]) + ppp.IndexType = proto.Int32(int32(this.FormatCards(playerEx.cardsO))) + pd.CardsO = ppp + pd.IsDP = playerEx.isDP + } + } + if playerEx.cards[0] != -1 { + pd.Cards = common.CopySliceIntToInt32(playerEx.cards[:]) + } + if playerEx.allGroup != nil { + pd.AllCardsO, _ = AllGroupToProto(playerEx.allGroup) + } + if p == nil || playerEx.SnId != p.SnId { + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateSendCards || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateOptCard { + pd.Cards = nil + pd.AllCardsO = nil + if pd.CardsO.IndexType > 0 { + ppp := &thirteen.Poker{} + ppp.IndexType = 0 + pd.CardsO = ppp + pd.IsDP = false + } + } + } + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateShowCards || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateHit || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateBilled { + pd.Score = playerEx.score[:] + pd.TableScore = playerEx.tableScore[:] + var hitScore []*thirteen.HitScore + for k, v := range playerEx.winThreePos { + pck := &thirteen.HitScore{ + Pos: proto.Int32(int32(k)), + Score: proto.Int64(v), + } + hitScore = append(hitScore, pck) + } + pd.Hit = hitScore + } + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateBilled { + pd.WinCoin = playerEx.gainCoin + } + pack.Players = append(pack.Players, pd) + } + // 备份的玩家信息 + for _, playerEx := range this.PlayerBackup { + if playerEx != nil || !playerEx.isBilled { + continue + } + pd := &thirteen.ThirteenPlayerData{ + SnId: proto.Int32(playerEx.SnId), + Name: proto.String(playerEx.Name), + Head: proto.Int32(playerEx.Head), + Sex: proto.Int32(playerEx.Sex), + Coin: proto.Int64(playerEx.Coin), + Pos: proto.Int(playerEx.Pos), + Flag: proto.Int(playerEx.flag), + Longitude: proto.Int32(playerEx.Longitude), + Latitude: proto.Int32(playerEx.Latitude), + City: proto.String(playerEx.City), + AgentCode: proto.String(playerEx.AgentCode), + HeadOutLine: proto.Int32(playerEx.HeadOutLine), + VIP: proto.Int32(playerEx.VIP), + IsStand: proto.Bool(playerEx.isStand), + IsConfirm: proto.Bool(playerEx.deterMine), + IsLeave: true, + RoleId: playerEx.RoleId, + } + ppp := &thirteen.Poker{} + ppp.IndexType = -1 + pd.CardsO = ppp + if playerEx.cardsO != nil { + if playerEx.cardsO.PokerType > 0 { + //确定之后为特殊牌型 + ppp := &thirteen.Poker{} + ppp.Head = common.CopySliceIntToInt32(playerEx.cards[:3]) + ppp.Mid = common.CopySliceIntToInt32(playerEx.cards[3:8]) + ppp.End = common.CopySliceIntToInt32(playerEx.cards[8:]) + ppp.IndexType = proto.Int32(int32(playerEx.cardsO.PokerType * 1000000)) + pd.CardsO = ppp + pd.IsDP = playerEx.isDP + } else if playerEx.cardsO.PokerType == 0 { + //确定之后为普通牌型 + ppp := &thirteen.Poker{} + ppp.Head = common.CopySliceIntToInt32(playerEx.cardsO.Head[:]) + ppp.Mid = common.CopySliceIntToInt32(playerEx.cardsO.Mid[:]) + ppp.End = common.CopySliceIntToInt32(playerEx.cardsO.End[:]) + ppp.IndexType = proto.Int32(int32(this.FormatCards(playerEx.cardsO))) + pd.CardsO = ppp + pd.IsDP = playerEx.isDP + } + } + if playerEx.cards[0] != -1 { + pd.Cards = common.CopySliceIntToInt32(playerEx.cards[:]) + } + if playerEx.allGroup != nil { + pd.AllCardsO, _ = AllGroupToProto(playerEx.allGroup) + } + if p == nil || playerEx.SnId != p.SnId { + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateSendCards || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateOptCard { + pd.Cards = nil + pd.AllCardsO = nil + if pd.CardsO.IndexType > 0 { + ppp := &thirteen.Poker{} + ppp.IndexType = 0 + pd.CardsO = ppp + pd.IsDP = false + } + } + } + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateShowCards || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateHit || + s.SceneState.GetState() == rule.ThirteenWaterSceneStateBilled { + pd.Score = playerEx.score[:] + pd.TableScore = playerEx.tableScore[:] + var hitScore []*thirteen.HitScore + for k, v := range playerEx.winThreePos { + pck := &thirteen.HitScore{ + Pos: proto.Int32(int32(k)), + Score: proto.Int64(v), + } + hitScore = append(hitScore, pck) + } + pd.Hit = hitScore + } + if s.SceneState.GetState() == rule.ThirteenWaterSceneStateBilled { + pd.WinCoin = playerEx.gainCoin + } + pack.Players = append(pack.Players, pd) + } + + proto.SetDefaults(pack) + if p != nil { + p.SyncFlag() + } + logger.Logger.Trace("SCThirteenWaterRoomInfo:", pack) + return pack +} + +func (this *SceneEx) GetBaseScore() int32 { //游戏底分 + if this.DbGameFree.FreeMode == 1 { + baseScore := this.GetParam(rule.ParamBaseScore) + if baseScore > 0 { + return baseScore + } + } + + if this.DbGameFree != nil { + return this.DbGameFree.GetBaseScore() + } + return 1 +} + +func (this *SceneEx) GetBaiPai() time.Duration { + s := this.GetParam(rule.ParamBaiPai) + if s > 0 { + return time.Duration(s) * time.Second + } + return rule.ThirteenWaterOptCardTimeout +} + +func (this *SceneEx) HasLaiZi() bool { + s := this.GetParam(rule.ParamLaiZi) + if s >= 0 { + return s == 1 + } + return false +} + +func (this *SceneEx) AutoCombine() bool { + s := this.GetParam(rule.ParamAuto) + if s >= 0 { + return s == 0 + } + return true +} + +// GetLeaveDeductCoin 离场扣分 +func (this *SceneEx) GetLeaveDeductCoin() int64 { + return int64(this.GetDBGameFree().GetLeaveDeduct()) * int64(this.GetBaseScore()) +} + +func (this *SceneEx) GetScore(player *PlayerEx) { + if player == nil || player.isDP { + return + } + // 计算特殊牌型分数 + ptRate := int64(0) + //清龙* 一条龙* 十二皇族 三同花顺* 三分天下 全大 全小 凑一色* 四套三条 五对三条 六对半* 三顺子* 三同花 + if player.cardsO.PokerType != 0 { + this.specialTypeNum++ + //特殊牌处理 + if player.cardsO.PokerType >= 1 && player.cardsO.PokerType <= 13 { + ptRate = rule.SpecialScore[player.cardsO.PokerType] + } + } + if this.specialTypeNum > 0 { + for _, p := range this.players { + if p != nil && p.IsGameing() && player.cardsO.PokerType != 0 { + if player.Pos == p.Pos { + continue + } + if p.cardsO.PokerType == 0 || p.isDP { + player.score[3] += ptRate + p.score[3] -= ptRate + player.winAllPlayers[p.Pos] += ptRate + p.winAllPlayers[player.Pos] -= ptRate + } else if p.cardsO.PokerType != 0 { + //多个特殊牌型处理 + if player.cardsO.PokerType < p.cardsO.PokerType { + player.score[3] += ptRate + p.score[3] -= ptRate + player.winAllPlayers[p.Pos] += ptRate + p.winAllPlayers[player.Pos] -= ptRate + } + } + } + } + } + // 计算普通牌型分数 + for _, p := range this.players { + if p != nil && p.IsGameing() && player.cardsO.PokerType == 0 { + if player.Pos == p.Pos || p.cardsO.PokerType != 0 { + continue + } + s := 0 + score := int64(0) //赢的分数 + //头墩 + rate := int64(1) + n := this.logic.CompareHead(player.cardsO.Head, p.cardsO.Head) + h := this.logic.GetType(player.cardsO.Head[:]) + if h == rule.PokersTypeThree { + rate += 2 + } + if n == 1 || p.isDP { + player.score[0] += rate + p.score[0] -= rate + score += rate + s++ + player.winAllPlayers[p.Pos] += rate + p.winAllPlayers[player.Pos] -= rate + player.tableScore[3] += rate - 1 + } + //中墩 + rate = int64(1) + n = this.logic.CompareFive(player.cardsO.Mid, p.cardsO.Mid) + m := this.logic.GetType(player.cardsO.Mid[:]) + if m == rule.PokersTypeFullHouse { + rate += 1 + } else if m == rule.PokersTypeFour { + rate += 6 + } else if m == rule.PokersTypeStraightFlush { + rate += 8 + } else if m == rule.PokersTypeFive { + rate += 10 + } + if n == 1 || p.isDP { + player.score[1] += rate + p.score[1] -= rate + score += rate + s++ + player.winAllPlayers[p.Pos] += rate + p.winAllPlayers[player.Pos] -= rate + player.tableScore[4] += rate - 1 + } + //尾墩 + rate = int64(1) + n = this.logic.CompareFive(player.cardsO.End, p.cardsO.End) + e := this.logic.GetType(player.cardsO.End[:]) + if e == rule.PokersTypeFour { + rate += 3 + } else if e == rule.PokersTypeStraightFlush { + rate += 4 + } else if e == rule.PokersTypeFive { + rate += 5 + } + if n == 1 || p.isDP { + player.score[2] += rate + p.score[2] -= rate + score += rate + s++ + player.winAllPlayers[p.Pos] += rate + p.winAllPlayers[player.Pos] -= rate + player.tableScore[5] += rate - 1 + } + if s == 3 { + player.winThreePos[p.Pos] = score + } + } + } +} + +// 发送玩家的所有牌和所有牌型 +func (this *SceneEx) SendToPlayerCards(s *base.Scene) { + for _, player := range this.players { + if player != nil && player.IsGameing() { + all, k := AllGroupToProto(player.allGroup) + pack := &thirteen.SCThirteenPlayerCards{ + Cards: common.CopySliceIntToInt32(player.cards[:]), + Pos: proto.Int32(int32(player.Pos)), + AllCardsO: all, + } + // 记录默认牌型 + if k > 0 { + player.defGroup = player.allGroup[k] + } + if len(pack.AllCardsO) == 0 { + logger.Logger.Warnf("no poker suggest: %v %v", player.cards, rule.PokersShow(player.cards[:])) + // 没有推荐牌,随便给个数据 + pack.AllCardsO = []*thirteen.Poker{ + { + IndexType: 90909, + Head: common.CopySliceIntToInt32(player.cards[:3]), + Mid: common.CopySliceIntToInt32(player.cards[3:8]), + End: common.CopySliceIntToInt32(player.cards[8:]), + }, + } + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCThirteenWaterPlayerCards:", pack) + player.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerCards), pack) + Send := &thirteen.SCThirteenPlayerCards{ + Pos: proto.Int32(int32(player.Pos)), + } + proto.SetDefaults(Send) + logger.Logger.Trace("SCThirteenWaterPlayerCards:", Send) + s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerCards), Send, player.GetSid()) + } + } +} + +func AllGroupToProto(allGroup map[int]*rule.Group) ([]*thirteen.Poker, int) { + var pack []*thirteen.Poker + for k, v := range allGroup { + p := &thirteen.Poker{ + Head: common.CopySliceIntToInt32(v.Head[:]), + Mid: common.CopySliceIntToInt32(v.Mid[:]), + End: common.CopySliceIntToInt32(v.End[:]), + IndexType: proto.Int32(int32(k)), + } + pack = append(pack, p) + } + + sort.Slice(pack, func(i, j int) bool { + ii := pack[i] + jj := pack[j] + // 特殊牌在前面 + if ii.IndexType >= 1000000 && jj.IndexType >= 1000000 { + return ii.IndexType < jj.IndexType + } + if ii.IndexType >= 1000000 { + return true + } + if jj.IndexType >= 1000000 { + return false + } + // 普通牌型,从尾墩到头墩大的在前面 + if ii.IndexType%100 < jj.IndexType%100 { + return true + } else if ii.IndexType%100 > jj.IndexType%100 { + return false + } + + if ii.IndexType%10000/100 < jj.IndexType%10000/100 { + return true + } else if ii.IndexType%10000/100 > jj.IndexType%10000/100 { + return false + } + + if ii.IndexType/10000 < jj.IndexType/10000 { + return true + } else if ii.IndexType/10000 > jj.IndexType/10000 { + return false + } + return false + }) + + //for _, v := range pack { + // logger.Logger.Tracef("AllGroupToProto: %v %v %v %v", v.IndexType, rule.PokersShow(common.CopySliceInt32ToInt(v.Head)), rule.PokersShow(common.CopySliceInt32ToInt(v.Mid)), rule.PokersShow(common.CopySliceInt32ToInt(v.End))) + //} + if len(pack) > 0 { + return pack, int(pack[0].IndexType) + } + + return pack, 0 +} + +// CheckIsCanHit 检查是否可以打枪 +func (this *SceneEx) CheckIsCanHit() bool { + pt := 0 //特殊牌型数量 + isHit := 0 //几个人可以打枪 + for _, v := range this.players { + if v != nil && v.IsGameing() { + if v.cardsO.PokerType != 0 { + pt++ + } + //全垒打: 打枪所有人并且至少打3人 + if len(v.winThreePos) >= 3 && len(v.winThreePos) == this.gamePlayerNum-1 { + this.isCanAllHitPos = v.Pos + } + if len(v.winThreePos) > 0 { + isHit++ + } + } + } + // 人数至少2人 + if /*pt == 0 &&*/ this.gamePlayerNum >= 2 && isHit > 0 { + return true + } + this.isCanAllHitPos = -1 + return false +} + +func (this *SceneEx) SelectCards(p *PlayerEx, indexType int) *rule.Group { + switch indexType { + case -1: // 使用特殊牌型 + for k, v := range p.allGroup { + if k >= 1000000 { + p.cardsO = v + break + } + } + case -2: // 使用牌值最大的牌型 + this.GetMaxCardsO(p) + + // 真人默认选牌规则 + if !p.IsRob && p.defGroup != nil { + p.cardsO = p.defGroup + } + } + + if indexType > 10000 { // 从推荐牌中选一个 + c := p.allGroup[indexType] + if c != nil { + p.cardsO = c + } + } + + // 还没有确认就随机推荐一个 + if p.cardsO == nil || p.cardsO.PokerType == -1 { + for k, v := range p.allGroup { + p.cardsO = v + if k >= 1000000 { + break + } + } + logger.Logger.Tracef("随机推荐:SnId(%v) %v", p.SnId, p.cardsO) + } + return p.cardsO +} + +// SendSelectCards 玩家选择特定的牌显示 +func (this *SceneEx) SendSelectCards(player *PlayerEx, indexType int, opcode int64) { + ShowCardsO := this.SelectCards(player, indexType) + + pack := &thirteen.SCThirteenPlayerOp{ + OpRetCode: thirteen.OpResultCode_OPRC_Sucess, + OpCode: int32(opcode), + Pos: proto.Int32(int32(player.Pos)), + Cards: &thirteen.Poker{}, + } + + if indexType == -1 || indexType >= 1000000 { // 特殊牌 + pack.Cards.Head = common.CopySliceIntToInt32(player.cards[:3]) + pack.Cards.Mid = common.CopySliceIntToInt32(player.cards[3:8]) + pack.Cards.End = common.CopySliceIntToInt32(player.cards[8:]) + } else { + // 自动组牌或玩家自选 + pack.Cards.Head = common.CopySliceIntToInt32(ShowCardsO.Head[:]) + pack.Cards.Mid = common.CopySliceIntToInt32(ShowCardsO.Mid[:]) + pack.Cards.End = common.CopySliceIntToInt32(ShowCardsO.End[:]) + pack.Cards.IsDP = this.logic.IsDP(ShowCardsO.Head, ShowCardsO.Mid, ShowCardsO.End) + } + pack.Cards.IndexType = proto.Int32(int32(this.FormatCards(ShowCardsO))) + pack.Cards.Pos = int32(player.GetPos()) + proto.SetDefaults(pack) + logger.Logger.Trace("SCThirteenPlayerOp1:", pack) + player.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack) + + pack.Cards.Head = nil + pack.Cards.Mid = nil + pack.Cards.End = nil + pack.Cards.IndexType = 0 + pack.Cards.IsDP = false + logger.Logger.Trace("SCThirteenPlayerOp2:", pack) + this.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack, player.GetSid()) + for _, v := range this.seats { + if v == nil { + continue + } + if v.SnId != player.SnId && v.IsRobot() { + logger.Logger.Trace("SCThirteenPlayerOpRobot:", pack) + v.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack) + } + } +} + +func (this *SceneEx) FormatCards(cardsO *rule.Group) int { + num := 0 + if cardsO != nil && cardsO.PokerType != 0 { + num = cardsO.PokerType * 1000000 + } else if cardsO != nil && cardsO.PokerType == 0 && cardsO.Head[0] != -1 { + num += this.logic.GetType(cardsO.Head[:]) * 10000 + num += this.logic.GetType(cardsO.Mid[:]) * 100 + num += this.logic.GetType(cardsO.End[:]) + } + return num +} + +// GetMinCards 默认获取玩家最小牌值的类型 +func (this *SceneEx) GetMinCards(player *PlayerEx) { + min := 1000000 + for v, m := range player.allGroup { + sum := 0 + if v >= 1000000 { + continue + } + a := v / 10000 + b := (v - a*10000) / 100 + c := v - a*10000 - b*100 + s := []int{a, b, c} + for _, k := range s { + if k >= 1 && k <= 20 { + sum += 20 - k + } + } + if min >= sum { + min = sum + player.cardsO = m + } + } +} + +func (this *SceneEx) GetRandsType() (playerCards [13]int) { + //r := rand.Intn(15) + 1 + //全垒打 + //playerCards = [13]int{9, 9 + 13, 9 + 13*2, 2, 3, 4, 5, 6, 3 + 13, 4 + 13, 5 + 13, 6 + 13, 7 + 13} + //return + c := [][13]int{ + //清龙 + {39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}, + //一条龙 + {13, 14, 28, 29, 30, 44, 45, 7, 34, 9, 23, 11, 51}, + //十二皇族 + {9, 10, 23, 36, 49, 11, 24, 37, 50, 12, 25, 38, 51}, + //三同花顺 + {1, 2, 3, 4, 5, 15, 16, 17, 18, 19, 27, 28, 29}, + //三分天下 + {10, 23, 36, 49, 11, 24, 37, 50, 12, 25, 38, 51, 13}, + //全大 + {7, 21, 35, 23, 11, 38, 51, 33, 48, 49, 50, 12, 25}, + //全小 + {14, 27, 40, 15, 28, 41, 4, 17, 5, 18, 6, 19, 32}, + //凑一色 + {13, 14, 40, 16, 42, 18, 44, 19, 46, 47, 23, 49, 25}, + //四套三条 + {5, 18, 44, 6, 19, 32, 7, 33, 46, 10, 23, 36, 50}, + //五对三条 + {17, 30, 5, 31, 6, 32, 8, 47, 23, 36, 12, 25, 38}, + //六对半 + {17, 30, 5, 31, 6, 32, 8, 47, 23, 36, 38, 51, 39}, + //三顺子 + {1, 28, 16, 17, 18, 30, 31, 45, 20, 8, 36, 50, 12}, + //三同花 + {2, 3, 6, 4 + 13, 8 + 13, 9 + 13, 13, 25, 31, 34, 27, 28, 29}, + } + if this.r < 12 { + this.r++ + } else { + this.r = 1 + } + playerCards = c[this.r] + return +} + +// SortCards 把所有牌排序 +//func (this *SceneEx) SortCards() { +// //确定所有玩家拿可以拿到的最大牌型 +// for _, player := range this.players { +// if player != nil && player.IsGameing() { +// this.GetMaxCardsO(player) +// } +// } +// //计算总分数 +// this.CountScore() +// scoreMap := make(map[int]int64) +// for _, player := range this.seats { +// if player != nil && player.IsGameing() { +// score := int64(0) +// //计算玩家输赢的分数 +// for _, v := range player.score { +// score += v +// } +// scoreMap[player.Pos] = score +// } +// } +// for i := 0; i < 6; i++ { +// max := int64(-1000) +// pos := -1 +// for k, v := range scoreMap { +// if max <= v { +// max = v +// pos = k +// } +// } +// if pos != -1 { +// delete(scoreMap, pos) +// this.cardsSlice = append(this.cardsSlice, this.seats[pos].cards) +// this.seats[pos].cards = [13]int{-1} +// if len(scoreMap) == 0 { +// break +// } +// } +// } +//} + +// GetMaxCardsO 一副牌中拿最大牌值的方案 +func (this *SceneEx) GetMaxCardsO(p *PlayerEx) { + if p != nil { + p.cardsO = rule.GetMaxCard(p.allGroup) + } +} + +// cardsSort 按从大到小的排列发牌 +func (this *SceneEx) cardsSort(cardArr [][13]int, cardGroup []map[int]*rule.Group) { + type P struct { + i int + score [6]int64 + cardsO *rule.Group + cards [13]int + group map[int]*rule.Group + winThreePos map[int]int64 + winAllPlayers map[int]int64 + } + + var ps []*P + for i := 0; i < len(cardArr); i++ { + p := &P{ + i: i, + score: [6]int64{0, 0, 0, 0, 0, 0}, + cardsO: rule.GetMaxCard(cardGroup[i]), + cards: cardArr[i], + group: cardGroup[i], + winThreePos: map[int]int64{}, + winAllPlayers: map[int]int64{}, + } + ps = append(ps, p) + } + + var specialTypeNum int + for i := 0; i < len(cardArr); i++ { + player := ps[i] + + // 计算特殊牌型分数 + ptRate := int64(0) + if player.cardsO.PokerType != 0 { + specialTypeNum++ + //特殊牌处理 + if player.cardsO.PokerType >= 1 && player.cardsO.PokerType <= 13 { + ptRate = rule.SpecialScore[player.cardsO.PokerType] + } + } + + if specialTypeNum > 0 { + for _, p := range ps { + if player.cardsO.PokerType != 0 { + if player.i == p.i { + continue + } + if p.cardsO.PokerType == 0 { + player.score[3] += ptRate + p.score[3] -= ptRate + player.winAllPlayers[p.i] += ptRate + p.winAllPlayers[player.i] -= ptRate + } else if p.cardsO.PokerType != 0 { + //多个特殊牌型处理 + if player.cardsO.PokerType < p.cardsO.PokerType { + player.score[3] += ptRate + p.score[3] -= ptRate + player.winAllPlayers[p.i] += ptRate + p.winAllPlayers[player.i] -= ptRate + } + } + } + } + } + + // 计算普通牌型分数 + for _, p := range ps { + if player.cardsO.PokerType == 0 { + if player.i == p.i || p.cardsO.PokerType != 0 { + continue + } + s := 0 + score := int64(0) //赢的分数 + //头墩 + rate := int64(1) + n := this.logic.CompareHead(player.cardsO.Head, p.cardsO.Head) + h := this.logic.GetType(player.cardsO.Head[:]) + if h == rule.PokersTypeThree { + rate += 2 + } + if n == 1 { + player.score[0] += rate + p.score[0] -= rate + score += rate + player.winAllPlayers[p.i] += rate + p.winAllPlayers[player.i] -= rate + s++ + } + //中墩 + rate = int64(1) + n = this.logic.CompareFive(player.cardsO.Mid, p.cardsO.Mid) + m := this.logic.GetType(player.cardsO.Mid[:]) + if m == rule.PokersTypeFullHouse { + rate += 1 + } else if m == rule.PokersTypeFour { + rate += 6 + } else if m == rule.PokersTypeStraightFlush { + rate += 8 + } else if m == rule.PokersTypeFive { + rate += 10 + } + if n == 1 { + player.score[1] += rate + p.score[1] -= rate + score += rate + player.winAllPlayers[p.i] += rate + p.winAllPlayers[player.i] -= rate + s++ + } + //尾墩 + rate = int64(1) + n = this.logic.CompareFive(player.cardsO.End, p.cardsO.End) + e := this.logic.GetType(player.cardsO.End[:]) + if e == rule.PokersTypeFour { + rate += 3 + } else if e == rule.PokersTypeStraightFlush { + rate += 4 + } else if e == rule.PokersTypeFive { + rate += 5 + } + if n == 1 { + player.score[2] += rate + p.score[2] -= rate + score += rate + player.winAllPlayers[p.i] += rate + p.winAllPlayers[player.i] -= rate + s++ + } + if s == 3 { + player.winThreePos[p.i] = score + } + } + } + } + + // 打枪分 + isCanAllHitPos := -1 + pt := 0 //特殊牌型数量 + isHit := 0 //几个人可以打枪 + for _, v := range ps { + if v.cardsO.PokerType != 0 { + pt++ + } + //全垒打: 打枪所有人并且至少打3人 + if len(v.winThreePos) >= 3 && len(v.winThreePos) == this.gamePlayerNum-1 { + isCanAllHitPos = v.i + } + if len(v.winThreePos) > 0 { + isHit++ + } + } + // 可以打枪:没有特殊牌型,人数至少2人 + if pt == 0 && this.gamePlayerNum >= 2 && isHit > 0 { + for _, playerEx := range ps { + if len(playerEx.winThreePos) > 0 { + for k, v := range playerEx.winThreePos { + // 打枪 + ps[k].score[4] -= v + ps[k].winAllPlayers[playerEx.i] -= v + playerEx.score[4] += v + playerEx.winAllPlayers[k] += v + // 全垒打分数 + if playerEx.i == isCanAllHitPos { + ps[k].score[5] -= v + ps[k].winAllPlayers[playerEx.i] -= v + playerEx.score[5] += v + playerEx.winAllPlayers[k] += v + } + } + } + } + } + + sort.Slice(ps, func(i, j int) bool { + var a, b int64 + for _, v := range ps[i].score { + a += v + } + for _, v := range ps[j].score { + b += v + } + return a > b + }) + for i := 0; i < len(cardArr); i++ { + cardArr[i] = ps[i].cards + cardGroup[i] = ps[i].group + } +} + +// CountScore 计算总分数 +func (this *SceneEx) CountScore() { + //计算头、中、尾道、特殊牌分数 + for _, player := range this.seats { + if player != nil && player.IsGameing() { + this.GetScore(player) + // 离场补偿分 + if this.LeaveNum > 0 { + player.score[6] = int64(this.LeaveNum) * int64(this.GetDBGameFree().GetLeaveCombat()) + } + } + } + for _, player := range this.seats { + if player != nil && player.IsGameing() { + for i := 0; i < 3; i++ { + player.tableScore[i] = player.score[i] - player.tableScore[i+3] + } + } + } + + this.entryHitState = this.CheckIsCanHit() + + if !this.entryHitState { + //如果不能进入打枪阶段 不计算打枪和全垒打 + return + } + //计算打枪分数 + for _, playerEx := range this.players { + if playerEx != nil && playerEx.IsGameing() { + if len(playerEx.winThreePos) > 0 { + for k, v := range playerEx.winThreePos { + // 并且都不是特殊牌型 + if playerEx.cardsO.PokerType != 0 || this.seats[k].cardsO.PokerType != 0 { + continue + } + // 打枪 + this.seats[k].score[4] -= v + this.seats[k].winAllPlayers[playerEx.Pos] -= v + playerEx.score[4] += v + playerEx.winAllPlayers[k] += v + // 全垒打分数 + if playerEx.Pos == this.isCanAllHitPos { + this.seats[k].score[5] -= v + this.seats[k].winAllPlayers[playerEx.Pos] -= v + playerEx.score[5] += v + playerEx.winAllPlayers[k] += v + } + } + } + } + } +} + +func (this *SceneEx) GetInGameNum() { + for _, v := range this.players { + if v != nil && v.IsGameing() { + this.gamePlayerNum++ + if v.IsRob { + this.robotNum++ + } + } + } +} + +// 获取最小牌 +func (this *SceneEx) GetAllMinCards() (a [13]int) { + for i := len(this.cardsSlice) - 1; i >= 0; i-- { + a = this.cardsSlice[i] + this.cardsSlice[i] = [13]int{-1} + this.cardsSlice = append(this.cardsSlice[:i], this.cardsSlice[i+1:]...) + break + } + return [13]int{-1} +} + +// 获取最大牌 +func (this *SceneEx) GetAllMaxCards() (a [13]int) { + if this.nowMaxCardsIsIn { + this.nowMaxCardsIsIn = false + } + for i := 0; i < len(this.cardsSlice); i++ { + a = this.cardsSlice[i] + this.cardsSlice = append(this.cardsSlice[:i], this.cardsSlice[i+1:]...) + break + } + return [13]int{-1} +} + +// 随机拿牌 +func (this *SceneEx) GetRandCards(b bool) (a [13]int) { + n := len(this.cardsSlice) + if n == 0 { + return [13]int{-1} + } + r := rand.Intn(n) + if !b { + if this.nowMaxCardsIsIn { + //最大牌还在 + //不带最大牌随机 + if n == 1 { + //就剩一副牌 + return [13]int{-1} + } + if r == 0 { + //随到最大牌 + r++ + } + } + } else { + //带最大牌随机 + if this.nowMaxCardsIsIn { + if r == 0 { + this.nowMaxCardsIsIn = false + } + } + } + + a = this.cardsSlice[r] + this.cardsSlice = append(this.cardsSlice[:r], this.cardsSlice[r+1:]...) + return +} + +func (this *SceneEx) ShowCards() { + var allCards []*thirteen.Poker + for _, p := range this.players { + if p != nil && p.IsGameing() && p.cardsO != nil { + var hitScore []*thirteen.HitScore + for k, v := range p.winThreePos { + pck := &thirteen.HitScore{ + Pos: proto.Int32(int32(k)), + Score: proto.Int64(v), + } + hitScore = append(hitScore, pck) + } + pk := &thirteen.Poker{ + IndexType: proto.Int32(int32(this.FormatCards(p.cardsO))), + Head: common.IntSliceToInt32(p.cardsO.Head[:]), + Mid: common.IntSliceToInt32(p.cardsO.Mid[:]), + End: common.IntSliceToInt32(p.cardsO.End[:]), + IsDP: p.isDP, + Pos: int32(p.GetPos()), + Score: p.score[:], + Hit: hitScore, + TableScore: p.tableScore[:], + Cards: common.IntSliceToInt32(p.cards[:]), + } + allCards = append(allCards, pk) + } + } + + opCode := 0 + if this.GetSceneState().GetState() == rule.ThirteenWaterSceneStateHit { + opCode = 1 // 打枪 + } + + pc := &thirteen.SCThirteenShowCards{ + OpCode: proto.Int32(int32(opCode)), + AllCards: allCards, + } + proto.SetDefaults(pc) + logger.Logger.Trace("SCThirteenWaterShowCards is pc: ", pc) + this.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenShowCards), pc, 0) +} + +func (this *SceneEx) CountBilled() { + var totalWinScore, totalLoseScore int64 // 总赢分 + for _, v := range this.players { + if v != nil && v.IsGameing() { + // 玩家总分 + v.totalScore = 0 + for _, vv := range v.score[:6] { + v.totalScore += vv + } + v.totalScore *= int64(this.GetBaseScore()) + if v.totalScore > 0 { + if v.totalScore > v.Coin { + v.totalScore = v.Coin + } + } else if v.totalScore < 0 { + if v.totalScore < -v.Coin { + v.totalScore = -v.Coin + } + } + // 总输赢分 + if v.totalScore > 0 { + totalWinScore += v.totalScore + } else if v.totalScore < 0 { + totalLoseScore += -v.totalScore + } + logger.Logger.Tracef("玩家[%d]总赢分: %d", v.GetPos(), v.totalScore) + } + } + + if totalWinScore > totalLoseScore { + // 赢分大,输分玩家分数不需要修改 + for _, v := range this.players { + if v != nil && v.IsGameing() { + if v.totalScore > 0 { + v.gainCoin = int64(float64(v.totalScore) * float64(totalLoseScore) / float64(totalWinScore)) + } else if v.totalScore < 0 { + v.gainCoin = v.totalScore + } + } + } + } else if totalWinScore < totalLoseScore { + // 输分大,赢分玩家分数不需要修改 + for _, v := range this.players { + if v != nil && v.IsGameing() { + if v.totalScore > 0 { + v.gainCoin = v.totalScore + } else if v.totalScore < 0 { + v.gainCoin = int64(float64(v.totalScore) * float64(totalLoseScore) / float64(totalWinScore)) + } + } + } + } else { + for _, v := range this.players { + if v != nil && v.IsGameing() { + v.gainCoin = v.totalScore + } + } + } + + for _, playerEx := range this.players { + if playerEx == nil || !playerEx.IsGameing() { + continue + } + if playerEx.gainCoin > 0 { + gainCoin := playerEx.gainCoin + playerEx.gainCoin = playerEx.gainCoin * int64(10000-this.DbGameFree.GetTaxRate()) / 10000 + playerEx.taxCoin = gainCoin - playerEx.gainCoin + } + logger.Logger.Tracef("玩家分数 %v, coin:%v tax:%v win:%v", playerEx.SnId, playerEx.gainCoin, playerEx.taxCoin, playerEx.winAllPlayers) + } +} + +func (this *SceneEx) BackupPlayer(p *PlayerEx, isBilled bool) { + testLog := make([]string, len(p.TestLog)) + copy(testLog, p.TestLog) + this.PlayerBackup[p.SnId] = &PlayerData{ + SnId: p.SnId, + cards: p.cards, + cardsO: p.cardsO, + isDP: p.isDP, + gainCoin: p.gainCoin, + taxCoin: p.taxCoin, + deterMine: p.deterMine, + score: p.score, + tableScore: p.tableScore, + winThreePos: p.winThreePos, + winAllPlayers: p.winAllPlayers, + isStand: p.isStand, + isBilled: isBilled, + IsRob: p.IsRob, + Pos: p.Pos, + Coin: p.Coin, + StartCoin: p.StartCoin, + Head: p.Head, + flag: p.GetFlag(), + Platform: p.Platform, + Channel: p.Channel, + PackageID: p.PackageID, + PromoterTree: p.PromoterTree, + InviterId: p.InviterId, + WBLevel: p.WBLevel, + CurIsWin: p.CurIsWin, + BeUnderAgentCode: p.BeUnderAgentCode, + Name: p.Name, + Sex: p.Sex, + City: p.City, + Longitude: p.Longitude, + Latitude: p.Latitude, + AgentCode: p.AgentCode, + HeadOutLine: p.HeadOutLine, + VIP: p.VIP, + allGroup: p.allGroup, + TestLog: testLog, + RoleId: p.PlayerData.GetRoleId(), + } +} + +func (this *SceneEx) SendHandCardOdds() { + var realPlayersGood, realPlayersBad, realPlayers, robotPlayers []*PlayerEx + var G, B int32 + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat.IsRob { + robotPlayers = append(robotPlayers, seat) + } else { + seat.odds = this.GetPlayerOdds(seat.Player, this.GameId, this.robotNum > 0) + if seat.odds > 0 { + realPlayersGood = append(realPlayersGood, seat) + G += seat.odds + } else if seat.odds < 0 { + realPlayersBad = append(realPlayersBad, seat) + B -= seat.odds + } else { + realPlayers = append(realPlayers, seat) + } + } + } + } + + this.poker.Init() + + cardsArr := make([][13]int, this.poker.N*4) + cardsGroup := make([]map[int]*rule.Group, this.poker.N*4) + for k := range cardsArr { + cardsArr[k] = this.poker.Get13Crads() + cardsGroup[k] = this.logic.Suggest(cardsArr[k]) + } + + f := func(players *[]*PlayerEx) { + if players == nil || len(*players) == 0 { + return + } + var p *PlayerEx // 发牌给这个玩家 + var a int + for _, v := range *players { + a += int(math.Abs(float64(v.odds))) + } + n := this.RandInt(a) + total := 0 + for k, v := range *players { + total += int(math.Abs(float64(v.odds))) + if n < total { + p = (*players)[k] + *players = append((*players)[:k], (*players)[k+1:]...) + break + } + } + + var cards [13]int + var group map[int]*rule.Group + if p.odds > 0 { + // 拿好牌 + cards = cardsArr[0] + cardsArr = cardsArr[1:] + group = cardsGroup[0] + cardsGroup = cardsGroup[1:] + } else { + // 拿坏牌 + cards = cardsArr[len(cardsArr)-1] + cardsArr = cardsArr[:len(cardsArr)-1] + group = cardsGroup[len(cardsGroup)-1] + cardsGroup = cardsGroup[:len(cardsGroup)-1] + } + p.cards = cards + p.allGroup = group + } + + // 需要换牌 + isGood := len(realPlayersGood) > 0 && int32(this.RandInt(1000)) < G + isBad := len(realPlayersBad) > 0 && int32(this.RandInt(1000)) < B + logger.Logger.Tracef("Thirteen SendHandCardOdds Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B) + if isGood || isBad { + // 按从大到小排序 + this.cardsSort(cardsArr, cardsGroup) + + // 发好牌 + if isGood { + l := len(realPlayersGood) + for i := 0; i < l; i++ { + f(&realPlayersGood) + } + } + // 发坏牌 + if isBad { + l := len(realPlayersBad) + for i := 0; i < l; i++ { + f(&realPlayersBad) + } + } + } + // 随机拿牌 + for _, v := range this.players { // map随机 + if v == nil || !v.IsGameing() || v.cards[0] != -1 { + continue + } + v.cards = cardsArr[0] + v.allGroup = cardsGroup[0] + cardsArr = cardsArr[1:] + cardsGroup = cardsGroup[1:] + } + + for _, player := range this.players { + if player.IsGameing() && !player.IsRob { + logger.Logger.Trace("snid:", player.SnId) + player.TestLog = append(player.TestLog, fmt.Sprintf("随机换牌 Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B)) + for _, v := range player.TestLog { + logger.Logger.Trace(v) + } + pack := &thirteen.SCThirteenTest{} + player.TestLog = append(player.TestLog, fmt.Sprintf("随机换牌 Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B)) + pack.Data = strings.Join(player.TestLog, "\n") + proto.SetDefaults(pack) + player.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenTest), pack) + logger.Logger.Trace("SnId: ", player.SnId, ";SCThirteenTest: ", pack) + } + } +} diff --git a/gamesrv/thirteen/scenepolicy.go b/gamesrv/thirteen/scenepolicy.go new file mode 100644 index 0000000..a89a3e9 --- /dev/null +++ b/gamesrv/thirteen/scenepolicy.go @@ -0,0 +1,1516 @@ +package thirteen + +import ( + "strconv" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/thirteen" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/thirteen" +) + +var PolicyThirteenSingleton = &PolicyThirteen{} + +type PolicyThirteen struct { + base.BaseScenePolicy + states [rule.ThirteenWaterSceneStateMax]base.SceneState +} + +func (this *PolicyThirteen) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewThirteenWaterSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.ExtraData = sceneEx + } + } + return sceneEx +} + +func (this *PolicyThirteen) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &PlayerEx{Player: p} + if playerEx != nil { + p.ExtraData = playerEx + } + return playerEx +} + +func (this *PolicyThirteen) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *PolicyThirteen) OnStart, sceneId=", s.GetSceneId()) + + sceneEx := NewThirteenWaterSceneData(s) + if sceneEx != nil { + if sceneEx.init() { + s.ExtraData = sceneEx + s.ChangeSceneState(rule.ThirteenWaterSceneStateWait) + } + } +} + +func (this *PolicyThirteen) OnStop(s *base.Scene) { + logger.Logger.Trace("(this *PolicyThirteen) OnStop , sceneId=", s.GetSceneId()) +} + +func (this *PolicyThirteen) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.SceneState != nil { + s.SceneState.OnTick(s) + } +} + +func (this *PolicyThirteen) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + pos := -1 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + if sceneEx.seats[i] == nil { + pos = i + break + } + } + if pos != -1 { + playerEx := &PlayerEx{Player: p} + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + p.Pos = pos + p.ExtraData = playerEx + playerEx.Clear() + if sceneEx.Gaming { + p.MarkFlag(base.PlayerState_WaitNext) + p.UnmarkFlag(base.PlayerState_Ready) + } + //send msg + if len(s.Players) > 1 { + //向其他人广播玩家进入信息 + pack := &thirteen.SCThirteenPlayerEnter{ + Data: &thirteen.ThirteenPlayerData{ + SnId: proto.Int32(p.SnId), + Name: proto.String(p.Name), + Head: proto.Int32(p.Head), + Sex: proto.Int32(p.Sex), + Coin: proto.Int64(p.Coin), + Pos: proto.Int(pos), + Flag: proto.Int(p.GetFlag()), + Longitude: proto.Int32(p.Longitude), + Latitude: proto.Int32(p.Latitude), + City: proto.String(p.City), + //Params: proto.String(p.Params), + AgentCode: proto.String(p.AgentCode), + HeadOutLine: proto.Int32(p.HeadOutLine), + VIP: proto.Int32(p.VIP), + RoleId: p.PlayerData.GetRoleId(), + }, + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCThirteenWaterPlayerEnter:", pack) + s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), pack, p.GetSid()) + } + //给自己发送房间信息 + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } + } +} + +func (this *PolicyThirteen) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.SnId) + if s == nil || p == nil { + return + } + if !this.CanChangeCoinScene(s, p) { + return + } + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return + } + if !playerEx.IsGameing() { + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, nil) + return + } + + isBilled := false + switch sceneEx.GetSceneState().GetState() { + case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateOptCard: + // 发牌,选牌,离场扣分 + n := sceneEx.GetLeaveDeductCoin() + if playerEx.Coin < n { // 不够扣,不能离开 + return + } + playerEx.AddCoin(-n, common.GainWay_LeaveDeduct, base.SyncFlag_ToClient, "system", s.GetSceneName()) + sceneEx.LeaveNum++ + + case rule.ThirteenWaterSceneStateShowCards, rule.ThirteenWaterSceneStateHit: + // 亮牌,打枪,提前结算;(重连和结算使用备份数据) + isBilled = true + if playerEx.gainCoin > 0 { + //税前赢的钱 税收 下注额 + playerEx.AddServiceFee(playerEx.taxCoin) + playerEx.AddCoin(int64(playerEx.gainCoin), common.GainWay_CoinSceneWin, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = 1 + } else if playerEx.gainCoin < 0 { + playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneLost, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = -1 + } + // 离场补偿分 + if playerEx.score[6] > 0 { + playerEx.AddCoin(playerEx.score[6]*int64(sceneEx.GetBaseScore()), common.GainWay_LeaveCombat, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } + + if playerEx.IsGameing() && !playerEx.IsRob { + totalin, totalout := int64(0), int64(0) + if playerEx.CurIsWin > 0 { + totalout = playerEx.gainCoin + playerEx.taxCoin + } else { + totalin -= playerEx.gainCoin + } + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + sceneEx.SaveGamePlayerListLog(playerEx.SnId, base.GetSaveGamePlayerListLogParam(playerEx.Platform, playerEx.Channel, playerEx.BeUnderAgentCode, + playerEx.PackageID, sceneEx.logid, playerEx.InviterId, totalin, totalout, playerEx.taxCoin, + 0, 0, playerEx.gainCoin, validBet, validFlow, sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), false)) + + sceneEx.Statistics(&base.StaticParam{ + SnId: playerEx.SnId, + Gain: playerEx.gainCoin, + GainTax: playerEx.taxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.robotNum > 0, + }) + } + } + + // 游戏已开始但未结算,玩家离开,备份玩家数据 + if sceneEx.Gaming && playerEx.IsGameing() { + sceneEx.BackupPlayer(playerEx, isBilled) + } + + // 清理玩家数据 + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, nil) +} + +func (this *PolicyThirteen) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) +} + +func (this *PolicyThirteen) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if _, ok := p.ExtraData.(*PlayerEx); ok { + //发送房间信息给自己 + if p.IsGameing() { + p.MarkFlag(base.PlayerState_Ready) + } + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +func (this *PolicyThirteen) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if _, ok := p.ExtraData.(*PlayerEx); ok { + //发送房间信息给自己 + if p.IsGameing() { + p.MarkFlag(base.PlayerState_Ready) + } + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func (this *PolicyThirteen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerOp, sceneId=", s.GetSceneId(), " player=", p.SnId, " opcode=", opcode, " params=", params) + if s.SceneState != nil { + p.LastOPTimer = time.Now() + p.Trusteeship = 0 + return s.SceneState.OnPlayerOp(s, p, opcode, params) + } + return true +} + +func (this *PolicyThirteen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " eventcode=", evtcode, " params=", params) + if s.SceneState != nil { + s.SceneState.OnPlayerEvent(s, p, evtcode, params) + } +} + +func (this *PolicyThirteen) OnAudienceEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnAudienceEnter, sceneId=", s.GetSceneId(), " player=", p.SnId) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + //给自己发送房间信息 + this.SendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.AudienceEventEnter, nil) + } +} + +func (this *PolicyThirteen) OnAudienceLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnAudienceLeave, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.FirePlayerEvent(p, base.AudienceEventLeave, nil) +} + +func (this *PolicyThirteen) OnAudienceDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnAudienceDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId) + s.AudienceLeave(p, common.PlayerLeaveReason_DropLine) + s.FirePlayerEvent(p, base.AudienceEventDropLine, nil) +} + +func (this *PolicyThirteen) OnAudienceSit(s *base.Scene, p *base.Player) { + //十三水观众坐下 + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *PolicyThirteen) OnAudienceSit, sceneId=", s.GetSceneId(), " player=", p.GetName()) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + + pos := p.Pos + if pos != -1 && sceneEx.seats[pos] == nil { + playerEx := &PlayerEx{Player: p} + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + p.Pos = pos + p.ExtraData = playerEx + playerEx.Clear() + if sceneEx.Gaming { + p.MarkFlag(base.PlayerState_WaitNext) + p.UnmarkFlag(base.PlayerState_Ready) + p.SyncFlag() + } + //send msg + if len(s.Players) > 1 { + //向其他人广播玩家进入信息 + pack := &thirteen.SCThirteenPlayerEnter{ + Data: &thirteen.ThirteenPlayerData{ + SnId: proto.Int32(p.SnId), + Name: proto.String(p.Name), + Head: proto.Int32(p.Head), + Sex: proto.Int32(p.Sex), + Coin: proto.Int64(p.Coin), + Pos: proto.Int(pos), + Flag: proto.Int(p.GetFlag()), + Longitude: proto.Int32(p.Longitude), + Latitude: proto.Int32(p.Latitude), + City: proto.String(p.City), + HeadOutLine: proto.Int32(p.HeadOutLine), + VIP: proto.Int32(p.VIP), + RoleId: p.PlayerData.GetRoleId(), + }, + } + proto.SetDefaults(pack) + s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), pack, p.GetSid()) + } + //给自己发送房间信息 + this.SendRoomInfo(s, p, sceneEx) + //sceneEx.CheckAndSendDisbandInfo(p) + } + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } +} + +func (this *PolicyThirteen) IsCompleted(s *base.Scene) bool { + if s == nil { + return false + } + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return !sceneEx.Gaming + } + return false +} + +func (this *PolicyThirteen) IsCanForceStart(s *base.Scene) bool { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return len(s.Players) >= 2 && !sceneEx.Gaming + } + return false +} + +func (this *PolicyThirteen) ForceStart(s *base.Scene) { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if sceneEx.SceneState.GetState() == rule.ThirteenWaterSceneStateWait { + s.ChangeSceneState(rule.ThirteenWaterSceneStateStart) + } + } +} + +func (this *PolicyThirteen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return false + } + if s.SceneState != nil { + return s.SceneState.CanChangeCoinScene(s, p) + } + return false +} + +func (this *PolicyThirteen) SendRoomInfo(s *base.Scene, p *base.Player, sceneEx *SceneEx) { + pack := sceneEx.ThirteenWaterCreateRoomInfoPacket(s, p) + p.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomInfo), pack) +} + +func ThirteenWaterBroadcastRoomState(s *base.Scene, params ...int32) { + pack := &thirteen.SCThirteenRoomState{ + State: proto.Int(s.SceneState.GetState()), + Params: params, + } + s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomState), pack, 0) +} + +//===================================== +// BaseState 状态基类 +//===================================== + +type BaseState struct { +} + +func (this *BaseState) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + return int(time.Now().Sub(sceneEx.StateStartTime) / time.Second) + } + return 0 +} + +func (this *BaseState) CanChangeTo(s base.SceneState) bool { + return true +} + +func (this *BaseState) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + if !playerEx.IsGameing() { + return true + } + + switch sceneEx.GetSceneState().GetState() { + case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateOptCard: + if playerEx.Coin < sceneEx.GetLeaveDeductCoin() { // 不够扣,不能离开 + return false + } + } + return true +} + +func (this *BaseState) OnEnter(s *base.Scene) { + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + sceneEx.StateStartTime = time.Now() + } +} + +func (this *BaseState) OnLeave(s *base.Scene) {} + +func (this *BaseState) OnTick(s *base.Scene) {} + +func (this *BaseState) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + switch opcode { + case rule.ThirteenWaterPlayerOpStandup: + if len(params) > 0 { + if params[0] == 0 { + playerEx.isStand = true + } else { + playerEx.isStand = false + } + pack := &thirteen.SCThirteenPlayerOp{ + OpRetCode: thirteen.OpResultCode_OPRC_Sucess, + OpCode: int32(opcode), + OpParam: params, + Pos: int32(playerEx.GetPos()), + } + playerEx.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack) + logger.Logger.Trace("TWMmoPacketID_PACKET_SCThirteenPlayerOp:", pack) + } + return true + case rule.ThirteenWaterPlayerOpTest: + if !common.Config.IsDevMode { + return false + } + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + sceneEx.testPokers = make([]int64, len(params)) + copy(sceneEx.testPokers, params) + return true + } + + return false +} + +func (this *BaseState) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { +} + +//===================================== +// StateWait 匹配中 +//===================================== + +type StateWait struct { + BaseState +} + +func (this *StateWait) GetState() int { + return rule.ThirteenWaterSceneStateWait +} + +func (this *StateWait) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.ThirteenWaterSceneStateStart { + return true + } + return false +} + +func (this *StateWait) GetTimeout(s *base.Scene) int { + return 0 +} + +func (this *StateWait) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if s.Gaming { + s.NotifySceneRoundPause() + } + s.Gaming = false + ThirteenWaterBroadcastRoomState(s) + if sceneEx.CanStart() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateStart) + } + } +} + +// 玩家事件 +func (this *StateWait) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *StateWait) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode) + this.BaseState.OnPlayerEvent(s, p, evtcode, params) + if _, ok := s.ExtraData.(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + case base.PlayerEventEnter: + if !p.IsReady() { + p.MarkFlag(base.PlayerState_Ready) + p.SyncFlag() + } + } + } +} + +func (this *StateWait) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if s.CheckNeedDestroy() { + sceneEx.SceneDestroy(true) + return + } + if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterSceneWaitTimeout { + //切换到准备开局状态 + if sceneEx.CanStart() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateStart) + } else if s.GetRobotNum() == 1 { + tNow := time.Now() + for _, p := range s.Players { + if p.IsRob && tNow.Sub(p.GetLastOPTimer()) > time.Second*time.Duration(30+s.Rand.Int63n(60)) { + //p.LastOPTimer = tNow.Add(time.Minute * 30) + s.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + } + } + } + } + } +} + +//===================================== +// StateStart 开始倒计时 +//===================================== + +type StateStart struct { + BaseState +} + +func (this *StateStart) GetState() int { + return rule.ThirteenWaterSceneStateStart +} + +func (this *StateStart) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateBilled, rule.ThirteenWaterSceneStateWait: + return true + } + return false +} + +func (this *StateStart) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if _, ok := s.ExtraData.(*SceneEx); ok { + ThirteenWaterBroadcastRoomState(s) + s.Gaming = false + } +} + +func (this *StateStart) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterStartTimeout { + if sceneEx.Creator != 0 && sceneEx.GetRealPlayerNum() == 0 { + sceneEx.Destroy(true) + return + } + //切换到等待操作状态 + if sceneEx.CanStart() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateSendCards) + } else { + s.ChangeSceneState(rule.ThirteenWaterSceneStateWait) + } + } + } +} + +func (this *StateStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *StateStart) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode) + this.BaseState.OnPlayerEvent(s, p, evtcode, params) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + switch evtcode { + case base.PlayerEventLeave: + if !sceneEx.CanStart() { + logger.Logger.Tracef("(this *StateStart) OnPlayerEvent s.ChangeSceneState(ThirteenWaterSceneStateWait) %v", s.GetSceneId()) + s.ChangeSceneState(rule.ThirteenWaterSceneStateWait) + } + case base.PlayerEventEnter: + if !p.IsReady() { + p.MarkFlag(base.PlayerState_Ready) + p.SyncFlag() + } + } + } +} + +//===================================== +// StateSendCard 发牌状态 +//===================================== + +type StateSendCard struct { + BaseState +} + +func (this *StateSendCard) GetState() int { + return rule.ThirteenWaterSceneStateSendCards +} + +func (this *StateSendCard) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateOptCard, rule.ThirteenWaterSceneStateWait: + return true + } + return false +} + +func (this *StateSendCard) OnEnter(s *base.Scene) { + logger.Logger.Tracef("(this *StateSendCard) OnEnter, sceneid=%v", s.GetSceneId()) + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + s.Gaming = true + sceneEx.GameNowTime = time.Now() + sceneEx.logid, _ = model.AutoIncGameLogId() + //通知world开始第几局 + sceneEx.NumOfGames++ + s.NotifySceneRoundStart(sceneEx.NumOfGames) + //开始记录录像 + //if !s.Testing && s.rr != nil { + // pack := sceneEx.ThirteenWaterCreateRoomInfoPacket(s, nil) + // if pack != nil { + // s.rr.Init(int(thirteen.MmoPacketID_PACKET_SC_WINTHREE_ROOMINFO), pack) + // } + //} + + sceneEx.Clear() + sceneEx.GetInGameNum() + ThirteenWaterBroadcastRoomState(s, int32(sceneEx.NumOfGames)) + if len(sceneEx.testPokers) > 0 { + // 调试 + for _, v := range sceneEx.seats { + if v == nil || !v.IsGameing() { + continue + } + if len(sceneEx.testPokers) >= 13 { + for k, vv := range sceneEx.testPokers[:13] { + v.cards[k] = int(vv) + } + v.allGroup = sceneEx.logic.Suggest(v.cards) + sceneEx.testPokers = sceneEx.testPokers[13:] + } else { + sceneEx.testPokers = nil + v.cards = sceneEx.poker.Get13Crads() + } + } + } else { + //for i := 0; i < 10; i++ { + // isHaveQingLong := false + // lessCoin := 0 + // isF := false + // for _, player := range sceneEx.players { + // if player != nil && player.IsGameing() { + // if isF && !player.IsRob { + // player.cards = sceneEx.GetRandsType() + // isF = false + // } else { + // player.cards = sceneEx.poker.Get13Crads() + // } + // player.allGroup = sceneEx.logic.Suggest(player.cards) + // //todo 遇到清龙重新发牌 十次之后 不处理 + // for k, v := range player.allGroup { + // if k == 1000 || v.PokerType == 1 { + // isHaveQingLong = true + // break + // } + // } + // if player.Coin < 108*int64(sceneEx.GetBaseScore()) { + // lessCoin++ + // } + // } + // } + // if isHaveQingLong && lessCoin > 0 { + // sceneEx.poker.Init() + // } else { + // break + // } + //} + sceneEx.SendHandCardOdds() + } + sceneEx.SendToPlayerCards(s) + } +} + +func (this *StateSendCard) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterSendCardsTimeout { + if sceneEx.CheckNeedDestroy() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateWait) + } else { + s.ChangeSceneState(rule.ThirteenWaterSceneStateOptCard) + } + } + } +} + +//===================================== +// StateOp 选牌状态 +//===================================== + +type StateOp struct { + BaseState +} + +func (this *StateOp) GetState() int { + return rule.ThirteenWaterSceneStateOptCard +} + +func (this *StateOp) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateShowCards: + return true + } + return false +} + +// 状态进入时 +func (this *StateOp) OnEnter(s *base.Scene) { + logger.Logger.Tracef("(this *StateOp) OnEnter, sceneid=%v", s.GetSceneId()) + this.BaseState.OnEnter(s) + ThirteenWaterBroadcastRoomState(s) +} + +// 玩家操作 +func (this *StateOp) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + logger.Logger.Infof("(this *StateOp) OnPlayerOp p.snid (%v) opcode (%v)", p.SnId, opcode) + if this.BaseState.OnPlayerOp(s, p, opcode, params) { + return true + } + + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return false + } + playerEx, ok := p.ExtraData.(*PlayerEx) + if !ok { + return false + } + + returnFunc := func(code thirteen.OpResultCode) { + pack := &thirteen.SCThirteenPlayerOp{ + OpRetCode: code, + OpCode: int32(opcode), + OpParam: params, + Pos: int32(playerEx.GetPos()), + } + if code == thirteen.OpResultCode_OPRC_Error { + proto.SetDefaults(pack) + logger.Logger.Trace("SCThirteenPlayerOp:", pack) + playerEx.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack) + return + } + } + + if !playerEx.IsGameing() { + returnFunc(thirteen.OpResultCode_OPRC_Error) + return true + } + + switch opcode { + case rule.ThirteenWaterPlayerOpMS: //确定牌 + //if playerEx.deterMine { // 确认牌型 + // returnFunc(thirteen.OpResultCode_OPRC_Error) + // return false + //} + if len(params) == 13 { + //校验牌 + a := rule.DelCards(playerEx.cards[:], common.Int64Toint(params)) + if len(a) != 0 { + logger.Logger.Error("the cards is error.") + returnFunc(thirteen.OpResultCode_OPRC_Error) + return true + } + //牌赋值 + copy(playerEx.cardsO.Head[:], common.Int64Toint(params[:3])) + copy(playerEx.cardsO.Mid[:], common.Int64Toint(params[3:8])) + copy(playerEx.cardsO.End[:], common.Int64Toint(params[8:])) + playerEx.cardsO.PokerType = 0 + sceneEx.SendSelectCards(playerEx, 0, int64(opcode)) + } else { + sceneEx.SendSelectCards(playerEx, int(params[0]), int64(opcode)) + } + playerEx.Trusteeship = 0 + playerEx.UnmarkFlag(base.PlayerState_Auto) + playerEx.deterMine = true + //如果所有玩家都确认牌之后 可以直接开始下一阶段 + a := true + for _, v := range sceneEx.players { + if v != nil && v.IsGameing() { + if !v.deterMine { + a = false + } + } + } + if a { + //提前进入亮牌阶段 + s.ChangeSceneState(rule.ThirteenWaterSceneStateShowCards) + } + case rule.ThirteenWaterPlayerOpReset: + // 取消确认 + playerEx.deterMine = false + pack := &thirteen.SCThirteenPlayerOp{ + OpRetCode: thirteen.OpResultCode_OPRC_Sucess, + OpCode: int32(opcode), + OpParam: params, + Pos: int32(playerEx.GetPos()), + } + playerEx.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack, 0) + + default: + return false + } + return false +} + +func (this *StateOp) OnLeave(s *base.Scene) { + logger.Logger.Tracef("(this *StateOp) OnLeave, sceneid=%v", s.GetSceneId()) + this.BaseState.OnLeave(s) + sceneEx, ok := s.ExtraData.(*SceneEx) + if !ok { + return + } + + for _, player := range sceneEx.players { + if player != nil && player.IsGameing() { + if player.cardsO != nil && player.cardsO.PokerType != -1 { + if player.cardsO.PokerType < 1000000 { + player.isDP = sceneEx.logic.IsDP(player.cardsO.Head, player.cardsO.Mid, player.cardsO.End) + } + continue + } + //没有确定牌的玩家帮他确定牌 + sceneEx.SelectCards(player, -2) + player.deterMine = true + player.Trusteeship++ + // 标记托管状态 + player.MarkFlag(base.PlayerState_Auto) + } + } + + // test + //type CC struct { + // Cards [13]int + // Card0 *rule.Group + // Coin int64 + //} + //if len(sceneEx.players) == 3 { + // sceneEx.Params[3] = 200000 + // ls := []CC{ + // { + // Cards: [13]int{3, 9, 30, 40, 39, 14, 18, 47, 27, 4, 19, 6, 22}, + // Card0: &rule.Group{ + // Head: [3]int{39, 18, 47}, + // Mid: [5]int{30, 4, 19, 6, 3}, + // End: [5]int{40, 27, 14, 22, 9}, + // //PokerType: 100804, + // }, + // Coin: 550900, + // }, + // { + // Cards: [13]int{43, 53, 24, 34, 28, 36, 12, 20, 52, 5, 8, 25, 2}, + // Card0: &rule.Group{ + // Head: [3]int{36, 20, 5}, + // Mid: [5]int{28, 2, 34, 8, 43}, + // End: [5]int{25, 12, 53, 24, 52}, + // //PokerType: 100803, + // }, + // Coin: 560500, + // }, + // } + // var i int + // for _, v := range sceneEx.players { + // if v.SnId != 33424700 { + // info := ls[i] + // v.cards = info.Cards + // v.cardsO = info.Card0 + // v.Coin = info.Coin + // i++ + // } else { + // // 真人 + // v.cards = [13]int{10, 35, 16, 29, 45, 0, 33, 37, 1, 50, 46, 31, 44} + // v.cardsO = &rule.Group{ + // Head: [3]int{10, 46, 44}, + // Mid: [5]int{50, 16, 45, 0, 1}, + // End: [5]int{37, 35, 33, 31, 29}, + // //PokerType: 101005, + // } + // v.Coin = 8968339671 + // } + // } + //} + + //type CC struct { + // Cards [13]int + // Card0 *rule.Group + // Coin int64 + //} + //if len(sceneEx.players) == 3 { + // sceneEx.Params[3] = 200000 + // ls := []CC{ + // { + // Cards: [13]int{10, 21, 35, 34, 19, 31, 47, 15, 44, 2, 23, 18, 38}, + // Card0: &rule.Group{ + // Head: [3]int{35, 19, 38}, + // Mid: [5]int{44, 31, 18, 15, 2}, + // End: [5]int{47, 34, 21, 23, 10}, + // }, + // Coin: 179758402, + // }, + // { + // Cards: [13]int{20, 3, 39, 14, 52, 25, 7, 53, 22, 16, 43, 30, 17}, + // Card0: &rule.Group{ + // Head: [3]int{3, 39, 7}, + // Mid: [5]int{25, 22, 20, 16, 14}, + // End: [5]int{43, 30, 17, 53, 52}, + // }, + // Coin: 993000, + // }, + // } + // var i int + // for _, v := range sceneEx.players { + // if v.SnId != 33424700 { + // info := ls[i] + // v.cards = info.Cards + // v.cardsO = info.Card0 + // v.Coin = info.Coin + // i++ + // } else { + // // 真人 + // v.cards = [13]int{12, 13, 9, 36, 49, 24, 32, 48, 4, 33, 6, 45, 50} + // v.cardsO = &rule.Group{ + // Head: [3]int{13, 4, 33}, + // Mid: [5]int{48, 9, 49, 36, 12}, + // End: [5]int{45, 32, 6, 50, 24}, + // } + // v.Coin = 1150000 + // } + // } + //} + + //type CC struct { + // Cards [13]int + // Card0 *rule.Group + // Coin int64 + //} + //if len(sceneEx.players) == 3 { + // sceneEx.Params[3] = 200000 + // ls := []CC{ + // { + // Cards: [13]int{18, 12, 3, 10, 9, 8, 19, 14, 51, 25, 28, 24, 33}, + // Card0: &rule.Group{ + // Head: [3]int{51, 28, 33}, + // Mid: [5]int{12, 10, 9, 8, 3}, + // End: [5]int{25, 24, 19, 18, 14}, + // }, + // Coin: 298348695, + // }, + // { + // Cards: [13]int{26, 43, 44, 1, 0, 16, 50, 48, 34, 29, 17, 32, 37}, + // Card0: &rule.Group{ + // Head: [3]int{16, 50, 48}, + // Mid: [5]int{44, 1, 43, 17, 0}, + // End: [5]int{37, 34, 32, 29, 26}, + // }, + // Coin: 263600, + // }, + // } + // var i int + // for _, v := range sceneEx.players { + // if v.SnId != 33424700 { + // info := ls[i] + // v.cards = info.Cards + // v.cardsO = info.Card0 + // v.Coin = info.Coin + // i++ + // } else { + // // 真人 + // v.cards = [13]int{20, 47, 2, 36, 38, 39, 6, 15, 46, 21, 40, 52, 7} + // v.cardsO = &rule.Group{ + // Head: [3]int{39, 6, 40}, + // Mid: [5]int{15, 2, 47, 21, 38}, + // End: [5]int{46, 20, 7, 52, 36}, + // } + // v.Coin = 586000 + // } + // } + //} + + // test + + // 计算总分数 + sceneEx.CountScore() + // 预结算,玩家余额在结算状态修改 + sceneEx.CountBilled() +} + +func (this *StateOp) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.GetBaiPai() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateShowCards) + } + } +} + +//===================================== +// StateOp 看牌状态 +//===================================== + +type StateShow struct { + BaseState +} + +func (this *StateShow) GetState() int { + return rule.ThirteenWaterSceneStateShowCards +} + +func (this *StateShow) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateShowCards, rule.ThirteenWaterSceneStateHit, rule.ThirteenWaterSceneStateBilled: //玩家操作状态 + return true + } + return false +} + +// 状态进入 +func (this *StateShow) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + ThirteenWaterBroadcastRoomState(s) + logger.Logger.Tracef("(this *StateShow) OnEnter, sceneid=%v currpos:%v", s.GetSceneId(), sceneEx.currOpPos) + sceneEx.ShowCards() + // 每人看牌5秒,特殊牌型不算; + var n int + var has bool + for _, v := range sceneEx.players { + if v != nil && v.IsGameing() && v.cardsO != nil { + n++ + if v.cardsO.PokerType == 1 { // 有青龙 + has = true + } + } + } + n -= sceneEx.specialTypeNum + sceneEx.specialTime = time.Second * time.Duration(n*5) + // pk动画 2秒 + sceneEx.specialTime += time.Second * 2 + // 特殊牌型 4.5秒;至尊青龙5.5秒 + if sceneEx.specialTypeNum > 0 { + if has { + sceneEx.specialTime += time.Millisecond * 5500 + } else { + sceneEx.specialTime += time.Millisecond * 4500 + } + } + logger.Logger.Tracef("show cards: %v %v", n, sceneEx.specialTime) + if sceneEx.specialTime <= 0 { + sceneEx.specialTime = time.Second + } + } +} + +func (this *StateShow) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.specialTime { + if sceneEx.entryHitState { + s.ChangeSceneState(rule.ThirteenWaterSceneStateHit) + } else { + s.ChangeSceneState(rule.ThirteenWaterSceneStateBilled) + } + } + } +} + +//===================================== +// StateHit 打枪状态 +//===================================== + +type StateHit struct { + BaseState +} + +func (this *StateHit) GetState() int { + return rule.ThirteenWaterSceneStateHit +} + +func (this *StateHit) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateBilled: + return true + } + return false +} + +// 状态进入 +func (this *StateHit) OnEnter(s *base.Scene) { + this.BaseState.OnEnter(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + ThirteenWaterBroadcastRoomState(s) + logger.Logger.Tracef("(this *StateHit) OnEnter, sceneid=%v currpos:%v", s.GetSceneId(), sceneEx.currOpPos) + + hitNum := 0 //打枪人数 + for _, playerEx := range sceneEx.players { + if playerEx != nil && playerEx.IsGameing() { + hitSum := len(playerEx.winThreePos) + if hitSum > 0 { + hitNum += hitSum + } + } + } + if sceneEx.isCanAllHitPos != -1 { + hitNum++ + } + // 每个打枪加1秒,全垒打再加1秒 + sceneEx.hitTime += time.Second * (time.Duration(hitNum)) + sceneEx.ShowCards() + } +} + +func (this *StateHit) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.hitTime { + s.ChangeSceneState(rule.ThirteenWaterSceneStateBilled) + } + } +} + +//===================================== +// StateBilled 结算 +//===================================== + +type StateBilled struct { + BaseState +} + +func (this *StateBilled) GetState() int { + return rule.ThirteenWaterSceneStateBilled +} + +func (this *StateBilled) CanChangeTo(s base.SceneState) bool { + switch s.GetState() { + case rule.ThirteenWaterSceneStateStart, rule.ThirteenWaterSceneStateWait: + return true + } + return false +} + +func (this *StateBilled) OnEnter(s *base.Scene) { + logger.Logger.Tracef("(this *StateBilled) OnEnter, sceneid=%v", s.GetSceneId()) + this.BaseState.OnEnter(s) + ThirteenWaterBroadcastRoomState(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + //通知客户端结算结果 + var billed []*thirteen.Billed + // 没离开,剩余玩家结算 + for _, playerEx := range sceneEx.players { + if playerEx != nil && playerEx.IsGameing() { + if playerEx.gainCoin > 0 { + //税前赢的钱 税收 下注额 + playerEx.AddServiceFee(playerEx.taxCoin) + playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneWin, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = 1 + } else if playerEx.gainCoin < 0 { + playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneLost, base.SyncFlag_ToClient, "system", s.GetSceneName()) + playerEx.CurIsWin = -1 + } + // 离场补偿分 + if playerEx.score[6] > 0 { + playerEx.AddCoin(playerEx.score[6]*int64(sceneEx.GetBaseScore()), common.GainWay_LeaveCombat, base.SyncFlag_ToClient, "system", s.GetSceneName()) + } + sceneEx.Statistics(&base.StaticParam{ + SnId: playerEx.SnId, + Gain: playerEx.gainCoin, + GainTax: playerEx.taxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.robotNum > 0, + }) + billed = append(billed, &thirteen.Billed{ + Pos: proto.Int32(int32(playerEx.Pos)), + Coin: proto.Int64(playerEx.Coin), + WinCoin: proto.Int64(playerEx.gainCoin + playerEx.score[6]*int64(sceneEx.GetBaseScore())), + CombatCoin: playerEx.score[6] * int64(sceneEx.GetBaseScore()), + }) + } + } + // 提前离开的 + for _, playerEx := range sceneEx.PlayerBackup { + if playerEx != nil && playerEx.isBilled { + billed = append(billed, &thirteen.Billed{ + Pos: proto.Int32(int32(playerEx.Pos)), + Coin: proto.Int64(playerEx.Coin), + WinCoin: proto.Int64(playerEx.gainCoin + playerEx.score[6]*int64(sceneEx.GetBaseScore())), + CombatCoin: playerEx.score[6] * int64(sceneEx.GetBaseScore()), + }) + } + } + + pack := &thirteen.SCThirteenBilled{ + AllBilled: billed, + } + logger.Logger.Trace("SCThirteenWaterBilled is pack: ", pack) + sceneEx.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenBilled), pack, 0) + + //体验场不走统计 + if !sceneEx.Testing { + if sceneEx.gamePlayerNum-sceneEx.robotNum > 0 { + /////////////////////////////////////统计牌局详细记录 + thirteenWaterType := model.ThirteenWaterType{ + RoomId: int32(sceneEx.SceneId), + RoomRounds: int32(sceneEx.NumOfGames), + RoomType: int32(sceneEx.SceneType), + BaseScore: sceneEx.GetBaseScore(), + NowRound: int32(sceneEx.NumOfGames), + ClubRate: sceneEx.Scene.PumpCoin, + } + var person []model.ThirteenWaterPerson + for _, o_player := range sceneEx.players { + if o_player != nil && o_player.IsGameing() { + p := model.ThirteenWaterPerson{ + UserId: o_player.SnId, + ChangeCoin: o_player.gainCoin + o_player.score[6]*int64(sceneEx.GetBaseScore()), + Cardinfo: o_player.cards[:], + StartCoin: o_player.StartCoin, + UserIcon: o_player.Head, + IsRob: o_player.IsRob, + Flag: o_player.GetFlag(), + Tax: o_player.taxCoin, + Platform: o_player.Platform, + Channel: o_player.Channel, + Promoter: strconv.Itoa(int(o_player.PromoterTree)), + PackageTag: o_player.PackageID, + InviterId: o_player.InviterId, + WBLevel: o_player.WBLevel, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(o_player.SnId)), + TableScore: o_player.tableScore, + LeaveCombat: int(o_player.score[6]), + AllFight: int(o_player.score[5]), + GunScore: int(o_player.score[4]), + TestLog: o_player.TestLog, + } + score := int64(0) + for _, v := range o_player.score { + score += v + } + p.AllScore = int(score) + p.CardsO = model.ThirteenWaterPoker{ + Head: o_player.cardsO.Head, + Mid: o_player.cardsO.Mid, + End: o_player.cardsO.End, + PokerType: sceneEx.FormatCards(o_player.cardsO), + } + if p.CardsO.PokerType > 1000000 { + p.SpecialName = rule.SpecialTypeName[p.CardsO.PokerType/1000000] + copy(p.CardsO.Head[:], o_player.cards[:3]) + copy(p.CardsO.Mid[:], o_player.cards[3:8]) + copy(p.CardsO.End[:], o_player.cards[8:]) + } else if p.CardsO.PokerType > 10000 { + p.HeadName = rule.PokersTypeName[p.CardsO.PokerType%1000000/10000] + p.MidName = rule.PokersTypeName[p.CardsO.PokerType%10000/100] + p.EndName = rule.PokersTypeName[p.CardsO.PokerType%100] + p.IsDP = o_player.isDP + } + p.IsWin = int32(o_player.CurIsWin) + person = append(person, p) + /// + if !o_player.IsRob { + //有真人 存真人的映射表 + totalin, totalout := int64(0), int64(0) + if o_player.CurIsWin > 0 { + totalout = o_player.gainCoin + o_player.taxCoin + } else { + totalin -= o_player.gainCoin + } + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + sceneEx.SaveGamePlayerListLog(o_player.SnId, base.GetSaveGamePlayerListLogParam(o_player.Platform, o_player.Channel, o_player.BeUnderAgentCode, + o_player.PackageID, sceneEx.logid, o_player.InviterId, totalin, totalout, o_player.taxCoin, + 0, 0, o_player.gainCoin, validBet, validFlow, p.IsFirst, false)) + } + } + } + for _, o_player := range sceneEx.PlayerBackup { + if o_player != nil && o_player.isBilled { + p := model.ThirteenWaterPerson{ + UserId: o_player.SnId, + ChangeCoin: o_player.gainCoin + o_player.score[6]*int64(sceneEx.GetBaseScore()), + Cardinfo: o_player.cards[:], + StartCoin: o_player.StartCoin, + UserIcon: o_player.Head, + IsRob: o_player.IsRob, + Flag: o_player.flag, + Tax: o_player.taxCoin, + ClubPump: o_player.clubPump, + Platform: o_player.Platform, + Channel: o_player.Channel, + Promoter: strconv.Itoa(int(o_player.PromoterTree)), + PackageTag: o_player.PackageID, + InviterId: o_player.InviterId, + WBLevel: o_player.WBLevel, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(o_player.SnId)), + TableScore: o_player.tableScore, + LeaveCombat: int(o_player.score[6]), + AllFight: int(o_player.score[5]), + GunScore: int(o_player.score[4]), + TestLog: o_player.TestLog, + } + score := int64(0) + for _, v := range o_player.score { + score += v + } + p.AllScore = int(score) + p.CardsO = model.ThirteenWaterPoker{ + Head: o_player.cardsO.Head, + Mid: o_player.cardsO.Mid, + End: o_player.cardsO.End, + PokerType: sceneEx.FormatCards(o_player.cardsO), + } + if p.CardsO.PokerType > 1000000 { + p.SpecialName = rule.SpecialTypeName[p.CardsO.PokerType/1000000] + copy(p.CardsO.Head[:], o_player.cards[:3]) + copy(p.CardsO.Mid[:], o_player.cards[3:8]) + copy(p.CardsO.End[:], o_player.cards[8:]) + } else if p.CardsO.PokerType > 10000 { + p.HeadName = rule.PokersTypeName[p.CardsO.PokerType%1000000/10000] + p.MidName = rule.PokersTypeName[p.CardsO.PokerType%10000/100] + p.EndName = rule.PokersTypeName[p.CardsO.PokerType%100] + p.IsDP = o_player.isDP + } + p.IsWin = int32(o_player.CurIsWin) + person = append(person, p) + } + } + thirteenWaterType.PlayerData = person + thirteenWaterType.PlayerCount = len(person) + info, err := model.MarshalGameNoteByFIGHT(&thirteenWaterType) + if err == nil { + sceneEx.SaveGameDetailedLog(sceneEx.logid, info, &base.GameDetailedParam{}) + } + } + } + } +} + +func (this *StateBilled) OnLeave(s *base.Scene) { + logger.Logger.Tracef("(this *StateBilled) OnLeave, sceneid=%v", s.GetSceneId()) + this.BaseState.OnLeave(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + //结束记录回放录像 + sceneEx.RecordReplayOver() + for _, v := range sceneEx.players { + if v != nil { + v.UnmarkFlag(base.PlayerState_WaitNext) + v.UnmarkFlag(base.PlayerState_GameBreak) + v.MarkFlag(base.PlayerState_Ready) + v.SyncFlag() + } + } + + hasLeave := false + //剔除下线玩家 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + player_data := sceneEx.seats[i] + if player_data == nil { + continue + } + if sceneEx.IsMatchScene() { + continue + } + if !player_data.IsOnLine() { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_DropLine, true) + hasLeave = true + continue + } + if player_data.IsRob { + if s.CoinOverMaxLimit(player_data.GetCoin(), player_data.Player) { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true) + hasLeave = true + continue + } + } + if !s.CoinInLimit(player_data.GetCoin()) { + sceneEx.PlayerLeave(player_data.Player, s.NotCoinInLimitType(player_data.GetCoin()), true) + hasLeave = true + continue + } + if player_data.Trusteeship >= 1 { + s.PlayerLeave(player_data.Player, common.PlayerLeaveReason_LongTimeNoOp, true) + hasLeave = true + continue + } + if player_data.isStand { // 站起离开 + s.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true) + hasLeave = true + continue + } + } + + if !hasLeave && !sceneEx.IsRobFightGame() && !sceneEx.IsMatchScene() { + s.TryDismissRob() + } + + if s.CheckNeedDestroy() || (s.IsMatchScene() && (!s.MatchFinals || (s.MatchFinals && s.NumOfGames >= 2))) { // 非决赛打一场 决赛打两场 + sceneEx.SceneDestroy(true) + } + s.TryRelease() + } +} + +func (this *StateBilled) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.BaseState.OnPlayerOp(s, p, opcode, params) { + return true + } + return false +} + +func (this *StateBilled) OnTick(s *base.Scene) { + this.BaseState.OnTick(s) + if sceneEx, ok := s.ExtraData.(*SceneEx); ok { + if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterBilledTimeout { + if sceneEx.CanStart() { + s.ChangeSceneState(rule.ThirteenWaterSceneStateStart) + } else { + s.ChangeSceneState(rule.ThirteenWaterSceneStateWait) + } + return + } + } +} + +// ////////////////////////////////////////////////////////////////////////////// +func (this *PolicyThirteen) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + if stateid < 0 || stateid >= rule.ThirteenWaterSceneStateMax { + return + } + this.states[stateid] = state +} + +func (this *PolicyThirteen) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < rule.ThirteenWaterSceneStateMax { + return this.states[stateid] + } + return nil +} + +func init() { + PolicyThirteenSingleton.RegisteSceneState(&StateWait{}) + PolicyThirteenSingleton.RegisteSceneState(&StateStart{}) + PolicyThirteenSingleton.RegisteSceneState(&StateSendCard{}) + PolicyThirteenSingleton.RegisteSceneState(&StateOp{}) + PolicyThirteenSingleton.RegisteSceneState(&StateShow{}) + PolicyThirteenSingleton.RegisteSceneState(&StateHit{}) + PolicyThirteenSingleton.RegisteSceneState(&StateBilled{}) + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + base.RegisteScenePolicy(common.GameID_Thirteen4, 0, PolicyThirteenSingleton) + base.RegisteScenePolicy(common.GameID_Thirteen8, 0, PolicyThirteenSingleton) + base.RegisteScenePolicy(common.GameID_ThirteenFree, 0, PolicyThirteenSingleton) + base.RegisteScenePolicy(common.GameID_ThirteenFreeLaiZi, 0, PolicyThirteenSingleton) + return nil + }) +} diff --git a/gamesrv/tienlen/action_tienlen.go b/gamesrv/tienlen/action_tienlen.go new file mode 100644 index 0000000..8404d65 --- /dev/null +++ b/gamesrv/tienlen/action_tienlen.go @@ -0,0 +1,54 @@ +package tienlen + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +// tienlen +type CSTienLenPlayerOpPacketFactory struct { +} +type CSTienLenPlayerOpHandler struct { +} + +func (this *CSTienLenPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &tienlen.CSTienLenPlayerOp{} + return pack +} +func (this *CSTienLenPlayerOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTienLenPlayerOpHandler Process recv ", data) + if msg, ok := data.(*tienlen.CSTienLenPlayerOp); ok { + p := base.PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSTienLenPlayerOpHandler p == nil") + return nil + } + scene := p.GetScene() + if scene == nil { + logger.Logger.Warn("CSTienLenPlayerOpHandler p.scene == nil") + return nil + } + if !common.IsTienLen(scene.GetGameId()) { + logger.Logger.Error("CSTienLenPlayerOpHandler gameId Error ", scene.GameId) + return nil + } + if !scene.HasPlayer(p) { + return nil + } + sp := scene.GetScenePolicy() + if sp != nil { + sp.OnPlayerOp(scene, p, int(msg.GetOpCode()), msg.GetOpParam()) + } + return nil + } + return nil +} + +func init() { + //tienlen + common.RegisterHandler(int(tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), &CSTienLenPlayerOpHandler{}) + netlib.RegisterFactory(int(tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), &CSTienLenPlayerOpPacketFactory{}) +} diff --git a/gamesrv/tienlen/playerdata_tienlen.go b/gamesrv/tienlen/playerdata_tienlen.go new file mode 100644 index 0000000..d5243be --- /dev/null +++ b/gamesrv/tienlen/playerdata_tienlen.go @@ -0,0 +1,211 @@ +package tienlen + +import ( + "math/rand" + "strconv" + "time" + + rule "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tienlen" +) + +// 玩家身上的额外数据 +type TienLenPlayerData struct { + *base.Player //玩家信息 + cards [rule.HandCardNum]int32 //手牌信息 + delCards [][]int32 //出过的手牌 + bombScore int64 //炸弹得分 + bombTaxScore int64 //炸弹税收 + isPass bool //小轮标记过牌 + isDelAll bool //出完牌了 + robotGameTimes int32 //机器人局数限制 + winCoin int64 //本局赢的钱 + odds int32 // 调控概率 + bombRankScore int64 // 排位炸弹积分 + tianHu int32 // 天胡类型 + ThinkLongCnt int32 //长考次数 + curHandLimitTimeOut time.Duration // 出牌时间上限 + isNotOverLastHand bool //手牌不能压过上家出牌标志 + cardScore int // 手牌评分 +} + +func (this *TienLenPlayerData) init() { + for i := int32(0); i < rule.HandCardNum; i++ { + this.cards[i] = rule.InvalideCard + } + this.delCards = [][]int32{} + this.bombScore = 0 + this.bombTaxScore = 0 + this.isPass = false + this.isDelAll = false + this.robotGameTimes = rule.RobotGameTimesMin + rand.Int31n(rule.RobotGameTimesMax) + this.winCoin = 0 + this.odds = 0 + this.bombRankScore = 0 + this.tianHu = 0 + this.ThinkLongCnt = 0 + this.curHandLimitTimeOut = rule.TienLenPlayerOpTimeout + this.isNotOverLastHand = false + this.cardScore = 0 +} + +func (this *TienLenPlayerData) Clear() { + for i := int32(0); i < rule.HandCardNum; i++ { + this.cards[i] = rule.InvalideCard + } + this.delCards = [][]int32{} + this.bombScore = 0 + this.bombTaxScore = 0 + this.isPass = false + this.isDelAll = false + this.winCoin = 0 + this.odds = 0 + this.bombRankScore = 0 + this.tianHu = 0 + this.ThinkLongCnt = 0 + this.curHandLimitTimeOut = rule.TienLenPlayerOpTimeout + this.isNotOverLastHand = false + + this.MarkFlag(base.PlayerState_WaitNext) + this.TestLog = this.TestLog[:0] +} + +// CanOp 能否操作 +func (this *TienLenPlayerData) CanOp() bool { + if this.IsGameing() && !this.isPass && !this.isDelAll { + return true + } + return false +} + +// TotalOutIn 计算玩家赔率 产出/投入 +func (this *TienLenPlayerData) TotalOutIn(gameId int32) (int64, int64) { + if this.GDatas != nil { + if d, exist := this.GDatas[strconv.Itoa(int(gameId))]; exist { + return d.Statics.TotalOut, d.Statics.TotalIn + } + } + return 0, 0 +} + +// GetThinkLongTime 根据长考次数获取当前出牌时间 +func (this *TienLenPlayerData) GetThinkLongTime() time.Duration { + if this.IsRobot() { + return rule.TienLenPlayerOpTimeout + } + + CurThinkLongCnt := this.ThinkLongCnt + if CurThinkLongCnt < 0 { + CurThinkLongCnt = 0 + } + + for _, TlValue := range rule.ThinkLongSct { + if CurThinkLongCnt >= TlValue.Min && CurThinkLongCnt <= TlValue.Max { + return TlValue.Time + } + } + + return rule.TienLenPlayerOpTimeout +} + +// RefreshThinkLongCnt 根据出牌时间计算 刷新长考次数 +func (this *TienLenPlayerData) RefreshThinkLongCnt(handTime time.Duration, bSetZero bool) { + if this.IsRobot() { + return + } + + CurThingLongTime := this.GetThinkLongTime() + + if bSetZero { + this.ThinkLongCnt = 0 + } else { + // 长考时间减去 出牌时间 + second := (CurThingLongTime - handTime) / (time.Second) + if second <= 5 { + this.ThinkLongCnt++ + } + } + + this.SendThinkLongCnt() +} + +// SendThinkLongCnt 发送长考次数 +func (this *TienLenPlayerData) SendThinkLongCnt() { + + if this.IsRobot() { + return + } + + pack := &tienlen.SCTienLenPlayerThinkLongCnt{} + + pack.ThinkLongCnt = this.ThinkLongCnt + + proto.SetDefaults(pack) + + this.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenThinkLongCnt), pack) + //logger.Logger.Trace("-------(this *TienLenPlayerData) SendThinkLongCnt player SnId:", this.SnId, ";SCTienLenPlayerThinkLongCnt: ", pack.ThinkLongCnt) +} + +// RefreshCurHandLimitTimeOut 刷新玩家出牌时间 +func (this *TienLenPlayerData) RefreshCurHandLimitTimeOut() { + if this.IsRobot() { + return + } + + this.curHandLimitTimeOut = this.GetThinkLongTime() +} + +func (this *TienLenPlayerData) GetCurHandLimitTimeOut() time.Duration { + if this.IsRobot() { + return rule.TienLenPlayerOpTimeout + } + + return this.curHandLimitTimeOut +} + +// EnterAutoState 游戏中 进入自动托管状态 +func (this *TienLenPlayerData) EnterAutoState() { + if this.IsRobot() { + return + } + + if this.isNotOverLastHand { + return + } + + if this.IsAuto() { + return + } + + this.RefreshThinkLongCnt(0, true) + + s := this.GetScene() + if s != nil { + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + if sceneEx.GetGaming() { + this.MarkFlag(base.PlayerState_Auto) + this.SyncFlag(true) + //logger.Logger.Trace("(this *TienLenPlayerData) EnterAutoState", " player=", this.Name, " ThinkLongCnt=", this.ThinkLongCnt) + } + } + } +} + +// 游戏中 离开自动托管状态 +func (this *TienLenPlayerData) LeaveAutoState(s *base.Scene) { + if this.IsRobot() { + return + } + + this.RefreshThinkLongCnt(0, true) + + if _, ok := s.GetExtraData().(*TienLenSceneData); ok { + this.UnmarkFlag(base.PlayerState_Auto) + + onlyMyself := []bool{true} + this.SyncFlag(onlyMyself...) + //logger.Logger.Trace("(this *TienLenPlayerData) LeaveAutoState", " player:", this.SnId, " Flag=", this.GetFlag()) + } +} diff --git a/gamesrv/tienlen/scenedata_tienlen.go b/gamesrv/tienlen/scenedata_tienlen.go new file mode 100644 index 0000000..3838f09 --- /dev/null +++ b/gamesrv/tienlen/scenedata_tienlen.go @@ -0,0 +1,2018 @@ +package tienlen + +import ( + "encoding/json" + "fmt" + "math" + "math/rand" + "sort" + "strings" + "time" + + "mongo.games.com/goserver/core/logger" + + tienlenApi "mongo.games.com/game/api3th/smart/tienlen" + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/game/srvdata" +) + +// 房间上的额外数据 +type TienLenSceneData struct { + *base.Scene //场景 + players map[int32]*TienLenPlayerData //玩家信息 + seats [4]*TienLenPlayerData //玩家 + poker *rule.Poker //扑克牌对象 + curGamingPlayerNum int //当前在玩玩家数量 + lastGamingPlayerNum int //上局参与游戏的人数 + lastOpPos int32 //前一个出牌的玩家位置 + currOpPos int32 //当前等待出牌的玩家位置 + lastBombPos int32 //上一个出炸弹的玩家位置 + curBombPos int32 //当前出炸弹的玩家位置 + roundScore int32 //小轮分 + startOpPos int32 //开始操作的玩家,拿最小牌的先出牌 + curMinCard int32 //当局最小牌值 + tianHuSnids []int32 //天胡玩家id + winSnids []int32 //赢家id + lastWinSnid int32 //上局赢家id + masterSnid int32 //房主 + delCards [][]int32 //已出牌 + delOrders []int32 //出牌顺序 + isKongBomb bool //是否空放,打出炸弹没人能接 + bombToEnd int //空放炸弹致底分翻倍次数 + card_play_action_seq []string //出牌历史记录 + card_play_action_seq_int32 [][]int32 //出牌历史记录 + lastPos int32 //前一个玩家位置 + isAllRob bool //是否是纯AI场 + robotGamingNum int // 机器人玩家数量 + allPlayerCards [][]int32 //所有玩家的牌 + recordId string // 牌局记录id + testPokers []int32 // 发指定的牌,用于测试 + cHintCards []int32 //客户端提示出牌记录 + isCardsKu bool //是否是牌库 + cardsKuId int32 //牌库ID +} + +func NewTienLenSceneData(s *base.Scene) *TienLenSceneData { + sceneEx := &TienLenSceneData{ + Scene: s, + poker: rule.NewPoker(), + players: make(map[int32]*TienLenPlayerData), + } + sceneEx.Clear() + return sceneEx +} + +func (this *TienLenSceneData) init() bool { + this.tianHuSnids = []int32{} + this.winSnids = []int32{} + this.roundScore = 0 + this.currOpPos = rule.InvalidePos + this.lastOpPos = rule.InvalidePos + this.curBombPos = rule.InvalidePos + this.lastBombPos = rule.InvalidePos + this.lastPos = rule.InvalidePos + this.UnmarkPass() + this.curGamingPlayerNum = 0 + this.curMinCard = 0 + this.delCards = [][]int32{} + this.delOrders = []int32{} + this.card_play_action_seq = []string{} + this.card_play_action_seq_int32 = [][]int32{} + this.bombToEnd = 0 + this.allPlayerCards = [][]int32{} + this.recordId, _ = model.AutoIncGameLogId() + if this.GetPlayerNum() == 0 { + this.SetPlayerNum(rule.MaxNumOfPlayer) + } + + this.cHintCards = []int32{} + return true +} + +func (this *TienLenSceneData) Clear() { + this.tianHuSnids = []int32{} + this.winSnids = []int32{} + this.roundScore = 0 + this.currOpPos = rule.InvalidePos + this.lastOpPos = rule.InvalidePos + this.curBombPos = rule.InvalidePos + this.lastBombPos = rule.InvalidePos + this.lastPos = rule.InvalidePos + this.curGamingPlayerNum = 0 + this.curMinCard = 0 + this.UnmarkPass() + this.delCards = [][]int32{} + this.delOrders = []int32{} + this.card_play_action_seq = []string{} + this.card_play_action_seq_int32 = [][]int32{} + this.bombToEnd = 0 + this.isAllRob = false + this.robotGamingNum = 0 + this.allPlayerCards = [][]int32{} + this.recordId, _ = model.AutoIncGameLogId() + for _, player := range this.players { + if player != nil { + player.UnmarkFlag(base.PlayerState_WaitNext) + } + } + this.cHintCards = []int32{} +} + +func (this *TienLenSceneData) CanStart() bool { + if this.GetGaming() == true { + return false + } + + nPlayerCount := this.GetPlayerCnt() + nRobotCount := this.GetRobotCnt() + if this.IsMatchScene() { + if nRobotCount == 4 { + this.isAllRob = true + } + if nPlayerCount == 4 { // 比赛场4人开赛 + return true + } else { + return false + } + } + // 房间人数>=2开始,并且有真人或者是预创建房间,并且有房主 + if nPlayerCount >= 2 && (this.GetRealPlayerNum() > 0 || this.IsPreCreateScene()) { //人数>=2开始 + return true + } + return false +} + +func (this *TienLenSceneData) GetPlayerCnt() int { + var cnt int + for i := 0; i < this.GetPlayerNum(); i++ { + playerEx := this.seats[i] + if playerEx == nil { + continue + } + cnt++ + } + return cnt +} +func (this *TienLenSceneData) GetGameingPlayerCnt() int { + var cnt int + for i := 0; i < this.GetPlayerNum(); i++ { + playerEx := this.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsGameing() { + cnt++ + } + } + return cnt +} + +func (this *TienLenSceneData) GetRobotCnt() int { + var cnt int + for i := 0; i < this.GetPlayerNum(); i++ { + playerEx := this.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsRob { + cnt++ + } + } + return cnt +} + +func (this *TienLenSceneData) GetGameingRobotCnt() int { + var cnt int + for i := 0; i < this.GetPlayerNum(); i++ { + playerEx := this.seats[i] + if playerEx == nil { + continue + } + if playerEx.IsRob && playerEx.IsGameing() { + cnt++ + } + } + return cnt +} + +func (this *TienLenSceneData) GetSeatPlayerCnt() int { + var cnt int + for i := 0; i < this.GetPlayerNum(); i++ { + playerEx := this.seats[i] + if playerEx == nil { + continue + } + cnt++ + } + return cnt +} + +func (this *TienLenSceneData) BroadcastPlayerLeave(p *base.Player, reason int) { + scLeavePack := &tienlen.SCTienLenPlayerLeave{ + Pos: proto.Int(p.GetPos()), + } + proto.SetDefaults(scLeavePack) + logger.Logger.Trace("SCTienLenPlayerLeave: ", p.SnId, " scLeavePack: ", scLeavePack) + this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerLeave), scLeavePack, p.GetSid()) +} + +func (this *TienLenSceneData) BroadcastAudienceNum(p *base.Player) { + pack := &tienlen.SCTienLenUpdateAudienceNum{ + AudienceNum: proto.Int(this.GetAudiencesNum()), + } + proto.SetDefaults(pack) + this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenUpdateAudienceNum), pack, p.GetSid()) +} + +func (this *TienLenSceneData) delPlayer(p *base.Player) { + if p, exist := this.players[p.SnId]; exist { + this.seats[p.GetPos()] = nil + delete(this.players, p.SnId) + } +} +func (this *TienLenSceneData) OnPlayerLeave(p *base.Player, reason int) { + // 清除上局赢家id + if this.lastWinSnid == p.SnId { + this.lastWinSnid = 0 + } + + this.delPlayer(p) + this.BroadcastPlayerLeave(p, reason) + + if !p.IsRob { //真人离开 + hadSeat := false + for _, seat := range this.seats { + if seat != nil { + hadSeat = true + break + } + } + if !hadSeat { //座位上没有人了,把观众踢出去 + audiences := this.GetAudiences() + for _, audience := range audiences { + if audience != nil { + this.AudienceLeave(audience, common.PlayerLeaveReason_RoomClose) + } + } + } + } +} + +func (this *TienLenSceneData) SceneDestroy(force bool) { + //销毁房间 + this.Scene.Destroy(force) +} + +// 广播房主更换 +func (this *TienLenSceneData) BroadcastUpdateMasterSnid() { + pack := &tienlen.SCTienLenUpdateMasterSnid{ + MasterSnid: proto.Int32(this.masterSnid), + } + proto.SetDefaults(pack) + this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenUpdateMasterSnid), pack, 0) + logger.Logger.Trace("BroadcastUpdateMasterSnid ", pack) +} + +// 广播当前操作的玩家位置 +func (this *TienLenSceneData) BroadcastOpPos() { + + if this.currOpPos != rule.InvalidePos && this.seats[this.currOpPos] != nil && this.seats[this.currOpPos].IsRobot() { + //计算其它人手牌,所有牌-出过的牌-自己手牌 + othercards := []int32{} + handcards := []int32{} + /* for i := int32(0); i < 52; i++ { + othercards = append(othercards, i) + }*/ + for _, cards := range this.allPlayerCards { + for _, card := range cards { + if card != -1 { + othercards = append(othercards, tienlenApi.CardToAiCard[card]) + } + } + } + logger.Logger.Info("--------------------------------------othercards = ", othercards) + card_play_action_seqs := []string{} + for _, cards := range this.card_play_action_seq_int32 { + //转棋牌到AI中的牌 + aicards := []int32{} + for _, card := range cards { + aicards = append(aicards, tienlenApi.CardToAiCard[card]) + } + + card_play_action_seqs = append(card_play_action_seqs, common.Int32SliceToString(aicards, ",")) + othercards = common.DelSliceIn32s(othercards, aicards) + } + //删除手牌 + for _, v := range this.seats[this.currOpPos].cards { + if v != -1 { + aicard := tienlenApi.CardToAiCard[v] + othercards = common.DelSliceInt32(othercards, aicard) + handcards = append(handcards, aicard) + } + } + //logger.Logger.Infof("%v出牌历史记录:%v",this.currOpPos,this.card_play_action_seq) + //logger.Logger.Infof("%v出牌历史记录:%v",this.currOpPos,common.StringSliceToString(card_play_action_seqs,"|")) + //logger.Logger.Infof("%v手牌:%v 数量:%v",this.currOpPos,handcards,len(handcards)) + //logger.Logger.Infof("%v其它人牌:%v 数量:%v",this.currOpPos,othercards,len(othercards)) + + lastmove := make(map[int][]int32) + numCardsLeft := make(map[int]int) + playedCards := make(map[int][]int32) + for pos, v := range this.seats { + if v != nil && v.IsGameing() { + //出过的牌 + delcards := []int32{} + for _, cards := range v.delCards { + for _, card := range cards { + aicard := tienlenApi.CardToAiCard[card] + delcards = append(delcards, aicard) + } + } + //logger.Logger.Infof("%v出过的牌:%v",pos,delcards) + //logger.Logger.Infof("%v剩余的牌:%v",pos,13-len(delcards)) + playedCards[pos] = delcards + numCardsLeft[pos] = 13 - len(delcards) + last := len(v.delCards) + if last > 0 { + aicards := []int32{} + for _, card := range v.delCards[last-1] { + aicards = append(aicards, tienlenApi.CardToAiCard[card]) + } + //logger.Logger.Infof("%v最后一次出牌:%v",pos,aicards) + lastmove[pos] = aicards + } else { + //logger.Logger.Infof("%v最后一次出牌:%v",pos,"") + lastmove[pos] = []int32{} + } + + } else { + numCardsLeft[pos] = 0 + } + } + isFirstHand := false + if this.lastOpPos == rule.InvalidePos { //首出玩家 + //有赢家,赢家先出,出牌不受限制 + //无赢家,手持最小牌先出,最小牌必先出 + winPos := this.FindWinPos() + if winPos == -1 { //无赢家 + isFirstHand = true + } + } + isWin := true + B := int32(0) + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if !seat.IsRob { + seat.odds = this.GetPlayerOdds(seat.Player, this.GameId, this.robotGamingNum > 0) + if seat.odds < 0 { + B -= seat.odds + } + } + } + } + if B < 0 { + isWin = false + } + isTienLenYule := this.IsTienLenYule() + pack := &tienlen.SCTienLenAIData{ + BombNum: 0, //炸弹数量 + CardPlayActionSeq: proto.String(strings.Replace(common.StringSliceToString(card_play_action_seqs, "|"), "-1", "", -1)), + LastMove_0: proto.String(common.Int32SliceToString(lastmove[0], ",")), + LastMove_1: proto.String(common.Int32SliceToString(lastmove[1], ",")), + LastMove_2: proto.String(common.Int32SliceToString(lastmove[2], ",")), + LastMove_3: proto.String(common.Int32SliceToString(lastmove[3], ",")), + NumCardsLeft_0: proto.Int32(int32(numCardsLeft[0])), + NumCardsLeft_1: proto.Int32(int32(numCardsLeft[1])), + NumCardsLeft_2: proto.Int32(int32(numCardsLeft[2])), + NumCardsLeft_3: proto.Int32(int32(numCardsLeft[3])), + OtherHandCards: proto.String(common.Int32SliceToString(othercards, ",")), + PlayedCards_0: proto.String(common.Int32SliceToString(playedCards[0], ",")), + PlayedCards_1: proto.String(common.Int32SliceToString(playedCards[1], ",")), + PlayedCards_2: proto.String(common.Int32SliceToString(playedCards[2], ",")), + PlayedCards_3: proto.String(common.Int32SliceToString(playedCards[3], ",")), + PlayerHandCards: proto.String(common.Int32SliceToString(handcards, ",")), + PlayerPosition: proto.Int32(this.currOpPos), + IsTienLenYule: proto.Bool(isTienLenYule), + IsFirstHand: proto.Bool(isFirstHand), + CardsLeft_0: this.GetPlayerCards(0), + CardsLeft_1: this.GetPlayerCards(1), + CardsLeft_2: this.GetPlayerCards(2), + CardsLeft_3: this.GetPlayerCards(3), + LastPos: proto.Int32(this.lastOpPos), + IsEnd: this.IsTienLenToEnd(), + WinSnids: this.winSnids, + IsWin: isWin, + } + proto.SetDefaults(pack) + this.seats[this.currOpPos].SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenAI), pack) + logger.Logger.Infof("--------------------Send Robot AI Data:%v", pack) + } + + pack := &tienlen.SCTienLenCurOpPos{ + Pos: proto.Int32(this.currOpPos), + IsNew: proto.Bool(this.currOpPos == this.lastOpPos || this.lastOpPos == rule.InvalidePos), + } + + lastOpPlayer := this.GetLastOpPlayer() + if lastOpPlayer != nil && this.currOpPos != this.lastOpPos { + if len(lastOpPlayer.delCards) > 0 { + lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] + for _, card := range lastDelCards { + if card != rule.InvalideCard { + pack.Cards = append(pack.Cards, card) + } + } + } + } + + if this.lastPos != rule.InvalidePos && this.lastOpPos == this.lastPos { + // 特殊牌型才去添加额外延迟时间 + if rule.NeedExDelay(pack.Cards) { + pack.ExDelay = proto.Int32(int32(rule.DelayCanOp)) + } + } + + proto.SetDefaults(pack) + this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenCurOpPos), pack, 0) + logger.Logger.Trace("(this *TienLenSceneData) BroadcastOpPos TienLenPacketID_PACKET_SCTienLenCurOpPos", this.GetSceneId(), ";pack:", pack) +} + +// 获取玩家剩余的牌 +func (this *TienLenSceneData) GetPlayerCards(pos int32) []int32 { + data := []int32{} + if len(this.allPlayerCards)-1 < int(pos) { + return data + } + + cards := this.allPlayerCards[pos] + //去除值为-1的牌 + for _, card := range cards { + if card != -1 { + data = append(data, card) + } + } + + return data +} + +// 出牌 +func (this *TienLenSceneData) DelCards(playerEx *TienLenPlayerData, cards []int32) bool { + if playerEx != nil && len(cards) != 0 { + cs := make([]int32, len(cards)) + copy(cs, cards) + for _, delCard := range cs { + for i, hcard := range playerEx.cards { + if delCard == hcard && hcard != rule.InvalideCard { + playerEx.cards[i] = rule.InvalideCard + continue + } + } + } + rule.MinSortCards(cs) + playerEx.delCards = append(playerEx.delCards, cs) + this.delCards = append(this.delCards, cs) + this.delOrders = append(this.delOrders, playerEx.SnId) + this.card_play_action_seq = append(this.card_play_action_seq, fmt.Sprintf("%v-%v", playerEx.GetPos(), common.Int32SliceToString(cs, ","))) + this.card_play_action_seq_int32 = append(this.card_play_action_seq_int32, cs) + return true + } + return false +} + +func (this *TienLenSceneData) PlayerCanOp(pos int32) bool { + if pos < 0 || pos >= int32(this.GetPlayerNum()) { + return false + } + if this.seats[pos] != nil && this.seats[pos].CanOp() { + return true + } + this.card_play_action_seq = append(this.card_play_action_seq, fmt.Sprintf("%v-过", pos)) + this.card_play_action_seq_int32 = append(this.card_play_action_seq_int32, []int32{-1}) + return false +} + +func (this *TienLenSceneData) AllPlayerEnterGame() { + + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] == nil { + continue + } + + if this.seats[i].IsMarkFlag(base.PlayerState_GameBreak) == false { + this.seats[i].UnmarkFlag(base.PlayerState_WaitNext) + this.seats[i].SyncFlag() + } + } +} + +// 娱乐版 +func (this *TienLenSceneData) IsTienLenYule() bool { + return common.IsTienLenYuLe(this.GetGameId()) +} + +// 打到底 +func (this *TienLenSceneData) IsTienLenToEnd() bool { + return common.IsTienLenToEnd(this.GetGameId()) +} +func (this *TienLenSceneData) GetFreeGameSceneType() int32 { + return int32(this.SceneType) +} + +// 比赛场发牌 +// 纯真人,随机发牌 +// 有机器人和真人,真人拿好牌 +func (this *TienLenSceneData) SendHandCard_Match() { + this.poker.Shuffle() + buf := this.poker.GetPokerBuf() + cardss := map[int][]int32{} + for i, card := range buf { + if int32(card) != rule.InvalideCard { + index := i / 13 + cardss[index] = append(cardss[index], int32(card)) + } + } + + realPlayers := []*TienLenPlayerData{} + robotPlayers := []*TienLenPlayerData{} + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat.IsRob { + robotPlayers = append(robotPlayers, seat) + } else { + realPlayers = append(realPlayers, seat) + } + } + } + if len(realPlayers) > 0 && len(robotPlayers) > 0 { + type gradeInfo struct { + id int + grade int + cards []int32 + } + grades := []gradeInfo{} + for i, card13 := range cardss { + cardTmp := make([]int32, 13) + copy(cardTmp, card13) + grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) + gi := gradeInfo{ + id: i, + grade: grade, + cards: card13, + } + grades = append(grades, gi) + } + realWin := 1 //-1最差 0正常 1最好 + switch realWin { + case -1: + sort.Slice(grades, func(i, j int) bool { + return grades[i].grade > grades[j].grade + }) + case 0: + case 1: + sort.Slice(grades, func(i, j int) bool { + return grades[i].grade < grades[j].grade + }) + } + + for i, gi := range grades { + cardss[i] = gi.cards + } + } + + minCard := int32(999) + if len(robotPlayers) > 0 { + for i, seat := range robotPlayers { + for index, card := range cardss[i] { + seat.cards[index] = card + if rule.Value(card) < rule.Value(minCard) { + this.startOpPos = int32(seat.GetPos()) + minCard = card + this.curMinCard = minCard + } else if rule.Value(card) == rule.Value(minCard) { + if rule.Color(card) < rule.Color(minCard) { + this.startOpPos = int32(seat.GetPos()) + minCard = card + this.curMinCard = minCard + } + } + } + + cardTmp := make([]int32, 13) + copy(cardTmp, cardss[i]) + grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) + logger.Logger.Trace("SnId: ", seat.SnId, ";grade: ", grade) + + pack := &tienlen.SCTienLenCard{} + for j := int32(0); j < rule.HandCardNum; j++ { + pack.Cards = append(pack.Cards, seat.cards[j]) + } + proto.SetDefaults(pack) + seat.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) + logger.Logger.Trace("SnId: ", seat.SnId, ";SCTienLenCard: ", pack.Cards) + } + } + + if len(realPlayers) > 0 { + for i, seat := range realPlayers { + for index, card := range cardss[len(robotPlayers)+i] { + seat.cards[index] = card + if rule.Value(card) < rule.Value(minCard) { + this.startOpPos = int32(seat.GetPos()) + minCard = card + this.curMinCard = minCard + } else if rule.Value(card) == rule.Value(minCard) { + if rule.Color(card) < rule.Color(minCard) { + this.startOpPos = int32(seat.GetPos()) + minCard = card + this.curMinCard = minCard + } + } + } + + cardTmp := make([]int32, 13) + copy(cardTmp, cardss[len(robotPlayers)+i]) + grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) + logger.Logger.Trace("SnId: ", seat.SnId, ";grade: ", grade) + + pack := &tienlen.SCTienLenCard{} + for j := int32(0); j < rule.HandCardNum; j++ { + pack.Cards = append(pack.Cards, seat.cards[j]) + } + proto.SetDefaults(pack) + seat.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) + logger.Logger.Trace("SnId: ", seat.SnId, ";SCTienLenCard: ", pack.Cards) + } + } +} + +// Shuffle 发牌 +// offGrade 需要牌分最少差值(非打到底牌分最大的一组牌和最小的一组牌的分差;打到底,分数最少的两组牌的分差) +// num 函数内部使用,限制搜索次数 +// maxOff 函数内部使用 +func (this *TienLenSceneData) Shuffle(offGrade, num, maxOff int) { + this.poker.Shuffle() + buf := this.poker.GetPokerBuf() + cardss := map[int][]int32{} + for i, card := range buf { + if int32(card) != rule.InvalideCard { + index := i / 13 + cardss[index] = append(cardss[index], int32(card)) + } + } + type gradeInfo struct { + id int + grade int + cards []int32 + } + grades := []gradeInfo{} + for i, card13 := range cardss { + cardTmp := make([]int32, 13) + copy(cardTmp, card13) + cardsTypeMap := rule.GetCardsType(cardTmp, this.IsTienLenYule()) + grade, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardTmp) + gi := gradeInfo{ + id: i, + grade: grade, + cards: card13, + } + grades = append(grades, gi) + } + sort.Slice(grades, func(i, j int) bool { + return grades[i].grade < grades[j].grade + }) + if grades[len(grades)-1].grade == 9999 && !this.poker.IsTianhuPoker() { //天胡 + this.poker.TianhuPokerBuf() + } + if this.IsTienLenToEnd() { + //所有值都比最小值大offGrade分 + off := grades[1].grade - grades[0].grade + if off > maxOff { + maxOff = off + this.poker.CopyPokerBuf() + } + if offGrade == 0 || off >= offGrade || num > 10000 { + if num > 10000 { + if this.poker.IsTianhuPoker() && rand.Intn(100) < 20 { + this.poker.UnTianhuPokerBuf() + } else { + this.poker.UncopyPokerBuf() + } + } + return + } + } else { + //最大值比最小值大offGrade分 + off := grades[len(grades)-1].grade - grades[0].grade + if off > maxOff && grades[len(grades)-1].grade != 9999 { + maxOff = off + this.poker.CopyPokerBuf() + } + if offGrade == 0 || off >= offGrade || num > 10000 { + if num > 10000 { + if this.poker.IsTianhuPoker() && rand.Intn(100) < 20 { + this.poker.UnTianhuPokerBuf() + } else { + this.poker.UncopyPokerBuf() + } + } + return + } + } + num++ + this.Shuffle(offGrade, num, maxOff) +} + +/* +公共房:赔率发牌 +私人房:随机发牌 +比赛场: + 纯真人,随机发牌 + 有机器人和真人,真人拿好牌 + 纯机器人,随机发牌 + +赔率发牌 +1.52张牌随机分成4组(评估牌好坏) +2.找出个人赔率最低的玩家 +3.只有一个真实玩家 + 1.该玩家个人赔率≤80%,30%概率给予玩家最好牌,70%概率随机发牌 + 2.该玩家 80%<个人赔率≤95%,随机发牌 + 3.该玩家 95%<个人赔率≤98%,50%概率给予玩家最小牌,50%概率随机发牌 + 4.该玩家 个人赔率>98%,给予玩家最小牌,切保证最大牌型和最小牌型之间的分差≥5(代码里对应50) +4.多个真实玩家 + 1.纯真人 + 50%概率给予最大牌,50%概率随机发牌 + 2.有机器人 + 1.该玩家个人赔率≤90%,50%概率给予玩家最好牌,50%概率随机发牌 + 2.该玩家 90%<个人赔率>98%,随机发牌 + 3.该玩家 个人赔率>98%,给予机器人最大牌 +5.根据赔率最低玩家发牌,如果被控人拿最小牌,机器人拿最大牌,其余玩家随机发牌 +*/ + +// SendHandCard_LoseRate 按照赔率发牌 +//func (this *TienLenSceneData) SendHandCard_LoseRate() { +// var minLoseRateSnid int32 //snid +// var realPlayerType int //-1最小牌 0正常随机 1最大牌 +// var offGrade int //需求分值差 +// +// realPlayers := []*TienLenPlayerData{} +// robotPlayers := []*TienLenPlayerData{} +// for _, seat := range this.seats { +// if seat != nil && seat.IsGameing() { +// if seat.IsRob { +// robotPlayers = append(robotPlayers, seat) +// } else { +// realPlayers = append(realPlayers, seat) +// } +// } +// } +// if len(realPlayers) > 0 { +// //找出个人赔率最低的玩家 +// minLoseRate := realPlayers[0].LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) +// minLoseRateSnid = realPlayers[0].SnId +// for _, player := range realPlayers { +// loseRate := player.LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) +// logger.Logger.Trace("snid: ", player.SnId, " loseRate: ", loseRate) +// if loseRate < minLoseRate { +// minLoseRate = loseRate +// minLoseRateSnid = player.SnId +// } +// } +// if !this.IsMatchScene() && !this.IsPrivateScene() { //比赛场或者私有房间纯随机 +// //一个真实玩家 +// if len(realPlayers) == 1 { +// //1.该玩家个人赔率≤80%,30%概率给予玩家最好牌,70%概率随机发牌 +// if minLoseRate <= 0.8 { +// if rand.Int31n(100) < 30 { +// realPlayerType = 1 +// } else { +// realPlayerType = 0 +// } +// } else if minLoseRate <= 0.95 { //2.该玩家 80%<个人赔率≤95%,随机发牌 +// realPlayerType = 0 +// } else if minLoseRate <= 0.98 { //3.该玩家 95%<个人赔率≤98%,50%概率给予玩家最小牌,50%概率随机发牌 +// if rand.Int31n(100) < 50 { +// realPlayerType = -1 +// } else { +// realPlayerType = 0 +// } +// } else { //3.该玩家 个人赔率>98%,给予玩家最小牌,切保证最大牌型和最小牌型之间的分差≥5(代码里对应50) +// realPlayerType = -1 +// offGrade = 40 +// } +// } else { //多个真人 +// if len(robotPlayers) == 0 { //纯真人 +// if this.IsPrivateScene() { //私有房间纯随机 +// realPlayerType = 0 +// } else { +// if rand.Int31n(100) < 50 { //50%概率给予最大牌,50%概率随机发牌 +// realPlayerType = 1 +// } else { +// realPlayerType = 0 +// } +// } +// } else { +// //1.该玩家个人赔率≤90%,50%概率给予玩家最好牌,50%概率随机发牌 +// if minLoseRate <= 0.9 { +// if rand.Int31n(100) < 50 { +// realPlayerType = 1 +// } else { +// realPlayerType = 0 +// } +// } else if minLoseRate < 0.98 { //2.该玩家 90%<个人赔率>98%,随机发牌 +// realPlayerType = 0 +// } else { //3.该玩家 个人赔率>98%,给予机器人最大牌 +// realPlayerType = -1 +// } +// } +// } +// } +// } +// +// this.Shuffle(offGrade, 0, 0) +// +// buf := this.poker.GetPokerBuf() +// cardss := map[int][]int32{} +// for i, card := range buf { +// if int32(card) != rule.InvalideCard { +// index := i / 13 +// cardss[index] = append(cardss[index], int32(card)) +// } +// } +// +// type gradeInfo struct { +// id int +// grade int +// cards []int32 +// } +// grades := []gradeInfo{} +// for i, card13 := range cardss { +// cardTmp := make([]int32, 13) +// copy(cardTmp, card13) +// grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) +// gi := gradeInfo{ +// id: i, +// grade: grade, +// cards: card13, +// } +// grades = append(grades, gi) +// } +// if realPlayerType != 0 { +// sort.Slice(grades, func(i, j int) bool { +// return grades[i].grade > grades[j].grade +// }) +// } +// +// for i, gi := range grades { +// cardss[i] = gi.cards +// } +// +// Grades := make(map[int32]int) +// minCard := int32(999) +// // 被控人 +// for snid, player := range this.players { +// if player.IsGameing() && snid == minLoseRateSnid { +// cards := cardss[0] //最大 +// if realPlayerType == -1 { +// cards = cardss[len(cardss)-1] //最小 +// delete(cardss, len(cardss)-1) +// } else { +// delete(cardss, 0) +// } +// switch realPlayerType { +// case -1: +// logger.Logger.Trace("player get min grade !") +// case 0: +// logger.Logger.Trace("player get rand grade !") +// case 1: +// logger.Logger.Trace("player get max grade !") +// } +// for index, card := range cards { +// player.cards[index] = card +// if rule.Value(card) < rule.Value(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } else if rule.Value(card) == rule.Value(minCard) { +// if rule.Color(card) < rule.Color(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } +// } +// } +// +// cardTmp := make([]int32, 13) +// copy(cardTmp, cards) +// grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) +// logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) +// Grades[player.SnId] = grade +// +// pack := &tienlen.SCTienLenCard{} +// for j := int32(0); j < rule.HandCardNum; j++ { +// pack.Cards = append(pack.Cards, player.cards[j]) +// } +// proto.SetDefaults(pack) +// player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) +// logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) +// break +// } +// } +// if realPlayerType == -1 { //被控人拿最小牌,机器人拿最大牌 +// snids := []int32{} +// for snid, player := range this.players { +// if player.IsRob && player.IsGameing() && snid != minLoseRateSnid && player.cards[0] == rule.InvalideCard { +// snids = append(snids, snid) +// } +// } +// if len(snids) > 0 { +// snid := snids[rand.Intn(len(snids))] +// player := this.players[snid] +// if player != nil { +// cards := cardss[0] //最大 +// delete(cardss, 0) +// for index, card := range cards { +// player.cards[index] = card +// if rule.Value(card) < rule.Value(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } else if rule.Value(card) == rule.Value(minCard) { +// if rule.Color(card) < rule.Color(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } +// } +// } +// +// cardTmp := make([]int32, 13) +// copy(cardTmp, cards) +// grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) +// logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) +// Grades[player.SnId] = grade +// +// pack := &tienlen.SCTienLenCard{} +// for j := int32(0); j < rule.HandCardNum; j++ { +// pack.Cards = append(pack.Cards, player.cards[j]) +// } +// proto.SetDefaults(pack) +// player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) +// logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) +// } +// } +// } +// for _, cards := range cardss { +// for snid, player := range this.players { +// if player.IsGameing() && snid != minLoseRateSnid && player.cards[0] == rule.InvalideCard { +// for index, card := range cards { +// player.cards[index] = card +// if rule.Value(card) < rule.Value(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } else if rule.Value(card) == rule.Value(minCard) { +// if rule.Color(card) < rule.Color(minCard) { +// this.startOpPos = int32(player.GetPos()) +// minCard = card +// this.curMinCard = minCard +// } +// } +// } +// +// cardTmp := make([]int32, 13) +// copy(cardTmp, cards) +// grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) +// logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) +// Grades[player.SnId] = grade +// +// pack := &tienlen.SCTienLenCard{} +// for j := int32(0); j < rule.HandCardNum; j++ { +// pack.Cards = append(pack.Cards, player.cards[j]) +// } +// proto.SetDefaults(pack) +// player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) +// logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) +// break +// } +// } +// } +// +// // 测试数据 +// for snid, player := range this.players { +// if player.IsGameing() && !player.IsRob { +// pack := &tienlen.SCTienLenCardTest{} +// if snid == minLoseRateSnid { +// switch realPlayerType { +// case -1: +// pack.Type = 1 +// case 0: +// pack.Type = 2 +// case 1: +// pack.Type = 3 +// } +// } +// pack.Totalout, pack.Totalin = player.TotalOutIn(int32(this.GetGameId())) +// pack.LoseRate = player.LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) +// if Grades != nil { +// pack.Grades = make(map[int32]int32) +// for id, grade := range Grades { +// pack.Grades[id] = int32(grade) +// } +// } +// proto.SetDefaults(pack) +// player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCardTest), pack) +// logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCardTest: ", pack) +// } +// } +//} + +// SendHandCardOdds 调控发牌 +/* +1.计算玩家调控概率 + 新手天胡体验发牌 end +2.根据玩家调控概率随机获得需要控输或控赢的玩家 + 1.有控输玩家 + 1.机器人发天胡 end + 2.分差发牌,机器人发好牌 end + 2.有控赢,给控赢的玩家发好牌,其余随机发牌 end +*/ +func (this *TienLenSceneData) SendHandCardOdds() { + var realPlayersGood, realPlayersBad, realPlayers, robotPlayers, novicePlayers, notNoviceRealPlayers []*TienLenPlayerData + var G, B int32 + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat.IsRob { + robotPlayers = append(robotPlayers, seat) + } else { + seat.odds = this.GetPlayerOdds(seat.Player, this.GameId, this.robotGamingNum > 0) + if seat.odds > 0 { + realPlayersGood = append(realPlayersGood, seat) + G += seat.odds + } else if seat.odds < 0 { + realPlayersBad = append(realPlayersBad, seat) + B -= seat.odds + } else { + realPlayers = append(realPlayers, seat) + } + _, isNovice := seat.NoviceOdds(this.GameId) + if isNovice { + novicePlayers = append(novicePlayers, seat) + } else { + notNoviceRealPlayers = append(notNoviceRealPlayers, seat) + } + } + } + } + + var Grades = make(map[int32]int32) + var minCard = int32(999) + f1 := func(p *TienLenPlayerData, cards []int32) { // 发牌方法 + cardsTypeMap := rule.GetCardsType(cards, this.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cards) + Grades[p.SnId] = int32(score) // 测试用 + for index, card := range cards { + p.cards[index] = card + if rule.Value(card) < rule.Value(minCard) { + this.startOpPos = int32(p.GetPos()) + minCard = card + this.curMinCard = minCard + } else if rule.Value(card) == rule.Value(minCard) { + if rule.Color(card) < rule.Color(minCard) { + this.startOpPos = int32(p.GetPos()) + minCard = card + this.curMinCard = minCard + } + } + } + pack := &tienlen.SCTienLenCard{} + for j := int32(0); j < rule.HandCardNum; j++ { + pack.Cards = append(pack.Cards, p.cards[j]) + } + proto.SetDefaults(pack) + p.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) + logger.Logger.Trace("SnId: ", p.SnId, ";SCTienLenCard: ", pack) + } + + // 新手天胡体验 + // 前两局不能天胡,之后根据概率给一次天胡,至少天胡一次 + noviceTianHu := false + noviceCtrl := false + config := srvdata.PBDB_NewPlayerMgr.GetData(int32(this.GameId)) + if config != nil && config.GetTianHuRate() > 0 && len(this.testPokers) == 0 { // 启用新手天胡体验 + rand.Shuffle(len(novicePlayers), func(i, j int) { + novicePlayers[i], novicePlayers[j] = novicePlayers[j], novicePlayers[i] + }) + keyNovice := common.GetKeyNoviceGameId(this.GameId) + for _, v := range novicePlayers { + data, ok := v.GDatas[keyNovice] + if !ok { + data = &model.PlayerGameInfo{FirstTime: time.Now()} + v.GDatas[keyNovice] = data + } + // 前两局不能天胡 + if data.Statics.GameTimes < 2 { + continue + } + if int(config.GetTianHuRate()) > this.RandInt(1000) { + var allcs []int32 + for i := 0; i < 52; i++ { + allcs = append(allcs, int32(i)) + } + rand.Shuffle(len(allcs), func(i, j int) { + allcs[i], allcs[j] = allcs[j], allcs[i] + }) + cs, _ := rule.GetTianHu() + allcs = common.DelSliceIn32s(allcs, cs) + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat != v { + f1(seat, allcs[:13]) + allcs = allcs[13:] + } else { + f1(seat, cs) + } + } + } + v.TestLog = append(v.TestLog, fmt.Sprintf("新手天胡体验,玩家:%d 发天胡 num:%v", v.SnId, data.Statics.GameTimes)) + logger.Logger.Tracef("新手天胡体验,玩家:%d 发天胡", v.SnId) + noviceTianHu = true + noviceCtrl = true + } + } + if !noviceTianHu { + // 没有新手玩家天胡,随机一个非新手玩家,如果没有天胡过,给一次天胡 + rand.Shuffle(len(notNoviceRealPlayers), func(i, j int) { + notNoviceRealPlayers[i], notNoviceRealPlayers[j] = notNoviceRealPlayers[j], notNoviceRealPlayers[i] + }) + for _, v := range notNoviceRealPlayers { + data, ok := v.GDatas[keyNovice] + if !ok { + data = &model.PlayerGameInfo{FirstTime: time.Now()} + v.GDatas[keyNovice] = data + } + + if data.Statics.GetInt(rule.StaticsTianHuTimes) == 0 { + var allcs []int32 + for i := 0; i < 52; i++ { + allcs = append(allcs, int32(i)) + } + rand.Shuffle(len(allcs), func(i, j int) { + allcs[i], allcs[j] = allcs[j], allcs[i] + }) + cs, _ := rule.GetTianHu() + allcs = common.DelSliceIn32s(allcs, cs) + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat != v { + f1(seat, allcs[:13]) + allcs = allcs[13:] + } else { + f1(seat, cs) + } + } + } + v.TestLog = append(v.TestLog, fmt.Sprintf("新手阶段没有天胡过,玩家:%d 发天胡 num:%v", v.SnId, data.Statics.GameTimes)) + logger.Logger.Tracef("新手阶段没有天胡过,玩家:%v 发天胡", v.SnId) + noviceCtrl = true + } + } + } + } + + gameConfig := base.PlatformConfigSingleton.Get(this.Platform).GameConfig + + // testPokers 用于测试 + isTestPoker := false + if len(this.testPokers) > 1 { + var allcs []int32 + for i := 0; i < 52; i++ { + allcs = append(allcs, int32(i)) + } + rand.Shuffle(len(allcs), func(i, j int) { + allcs[i], allcs[j] = allcs[j], allcs[i] + }) + + if len(this.testPokers) > 14 { + this.testPokers = this.testPokers[:14] + } + if len(this.testPokers) < 14 { + allcs = common.DelSliceIn32s(allcs, this.testPokers[1:]) + this.testPokers = append(this.testPokers, allcs[:14-len(this.testPokers)]...) + allcs = allcs[14-len(this.testPokers):] + } + + allcs = common.DelSliceIn32s(allcs, this.testPokers[1:]) + for _, seat := range this.seats { + if seat != nil && seat.IsGameing() { + if seat.SnId == this.testPokers[0] { + f1(seat, this.testPokers[1:]) + } else { + f1(seat, allcs[:13]) + allcs = allcs[13:] + } + } + } + this.testPokers = nil + isTestPoker = true + } + // testPokers + + // 需要换牌 + isGood := len(realPlayersGood) > 0 && int32(this.RandInt(1000)) < G + isBad := len(realPlayersBad) > 0 && int32(this.RandInt(1000)) < B + logger.Logger.Tracef("TienLen SendHandCardOdds Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B) + + if isBad && !isTestPoker && len(robotPlayers) > 0 && !noviceCtrl && !noviceTianHu { + gf := base.PlatformConfigSingleton.Get(this.Platform).GameConfig + items := []int32{gf.GetTianHu(), gf.GetPaiKu(), gf.GetFenCha()} + score := this.RandInt(int(gf.GetTianHu() + gf.GetPaiKu() + gf.GetFenCha())) + sum := int32(0) + tp := 0 + for k, v := range items { + sum += v + if int32(score) < sum { + tp = k + break + } + } + logger.Logger.Tracef("TienLen SendHandCardOdds TianHu:%v PaiKu:%v FenCha:%v, tp:%v", items[0], items[1], items[2], tp) + switch tp { + case 0: // 天胡发牌 + logger.Logger.Tracef("TienLen SendHandCardOdds TianHuRate") + p := robotPlayers[0] + th, _ := rule.GetTianHu() + if len(th) == rule.Hand_CardNum { + // 机器人发天胡,其它玩家随机发牌 + f1(p, th) + var allCards []int32 + for i := 0; i < rule.POKER_CNT; i++ { + allCards = append(allCards, int32(i)) + } + allCards = common.DelSliceIn32s(allCards, th) + rand.Shuffle(len(allCards), func(i, j int) { + allCards[i], allCards[j] = allCards[j], allCards[i] + }) + for _, v := range this.players { // map随机 + if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { + continue + } + f1(v, allCards[:rule.Hand_CardNum]) + allCards = allCards[rule.Hand_CardNum:] + } + } + p.TestLog = append(p.TestLog, fmt.Sprintf("天胡发牌 snid%v 权重%v", p.SnId, gameConfig.GetTianHu())) + + case 1: // 牌库发牌 + logger.Logger.Tracef("TienLen SendHandCardOdds 牌库") + //todo 牌库 + random := rand.Intn(10000) + var cardsArr [][]int32 + this.isCardsKu = true + this.cardsKuId = int32(random) + if this.IsTienLenYule() { + cardsPool := srvdata.PBDB_CardsYuLeMgr.GetData(int32(random)) + logger.Logger.Tracef("娱乐牌库发牌!!!!!! 随机到的牌库id = %d\n,db_CardsYuLe = %v\n", cardsPool.Id, cardsPool) + var numbers []int32 + err := json.Unmarshal([]byte(cardsPool.Card1), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card2), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card3), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card4), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + //排序 + sort.Slice(cardsArr, func(i, j int) bool { + cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) + score1, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) + return score1 > score2 + }) + if isGood { + sort.Slice(realPlayersGood, func(i, j int) bool { + return realPlayersGood[i].odds > realPlayersGood[j].odds + }) + for _, v := range realPlayersGood { + f1(v, cardsArr[0]) + cardsArr = cardsArr[1:] + } + } + if isBad { + sort.Slice(realPlayersBad, func(i, j int) bool { + return realPlayersBad[i].odds < realPlayersBad[j].odds + }) + for _, v := range realPlayersBad { + f1(v, cardsArr[len(cardsArr)-1]) + cardsArr = cardsArr[:len(cardsArr)-1] + } + } + //机器人发牌和不调控的人 + for _, v := range append(robotPlayers, realPlayers...) { + f1(v, cardsArr[0]) + cardsArr = cardsArr[1:] + } + } else { + cardsPool := srvdata.PBDB_CardsJDMgr.GetData(int32(random)) + logger.Logger.Tracef("经典牌库发牌!!!!!! 随机到的牌库id = %d\n,db_CardsYuLe = %v\n", cardsPool.Id, cardsPool) + var numbers []int32 + err := json.Unmarshal([]byte(cardsPool.Card1), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card2), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card3), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + numbers = []int32{} + err = json.Unmarshal([]byte(cardsPool.Card4), &numbers) + if err != nil { + fmt.Println("JSON unmarshaling failed:", err) + return + } + cardsArr = append(cardsArr, numbers) + //排序 + sort.Slice(cardsArr, func(i, j int) bool { + cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) + score1, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) + return score1 > score2 + }) + if isGood { + sort.Slice(realPlayersGood, func(i, j int) bool { + return realPlayersGood[i].odds > realPlayersGood[j].odds + }) + for _, v := range realPlayersGood { + f1(v, cardsArr[0]) + cardsArr = cardsArr[1:] + } + } + if isBad { + sort.Slice(realPlayersBad, func(i, j int) bool { + return realPlayersBad[i].odds < realPlayersBad[j].odds + }) + for _, v := range realPlayersBad { + f1(v, cardsArr[len(cardsArr)-1]) + cardsArr = cardsArr[:len(cardsArr)-1] + } + } + //机器人发牌和不调控的人 + for _, v := range append(robotPlayers, realPlayers...) { + f1(v, cardsArr[0]) + cardsArr = cardsArr[1:] + } + } + case 2: // 分差发牌 + logger.Logger.Tracef("TienLen SendHandCardOdds 分差发牌") + for i := 0; i <= 20; i++ { + allCards := rand.Perm(rule.POKER_CNT) + var cardsArr [][]int32 + for i := 0; i < 4; i++ { + cardsArr = append(cardsArr, common.CopySliceIntToInt32(allCards[:13])) + allCards = allCards[13:] + } + sort.Slice(cardsArr, func(i, j int) bool { + cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) + return score > score2 + }) + if len(cardsArr) > 0 { + cardsTypeMap := rule.GetCardsType(cardsArr[0], this.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[0]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[len(cardsArr)-1], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[len(cardsArr)-1]) + if score-score2 > int(gameConfig.GetFenChaScore()) { + logger.Logger.Tracef("分差发牌,分差:%v", score-score2) + for _, v := range robotPlayers { + if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { + continue + } + f1(v, cardsArr[0]) + cardsArr = cardsArr[1:] + } + for _, v := range this.players { // map随机 + if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { + continue + } + f1(v, cardsArr[len(cardsArr)-1]) + cardsArr = cardsArr[:len(cardsArr)-1] + v.TestLog = append(v.TestLog, fmt.Sprintf("分差发牌 snid%v 权重%v 需要分差%v 真实分差%v", v.SnId, + gameConfig.GetFenCha(), gameConfig.GetFenChaScore(), score-score2)) + } + break + } + } + } + } + } + + this.Shuffle(0, 0, 0) + buf := this.poker.GetPokerBuf() + cardsArr := make([][]int32, 4) + for i, card := range buf { + if int32(card) != rule.InvalideCard { + index := i / 13 + cardsArr[index] = append(cardsArr[index], int32(card)) + } + } + f2 := func(players *[]*TienLenPlayerData) { + if players == nil || len(*players) == 0 { + return + } + var p *TienLenPlayerData // 发牌给这个玩家 + var a int + for _, v := range *players { + a += int(math.Abs(float64(v.odds))) + } + n := this.RandInt(a) + total := 0 + for k, v := range *players { + total += int(math.Abs(float64(v.odds))) + if n < total { + p = (*players)[k] + *players = append((*players)[:k], (*players)[k+1:]...) + break + } + } + + var cards []int32 + if p.odds > 0 { + // 拿好牌 + cards = cardsArr[0] + cardsArr = cardsArr[1:] + } else { + // 拿坏牌 + cards = cardsArr[len(cardsArr)-1] + cardsArr = cardsArr[:len(cardsArr)-1] + } + if p.cards[0] == rule.InvalideCard { + f1(p, cards) + } + } + + if !isBad && !isTestPoker && !noviceCtrl && !noviceTianHu { // 天胡调控没有生效 + if isGood || isBad { + // 牌平分,按从大到小排序 + // 使用分差配置,最好牌和最差牌的牌型分差大于分差配置 + for i := 0; i < 20; i++ { // 尝试20次,如果还不能满足分差配置,则直接发牌 + cardsArr = cardsArr[:0] + allCards := rand.Perm(rule.POKER_CNT) + for i := 0; i < 4; i++ { + cardsArr = append(cardsArr, common.CopySliceIntToInt32(allCards[:13])) + allCards = allCards[13:] + } + sort.Slice(cardsArr, func(i, j int) bool { + cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) + return score > score2 + }) + if len(cardsArr) > 0 { + cardsTypeMap := rule.GetCardsType(cardsArr[0], this.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[0]) + cardsTypeMap2 := rule.GetCardsType(cardsArr[len(cardsArr)-1], this.IsTienLenYule()) + score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[len(cardsArr)-1]) + if score-score2 > int(gameConfig.GetGoodFenCha()) { + break + } + } + } + // 排序 + type gradeInfo struct { + grade int + cards []int32 + } + var grades []gradeInfo + for _, card13 := range cardsArr { + cardTmp := make([]int32, 13) + copy(cardTmp, card13) + cardsTypeMap := rule.GetCardsType(cardTmp, this.IsTienLenYule()) + grade, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardTmp) + gi := gradeInfo{ + grade: grade, + cards: card13, + } + grades = append(grades, gi) + } + sort.Slice(grades, func(i, j int) bool { + return grades[i].grade > grades[j].grade + }) + for i, gi := range grades { + cardsArr[i] = gi.cards + } + // 发好牌 + if isGood { + l := len(realPlayersGood) + for i := 0; i < l; i++ { + f2(&realPlayersGood) + } + } + // 发坏牌 + if isBad { + l := len(realPlayersBad) + for i := 0; i < l; i++ { + f2(&realPlayersBad) + } + } + } + } + + // 随机拿牌 + for _, v := range this.players { // map随机 + if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { + continue + } + f1(v, cardsArr[len(cardsArr)-1]) + cardsArr = cardsArr[:len(cardsArr)-1] + } + + // 测试 + for _, player := range this.players { + if player.IsGameing() && !player.IsRob { + pack := &tienlen.SCTienLenCardTest{} + pack.Totalout, pack.Totalin = player.TotalOutIn(int32(this.GetGameId())) + pack.LoseRate = player.LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) + if Grades != nil { + pack.Grades = make(map[int32]int32) + for id, grade := range Grades { + pack.Grades[id] = int32(grade) + } + } + logger.Logger.Trace("snid:", player.SnId) + player.TestLog = append(player.TestLog, fmt.Sprintf("随机换牌 Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B)) + pack.Data = strings.Join(player.TestLog, "\n") + proto.SetDefaults(pack) + player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCardTest), pack) + //logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCardTest: ", pack) + for _, v := range player.TestLog { + logger.Logger.Trace(v) + } + } + } +} + +func (this *TienLenSceneData) SendHandCardTest() { + this.poker.Shuffle() + buf := this.poker.GetPokerBuf() + //牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 + //红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 + //方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 + //梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 + //黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + test1 := []int32{35, 34, 33, 32, 31, 30, 0} + test2 := []int32{42, + 29, + 16, + 3} + test3 := []int32{} + test4 := []int32{} + + need1 := rule.HandCardNum - int32(len(test1)) + need2 := rule.HandCardNum - int32(len(test2)) + need3 := rule.HandCardNum - int32(len(test3)) + need4 := rule.HandCardNum - int32(len(test4)) + + tmpBuf := []int32{} + + for i, card := range buf { + for _, card1 := range test1 { + if int32(card) == card1 { + buf[i] = rule.Card(rule.InvalideCard) + } + } + for _, card1 := range test2 { + if int32(card) == card1 { + buf[i] = rule.Card(rule.InvalideCard) + } + } + for _, card1 := range test3 { + if int32(card) == card1 { + buf[i] = rule.Card(rule.InvalideCard) + } + } + for _, card1 := range test4 { + if int32(card) == card1 { + buf[i] = rule.Card(rule.InvalideCard) + } + } + } + + for _, card := range buf { + if int32(card) != rule.InvalideCard { + tmpBuf = append(tmpBuf, int32(card)) + } + } + + var n int + minCard := int32(999) + for _, seatPlayerEx := range this.seats { + if seatPlayerEx != nil { + bb := []int32{} + for i := n * rule.Hand_CardNum; i < (n+1)*rule.Hand_CardNum; i++ { + bb = append(bb, int32(buf[i])) + } + bb = []int32{} + switch seatPlayerEx.GetPos() { + case 0: + for _, card := range test1 { + bb = append(bb, card) + } + for i := int32(0); i < need1; i++ { + bb = append(bb, tmpBuf[i]) + } + tmpBuf = append(tmpBuf[need1:]) + case 1: + for _, card := range test2 { + bb = append(bb, card) + } + for i := int32(0); i < need2; i++ { + bb = append(bb, tmpBuf[i]) + } + tmpBuf = append(tmpBuf[need2:]) + case 2: + for _, card := range test3 { + bb = append(bb, card) + } + for i := int32(0); i < need3; i++ { + bb = append(bb, tmpBuf[i]) + } + tmpBuf = append(tmpBuf[need3:]) + case 3: + for _, card := range test4 { + bb = append(bb, card) + } + for i := int32(0); i < need4; i++ { + bb = append(bb, tmpBuf[i]) + } + tmpBuf = append(tmpBuf[need4:]) + } + //排下序,正常应该客户端排序 + sort.Slice(bb, func(i, j int) bool { + v_i := rule.Value(int32(bb[i])) + v_j := rule.Value(int32(bb[j])) + c_i := rule.Color(int32(bb[i])) + c_j := rule.Color(int32(bb[j])) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + for idx, card := range bb { + seatPlayerEx.cards[idx] = int32(card) + if rule.Value(int32(card)) < rule.Value(minCard) { + this.startOpPos = int32(seatPlayerEx.GetPos()) + minCard = int32(card) + this.curMinCard = minCard + } else if rule.Value(int32(card)) == rule.Value(minCard) { + if rule.Color(int32(card)) < rule.Color(minCard) { + this.startOpPos = int32(seatPlayerEx.GetPos()) + minCard = int32(card) + this.curMinCard = minCard + } + } + } + pack := &tienlen.SCTienLenCard{} + for j := int32(0); j < rule.HandCardNum; j++ { + pack.Cards = append(pack.Cards, int32(seatPlayerEx.cards[j])) + } + proto.SetDefaults(pack) + seatPlayerEx.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) + logger.Logger.Trace("player_id", seatPlayerEx.SnId, ";SCTienLenCard", pack.Cards) + n++ + } + } +} + +func (this *TienLenSceneData) SetCurOpPos(pos int32) { + this.currOpPos = pos +} +func (this *TienLenSceneData) GetCurOpPos() int32 { + return this.currOpPos +} +func (this *TienLenSceneData) GetCurOpPlayer() *TienLenPlayerData { + if this.currOpPos < 0 || this.currOpPos >= int32(this.GetPlayerNum()) { + return nil + } + return this.seats[this.currOpPos] +} +func (this *TienLenSceneData) SetLastOpPos(pos int32) { + this.lastOpPos = pos + + //this.RefreshPlayerHandLimitTimeOut() +} +func (this *TienLenSceneData) GetLastOpPos() int32 { + return this.lastOpPos +} +func (this *TienLenSceneData) GetLastOpPlayer() *TienLenPlayerData { + if this.lastOpPos < 0 || this.lastOpPos >= int32(this.GetPlayerNum()) { + return nil + } + return this.seats[this.lastOpPos] +} + +func (this *TienLenSceneData) GetLastBombPlayer() *TienLenPlayerData { + if this.lastBombPos < 0 || this.lastBombPos >= int32(this.GetPlayerNum()) { + return nil + } + return this.seats[this.lastBombPos] +} +func (this *TienLenSceneData) GetCurBombPlayer() *TienLenPlayerData { + if this.curBombPos < 0 || this.curBombPos >= int32(this.GetPlayerNum()) { + return nil + } + return this.seats[this.curBombPos] +} + +// 逆时针找一个空位 +func (this *TienLenSceneData) FindOnePos() int { + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] == nil { + return i + } + } + return int(rule.InvalidePos) +} + +// 刷新玩家出牌时间 +func (this *TienLenSceneData) RefreshPlayerHandLimitTimeOut() { + CurPlayer := this.GetCurOpPlayer() + if CurPlayer == nil { + return + } + + if CurPlayer.IsRobot() { + return + } + + //curCpCards := []int32{} + //for _, card := range CurPlayer.cards { + // if card != rule.InvalideCard { + // curCpCards = append(curCpCards, card) + // } + //} + + lastOpPlayer := this.GetLastOpPlayer() + if lastOpPlayer != nil && len(lastOpPlayer.delCards) != 0 { + // lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] + //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " lastDelCards", lastDelCards) + + //recmCards := rule.RecommendCardsWithLastCards(lastDelCards, curCpCards) + //if this.IsTienLenYule() { + // recmCards = rule.RecommendCardsWithLastCards_yl(lastDelCards, curCpCards) + //} + + //canDel, _, _ := rule.CanDel(lastDelCards, recmCards, this.IsTienLenToEnd()) + //if this.IsTienLenYule() { + // canDel, _, _ = rule.CanDel_yl(lastDelCards, recmCards, this.IsTienLenToEnd()) + //} + + //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " ThinkLongCnt:", lastOpPlayer.ThinkLongCnt, " flag:", lastOpPlayer.GetFlag()) + + if int32(CurPlayer.GetPos()) != this.lastOpPos { + //if !canDel { // 压不住别人 + // //CurPlayer.curHandLimitTimeOut = rule.TienLenHandNotExceedTimeLimit + // //CurPlayer.isNotOverLastHand = true + // //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---压不住") + //} else { + // //CurPlayer.RefreshCurHandLimitTimeOut() + // //CurPlayer.isNotOverLastHand = false + // //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---可以压住") + //} + + CurPlayer.RefreshCurHandLimitTimeOut() + CurPlayer.isNotOverLastHand = false + } else { + CurPlayer.RefreshCurHandLimitTimeOut() + CurPlayer.isNotOverLastHand = false + //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---本轮自己首出牌") + } + + //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer lastOpPos: ", this.lastOpPos, " currOpPos: ", CurPlayer.GetPos()) + + } +} + +func (this *TienLenSceneData) DoNext(pos int32) int32 { + nextPos := this.GetNextOpPos(pos) + if nextPos != rule.InvalidePos { + this.SetCurOpPos(nextPos) + this.StateStartTime = time.Now() + } + this.lastPos = pos + + //this.RefreshPlayerHandLimitTimeOut() + //logger.Logger.Trace("(this *TienLenSceneData) DoNext pos: ", pos, " nextPos:", nextPos, " StateStartTime:", this.StateStartTime) + this.cHintCards = []int32{} + return nextPos +} + +func (this *TienLenSceneData) GetNextOpPos(pos int32) int32 { + if pos == rule.InvalidePos { + return rule.InvalidePos + } + if pos < 0 || pos >= int32(this.GetPlayerNum()) { + return rule.InvalidePos + } + + for i := pos + 1; i < int32(this.GetPlayerNum()); i++ { + if this.PlayerCanOp(i) { + return i + } + } + for i := int32(0); i < pos; i++ { + if this.PlayerCanOp(i) { + return i + } + } + + return rule.InvalidePos +} + +func (this *TienLenSceneData) UnmarkPass() { + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] != nil { + this.seats[i].isPass = false + } + } +} + +func (this *TienLenSceneData) FindWinPos() int { + winPos := -1 + if this.lastGamingPlayerNum != 0 && this.curGamingPlayerNum != 0 && this.lastWinSnid != 0 { + haveLastWinPos := -1 + for i := 0; i < this.GetPlayerNum(); i++ { + if this.seats[i] != nil { + if this.seats[i].SnId == this.lastWinSnid { + haveLastWinPos = i + break + } + } + } + if haveLastWinPos != -1 { + if this.lastGamingPlayerNum > 2 { + winPos = haveLastWinPos + } else if this.lastGamingPlayerNum == 2 { + if this.curGamingPlayerNum == 2 { + winPos = haveLastWinPos + } + } + } + } + return winPos +} + +func (this *TienLenSceneData) TrySmallGameBilled() { + // 看是不是炸弹,是炸弹结算分 + if this.isKongBomb { + this.bombToEnd++ + this.isKongBomb = false + } + if this.roundScore > 0 && this.curBombPos != rule.InvalidePos && this.lastBombPos != rule.InvalidePos { + winPlayer := this.GetCurBombPlayer() + losePlayer := this.GetLastBombPlayer() + baseScore := this.GetBaseScore() + var rankScore = int64(this.roundScore) + score := int64(this.roundScore) * int64(baseScore) + if this.IsTienLenToEnd() { + score = int64(this.roundScore) * int64(baseScore) / 100 //百分比 + } + losePlayerCoin := losePlayer.GetCoin() + if !this.IsMatchScene() && losePlayerCoin < score { //输完 + score = losePlayerCoin + } + if score != 0 { + taxRate := this.DbGameFree.GetTaxRate() //万分比 + gainScore := int64(float64(score) * float64(10000-taxRate) / 10000.0) //税后 + bombTaxScore := score - gainScore + // win + if this.IsMatchScene() { + winPlayer.AddCoinNoLog(gainScore, 0) + } else { + winPlayer.AddCoin(gainScore, common.GainWay_CoinSceneWin, 0, "system", this.GetSceneName()) + } + winPlayer.winCoin += gainScore + winPlayer.bombScore += gainScore + winPlayer.bombTaxScore += bombTaxScore + if this.IsTienLenToEnd() { + winPlayer.bombRankScore += int64(float64(rankScore) / 100.0 * float64(rule.RankBaseScoreToEnd)) + } else { + winPlayer.bombRankScore += rankScore * rule.RankBaseScore + } + //lose + if this.IsMatchScene() { + losePlayer.AddCoinNoLog(-score, 0) + } else { + losePlayer.AddCoin(-score, common.GainWay_CoinSceneLost, 0, "system", this.GetSceneName()) + } + losePlayer.winCoin -= score + losePlayer.bombScore -= score + if this.IsTienLenToEnd() { + losePlayer.bombRankScore -= int64(float64(rankScore) / 100.0 * float64(rule.RankBaseScoreToEnd)) + } else { + losePlayer.bombRankScore -= rankScore * rule.RankBaseScore + } + + pack := &tienlen.SCTienLenSmallGameBilled{ + WinPos: proto.Int(winPlayer.GetPos()), + WinPosCoin: proto.Int64(winPlayer.GetCoin()), + WinCoin: proto.Int64(gainScore), + LosePos: proto.Int(losePlayer.GetPos()), + LosePosCoin: proto.Int64(losePlayer.GetCoin()), + LoseCoin: proto.Int64(score), + } + proto.SetDefaults(pack) + this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenSmallGameBilled), pack, 0) + logger.Logger.Trace("SCTienLenSmallGameBilled: ", pack) + } + } + this.curBombPos = rule.InvalidePos + this.lastBombPos = rule.InvalidePos + this.roundScore = 0 + this.UnmarkPass() +} + +func (this *TienLenSceneData) IsTianhuPlayer(snid int32) bool { + for _, tianhusnid := range this.tianHuSnids { + if snid == tianhusnid { + return true + } + } + return false +} + +func (this *TienLenSceneData) IsWinPlayer(snid int32) bool { + for _, winSnid := range this.winSnids { + if snid == winSnid { + return true + } + } + return false +} + +// SystemCoinOut 系统投入产出都要扣税 +func (this *TienLenSceneData) SystemCoinOut() int64 { + systemGain := int64(0) + taxRate := this.GetDBGameFree().GetTaxRate() + for i := 0; i < this.GetPlayerNum(); i++ { + playerData := this.seats[i] + if playerData != nil && playerData.IsGameing() && playerData.IsRob { + gain := playerData.winCoin - playerData.bombScore + if playerData.bombScore > 0 && gain > 0 { + // 小结算赢,局结算都赢 + systemGain += playerData.winCoin + } else if playerData.bombScore > 0 && gain < 0 { + // 小结算赢,局结算输 + systemGain += playerData.bombScore - int64(float64(gain)*float64(10000-taxRate)/10000.0) + } else if playerData.bombScore < 0 && gain > 0 { + // 小结算输,局结算赢 + systemGain += int64(float64(playerData.bombScore)*float64(10000-taxRate)/10000.0) + gain + } else if playerData.bombScore < 0 && gain < 0 { + // 小结算输,局结算输 + systemGain += int64(float64(playerData.bombScore)*float64(10000-taxRate)/10000.0) + int64(float64(gain)*float64(10000-taxRate)/10000.0) + } + } + } + return systemGain +} diff --git a/gamesrv/tienlen/scenepolicy_tienlen.go b/gamesrv/tienlen/scenepolicy_tienlen.go new file mode 100644 index 0000000..2aaa2e6 --- /dev/null +++ b/gamesrv/tienlen/scenepolicy_tienlen.go @@ -0,0 +1,2716 @@ +package tienlen + +import ( + "fmt" + "math" + "math/rand" + "sort" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/game/srvdata" +) + +var ScenePolicyTienLenSingleton = &ScenePolicyTienLen{} + +type ScenePolicyTienLen struct { + base.BaseScenePolicy + states [rule.TienLenSceneStateMax]base.SceneState +} + +// 创建场景扩展数据 +func (this *ScenePolicyTienLen) CreateSceneExData(s *base.Scene) interface{} { + sceneEx := NewTienLenSceneData(s) + if sceneEx != nil { + sceneEx.Clear() + if sceneEx.init() { + s.SetExtraData(sceneEx) + } + } + return sceneEx +} + +// 创建玩家扩展数据 +func (this *ScenePolicyTienLen) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} { + playerEx := &TienLenPlayerData{Player: p} + if playerEx != nil { + playerEx.init() + p.SetExtraData(playerEx) + } + return playerEx +} + +// 场景开启事件 +func (this *ScenePolicyTienLen) OnStart(s *base.Scene) { + logger.Logger.Trace("(this *ScenePolicyTienLen) OnStart, GetSceneId()=", s.GetSceneId()) + + sceneEx := NewTienLenSceneData(s) + if sceneEx != nil { + sceneEx.Clear() + if sceneEx.init() { + s.SetExtraData(sceneEx) + s.ChangeSceneState(rule.TienLenSceneStateWaitPlayer) + } + } +} + +// 场景心跳事件 +func (this *ScenePolicyTienLen) OnTick(s *base.Scene) { + if s == nil { + return + } + if s.GetSceneState() != nil { + s.GetSceneState().OnTick(s) + } +} + +// 玩家进入事件 +func (this *ScenePolicyTienLen) OnPlayerEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerEnter, GetSceneId()=", s.GetSceneId(), " player=", p.GetName()) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + // pos + pos := sceneEx.FindOnePos() + if p.Pos != -1 && sceneEx.seats[p.Pos] == nil { + pos = p.Pos + } + if pos < 0 || pos > 3 || sceneEx.seats[pos] != nil { + p.MarkFlag(base.PlayerState_EnterSceneFailed) + cnt := len(sceneEx.players) + logger.Logger.Warnf("ScenePolicyTienLen.OnPlayerEnter(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames) + return + } + + // playerEx + playerEx := &TienLenPlayerData{Player: p} + playerEx.init() + playerEx.SetPos(pos) + if sceneEx.GetGaming() { + playerEx.MarkFlag(base.PlayerState_WaitNext) + playerEx.SyncFlag() + } + p.SetExtraData(playerEx) + + // sceneEx + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + + // 广播个人信息 + playerData := TienLenCreatePlayerData(p, p.GetRankScore(rule.RankType)) + if sceneEx.IsMatchScene() && p.IsRob { + if len(p.MatchParams) > 2 { + if p.MatchParams[2] != 0 { + playerData.CopySnid = p.MatchParams[2] + } + } + if len(p.MatchParams) > 3 { + if p.MatchParams[3] != 0 { + playerData.CopyRoleId = p.MatchParams[3] + } + } + } + pack := &tienlen.SCTienLenPlayerEnter{ + Data: playerData, + } + proto.SetDefaults(pack) + s.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerEnter), pack, p.GetSid()) + + // 给自己发送房间信息 + TienLenSendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + } +} + +// 玩家离开事件 +func (this *ScenePolicyTienLen) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerLeave, GetSceneId()=", s.GetSceneId(), " player=", p.SnId) + sceneEx, ok := s.GetExtraData().(*TienLenSceneData) + if !ok { + return + } + sceneEx.OnPlayerLeave(p, reason) + s.FirePlayerEvent(p, base.PlayerEventLeave, []int64{int64(reason)}) +} + +// 玩家掉线 +func (this *ScenePolicyTienLen) OnPlayerDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerDropLine, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + sceneEx, ok := s.GetExtraData().(*TienLenSceneData) + if !ok { + return + } + + if sceneEx.GetGaming() { + p.MarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } else { + s.PlayerLeave(p, common.PlayerLeaveReason_DropLine, true) + } + + s.FirePlayerEvent(p, base.PlayerEventDropLine, nil) +} + +// 玩家重连 +func (this *ScenePolicyTienLen) OnPlayerRehold(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerRehold, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + if _, ok := p.GetExtraData().(*TienLenPlayerData); ok { + if p.IsMarkFlag(base.PlayerState_Auto) { + p.UnmarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } + + //发送房间信息给自己 + TienLenSendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventRehold, nil) + } + } +} + +// 返回房间 +func (this *ScenePolicyTienLen) OnPlayerReturn(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerReturn, GetSceneId()=", s.GetSceneId(), " player=", p.Name) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + if _, ok := p.GetExtraData().(*TienLenPlayerData); ok { + if p.IsMarkFlag(base.PlayerState_Auto) { + p.UnmarkFlag(base.PlayerState_Auto) + p.SyncFlag() + } + + //发送房间信息给自己 + TienLenSendRoomInfo(s, p, sceneEx) + s.FirePlayerEvent(p, base.PlayerEventReturn, nil) + } + } +} + +func (this *ScenePolicyTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if s == nil || p == nil { + return false + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerOp, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " opcode=", opcode, " params=", params) + if s.GetSceneState() != nil { + if s.GetSceneState().OnPlayerOp(s, p, opcode, params) { + p.LastOPTimer = time.Now() + return true + } + return false + } + return true +} + +func (this *ScenePolicyTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("(this *ScenePolicyTienLen) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " eventcode=", evtcode, " params=", params) + if s.GetSceneState() != nil { + s.GetSceneState().OnPlayerEvent(s, p, evtcode, params) + } +} + +func (this *ScenePolicyTienLen) OnAudienceEnter(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyTienLen OnAudienceEnter, sceneId=", s.SceneId, " player=", p.SnId) + this.BaseScenePolicy.OnAudienceEnter(s, p) + if sceneEx, ok := s.ExtraData.(*TienLenSceneData); ok { + //给自己发送房间信息 + p.UnmarkFlag(base.PlayerState_Leave) + TienLenSendRoomInfo(s, p, sceneEx) + sceneEx.BroadcastAudienceNum(p) + } +} + +func (this *ScenePolicyTienLen) OnAudienceLeave(s *base.Scene, p *base.Player, reason int) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyTienLen OnAudienceLeave, sceneId=", s.SceneId, " player=", p.SnId, " reason=", reason) + if sceneEx, ok := s.ExtraData.(*TienLenSceneData); ok { + sceneEx.BroadcastAudienceNum(p) + } +} + +func (this *ScenePolicyTienLen) OnAudienceSit(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyTienLen OnAudienceSit, sceneId=", s.SceneId, " player=", p.SnId) + sceneEx, ok := s.GetExtraData().(*TienLenSceneData) + if !ok { + return + } + if s.Gaming && !s.GetBEnterAfterStart() { + return + } + + //自动带入金币 + pos := sceneEx.FindOnePos() + if pos < 0 || pos > 3 || sceneEx.seats[pos] != nil { + p.MarkFlag(base.PlayerState_EnterSceneFailed) + cnt := len(sceneEx.players) + logger.Logger.Warnf("ScenePolicyTienLen.OnAudienceSit(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames) + return + } + + playerEx := &TienLenPlayerData{Player: p} + playerEx.init() + playerEx.SetPos(pos) + p.UnmarkFlag(base.PlayerState_Audience) + p.UnmarkFlag(base.PlayerState_Leave) + if sceneEx.GetGaming() { + playerEx.MarkFlag(base.PlayerState_WaitNext) + p.SyncFlag() + } + + p.SetExtraData(playerEx) + sceneEx.seats[pos] = playerEx + sceneEx.players[p.SnId] = playerEx + + //广播个人信息 + playerData := TienLenCreatePlayerData(p, p.GetRankScore(rule.RankType)) + pack := &tienlen.SCTienLenPlayerEnter{ + Data: playerData, + } + proto.SetDefaults(pack) + s.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerEnter), pack, p.GetSid()) + + //给自己发送房间信息 + TienLenSendRoomInfo(s, p, sceneEx) + sceneEx.BroadcastAudienceNum(p) + + s.FirePlayerEvent(p, base.PlayerEventEnter, nil) + +} + +func (this *ScenePolicyTienLen) OnAudienceDropLine(s *base.Scene, p *base.Player) { + if s == nil || p == nil { + return + } + logger.Logger.Trace("ScenePolicyTienLen OnAudienceDropLine, sceneId=", s.SceneId, " player=", p.SnId) + s.AudienceLeave(p, common.PlayerLeaveReason_DropLine) + if sceneEx, ok := s.ExtraData.(*TienLenSceneData); ok { + sceneEx.BroadcastAudienceNum(p) + } +} + +// 是否完成了整个牌局 +func (this *ScenePolicyTienLen) IsCompleted(s *base.Scene) bool { + if s == nil { + return false + } + return false +} + +// 是否可以强制开始 +func (this *ScenePolicyTienLen) IsCanForceStart(s *base.Scene) bool { + return false +} + +// 当前状态能否换桌 +func (this *ScenePolicyTienLen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s == nil || p == nil { + return false + } + if s.GetSceneState() != nil { + return s.GetSceneState().CanChangeCoinScene(s, p) + } + return true +} + +func (this *ScenePolicyTienLen) ForceStart(s *base.Scene) { + s.ChangeSceneState(rule.TienLenSceneStateWaitStart) +} + +func TienLenCreatePlayerData(p *base.Player, rankScore int64) *tienlen.TienLenPlayerData { + playerEx, _ := p.GetExtraData().(*TienLenPlayerData) + pd := &tienlen.TienLenPlayerData{ + Name: proto.String(p.Name), + SnId: proto.Int32(p.SnId), + Head: proto.Int32(p.Head), + Sex: proto.Int32(p.Sex), + Coin: proto.Int64(p.GetCoin()), + Flag: proto.Int(p.GetFlag()), + Longitude: proto.Int32(p.Longitude), + Latitude: proto.Int32(p.Latitude), + City: proto.String(p.City), + VIP: proto.Int32(p.VIP), + HeadOutLine: proto.Int32(p.HeadOutLine), + NiceId: proto.Int32(p.NiceId), + Pos: proto.Int(p.GetPos()), + RankScore: rankScore, + } + if playerEx != nil { + pd.ThinkLongCnt = playerEx.ThinkLongCnt + } + if p.Roles != nil { + pd.RoleId = proto.Int32(p.Roles.ModId) + } + if p.Items != nil { + pd.Items = make(map[int32]int32) + for id, num := range p.Items { + pd.Items[id] = proto.Int32(num) + } + } + if len(p.MatchParams) > 0 { + pd.MatchRankId = p.MatchParams[0] + } + if len(p.MatchParams) > 1 { + pd.Lv = p.MatchParams[1] + } + logger.Logger.Trace("TienLenCreatePlayerData pd : ", pd) + + return pd +} + +func TienLenCreateRoomInfoPacket(s *base.Scene, p *base.Player, sceneEx *TienLenSceneData) *tienlen.SCTienLenRoomInfo { + pack := &tienlen.SCTienLenRoomInfo{ + RoomId: proto.Int(s.GetSceneId()), + Creator: proto.Int32(s.GetCreator()), + GameId: proto.Int(s.GetGameId()), + RoomMode: proto.Int(s.GetSceneMode()), + Params: s.GetParams(), + State: proto.Int32(int32(s.GetSceneState().GetState())), + TimeOut: proto.Int(s.GetSceneState().GetTimeout(s)), + NumOfGames: proto.Int(sceneEx.NumOfGames), + TotalOfGames: proto.Int(sceneEx.TotalOfGames), + CurOpIdx: proto.Int(-1), + MasterSnid: proto.Int32(sceneEx.masterSnid), + AudienceNum: proto.Int(s.GetAudiencesNum()), + BaseScore: proto.Int32(s.GetBaseScore()), + MaxPlayerNum: proto.Int(s.GetPlayerNum()), + RankType: s.GetDBGameFree().GetRankType(), + SceneAdd: s.GetDBGameFree().GetSceneAdd(), + // 比赛场相关 + Round: proto.Int32(s.MatchRound), + CurPlayerNum: proto.Int32(s.MatchCurPlayerNum), + NextNeed: proto.Int32(s.MatchNextNeed), + RecordId: sceneEx.recordId, + } + pack.IsMatch = int32(0) + // 0.普通场 1.锦标赛 2.冠军赛 3.vip专属 + if s.IsMatchScene() { + pack.IsMatch = s.MatchType + } + pack.MatchFinals = 0 + if s.MatchFinals { + pack.MatchFinals = 1 + if s.NumOfGames >= 2 { + pack.MatchFinals = 2 + } + } + for _, snid := range sceneEx.winSnids { + pack.WinSnids = append(pack.WinSnids, snid) + } + if s.GetSceneState().GetState() == rule.TienLenSceneStateBilled { + sceneEx.currOpPos = -1 + pack.CurOpIdx = proto.Int32(-1) + } + if s.GetSceneState().GetState() >= rule.TienLenSceneStatePlayerOp { + pack.CurOpIdx = proto.Int32(sceneEx.currOpPos) + } + playerEx, _ := p.GetExtraData().(*TienLenPlayerData) + //玩家信息.第一个必然是自己 + pd := TienLenCreatePlayerData(p, p.GetRankScore(rule.RankType)) + if playerEx != nil { + //手牌 + for i := int32(0); i < rule.HandCardNum; i++ { + if playerEx.cards[i] != rule.InvalideCard { + pd.Cards = append(pd.Cards, playerEx.cards[i]) + } + } + if sceneEx.IsMatchScene() && p.IsRob { + if len(p.MatchParams) > 2 { + if p.MatchParams[2] != 0 { + pd.CopySnid = p.MatchParams[2] + } + } + if len(p.MatchParams) > 3 { + if p.MatchParams[3] != 0 { + pd.CopyRoleId = p.MatchParams[3] + } + } + } + + } + pack.Players = append(pack.Players, pd) + + //剩下的按座位排序 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + nowPlayer := sceneEx.seats[i] + if nowPlayer != nil { + if nowPlayer.SnId != p.SnId { + pd1 := TienLenCreatePlayerData(nowPlayer.Player, nowPlayer.Player.GetRankScore(rule.RankType)) + if sceneEx.IsMatchScene() && nowPlayer.IsRob { + if len(nowPlayer.MatchParams) > 2 { + if nowPlayer.MatchParams[2] != 0 { + pd1.CopySnid = nowPlayer.MatchParams[2] + } + } + if len(nowPlayer.MatchParams) > 3 { + if nowPlayer.MatchParams[3] != 0 { + pd1.CopyRoleId = nowPlayer.MatchParams[3] + } + } + } + //手牌 + for j := int32(0); j < rule.HandCardNum; j++ { + if nowPlayer.cards[j] != rule.InvalideCard { + if s.GetSceneState().GetState() == rule.TienLenSceneStateBilled { //结算状态显示用 + pd1.Cards = append(pd1.Cards, nowPlayer.cards[j]) + } else { + pd1.Cards = append(pd1.Cards, rule.InvalideCard) + } + } + } + pack.Players = append(pack.Players, pd1) + } + } + } + //上两手玩家出的牌(有序) + insertNum := 0 + for i := len(sceneEx.delOrders) - 1; i >= 0 && insertNum < 2; i-- { + lastDelCard := &tienlen.LastDelCard{} + delCards := sceneEx.delCards[i] + for j := 0; j < len(delCards); j++ { + lastDelCard.Cards = append(lastDelCard.Cards, delCards[j]) + } + pack.LastDelCards = append(pack.LastDelCards, lastDelCard) + insertNum++ + } + proto.SetDefaults(pack) + return pack +} + +func TienLenSendRoomInfo(s *base.Scene, p *base.Player, sceneEx *TienLenSceneData) { + pack := TienLenCreateRoomInfoPacket(s, p, sceneEx) + ok := p.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenRoomInfo), pack) + logger.Logger.Trace("SCTienLenSendRoomInfo isok : ", ok, ",pack", pack) +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BaseState +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type SceneBaseStateTienLen struct { +} + +func (this *SceneBaseStateTienLen) GetTimeout(s *base.Scene) int { + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + return int(time.Now().Sub(sceneEx.StateStartTime) / time.Second) + } + return 0 +} + +func (this *SceneBaseStateTienLen) CanChangeTo(s base.SceneState) bool { + return true +} + +// 当前状态能否换桌 +func (this *SceneBaseStateTienLen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + return !p.IsGameing() || s.GetDestroyed() +} + +func (this *SceneBaseStateTienLen) OnEnter(s *base.Scene) { + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + sceneEx.StateStartTime = time.Now() + } +} + +func (this *SceneBaseStateTienLen) OnLeave(s *base.Scene) {} + +func (this *SceneBaseStateTienLen) OnTick(s *base.Scene) { + //场景状态是所有房间公用,房间的私有属性不能放到场景状态上 + if time.Now().Unix() > s.GetTimerRandomRobot() { + s.RandRobotCnt() + s.SetTimerRandomRobot(s.GetRobotTime()) + } +} + +// 发送玩家操作情况 +func (this *SceneBaseStateTienLen) OnPlayerSToCOp(s *base.Scene, p *base.Player, pos int, opcode int, opRetCode tienlen.OpResultCode, params []int64) { + pack := &tienlen.SCTienLenPlayerOp{ + OpCode: proto.Int(opcode), + SnId: proto.Int32(p.SnId), + OpRetCode: opRetCode, + OpParam: params, + } + proto.SetDefaults(pack) + p.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerOp), pack) + //logger.Logger.Trace("发送玩家操作情况 ", pack) +} + +// 广播发送玩家操作情况 +func (this *SceneBaseStateTienLen) BroadcastPlayerSToCOp(s *base.Scene, snid int32, pos int, opcode int, opRetCode tienlen.OpResultCode, params []int64) { + pack := &tienlen.SCTienLenPlayerOp{ + OpCode: proto.Int(opcode), + SnId: proto.Int32(snid), + OpRetCode: opRetCode, + OpParam: params, + } + proto.SetDefaults(pack) + s.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerOp), pack, 0) + if opRetCode == tienlen.OpResultCode_OPRC_Sucess { + cards := []int{} + for _, param := range params { + cards = append(cards, rule.ValueStr(int32(param))) + } + //logger.Logger.Trace("广播发送玩家操作情况 ", pack, " snid:", snid, " cards:", cards) + } +} + +func (this *SceneBaseStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + logger.Logger.Trace("SceneBaseStateTienLen.", " s.GetSceneId() : ", s.GetSceneId(), " p.SnId : ", p.SnId, " opcode : ", opcode, " params ", params) + + sceneEx, _ := s.GetExtraData().(*TienLenSceneData) + if sceneEx == nil { + return true + } + playerEx, _ := p.GetExtraData().(*TienLenPlayerData) + if playerEx == nil { + return true + } + + switch opcode { + case int(rule.TienLenPlayerOpTest): + if !common.Config.IsDevMode { + return false + } + sceneEx.testPokers = make([]int32, len(params)+1) + sceneEx.testPokers[0] = p.SnId + copy(sceneEx.testPokers[1:], common.Int64ToInt32(params)) + return true + case int(rule.TienLenPlayerOpCanelAuto): //取消托管 + playerEx.LeaveAutoState(s) + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, tienlen.OpResultCode_OPRC_Sucess, params) + return true + } + + return false +} + +func (this *SceneBaseStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + sceneEx, ok := s.GetExtraData().(*TienLenSceneData) + if ok { + // 没有防住,选房主 + if sceneEx.GetPlayer(sceneEx.masterSnid) == nil { + findOne := false + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerExSeat := sceneEx.seats[i] + if playerExSeat != nil && playerExSeat.SnId != sceneEx.masterSnid { + sceneEx.masterSnid = playerExSeat.SnId + sceneEx.BroadcastUpdateMasterSnid() + findOne = true + break + } + } + if !findOne { + sceneEx.masterSnid = 0 + sceneEx.BroadcastUpdateMasterSnid() + } + } + } +} + +func (this *SceneBaseStateTienLen) BroadcastRoomState(s *base.Scene, state int, params ...int64) { + pack := &tienlen.SCTienLenRoomState{ + State: proto.Int(state), + Params: params, + } + proto.SetDefaults(pack) + s.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenRoomState), pack, 0) +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TienLenSceneStateWaitPlayer +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type SceneWaitPlayerStateTienLen struct { + SceneBaseStateTienLen +} + +func (this *SceneWaitPlayerStateTienLen) GetState() int { + return rule.TienLenSceneStateWaitPlayer +} + +func (this *SceneWaitPlayerStateTienLen) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.TienLenSceneStateWaitStart || s.GetState() == rule.TienLenSceneStateHandCard { + return true + } + return false +} + +// 当前状态能否换桌 +func (this *SceneWaitPlayerStateTienLen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s.IsMatchScene() { + return false + } + return true +} + +func (this *SceneWaitPlayerStateTienLen) OnEnter(s *base.Scene) { + this.SceneBaseStateTienLen.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + sceneEx.Clear() + sceneEx.SetGaming(false) + this.BroadcastRoomState(s, this.GetState()) + } +} + +// 状态离开时 +func (this *SceneWaitPlayerStateTienLen) OnLeave(s *base.Scene) { + this.SceneBaseStateTienLen.OnLeave(s) + logger.Logger.Tracef("(this *SceneWaitPlayerStateTienLen) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneWaitPlayerStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateTienLen.OnPlayerOp(s, p, opcode, params) { + return true + } + return true +} + +// 玩家事件 +func (this *SceneWaitPlayerStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneWaitPlayerStateTienLen) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneBaseStateTienLen.OnPlayerEvent(s, p, evtcode, params) + + if _, ok := p.GetExtraData().(*TienLenPlayerData); ok { + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + switch evtcode { + case base.PlayerEventEnter: + //如果有人进入, 检查在线人是否能够开启游戏,够的话,切换到延迟开启状态 + if sceneEx.CanStart() { + logger.Logger.Tracef("(this *SceneWaitPlayerStateTienLen) OnPlayerEvent s.ChangeSceneState(TienLenSceneStateWaitStart) %v", s.GetSceneId()) + s.ChangeSceneState(rule.TienLenSceneStateWaitStart) + } + } + } + } +} + +func (this *SceneWaitPlayerStateTienLen) OnTick(s *base.Scene) { + this.SceneBaseStateTienLen.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + if s.CheckNeedDestroy() { + sceneEx.SceneDestroy(true) + return + } + if sceneEx.CanStart() { + logger.Logger.Tracef("(this *SceneWaitPlayerStateTienLen) OnTick s.ChangeSceneState(TienLenSceneStateWaitStart) %v", s.GetSceneId()) + s.ChangeSceneState(rule.TienLenSceneStateWaitStart) + } else if !s.GetGaming() && s.GetRobotNum() == 1 { + tNow := time.Now() + for _, p := range s.Players { + if p.IsRob && tNow.Sub(p.GetLastOPTimer()) > time.Second*time.Duration(30+rand.Int63n(60)) { + //p.LastOPTimer = tNow.Add(time.Minute * 30) + s.PlayerLeave(p, common.PlayerLeaveReason_Normal, true) + } + } + } + } +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TienLenSceneStateWaitStart +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type SceneWaitStartStateTienLen struct { + SceneBaseStateTienLen +} + +func (this *SceneWaitStartStateTienLen) GetState() int { + return rule.TienLenSceneStateWaitStart +} + +func (this *SceneWaitStartStateTienLen) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.TienLenSceneStateWaitPlayer || s.GetState() == rule.TienLenSceneStateWaitStart || s.GetState() == rule.TienLenSceneStateHandCard { + return true + } + return false +} + +// 当前状态能否换桌 +func (this *SceneWaitStartStateTienLen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool { + if s.IsMatchScene() { + return false + } + return true +} + +func (this *SceneWaitStartStateTienLen) OnEnter(s *base.Scene) { + this.SceneBaseStateTienLen.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + sceneEx.Clear() + sceneEx.SetGaming(false) + this.BroadcastRoomState(s, this.GetState()) + logger.Logger.Trace("(this *SceneWaitStartStateTienLen) OnEnter", this.GetState()) + } +} + +// 状态离开时 +func (this *SceneWaitStartStateTienLen) OnLeave(s *base.Scene) { + this.SceneBaseStateTienLen.OnLeave(s) + logger.Logger.Tracef("(this *SceneWaitStartStateTienLen) OnLeave", this.GetState()) +} + +// 玩家操作 +func (this *SceneWaitStartStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateTienLen.OnPlayerOp(s, p, opcode, params) { + return true + } + sceneEx, _ := s.GetExtraData().(*TienLenSceneData) + if sceneEx != nil { + playerEx, _ := p.GetExtraData().(*TienLenPlayerData) + if playerEx != nil { + opRetCode := tienlen.OpResultCode_OPRC_Error + if playerEx.SnId == sceneEx.masterSnid && this.GetState() == rule.TienLenSceneStateWaitStart { + if sceneEx.IsMatchScene() { + return false + } + switch int32(opcode) { + case rule.TienLenPlayerOpStart: //房主开始游戏 + if sceneEx.CanStart() == false { + s.ChangeSceneState(rule.TienLenSceneStateWaitPlayer) + } else { + opRetCode = tienlen.OpResultCode_OPRC_Sucess + s.ChangeSceneState(rule.TienLenSceneStateHandCard) + } + } + } + if opRetCode == tienlen.OpResultCode_OPRC_Sucess { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, opRetCode, params) + } else { + logger.Logger.Tracef("SceneWaitStartStateTienLen OnPlayerOp snid:%v, masterSnid:%v", playerEx.SnId, sceneEx.masterSnid) + } + } + } + return true +} + +// 玩家事件 +func (this *SceneWaitStartStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneWaitStartStateTienLen) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneBaseStateTienLen.OnPlayerEvent(s, p, evtcode, params) + + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + switch evtcode { + case base.PlayerEventLeave: + //如果有人退出, 检查在线人是否能够开启游戏,不够的话,切换到等待状态 + if sceneEx.CanStart() == false { + s.ChangeSceneState(rule.TienLenSceneStateWaitPlayer) + } + } + } +} + +func (this *SceneWaitStartStateTienLen) OnTick(s *base.Scene) { + this.SceneBaseStateTienLen.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + if sceneEx.IsMatchScene() { + delayT := time.Second * 2 + if sceneEx.MatchRound != 1 { //第一轮延迟2s,其他延迟3s 配合客户端播放动画 + delayT = time.Second * 4 + } + if time.Now().Sub(sceneEx.StateStartTime) > delayT { + s.ChangeSceneState(rule.TienLenSceneStateHandCard) // 比赛场直接发牌 + return + } + } + if sceneEx.SceneMode == common.SceneMode_Public { + if time.Now().Sub(sceneEx.StateStartTime) > rule.TienLenWaitStartTimeout { + if sceneEx.Creator != 0 && sceneEx.GetRealPlayerNum() == 0 { + sceneEx.Destroy(true) + return + } + //开始前再次检查开始条件 + if sceneEx.CanStart() == true { + s.ChangeSceneState(rule.TienLenSceneStateHandCard) + } else { + s.ChangeSceneState(rule.TienLenSceneStateWaitPlayer) + } + } + } + } +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TienLenSceneStateHandCard 发牌 +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type SceneHandCardStateTienLen struct { + SceneBaseStateTienLen +} + +func (this *SceneHandCardStateTienLen) GetState() int { + return rule.TienLenSceneStateHandCard +} + +func (this *SceneHandCardStateTienLen) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.TienLenSceneStatePlayerOp || s.GetState() == rule.TienLenSceneStateBilled { + return true + } + return false +} + +func (this *SceneHandCardStateTienLen) OnEnter(s *base.Scene) { + this.SceneBaseStateTienLen.OnEnter(s) + + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + sceneEx.Clear() + sceneEx.SetGaming(true) + sceneEx.GameNowTime = time.Now() + sceneEx.AllPlayerEnterGame() + + //参与游戏次数 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerEx := sceneEx.seats[i] + if playerEx != nil { + if playerEx.IsGameing() { + playerEx.GameTimes++ + sceneEx.curGamingPlayerNum++ + if playerEx.IsRob { + playerEx.robotGameTimes-- + sceneEx.robotGamingNum++ + } + } + } + } + + s.NumOfGames++ + s.NotifySceneRoundStart(s.NumOfGames) + this.BroadcastRoomState(s, this.GetState(), int64(s.NumOfGames)) + + //同步防伙牌数据 + sceneEx.SyncScenePlayer() + //发牌 + if rule.TestOpen { + sceneEx.SendHandCardTest() + } else { + if sceneEx.IsMatchScene() { + sceneEx.SendHandCard_Match() + } else { + sceneEx.SendHandCardOdds() + } + } + + for _, seat := range sceneEx.seats { + if seat != nil { + tmpCards := seat.cards[:] + sceneEx.allPlayerCards = append(sceneEx.allPlayerCards, tmpCards) + logger.Logger.Info("--------------------------------------allplayerCards = ", sceneEx.allPlayerCards) + seat.tianHu = 0 + switch { + case rule.Have2FourBomb(tmpCards): + seat.tianHu = rule.TianHu2Four + case rule.Have6StraightTwin(tmpCards): + seat.tianHu = rule.TianHu6StraightTwin + case rule.Have12Straight(tmpCards): + seat.tianHu = rule.TianHu12Straight + } + if seat.tianHu > 0 { + keyNovice := common.GetKeyNoviceGameId(sceneEx.GameId) + data, ok := seat.GDatas[keyNovice] + if !ok { + data = &model.PlayerGameInfo{FirstTime: time.Now()} + seat.GDatas[keyNovice] = data + } + data.Statics.Incr(rule.StaticsTianHuTimes) + sceneEx.tianHuSnids = append(sceneEx.tianHuSnids, seat.SnId) // 天胡玩家 + if !common.InSliceInt32(sceneEx.winSnids, seat.SnId) { + sceneEx.winSnids = append(sceneEx.winSnids, seat.SnId) // 赢家snid + } + } + // 手牌分数 + cardsTypeMap := rule.GetCardsType(tmpCards, sceneEx.IsTienLenYule()) + score, _ := rule.GetCardTypeScore(cardsTypeMap, sceneEx.IsTienLenYule(), tmpCards) + seat.cardScore = score + } + } + if len(sceneEx.tianHuSnids) == 0 { //没有天胡玩家 + //有赢家,赢家先出;无赢家手持最小牌先 + pos := int32(sceneEx.FindWinPos()) + if pos == -1 { + pos = sceneEx.startOpPos + } + pack := &tienlen.SCTienLenFirstOpPos{ + Pos: proto.Int32(pos), + } + proto.SetDefaults(pack) + sceneEx.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenFirstOpPos), pack, 0) + logger.Logger.Trace("SCTienLenFirstOpPos: ", pack) + } + } +} + +// 状态离开时 +func (this *SceneHandCardStateTienLen) OnLeave(s *base.Scene) { + this.SceneBaseStateTienLen.OnLeave(s) + + logger.Logger.Tracef("(this *SceneHandCardStateTienLen) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *SceneHandCardStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateTienLen.OnPlayerOp(s, p, opcode, params) { + return true + } + + return true +} + +// 玩家事件 +func (this *SceneHandCardStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneHandCardStateTienLen) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneBaseStateTienLen.OnPlayerEvent(s, p, evtcode, params) +} + +func (this *SceneHandCardStateTienLen) OnTick(s *base.Scene) { + this.SceneBaseStateTienLen.OnTick(s) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + newTime := rule.TienLenHandCardTimeout + if sceneEx.isAllRob { + newTime = time.Second * 1 + } + if time.Now().Sub(sceneEx.StateStartTime) > newTime { + if len(sceneEx.tianHuSnids) != 0 { + //天胡牌型直接结算 + logger.Logger.Trace("tianHu to Billed: ", sceneEx.tianHuSnids) + s.ChangeSceneState(rule.TienLenSceneStateBilled) + } else { + //正常出牌:有赢家,赢家先出;无赢家手持最小牌先出(最小牌必出) + winPos := sceneEx.FindWinPos() + if winPos != -1 { //有赢家 + sceneEx.SetCurOpPos(int32(winPos)) + } else { + sceneEx.SetCurOpPos(sceneEx.startOpPos) + } + s.ChangeSceneState(rule.TienLenSceneStatePlayerOp) + } + } + } +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TienLenSceneStatePlayerOp 出牌(玩家操作阶段) +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type ScenePlayerOpStateTienLen struct { + SceneBaseStateTienLen +} + +func (this *ScenePlayerOpStateTienLen) GetState() int { + return rule.TienLenSceneStatePlayerOp +} + +func (this *ScenePlayerOpStateTienLen) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.TienLenSceneStateBilled { + return true + } + return false +} + +func (this *ScenePlayerOpStateTienLen) OnEnter(s *base.Scene) { + this.SceneBaseStateTienLen.OnEnter(s) + this.BroadcastRoomState(s, this.GetState()) + + sceneEx, _ := s.GetExtraData().(*TienLenSceneData) + if sceneEx != nil { + sceneEx.BroadcastOpPos() + } +} + +// 状态离开时 +func (this *ScenePlayerOpStateTienLen) OnLeave(s *base.Scene) { + this.SceneBaseStateTienLen.OnLeave(s) + + logger.Logger.Tracef("(this *SceneHandCardStateTienLen) OnLeave, sceneid=%v", s.GetSceneId()) +} + +// 玩家操作 +func (this *ScenePlayerOpStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateTienLen.OnPlayerOp(s, p, opcode, params) { + return true + } + sceneEx, _ := s.GetExtraData().(*TienLenSceneData) + if sceneEx != nil { + playerEx, _ := p.GetExtraData().(*TienLenPlayerData) + if playerEx != nil { + if sceneEx.GetCurOpPos() != int32(playerEx.GetPos()) { + return false + } + + opRetCode := tienlen.OpResultCode_OPRC_Error + switch int32(opcode) { + case rule.TienLenPlayerOpPlay: //出牌 + + delCards := []int32{} + for _, card := range params { + isHave := false + for _, hcard := range playerEx.cards { //去手牌里找找看有没有 + if int32(card) == hcard && hcard != rule.InvalideCard { + isHave = true + } + } + if isHave { + delCards = append(delCards, int32(card)) + } else { + opRetCode = tienlen.OpResultCode_OPRC_Error + break + } + } + if len(delCards) == len(params) && len(delCards) > 0 { + isRule, _ := rule.RulePopEnable(delCards) + ruleType := rule.Tienlen_Pass + if sceneEx.IsTienLenYule() { + isRule, ruleType = rule.RulePopEnable_yl(delCards) + } + //logger.Logger.Trace("ScenePlayerOpStateTienLen,2params:", params, " isRule:", isRule) + if isRule { //符合出牌规则 + if int32(playerEx.GetPos()) == sceneEx.lastOpPos || sceneEx.lastOpPos == rule.InvalidePos { //当前操作者和上一个操作者是同一个人,必出牌 + if ruleType == rule.Plane_Single { //飞机带单只能最后一手出 + haveCardNum := 0 + for _, hcard := range playerEx.cards { + if hcard != rule.InvalideCard { + haveCardNum++ + } + } + if len(delCards) != haveCardNum { + isRule = false + opRetCode = tienlen.OpResultCode_OPRC_Error + logger.Logger.Trace("Plane_Single, delCards:", delCards, " haveCardNum:", haveCardNum) + } + } + if isRule { + if sceneEx.lastOpPos == rule.InvalidePos { //首出玩家 + //有赢家,赢家先出,出牌不受限制 + //无赢家,手持最小牌先出,最小牌必先出 + winPos := sceneEx.FindWinPos() + //logger.Logger.Trace("ScenePlayerOpStateTienLen,8params:", params, " winPos:", winPos) + if winPos == -1 { //无赢家 + haveMinCard := false + for _, card := range delCards { + if card == sceneEx.curMinCard { //最小牌必先出 + haveMinCard = true + } + } + //logger.Logger.Trace("ScenePlayerOpStateTienLen,9params:", params, " curMinCard:", sceneEx.curMinCard, " haveMinCard", haveMinCard) + if haveMinCard { + isDel := sceneEx.DelCards(playerEx, delCards) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,3params:", params, " isDel:", isDel) + if isDel { + + playerEx.RefreshThinkLongCnt(time.Now().Sub(sceneEx.StateStartTime), false) + + sceneEx.DoNext(int32(playerEx.GetPos())) + opRetCode = tienlen.OpResultCode_OPRC_Sucess + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.RefreshPlayerHandLimitTimeOut() + } + } + } else { + isDel := sceneEx.DelCards(playerEx, delCards) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,10params:", params, " isDel:", isDel) + if isDel { + + playerEx.RefreshThinkLongCnt(time.Now().Sub(sceneEx.StateStartTime), false) + + sceneEx.DoNext(int32(playerEx.GetPos())) + opRetCode = tienlen.OpResultCode_OPRC_Sucess + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.RefreshPlayerHandLimitTimeOut() + } + } + } else { + isDel := sceneEx.DelCards(playerEx, delCards) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,4params:", params, " isDel:", isDel) + if isDel { + + playerEx.RefreshThinkLongCnt(time.Now().Sub(sceneEx.StateStartTime), false) + + nextPos := sceneEx.DoNext(int32(playerEx.GetPos())) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,4paramssss:", params, " nextPos:", nextPos) + if sceneEx.IsTienLenToEnd() && nextPos == rule.InvalidePos { + sceneEx.UnmarkPass() + nextPos = sceneEx.DoNext(int32(playerEx.GetPos())) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,4paramssss:", params, " nextPos:", nextPos) + } + opRetCode = tienlen.OpResultCode_OPRC_Sucess + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.RefreshPlayerHandLimitTimeOut() + } + } + if opRetCode == tienlen.OpResultCode_OPRC_Sucess { + isBomb := rule.IsFourBomb(delCards) + if isBomb { + sceneEx.isKongBomb = true + } + } + sceneEx.UnmarkPass() + } + } else { //当前操作者和上一个操作者不是同一个人,必压制 + if !playerEx.isPass { + lastOpPlayer := sceneEx.GetLastOpPlayer() + //logger.Logger.Trace("ScenePlayerOpStateTienLen,5params:", params, " lastOpPlayer:", lastOpPlayer) + if lastOpPlayer != nil && len(lastOpPlayer.delCards) != 0 { + lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] + canDel, isBomb, bombScore := rule.CanDel(lastDelCards, delCards, sceneEx.IsTienLenToEnd()) + if sceneEx.IsTienLenYule() { + canDel, isBomb, bombScore = rule.CanDel_yl(lastDelCards, delCards, sceneEx.IsTienLenToEnd()) + } + //logger.Logger.Trace("ScenePlayerOpStateTienLen,6params:", params, " canDel:", canDel, " lastDelCards:", lastDelCards) + if canDel { + if isBomb { + sceneEx.curBombPos = int32(playerEx.GetPos()) + sceneEx.lastBombPos = sceneEx.lastOpPos + sceneEx.roundScore += bombScore + } else { + sceneEx.curBombPos = rule.InvalidePos + sceneEx.lastBombPos = rule.InvalidePos + sceneEx.roundScore = 0 + } + isDel := sceneEx.DelCards(playerEx, delCards) + //logger.Logger.Trace("ScenePlayerOpStateTienLen,7params:", params, " isDel:", isDel) + if isDel { + + playerEx.RefreshThinkLongCnt(time.Now().Sub(sceneEx.StateStartTime), false) + + nextPos := sceneEx.DoNext(int32(playerEx.GetPos())) + if sceneEx.IsTienLenToEnd() && nextPos == rule.InvalidePos { + sceneEx.UnmarkPass() + sceneEx.DoNext(int32(playerEx.GetPos())) + } + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.RefreshPlayerHandLimitTimeOut() + opRetCode = tienlen.OpResultCode_OPRC_Sucess + sceneEx.isKongBomb = false + } + } + } + } + } + } + } + case rule.TienLenPlayerOpPass: //过牌 + if int32(playerEx.GetPos()) == sceneEx.lastOpPos { //当前操作者和上一个操作者是同一个人,必出牌,不能过牌 + opRetCode = tienlen.OpResultCode_OPRC_Error + } else { + if sceneEx.lastOpPos != rule.InvalidePos { + + playerEx.RefreshThinkLongCnt(time.Now().Sub(sceneEx.StateStartTime), false) + + sceneEx.card_play_action_seq = append(sceneEx.card_play_action_seq, fmt.Sprintf("%v-过", playerEx.GetPos())) + sceneEx.card_play_action_seq_int32 = append(sceneEx.card_play_action_seq_int32, []int32{-1}) + nextPos := sceneEx.DoNext(int32(playerEx.GetPos())) + + if sceneEx.IsTienLenToEnd() && nextPos == rule.InvalidePos { + sceneEx.UnmarkPass() + nextPos = sceneEx.DoNext(int32(playerEx.GetPos())) + sceneEx.SetLastOpPos(int32(nextPos)) + } + + sceneEx.RefreshPlayerHandLimitTimeOut() + opRetCode = tienlen.OpResultCode_OPRC_Sucess + playerEx.isPass = true + if nextPos == sceneEx.lastOpPos { //一轮都不出牌 + sceneEx.TrySmallGameBilled() + } + } + } + case rule.TienLenPlayerClientHintCards: + if int32(playerEx.GetPos()) == sceneEx.lastOpPos || sceneEx.lastOpPos == rule.InvalidePos { + logger.Logger.Trace("ScenePlayerOpStateTienLen,107 OPRC_Error:", params, " opcode:", opcode, " snid:", p.SnId, " pos:", playerEx.GetPos()) + } else { + if len(params) > 0 { + sceneEx.cHintCards = make([]int32, len(params)+1) + sceneEx.cHintCards[0] = p.SnId + copy(sceneEx.cHintCards[1:], common.Int64ToInt32(params)) + opRetCode = tienlen.OpResultCode_OPRC_Hint + } + } + default: + opRetCode = tienlen.OpResultCode_OPRC_Error + } + + //next + if opRetCode == tienlen.OpResultCode_OPRC_Sucess { + this.BroadcastPlayerSToCOp(s, playerEx.SnId, playerEx.GetPos(), opcode, opRetCode, params) + + delCardNum := 0 + for _, hcard := range playerEx.cards { + if hcard == rule.InvalideCard { + delCardNum++ + } + } + if delCardNum == rule.Hand_CardNum { //牌出完了 + sceneEx.TrySmallGameBilled() + if !common.InSliceInt32(sceneEx.winSnids, playerEx.SnId) { + sceneEx.winSnids = append(sceneEx.winSnids, playerEx.SnId) + } + if sceneEx.IsTienLenToEnd() { //打到底 + if sceneEx.GetGameingPlayerCnt()-1 == len(sceneEx.winSnids) { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } else { + playerEx.isDelAll = true + sceneEx.BroadcastOpPos() + } + } else { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } + } else { + sceneEx.BroadcastOpPos() + } + } else if opRetCode == tienlen.OpResultCode_OPRC_Error { + this.OnPlayerSToCOp(s, p, playerEx.GetPos(), opcode, opRetCode, params) + + logger.Logger.Trace("ScenePlayerOpStateTienLen,100 OPRC_Error:", params, " opcode:", opcode, " snid:", p.SnId, " pos:", playerEx.GetPos()) + } + } + } + + return true +} + +// 玩家事件 +func (this *ScenePlayerOpStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + logger.Logger.Trace("(this *SceneHandCardStateTienLen) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode) + this.SceneBaseStateTienLen.OnPlayerEvent(s, p, evtcode, params) +} + +func (this *ScenePlayerOpStateTienLen) OnTick(s *base.Scene) { + this.SceneBaseStateTienLen.OnTick(s) + + sceneEx, ok := s.GetExtraData().(*TienLenSceneData) + if !ok { + return + } + + curOpPos := sceneEx.GetCurOpPos() + + if curOpPos == rule.InvalidePos { + return + } + + playerEx := sceneEx.seats[curOpPos] + if playerEx == nil { + return + } + + if time.Now().Sub(sceneEx.StateStartTime) < playerEx.GetCurHandLimitTimeOut() { + return + } + + if !playerEx.IsRob { + logger.Logger.Trace("(this *ScenePlayerOpStateTienLen) OnTick snid: ", + playerEx.SnId, " HandLTime:", playerEx.GetCurHandLimitTimeOut(), " thinkCnt:", playerEx.ThinkLongCnt, + " flag:", playerEx.GetFlag()) + } + + playerEx.EnterAutoState() + + //超时后单轮首出玩家出最小的牌,非单轮首出玩家不出牌 + if sceneEx.lastOpPos == curOpPos || sceneEx.lastOpPos == rule.InvalidePos { //单轮首出玩家 + cpCards := playerEx.cards[:] + rule.MinSortCards(cpCards) + + var delCards []int32 + for _, card := range cpCards { + if card != rule.InvalideCard { + delCards = append(delCards, card) + break + } + } + + isDel := sceneEx.DelCards(playerEx, delCards) + if isDel { + sceneEx.DoNext(int32(playerEx.GetPos())) + sceneEx.UnmarkPass() + this.BroadcastPlayerSToCOp(s, playerEx.SnId, playerEx.GetPos(), int(rule.TienLenPlayerOpPlay), + tienlen.OpResultCode_OPRC_Sucess, common.CopySliceInt32ToInt64(delCards)) + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.RefreshPlayerHandLimitTimeOut() + sceneEx.BroadcastOpPos() + } else { + sceneEx.DoNext(int32(playerEx.GetPos())) + playerEx.isPass = true + this.BroadcastPlayerSToCOp(s, playerEx.SnId, playerEx.GetPos(), int(rule.TienLenPlayerOpPass), tienlen.OpResultCode_OPRC_Sucess, []int64{}) + sceneEx.RefreshPlayerHandLimitTimeOut() + sceneEx.BroadcastOpPos() + } + sceneEx.isKongBomb = false + delAll := 0 + for _, card := range playerEx.cards { + if card == rule.InvalideCard { + delAll++ + } + } + if delAll == rule.Hand_CardNum { // 出完 + sceneEx.TrySmallGameBilled() + if !common.InSliceInt32(sceneEx.winSnids, playerEx.SnId) { + sceneEx.winSnids = append(sceneEx.winSnids, playerEx.SnId) + } + if sceneEx.IsTienLenToEnd() { //打到底 + if sceneEx.GetGameingPlayerCnt()-1 == len(sceneEx.winSnids) { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } else { + playerEx.isDelAll = true + sceneEx.BroadcastOpPos() + } + } else { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } + } + } else { + + bHandCard := false + if len(sceneEx.cHintCards) >= 2 && playerEx.SnId == sceneEx.cHintCards[0] { + lastOpPlayer := sceneEx.GetLastOpPlayer() + if lastOpPlayer != nil && len(lastOpPlayer.delCards) != 0 { + lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] + + hintCards := sceneEx.cHintCards[1:] + recmCards := make([]int32, len(hintCards)) + copy(recmCards, hintCards) + + bExistHandCards := true + for _, card := range recmCards { + isHave := false + for _, hcard := range playerEx.cards { //去手牌里找找看有没有 + if int32(card) == hcard && hcard != rule.InvalideCard { + isHave = true + break + } + } + + if !isHave { + bExistHandCards = false + break + } + } + + isRule, _ := rule.RulePopEnable(recmCards) + if sceneEx.IsTienLenYule() { + isRule, _ = rule.RulePopEnable_yl(recmCards) + } + + if bExistHandCards && isRule { + // 检查能否压得住 + canDel, isBomb, bombScore := rule.CanDel(lastDelCards, recmCards, sceneEx.IsTienLenToEnd()) + if sceneEx.IsTienLenYule() { + canDel, isBomb, bombScore = rule.CanDel_yl(lastDelCards, recmCards, sceneEx.IsTienLenToEnd()) + } + + if canDel { + if isBomb { + sceneEx.curBombPos = int32(playerEx.GetPos()) + sceneEx.lastBombPos = sceneEx.lastOpPos + sceneEx.roundScore += bombScore + } else { + sceneEx.curBombPos = rule.InvalidePos + sceneEx.lastBombPos = rule.InvalidePos + sceneEx.roundScore = 0 + } + + isDel := sceneEx.DelCards(playerEx, recmCards) + if isDel { + nextPos := sceneEx.DoNext(int32(playerEx.GetPos())) + if sceneEx.IsTienLenToEnd() && nextPos == rule.InvalidePos { + sceneEx.UnmarkPass() + sceneEx.DoNext(int32(playerEx.GetPos())) + } + + sceneEx.SetLastOpPos(int32(playerEx.GetPos())) + sceneEx.isKongBomb = false + sceneEx.RefreshPlayerHandLimitTimeOut() + retParams := []int64{} + for _, card := range recmCards { + retParams = append(retParams, int64(card)) + } + + this.BroadcastPlayerSToCOp(s, playerEx.SnId, playerEx.GetPos(), int(rule.TienLenPlayerOpAutoHandCard), tienlen.OpResultCode_OPRC_Sucess, retParams) + + delCardNum := 0 + for _, hcard := range playerEx.cards { + if hcard == rule.InvalideCard { + delCardNum++ + } + } + + if delCardNum == rule.Hand_CardNum { //牌出完了 + sceneEx.TrySmallGameBilled() + if !common.InSliceInt32(sceneEx.winSnids, playerEx.SnId) { + sceneEx.winSnids = append(sceneEx.winSnids, playerEx.SnId) + } + if sceneEx.IsTienLenToEnd() { //打到底 + if sceneEx.GetGameingPlayerCnt()-1 == len(sceneEx.winSnids) { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } else { + playerEx.isDelAll = true + sceneEx.BroadcastOpPos() + } + } else { + sceneEx.ChangeSceneState(rule.TienLenSceneStateBilled) + } + } else { + sceneEx.BroadcastOpPos() + } + + bHandCard = true + } + } + } + } + + // 用完后置空 + sceneEx.cHintCards = []int32{} + } + + if !bHandCard { + sceneEx.card_play_action_seq = append(sceneEx.card_play_action_seq, fmt.Sprintf("%v-过", playerEx.GetPos())) + sceneEx.card_play_action_seq_int32 = append(sceneEx.card_play_action_seq_int32, []int32{-1}) + nextPos := sceneEx.DoNext(int32(playerEx.GetPos())) + if sceneEx.IsTienLenToEnd() && nextPos == rule.InvalidePos { + sceneEx.UnmarkPass() + nextPos = sceneEx.DoNext(int32(playerEx.GetPos())) + sceneEx.SetLastOpPos(int32(nextPos)) + } + sceneEx.RefreshPlayerHandLimitTimeOut() + playerEx.isPass = true + if nextPos == sceneEx.lastOpPos { //一轮都不出牌 + sceneEx.TrySmallGameBilled() + } + + this.BroadcastPlayerSToCOp(s, playerEx.SnId, playerEx.GetPos(), int(rule.TienLenPlayerOpPass), tienlen.OpResultCode_OPRC_Sucess, []int64{}) + sceneEx.BroadcastOpPos() + } + } + + sceneEx.StateStartTime = time.Now() +} + +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Billed +// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +type SceneBilledStateTienLen struct { + SceneBaseStateTienLen +} + +func (this *SceneBilledStateTienLen) GetState() int { + return rule.TienLenSceneStateBilled +} + +func (this *SceneBilledStateTienLen) CanChangeTo(s base.SceneState) bool { + if s.GetState() == rule.TienLenSceneStateWaitPlayer || s.GetState() == rule.TienLenSceneStateWaitStart { + return true + } + return false +} + +func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { + this.SceneBaseStateTienLen.OnEnter(s) + this.BroadcastRoomState(s, this.GetState()) + + //在这里执行结算 + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + winScore := int64(0) + winRankScore := int64(0) + pack := &tienlen.SCTienLenGameBilled{} + tienlenType := model.TienLenType{ + GameId: sceneEx.GameId, + RoomId: int32(sceneEx.GetSceneId()), + RoomType: sceneEx.GetFreeGameSceneType(), + NumOfGames: int32(sceneEx.Scene.NumOfGames), + BankId: sceneEx.masterSnid, + PlayerCount: sceneEx.curGamingPlayerNum, + BaseScore: s.GetBaseScore(), + TaxRate: s.DbGameFree.GetTaxRate(), + RoomMode: s.GetSceneMode(), + } + + nGamingPlayerCount := sceneEx.GetGameingPlayerCnt() + if len(sceneEx.tianHuSnids) == nGamingPlayerCount { //和 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + playerEx := sceneEx.seats[i] + if playerEx == nil { + continue + } + if !playerEx.IsGameing() { + continue + } + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(playerEx.SnId), + IsWin: proto.Int32(0), + WinCoin: proto.Int64(0), + GameCoin: proto.Int64(playerEx.GetCoin()), + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + TianHu: playerEx.tianHu, + } + playerEx.CurIsWin = int64(0) + tienlenPerson := model.TienLenPerson{ + UserId: playerEx.SnId, + UserIcon: playerEx.Head, + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + WBLevel: playerEx.WBLevel, + IsRob: playerEx.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), + IsLeave: false, + IsWin: 0, + GainCoin: 0, + BombCoin: 0, + BillCoin: 0, + GainTaxCoin: 0, + BombTaxCoin: 0, + BillTaxCoin: 0, + Seat: playerEx.GetPos(), + IsTianHu: sceneEx.IsTianhuPlayer(playerEx.SnId), + TestLog: playerEx.TestLog, + CardsScore: playerEx.cardScore, + } + //排下序,正常应该客户端排序 + cards := make([]int32, rule.HandCardNum) + copy(cards, playerEx.cards[:]) + sort.Slice(cards, func(i, j int) bool { + v_i := rule.Value(int32(cards[i])) + v_j := rule.Value(int32(cards[j])) + c_i := rule.Color(int32(cards[i])) + c_j := rule.Color(int32(cards[j])) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + for _, card := range cards { + if card != rule.InvalideCard { + billData.Cards = append(billData.Cards, card) + tienlenPerson.CardInfoEnd = append(tienlenPerson.CardInfoEnd, card) + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(playerEx.Player) //和 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + } else { //输赢局 + if sceneEx.IsTienLenToEnd() && len(sceneEx.tianHuSnids) == 0 { + lastSnid := int32(0) + playerNum := 0 + for _, p := range sceneEx.players { + if p != nil && p.IsGameing() { + playerNum++ + if !common.InSliceInt32(sceneEx.winSnids, p.SnId) { + lastSnid = p.SnId + } + } + } + if lastSnid == 0 { + logger.Logger.Error("TienLenToEndGameBilled Error: lastSnid == 0") + return + } + if playerNum-1 != len(sceneEx.winSnids) { + logger.Logger.Error("TienLenToEndGameBilled Error: playerNum: ", playerNum, " this.winSnids: ", sceneEx.winSnids) + return + } + // 没出完牌的输家 + losePlayerScore := int64(0) + loseRankScore := int64(0) + losePlayer := sceneEx.players[lastSnid] + if losePlayer != nil { + var rankScore int64 // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var roleScore int64 // 角色加成 + var roleAdd int32 + var otherScore int64 // 额外总加分 + oldRankScore := losePlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()) + playerLoseScore := rule.GetLoseScore(losePlayer.cards, true) + rankScore = int64(playerLoseScore) + 100 + score := int64(float64(s.GetBaseScore()) * (float64(playerLoseScore) + 100.0) / 100.0) //手牌输分和基础 + gainScore := score + if sceneEx.IsTienLenYule() && sceneEx.bombToEnd > 0 { //娱乐版空放炸弹底分翻倍 + logger.Logger.Trace("TienLenYule,bombToEnd: ", sceneEx.bombToEnd, " SnId: ", losePlayer.SnId) + for bomb := 0; bomb < sceneEx.bombToEnd; bomb++ { + gainScore *= 2 + rankScore *= 2 + } + } + losePlayerCoin := losePlayer.GetCoin() + if !sceneEx.IsMatchScene() && losePlayerCoin < gainScore { + gainScore = losePlayerCoin + } + losePlayerScore = gainScore + if sceneEx.IsMatchScene() { //比赛场是积分,不应该增加账变 + losePlayer.AddCoinNoLog(int64(-gainScore), 0) + } else { + losePlayer.AddCoin(int64(-gainScore), common.GainWay_CoinSceneLost, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + rankScore = int64(float64(rankScore) / 100.0 * float64(-rule.RankBaseScoreToEnd)) + loseRankScore = -rankScore + rankScore += losePlayer.bombRankScore + if rankScore > 0 { + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100.0)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(losePlayer.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&losePlayer.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + } + otherScore = sceneTypeScore + vipScore + roleScore + losePlayer.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) // 炸弹分一起算 + } + losePlayer.winCoin -= gainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(losePlayer.SnId), + IsWin: proto.Int32(2), + WinCoin: proto.Int64(gainScore), + GameCoin: proto.Int64(losePlayer.GetCoin()), + RankScore: losePlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: losePlayer.tianHu, + } + billData.WinRankScore = billData.RankScore - oldRankScore + if otherScore > 0 { + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: losePlayer.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + } + isWin := int32(0) + billCoin := losePlayer.bombScore - gainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + losePlayer.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: losePlayer.SnId, + UserIcon: losePlayer.Head, + Platform: losePlayer.Platform, + Channel: losePlayer.Channel, + Promoter: losePlayer.BeUnderAgentCode, + PackageTag: losePlayer.PackageID, + InviterId: losePlayer.InviterId, + WBLevel: losePlayer.WBLevel, + IsRob: losePlayer.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(losePlayer.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: -gainScore, + BombCoin: losePlayer.bombScore, + BillCoin: billCoin, + GainTaxCoin: 0, + BombTaxCoin: losePlayer.bombTaxScore, + BillTaxCoin: losePlayer.bombTaxScore, + Seat: losePlayer.GetPos(), + IsTianHu: false, + TestLog: losePlayer.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: losePlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: losePlayer.bombRankScore, + CardsScore: losePlayer.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == losePlayer.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + //排下序,正常应该客户端排序 + cards := make([]int32, rule.HandCardNum) + copy(cards, losePlayer.cards[:]) + sort.Slice(cards, func(i, j int) bool { + v_i := rule.Value(int32(cards[i])) + v_j := rule.Value(int32(cards[j])) + c_i := rule.Color(int32(cards[i])) + c_j := rule.Color(int32(cards[j])) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + for _, card := range cards { + if card != rule.InvalideCard { + billData.Cards = append(billData.Cards, card) + tienlenPerson.CardInfoEnd = append(tienlenPerson.CardInfoEnd, card) + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(losePlayer.Player) //输家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + + lastWinPlayerScore := int64(0) + lastWinPlayerRankScore := int64(0) + if playerNum == 3 || playerNum == 4 { + // 最后一位出完牌的输家 + lastWinPlayer := sceneEx.players[sceneEx.winSnids[len(sceneEx.winSnids)-1]] + if lastWinPlayer != nil { + var rankScore int64 // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var otherScore int64 // 额外总加分 + oldRankScore := lastWinPlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()) + lastWinScore := rule.GetLoseScore(lastWinPlayer.cards, true) + rankScore = int64(lastWinScore) + 50 + lastWinscore := int64(float64(s.GetBaseScore()) * float64(int64(lastWinScore)+50) / 100.0) //手牌输分和基础 + astWinGainScore := lastWinscore + if sceneEx.IsTienLenYule() && sceneEx.bombToEnd > 0 { //娱乐版空放炸弹底分翻倍 + logger.Logger.Trace("TienLenYule,bombToEnd: ", sceneEx.bombToEnd, " SnId: ", lastWinPlayer.SnId) + for bomb := 0; bomb < sceneEx.bombToEnd; bomb++ { + astWinGainScore *= 2 + rankScore *= 2 + } + } + lastWinPlayerCoin := lastWinPlayer.GetCoin() + if !sceneEx.IsMatchScene() && lastWinPlayerCoin < astWinGainScore { + astWinGainScore = lastWinPlayerCoin + } + lastWinPlayerScore = astWinGainScore + if sceneEx.IsMatchScene() { + lastWinPlayer.AddCoinNoLog(int64(-astWinGainScore), 0) + } else { + lastWinPlayer.AddCoin(int64(-astWinGainScore), common.GainWay_CoinSceneLost, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + rankScore = int64(float64(rankScore) / 100.0 * float64(-rule.RankBaseScoreToEnd)) + lastWinPlayerRankScore = -rankScore + rankScore += lastWinPlayer.bombRankScore + if rankScore > 0 { + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100.0)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(lastWinPlayer.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&lastWinPlayer.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + } + otherScore = sceneTypeScore + vipScore + roleScore + lastWinPlayer.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) // 炸弹分一起算 + } + lastWinPlayer.winCoin -= astWinGainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(lastWinPlayer.SnId), + IsWin: proto.Int32(2), + WinCoin: proto.Int64(astWinGainScore), + GameCoin: proto.Int64(lastWinPlayer.GetCoin()), + RankScore: lastWinPlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: lastWinPlayer.tianHu, + } + billData.WinRankScore = billData.RankScore - oldRankScore + if otherScore > 0 { + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: lastWinPlayer.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + } + isWin := int32(0) + billCoin := lastWinPlayer.bombScore - astWinGainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + lastWinPlayer.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: lastWinPlayer.SnId, + UserIcon: lastWinPlayer.Head, + Platform: lastWinPlayer.Platform, + Channel: lastWinPlayer.Channel, + Promoter: lastWinPlayer.BeUnderAgentCode, + PackageTag: lastWinPlayer.PackageID, + InviterId: lastWinPlayer.InviterId, + WBLevel: lastWinPlayer.WBLevel, + IsRob: lastWinPlayer.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(lastWinPlayer.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: -astWinGainScore, + BombCoin: lastWinPlayer.bombScore, + BillCoin: billCoin, + GainTaxCoin: 0, + BombTaxCoin: lastWinPlayer.bombTaxScore, + BillTaxCoin: lastWinPlayer.bombTaxScore, + Seat: lastWinPlayer.GetPos(), + IsTianHu: false, + TestLog: lastWinPlayer.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: lastWinPlayer.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: lastWinPlayer.bombRankScore, + CardsScore: lastWinPlayer.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == lastWinPlayer.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(lastWinPlayer.Player) //输家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + } + + //first + playerEx := sceneEx.players[sceneEx.winSnids[0]] + if playerEx != nil { + var rankScore int64 // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var otherScore int64 // 额外总加分 + oldRankScore := playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()) + rankScore = loseRankScore + taxRate := sceneEx.DbGameFree.GetTaxRate() //万分比 + gainScore := int64(float64(losePlayerScore) * float64(10000-taxRate) / 10000.0) //税后 + gainTaxScore := losePlayerScore - gainScore // 税收 + if playerNum == 3 { + gainScore = int64(float64(losePlayerScore+lastWinPlayerScore) * float64(10000-taxRate) / 10000.0) //税后 + gainTaxScore = losePlayerScore + lastWinPlayerScore - gainScore + rankScore = loseRankScore + lastWinPlayerRankScore + } + if sceneEx.IsMatchScene() { + playerEx.AddCoinNoLog(int64(gainScore), 0) + } else { + playerEx.AddCoin(gainScore, common.GainWay_CoinSceneWin, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + rankScore += playerEx.bombRankScore + if rankScore > 0 { + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100.0)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + } + otherScore = sceneTypeScore + vipScore + roleScore + playerEx.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) // 炸弹分一起算 + } + playerEx.winCoin += gainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(playerEx.SnId), + IsWin: proto.Int32(1), + WinCoin: proto.Int64(gainScore), + GameCoin: proto.Int64(playerEx.GetCoin()), + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: playerEx.tianHu, + } + billData.WinRankScore = billData.RankScore - oldRankScore + if otherScore > 0 { + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: playerEx.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + } + isWin := int32(0) + billCoin := playerEx.bombScore + gainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + playerEx.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: playerEx.SnId, + UserIcon: playerEx.Head, + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + WBLevel: playerEx.WBLevel, + IsRob: playerEx.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: gainScore, + BombCoin: playerEx.bombScore, + BillCoin: billCoin, + GainTaxCoin: gainTaxScore, + BombTaxCoin: playerEx.bombTaxScore, + BillTaxCoin: playerEx.bombTaxScore + gainTaxScore, + Seat: playerEx.GetPos(), + IsTianHu: sceneEx.IsTianhuPlayer(playerEx.SnId), + TestLog: playerEx.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: playerEx.bombRankScore, + CardsScore: playerEx.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == playerEx.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(playerEx.Player) //赢家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + //second + if playerNum == 4 { + playerEx = sceneEx.players[sceneEx.winSnids[1]] + if playerEx != nil { + var rankScore int64 // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var otherScore int64 // 额外总加分 + oldRankScore := playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()) + rankScore = lastWinPlayerRankScore + taxRate := sceneEx.DbGameFree.GetTaxRate() //万分比 + gainScore := int64(float64(lastWinPlayerScore) * float64(10000-taxRate) / 10000.0) //税后 + gainTaxScore := lastWinPlayerScore - gainScore + if sceneEx.IsMatchScene() { + playerEx.AddCoinNoLog(int64(gainScore), 0) + } else { + playerEx.AddCoin(gainScore, common.GainWay_CoinSceneWin, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + rankScore += playerEx.bombRankScore + if rankScore > 0 { + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100.0)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + } + otherScore = sceneTypeScore + vipScore + roleScore + playerEx.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) // 炸弹分一起算 + } + playerEx.winCoin += gainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(playerEx.SnId), + IsWin: proto.Int32(1), + WinCoin: proto.Int64(gainScore), + GameCoin: proto.Int64(playerEx.GetCoin()), + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: playerEx.tianHu, + } + billData.WinRankScore = billData.RankScore - oldRankScore + if otherScore > 0 { + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: playerEx.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + } + isWin := int32(0) + billCoin := playerEx.bombScore + gainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + playerEx.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: playerEx.SnId, + UserIcon: playerEx.Head, + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + WBLevel: playerEx.WBLevel, + IsRob: playerEx.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: gainScore, + BombCoin: playerEx.bombScore, + BillCoin: billCoin, + GainTaxCoin: gainTaxScore, + BombTaxCoin: playerEx.bombTaxScore, + BillTaxCoin: playerEx.bombTaxScore + gainTaxScore, + Seat: playerEx.GetPos(), + IsTianHu: sceneEx.IsTianhuPlayer(playerEx.SnId), + TestLog: playerEx.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: playerEx.bombRankScore, + CardsScore: playerEx.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == playerEx.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(playerEx.Player) //赢家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + } + } + } else { + // 输家 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + var rankScore int64 // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var roleScore int64 // 角色加成 + var roleAdd int32 + var otherScore int64 // 额外总加分 + playerEx := sceneEx.seats[i] + if playerEx == nil { + continue + } + oldRankScore := playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()) + if !playerEx.IsGameing() { + continue + } + if sceneEx.IsTianhuPlayer(playerEx.SnId) { + continue + } + if sceneEx.IsWinPlayer(playerEx.SnId) && !sceneEx.IsTienLenToEnd() { + continue + } + logger.Logger.Trace("SceneBilledStateTienLe,losePos: ", i, " SnId: ", playerEx.SnId, " BaseScore: ", s.GetBaseScore()) + playerLoseScore := rule.GetLoseScore(playerEx.cards, sceneEx.IsTienLenToEnd()) + score := int64(s.GetBaseScore()) * int64(playerLoseScore) + rankScore = int64(playerLoseScore) + gainScore := int64(0) + if len(sceneEx.tianHuSnids) != 0 { //天胡结算翻倍 + gainScore = int64(score) * int64(len(sceneEx.tianHuSnids)) * 2 + rankScore = int64(playerLoseScore) * int64(len(sceneEx.tianHuSnids)) * 2 + if sceneEx.IsTienLenToEnd() { //打到底天胡结算加上2倍底注(不是翻倍) + gainScore = int64(float64(score)/100.0 + float64(s.GetBaseScore()*2)) + rankScore = int64(playerLoseScore) + 200 + } + } else { //正常结算 + gainScore = int64(score) + if sceneEx.IsTienLenYule() && sceneEx.bombToEnd > 0 { //娱乐版空放炸弹底分翻倍 + logger.Logger.Trace("TienLenYule,bombToEnd: ", sceneEx.bombToEnd, " SnId: ", playerEx.SnId) + for bomb := 0; bomb < sceneEx.bombToEnd; bomb++ { + gainScore *= 2 + rankScore *= 2 + } + } + } + losePlayerCoin := playerEx.GetCoin() + if !sceneEx.IsMatchScene() && losePlayerCoin < gainScore { + gainScore = losePlayerCoin + } + winScore += gainScore + if sceneEx.IsMatchScene() { + playerEx.AddCoinNoLog(int64(-gainScore), 0) + } else { + playerEx.AddCoin(int64(-gainScore), common.GainWay_CoinSceneLost, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + // 排位积分 + if sceneEx.IsTienLenToEnd() { + rankScore = int64(float64(rankScore) / 100.0 * float64(-rule.RankBaseScoreToEnd)) + } else { + rankScore *= -rule.RankBaseScore + } + winRankScore += -rankScore // 不含炸弹分 + rankScore += playerEx.bombRankScore + if rankScore > 0 { + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + } + otherScore = sceneTypeScore + vipScore + roleScore + playerEx.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) // 炸弹分一起算 + } + playerEx.winCoin -= gainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(playerEx.SnId), + IsWin: proto.Int32(2), + WinCoin: proto.Int64(gainScore), + GameCoin: proto.Int64(playerEx.GetCoin()), + //WinRankScore: -rankScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: playerEx.tianHu, + } + billData.WinRankScore = billData.RankScore - oldRankScore + if otherScore > 0 { + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: playerEx.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + } + + isWin := int32(0) + billCoin := playerEx.bombScore - gainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + playerEx.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: playerEx.SnId, + UserIcon: playerEx.Head, + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + WBLevel: playerEx.WBLevel, + IsRob: playerEx.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: -gainScore, + BombCoin: playerEx.bombScore, + BillCoin: billCoin, + GainTaxCoin: 0, + BombTaxCoin: playerEx.bombTaxScore, + BillTaxCoin: playerEx.bombTaxScore, + Seat: playerEx.GetPos(), + IsTianHu: false, + TestLog: playerEx.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: playerEx.bombRankScore, + CardsScore: playerEx.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == playerEx.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + //排下序,正常应该客户端排序 + cards := make([]int32, rule.HandCardNum) + copy(cards, playerEx.cards[:]) + sort.Slice(cards, func(i, j int) bool { + v_i := rule.Value(int32(cards[i])) + v_j := rule.Value(int32(cards[j])) + c_i := rule.Color(int32(cards[i])) + c_j := rule.Color(int32(cards[j])) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + for _, card := range cards { + if card != rule.InvalideCard { + billData.Cards = append(billData.Cards, card) + tienlenPerson.CardInfoEnd = append(tienlenPerson.CardInfoEnd, card) + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(playerEx.Player) //输家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + logger.Logger.Trace("SceneBilledStateTienLe, winSnids: ", sceneEx.winSnids, " winScore: ", winScore) + + //赢家 + if len(sceneEx.tianHuSnids) != 0 { + winScore = winScore / int64(len(sceneEx.tianHuSnids)) + winRankScore = winRankScore / int64(len(sceneEx.tianHuSnids)) + } + for _, winSnid := range sceneEx.winSnids { + var rankScore = winRankScore // 排位积分 + var sceneTypeScore int64 // 场次额外积分 + var vipScore int64 // vip加成 + var roleScore int64 // 角色加成 + var roleAdd int32 + var otherScore int64 // 额外总加分 + playerEx := sceneEx.players[winSnid] + if playerEx != nil { + taxRate := sceneEx.DbGameFree.GetTaxRate() //万分比 + gainScore := int64(float64(winScore) * float64(10000-taxRate) / 10000.0) //税后 + gainTaxScore := winScore - gainScore + if sceneEx.IsMatchScene() { + playerEx.AddCoinNoLog(int64(gainScore), 0) + } else { + playerEx.AddCoin(gainScore, common.GainWay_CoinSceneWin, 0, "system", s.GetSceneName()) + } + if sceneEx.IsRankMatch() { + // 排位积分 + rankScore += playerEx.bombRankScore + // 场次加成分 + sceneTypeScore = int64(math.Ceil(float64(rankScore) * float64(sceneEx.GetDBGameFree().GetSceneAdd()) / 100.0)) // 场次加成 + // vip加成分 + vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) + // 角色加成分 + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) + otherScore = sceneTypeScore + vipScore + roleScore + playerEx.AddRankScore(sceneEx.GetDBGameFree().GetRankType(), rankScore+otherScore) + } + playerEx.winCoin += gainScore + billData := &tienlen.TienLenPlayerGameBilled{ + SnId: proto.Int32(playerEx.SnId), + IsWin: proto.Int32(1), + WinCoin: proto.Int64(gainScore), + GameCoin: proto.Int64(playerEx.GetCoin()), + WinRankScore: rankScore + otherScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + TianHu: playerEx.tianHu, + } + // 场次加成配置 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 1, + ItemId: 0, + Addition: sceneEx.GetDBGameFree().GetSceneAdd(), + Score: sceneTypeScore, + }) + // vip加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 5, + ItemId: 0, + Addition: playerEx.VipExtra, + Score: vipScore, + }) + // 角色加成 + billData.AddItems = append(billData.AddItems, &tienlen.AddItem{ + ItemType: 4, + ItemId: 0, + Addition: roleAdd, + Score: roleScore, + }) + + isWin := int32(0) + billCoin := playerEx.bombScore + gainScore + if billCoin > 0 { + isWin = 1 + } else if billCoin < 0 { + isWin = -1 + } + playerEx.CurIsWin = int64(isWin) + tienlenPerson := model.TienLenPerson{ + UserId: playerEx.SnId, + UserIcon: playerEx.Head, + Platform: playerEx.Platform, + Channel: playerEx.Channel, + Promoter: playerEx.BeUnderAgentCode, + PackageTag: playerEx.PackageID, + InviterId: playerEx.InviterId, + WBLevel: playerEx.WBLevel, + IsRob: playerEx.IsRob, + IsFirst: sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), + IsLeave: false, + IsWin: isWin, + GainCoin: gainScore, + BombCoin: playerEx.bombScore, + BillCoin: billCoin, + GainTaxCoin: gainTaxScore, + BombTaxCoin: playerEx.bombTaxScore, + BillTaxCoin: playerEx.bombTaxScore + gainTaxScore, + Seat: playerEx.GetPos(), + IsTianHu: sceneEx.IsTianhuPlayer(playerEx.SnId), + TestLog: playerEx.TestLog, + WinRankScore: rankScore + otherScore, + RankScore: playerEx.GetRankScore(sceneEx.GetDBGameFree().GetRankType()), + AddScore: otherScore, + BombRankScore: playerEx.bombRankScore, + CardsScore: playerEx.cardScore, + } + tienlenPerson.DelOrderCards = make(map[int][]int32, len(sceneEx.delOrders)) + for i2, orderSnid := range sceneEx.delOrders { + if orderSnid == playerEx.SnId { + tienlenPerson.DelOrderCards[i2] = sceneEx.delCards[i2] + } + } + //排下序,正常应该客户端排序 + cards := make([]int32, rule.HandCardNum) + copy(cards, playerEx.cards[:]) + sort.Slice(cards, func(i, j int) bool { + v_i := rule.Value(int32(cards[i])) + v_j := rule.Value(int32(cards[j])) + c_i := rule.Color(int32(cards[i])) + c_j := rule.Color(int32(cards[j])) + if v_i > v_j { + return false + } else if v_i == v_j { + return c_i < c_j + } + return true + }) + for _, card := range cards { + if card != rule.InvalideCard { + billData.Cards = append(billData.Cards, card) + tienlenPerson.CardInfoEnd = append(tienlenPerson.CardInfoEnd, card) + } + } + pack.Datas = append(pack.Datas, billData) + sceneEx.TryBillExGameDrop(playerEx.Player) //赢家 + tienlenType.PlayerData = append(tienlenType.PlayerData, tienlenPerson) + } + } + } + } + + proto.SetDefaults(pack) + s.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenGameBilled), pack, 0) + logger.Logger.Trace("TienLenPacketID_PACKET_SCTienLenGameBilled gameFreeId:", sceneEx.GetGameFreeId(), ";pack:", pack) + + // 牌局记录 + info, err := model.MarshalGameNoteByFIGHT(&tienlenType) + if err == nil { + isSave := false + for _, o_player := range tienlenType.PlayerData { + if !sceneEx.Testing && !o_player.IsRob { + isSave = true + var totalin, totalout int64 + if o_player.GainCoin < 0 { + totalin -= (o_player.GainCoin + o_player.GainTaxCoin) + } else { + totalout += (o_player.GainCoin + o_player.GainTaxCoin) + } + if o_player.BombCoin < 0 { + totalin -= (o_player.BombCoin + o_player.BombTaxCoin) + } else { + totalout += (o_player.BombCoin + o_player.BombTaxCoin) + } + + validFlow := totalin + totalout + validBet := common.AbsI64(totalin - totalout) + sceneEx.SaveFriendRecord(o_player.UserId, o_player.IsWin, o_player.BillCoin, sceneEx.GetBaseScore()) + + // 玩家数据统计 + winState := int32(0) + if o_player.IsWin == 1 { + winState = 1 + } else if o_player.IsWin == 2 { + winState = 2 + } else { + winState = 3 + } + sceneEx.Statistics(&base.StaticParam{ + SnId: o_player.UserId, + Gain: o_player.GainCoin + o_player.BombCoin, + GainTax: o_player.GainTaxCoin + o_player.BombTaxCoin, + IsAddTimes: true, + HasRobotGaming: sceneEx.robotGamingNum > 0, + WinState: winState, + }) + + // 保存玩家游戏记录 + sceneEx.SaveGamePlayerListLog(o_player.UserId, + base.GetSaveGamePlayerListLogParam(o_player.Platform, o_player.Channel, o_player.Promoter, + o_player.PackageTag, sceneEx.recordId, o_player.InviterId, totalin, totalout, o_player.BillTaxCoin, + 0, 0, o_player.GainCoin+o_player.BombCoin, validBet, validFlow, o_player.IsFirst, o_player.IsLeave)) + } + } + if isSave { + // 牌局记录 + sceneEx.SaveGameDetailedLog(sceneEx.recordId, info, &base.GameDetailedParam{}) + } + } + + if s.IsMatchScene() { //记录比赛数据 + var matchRobotGrades []base.MatchRobotGrade + var realPlayers []*base.Player + for _, p := range sceneEx.players { + if p != nil && p.IsGameing() { + if p.IsRob { + CopySnid := p.SnId + if len(p.MatchParams) > 2 && p.MatchParams[2] != 0 { + CopySnid = p.MatchParams[2] + } + mrg := &base.MatchRobotGrade{ + CopySnid: CopySnid, + Grade: int32(p.Coin), + } + matchRobotGrades = append(matchRobotGrades, *mrg) + } else { + p.MatchRobotGrades = nil + realPlayers = append(realPlayers, p.Player) + } + } + + } + if realPlayers != nil && len(realPlayers) == 1 && matchRobotGrades != nil && len(matchRobotGrades) > 0 { + //真人把机器人记录带出去 + realPlayers[0].MatchRobotGrades = matchRobotGrades + } + } + if sceneEx.isCardsKu { + playerEx := sceneEx.players[sceneEx.winSnids[0]] + logger.Logger.Infof("telnet使用牌库,模式:是否娱乐 %t,随机到的牌库id = %d,赢家是否是机器人:%t,赢家snid = %d\n", sceneEx.IsTienLenYule(), sceneEx.cardsKuId, playerEx.IsRob, playerEx.SnId) + } + sceneEx.isCardsKu = false + sceneEx.cardsKuId = -1 + if !sceneEx.IsRankMatch() { + sceneEx.NotifySceneRoundPause() + } + } +} + +// 状态离开时 +func (this *SceneBilledStateTienLen) OnLeave(s *base.Scene) { + this.SceneBaseStateTienLen.OnLeave(s) + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + sceneEx.lastGamingPlayerNum = sceneEx.curGamingPlayerNum + sceneEx.curGamingPlayerNum = 0 + if len(sceneEx.tianHuSnids) != sceneEx.GetGameingPlayerCnt() { //非和局 + sceneEx.lastWinSnid = sceneEx.winSnids[0] + } + sceneEx.SetGaming(false) + + hasLeave := false + //剔除下线玩家 + for i := 0; i < sceneEx.GetPlayerNum(); i++ { + player_data := sceneEx.seats[i] + if player_data == nil { + continue + } + player_data.Clear() + if sceneEx.IsMatchScene() { + continue + } + if !player_data.IsOnLine() { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_DropLine, true) + hasLeave = true + continue + } + if player_data.IsRob { + if player_data.robotGameTimes <= 0 { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true) + hasLeave = true + continue + } + if s.CoinOverMaxLimit(player_data.GetCoin(), player_data.Player) { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true) + hasLeave = true + continue + } + } + if !s.CoinInLimit(player_data.GetCoin()) { + sceneEx.PlayerLeave(player_data.Player, s.NotCoinInLimitType(player_data.GetCoin()), true) + hasLeave = true + continue + } + + if player_data.IsAuto() && !player_data.IsRob { + player_data.LeaveAutoState(s) + + if !(s.IsMatchScene() || s.IsRankMatch()) { + sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_AutoState, true) + hasLeave = true + } + continue + } + } + + if !hasLeave && !sceneEx.IsRobFightGame() && !sceneEx.IsMatchScene() { + s.TryDismissRob() + } + + if s.CheckNeedDestroy() || (s.IsMatchScene() && (!s.MatchFinals || (s.MatchFinals && s.NumOfGames >= 2))) { // 非决赛打一场 决赛打两场 + sceneEx.SceneDestroy(true) + } + s.RankMatchDestroy() + s.TryRelease() + } +} + +// 玩家操作 +func (this *SceneBilledStateTienLen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool { + if this.SceneBaseStateTienLen.OnPlayerOp(s, p, opcode, params) { + return true + } + return true +} + +// 玩家事件 +func (this *SceneBilledStateTienLen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) { + this.SceneBaseStateTienLen.OnPlayerEvent(s, p, evtcode, params) +} + +func (this *SceneBilledStateTienLen) OnTick(s *base.Scene) { + this.SceneBaseStateTienLen.OnTick(s) + + if sceneEx, ok := s.GetExtraData().(*TienLenSceneData); ok { + newTime := rule.TienLenBilledTimeout + if sceneEx.isAllRob { + newTime = time.Second * 1 + } + n := len(sceneEx.tianHuSnids) + newTime += time.Duration(n) * rule.TIenLenTianhuTimeout + if time.Now().Sub(sceneEx.StateStartTime) > newTime { + //开始前再次检查开始条件 + if sceneEx.CanStart() == true { + s.ChangeSceneState(rule.TienLenSceneStateWaitStart) + } else { + s.ChangeSceneState(rule.TienLenSceneStateWaitPlayer) + } + } + } +} + +// ////////////////////////////////////////////////////////////////////////////// +func (this *ScenePolicyTienLen) RegisteSceneState(state base.SceneState) { + if state == nil { + return + } + stateid := state.GetState() + if stateid < 0 || stateid >= rule.TienLenSceneStateMax { + return + } + this.states[stateid] = state +} + +func (this *ScenePolicyTienLen) GetSceneState(s *base.Scene, stateid int) base.SceneState { + if stateid >= 0 && stateid < rule.TienLenSceneStateMax { + return this.states[stateid] + } + return nil +} + +func init() { + ScenePolicyTienLenSingleton.RegisteSceneState(&SceneWaitPlayerStateTienLen{}) + ScenePolicyTienLenSingleton.RegisteSceneState(&SceneWaitStartStateTienLen{}) + ScenePolicyTienLenSingleton.RegisteSceneState(&SceneHandCardStateTienLen{}) + ScenePolicyTienLenSingleton.RegisteSceneState(&ScenePlayerOpStateTienLen{}) + ScenePolicyTienLenSingleton.RegisteSceneState(&SceneBilledStateTienLen{}) + + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + // 自由桌 + base.RegisteScenePolicy(common.GameId_TienLen, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLen_yl, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLen_toend, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLen_yl_toend, 0, ScenePolicyTienLenSingleton) + // 比赛 + base.RegisteScenePolicy(common.GameId_TienLen_m, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLen_m_toend, 0, ScenePolicyTienLenSingleton) + // 手动选场 + base.RegisteScenePolicy(common.GameId_TienLenSelect, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenSelect_toend, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenSelect_yl, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenSelect_yl_toend, 0, ScenePolicyTienLenSingleton) + // 排位赛 + base.RegisteScenePolicy(common.GameId_TienLenRank, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenRank_toend, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenRank_yl, 0, ScenePolicyTienLenSingleton) + base.RegisteScenePolicy(common.GameId_TienLenRank_yl_toend, 0, ScenePolicyTienLenSingleton) + return nil + }) +} diff --git a/gamesrv/transact/transact_addcoin.go b/gamesrv/transact/transact_addcoin.go new file mode 100644 index 0000000..99267b5 --- /dev/null +++ b/gamesrv/transact/transact_addcoin.go @@ -0,0 +1,80 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" +) + +const ( + TRANSACT_ADDCOIN_CTX = iota +) + +type AddCoinTransactHandler struct { +} + +func (this *AddCoinTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnExcute ") + ctx := &common.WGAddCoin{} + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), ctx) + if err != nil { + logger.Logger.Trace("AddCoinTransactHandler.OnExcute failed:", err) + return transact.TransExeResult_Failed + } + p := base.PlayerMgrSington.GetPlayerBySnId(ctx.SnId) + if p != nil { + s := p.GetScene() + if s != nil { + sp := s.GetScenePolicy() + if sp != nil && sp.CanAddCoin(s, p, ctx.Coin) { + p.AddCoinAsync(ctx.Coin, ctx.GainWay, true, ctx.Broadcast, ctx.Oper, ctx.Remark, ctx.WriteLog) + //触发下事件 + sp.OnPlayerEvent(s, p, base.PlayerEventRecharge, []int64{ctx.Coin}) + // + tNode.TransEnv.SetField(TRANSACT_ADDCOIN_CTX, ctx) + tNode.TransRep.RetFiels = p.Coin + return transact.TransExeResult_Success + } + } + } + + return transact.TransExeResult_Failed +} + +func (this *AddCoinTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *AddCoinTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnRollBack ") + ud := tNode.TransEnv.GetField(TRANSACT_ADDCOIN_CTX) + if ctx, ok := ud.(*common.WGAddCoin); ok { + p := base.PlayerMgrSington.GetPlayerBySnId(ctx.SnId) + if p != nil { + s := p.GetScene() + if s != nil { + sp := s.GetScenePolicy() + if sp != nil && sp.CanAddCoin(s, p, -ctx.Coin) { + p.AddCoinAsync(-ctx.Coin, ctx.GainWay, true, ctx.Broadcast, ctx.Oper, ctx.Remark, ctx.WriteLog) + //触发下事件 + sp.OnPlayerEvent(s, p, base.PlayerEventRecharge, []int64{0}) + return transact.TransExeResult_Success + } + } + } + } + return transact.TransExeResult_Success +} + +func (this *AddCoinTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_AddCoin, &AddCoinTransactHandler{}) +} diff --git a/gamesrv/transact/transact_coinscenechange.go b/gamesrv/transact/transact_coinscenechange.go new file mode 100644 index 0000000..4144e45 --- /dev/null +++ b/gamesrv/transact/transact_coinscenechange.go @@ -0,0 +1,66 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" +) + +var CoinSceneChangePacket = &common.WGCoinSceneChange{} + +type CoinSceneChangeTransactHandler struct { +} + +func (this *CoinSceneChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnExcute ") + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), CoinSceneChangePacket) + if err == nil { + player := base.PlayerMgrSington.GetPlayerBySnId(CoinSceneChangePacket.SnId) + if player == nil { + logger.Logger.Tracef("CoinSceneChangeTransactHandler.OnExcute player == nil snid=%v", CoinSceneChangePacket.SnId) + return transact.TransExeResult_Success + } + + if player.GetScene() == nil { + logger.Logger.Tracef("CoinSceneChangeTransactHandler.OnExcute player.GetScene() == nil snid=%v expect=%v", CoinSceneChangePacket.SnId, CoinSceneChangePacket.SceneId) + return transact.TransExeResult_Success + } + + if !player.GetScene().CanChangeCoinScene(player) { + logger.Logger.Tracef("CoinSceneChangeTransactHandler.OnExcute !GetScene().CanChangeCoinScene snid=%v sceneid=%v state=%v", CoinSceneChangePacket.SnId, player.GetScene().SceneId, player.GetScene().SceneState.GetState()) + return transact.TransExeResult_Failed + } + + if player.GetScene().HasPlayer(player) { + player.GetScene().PlayerLeave(player, common.PlayerLeaveReason_ChangeCoinScene, true) + } else { + player.GetScene().AudienceLeave(player, common.PlayerLeaveReason_ChangeCoinScene) + } + + return transact.TransExeResult_Success + } + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnExcute failed") + return transact.TransExeResult_Failed +} + +func (this *CoinSceneChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *CoinSceneChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *CoinSceneChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_CoinSceneChange, &CoinSceneChangeTransactHandler{}) +} diff --git a/gamesrv/transact/transact_daytimechange.go b/gamesrv/transact/transact_daytimechange.go new file mode 100644 index 0000000..1dee438 --- /dev/null +++ b/gamesrv/transact/transact_daytimechange.go @@ -0,0 +1,110 @@ +package transact + +import ( + "container/list" + "fmt" + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" +) + +type DayTimeChangeListener interface { + OnMiniTimer() + OnHourTimer() + OnDayTimer() + OnWeekTimer() + OnMonthTimer() +} + +var DayTimeChangeListeners = list.New() +var WGDayTimeChangePack = &common.WGDayTimeChange{} +var LastDayTimeRec = common.WGDayTimeChange{} + +func RegisteDayTimeChangeListener(lis DayTimeChangeListener) { + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if e.Value == lis { + panic(fmt.Sprintf("RegisteDayTimeChangeListener repeated : %v", lis)) + } + } + DayTimeChangeListeners.PushBack(lis) +} + +type DayTimeChangeTransactHandler struct { +} + +func (this *DayTimeChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), WGDayTimeChangePack) + if err == nil { + if LastDayTimeRec.LastMin != WGDayTimeChangePack.LastMin { + LastDayTimeRec.LastMin = WGDayTimeChangePack.LastMin + //SceneMgrSington.OnMiniTimer() + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if lis, ok := e.Value.(DayTimeChangeListener); ok { + utils.CatchPanic(func() { lis.OnMiniTimer() }) + } + } + } + if LastDayTimeRec.LastHour != WGDayTimeChangePack.LastHour { + LastDayTimeRec.LastHour = WGDayTimeChangePack.LastHour + //SceneMgrSington.OnHourTimer() + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if lis, ok := e.Value.(DayTimeChangeListener); ok { + utils.CatchPanic(func() { lis.OnHourTimer() }) + } + } + } + if LastDayTimeRec.LastDay != WGDayTimeChangePack.LastDay { + LastDayTimeRec.LastDay = WGDayTimeChangePack.LastDay + //SceneMgrSington.OnDayTimer() + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if lis, ok := e.Value.(DayTimeChangeListener); ok { + utils.CatchPanic(func() { lis.OnDayTimer() }) + } + } + } + if LastDayTimeRec.LastWeek != WGDayTimeChangePack.LastWeek { + LastDayTimeRec.LastWeek = WGDayTimeChangePack.LastWeek + //SceneMgrSington.OnWeekTimer() + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if lis, ok := e.Value.(DayTimeChangeListener); ok { + utils.CatchPanic(func() { lis.OnWeekTimer() }) + } + } + } + if LastDayTimeRec.LastMonth != WGDayTimeChangePack.LastMonth { + LastDayTimeRec.LastMonth = WGDayTimeChangePack.LastMonth + //SceneMgrSington.OnMonthTimer() + for e := DayTimeChangeListeners.Front(); e != nil; e = e.Next() { + if lis, ok := e.Value.(DayTimeChangeListener); ok { + utils.CatchPanic(func() { lis.OnMonthTimer() }) + } + } + } + } + + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_DayTimeChange, &DayTimeChangeTransactHandler{}) + RegisteDayTimeChangeListener(base.CoinPoolMgr) + RegisteDayTimeChangeListener(base.SceneMgrSington) +} diff --git a/gamesrv/transact/transact_matchscenechange.go b/gamesrv/transact/transact_matchscenechange.go new file mode 100644 index 0000000..b990452 --- /dev/null +++ b/gamesrv/transact/transact_matchscenechange.go @@ -0,0 +1,66 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" +) + +var MatchSceneChangePacket = &common.WGCoinSceneChange{} + +type MatchSceneChangeTransactHandler struct { +} + +func (this *MatchSceneChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnExcute ") + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), MatchSceneChangePacket) + if err == nil { + player := base.PlayerMgrSington.GetPlayerBySnId(MatchSceneChangePacket.SnId) + if player == nil { + logger.Logger.Tracef("MatchSceneChangeTransactHandler.OnExcute player == nil snid=%v", MatchSceneChangePacket.SnId) + return transact.TransExeResult_Success + } + + if player.GetScene() == nil { + logger.Logger.Tracef("MatchSceneChangeTransactHandler.OnExcute player.scene == nil snid=%v expect=%v", MatchSceneChangePacket.SnId, MatchSceneChangePacket.SceneId) + return transact.TransExeResult_Success + } + + if !player.GetScene().CanChangeCoinScene(player) { + logger.Logger.Tracef("MatchSceneChangeTransactHandler.OnExcute !scene.CanChangeCoinScene snid=%v sceneid=%v state=%v", MatchSceneChangePacket.SnId, player.GetScene().SceneId, player.GetScene().SceneState.GetState()) + return transact.TransExeResult_Failed + } + + if player.GetScene().HasPlayer(player) { + player.GetScene().PlayerLeave(player, common.PlayerLeaveReason_OnBilled, true) + } else { + player.GetScene().AudienceLeave(player, common.PlayerLeaveReason_OnBilled) + } + + return transact.TransExeResult_Success + } + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnExcute failed") + return transact.TransExeResult_Failed +} + +func (this *MatchSceneChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *MatchSceneChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *MatchSceneChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_MatchSceneChange, &MatchSceneChangeTransactHandler{}) +} diff --git a/gamesrv/transact/transact_minigameaddcoin.go b/gamesrv/transact/transact_minigameaddcoin.go new file mode 100644 index 0000000..0199dcc --- /dev/null +++ b/gamesrv/transact/transact_minigameaddcoin.go @@ -0,0 +1,81 @@ +package transact + +// +//import ( +// "mongo.games.com/game/common" +// "mongo.games.com/game/gamesrv/base" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +// "mongo.games.com/goserver/core/transact" +//) +// +//const ( +// TRANSACT_MINIGAMEADDCOIN_CTX = iota +//) +// +//type MiniGameAddCoinTransactHandler struct { +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnExcute ") +// ctx := &common.WGAddCoin{} +// err := netlib.UnmarshalPacketNoPackId(ud.([]byte), ctx) +// if err != nil { +// logger.Logger.Trace("AddCoinTransactHandler.OnExcute failed:", err) +// return transact.TransExeResult_Failed +// } +// p := base.PlayerMgrSington.GetPlayerBySnId(ctx.SnId) +// if p != nil { +// s := p.GetScene() +// if s != nil { +// sp := s.GetScenePolicy() +// if sp != nil && sp.CanAddCoin(s, p, ctx.Coin) { +// p.AddCoinAsync(ctx.Coin, ctx.GainWay, true, ctx.Broadcast, ctx.Oper, ctx.Remark, ctx.WriteLog) +// //触发下事件 +// sp.OnPlayerEvent(s, p, base.PlayerEventAddCoin, []int64{ctx.Coin}) +// // +// tNode.TransEnv.SetField(TRANSACT_MINIGAMEADDCOIN_CTX, ctx) +// tNode.TransRep.RetFiels = p.Coin +// return transact.TransExeResult_Success +// } +// } +// } +// +// return transact.TransExeResult_Failed +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnCommit ") +// return transact.TransExeResult_Success +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnRollBack ") +// ud := tNode.TransEnv.GetField(TRANSACT_ADDCOIN_CTX) +// if ctx, ok := ud.(*common.WGAddCoin); ok { +// p := base.PlayerMgrSington.GetPlayerBySnId(ctx.SnId) +// if p != nil { +// s := p.GetScene() +// if s != nil { +// sp := s.GetScenePolicy() +// if sp != nil && sp.CanAddCoin(s, p, -ctx.Coin) { +// p.AddCoinAsync(-ctx.Coin, ctx.GainWay, true, ctx.Broadcast, ctx.Oper, ctx.Remark, ctx.WriteLog) +// //触发下事件 +// sp.OnPlayerEvent(s, p, base.PlayerEventAddCoin, []int64{-ctx.Coin}) +// return transact.TransExeResult_Success +// } +// } +// } +// } +// return transact.TransExeResult_Success +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, +// retCode int, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnChildTransRep ") +// return transact.TransExeResult_Success +//} +// +//func init() { +// transact.RegisteHandler(common.TransType_MiniGameAddCoin, &MiniGameAddCoinTransactHandler{}) +//} diff --git a/gamesrv/transact/transact_queryallcoinpoolstates.go b/gamesrv/transact/transact_queryallcoinpoolstates.go new file mode 100644 index 0000000..a897838 --- /dev/null +++ b/gamesrv/transact/transact_queryallcoinpoolstates.go @@ -0,0 +1,58 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "time" +) + +var QueryAllCoinPoolTimeOut = time.Second * 30 + +const ( + QueryAllCoinPoolTransactParam_ParentNode int = iota + QueryAllCoinPoolTransactParam_Data +) + +type QueryAllCoinPoolTransactHandler struct { +} + +func (this *QueryAllCoinPoolTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnExcute ") + PlatformStates := make(map[string]*common.PlatformStates) + pfs := &common.QueryGames{} + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), pfs) + if err == nil { + for pfId, game := range pfs.Index { + // pfId 为paltform ID p为=该平台下的所有开启的游戏 + pf := &common.PlatformStates{} + pf.Platform = pfId + settings := base.CoinPoolMgr.GetCoinPoolStatesByPlatform(pfId, game) + pf.GamesVal = settings + PlatformStates[pfId] = pf + } + } + tNode.TransRep.RetFiels = PlatformStates + return transact.TransExeResult_Success +} + +func (this *QueryAllCoinPoolTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *QueryAllCoinPoolTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *QueryAllCoinPoolTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_QueryAllCoinPool, &QueryAllCoinPoolTransactHandler{}) +} diff --git a/gamesrv/transact/transact_querycoinpool.go b/gamesrv/transact/transact_querycoinpool.go new file mode 100644 index 0000000..250ea3d --- /dev/null +++ b/gamesrv/transact/transact_querycoinpool.go @@ -0,0 +1,51 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "time" +) + +var QueryCoinPoolTimeOut = time.Second * 30 + +const ( + QueryCoinPoolTransactParam_ParentNode int = iota + QueryCoinPoolTransactParam_Data +) + +type QueryCoinPoolTransactHandler struct { +} + +func (this *QueryCoinPoolTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("QueryCoinPoolTransactHandler.OnExcute ") + pack := &common.W2GQueryCoinPool{} + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), pack) + if err == nil { + settings := base.CoinPoolMgr.GetCoinPoolSettingByGame(pack.Platform, pack.GameId, pack.GameMode, pack.GroupId) + tNode.TransRep.RetFiels = settings + } + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("QueryCoinPoolTransactHandler.OnCommit ") + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("QueryCoinPoolTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("QueryCoinPoolTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_QueryCoinPool, &QueryCoinPoolTransactHandler{}) +} diff --git a/gamesrv/transact/trascate_gamesrv.go b/gamesrv/transact/trascate_gamesrv.go new file mode 100644 index 0000000..158c13b --- /dev/null +++ b/gamesrv/transact/trascate_gamesrv.go @@ -0,0 +1,153 @@ +package transact + +// +//import ( +// "errors" +// "fmt" +// "mongo.games.com/game/common" +// "mongo.games.com/game/gamesrv/base" +// "mongo.games.com/game/model" +// "mongo.games.com/game/proto" +// webapi_proto "mongo.games.com/game/protocol/webapi" +// "mongo.games.com/goserver/core/basic" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +// "mongo.games.com/goserver/core/task" +// "mongo.games.com/goserver/core/transact" +// "sync" +//) +// +//const __REQIP__ = "__REQIP__" +// +//var ( +// WebAPIErrParam = errors.New("param err") +// WebAPIErrNoPlayer = errors.New("player no find") +//) +// +//func init() { +// transact.RegisteHandler(common.TransType_GameSrvWebApi, &WebAPITranscateHandler{}) +//} +// +//var WebAPIHandlerMgrSingleton = &WebAPIHandlerMgr{wshMap: make(map[string]WebAPIHandler)} +// +//type WebAPITranscateHandler struct { +//} +// +//func (this *WebAPITranscateHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("WebAPITranscateHandler.OnExcute ") +// req := &common.M2GWebApiRequest{} +// err := netlib.UnmarshalPacketNoPackId(ud.([]byte), req) +// if err == nil { +// wsh := WebAPIHandlerMgrSingleton.GetWebAPIHandler(req.Path) +// if wsh == nil { +// logger.Logger.Error("WebAPITranscateHandler no registe WebAPIHandler ", req.Path) +// return transact.TransExeResult_Failed +// } +// tag, msg := wsh.Handler(tNode, req.Body) +// tNode.TransRep.RetFiels = msg +// switch tag { +// case common.ResponseTag_Ok: +// return transact.TransExeResult_Success +// case common.ResponseTag_TransactYield: +// return transact.TransExeResult_Yield +// } +// } +// logger.Logger.Error("WebAPITranscateHandler.OnExcute err:", err.Error()) +// return transact.TransExeResult_Failed +//} +// +//func (this *WebAPITranscateHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("WebAPITranscateHandler.OnCommit ") +// return transact.TransExeResult_Success +//} +// +//func (this *WebAPITranscateHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("WebAPITranscateHandler.OnRollBack ") +// return transact.TransExeResult_Success +//} +// +//func (this *WebAPITranscateHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, +// ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("WebAPITranscateHandler.OnChildTransRep ") +// return transact.TransExeResult_Success +//} +// +//type WebAPIHandler interface { +// Handler(*transact.TransNode, []byte) (int, proto.Message) +//} +// +//type WebAPIHandlerWrapper func(*transact.TransNode, []byte) (int, proto.Message) +// +//func (wshw WebAPIHandlerWrapper) Handler(tNode *transact.TransNode, params []byte) (int, proto.Message) { +// return wshw(tNode, params) +//} +// +//type WebAPIHandlerMgr struct { +// wshMap map[string]WebAPIHandler +// DataWaitList sync.Map +//} +// +//func (this *WebAPIHandlerMgr) RegisteWebAPIHandler(name string, wsh WebAPIHandler) { +// this.wshMap[name] = wsh +//} +// +//func (this *WebAPIHandlerMgr) GetWebAPIHandler(name string) WebAPIHandler { +// if wsh, exist := this.wshMap[name]; exist { +// return wsh +// } +// return nil +//} +// +//func init() { +// //单控 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/SinglePlayerAdjust", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params []byte) (int, proto.Message) { +// pack := &webapi_proto.SASinglePlayerAdjust{} +// msg := &webapi_proto.ASSinglePlayerAdjust{} +// err := proto.Unmarshal(params, msg) +// if err != nil { +// fmt.Printf("err:%v", err) +// pack.Tag = webapi_proto.TagCode_FAILED +// pack.Msg = "数据序列化失败" +// return common.ResponseTag_ParamError, pack +// } +// pack.Tag = webapi_proto.TagCode_SUCCESS +// switch msg.GetOpration() { +// case 1: +// psa := base.PlayerSingleAdjustMgr.AddNewSingleAdjust(msg.GetPlayerSingleAdjust()) +// if psa != nil { +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// return model.AddNewSingleAdjust(psa) +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// if data != nil { +// pack.Tag = webapi_proto.TagCode_FAILED +// pack.Msg = "insert err" + data.(error).Error() +// } +// tNode.TransRep.RetFiels = pack +// tNode.Resume() +// }), "AddNewSingleAdjust").Start() +// return common.ResponseTag_TransactYield, pack +// } +// case 2: +// base.PlayerSingleAdjustMgr.EditSingleAdjust(msg.GetPlayerSingleAdjust()) +// case 3: +// psa := msg.PlayerSingleAdjust +// if psa != nil { +// base.PlayerSingleAdjustMgr.DeleteSingleAdjust(psa.Platform, psa.SnId, psa.GameFreeId) +// } +// case 4: +// ps := msg.PlayerSingleAdjust +// webp := base.PlayerSingleAdjustMgr.GetSingleAdjust(ps.Platform, ps.SnId, ps.GameFreeId) +// if webp == nil { +// pack.Tag = webapi_proto.TagCode_FAILED +// pack.Msg = fmt.Sprintf("webp == nil %v %v %v", ps.Platform, ps.SnId, ps.GameFreeId) +// } +// pack.PlayerSingleAdjust = webp +// default: +// pack.Tag = webapi_proto.TagCode_FAILED +// pack.Msg = "Opration param is error!" +// return common.ResponseTag_ParamError, pack +// } +// return common.ResponseTag_Ok, pack +// })) +//} diff --git a/gamesrv/transact/trascate_stop.go b/gamesrv/transact/trascate_stop.go new file mode 100644 index 0000000..bf4aeb8 --- /dev/null +++ b/gamesrv/transact/trascate_stop.go @@ -0,0 +1,45 @@ +package transact + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamesrv/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" + "time" +) + +func init() { + transact.RegisteHandler(common.TransType_StopServer, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnExecuteWrapper %x", tNode.MyTnp.TId) + base.SceneMgrSington.DestoryAllScene() + //通知机器人关闭 + npcSess := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), common.RobotServerType, common.RobotServerId) + if npcSess != nil { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_StopServer, + Ot: transact.TransOwnerType(common.RobotServerType), + Oid: common.RobotServerId, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + tNode.StartChildTrans(tnp, nil, time.Second*5) + logger.Logger.Infof("StopApi start TransType_StopServer StartChildTrans srvid:%v srvtype:%v", common.RobotServerId, common.RobotServerType) + } + return transact.TransExeResult_Success + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnCommitWrapper") + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnRollBackWrapper") + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + return transact.TransExeResult(retCode) + }), + }) +} diff --git a/gatesrv/README b/gatesrv/README new file mode 100644 index 0000000..e353f6f --- /dev/null +++ b/gatesrv/README @@ -0,0 +1,3 @@ +1.客户端连接所在服务,消息转发到wordsrv,gamesrv等 +2.用户组管理功能,群消息功能 +3.客户端后后台通信透传 \ No newline at end of file diff --git a/gatesrv/action_server.go b/gatesrv/action_server.go new file mode 100644 index 0000000..97bf5bd --- /dev/null +++ b/gatesrv/action_server.go @@ -0,0 +1,134 @@ +package main + +import ( + "bytes" + "encoding/gob" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/server" +) + +func init() { + // 绑定gamesrv session + netlib.RegisterFactory(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONBIND), netlib.PacketFactoryWrapper(func() interface{} { + return &server.GGPlayerSessionBind{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONBIND), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive GGPlayerSessionBind:", pack) + if msg, ok := pack.(*server.GGPlayerSessionBind); ok { + sid := msg.GetSid() + clientss := srvlib.ClientSessionMgrSington.GetSession(sid) + if clientss != nil { + clientss.SetAttribute(common.ClientSessionAttribute_GameServer, s) + } + } + return nil + })) + + // 解绑定gamesrv session + netlib.RegisterFactory(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), netlib.PacketFactoryWrapper(func() interface{} { + return &server.GGPlayerSessionUnBind{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_GG_PLAYERSESSIONUNBIND), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive GGPlayerSessionUnBind:", pack) + if msg, ok := pack.(*server.GGPlayerSessionUnBind); ok { + sid := msg.GetSid() + clientss := srvlib.ClientSessionMgrSington.GetSession(sid) + if clientss != nil { + clientss.RemoveAttribute(common.ClientSessionAttribute_GameServer) + } + } + return nil + })) + + // 绑定session组标记 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_SG_BINDGROUPTAG), netlib.PacketFactoryWrapper(func() interface{} { + return &server.SGBindGroupTag{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_SG_BINDGROUPTAG), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive WGBindGroupTag:", pack) + if msg, ok := pack.(*server.SGBindGroupTag); ok { + if len(msg.GetTags()) == 0 { + return nil + } + + sid := msg.GetSid() + clientss := srvlib.ClientSessionMgrSington.GetSession(sid) + if clientss != nil { + var myTags []string + param := clientss.GetAttribute(common.ClientSessionAttribute_GroupTag) + if param != nil { + if tags, ok := param.([]string); ok { + myTags = tags + } + } + switch msg.GetCode() { + case server.SGBindGroupTag_OpCode_Add: //add tags + var addTags []string + for _, t := range msg.GetTags() { + if !common.InSliceString(myTags, t) { + addTags = append(addTags, t) + myTags = append(myTags, t) + } + } + if len(addTags) != 0 { + clientss.SetAttribute(common.ClientSessionAttribute_GroupTag, myTags) + CustomGroupMgrSington.AddToGroup(addTags, sid, clientss) + } + case server.SGBindGroupTag_OpCode_Del: //del tags + var delTags []string + var ok bool + for _, t := range msg.GetTags() { + if myTags, ok = common.DelSliceString(myTags, t); ok { + delTags = append(delTags, t) + } + } + if len(delTags) != 0 { + clientss.SetAttribute(common.ClientSessionAttribute_GroupTag, myTags) + CustomGroupMgrSington.DelFromGroup(delTags, sid) + } + } + } + } + return nil + })) + + // 自定义组播,给用户组发消息 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_SS_CUSTOMTAG_MULTICAST), netlib.PacketFactoryWrapper(func() interface{} { + return &server.SSCustomTagMulticast{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_SS_CUSTOMTAG_MULTICAST), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSCustomTagMulticast:", pack) + if msg, ok := pack.(*server.SSCustomTagMulticast); ok { + tags := msg.GetTags() + CustomGroupMgrSington.Broadcast(tags, msg.RawData) + } + return nil + })) + + // 同步玩家数据 + netlib.RegisterFactory(int(server.SSPacketID_PACKET_WR_PlayerData), netlib.PacketFactoryWrapper(func() interface{} { + return &server.WRPlayerData{} + })) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_WR_PlayerData), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + if msg, ok := pack.(*server.WRPlayerData); ok { + pd := &model.PlayerData{} + buf := bytes.NewBuffer(msg.GetPlayerData()) + dec := gob.NewDecoder(buf) + err := dec.Decode(pd) + if err != nil { + logger.Logger.Warnf("WRPlayerData Unmarshal %v error:%v", pd, err) + return nil + } + ns := srvlib.ClientSessionMgrSington.GetSession(msg.GetSid()) + if ns != nil { + ns.SetAttribute(common.ClientSessionAttribute_PlayerData, pd) + } + } + return nil + })) +} diff --git a/gatesrv/broadcasthandler.go b/gatesrv/broadcasthandler.go new file mode 100644 index 0000000..1fb80a0 --- /dev/null +++ b/gatesrv/broadcasthandler.go @@ -0,0 +1,64 @@ +package main + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + BroadcastMaker = &BroadcastPacketFactory{} +) + +type BroadcastPacketFactory struct { +} + +type BroadcastHandler struct { +} + +func init() { + // 给所有玩家或某个类型的所有服务发消息 + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), &BroadcastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), BroadcastMaker) +} + +func (this *BroadcastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketBroadcast{} + return pack +} + +func (this *BroadcastPacketFactory) CreateBroadcastPacket(sp *protocol.BCSessionUnion, packetid int, data interface{}) (rawproto.Message, error) { + pack := &protocol.SSPacketBroadcast{ + SessParam: sp, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Warn("BroadcastPacketFactory.CreateBroadcastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *BroadcastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if bp, ok := data.(*protocol.SSPacketBroadcast); ok { + pd := bp.GetData() + sp := bp.GetSessParam() + if bcss := sp.GetBcss(); bcss != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(bp.GetPacketId()), pd, int(bcss.GetSArea()), int(bcss.GetSType())) + } else { + srvlib.ClientSessionMgrSington.Broadcast(int(bp.GetPacketId()), pd) + } + } + return nil +} diff --git a/gatesrv/clientlog.go b/gatesrv/clientlog.go new file mode 100644 index 0000000..dfcc742 --- /dev/null +++ b/gatesrv/clientlog.go @@ -0,0 +1,62 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/game/protocol/player" +) + +func init() { + LogChannelSingleton.RegisterLogCName(mq.BackClientLog, &model.ClientLogMysql{}) + + // 客户端日志 + netlib.Register(int(player.PlayerPacketID_PACKET_CS_CLIENT_LOG), player.CSClientLog{}, CSClientLog) +} + +func CSClientLog(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive CSClientLog ", pack) + msg, ok := pack.(*player.CSClientLog) + if !ok { + return nil + } + + var platform string + var snid int32 + var p *model.PlayerData + if s != nil { + var pdi = s.GetAttribute(common.ClientSessionAttribute_PlayerData) + if pdi != nil { + pd, ok := pdi.(*model.PlayerData) + if ok { + p = pd + } + } + } + + if p != nil { + platform = p.Platform + snid = p.SnId + } + + //LogChannelSingleton.WriteLog(&model.ClientLog{ + // Data: msg.GetData(), + // Platform: platform, + // Snid: snid, + // Ts: time.Now().Unix(), + //}) + + LogChannelSingleton.WriteLog(&model.ClientLogMysql{ + Data: msg.GetData(), + Platform: platform, + Snid: snid, + Ts: time.Now().Unix(), + }) + + return nil +} diff --git a/gatesrv/clientsessionclose.go b/gatesrv/clientsessionclose.go new file mode 100644 index 0000000..79724bb --- /dev/null +++ b/gatesrv/clientsessionclose.go @@ -0,0 +1,87 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/login" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" +) + +/* +客户端连接中间件 +*/ + +type ClientSessionCloseHandler struct { +} + +func (sfcl *ClientSessionCloseHandler) GetName() string { + return "handler-client-close" +} + +func (this *ClientSessionCloseHandler) GetInterestOps() uint { + return 1< + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gatesrv/main.go b/gatesrv/main.go new file mode 100644 index 0000000..a5d7323 --- /dev/null +++ b/gatesrv/main.go @@ -0,0 +1,21 @@ +package main + +import ( + _ "games.yol.com/win88" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/module" +) + +func main() { + core.RegisterConfigEncryptor(common.ConfigFE) + defer core.ClosePackages() + core.LoadPackages("config.json") + + model.InitGameParam() + + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/gatesrv/monitormgr.go b/gatesrv/monitormgr.go new file mode 100644 index 0000000..925fc22 --- /dev/null +++ b/gatesrv/monitormgr.go @@ -0,0 +1,121 @@ +package main + +import ( + "mongo.games.com/goserver/core/module" +) + +//var MonitorMgrSington = &MonitorMgr{} + +type MonitorMgr struct { +} + +func (this *MonitorMgr) ModuleName() string { + return "MonitorMgr" +} + +func (this *MonitorMgr) Init() { + +} + +func (this *MonitorMgr) Update() { + //logic stats + //logicStats := profile.GetStats() + //if len(logicStats) > 0 { + // logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats) + // if logLogic != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("logic", logLogic) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////net session stats + //netStats := netlib.Stats() + //if len(netStats) > 0 { + // logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats) + // if logNet != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("net", logNet) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////schedule stats + //jobStats := schedule.Stats() + //if len(jobStats) > 0 { + // logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats) + // if logJob != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("job", logJob) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////trans stats + //transStats := transact.Stats() + //if len(transStats) > 0 { + // logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats) + // if logTrans != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("transact", logTrans) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////panic stats + //panicStats := utils.GetPanicStats() + //if len(panicStats) > 0 { + // for key, stats := range panicStats { + // logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats) + // if logPanic != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("panic", logPanic) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + // } + //} + // + ////object command quene stats + //objStats := core.AppCtx.GetStats() + //if len(objStats) > 0 { + // logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats) + // if logCmd != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("cmdque", logCmd) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////gorouting count, eg. system info + //runtimeStats := utils.StatsRuntime() + //logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats) + //if logRuntime != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.InsertMonitorData("runtime", logRuntime) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + //} +} + +func (this *MonitorMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + //gob registe + //gob.Register(profile.TimeElement{}) + //gob.Register(map[string]profile.TimeElement{}) + //gob.Register(netlib.ServiceStats{}) + //gob.Register(map[int]netlib.ServiceStats{}) + //gob.Register(schedule.TaskStats{}) + //gob.Register(map[string]schedule.TaskStats{}) + //gob.Register(transact.TransStats{}) + //gob.Register(map[int]transact.TransStats{}) + //gob.Register(utils.PanicStackInfo{}) + //gob.Register(map[string]utils.PanicStackInfo{}) + //gob.Register(basic.CmdStats{}) + //gob.Register(map[string]basic.CmdStats{}) + //gob.Register(utils.RuntimeStats{}) + //gob registe + + //module.RegisteModule(MonitorMgrSington, time.Minute*5, 0) +} diff --git a/gatesrv/multicasthandler.go b/gatesrv/multicasthandler.go new file mode 100644 index 0000000..ce0f29e --- /dev/null +++ b/gatesrv/multicasthandler.go @@ -0,0 +1,79 @@ +package main + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + MulticastMaker = &MulticastPacketFactory{} +) + +type MulticastPacketFactory struct { +} + +type MulticastHandler struct { +} + +func init() { + // 给某些玩家和某些服务发消息 + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), &MulticastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), MulticastMaker) +} + +func (this *MulticastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketMulticast{} + return pack +} + +func (this *MulticastPacketFactory) CreateMulticastPacket(packetid int, data interface{}, sis ...*protocol.MCSessionUnion) (rawproto.Message, error) { + pack := &protocol.SSPacketMulticast{ + Sessions: sis, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Info("MulticastPacketFactory.CreateMulticastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *MulticastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if mp, ok := data.(*protocol.SSPacketMulticast); ok { + pd := mp.GetData() + sis := mp.GetSessions() + for _, si := range sis { + ns := this.getSession(si) + if ns != nil { + ns.Send(int(mp.GetPacketId()), pd /*, s.GetSessionConfig().IsInnerLink*/) + } + } + } + return nil +} + +func (this *MulticastHandler) getSession(su *protocol.MCSessionUnion) *netlib.Session { + cs := su.GetMccs() + if cs != nil { + return srvlib.ClientSessionMgrSington.GetSession(cs.GetSId()) + } + + ss := su.GetMcss() + if ss != nil { + return srvlib.ServerSessionMgrSington.GetSession(int(ss.GetSArea()), int(ss.GetSType()), int(ss.GetSId())) + } + + return nil +} diff --git a/gatesrv/packetdispatchhandler.go b/gatesrv/packetdispatchhandler.go new file mode 100644 index 0000000..424d53c --- /dev/null +++ b/gatesrv/packetdispatchhandler.go @@ -0,0 +1,111 @@ +package main + +import ( + "bytes" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/rank" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" +) + +func init() { + netlib.RegisteUnknowPacketHandlerCreator("packetdispatchhandler", func() netlib.UnknowPacketHandler { + return netlib.UnknowPacketHandlerWrapper(func(s *netlib.Session, packetid int, logicNo uint32, data []byte) bool { + if !s.Auth { + logger.Logger.Trace("packetdispatchhandler session not auth! ") + return false + } + var ss *netlib.Session + switch { + case packetid >= 2000 && packetid < 5000: + worldSess := s.GetAttribute(common.ClientSessionAttribute_WorldServer) + if worldSess == nil { + ss = srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), srvlib.WorldServerType, common.GetWorldSrvId()) + s.SetAttribute(common.ClientSessionAttribute_WorldServer, ss) + } else { + ss = worldSess.(*netlib.Session) + if ss == nil { + ss = srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), srvlib.WorldServerType, common.GetWorldSrvId()) + s.SetAttribute(common.ClientSessionAttribute_WorldServer, ss) + } + } + + case packetid >= 10000 && packetid < 12000: + + if rankSession == nil { + logger.Logger.Trace("packetdispatchhandler redirect ranksrv is nil ", packetid) + return true + } + + buf := bytes.NewBuffer(nil) + buf.Write(data) + + var pdi = s.GetAttribute(common.ClientSessionAttribute_PlayerData) + if pdi == nil { + logger.Logger.Trace("packetdispatchhandler redirect playerdata is nil ", packetid) + return true + } + var playerData = pdi.(*model.PlayerData) + + gateTransmit := &rank.GateTransmit{ + Platform: playerData.Platform, + Snid: playerData.GetSnId(), + PacketData: buf.Bytes(), + } + + b, err := netlib.MarshalPacket(packetid, gateTransmit) + if err != nil { + logger.Logger.Error("packetdispatchhandler redirect marshal error ", err) + return true + } + + pack := &server.SSTransmit{ + PacketData: b, + } + param := s.GetAttribute(srvlib.SessionAttributeClientSession) + if param != nil { + if sid, ok := param.(srvlib.SessionId); ok { + pack.SessionId = proto.Int64(sid.Get()) + } + } + proto.SetDefaults(pack) + rankSession.Send(int(server.TransmitPacketID_PACKET_SS_PACKET_TRANSMIT), pack) + + return true + + default: + gameSess := s.GetAttribute(common.ClientSessionAttribute_GameServer) + if gameSess == nil { + logger.Logger.Trace("packetdispatchhandler not found fit gamesession! ", packetid) + return true + } + ss = gameSess.(*netlib.Session) + } + + if ss == nil { + logger.Logger.Trace("packetdispatchhandler redirect server session is nil ", packetid) + return true + } + //must copy + buf := bytes.NewBuffer(nil) + buf.Write(data) + pack := &server.SSTransmit{ + PacketData: buf.Bytes(), + } + param := s.GetAttribute(srvlib.SessionAttributeClientSession) + if param != nil { + if sid, ok := param.(srvlib.SessionId); ok { + pack.SessionId = proto.Int64(sid.Get()) + } + } + proto.SetDefaults(pack) + ss.Send(int(server.TransmitPacketID_PACKET_SS_PACKET_TRANSMIT), pack) + logger.Logger.Trace("transmit:", pack) + return true + }) + }) +} diff --git a/gatesrv/packetquota.json b/gatesrv/packetquota.json new file mode 100644 index 0000000..9c14a38 --- /dev/null +++ b/gatesrv/packetquota.json @@ -0,0 +1,10 @@ +{ + "1001": { + "Burst": 20, + "QPS": 10 + }, + "1002": { + "Burst": 10, + "QPS": 5 + } +} diff --git a/gatesrv/ranksessionmgr.go b/gatesrv/ranksessionmgr.go new file mode 100644 index 0000000..00994cd --- /dev/null +++ b/gatesrv/ranksessionmgr.go @@ -0,0 +1,38 @@ +package main + +import ( + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var rankSession *netlib.Session + +func init() { + srvlib.ServerSessionMgrSington.AddListener(new(RankSessionMgr)) +} + +type RankSessionMgr struct { +} + +func (r *RankSessionMgr) OnRegiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.RankServerType { + rankSession = s + } + } + } +} + +func (r *RankSessionMgr) OnUnregiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.RankServerType { + rankSession = nil + } + } + } +} diff --git a/gatesrv/serverctrl.go b/gatesrv/serverctrl.go new file mode 100644 index 0000000..599a84a --- /dev/null +++ b/gatesrv/serverctrl.go @@ -0,0 +1,25 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/mongo" + "mongo.games.com/goserver/srvlib" +) + +func init() { + common.RegisteServerCtrlCallback(func(code int32) { + switch code { + case common.SrvCtrlStateSwitchCode: + pack := &server.ServerStateSwitch{ + SrvType: proto.Int(common.GetSelfSrvType()), + SrvId: proto.Int(common.GetSelfSrvId()), + } + proto.SetDefaults(pack) + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_GB_STATE_SWITCH), pack, common.GetSelfAreaId(), srvlib.WorldServerType) + case common.SrvCtrlResetMgoSession: + mongo.ResetAllSession() + } + }) +} diff --git a/gatesrv/ssdisconnect.go b/gatesrv/ssdisconnect.go new file mode 100644 index 0000000..e256346 --- /dev/null +++ b/gatesrv/ssdisconnect.go @@ -0,0 +1,30 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/protocol/login" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" +) + +func init() { + netlib.RegisterFactory(int(login.GatePacketID_PACKET_SS_DICONNECT), netlib.PacketFactoryWrapper(func() interface{} { + return &login.SSDisconnect{} + })) + + netlib.RegisterHandler(int(login.GatePacketID_PACKET_SS_DICONNECT), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSDisconnect", pack) + if ssdis, ok := pack.(*login.SSDisconnect); ok { + client := srvlib.ClientSessionMgrSington.GetSession(ssdis.GetSessionId()) + if client != nil { + client.SetAttribute(common.ClientSessionAttribute_State, common.ClientState_Logouted) + if ssdis.GetType() != 2 { //非异常情况要告知客户端什么原因断线 + client.Send(int(login.GatePacketID_PACKET_SS_DICONNECT), ssdis) + } + client.Close() + } + } + return nil + })) +} diff --git a/gatesrv/trascate_stop.go b/gatesrv/trascate_stop.go new file mode 100644 index 0000000..cb1528a --- /dev/null +++ b/gatesrv/trascate_stop.go @@ -0,0 +1,28 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" +) + +func init() { + transact.RegisteHandler(common.TransType_StopServer, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnExecuteWrapper %x", tNode.MyTnp.TId) + return transact.TransExeResult_Success + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnCommitWrapper") + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnRollBackWrapper") + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + return transact.TransExeResult(retCode) + }), + }) +} diff --git a/gatesrv/worldsessionclose.go b/gatesrv/worldsessionclose.go new file mode 100644 index 0000000..0b64cf0 --- /dev/null +++ b/gatesrv/worldsessionclose.go @@ -0,0 +1,47 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +/* + worldsrv关闭,客户端连接都断开 +*/ + +const ( + WorldSessionCloseHandlerName = "handler-world-close" +) + +type WorldSessionCloseHandler struct { + netlib.BasicSessionHandler +} + +func (sfcl WorldSessionCloseHandler) GetName() string { + return WorldSessionCloseHandlerName +} + +func (this *WorldSessionCloseHandler) GetInterestOps() uint { + return 1 << netlib.InterestOps_Closed +} + +func (this *WorldSessionCloseHandler) OnSessionClosed(s *netlib.Session) { + logger.Logger.Warn("WorldSessionCloseHandler OnSessionClosed ", s.Id) + //close all client session + param := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if registePacket, ok := param.(*protocol.SSSrvRegiste); ok { + if registePacket.GetType() == srvlib.WorldServiceType { + srvlib.ClientSessionMgrSington.CloseAll() + } + } + + return +} + +func init() { + netlib.RegisteSessionHandlerCreator(WorldSessionCloseHandlerName, func() netlib.SessionHandler { + return &WorldSessionCloseHandler{} + }) +} diff --git a/gen_data.bat b/gen_data.bat new file mode 100644 index 0000000..cc8f540 --- /dev/null +++ b/gen_data.bat @@ -0,0 +1,19 @@ +@echo off +set work_path="D:\trunk\src\games.yol.com\win88" +set protoc=%work_path%\bin\protoc-3.19.4-win64\bin\protoc.exe +set protoc-gen-go-plugin-path="%work_path%\bin\protoc-gen-go.exe" + +cd %work_path%/tools/xlsx2proto +if not exist xlsx2proto.exe ( + go build +) +xlsx2proto.exe + +cd ../../protocol/server +%protoc% --plugin=protoc-gen-go=%protoc-gen-go-plugin-path% --go_out=. pbdata.proto + +cd ../../tools/xlsx2binary +go build +xlsx2binary.exe + +pause diff --git a/gen_go.bat b/gen_go.bat new file mode 100644 index 0000000..d30c926 --- /dev/null +++ b/gen_go.bat @@ -0,0 +1,19 @@ +@echo off +set work_path=%cd% +set proto_path=%work_path%\protocol +set protoc=%work_path%\bin\protoc-3.19.4-win64\bin\protoc.exe +set protoc-gen-go-plugin-path="%work_path%\bin\protoc-gen-go.exe" + +echo %protoc3% +cd %proto_path% +for /d %%s in (,*) do ( + if %%s NEQ webapi ( + cd %%s + for %%b in (,*.proto) do ( + echo %%b + %protoc% --plugin=protoc-gen-go=%protoc-gen-go-plugin-path% --go_out=. %%b + ) + cd .. + ) +) +pause \ No newline at end of file diff --git a/gen_goWebProto.bat b/gen_goWebProto.bat new file mode 100644 index 0000000..035d7d6 --- /dev/null +++ b/gen_goWebProto.bat @@ -0,0 +1,21 @@ + +copy +@echo off +set work_path=%cd% +set proto_path=%work_path%\protocol +set protoc=%work_path%\bin\protoc-3.19.4-win64\bin\protoc.exe +set protoc-gen-go-plugin-path="%work_path%\bin\protoc-gen-go.exe" + +echo %protoc3% +cd %proto_path% +for /d %%s in (,*) do ( + rem if %%s NEQ webapi ( + cd %%s + for %%b in (,*.proto) do ( + echo %%b + %protoc% --plugin=protoc-gen-go=%protoc-gen-go-plugin-path% --go_out=. %%b + ) + cd .. + rem ) +) +pause \ No newline at end of file diff --git a/gen_proto2js.bat b/gen_proto2js.bat new file mode 100644 index 0000000..fe7b0db --- /dev/null +++ b/gen_proto2js.bat @@ -0,0 +1,7 @@ + +@echo off +cd tools/proto2js +go fmt +go build +proto2js.exe +pause \ No newline at end of file diff --git a/imports.go b/imports.go new file mode 100644 index 0000000..39a7cac --- /dev/null +++ b/imports.go @@ -0,0 +1,12 @@ +package win88 + +import ( + _ "mongo.games.com/goserver/core/admin" + _ "mongo.games.com/goserver/core/builtin/action" + _ "mongo.games.com/goserver/core/builtin/filter" + _ "mongo.games.com/goserver/core/cmdline" + _ "mongo.games.com/goserver/core/netlib" + _ "mongo.games.com/goserver/core/signal" + _ "mongo.games.com/goserver/srvlib/action" + _ "mongo.games.com/goserver/srvlib/handler" +) diff --git a/mgrsrv/README b/mgrsrv/README new file mode 100644 index 0000000..771a375 --- /dev/null +++ b/mgrsrv/README @@ -0,0 +1,2 @@ +1.提供http服务 +2.服务之间消息转发 \ No newline at end of file diff --git a/mgrsrv/api/broadcasthandler.go b/mgrsrv/api/broadcasthandler.go new file mode 100644 index 0000000..a42c07c --- /dev/null +++ b/mgrsrv/api/broadcasthandler.go @@ -0,0 +1,61 @@ +package api + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + BroadcastMaker = &BroadcastPacketFactory{} +) + +type BroadcastPacketFactory struct { +} + +type BroadcastHandler struct { +} + +func init() { + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), &BroadcastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), BroadcastMaker) +} + +func (this *BroadcastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketBroadcast{} + return pack +} + +func (this *BroadcastPacketFactory) CreateBroadcastPacket(sp *protocol.BCSessionUnion, packetid int, data interface{}) (rawproto.Message, error) { + pack := &protocol.SSPacketBroadcast{ + SessParam: sp, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Warn("BroadcastPacketFactory.CreateBroadcastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *BroadcastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if bp, ok := data.(*protocol.SSPacketBroadcast); ok { + pd := bp.GetData() + sp := bp.GetSessParam() + if bcss := sp.GetBcss(); bcss != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(bp.GetPacketId()), pd, int(bcss.GetSArea()), int(bcss.GetSType())) + } + } + return nil +} diff --git a/mgrsrv/api/commonfunc.go b/mgrsrv/api/commonfunc.go new file mode 100644 index 0000000..9f1b73c --- /dev/null +++ b/mgrsrv/api/commonfunc.go @@ -0,0 +1,50 @@ +package api + +import ( + "fmt" + "net/http" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" +) + +var ApiDefaultTimeout = time.Second * 30 + +type HandlerWrapper func(*WebApiEvent, []byte) bool + +type WebApiEvent struct { + req *http.Request + path string + rawQuery string + body []byte + h HandlerWrapper + res chan []byte +} + +func (this *WebApiEvent) Done(o *basic.Object) error { + defer o.ProcessSeqnum() + this.h(this, this.body) + return nil +} + +func (this *WebApiEvent) Response(data []byte) { + this.res <- data +} + +func webApiResponse(rw http.ResponseWriter, data []byte) bool { + dataLen := len(data) + rw.Header().Set("Content-Length", fmt.Sprintf("%v", dataLen)) + rw.WriteHeader(http.StatusOK) + pos := 0 + for pos < dataLen { + writeLen, err := rw.Write(data[pos:]) + if err != nil { + logger.Logger.Info("webApiResponse SendData error:", err, " data=", string(data[:]), " pos=", pos, " writelen=", writeLen, " dataLen=", dataLen) + return false + } + pos += writeLen + } + + return true +} diff --git a/mgrsrv/api/config.go b/mgrsrv/api/config.go new file mode 100644 index 0000000..2e807c6 --- /dev/null +++ b/mgrsrv/api/config.go @@ -0,0 +1,28 @@ +package api + +import ( + "mongo.games.com/goserver/core" +) + +var Config = Configuration{} + +type Configuration struct { + StartScript string + IsDevMode bool +} + +func (this *Configuration) Name() string { + return "srvctrl" +} + +func (this *Configuration) Init() error { + return nil +} + +func (this *Configuration) Close() error { + return nil +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/mgrsrv/api/logchannel.go b/mgrsrv/api/logchannel.go new file mode 100644 index 0000000..5993b72 --- /dev/null +++ b/mgrsrv/api/logchannel.go @@ -0,0 +1,83 @@ +package api + +import ( + "reflect" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/broker/rabbitmq" +) + +/* + api调用记录写入rabbitmq,然后dbproxy收到rabbitmq数据再写到数据库 +*/ + +const ( + LOGCHANEL_BLACKHOLE = "_null_" +) + +var RabbitMQPublisher *mq.RabbitMQPublisher + +var LogChannelSington = &LogChannel{ + cName: make(map[reflect.Type]string), +} + +type LogChannel struct { + cName map[reflect.Type]string +} + +func (c *LogChannel) RegisteLogCName(cname string, log interface{}) { + t := c.getLogType(log) + c.cName[t] = cname +} + +func (c *LogChannel) getLogType(log interface{}) reflect.Type { + return reflect.Indirect(reflect.ValueOf(log)).Type() +} + +func (c *LogChannel) getLogCName(log interface{}) string { + t := c.getLogType(log) + if name, exist := c.cName[t]; exist { + return name + } + return "" +} + +func (c *LogChannel) WriteLog(log interface{}) { + cname := c.getLogCName(log) + if cname == "" { + cname = LOGCHANEL_BLACKHOLE + } + RabbitMQPublisher.Send(cname, log) +} + +func (c *LogChannel) WriteMQData(data *model.RabbitMQData) { + RabbitMQPublisher.Send(data.MQName, data.Data) +} + +func init() { + LogChannelSington.RegisteLogCName(model.APILogCollName, &model.APILog{}) + + //首先加载游戏配置 + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + //rabbitmq打开链接 + RabbitMQPublisher = mq.NewRabbitMQPublisher(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}, common.CustomConfig.GetInt("RMQPublishBacklog")) + if RabbitMQPublisher != nil { + err := RabbitMQPublisher.Start() + if err != nil { + panic(err) + } + } + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + //关闭rabbitmq连接 + if RabbitMQPublisher != nil { + RabbitMQPublisher.Stop() + } + return nil + }) +} diff --git a/mgrsrv/api/webapi_actthrsrv.go b/mgrsrv/api/webapi_actthrsrv.go new file mode 100644 index 0000000..3971212 --- /dev/null +++ b/mgrsrv/api/webapi_actthrsrv.go @@ -0,0 +1,203 @@ +package api + +// +//import ( +// "encoding/json" +// "io/ioutil" +// "net/http" +// "time" +// +// "crypto/md5" +// "encoding/hex" +// "fmt" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core" +// "mongo.games.com/goserver/core/admin" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +// "mongo.games.com/goserver/core/transact" +// "mongo.games.com/goserver/core/utils" +// "io" +// "sync/atomic" +//) +// +//// API +//// http://127.0.0.1:9595/api/Report/QueryOnlineReportList?ts=20141024000000&sign=41cc8cee8dd93f7dc70b6426cfd1029d +// +//func ActThrSrvApi(rw http.ResponseWriter, req *http.Request) { +// defer utils.DumpStackIfPanic("api.ActThrSrvApi") +// logger.Logger.Info("ActThrSrvApi receive:", req.URL.Path, req.URL.RawQuery) +// +// if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false { +// logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr) +// return +// } +// data, err := io.ReadAll(req.Body) +// if err != nil { +// logger.Logger.Info("Body err.", err) +// webApiResponse(rw, map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Post data is null!"}) +// return +// } +// m := req.URL.Query() +// timestamp := m.Get("nano") +// if timestamp == "" { +// logger.Logger.Info(req.RemoteAddr, " ActThrSrvApi param error: nano not allow null") +// return +// } +// sign := m.Get("sign") +// if sign == "" { +// logger.Logger.Info(req.RemoteAddr, " ActThrSrvApi param error: sign not allow null") +// return +// } +// startTime := time.Now().UnixNano() +// args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, req.URL.Path, string(data), timestamp) +// h := md5.New() +// io.WriteString(h, args) +// realSign := hex.EncodeToString(h.Sum(nil)) +// if realSign != sign && !common.Config.IsDevMode { +// logger.Logger.Info(req.RemoteAddr, " srvCtrlMain sign error: expect ", realSign, " ; but get ", sign, " raw=", args) +// webApiResponse(rw, map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Sign error!"}) +// return +// } +// +// var stats *ApiStats +// if v, exist := WebApiStats.Load(req.URL.Path); exist { +// stats = v.(*ApiStats) +// } else { +// stats = &ApiStats{} +// WebApiStats.Store(req.URL.Path, stats) +// } +// var rep map[string]interface{} +// start := time.Now() +// res := make(chan map[string]interface{}, 1) +// core.CoreObject().SendCommand(&WebApiEvent{req: req, path: req.URL.Path, h: HandlerWrapper(func(event *WebApiEvent, data []byte) bool { +// logger.Logger.Trace("ActThrSrvApi start transcate") +// tnp := &transact.TransNodeParam{ +// Tt: common.TransType_ActThrSrvWebApi, +// Ot: transact.TransOwnerType(common.GetSelfSrvType()), +// Oid: common.GetSelfSrvId(), +// AreaID: common.GetSelfAreaId(), +// } +// tNode := transact.DTCModule.StartTrans(tnp, event, transact.DefaultTransactTimeout) //超时时间30秒 +// if tNode != nil { +// tNode.TransEnv.SetField(WEBAPI_TRANSACTE_EVENT, event) +// tNode.Go(core.CoreObject()) +// } +// return true +// }), body: data, rawQuery: req.URL.RawQuery, res: res}, false) +// select { +// case rep = <-res: +// if rep != nil { +// webApiResponse(rw, rep) +// } +// case <-time.After(ApiDefaultTimeout): +// rep = make(map[string]interface{}) +// rep[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// rep[webapi.RESPONSE_ERRMSG] = "proccess timeout!" +// webApiResponse(rw, rep) +// if stats != nil { +// atomic.AddInt64(&stats.TimeoutTimes, 1) +// } +// } +// ps := int64(time.Now().Sub(start) / time.Millisecond) +// if stats != nil { +// atomic.AddInt64(&stats.RunTimes, 1) +// atomic.AddInt64(&stats.TotalRuningTime, ps) +// if atomic.LoadInt64(&stats.MaxRuningTime) < ps { +// atomic.StoreInt64(&stats.MaxRuningTime, ps) +// } +// } +// result, err := json.Marshal(rep) +// if err == nil { +// log := model.NewAPILog(req.URL.Path, req.URL.RawQuery, string(data[:]), req.RemoteAddr, string(result[:]), startTime, ps) +// APILogChannelSington.Write(log) +// } +// return +//} +// +////-------------------------------------------------------------------------------------- +//func init() { +// transact.RegisteHandler(common.TransType_ActThrSrvWebApi, &transact.TransHanderWrapper{ +// OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("ActThrSrvApi start TransType_WebApi OnExecuteWrapper ") +// tnp := &transact.TransNodeParam{ +// Tt: common.TransType_ActThrSrvWebApi, +// Ot: transact.TransOwnerType(common.ActThrServerType), +// Oid: common.ActThrServerID, +// AreaID: common.GetSelfAreaId(), +// Tct: transact.TransactCommitPolicy_TwoPhase, +// } +// if event, ok := ud.(*WebApiEvent); ok { +// userData := &common.M2GWebApiRequest{Path: event.path, RawQuery: event.rawQuery, Body: event.body, ReqIp: event.req.RemoteAddr} +// tNode.StartChildTrans(tnp, userData, transact.DefaultTransactTimeout) +// +// pid := tNode.MyTnp.TId +// cid := tnp.TId +// logger.Logger.Tracef("ActThrSrvApi start TransType_WebApi OnExecuteWrapper tid:%x childid:%x", pid, cid) +// return transact.TransExeResult_Success +// } +// return transact.TransExeResult_Failed +// }), +// OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("ActThrSrvApi start TransType_WebApi OnCommitWrapper") +// event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent) +// resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE) +// if userData, ok := resp.(*common.M2GWebApiResponse); ok { +// if len(userData.Body) > 0 { +// m := make(map[string]interface{}) +// err := json.Unmarshal(userData.Body, &m) +// if err == nil { +// event.Response(m) +// return transact.TransExeResult_Success +// } +// } +// } +// event.Response(map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}) +// return transact.TransExeResult_Success +// }), +// OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("ActThrSrvApi start TransType_WebApi OnRollBackWrapper") +// event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent) +// resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE) +// if userData, ok := resp.(*common.M2GWebApiResponse); ok { +// if len(userData.Body) > 0 { +// m := make(map[string]interface{}) +// err := json.Unmarshal(userData.Body, &m) +// if err == nil { +// event.Response(m) +// return transact.TransExeResult_Success +// } +// } +// return transact.TransExeResult_Success +// } +// event.Response(map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}) +// return transact.TransExeResult_Success +// }), +// OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { +// logger.Logger.Tracef("ActThrSrvApi start TransType_WebApi OnChildRespWrapper ret:%v childid:%x", retCode, hChild) +// userData := &common.M2GWebApiResponse{} +// err := netlib.UnmarshalPacketNoPackId(ud.([]byte), userData) +// if err == nil { +// tNode.TransEnv.SetField(WEBAPI_TRANSACTE_RESPONSE, userData) +// } else { +// logger.Logger.Trace("trascate.OnChildRespWrapper err:", err) +// } +// return transact.TransExeResult(retCode) +// }), +// }) +// +// //测试 +// admin.MyAdminApp.Route("/api/ActThr/Test", ActThrSrvApi) +// +// //增加自动黑白名单控制 +// admin.MyAdminApp.Route("/api/ActThr/Upsert", ActThrSrvApi) +// +// //删除自动黑白名单控制 +// admin.MyAdminApp.Route("/api/ActThr/Delete", ActThrSrvApi) +// +// //删除所有添加的黑名单 +// admin.MyAdminApp.Route("/api/ActThr/ResetBW", ActThrSrvApi) +// +//} diff --git a/mgrsrv/api/webapi_gamesrv.go b/mgrsrv/api/webapi_gamesrv.go new file mode 100644 index 0000000..5fb0d8d --- /dev/null +++ b/mgrsrv/api/webapi_gamesrv.go @@ -0,0 +1,189 @@ +package api + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" + "mongo.games.com/goserver/srvlib" + "net/http" + "sync/atomic" + "time" +) + +const ( + GAMESRVAPI_TRANSACTE_EVENT = "GAMESRVAPI_TRANSACTE_EVENT" + GAMESRVAPI_TRANSACTE_RESPONSE = "GAMESRVAPI_TRANSACTE_RESPONSE" +) + +// type ResponseData struct { +// State int +// Data string +// } +func GameSrvWebAPI(rw http.ResponseWriter, req *http.Request) { + defer utils.DumpStackIfPanic("api.GameSrvApi") + logger.Logger.Info("GameSrvApi receive:", req.URL.Path, req.URL.RawQuery) + + if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false { + logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr) + return + } + data, err := io.ReadAll(req.Body) + if err != nil { + webApiResponse(rw, nil) + return + } + m := req.URL.Query() + timestamp := m.Get("nano") + if timestamp == "" { + logger.Logger.Info(req.RemoteAddr, " GameSrvApi param error: nano not allow null") + return + } + sign := m.Get("sign") + if sign == "" { + logger.Logger.Info(req.RemoteAddr, " GameSrvApi param error: sign not allow null") + return + } + startTime := time.Now().UnixNano() + args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, req.URL.Path, string(data), timestamp) + h := md5.New() + io.WriteString(h, args) + realSign := hex.EncodeToString(h.Sum(nil)) + if realSign != sign && !common.Config.IsDevMode { + logger.Logger.Info(req.RemoteAddr, " srvCtrlMain sign error: expect ", realSign, " ; but get ", sign) + webApiResponse(rw, nil) + return + } + var stats *ApiStats + if v, exist := WebApiStats.Load(req.URL.Path); exist { + stats = v.(*ApiStats) + } else { + stats = &ApiStats{} + WebApiStats.Store(req.URL.Path, stats) + } + var rep []byte + start := time.Now() + res := make(chan []byte, 1) + suc := core.CoreObject().SendCommand(&WebApiEvent{req: req, path: req.URL.Path, h: HandlerWrapper(func(event *WebApiEvent, data []byte) bool { + logger.Logger.Trace("GameSrvApi start transcate") + tnp := &transact.TransNodeParam{ + Tt: common.TransType_GameSrvWebApi, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + tNode := transact.DTCModule.StartTrans(tnp, event, transact.DefaultTransactTimeout) + if tNode != nil { + tNode.TransEnv.SetField(GAMESRVAPI_TRANSACTE_EVENT, event) + tNode.Go(core.CoreObject()) + } + return true + }), body: data, rawQuery: req.URL.RawQuery, res: res}, false) + if suc { + select { + case rep = <-res: + if rep != nil { + webApiResponse(rw, rep) + } + case <-time.After(ApiDefaultTimeout): + webApiResponse(rw, rep) + if stats != nil { + atomic.AddInt64(&stats.TimeoutTimes, 1) + } + } + } else { + webApiResponse(rw, nil) + if stats != nil { + atomic.AddInt64(&stats.UnreachTimes, 1) + } + } + ps := int64(time.Now().Sub(start) / time.Millisecond) + if stats != nil { + atomic.AddInt64(&stats.RunTimes, 1) + atomic.AddInt64(&stats.TotalRuningTime, ps) + if atomic.LoadInt64(&stats.MaxRuningTime) < ps { + atomic.StoreInt64(&stats.MaxRuningTime, ps) + } + } + result, err := json.Marshal(rep) + if err == nil { + log := model.NewAPILog(req.URL.Path, req.URL.RawQuery, string(data[:]), req.RemoteAddr, string(result[:]), startTime, ps) + LogChannelSington.WriteLog(log) + } + return +} + +// //-------------------------------------------------------------------------------------- +func init() { + transact.RegisteHandler(common.TransType_GameSrvWebApi, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("GameSrvApi start TransType_GameSrvWebApi OnExecuteWrapper") + gameSrvIds := common.GetGameSrvIds() + logger.Logger.Trace("Current game id:", gameSrvIds) + for _, value := range gameSrvIds { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_GameSrvWebApi, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: value, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + if event, ok := ud.(*WebApiEvent); ok { + userData := &common.M2GWebApiRequest{Path: event.path, RawQuery: event.rawQuery, Body: event.body, ReqIp: event.req.RemoteAddr} + ter := tNode.StartChildTrans(tnp, userData, transact.DefaultTransactTimeout) + if ter != transact.TransExeResult_Success { + logger.Logger.Tracef("StartChildTrans %v game server failed.", value) + } + } else { + logger.Logger.Trace("Conver ud to WebApiEvent failed OnExecuteWrapper") + } + } + return transact.TransExeResult_Success + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("GameSrvApi start TransType_GameSrvWebApi OnCommitWrapper") + event := tNode.TransEnv.GetField(GAMESRVAPI_TRANSACTE_EVENT).(*WebApiEvent) + resp := tNode.TransEnv.GetField(GAMESRVAPI_TRANSACTE_RESPONSE) + if ud, ok := resp.([]byte); ok { + event.Response(netlib.SkipHeaderGetRaw(ud)) + return transact.TransExeResult_Success + } + event.Response(nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}*/) + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("GameSrvApi start TransType_GameSrvWebApi OnRollBackWrapper") + event := tNode.TransEnv.GetField(GAMESRVAPI_TRANSACTE_EVENT).(*WebApiEvent) + resp := tNode.TransEnv.GetField(GAMESRVAPI_TRANSACTE_RESPONSE) + if ud, ok := resp.([]byte); ok { + event.Response(netlib.SkipHeaderGetRaw(ud)) + return transact.TransExeResult_Success + } + event.Response(nil) + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Tracef("GameSrvApi OnChildRespWrapper %v:%v", hChild, ud) + tNode.TransEnv.SetField(GAMESRVAPI_TRANSACTE_RESPONSE, ud) + return transact.TransExeResult(retCode) + }), + }) + // //参数设置 + // admin.MyAdminApp.Route("/api/Param/CommonTax", GameSrvWebAPI) + // //捕鱼金币池查询 + // admin.MyAdminApp.Route("/api/CoinPool/FishingPool", GameSrvWebAPI) + // //通用金币池查询 + // admin.MyAdminApp.Route("/api/CoinPool/GamePool", GameSrvWebAPI) + // //捕鱼渔场保留金币 + // admin.MyAdminApp.Route("/api/CoinPool/GameFishsAllCoin", GameSrvWebAPI) + // //单控数据 + //admin.MyAdminApp.Route("/api/mongo.games.com/game/SinglePlayerAdjust", GameSrvWebAPI) +} diff --git a/mgrsrv/api/webapi_ranksrv.go b/mgrsrv/api/webapi_ranksrv.go new file mode 100644 index 0000000..e0adcc9 --- /dev/null +++ b/mgrsrv/api/webapi_ranksrv.go @@ -0,0 +1,186 @@ +package api + +// +//import ( +// "crypto/md5" +// "encoding/hex" +// "encoding/json" +// "fmt" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core" +// "mongo.games.com/goserver/core/admin" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +// "mongo.games.com/goserver/core/transact" +// "mongo.games.com/goserver/core/utils" +// "io" +// "io/ioutil" +// "net/http" +// "time" +//) +// +//const ( +// RANKSRVAPI_TRANSACTE_EVENT = "GAMESRVAPI_TRANSACTE_EVENT" +// RANKSRVAPI_TRANSACTE_RESPONSE = "RANKSRVAPI_TRANSACTE_RESPONSE" +//) +// +//// 处理 web 请求 rank server 相关的配置协议, 转发至 rank server 处理 +// +//func RankSrvApi(rw http.ResponseWriter, req *http.Request) { +// defer utils.DumpStackIfPanic("api.RankSrvApi") +// logger.Logger.Info("RankSrvApi receive:", req.URL.Path, req.URL.RawQuery) +// +// if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false { +// logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr) +// return +// } +// data, err := io.ReadAll(req.Body) +// if err != nil { +// logger.Logger.Info("Body err.", err) +// webApiResponse(rw, map[string]interface{}{ +// webapi.RESPONSE_STATE: webapi.STATE_ERR, +// webapi.RESPONSE_ERRMSG: "Post data is null!", +// }) +// return +// } +// logger.Logger.Info(string(data)) +// m := req.URL.Query() +// timestamp := m.Get("nano") +// if timestamp == "" { +// logger.Logger.Info(req.RemoteAddr, " RankSrvApi param error: nano not allow null") +// return +// } +// sign := m.Get("sign") +// if sign == "" { +// logger.Logger.Info(req.RemoteAddr, " RankSrvApi param error: sign not allow null") +// return +// } +// startTime := time.Now().UnixNano() +// args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, req.URL.Path, string(data), timestamp) +// h := md5.New() +// io.WriteString(h, args) +// realSign := hex.EncodeToString(h.Sum(nil)) +// if realSign != sign && !common.Config.IsDevMode { +// logger.Logger.Info(req.RemoteAddr, " srvCtrlMain sign error: expect ", realSign, " ; but get ", sign, " raw=", args) +// webApiResponse(rw, map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Sign error!"}) +// return +// } +// var rep map[string]interface{} +// start := time.Now() +// res := make(chan map[string]interface{}, 1) +// core.CoreObject().SendCommand(&WebApiEvent{req: req, path: req.URL.Path, h: HandlerWrapper(func(event *WebApiEvent, data []byte) bool { +// logger.Logger.Trace("RankSrvApi start transcate") +// tnp := &transact.TransNodeParam{ +// Tt: common.TransType_WebApi_ForRank, +// Ot: transact.TransOwnerType(common.GetSelfSrvType()), +// Oid: common.GetSelfSrvId(), +// AreaID: common.GetSelfAreaId(), +// } +// logger.Info("call info:", common.GetSelfAreaId(), common.GetSelfSrvType(), common.GetSelfSrvId()) +// tNode := transact.DTCModule.StartTrans(tnp, event, transact.DefaultTransactTimeout) //超时时间30秒 +// if tNode != nil { +// tNode.TransEnv.SetField(RANKSRVAPI_TRANSACTE_EVENT, event) +// tNode.Go(core.CoreObject()) +// } +// return true +// }), body: data, rawQuery: req.URL.RawQuery, res: res}, false) +// select { +// case rep = <-res: +// if rep != nil { +// webApiResponse(rw, rep) +// } +// case <-time.After(ApiDefaultTimeout): +// rep = make(map[string]interface{}) +// rep[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// rep[webapi.RESPONSE_ERRMSG] = "proccess timeout!" +// webApiResponse(rw, rep) +// } +// ps := int64(time.Now().Sub(start) / time.Millisecond) +// result, err := json.Marshal(rep) +// if err == nil { +// log := model.NewAPILog(req.URL.Path, req.URL.RawQuery, string(data[:]), req.RemoteAddr, string(result[:]), startTime, ps) +// APILogChannelSington.Write(log) +// } +// return +//} +// +//func init() { +// transact.RegisteHandler(common.TransType_WebApi_ForRank, &transact.TransHanderWrapper{ +// OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("RankSrvApi start TransType_WebApi_ForRank OnExecuteWrapper ") +// tnp := &transact.TransNodeParam{ +// Tt: common.TransType_WebApi_ForRank, +// Ot: transact.TransOwnerType(common.RankServerType), +// Oid: common.GetRankSrvId(), +// AreaID: common.GetSelfAreaId(), +// Tct: transact.TransactCommitPolicy_TwoPhase, +// } +// logger.Infof("params: %+v", tnp) +// if event, ok := ud.(*WebApiEvent); ok { +// userData := &common.M2GWebApiRequest{Path: event.path, RawQuery: event.rawQuery, Body: event.body, ReqIp: event.req.RemoteAddr} +// tNode.StartChildTrans(tnp, userData, transact.DefaultTransactTimeout) +// +// pid := tNode.MyTnp.TId +// cid := tnp.TId +// logger.Logger.Tracef("RankSrvApi start TransType_WebApi_ForRank OnExecuteWrapper tid:%x childid:%x", pid, cid) +// return transact.TransExeResult_Success +// } +// return transact.TransExeResult_Failed +// }), +// OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("RankSrvApi start TransType_WebApi_ForRank OnCommitWrapper") +// event := tNode.TransEnv.GetField(RANKSRVAPI_TRANSACTE_EVENT).(*WebApiEvent) +// resp := tNode.TransEnv.GetField(RANKSRVAPI_TRANSACTE_RESPONSE) +// if userData, ok := resp.(*common.M2GWebApiResponse); ok { +// if len(userData.Body) > 0 { +// m := make(map[string]interface{}) +// err := json.Unmarshal(userData.Body, &m) +// if err == nil { +// event.Response(m) +// return transact.TransExeResult_Success +// } +// } +// } +// event.Response(map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}) +// return transact.TransExeResult_Success +// }), +// OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("RankSrvApi start TransType_WebApi_ForRank OnRollBackWrapper") +// event := tNode.TransEnv.GetField(RANKSRVAPI_TRANSACTE_EVENT).(*WebApiEvent) +// resp := tNode.TransEnv.GetField(RANKSRVAPI_TRANSACTE_RESPONSE) +// if userData, ok := resp.(*common.M2GWebApiResponse); ok { +// if len(userData.Body) > 0 { +// m := make(map[string]interface{}) +// err := json.Unmarshal(userData.Body, &m) +// if err == nil { +// event.Response(m) +// return transact.TransExeResult_Success +// } +// } +// return transact.TransExeResult_Success +// } +// event.Response(map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}) +// return transact.TransExeResult_Success +// }), +// OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { +// logger.Logger.Tracef("RankSrvApi start TransType_WebApi_ForRank OnChildRespWrapper ret:%v childid:%x", retCode, hChild) +// userData := &common.M2GWebApiResponse{} +// err := netlib.UnmarshalPacketNoPackId(ud.([]byte), userData) +// if err == nil { +// tNode.TransEnv.SetField(RANKSRVAPI_TRANSACTE_RESPONSE, userData) +// } else { +// logger.Logger.Trace("trascate.OnChildRespWrapper err:", err) +// } +// return transact.TransExeResult(retCode) +// }), +// }) //RegisteHandler +// +// admin.MyAdminApp.Route("/api/rank/getConfig", RankSrvApi) +// admin.MyAdminApp.Route("/api/rank/updateConfig", RankSrvApi) +// admin.MyAdminApp.Route("/api/rank/debug/settings", RankSrvApi) +// admin.MyAdminApp.Route("/api/rank/debug/board", RankSrvApi) +// admin.MyAdminApp.Route("/api/rank/reset", RankSrvApi) +// admin.MyAdminApp.Route("/api/rank/syncUser", RankSrvApi) // 同步主库玩家信息 +//} diff --git a/mgrsrv/api/webapi_srctrl.go b/mgrsrv/api/webapi_srctrl.go new file mode 100644 index 0000000..e2b06a6 --- /dev/null +++ b/mgrsrv/api/webapi_srctrl.go @@ -0,0 +1,271 @@ +package api + +import ( + "io" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + msg_proto "mongo.games.com/game/protocol/message" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/admin" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/timer" + "mongo.games.com/goserver/core/utils" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" + srvlibprotocol "mongo.games.com/goserver/srvlib/protocol" + "net/http" + "os/exec" + "strconv" + "time" +) + +//-------------------------------切换状态------------------------------------------------------- + +func ServerStateSwitch(rw http.ResponseWriter, data []byte) { + pack := &webapi.SAServerStateSwitch{} + var gs webapi.ASServerStateSwitch + err := proto.Unmarshal(data, &gs) + if err != nil { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "proto.Unmarshal is err:" + err.Error() + by, _ := proto.Marshal(pack) + webApiResponse(rw, by) + return + } + + if gs.SrvId == 0 || gs.SrvType == 0 { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "SrvCtrlStateSwitch failed SrvId or SrvType is zero." + by, _ := proto.Marshal(pack) + webApiResponse(rw, by) + return + } + s := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), int(gs.SrvType), int(gs.SrvId)) + logger.Logger.Trace("srvCtrl_StateSwitch enter") + if s != nil { + ctrlPacket := &server.ServerCtrl{ + CtrlCode: proto.Int32(common.SrvCtrlStateSwitchCode), + } + proto.SetDefaults(ctrlPacket) + s.Send(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, true) + pack.Tag = webapi.TagCode_SUCCESS + pack.Msg = "Server is changed." + rep, _ := proto.Marshal(pack) + webApiResponse(rw, rep) + return + } + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "[stateswitch] no find " + strconv.Itoa(int(gs.SrvId)) + "-" + strconv.Itoa(int(gs.SrvType)) + " server" + rep, _ := proto.Marshal(pack) + webApiResponse(rw, rep) +} +func SrvCtrlClose(rw http.ResponseWriter, data []byte) { + logger.Logger.Trace("srvCtrl_Close enter") + msg := &webapi.ASSrvCtrlClose{} + pack := &webapi.SASrvCtrlClose{ + Tag: webapi.TagCode_SUCCESS, + } + err := proto.Unmarshal(data, msg) + if err != nil { + pack.Msg = "Unmarshal CtrlClose" + err.Error() + rep, _ := proto.Marshal(pack) + webApiResponse(rw, rep) + return + } + ctrlPacket := &server.ServerCtrl{ + CtrlCode: proto.Int32(common.SrvCtrlCloseCode), + } + proto.SetDefaults(ctrlPacket) + var closeType []int + srvType := int(msg.SrvType) + if msg.SrvType == 0 { + closeType = append(closeType, srvlib.GameServerType, srvlib.GateServerType, srvlib.WorldServerType) + } else if srvType == srvlib.GameServerType || srvType == srvlib.GateServerType || srvType == srvlib.WorldServerType { + closeType = append(closeType, int(msg.SrvType)) + } + for _, serverType := range closeType { + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, common.GetSelfAreaId(), serverType) + } + pack.Msg = "success" + rep, _ := proto.Marshal(pack) + webApiResponse(rw, rep) +} +func srvCtrlExecShell(shell string) bool { + cmd := exec.Command(shell) + if cmd == nil { + logger.Logger.Info("srvCtrlExecShell exec.Command(", shell, ") failed") + return false + } + logger.Logger.Info("srvCtrlExecShell exec.Command(", shell, ")") + + stdout, err := cmd.StdoutPipe() + if err != nil { + logger.Logger.Info("srvCtrlExecShell.cmd.StdoutPipe() error:", err) + return false + } + + stderr, err := cmd.StderrPipe() + if err != nil { + logger.Logger.Info("srvCtrlExecShell.cmd.StderrPipe() error:", err) + return false + } + + err = cmd.Start() + if err != nil { + logger.Logger.Info("srvCtrlExecShell.cmd.Start()", err) + return false + } + + ber, err := io.ReadAll(stderr) + if err != nil { + logger.Logger.Info("srvCtrlExecShell.io.ReadAll(stderr)", err) + return false + } + logger.Logger.Info("stderr=", string(ber[:])) + + bsr, err := io.ReadAll(stdout) + if err != nil { + logger.Logger.Info("srvCtrlExecShell.io.ReadAll(stdout)", err) + return false + } + logger.Logger.Info("stdout=", string(bsr[:])) + + err = cmd.Wait() + if err != nil { + logger.Logger.Info("srvCtrlExecShell.cmd.Wait()", err) + return false + } + return true +} + +func SrvCtrlStartScript(rw http.ResponseWriter, data []byte) { + logger.Logger.Trace("srvCtrl_Start enter") + msg := &webapi.ASSrvCtrlStartScript{} + pack := &webapi.SASrvCtrlStartScript{ + Tag: webapi.TagCode_SUCCESS, + } + err := proto.Unmarshal(data, msg) + if err != nil { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "Unmarshal ASSrvCtrlStartScript is err:" + err.Error() + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) + return + } + if srvCtrlExecShell(Config.StartScript) { + pack.Msg = "Start success" + } else { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "Start failed" + } + rep, _ := proto.Marshal(pack) + webApiResponse(rw, rep) +} +func SrvApi(rw http.ResponseWriter, req *http.Request) { + defer utils.DumpStackIfPanic("api.SrvCtrlApi") + logger.Logger.Info("srvCtrl_StateSwitch receive:", req.URL.Path, req.URL.RawQuery) + + if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false { + logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr) + return + } + data, err := io.ReadAll(req.Body) + if err != nil { + webApiResponse(rw, nil) + return + } + switch req.URL.Path { + case "/api/Ctrl/ServerStateSwitch": + ServerStateSwitch(rw, data) + case "/api/Ctrl/SrvCtrlClose": + SrvCtrlClose(rw, data) + case "/api/Ctrl/SrvCtrlStartScript": + SrvCtrlStartScript(rw, data) + case "/api/Ctrl/SrvCtrlNotice": + SrvCtrlNotice(rw, data) + } +} + +var noticeMap = make(map[timer.TimerHandle]struct{}) + +func SrvCtrlNotice(rw http.ResponseWriter, data []byte) { + logger.Logger.Trace("SrvCtrlNotice") + pack := &webapi.SASrvCtrlNotice{} + msg := &webapi.ASSrvCtrlNotice{} + err := proto.Unmarshal(data, msg) + if err != nil { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "Unmarshal ASSrvCtrlNotice is err:" + err.Error() + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) + return + } + if msg.OpNotice == 1 { + logger.Logger.Trace("srvCtrl_StopNotice enter") + for h, _ := range noticeMap { + timer.StopTimer(h) + } + noticeMap = make(map[timer.TimerHandle]struct{}) + pack.Tag = webapi.TagCode_SUCCESS + pack.Msg = "stop all notice success" + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) + return + } + logger.Logger.Trace("srvCtrl_Notice enter") + if msg.Notice == "" { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "Notice is nil" + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) + return + } + noticePacket := &server.ServerNotice{ + Text: proto.String(msg.GetNotice()), + } + proto.SetDefaults(noticePacket) + sc := &protocol.BCSessionUnion{ + Bccs: &protocol.BCClientSession{}, + } + broadcast, err := BroadcastMaker.CreateBroadcastPacket(sc, int(msg_proto.MSGPacketID_PACKET_SC_NOTICE), noticePacket) + if err != nil || broadcast == nil { + pack.Tag = webapi.TagCode_FAILED + pack.Msg = "send notice failed(inner error)" + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) + return + } + funcNotice := func() { + srvlib.ServerSessionMgrSington.Broadcast(int(srvlibprotocol.SrvlibPacketID_PACKET_SS_BROADCAST), broadcast, common.GetSelfAreaId(), srvlib.GateServerType) + } + funcNotice() + h, b := timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + funcNotice() + return true + }), nil, time.Second*time.Duration(msg.GetInterval()), int(msg.GetTimes())) + if b { + noticeMap[h] = struct{}{} + } + pack.Tag = webapi.TagCode_SUCCESS + pack.Msg = "send notice success" + r, _ := proto.Marshal(pack) + webApiResponse(rw, r) +} + +// -------------------------------------------------------------------------------------- +func init() { + //切换状态 + admin.MyAdminApp.Route("/api/Ctrl/ServerStateSwitch", SrvApi) + //获取服务器列表 + admin.MyAdminApp.Route("/api/Ctrl/ListServerStates", WorldSrvApi) + //关服 + admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlClose", SrvApi) + //执行脚本 + admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlStartScript", SrvApi) + //发送 停止 公告 + admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlNotice", SrvApi) + //重置Etcd + admin.MyAdminApp.Route("/api/Ctrl/ResetEtcdData", WorldSrvApi) +} diff --git a/mgrsrv/api/webapi_worldsrv.go b/mgrsrv/api/webapi_worldsrv.go new file mode 100644 index 0000000..159a929 --- /dev/null +++ b/mgrsrv/api/webapi_worldsrv.go @@ -0,0 +1,258 @@ +package api + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "sync" + "sync/atomic" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/admin" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" + "mongo.games.com/goserver/srvlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" +) + +// API +// http://127.0.0.1:9595/api/Report/QueryOnlineReportList?ts=20141024000000&sign=41cc8cee8dd93f7dc70b6426cfd1029d +const ( + WEBAPI_TRANSACTE_EVENT int = iota + WEBAPI_TRANSACTE_RESPONSE +) + +var WebApiStats = new(sync.Map) + +type ApiStats struct { + RunTimes int64 //执行次数 + TotalRuningTime int64 //总执行时间 + MaxRuningTime int64 //最长执行时间 + TimeoutTimes int64 //执行超时次数 + UnreachTimes int64 //不可达次数 +} + +func WorldSrvApi(rw http.ResponseWriter, req *http.Request) { + defer utils.DumpStackIfPanic("api.WorldSrvApi") + logger.Logger.Info("WorldSrvApi receive:", req.URL.Path, req.URL.RawQuery) + + if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false { + logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr) + return + } + data, err := io.ReadAll(req.Body) + if err != nil { + logger.Logger.Info("Body err.", err) + webApiResponse(rw, nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Post data is null!"}*/) + return + } + m := req.URL.Query() + timestamp := m.Get("nano") + if timestamp == "" { + logger.Logger.Info(req.RemoteAddr, " WorldSrvApi param error: nano not allow null") + return + } + sign := m.Get("sign") + if sign == "" { + logger.Logger.Info(req.RemoteAddr, " WorldSrvApi param error: sign not allow null") + return + } + startTime := time.Now().UnixNano() + args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, req.URL.Path, string(data), timestamp) + h := md5.New() + io.WriteString(h, args) + realSign := hex.EncodeToString(h.Sum(nil)) + if realSign != sign && !common.Config.IsDevMode { + logger.Logger.Info(req.RemoteAddr, " srvCtrlMain sign error: expect ", realSign, " ; but get ", sign, " raw=", args) + webApiResponse(rw, nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Sign error!"}*/) + return + } + + var stats *ApiStats + if v, exist := WebApiStats.Load(req.URL.Path); exist { + stats = v.(*ApiStats) + } else { + stats = &ApiStats{} + WebApiStats.Store(req.URL.Path, stats) + } + var rep []byte + start := time.Now() + res := make(chan []byte, 1) + suc := core.CoreObject().SendCommand(&WebApiEvent{req: req, path: req.URL.Path, h: HandlerWrapper(func(event *WebApiEvent, data []byte) bool { + logger.Logger.Trace("WorldSrvApi start transcate") + tnp := &transact.TransNodeParam{ + Tt: common.TransType_WebApi, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + tNode := transact.DTCModule.StartTrans(tnp, event, transact.DefaultTransactTimeout) //超时时间30秒 + if tNode != nil { + tNode.TransEnv.SetField(WEBAPI_TRANSACTE_EVENT, event) + tNode.Go(core.CoreObject()) + } + return true + }), body: data, rawQuery: req.URL.RawQuery, res: res}, false) + if suc { + select { + case rep = <-res: + if rep != nil { + webApiResponse(rw, rep) + } + case <-time.After(ApiDefaultTimeout): + //rep = make(map[string]interface{}) + //rep[webapi.RESPONSE_STATE] = webapi.STATE_ERR + //rep[webapi.RESPONSE_ERRMSG] = "proccess timeout!" + webApiResponse(rw, rep) + if stats != nil { + atomic.AddInt64(&stats.TimeoutTimes, 1) + } + } + } else { + webApiResponse(rw, nil) + if stats != nil { + atomic.AddInt64(&stats.UnreachTimes, 1) + } + } + ps := int64(time.Now().Sub(start) / time.Millisecond) + if stats != nil { + atomic.AddInt64(&stats.RunTimes, 1) + atomic.AddInt64(&stats.TotalRuningTime, ps) + if atomic.LoadInt64(&stats.MaxRuningTime) < ps { + atomic.StoreInt64(&stats.MaxRuningTime, ps) + } + } + result, err := json.Marshal(rep) + if err == nil { + log := model.NewAPILog(req.URL.Path, req.URL.RawQuery, string(data[:]), req.RemoteAddr, string(result[:]), startTime, ps) + LogChannelSington.WriteLog(log) + } + return +} + +// -------------------------------------------------------------------------------------- +func init() { + transact.RegisteHandler(common.TransType_WebApi, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnExecuteWrapper ") + tnp := &transact.TransNodeParam{ + Tt: common.TransType_WebApi, + Ot: transact.TransOwnerType(srvlib.WorldServerType), + Oid: common.GetWorldSrvId(), + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + if event, ok := ud.(*WebApiEvent); ok { + userData := &common.M2GWebApiRequest{Path: event.path, RawQuery: event.rawQuery, Body: event.body, ReqIp: event.req.RemoteAddr} + tNode.StartChildTrans(tnp, userData, transact.DefaultTransactTimeout) + + pid := tNode.MyTnp.TId + cid := tnp.TId + logger.Logger.Tracef("WorldSrvApi start TransType_WebApi OnExecuteWrapper tid:%x childid:%x", pid, cid) + return transact.TransExeResult_Success + } + return transact.TransExeResult_Failed + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnCommitWrapper") + event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent) + resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE) + if ud, ok := resp.([]byte); ok { + event.Response(netlib.SkipHeaderGetRaw(ud)) + return transact.TransExeResult_Success + } + event.Response(nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}*/) + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnRollBackWrapper") + event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent) + resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE) + if ud, ok := resp.([]byte); ok { + event.Response(netlib.SkipHeaderGetRaw(ud)) + return transact.TransExeResult_Success + } + event.Response(nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}*/) + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Tracef("WorldSrvApi start TransType_WebApi OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + tNode.TransEnv.SetField(WEBAPI_TRANSACTE_RESPONSE, ud) + return transact.TransExeResult(retCode) + }), + }) + + // 查询在线玩家列表,筛选,排序 + admin.MyAdminApp.Route("/api/Report/QueryOnlineReportList", WorldSrvApi) + // 在线统计 + admin.MyAdminApp.Route("/api/Report/OnlineReportTotal", WorldSrvApi) + // 加减金币钻石 + admin.MyAdminApp.Route("/api/mongo.games.com/game/AddCoinByIdAndPT", WorldSrvApi) + // 查询玩家信息 + admin.MyAdminApp.Route("/api/Player/PlayerData", WorldSrvApi) + // 查询多个玩家信息 + admin.MyAdminApp.Route("/api/Player/MorePlayerData", WorldSrvApi) + // 踢下线 + admin.MyAdminApp.Route("/api/Player/KickPlayer", WorldSrvApi) + // 黑白名单调控 + admin.MyAdminApp.Route("/api/Player/WhiteBlackControl", WorldSrvApi) + // 设置黑名单 + admin.MyAdminApp.Route("/api/Player/BlackBySnId", WorldSrvApi) + // 修改玩家内存属性 + admin.MyAdminApp.Route("/api/Cache/UpdatePlayerElement", WorldSrvApi) + + // 水池相关 + admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryGamePoolByGameId", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryAllGamePool", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/RefreshGamePool", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/UpdateGamePool", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/ResetGamePool", WorldSrvApi) + + // 邮箱相关 + admin.MyAdminApp.Route("/api/mongo.games.com/game/CreateShortMessage", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryShortMessageList", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/DeleteShortMessage", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/DeleteAllShortMessage", WorldSrvApi) + + // 获取房间列表 + admin.MyAdminApp.Route("/api/Cache/ListRoom", WorldSrvApi) + // 获取房间信息 + admin.MyAdminApp.Route("/api/Cache/GetRoom", WorldSrvApi) + // 销毁房间 + admin.MyAdminApp.Route("/api/Cache/DestroyRoom", WorldSrvApi) + // 兑换物品 + admin.MyAdminApp.Route("/api/Customer/UpExchangeStatus", WorldSrvApi) + // 3方平台 + admin.MyAdminApp.Route("/api/thd/UpdatePlayerCoin", WorldSrvApi) + // 兑换卷 + admin.MyAdminApp.Route("/api/mongo.games.com/game/CreateJYB", WorldSrvApi) + admin.MyAdminApp.Route("/api/mongo.games.com/game/UpdateJYB", WorldSrvApi) + // 支付回调 + admin.MyAdminApp.Route("/api/pay/CallbackPayment", WorldSrvApi) + // 资源变更(给所有玩家发送消息) + admin.MyAdminApp.Route("/api/mongo.games.com/game/resource", WorldSrvApi) + // 修改手机号 + admin.MyAdminApp.Route("/api/player/update_tel", WorldSrvApi) + // 删除账号 + admin.MyAdminApp.Route("/api/player/delete", WorldSrvApi) +} + +func Stats() map[string]ApiStats { + stats := make(map[string]ApiStats) + WebApiStats.Range(func(k, v interface{}) bool { + if s, ok := v.(*ApiStats); ok { + ss := *s //计数可能不精准 + stats[k.(string)] = ss + } + return true + }) + return stats +} diff --git a/mgrsrv/config.json b/mgrsrv/config.json new file mode 100644 index 0000000..15c323d --- /dev/null +++ b/mgrsrv/config.json @@ -0,0 +1,98 @@ +{ + "netlib": { + "SrvInfo": { + "Name": "ManagerServer", + "Type": 5, + "Id": 501, + "AreaID": 1, + "Banner": [ + "=================", + "manager server", + "=================" + ] + }, + "IoServices": [ + { + "Id": 501, + "Type": 5, + "AreaId": 1, + "Name": "ManagerService", + "Ip": "", + "Port": 3002, + "MaxDone": 20, + "MaxPend": 20, + "MaxPacket": 6553500, + "MaxConn": 10000, + "RcvBuff": 8192, + "SndBuff": 8192, + "WriteTimeout": 30, + "ReadTimeout": 30, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler" + ] + } + ] + }, + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 31004 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal": { + "SupportSignal": true + }, + "admin": { + "SupportAdmin": true, + "AdminHttpPort": 9899 + }, + "costum": { + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog": 1024 + }, + "common": { + "AppId": "5c56d1644966f078bfb90c71", + "IsDevMode": true + } +} \ No newline at end of file diff --git a/mgrsrv/doc.go b/mgrsrv/doc.go new file mode 100644 index 0000000..f7a2363 --- /dev/null +++ b/mgrsrv/doc.go @@ -0,0 +1,5 @@ +package main + +// Responsibilities: +// 1:responsible for managing the relationship between the server group. +// 2:message forwarding. diff --git a/mgrsrv/logger.xml b/mgrsrv/logger.xml new file mode 100644 index 0000000..cb66204 --- /dev/null +++ b/mgrsrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mgrsrv/main.go b/mgrsrv/main.go new file mode 100644 index 0000000..4528371 --- /dev/null +++ b/mgrsrv/main.go @@ -0,0 +1,39 @@ +package main + +import ( + _ "games.yol.com/win88" + _ "mongo.games.com/game/mgrsrv/api" + _ "mongo.games.com/game/srvdata" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" +) + +func main() { + core.RegisterConfigEncryptor(common.ConfigFE) + defer core.ClosePackages() + core.LoadPackages("config.json") + + model.InitGameParam() + logger.Logger.Warnf("log data %v", srvdata.Config.RootPath) + waiter := module.Start() + waiter.Wait("main()") +} + +func init() { + //首先加载游戏配置 + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + model.StartupRPClient(common.CustomConfig.GetString("MgoRpcCliNet"), common.CustomConfig.GetString("MgoRpcCliAddr"), time.Duration(common.CustomConfig.GetInt("MgoRpcCliReconnInterV"))*time.Second) + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + model.ShutdownRPClient() + return nil + }) +} diff --git a/mgrsrv/monitormgr.go b/mgrsrv/monitormgr.go new file mode 100644 index 0000000..c24567b --- /dev/null +++ b/mgrsrv/monitormgr.go @@ -0,0 +1,147 @@ +package main + +import ( + "encoding/gob" + "mongo.games.com/game/common" + "mongo.games.com/game/mgrsrv/api" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/profile" + "mongo.games.com/goserver/core/schedule" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" + "time" +) + +var MonitorMgrSington = &MonitorMgr{} + +type MonitorMgr struct { +} + +func (this *MonitorMgr) ModuleName() string { + return "MonitorMgr" +} + +func (this *MonitorMgr) Init() { + +} + +func (this *MonitorMgr) Update() { + //webapi stats + apiStats := api.Stats() + if len(apiStats) > 0 { + log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", apiStats) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("webapi", log) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //logic stats + logicStats := profile.GetStats() + if len(logicStats) > 0 { + logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats) + if logLogic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("logic", logLogic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //net session stats + netStats := netlib.Stats() + if len(netStats) > 0 { + logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats) + if logNet != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("net", logNet) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //schedule stats + jobStats := schedule.Stats() + if len(jobStats) > 0 { + logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats) + if logJob != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("job", logJob) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //trans stats + transStats := transact.Stats() + if len(transStats) > 0 { + logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats) + if logTrans != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("transact", logTrans) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //panic stats + panicStats := utils.GetPanicStats() + if len(panicStats) > 0 { + for key, stats := range panicStats { + logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats) + if logPanic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("panic", logPanic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + } + + //object command quene stats + objStats := core.AppCtx.GetStats() + if len(objStats) > 0 { + logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats) + if logCmd != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("cmdque", logCmd) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //gorouting count, eg. system info + runtimeStats := utils.StatsRuntime() + logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats) + if logRuntime != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertMonitorData("runtime", logRuntime) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } +} + +func (this *MonitorMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + //gob registe + gob.Register(api.ApiStats{}) + gob.Register(map[string]api.ApiStats{}) + gob.Register(profile.TimeElement{}) + gob.Register(map[string]profile.TimeElement{}) + gob.Register(netlib.ServiceStats{}) + gob.Register(map[int]netlib.ServiceStats{}) + gob.Register(schedule.TaskStats{}) + gob.Register(map[string]schedule.TaskStats{}) + gob.Register(transact.TransStats{}) + gob.Register(map[int]transact.TransStats{}) + gob.Register(utils.PanicStackInfo{}) + gob.Register(map[string]utils.PanicStackInfo{}) + gob.Register(basic.CmdStats{}) + gob.Register(map[string]basic.CmdStats{}) + gob.Register(utils.RuntimeStats{}) + //gob registe + + module.RegisteModule(MonitorMgrSington, time.Minute*5, 0) +} diff --git a/mgrsrv/stop.go b/mgrsrv/stop.go new file mode 100644 index 0000000..bffe57f --- /dev/null +++ b/mgrsrv/stop.go @@ -0,0 +1,169 @@ +package main + +import ( + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/signal" + "os" + "time" + + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +const ( + STOPAPI_TRANSACTE_UD int = iota +) + +type StopAPIUserData struct { + wait chan struct{} + srvtype int + timeout time.Duration +} + +type InterruptSignalHandler struct { +} + +func (ish *InterruptSignalHandler) Process(s os.Signal, ud interface{}) error { + logger.Logger.Warn("Receive Interrupt signal, process be close!!!") + wait := make(chan struct{}, 1) + + //wait all world server close + StopServer(wait, srvlib.WorldServerType, time.Minute*5) + + //shutdown all server + ShutdownAllServer(wait, time.Minute*10) + + //close self + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + module.Stop() + return nil + }), false) + return nil +} + +func StopServer(wait chan struct{}, srvtype int, timeout time.Duration) { + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + logger.Logger.Infof("StopApi start transcate srvtype(%v) timeout(%v)", srvtype, timeout) + tnp := &transact.TransNodeParam{ + Tt: common.TransType_StopServer, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + tNode := transact.DTCModule.StartTrans(tnp, &StopAPIUserData{wait: wait, srvtype: srvtype, timeout: timeout}, timeout) + if tNode != nil { + tNode.Go(core.CoreObject()) + } + return nil + }), false) + select { + case _ = <-wait: + logger.Logger.Infof("StopApi transcate srvtype(%v) all done!!!", srvtype) + case <-time.After(timeout): + logger.Logger.Infof("StopApi transcate srvtype(%v) timeout force stop!!!", srvtype) + } +} + +func ShutdownAllServer(wait chan struct{}, timeout time.Duration) { + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + logger.Logger.Infof("StopApi start shutdown all server") + ctrlPacket := &server.ServerCtrl{ + CtrlCode: proto.Int32(common.SrvCtrlCloseCode), + } + proto.SetDefaults(ctrlPacket) + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, common.GetSelfAreaId(), srvlib.GameServerType) + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, common.GetSelfAreaId(), srvlib.GateServerType) + srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, common.GetSelfAreaId(), srvlib.WorldServerType) + + //启动定时器检测 + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + gameId := srvlib.ServerSessionMgrSington.GetServerId(common.GetSelfAreaId(), srvlib.GameServerType) + if gameId != -1 { + logger.Logger.Infof("StopApi start shutdown all server gameId:%v", gameId) + return true + } + gateId := srvlib.ServerSessionMgrSington.GetServerId(common.GetSelfAreaId(), srvlib.GateServerType) + if gateId != -1 { + logger.Logger.Infof("StopApi start shutdown all server gateId:%v", gateId) + return true + } + worldId := srvlib.ServerSessionMgrSington.GetServerId(common.GetSelfAreaId(), srvlib.WorldServerType) + if worldId != -1 { + logger.Logger.Infof("StopApi start shutdown all server worldId:%v", worldId) + return true + } + wait <- struct{}{} + timer.StopTimer(h) + return true + }), nil, time.Second, -1) + return nil + }), false) + + select { + case _ = <-wait: + logger.Logger.Info("StopApi ShutdownAllServer all done!!!") + case <-time.After(timeout): + logger.Logger.Info("StopApi ShutdownAllServer timeout force stop!!!") + } +} +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + signal.SignalHandlerModule.ClearHandler(os.Interrupt) + signal.SignalHandlerModule.RegisteHandler(os.Interrupt, &InterruptSignalHandler{}, nil) + return nil + }) + + transact.RegisteHandler(common.TransType_StopServer, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnExecuteWrapper ") + if stopUD, ok := ud.(*StopAPIUserData); ok { + tNode.TransEnv.SetField(STOPAPI_TRANSACTE_UD, ud) + ids := srvlib.ServerSessionMgrSington.GetServerIds(common.GetSelfAreaId(), stopUD.srvtype) + for _, id := range ids { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_StopServer, + Ot: transact.TransOwnerType(stopUD.srvtype), + Oid: id, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + tNode.StartChildTrans(tnp, nil, stopUD.timeout) + } + return transact.TransExeResult_Success + } + return transact.TransExeResult_Failed + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnCommitWrapper") + field := tNode.TransEnv.GetField(STOPAPI_TRANSACTE_UD) + if field != nil { + if ud, ok := field.(*StopAPIUserData); ok { + ud.wait <- struct{}{} + } + } + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnRollBackWrapper") + field := tNode.TransEnv.GetField(STOPAPI_TRANSACTE_UD) + if field != nil { + if ud, ok := field.(*StopAPIUserData); ok { + ud.wait <- struct{}{} + } + } + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + return transact.TransExeResult(retCode) + }), + }) +} diff --git a/model/account.go b/model/account.go new file mode 100644 index 0000000..c5fd3f8 --- /dev/null +++ b/model/account.go @@ -0,0 +1,547 @@ +package model + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "fmt" + "io" + "strconv" + "time" + + "github.com/globalsign/mgo/bson" + + "mongo.games.com/game/common" +) + +// 登录次数,最后登录最后登出时间并不准确 + +type Account struct { + AccountId bson.ObjectId `bson:"_id"` + SnId int32 //玩家账号直接在这里生成 + UserName string //Service Provider AccountId + PassWord string //昵称密码 + TelPassWord string //帐号密码 + BackPassWord string //备份账号密码 + //VIP int32 //VIP帐号 等级 + Platform string //平台 + Channel string //渠道 + Promoter string //推广员 + SubPromoter string //子推广员 + InviterId int32 //邀请人ID + PromoterTree int32 //推广树ID + Tel string //电话号码 + Params string //其他参数 + DeviceOs string //系统 + PackegeTag string //包标识 + Package string //包名 android:包名 ios:bundleid + DeviceInfo string //设备信息 + LoginTimes int //登录次数 + State int64 //冻结到期时间戳 + RegisteTime time.Time //注册时间 + LastLoginTime time.Time //最后一次登录时间 + LastLogoutTime time.Time //最后一次登出时间 + Flag int32 //二次推广用户标记 + Remark string //备注信息 + TagKey int32 //包标识关键字 + AccountType int32 //1.google 2.facebook + RegisterTs int64 // 注册时间戳 + TelPassWordExpire int64 // 手机验证码登录过期时间戳 +} + +func NewAccount() *Account { + account := &Account{AccountId: bson.NewObjectId()} + return account +} + +type AccIsExistArg struct { + AccId string + Username string + Password string + Tel string + Platform string + Ts int64 + LoginType int32 + TagKey int32 + VerifyToken bool + CodeValid bool +} +type AccRet struct { + Acc *Account + Tag int +} + +func AccountIsExist(userName, tel, passWord, platform string, ts int64, logintype int32, tagkey int32, verifyToken, codeValid bool) (*Account, int) { + if rpcCli == nil { + return nil, 0 + } + + args := &AccIsExistArg{ + Username: userName, + Password: passWord, + Tel: tel, + Platform: platform, + Ts: ts, + LoginType: logintype, + TagKey: tagkey, + VerifyToken: verifyToken, + CodeValid: codeValid, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.AccountIsExist", args, ret, time.Second*30) + if err != nil { + return nil, 0 + } + return ret.Acc, ret.Tag +} + +func AccountTelIsRegiste(tel, platform string, tagkey int32) bool { + if rpcCli == nil { + return false + } + + args := &AccIsExistArg{ + Tel: tel, + Platform: platform, + TagKey: tagkey, + } + var ret bool + rpcCli.CallWithTimeout("AccountSvc.AccountTelIsRegiste", args, &ret, time.Second*30) + return ret +} + +func InsertAccount(userName, passWord, platform, channel, promoter, params, deviceOs string, inviterId, promoterTree int32, + packTag, packname, deviceInfo string, subPromoter string, tagkey int32) (*Account, int) { + if rpcCli == nil { + return nil, 0 + } + + acc := NewAccount() + if acc == nil { + return nil, 0 + } + + tCur := time.Now() + acc.UserName = userName + acc.PassWord = passWord + acc.TelPassWord = passWord + acc.Platform = platform + acc.Channel = channel + acc.Params = params + acc.SubPromoter = subPromoter + acc.DeviceOs = deviceOs + acc.Promoter = promoter + acc.InviterId = inviterId + acc.PromoterTree = promoterTree + acc.LastLoginTime = tCur + acc.RegisteTime = tCur + acc.LoginTimes = 1 + acc.PackegeTag = packTag + acc.Package = packname + acc.DeviceInfo = deviceInfo + acc.TagKey = tagkey + acc.RegisterTs = tCur.Unix() + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.InsertAccount", acc, ret, time.Second*30) + if err != nil { + return nil, 0 + } + return ret.Acc, ret.Tag +} + +func InsertTelAccount(userName, passWord, platform, channel, promoter, params string, inviterId, promoterTree int32, tel, + telpassword, packTag, packname, deviceInfo string, tagkey, accountType int32, deviceOs string) (*Account, int) { + if rpcCli == nil { + return nil, 0 + } + acc := NewAccount() + if acc == nil { + return nil, 0 + } + + tCur := time.Now() + acc.UserName = userName + acc.PassWord = passWord + acc.Tel = tel + acc.TelPassWord = telpassword + acc.Platform = platform + acc.Channel = channel + acc.Promoter = promoter + acc.Params = params + acc.InviterId = inviterId + acc.PromoterTree = promoterTree + acc.LastLoginTime = tCur + acc.RegisteTime = tCur + acc.LoginTimes = 1 + acc.PackegeTag = packTag + acc.Package = packname + acc.DeviceInfo = deviceInfo + acc.TagKey = tagkey + acc.AccountType = accountType + acc.RegisterTs = tCur.Unix() + acc.DeviceOs = deviceOs + acc.TelPassWordExpire = tCur.AddDate(0, 0, common.TelLoginValidDays).Unix() + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.InsertTelAccount", acc, ret, time.Second*30) + if err != nil { + return nil, 0 + } + return ret.Acc, ret.Tag +} + +func UpgradeAccount(accId, tel, passWord, platform string, tagkey int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &AccIsExistArg{ + AccId: accId, + Password: passWord, + Tel: tel, + Platform: platform, + TagKey: tagkey, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UpgradeAccount", args, ret, time.Second*30) + if err != nil { + return fmt.Errorf("UpgradeAccount err:%v tel:%v accid:%v", err, tel, accId) + } + if ret.Acc != nil && ret.Acc.Tel != "" { + return errors.New("Tel is Bind") + } + return nil +} + +type BindTelAccountArgs struct { + Platform string + AccId string + Tel string + TagKey int32 +} + +type BindTelAccountRet struct { + Code int // 0成功 1失败 + Acc *Account +} + +func BindTelAccount(accId, tel, platform string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &BindTelAccountArgs{ + Platform: platform, + AccId: accId, + Tel: tel, + } + ret := &BindTelAccountRet{} + err := rpcCli.CallWithTimeout("AccountSvc.BindTelAccount", args, ret, time.Second*30) + if err != nil { + return fmt.Errorf("BindTelAccount err:%v tel:%v accid:%v", err, tel, accId) + } + if ret.Code != 0 { + return fmt.Errorf("BindTelAccount err:%v tel:%v accid:%v", err, tel, accId) + } + if ret.Acc != nil && ret.Acc.Tel != "" { + return errors.New("Tel is Bind") + } + return nil +} + +func LogoutAccount(acc *Account) error { + if acc == nil { + return nil + } + if rpcCli == nil { + return ErrRPClientNoConn + } + + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.LogoutAccount", acc, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +type AccFreezeArg struct { + AccId string + Platform string + FreezeTime int +} + +func FreezeAccount(plt, accId string, freezeTime int) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &AccFreezeArg{ + AccId: accId, + Platform: plt, + FreezeTime: freezeTime, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.FreezeAccount", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func UnfreezeAccount(plt, accId string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &AccFreezeArg{ + AccId: accId, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UnfreezeAccount", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +type AccIdArg struct { + AccId string + Platform string +} + +type UserNameArg struct { + UserName string + Platform string +} + +func GetAccount(plt, accId string) (*Account, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &AccIdArg{ + AccId: accId, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.GetAccount", args, ret, time.Second*30) + if err != nil { + return nil, err + } + return ret.Acc, err +} + +func GetAccountByName(plt, username string) (*Account, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &UserNameArg{ + UserName: username, + Platform: plt, + } + var ret AccRet + err := rpcCli.CallWithTimeout("AccountSvc.GetAccountByUserName", args, &ret, time.Second*30) + if err != nil { + return nil, err + } + return ret.Acc, err +} + +type UserSnidArg struct { + Snid int32 + Platform string +} + +func GetAccountBySnid(plt string, snid int32) (*Account, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &UserSnidArg{ + Snid: snid, + Platform: plt, + } + var ret AccRet + err := rpcCli.CallWithTimeout("AccountSvc.GetAccountBySnId", args, &ret, time.Second*30) + if err != nil { + return nil, err + } + return ret.Acc, err +} + +// 删除账号 +func RemoveAccount(plt, accId string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &AccIdArg{ + AccId: accId, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.RemoveAccount", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +// pwd暂时用明文.需要使用MD5加密。参照task_login.go +func EditAccountPwd(plt, accId, pwd string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &Account{ + AccountId: bson.ObjectIdHex(accId), + PassWord: pwd, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.EditAccountPwd", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func ResetBackAccountPwd(plt, accId string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &AccIdArg{ + AccId: accId, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.ResetBackAccountPwd", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func GenAccountPwd(pwd string) string { + //md5(原始密码+AppId) + raw := fmt.Sprintf("%v%v", pwd, common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + hashsum := hex.EncodeToString(h.Sum(nil)) + return hashsum +} + +/* + * 修改帐号密码 + */ +func UpdatePlayerPassword(plt, accId, oldpassword, passWord string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &Account{ + AccountId: bson.ObjectIdHex(accId), + PassWord: passWord, + BackPassWord: oldpassword, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UpdatePlayerPassword", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +/* + * 修改Token帐号密码 + */ +func UpdatePlayerTokenPassword(plt, accId, passWord string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &Account{ + AccountId: bson.ObjectIdHex(accId), + PassWord: passWord, + Platform: plt, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UpdatePlayerTokenPassword", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +/* + * 找回密码 + */ +func GetBackPlayerPassword(tel, passWord, platform string, tagkey int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &Account{ + Tel: tel, + Platform: platform, + TagKey: tagkey, + PassWord: passWord, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.GetBackPlayerPassword", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func UpdateAccountPlatformInfo(account string, platform, channel, promoter, inviterid, promoterTree int32, packTag string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &Account{ + AccountId: bson.ObjectIdHex(account), + Platform: strconv.Itoa(int(platform)), + Channel: strconv.Itoa(int(channel)), + Promoter: strconv.Itoa(int(promoter)), + PromoterTree: promoterTree, + InviterId: inviterid, + PackegeTag: packTag, + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UpdateAccountPlatformInfo", args, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func GetRobotAccounts(limit int) []Account { + if rpcCli == nil { + return nil + } + + var accs []Account + err := rpcCli.CallWithTimeout("AccountSvc.GetRobotAccounts", limit, &accs, time.Second*30) + if err != nil { + return nil + } + return accs +} + +func SaveToDelBackupAccount(acc *Account) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.SaveToDelBackupAccount", acc, ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func UpdateAccount(acc *Account) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := &AccRet{} + err := rpcCli.CallWithTimeout("AccountSvc.UpdateAccount", acc, ret, time.Second*30) + if err != nil { + return err + } + return nil +} diff --git a/model/actmonitorlist.go b/model/actmonitorlist.go new file mode 100644 index 0000000..b5f2c83 --- /dev/null +++ b/model/actmonitorlist.go @@ -0,0 +1,85 @@ +package model + +import ( + "mongo.games.com/goserver/core/logger" + "time" +) + +type ActMonitor struct { + SeqNo int64 + SnId int32 + Platform string //平台 + MonitorType int32 //二进制 1.登录 2.兑换 3.游戏 + CreateTime int64 //创建时间 + Creator string //创建者 + ReMark string //备注 +} + +func NewActMonitor() *ActMonitor { + log := &ActMonitor{} + return log +} + +func NewActMonitorEx(snid int32, platform string, MonitorType int32, Creator, ReMark string) *ActMonitor { + cl := NewActMonitor() + cl.SnId = snid + cl.Platform = platform + cl.MonitorType = MonitorType + cl.CreateTime = time.Now().Unix() + cl.Creator = Creator + cl.ReMark = ReMark + return cl +} + +type ActMonitoRet struct { + Err error + Data []*ActMonitor +} + +func GetAllActMonitorData() (data []*ActMonitor) { + if rpcCli == nil { + return + } + ret := &ActMonitoRet{} + err := rpcCli.CallWithTimeout("ActMonitorSvc.GetAllActMonitorData", 0, ret, time.Second*30) + if err != nil { + logger.Logger.Error("Get ActMonitor data eror.", err) + } + return +} +func UpsertSignleActMonitor(am *ActMonitor) (err error) { + if rpcCli == nil { + return + } + ret := &ActMonitoRet{} + err = rpcCli.CallWithTimeout("ActMonitorSvc.UpsertSignleActMonitor", am, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpsertSignleActMonitor eror.", err) + } + return +} + +func UpdateSignleActMonitor(am *ActMonitor) (err error) { + if rpcCli == nil { + return + } + ret := &ActMonitoRet{} + err = rpcCli.CallWithTimeout("ActMonitorSvc.UpdateSignleActMonitor", am, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpdateSignleActMonitor eror.", err) + } + return +} + +func RemoveActMonitorOne(seqno int) error { + if rpcCli == nil { + return nil + } + ret := &ActMonitoRet{} + err := rpcCli.CallWithTimeout("ActMonitorSvc.RemoveActMonitorOne", seqno, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.RemoveActMonitorOne eror.", err) + return err + } + return nil +} diff --git a/model/apilog.go b/model/apilog.go new file mode 100644 index 0000000..955a1f4 --- /dev/null +++ b/model/apilog.go @@ -0,0 +1,72 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + APILogDBName = "log" + APILogCollName = "log_api" +) + +const ( + APILogType_Login int32 = iota + APILogType_Rehold + APILogType_Logout +) + +type APILog struct { + LogId bson.ObjectId `bson:"_id"` + Path string + RawQuery string + Body string + Ip string + Result string + ProcessTime int64 + GetTime int64 + Time time.Time +} + +func NewAPILog(path, rawQuery, body, ip, result string, getTime int64, processTime int64) *APILog { + cl := &APILog{LogId: bson.NewObjectId()} + cl.Path = path + cl.RawQuery = rawQuery + cl.Body = body + cl.Ip = ip + cl.GetTime = getTime + cl.Result = result + cl.ProcessTime = processTime + cl.Time = time.Now() + return cl +} + +func InsertAPILog(log *APILog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertAPILog rpcCli == nil") + return + } + + var ret bool + err = rpcCli.CallWithTimeout("APILogSvc.InsertAPILog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertAPILog error:", err) + } + return +} + +func RemoveAPILog(ts int64) (chged *mgo.ChangeInfo, err error) { + if rpcCli == nil { + logger.Logger.Error("model.RemoveAPILog rpcCli == nil") + return + } + + err = rpcCli.CallWithTimeout("APILogSvc.RemoveAPILog", ts, &chged, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveAPILog error:", err) + } + return +} diff --git a/model/autoincrementid.go b/model/autoincrementid.go new file mode 100644 index 0000000..89ef0fc --- /dev/null +++ b/model/autoincrementid.go @@ -0,0 +1,9 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" +) + +func AutoIncGameLogId() (string, error) { + return bson.NewObjectId().Hex(), nil +} diff --git a/model/baginfo.go b/model/baginfo.go new file mode 100644 index 0000000..84e994b --- /dev/null +++ b/model/baginfo.go @@ -0,0 +1,85 @@ +package model + +import ( + "fmt" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +type BagInfo struct { + BagId bson.ObjectId `bson:"_id"` + SnId int32 //玩家账号直接在这里生成 + Platform string //平台 + BagItem map[int32]*Item //背包数据 key为itemId +} + +type Item struct { + ItemId int32 // 物品ID + ItemNum int64 // 物品数量 + ObtainTime int64 //获取的时间 +} +type GetBagInfoArgs struct { + Plt string + SnId int32 +} + +func NewBagInfo(sid int32, plt string) *BagInfo { + return &BagInfo{BagId: bson.NewObjectId(), SnId: sid, Platform: plt, BagItem: make(map[int32]*Item)} +} + +func GetBagInfo(sid int32, plt string) *BagInfo { + if rpcCli == nil { + return nil + } + ret := &BagInfo{} + args := &GetBagInfoArgs{ + SnId: sid, + Plt: plt, + } + err := rpcCli.CallWithTimeout("BagSvc.GetBagItem", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("GetBagInfo err:%v SnId:%v ", err, args.SnId) + return nil + } + return ret +} + +func UpBagItem(args *BagInfo) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + ret := false + err := rpcCli.CallWithTimeout("BagSvc.UpgradeBag", args, &ret, time.Second*30) + if err != nil { + return fmt.Errorf("UpgradeBag err:%v SnId:%v BagId:%v", err, args.SnId, args.BagId) + } + + return nil +} + +func SaveDBBagItem(args *BagInfo) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := false + err := rpcCli.CallWithTimeout("BagSvc.AddBagItem", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func SaveToDelBackupBagItem(args *BagInfo) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := false + err := rpcCli.CallWithTimeout("BagSvc.UpdateBag", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} diff --git a/model/bankbindlog.go b/model/bankbindlog.go new file mode 100644 index 0000000..406ce0f --- /dev/null +++ b/model/bankbindlog.go @@ -0,0 +1,42 @@ +package model + +import ( + "time" +) + +var ( + BankBindDBName = "log" + BankBindCollName = "log_bankbind" +) + +const ( + BankBindLogType_Bank int32 = iota + BankBindLogType_Ali +) + +type BankBindLog struct { + Snid int32 + Platform string + LogType int32 + Name string + Card string + Modify int32 + Time time.Time +} + +func NewBankBindLog(snid int32, platform string, logtype int32, name, card string, modify int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + log := &BankBindLog{ + Snid: snid, + Platform: platform, + LogType: logtype, + Name: name, + Card: card, + Modify: modify, + Time: time.Now(), + } + var ret bool + return rpcCli.CallWithTimeout("BankBindLogSvc.InsertBankBindLog", log, &ret, time.Second*30) +} diff --git a/model/bankruptlog.go b/model/bankruptlog.go new file mode 100644 index 0000000..fd7a57c --- /dev/null +++ b/model/bankruptlog.go @@ -0,0 +1,43 @@ +package model + +import "time" +import "github.com/globalsign/mgo/bson" + +// 破产次数统计 +var ( + BankruptLogCollName = "log_bankrupt" +) + +// 破产次数统计 +type BankruptLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + GameId int //游戏id + GameFreeID int32 //房间id + + CreateTime int64 // 注册时间 + UseCoin int64 // 消耗金币 + Ts int64 // 破产时间 +} + +func NewBankruptLog() *BankruptLog { + log := &BankruptLog{LogId: bson.NewObjectId()} + return log +} + +func NewBankruptLogEx(snid int32, gamefreeid int32, createtime, usecoin int64, platform string, gameId int) *BankruptLog { + cl := NewBankruptLog() + cl.SnId = snid + cl.Platform = platform + cl.GameId = gameId + + cl.GameFreeID = gamefreeid + cl.CreateTime = createtime + + now := time.Now() + cl.Ts = now.Unix() + cl.UseCoin = usecoin + + return cl +} diff --git a/model/blackwhitelog.go b/model/blackwhitelog.go new file mode 100644 index 0000000..df59704 --- /dev/null +++ b/model/blackwhitelog.go @@ -0,0 +1,103 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +type BlackWhiteCoinLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //用户ID + WBLevel int32 + WBCoinLimit int64 + ResetTotalCoin int32 + Platform string //平台id + Time time.Time //账单时间 +} + +func NewBlackWhiteCoinLog(snid, wbLevel int32, wbCoinLimit int64, resetTotalCoin int32, platform string) *BlackWhiteCoinLog { + tNow := time.Now() + log := &BlackWhiteCoinLog{ + LogId: bson.NewObjectId(), + SnId: snid, + WBLevel: wbLevel, + WBCoinLimit: wbCoinLimit, + ResetTotalCoin: resetTotalCoin, + Platform: platform, + Time: tNow, + } + return log +} + +type BlackWhiteCoinArg struct { + Id bson.ObjectId + SnId int32 + BillNo int64 + Platform string +} +type BlackWhiteCoinRet struct { + Err error + Data *BlackWhiteCoinLog +} + +func InsertBlackWhiteCoinLogs(logs ...*BlackWhiteCoinLog) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := &BlackWhiteCoinRet{} + err := rpcCli.CallWithTimeout("BlackWhiteCoinSvc.InsertBlackWhiteCoinLogs", logs, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.InsertBlackWhiteCoinLogs error", err) + return err + } + return ret.Err +} +func InsertBlackWhiteCoinLog(log *BlackWhiteCoinLog) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + ret := &BlackWhiteCoinRet{} + err := rpcCli.CallWithTimeout("BlackWhiteCoinSvc.InsertBlackWhiteCoinLog", log, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.InsertBlackWhiteCoinLog error", err) + return err + } + return ret.Err +} + +func RemoveBlackWhiteCoinLog(id bson.ObjectId, platform string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &BlackWhiteCoinArg{ + Id: id, + Platform: platform, + } + ret := &BlackWhiteCoinRet{} + err := rpcCli.CallWithTimeout("BlackWhiteCoinSvc.RemoveBlackWhiteCoinLog", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.RemoveBlackWhiteCoinLog error", err) + return err + } + return ret.Err +} + +func GetBlackWhiteCoinLogByBillNo(snid int32, billNo int64, platform string) (*BlackWhiteCoinLog, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &BlackWhiteCoinArg{ + SnId: snid, + BillNo: billNo, + Platform: platform, + } + ret := &BlackWhiteCoinRet{} + err := rpcCli.CallWithTimeout("BlackWhiteCoinSvc.GetBlackWhiteCoinLogByBillNo", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetBlackWhiteCoinLogByBillNo error", err) + return nil, err + } + return ret.Data, ret.Err +} diff --git a/model/bullfightparam.go b/model/bullfightparam.go new file mode 100644 index 0000000..ef9cb00 --- /dev/null +++ b/model/bullfightparam.go @@ -0,0 +1,68 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +type BullFightParam struct { + // + N int32 // = 10 //循环周期 + PayUserLost float64 // = 30 //充值用户忍耐倍数 + NoPayUserLost float64 // = 50 //非充值用户忍耐倍数 + PayCoinBase float64 // = 2000 //充值增加次数倍数 + BaseRate float64 // = 4 //默认系数 + + BankModifyRate float64 // = 500 //输赢修正比例 + BankModifyPowRate float64 // = 3 //输赢修正比例 + BankModifyMaxRate float64 // = 5000 //最大修正比例 + +} + +var BullFightParamPath = "../data/bullfightparam.json" +var BullFightParamData = &BullFightParam{} + +func InitBullFightParam() { + buf, err := os.ReadFile(BullFightParamPath) + if err != nil { + logger.Logger.Warn("InitBullFightParam os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, BullFightParamData) + if err != nil { + logger.Logger.Warn("InitBullFightParam json.Unmarshal error ->", err) + } + if BullFightParamData.N == 0 { + BullFightParamData.N = 10 + } + + if BullFightParamData.PayUserLost == 0 { + BullFightParamData.PayUserLost = 30 + } + + if BullFightParamData.NoPayUserLost == 0 { + BullFightParamData.NoPayUserLost = 50 + } + + if BullFightParamData.BaseRate == 0 { + BullFightParamData.BaseRate = 4 + } + + if BullFightParamData.PayCoinBase == 0 { + BullFightParamData.PayCoinBase = 4 + } + + if BullFightParamData.BankModifyRate == 0 { + BullFightParamData.BankModifyRate = 500 + } + + if BullFightParamData.BankModifyPowRate == 0 { + BullFightParamData.BankModifyPowRate = 3 + } + + if BullFightParamData.BankModifyMaxRate == 0 { + BullFightParamData.BankModifyMaxRate = 5000 + } + +} diff --git a/model/burstjackpotlog.go b/model/burstjackpotlog.go new file mode 100644 index 0000000..2a50f01 --- /dev/null +++ b/model/burstjackpotlog.go @@ -0,0 +1,74 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +var ( + BurstJackpotLogDBName = "log" + BurstJackpotLogCollName = "log_burstjackpotlog" + TopicProbeBurstJackpotLogAck = "ack_burstjackpotlog" +) + +type BurstJackpotLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string + GameId int32 + GameFreeId int32 + Name string //用户昵称 + WinCoin int64 //爆奖金额 + TotalBet int64 //下注 + Ts int64 //创建时间 +} + +func NewBurstJackpotLog(platform string, gameId, gameFreeId int32, name string, winCoin, totalBet int64) *BurstJackpotLog { + log := &BurstJackpotLog{ + LogId: bson.NewObjectId(), + Platform: platform, + GameId: gameId, + GameFreeId: gameFreeId, + Name: name, + WinCoin: winCoin, + TotalBet: totalBet, + Ts: time.Now().Unix(), + } + return log +} + +type BurstJackpotArg struct { + Platform string + GameId int32 + Logs []BurstJackpotLog +} + +func GetBurstJackpotLog(platform string, gameId int32) []BurstJackpotLog { + if rpcCli == nil { + logger.Logger.Error("GetBurstJackpotLog RPClient no connect") + return nil + } + args := &BurstJackpotArg{ + Platform: platform, + GameId: gameId, + } + var ret []BurstJackpotLog + err := rpcCli.CallWithTimeout("BurstJackpotSvc.GetBurstJackpotLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetBurstJackpotLog error", err) + return nil + } + return ret +} +func InsertBurstJackpotLogs(logs *BurstJackpotLog) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err := rpcCli.CallWithTimeout("BurstJackpotSvc.InsertBurstJackpotLogs", logs, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.InsertBurstJackpotLogs error", err) + return err + } + return err +} diff --git a/model/chat.go b/model/chat.go new file mode 100644 index 0000000..41f26cc --- /dev/null +++ b/model/chat.go @@ -0,0 +1,105 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "sort" + "time" +) + +type Chat struct { + Id bson.ObjectId `bson:"_id"` + BindSnid string //snid1 < snid2 + ChatContent []*ChatContent + LastChatTs int64 +} + +type ChatContent struct { + SrcSnId int32 + Content string + Ts int64 +} + +const ChatMaxNum = 1000 //聊天记录上限 + +type ChatRet struct { + C *Chat +} + +type ChatByKey struct { + Platform string + BindSnId string + C *Chat +} + +func NewChat(bindsnid string) *Chat { + f := &Chat{Id: bson.NewObjectId()} + f.BindSnid = bindsnid + f.LastChatTs = time.Now().Unix() + f.ChatContent = []*ChatContent{} + return f +} + +func UpsertChat(platform, bindsnid string, chat *Chat) *Chat { + if rpcCli == nil { + logger.Logger.Error("model.UpsertChat rpcCli == nil") + return nil + } + if chat != nil && chat.ChatContent != nil && len(chat.ChatContent) > ChatMaxNum { + sort.Slice(chat.ChatContent, func(i, j int) bool { + if chat.ChatContent[i].Ts < chat.ChatContent[j].Ts { + return false + } + return true + }) + chat.ChatContent = append(chat.ChatContent[:ChatMaxNum]) + } + args := &ChatByKey{ + Platform: platform, + BindSnId: bindsnid, + C: chat, + } + ret := &ChatRet{} + err := rpcCli.CallWithTimeout("ChatSvc.UpsertChat", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertChat error:", err) + return nil + } + return ret.C +} + +func QueryChatByBindSnid(platform, bindsnid string) (chat *Chat, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryChatByBindSnid rpcCli == nil") + return + } + args := &ChatByKey{ + Platform: platform, + BindSnId: bindsnid, + } + var ret *ChatRet + err = rpcCli.CallWithTimeout("ChatSvc.QueryChatByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryChatByBindSnid error:", err) + } + if ret != nil { + chat = ret.C + } + return +} + +func DelChat(platform, bindsnid string) { + if rpcCli == nil { + logger.Logger.Error("model.DelChat rpcCli == nil") + return + } + args := &ChatByKey{ + Platform: platform, + BindSnId: bindsnid, + } + err := rpcCli.CallWithTimeout("ChatSvc.DelChat", args, nil, time.Second*30) + if err != nil { + logger.Logger.Error("DelChat error:", err) + } + return +} diff --git a/model/clientlog.go b/model/clientlog.go new file mode 100644 index 0000000..3144786 --- /dev/null +++ b/model/clientlog.go @@ -0,0 +1,16 @@ +package model + +var ( + ClientLogDBName = "log" + ClientLogCollName = "log_clientlog" + ClientLogMysqlCollName = "log_clientlog_mysql" +) + +type ClientLog struct { + Data string // 客户端数据,json格式 + Platform string // 平台id + Snid int32 // 玩家id + Ts int64 // 时间戳 +} + +type ClientLogMysql ClientLog diff --git a/model/clientparam.go b/model/clientparam.go new file mode 100644 index 0000000..db84ec7 --- /dev/null +++ b/model/clientparam.go @@ -0,0 +1,19 @@ +package model + +import ( + "mongo.games.com/goserver/core/logger" + "os" +) + +var ClientParamPath = "../data/clientparam.json" + +var ClinetBuf []byte + +func InitClientParam() { + buf, err := os.ReadFile(ClientParamPath) + if err != nil { + logger.Logger.Warn("InitClientParam os.ReadFile error ->", err) + } + logger.Logger.Info("InitClientParam param=", string(buf)) + ClinetBuf = buf +} diff --git a/model/coingivelog.go b/model/coingivelog.go new file mode 100644 index 0000000..03a4be1 --- /dev/null +++ b/model/coingivelog.go @@ -0,0 +1,272 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + CoinGiveLogDBName = "log" + CoinGiveLogCollName = "log_coingive" +) + +const ( + COINGIVETYPE_PAY = 0 //充值 + COINGIVETYPE_SYSTEM = 1 //系统赠送 + +) + +const ( + COINGIVEVER = 0 //当前数据版本 +) + +type CoinGiveLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PromoterTree int32 //全民树 + PayCoin int64 //充值金额 + GiveCoin int64 //赠送金额 + Remark string //备注 + Opter string //操作接口 + State int64 //预留处理标记用 + FLow int64 //完成流水 + Ts int64 //时间戳 纳秒 + NeedFlowRate int32 //需要流水比例 + NeedGiveFlowRate int32 //则送需要流水比例 + Time time.Time //时间戳 + CoinType int32 //金币类型 0:钱包 1:保险箱 + RecType int32 //记录类型 0:充值 1:系统赠送 + LogType int32 //log类型 + Ver int32 //数据版本 +} + +func NewCoinGiveLogEx(snid int32, username string, payCoin, giveCoin int64, coinType, logType, promoterTree, recType, ver int32, platform, channel, + promoter, remark, op, packageid string, needFlowRate, needGiveFlowRate int32) *CoinGiveLog { + cl := &CoinGiveLog{LogId: bson.NewObjectId()} + cl.SnId = snid + //cl.UserName = username + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.PayCoin = payCoin + cl.GiveCoin = giveCoin + cl.RecType = recType + cl.CoinType = coinType + cl.LogType = logType + cl.Ver = ver + cl.PromoterTree = promoterTree + cl.Remark = remark + cl.Opter = op + cl.Time = time.Now() + cl.Ts = cl.Time.Local().UnixNano() + cl.NeedFlowRate = needFlowRate + cl.NeedGiveFlowRate = needGiveFlowRate + //cl.PackageTag = packageid + return cl +} + +type UpdateGiveCoinLastFlowArg struct { + Plt string + LogId string + Flow int64 +} + +func UpdateGiveCoinLastFlow(platform, logid string, flow int64) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.UpdateGiveCoinLastFlow rpcCli == nil") + return + } + args := &UpdateGiveCoinLastFlowArg{ + Plt: platform, + LogId: logid, + Flow: flow, + } + var ret bool + err = rpcCli.CallWithTimeout("CoinGiveLogSvc.UpdateGiveCoinLastFlow", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdateGiveCoinLastFlow error:", err) + } + return +} + +type GetGiveCoinLastFlowArg struct { + Plt string + LogId string +} + +func GetGiveCoinLastFlow(platform, logid string) (log *CoinGiveLog) { + if rpcCli == nil { + logger.Logger.Error("model.GetGiveCoinLastFlow rpcCli == nil") + return nil + } + + args := &GetGiveCoinLastFlowArg{ + Plt: platform, + LogId: logid, + } + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.GetGiveCoinLastFlow", args, &log, time.Second*30) + if err != nil { + logger.Logger.Warn("GetGiveCoinLastFlow error:", err) + } + + return +} + +type GetCoinGiveLogListArg struct { + Plt string + SnId int32 + Ver int32 + TsStart int64 + TsEnd int64 +} + +func GetCoinGiveLogList(platform string, snid int32, ver int32, tsStart int64, tsEnd int64) (logs []*CoinGiveLog) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinGiveLogList rpcCli == nil") + return nil + } + + args := &GetCoinGiveLogListArg{ + Plt: platform, + SnId: snid, + Ver: ver, + TsStart: tsStart, + TsEnd: tsEnd, + } + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.GetCoinGiveLogList", args, &logs, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinGiveLogList error:", err) + } + + return +} + +type GetCoinGiveLogListByStateArg struct { + Plt string + SnId int32 + State int64 +} + +func GetCoinGiveLogListByState(platform string, snid int32, state int64) (logs []*CoinGiveLog) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinGiveLogListByState rpcCli == nil") + return nil + } + + args := &GetCoinGiveLogListByStateArg{ + Plt: platform, + SnId: snid, + State: state, + } + + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.GetCoinGiveLogListByState", args, &logs, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinGiveLogListByState error:", err) + } + + return +} + +type ResetCoinGiveLogListArg struct { + Plt string + SnId int32 + Ts int64 +} + +func ResetCoinGiveLogList(platform string, snid int32, ts int64) error { + if rpcCli == nil { + logger.Logger.Error("model.ResetCoinGiveLogList rpcCli == nil") + return nil + } + + args := &ResetCoinGiveLogListArg{ + Plt: platform, + SnId: snid, + Ts: ts, + } + + var ret bool + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.ResetCoinGiveLogList", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("ResetCoinGiveLogList error:", err) + } + + return err +} + +type SetCoinGiveLogListArg struct { + Plt string + SnId int32 + State int64 + TsStart int64 + TsEnd int64 +} + +func SetCoinGiveLogList(platform string, snid int32, startTs int64, endTs int64, v int64) error { + if rpcCli == nil { + logger.Logger.Error("model.SetCoinGiveLogList rpcCli == nil") + return nil + } + + args := &SetCoinGiveLogListArg{ + Plt: platform, + SnId: snid, + State: v, + TsStart: startTs, + TsEnd: endTs, + } + + var ret bool + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.SetCoinGiveLogList", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("SetCoinGiveLogList error:", err) + } + return err +} + +type UpdateCoinLogArg struct { + Plt string + LogId string + State int64 +} + +func UpdateCoinLog(platform, logid string, v int64) error { + if rpcCli == nil { + logger.Logger.Error("model.UpdateCoinLog rpcCli == nil") + return nil + } + + args := &UpdateCoinLogArg{ + Plt: platform, + LogId: logid, + State: v, + } + + var ret bool + err := rpcCli.CallWithTimeout("CoinGiveLogSvc.UpdateCoinLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdateCoinLog error:", err) + } + + return err +} + +func InsertGiveCoinLog(log *CoinGiveLog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertGiveCoinLog rpcCli == nil") + return nil + } + + var ret bool + err = rpcCli.CallWithTimeout("CoinGiveLogSvc.InsertGiveCoinLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertGiveCoinLog error:", err) + } + + return +} diff --git a/model/coinlog.go b/model/coinlog.go new file mode 100644 index 0000000..5f08d0e --- /dev/null +++ b/model/coinlog.go @@ -0,0 +1,333 @@ +package model + +import ( + "sync/atomic" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" +) + +var ( + CoinLogDBName = "log" + CoinLogCollName = "log_coinex" + TopicProbeCoinLogAck = "ack_logcoin" +) + +var COINEX_GLOBAL_SEQ = int64(0) + +type CoinLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PackageTag string //推广包标识 + Count int64 //帐变数量 + RestCount int64 //钱包余额 + SafeBoxCount int64 //保险箱余额 + DiamondCount int64 //钻石余额 + Oper string //操作者 + Remark string //备注 + Time time.Time //时间戳 + InGame int32 //0:其他 1~N:具体游戏id + Ver int32 //数据版本(暂时不用) + CoinType int32 //账变类型 common.BillTypeCoin... + LogType int32 //账变类型 common.GainWay_NewPlayer... + RoomId int32 //房间id + SeqNo int64 //流水号(隶属于进程) + Ts int64 //时间戳 + CoinAdd int64 // 金币加成 + DiamondAdd int64 // 钻石加成 + GameFreeId int64 // 场次id + BaseCoin int64 // 底分 +} + +type CoinLogV1 struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PackageTag string //推广包标识 + CoinType int32 // 账变类型 common.BillTypeCoin... + LogType int32 // 账变类型 common.GainWay_NewPlayer... + Count int64 // 帐变数量 + Add int64 // 加成 + RestCount int64 // 余额 + Oper string //操作者 + Remark string //备注 + Time time.Time //时间戳 + InGame int32 //0:其他 1~N:具体游戏id + Ver int32 //数据版本 + SeqNo int64 //流水号(隶属于进程) + Ts int64 //时间戳 + GameFreeId int64 // 场次id + BaseCoin int64 // 底分 +} + +func NewCoinLog() *CoinLog { + log := &CoinLog{LogId: bson.NewObjectId()} + return log +} + +type CoinLogParam struct { + Platform string // 平台id + SnID int32 // 玩家id + ChangeType int32 // 变化类型 common.BillTypeCoin 金币 common.BillTypeDiamond 钻石 + ChangeNum int64 // 变化数量 + RemainNum int64 // 余额 + Add int64 // 加成 + LogType int32 // 日志类型 + GameID int64 // 游戏id + GameFreeID int64 // 场次id + BaseCoin int64 // 底注 + Operator string // 操作人 + Remark string // 备注 +} + +func NewCoinLogEx(param *CoinLogParam) *CoinLog { + tNow := time.Now() + cl := NewCoinLog() + cl.SnId = param.SnID + cl.Platform = param.Platform + cl.Count = param.ChangeNum + cl.Oper = param.Operator + cl.Remark = param.Remark + cl.InGame = int32(param.GameID) + cl.CoinType = param.ChangeType + cl.LogType = param.LogType + cl.SeqNo = atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1) + cl.Time = tNow + cl.Ts = tNow.Unix() + cl.GameFreeId = param.GameFreeID + cl.BaseCoin = param.BaseCoin + + //todo 后期修改数据结构,余额,加成,各用一个字段存储; NewCoinLogExV2 + switch param.ChangeType { + case common.BillTypeCoin: + cl.RestCount = param.RemainNum + cl.CoinAdd = param.Add + case common.BillTypeDiamond: + cl.DiamondCount = param.RemainNum + cl.DiamondAdd = param.Add + } + + return cl +} + +// NewCoinLogExV1 账变记录 +func NewCoinLogExV1(param *CoinLogParam) *CoinLogV1 { + now := time.Now() + cl := CoinLogV1{ + LogId: bson.NewObjectId(), + SnId: param.SnID, + Platform: param.Platform, + Channel: "", + Promoter: "", + PackageTag: "", + CoinType: param.ChangeType, + LogType: param.LogType, + Count: param.ChangeNum, + Add: param.Add, + RestCount: param.RemainNum, + Oper: param.Operator, + Remark: param.Remark, + Time: now, + InGame: int32(param.GameID), + Ver: 1, + SeqNo: atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1), + Ts: now.Unix(), + GameFreeId: param.GameFreeID, + BaseCoin: param.BaseCoin, + } + return &cl +} + +type GetCoinLogBySnidAndLessTsArg struct { + Plt string + SnId int32 + Ts int64 +} + +func GetCoinLogBySnidAndLessTs(plt string, id int32, ts int64) (ret []CoinLog, err error) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinLogBySnidAndLessTs rpcCli == nil") + return + } + args := &GetCoinLogBySnidAndLessTsArg{ + Plt: plt, + SnId: id, + Ts: ts, + } + err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogBySnidAndLessTs", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinLogBySnidAndLessTs error:", err) + } + return +} + +type GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeArg struct { + Plt string + SnId int32 + LogType int + BillType int + FromIdx int + ToIdx int + StartTs int64 + EndTs int64 +} +type GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeRet struct { + Logs []CoinLog + Count int +} + +func GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange(plt string, id int32, billType, logType int, startts, endts int64, fromIndex, toIndex int) (logs []CoinLog, count int, err error) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinLogBySnidAndInGameAndGreaterTs rpcCli == nil") + return + } + + args := &GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeArg{ + Plt: plt, + SnId: id, + BillType: billType, + LogType: logType, + FromIdx: fromIndex, + ToIdx: toIndex, + StartTs: startts, + EndTs: endts, + } + var ret GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeRet + err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange error:", err) + return + } + + logs = ret.Logs + count = ret.Count + return +} + +type GetCoinLogGameReq struct { + Plt string + SnId int32 + BillType int + FromIdx int + ToIdx int + StartTs int64 + EndTs int64 +} +type GetCoinLogGameRet struct { + Logs []CoinLog + Count int +} + +func GetCoinLogGame(plt string, id int32, billType int, startts, endts int64, fromIndex, toIndex int) (logs []CoinLog, count int, err error) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinLogGame rpcCli == nil") + return + } + + args := &GetCoinLogGameReq{ + Plt: plt, + SnId: id, + BillType: billType, + FromIdx: fromIndex, + ToIdx: toIndex, + StartTs: startts, + EndTs: endts, + } + var ret GetCoinLogGameRet + err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogGame", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinLogGame error:", err) + return + } + + logs = ret.Logs + count = ret.Count + return +} + +func InsertCoinLog(log *CoinLog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertCoinLog rpcCli == nil") + return + } + + var ret bool + err = rpcCli.CallWithTimeout("CoinLogSvc.InsertCoinLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertCoinLog error:", err) + } + return +} +func RemoveCoinLog(ts int64) (chged *mgo.ChangeInfo, err error) { + if rpcCli == nil { + logger.Logger.Error("model.RemoveAPILog rpcCli == nil") + return + } + args := &RemoveCoinLogOneArg{ + Plt: "1", + Ts: ts, + } + err = rpcCli.CallWithTimeout("CoinLogSvc.RemoveCoinLog", args, &chged, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveCoinLog error:", err) + } + return +} + +type RemoveCoinLogOneArg struct { + Plt string + Id bson.ObjectId + Ts int64 +} + +func RemoveCoinLogOne(plt string, id bson.ObjectId) error { + if rpcCli == nil { + logger.Logger.Error("model.RemoveCoinLogOne rpcCli == nil") + return nil + } + + args := &RemoveCoinLogOneArg{ + Plt: plt, + Id: id, + } + var ret bool + err := rpcCli.CallWithTimeout("CoinLogSvc.RemoveCoinLogOne", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveCoinLogOne error:", err) + } + return err +} + +type UpdateCoinLogRemarkArg struct { + Plt string + Id bson.ObjectId + Remark string +} + +func UpdateCoinLogRemark(plt string, id bson.ObjectId, remark string) error { + if rpcCli == nil { + logger.Logger.Error("model.UpdateCoinLogRemark rpcCli == nil") + return nil + } + + args := &UpdateCoinLogRemarkArg{ + Plt: plt, + Id: id, + Remark: remark, + } + var ret bool + err := rpcCli.CallWithTimeout("CoinLogSvc.UpdateCoinLogRemark", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdateCoinLogRemark error:", err) + } + return err +} diff --git a/model/coinpoolsetting.go b/model/coinpoolsetting.go new file mode 100644 index 0000000..e48b169 --- /dev/null +++ b/model/coinpoolsetting.go @@ -0,0 +1,137 @@ +package model + +import ( + "fmt" + "mongo.games.com/game/protocol/server" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + CoinPoolSettingDBName = "user" + CoinPoolSettingCollName = "user_coinpoolsetting" + CoinPoolSettingHisDBName = "log" + CoinPoolSettingHisCollName = "log_coinpoolsetting" +) + +var CoinPoolSettingDatas = make(map[string]*CoinPoolSetting) + +type CoinPoolSetting struct { + Id bson.ObjectId `bson:"_id"` + Platform string // 平台id + GameFreeId int32 // 游戏id + GroupId int32 // 组id + ServerId int32 // 服务器id + InitValue int64 // 初始库存值 + LowerLimit int64 // 库存下限 + UpperLimit int64 // 库存上限 + QuDu int64 // 曲度值 + UpperOdds int32 // 上线初始概率 + UpperOddsMax int32 // 上线最大概率 + LowerOdds int32 // 下线初始概率 + LowerOddsMax int32 // 下线最大概率 + ProfitRate int32 // 收益抽取比例 + // 扩展参数 + ResetTime int64 // 重置时间 + UptTime time.Time // 更新时间 + Switch int32 // 开关 0开启 1关闭 +} + +func NewCoinPoolSetting(platform string, groupId, gamefreeid, srverId int32, db *server.DB_GameCoinPool) *CoinPoolSetting { + cl := &CoinPoolSetting{Id: bson.NewObjectId()} + cl.Platform = platform + cl.GroupId = groupId + cl.GameFreeId = gamefreeid + cl.ServerId = srverId + cl.InitValue = db.InitValue + cl.LowerLimit = db.LowerLimit + cl.UpperLimit = db.UpperLimit + cl.QuDu = db.QuDu + cl.UpperOdds = db.UpperOdds + cl.UpperOddsMax = db.UpperOddsMax + cl.LowerOdds = db.LowerOdds + cl.LowerOddsMax = db.LowerOddsMax + cl.ProfitRate = db.ProfitRate + cl.ResetTime = time.Now().Unix() + cl.UptTime = time.Now() + return cl +} + +func GetAllCoinPoolSettingData() error { + if rpcCli == nil { + return ErrRPClientNoConn + } + var datas []CoinPoolSetting + err := rpcCli.CallWithTimeout("CoinPoolSettingSvc.GetAllCoinPoolSettingData", struct{}{}, &datas, time.Second*30) + if err != nil { + logger.Logger.Warn("GetAllCoinPoolSettingData error:", err) + return err + } + for i := 0; i < len(datas); i++ { + dbSetting := &datas[i] + ManageCoinPoolSetting(dbSetting) + } + return nil +} + +type UpsertCoinPoolSettingArgs struct { + Cps *CoinPoolSetting + Old *CoinPoolSetting +} + +func UpsertCoinPoolSetting(cps, old *CoinPoolSetting) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + args := &UpsertCoinPoolSettingArgs{ + Cps: cps, + Old: old, + } + err = rpcCli.CallWithTimeout("CoinPoolSettingSvc.UpsertCoinPoolSetting", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpsertCoinPoolSetting error:", err) + return err + } + return +} + +func GetCoinPoolSetting(gameFreeId, serverId, groupId int32, platform string) *CoinPoolSetting { + var key string + if groupId != 0 { + key = fmt.Sprintf("%v+%v_%v", gameFreeId, groupId, serverId) + } else { + key = fmt.Sprintf("%v_%v_%v", gameFreeId, platform, serverId) + } + if data, exist := CoinPoolSettingDatas[key]; exist { + return data + } + return nil +} + +func ManageCoinPoolSetting(dbSetting *CoinPoolSetting) { + var key string + if dbSetting.GroupId != 0 { + key = fmt.Sprintf("%v+%v_%v", dbSetting.GameFreeId, dbSetting.GroupId, dbSetting.ServerId) + } else { + key = fmt.Sprintf("%v_%v_%v", dbSetting.GameFreeId, dbSetting.Platform, dbSetting.ServerId) + } + CoinPoolSettingDatas[key] = dbSetting +} + +// 删除水池历史调控记录 +func RemoveCoinPoolSettingHis(ts time.Time) (*mgo.ChangeInfo, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + var ret mgo.ChangeInfo + err := rpcCli.CallWithTimeout("CoinPoolSettingSvc.RemoveCoinPoolSettingHis", ts, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveCoinPoolSettingHis error:", err) + return &ret, err + } + return &ret, err +} diff --git a/model/coinwal.go b/model/coinwal.go new file mode 100644 index 0000000..c615844 --- /dev/null +++ b/model/coinwal.go @@ -0,0 +1,82 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + CoinWALDBName = "log" + CoinWALCollName = "log_coinwal" +) + +type CoinWAL struct { + Id bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Count int64 //帐变数量 + InGame int32 //0:其他 1~N:具体游戏id + SceneId int32 //房间id + CoinType int32 //金币类型 0:钱包 1:保险箱 2:俱乐部账户 + LogType int32 //log类型 + CurTs int64 //加个插入时间戳 + Ts int64 //时间戳 +} + +func NewCoinWAL(snid int32, count int64, logType, inGame int32, cointype int32, roomid int32, ts int64) *CoinWAL { + cl := &CoinWAL{Id: bson.NewObjectId()} + cl.SnId = snid + cl.Count = count + cl.InGame = inGame + cl.CoinType = cointype + cl.LogType = logType + cl.SceneId = roomid + cl.Ts = ts + cl.CurTs = time.Now().Unix() + return cl +} + +type CoinWALWithSnid_InGame_GreaterTsArgs struct { + Plt string + SnId int32 + RoomId int32 + Ts int64 +} + +func GetCoinWALBySnidAndInGameAndGreaterTs(plt string, id int32, roomid int32, ts int64) (ret []CoinWAL, err error) { + if rpcCli == nil { + logger.Logger.Error("model.GetCoinWALBySnidAndInGameAndGreaterTs rpcCli == nil") + return + } + args := &CoinWALWithSnid_InGame_GreaterTsArgs{ + Plt: plt, + SnId: id, + RoomId: roomid, + Ts: ts, + } + err = rpcCli.CallWithTimeout("CoinWALSvc.GetCoinWALBySnidAndInGameAndGreaterTs", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetCoinWALBySnidAndInGameAndGreaterTs error:", err) + } + return +} + +func RemoveCoinWALBySnidAndInGameAndGreaterTs(plt string, id int32, roomid int32, ts int64) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.RemoveCoinWALBySnidAndInGameAndGreaterTs rpcCli == nil") + return + } + args := &CoinWALWithSnid_InGame_GreaterTsArgs{ + Plt: plt, + SnId: id, + RoomId: roomid, + Ts: ts, + } + var ret bool + err = rpcCli.CallWithTimeout("CoinWALSvc.RemoveCoinWALBySnidAndInGameAndGreaterTs", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveCoinWALBySnidAndInGameAndGreaterTs error:", err) + } + return +} diff --git a/model/crashhash.go b/model/crashhash.go new file mode 100644 index 0000000..a3a0831 --- /dev/null +++ b/model/crashhash.go @@ -0,0 +1,66 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +type CrashHash struct { + CrashHashId bson.ObjectId `bson:"_id"` + Wheel int //第几轮 + Hash string //服务器Hash +} + +func NewCrashHash() *CrashHash { + crashhash := &CrashHash{CrashHashId: bson.NewObjectId()} + return crashhash +} + +type HashIsExistArg struct { + Wheel int //第几轮 +} +type HashRet struct { + Hash []*CrashHash + Tag int +} + +func InsertCrashHash(wheel int, hashstr string) (*CrashHash, int) { + if rpcCli == nil { + return nil, 4 + } + + hash := NewCrashHash() + if hash == nil { + return nil, 4 + } + + hash.Wheel = wheel + hash.Hash = hashstr + + ret := &HashRet{} + err := rpcCli.CallWithTimeout("CrashHashSvc.InsertCrashHash", hash, ret, time.Second*30) + if err != nil { + return nil, 0 + } + return nil, ret.Tag +} + +type HashIdArg struct { + Wheel int +} + +func GetCrashHash(wheel int) (data []*CrashHash) { + if rpcCli == nil { + return + } + args := &HashIdArg{ + Wheel: wheel, + } + ret := &HashRet{} + err := rpcCli.CallWithTimeout("CrashHashSvc.GetCrashHash", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("Get GetCrashHash data eror.", err) + } + return ret.Hash +} diff --git a/model/crashhashatom.go b/model/crashhashatom.go new file mode 100644 index 0000000..bcbf6f6 --- /dev/null +++ b/model/crashhashatom.go @@ -0,0 +1,81 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +type CrashHashAtom struct { + CrashHashAtomId bson.ObjectId `bson:"_id"` + Wheel int //第几轮 + Hash string //服务器Hash +} + +func NewCrashHashAtom() *CrashHashAtom { + crashhash := &CrashHashAtom{CrashHashAtomId: bson.NewObjectId()} + return crashhash +} + +type HashAtomIsExistArg struct { + Wheel int //第几轮 +} +type HashAtomRet struct { + Hash []*CrashHashAtom + Tag int +} + +func InsertCrashHashAtom(wheel int, hashstr string) (*CrashHashAtom, int) { + if rpcCli == nil { + return nil, 4 + } + + hash := NewCrashHashAtom() + if hash == nil { + return nil, 4 + } + + hash.Wheel = wheel + hash.Hash = hashstr + + ret := &HashAtomRet{} + err := rpcCli.CallWithTimeout("CrashHashAtomSvc.InsertCrashHashAtom", hash, ret, time.Second*30) + if err != nil { + return nil, 0 + } + return nil, ret.Tag +} + +type HashAtomIdArg struct { + Wheel int +} + +//func GetCrashHashAtom(wheel int) (*HashRet, error) { +// if rpcCli == nil { +// return nil, ErrRPClientNoConn +// } +// args := &HashIdArg{ +// Wheel: wheel, +// } +// var ret *HashRet +// err := rpcCli.CallWithTimeout("CrashHashAtomSvc.GetCrashHashAtom", args, ret, time.Second*30) +// if err != nil { +// return nil, err +// } +// return ret, err +//} + +func GetCrashHashAtom(wheel int) (data []*CrashHashAtom) { + if rpcCli == nil { + return + } + args := &HashAtomIdArg{ + Wheel: wheel, + } + ret := &HashAtomRet{} + err := rpcCli.CallWithTimeout("CrashHashAtomSvc.GetCrashHashAtom", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("Get GetCrashHashAtom data eror.", err) + } + return ret.Hash +} diff --git a/model/dataevent.go b/model/dataevent.go new file mode 100644 index 0000000..b779659 --- /dev/null +++ b/model/dataevent.go @@ -0,0 +1,566 @@ +package model + +import ( + "encoding/json" + "strconv" + "time" + + "github.com/jinzhu/now" +) + +const ( + TimeFormat_DataEvent = "2006-01-02 15:04:05" +) + +const ( + DataEventEx_PlayerLogin int32 = iota //玩家登录事件 + DataEventEx_PlayerBindPhone //玩家绑定手机事件 + DataEventEx_PlayerBindAlipay //玩家绑定alipay事件 + DataEventEx_PlayerGameRec //玩家游戏事件 + DataEventEx_PlayerGameRecPay //玩家充值游戏事件 + DataEventEx_Bankruptcy //破产统计事件 + DataEventEx_PlayerPay //玩家充值事件 + DataEventEx_SystemGive //系统赠送事件 +) + +// 写入队列的数据 +type RabbitMQDataRaw struct { + Source int32 + Data interface{} +} + +// 冰河世纪解析的数据 +type IceAgeGameNoteData struct { + Source int32 + Data *IceAgeType +} + +// 复仇者联盟解析的数据 +type AvengersGameNoteData struct { + Source int32 + Data *GameResultLog +} + +//// 复仇者联盟解析的数据 +//type AvengersGameNoteData struct { +// Source int32 +// Data *AvengersType +//} + +// 财神解析的数据 +type CaiShenGameNoteData struct { + Source int32 + Data *CaiShenType +} + +// 百战成神解析的数据 +type TamQuocGameNoteData struct { + Source int32 + Data *TamQuocType +} + +// 复活岛解析的数据 +type EasterIslandGameNoteData struct { + Source int32 + Data *EasterIslandType +} + +// 糖果解析的数据 +type CandyGameNoteData struct { + Source int32 + Data *CandyType +} + +// MiniPoker解析的数据 +type MiniPokerGameNoteData struct { + Source int32 + Data *MiniPokerType +} + +// CaoThap解析的数据 +type CaoThapGameNoteData struct { + Source int32 + Data *CaoThapType +} + +// 幸运骰子解析的数据 +type LuckyDiceGameNoteData struct { + Source int32 + Data *LuckyDiceType +} + +// 在线统计 +type PlayerOnlineEvent struct { + Online map[int]int + Time time.Time +} + +func MarshalPlayerOnlineEvent(source int32, online map[string]int) (data string, err error) { + m := map[int]int{} + for k, v := range online { + i, _ := strconv.Atoi(k) + m[i] = v + } + raw := &RabbitMQDataRaw{ + Source: source, + Data: &PlayerOnlineEvent{ + Online: m, + Time: time.Now(), + }, + } + d, err := json.Marshal(raw) + if err != nil { + return + } + return string(d), nil +} + +// 玩家登录 +type PlayerLoginEvent struct { + SnId int32 //用户ID + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + OS string //操作系统 + TelephonePromoter int32 //电销 + CreateTime int64 //创建时间 + CreateDayTime int64 //创建时间0点 + LoginTime int64 //登录时间 + UpgradeTime int64 //升级账号时间 + LastLoginIP string //登录ip + IsBindPhone int32 //是否绑定过手机号 + IsNew int32 //是否是新用户,1是 0否 + DeviceId string //设备id +} + +func CreatePlayerLoginEvent(snid int32, channel, promoter, platform, city, os, ip string, createTime, + upgradeTime time.Time, isBindPhone int32, telephonePromoter int32, deviceId string) *PlayerLoginEvent { + isNew := int32(0) + if createTime.Local().YearDay() == time.Now().Local().YearDay() && createTime.Local().Year() == time.Now().Local().Year() { + isNew = 1 + } + return &PlayerLoginEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + Platform: platform, + City: city, + OS: os, + TelephonePromoter: telephonePromoter, + CreateTime: createTime.Local().Unix(), + CreateDayTime: now.New(createTime.Local()).BeginningOfDay().Local().Unix(), + LoginTime: time.Now().Local().Unix(), + UpgradeTime: upgradeTime.Local().Unix(), + LastLoginIP: ip, + IsBindPhone: isBindPhone, + IsNew: isNew, + DeviceId: deviceId, + } +} + +func MarshalPlayerLoginEvent(source, snid int32, channel, promoter, platform, city, os, ip string, + createTime, upgradeTime time.Time, isBindPhone int32, telephonePromoter int32, deviceId string) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: source, + Data: CreatePlayerLoginEvent(snid, channel, promoter, platform, city, os, ip, createTime, + upgradeTime, isBindPhone, telephonePromoter, deviceId), + } + + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 用户升级账号 +type PlayerBindPhoneEvent struct { + SnId int32 //用户ID + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + OS string //操作系统 + Value int32 //占位用 + TelephonePromoter int32 //电销 + CreateTime int64 //创建日期 + BindTime int64 //绑定日期 +} + +func CreatePlayerBindPhoneEvent(snid int32, channel, promoter, platform, city, os string, + createTime time.Time, telephonePromoter int32) *PlayerBindPhoneEvent { + return &PlayerBindPhoneEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + TelephonePromoter: telephonePromoter, + Platform: platform, + City: city, + OS: os, + Value: 1, + CreateTime: createTime.Unix(), + BindTime: time.Now().Unix(), + } +} + +//func MarshalPlayerBindPhoneEvent(source, snid int32, channel, promoter, platform, city, os string, +// createTime time.Time, telephonePromoter int32) (data string, err error) { +// raw := &RabbitMQDataRaw{ +// Source: source, +// Data: CreatePlayerBindPhoneEvent(snid, channel, promoter, platform, city, os, createTime, telephonePromoter), +// } +// d, e := json.Marshal(raw) +// if e == nil { +// data = string(d[:]) +// } +// err = e +// return +//} + +// 用户升级账号 +type PlayerBindAlipayEvent struct { + SnId int32 //用户ID + Channel string //渠道 + Promoter string //推广 + TelephonePromoter int32 //电销 + Platform string //平台 + City string //城市 + OS string //操作系统 + Value int32 //占位用 + BindTime int64 //绑定日期 +} + +func MarshalPlayerBindAlipayEvent(source, snid int32, channel, promoter, platform, city, os string, telephonePromoter int32) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: source, + Data: &PlayerBindAlipayEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + Platform: platform, + TelephonePromoter: telephonePromoter, + City: city, + OS: os, + Value: 1, + BindTime: time.Now().Local().Unix(), + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 玩家游戏记录 +type PlayerGameRecEvent struct { + RecordId string //游戏记录ID + SnId int32 //用户ID + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + OS string //操作系统 + TelephonePromoter int32 //电销标记 + GameId int32 //游戏id + ModeId int32 //游戏模式 + Tax int64 //税收 + //Taxex int64 //税收2 + Amount int64 //金币变化(正值为赢;负值为输) + CreateTime int64 //创建时间 + CreateDayTime int64 //账号创建时间0点 + ValidBet int64 //有效下注数量 + ValidFlow int64 //有效流水数量 + Out int64 //产出 + In int64 //投入 + IsNew int32 //是否是新人 + GameFreeID int32 //游戏freeid + GamingTime int32 //游戏开始到玩家结算的时长 单位:秒 + FirstTime int64 //首次玩该场次游戏时间 + PlayTimes int64 //该场次游戏次数 + FirstGameTime int64 //首次玩游戏时间 + PlayGameTimes int64 //该游戏总次数 + LastLoginTime int64 //最后登录时间 + DeviceId string //设备id +} + +func CreatePlayerGameRecEvent(snid int32, tax, taxex, amount, validbet, validflow, in, out int64, gameid, gameFreeId, modeid int32, recordId, channel, promoter, + platform, city, os string, createDayTime time.Time, gamingTime int32, firstGameFreeTime, firstGameTime time.Time, + playGameFreeTimes, playerGameTimes int64, lastLoginTime time.Time, teleponePromoter int32, deviceId string) *PlayerGameRecEvent { + isNewbie := int32(0) + tCreateDay := now.New(createDayTime).BeginningOfDay() + if now.BeginningOfDay().Equal(tCreateDay) { + isNewbie = 1 + } + if gamingTime < 0 { + gamingTime = 0 + } + return &PlayerGameRecEvent{RecordId: recordId, + SnId: snid, + Channel: channel, + Promoter: promoter, + TelephonePromoter: teleponePromoter, + Platform: platform, + City: city, + OS: os, + GameId: gameid, + ModeId: modeid, + Tax: tax, + //Taxex: taxex, + Amount: amount, + ValidBet: validbet, + ValidFlow: validflow, + In: in, + Out: out, + CreateTime: time.Now().Local().Unix(), + CreateDayTime: tCreateDay.Local().Unix(), + IsNew: isNewbie, + GameFreeID: gameFreeId, + GamingTime: gamingTime, + FirstTime: firstGameFreeTime.Unix(), + FirstGameTime: firstGameTime.Unix(), + PlayTimes: playGameFreeTimes, + PlayGameTimes: playerGameTimes, + LastLoginTime: lastLoginTime.Unix(), + DeviceId: deviceId} +} + +func MarshalPlayerGameRecEvent(source, snid int32, tax, taxex, amount, validbet, validflow, in, out int64, gameid, gameFreeId, modeid int32, recordId, channel, promoter, + platform, city, os string, createDayTime time.Time, gamingTime int32, firstGameFreeTime time.Time, + playGameFreeTimes int64, lastLoginTime time.Time, telephonePromoter int32, firstGameTime time.Time, + playGameTimes int64, deviceId string) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: source, + Data: CreatePlayerGameRecEvent(snid, tax, taxex, amount, validbet, validflow, in, out, gameid, gameFreeId, modeid, recordId, channel, promoter, + platform, city, os, createDayTime, gamingTime, firstGameFreeTime, firstGameTime, playGameFreeTimes, playGameTimes, lastLoginTime, telephonePromoter, deviceId), + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 玩家游戏记录 +type PlayerGameRecPayEvent struct { + SnId int32 //用户ID + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + OS string //操作系统 + TelephonePromoter int32 //电销标签 + IsNew int32 //是否新人 + IsPay int32 //是否付费 + IsGame int32 //是否游戏 + CreateTime int64 //记录创建时间 + CreateDayTime int64 //记录创建时间0点 + Time int64 //当前时间 + RegisterDayTime int64 //玩家注册时间 +} + +func MarshalPlayerGameRecPayEvent(source, snid, isPay, isGame int32, channel, promoter, platform, city, os string, + createDayTime time.Time, orderCreateTime int64, telephonePromoter int32) (data string, err error) { + isNewbie := int32(0) + if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) { + isNewbie = 1 + } + tNow := time.Now() + raw := &RabbitMQDataRaw{ + Source: source, + Data: &PlayerGameRecPayEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + Platform: platform, + City: city, + OS: os, + IsNew: isNewbie, + TelephonePromoter: telephonePromoter, + IsPay: isPay, + IsGame: isGame, + RegisterDayTime: createDayTime.Local().Unix(), + CreateTime: time.Unix(orderCreateTime, 0).Local().Unix(), + CreateDayTime: now.New(time.Unix(orderCreateTime, 0)).BeginningOfDay().Local().Unix(), + Time: tNow.Local().Unix(), + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 破产统计 +type BankruptcyEvent struct { + SnId int32 //用户id + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + Value int32 //值 + TelephonePromoter int32 //电销标签 + IsNew int32 //是否新人 + Time int64 //操作时间 + GameId int32 //游戏id + GameMode int32 //游戏模式id + GameFreeId int32 //游戏场次id +} + +func MarshalBankruptcyEvent(source, snid, telephonePromoter int32, channel, promoter, platform, city string, createDayTime time.Time, gameId, gameMode, gameFreeId int32) (data string, err error) { + isNewbie := int32(0) + if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) { + isNewbie = 1 + } + raw := &RabbitMQDataRaw{ + Source: source, + Data: &BankruptcyEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + TelephonePromoter: telephonePromoter, + Platform: platform, + City: city, + IsNew: isNewbie, + Value: 0, + Time: time.Now().Local().Unix(), + GameId: gameId, + GameMode: gameMode, + GameFreeId: gameFreeId, + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 充值统计 +type PlayerPayEvent struct { + SnId int32 //用户id + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + TelephonePromoter int32 //电销标记 + Tag int32 //#充值类型 0 API直接充值 1在线充值 + BeforeCoin int32 //充值前钱包数量 + BeforeBank int32 //充值前保险柜数量 + Amount int32 //充值金额 + IsNew int32 //是否是新人 + Time int64 //操作时间 +} + +func MarshalPlayerPayEvent(source, snid, tag, beforeCoin, beforeBank, amount int32, channel, + promoter, platform, city string, createDayTime time.Time, orderCreateTime int64, + telephonePromoter int32) (data string, err error) { + isNewbie := int32(0) + if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) { + isNewbie = 1 + } + raw := &RabbitMQDataRaw{ + Source: source, + Data: &PlayerPayEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + Platform: platform, + City: city, + Tag: tag, + TelephonePromoter: telephonePromoter, + BeforeCoin: beforeCoin, + BeforeBank: beforeBank, + Amount: amount, + IsNew: isNewbie, + Time: time.Unix(orderCreateTime, 0).Local().Unix(), + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 系统赠送 +type SystemGiveEvent struct { + SnId int32 //用户id + Channel string //渠道 + Promoter string //推广 + Platform string //平台 + City string //城市 + TelephonePromoter int32 //电销 + Tag int32 //#充值类型 0 API直接充值 1在线充值 + Amount int32 //充值金额 + Time int64 //操作时间 +} + +func MarshalSystemGiveEvent(source, snid, tag, amount int32, channel, promoter, platform, city string, + telephonePromoter int32) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: source, + Data: &SystemGiveEvent{ + SnId: snid, + Channel: channel, + Promoter: promoter, + Platform: platform, + TelephonePromoter: telephonePromoter, + City: city, + Tag: tag, + Amount: amount, + Time: time.Now().Local().Unix(), + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 水池变化记录 +type GameCoinPoolEvent struct { + Platform string //平台 + GameId int32 //游戏id + GroupId int32 //组id + ChangeCoin int64 //变化金币 + CurCoin int64 //变化后金币 + UpCoin int64 //上限 + DownCoin int64 //下限 + Time int64 //操作时间 +} + +func MarshalGameCoinPoolEvent(source int32, platform string, gameid, groupId int32, changeCoin, + curCoin, upCoin, downCoin int64) (data string, err error) { + + raw := &RabbitMQDataRaw{ + Source: source, + Data: &GameCoinPoolEvent{ + Platform: platform, + GameId: gameid, + + GroupId: groupId, + ChangeCoin: changeCoin, + CurCoin: curCoin, + UpCoin: upCoin, + DownCoin: downCoin, + Time: time.Now().Local().Unix(), + }, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} diff --git a/model/dataeventex.go b/model/dataeventex.go new file mode 100644 index 0000000..92e39ad --- /dev/null +++ b/model/dataeventex.go @@ -0,0 +1,193 @@ +package model + +import ( + "encoding/json" +) + +const ( + DATASOURCE_NIL = iota + DATASOURCE_HUNDRED //1:百人场 百人牛牛、红黑、龙虎 奔驰宝马 森林舞会 红包 德州牛仔 鱼虾蟹 + DATASOURCE_FIGHT //2.对战场 经典牛牛、抢庄牛牛、推饼、赢三张、德州、十三水 斗地主、跑得快 二人麻将 十点半 + DATASOURCE_ROLL //3.拉霸 水浒装 水果机 足球英豪 女赌神 世界杯 绝地求生 皇家老虎机 财神到 冰河世纪 财神 百战成神 复仇者联盟 复活岛 + DATASOURCE_FISH //4.捕鱼 + DATASOURCE_MINI //5.小游戏 candy、caothap、minipoker、luckydice +) + +// 百人场数据类型 ;百人牛牛、红黑、龙虎 奔驰宝马 森林舞会 +func MarshalGameNoteByHUNDRED(hundRed interface{}) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: DATASOURCE_HUNDRED, + Data: hundRed, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +func UnMarshalGameNoteByHUNDRED(data string) (roll interface{}, err error) { + gnd := &RabbitMQDataRaw{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 对战场数据类型; 经典牛牛、抢庄牛牛、推饼、赢三张、德州、十三水 二人麻将、梭哈 +func MarshalGameNoteByFIGHT(fight interface{}) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: DATASOURCE_FIGHT, + Data: fight, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 拉霸 +func MarshalGameNoteByROLL(roll interface{}) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: DATASOURCE_ROLL, + Data: roll, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 小游戏 +func MarshalGameNoteByMini(mini interface{}) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: DATASOURCE_MINI, + Data: mini, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} + +// 冰河世纪游戏记录 +func UnMarshalIceAgeGameNote(data string) (roll interface{}, err error) { + gnd := &IceAgeGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 复仇者联盟游戏记录 +func UnMarshalAvengersGameNote(data string) (roll interface{}, err error) { + gnd := &AvengersGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +//// 复仇者联盟游戏记录 +//func UnMarshalAvengersGameNote(data string) (roll interface{}, err error) { +// gnd := &AvengersGameNoteData{} +// if err := json.Unmarshal([]byte(data), gnd); err != nil { +// return nil, err +// } +// roll = gnd.Data +// return +//} + +// 财神游戏记录 +func UnMarshalCaiShenGameNote(data string) (roll interface{}, err error) { + gnd := &CaiShenGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 财神游戏记录 +func UnMarshalTamQuocGameNote(data string) (roll interface{}, err error) { + gnd := &TamQuocGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 复活岛游戏记录 +func UnMarshalEasterIslandGameNote(data string) (roll interface{}, err error) { + gnd := &EasterIslandGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 糖果游戏记录 +func UnMarshalCandyGameNote(data string) (roll interface{}, err error) { + gnd := &CandyGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// MiniPoker游戏记录 +func UnMarshalMiniPokerGameNote(data string) (roll interface{}, err error) { + gnd := &MiniPokerGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// CaoThap游戏记录 +func UnMarshalCaoThapGameNote(data string) (roll interface{}, err error) { + gnd := &CaoThapGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 幸运骰子游戏记录 +func UnMarshalLuckyDiceGameNote(data string) (roll interface{}, err error) { + gnd := &LuckyDiceGameNoteData{} + if err := json.Unmarshal([]byte(data), gnd); err != nil { + return nil, err + } + roll = gnd.Data + return +} + +// 捕鱼 +func MarshalGameNoteByFISH(fish interface{}) (data string, err error) { + raw := &RabbitMQDataRaw{ + Source: DATASOURCE_FISH, + Data: fish, + } + d, e := json.Marshal(raw) + if e == nil { + data = string(d[:]) + } + err = e + return +} diff --git a/model/dberror.go b/model/dberror.go new file mode 100644 index 0000000..1b9c555 --- /dev/null +++ b/model/dberror.go @@ -0,0 +1,30 @@ +package model + +import ( + "errors" + "fmt" +) + +var ( + NOT_OPEN = "not open" + ErrRPClientNoConn = errors.New("RPClient no connect") +) + +func NewDBError(dbName, collectionName, errMsg string) error { + err := &DBError{ + DBName: dbName, + CollectionName: collectionName, + Msg: errMsg, + } + return err +} + +type DBError struct { + DBName string + CollectionName string + Msg string +} + +func (dbe *DBError) Error() string { + return fmt.Sprintf("[%v].[%v]: %v", dbe.DBName, dbe.CollectionName, dbe.Msg) +} diff --git a/model/dbshoplog.go b/model/dbshoplog.go new file mode 100644 index 0000000..d62cbc0 --- /dev/null +++ b/model/dbshoplog.go @@ -0,0 +1,160 @@ +package model + +import ( + "errors" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + DbShopDBName = "log" + DbShopCollName = "log_dbshop" +) + +type ItemInfo struct { + ItemId int32 + ItemNum int64 +} +type DbShop struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台 + PageId int32 + ShopId int32 //商品id + ShopName string //商品名称 + SnId int32 //兑换玩家 + Count int32 //兑换数量 + Amount []int32 //0.金币 1.钻石 2.经验 获得 + ItemInfo []ItemInfo //道具 获得 + Consume int32 //消费类型 1.金币 2.钻石 3.现金 + ConsumeNum int32 //消费金额 + ConsumeType string //消费类型符号 + ConsumeTypeNum int32 //消费类型符号对应金额 + GainWay int32 + State int32 //状态 0.默认 1.成功 2.失败 3.未发货准备发货 + UserName string //姓名 + OtherParams []int32 //额外参数 + UserTel string //手机号 + Remark string //备注信息 + Operator string //操作人 + CreateTs time.Time //订单生成时间 + OpTs time.Time //订单最后操作时间 + Ts int64 +} +type DbShopLogArgs struct { + Log *DbShop +} + +func NewDbShop(platform string, pageId int32, amount []int32, consumeType string, consumeTypeNum, consume, consumeNum, gainWay int32, + itemInfo []ItemInfo, shopId int32, shopName string, snId, state int32, remark string, other []int32) *DbShop { + t := time.Now() + return &DbShop{ + LogId: bson.NewObjectId(), + Platform: platform, + PageId: pageId, + Amount: amount, + Consume: consume, + ConsumeNum: consumeNum, + ConsumeType: consumeType, + ConsumeTypeNum: consumeTypeNum, + GainWay: gainWay, + ItemInfo: itemInfo, + ShopId: shopId, + ShopName: shopName, + SnId: snId, + Count: 1, + State: state, + UserName: "", + UserTel: "", + Remark: remark, + Operator: "sys", + CreateTs: t, + OpTs: t, + Ts: t.Unix(), + OtherParams: other, + } +} +func InsertDbShopLog(log *DbShop) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertDbShopLog rpcCli == nil") + return + } + var ret bool + args := &DbShopLogArgs{ + Log: log, + } + err = rpcCli.CallWithTimeout("DbShopLogSvc.InsertDbShopLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertDbShopLog error:", err) + return + } + return +} +func GetDbShopLog(platform string, loginId string) *DbShop { + if rpcCli == nil { + logger.Logger.Error("model.GetDbShopLog rpcCli == nil") + return nil + } + var ret DbShop + args := &DbShopLogArgs{ + Log: &DbShop{LogId: bson.ObjectIdHex(loginId), Platform: platform}, + } + err := rpcCli.CallWithTimeout("DbShopLogSvc.GetDbShopLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetDbShopLog error:", err) + return nil + } + return &ret +} +func UpdateDbShopState(platform string, loginId string, state int32) error { + if rpcCli == nil { + logger.Logger.Error("model.UpdateDbShopState rpcCli == nil") + return errors.New(" rpcCli == nil") + } + var ret bool + args := &DbShopLogArgs{ + Log: &DbShop{LogId: bson.ObjectIdHex(loginId), Platform: platform, State: state}, + } + err := rpcCli.CallWithTimeout("DbShopLogSvc.UpdateDbShopState", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdateDbShopState error:", err) + return err + } + if !ret { + return errors.New("update fail " + loginId) + } + return nil +} +func GetDbShopLogsByPage(platform string, snid, pageId int32) []*DbShop { + if rpcCli == nil { + logger.Logger.Error("model.GetDbShopLogsByPage rpcCli == nil") + return nil + } + var ret []*DbShop + args := &DbShopLogArgs{ + Log: &DbShop{PageId: pageId, Platform: platform, SnId: snid}, + } + err := rpcCli.CallWithTimeout("DbShopLogSvc.GetDbShopLogsByPage", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetDbShopLogsByPage error:", err) + return nil + } + return ret +} +func GetDbShopLogsByState(platform string, snid int32) []*DbShop { + if rpcCli == nil { + logger.Logger.Error("model.GetDbShopLogsByState rpcCli == nil") + return nil + } + var ret []*DbShop + args := &DbShopLogArgs{ + Log: &DbShop{State: 3, Platform: platform, SnId: snid}, + } + err := rpcCli.CallWithTimeout("DbShopLogSvc.GetDbShopLogsByState", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("GetDbShopLogsByState error:", err) + return nil + } + return ret +} diff --git a/model/fishingparam.go b/model/fishingparam.go new file mode 100644 index 0000000..3c842e7 --- /dev/null +++ b/model/fishingparam.go @@ -0,0 +1,373 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +type FishingParam struct { + //2.1 历史流水调整系数相关配置 + Wa float64 // = 100000 //历史流水的调整基准,高于此值后历史流水系数不再变化 + Ramax float64 // = 1.3 //历史流水调整系数最大值 + Ramin float64 // = 0.97 //历史流水调整系数最小值 + + //2.2 历史输赢调整系数相关配置 + Hmax_chu float64 // = 20000 + Hmin_chu float64 // = -15000 + Beta_chu float64 // = 0.0001 + Alpha_chu float64 // = 0.00015 + + RwMax float64 // = 1.03 + + Hmax_zho float64 // = 100000 + Hmin_zho float64 // = -100000 + Beta_zho float64 // = 0.0001 + Alpha_zho float64 // = 0.0001 + + Hmax_gao float64 // = 200000 + Hmin_gao float64 // = -300000 + Beta_gao float64 // = 0.0002 + Alpha_gao float64 // = 0.00005 + + R1 float64 // = 1 + Theta float64 // = 0.5 + + //2.3 充值行为调整系数相关配置 + P1 float64 // = 10000 //充值流水分段1 + Rp1 float64 // = 1.01 //在充值后水流在[0,P1]之间的调整系数 + P2 float64 // = 30000 //充值流水分段2 + Rp2 float64 // = 1.001 //在充值后水流在[P1,P2]之间的调整系数 + Rp3 float64 // = 1 //在充值后水流在[P2,∞]之间的调整系数 + + //2.4 每日优惠系数相关配置 + D1 float64 // = 2000 //日流水分段1 + Rd1 float64 // = 1.01 //日水流在[0,D1)之间的调整系数 + D2 float64 // = 5000 //日流水分段2 + Rd2 float64 // = 1.001 //日水流在[D1,D2)之间的调整系数 + Rd3 float64 // = 1 //日水流在[D2,∞)之间的调整系数 + + //2.5 捕鱼平台调整系数相关配置 + Tmin float64 // = 0 //平台收益底线 + R2 float64 // = 1 //平台收益调整系数基准值 + Rtmin float64 // = 0.5 //平台收益调整系数最小值 + Rtmin2 float64 // = 0 // 寻红夺宝特有字段 + Delta_chu float64 // = 0.00001 //初级场下压系数 + Delta_zho float64 // = 0.00001 //中级场下压系数 + Delta_gao float64 // = 0.00001 //高级场下压系数 + ReturnCoefficient_chu float64 // 初级场上限返还系数 + ReturnCoefficient_zho float64 // 中级场上限返还系数 + ReturnCoefficient_gao float64 // 高级场上限返还系数 + UpperLimitReturnCoefficient [][]float64 // 水池平台收益返还 + UpperFishMultiple [][]ControlParam //鱼倍数百分比上限 (按照权重随机) + LowerFishMultiple [][]ControlParam //鱼倍数百分比下限 (按照权重随机) + UpperFishMultipleFixedValue [][]ControlParam //鱼倍数百分比上限 (指定固定值) + LowerFishMultipleFixedValue [][]ControlParam //鱼倍数百分比下限 (指定固定值) + PersonalPoolFloatingCoefficient []int32 // 个人水池场此浮动系数 + PersonalPoolInitialValue []int32 // 个人水池场此初始值 + JackpotRate float64 // 天天捕鱼jack奖池抽成 + JackpotOne int32 // 天天捕鱼jack奖池一等奖概率 + JackpotTwo int32 // 天天捕鱼jack奖池二等奖概率 + JackpotThreeRate int32 // 天天捕鱼jack奖池三等奖概率 + JackpotFourRate int32 // 天天捕鱼jack奖池四等奖概率 + JackpotD1 int32 // 天天捕鱼jack奖池爆奖系数 + JackpotD2 int32 // 天天捕鱼jack奖池爆奖系数 + JackpotD3 int32 // 天天捕鱼jack奖池爆奖系数 + JackpotD4 int32 // 天天捕鱼jack奖池爆奖系数 + JackpotBate1 int32 // 奖池分母 + JackpotBate2 int32 // 奖池分母 + JackpotBate3 int32 // 奖池分母 + JackpotCoin1 int64 // 奖池奖励金币 + JackpotCoin2 int64 // 奖池奖励金币 + JackpotCoin3 int64 // 奖池奖励金币 + JackpotInitCoin int64 //奖池初始金额 + PranaRatio float64 // 能量炮抽成比例 + PranaECoin int32 // 初级场蓄能上限 + PranaMCoin int32 // 中级场蓄能上限 + PranaHCoin int32 // 高级场蓄能上限 + FishDealY1 int32 //致命一击系数Y + FishDealY2 int32 //致命一击系数Y + FishDealY3 int32 //致命一击系数Y + FishDealY4 int32 //致命一击系数Y + //CommonPoolRatio float64 // 公共池比例 + //PersonPoolRatio float64 // 私人池比例 + SLFishRate float64 //特殊鱼L概率 + SHFishRate float64 //特殊鱼H概率 + SignRate int + + LimiteWinCoin1 int64 //天天捕鱼初级场单条鱼赢取上限 + LimiteWinCoin2 int64 //天天捕鱼中级场单条鱼赢取上限 + LimiteWinCoin3 int64 //天天捕鱼高级场单条鱼赢取上限 + FreeUserLimit int64 //免费用户金币赢取上限 +} + +/* +控制百分比 +*/ +type ControlParam struct { + Percentage int // 炮数占比 + Weight int // 对应权重 +} + +var FishingParamPath = "../data/fishingparam.json" +var FishingParamData = &FishingParam{} + +func InitFishingParam() { + buf, err := os.ReadFile(FishingParamPath) + if err != nil { + logger.Logger.Warn("InitFishingParam os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, FishingParamData) + if err != nil { + logger.Logger.Warn("InitFishingParam json.Unmarshal error ->", err) + } + if FishingParamData.Wa == 0 { + FishingParamData.Wa = 100000 //历史流水的调整基准,高于此值后历史流水系数不再变化 + } + if FishingParamData.Ramax == 0 { + FishingParamData.Ramax = 1.3 //历史流水调整系数最大值 + } + if FishingParamData.Ramin == 0 { + FishingParamData.Ramin = 0.97 //历史流水调整系数最小值 + } + //2.2 历史输赢调整系数相关配置 + if FishingParamData.Hmax_chu == 0 { + FishingParamData.Hmax_chu = 20000 + } + if FishingParamData.Hmin_chu == 0 { + FishingParamData.Hmin_chu = -15000 + } + if FishingParamData.Beta_chu == 0 { + FishingParamData.Beta_chu = 0.0001 + } + if FishingParamData.Alpha_chu == 0 { + FishingParamData.Alpha_chu = 0.00015 + } + + if FishingParamData.RwMax == 0 { + FishingParamData.RwMax = 1.03 + } + + if FishingParamData.Hmax_zho == 0 { + FishingParamData.Hmax_zho = 100000 + } + if FishingParamData.Hmin_zho == 0 { + FishingParamData.Hmin_zho = -100000 + } + if FishingParamData.Beta_zho == 0 { + FishingParamData.Beta_zho = 0.0001 + } + if FishingParamData.Alpha_zho == 0 { + FishingParamData.Alpha_zho = 0.0001 + } + + if FishingParamData.Hmax_gao == 0 { + FishingParamData.Hmax_gao = 200000 + } + if FishingParamData.Hmin_gao == 0 { + FishingParamData.Hmin_gao = -300000 + } + if FishingParamData.Beta_gao == 0 { + FishingParamData.Beta_gao = 0.0002 + } + if FishingParamData.Alpha_gao == 0 { + FishingParamData.Alpha_gao = 0.00005 + } + if FishingParamData.R1 == 0 { + FishingParamData.R1 = 1 + } + if FishingParamData.Theta == 0 { + FishingParamData.Theta = 0.5 + } + //2.3 充值行为调整系数相关配置 + if FishingParamData.P1 == 0 { + FishingParamData.P1 = 10000 //充值流水分段1 + } + if FishingParamData.Rp1 == 0 { + FishingParamData.Rp1 = 1.01 //在充值后水流在[0,P1]之间的调整系数 + } + if FishingParamData.P2 == 0 { + FishingParamData.P2 = 30000 //充值流水分段2 + } + if FishingParamData.Rp2 == 0 { + FishingParamData.Rp2 = 1.001 //在充值后水流在[P1,P2]之间的调整系数 + } + if FishingParamData.Rp3 == 0 { + FishingParamData.Rp3 = 1 //在充值后水流在[P2,∞]之间的调整系数 + } + //2.4 每日优惠系数相关配置 + if FishingParamData.D1 == 0 { + FishingParamData.D1 = 2000 //日流水分段1 + } + if FishingParamData.Rd1 == 0 { + FishingParamData.Rd1 = 1.01 //日水流在[0,D1)之间的调整系数 + } + if FishingParamData.D2 == 0 { + FishingParamData.D2 = 5000 //日流水分段2 + } + if FishingParamData.Rd2 == 0 { + FishingParamData.Rd2 = 1.001 //日水流在[D1,D2)之间的调整系数 + } + if FishingParamData.Rd3 == 0 { + FishingParamData.Rd3 = 1 //日水流在[D2,∞)之间的调整系数 + } + //2.5 捕鱼平台调整系数相关配置 + if FishingParamData.R2 == 0 { + FishingParamData.R2 = 1 //平台收益调整系数基准值 + } + if FishingParamData.Rtmin == 0 { + FishingParamData.Rtmin = 0.5 //平台收益调整系数最小值 + } + if FishingParamData.Rtmin2 == 0 { + FishingParamData.Rtmin2 = 0 //平台收益调整系数最小值 + } + if FishingParamData.Delta_chu == 0 { + FishingParamData.Delta_chu = 0.00001 //初级场下压系数 + } + if FishingParamData.Delta_zho == 0 { + FishingParamData.Delta_zho = 0.00001 //中级场下压系数 + } + if FishingParamData.Delta_gao == 0 { + FishingParamData.Delta_gao = 0.00001 //高级场下压系数 + } + if FishingParamData.ReturnCoefficient_chu == 0 { + FishingParamData.ReturnCoefficient_chu = 1 // 初级场返还系数 + } + if FishingParamData.ReturnCoefficient_zho == 0 { + FishingParamData.ReturnCoefficient_zho = 1 // 中级场返还系数 + } + if FishingParamData.ReturnCoefficient_gao == 0 { + FishingParamData.ReturnCoefficient_gao = 1 // 高级场返还系数 + } + if len(FishingParamData.UpperLimitReturnCoefficient) == 0 { + FishingParamData.UpperLimitReturnCoefficient = [][]float64{ + {1.5, 2, 1.5}, + {1.2, 1.5, 1.2}, + {1, 1.2, 1.05}, + } + } + + if len(FishingParamData.UpperFishMultiple) == 0 { + FishingParamData.UpperFishMultiple = [][]ControlParam{ + {{140, 8}, {150, 8}, {160, 7}, {170, 7}, {180, 6}, {190, 6}, {200, 4}}, + //{{140, 10}, {150, 8}, {160, 7}, {170, 6}, {180, 6}, {190, 6}, {200, 4}}, + {{140, 8}, {150, 8}, {160, 7}, {170, 7}, {180, 6}, {200, 2}}, + } + + } + + if len(FishingParamData.LowerFishMultiple) == 0 { + FishingParamData.LowerFishMultiple = [][]ControlParam{ + {{10, 6}, {20, 6}, {30, 7}, {40, 7}, + {50, 8}, {60, 8}, {70, 9}, {80, 9}, + {90, 10}, {100, 10}, {110, 10}, {120, 9}, {130, 9}}, + //{{10, 6}, {20, 6}, {30, 6}, {40, 7}, + // {50, 8}, {60, 10}, {70, 12}, {80, 15}, + // {90, 20}, {100, 25}, {110, 20}, {120, 15}, {130, 12}}, + {{20, 6}, {30, 7}, {40, 7}, {50, 8}, {60, 8}, + {70, 9}, {80, 9}, {90, 10}, {100, 10}, {110, 10}, {120, 9}, {130, 9}}, + } + } + if len(FishingParamData.PersonalPoolFloatingCoefficient) == 0 { + FishingParamData.PersonalPoolFloatingCoefficient = []int32{1, 1, 1} + } + + if len(FishingParamData.PersonalPoolInitialValue) == 0 { + FishingParamData.PersonalPoolInitialValue = []int32{100, 1000, 10000} + } + + if FishingParamData.JackpotRate == 0 { + FishingParamData.JackpotRate = 0.05 + } + if FishingParamData.JackpotOne == 0 { + FishingParamData.JackpotOne = 3 + } + if FishingParamData.JackpotTwo == 0 { + FishingParamData.JackpotTwo = 5 + } + if FishingParamData.JackpotThreeRate == 0 { + FishingParamData.JackpotThreeRate = 12 + } + if FishingParamData.JackpotFourRate == 0 { + FishingParamData.JackpotFourRate = 80 + } + if FishingParamData.JackpotD1 == 0 { + FishingParamData.JackpotD1 = 5 + } + if FishingParamData.JackpotD2 == 0 { + FishingParamData.JackpotD2 = 3 + } + if FishingParamData.JackpotD3 == 0 { + FishingParamData.JackpotD1 = 2 + } + if FishingParamData.JackpotD4 == 0 { + FishingParamData.JackpotD4 = 1 + } + if FishingParamData.JackpotBate1 == 0 { + FishingParamData.JackpotBate1 = 500000 + } + if FishingParamData.JackpotBate2 == 0 { + FishingParamData.JackpotBate2 = 5000000 + } + if FishingParamData.JackpotBate3 == 0 { + FishingParamData.JackpotBate3 = 50000000 + } + if FishingParamData.JackpotCoin1 == 0 { + FishingParamData.JackpotCoin1 = 5000 + } + if FishingParamData.JackpotCoin2 == 0 { + FishingParamData.JackpotCoin2 = 50000 + } + if FishingParamData.JackpotCoin3 == 0 { + FishingParamData.JackpotCoin3 = 500000 + } + if FishingParamData.PranaRatio == 0 { + FishingParamData.PranaRatio = 0.01 + } + if FishingParamData.PranaECoin == 0 { + FishingParamData.PranaECoin = 2000 + } + if FishingParamData.PranaMCoin == 0 { + FishingParamData.PranaMCoin = 20000 + } + if FishingParamData.PranaHCoin == 0 { + FishingParamData.PranaHCoin = 200000 + } + if FishingParamData.FishDealY1 == 0 { + FishingParamData.FishDealY1 = 15 + } + if FishingParamData.FishDealY2 == 0 { + FishingParamData.FishDealY2 = 13 + } + if FishingParamData.FishDealY3 == 0 { + FishingParamData.FishDealY3 = 10 + } + + if FishingParamData.SLFishRate == 0 { + FishingParamData.SLFishRate = 0.60 + } + + if FishingParamData.SHFishRate == 0 { + FishingParamData.SHFishRate = 0.80 + } + if FishingParamData.SignRate == 0 { + FishingParamData.SignRate = 70 + } + if FishingParamData.JackpotInitCoin == 0 { + FishingParamData.JackpotInitCoin = 234982125 + //FishingParamData.JackpotInitCoin = 0 + } + if FishingParamData.LimiteWinCoin1 == 0 { + FishingParamData.LimiteWinCoin1 = 20000 + } + if FishingParamData.LimiteWinCoin2 == 0 { + FishingParamData.LimiteWinCoin2 = 60000 + } + if FishingParamData.LimiteWinCoin3 == 0 { + FishingParamData.LimiteWinCoin3 = 300000 + } + if FishingParamData.FreeUserLimit == 0 { + FishingParamData.FreeUserLimit = 2000 + } +} diff --git a/model/fishjacklog.go b/model/fishjacklog.go new file mode 100644 index 0000000..4b69c58 --- /dev/null +++ b/model/fishjacklog.go @@ -0,0 +1,87 @@ +package model + +import ( + "time" + + "sync/atomic" + + "github.com/globalsign/mgo/bson" +) + +var ( + FishJackpotLogDBName = "log" + FishJackpotLogCollName = "log_fishjackpotlog" +) + +const FishJackpotLogMaxLimitPerQuery = 10 + +type FishJackpotLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Ts int64 //时间戳 + Time time.Time //时间戳 + InGame int32 //0:其他 1~N:具体游戏id + JackType int32 //log类型 + Coin int64 // 中奖金额 + Name string // 名字 + RoomId int32 //房间id + SeqNo int64 //流水号(隶属于进程) +} + +func NewFishJackpotLog() *FishJackpotLog { + log := &FishJackpotLog{LogId: bson.NewObjectId()} + return log +} + +func NewFishJackpotLogEx(snid int32, coin int64, roomid, jackType, inGame int32, platform, channel, name string) *FishJackpotLog { + cl := NewFishJackpotLog() + cl.SnId = snid + cl.Platform = platform + cl.Channel = channel + tNow := time.Now() + cl.Ts = tNow.Unix() + cl.Time = tNow + cl.Coin = coin + cl.InGame = inGame + cl.JackType = jackType + cl.Name = name + cl.RoomId = roomid + cl.SeqNo = atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1) + return cl +} + +type GetLogArgs struct { + Platform string +} +type GetLogRet struct { + Logs []FishJackpotLog +} + +func GetFishJackpotLogByPlatform(platform string) (log []FishJackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetLogArgs{Platform: platform} + ret := &GetLogRet{} + err = rpcCli.CallWithTimeout("FishJackLogSvc.GetFishJackpotLogByPlatform", args, ret, time.Second*30) + return ret.Logs, err +} + +type InsertLogArgs struct { + Log *FishJackpotLog +} +type InsertLogRet struct { + Tag int +} + +func InsertSignleFishJackpotLog(log *FishJackpotLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &InsertLogArgs{Log: log} + ret := &InsertLogRet{} + err = rpcCli.CallWithTimeout("FishJackLogSvc.InsertSignleFishJackpotLog", args, ret, time.Second*30) + return err +} diff --git a/model/fishpath.go b/model/fishpath.go new file mode 100644 index 0000000..6f1225d --- /dev/null +++ b/model/fishpath.go @@ -0,0 +1,32 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +type FishPath struct { + Id int32 + Stay int32 + Length int32 +} +type fishPathFile struct { + Count int32 + Pools []FishPath +} + +var FishPathPath = "../data/fishpath/path.json" + +func GetFishPath() []FishPath { + buf, err := os.ReadFile(FishPathPath) + if err != nil { + logger.Logger.Warn("GetFishPath os.ReadFile error ->", err) + } + var fileData = &fishPathFile{} + err = json.Unmarshal(buf, fileData) + if err != nil { + logger.Logger.Warn("GetFishPath json.Unmarshal error ->", err) + } + return fileData.Pools +} diff --git a/model/friend.go b/model/friend.go new file mode 100644 index 0000000..eb112dc --- /dev/null +++ b/model/friend.go @@ -0,0 +1,130 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +type Friend struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + BindFriend []*BindFriend + UpdateTime int64 + Name string + Head int32 + HeadUrl string + Sex int32 + Coin int64 + Diamond int64 + VCard int64 + Roles map[int32]int32 //人物 + Pets map[int32]int32 //宠物 + Shield []int32 + Age int32 + Signature string + GameID []int32 + LogoutTime int64 //登出时间 + Dirty bool `bson:"-"` +} + +type BindFriend struct { + SnId int32 + CreateTime int64 //建立时间 + //缓存数据 + Platform string `bson:"-"` + Name string `bson:"-"` + Head int32 `bson:"-"` + HeadUrl string `bson:"-"` + Sex int32 `bson:"-"` + LogoutTime int64 `bson:"-"` + RoleId int32 `bson:"-"` +} + +type FriendRet struct { + Fri *Friend +} + +type FriendByKey struct { + Platform string + SnId int32 +} + +type FriendsRet struct { + Fris []*Friend +} + +type FriendsByKey struct { + Platform string + SnIds []int32 +} + +func NewFriend(platform string, snid int32) *Friend { + f := &Friend{Id: bson.NewObjectId()} + f.Platform = platform + f.SnId = snid + f.Roles = make(map[int32]int32) + f.Pets = make(map[int32]int32) + f.Shield = []int32{} + f.UpdateTime = time.Now().Unix() + f.BindFriend = []*BindFriend{} + f.GameID = []int32{} + f.Dirty = true + return f +} + +func UpsertFriend(friend *Friend) *Friend { + if rpcCli == nil { + logger.Logger.Error("model.UpsertFriend rpcCli == nil") + return nil + } + + ret := &FriendRet{} + err := rpcCli.CallWithTimeout("FriendSvc.UpsertFriend", friend, ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertFriend error:", err) + return nil + } + return ret.Fri +} + +func QueryFriendBySnid(platform string, snid int32) (friend *Friend, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryFriendBySnid rpcCli == nil") + return + } + args := &FriendByKey{ + Platform: platform, + SnId: snid, + } + var ret *FriendRet + err = rpcCli.CallWithTimeout("FriendSvc.QueryFriendByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryFriendBySnid error:", err) + } + if ret != nil { + friend = ret.Fri + } + return +} + +func QueryFriendsBySnids(platform string, snids []int32) (fris []*Friend, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryFriendsBySnids rpcCli == nil") + return + } + args := &FriendsByKey{ + Platform: platform, + SnIds: snids, + } + ret := &FriendsRet{} + err = rpcCli.CallWithTimeout("FriendSvc.QueryFriendsByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryFriendsBySnids error:", err) + } + if ret != nil { + fris = ret.Fris + } + return +} diff --git a/model/friendapply.go b/model/friendapply.go new file mode 100644 index 0000000..b8d2e0d --- /dev/null +++ b/model/friendapply.go @@ -0,0 +1,98 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +type FriendApply struct { + Id bson.ObjectId `bson:"_id"` + Snid int32 + ApplySnids []*FriendApplySnid +} + +type FriendApplySnid struct { + SnId int32 + Name string + Head int32 + CreateTs int64 + HeadUrl string + RoleId int32 +} + +type FriendApplyRet struct { + FA *FriendApply +} + +type FriendApplyByKey struct { + Platform string + SnId int32 + FA *FriendApply +} + +func NewFriendApply(snid int32) *FriendApply { + f := &FriendApply{Id: bson.NewObjectId()} + f.Snid = snid + f.ApplySnids = []*FriendApplySnid{} + return f +} + +func UpsertFriendApply(platform string, snid int32, fa *FriendApply) *FriendApply { + if rpcCli == nil { + logger.Logger.Error("model.UpsertFriendApply rpcCli == nil") + return nil + } + if fa.ApplySnids == nil || len(fa.ApplySnids) == 0 { + DelFriendApply(platform, snid) + return nil + } + args := &FriendApplyByKey{ + Platform: platform, + SnId: snid, + FA: fa, + } + var ret *FriendApplyRet + err := rpcCli.CallWithTimeout("FriendApplySvc.UpsertFriendApply", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpsertFriendApply error:", err) + return nil + } + return ret.FA +} + +func QueryFriendApplyBySnid(platform string, snid int32) (fa *FriendApply, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryFriendApplyBySnid rpcCli == nil") + return + } + args := &FriendApplyByKey{ + Platform: platform, + SnId: snid, + } + var ret *FriendApplyRet + err = rpcCli.CallWithTimeout("FriendApplySvc.QueryFriendApplyByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("QueryFriendApplyBySnid error:", err) + } + if ret != nil { + fa = ret.FA + } + return +} + +func DelFriendApply(platform string, snid int32) { + if rpcCli == nil { + logger.Logger.Error("model.UpsertFriendApply rpcCli == nil") + return + } + args := &FriendApplyByKey{ + Platform: platform, + SnId: snid, + } + err := rpcCli.CallWithTimeout("FriendApplySvc.DelFriendApply", args, nil, time.Second*30) + if err != nil { + logger.Logger.Warn("DelFriendApply error:", err) + } + return +} diff --git a/model/friendrecord.go b/model/friendrecord.go new file mode 100644 index 0000000..e412d62 --- /dev/null +++ b/model/friendrecord.go @@ -0,0 +1,71 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +var ( + FriendRecordLogDBName = "log" + FriendRecordLogCollName = "log_friendrecordlog" +) + +// 好友聊天 战绩 +type FriendRecord struct { + LogId bson.ObjectId `bson:"_id"` //记录ID + Platform string + SnId int32 //玩家id + IsWin int32 //1:赢 0:平 -1:输 + GameId int32 //游戏场次 + BaseScore int32 //底分 + BillCoin int64 //输赢分(税后) + MatchType int32 + Ts int64 +} + +func NewFriendRecordLog() *FriendRecord { + return &FriendRecord{LogId: bson.NewObjectId()} +} +func NewFriendRecordLogEx(platform string, snid, isWin, gameId, baseScore int32, billCoin int64, matchType int32) *FriendRecord { + fri := NewFriendRecordLog() + fri.Platform = platform + fri.SnId = snid + fri.IsWin = isWin + fri.GameId = gameId + fri.BaseScore = baseScore + fri.BillCoin = billCoin + fri.MatchType = matchType + fri.Ts = time.Now().Unix() + return fri +} + +type FriendRecordSnidArg struct { + Platform string + SnId, GameId int32 + Size int +} + +type FriendRecordSnidRet struct { + FR []*FriendRecord +} + +func GetFriendRecordLogBySnid(platform string, snid, gameid int32, size int) []*FriendRecord { + if rpcCli == nil { + logger.Logger.Error("model.GetFriendRecordLogBySnid rpcCli == nil") + return nil + } + args := &FriendRecordSnidArg{ + SnId: snid, + Platform: platform, + GameId: gameid, + Size: size, + } + ret := &FriendRecordSnidRet{} + err := rpcCli.CallWithTimeout("FriendRecordLogSvc.GetFriendRecordLogBySnid", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetFriendRecordLogBySnid is error", err) + return nil + } + return ret.FR +} diff --git a/model/friendunread.go b/model/friendunread.go new file mode 100644 index 0000000..1bba977 --- /dev/null +++ b/model/friendunread.go @@ -0,0 +1,114 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +type FriendUnread struct { + Id bson.ObjectId `bson:"_id"` + Snid int32 + UnreadSnids []*FriendUnreadSnid +} + +type FriendUnreadSnid struct { + SnId int32 + UnreadNum int32 +} + +type FriendUnreadRet struct { + FU *FriendUnread +} + +type FriendUnreadByKey struct { + Platform string + SnId int32 + FU *FriendUnread + UnreadSnid int32 +} + +func NewFriendUnread(snid int32) *FriendUnread { + f := &FriendUnread{Id: bson.NewObjectId()} + f.Snid = snid + f.UnreadSnids = []*FriendUnreadSnid{} + return f +} + +func UpsertFriendUnread(platform string, snid, unreadSnid int32) *FriendUnread { + if rpcCli == nil { + logger.Logger.Error("model.UpsertFriendUnread rpcCli == nil") + return nil + } + args := &FriendUnreadByKey{ + Platform: platform, + SnId: snid, + UnreadSnid: unreadSnid, + } + var ret *FriendUnreadRet + err := rpcCli.CallWithTimeout("FriendUnreadSvc.UpsertFriendUnread", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpsertFriendUnread error:", err) + return nil + } + return ret.FU +} + +func UpdateFriendUnread(platform string, snid int32, fu *FriendUnread) *FriendUnread { + if rpcCli == nil { + logger.Logger.Error("model.UpdateFriendUnread rpcCli == nil") + return nil + } + if fu.UnreadSnids == nil || len(fu.UnreadSnids) == 0 { + DelFriendUnread(platform, snid) + return nil + } + args := &FriendUnreadByKey{ + Platform: platform, + SnId: snid, + FU: fu, + } + var ret *FriendUnreadRet + err := rpcCli.CallWithTimeout("FriendUnreadSvc.UpdateFriendUnread", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdateFriendUnread error:", err) + return nil + } + return ret.FU +} + +func QueryFriendUnreadBySnid(platform string, snid int32) (fu *FriendUnread, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryFriendUnreadBySnid rpcCli == nil") + return + } + args := &FriendUnreadByKey{ + Platform: platform, + SnId: snid, + } + var ret *FriendUnreadRet + err = rpcCli.CallWithTimeout("FriendUnreadSvc.QueryFriendUnreadByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("QueryFriendUnreadBySnid error:", err) + } + if ret != nil { + fu = ret.FU + } + return +} + +func DelFriendUnread(platform string, snid int32) { + if rpcCli == nil { + logger.Logger.Error("model.DelFriendUnread rpcCli == nil") + return + } + args := &FriendUnreadByKey{ + Platform: platform, + SnId: snid, + } + err := rpcCli.CallWithTimeout("FriendUnreadSvc.DelFriendUnread", args, nil, time.Second*30) + if err != nil { + logger.Logger.Warn("DelFriendUnread error:", err) + } + return +} diff --git a/model/gameconfig.go b/model/gameconfig.go new file mode 100644 index 0000000..0beff94 --- /dev/null +++ b/model/gameconfig.go @@ -0,0 +1,83 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +type ThirdConfigData struct { + Platform string + AgentName string + AgentKey string +} +type GameConfig struct { + DgConfig []ThirdConfigData //Dg的配置信息 + HboConfig []ThirdConfigData //Hbo的配置信息 +} + +var GameConfigPath = "../data/thrconfig.json" +var GameConfigData = &GameConfig{} + +func InitGameConfig() { + buf, err := os.ReadFile(GameConfigPath) + if err != nil { + logger.Logger.Error("InitGameParam os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, GameConfigData) + if err != nil { + logger.Logger.Error("InitGameParam json.Unmarshal error ->", err) + return + } +} + +func GetDgConfigByPlatform(platform string) (string, string, string) { + + for _, value := range GameConfigData.DgConfig { + if value.Platform == platform { + return value.AgentName, value.AgentKey, "DG" + } + } + for _, value := range GameConfigData.HboConfig { + if value.Platform == platform { + return value.AgentName, value.AgentKey, "HBO" + } + } + return "", "", "" +} + +func OnlyGetDgConfigByPlatform(platform string) (string, string, string) { + + for _, value := range GameConfigData.DgConfig { + if value.Platform == platform { + return value.AgentName, value.AgentKey, "DG" + } + } + return "", "", "" +} + +func OnlyGetHboConfigByPlatform(platform string) (string, string, string) { + for _, value := range GameConfigData.HboConfig { + if value.Platform == platform { + return value.AgentName, value.AgentKey, "HBO" + } + } + return "", "", "" +} + +func GetAllDgAgent() []string { + agentNameArr := []string{} + for _, value := range GameConfigData.DgConfig { + agentNameArr = append(agentNameArr, value.AgentName) + } + return agentNameArr +} + +func GetAllHboAgent() []string { + agentNameArr := []string{} + for _, value := range GameConfigData.HboConfig { + agentNameArr = append(agentNameArr, value.AgentName) + } + return agentNameArr +} diff --git a/model/gamedata.go b/model/gamedata.go new file mode 100644 index 0000000..6e4ba72 --- /dev/null +++ b/model/gamedata.go @@ -0,0 +1,186 @@ +package model + +import ( + "regexp" + "sync" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var gameKVDatas = sync.Map{} + +type GameKVData struct { + DataId bson.ObjectId `bson:"_id"` + Key string + IntVal int64 + FloatVal float64 + StrVal string + IntArr []int64 +} + +func InitGameKVData() error { + if rpcCli == nil { + logger.Logger.Errorf("model.InitGameKVData rpcCli == nil") + return nil + } + + var args struct{} + var datas []*GameKVData + err := rpcCli.CallWithTimeout("GameKVDataSvc.GetAllGameKVData", args, &datas, time.Second*30) + if err != nil { + return err + } + + for i := 0; i < len(datas); i++ { + gameKVDatas.Store(datas[i].Key, datas[i]) + } + + //@test code + //for i := 0; i < 100; i++ { + // UptIntKVGameData(strconv.Itoa(i), int64(i)) + //} + //@test code + return nil +} + +func GetIntKVGameData(key string) int64 { + if val, exist := gameKVDatas.Load(key); exist { + if data, ok := val.(*GameKVData); ok { + return data.IntVal + } + } + return 0 +} + +func GetStrKVGameData(key string) string { + if val, exist := gameKVDatas.Load(key); exist { + if data, ok := val.(*GameKVData); ok { + return data.StrVal + } + } + return "" +} + +func GetFloatKVGameData(key string) float64 { + if val, exist := gameKVDatas.Load(key); exist { + if data, ok := val.(*GameKVData); ok { + return data.FloatVal + } + } + return 0 +} + +func GetIntArrKVGameData(key string) []int64 { + if val, exist := gameKVDatas.Load(key); exist { + if data, ok := val.(*GameKVData); ok { + return data.IntArr + } + } + return []int64{} +} + +func MatchStrKVGameData(expr string) map[string]*GameKVData { + regExpr, err := regexp.Compile(expr) + if err == nil { + ret := make(map[string]*GameKVData) + gameKVDatas.Range(func(k, v interface{}) bool { + if regExpr.MatchString(k.(string)) { + ret[k.(string)] = v.(*GameKVData) + } + return true + }) + return ret + } + + return nil +} + +func UptIntKVGameData(key string, val int64) error { + if rpcCli == nil { + logger.Logger.Error("model.UptIntKVGameData rpcCli == nil") + return nil + } + if value, exist := gameKVDatas.Load(key); exist { + if data, ok := value.(*GameKVData); ok { + data.IntVal = val + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + } else { + data := &GameKVData{ + DataId: bson.NewObjectId(), + Key: key, + IntVal: val, + } + gameKVDatas.Store(key, data) + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + return nil +} + +func UptFloatKVGameData(key string, val float64) error { + if value, exist := gameKVDatas.Load(key); exist { + if data, ok := value.(*GameKVData); ok { + data.FloatVal = val + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + } else { + data := &GameKVData{ + DataId: bson.NewObjectId(), + Key: key, + FloatVal: val, + } + gameKVDatas.Store(key, data) + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + return nil +} + +func UptStrKVGameData(key string, val string) error { + if rpcCli == nil { + logger.Logger.Error("model.UptStrKVGameData rpcCli == nil") + return nil + } + if value, exist := gameKVDatas.Load(key); exist { + if data, ok := value.(*GameKVData); ok { + data.StrVal = val + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + } else { + data := &GameKVData{ + DataId: bson.NewObjectId(), + Key: key, + StrVal: val, + } + gameKVDatas.Store(key, data) + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + return nil +} + +func UptIntArrKVGameData(key string, arr []int64) error { + if value, exist := gameKVDatas.Load(key); exist { + if data, ok := value.(*GameKVData); ok { + data.IntArr = arr + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + } else { + data := &GameKVData{ + DataId: bson.NewObjectId(), + Key: key, + IntArr: arr, + } + gameKVDatas.Store(key, data) + var ok bool + return rpcCli.CallWithTimeout("GameKVDataSvc.UptGameKVData", data, &ok, time.Second*30) + } + return nil +} diff --git a/model/gamedetailedlog.go b/model/gamedetailedlog.go new file mode 100644 index 0000000..55f48a6 --- /dev/null +++ b/model/gamedetailedlog.go @@ -0,0 +1,225 @@ +package model + +import ( + "errors" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + GameDetailedLogDBName = "log" + GameDetailedLogCollName = "log_gamedetailed" +) + +// 水池上下文 +type CoinPoolCtx struct { + LowerLimit int64 //水池下限 + UpperLimit int64 //水池上限 + CurrValue int32 //当前水位 + CurrMode int32 //当前水位模式 + Controlled bool //被水池控制了 +} + +type GameDetailedLogRet struct { + Gplt GameDetailedLogType +} +type GameDetailedLogType struct { + PageNo int //当前页码 + PageSize int //每页数量 + PageSum int //总页码 + Data []*GameDetailedLog //当页数据 +} + +type GameDetailedLog struct { + Id bson.ObjectId `bson:"_id"` //记录ID + LogId string //记录ID + GameId int32 //游戏id + ClubId int32 //俱乐部Id + ClubRoom string //俱乐部包间 + Platform string //平台id + Channel string //渠道 + Promoter string //推广员 + MatchId int32 //比赛ID + SceneId int32 //场景ID + GameMode int32 //游戏类型 + GameFreeid int32 //游戏类型房间号 + PlayerCount int32 //玩家数量 + GameTiming int32 //本局游戏用时(mm) + GameBaseBet int32 //游戏单位低分 + GameDetailedNote string //游戏详情 + GameDetailVer int32 //游戏详情版本 + CpCtx CoinPoolCtx //水池上下文信息 + Time time.Time //记录时间 + Trend20Lately string //最近游戏走势 + Ts int64 //时间戳 +} + +func NewGameDetailedLog() *GameDetailedLog { + log := &GameDetailedLog{Id: bson.NewObjectId()} + return log +} + +func NewGameDetailedLogEx(logid string, gameid, sceneid, gamemode, gamefreeid, playercount, gametiming, gamebasebet int32, + gamedetailednote string, platform string, clubId int32, clubRoom string, cpCtx CoinPoolCtx, ver int32, trend20Lately string) *GameDetailedLog { + cl := NewGameDetailedLog() + cl.LogId = logid + cl.GameId = gameid + cl.ClubId = clubId + cl.ClubRoom = clubRoom + cl.SceneId = sceneid + cl.GameMode = gamemode + cl.GameFreeid = gamefreeid + cl.PlayerCount = playercount + cl.GameTiming = gametiming + cl.GameBaseBet = gamebasebet + cl.GameDetailedNote = gamedetailednote + cl.Platform = platform + tNow := time.Now() + cl.Time = tNow + cl.CpCtx = cpCtx + cl.GameDetailVer = ver + cl.Trend20Lately = trend20Lately + cl.Ts = time.Now().Unix() + return cl +} + +func InsertGameDetailedLog(log *GameDetailedLog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertGameDetailedLog rpcCli == nil") + return + } + var ret bool + err = rpcCli.CallWithTimeout("GameDetailedSvc.InsertGameDetailedLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("model.InsertGameDetailedLog error:", err) + return + } + return +} + +type RemoveGameDetailedLogArg struct { + Plt string + Ts time.Time +} + +func RemoveGameDetailedLog(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + if rpcCli == nil { + logger.Logger.Error("model.RemoveGameDetailedLog rpcCli == nil") + return nil, errors.New("rpcCli is nil") + } + args := &RemoveGameDetailedLogArg{ + Plt: plt, + Ts: ts, + } + var ret mgo.ChangeInfo + err := rpcCli.CallWithTimeout("GameDetailedSvc.RemoveGameDetailedLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("model.RemoveGameDetailedLog error:", err) + return nil, err + } + return &ret, nil +} + +type GameDetailedArg struct { + Plt string + StartTime, EndTime int64 +} + +func GetAllGameDetailedLogsByTs(plt string, startTime, endTime int64) (data []GameDetailedLog) { + if rpcCli == nil { + logger.Logger.Error("model.GetAllGameDetailedLogsByTs rpcCli == nil") + return nil + } + args := &GameDetailedArg{ + Plt: plt, + StartTime: startTime, + EndTime: endTime, + } + + err := rpcCli.CallWithTimeout("GameDetailedSvc.GetAllGameDetailedLogsByTs", args, &data, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetAllGameDetailedLogsByTs is error", err) + return nil + } + return +} + +type GetPlayerHistoryArg struct { + Plt string + LogId string +} + +func GetPlayerHistory(plt, logid string) *GameDetailedLog { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerHistory rpcCli == nil") + return nil + } + args := &GetPlayerHistoryArg{ + Plt: plt, + LogId: logid, + } + var ret GameDetailedLog + err := rpcCli.CallWithTimeout("GameDetailedSvc.GetPlayerHistory", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerHistory is error", err) + return nil + } + return &ret +} + +type GameDetailedGameIdAndArg struct { + Plt string + Gameid int + LimitNum int +} + +func GetAllGameDetailedLogsByGameIdAndTs(plt string, gameid int, limitnum int) (data []GameDetailedLog) { + if rpcCli == nil { + logger.Logger.Error("model.GetAllGameDetailedLogsByGameIdAndTs rpcCli == nil") + return nil + } + args := &GameDetailedGameIdAndArg{ + Plt: plt, + Gameid: gameid, + LimitNum: limitnum, + } + + err := rpcCli.CallWithTimeout("GameDetailedSvc.GetAllGameDetailedLogsByGameIdAndTs", args, &data, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetAllGameDetailedLogsByGameIdAndTs is error", err) + return nil + } + return +} + +type GetPlayerHistoryAPIArg struct { + //SnId int32 + Platform string + StartTime, EndTime int64 + PageNo, PageSize int +} + +func GetPlayerHistoryAPI(snId int32, platform string, startTime, endTime int64, pageno, pagesize int) (gdt GameDetailedLogType) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerHistoryAPI rpcCli == nil") + return + } + args := &GetPlayerHistoryAPIArg{ + //SnId: snId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + PageNo: pageno, + PageSize: pagesize, + } + ret := &GameDetailedLogRet{} + err := rpcCli.CallWithTimeout("GameDetailedSvc.GetPlayerHistoryAPI", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerHistoryAPI is error", err) + return + } + return ret.Gplt +} diff --git a/model/gamelogtype.go b/model/gamelogtype.go new file mode 100644 index 0000000..83a6ecf --- /dev/null +++ b/model/gamelogtype.go @@ -0,0 +1,1679 @@ +package model + +// 赢三张 +type WinThreeType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []WinThreePerson //玩家信息 + Chip []PlayerChip //出牌详情 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool // 是否启用智能化运营 + GamePreUseSmartState int //-1:正常 + GameLastUseSmartState int //-1:正常 + RobotUseSmartState int64 // Robot使用智能化运营状况 +} +type PlayerChip struct { + UserId int32 //玩家ID + BetTotal int64 //玩家总投注 + Chip int64 //玩家得分 + StartCoin int64 //玩家开始前金币 + BetAfterCoin int64 //玩家投注后金币,也就是客户端应该显示的金币 + IsCheck bool //是否看牌 + Round int32 //当前轮次 + Op int32 //操作 +} + +type WinThreePerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + KindOfCard int32 //牌型 + IsWin int32 //输赢 + IsRob bool //是否是机器人 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + Flag int //标识 + Pos int32 //位置 + StartCoin int64 //开始金币 + BetCoin int64 //押注额度 + RoundCheck int32 //轮次,看牌的 + IsFirst bool //是否第一次 + IsLeave bool + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + SingleFlag int32 //单控标记 +} + +// 经典牛牛 抢庄牛牛 +type BullFightType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []BullFightPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool // 是否启用智能化运营 +} +type BullFightPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + CardBakinfo []int32 //牌值 + IsWin int32 //输赢 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + TaxLottery int64 //彩金池,增加值 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + Flag int //标识 + IsFirst bool + StartCoin int64 //开始金币 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + Rate int64 //斗牛倍率 + WBLevel int32 //黑白名单等级 + RobBankRate int64 //抢庄倍率 + SingleAdjust int32 // 单控输赢 1赢 2输 +} + +// 斗地主、跑得快 +type DoudizhuType struct { + RoomId int //房间Id + BasicScore int //基本分 + Spring int //春天 1代表春天 + BombScore int //炸弹分 + BaseScore int32 //底分 + PlayerCount int32 //玩家数量 + BaseCards []int //底牌 + BankerId int32 //斗地主地主Id + PlayerData []DoudizhuPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool // 是否启用智能化运营 +} +type DoudizhuPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + OutCards [][]int64 //出的牌 + SurplusCards []int32 //剩下的牌 + IsWin int64 //输赢 + IsRob bool //是否是机器人 + IsFirst bool + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + StartCoin int64 //开始的金币数量 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 +} + +// 推饼 +type TuibingType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []TuibingPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool // 是否启用智能化运营 +} +type TuibingPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + IsWin int32 //输赢 + IsRob bool //是否是机器人 + IsFirst bool + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + Flag int //标识 + StartCoin int64 //开始金币 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + Rate int64 //倍率 + WBLevel int32 //黑白名单等级 +} + +// 十三水 +// 十三水牌值 +type ThirteenWaterType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []ThirteenWaterPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 +} + +type ThirteenWaterPoker struct { + Head [3]int + Mid [5]int + End [5]int + PokerType int +} + +type ThirteenWaterPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 // 玩家总赢分 + Cardinfo []int // 牌值 + CardsO ThirteenWaterPoker + IsWin int32 // 输赢 + Tax int64 // 税收 + ClubPump int64 // 俱乐部税收 + IsRob bool // 是否是机器人 + IsFirst bool + Flag int //标识 + StartCoin int64 //开始金币 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + Rate int64 //十三水分数 + WBLevel int32 //黑白名单等级 + TableScore [6]int64 // 0头墩,1中墩,2尾墩,3头墩额外加分,4中墩额外加分,5尾墩额外加分 + LeaveCombat int // 逃跑补偿分 + AllFight int // 全垒打分 + GunScore int // 打枪分 + AllScore int // 总得分 + HeadName string // 头墩牌型名称 + MidName string // 中墩牌型名称 + EndName string // 尾墩牌型名称 + SpecialName string // 特殊牌型名称 + IsDP bool // 是否倒排 + TestLog []string +} + +// 二人麻将 +type MahjongType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int32 //玩家数量 + BaseScore int32 //底分 + HuType []int64 //本局胡牌牌型 + PlayerData []MahjongPerson //玩家信息 +} +type MahjongPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + SurplusCards []int64 //剩下的牌 + CardsChow [][]int64 //吃的牌 + CardsPong []int64 //碰的牌 + CardsMingKong []int64 //明杠的牌 + CardsAnKong []int64 //暗杠的牌 + IsWin int32 //输赢 + StartCoin int64 //开始金币 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + IsFirst bool + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 +} + +// 百人牛牛 龙虎斗 百家乐 红黑大战 +type HundredType struct { + RegionId int32 //边池ID 庄家 天 地 玄 黄 + IsWin int //边池输赢 + Rate int //倍数 + CardsInfo []int32 //扑克牌值 + PlayerData []HundredPerson //玩家属性 + CardsKind int32 //牌类型 + CardPoint int32 //点数 + IsSmartOperation bool // 是否启用智能化运营 +} +type HundredPerson struct { + UserId int32 //用户Id + UserBetTotal int64 //用户下注 + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsRob bool //是否是机器人 + IsFirst bool + WBLevel int32 //黑白名单等级 + Result int32 //单控结果 + UserBetTotalDetail []int64 //用户下注明细 +} + +// 碰撞 +type CrashType struct { + RegionId int32 //边池ID 庄家 天 地 玄 黄 + IsWin int //边池输赢 + Rate int //倍数 + CardsInfo []int32 //扑克牌值 + PlayerData []CrashPerson //玩家属性 + CardsKind int32 //牌类型 + CardPoint int32 //点数 + IsSmartOperation bool // 是否启用智能化运营 + Hash string //Hash + Period int //当前多少期 + Wheel int //第几轮 +} + +// 碰撞 +type CrashPerson struct { + UserId int32 //用户Id + UserBetTotal int64 //用户下注 + UserMultiple int32 //下注倍数 + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsRob bool //是否是机器人 + IsFirst bool + WBLevel int32 //黑白名单等级 + Result int32 //单控结果 + UserBetTotalDetail []int64 //用户下注明细 + Tax int64 //税收 +} + +// 十点半 +type TenHalfType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []TenHalfPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool // 是否启用智能化运营 +} + +type TenHalfPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + IsWin int32 //输赢 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + Flag int //标识 + IsFirst bool + StartCoin int64 //开始金币 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 +} + +type GanDengYanType struct { + RoomId int // 房间Id + RoomRounds int32 // 建房局数 + NowRound int32 // 当前局数 + BombScore int // 炸弹倍率 + BaseScore int32 // 底分 + PlayerCount int32 // 玩家数量 + BankerId int32 // 庄家id + PlayerData []GanDengYanPlayer // 玩家信息 + ClubRate int32 // 俱乐部抽水比例 +} + +type GanDengYanHandCards struct { + Cards []int64 + Index int32 +} + +type GanDengYanPlayer struct { + UserId int32 // 玩家ID + UserIcon int32 // 玩家头像 + ChangeCoin int64 // 玩家得分 + OutCards []*GanDengYanHandCards // 出的牌 + SurplusCards []int32 // 剩下的牌 + IsWin int64 // 输赢 + IsRob bool // 是否是机器人 + IsFirst bool + Tax int64 // 税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 // 俱乐部额外抽水 + StartCoin int64 // 开始的金币数量 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + NumMagic int32 // 癞子或2倍数 + NumCards int32 // 剩余牌数 + NumHand int32 // 手牌倍数 + Num int32 // 总倍数 +} + +// 红包扫雷 +type RedPackGameType struct { + BankerId int32 //庄家id + BankerBet int32 //庄家下注金额 + BankerWin int32 //庄家赢取 + BankerRest int32 //庄家退回钱数 + Rate int32 //倍数 + BombNum int32 //雷号 + HitBombCnt int32 //中雷的人数 + PlayerData []*RedPackPerson //玩家属性 +} +type RedPackPerson struct { + UserId int32 //用户Id + IsFirst bool + BeforeCoin int64 //抢包前金额 + AfterCoin int64 //抢包后金额 + ChangeCoin int64 //金额变化 + GrabCoin int32 //抢到红包的数量 + IsHit bool //是否中雷 + IsRob bool //是否是机器人 +} + +// 鱼场 +type BulletLevelTimes struct { + Level int32 //等级 + Times int32 //次数 +} +type FishCoinNum struct { + ID int32 // 鱼id + Num int32 //打死数量 + Power int32 //子弹价值 + Coin int32 //金币(总金币) + HitNum int32 //击中次数 +} + +type FishPlayerData struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + TotalIn int64 //玩家该阶段总投入 + TotalOut int64 //玩家该阶段总产出 + CurrCoin int64 //记录时玩家当前金币量 +} + +type FishDetiel struct { + BulletInfo *[]BulletLevelTimes //子弹统计 + HitInfo *[]FishCoinNum // + PlayData *FishPlayerData //统计 +} + +// 拉霸 +// 绝地求生记录详情 +type IslandSurvivalGameType struct { + //all + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + BetCoin int64 //下注金额 + WinCoin int64 //下注赢取金额 + IsFirst bool + Smallgamewinscore int64 //吃鸡游戏赢取的分数 + ChangeCoin int64 //本局游戏金额总变化 + FreeTimes int32 //免费转动次数 + AllWinNum int32 //中奖的线数 + LeftEnemy int32 //剩余敌人,需保存 + Killed int32 //总击杀敌人,需保存 + HitPoolIdx int //下注索引 + Cards []int //15张牌 +} + +const ( + WaterMarginSmallGame_Unop int32 = iota + WaterMarginSmallGame_AddOp + WaterMarginSmallGame_SubOp +) + +// 水浒传小游戏数据 +type WaterMarginSmallGameInfo struct { + AddOrSub int32 //加减操作 0:表示未操作 1:加 2:减 + Score int64 //本局小游戏参与的分数 + Multiple int32 //倍数 0:表示本局输了 >1:表示猜对小游戏 + Dice1 int32 //骰子1的点数 + Dice2 int32 //骰子2的点数 +} + +type WaterMarginType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + IsFirst bool //是否一次游戏 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + JackpotNowCoin int64 //爆奖金额 + Cards []int //15张牌 + HitPoolIdx int //压分的索引 + JackpotHitFlag int //如果jackpotnowcoin>0;该值标识中了哪些奖池,二进制字段 1:小奖(第0位) 2:中奖(第1位) 4:大奖(第2位) + TrigFree bool //是否触发免费转动 + SMGame []*WaterMarginSmallGameInfo //小游戏数据 +} + +type FootBallHeroesType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsFirst bool + Score int32 //总押注数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + Cards []int //15张牌 + CoinReward []int64 //礼物奖励 + Smallgamescore int64 //小游戏分数 + Smallgamewinscore int64 //小游戏赢取的分数 + SmallgameList []int32 //小游戏记录 + HitPoolIdx int //当前命中的奖池 +} +type FruitMachineType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + IsFirst bool + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + JackpotNowCoin int64 //爆奖金额 + Cards []int //15张牌 + Smallgamescore int64 //小游戏分数 + HitPoolIdx int //当前命中的奖池 +} +type GoddessType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + IsFirst bool + Smallgamescore int64 //小游戏分数 + Smallgamewinscore int64 //小游戏赢取的分数 + SmallgameCard int32 //小游戏卡牌 + CardsGoddess [3]int32 //3张牌 + BetMultiple int32 //押倍倍数 + SmallgameList []int32 //小游戏记录 + HitPoolIdx int //当前命中的奖池 +} +type RollLineType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + RoseCount int32 //玫瑰数量 + Cards []int //15张牌 + CoinReward []int64 //礼物奖励 + HitPoolIdx int //当前命中的奖池 +} +type IceAgeType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards [][]int32 // 消除前后的牌(消除前15张,消除后15张...) + BetLines []int64 //下注的线 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + IsFree bool // 是否免费 + TotalBonusValue int64 // 总bonus数 + WBLevel int32 //黑白名单等级 + WinLines [][]int // 赢分的线 + WinJackpot int64 // 赢奖池分数 + WinBonus int64 // 赢小游戏分数 +} +type TamQuocType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards []int32 //15张牌 + BetLines []int64 //下注的线 + WBLevel int32 //黑白名单等级 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + IsFree bool // 是否免费 + TotalBonusValue int64 // 总bonus数 + WinLines []int // 赢分的线 + WinJackpot int64 // 赢奖池分数 + WinBonus int64 // 赢小游戏分数 +} +type CaiShenType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards []int32 //15张牌 + WBLevel int32 //黑白名单等级 + BetLines []int64 //下注的线 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + IsFree bool // 是否免费 + TotalBonusValue int64 // 总bonus数 + WinLines []int // 赢分的线 + WinJackpot int64 // 赢奖池分数 + WinBonus int64 // 赢小游戏分数 +} +type AvengersType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards []int32 //15张牌 + WBLevel int32 //黑白名单等级 + BetLines []int64 //下注的线 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + IsFree bool // 是否免费 + TotalBonusValue int64 // 总bonus数 + WinLines []int // 赢分的线 + WinJackpot int64 // 赢奖池分数 + WinBonus int64 // 赢小游戏分数 +} +type EasterIslandType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards []int32 //15张牌 + WBLevel int32 //黑白名单等级 + BetLines []int64 //下注的线 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + IsFree bool // 是否免费 + TotalBonusValue int64 // 总bonus数 + WinLines []int // 赢分的线 + WinJackpot int64 // 赢奖池分数 + WinBonus int64 // 赢小游戏分数 +} +type RollTeamType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + AllWinNum int32 //中奖的线数 + IsFirst bool + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + PokerBaseCoin int32 //扑克游戏获得的金币 + PokerRate int32 //游戏的翻倍值 + GameCount int32 //游戏次数 + HitPoolIdx int //当前命中的奖池 + Cards []int //15张牌 +} +type RollPerson struct { + UserId int32 //用户Id + UserBetTotal int64 //用户下注 + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsFirst bool + IsRob bool //是否是机器人 + WBLevel int32 //黑白名单等级 +} +type RollHundredType struct { + RegionId int32 //边池ID -1.庄家 0.大众 1.雷克萨斯 2.宝马 3.奔驰 4.保时捷 5.玛莎拉蒂 6.兰博基尼 7.法拉利 + IsWin int //边池输赢 1.赢 0.平 -1.输 + Rate int //倍数 + SType int32 //特殊牌型 临时使用 + RollPerson []RollPerson +} + +// 梭哈 +type FiveCardType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []FiveCardPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 +} + +// 梭哈 +type FiveCardPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + IsWin int32 //输赢 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + BetTotal int64 //用户当局总下注 + IsAllIn bool //是否全下 + WBLevel int32 //黑白名单等级 +} + +// 骰子 +type RollPointPerson struct { + UserId int32 //用户Id + UserBetTotal int64 //用户下注 + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsFirst bool + IsRob bool //是否是机器人 + WBLevel int32 //黑白名单等级 + BetCoin []int32 //押注金币 +} +type RollPointType struct { + RoomId int32 + Point []int32 + Score []int32 + BetCoin int64 + WinCoin int64 + Person []RollPointPerson +} + +// 轮盘 +type RouletteType struct { + BankerInfo RouletteBanker //庄家信息 + Person []RoulettePerson //下注玩家列表 + RouletteRegion []RouletteRegion //下区域 +} +type RouletteBanker struct { + Point int //当局开的点数 + TotalBetCoin int64 //总下注金额 + TotalWinCoin int64 //总输赢金额 +} +type RouletteRegion struct { + Id int //0-156 下注位置编号 + IsWin int //是否中奖 + BetCoin int64 //当前区域总下注金额 + WinCoin int64 //当前区域总输赢金额 + Player []RoulettePlayer //当前区域下注玩家列表 +} +type RoulettePlayer struct { + UserId int32 //用户Id + BetCoin int64 //当局下注额 +} +type RoulettePerson struct { + UserId int32 //用户Id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + UserBetTotal int64 //用户总下注 + UserWinCoin int64 //用户输赢 + IsRob bool //是否是机器人 + WBLevel int32 //黑白名单等级 + BetCoin map[int]int64 //下注区域对应金额 +} + +// 九线拉王 +type NineLineKingType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + IsFirst bool + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + AllWinNum int32 //中奖的线数 + FreeTimes int32 //免费转动次数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + JackpotNowCoin int64 //爆奖金额 + Cards []int //15张牌 + HitPoolIdx int //当前命中的奖池 + CommPool int64 //公共奖池 + PersonPool int64 //私人奖池 +} + +// 飞禽走兽 +type RollAnimalsType struct { + BetTotal int64 //总下注 + WinCoin int64 //用户输赢 + WinFlag []int64 //中奖元素 + RollLog []RollHundredType //每个区域下注信息 +} + +// 血战 +type BloodMahjongType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int32 //玩家数量 + BaseScore int32 //底分 + PlayerData []BloodMahjongPerson //玩家信息 +} + +// 碰杠牌 +type BloodMahjongCardsLog struct { + Card int64 //牌 + Pos []int //0.东 1.南 2.西 3.北 + Flag int //1.碰 2.明杠 3.暗杠 4.补杠 +} + +// 分数类型 +type BloodMahjongScoreTiles struct { + LogType int //0.胡 1.刮风 2.下雨 3.退税 4.查花猪 5.查大叫 6.被抢杠 补杠退钱 7.呼叫转移 + OtherPos []int //源自哪个位置的玩家 + Coin int64 //理论进账 + ActualCoin int64 //实际进账 + Rate int32 //倍率 + Params []int64 //【Honor】//胡牌类型 +} +type BloodMahjongPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Pos int32 //玩家位置 0.东 1.南 2.西 3.北 + IsLeave bool //是否离场 + Bankruptcy bool //是否破产 + HuNumber int32 //第几胡 1 2 3 + LackColor int64 //定缺花色 + Hands []int64 //手牌 + HuCards []int64 //胡牌 + CardsLog []BloodMahjongCardsLog //碰杠牌 + ScoreTiles []BloodMahjongScoreTiles //分数 + IsWin int32 //输赢 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + StartCoin int64 //开始金币 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 +} + +type PCProp struct { + Id uint32 + TypeName string // 金币类型 + AreaType int32 // 所在区域 0平台上 1有效区 2无效区 3小车内 + CoinVal int64 // 金币面值 + X float32 + Y float32 + Z float32 + RotX float32 + RotY float32 + RotZ float32 +} + +// 推币机 +type PushingCoinRecord struct { + RoomId int // 房间id + RoomType int // 房间类型 + GameMode int // 游戏模式 + BaseScore int64 // 底分 + ShakeTimes int32 // 震动次数 + WallUpTimes int32 // 升墙次数 + EventTimes []int32 // 事件次数 + Props []*PCProp // 所有金币 +} + +type HuntingRecord struct { + RoomId int // 房间ID + BaseScore int // 底分 + SnId int32 // 玩家ID + StartCoin int64 // 下注前金额 + Coin int64 // 下注后金额 + ChangeCoin int64 // 金币变化 + CoinPool int64 // 爆奖金额 + Point int64 // 单线点数 + LineNum int64 // 线数 + BetCoin int64 // 下注金额 + WinCoin int64 // 产出金额 + Level int // 当前关卡 + Gain int64 // 翻牌奖励 +} + +// 对战三公 +type SanGongPVPType struct { + RoomId int32 //房间ID + RoomRounds int32 //建房局数 + RoomType int32 //房间类型 + NowRound int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + PlayerData []SanGongPVPPerson //玩家信息 + ClubRate int32 //俱乐部抽水比例 + IsSmartOperation bool //是否启用智能化运营 +} +type SanGongPVPPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + ChangeCoin int64 //玩家得分 + Cardinfo []int32 //牌值 + IsWin int32 //输赢 + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + ClubPump int64 //俱乐部额外抽水 + IsRob bool //是否是机器人 + Flag int //标识 + IsFirst bool + StartCoin int64 //开始金币 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 +} + +// 德州牛仔 +type DZNZCardInfo struct { + CardId int32 //手牌ID 牛仔 公共牌 公牛 + CardsInfo []int32 //扑克牌值 + CardsKind int32 //牌类型 +} +type DZNZZoneInfo struct { + RegionId int32 //13个下注区域 + IsWin int //边池输赢 + Rate float32 //倍数 + PlayerData []HundredPerson //玩家属性 + IsSmartOperation bool //是否启用智能化运营 +} +type DZNZHundredInfo struct { + CardData []DZNZCardInfo //发牌信息 + ZoneInfo []DZNZZoneInfo //每个下注区域信息 +} + +// 财运之神 +type FortuneZhiShenType struct { + //基本信息 + RoomId int //房间Id + BasicScore int32 //单注 + PlayerSnId int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + TotalBetCoin int64 //总押注 + TotalLine int32 //总线数(固定) + TotalWinCoin int64 //总派彩 + NowGameState int //当前游戏模式(0,1,2,3)普通/免费/停留旋转/停留旋转2 + NowNRound int //第几轮 + IsOffline int //0,1 正常(不显示)/掉线(显示) + FirstFreeTimes int //免费游戏剩余次数 + SecondFreeTimes int //停留旋转游戏剩余次数 + //中奖统计 + HitPrizePool []int64 //命中奖池(小奖|中奖|大奖|巨奖) + WinLineNum int //中奖线个数 + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + GemstoneNum int //宝石数量 + GemstoneWinCoin int64 //宝石派彩 + //详情 + Cards []int32 //元素顺序 横向 + GemstoneRateCoin []int64 //宝石金额 横向 + //中奖线详情 + WinLine []FortuneZhiShenWinLine + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 +} +type FortuneZhiShenWinLine struct { + Id int //线号 + EleValue int32 //元素值 + Num int //数量 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + WinFreeGame int //(0,1,2)旋转并停留*3/免费游戏*6/免费游戏*3 +} + +// 金鼓齐鸣记录详情 +type GoldDrumWinLineInfo struct { + EleValue int //元素值 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + GameType int //(0,1,2)无/免费游戏/聚宝盆游戏 +} +type GoldDrumGameType struct { + //all + RoomId int32 //房间Id + BasicScore int32 //单注分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + BetCoin int64 //下注金额 + WinCoin int64 //下注赢取金额总金额 + //Smallgamewinscore int64 //小游戏赢取的分数 + ChangeCoin int64 //本局游戏金额总变化 + FreeTimes int32 //免费转动次数 + AllWinNum int32 //中奖的线数 + HitPoolIdx int //下注索引 + Cards []int //15张牌 + + NowGameState int //当前游戏模式(0,1)普通/免费 + HitPrizePool []int64 //命中奖池(多喜小奖|多寿中奖|多禄大奖|多福巨奖) + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + WinLineInfo []GoldDrumWinLineInfo //中奖线详情 + + NowFreeGameTime int32 //当前免费游戏第几次 + CornucopiaCards []int32 //聚宝盆游戏数据 -1 未开启 0小将 1中奖 2大奖 3巨奖 + IsOffline bool //玩家是否掉线 true 掉线 + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 +} + +// 金福报喜记录详情 +type CopperInfo struct { + Pos int32 //铜钱元素索引,从0开始 + Coin int64 //铜钱奖励金币 +} +type GoldBlessWinLineInfo struct { + EleValue int //元素值 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + GameType int //(0,1,2,3)无/免费游戏/聚宝盆游戏/招福纳财游戏 +} +type GoldBlessGameType struct { + //all + RoomId int32 //房间Id + BasicScore int32 //单注分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + BetCoin int64 //下注金额 + WinCoin int64 //本局总赢取金额 + //Smallgamewinscore int64 //小游戏赢取的分数 + ChangeCoin int64 //本局游戏金额总变化 + FreeTimes int32 //免费转动次数 + AllWinNum int32 //中奖的线数 + HitPoolIdx int //下注索引 + Cards []int //15张牌 + + NowGameState int //当前游戏模式(0,1,2)普通/免费/招福纳财 + HitPrizePool []int64 //命中奖池(多喜小奖|多寿中奖|多禄大奖|多福巨奖) + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + WinLineInfo []GoldBlessWinLineInfo //中奖线详情 + CopperNum int32 //本局铜钱数量 + CopperCoin int64 //本局铜钱金额 + CoppersInfo []CopperInfo //铜钱结构 + + NowFreeGameTime int32 //当前免费游戏第几次 + CornucopiaCards []int32 //聚宝盆游戏数据 -1 未开启 0小将 1中奖 2大奖 3巨奖 + IsOffline bool //玩家是否掉线 true 掉线 + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 +} + +// 发发发 +type Classic888Type struct { + //基本信息 + RoomId int //房间Id + BasicScore int32 //单注 + PlayerSnId int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + TotalBetCoin int64 //总押注 + TotalLine int32 //总线数(固定) + TotalWinCoin int64 //总派彩 + NowGameState int //当前游戏模式(0,1,2)普通/免费/停留旋转 + NowNRound int //第几轮 + IsOffline int //0,1 正常(不显示)/掉线(显示) + FirstFreeTimes int //免费游戏剩余次数 + SecondFreeTimes int //停留旋转游戏剩余次数 + //中奖统计 + HitPrizePool []int64 //命中奖池(小奖|中奖|大奖|巨奖) + WinLineNum int //中奖线个数 + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + LanternNum int //灯笼数量 + LanternWinCoin int64 //灯笼派彩 + //详情 + Cards []int32 //元素顺序 横向 + LanternRateCoin []int64 //灯笼金额 横向 + //中奖线详情 + WinLine []Classic888WinLine + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 +} +type Classic888WinLine struct { + Id int //线号 + EleValue int32 //元素值 + Num int //数量 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + WinFreeGame int //(0,1,2,3)旋转并停留*3/免费游戏*6/免费游戏*9/免费游戏*15 +} + +// 多福 +type RichBlessedType struct { + //基本信息 + RoomId int //房间Id + BasicScore int32 //单注 + PlayerSnId int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + TotalBetCoin int64 //总押注 + TotalLine int32 //总线数(固定) + TotalWinCoin int64 //总派彩 + NowGameState int //当前游戏模式(0,1,2)普通/免费/jack小游戏 + NowNRound int //第几轮 + IsOffline int //0,1 正常(不显示)/掉线(显示) + FirstFreeTimes int //免费游戏剩余次数 + //中奖统计 + WinLineNum int //中奖线个数 + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + //详情 + Cards []int32 //普通游戏/免费游戏 + //jack游戏 + JackEleValue int32 //元素值 + JackMidCards []int64 //掀开位置 + //中奖线详情 + WinLine []RichBlessedWinLine + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 + RealCtrl bool //人工调控 + WBState int32 //调控等级 + WeightKey int32 //当前使用权重 +} +type RichBlessedWinLine struct { + Id int //线号 + EleValue int32 //元素值 + Num int //数量 + Rate int64 //倍率 + WinCoin int64 //单线派彩 +} + +// 经典777 +type FruitsType struct { + //基本信息 + RoomId int //房间Id + BasicScore int32 //单注 + PlayerSnId int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + TotalBetCoin int64 //总押注 + TotalLine int32 //总线数(固定) + TotalWinCoin int64 //总派彩 + NowGameState int //当前游戏模式(0,1,2)普通/免费/小玛丽 + NowNRound int //第几轮 + IsOffline int //0,1 正常(不显示)/掉线(显示) + FirstFreeTimes int //免费游戏剩余次数 + MaryFreeTimes int //停留旋转游戏剩余次数 + //中奖统计 + HitPrizePool int64 //命中奖池金额 + WinLineNum int //中奖线个数 + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + JackPotNum int //777数量 + JackPotWinCoin int64 //777派彩 + //详情 + Cards []int32 //普通游戏/免费游戏 + //玛丽游戏 + MaryOutSide int32 //外圈 + MaryMidCards []int32 //内圈 + //中奖线详情 + WinLine []FruitsWinLine + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 + RealCtrl bool //人工调控 + WBState int32 //调控等级 + WeightKey int32 //当前使用权重 +} +type FruitsWinLine struct { + Id int //线号 + EleValue int32 //元素值 + Num int //数量 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + WinFreeGame int //(0,1,2,3,4,5)小玛丽1/小玛丽2/小玛丽3/免费5/免费8/免费10 +} + +// 无尽宝藏记录详情 +type EndlessTreasureWinLineInfo struct { + EleValue int //元素值 + Rate int64 //倍率 + WinCoin int64 //单线派彩 + GameType int //(0,1,2,3)无/免费游戏/聚宝盆游戏/节节高游戏 +} +type EndlessTreasureGameType struct { + //all + RoomId int32 //房间Id + TotalBetScore int32 //总押注 + BasicScore float32 //单注分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + BetCoin int64 //下注金额 + WinCoin int64 //本局总赢取金额 + ChangeCoin int64 //本局游戏金额总变化 + FreeTimes int32 //免费转动次数 + AllWinNum int32 //中奖的线数 + Cards []int //15张牌 + + NowGameState int //当前游戏模式(0,1,2)普通/免费/节节高 + HitPrizePool []int64 //命中奖池(小奖|中奖|大奖|巨奖) + WinLineRate int64 //中奖线总倍率 + WinLineCoin int64 //中奖线派彩 + WinLineInfo []EndlessTreasureWinLineInfo //中奖线详情 + CopperNum int32 //本局铜钱数量 + CopperCoin int64 //本局铜钱金额 + CoppersInfo []CopperInfo //铜钱结构 + + NowFreeGameTime int32 //当前免费游戏第几次 + IsOffline bool //玩家是否掉线 true 掉线 + AddFreeTimes int32 //本局新增免费转动次数 + WBLevel int32 //黑白名单等级 + TaxCoin int64 //税收 +} + +// 拉霸类游戏 基础牌局记录 +type SlotBaseResultType struct { + RoomId int32 //房间Id + BasicBet int32 //基本分(单注金额) + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + IsFirst bool //是否第一次玩游戏 + IsFree bool //是否免费 + TotalBet int32 //总押注金额 + WinRate int32 //中奖的倍率 + FreeTimes int32 //免费转动次数 + AllWinNum int32 //中奖的总线数 + Tax int64 //暗税 + WBLevel int32 //黑白名单等级 + SingleFlag int32 //0不控1单控赢2单控输 + WinLineScore int64 //中奖线赢取分数 + WinJackpot int64 //奖池赢取分数 + WinSmallGame int64 //小游戏赢取分数 + WinTotal int64 //本次游戏总赢取(本次游戏赢取总金额=中奖线赢取+奖池赢取+小游戏赢取) + Cards []int32 //15张牌 + IsFoolPlayer bool //是否是新手 +} + +// 小火箭游戏 每局记录 +type SmallRocketBaseResultType struct { + //all + RoomId int32 //房间Id + TotalBetVal int64 //总押注 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + WinCoin int64 //本局总赢取金额 + + BetCoin1 int64 //下注金额1 + BetMul1 float64 //下注倍数1 + IsAutoBetAndTake1 bool //自动领取1 + TakeBetMul1 float64 //提款倍数1 + BetCoin2 int64 //下注金额2 + BetMul2 float64 //下注倍数2 + IsAutoBetAndTake2 bool //自动领取2 + TakeBetMul2 float64 //提款倍数2 + + TaxCoin int64 //税收 +} + +type GameResultLog struct { + BaseResult *SlotBaseResultType + AllLine int32 //线路数 + UserName string //昵称 + WinLines []int //赢分的线 + BetLines []int64 //下注的线 +} + +// 幸运骰子 +type LuckyDiceType struct { + RoomId int32 //房间Id + RoundId int32 //局数编号 + BaseScore int32 //底分 + PlayerSnid int32 //玩家id + UserName string //昵称 + BeforeCoin int64 //本局前金额 + AfterCoin int64 //本局后金额 + ChangeCoin int64 //金额变化 + Bet int64 //总押注数 + Refund int64 //返还押注数 + Award int64 //获奖金额 + BetSide int32 //压大压小 0大 1小 + Dices []int32 //3个骰子值 + Tax int64 //赢家税收 + WBLevel int32 //黑白名单等级 +} + +type CandyType struct { + RoomId int32 //房间Id + SpinID int32 //局数编号 + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + AllWinNum int32 //中奖的线数 + WinScore int32 //中奖的倍率 + AllLine int32 //线路数 + Cards []int32 //9张牌 + BetLines []int64 //下注的线 + WBLevel int32 //黑白名单等级 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + WinLines []int // 赢分的线 + WinJackpot int64 // 赢奖池分数 +} +type MiniPokerType struct { + RoomId int32 //房间Id + SpinID int32 //局数编号 + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + WinScore int32 //中奖的倍率 + Cards []int32 //5张牌 + WBLevel int32 //黑白名单等级 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + WinJackpot int64 // 赢奖池分数 +} +type CaoThapType struct { + RoomId int32 //房间Id + BasicScore int32 //基本分 + PlayerSnid int32 //玩家id + BeforeCoin int64 //下注前金额 + AfterCoin int64 //下注后金额 + ChangeCoin int64 //金额变化 + Score int32 //总押注数 + Tax int64 //暗税 + IsFirst bool + Cards []int32 //翻的牌 + WBLevel int32 //黑白名单等级 + UserName string // 昵称 + TotalPriceValue int64 // 总赢分 + WinJackpot int64 // 赢奖池分数 + BetInfo []CaoThapBetInfo // 每次下注信息 +} +type CaoThapBetInfo struct { + TurnID int32 // 操作ID + TurnTime int64 // 操作时间 + BetValue int64 // 下注金额 + Card int32 // 牌值 + PrizeValue int64 // 赢分 +} + +// 21点 +type BlackJackType struct { + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int //当前局数 + PlayerCount int //玩家数量 + PlayerData []*BlackJackPlayer //玩家信息 + BankerCards []int32 //庄家牌 + BankerCardType int32 //牌型 1:黑杰克 2:五小龙 3:其它点数 4:爆牌 + BankerCardPoint []int32 //点数 + BetCoin int64 //总下注 + GainCoinTax int64 //总输赢分(税前) +} + +type BlackJackCardInfo struct { + Cards []int32 //闲家牌 + CardType int32 //牌型 1:黑杰克 2:五小龙 3:其它点数 4:爆牌 + CardPoint []int32 //点数 + BetCoin int64 //下注 + GainCoinNoTax int64 //总输赢分(税后) + IsWin int32 //输赢 1赢 0平 -1输 +} + +type BlackJackPlayer struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + Flag int //标识 + IsFirst bool //是否第一次 + Hands []BlackJackCardInfo //牌值 + IsWin int32 //输赢 + GainCoinNoTax int64 //总输赢分(税后) + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + BaoCoin int64 //保险金 + BaoChange int64 //保险金输赢分 + BetCoin int64 //下注额 + BetChange int64 //下注输赢分 + Seat int //座位号 +} + +type DezhouPots struct { + BetTotal int64 //边池下注 + Player []DezhouPotPlayer //边池的玩家 +} +type DezhouPotPlayer struct { + Snid int32 //玩家ID + IsWin int32 //边池输赢 +} + +// 德州牌局记录 +type DeZhouUserOp struct { + Snid int32 // 操作人 + Op int32 // 操作类型 见 dezhoupoker.proto + Stage int // 所处牌局阶段 见 constants.go + Chip int64 // 操作筹码, (不下注为0) + ChipOnTable int64 // 操作后桌子上筹码 + Round int32 // 轮数 + Sec float64 // 操作时距离本局开始时的秒数 + TargetId int32 // 操作对象ID(没其他玩家为对象为0) +} + +// 德州 +type DezhouType struct { + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int32 //当前局数 + BankId int32 //庄家ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + BaseCards []int32 //公牌 只限于德州用 + PlayerData []DezhouPerson //玩家信息 + Pots []DezhouPots //边池情况 + Actions string //牌局记录 + UserOps []*DeZhouUserOp //牌局记录new +} +type DezhouPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + InitCard []int32 //初始牌值 + Cardinfo []int32 //牌值 + IsWin int32 //输赢 + GainCoinNoTax int64 //总输赢分(税后) + Tax int64 //税,不一定有值,只是作为一个临时变量使用 + BetTotal int64 //用户当局总下注 + IsAllIn bool //是否全下 + RoundFold int32 //第几轮弃牌 + CardInfoEnd []int32 //结算时的牌型 + Seat int //座位号 +} + +// tienlen +type TienLenType struct { + GameId int //游戏id + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int32 //当前局数 + BankId int32 //房主ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + TaxRate int32 //税率(万分比) + PlayerData []TienLenPerson //玩家信息 + RoomMode int +} + +type TienLenAddItem struct { + ItemType int32 // 类型 + ItemId int32 // id + Addition int32 // 加成百分比 +} + +type TienLenPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + IsWin int32 //输赢 + Seat int //座位号 + GainCoin int64 //手牌输赢分(税后) + BombCoin int64 //炸弹输赢分(税后) + BillCoin int64 //最终得分(税后) + GainTaxCoin int64 //手牌税收 + BombTaxCoin int64 //炸弹税收 + BillTaxCoin int64 //最终税收 + DelOrderCards map[int][]int32 //已出牌 + CardInfoEnd []int32 //结算时的牌型 + IsTianHu bool //是否天胡 + TestLog []string + WinRankScore int64 // 排位积分变化(包含额外加分) + RankScore int64 // 排位积分 + AddScore int64 // 排位积额外加分 + BombRankScore int64 // 排位炸弹分 + CardsScore int // 手牌分 + FaPaiType int // 发牌类型 +} + +// chesstitians +type ChesstitiansType struct { + GameId int //游戏id + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int32 //当前局数 + BankId int32 //房主ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + TaxRate int32 //税率(万分比) + PlayerData []*ChesstitiansPerson //玩家信息 + RoomMode int +} +type ChesstitiansPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + IsWin int32 //输赢 + Seat int //座位号 + GainCoin int64 //手牌输赢分(税后) + GainTaxCoin int64 //手牌税收 +} + +// tala +type TaLaType struct { + GameId int //游戏id + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int32 //当前局数 + BankId int32 //房主ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + TaxRate int32 //税率(万分比) + RoomMode int + PlayerData []TaLaPerson //玩家信息 +} +type TaLaPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + Seat int //座位号 + ChiCoin int64 //吃输赢分(税后) + BillCoin int64 //最终得分(税后) + ChiTaxCoin int64 //吃税收 + BillTaxCoin int64 //最终税收 + IsHu bool //胡 + IsWin int32 //胜负 0平 1胜 2负 + IsLoseHu bool //包赔 + Phoms [][]int32 //phom + IsNoPhom bool //瘪 + Cards []int32 //手牌 + CardsValue int32 //点数 + OpPhom []int32 //寄 + TaLaPersonOp []TaLaPersonOp //操作信息 +} +type TaLaPersonOp struct { + Round int32 + MoCard int32 + ChuCard int32 + ChiCard int32 + Cards []int32 +} + +// samloc +type SamLocType struct { + GameId int //游戏id + RoomId int32 //房间ID + RoomType int32 //房间类型 + NumOfGames int32 //当前局数 + BankId int32 //房主ID + PlayerCount int //玩家数量 + BaseScore int32 //底分 + TaxRate int32 //税率(万分比) + PlayerData []SamLocPerson //玩家信息 + RoomMode int +} +type SamLocPerson struct { + UserId int32 //玩家ID + UserIcon int32 //玩家头像 + Platform string `json:"-"` + Channel string `json:"-"` + Promoter string `json:"-"` + PackageTag string `json:"-"` + InviterId int32 `json:"-"` + WBLevel int32 //黑白名单等级 + IsRob bool //是否是机器人 + IsFirst bool //是否第一次 + IsLeave bool //中途离开 + IsWin int32 //输赢 + Seat int //座位号 + GainCoin int64 //手牌输赢分(税后) + BombCoin int64 //炸弹输赢分(税后) + BillCoin int64 //最终得分(税后) + GainTaxCoin int64 //手牌税收 + BombTaxCoin int64 //炸弹税收 + BillTaxCoin int64 //最终税收 + DelOrderCards map[int][]int32 //已出牌 + CardInfoEnd []int32 //结算时的牌型 + IsTianHu bool //是否天胡 +} diff --git a/model/gameparam.go b/model/gameparam.go new file mode 100644 index 0000000..b39436d --- /dev/null +++ b/model/gameparam.go @@ -0,0 +1,235 @@ +package model + +import ( + "encoding/json" + "os" + + "github.com/globalsign/mgo" + "mongo.games.com/goserver/core/logger" +) + +const TimeFormat = "2006-01-02 15:04:05" + +type GameParam struct { + Observers []func() + NewPlayerCoin int32 //新建角色初始金豆数量 + VerifyClientVersion bool //验证客户端版本信息 + UseEtcd bool //是否使用etcd + SrvMaintain bool //服务器维护切换 + HundredScenePreCreate bool //百人场预创建 + GuestDefaultName string //游客默认昵称 + SpreadAccountQPT int32 //流水上报 条数/每次 + FakeVerifyCode string //假的验证码 + MorePlayerLimit int //moreplayerdata单次最大查询用户量 + SamePlaceLimit int32 //和同一个人游戏的局数限制 + RobotInviteInitInterval int64 //机器人邀请的初始间隔时间(s) + RobotInviteIntervalMax int64 //机器人邀请的最大间隔时间(s) + SceneMaxIdle int64 //空房间最大空闲时间(超过指定时间自动销毁)(s) + LoginStateCacheSec int32 //登录态数据缓存时间 + KickoutDefaultFreezeMinute int64 //踢下线默认封号几分钟 + GameConfigGroupUseRobot bool //游戏分组使用机器人 + SameIpNoLimit bool //同ip不做限制 + PreLoadRobotCount int //预加载机器人数量 + QMOptimization bool //全民推广流水优化 + EnterAfterStartSwitch bool //中途加入开关 + MongoDebug bool //mongodb调试开关 + WhiteHttpAddr []string //白名单 + RbAutoBalance bool + RbAutoBalanceRate float64 + RbAutoBalanceCoin int64 + ThirdPltTransferInterval int32 //查询第三方平台向系统能否转账时间间隔,单位秒 + ThirdPltTransferMaxTry int32 //查询第三方平台向系统能否转账,最大尝试次数。默认是1次 + ThirdPltReqTimeout int32 //三方平台接口请求超时时间设置,默认是30s + IosStablePrizeMinRecharge int32 //IOS稳定版最低充值需求 + IosStableInstallPrize int32 //IOS稳定版安装奖励 + ValidDeviceInfo bool //是否验证设备信息 + InvalidRobotAccRate int //每次更换过期机器人账号的比例,百分比 + InvalidRobotDay int //机器人过期的天数 + CreatePrivateSceneCnt int //每人可以创建私有房间数量 + PrivateSceneLogLimit int //私有房间日志上限 + PrivateSceneFreeDistroySec int //私有房间免费解散时间,默认600秒 + PrivateSceneDestroyTax int //私有房间提前解散税收,百分比 + NumOfGamesConfig []int32 //私人房间局数 + BacklogGameHorseRaceLamp int //游戏内公告储备多少条,超出丢弃 + IsRobFightTest bool //是否开启机器人自己对战功能 + BullFightCtrl0108 bool //牛牛是否使用新功能规则 + OpenPoolRec bool //是否打开水池数据记录 + CoinPoolMinOutRate int32 //水池最小出分 + CoinPoolMaxOutRate int32 //水池最大出分 + MaxRTP float64 //最大rtp + AddRTP float64 //附加rtp + PlayerWatchNum int32 //百人游戏允许围观的局数 + NotifyPlayerWatchNum int32 //百人游戏围观多少局的时候开始提示 + CgAddr string //后台cg工程地址 + ReplayDataUseJson bool //回放数据使用json格式 + MaxAudienceNum int //最大观战人数 + IsFindRoomByGroup bool //查询房间列表时是否使用互通查询,默认是不使用 + NoOpTimes int32 //对战场允许托管的局数 + UseBevRobot bool //是否使用行为树机器人 + ClosePreCreateRoom bool //关闭予创建房间 + CloseQMThr bool //关闭全民三方流水计算 + ErrResetMongo bool //发生主从问题,是否重置连接 + InvitePromoterBind bool //绑定全民判定是否推广员 + CSURL string //客户端域名 + InitGameHash []string //初始哈希 + AtomGameHash []string //原子哈希 + MatchSeasonRankMaxNum int //在榜最大人数 + CloseNoviceGame []int // 关闭部分新手补偿调控,游戏id + CloseNovice bool // 关闭全部游戏新手补偿调控 + RankSeasonMaxNum int // 排位赛排行榜最大人数 + RankPlayerCoinMaxNum int // 金币榜最大人数 + CloseRankListRobot bool // 关闭排行榜机器人数据 + RankTimeout int // 排行榜刷新时间,秒 + AgoraAddress string // 客户端语音api服务地址 + InviteUrl string // 邀请活动,玩家分享页面地址 + WinCoinUpdateTime int // 收入榜每日几点更新 + RankWinCoinMaxNum int // 收入榜最大人数 + RankInviteMaxNum int32 // 邀请排行榜最大人数 +} + +var GameParamPath = "../data/gameparam.json" +var GameParamData = &GameParam{} + +func InitGameParam() { + buf, err := os.ReadFile(GameParamPath) + if err != nil { + logger.Logger.Error("InitGameParam os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, GameParamData) + if err != nil { + logger.Logger.Error("InitGameParam json.Unmarshal error ->", err) + } + if GameParamData.SpreadAccountQPT == 0 { + GameParamData.SpreadAccountQPT = 100 + } + if GameParamData.MorePlayerLimit == 0 { + GameParamData.MorePlayerLimit = 50 + } + if GameParamData.SamePlaceLimit > 10 { + GameParamData.SamePlaceLimit = 10 + } + if GameParamData.RobotInviteInitInterval == 0 { + GameParamData.RobotInviteInitInterval = 2 + } + if GameParamData.RobotInviteIntervalMax == 0 { + GameParamData.RobotInviteIntervalMax = 10 + } + if GameParamData.SceneMaxIdle == 0 { + GameParamData.SceneMaxIdle = 3600 + } + if GameParamData.LoginStateCacheSec == 0 { + GameParamData.LoginStateCacheSec = 300 + } + if GameParamData.KickoutDefaultFreezeMinute == 0 { + GameParamData.KickoutDefaultFreezeMinute = 5 + } + + if GameParamData.MaxRTP <= 0.00001 { + GameParamData.MaxRTP = 0.999999 + } + + if GameParamData.AddRTP <= 0.00001 { + GameParamData.AddRTP = 0.1 + } + + mgo.SetDebug(GameParamData.MongoDebug) + if GameParamData.RbAutoBalance { + if GameParamData.RbAutoBalanceRate == 0 { + GameParamData.RbAutoBalanceRate = 2 + } + if GameParamData.RbAutoBalanceCoin == 0 { + GameParamData.RbAutoBalanceCoin = 100000 + } + } + if GameParamData.ThirdPltTransferInterval == 0 { + GameParamData.ThirdPltTransferInterval = 3 + } + if GameParamData.ThirdPltTransferMaxTry == 0 { + GameParamData.ThirdPltTransferMaxTry = 1 + } + if GameParamData.ThirdPltReqTimeout == 0 { + GameParamData.ThirdPltReqTimeout = 30 + } + if GameParamData.IosStablePrizeMinRecharge == 0 { + GameParamData.IosStablePrizeMinRecharge = 5000 + } + if GameParamData.IosStableInstallPrize == 0 { + GameParamData.IosStableInstallPrize = 100 + } + if GameParamData.InvalidRobotDay == 0 { + GameParamData.InvalidRobotDay = 90 + } + if GameParamData.CreatePrivateSceneCnt == 0 { + GameParamData.CreatePrivateSceneCnt = 20 + } + if GameParamData.PrivateSceneLogLimit == 0 { + GameParamData.PrivateSceneLogLimit = 7000 + } + if GameParamData.PrivateSceneFreeDistroySec == 0 { + GameParamData.PrivateSceneFreeDistroySec = 600 + } + if GameParamData.PrivateSceneDestroyTax == 0 { + GameParamData.PrivateSceneDestroyTax = 5 + } + if len(GameParamData.NumOfGamesConfig) == 0 { + GameParamData.NumOfGamesConfig = []int32{5, 10, 20, 50} + } + if GameParamData.CoinPoolMinOutRate == 0 { + GameParamData.CoinPoolMinOutRate = 33 + } + + if GameParamData.CoinPoolMaxOutRate == 0 { + GameParamData.CoinPoolMaxOutRate = 66 + } + + if GameParamData.PlayerWatchNum <= 2 { + GameParamData.PlayerWatchNum = 20 + } + if GameParamData.NotifyPlayerWatchNum <= 2 { + GameParamData.NotifyPlayerWatchNum = 17 + } + if GameParamData.NotifyPlayerWatchNum >= GameParamData.PlayerWatchNum { + GameParamData.NotifyPlayerWatchNum = GameParamData.PlayerWatchNum - 3 + } + if GameParamData.NoOpTimes <= 2 { + GameParamData.NoOpTimes = 2 + } + + if GameParamData.BacklogGameHorseRaceLamp == 0 { + GameParamData.BacklogGameHorseRaceLamp = 50 + } + if GameParamData.MaxAudienceNum == 0 { + GameParamData.MaxAudienceNum = 20 + } + for _, v := range GameParamData.Observers { + v() + } + if len(GameParamData.InitGameHash) == 0 { + GameParamData.InitGameHash = []string{"ff6c5b1daa1068897377f7a64a762eafda4d225f25bf8e3bb476a7c4f2d10468"} + } + if len(GameParamData.AtomGameHash) == 0 { + GameParamData.AtomGameHash = []string{`0ead8d98e67a7c9197a6bb0e664bb84adbeb25e4e0db63d2158e48b98a50534d`} + } + if GameParamData.MatchSeasonRankMaxNum == 0 { + GameParamData.MatchSeasonRankMaxNum = 50 + } + if GameParamData.RankSeasonMaxNum == 0 { + GameParamData.RankSeasonMaxNum = 100 + } + if GameParamData.RankPlayerCoinMaxNum == 0 { + GameParamData.RankPlayerCoinMaxNum = 100 + } + if GameParamData.RankTimeout == 0 { + GameParamData.RankTimeout = 600 + } + if GameParamData.RankWinCoinMaxNum == 0 { + GameParamData.RankWinCoinMaxNum = 100 + } + if GameParamData.RankInviteMaxNum == 0 { + GameParamData.RankInviteMaxNum = 50 + } + if GameParamData.WinCoinUpdateTime == 0 { + GameParamData.WinCoinUpdateTime = 21 + } +} diff --git a/model/gameplayerlistlog.go b/model/gameplayerlistlog.go new file mode 100644 index 0000000..c28fa63 --- /dev/null +++ b/model/gameplayerlistlog.go @@ -0,0 +1,355 @@ +package model + +import ( + "errors" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + GamePlayerListLogDBName = "log" + GamePlayerListLogCollName = "log_gameplayerlistlog" +) + +type GameTotalRecord struct { + GameTotal int32 //今日牌局数 + GameTotalCoin int32 //今日游戏流水 + GameWinTotal int32 //今日游戏输赢总额 +} + +type GamePlayerListLog struct { + LogId bson.ObjectId `bson:"_id"` //记录ID + SnId int32 //用户Id + Name string //名称 + GameId int32 //游戏id + BaseScore int32 //游戏底注 + ClubId int32 //俱乐部Id + ClubRoom string //俱乐部包间 + TaxCoin int64 //税收 + ClubPumpCoin int64 //俱乐部额外抽水 + Platform string //平台id + Channel string //渠道 + Promoter string //推广员 + PackageTag string //包标识 + SceneId int32 //场景ID + GameMode int32 //游戏类型 + GameFreeid int32 //游戏类型房间号 + GameDetailedLogId string //游戏记录Id + IsFirstGame bool //是否第一次游戏 + //对于拉霸类:BetAmount=100 WinAmountNoAnyTax=0 (表示投入多少、收益多少,值>=0) + //拉霸类小游戏会是:BetAmount=0 WinAmountNoAnyTax=100 (投入0、收益多少,值>=0) + //对战场:BetAmount=0 WinAmountNoAnyTax=100 (投入会有是0、收益有正负,WinAmountNoAnyTax=100则盈利,WinAmountNoAnyTax=-100则输100) + BetAmount int64 //下注金额 + WinAmountNoAnyTax int64 //盈利金额,不包含任何税 + TotalIn int64 //本局投入 + TotalOut int64 //本局产出 + Time time.Time //记录时间 + RoomType int32 //房间类型 + GameDif string //游戏标识 + GameClass int32 //游戏类型 1棋牌 2电子 3百人 4捕鱼 5视讯 6彩票 7体育 + MatchId int32 + MatchType int32 //0.普通场 1.锦标赛 2.冠军赛 3.vip专属 + Ts int32 + IsFree bool //拉霸专用 是否免费 + WinSmallGame int64 //拉霸专用 小游戏奖励 + WinTotal int64 //拉霸专用 输赢 +} + +func NewGamePlayerListLog() *GamePlayerListLog { + log := &GamePlayerListLog{LogId: bson.NewObjectId()} + return log +} +func NewGamePlayerListLogEx(snid int32, gamedetailedlogid string, platform, channel, promoter, packageTag string, gameid, baseScore, + sceneid, gamemode, gamefreeid int32, totalin, totalout int64, clubId int32, clubRoom string, taxCoin, pumpCoin int64, roomType int32, + betAmount, winAmountNoAnyTax int64, key, name string, gameClass int32, isFirst bool, matchid, matchType int32, + isFree bool, winSmallGame, winTotal int64) *GamePlayerListLog { + cl := NewGamePlayerListLog() + cl.SnId = snid + cl.GameDetailedLogId = gamedetailedlogid + cl.Platform = platform + cl.Name = name + cl.Channel = channel + cl.Promoter = promoter + cl.PackageTag = packageTag + cl.GameFreeid = gamefreeid + cl.GameId = gameid + cl.BaseScore = baseScore + cl.ClubId = clubId + cl.GameMode = gamemode + cl.SceneId = sceneid + cl.TotalIn = totalin + cl.TotalOut = totalout + cl.ClubRoom = clubRoom + cl.TaxCoin = taxCoin + cl.IsFirstGame = isFirst + cl.ClubPumpCoin = pumpCoin + cl.RoomType = roomType + cl.BetAmount = betAmount + cl.WinAmountNoAnyTax = winAmountNoAnyTax + cl.GameDif = key + cl.GameClass = gameClass + cl.IsFree = isFree + cl.WinSmallGame = winSmallGame + cl.WinTotal = winTotal + tNow := time.Now() + cl.Ts = int32(tNow.Unix()) + cl.Time = tNow + cl.MatchId = matchid + cl.MatchType = matchType + return cl +} + +type GamePlayerListRet struct { + Gtr *GameTotalRecord + Gplt GamePlayerListType +} + +func InsertGamePlayerListLog(log *GamePlayerListLog) error { + if rpcCli == nil { + logger.Logger.Error("model.InsertGamePlayerListLog rpcCli == nil") + return errors.New("rpcCli == nil") + } + var ret bool + err := rpcCli.CallWithTimeout("GamePlayerListSvc.InsertGamePlayerListLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.InsertGamePlayerListLog is error", err) + return err + } + return nil +} + +type NeedGameRecord struct { + LogId bson.ObjectId `bson:"_id"` //记录ID + SnId int32 //用户Id + Username string //三方用户名 + ClubId int32 //俱乐部Id + ClubRoom string //俱乐部包间 + GameFreeid int32 //游戏类型 + TaxCoin int64 //税收 + ClubPumpCoin int64 //俱乐部额外抽水 + TotalIn int64 //本局投入 + TotalOut int64 //本局产出 + BetAmount int64 //投注金额(仅在个人中心表现使用,有原始数据加工得到) + WinAmountNoAnyTax int64 //盈利金额,不包含任何税(仅在个人中心表现使用,有原始数据加工得到) + SceneId int32 //场景ID + Ts int32 //记录时间 + RoomType int32 + GameDetailedLogId string // 游戏记录id + ThirdOrderId string // 三方游戏记录id + IsFree bool //拉霸专用 是否免费 + WinSmallGame int64 //拉霸专用 小游戏奖励 + WinTotal int64 //拉霸专用 输赢 + MatchType int32 //0.普通场 1.锦标赛 2.冠军赛 3.vip专属 +} + +type RecentGameIDs struct { + GameID int32 +} + +type GamePlayerListType struct { + PageNo int //当前页码 + PageSize int //每页数量 + PageSum int //总页码 + Data []*NeedGameRecord //当页数据 + *GameTotalRecord +} +type TotalCoin struct { + TotalIn int64 //本局投入 + TotalOut int64 //本局产出 + TaxCoin int64 //税收 + ClubPumpCoin int64 //俱乐部额外抽水 +} +type GamePlayerListArg struct { + SnId int32 + Platform string + StartTime, EndTime int64 + ClubId int32 + PageNo, PageSize, GameId int + RoomType, GameClass int32 +} +type GamePlayerListAPIArg struct { + SnId int32 + Platform string + StartTime, EndTime int64 + PageNo, PageSize int +} + +type GamePlayerExistListArg struct { + SnId int32 + Platform string + StartTime int64 + DayNum int +} + +func GetPlayerCount(SnId int32, platform string, startTime, endTime int64, clubId int32) *GameTotalRecord { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerCount rpcCli == nil") + return nil + } + if clubId == 0 { + return nil + } + args := &GamePlayerListArg{ + SnId: SnId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + ClubId: clubId, + } + ret := &GamePlayerListRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerCount", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerCount is error", err) + return nil + } + return ret.Gtr +} + +func GetPlayerListLog(snId int32, platform string, pageNo, pageSize int, startTime, endTime int64, clubId int32, n int) (gpt GamePlayerListType) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerListLog rpcCli == nil") + return + } + if n == 1 { + gpt.GameTotalRecord = GetPlayerCount(snId, platform, startTime, endTime, clubId) + } + if clubId == 0 { + return + } + args := &GamePlayerListArg{ + SnId: snId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + ClubId: clubId, + PageNo: pageNo, + PageSize: pageSize, + } + ret := &GamePlayerListRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerListLog", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerListLog is error", err) + return + } + return ret.Gplt +} + +func GetPlayerListByHall(snId int32, platform string, pageNo, pageSize int, startTime, endTime int64, roomType, gameClass int32) (gdt GamePlayerListType) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerListByHall rpcCli == nil") + return + } + args := &GamePlayerListArg{ + SnId: snId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + PageNo: pageNo, + PageSize: pageSize, + RoomType: roomType, + GameClass: gameClass, + } + ret := &GamePlayerListRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerListByHall", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerListByHall is error", err) + return + } + return ret.Gplt +} + +func GetPlayerListByHallEx(snId int32, platform string, pageNo, pageSize int, startTime, endTime int64, roomType, gameClass int32, gameid int) (gdt GamePlayerListType) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerListByHall rpcCli == nil") + return + } + args := &GamePlayerListArg{ + SnId: snId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + PageNo: pageNo, + PageSize: pageSize, + RoomType: roomType, + GameClass: gameClass, + GameId: gameid, + } + ret := &GamePlayerListRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerListByHallEx", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerListByHallEx is error", err) + return + } + return ret.Gplt +} + +func GetPlayerListByHallExAPI(snId int32, platform string, startTime, endTime int64, pageno, pagesize int) (gdt GamePlayerListType) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerListByHallExAPI rpcCli == nil") + return + } + args := &GamePlayerListAPIArg{ + SnId: snId, + Platform: platform, + StartTime: startTime, + EndTime: endTime, + PageNo: pageno, + PageSize: pagesize, + } + ret := &GamePlayerListRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerListByHallExAPI", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerListByHallExAPI is error", err) + return + } + return ret.Gplt +} + +func GetPlayerExistListByTs(snId int32, platform string, startTime int64, dayNum int) []int64 { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerExistListByTs rpcCli == nil") + return nil + } + args := &GamePlayerExistListArg{ + SnId: snId, + Platform: platform, + StartTime: startTime, + DayNum: dayNum, + } + var ret []int64 + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetPlayerExistListByTs", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerExistListByTs is error", err) + return nil + } + return ret +} + +type RecentGameReq struct { + Platform string + SnID int32 +} + +type RecentGameRet struct { + GameID []int32 +} + +func GetRecentGame(platform string, snid int32) []int32 { + if rpcCli == nil { + logger.Logger.Error("model.GetRecentGame rpcCli == nil") + return nil + } + args := &RecentGameReq{ + platform, + snid, + } + ret := &RecentGameRet{} + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetRecentGame", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetRecentGame is error", err) + return nil + } + return ret.GameID +} diff --git a/model/gmac.go b/model/gmac.go new file mode 100644 index 0000000..fb36711 --- /dev/null +++ b/model/gmac.go @@ -0,0 +1,37 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +//Game Manager Access Control + +type GMAC struct { + InviteRobot int32 + InviteRobotAgent int32 + AgentCreateRoom int32 + SpecialCareOf int32 + SpecailCareOfIds []int32 + LuYiTingLevel int32 + ChangeCardLevel int32 + WhiteList []string +} + +var GMACPath = "../data/gmac.json" +var GMACData = &GMAC{} + +func InitGMAC() { + buf, err := os.ReadFile(GMACPath) + if err != nil { + logger.Logger.Warn("InitGMAC os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, GMACData) + if err != nil { + logger.Logger.Warn("InitGMAC json.Unmarshal error ->", err) + } + + logger.Logger.Info("InitGMAC param=", GMACData) +} diff --git a/model/gradelog.go b/model/gradelog.go new file mode 100644 index 0000000..77a468e --- /dev/null +++ b/model/gradelog.go @@ -0,0 +1,323 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" +) + +var ( + GradeLogDBName = "log" + GradeLogCollName = "log_grade" +) + +const GradeLogMaxLimitPerQuery = 100 + +type GradeLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PackageTag string //推广包标识 + InviterId int32 //邀请人id + Count int64 + RestCount int64 + Oper string + Remark string + Time time.Time + Ver int32 + LogType int32 + Status int32 //状态 0 默认 1超时 +} + +func NewGradeLog() *GradeLog { + log := &GradeLog{LogId: bson.NewObjectId()} + return log +} + +func NewGradeLogEx(snid int32, count, restCount int64, ver, logType int32, oper, remark string, platform, channel, promoter, packageId string, inviterId int32) *GradeLog { + cl := NewGradeLog() + cl.SnId = snid + cl.Count = count + cl.RestCount = restCount + cl.Oper = oper + cl.Remark = remark + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.PackageTag = packageId + cl.InviterId = inviterId + cl.Time = time.Now() + cl.Ver = ver + cl.LogType = logType + return cl +} +func InsertGradeLog(snid int32, count, restCount int64, ver, logType int32, oper, remark string, platform, channel, promoter, packageId string, inviterId int32) error { + + if rpcCli == nil { + return ErrRPClientNoConn + } + + cl := NewGradeLog() + cl.SnId = snid + cl.Count = count + cl.RestCount = restCount + cl.Oper = oper + cl.Remark = remark + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.PackageTag = packageId + cl.InviterId = inviterId + cl.Time = time.Now() + cl.Ver = ver + cl.LogType = logType + + var ret bool + return rpcCli.CallWithTimeout("GradeLogSvc.InsertGradeLog", cl, &ret, time.Second*30) +} + +func InsertGradeLogs(logs ...*GradeLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("GradeLogSvc.InsertGradeLogs", logs, &ret, time.Second*30) +} + +type UpdateGradeLogStatusArgs struct { + Plt string + Id bson.ObjectId + Status int32 +} + +func UpdateGradeLogStatus(plt string, id bson.ObjectId, status int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &UpdateGradeLogStatusArgs{ + Plt: plt, + Id: id, + Status: status, + } + var ret bool + return rpcCli.CallWithTimeout("GradeLogSvc.UpdateGradeLogStatus", args, &ret, time.Second*30) +} + +type GradeLogLog struct { + Logs []*GradeLog + PageNo int32 + PageSize int32 + PageNum int32 +} + +type GetGradeLogByPageAndSnIdArgs struct { + Plt string + SnId int32 + PageNo int32 + PageSize int32 +} + +func GetGradeLogByPageAndSnId(plt string, snid, pageNo, pageSize int32) (ret *GradeLogLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetGradeLogByPageAndSnIdArgs{ + Plt: plt, + SnId: snid, + PageNo: pageNo, + PageSize: pageSize, + } + err = rpcCli.CallWithTimeout("GradeLogSvc.GetGradeLogByPageAndSnId", args, &ret, time.Second*30) + return +} + +type RemoveGradeLogArgs struct { + Plt string + Id bson.ObjectId +} + +func RemoveGradeLog(plt string, id bson.ObjectId) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &RemoveGradeLogArgs{ + Plt: plt, + Id: id, + } + var ret bool + return rpcCli.CallWithTimeout("GradeLogSvc.RemoveGradeLog", args, &ret, time.Second*30) +} + +type GetGradeLogBySnidAndLessTsArgs struct { + Plt string + SnId int32 + Ts time.Time +} + +func GetGradeLogBySnidAndLessTs(plt string, id int32, ts time.Time) (ret []GradeLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetGradeLogBySnidAndLessTsArgs{ + Plt: plt, + SnId: id, + Ts: ts, + } + err = rpcCli.CallWithTimeout("GradeLogSvc.GetGradeLogBySnidAndLessTs", args, &ret, time.Second*30) + return +} + +// /////////////////////////////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////////////////////////////// +// 积分兑换对账数据 +var ( + BillGradeLogDBName = "user" + BillGradeLogCollName = "user_billgradelog" +) + +type BillGradeLog struct { + LogId string //账单ID + Platform string //平台号 + GradeLogId string //账变对应记录id + SnId int32 //用户ID + ShopId int32 //商品id + ShopType int32 //商品类型 0.虚拟 1.实物 + ShopName string //商品名称 + Grade int64 //账单积分 + Coin int64 //账单兑换金额 + TimeStamp int64 //生效时间戳 + CreateTime time.Time //账单时间 + Ver int32 //账单版本号 + Oper string //操作人 + Status int32 //状态 0.创建 1.超时 3.取消订单 4.异常 9.已发货 +} + +func NewBillGradeLog(platform string, SnId, ShopId, ShopType int32, ShopName string, Grade, Coin int64, Oper, GradeLogId string) *BillGradeLog { + tNow := time.Now() + log := &BillGradeLog{ + LogId: bson.NewObjectId().Hex(), + Platform: platform, + SnId: SnId, + ShopId: ShopId, + ShopType: ShopType, + ShopName: ShopName, + Grade: Grade, + Coin: Coin, + Ver: VER_PLAYER_MAX - 1, + TimeStamp: tNow.UnixNano(), + CreateTime: tNow, + Oper: Oper, + GradeLogId: GradeLogId, + Status: 0, + } + return log +} + +func InsertBillGradeLogs(logs ...*BillGradeLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("BillGradeLogSvc.InsertBillGradeLogs", logs, &ret, time.Second*30) +} + +func InsertBillGradeLog(log *BillGradeLog) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("BillGradeLogSvc.InsertBillGradeLog", log, &ret, time.Second*30) +} + +type RemoveBillGradeLogArgs struct { + Plt string + LogId string +} + +func RemoveBillGradeLog(plt string, logid string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &RemoveBillGradeLogArgs{ + Plt: plt, + LogId: logid, + } + var ret bool + return rpcCli.CallWithTimeout("BillGradeLogSvc.InsertBillGradeLog", args, &ret, time.Second*30) +} + +type UpdateBillGradeLogStatusArgs struct { + Plt string + LogId string + Status int32 +} + +func UpdateBillGradeLogStatus(plt string, logId string, status int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &UpdateBillGradeLogStatusArgs{ + Plt: plt, + LogId: logId, + Status: status, + } + var ret bool + return rpcCli.CallWithTimeout("BillGradeLogSvc.UpdateBillGradeLogStatus", args, &ret, time.Second*30) +} + +type GetAllBillGradeLogArgs struct { + Plt string + SnId int32 + Ts int64 +} + +func GetAllBillGradeLog(plt string, snid int32, ts int64) (ret []BillGradeLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetAllBillGradeLogArgs{ + Plt: plt, + SnId: snid, + Ts: ts, + } + err = rpcCli.CallWithTimeout("BillGradeLogSvc.GetAllBillGradeLog", args, &ret, time.Second*30) + return +} + +type GetBillGradeLogByLogIdArgs struct { + Plt string + LogId string +} + +func GetBillGradeLogByLogId(plt string, logId string) (ret *BillGradeLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetBillGradeLogByLogIdArgs{ + Plt: plt, + LogId: logId, + } + err = rpcCli.CallWithTimeout("BillGradeLogSvc.GetBillGradeLogByLogId", args, &ret, time.Second*30) + return +} + +type GetBillGradeLogByBillNoArgs struct { + Plt string + SnId int32 + BillNo int64 +} + +func GetBillGradeLogByBillNo(plt string, snid int32, billNo int64) (ret *BillGradeLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetBillGradeLogByBillNoArgs{ + Plt: plt, + SnId: snid, + BillNo: billNo, + } + err = rpcCli.CallWithTimeout("BillGradeLogSvc.GetBillGradeLogByLogId", args, &ret, time.Second*30) + return +} diff --git a/model/horseracelamp.go b/model/horseracelamp.go new file mode 100644 index 0000000..33552d6 --- /dev/null +++ b/model/horseracelamp.go @@ -0,0 +1,214 @@ +package model + +import ( + "errors" + "github.com/globalsign/mgo/bson" + "time" +) + +// 跑马灯 +var ( + HorseRaceLampDBName = "user" + HorseRaceLampCollName = "user_horseracelamp" +) + +type RemoveHorseRaceLampArg struct { + Platform string + Key string +} + +type RemoveHorseRaceLampRet struct { + Data *interface{} + Tag int +} +type HorseRaceLamp struct { + Id bson.ObjectId `bson:"_id"` + Channel string //渠道 + Title string //标题 + Content string //公告内容 + Footer string + StartTime int64 //开始播放的时间 + Interval int32 //播放间隔 + Count int32 //播放次数 + CreateTime int64 //创建公告的时间 + Priority int32 //播放优先级 + MsgType int32 //公告类型 + Platform string //公告播放的平台 + State int32 //状态 0.启用;1.关闭 + Target []int32 + StandSec int32 +} + +func NewHorseRaceLamp(channel, platform, title, content, footer string, startTime int64, interval int32, count int32, priority, state int32, + msgType int32, target []int32, standSec int32) *HorseRaceLamp { + horseracelamp := &HorseRaceLamp{ + Id: bson.NewObjectId(), + Channel: channel, + Title: title, + Content: content, + Footer: footer, + StartTime: startTime, + Interval: interval, + Count: count, + Priority: priority, + CreateTime: time.Now().Unix(), + MsgType: msgType, + Platform: platform, + State: state, + Target: target, + StandSec: standSec, + } + return horseracelamp +} + +type InsertHorseRaceLampArgs struct { + HorseRaceLamps []*HorseRaceLamp + Platform string +} + +func InsertHorseRaceLamp(platform string, notices ...*HorseRaceLamp) (err error) { + if len(notices) == 0 { + return errors.New("no data") + } + if rpcCli == nil { + return ErrRPClientNoConn + } + args := InsertHorseRaceLampArgs{ + HorseRaceLamps: notices, + Platform: platform, + } + var ret bool + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.InsertHorseRaceLamp", args, &ret, time.Second*30) + if err != nil { + return err + } + return +} + +type GetHorseRaceLampArgs struct { + Platform string + Id bson.ObjectId +} + +func GetHorseRaceLamp(platform string, Id bson.ObjectId) (ret *HorseRaceLamp, err error) { + args := GetHorseRaceLampArgs{ + Platform: platform, + Id: Id, + } + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.GetHorseRaceLamp", args, &ret, time.Second*30) + if err != nil { + return nil, err + } + return +} + +func RemoveHorseRaceLamp(plt, key string) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &RemoveHorseRaceLampArg{Key: key, Platform: plt} + var ret bool + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.RemoveHorseRaceLamp", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +/* +func StopHorseRaceLamp(key string) (err error) { + Id := bson.ObjectIdHex(key) + err = HorseRaceLampCollection().Update(bson.M{"_id": Id}, bson.D{{"$set", bson.D{{"state", NOTICESTATE_STOP}}}}) + return err +}*/ + +func GetAllHorseRaceLamp(plt string) (notice []HorseRaceLamp, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.GetAllHorseRaceLamp", plt, ¬ice, time.Second*30) + if err != nil { + return nil, err + } + return notice, nil +} + +type EditHorseRaceLampArg struct { + Key string + Channel string + Platform string + Title string + Content string + Footer string + StartTime int64 + Interval int32 + Count int32 + Priority int32 + MsgType int32 + State int32 + Target []int32 + StandSec int32 +} +type EditHorseRaceLampRet struct { + Tag int +} + +func EditHorseRaceLamp(hrl *HorseRaceLamp) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &EditHorseRaceLampArg{ + Key: hrl.Id.Hex(), + Channel: hrl.Channel, + Platform: hrl.Platform, + Title: hrl.Title, + Content: hrl.Content, + Footer: hrl.Footer, + StartTime: hrl.StartTime, + Interval: hrl.Interval, + Count: hrl.Count, + Priority: hrl.Priority, + MsgType: hrl.MsgType, + State: hrl.State, + Target: hrl.Target, + StandSec: hrl.StandSec, + } + var ret bool + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.EditHorseRaceLamp", args, &ret, time.Second*30) + return err +} + +type QueryHorseRaceLampArg struct { + Platform string + MsgType int32 + State int32 + ToIndex int32 + FromIndex int + LimitDataNum int +} +type QueryHorseRaceLampRet struct { + Data []HorseRaceLamp + Count int +} + +func GetHorseRaceLampInRangeTsLimitByRange(platform string, msgType, state, fromIndex, + toIndex int32) (ret *QueryHorseRaceLampRet, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + limitDataNum := toIndex - fromIndex + if limitDataNum < 0 { + limitDataNum = 0 + } + args := &QueryHorseRaceLampArg{ + Platform: platform, + MsgType: msgType, + State: state, + FromIndex: int(fromIndex), + ToIndex: toIndex, + LimitDataNum: int(limitDataNum), + } + err = rpcCli.CallWithTimeout("HorseRaceLampSvc.GetHorseRaceLampInRangeTsLimitByRange", args, &ret, time.Second*30) + return +} diff --git a/model/hundredjacklog.go b/model/hundredjacklog.go new file mode 100644 index 0000000..94126f6 --- /dev/null +++ b/model/hundredjacklog.go @@ -0,0 +1,211 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "sync/atomic" + "time" +) + +var ( + HundredjackpotLogDBName = "log" + HundredjackpotLogCollName = "log_hundredjackpotlog" // 游戏爆奖 +) + +// HundredjackpotLog 百人场爆奖结构 +type HundredjackpotLog struct { + LogID bson.ObjectId `bson:"_id"` + SnID int32 // 玩家id + Platform string // 平台名称 + Channel string // 渠道名称 + Ts int64 // 时间戳 + Time time.Time // 时间戳 + InGame int32 // 0:其他 1~N:具体游戏id + LogType int32 // log类型 (具体见对应游戏) + Coin int64 // 中奖金额 + TurnCoin int64 // 当前金额 + Name string // 名字 + Vip int32 // vip等级 + RoomID int32 // 房间id ->gamefreeId + LikeNum int32 // 点赞数 + LinkeSnids string // 点赞人 + PlayblackNum int32 // 回放次数 + GameData []string // 回放数据json + SeqNo int64 // 流水号(隶属于进程) +} + +// HundredjackpotLogMaxLimitPerQuery 上榜人数 +const HundredjackpotLogMaxLimitPerQuery = 99 + +// NewHundredjackpotLog 实例 +func NewHundredjackpotLog() *HundredjackpotLog { + log := &HundredjackpotLog{LogID: bson.NewObjectId()} + return log +} + +// NewHundredjackpotLogEx 赋值创建 +func NewHundredjackpotLogEx(snid int32, coin, turncoin int64, roomid, logType, inGame, vip int32, platform, channel, name string, gamedata []string) *HundredjackpotLog { + cl := NewHundredjackpotLog() + cl.SnID = snid + cl.Platform = platform + cl.Channel = channel + tNow := time.Now() + cl.Ts = tNow.Unix() + cl.Time = tNow + cl.Coin = coin + cl.InGame = inGame + cl.Vip = vip + cl.LogType = logType + cl.Name = name + cl.RoomID = roomid + cl.GameData = gamedata + cl.TurnCoin = turncoin + cl.SeqNo = atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1) + return cl +} + +// GetHundredjackpotLogTsByPlatformAndGameID 时间排名 +type GetHundredjackpotLogArgs struct { + Plt string + Id1 int32 + Id2 int32 +} + +func GetHundredjackpotLogTsByPlatformAndGameID(platform string, gameid int32) (ret []HundredjackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetHundredjackpotLogArgs{ + Plt: platform, + Id1: gameid, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.GetHundredjackpotLogTsByPlatformAndGameID", args, &ret, time.Second*30) + + return +} + +// GetHundredjackpotLogCoinByPlatformAndGameID 中奖金币排名 +func GetHundredjackpotLogCoinByPlatformAndGameID(platform string, gameid int32) (ret []HundredjackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetHundredjackpotLogArgs{ + Plt: platform, + Id1: gameid, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.GetHundredjackpotLogCoinByPlatformAndGameID", args, &ret, time.Second*30) + return +} + +// GetHundredjackpotLogTsByPlatformAndGameFreeID 时间排名 +func GetHundredjackpotLogTsByPlatformAndGameFreeID(platform string, gamefreeid int32) (ret []HundredjackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetHundredjackpotLogArgs{ + Plt: platform, + Id1: gamefreeid, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.GetHundredjackpotLogTsByPlatformAndGameFreeID", args, &ret, time.Second*30) + return +} + +// GetHundredjackpotLogCoinByPlatformAndGameFreeID 中奖金币排名 +func GetHundredjackpotLogCoinByPlatformAndGameFreeID(platform string, gamefreeid int32) (ret []HundredjackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetHundredjackpotLogArgs{ + Plt: platform, + Id1: gamefreeid, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.GetHundredjackpotLogCoinByPlatformAndGameFreeID", args, &ret, time.Second*30) + return +} + +// GetLastHundredjackpotLogBySnidAndGameID . +func GetLastHundredjackpotLogBySnidAndGameID(plt string, id int32, gamefreeid int32) (log *HundredjackpotLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetHundredjackpotLogArgs{ + Plt: plt, + Id1: id, + Id2: gamefreeid, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.GetLastHundredjackpotLogBySnidAndGameID", args, &log, time.Second*30) + return +} + +// InsertHundredjackpotLog . +func InsertHundredjackpotLog(log *HundredjackpotLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.InsertHundredjackpotLog", log, &ret, time.Second*30) + return +} + +// UpdateLikeNum 点赞次数 +type UpdateLikeNumArgs struct { + Plt string + Id bson.ObjectId + LikeNum int32 + LikeSnIds string +} + +func UpdateLikeNum(plt string, id bson.ObjectId, likeNum int32, linkeSnids string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &UpdateLikeNumArgs{ + Plt: plt, + Id: id, + LikeNum: likeNum, + LikeSnIds: linkeSnids, + } + var ret bool + return rpcCli.CallWithTimeout("HundredjackpotLogSvc.UpdateLikeNum", args, &ret, time.Second*30) +} + +// UpdatePlayBlackNum 回放次数 +type UpdatePlayBlackNumArgs struct { + Plt string + Id bson.ObjectId + PlayblackNum int32 +} + +func UpdatePlayBlackNum(plt string, id bson.ObjectId, playblackNum int32) (ret []string, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &UpdatePlayBlackNumArgs{ + Plt: plt, + Id: id, + PlayblackNum: playblackNum, + } + err = rpcCli.CallWithTimeout("HundredjackpotLogSvc.UpdatePlayBlackNum", args, &ret, time.Second*30) + return +} + +// RemoveHundredjackpotLogOne 移除 +type RemoveHundredjackpotLogOneArgs struct { + Plt string + Id bson.ObjectId +} + +func RemoveHundredjackpotLogOne(plt string, id bson.ObjectId) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &RemoveHundredjackpotLogOneArgs{ + Plt: plt, + Id: id, + } + var ret bool + return rpcCli.CallWithTimeout("HundredjackpotLogSvc.RemoveHundredjackpotLogOne", args, &ret, time.Second*30) +} diff --git a/model/idmgr.go b/model/idmgr.go new file mode 100644 index 0000000..bce997e --- /dev/null +++ b/model/idmgr.go @@ -0,0 +1,38 @@ +package model + +import "math/rand" + +const ( + ClubId = "clubid" +) + +// ID管理器 +type IdManager struct { + CurList []int +} + +func (this *IdManager) PopId() int { + if len(this.CurList) > 0 { + id := this.CurList[0] + this.CurList = this.CurList[1:] + return id + } else { + return 0 + } +} +func (this *IdManager) PushId(id int) { + this.CurList = append(this.CurList, id) +} +func (this *IdManager) Init() { + spcId := map[int]bool{111111: true, 222222: true, 333333: true, 444444: true, + 555555: true, 666666: true, 777777: true, 888888: true} + idArr := rand.Perm(999999) + for _, value := range idArr { + if _, ok := spcId[value]; ok { + continue + } + if value > 1000 { + this.CurList = append(this.CurList, value) + } + } +} diff --git a/model/init.go b/model/init.go new file mode 100644 index 0000000..b0fe15e --- /dev/null +++ b/model/init.go @@ -0,0 +1,29 @@ +package model + +import ( + "mongo.games.com/game/rpc" + "time" +) + +var ( + rpcCli *rpc.RPClient +) + +func StartupRPClient(net, addr string, d time.Duration) { + cli := rpcCli + if cli != nil { + cli.Stop() + } + rpcCli = rpc.NewRPClient(net, addr, d) + if rpcCli != nil { + rpcCli.Start() + } +} + +func ShutdownRPClient() { + if rpcCli != nil { + cli := rpcCli + rpcCli = nil + cli.Stop() + } +} diff --git a/model/invitecode.go b/model/invitecode.go new file mode 100644 index 0000000..c99bc8b --- /dev/null +++ b/model/invitecode.go @@ -0,0 +1,156 @@ +package model + +import ( + "errors" + "time" + + "github.com/globalsign/mgo/bson" + + "mongo.games.com/goserver/core/logger" +) + +type InviteCode struct { + Id bson.ObjectId `bson:"_id"` + SnId int32 + Code string +} + +type InviteSnIdReq struct { + Platform string + Code string +} + +type InviteSnIdRet struct { + SnId int32 +} + +func GetSnIdByCode(platform string, code string) (int32, error) { + if rpcCli == nil { + logger.Logger.Warnf("rpcCli is nil") + return 0, errors.New("rpcCli is nil") + } + + req := &InviteSnIdReq{ + Platform: platform, + Code: code, + } + ret := &InviteSnIdRet{} + err := rpcCli.CallWithTimeout("InviteCodeSvc.GetSnIdByCode", req, ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("GetSnIdByCode err:%v", err) + return 0, err + } + + return ret.SnId, err +} + +// InviteScore 积分记录 +type InviteScore struct { + Id bson.ObjectId `bson:"_id"` + Platform string // 平台id + SnId int32 // 被邀请人id + InviteSnId int32 // 邀请人id + Tp int32 // 积分类型 common.InviteScoreType~ + Score int64 // 积分 + Ts int64 // 时间戳 +} + +type InviteScoreReq struct { + Platform string + SnId int32 +} + +type InviteScoreRet struct { + Score int64 + ZScore int64 +} + +// GetInviteScore 查询总积分 +// 下级玩家所有积分 +func GetInviteScore(plt string, snid int32) (int64, int64, error) { + if rpcCli == nil { + logger.Logger.Warnf("rpcCli is nil") + return 0, 0, errors.New("rpcCli is nil") + } + + req := &InviteScoreReq{ + Platform: plt, + SnId: snid, + } + ret := &InviteScoreRet{} + err := rpcCli.CallWithTimeout("BindScoreSvc.GetInviteScore", req, ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("GetInviteScore err:%v", err) + return 0, 0, err + } + + return ret.Score, ret.ZScore, err +} + +// SaveInviteScore 保存积分变更记录 +func SaveInviteScore(b *InviteScore) error { + if rpcCli == nil { + logger.Logger.Warnf("rpcCli is nil") + return errors.New("rpcCli is nil") + } + + ret := false + err := rpcCli.CallWithTimeout("BindScoreSvc.SaveInviteScore", b, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("SaveInviteScore err:%v", err) + return err + } + return nil +} + +const EvtBindInvite = "evt_bind_invite" +const AckBindNum = "ack_bind_num" +const EvtInviteScore = "evt_invitescore" + +type BindInvite struct { + Platform string + SnId int32 + InviteSnId int32 + Ts int64 +} + +type BindNum struct { + SnId int32 + Num int32 +} + +type InviteLisArgs struct { + Platform string + SnId int32 +} + +type InviteInfo struct { + Name string + SnId int32 + CreateTs int64 + Score int64 + ModId int32 +} + +type InviteListRet struct { + List []*InviteInfo +} + +func GetInviteList(platform string, snid int32) ([]*InviteInfo, error) { + if rpcCli == nil { + logger.Logger.Error("model.GetInviteList rpcCli == nil") + return nil, nil + } + + req := &InviteLisArgs{ + Platform: platform, + SnId: snid, + } + ret := new(InviteListRet) + err := rpcCli.CallWithTimeout("BindScoreSvc.GetInviteList", req, ret, time.Second*30) + if err != nil { + logger.Logger.Error("GetInviteList error:", err) + return nil, err + } + return ret.List, nil +} diff --git a/model/itemdatalog.go b/model/itemdatalog.go new file mode 100644 index 0000000..2ad2c5a --- /dev/null +++ b/model/itemdatalog.go @@ -0,0 +1,41 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "time" +) + +var ( + ItemLogDBName = "log" + ItemLogCollName = "log_itemlog" + TopicProbeItemLogAck = "ack_itemlog" +) + +type ItemLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台 + SnId int32 //玩家id + LogType int32 //记录类型 0.获取 1.消耗 + ItemId int32 //道具id + ItemName string //道具名称 + Count int64 //个数 + CreateTs int64 //记录时间 + Remark string //备注 +} + +func NewItemLog() *ItemLog { + log := &ItemLog{LogId: bson.NewObjectId()} + return log +} +func NewItemLogEx(platform string, snId, logType, itemId int32, itemName string, count int64, remark string) *ItemLog { + itemLog := NewItemLog() + itemLog.Platform = platform + itemLog.SnId = snId + itemLog.LogType = logType + itemLog.ItemId = itemId + itemLog.ItemName = itemName + itemLog.Count = count + itemLog.CreateTs = time.Now().Unix() + itemLog.Remark = remark + return itemLog +} diff --git a/model/jackpotdatalog.go b/model/jackpotdatalog.go new file mode 100644 index 0000000..2318ce8 --- /dev/null +++ b/model/jackpotdatalog.go @@ -0,0 +1,85 @@ +package model + +// +//var ( +// JackPotLogDBName = "log" +// JackPotLogCollName = "log_jackpotlog" +// TopicProbeJackPotLogAck = "ack_jackpotlog" +//) +// +//type JackPot struct { +// Val int64 +// Ts int64 +//} +//type JackPotLog struct { +// LogId bson.ObjectId `bson:"_id"` +// Platform string //平台 +// GameFreeId int32 +// WaterVal []byte +// JkVal1 []byte +// JkVal2 []byte +// JkVal3 []byte +// JkVal4 []byte +// Ts int64 //记录时间 +//} +// +//func NewJackPotLog() *JackPotLog { +// log := &JackPotLog{LogId: bson.NewObjectId()} +// return log +//} +// +//func NewJackPotLogEx(gameFreeId int32, platform string, waterVal, jkVal1, jkVal2, jkVal3, jkVal4 int64) *JackPotLog { +// jackPot := NewJackPotLog() +// now := time.Now() +// ts := now.Unix() - int64(now.Second()) - int64(60*now.Minute()) - int64(60*60*now.Hour()) +// jackPot.Platform = platform +// jackPot.GameFreeId = gameFreeId +// w, _ := json.Marshal([]JackPot{{Val: waterVal, Ts: ts}}) +// jackPot.WaterVal = w +// jk1, _ := json.Marshal([]JackPot{{Val: jkVal1, Ts: ts}}) +// jackPot.JkVal1 = jk1 +// jk2, _ := json.Marshal([]JackPot{{Val: jkVal2, Ts: ts}}) +// jackPot.JkVal1 = jk2 +// jk3, _ := json.Marshal([]JackPot{{Val: jkVal3, Ts: ts}}) +// jackPot.JkVal1 = jk3 +// jk4, _ := json.Marshal([]JackPot{{Val: jkVal4, Ts: ts}}) +// jackPot.JkVal1 = jk4 +// jackPot.Ts = ts +// return jackPot +//} +// +//type DelJKData struct { +// Platform string +// Ts time.Time +//} +// +//func DelJackPotLog(platform string, ts time.Time) error { +// if rpcCli == nil { +// return errors.New("rpcCli == nil") +// } +// args := &DelJKData{ +// Platform: platform, +// Ts: ts, +// } +// var ret bool +// err := rpcCli.CallWithTimeout("JackPotLogSvc.DelJackPotLog", args, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Error("model.DelJackPotLog eror.", err) +// } +// return err +//} +//func GetJackPotLog(platform string, ts time.Time) (error, []JackPotLog) { +// if rpcCli == nil { +// return errors.New("rpcCli == nil"), nil +// } +// args := &DelJKData{ +// Platform: platform, +// Ts: ts, +// } +// var ret []JackPotLog +// err := rpcCli.CallWithTimeout("JackPotLogSvc.GetJackPotLog", args, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Error("model.GetJackPotLog eror.", err) +// } +// return err, ret +//} diff --git a/model/jyb.go b/model/jyb.go new file mode 100644 index 0000000..7fc248e --- /dev/null +++ b/model/jyb.go @@ -0,0 +1,281 @@ +package model + +import ( + "errors" + "fmt" + "math/rand" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +// 礼包码 +var ( + chars = []rune{ + '4', '1', '7', '2', '3', '6', '5', '8', '9', + 'q', 'n', 'c', 'r', 't', 'm', 'a', 'd', 'e', 'h', 'j', 'l', 'f', 'b', 'g', + 'Q', 'N', 'C', 'R', 'T', 'M', 'A', 'D', 'E', 'H', 'J', 'L', 'F', 'B', 'G', + } + divider = 'i' // 分割标识(区分补位,应该是chars里面没出现的字符) + charLen = uint64(len(chars)) + charIndexMap = make(map[rune]int, len(chars)) // 初始化就完成写入 不存在并发读写问题 + Keystart int64 = 100000 // 单兑换码上限十万 + +) + +var ( + ErrJYBCode = errors.New("CodeExist") + ErrJYBRpc = errors.New("Rpcisnil") + ErrJYBPlCode = errors.New("PlayCodeExist") // 已经兑换过该礼包 + ErrJybISReceive = errors.New("jyb is receive") // 该兑换码已被使用 + ErrJybTsTimeErr = errors.New("jyb ts is not") // 该兑换码已过期 + ErrJybIsNotExist = errors.New("jyb is not exist") // 请输入正确的兑换码 +) + +func init() { + for i, char := range chars { + charIndexMap[char] = i + } +} + +type JybInfoAward struct { + Item []*Item // 道具 + Coin int64 // 金币 + Diamond int64 // 钻石 +} + +type JybInfos struct { + Jybs []*JybInfo +} + +type JybInfo struct { + JybId bson.ObjectId `bson:"_id"` // 礼包ID + Platform string //平台 + Name string // 礼包名称 + CodeType int32 // 礼包类型 1 通用 2 特殊 + StartTime int64 // 开始时间 Unix + EndTime int64 // 结束时间 + Content string // 礼包内容 + Max int32 // 总个数 + Receive int32 // 领取个数 + CodeStart int64 // Code生成起始 + Code map[string]int32 // 礼包码 + Award *JybInfoAward // 礼包内东西 + CreateTs int64 +} + +// JybKey 自增 +type JybKey struct { + KeyId bson.ObjectId `bson:"_id"` + Keyint int64 + Plakeyid string // keystart+Platform + GeCode map[string]string // 通用礼包 +} + +// 验证玩家是否领取礼包 可能存在map并发问题 +type JybUserInfo struct { + JybUserId bson.ObjectId `bson:"_id"` + SnId int32 // 玩家id + Platform string //平台 + JybInfos map[string]int32 //已领取礼包 web和world使用 注意线程安全 +} + +type GetJybInfoArgs struct { + Id string // 礼包ID + Plt string // 平台 + UseCode string // 礼包码 + SnId int32 // 玩家id + CodeType int32 +} + +type InitJybInfoArgs struct { + Plt string // 平台 +} + +type VerifyUpJybInfoArgs struct { + UseCode string // 礼包码 + CodeStart int64 // Code生成起始 + Plt string // 平台 + SnId int32 // 玩家id + CodeType int32 +} + +type CreateJyb struct { + *JybInfo + Codelen int32 + Num uint64 +} + +// NewJybUserInfo 初始化 +func NewJybKey(plt string) *JybKey { + jk := &JybKey{KeyId: bson.NewObjectId(), Keyint: Keystart, Plakeyid: fmt.Sprintf("%d_%s", Keystart, plt)} + return jk +} + +// sync.Map new(sync.Map) +// NewJybUserInfo 初始化 +func NewJybUserInfo(sid int32, plt string) *JybUserInfo { + jUserInfo := &JybUserInfo{JybUserId: bson.NewObjectId(), SnId: sid, Platform: plt, JybInfos: make(map[string]int32)} + return jUserInfo +} + +func NewJybInfo(plt, name, content, code string, startTs, endTs int64, max, codetype int32, award *JybInfoAward) *JybInfo { + jyb := &JybInfo{ + JybId: bson.NewObjectId(), + Platform: plt, + Name: name, + Content: content, + CodeType: codetype, + StartTime: startTs, + EndTime: endTs, + Max: max, + Receive: 0, + Code: make(map[string]int32), + Award: award, + CreateTs: time.Now().Unix(), + } + if codetype == 1 { + jyb.Code[code] = 1 + } + return jyb +} + +func NewJybCode(jyb *JybInfo, codelen int32, num uint64) { + cl := int(codelen) + num += uint64(jyb.CodeStart) + for i := uint64(jyb.CodeStart); i != num; i++ { + key := Id2Code(i, cl, true) + jyb.Code[key] = 1 + } +} + +func CreateJybInfo(args *CreateJyb) error { + if rpcCli == nil { + return ErrJYBRpc + } + ret := &JybInfo{} + err := rpcCli.CallWithTimeout("JybSvc.CreateJybItem", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("CreateJybInfo err:%v Id:%v ", err, args.JybId) + return err + } + //args.JybInfo = ret // 获取code + return nil +} + +func UpgradeJybUser(args *GetJybInfoArgs) *JybInfo { + if rpcCli == nil { + return nil + } + ret := &JybInfo{} + err := rpcCli.CallWithTimeout("JybUserSvc.UpgradeJybUser", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("UpgradeJybUser err:%v Id:%v ", err, args.UseCode) + return nil + } + return ret +} + +func VerifyUpJybInfo(args *VerifyUpJybInfoArgs) (*JybInfo, error) { + if rpcCli == nil { + return nil, ErrJYBRpc + } + ret := &JybInfo{} + err := rpcCli.CallWithTimeout("JybUserSvc.VerifyUpJybUser", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("VerifyUpJybUser err:%v Id:%v ", err, args.UseCode) + return nil, err + } + return ret, err +} + +func DelJybInfo(args *GetJybInfoArgs) error { + + if rpcCli == nil { + return nil + } + + ret := false + err := rpcCli.CallWithTimeout("JybSvc.DelJyb", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("DelJybInfo err:%v Id:%v ", err, args.Id) + } + + return err +} + +func InitJybItem(args *InitJybInfoArgs) *JybInfos { + + if rpcCli == nil { + return nil + } + + ret := &JybInfos{} + err := rpcCli.CallWithTimeout("JybSvc.InitJybItem", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("InitJybItem err:%v Id:%v ", err, args.Plt) + return nil + } + + return ret +} + +func reverseSlice(strSlice []rune) []rune { + sLen := len(strSlice) + for i := 0; i < sLen/2; i++ { + strSlice[i], strSlice[sLen-i-1] = strSlice[sLen-i-1], strSlice[i] + } + return strSlice +} + +// Id2Code 把id转为兑换码 +func Id2Code(id uint64, codeMixLength int, isRandomFix bool) string { + code := make([]rune, 0, codeMixLength) + for id/charLen > 0 { + code = append(code, chars[id%charLen]) + id /= charLen + } + + code = append(code, chars[id%charLen]) // 处理未除尽的余数 + // slice.ReverseSlice(code) + reverseSlice(code) + fixLen := codeMixLength - len(code) // 需要补码的长度 + if fixLen > 0 { + rand.Seed(time.Now().UnixNano()) + code = append(code, divider) + for i := 0; i < fixLen-1; i++ { + // 每次固定,如果需要变的话,后面补码的内容可以改变 + if isRandomFix { + code = append(code, chars[rand.Intn(int(charLen))]) + } else { + code = append(code, chars[i]) + } + } + } + return string(code) +} + +func Code2Id(code string) (uint64, error) { + if len(code) == 0 { + return 0, nil + } + var id uint64 = 0 + codeRuneList := []rune(code) + for i := range codeRuneList { + // 如果是补码标志直接退出 + if codeRuneList[i] == divider { + break + } + charIndex, ok := charIndexMap[codeRuneList[i]] + if !ok { + return 0, errors.New("code有误,解码失败") + } + if i > 0 { + id = id*charLen + uint64(charIndex) + } else { + id = uint64(charIndex) + } + } + return id, nil +} diff --git a/model/loginlog.go b/model/loginlog.go new file mode 100644 index 0000000..46d086b --- /dev/null +++ b/model/loginlog.go @@ -0,0 +1,124 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" +) + +var ( + LoginLogDBName = "log" + LoginLogCollName = "log_login" +) + +type ClientLoginInfo struct { + LoginType int32 + ApkVer int32 + ResVer int32 + InviterId int32 + PromoterTree int32 + Sid int64 + UserName string + PlatformTag string + Promoter string + HeadUrl string + DeviceOS string +} + +// LoginLog 登录登出记录 +type LoginLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台id + Channel string //渠道 + Promoter string //推广员 + Package string //包名 + PackageTag string //推广包标识 + InviterId int32 //邀请人id + PromoterTree int32 + SnId int32 + LogType int32 + ApkVer int32 + ResVer int32 + Tel string + IP string + City string + IsBind bool + TotalCoin int64 // 总余额 + GameId int // 玩家掉线时所在游戏 + Time time.Time + Ts int64 + LastGameID int // 玩家最后所在游戏id + + DeviceName string + AppVersion string + BuildVersion string + AppChannel string + DeviceOS string +} + +func NewLoginLog(snId, logType int32, tel, ip, platform, channel, promoter, packageId, city string, + clog *ClientLoginInfo, totalCoin int64, gameId, lastGameId int, + DeviceName, PackageName, AppVersion, BuildVersion, AppChannel string) *LoginLog { + now := time.Now() + cl := &LoginLog{LogId: bson.NewObjectId()} + cl.SnId = snId + cl.LogType = logType + cl.IP = ip + cl.Tel = tel + cl.City = city + cl.Time = now + cl.Ts = now.Unix() + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.PackageTag = packageId + if clog != nil { + cl.InviterId = clog.InviterId + cl.ApkVer = clog.ApkVer + cl.ResVer = clog.ResVer + cl.PackageTag = clog.PlatformTag + cl.DeviceOS = clog.DeviceOS + } + cl.TotalCoin = totalCoin + cl.GameId = gameId + cl.LastGameID = lastGameId + if tel != "" { + cl.IsBind = true + } else { + cl.IsBind = false + } + + cl.Package = PackageName + cl.DeviceName = DeviceName + cl.AppVersion = AppVersion + cl.BuildVersion = BuildVersion + cl.AppChannel = AppChannel + + return cl +} + +//func InsertLoginLogs(logs ...*LoginLog) (err error) { +// if rpcCli == nil { +// logger.Logger.Error("model.InsertLoginLogs rpcCli == nil") +// return +// } +// var ret bool +// err = rpcCli.CallWithTimeout("LoginsLogSvc.InsertLoginLogs", logs, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Warn("InsertLoginLogs error:", err) +// } +// return +//} +// +//func InsertSignleLoginLog(log *LoginLog) (err error) { +// if rpcCli == nil { +// logger.Logger.Error("model.InsertLoginLogs rpcCli == nil") +// return +// } +// var ret bool +// err = rpcCli.CallWithTimeout("LoginsLogSvc.InsertLoginLogs", log, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Warn("InsertLoginLogs error:", err) +// } +// return +//} diff --git a/model/lottery.go b/model/lottery.go new file mode 100644 index 0000000..f986c9e --- /dev/null +++ b/model/lottery.go @@ -0,0 +1,70 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "time" +) + +const ( + LOTTERY_LOG_MAX = 30 +) + +var ( + LotteryDBName = "user" + LotteryCollName = "user_lottery" +) + +// 奖池记录 +type GameLotteryLog struct { + Time int32 //时间 + Nick string //昵称 + Card []int32 //牌数据 + Kind int32 //牌型 + Coin int32 //获得奖金 +} + +type GameLottery struct { + Id int32 //游戏场次id + GameId int32 //游戏id + Value int64 //彩金数量 + Logs []*GameLotteryLog //彩金获得记录 +} + +type Lottery struct { + Id bson.ObjectId `bson:"_id"` + Dirty int32 `bson:"-"` + Platform string //平台编号 + Lotteries map[int32]*GameLottery //奖金池 + CreateTime time.Time //创建日期 + UpdateTime time.Time //最后更新日期 +} + +func NewLottery(platform string) *Lottery { + cl := &Lottery{ + Id: bson.NewObjectId(), + Dirty: 1, + Platform: platform, + CreateTime: time.Now(), + Lotteries: make(map[int32]*GameLottery), + } + return cl +} + +func GetAllLottery() (ret []Lottery, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + err = rpcCli.CallWithTimeout("LotterySvc.GetAllLottery", struct{}{}, &ret, time.Second*30) + return +} + +func UpsertLottery(item *Lottery) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + var ret bool + err = rpcCli.CallWithTimeout("LotterySvc.UpsertLottery", item, &ret, time.Second*30) + return +} diff --git a/model/lotterycfg.go b/model/lotterycfg.go new file mode 100644 index 0000000..75fedb3 --- /dev/null +++ b/model/lotterycfg.go @@ -0,0 +1,44 @@ +package model + +import "encoding/json" + +type LotteryConfiger interface { + HitRatio(card []int32, kind int32) int32 + GetTaxRatio() int32 +} + +type LotteryConfigParser func(cfg string) (LotteryConfiger, error) + +type KindOfCard struct { + Kind int32 //牌型 + Ratio int32 //奖池比例,万分比 +} + +// 彩金配置 +type LotteryConfig struct { + Type int32 //奖池类型 0:税收 + Ratio int32 //比例 百分比 + Koc []KindOfCard //特殊牌型获得奖池占比 +} + +func (this *LotteryConfig) GetTaxRatio() int32 { + return this.Ratio +} + +func (this *LotteryConfig) HitRatio(card []int32, kind int32) int32 { + //@testcode + //return 500 + //@testcode + for i := 0; i < len(this.Koc); i++ { + if this.Koc[i].Kind == kind { + return this.Koc[i].Ratio + } + } + return 0 +} + +func CommonLotteryConfigParser(cfg string) (LotteryConfiger, error) { + dncfg := &LotteryConfig{} + err := json.Unmarshal([]byte(cfg), dncfg) + return dncfg, err +} diff --git a/model/lotterylog.go b/model/lotterylog.go new file mode 100644 index 0000000..f79d465 --- /dev/null +++ b/model/lotterylog.go @@ -0,0 +1,103 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +var ( + LotteryLogDBName = "log" + LotteryLogCollName = "log_lottery" +) + +type LotteryLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台名称 + GameFreeId int32 + GameId int32 + SnId int32 + NickName string + IsRob bool + Cards []int32 + Kind int32 + Coin int32 //变化金额 + Pool int64 //变化前彩金池数量 + RecId string + Time time.Time +} + +func NewLotteryLog() *LotteryLog { + log := &LotteryLog{LogId: bson.NewObjectId()} + return log +} + +func NewLotteryLogEx(platform string, gamefreeid, gameid, snid int32, nick string, isRob bool, cards []int32, kind, coin int32, pool int64, recId string) *LotteryLog { + cl := NewLotteryLog() + cl.SnId = snid + cl.Platform = platform + cl.NickName = nick + cl.IsRob = isRob + cl.GameFreeId = gamefreeid + cl.GameId = gameid + cl.Cards = cards + cl.Kind = kind + cl.Coin = coin + cl.RecId = recId + cl.Pool = pool + cl.Time = time.Now() + return cl +} +func InsertLotteryLog(platform string, gamefreeid, gameid, snid int32, nick string, isRob bool, cards []int32, kind, coin int32, pool int64, recId string) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + cl := NewLotteryLogEx(platform, gamefreeid, gameid, snid, nick, isRob, cards, kind, coin, pool, recId) + return rpcCli.CallWithTimeout("LotteryLogSvc.InsertLotteryLog", cl, &ret, time.Second*30) +} + +func InsertLotteryLogs(logs ...*LotteryLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("LotteryLogSvc.InsertLotteryLogs", logs, &ret, time.Second*30) +} + +type RemoveLotteryLogArgs struct { + Plt string + Ts time.Time +} + +func RemoveLotteryLog(plt string, ts time.Time) (ret *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &RemoveLotteryLogArgs{ + Plt: plt, + Ts: ts, + } + err = rpcCli.CallWithTimeout("LotteryLogSvc.RemoveLotteryLog", args, &ret, time.Second*30) + return +} + +type GetLotteryLogBySnidAndLessTsArgs struct { + Plt string + Id int32 + Ts time.Time +} + +func GetLotteryLogBySnidAndLessTs(plt string, id int32, ts time.Time) (ret []LotteryLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetLotteryLogBySnidAndLessTsArgs{ + Plt: plt, + Id: id, + Ts: ts, + } + err = rpcCli.CallWithTimeout("LotteryLogSvc.GetLotteryLogBySnidAndLessTs", args, &ret, time.Second*30) + return +} diff --git a/model/match.go b/model/match.go new file mode 100644 index 0000000..5631401 --- /dev/null +++ b/model/match.go @@ -0,0 +1,77 @@ +package model + +import "net" + +const ( + MatchType_Unkonw int32 = iota + MatchType_Free + MatchType_Max +) + +const ( + MatchOp_Unknow int32 = iota + MatchOp_Continue //继续 + MatchOp_Break //中断 + MatchOp_Quit //退赛 + MatchOp_AddSignupFee //补充报名费 + MatchOp_Max +) + +// 比赛报名消耗类型 +const ( + MatchCostType_Free int32 = iota //免费次数 + MatchCostType_Ticket //报名券 + MatchCostType_Coin //金币 + MatchCostType_Max +) + +type MatchId int32 + +func MakeMatchId(matchType, matchRuleId, matchSceneType int32) MatchId { + return MatchId(matchType*10000 + matchRuleId*100 + matchSceneType) +} + +func (id MatchId) MatchType() int32 { + return int32(id) / 10000 +} + +func (id MatchId) MatchRuleId() int32 { + return (int32(id) % 10000) / 100 +} + +func (id MatchId) MatchSceneType() int32 { + return int32(id) % 100 +} + +type Match struct { + Id MatchId +} + +// 比赛成绩 +type MatchAchievement struct { + MatchId int32 //比赛ID + Coin int32 //累计获得金币数量 + Grade int32 //累计获得积分数量 + BestRank int32 //最高排名 + FinalTimes int32 //决赛次数 + GameTimes int32 //游戏次数 + ChampionTimes int32 //冠军次数 + Ts int32 //时间戳 + CreateTs int32 //首次创建记录时间戳 + Datas []int32 +} + +type PlayerMatchSignup struct { + SnId int32 //玩家id + CostType int32 //报名费类型 0:免费 1:入场券 2:金币 + CostValue int32 //消耗的值 + Ts int64 //报名时间戳 秒 + IsRob bool //是否是机器人 + IP net.IP //IP地址 +} + +// 比赛报名信息 +type MatchSignup struct { + MatchId int32 //比赛id + SignupData []*PlayerMatchSignup //报名费 +} diff --git a/model/matchlog.go b/model/matchlog.go new file mode 100644 index 0000000..0f47987 --- /dev/null +++ b/model/matchlog.go @@ -0,0 +1,113 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +const ( + MATCHPLAYER_FLAG_ISROB uint = iota //机器人 + MATCHPLAYER_FLAG_ISFIRST //首次参赛 + MATCHPLAYER_FLAG_ISNEWBIE //新人 + MATCHPLAYER_FLAG_ISQUIT //退赛 + MATCHPLAYER_FLAG_SIGNUP_USEFREE //免费报名 + MATCHPLAYER_FLAG_SIGNUP_USETICKET //使用报名券报名 + MATCHPLAYER_FLAG_SIGNUP_USECOIN //使用金币报名 +) + +// 比赛各阶段时间消耗 +type MatchProcessTimeSpend struct { + Name string //赛程名称 + MMType int32 //赛制类型 + Spend int32 //用时,单位:秒 +} + +// 比赛参赛人员信息 +type MatchPlayer struct { + SnId int32 //玩家id + Flag int32 //玩家类型,二进制 第1位:1是机器人,0玩家 第2位:1首次参加 0:非首次参加 第3位:1新人 0:老玩家 第4位:1退赛 0未退赛 第5位: 1免费报名 第6位: 1使用入场券报名 第7位: 1使用金币报名 + Spend int32 //报名消耗 + Gain int32 //获得奖励 + Rank int32 //名次 + WaitTime int32 //从报名到开始比赛的等待时间 单位:秒 + MatchTime int32 //比赛中用时 从开始比赛到比赛结束总的用时 单位:秒 +} + +// 比赛牌局记录 +type MatchGameLog struct { + GameLogId string //牌局id + Name string //赛程名称 + ProcessIdx int32 //赛程索引 + NumOfGame int32 //第几局 + SpendTime int32 //花费时间 + SnIds []int32 //参与玩家 +} + +// 比赛详情 +type MatchLog struct { + Id bson.ObjectId `bson:"_id"` + Platform string //平台编号 + MatchId int32 //比赛编号 + MatchName string //比赛名称 + GameFreeId int32 //游戏类型 + StartTime time.Time //开始时间 + EndTime time.Time //结束时间 + Players []*MatchPlayer //参赛人员数据 + TimeSpend []*MatchProcessTimeSpend //赛程用时 + GameLogs []*MatchGameLog //牌局记录 +} + +func (ml *MatchLog) AppendMatchProcess(name string, mmtype int32, spend int32) { + ml.TimeSpend = append(ml.TimeSpend, &MatchProcessTimeSpend{ + Name: name, + MMType: mmtype, + Spend: spend, + }) +} + +func (ml *MatchLog) AppendGameLog(name string, gamelogId string, processIdx, numOfGame, spendTime int32, snids []int32) { + ml.GameLogs = append(ml.GameLogs, &MatchGameLog{ + Name: name, + GameLogId: gamelogId, + ProcessIdx: processIdx, + NumOfGame: numOfGame, + SpendTime: spendTime, + SnIds: snids, + }) +} + +var ( + MatchLogDBName = "log" + MatchLogCollName = "log_matchlog" +) + +func NewMatchLog() *MatchLog { + return &MatchLog{Id: bson.NewObjectId()} +} + +func InsertMatchLogs(logs ...*MatchLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("MatchLogSvc.InsertMatchLogs", logs, &ret, time.Second*30) +} + +type RemoveMatchLogsArgs struct { + Plt string + Ts time.Time +} + +func RemoveMatchLogs(plt string, ts time.Time) (ret *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &RemoveMatchLogsArgs{ + Plt: plt, + Ts: ts, + } + rpcCli.CallWithTimeout("MatchLogSvc.RemoveMatchLogs", args, &ret, time.Second*30) + return +} diff --git a/model/matchrec.go b/model/matchrec.go new file mode 100644 index 0000000..e5477cd --- /dev/null +++ b/model/matchrec.go @@ -0,0 +1,74 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +type MatchRec struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + MatchId int32 + MatchName string + GameFreeId int32 + Rank int32 + SignupType int32 + SignupCnt int32 + Prizes int32 + Grade int32 + Ts time.Time +} + +var ( + MatchRecDBName = "log" + MatchRecCollName = "log_matchrec" +) + +func NewMatchRec() *MatchRec { + return &MatchRec{Id: bson.NewObjectId()} +} + +func InsertMatchRecs(logs ...*MatchRec) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + return rpcCli.CallWithTimeout("MatchRecSvc.InsertMatchRecs", logs, &ret, time.Second*30) +} + +type FetchMatchRecsArgs struct { + Plt string + SnId int32 +} + +func FetchMatchRecs(plt string, snid int32) (recs []MatchRec, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &FetchMatchRecsArgs{ + Plt: plt, + SnId: snid, + } + err = rpcCli.CallWithTimeout("MatchRecSvc.FetchMatchRecs", args, &recs, time.Second*30) + return +} + +type RemoveMatchRecsArgs struct { + Plt string + Ts time.Time +} + +func RemoveMatchRecs(plt string, ts time.Time) (ret *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &RemoveMatchRecsArgs{ + Plt: plt, + Ts: ts, + } + rpcCli.CallWithTimeout("MatchRecSvc.RemoveMatchRecs", args, &ret, time.Second*30) + return +} diff --git a/model/matchseason.go b/model/matchseason.go new file mode 100644 index 0000000..dd38aea --- /dev/null +++ b/model/matchseason.go @@ -0,0 +1,215 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "time" +) + +// 段位信息 +type MatchSeason struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + Name string + SeasonId int32 //赛季id + Lv int32 //段位 + LastLv int32 //上赛季段位 + IsAward bool //上赛季是否领奖 + AwardTs int64 //领奖时间 + UpdateTs int64 +} + +type MatchSeasonRet struct { + Ms *MatchSeason +} + +type MatchSeasonRets struct { + Mss []*MatchSeason +} + +type MatchSeasonByKey struct { + Platform string + SnId int32 +} + +func NewMatchSeason(platform string, snid int32, name string, sid, lv int32) *MatchSeason { + ms := &MatchSeason{Id: bson.NewObjectId()} + ms.Platform = platform + ms.SnId = snid + ms.Name = name + ms.SeasonId = sid + ms.Lv = lv + ms.LastLv = 0 + ms.IsAward = false + ms.AwardTs = 0 + ms.UpdateTs = time.Now().Unix() + return ms +} + +func UpsertMatchSeason(MatchSeason *MatchSeason) *MatchSeason { + if rpcCli == nil { + logger.Logger.Error("model.UpsertMatchSeason rpcCli == nil") + return nil + } + + ret := &MatchSeasonRet{} + err := rpcCli.CallWithTimeout("MatchSeasonSvc.UpsertMatchSeason", MatchSeason, ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertMatchSeason error:", err) + return nil + } + return ret.Ms +} + +func QueryMatchSeasonBySnid(platform string, snid int32) (MatchSeason *MatchSeason, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryMatchSeasonBySnid rpcCli == nil") + return + } + args := &MatchSeasonByKey{ + Platform: platform, + SnId: snid, + } + var ret *MatchSeasonRet + err = rpcCli.CallWithTimeout("MatchSeasonSvc.QueryMatchSeasonByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryMatchSeasonBySnid error:", err) + } + if ret != nil { + MatchSeason = ret.Ms + } + return +} + +func QueryMatchSeason(platform string) (MatchSeasons []*MatchSeason, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryMatchSeason rpcCli == nil") + return + } + var ret *MatchSeasonRets + err = rpcCli.CallWithTimeout("MatchSeasonSvc.QueryMatchSeason", platform, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryMatchSeason error:", err) + } + if ret != nil { + MatchSeasons = ret.Mss + } + return +} + +// 赛季信息 +type MatchSeasonId struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SeasonId int32 //赛季id + StartStamp int64 //开始时间戳 + EndStamp int64 //结束时间戳 + UpdateTs int64 //更新时间戳 +} + +type MatchSeasonIdRet struct { + MsId *MatchSeasonId +} + +type MatchSeasonIdByKey struct { + Platform string +} + +func NewMatchSeasonId(platform string, sid int32, sstamp, estamp int64) *MatchSeasonId { + ms := &MatchSeasonId{Id: bson.NewObjectId()} + ms.Platform = platform + ms.SeasonId = sid + ms.StartStamp = sstamp + ms.EndStamp = estamp + ms.UpdateTs = time.Now().Unix() + return ms +} + +func UpsertMatchSeasonId(MatchSeasonId *MatchSeasonId) *MatchSeasonId { + if rpcCli == nil { + logger.Logger.Error("model.UpsertMatchSeasonId rpcCli == nil") + return nil + } + + ret := &MatchSeasonIdRet{} + err := rpcCli.CallWithTimeout("MatchSeasonIdSvc.UpsertMatchSeasonId", MatchSeasonId, ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertMatchSeasonId error:", err) + return nil + } + return ret.MsId +} + +func QueryMatchSeasonId(platform string) (MatchSeasonId *MatchSeasonId, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryMatchSeasonId rpcCli == nil") + return + } + args := &MatchSeasonIdByKey{ + Platform: platform, + } + var ret *MatchSeasonIdRet + err = rpcCli.CallWithTimeout("MatchSeasonIdSvc.QueryMatchSeasonId", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryMatchSeasonId error:", err) + } + if ret != nil { + MatchSeasonId = ret.MsId + } + return +} + +// 排行榜 +type MatchSeasonRank struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + Name string + Lv int32 //段位 + UpdateTs int64 +} + +type MatchSeasonRankRet struct { + MsRanks []*MatchSeasonRank +} + +type MatchSeasonRankByKey struct { + Platform string + MsRanks []*MatchSeasonRank +} + +func UpsertMatchSeasonRank(platform string, MatchSeasonRanks []*MatchSeasonRank) { + if rpcCli == nil { + logger.Logger.Error("model.UpsertMatchSeasonRank rpcCli == nil") + return + } + args := &MatchSeasonRankByKey{ + Platform: platform, + MsRanks: MatchSeasonRanks, + } + err := rpcCli.CallWithTimeout("MatchSeasonRankSvc.UpsertMatchSeasonRank", args, nil, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertMatchSeasonRank error:", err) + return + } +} + +func QueryMatchSeasonRank(platform string) (MatchSeasonRanks []*MatchSeasonRank, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryMatchSeasonRank rpcCli == nil") + return + } + args := &MatchSeasonRankByKey{ + Platform: platform, + } + var ret *MatchSeasonRankRet + err = rpcCli.CallWithTimeout("MatchSeasonRankSvc.QueryMatchSeasonRank", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("QueryMatchSeasonRank error:", err) + } + if ret != nil { + MatchSeasonRanks = ret.MsRanks + } + return +} diff --git a/model/message.go b/model/message.go new file mode 100644 index 0000000..d6ffea6 --- /dev/null +++ b/model/message.go @@ -0,0 +1,353 @@ +package model + +import ( + "errors" + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +const ( + MSGSTATE_UNREAD int32 = iota + MSGSTATE_READED + MSGSTATE_REMOVEED +) + +const ( + MSGATTACHSTATE_DEFAULT int32 = iota + MSGATTACHSTATE_GOT +) + +const ( + MSGTYPE_MAIL int32 = iota + MSGTYPE_GONGGAO + MSGTYPE_INVITECODE + MSGTYPE_GIFT + MSGTYPE_GOLDCOMERANK + MSGTYPE_YEB // 余额宝 + MSGTYPE_ClubGet // 俱乐部会长给会员 会员得到 + MSGTYPE_ClubPump // 俱乐部会长给会员 会长获得抽水 + MSGTYPE_IOSINSTALLSTABLE //IOS安装稳定版 + MSGTYPE_REBATE //奖励流水返利 + MSGTYPE_RANDCOIN //红包雨 + MSGTYPE_MATCH_SIGNUPFEE //比赛报名费 + MSGTYPE_MATCH_TICKETREWARD //比赛报名券奖励 + MSGTYPE_MATCH_SHOPEXCHANGE //积分商城兑换 + MSGTYPE_MATCH_SHOPERETURN //积分商城兑换退还 + MSGTYPE_ITEM //获取道具 + MSGTYPE_RANK_REWARD //排行榜奖励 +) + +const ( + HallAll int64 = 1 << iota //所有大厅都显示 + HallMain //主大厅显示 + HallTienlen //len大厅显示 + HallFish //fish大厅显示 +) + +const MSG_MAX_COUNT int = 100 //玩家最多保存100封邮件 ,调整大一点 + +var ( + MessageDBName = "user" + MessageCollName = "user_msg" +) + +type Message struct { + Id bson.ObjectId `bson:"_id"` + Pid string //原始消息的ID + MType int32 //消息类型 + Title string //标题 + Content string //内容 + Oper int32 //0.系统 1.玩家 + SrcId int32 //发送人ID + SrcName string //发送人名字 + SnId int32 //目标人ID + Coin int64 //携带金币数量 + Ticket int64 //比赛报名券 + Grade int64 //积分 + Diamond int64 //钻石 + State int32 //当前消息的状态 + CreatTs int64 //创建时间戳 + AttachState int32 //附件状态 + GiftId string // + Params []int32 //额外参数 + Platform string //平台信息 + ShowId int64 //区分主子游戏大厅 +} + +func NewMessage(pid string, srcId int32, srcName string, snid, mType int32, title, content string, coin, diamond int64, + state int32, addTime int64, attachState int32, giftId string, params []int32, platform string, showId int64) *Message { + if srcName == "" { + srcName = "{\"zh\":\"系统\",\"vi\":\"GM\",\"en\":\"GM\",\"kh\":\"GM\"}" + } + msg := &Message{ + Id: bson.NewObjectId(), + Pid: pid, + MType: mType, + Title: title, + Content: content, + SrcId: srcId, + SrcName: srcName, + SnId: snid, + Coin: coin, + State: state, + CreatTs: addTime, + AttachState: attachState, + GiftId: giftId, + Params: params, + Platform: platform, + Diamond: diamond, + ShowId: showId, + } + if msg.Pid == "" { + msg.Pid = msg.Id.Hex() + } + return msg +} +func NewMessageByPlayer(pid string, oper, srcId int32, srcName string, snid, mType int32, title, content string, coin, diamond int64, + state int32, addTime int64, attachState int32, giftId string, params []int32, platform string, showId int64) *Message { + + msg := &Message{ + Id: bson.NewObjectId(), + Pid: pid, + MType: mType, + Title: title, + Content: content, + Oper: oper, + SrcId: srcId, + SrcName: srcName, + SnId: snid, + Coin: coin, + State: state, + CreatTs: addTime, + AttachState: attachState, + GiftId: giftId, + Params: params, + Platform: platform, + Diamond: diamond, + ShowId: showId, + } + if msg.Pid == "" { + msg.Pid = msg.Id.Hex() + } + return msg +} + +type InsertMsgArg struct { + Platform string + Msgs []Message +} + +func InsertMessage(plt string, msgs ...*Message) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + docs := make([]Message, 0, len(msgs)) + for _, msg := range msgs { + docs = append(docs, *msg) + } + if len(docs) == 0 { + return errors.New("no data") + } + args := &InsertMsgArg{Platform: plt, Msgs: docs} + var ret bool + err = rpcCli.CallWithTimeout("MessageSvc.InsertMessage", args, &ret, time.Second*30) + if err != nil { + return err + } + if !ret { + return errors.New("InsertMessage error") + } + return nil +} + +type GetMessageArgs struct { + Plt string + SnId int32 +} + +func GetNotDelMessage(plt string, snid int32) (ret []Message, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetMessageArgs{Plt: plt, SnId: snid} + err = rpcCli.CallWithTimeout("MessageSvc.GetNotDelMessage", args, &ret, time.Second*30) + if err != nil { + return nil, err + } + return ret, nil +} + +func GetMessage(plt string, snid int32) (ret []Message, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetMessageArgs{Plt: plt, SnId: snid} + err = rpcCli.CallWithTimeout("MessageSvc.GetMessage", args, &ret, time.Second*30) + if err != nil { + return nil, err + } + return ret, nil +} + +//func GetMessageByState(snid, state int32) (ret []Message, err error) { +// err = MessageCollection().Find(bson.M{"snid": snid, "state": state}).All(&ret) +// return +//} +//func GetMessageByNotState(snid, state int32) (ret []Message, err error) { +// err = MessageCollection().Find(bson.M{"snid": snid, "state": bson.M{"$ne": state}}).All(&ret) +// return +//} + +type GetMsgArg struct { + Platform string + IdStr string +} + +func GetMessageById(IdStr string, plt string) (msg *Message, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetMsgArg{Platform: plt, IdStr: IdStr} + err = rpcCli.CallWithTimeout("MessageSvc.GetMessageById", args, &msg, time.Second*30) + if err != nil { + return nil, err + } + return msg, nil +} + +func GetSubscribeMessage(plt string) (msg []Message, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + err = rpcCli.CallWithTimeout("MessageSvc.GetSubscribeMessage", plt, &msg, time.Second*30) + if err != nil { + return nil, err + } + return msg, nil +} + +type MsgArgs struct { + Platform string + StartTs, EndTs int64 + ToIndex, DestSnId, MsgType, FromIndex int +} +type RetMsg struct { + Msg []Message + Count int +} + +func GetMessageInRangeTsLimitByRange(mas *MsgArgs) (ret *RetMsg, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + err = rpcCli.CallWithTimeout("MessageSvc.GetMessageInRangeTsLimitByRange", mas, &ret, time.Second*30) + if err != nil { + return nil, err + } + return ret, nil +} + +type ReadMsgArgs struct { + Platform string + Id bson.ObjectId +} + +func ReadMessage(id bson.ObjectId, plt string) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &ReadMsgArgs{ + Platform: plt, + Id: id, + } + var ret bool + err = rpcCli.CallWithTimeout("MessageSvc.ReadMessage", args, &ret, time.Second*30) + return err +} + +type DelMsgArgs struct { + Platform string + Id bson.ObjectId + Del int32 // 默认0 1为假删 +} + +type DelAllMsgArgs struct { + Platform string + Ids []bson.ObjectId + Del int32 // 默认0 1为假删 +} + +func DelMessage(args *DelMsgArgs) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + var ret bool + err = rpcCli.CallWithTimeout("MessageSvc.DelMessage", args, &ret, time.Second*30) + return err +} + +func DelAllMessage(args *DelAllMsgArgs) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + var ret bool + err = rpcCli.CallWithTimeout("MessageSvc.DelAllMessage", args, &ret, time.Second*30) + if !ret { + return err + } + return nil +} + +type AttachMsgArgs struct { + Platform string + Id bson.ObjectId + Ts int64 +} + +func GetMessageAttach(id bson.ObjectId, plt string) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &AttachMsgArgs{ + Platform: plt, + Id: id, + } + var ret bool + err = rpcCli.CallWithTimeout("MessageSvc.GetMessageAttach", args, &ret, time.Second*30) + return err +} + +type AttachMsgsArgs struct { + Platform string + Ids []string +} + +func GetMessageAttachs(ids []string, plt string) (*[]string, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &AttachMsgsArgs{ + Platform: plt, + Ids: ids, + } + var ret []string + err := rpcCli.CallWithTimeout("MessageSvc.GetMessageAttachs", args, &ret, time.Second*30) + return &ret, err +} + +func RemoveMessages(platform string, ts int64) (ret *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &AttachMsgArgs{ + Platform: platform, + Ts: ts, + } + err = rpcCli.CallWithTimeout("MessageSvc.RemoveMessages", args, &ret, time.Second*30) + return +} diff --git a/model/monitor.go b/model/monitor.go new file mode 100644 index 0000000..1085f9c --- /dev/null +++ b/model/monitor.go @@ -0,0 +1,106 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + MonitorDBName = "monitor" + MonitorPrefixName = "m" +) + +type MonitorData struct { + LogId bson.ObjectId `bson:"_id"` + SrvId int32 //服务器id + SrvType int32 //服务器类型 + Key string //自定义key + Time time.Time //时间戳 + Data interface{} //数据体 +} + +func NewMonitorData(srvid, srvtype int32, key string, data interface{}) *MonitorData { + log := &MonitorData{ + LogId: bson.NewObjectId(), + SrvId: srvid, + SrvType: srvtype, + Key: key, + Data: data, + Time: time.Now(), + } + return log +} + +type MonitorDataArg struct { + Name string + Log *MonitorData +} + +func InsertMonitorData(name string, log *MonitorData) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertMonitorData rpcCli == nil") + return + } + args := &MonitorDataArg{ + Name: name, + Log: log, + } + var ret bool + err = rpcCli.CallWithTimeout("MonitorDataSvc.InsertMonitorData", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertMonitorData error:", err) + } + return +} + +func UpsertMonitorData(name string, log *MonitorData) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.UpsertMonitorData rpcCli == nil") + return + } + args := &MonitorDataArg{ + Name: name, + Log: log, + } + var ret bool + err = rpcCli.CallWithTimeout("MonitorDataSvc.UpsertMonitorData", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpsertMonitorData error:", err) + } + return +} + +func RemoveMonitorData(t time.Time) (chged []*mgo.ChangeInfo, err error) { + if rpcCli == nil { + logger.Logger.Error("model.RemoveMonitorData rpcCli == nil") + return + } + err = rpcCli.CallWithTimeout("MonitorDataSvc.RemoveMonitorData", t, &chged, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveMonitorData error:", err) + } + return +} + +type PlayerOLStats struct { + PlatformStats map[string]*PlayerStats + RobotStats PlayerStats +} + +type PlayerStats struct { + InGameCnt map[int32]map[int32]int32 + InHallCnt int32 +} + +type APITransactStats struct { + RunTimes int64 //执行次数 + TotalRuningTime int64 //总执行时间 + MaxRuningTime int64 //最长执行时间 + TotalBodyLen int64 //返回体总长度 + MaxBodyLen int //最长返回体长度 + SuccessTimes int64 //执行成功次数 + FailedTimes int64 //执行失败次数 +} diff --git a/model/monthlyprofitpool.go b/model/monthlyprofitpool.go new file mode 100644 index 0000000..8dc1ee8 --- /dev/null +++ b/model/monthlyprofitpool.go @@ -0,0 +1,65 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +var ( + MonthlyProfitPoolDBName = "log" + MonthlyProfitPoolCollName = "log_monthlyprofitpool" +) + +type MonthlyProfitPool struct { + LogId bson.ObjectId `bson:"_id"` //记录ID + ServerId int32 //服务器id + GroupId int32 //组id + Platform string //平台id + GameFreeid int32 //游戏类型房间号 + Coin int64 //水池金币 + Time time.Time //记录时间 +} + +func NewMonthlyProfitPool() *MonthlyProfitPool { + log := &MonthlyProfitPool{LogId: bson.NewObjectId()} + return log +} + +func NewMonthlyProfitPoolEx(serverid, gamefreeid, GroupId int32, platform string, coin int64) *MonthlyProfitPool { + cl := NewMonthlyProfitPool() + cl.ServerId = serverid + cl.GameFreeid = gamefreeid + cl.GroupId = GroupId + cl.Coin = coin + cl.Platform = platform + cl.Time = time.Now() + return cl +} + +func InsertMonthlyProfitPool(logs ...*MonthlyProfitPool) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err = rpcCli.CallWithTimeout("MonthlyProfitPoolSvc.InsertMonthlyProfitPool", logs, &ret, time.Second*30) + return +} + +func InsertSignleMonthlyProfitPool(log *MonthlyProfitPool) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err = rpcCli.CallWithTimeout("MonthlyProfitPoolSvc.InsertSignleMonthlyProfitPool", log, &ret, time.Second*30) + return +} + +func RemoveMonthlyProfitPool(ts time.Time) (ret *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + err = rpcCli.CallWithTimeout("MonthlyProfitPoolSvc.RemoveMonthlyProfitPool", ts, &ret, time.Second*30) + return +} diff --git a/model/normalparam.go b/model/normalparam.go new file mode 100644 index 0000000..556cb1e --- /dev/null +++ b/model/normalparam.go @@ -0,0 +1,179 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +type NormalParam struct { + // + LHMin int // = 6000 //龙虎最小几率 + LHMax int // = 8500 //龙虎最大几率 + FishRate int // = 10 //捕鱼流水比例 + FishLuckRate int // = 10 //捕鱼积分流水比例 + PoolMaxOutRate int //=0 //最大出分参考比例 + MaxAddBase int //=10 //最大强化概率倍数 + IsCloseAddRtp int //=1 //是否关闭附加概率 + NormalAddBase int //=5 //正常强化概率倍数 + NewManRate float64 //=0.5 //是否属于新人 + AttrWeight []float64 //属性权重 押注权重 历史流水权重 今日流水权重 新手权重 + WinWeight []float64 //胜率参考权重 历史权重,现在权重 + BetWeight []float64 //押注参考权重 历史权重,现在权重 + WinMaxRate float64 //最大胜率 + DLAddRateBase float64 //= 1.5//龙虎基数 + BetMaxBase float64 //平均押注额的倍率 + BetCutoffRate float64 //押注折算比例 + VolatilityMaxRate float64 //最大波动率调整 + VolatilityGameNum float64 //波动率调整局数 + AddRateNum float64 //增加倍数 + BullCardTypeWin []int //属性权重 出现赢的概率 + BullCardTypeWin2 []int //属性权重 出现赢的概率 + RobotRandomTimeMin int //机器人随机时间 + RobotRandomTimeMax int //机器人随机时间 + RollCoinNeedMin []int64 //老虎机类需要最小的流水值 + RollCoinMinLine int32 //不满足流水,最小的线数比例 + IsRollCoinClose int32 //不满足流水,是否关闭 + HBChangeBankCardRate int //百人牛牛庄换牌概率 + HBChangeBankCardLevel int32 //百人牛牛庄换牌增减值 + HZJHChangeBankCardRate int //百人金华庄换牌概率 + LHChangeCardRate int //龙虎杀牌换牌概率 + RobotVipChangeRate int //vip调整概率 + FishBWAddNum int //捕鱼黑白名单炮弹计数 + +} + +var NormalParamPath = "../data/normalparam.json" +var NormalParamData = &NormalParam{} + +func InitNormalParam() { + buf, err := os.ReadFile(NormalParamPath) + if err != nil { + logger.Logger.Warn("InitNormalParam os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, NormalParamData) + if err != nil { + logger.Logger.Warn("InitNormalParam json.Unmarshal error ->", err) + } + + if NormalParamData.LHMin == 0 { + NormalParamData.LHMin = 6000 + } + + if NormalParamData.LHMax == 0 { + NormalParamData.LHMax = 8500 + } + + if NormalParamData.FishRate == 0 { + NormalParamData.FishRate = 5 + } + + if NormalParamData.FishLuckRate == 0 { + NormalParamData.FishLuckRate = 10 + } + + if NormalParamData.MaxAddBase == 0 { + NormalParamData.MaxAddBase = 10 + } + + if NormalParamData.NormalAddBase == 0 { + NormalParamData.NormalAddBase = 5 + } + + if len(NormalParamData.AttrWeight) == 0 { + NormalParamData.AttrWeight = []float64{0.2, 0.2, 0.5, 0.1} + } + + if len(NormalParamData.WinWeight) == 0 { + NormalParamData.WinWeight = []float64{0.3, 0.7} + } + if NormalParamData.WinMaxRate <= 1 { + NormalParamData.WinMaxRate = 4 + } + + if NormalParamData.DLAddRateBase <= 0.1 { + NormalParamData.DLAddRateBase = 1.8 + } + + if NormalParamData.NewManRate <= 0.0001 { + NormalParamData.NewManRate = 0.1 + } + + if len(NormalParamData.BetWeight) == 0 { + NormalParamData.BetWeight = []float64{0.6, 0.4} + } + + if NormalParamData.BetMaxBase <= 0.0001 { + NormalParamData.BetMaxBase = 30.0 + } + + if NormalParamData.BetCutoffRate <= 0.0001 { + NormalParamData.BetCutoffRate = 0.3 + } + + if NormalParamData.VolatilityMaxRate <= 0.0001 { + NormalParamData.VolatilityMaxRate = 0.5 + } + + if NormalParamData.VolatilityGameNum <= 0.0001 { + NormalParamData.VolatilityGameNum = 800.0 + } + + if NormalParamData.AddRateNum <= 0.0001 { + NormalParamData.AddRateNum = 2.2 + } + + if NormalParamData.RobotRandomTimeMin == 0 { + NormalParamData.RobotRandomTimeMin = 15 + } + + if NormalParamData.RobotRandomTimeMax == 0 { + NormalParamData.RobotRandomTimeMax = 40 + } + + if NormalParamData.RollCoinMinLine == 0 { + NormalParamData.RollCoinMinLine = 30 + } + + if NormalParamData.RollCoinMinLine > 100 { + NormalParamData.RollCoinMinLine = 100 + } + + if len(NormalParamData.RollCoinNeedMin) == 0 { + NormalParamData.RollCoinNeedMin = []int64{30000, 100000, 300000, 1000000} + } + + if len(NormalParamData.BullCardTypeWin) == 0 { + NormalParamData.BullCardTypeWin = []int{1300, 2500, 3000, 2500, 3000} + } + + if len(NormalParamData.BullCardTypeWin2) == 0 { + NormalParamData.BullCardTypeWin2 = []int{1300, 2500, 3000, 2500, 3000} + } + + if NormalParamData.HBChangeBankCardRate == 0 { + NormalParamData.HBChangeBankCardRate = 500 + } + + if NormalParamData.HBChangeBankCardLevel == 0 { + NormalParamData.HBChangeBankCardLevel = 1 + } + + if NormalParamData.HZJHChangeBankCardRate == 0 { + NormalParamData.HZJHChangeBankCardRate = 300 + } + + if NormalParamData.RobotVipChangeRate == 0 { + NormalParamData.RobotVipChangeRate = 2000 + } + + if NormalParamData.LHChangeCardRate == 0 { + NormalParamData.LHChangeCardRate = 300 + } + + if NormalParamData.FishBWAddNum == 0 { + NormalParamData.FishBWAddNum = 20 + } + +} diff --git a/model/onlinelog.go b/model/onlinelog.go new file mode 100644 index 0000000..6b4df8a --- /dev/null +++ b/model/onlinelog.go @@ -0,0 +1,45 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/common" +) + +var ( + OnlineLogDBName = "log" + OnlineLogCollName = "log_online" +) + +// OnlineLog 登录登出记录 +type OnlineLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台id + SnId int32 + OnlineType int32 // 上线类型,登录,重连 + OnlineTs int64 // 在线时间 + OfflineType int32 // 离线类型,断线,登出 + OfflineTs int64 // 离线时间 +} + +func NewOnlineLog(id bson.ObjectId, snId, logType int32, platform string) *OnlineLog { + now := time.Now() + cl := &OnlineLog{} + if id == "" { + cl.LogId = bson.NewObjectId() + } else { + cl.LogId = id + } + cl.Platform = platform + cl.SnId = snId + switch logType { + case common.LoginLogTypeLogin, common.LoginLogTypeRehold: + cl.OnlineType = logType + cl.OnlineTs = now.Unix() + case common.LoginLogTypeDrop, common.LoginLogTypeLogout: + cl.OfflineType = logType + cl.OfflineTs = now.Unix() + } + return cl +} diff --git a/model/paycoinlog.go b/model/paycoinlog.go new file mode 100644 index 0000000..cbca0e7 --- /dev/null +++ b/model/paycoinlog.go @@ -0,0 +1,194 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" +) + +var ( + PayCoinLogDBName = "user" + PayCoinLogCollName = "user_coinlog" +) + +const ( + PayCoinLogType_Coin int32 = iota //金币对账日志 + PayCoinLogType_SafeBoxCoin //保险箱对账日志 + PayCoinLogType_Club //俱乐部对账日志 + PayCoinLogType_Ticket //比赛入场券 +) + +type PayCoinLog struct { + LogId bson.ObjectId `bson:"_id"` + BillNo int64 //账单ID + SnId int32 //用户ID + Coin int64 //账单金币额 + CoinEx int64 //额外赠送金币 + Inside int32 // + TimeStamp int64 //生效时间戳 + Time time.Time //账单时间 + Ver int32 //账单版本号 + Oper string //操作人 + LogType int32 //日志类型(金币或者保险箱) + Status int32 //状态 0 默认 1超时 +} + +func NewPayCoinLog(billNo int64, snid int32, coin int64, inside int32, oper string, logType int32, coinEx int64) *PayCoinLog { + tNow := time.Now() + log := &PayCoinLog{ + LogId: bson.NewObjectId(), + BillNo: billNo, + SnId: snid, + Coin: coin, + CoinEx: coinEx, + Inside: inside, + Ver: VER_PLAYER_MAX - 1, + TimeStamp: tNow.UnixNano(), + Time: tNow, + Oper: oper, + LogType: logType, + Status: 0, + } + return log +} + +type InsertPayCoinLogArgs struct { + Plt string + Logs []*PayCoinLog +} + +func InsertPayCoinLogs(plt string, logs ...*PayCoinLog) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &InsertPayCoinLogArgs{Plt: plt, Logs: logs} + var ret bool + err = rpcCli.CallWithTimeout("PayCoinLogSvc.InsertPayCoinLogs", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +func InsertPayCoinLog(plt string, log *PayCoinLog) error { + return InsertPayCoinLogs(plt, log) +} + +type RemovePayCoinLogArgs struct { + Plt string + Id bson.ObjectId +} + +func RemovePayCoinLog(plt string, id bson.ObjectId) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &RemovePayCoinLogArgs{Plt: plt, Id: id} + var ret bool + err = rpcCli.CallWithTimeout("PayCoinLogSvc.RemovePayCoinLog", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +type UpdatePayCoinLogStatusArgs struct { + Plt string + Id bson.ObjectId + Status int32 +} + +func UpdatePayCoinLogStatus(plt string, id bson.ObjectId, status int32) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &UpdatePayCoinLogStatusArgs{Plt: plt, Id: id, Status: status} + var ret bool + err = rpcCli.CallWithTimeout("PayCoinLogSvc.UpdatePayCoinLogStatus", args, &ret, time.Second*30) + if err != nil { + return err + } + return nil +} + +type GetPayCoinLogArgs struct { + Plt string + SnId int32 + Cond int64 +} + +func GetAllPayCoinLog(plt string, snid int32, ts int64) (ret []PayCoinLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetPayCoinLogArgs{Plt: plt, SnId: snid, Cond: ts} + err = rpcCli.CallWithTimeout("PayCoinLogSvc.GetAllPayCoinLog", args, &ret, time.Second*30) + if err != nil { + return + } + + return +} + +func GetAllPaySafeBoxCoinLog(plt string, snid int32, ts int64) (ret []PayCoinLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetPayCoinLogArgs{Plt: plt, SnId: snid, Cond: ts} + err = rpcCli.CallWithTimeout("PayCoinLogSvc.GetAllPaySafeBoxCoinLog", args, &ret, time.Second*30) + if err != nil { + return + } + return +} + +func GetAllPayClubCoinLog(plt string, snid int32, ts int64) (ret []PayCoinLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetPayCoinLogArgs{Plt: plt, SnId: snid, Cond: ts} + err = rpcCli.CallWithTimeout("PayCoinLogSvc.GetAllPayClubCoinLog", args, &ret, time.Second*30) + if err != nil { + return + } + return +} + +func GetAllPayTicketCoinLog(plt string, snid int32, ts int64) (ret []PayCoinLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetPayCoinLogArgs{Plt: plt, SnId: snid, Cond: ts} + err = rpcCli.CallWithTimeout("PayCoinLogSvc.GetAllPayTicketCoinLog", args, &ret, time.Second*30) + if err != nil { + return + } + return +} + +type GetPayCoinLogByBillNoArgs struct { + Plt string + SnId int32 + BillNo int64 +} + +func GetPayCoinLogByBillNo(plt string, snid int32, billNo int64) (log *PayCoinLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetPayCoinLogArgs{Plt: plt, SnId: snid, Cond: billNo} + err = rpcCli.CallWithTimeout("PayCoinLogSvc.GetPayCoinLogByBillNo", args, &log, time.Second*30) + if err != nil { + return + } + + return +} diff --git a/model/player.go b/model/player.go new file mode 100644 index 0000000..0e44609 --- /dev/null +++ b/model/player.go @@ -0,0 +1,2908 @@ +package model + +import ( + "bytes" + "crypto/md5" + "encoding/gob" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "hash/crc32" + "io" + "math/rand" + "os" + "strconv" + "strings" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/protocol/webapi" +) + +const ( + VER_PLAYER_DEFAULT int32 = iota //初始版本 + VER_PLAYER_MAX +) + +const ( + PLAYER_FLAGS_PRIVILEGE int64 = 1 << iota + PLAYER_FLAGS_FIRSTGAME //首次游戏 + PLAYER_FLAGS_FIRSTBINDTEL //首次绑定账号 + PLAYER_FLAGS_CANREBATE //是否能够返利 +) + +const ( + DEFAULT_PLAYER_SAFEBOX_PWD = "" //保险箱默认密码 +) + +const ( + MAX_RANK_COUNT = 50 +) + +type PlayerParams struct { + Platform int32 `json:"platform"` //0:web 1:ios 2:android + Ip string `json:"ip"` //ip地址 + City string `json:"city"` //所在城市 + Logininmodel string `json:"logininmodel"` //"app" 或者 "web" + Name string `json:"name"` //微信昵称 + Head string `json:"head"` //微信头像 + UnionId string `json:"unionid"` //微信unionid + IosIsStable bool `json:"iosIsStable"` //是否是IOS稳定版 + AccountType int32 //账号类型 +} + +type LoginCallback func(pd *PlayerData) + +var FirstLoginCB LoginCallback + +type PlayerDiffData struct { + Coin int64 //金币 + Diamond int64 //钻石 + VCoin int64 //V卡 + SafeBoxCoin int64 //保险箱金币 + CoinPayTotal int64 //总充值金额 + VIP int32 //vip等级 + TotalConvertibleFlow int64 //流水值 + ClubCoin int64 //俱乐部金币 + Ticket int64 //比赛入场券 + Grade int64 //积分 + ChessGrade int64 // 象棋积分 + RankScore map[int32]int64 // 排位积分 + PhoneScore int64 //手机积分 + InviteScore int64 // 邀请积分 +} + +type PlayerBaseData struct { + SnId int32 //数字唯一id + Name string //名字 + Sex int32 //性别 + Coin int64 //金币 +} + +const ( + TASKSTATUS_RUNNING int32 = iota //进行中 + TASKSTATUS_COMPLETE //已完成 + TASKSTATUS_PRIZEDRAWED //奖励已领取 + TASKSTATUS_EXPIRE //已超期 +) + +const ( + SystemFreeGive_GiveType_NewPlayer int32 = iota //创号赠送 + SystemFreeGive_GiveType_ActSign //签到赠送 + SystemFreeGive_GiveType_ActTurnplate //转盘 + SystemFreeGive_GiveType_ActContinuousSign //累签 + SystemFreeGive_GiveType_ShopAd //商城观看视频赠送 + SystemFreeGive_GiveType_ReliefFund //破产补助 + SystemFreeGive_GiveType_VipGift //vip领取 + SystemFreeGive_GiveType_MatchSeason //赛季奖励 + SystemFreeGive_GiveType_ActJybAward //礼包码兑换 + SystemFreeGive_GiveType_MailSystemGive //邮件系统赠送 + SystemFreeGive_GiveType_RankMatch //段位奖励 + SystemFreeGive_GiveType_BindTel // 绑定手机号奖励 +) +const ( + SystemFreeGive_CoinType_Coin int32 = iota //金币 + SystemFreeGive_CoinType_Diamond //钻石 +) + +type PlayerGameCtrlData struct { + CtrlData map[string]*PlayerGameStatics + RechargeCoin int64 //充值金额 + ExchangeCoin int64 //兑换金额 + TodayConvertibleFlow int64 //今日流水 + Ts int64 //日期 +} + +func NewPlayerGameCtrlData() *PlayerGameCtrlData { + t := &PlayerGameCtrlData{CtrlData: make(map[string]*PlayerGameStatics)} + t.Ts = time.Now().Unix() + return t +} + +type PlayerGameStatics struct { + GameTimes int64 //总局数 + WinGameTimes int64 //赢的局数 + LoseGameTimes int64 //输的局数 + DrawGameTimes int64 //和的局数 + TotalIn int64 //总游戏投入(输掉的金币) + TotalOut int64 //总游戏产出(赢取的金币),税前 + Tax int64 //总税收 + MaxSysOut int64 //单局最大盈利,税前 + Version int32 //数据版本,游戏有时候数据计算错误,需要重置所有玩家的数据 + WinGameTimesNum int64 // 连赢次数 + LoseGameTimesNum int64 // 连输次数 + ChessWinTimes int64 // 象棋连赢次数 + Other map[string]interface{} // 其他特殊统计信息(游戏自定义) +} + +func NewPlayerGameStatics() *PlayerGameStatics { + return &PlayerGameStatics{ + Other: make(map[string]interface{}), + } +} + +func (this *PlayerGameStatics) Custom() map[string]interface{} { + if this.Other == nil { + this.Other = make(map[string]interface{}) + } + return this.Other +} + +func (this *PlayerGameStatics) SetCustom(key string, value interface{}) { + if this.Other == nil { + this.Other = make(map[string]interface{}) + } + if value != nil { + this.Other[key] = value + } +} + +func (this *PlayerGameStatics) GetString(key string) string { + if this.Other == nil { + this.Other = make(map[string]interface{}) + } + ret, _ := this.Other[key].(string) + return ret +} + +func (this *PlayerGameStatics) SetInt(key string, n int) { + this.SetCustom(key, n) +} + +func (this *PlayerGameStatics) GetInt(key string) int { + if this.Other == nil { + this.Other = make(map[string]interface{}) + } + ret, _ := this.Other[key].(int) + return ret +} + +func (this *PlayerGameStatics) Incr(key string) { + this.SetInt(key, this.GetInt(key)+1) +} + +type PlayerGameInfo struct { + Statics PlayerGameStatics //游戏统计类数据 + FirstTime time.Time //首次参与游戏时间 + Data []int64 //游戏场次数据 + DataEx []byte //其他扩展数据,可自定义json或者其他二进制数据 + Version int32 //数据版本,游戏有时候数据计算错误,需要重置所有玩家的数据 +} + +type PlayerGameTotal struct { + ProfitCoin int64 //盈利总额 + BetCoin int64 //有效投注总额 + FlowCoin int64 //流水总额 +} +type ShopTotal struct { + AdLookedNum int32 //已经观看的次数 + AdReceiveNum int32 //已经领取的次数 +} +type RolePetInfo struct { + ModUnlock map[int32]int32 //已经解锁的id + ModId int32 //使用中的id +} + +// 七日签到数据 +type SignData struct { + SignIndex int //签到次数 + LastSignTickets int64 //上一次签到时间 时间戳 +} + +//// 在线奖励数据 +//type OnlineRewardData struct { +// Version int32 +// Ts int64 // 上次累计时间戳 +// OnlineDuration uint32 // 累计在线时长 +// RewardReceived uint32 // 奖励获取情况 +//} +// +//// 幸运转盘数据 +//type LuckyTurnTableData struct { +// Score int64 // 当前积分 +// TomorrowScore int64 // 明日积分 +// TomorrowFloatScore float64 // 明日积分64位的,防止精度丢失导致的数据不正常 +// +//} + +// 排行榜 +type Rank struct { + SnId int32 + Name string + Head int32 + VIP int32 + TotalCoin int64 +} + +// +//// 余额宝数据 +//type YebData struct { +// TotalIncome int64 // 累计收益 +// PrevIncome int64 // 上轮收益 +// Balance int64 // 余额(总金额) +// InterestTs int64 // 起息时间(该时间一小时后产生收益) +//} +// +//// 卡(周卡月卡)数据 +//type CardData struct { +// BuyTs int64 // 购买时间 +// ReceiveTs int64 // 领取时间 +//} +// +//// 阶梯充值数据 +//type StepRechargeData struct { +// Version int32 // 版本 +// CurrentStep int32 // 当前阶段 +// Ts int64 // (上次)充值时间 +//} + +// 财神任务数据 +type GoldTaskData struct { + TaskId int32 //任务ID + Data int64 //任务数据 + CompletedTimes int32 //已完成次数 +} + +type GoldTaskDatas struct { + ConfigVer int64 //时间戳 + DataTaskIdMap map[string]*GoldTaskData //taskid +} +type RebateData struct { + TodayRebateCoin int64 //今日返利 + YesterdayRebateCoin int64 //昨日返利 + ValidBetTotal int64 //今日有效下注 + YesterdayValidBetTotal int64 //昨日有效下注 + TotalRebateCoin int64 //今日累计已领取的返利 + TotalHaveRebateCoin int64 //往日累计未领取的返利 + TotalHaveValidBetTotal int64 //往日累计有效下注 +} + +type ShopData struct { + Id int32 // 商品ID + AddArea int32 // 加送百分比 + CostArea int32 // 消耗数量 + IsBuy bool //是否购买 +} + +////vip数据 +//type ActVipBonusData struct { +// Level int32 //等级礼包领取情况 位计算 +// Day int32 //每日领取情况 0 未领取 1已领取 +// Week int32 //每周领取情况 0 未领取 1已领取 +// Month int32 //每月领取情况 0 未领取 1已领取 +// LastWeek int64 //上次领取周时间 +// LastMonth int64 //上次领取月时间 +//} + +// 状态 +type PayActState struct { + Ts int64 //获得时间 +} + +func (this *GoldTaskDatas) GetData(taskid int32) *GoldTaskData { + if this.DataTaskIdMap != nil { + if td, ok := this.DataTaskIdMap[fmt.Sprintf("%v", taskid)]; ok { + return td + } + } + return nil +} + +// 比赛免费报名记录 +type MatchFreeSignupRec struct { + LastSignupTs int64 //最后一次报名时间 + UseTimes int32 //累计使用免费次数 +} + +type PlayerData struct { + Id bson.ObjectId `bson:"_id"` + AccountId string //账号id + AccountType int32 //账号类型 + SnId int32 //数字唯一id + NiceId int32 //靓号 + Name string //名字 + Remark string //备注 + Platform string //平台 + Channel string //渠道信息 + DeviceOS string //设备操作系统 + DeviceId string //设备id + PackageID string //推广包标识 对应客户端的packagetag + Package string //包信息 android:包名 ios:bundleid + IsRob bool //是否是机器人 + Head int32 //头像 + HeadUrl string //头像 + Sex int32 //性别 + HeadOutLine int32 //头像框 + VIP int32 //VIP帐号 等级 + GMLevel int32 //GM等级 + WinTimes int32 //胜利次数 + FailTimes int32 //失败次数 + DrawTimes int32 //平局次数 + WinCoin int64 //总赢钱数量(税前) + FailCoin int64 //总输钱数量 + TaxCoin int64 //总税收 + Tel string //电话号码 + Ip string //最后登录ip地址 + RegIp string //注册ip地址 + City string //城市 + Params string //其他参数 + AlipayAccount string //支付宝账号 + AlipayAccName string //支付宝实名 + Bank string //绑定的银行名称 + BankAccount string //绑定的银行账号 + BankAccName string //绑定的银行账号 + Coin int64 //金豆 + CoinPayTs int64 //金豆冲账时间戳 + CoinPayTotal int64 //在线总充值金额 + MoneyPayTotal int64 //在线总充值金额(实充) + CoinExchangeTotal int64 //总提现金额 兑换 + SafeBoxCoin int64 //保险箱金币 + SafeBoxPassword string //保险箱密码 + Diamond int64 //钻石 + InviterId int32 //邀请人Id + InviterName string //邀请人名称 + InviterHead int32 //邀请人头像 + BeUnderAgentCode string //隶属经销商(推广人) + SubBeUnderAgentCode string //经销商子id + Flags int64 //标记 + GameCoinTs int64 //游服金币对账时间戳 + Ver int32 //数据版本号 + CheckSum uint32 //校验码(预防暴库修改数据) + UpgradeTime time.Time //升级账号时间,绑定手机号时间 + CreateTime time.Time //创建时间 + LastLoginTime time.Time //最后登陆时间 + LastLogoutTime time.Time //缓存数据清除时间,最后数据持久化时间 + AllowSpeakTime int64 //允许下次发言的时间戳 + AgentType int32 //代理类型 0:普通用户 其它为代理 + GameTax int64 //总游戏税收 + SafeBoxCoinTs int64 //保险箱冲账时间戳 + WhiteFlag int32 //特殊白名单标记 + WBCoinTotalOut int64 //加入黑白名单后玩家总产出 + WBCoinTotalIn int64 //加入黑白名单后玩家总投入 + WBCoinLimit int64 //黑白名单输赢额度,额度变为0时自动解除黑白名单 + WBMaxNum int32 //黑白名单最大干预次数 + WBTime time.Time //黑白名单操作时间 + WBState int32 //调控状态 + TotalCoin int64 //总金币 + PromoterTree int32 //推广树信息 + TotalConvertibleFlow int64 //玩家流水总额 默认1:1 //流水统计使用 + TotalFlow int64 //历史总流水 + CanExchangeBeforeRecharge int64 //充值之前可兑换金额 //流水统计使用 + LastRechargeWinCoin int64 //充值后流水 + BlacklistType int32 //黑名单作用域和后台一样都是采用位标记的表示形式 // 0是不限制 第1位是游戏登录 第2位是兑换 第3位是充值,注意这个地方是黑名单管理的作用域+1 //主要是为了在mgo没有设置黑名单类型的时候,默认是不限制的 + ForceVip int32 //强制VIP等级,通过后台设置,如果设置了当前值,就不再通过paytotal计算vip等级 + LastExchangeTime int64 //最后兑换时间 + LastExchangeOrder string //最后的赠与订单 + LogicLevels []int32 //用户分层信息 + AutomaticTags []int32 //用户自动化标记 + TelephonePromoter int32 //电销推广员标识,用于电销标记 + TelephoneCallNum int32 //电销次数 + Ticket int64 //比赛券 + TicketPayTs int64 //比赛券冲账时间点 + TicketTotal int64 //累计总获得比赛券数量 + TicketTotalDel int64 //累计清理掉的数量 + Grade int64 //积分 + TagKey int32 //包标识关键字 + LoginTimes int //用户登录次数 + YesterdayGameData *PlayerGameCtrlData //昨日游戏统计数据 + TodayGameData *PlayerGameCtrlData //今日游戏统计数据 + IsFoolPlayer map[string]bool //每个游戏是否是新手玩家 + TotalGameData map[int][]*PlayerGameTotal //统计数据 1.棋牌 2.电子 3.捕鱼 4.视讯 5.彩票 6.体育 7.个人房间 8.俱乐部房间 9.三方游戏 + GDatas map[string]*PlayerGameInfo //玩家游戏统计数据 key:gameFreeId, key:gameid + MarkInfo string //用来备注玩家信息 + DeviceInfo string //设备信息 + WBLevel int32 //黑白名单 白:[1,10] 黑:[-1,-10] + ShopTotal map[int32]*ShopTotal //key为商品id + ShopLastLookTime map[int32]int64 //商品上一次的观看时间 + Roles *RolePetInfo //人物 + Pets *RolePetInfo //宠物 + WelfData *WelfareData //活动数据 + *SignData + VipMatchTimes int32 //VIP比赛场参与次数 + ChessGrade int64 // 国际象棋游戏积分 + Addr []string //玩家地址 + + // 用于个人水池计算,新手玩家不计入统计 + TotalIn int64 // 游戏总投入 + TotalOut int64 // 游戏总产出(税前) + PlayerTax int64 // 游戏总税收 + // 用于个人水池计算 + + DiamondToCoin int64 // 钻石兑换金币的金币数量 + MoneyPond int64 //捕鱼个人金币池 + UnMaxPower int64 //捕鱼解锁最大炮倍数 + PowerList []int32 //解锁的炮台列表 + FishLevel int64 //捕鱼玩家等级 + FishExp int64 //捕鱼经验值 + VipShopData map[int32]*ShopData //玩家vip商店信息 + VipShopRefreshCount int32 //vip当前已使用免费刷新次数 + VipExtra int32 //VIP赛季加成 + ShopID []int // 已经参与首冲翻倍的商品id + Age int32 // 年龄 + Signature string // 签名 + PhoneScore int64 //商城积分 + LotteryCount int32 //抽奖次数 + InitLotteryStatus bool //抽奖初始化状态 + Delete int // 是否删除 + InviteCode string // 邀请码 + InviteSnId int32 // 邀请人 + InviteScore int64 // 邀请积分 + OtherCode string // 绑定的邀请码 +} + +// 七日签到数据 +type NewSignData struct { + SignIndex int32 //签到次数 + SignTickets int64 //签到时间 时间戳 + AddupIndex []int32 //领取累计奖励 + TurnplateIdx []int32 //领取转盘下标 + VideoTicket int64 // 领取视频奖励时间戳 +} + +type TaskData struct { + N int64 + Ts int64 // 任务完成时间戳 +} + +type WelfareData struct { + ReliefFundTimes int32 //救济金领取次数 + Sign7 *NewSignData //七日签到 + FirstPayDay int32 //首充天数 + FirstPayTickets int64 //首充 时间戳 + ContinuousPayDay int32 //连续充值天数 + ContinuousPayTickets int64 //连续充值 时间戳 + BlindBoxId int32 //已经领取盲盒序号 + ContPayDay int32 //首充 从1开始 + VIPBag map[int32]map[int32]int32 //VIP充值礼包 key1-vipLevel key2 - shopId value- shopType (每日礼包 shopId的写死为0 value = 1) + Task map[int32]*TaskData // 任务 + PhoneLotteryTask map[int32]*TaskData // 抽手机任务 +} + +func NewWelfareData() *WelfareData { + return &WelfareData{ + Sign7: &NewSignData{}, + VIPBag: make(map[int32]map[int32]int32), + Task: make(map[int32]*TaskData), + PhoneLotteryTask: make(map[int32]*TaskData), + } +} + +type GradeShop struct { + ConsigneeName string //收货人名字 + ConsigneeTel string //收货人电话 + ConsigneeAddr string //收货人地址 + ExchangeDay map[int32]int32 //今天兑换了多少 +} + +type PlayerBaseInfo struct { + SnId int32 + AccountId string + Name string + Platform string + Channel string + BeUnderAgentCode string + DeviceOS string + PackageID string + Package string + Tel string + Head int32 + HeadUrl string + Sex int32 + GMLevel int32 + Coin int64 + SafeBoxCoin int64 + PromoterTree int32 + TelephoneCallNum int32 + LastLogoutTime time.Time + Roles *RolePetInfo +} + +type PlayerBaseInfo2 struct { + Name string + Roles *RolePetInfo + CreateTime time.Time +} + +type PlayerDataForWeb struct { + AlipayAccName string + AlipayAccount string + Bank string + BankAccName string + BankAccount string + BlackLevel int32 + Coin int64 + CoinExchangeTotal int64 + CoinPayTotal int64 + CreateTime time.Time + DeviceId string + DeviceOS string + DrawTimes int32 + FailCoin int64 + FailTimes int32 + GameTax int64 + Ip string + IsRob bool + LastLoginTime time.Time + MarkInfo string + Name string + Online bool + Package string + PackageID string + Platform string + RegIp string + SafeBoxCoin int64 + SnId int32 + Tel string + VIP int32 + WhiteLevel int32 + WinCoin int64 + WinTimes int32 +} + +type WebPlayerDataParam struct { + *PlayerData + RankScore map[int64]int64 + PlayerPoolUpper, PlayerPoolLower, PlayerPoolCurrent, PlayerPoolOdds int64 + RoleAdded, VipAdded, VipExp int64 +} + +func ConvertPlayerDataToWebData(param *WebPlayerDataParam) *webapi.PlayerData { + if param == nil || param.PlayerData == nil { + return nil + } + pdfw := new(webapi.PlayerData) + pdfw.Coin = param.Coin + pdfw.Diamond = param.Diamond + pdfw.CoinExchangeTotal = param.CoinExchangeTotal + pdfw.CoinPayTotal = param.CoinPayTotal + pdfw.CreateTime = param.CreateTime.Unix() + pdfw.DeviceId = param.DeviceId + pdfw.DeviceOS = param.DeviceOS + pdfw.DrawTimes = param.DrawTimes + pdfw.FailCoin = param.FailCoin + pdfw.FailTimes = param.FailTimes + pdfw.GameTax = param.GameTax + pdfw.Ip = param.Ip + pdfw.IsRob = param.IsRob + pdfw.LastLoginTime = param.LastLoginTime.Unix() + pdfw.MarkInfo = param.MarkInfo + pdfw.Name = param.Name + pdfw.Package = param.Package + pdfw.PackageID = param.PackageID + pdfw.Platform = param.Platform + pdfw.RegIp = param.RegIp + pdfw.SafeBoxCoin = param.SafeBoxCoin + pdfw.SnId = param.SnId + pdfw.Tel = param.Tel + pdfw.VIP = param.VIP + pdfw.WBLevel = param.WBLevel + pdfw.WinCoin = param.WinCoin + pdfw.WinTimes = param.WinTimes + pdfw.BlacklistType = param.BlacklistType + pdfw.AccountType = param.AccountType + if param.Roles != nil { + pdfw.RoleUnlock = int32(len(param.Roles.ModUnlock)) + for k := range param.Roles.ModUnlock { + pdfw.RolesIds = append(pdfw.RolesIds, k) + } + } + if param.Pets != nil { + pdfw.PetUnlock = int32(len(param.Pets.ModUnlock)) + for k := range param.Pets.ModUnlock { + pdfw.PetsIds = append(pdfw.PetsIds, k) + } + } + pdfw.ChessGrade = int32(param.ChessGrade) + pdfw.AllWinCoin = param.WinCoin - param.FailCoin + pdfw.AllFlow = param.TotalFlow + pdfw.RankScore = make(map[int64]int64) + if param.RankScore != nil { + pdfw.RankScore = param.RankScore + } + pdfw.PlayerPoolCurrent = param.PlayerPoolCurrent + pdfw.PlayerPoolUpper = param.PlayerPoolUpper + pdfw.PlayerPoolLower = param.PlayerPoolLower + pdfw.PlayerPoolRate = param.PlayerPoolOdds + pdfw.RoleAdded = param.RoleAdded + pdfw.VipAdded = param.VipAdded + pdfw.VipExp = param.VipExp + pdfw.ShopId = common.IntSliceToInt32(param.ShopID) + pdfw.Delete = int64(param.Delete) + pdfw.InviteCode = param.InviteCode + pdfw.InviteSnId = param.InviteSnId + pdfw.InviteScore = param.InviteScore + pdfw.Channel = param.Channel + return pdfw +} +func (this *PlayerData) IsMarkFlag(flag int) bool { + return this.Flags&(1<= time.Hour*24 { + return 0 + } + if this.TodayGameData != nil { + return this.TodayGameData.TodayConvertibleFlow + } + return 0 +} + +func (this *PlayerData) GetPlayerDataEncoder() (bytes.Buffer, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(this) + return buf, err +} + +func NewPlayerData(acc string, name string, id int32, channel, platform, promoter string, inviterId, + promoterTree int32, params, tel string, packTag, ip string, addCoin int64, unionid, deviceInfo string, + subPromoter string, tagkey, accountType int32) *PlayerData { + if len(name) == 0 { + logger.Logger.Trace("New player name is empty.") + return nil + } + //logger.Logger.Trace("New player information.") + //safebox password default is '111111' + raw := fmt.Sprintf("%v%v", DEFAULT_PLAYER_SAFEBOX_PWD, common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + tNow := time.Now() + isRobot := channel == common.Channel_Rob + + pd := &PlayerData{ + Id: bson.NewObjectId(), + AccountId: acc, + AccountType: accountType, + Name: name, + Channel: channel, + Platform: platform, + SnId: id, + InviterId: inviterId, + PromoterTree: promoterTree, + Head: rand.Int31n(common.HeadRange), + //Coin: int64(GameParamData.NewPlayerCoin) + addCoin, + SafeBoxPassword: pwd, + Ip: ip, + RegIp: ip, + Params: params, + Tel: tel, + SubBeUnderAgentCode: subPromoter, + AgentType: 0, + LastLoginTime: tNow.Local(), + LastLogoutTime: tNow.AddDate(0, 0, -1).Local(), + CreateTime: tNow.Local(), + Ver: VER_PLAYER_MAX - 1, + HeadOutLine: 1, + VIP: 0, + CoinPayTotal: 0, + MoneyPayTotal: 0, + IsRob: isRobot, + BeUnderAgentCode: promoter, + PackageID: packTag, + WBLevel: 0, + WBCoinTotalOut: 0, + WBCoinTotalIn: 0, + WBCoinLimit: 0, + YesterdayGameData: NewPlayerGameCtrlData(), + TodayGameData: NewPlayerGameCtrlData(), + TotalGameData: make(map[int][]*PlayerGameTotal), + GDatas: make(map[string]*PlayerGameInfo), + TagKey: tagkey, + ShopTotal: make(map[int32]*ShopTotal), + ShopLastLookTime: make(map[int32]int64), + IsFoolPlayer: make(map[string]bool), + //测试数据 + PowerList: []int32{1}, //默认炮台 + UnMaxPower: 10, //初始化炮倍最小倍数 + } + + if tel != "" { + pd.UpgradeTime = time.Now() + } + + pd.InitNewData(params) + + return pd +} + +func NewPlayerDataThird(acc string, name, headUrl string, id int32, channel, platform, promoter string, inviterId, + promoterTree int32, params, tel string, packTag, ip string, subPromoter string, tagkey, accountType int32, deviceOS string) *PlayerData { + if len(name) == 0 { + logger.Logger.Trace("New player name is empty.") + return nil + } + tNow := time.Now() + pd := &PlayerData{ + Id: bson.NewObjectId(), + AccountId: acc, + Name: name, + Channel: channel, + Platform: platform, + SnId: id, + InviterId: inviterId, + PromoterTree: promoterTree, + Head: rand.Int31n(common.HeadRange), + HeadUrl: headUrl, + //Coin: int64(GameParamData.NewPlayerCoin), + Ip: ip, + RegIp: ip, + Params: params, + Tel: tel, + SubBeUnderAgentCode: subPromoter, + LastLoginTime: tNow.Local(), + LastLogoutTime: tNow.AddDate(0, 0, -1).Local(), + CreateTime: tNow.Local(), + Ver: VER_PLAYER_MAX - 1, + HeadOutLine: 1, + IsRob: false, + BeUnderAgentCode: promoter, + PackageID: packTag, + YesterdayGameData: NewPlayerGameCtrlData(), + TodayGameData: NewPlayerGameCtrlData(), + TotalGameData: make(map[int][]*PlayerGameTotal), + GDatas: make(map[string]*PlayerGameInfo), + TagKey: tagkey, + ShopTotal: make(map[int32]*ShopTotal), + ShopLastLookTime: make(map[int32]int64), + AccountType: accountType, + DeviceOS: deviceOS, + } + + pd.InitNewData(params) + + return pd +} + +func (this *PlayerData) InitNewData(params string) { + this.TotalCoin = this.GetTotalCoin() + //pd.ProfitCoin = pd.TotalCoin - pd.CoinPayTotal + //0:男 1:女 + this.Sex = (this.Head%2 + 1) % 2 + if !this.IsRob { + this.Sex = 0 + } + //更新参数 + //this.UpdateParams(params) + //生成校验和 + RecalcuPlayerCheckSum(this) + + this.InitRolesAndPets() +} + +func (this *PlayerData) InitRolesAndPets() { + if this.Roles == nil || len(this.Roles.ModUnlock) == 0 { + this.Roles = new(RolePetInfo) + this.Roles.ModUnlock = make(map[int32]int32) + this.Roles.ModUnlock[2000001] = 1 + this.Roles.ModId = 2000001 + } + if this.Pets == nil { + this.Pets = new(RolePetInfo) + this.Pets.ModUnlock = make(map[int32]int32) + this.Pets.ModUnlock[1000001] = 1 + this.Pets.ModId = 1000001 + } + if _, ok := this.Pets.ModUnlock[1000001]; !ok { + this.Pets.ModUnlock[1000001] = 1 + this.Pets.ModId = 1000001 + } +} + +//func SavePlayerRebate(pd *PlayerData, thirdName string) error { +// if pd == nil { +// return nil +// } +// if rpcCli == nil { +// return nil +// } +// var ret bool +// err := rpcCli.CallWithTimeout("PlayerDataSvc.SavePlayerRebate", pd, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Trace("SavePlayerRebate failed:", err) +// return err +// } +// return nil +//} + +type GetAgentInfoArgs struct { + Plt string + Tel string +} + +// 获取代理信息 +func GetAgentInfo(plt, tel string) *PlayerData { + if rpcCli == nil { + return nil + } + args := &GetAgentInfoArgs{ + Plt: plt, + Tel: tel, + } + var pbi *PlayerData + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetAgentInfo", args, &pbi, time.Second*30) + if err != nil { + logger.Logger.Trace("GetPlayerBaseInfo failed:", err) + return nil + } + return pbi +} + +func ClonePlayerData(pd *PlayerData) *PlayerData { + if pd == nil { + return nil + } + + pd.TotalCoin = pd.GetTotalCoin() + //pd.ProfitCoin = pd.TotalCoin - pd.CoinPayTotal + //增加可维护性,适度降低性能 + buf, err := netlib.Gob.Marshal(pd) + if err != nil { + logger.Logger.Warnf("ClonePlayerData %v Gob.Marshal fail:%v", pd.SnId, err) + return nil + } + + pdCopy := &PlayerData{} + err = netlib.Gob.Unmarshal(buf, pdCopy) + if err != nil { + logger.Logger.Warnf("ClonePlayerData %v Gob.Unmarshal fail:%v", pd.SnId, err) + return nil + } + return pdCopy +} + +type GetPlayerDataArgs struct { + Plt string + Acc string +} +type PlayerDataRet struct { + Pd *PlayerData + IsNew bool +} + +func GetPlayerData(plt, acc string) (*PlayerData, bool) { + if rpcCli == nil { + return nil, false + } + args := &GetPlayerDataArgs{ + Plt: plt, + Acc: acc, + } + var ret PlayerDataRet + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerData", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("GetPlayerData failed:", err) + return nil, false + } + return ret.Pd, ret.IsNew +} + +type CreatePlayer struct { + Plt string + AccId string + NickName string + HeadUrl string +} + +func CreatePlayerDataByThird(plt, acc string, nickName, headUrl string) (*PlayerData, bool) { + if rpcCli == nil { + return nil, false + } + var args = &CreatePlayer{ + Plt: plt, + AccId: acc, + NickName: nickName, + HeadUrl: headUrl, + } + var ret = &PlayerDataRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.CreatePlayerDataByThird", args, ret, time.Second*30) + if err != nil { + logger.Logger.Trace("CreatePlayerDataByThird failed:", err) + return nil, false + } + return ret.Pd, ret.IsNew +} + +type PlayerDataArg struct { + Plt string + AccId string + AddCoin int32 + HeadUrl string + Name string +} + +func CreatePlayerDataOnRegister(plt, acc string, addCoin int32, name, headUrl string) (*PlayerData, bool) { + if rpcCli == nil { + return nil, false + } + + var args = &PlayerDataArg{ + Plt: plt, + AccId: acc, + AddCoin: addCoin, + Name: name, + HeadUrl: headUrl, + } + var ret = &PlayerDataRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.CreatePlayerDataOnRegister", args, ret, time.Second*30) + if err != nil { + logger.Logger.Trace("CreatePlayerDataOnRegister failed:", err) + return nil, false + } + return ret.Pd, true +} + +type GetPlayerDataBySnIdArgs struct { + Plt string + SnId int32 + CorrectData bool + CreateIfNotExist bool +} + +func GetPlayerDataBySnId(plt string, snid int32, correctData, createIfNotExist bool) (*PlayerData, bool) { + if rpcCli == nil { + return nil, false + } + args := &GetPlayerDataBySnIdArgs{ + Plt: plt, + SnId: snid, + CorrectData: correctData, + CreateIfNotExist: createIfNotExist, + } + var ret = &PlayerDataRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerDataBySnId", args, ret, time.Second*30) + if err != nil { + logger.Logger.Tracef("Get %v %v player data error:%v", plt, snid, err) + return nil, false + } + return ret.Pd, ret.IsNew +} + +//func UpdateCreateCreateClubNum(snId int32) error { +// if rpcCli == nil { +// return nil +// } +// var ret = &PlayerDataRet{} +// err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdateCreateCreateClubNum", snId, ret, time.Second*30) +// if err != nil { +// logger.Logger.Trace("Update player safeboxcoin error:", err) +// return err +// } +// return nil +//} + +type GetPlayerDatasBySnIdsArgs struct { + Plt string + SnIds []int32 + CorrectData bool +} + +func GetPlayerDatasBySnIds(plt string, snIds []int32, correctData bool) []*PlayerData { + if rpcCli == nil { + return nil + } + args := &GetPlayerDatasBySnIdsArgs{ + Plt: plt, + SnIds: snIds, + CorrectData: correctData, + } + var ret = []*PlayerData{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerDatasBySnIds", args, ret, time.Second*30) + if err != nil { + logger.Logger.Tracef("GetPlayerDatasBySnIds(snids=%v) error:%v", args, err) + return nil + } + + return ret +} + +type GetPlayerDataByUnionIdArgs struct { + Plt string + UnionId string + CorrectData bool +} + +func GetPlayerDataByUnionId(plt, unionid string, correctData bool) (*PlayerData, bool) { + if rpcCli == nil { + return nil, false + } + args := &GetPlayerDataByUnionIdArgs{ + Plt: plt, + UnionId: unionid, + CorrectData: correctData, + } + var ret = &PlayerDataRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerDataByUnionId", args, ret, time.Second*30) + if err != nil { + logger.Logger.Tracef("GetPlayerDataByUnionId % player data error:%v", args, err) + return nil, false + } + return ret.Pd, true +} + +type GetPlayerTelArgs struct { + Plt string + Tel string + TagKey int32 +} +type GetPlayerTelRet struct { + SnId int32 + Err error +} + +func GetPlayerTel(tel, platform string, tagkey int32) (int32, string) { + if rpcCli == nil { + return 0, "no find account" + } + args := &GetPlayerTelArgs{ + Tel: tel, + Plt: platform, + TagKey: tagkey, + } + ret := &GetPlayerTelRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerTel", args, ret, time.Second*30) + if err != nil { + logger.Logger.Warn("playerdata.Find err:", err) + return 0, err.Error() + } + return ret.SnId, "" +} + +type GetPlayerCoinArgs struct { + Plt string + SnId int32 +} +type GetPlayerCoinRet struct { + Coin int64 + SafeBoxCoin int64 + Err error +} + +func GetPlayerCoin(plt string, snid int32) (int64, int64, error) { + if rpcCli == nil { + return 0, 0, fmt.Errorf("db may be closed") + } + + args := GetPlayerCoinArgs{ + Plt: plt, + SnId: snid, + } + var ret = &GetPlayerCoinRet{} + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerCoin", args, ret, time.Second*30) + if err != nil { + logger.Logger.Trace("GetPlayerCoin error:", err) + return 0, 0, err + } + if ret.Err != nil { + return 0, 0, ret.Err + } + return ret.Coin, ret.SafeBoxCoin, nil +} + +func ExtractPlayerPackageName(packageTag string) string { + segs := strings.Split(packageTag, ".") + if len(segs) > 3 { + packageName := strings.Join(segs[:3], ".") + return packageName + } + return packageTag +} + +/* + * 保存玩家的全部信息 + */ +func SavePlayerData(pd *PlayerData) bool { + if rpcCli == nil { + return false + } + if pd != nil { + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SavePlayerData", pd, &ret, time.Second*30) + if err != nil { + logger.Logger.Errorf("SavePlayerData %v err:%v", pd.SnId, err) + return false + } + return ret + } + return false +} + +func BackupPlayerData(pd *PlayerData) { + if pd == nil { + return + } + + buf, err := json.Marshal(pd) + if err != nil { + logger.Logger.Info("BackupPlayerInfo json.Marshal error", err) + return + } + + fileName := fmt.Sprintf("%v.json", pd.AccountId) + err = os.WriteFile(fileName, buf, os.ModePerm) + if err != nil { + logger.Logger.Info("BackupPlayerInfo os.WriteFile error", err) + } +} + +func RestorePlayerData(fileName string) *PlayerData { + pd := &PlayerData{} + buf, err := os.ReadFile(fileName) + if err != nil { + logger.Logger.Info("RestorePlayerInfo os.ReadFile error", err) + return nil + } + + err = json.Unmarshal(buf, pd) + if err != nil { + logger.Logger.Info("RestorePlayerInfo json.Unmarshal error", err) + } + return pd +} + +type RemovePlayerArgs struct { + Plt string + SnId int32 +} + +func RemovePlayer(plt string, snid int32) error { + if rpcCli == nil { + return nil + } + args := &RemovePlayerArgs{ + Plt: plt, + SnId: snid, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.RemovePlayer", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("Remove player failed.") + return err + } + return nil +} + +type RemovePlayerByAccArgs struct { + Plt string + Acc string +} + +func RemovePlayerByAcc(plt, acc string) error { + if rpcCli == nil { + return nil + } + args := &RemovePlayerByAccArgs{ + Plt: plt, + Acc: acc, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.RemovePlayerByAcc", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("Remove player failed.") + return err + } + return nil +} + +/* + * 检查手机号是否存在 + */ +type GetPlayerSnidArgs struct { + Plt string + Acc string +} +type GetPlayerSnidRet struct { + SnId int32 +} + +func GetPlayerSnid(plt, acc string) int32 { + if rpcCli == nil { + return 0 + } + args := &GetPlayerSnidArgs{ + Plt: plt, + Acc: acc, + } + var ret int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerSnid", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerSnid is error ", err) + return 0 + } + return ret +} + +/* + * 检查昵称是否存在 + */ +type PlayerNickIsExistArgs struct { + Plt string + Name string +} + +func PlayerNickIsExist(plt, name string) bool { + if rpcCli == nil { + return false + } + args := &PlayerNickIsExistArgs{ + Plt: plt, + Name: name, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.PlayerNickIsExist", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.PlayerNickIsExist is error ", err) + return false + } + if err != nil { + return false + } + + return ret +} + +/* + * 检查玩家是否存在 + */ +type PlayerIsExistBySnIdArgs struct { + Plt string + SnId int32 +} + +func PlayerIsExistBySnId(plt string, snId int32) bool { + if rpcCli == nil { + return false + } + args := &PlayerIsExistBySnIdArgs{ + Plt: plt, + SnId: snId, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.PlayerIsExistBySnId", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.PlayerIsExistBySnId is error", err) + return false + } + + return ret +} + +type UpdatePackageId struct { + AccId string + SnId int32 + Tag string + Platform int32 + Channel int32 + Promoter int32 + PlatformStr string + ChannelStr string + PromoterStr string + Inviterid int32 + PromoterTree int32 + PackageTag string +} + +// 修改推广包标识 +func UpdatePlayerPackageId(snid int32, tag string, platform, channel, promoter, inviterid, promoterTree int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + SnId: snid, + Tag: tag, + Platform: platform, + Channel: channel, + Promoter: promoter, + Inviterid: inviterid, + PromoterTree: promoterTree, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPackageId", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player packageid error:", err) + return err + } + return nil +} +func UpdatePlayerPackageIdByStr(snid int32, tag string, platform, channel, promoter string, inviterid, promoterTree int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + SnId: snid, + Tag: tag, + PlatformStr: platform, + ChannelStr: channel, + PromoterStr: promoter, + Inviterid: inviterid, + PromoterTree: promoterTree, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPackageIdByStr", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("UpdatePlayerPackageIdByStr error:", err) + return err + } + return nil +} + +func UpdatePlayerPackageIdByAcc(accid string, tag string, platform, channel, promoter string, inviterid, promoterTree int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + AccId: accid, + Tag: tag, + PlatformStr: platform, + ChannelStr: channel, + PromoterStr: promoter, + Inviterid: inviterid, + PromoterTree: promoterTree, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPackageIdByAcc", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("UpdatePlayerPackageIdByAcc error:", err) + return err + } + return nil +} + +// 修改平台 +func UpdatePlayerPlatform(snid int, platform, channel string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + SnId: int32(snid), + PlatformStr: platform, + ChannelStr: channel, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPlatform", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player nick error:", err) + return err + } + return nil +} + +// 修改玩家无级代推广员id +func UpdatePlayerPromoterTree(plt string, snid int32, promoterTree int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + PlatformStr: plt, + SnId: snid, + PromoterTree: promoterTree, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPromoterTree", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("UpdatePlayerPromoterTree error:", err) + return err + } + return nil +} + +// 修改玩家全民推广 +func UpdatePlayerInviteID(plt string, snid int32, InviteID int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePackageId{ + PlatformStr: plt, + SnId: snid, + Inviterid: InviteID, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerInviteID", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("UpdatePlayerInviteID error:", err) + return err + } + return nil +} + +type UpdatePlayerInfo struct { + Plt string + Acc string + SnId int32 + Nick string + BlacklistType int32 + MarkInfo string + WhiteFlag int32 + PayActState map[int32]*PayActState + TelephonePromoter int32 + TelephoneCallNum int32 + Head int32 + Sex int32 +} + +/* + * 修改昵称 + */ +func UpdatePlayerNick(plt, acc string, nick string) int { + if rpcCli == nil { + return 2 + } + var args = &UpdatePlayerInfo{ + Plt: plt, + Acc: acc, + Nick: nick, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerNick", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player nick error:", err) + return 1 + } + if !ret { + return 1 + } + return 0 +} + +/* + * 修改黑名单类型 + */ +type UpdatePlayerBlacklistTypeArgs struct { + Plt string + SnId int32 + BlackListType int32 +} + +func UpdatePlayerBlacklistType(plt string, snid int32, blacklisttype int32) { + if rpcCli == nil { + return + } + var args = &UpdatePlayerBlacklistTypeArgs{ + Plt: plt, + SnId: snid, + BlackListType: blacklisttype, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerBlacklistType", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player blacklisttype error:", err) + return + } +} + +/* + * 修改玩家备注信息 + */ +type UpdatePlayerMarkInfoArgs struct { + Plt string + SnId int32 + MarkInfo string +} + +func UpdatePlayerMarkInfo(plt string, snid int32, mark string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerMarkInfoArgs{ + Plt: plt, + SnId: snid, + MarkInfo: mark, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerMarkInfo", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("Update player mark error:", err) + return err + } + return nil +} + +/* + * 修改玩家特殊白名单 + */ +type UpdatePlayerWhiteFlagArgs struct { + Plt string + SnId int32 + WhiteFlag int32 +} + +func UpdatePlayerWhiteFlag(plt string, snid int32, whiteFlag int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerWhiteFlagArgs{ + Plt: plt, + SnId: snid, + WhiteFlag: whiteFlag, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerWhiteFlag", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("Update player whiteFlag error:", err) + return err + } + return nil +} + +/* + * 修改玩家支付信息 + */ + +type UpdatePlayerPayActArgs struct { + Plt string + SnId int32 + PayActState map[int32]*PayActState +} + +func UpdatePlayerPayAct(plt string, snid int32, player map[int32]*PayActState) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerPayActArgs{ + Plt: plt, + SnId: snid, + PayActState: player, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerPayAct", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("Update player payact error:", err) + return err + } + return nil +} + +/* + * 修改玩家电销标记 + */ +type UpdatePlayerTelephonePromoterArgs struct { + Plt string + SnId int32 + TelephonePromoter string + TelephoneCallNum int32 +} + +func UpdatePlayerTelephonePromoter(plt string, snid int32, mark string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerTelephonePromoterArgs{ + Plt: plt, + SnId: snid, + TelephonePromoter: mark, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerTelephonePromoter", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("Update player telephonepromoter error:", err) + return err + } + return nil +} + +/* + * 修改玩家电销标记 + */ +func UpdatePlayerTelephoneCallNum(plt string, snid int32, mark int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerTelephonePromoterArgs{ + Plt: plt, + SnId: snid, + TelephoneCallNum: mark, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerTelephoneCallNum", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("Update player telephonecallnum error:", err) + return err + } + return nil +} + +/* + * 修改头像 + */ +type UpdatePlayeIconArgs struct { + Plt string + Acc string + Head int32 +} + +func UpdatePlayeIcon(plt, acc string, icon int32) int { + if rpcCli == nil { + return 2 + } + var args = &UpdatePlayeIconArgs{ + Plt: plt, + Acc: acc, + Head: icon, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayeIcon", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player icon error:", err) + return 1 + } + if !ret { + return 1 + } + return 0 +} + +/* + * 修改性别 + */ +type UpdatePlayeSexArgs struct { + Plt string + Acc string + Sex int32 +} + +func UpdatePlayeSex(plt, acc string, sex int32) int { + if rpcCli == nil { + return 2 + } + var args = &UpdatePlayeSexArgs{ + Plt: plt, + Acc: acc, + Sex: sex, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayeSex", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player sex error:", err) + return 1 + } + if !ret { + return 1 + } + return 0 +} + +/* + * 检查手机号是否存在 + */ +type PlayerTelIsExistArgs struct { + Tel string + Platform string + TagKey int32 +} + +func PlayerTelIsExist(tel, platform string, tagkey int32) bool { + if rpcCli == nil { + return false + } + var args = &PlayerTelIsExistArgs{ + Tel: tel, + Platform: platform, + TagKey: tagkey, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.PlayerTelIsExist", args, &ret, time.Second*30) + if err != nil { + return false + } + + return ret +} + +/* + * 绑定手机号 + */ +type UpdatePlayerTelArgs struct { + Plt string + SnId int32 + Tel string +} + +func UpdatePlayerTel(plt string, snid int32, tel string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerTelArgs{ + Plt: plt, + SnId: snid, + Tel: tel, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerTel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdatePlayerTel error:", err) + return err + } + return nil +} + +type ResetPlayerArgs struct { + Plt string + SnId int32 +} + +func ResetPlayer(plt string, snid int32) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &ResetPlayerArgs{ + Plt: plt, + SnId: snid, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.ResetPlayer", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("ResetPlayer error:", err) + return err + } + return nil +} + +type CountAlipayAccountCountArgs struct { + Platform string + AliPayAccount string +} + +func CountAlipayAccountCount(platform, alipayAccount string) int { + if rpcCli == nil { + return 0 + } + var args = &CountAlipayAccountCountArgs{ + Platform: platform, + AliPayAccount: alipayAccount, + } + var count int + err := rpcCli.CallWithTimeout("PlayerDataSvc.CountAlipayAccountCount", args, &count, time.Second*30) + if err != nil { + logger.Logger.Warn("CountAlipayAccountCount error:", err) + return 0 + } + return count +} + +type CountBankAlipayNameCountArgs struct { + SnId int32 + Platform string + AliPayAccName string + BankAccName string +} + +func CountBankAlipayNameCount(platform, name string, snid int32) int { + if rpcCli == nil { + return 0 + } + var args = &CountBankAlipayNameCountArgs{ + SnId: snid, + Platform: platform, + AliPayAccName: name, + BankAccName: name, + } + var ret int + err := rpcCli.CallWithTimeout("PlayerDataSvc.CountBankAlipayNameCount", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("CountBankAlipayNameCount error:", err) + return 0 + } + return ret +} + +/* + * 绑定支付宝账号 + */ +type UpdatePlayerAlipayArgs struct { + SnId int32 + Platform string + AliPayAccount string + AliPayAccName string +} + +func UpdatePlayerAlipay(plt string, snid int32, alipayAccount, alipayAccName string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerAlipayArgs{ + Platform: plt, + SnId: snid, + AliPayAccount: alipayAccount, + AliPayAccName: alipayAccName, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerAlipay", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdatePlayerAlipay error:", err) + return err + } + return err +} +func UpdatePlayerAlipayAccount(plt string, snid int, alipayAccount string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerAlipayArgs{ + Platform: plt, + SnId: int32(snid), + AliPayAccount: alipayAccount, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerAlipayAccount", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdatePlayerAlipay error:", err) + return err + } + return nil +} +func UpdatePlayerAlipayName(plt string, snid int, alipayAccName string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerAlipayArgs{ + Platform: plt, + SnId: int32(snid), + AliPayAccName: alipayAccName, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerAlipayName", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdatePlayerAlipay error:", err) + return err + } + return nil +} + +type CountBankAccountCountArgs struct { + Platform string + BankAccount string +} + +func CountBankAccountCount(platform, bankAccount string) int { + if rpcCli == nil { + return 0 + } + var args = &CountBankAccountCountArgs{ + Platform: platform, + BankAccount: bankAccount, + } + var ret int + err := rpcCli.CallWithTimeout("PlayerDataSvc.CountBankAccountCount", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("CountBankAccountCount error:", err) + return 0 + } + return ret +} + +/* + * 绑定银行账号 + */ +type UpdatePlayerBankArgs struct { + Plt string + SnId int32 + Bank string + BankAccount string + BankAccName string +} + +func UpdatePlayerBank(plt string, snid int32, bank, bankAccount, bankAccName string) error { + if rpcCli == nil { + return errors.New("user_playerinfo not open") + } + var args = &UpdatePlayerBankArgs{ + Plt: plt, + SnId: snid, + Bank: bank, + BankAccount: bankAccount, + BankAccName: bankAccName, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerBank", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("UpdatePlayerBank error:", err) + return err + } + return nil +} + +/* + * 修改玩家是否返利 + */ +type UpdatePlayerIsRebateArgs struct { + Plt string + SnId int32 + IsCanRebate int32 +} + +func UpdatePlayerIsRebate(plt string, snid int32, isRebate int32) int { + if rpcCli == nil { + return 2 + } + var args = &UpdatePlayerIsRebateArgs{ + Plt: plt, + SnId: snid, + IsCanRebate: isRebate, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerIsRebate", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player isRebate error:", err) + return 1 + } + + if !ret { + return 1 + } + return 0 +} + +/* + * 修改玩家是否可以修改昵称 + */ +type UpdatePlayerIsStopRenameArgs struct { + Plt string + SnId int32 + IsStopReName int32 +} + +func UpdatePlayerIsStopRename(plt string, snid int32, isStop int32) error { + if rpcCli == nil { + return errors.New("param err") + } + var args = &UpdatePlayerIsStopRenameArgs{ + Plt: plt, + SnId: snid, + IsStopReName: isStop, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerIsStopRename", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player isRename error:", err) + return err + } + return nil +} + +type PlayerRebindSnIdArgs struct { + Plt string + SnId int32 + NewSnId int32 +} + +func PlayerRebindSnId(plt string, snid int32, newSnId int32) error { + if snid == 0 || newSnId == 0 { + return errors.New("param err") + } + if rpcCli == nil { + return errors.New("rpcCli == nil") + } + var args = &PlayerRebindSnIdArgs{ + Plt: plt, + SnId: snid, + NewSnId: newSnId, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.PlayerRebindSnId", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("PlayerRebindSnId error:", err) + return err + } + return nil +} + +type PlayerSafeBoxArgs struct { + Acc string + OldPassWord string + PassWord string + Tel string + Platform string + TagKey int32 + SnId int32 +} + +/* + * 修改保险箱密码 + */ +func UpdateSafeBoxPassword(plt, acc string, oldpassword string, password string) int { + if rpcCli == nil { + return 2 + } + var args = &PlayerSafeBoxArgs{ + Platform: plt, + Acc: acc, + OldPassWord: oldpassword, + PassWord: password, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdateSafeBoxPassword", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player safeboxpassword error:", err) + return 1 + } + if !ret { + return 1 + } + return 0 +} + +/* + * 找回保险箱密码 + */ +func GetBackSafeBoxPassword(tel string, password, platform string, tagkey int32) error { + if rpcCli == nil { + return errors.New("GetBackSafeBoxPassword error") + } + var args = &PlayerSafeBoxArgs{ + Tel: tel, + PassWord: password, + Platform: platform, + TagKey: tagkey, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetBackSafeBoxPassword", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Update player safeboxpassword error:", err) + } + return err +} + +/* + * 重置保险箱密码 + */ +func ResetSafeBoxPassword(plt string, snid int, password string) error { + if rpcCli == nil { + return errors.New("ResetSafeBoxPassword error") + } + var args = &PlayerSafeBoxArgs{ + Platform: plt, + SnId: int32(snid), + PassWord: password, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.ResetSafeBoxPassword", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Trace("Reset player safeboxpassword error:", err) + return err + } + return err +} + +type PlayerSafeBoxCoin struct { + Plt string + SnId int32 + Coin, Diamond, SafeBoxCoin, CoinPayTs, SafeBoxCoinTs, Money int64 + LastexChangeTime int64 + TotalConvertibleFlow int64 + TotalFlow int64 + LastexChangeOrder string + ShopId []int +} + +func UpdatePlayerCoin(plt string, snid int32, coin, diamond, safeboxcoin, coinpayts, safeboxcoints, money int64, shopId []int) error { + if rpcCli == nil { + return fmt.Errorf("db may be closed") + } + var args = &PlayerSafeBoxCoin{ + Plt: plt, + SnId: snid, + Coin: coin, + Diamond: diamond, + SafeBoxCoin: safeboxcoin, + CoinPayTs: coinpayts, + SafeBoxCoinTs: safeboxcoints, + Money: money, + ShopId: shopId, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerCoin", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Errorf("UpdatePlayerCoin param:%v-%v-%v", snid, coin, safeboxcoin) + logger.Logger.Error("UpdatePlayerCoin error:", err) + return err + } + return nil +} + +func UpdatePlayerSetCoin(plt string, snid int32, coin int64) error { + if rpcCli == nil { + return fmt.Errorf("db may be closed") + } + var args = &PlayerSafeBoxCoin{ + Plt: plt, + SnId: snid, + Coin: coin, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerSetCoin", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpdatePlayerSetCoin error:", err) + return err + } + return nil +} + +func UpdatePlayerLastExchangeTime(plt string, snid int32, ts int64) error { + if rpcCli == nil { + return fmt.Errorf("db may be closed") + } + var args = &PlayerSafeBoxCoin{ + Plt: plt, + SnId: snid, + LastexChangeTime: ts, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerLastExchangeTime", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpdatePlayerLastExchangeTime error:", err) + return err + } + return nil +} + +func UpdatePlayerExchageFlow(plt string, snid int32, flow int64, flow2 int64) error { + if rpcCli == nil { + return fmt.Errorf("db may be closed") + } + var args = &PlayerSafeBoxCoin{ + Plt: plt, + SnId: snid, + TotalConvertibleFlow: flow, + TotalFlow: flow2, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerExchageFlow", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpdatePlayerExchageFlow error:", err) + return err + } + return nil +} +func UpdatePlayerExchageFlowAndOrder(plt string, snid int32, flow int64, logid string) error { + if rpcCli == nil { + return fmt.Errorf("db may be closed") + } + var args = &PlayerSafeBoxCoin{ + Plt: plt, + SnId: snid, + TotalConvertibleFlow: flow, + LastexChangeOrder: logid, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerExchageFlowAndOrder", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpdatePlayerExchageFlowAndOrder error:", err) + return err + } + return nil +} + +// 重要数据要做校验和 +func RecalcuPlayerCheckSum(pd *PlayerData) { + h := bytes.NewBuffer(nil) + io.WriteString(h, pd.AccountId) + io.WriteString(h, fmt.Sprintf("%v", pd.SnId)) + io.WriteString(h, fmt.Sprintf("%v", pd.Tel)) + io.WriteString(h, fmt.Sprintf("%v", pd.Coin)) + io.WriteString(h, fmt.Sprintf("%v", pd.SafeBoxCoin)) + io.WriteString(h, fmt.Sprintf("%v", pd.CoinPayTs)) + io.WriteString(h, fmt.Sprintf("%v", pd.SafeBoxCoinTs)) + io.WriteString(h, fmt.Sprintf("%v", pd.GameCoinTs)) + io.WriteString(h, common.GetAppId()) + pd.CheckSum = crc32.ChecksumIEEE(h.Bytes()) +} + +// 校验数据,防止数据被破坏或者爆库修改 +func VerifyPlayerCheckSum(pd *PlayerData) bool { + h := bytes.NewBuffer(nil) + io.WriteString(h, pd.AccountId) + io.WriteString(h, fmt.Sprintf("%v", pd.SnId)) + io.WriteString(h, fmt.Sprintf("%v", pd.Tel)) + io.WriteString(h, fmt.Sprintf("%v", pd.Coin)) + io.WriteString(h, fmt.Sprintf("%v", pd.SafeBoxCoin)) + io.WriteString(h, fmt.Sprintf("%v", pd.CoinPayTs)) + io.WriteString(h, fmt.Sprintf("%v", pd.SafeBoxCoinTs)) + io.WriteString(h, fmt.Sprintf("%v", pd.GameCoinTs)) + io.WriteString(h, common.GetAppId()) + checkSum := crc32.ChecksumIEEE(h.Bytes()) + return checkSum == pd.CheckSum +} + +/* + * 查找多用户 + */ +type PlayerSelect struct { + Id int + Tel string + NickName string + Coinl int + Coinh int + Ip string + Alipay string + Registerl int + Registerh int + Platform string + Channel string + Limit int +} + +func FindPlayerList(id int, tel string, nickname string, coinl int, coinh int, ip string, + alipay string, registerl int, registerh int, platform, channel string, limit int) []*PlayerBaseInfo { + var args = &PlayerSelect{ + Id: id, + Tel: tel, + NickName: nickname, + Coinl: coinl, + Coinh: coinh, + Ip: ip, + Alipay: alipay, + Registerl: registerl, + Registerh: registerh, + Platform: platform, + Channel: channel, + Limit: limit, + } + var ret []*PlayerBaseInfo + err := rpcCli.CallWithTimeout("PlayerDataSvc.FindPlayerList", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.FindPlayerList error:", err) + return nil + } + return ret +} + +type UpdateElement struct { + Plt string + SnId int32 + PlayerMap string +} + +func UpdatePlayerElement(plt string, id int32, playerMap map[string]interface{}) error { + if rpcCli == nil { + return fmt.Errorf("db may be close") + } + pm, _ := json.Marshal(playerMap) + var args = &UpdateElement{ + Plt: plt, + SnId: id, + PlayerMap: string(pm), + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdatePlayerElement", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warnf("UpdatePlayerElement error:%v", err) + return err + } + return nil +} + +type GetPlayerBaseInfoArgs struct { + Plt string + SnId int32 +} + +func GetPlayerBaseInfo(plt string, snid int32) *PlayerBaseInfo { + if rpcCli == nil { + return nil + } + args := &GetPlayerBaseInfoArgs{ + Plt: plt, + SnId: snid, + } + var ret PlayerBaseInfo + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerBaseInfo", args, &ret, time.Second*30) + if err != nil { + return nil + } + return &ret +} + +type BlackWhiteLevel struct { + SnId, WBLevel, WbMaxNum int32 + WbCoinTotalIn, WbCoinTotalOut, WbCoinLimit int64 + Platform string + WbState int32 + T time.Time +} + +func SetBlackWhiteLevel(snid, wbLevel, wbMaxNum, wbState int32, wbCoinTotalIn, wbCoinTotalOut, wbCoinLimit int64, platform string, t time.Time) error { + if rpcCli == nil { + return nil + } + var args = &BlackWhiteLevel{ + SnId: snid, + WBLevel: wbLevel, + WbMaxNum: wbMaxNum, + WbCoinTotalIn: wbCoinTotalIn, + WbCoinTotalOut: wbCoinTotalOut, + WbCoinLimit: wbCoinLimit, + Platform: platform, + WbState: wbState, + T: t, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SetBlackWhiteLevel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("SetBlackWhiteLevel error ", err) + return err + } + return nil +} + +func SetBlackWhiteLevelUnReset(snid, wbLevel, wbMaxNum, wbState int32, wbCoinLimit int64, platform string, t time.Time) error { + if rpcCli == nil { + return nil + } + var args = &BlackWhiteLevel{ + SnId: snid, + WBLevel: wbLevel, + WbMaxNum: wbMaxNum, + WbCoinLimit: wbCoinLimit, + Platform: platform, + WbState: wbState, + T: t, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SetBlackWhiteLevelUnReset", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("SetBlackWhiteLevelUnReset error ", err) + return err + } + return nil +} +func UpdateAllPlayerPackageTag(packageTag, platform, channel, promoter string, promoterTree, tagkey int32) error { + if rpcCli == nil { + return nil + } + args := &UpdatePackageId{ + PackageTag: packageTag, + PlatformStr: platform, + ChannelStr: channel, + PromoterStr: promoter, + PromoterTree: promoterTree, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.UpdateAllPlayerPackageTag", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("UpdateAllPlayerPackageTag error ", err) + return err + } + return nil +} + +type SetPlayerAtt struct { + SnId int32 + VipLevel int32 + Platform string + GmLevel int32 +} + +func SetVipLevel(snid, vipLevel int32, platform string) error { + if rpcCli == nil { + return nil + } + args := &SetPlayerAtt{ + SnId: snid, + VipLevel: vipLevel, + Platform: platform, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SetVipLevel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("SetVipLevel error ", err) + return err + } + return nil +} + +func SetGMLevel(snid, gmLevel int32, platform string) error { + if rpcCli == nil { + return nil + } + args := &SetPlayerAtt{ + SnId: snid, + GmLevel: gmLevel, + Platform: platform, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SetGMLevel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("SetGMLevel error ", err) + return err + } + return nil +} + +type GetSameParamPlayerArgs struct { + Plt string + Param string +} + +func GetSameIpPlayer(plt, ip string) []int32 { + if rpcCli == nil { + return nil + } + args := &GetSameParamPlayerArgs{ + Plt: plt, + Param: ip, + } + var ret []int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetSameIpPlayer", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("GetSameIpPlayer error ", err) + return nil + } + return ret +} + +func GetSameBankNamePlayer(plt, name string) []int32 { + if rpcCli == nil { + return nil + } + args := &GetSameParamPlayerArgs{ + Plt: plt, + Param: name, + } + var ret []int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetSameBankNamePlayer", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("GetSameIpPlayer error ", err) + return nil + } + return ret +} +func GetSameBankCardPlayer(plt, card string) []int32 { + if rpcCli == nil { + return nil + } + args := &GetSameParamPlayerArgs{ + Plt: plt, + Param: card, + } + var ret []int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetSameBankCardPlayer", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("GetSameBankCardPlayer error ", err) + return nil + } + return ret +} + +func GetRobotPlayers(limit int) []*PlayerData { + if rpcCli == nil { + return nil + } + var ret []*PlayerData + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetRobotPlayers", limit, &ret, time.Second*30) + if err != nil { + logger.Logger.Info("GetSameBankCardPlayer error ", err) + return nil + } + return ret +} + +type BackupPlayerRet struct { + Err error + IsSuccess bool + Pd *PlayerData +} + +/* + * 保存玩家的删除备份全部信息 + */ +func SaveDelBackupPlayerData(pd *PlayerData) bool { + if rpcCli == nil { + logger.Logger.Error("model.SaveDelBackupPlayerData rpcCli is nil") + return false + } + if pd != nil { + var ret bool + err := rpcCli.CallWithTimeout("PlayerDelBackupDataSvc.SaveDelBackupPlayerData", pd, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.SaveDelBackupPlayerData is error", err) + return false + } + return ret + } + return false +} + +func GetDelBackupPlayerData(plt string, snid int32) *PlayerData { + if rpcCli == nil { + return nil + } + args := &PlayerIsExistBySnIdArgs{ + Plt: plt, + SnId: snid, + } + ret := new(PlayerData) + err := rpcCli.CallWithTimeout("PlayerDelBackupDataSvc.GetPlayerData", args, ret, time.Second*30) + if err != nil { + logger.Logger.Trace("GetPlayerData failed:", err) + return nil + } + return ret +} + +type LogicInfoArg struct { + SnIds []int32 + LogicLevel int32 + Platform string +} + +func SetLogicLevel(snids []int32, logicLevel int32, platform string) error { + if rpcCli == nil { + return nil + } + args := &LogicInfoArg{ + SnIds: snids, + LogicLevel: logicLevel, + Platform: platform, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.SetLogicLevel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.SetLogicLevel error ", err) + return err + } + return nil +} + +func ClrLogicLevel(snids []int32, logicLevel int32, platform string) error { + if rpcCli == nil { + return nil + } + args := &LogicInfoArg{ + SnIds: snids, + LogicLevel: logicLevel, + Platform: platform, + } + var ret bool + err := rpcCli.CallWithTimeout("PlayerDataSvc.ClrLogicLevel", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.ClrLogicLevel error ", err) + return err + } + return nil +} + +func GetPlayerInviteSnid(plt string, snid int32) (int32, error) { + if rpcCli == nil { + return 0, errors.New("rpcCli is nil") + } + args := &PlayerIsExistBySnIdArgs{ + Plt: plt, + SnId: snid, + } + var ret int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetPlayerInviteSnid", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerInviteSnid error ", err) + return 0, err + } + return ret, nil +} + +func GetInviteNum(plt string, snid int32) (int32, error) { + if rpcCli == nil { + return 0, errors.New("rpcCli is nil") + } + args := &PlayerIsExistBySnIdArgs{ + Plt: plt, + SnId: snid, + } + var ret int32 + err := rpcCli.CallWithTimeout("PlayerDataSvc.GetInviteNum", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerInviteSnid error ", err) + return 0, err + } + return ret, nil +} + +// 所有游戏都要加上当天统计数据 +func (this *PlayerData) GetDaliyGameData(id int) (*PlayerGameStatics, *PlayerGameStatics) { + gameId := strconv.Itoa(id) + if this.TodayGameData == nil { + this.TodayGameData = NewPlayerGameCtrlData() + } + if this.TodayGameData.CtrlData == nil { + this.TodayGameData.CtrlData = make(map[string]*PlayerGameStatics) + } + if _, ok := this.TodayGameData.CtrlData[gameId]; !ok { + this.TodayGameData.CtrlData[gameId] = NewPlayerGameStatics() + } + if this.YesterdayGameData == nil { + this.YesterdayGameData = NewPlayerGameCtrlData() + } + if this.YesterdayGameData.CtrlData == nil { + this.YesterdayGameData.CtrlData = make(map[string]*PlayerGameStatics) + } + if _, ok := this.YesterdayGameData.CtrlData[gameId]; !ok { + this.YesterdayGameData.CtrlData[gameId] = NewPlayerGameStatics() + } + return this.TodayGameData.CtrlData[gameId], this.YesterdayGameData.CtrlData[gameId] +} + +func (this *PlayerData) GetTodayGameData(gameFreeid int32) *PlayerGameStatics { + gameFreeStr := strconv.Itoa(int(gameFreeid)) + if this.TodayGameData == nil { + this.TodayGameData = NewPlayerGameCtrlData() + } + if this.TodayGameData.CtrlData == nil { + this.TodayGameData.CtrlData = make(map[string]*PlayerGameStatics) + } + if _, ok := this.TodayGameData.CtrlData[gameFreeStr]; !ok { + this.TodayGameData.CtrlData[gameFreeStr] = NewPlayerGameStatics() + } + return this.TodayGameData.CtrlData[gameFreeStr] +} + +func (this *PlayerData) GetYestosdayGameData(gameFreeid string) *PlayerGameStatics { + + if this.YesterdayGameData == nil { + this.YesterdayGameData = &PlayerGameCtrlData{} + } + if this.YesterdayGameData.CtrlData == nil { + this.YesterdayGameData.CtrlData = make(map[string]*PlayerGameStatics) + } + if _, ok := this.YesterdayGameData.CtrlData[gameFreeid]; !ok { + this.YesterdayGameData.CtrlData[gameFreeid] = NewPlayerGameStatics() + } + return this.YesterdayGameData.CtrlData[gameFreeid] +} + +func (this *PlayerData) HasAutoTag(tag int32) bool { + return common.InSliceInt32(this.AutomaticTags, tag) +} + +func (this *PlayerData) MarkAutoTag(tag int32) bool { + if this.HasAutoTag(tag) { + return false + } + + this.AutomaticTags = append(this.AutomaticTags, tag) + return true +} + +func (this *PlayerData) GetGameFreeIdData(id string) *PlayerGameInfo { + data, ok := this.GDatas[id] + if !ok { + data = new(PlayerGameInfo) + this.GDatas[id] = data + } + return data +} + +// WBUpdate 更新黑白名单玩家投入产出 +// 返回 溢出数量 +func (this *PlayerData) WBUpdate(gain int64) int64 { + var n int64 + clearWB := false + if this.WBLevel != 0 /*&& this.WBState > 0*/ { + if gain > 0 { + this.WBCoinTotalOut += gain + } else { + this.WBCoinTotalIn += -gain + } + if this.WBCoinLimit != 0 { + if this.WBLevel > 0 && this.WBCoinTotalOut-this.WBCoinTotalIn >= this.WBCoinLimit { //自动解除白名单 + clearWB = true + n = (this.WBCoinTotalOut - this.WBCoinTotalIn) - this.WBCoinLimit + } else if this.WBLevel < 0 && this.WBCoinTotalIn-this.WBCoinTotalOut >= this.WBCoinLimit { //自动解除黑名单 + clearWB = true + n = (this.WBCoinTotalIn - this.WBCoinTotalOut) - this.WBCoinLimit + } + } + + if this.WBMaxNum > 0 { + if this.WBLevel > 0 && gain > 0 { + this.WBMaxNum -= 100 + if this.WBMaxNum <= 0 { + clearWB = true + } + } else if this.WBLevel < 0 && gain < 0 { + this.WBMaxNum -= 100 + if this.WBMaxNum <= 0 { + clearWB = true + } + } + } + } + + if clearWB { //自动解除黑白名单 + this.WBCoinTotalIn = 0 + this.WBCoinTotalOut = 0 + this.WBCoinLimit = 0 + this.WBMaxNum = 0 + this.WBState = 0 + this.WBLevel = 0 + } + return n +} + +// 解锁炮倍 +func (this *PlayerData) UnPlayerPower(power int64) bool { + if power == 0 { + return false + } + logger.Logger.Tracef("解锁炮倍 当前最大解锁炮倍:%v,要解锁的炮倍:%v", this.UnMaxPower, power) + if this.UnMaxPower >= power { + logger.Logger.Trace("当前解锁炮倍小于最大解锁炮倍!!! snid = %v", this.SnId) + return false + } + this.UnMaxPower = power + return true +} + +// 解锁炮台 不要直接调用 调用player.UnPlayerPowerListEx()接口 +func (this *PlayerData) UnPlayerPowerList(powerId int32) bool { + //判断有没有这个炮台 + status := true + for _, id := range this.PowerList { + if id == powerId { + status = false + break + } + } + if status { + this.PowerList = append(this.PowerList, powerId) + } + return status +} + +func (this *PlayerData) GetVipShopData(id, vipShopId int32) *ShopData { + for i, data := range this.VipShopData { + if data.Id == id && vipShopId == i { + return data + } + } + return nil +} + +func (this *PlayerData) GetPoolUpper(data *webapi.PlayerPool) int64 { + if data == nil { + return 0 + } + + var gameCoin int64 + var upRate int32 + // 按不同游戏类型获取 gameCoin 变量 + // 1:百人类/2:对战类/3.拉霸类/4.捕鱼类/5.休闲类/6.小游戏类 + info := this.GDatas[common.GetKeyGameType(2)] + if info != nil { + gameCoin = info.Statics.TotalOut + info.Statics.TotalIn + } + upRate = data.GetFightUp() + //todo 其它游戏类型 + + // 上线: 初始上线+(游戏输赢绝对值)*-0.01 + 兑换金币*2;游戏输赢绝对值按游戏类型分类统计,税前 + upLine := data.GetUpperLimit() + int64(float64(gameCoin)*-float64(upRate)/1000.0+float64(this.DiamondToCoin)*float64(data.GetPayUp())/1000.0) + + return upLine +} + +func (this *PlayerData) GetPoolLower(data *webapi.PlayerPool) int64 { + if data == nil { + return 0 + } + + var gameCoin int64 + var downRate int32 + // 按不同游戏类型获取 gameCoin 变量 + // 1:百人类/2:对战类/3.拉霸类/4.捕鱼类/5.休闲类/6.小游戏类 + info := this.GDatas[common.GetKeyGameType(2)] + if info != nil { + gameCoin = info.Statics.TotalOut + info.Statics.TotalIn + } + downRate = data.GetFightDown() + //todo 其它游戏类型 + + // 下线:初始下线+(游戏输赢绝对值)*-0.06 + 兑换金币*-2;游戏输赢绝对值按游戏类型分类统计 + lowLine := data.GetLowerLimit() + int64(float64(gameCoin)*-float64(downRate)/1000.0+float64(this.DiamondToCoin)*-float64(data.GetPayDown())/1000.0) + + return lowLine +} + +func (this *PlayerData) GetPoolCurrent() int64 { + return this.TotalOut - this.PlayerTax - this.TotalIn +} + +func (this *PlayerData) GetPoolOdds(data *webapi.PlayerPool) int64 { + if data == nil { + return 0 + } + + pCoin := this.GetPoolCurrent() + lowLine := this.GetPoolLower(data) + upLine := this.GetPoolUpper(data) + + q := float64(upLine-lowLine) * float64(data.GetQuDu()) / 1000.0 + up := pCoin - upLine + low := pCoin - lowLine + + var odds int64 + if up > 0 { + odds = int64(common.SliceMaxValue([]int{int(data.GetLowerOdds()) + int(float64(up)/q*float64(data.GetLowerOddsMax()-data.GetLowerOdds())), int(data.GetLowerOddsMax())})) + } else if low < 0 { + odds = int64(common.SliceMinValue([]int{int(data.GetUpperOdds()) + int(float64(low)/q*float64(data.GetUpperOdds()-data.GetUpperOddsMax())), int(data.GetUpperOddsMax())})) + } + return odds +} + +func (this *PlayerData) GetRoleId() int32 { + if this.Roles != nil && this.Roles.ModId != 0 { + return this.Roles.ModId + } + return common.DefaultRoleId +} diff --git a/model/playerbucketid.go b/model/playerbucketid.go new file mode 100644 index 0000000..801714b --- /dev/null +++ b/model/playerbucketid.go @@ -0,0 +1,63 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" +) + +//var ( +// playerBucketId = &PlayerBucketId{} +// playerBucketIdsCursor = int32(0) +// playerBucketIdLock = sync.Mutex{} +//) + +type PlayerBucketId struct { + Id bson.ObjectId `bson:"_id"` + StartPos int32 + EndPos int32 + Used bool +} + +func (receiver *PlayerBucketId) IsValid(cursor int32) bool { + return receiver.Id.Valid() && + receiver.StartPos != 0 && + receiver.EndPos != 0 && + receiver.StartPos != receiver.EndPos && + cursor >= receiver.StartPos && + cursor <= receiver.EndPos && + cursor != 0 +} + +//func GetOnePlayerIdFromBucket() (pid int32, err error) { +// playerBucketIdLock.Lock() +// defer playerBucketIdLock.Unlock() +// +// if !playerBucketId.IsValid() { +// var args struct{} +// err := rpcCli.CallWithTimeout("PlayerBucketIdSvc.GetPlayerOneBucketId", args, &playerBucketId, time.Second*30) +// if err != nil { +// return 0, err +// } +// if !playerBucketId.IsValid() { +// return 0, err +// } +// playerBucketIdsCursor = playerBucketId.StartPos +// } +// pid = playerBucketIdsCursor +// playerBucketIdsCursor++ +// return pid, nil +//} + +//func GiveBackPlayerIdBucket() { +// playerBucketIdLock.Lock() +// defer playerBucketIdLock.Unlock() +// +// if playerBucketId.IsValid() { +// var ret bool +// err := rpcCli.CallWithTimeout("PlayerBucketIdSvc.GiveBackPlayerIdBucket", &playerBucketId, &ret, time.Second*30) +// if err != nil { +// logger.Logger.Error("GiveBackPlayerIdBucket err:", err) +// } +// playerBucketId.StartPos = 0 +// playerBucketId.EndPos = 0 +// } +//} diff --git a/model/playerrankseason.go b/model/playerrankseason.go new file mode 100644 index 0000000..d555b3d --- /dev/null +++ b/model/playerrankseason.go @@ -0,0 +1,88 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +// 玩家排位赛信息 + +type RankAward struct { + Id int32 // 奖励ID + Ts int64 // 奖励领取时间 +} + +type PlayerRankInfo struct { + Score int64 // 积分 + Awards []*RankAward // 赛季奖励 +} + +type PlayerRankSeason struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + SeasonId int32 // 赛季id + RankType map[int32]*PlayerRankInfo + LastRankType map[int32]*PlayerRankInfo + UpdateTs int64 +} + +type NewPlayerRankSeasonArgs struct { + Platform string + SnId int32 + SeasonId int32 +} + +func NewPlayerRankSeason(args *NewPlayerRankSeasonArgs) *PlayerRankSeason { + ret := &PlayerRankSeason{ + Platform: args.Platform, + SnId: args.SnId, + SeasonId: args.SeasonId, + RankType: make(map[int32]*PlayerRankInfo), + LastRankType: make(map[int32]*PlayerRankInfo), + } + ret.UpdateTs = time.Now().Unix() + return ret +} + +func UpsertPlayerRankSeason(args *PlayerRankSeason) bool { + if rpcCli == nil { + logger.Logger.Error("model.UpsertPlayerRankSeason rpcCli == nil") + return false + } + + var ret bool + args.UpdateTs = time.Now().Unix() + err := rpcCli.CallWithTimeout("PlayerRankSeasonSvc.Upsert", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("PlayerRankSeasonSvc.Upsert error:", err) + return false + } + return ret +} + +type FindPlayerRankSeasonArgs struct { + Platform string + Id []int32 +} + +type FindPlayerRankSeasonReply struct { + List []*PlayerRankSeason +} + +func FindPlayerRankSeason(args *FindPlayerRankSeasonArgs) (*FindPlayerRankSeasonReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindPlayerRankSeason rpcCli == nil") + return nil, nil + } + + ret := new(FindPlayerRankSeasonReply) + err := rpcCli.CallWithTimeout("PlayerRankSeasonSvc.Find", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindPlayerRankSeason.Find error:", err) + return ret, err + } + return ret, nil +} diff --git a/model/playersingleadjust.go b/model/playersingleadjust.go new file mode 100644 index 0000000..8d4e2f4 --- /dev/null +++ b/model/playersingleadjust.go @@ -0,0 +1,160 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "time" +) + +type PlayerSingleAdjust struct { + Platform string + Id bson.ObjectId `bson:"_id"` + GameFreeId int32 + SnId int32 + Mode int32 //调控模式 1赢 2输 tinyint(1) + TotalTime int32 //调控总次数 + CurTime int32 //当前调控次数 + BetMin int64 //下注下限 + BetMax int64 //下注上限 + BankerLoseMin int64 //坐庄被输下限 + BankerWinMin int64 //坐庄被控赢下限 + CardMin int32 //牌型下限 + CardMax int32 //牌型上限 + Priority int32 //优先级 + WinRate int32 //万分比 + GameId int32 + GameMode int32 + Operator string + CreateTime int64 + UpdateTime int64 +} +type SingleAdjustRet struct { + Ret []*PlayerSingleAdjust +} + +func QueryAllSingleAdjust(platfrom string) (pass []*PlayerSingleAdjust, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryAllSingleAdjust rpcCli == nil") + return + } + var ret *SingleAdjustRet + err = rpcCli.CallWithTimeout("SingleAdjustSvc.QueryAllSingleAdjust", platfrom, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("QueryAllSingleAdjust error:", err) + } + if ret != nil { + pass = ret.Ret + } + return +} + +type SingleAdjustByKey struct { + Platform string + SnId int32 +} + +func QueryAllSingleAdjustByKey(platform string, snid int32) (pass []*PlayerSingleAdjust, err error) { + if rpcCli == nil { + logger.Logger.Error("model.QueryAllSingleAdjust rpcCli == nil") + return + } + args := &SingleAdjustByKey{ + Platform: platform, + SnId: snid, + } + var ret *SingleAdjustRet + err = rpcCli.CallWithTimeout("SingleAdjustSvc.QueryAllSingleAdjustByKey", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("QueryAllSingleAdjust error:", err) + } + if ret != nil { + pass = ret.Ret + } + return +} +func MarshalSingleAdjust(pas *PlayerSingleAdjust) []byte { + if pas != nil { + data, err := netlib.Gob.Marshal(pas) + if err == nil { + return data + } + logger.Logger.Warn("model.MarshalSingleAdjust err: ", err) + } + return nil +} +func UnmarshalSingleAdjust(data []byte) (psa *PlayerSingleAdjust) { + if data != nil { + err := netlib.Gob.Unmarshal(data, &psa) + if err != nil { + logger.Logger.Warn("model.UnmarshalSingleAdjust err: ", err) + return nil + } + } + return +} + +func WebSingleAdjustToModel(psa *webapi.PlayerSingleAdjust) *PlayerSingleAdjust { + psa_tmp := &PlayerSingleAdjust{ + Platform: psa.Platform, + GameFreeId: psa.GameFreeId, + SnId: psa.SnId, + Mode: psa.Mode, + TotalTime: psa.TotalTime, + CurTime: psa.CurTime, + BetMin: psa.BetMin, + BetMax: psa.BetMax, + BankerLoseMin: psa.BankerLoseMin, + BankerWinMin: psa.BankerWinMin, + CardMin: psa.CardMin, + CardMax: psa.CardMax, + Priority: psa.Priority, + WinRate: psa.WinRate, + GameId: psa.GameId, + GameMode: psa.GameMode, + Operator: psa.Operator, + CreateTime: psa.CreateTime, + UpdateTime: psa.UpdateTime, + } + if psa.Id != "" { + psa_tmp.Id = bson.ObjectIdHex(psa.Id) + } + return psa_tmp +} +func AddNewSingleAdjust(args *PlayerSingleAdjust) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.AddNewSingleAdjust rpcCli == nil") + return + } + var ret bool + err = rpcCli.CallWithTimeout("SingleAdjustSvc.AddNewSingleAdjust", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("AddNewSingleAdjust error:", err) + } + return +} +func EditSingleAdjust(args *PlayerSingleAdjust) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.EditSingleAdjust rpcCli == nil") + return + } + var ret bool + err = rpcCli.CallWithTimeout("SingleAdjustSvc.EditSingleAdjust", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("EditSingleAdjust error:", err) + } + return +} +func DeleteSingleAdjust(args *PlayerSingleAdjust) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.DeleteSingleAdjust rpcCli == nil") + return + } + var ret bool + err = rpcCli.CallWithTimeout("SingleAdjustSvc.DeleteSingleAdjust", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("DeleteSingleAdjust error:", err) + } + return +} diff --git a/model/privatescenelog.go b/model/privatescenelog.go new file mode 100644 index 0000000..acd7177 --- /dev/null +++ b/model/privatescenelog.go @@ -0,0 +1,80 @@ +package model + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "time" +) + +var ( + PrivateSceneLogDBName = "log" + PrivateSceneLogCollName = "log_privatescene" +) + +type PrivateSceneLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 // 玩家id + Platform string // 平台名称 + Channel string // 渠道名称 + Promoter string // 推广员 + PackageTag string // 推广包标识 + GameFreeId int32 // 游戏id + SceneId int32 // 房间id + CreateFee int32 // 房费 + CreateTime time.Time // 创建时间 + DestroyTime time.Time // 销毁时间 +} + +func NewPrivateSceneLog() *PrivateSceneLog { + return &PrivateSceneLog{ + LogId: bson.NewObjectId(), + } +} + +func InsertPrivateSceneLog(log *PrivateSceneLog) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + var ret bool + return rpcCli.CallWithTimeout("PrivateSceneLogSvc.InsertPrivateSceneLog", log, &ret, time.Second*30) +} + +type GetPrivateSceneLogBySnIdArgs struct { + Plt string + SnId int32 + Limit int +} + +func GetPrivateSceneLogBySnId(plt string, snid int32, limit int) ([]*PrivateSceneLog, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetPrivateSceneLogBySnIdArgs{ + Plt: plt, + SnId: snid, + Limit: limit, + } + var logs []*PrivateSceneLog + err := rpcCli.CallWithTimeout("PrivateSceneLogSvc.GetPrivateSceneLogBySnId", args, &logs, time.Second*30) + return logs, err +} + +type RemovePrivateSceneLogsArgs struct { + Plt string + Ts time.Time +} + +func RemovePrivateSceneLogs(plt string, ts time.Time) (*mgo.ChangeInfo, error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &RemovePrivateSceneLogsArgs{ + Plt: plt, + Ts: ts, + } + var ret *mgo.ChangeInfo + err := rpcCli.CallWithTimeout("PrivateSceneLogSvc.InsertPrivateSceneLog", args, &ret, time.Second*30) + return ret, err +} diff --git a/model/rabbit_mq.go b/model/rabbit_mq.go new file mode 100644 index 0000000..0a8ad2c --- /dev/null +++ b/model/rabbit_mq.go @@ -0,0 +1,89 @@ +package model + +import ( + "strconv" + "time" + + "mongo.games.com/game/mq" +) + +// GenerateOnline 在线统计 +func GenerateOnline(online map[string]int) *RabbitMQData { + m := map[int]int{} // 平台:真人数 + for k, v := range online { + i, _ := strconv.Atoi(k) + m[i] = v + } + params := make(map[string]interface{}) + params["Online"] = m + params["Time"] = time.Now().Unix() + return NewRabbitMQData(mq.BackOnline, params) +} + +// GenerateLogin 玩家登陆事件 +func GenerateLogin(o *PlayerLoginEvent) *RabbitMQData { + return NewRabbitMQData(mq.BackLogin, o) +} + +// GenerateGameEvent 游戏结算事件 +func GenerateGameEvent(o *PlayerGameRecEvent) *RabbitMQData { + return NewRabbitMQData(mq.BackGameRecord, o) +} + +// GenerateSystemFreeGive 系统免费赠送 +func GenerateSystemFreeGive(snid int32, name, platform string, givetype, cointype int32, count int64) *RabbitMQData { + params := make(map[string]string) + params["snid"] = strconv.Itoa(int(snid)) + params["name"] = name + params["platform"] = platform + params["givetype"] = strconv.Itoa(int(givetype)) //0创号赠送 1签到赠送(转盘、签到、累签) 2商城观看视频赠送 3破产补助 4vip领取 + params["cointype"] = strconv.Itoa(int(cointype)) //货币类型 0金币 1钻石 + params["count"] = strconv.Itoa(int(count)) + params["ts"] = strconv.Itoa(int(time.Now().Unix())) + return NewRabbitMQData(mq.BackSystemFreeGive, params) +} + +// GeneratePhoneLottery 手机抽奖统计 +func GeneratePhoneLottery(snid int32, platform string, items string, lotteryType, count, countType, score int) *RabbitMQData { + params := make(map[string]string) + params["Snid"] = strconv.Itoa(int(snid)) + params["Platform"] = platform + params["LotteryType"] = strconv.Itoa(lotteryType) //1-抽奖获得 2-兑换 3-抽奖次数 + params["Items"] = items + params["Score"] = strconv.Itoa(score) //兑换消耗积分 + params["CountType"] = strconv.Itoa(countType) //1-首次登录 2-玩游戏 3-输赢送 4-充值送 5-初始送 + params["Count"] = strconv.Itoa(count) + params["Ts"] = strconv.Itoa(int(time.Now().Unix())) + return NewRabbitMQData(mq.BackPhoneLottery, params) +} + +//type PlayerGameEntryEvent struct { +// RecordId string //游戏记录ID +// SnId int32 //用户ID +// Platform int32 //平台 +// OS int //0 Windows 1 Android 2 iOS +// GameId int //游戏id +// ModeId int //游戏模式 +// Time int64 //入场时间 RFC3339 +// Id int32 //游戏id +//} + +// GenerateEnterEvent 玩家或观众进场事件 +//func GenerateEnterEvent(recordId string, snId int32, platform, os string, gameId, modeId int, gameFreeId int32) *RabbitMQData { +// m := &PlayerGameEntryEvent{ +// RecordId: recordId, +// SnId: snId, +// GameId: gameId, +// ModeId: modeId, +// Time: time.Now().Unix(), +// Id: gameFreeId, +// } +// pf, err := strconv.Atoi(platform) +// if err != nil { +// logger.Error(err) +// return nil +// } +// m.Platform = int32(pf) +// m.OS = common.DeviceNum[os] +// return NewRabbitMQData(string(MqNameEnterEvent), m) +//} diff --git a/model/rabbitmqdata.go b/model/rabbitmqdata.go new file mode 100644 index 0000000..0ca93ac --- /dev/null +++ b/model/rabbitmqdata.go @@ -0,0 +1,14 @@ +package model + +type RabbitMQData struct { + MQName string // 是队列名称也是Routing key + Data interface{} +} + +func NewRabbitMQData(mqName string, data interface{}) *RabbitMQData { + log := &RabbitMQData{ + MQName: mqName, + Data: data, + } + return log +} diff --git a/model/rank.go b/model/rank.go new file mode 100644 index 0000000..8b9a6a1 --- /dev/null +++ b/model/rank.go @@ -0,0 +1,210 @@ +package model + +import ( + "time" + + "mongo.games.com/goserver/core/logger" +) + +const ( + MQRankSeason = "log_rankplayerscore" + MQRankPlayerCoin = "log_rankplayercoin" + MQRankPlayerInvite = "log_rankplayerinvite" +) + +type PlayerRankScore struct { + //Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + RankType int32 // 排位类型 + SeasonId int32 // 赛季id + Lv int32 // 段位 + Score int64 // 排位积分 + Name string + Sex int32 + HeadUrl string + Coin int64 + IsRobot bool + UpdateTs int64 + ModId int32 +} + +func UpsertPlayerRankSeasonList(args *PlayerRankScore) bool { + if rpcCli == nil { + logger.Logger.Error("model.UpsertPlayerRankSeason rpcCli == nil") + return false + } + + var ret bool + args.UpdateTs = time.Now().Unix() + err := rpcCli.CallWithTimeout("RankSeasonSvc.Upsert", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertPlayerRankSeasonList error:", err) + return false + } + return ret +} + +type FindPlayerRankSeasonListArgs struct { + Platform string + RankType int32 // 排位类型 + //SeasonId int32 // 赛季id +} + +type FindPlayerRankSeasonListReply struct { + List []*PlayerRankScore +} + +func FindPlayerRankSeasonList(args *FindPlayerRankSeasonListArgs) (*FindPlayerRankSeasonListReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindPlayerRankSeasonList rpcCli == nil") + return nil, nil + } + + ret := new(FindPlayerRankSeasonListReply) + err := rpcCli.CallWithTimeout("RankSeasonSvc.FindRankSeason", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindPlayerRankSeasonList error:", err) + return ret, err + } + return ret, nil +} + +type RankPlayerCoin struct { + Platform string + SnId int32 + Name string + Sex int32 + HeadUrl string + Coin int64 + UpdateTs int64 + ModId int32 +} + +func UpsertRankPlayerCoin(args *RankPlayerCoin) bool { + if rpcCli == nil { + logger.Logger.Error("model.UpsertRankPlayerCoin rpcCli == nil") + return false + } + + var ret bool + err := rpcCli.CallWithTimeout("RankPlayerCoinSvc.Upsert", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("UpsertRankPlayerCoin error:", err) + return false + } + return ret +} + +type FindPlayerCoinListArgs struct { + Platform string +} + +type FindPlayerCoinListReply struct { + List []*RankPlayerCoin +} + +func FindPlayerCoinList(args *FindPlayerCoinListArgs) (*FindPlayerCoinListReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindPlayerCoinList rpcCli == nil") + return nil, nil + } + + ret := new(FindPlayerCoinListReply) + err := rpcCli.CallWithTimeout("RankPlayerCoinSvc.Find", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindPlayerCoinList error:", err) + return ret, err + } + return ret, nil +} + +type PlayerRankInvite struct { + //Id bson.ObjectId `bson:"_id"` + SnId int32 + Score int64 // 邀请积分 + InviteNum int32 // 邀请人数 + Name string // 玩家名字 + ModId int32 // 头像ID +} + +type FindPlayerRankInviteListArgs struct { + Platform string + RankType int32 // 邀请榜时间类型: 总榜 周榜 月榜 +} + +type FindPlayerRankInviteListReply struct { + RankType int32 // 邀请榜时间类型: 总榜 周榜 月榜 + List []*PlayerRankInvite +} + +func FindPlayerRankInviteList(args *FindPlayerRankInviteListArgs) (*FindPlayerRankInviteListReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindPlayerRankInviteList rpcCli == nil") + return nil, nil + } + + ret := new(FindPlayerRankInviteListReply) + err := rpcCli.CallWithTimeout("BindScoreSvc.GetInviteRankList", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindPlayerRankInviteList error:", err) + return ret, err + } + return ret, nil +} + +type WinCoinInfo struct { + SnId int32 + Name string + Coin int64 + ModId int32 +} + +type FindWinCoinListArgs struct { + Platform string + StartTs, EndTs int64 +} + +type FindWinCoinListReply struct { + List []*WinCoinInfo +} + +func FindWinCoinListTienlen(args *FindWinCoinListArgs) (*FindWinCoinListReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindWinCoinListTienlen rpcCli == nil") + return nil, nil + } + + ret := new(FindWinCoinListReply) + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetWinCoinListTienlen", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindWinCoinList error:", err) + return ret, err + } + return ret, nil +} + +type FindWinCoinArgs struct { + Platform string + StartTs, EndTs int64 + SnId int32 +} + +type FindWinCoinReply struct { + List *WinCoinInfo +} + +func FindWinCoinTienlen(args *FindWinCoinArgs) (*FindWinCoinReply, error) { + if rpcCli == nil { + logger.Logger.Error("model.FindWinCoinTienlen rpcCli == nil") + return nil, nil + } + + ret := new(FindWinCoinReply) + err := rpcCli.CallWithTimeout("GamePlayerListSvc.GetWinCoinTienlen", args, ret, time.Second*30) + if err != nil { + logger.Logger.Error("FindWinCoinTienlen error:", err) + return ret, err + } + return ret, nil +} diff --git a/model/rankdata.go b/model/rankdata.go new file mode 100644 index 0000000..3850472 --- /dev/null +++ b/model/rankdata.go @@ -0,0 +1,95 @@ +package model + +//排行榜数据 +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "sort" + "time" +) + +type RankPlayerData struct { + SnId int32 + Name string + Val int64 +} + +type RankData struct { + Id bson.ObjectId + Key string + Data []*RankPlayerData //排行榜内已排好顺序的玩家信息 +} + +// 适用于小榜,比如总榜数量<=100,大榜适合用跳表实现 +func (rd *RankData) Insert(data *RankPlayerData, add bool) { + cnt := len(rd.Data) + idx := sort.Search(cnt, func(i int) bool { + return rd.Data[i].Val <= data.Val + }) + if add { + rd.Data = append(rd.Data, nil) + } + if idx != cnt { + copy(rd.Data[idx+1:], rd.Data[idx:cnt]) + } + cnt = len(rd.Data) + //索引确保 + if idx >= cnt { + idx = cnt - 1 + } + //当前数据放到idx位置 + rd.Data[idx] = data +} + +func (rd *RankData) Sort() { + less := func(i, j int) bool { //倒序 + return rd.Data[i].Val > rd.Data[j].Val + } + if !sort.SliceIsSorted(rd.Data, less) { + sort.Slice(rd.Data, less) + } +} + +func (rd *RankData) Clone() *RankData { + nrd := &RankData{ + Id: rd.Id, + Key: rd.Key, + Data: make([]*RankPlayerData, len(rd.Data)), + } + copy(nrd.Data, rd.Data) + return nrd +} + +func InitRankData(plt string) []*RankData { + if rpcCli == nil { + return nil + } + var data []*RankData + err := rpcCli.CallWithTimeout("RankDataSvc.InitRankData", plt, &data, time.Second*30) + if err != nil { + logger.Logger.Errorf("InitRankDBData err:%v", err) + return nil + } + + return data +} + +type SaveRankDataArgs struct { + Plt string + Key string + Data *RankData +} + +func SaveRankData(plt, key string, data *RankData) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &SaveRankDataArgs{ + Plt: plt, + Key: key, + Data: data, + } + var ret bool + return rpcCli.CallWithTimeout("RankDataSvc.SaveRankData", args, &ret, time.Second*30) +} diff --git a/model/rankseason.go b/model/rankseason.go new file mode 100644 index 0000000..799d5c5 --- /dev/null +++ b/model/rankseason.go @@ -0,0 +1,59 @@ +package model + +import ( + "time" + + "mongo.games.com/goserver/core/logger" +) + +type RankSeasonId struct { + //Id bson.ObjectId `bson:"_id"` + Platform string + SeasonId int32 //赛季id + StartTs int64 //开始时间戳 + EndTs int64 //结束时间戳 + UpdateTs int64 //更新时间戳 +} + +type RankSeasonIdRet struct { + MsId *RankSeasonId +} + +type RankSeasonIdByKey struct { + Platform string +} + +func UpsertRankSeasonId(RankSeasonId *RankSeasonId) bool { + if rpcCli == nil { + logger.Logger.Error("model.UpsertRankSeasonId rpcCli == nil") + return false + } + + var ret bool + RankSeasonId.UpdateTs = time.Now().Unix() + err := rpcCli.CallWithTimeout("RankSeasonIdSvc.Upsert", RankSeasonId, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.UpsertRankSeasonId error:", err) + return false + } + return ret +} + +func FindOneRankSeasonId(platform string) (RankSeasonId *RankSeasonId, err error) { + if rpcCli == nil { + logger.Logger.Error("model.FindOneRankSeasonId rpcCli == nil") + return + } + args := &RankSeasonIdByKey{ + Platform: platform, + } + var ret *RankSeasonIdRet + err = rpcCli.CallWithTimeout("RankSeasonIdSvc.FindOne", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Error("model.FindOneRankSeasonId error:", err) + } + if ret != nil { + RankSeasonId = ret.MsId + } + return +} diff --git a/model/rebatetasklog.go b/model/rebatetasklog.go new file mode 100644 index 0000000..4bb070f --- /dev/null +++ b/model/rebatetasklog.go @@ -0,0 +1,69 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "time" +) + +// //////////////////////////////////////////////////俱乐部 +var ( + RebateLogDBName = "log" + RebateLogCollName = "log_rebatetask" +) + +type Rebate struct { + Id bson.ObjectId `bson:"_id"` //记录ID + SnId int32 //玩家Id + CodeCoin int64 //洗码量 + RebateCoin int64 //洗码金额 + ReceiveType int32 //领取方式 + Ts int64 +} + +type InsertRebateLogArgs struct { + Log *Rebate + Plt string +} + +func InsertRebateLog(plt string, data *Rebate) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + + args := &InsertRebateLogArgs{ + Plt: plt, + Log: data, + } + var ret bool + return rpcCli.CallWithTimeout("RebateLogSvc.InsertRebateLog", args, &ret, time.Second*30) +} + +type RebateLog struct { + Rebates []*Rebate + PageNo int + PageSize int + PageSum int +} + +type GetRebateLogArgs struct { + Plt string + PageNo int + PageSize int + SnId int32 +} + +func GetRebateLog(plt string, pageNo, pageSize int, snId int32) (r *RebateLog, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + args := &GetRebateLogArgs{ + Plt: plt, + PageNo: pageNo, + PageSize: pageSize, + SnId: snId, + } + r = new(RebateLog) + err = rpcCli.CallWithTimeout("RebateLogSvc.GetRebateLog", args, &r, time.Second*30) + return +} diff --git a/model/relieffundlog.go b/model/relieffundlog.go new file mode 100644 index 0000000..a0ec914 --- /dev/null +++ b/model/relieffundlog.go @@ -0,0 +1,43 @@ +package model + +import "time" +import "github.com/globalsign/mgo/bson" + +// 破产金领取详情 +var ( + ReliefFundLogCollName = "log_relieffund" +) + +// 破产金领取详情 +type ReliefFundLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + + CreateTime int64 // 注册时间 + Ts int64 // 领取时间 + GetType int32 // 领取方式 4:广告 5:破产补助 + CoinType int32 //货币类型 0金币 1钻石 + GetAmount int64 // 领取金额 +} + +func NewReliefFundLog() *ReliefFundLog { + log := &ReliefFundLog{LogId: bson.NewObjectId()} + return log +} + +func NewReliefFundLogEx(snid int32, gettype, cointype int32, getamount, createtime int64, platform string) *ReliefFundLog { + cl := NewReliefFundLog() + cl.SnId = snid + cl.Platform = platform + + now := time.Now() + cl.Ts = now.Unix() + cl.CreateTime = createtime + + cl.GetType = gettype + cl.CoinType = cointype + cl.GetAmount = getamount + + return cl +} diff --git a/model/replayid.go b/model/replayid.go new file mode 100644 index 0000000..9d2182f --- /dev/null +++ b/model/replayid.go @@ -0,0 +1,9 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" +) + +func GetOneReplayId() (pid string, err error) { + return bson.NewObjectId().Hex(), nil +} diff --git a/model/safeboxlog.go b/model/safeboxlog.go new file mode 100644 index 0000000..0709e6b --- /dev/null +++ b/model/safeboxlog.go @@ -0,0 +1,142 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" +) + +var ( + SafeBoxDBName = "log" + SafeBoxCollName = "log_safeboxrec" +) + +const ( + SafeBoxLogType_Save int32 = iota + SafeBoxLogType_TakeOut + SafeBoxLogType_Exchange + SafeBoxLogType_Op +) + +type SafeBoxRec struct { + Id bson.ObjectId `bson:"_id"` + Platform string //平台id + Channel string //平台 + Promoter string //推广员 + UserId int32 //用户id + Count int64 //存取金额 + BeforeSafeBox int64 //操作前保险箱金额 + AfterSafeBox int64 //操作后保险箱金额 + BeforeCoin int64 //操作前钱包金币 + AfterCoin int64 //操作后钱包金币 + LogType int32 //操作类型 + IP string //ip + Oper string //操作者 + Time time.Time //操作时间 +} + +func InsertSafeBox(userid int32, opcount, beforesafebox, aftersafebox, beforecoin, aftercoin int64, logtype int32, + ts time.Time, ip, oper, platform, channel, promoter string) (error, bson.ObjectId) { + if rpcCli == nil { + return ErrRPClientNoConn, bson.ObjectId("") + } + + gr := &SafeBoxRec{} + gr.Id = bson.NewObjectId() + gr.UserId = userid + gr.Time = ts + gr.IP = ip + gr.Oper = oper + gr.Count = opcount + gr.BeforeSafeBox = beforesafebox + gr.AfterSafeBox = aftersafebox + gr.BeforeCoin = beforecoin + gr.AfterCoin = aftercoin + gr.LogType = logtype + gr.Platform = platform + gr.Channel = channel + gr.Promoter = promoter + var ret bool + err := rpcCli.CallWithTimeout("SafeBoxRecSvc.InsertSafeBox", gr, &ret, time.Second*30) + if err != nil { + return err, bson.ObjectId("") + } + return nil, gr.Id +} + +type GetSafeBoxsArgs struct { + Plt string + SnId int32 +} + +func GetSafeBoxs(plt string, userid int32) (recs []SafeBoxRec, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetSafeBoxsArgs{ + Plt: plt, + SnId: userid, + } + + err = rpcCli.CallWithTimeout("SafeBoxRecSvc.GetSafeBoxs", args, &recs, time.Second*30) + if err != nil { + return nil, err + } + return +} + +type RemoveSafeBoxsArgs struct { + Plt string + Ts time.Time +} + +func RemoveSafeBoxs(plt string, ts time.Time) (info *mgo.ChangeInfo, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &RemoveSafeBoxsArgs{ + Plt: plt, + Ts: ts, + } + + err = rpcCli.CallWithTimeout("SafeBoxRecSvc.RemoveSafeBoxs", args, &info, time.Second*30) + return +} + +type RemoveSafeBoxCoinLogArgs struct { + Plt string + Id bson.ObjectId +} + +func RemoveSafeBoxCoinLog(plt string, id bson.ObjectId) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + args := &RemoveSafeBoxCoinLogArgs{ + Plt: plt, + Id: id, + } + + var ret bool + err = rpcCli.CallWithTimeout("SafeBoxRecSvc.RemoveSafeBoxCoinLog", args, &ret, time.Second*30) + return +} + +type GetSafeBoxCoinLogArgs struct { + Plt string + Ts time.Time +} + +func GetSafeBoxCoinLog(plt string, ts time.Time) (recs []SafeBoxRec, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + args := &GetSafeBoxCoinLogArgs{ + Plt: plt, + Ts: ts, + } + + err = rpcCli.CallWithTimeout("SafeBoxRecSvc.GetSafeBoxCoinLog", args, &recs, time.Second*30) + return +} diff --git a/model/scenecoinlog.go b/model/scenecoinlog.go new file mode 100644 index 0000000..012813a --- /dev/null +++ b/model/scenecoinlog.go @@ -0,0 +1,91 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +// 每一局游戏记录log +var ( + SceneCoinLogDBName = "log" + SceneCoinLogCollName = "log_scenecoin" +) + +type SceneCoinLog struct { + LogId bson.ObjectId `bson:"_id"` + Platform string //平台id + Channel string //渠道 + Promoter string //推广员 + PackageTag string //推广包标识 + SnId int32 //玩家id + ChangeCoin int64 //变化金额 + OldCoin int64 //该局游戏前金额 + NewCoin int64 //当前游戏金币 + TotalBet int64 //下注额 + BaseRate int64 //底注 + GameId int32 //游戏类型 + EventType int64 //事件类型 输赢 + Ip string //IP + GameIdex int32 //房间id + SceneId int32 //场景ID + GameMode int32 //游戏类型 + GameFreeid int32 //游戏类型房间号 + TaxCoin int64 //税收 + WinCoin int64 //赢的总钱数,扣税之前 + JackpotWinCoin int64 //爆奖金额 + SmallGameWinCoin int64 //小游戏赢的钱 + SeatId int //座位号 + Time time.Time + Ts int32 +} + +func NewSceneCoinLog() *SceneCoinLog { + log := &SceneCoinLog{LogId: bson.NewObjectId()} + return log +} + +func NewSceneCoinLogEx(snid int32, changecoin, oldcoin, newcoin, eventtype, baserate, totalbet int64, gameid int32, ip string, gameidex int32, seatid int, platform, channel, promoter string, sceneid, gamemode, gamefreeid int32, taxcoin, wincoin, jackpotWinCoin, smallGameWinCoin int64, packageid string) *SceneCoinLog { + cl := NewSceneCoinLog() + cl.SnId = snid + cl.ChangeCoin = changecoin + cl.OldCoin = oldcoin + cl.NewCoin = newcoin + cl.TotalBet = totalbet + cl.BaseRate = baserate + cl.GameId = gameid + cl.GameIdex = gameidex + cl.SeatId = seatid + cl.EventType = eventtype + cl.Ip = ip + tNow := time.Now() + cl.Ts = int32(tNow.Unix()) + cl.Time = tNow + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.SceneId = sceneid + cl.GameFreeid = gamefreeid + cl.GameMode = gamemode + cl.TaxCoin = taxcoin + cl.WinCoin = wincoin + cl.JackpotWinCoin = jackpotWinCoin + cl.SmallGameWinCoin = smallGameWinCoin + cl.PackageTag = packageid + return cl +} + +func InsertSceneCoinLog(log *SceneCoinLog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertSceneCoinLog rpcCli == nil") + return + } + + var ret bool + err = rpcCli.CallWithTimeout("SceneCoinLogSvc.InsertSceneCoinLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertSceneCoinLog error:", err) + } + return +} diff --git a/model/smart.go b/model/smart.go new file mode 100644 index 0000000..14e71c0 --- /dev/null +++ b/model/smart.go @@ -0,0 +1,99 @@ +package model + +import "mongo.games.com/game/common" + +type SmartConfig struct { + SwitchPlatform []string // 智能化运营平台开关 如: ["-1"] 所有平台不启用,["1","2"] 平台1和平台2开启 + SwitchABTestTailFilter []int32 // 智能化运营,玩家尾号限定 如:[] 表示不限定尾号 , [1,5,7]表示只有尾号是1,5,7的用户进入ABtest + SwitchABTestSnIdFilter []int32 // 智能化运营,玩家限定 如:[] 表示不限定号 , [205123,205124]表示只有205123,205124用户进入ABtest + ABTestTick int32 // AB测试开始tick + SwitchPlatformABTestTick []string // 智能化运营”ABTestTick“平台开关 如:[] 全平台开启 ["-1"] 所有平台不启用,["1","2"] 平台1和平台2启用 + SwitchPlatformABTestTickSnIdFilter []int32 // AB测试智能化运营,玩家尾号限定 如:[] 表示不限定号 , [205123,205124]表示只有205123,205124用户进入ABtest +} + +// Switch 对战场开关状态 +func (c *SmartConfig) Switch(platform string, snid int32) bool { + n := len(c.SwitchPlatform) + if n > 0 && c.SwitchPlatform[0] == "-1" { + return false + } + if n == 0 { + return true + } + + has := false + for i := 0; i < n; i++ { + if c.SwitchPlatform[i] == platform { + has = true + break + } + } + if !has { + return false + } + //指定账号 + if len(c.SwitchABTestSnIdFilter) != 0 { + if common.InSliceInt32(c.SwitchABTestSnIdFilter, snid) { + return true + } + return false + } + //指定尾号 + if len(c.SwitchABTestTailFilter) != 0 { + if common.InSliceInt32(c.SwitchABTestTailFilter, snid%10) { + return true + } + return false + } + return true +} + +func (c *SmartConfig) SwitchTick(platform string, snid int32) bool { + n := len(c.SwitchPlatformABTestTick) + if n > 0 && c.SwitchPlatformABTestTick[0] == "-1" { + return true + } + + if n == 0 { + return false + } + + has := false + for i := 0; i < n; i++ { + if c.SwitchPlatformABTestTick[i] == platform { + has = true + } + } + + if !has { + return false + } + //指定账号 + if len(c.SwitchPlatformABTestTickSnIdFilter) != 0 { + if common.InSliceInt32(c.SwitchPlatformABTestTickSnIdFilter, snid%10) { + return true + } + return false + } + return false +} + +// SwitchHundred 百人场开关状态 +func (c *SmartConfig) SwitchHundred(platform string) bool { + n := len(c.SwitchPlatform) + if n > 0 && c.SwitchPlatform[0] == "-1" { + return false + } + if n == 0 { + return true + } + + has := false + for i := 0; i < n; i++ { + if c.SwitchPlatform[i] == platform { + has = true + break + } + } + return has +} diff --git a/model/sysprofitcoin.go b/model/sysprofitcoin.go new file mode 100644 index 0000000..d6aca68 --- /dev/null +++ b/model/sysprofitcoin.go @@ -0,0 +1,53 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" +) + +var ( + SysProfitCoinDBName = "user" + SysProfitCoinCollName = "user_sysprofitcoin" +) + +type SysProfitCoin struct { + LogId bson.ObjectId `bson:"_id"` //记录ID + Key string + ProfitCoin map[string]*SysCoin //系统收益 +} + +type SysCoin struct { + PlaysBet int64 // 玩家总下注 + SysPushCoin int64 // 系统总产出 + //CommonPool int64 // 公共产出池 部分游戏用到 + //Version int32 // 数据版本号 +} + +var sysProfitCoin = &SysProfitCoin{ + ProfitCoin: make(map[string]*SysCoin), +} + +func InitSysProfitCoinData(key string) *SysProfitCoin { + if rpcCli != nil { + var data *SysProfitCoin + err := rpcCli.CallWithTimeout("SysProfitCoinSvc.InitSysProfitCoinData", key, &data, time.Second*30) + if err != nil { + return sysProfitCoin + } + sysProfitCoin = data + if sysProfitCoin.ProfitCoin == nil { + sysProfitCoin.ProfitCoin = make(map[string]*SysCoin) + } + } + return sysProfitCoin +} + +func SaveSysProfitCoin(data *SysProfitCoin) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + sysProfitCoin = data + var ret bool + return rpcCli.CallWithTimeout("SysProfitCoinSvc.SaveSysProfitCoin", sysProfitCoin, &ret, time.Second*30) +} diff --git a/model/thdplatform.go b/model/thdplatform.go new file mode 100644 index 0000000..0cdde21 --- /dev/null +++ b/model/thdplatform.go @@ -0,0 +1,67 @@ +package model + +import ( + "github.com/globalsign/mgo/bson" + "time" +) + +const ( + THDPLATFORM_DG = "dg" +) + +var ( + ThdPlatformDBName = "user" + ThdPlatformCollName = "user_thdplatform" +) + +type ThirdPlatform struct { + Coin int64 //当前额度 + NextCoin int64 //下月额度 +} + +type PlatformOfThirdPlatform struct { + Id bson.ObjectId `bson:"_id"` + Platform string + ThdPlatform map[string]*ThirdPlatform + CreateTime time.Time + LastTime time.Time +} + +func NewThirdPlatform(platform string) *PlatformOfThirdPlatform { + tNow := time.Now() + log := &PlatformOfThirdPlatform{ + Id: bson.NewObjectId(), + Platform: platform, + ThdPlatform: make(map[string]*ThirdPlatform), + CreateTime: tNow, + LastTime: tNow, + } + return log +} + +func InsertThirdPlatform(platforms ...*PlatformOfThirdPlatform) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err = rpcCli.CallWithTimeout("PlatformOfThirdPlatformSvc.InsertThirdPlatform", platforms, &ret, time.Second*30) + return +} + +func UpdateThirdPlatform(platform *PlatformOfThirdPlatform) (err error) { + if rpcCli == nil { + return ErrRPClientNoConn + } + var ret bool + err = rpcCli.CallWithTimeout("PlatformOfThirdPlatformSvc.UpdateThirdPlatform", platform, &ret, time.Second*30) + return +} + +func GetAllThirdPlatform() (ret []PlatformOfThirdPlatform, err error) { + if rpcCli == nil { + return nil, ErrRPClientNoConn + } + + err = rpcCli.CallWithTimeout("PlatformOfThirdPlatformSvc.GetAllThirdPlatform", struct{}{}, &ret, time.Second*30) + return +} diff --git a/model/thridPlatformGameMappingConfig.go b/model/thridPlatformGameMappingConfig.go new file mode 100644 index 0000000..48b20c3 --- /dev/null +++ b/model/thridPlatformGameMappingConfig.go @@ -0,0 +1,45 @@ +package model + +import ( + "encoding/json" + "mongo.games.com/goserver/core/logger" + "os" +) + +var ( + ThirdPltGameMappingConfig = ThirdPlatformGameMappingConfiguration{} +) + +var GameMappingPath = "../data/DB_ThirdPlatformGameMapping.json" + +type ThirdPlatformGameMappingConfiguration struct { + Arr []mappingItem +} +type mappingItem struct { + SystemGameID int32 + ThirdPlatformName string + ThirdGameID string + Desc string +} + +func InitGameMappingConfig() { + buf, err := os.ReadFile(GameMappingPath) + if err != nil { + logger.Logger.Error("InitGameMappingConfig os.ReadFile error ->", err) + } + + err = json.Unmarshal(buf, &ThirdPltGameMappingConfig) + if err != nil { + logger.Logger.Error("InitGameMappingConfig json.Unmarshal error ->", err) + return + } +} + +func (this *ThirdPlatformGameMappingConfiguration) FindByGameID(systemGameID int32) (ok bool, item *mappingItem) { + for k, v := range this.Arr { + if v.SystemGameID == systemGameID { + return true, &this.Arr[k] + } + } + return false, nil +} diff --git a/model/upgradeaccountcoin.go b/model/upgradeaccountcoin.go new file mode 100644 index 0000000..a05de8d --- /dev/null +++ b/model/upgradeaccountcoin.go @@ -0,0 +1,64 @@ +package model + +import ( + "time" + + "github.com/globalsign/mgo/bson" +) + +var ( + UpgradeAccountCoinDBName = "log" + UpgradeAccountCoinCollName = "log_upgradeaccountcoin" +) + +type UpGradeAccountCoin struct { + LogId bson.ObjectId `bson:"_id"` + IP string //IP + Date int32 //YYYYMMDD + Coin int32 //金币 + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PackageTag string //包标识 + InviterId int32 //邀请人 + City string //城市 + CreateTime time.Time //创建日期 +} + +type UpGradeAccountCoinArgs struct { + Plt string + Ip string + Date int32 +} + +func GetUpgradeAccountCoinLogsByIPAndDate(ip, platform string, logTime time.Time) int32 { + year, month, day := logTime.Date() + date := year*10000 + int(month)*100 + day + if rpcCli == nil { + return 0 + } + + args := &UpGradeAccountCoinArgs{Plt: platform, Ip: ip, Date: int32(date)} + var count int32 + err := rpcCli.CallWithTimeout("UpGradeAccountCoinSvc.GetUpgradeAccountCoinLogsByIPAndDate", args, &count, time.Second*30) + if err != nil { + return 0 + } + return count +} + +func InsertUpgradeAccountCoinLog(ip string, logTime time.Time, coin int32, snId int32, channel, platform, promoter, packageTag, city string, inviterId int32) error { + if rpcCli == nil { + return ErrRPClientNoConn + } + year, month, day := logTime.Date() + date := year*10000 + int(month)*100 + day + log := &UpGradeAccountCoin{LogId: bson.NewObjectId(), IP: ip, Date: int32(date), Coin: coin, SnId: snId, Platform: platform, Channel: channel, City: city, Promoter: promoter, PackageTag: packageTag, InviterId: inviterId, CreateTime: time.Now()} + var ret bool + err := rpcCli.CallWithTimeout("UpGradeAccountCoinSvc.InsertUpgradeAccountCoinLog", log, &ret, time.Second*30) + if err != nil { + return err + } + return err +} diff --git a/model/viplog.go b/model/viplog.go new file mode 100644 index 0000000..edaac41 --- /dev/null +++ b/model/viplog.go @@ -0,0 +1,50 @@ +package model + +import ( + "mongo.games.com/goserver/core/logger" + "time" +) + +var ( + DbVipDBName = "log" + DbVipCollName = "log_vip" +) + +type DbVip struct { + Platform string //平台 + SnId int32 //玩家id + VipLevel int32 //领取礼包的VIP等级 + Type int32 //领取类型 0-每日礼包 1-每日金币礼包 2-固定礼包 + ConsumeType int32 //消耗类型 + ConsumeNum int64 //消耗数量 + ItemInfo []ItemInfo //道具 获得 + Ts int64 //领取时间 + ShopId int32 //商品id + ShopName string //商品名称 +} + +func NewDbVip(platform string, snId int32, vipLevel int32, Type int32, consumeType int32, consumeNum int64, itemInfo []ItemInfo, shopId int32, shopName string) *DbVip { + ts := time.Now() + return &DbVip{Platform: platform, SnId: snId, VipLevel: vipLevel, Type: Type, Ts: ts.Unix(), ConsumeType: consumeType, ConsumeNum: consumeNum, ItemInfo: itemInfo, ShopId: shopId, ShopName: shopName} +} + +type DbVipLogArgs struct { + Log *DbVip +} + +func InsertDbVipLog(log *DbVip) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertDbVipLog rpcCli == nil") + return + } + var ret bool + args := &DbVipLogArgs{ + Log: log, + } + err = rpcCli.CallWithTimeout("DbVipLogSvc.InsertDbVipLog", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertDbVipLog error:", err) + return + } + return +} diff --git a/model/welfarelog.go b/model/welfarelog.go new file mode 100644 index 0000000..fb6598b --- /dev/null +++ b/model/welfarelog.go @@ -0,0 +1,121 @@ +package model + +import ( + "sync/atomic" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" +) + +var ( + WelfareLogDBName = "log" + WelfareLogCollName = "log_welfare" +) + +const ( + WelfareBuyBlindBox = 0 // 购买盲盒 + WelfareBuyFirstPay = 1 // 购买首充 +) + +var COINEX_WelfLOBAL_SEQ = int64(0) + +type WelfareItem struct { + Type int32 //类型 1:金币 2:钻石 3:道具 + ItemId int32 //道具id + Num int64 //数量 + Name string //名称 +} + +type WelfareLog struct { + LogId bson.ObjectId `bson:"_id"` + SnId int32 //玩家id + Platform string //平台名称 + Channel string //渠道名称 + Promoter string //推广员 + PackageTag string //推广包标识 + Count int64 //帐变数量 + TakeCount int64 //消耗数量 + RestCount int64 //钱包余额 + DiamondCount int64 //钻石余额 + Oper string //操作者 + Remark string //备注 + Time time.Time //时间戳 + Day int32 //第几天领取 + Ver int32 //数据版本(暂时不用) + WType int32 //福利类型 + LogType int32 //log类型 + Item []*WelfareItem //道具 + Discount int64 //折扣(万分比) + SeqNo int64 //流水号(隶属于进程) + Ts int64 //时间戳 +} + +func NewWelfareLog() *WelfareLog { + log := &WelfareLog{LogId: bson.NewObjectId()} + return log +} + +func NewWelfareLogEx(snid int32, count, takeCount, restCount, diamondCount, discount int64, day, ver, logType, wtype int32, itemd []*WelfareItem, oper, remark, + platform, channel, promoter, packageid string) *WelfareLog { + cl := NewWelfareLog() + cl.SnId = snid + cl.Platform = platform + cl.Channel = channel + cl.Promoter = promoter + cl.Count = count + cl.TakeCount = takeCount + cl.RestCount = restCount + cl.Day = day + cl.DiamondCount = diamondCount + cl.Discount = discount + cl.Oper = oper + cl.Remark = remark + tNow := time.Now() + cl.Time = tNow + cl.Ver = ver + cl.WType = wtype + cl.LogType = logType + cl.Item = itemd + cl.PackageTag = packageid + cl.SeqNo = atomic.AddInt64(&COINEX_WelfLOBAL_SEQ, 1) + cl.Ts = tNow.Unix() + return cl +} + +func InsertWelfareLog(log *WelfareLog) (err error) { + if rpcCli == nil { + logger.Logger.Error("model.InsertWelfareLog rpcCli == nil") + return + } + + var ret bool + err = rpcCli.CallWithTimeout("WelfareLogSvc.InsertWelfareLog", log, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("InsertWelfareLog error:", err) + } + return +} + +type RemoveWelfareLogOneArg struct { + Plt string + Id bson.ObjectId +} + +func RemoveWelfareLogOne(plt string, id bson.ObjectId) error { + if rpcCli == nil { + logger.Logger.Error("model.RemoveWelfareLogOne rpcCli == nil") + return nil + } + + args := &RemoveCoinLogOneArg{ + Plt: plt, + Id: id, + } + var ret bool + err := rpcCli.CallWithTimeout("WelfareLogSvc.RemoveWelfareLogOne", args, &ret, time.Second*30) + if err != nil { + logger.Logger.Warn("RemoveWelfareLogOne error:", err) + } + return err +} diff --git a/mq/consumer.go b/mq/consumer.go new file mode 100644 index 0000000..26c49be --- /dev/null +++ b/mq/consumer.go @@ -0,0 +1,140 @@ +package mq + +import ( + "fmt" + "math/rand" + "os" + "sync" + "time" + + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" +) + +/* + 为了使用方便,这里统一启动订阅 +*/ + +var subscriberLock sync.RWMutex +var subscriber = make(map[string][]*Subscriber) + +type Subscriber struct { + broker.Subscriber + h broker.Handler + opts []broker.SubscribeOption +} + +// RegisterSubscriber 注册订阅处理方法 +// 不同订阅是在各自的协程中执行的 +func RegisterSubscriber(topic string, h broker.Handler, opts ...broker.SubscribeOption) { + s := Subscriber{ + h: h, + opts: opts, + } + subscriberLock.Lock() + subscriber[topic] = append(subscriber[topic], &s) + subscriberLock.Unlock() +} + +func UnregisterSubscriber(topic string) { + subscriberLock.Lock() + delete(subscriber, topic) + subscriberLock.Unlock() +} + +func GetSubscriber(topic string) []*Subscriber { + subscriberLock.RLock() + defer subscriberLock.RUnlock() + if s, ok := subscriber[topic]; ok { + return s + } + return nil +} + +func GetSubscribers() map[string][]*Subscriber { + ret := make(map[string][]*Subscriber) + subscriberLock.RLock() + defer subscriberLock.RUnlock() + for topic, s := range subscriber { + temp := make([]*Subscriber, len(s)) + copy(temp, s) + ret[topic] = temp + } + return ret +} + +type RabbitMQConsumer struct { + broker.Broker + url string + exchange rabbitmq.Exchange +} + +func NewRabbitMQConsumer(url string, exchange rabbitmq.Exchange) *RabbitMQConsumer { + mq := &RabbitMQConsumer{ + url: url, + exchange: exchange, + } + + rabbitmq.DefaultRabbitURL = mq.url + rabbitmq.DefaultExchange = mq.exchange + + mq.Broker = rabbitmq.NewBroker() + mq.Broker.Init() + return mq +} + +func (c *RabbitMQConsumer) Start() error { + if err := c.Connect(); err != nil { + return err + } + + sss := GetSubscribers() + for topic, ss := range sss { + for _, s := range ss { + sub, err := c.Subscribe(topic, s.h, s.opts...) + if err != nil { + return err + } + + s.Subscriber = sub + } + } + + return nil +} + +func (c *RabbitMQConsumer) Stop() error { + sss := GetSubscribers() + for _, ss := range sss { + for _, s := range ss { + s.Unsubscribe() + } + } + return c.Disconnect() +} + +func BackUp(e broker.Event, err error) { + tNow := time.Now() + filePath := fmt.Sprintf("%s/%s_%s_%09d_%04d.dat", BACKUP_PATH, e.Topic(), tNow.Format(TIME_FORMAT), tNow.Nanosecond(), rand.Int31n(10000)) + f, err := os.Create(filePath) + if err != nil { + logger.Logger.Errorf("RabbitMQPublisher.public(%s,%v) err:%v", e.Topic(), e.Message(), err) + return + } + defer f.Close() + var reason string + if err != nil { + reason = err.Error() + } + f.WriteString("reason:" + reason + "\n") + f.WriteString("data:" + string(e.Message().Body) + "\n") +} + +func init() { + if ok, _ := common.PathExists(BACKUP_PATH); !ok { + os.MkdirAll(BACKUP_PATH, os.ModePerm) + } +} diff --git a/mq/keyconf.go b/mq/keyconf.go new file mode 100644 index 0000000..827af8b --- /dev/null +++ b/mq/keyconf.go @@ -0,0 +1,17 @@ +package mq + +// 后台消息 +const ( + BackBankrupt = "log_bankrupt" + BackClientLog = "log_clientlog_mysql" + BackGameRecord = "evt_gamerec" + BackInviteScore = "evt_invitescore" + BackLogin = "evt_login" + BackOnline = "evt_online" + BackPhoneLottery = "evt_phonelottery" + BackReliefund = "log_relieffund" + BackSystemFreeGive = "evt_systemfreegive" +) + +// dbproxy 消息 +const () diff --git a/mq/publisher.go b/mq/publisher.go new file mode 100644 index 0000000..a703bd8 --- /dev/null +++ b/mq/publisher.go @@ -0,0 +1,176 @@ +package mq + +import ( + "encoding/json" + "errors" + "fmt" + "math/rand" + "os" + "sync" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + "mongo.games.com/goserver/core/logger" +) + +/* + 在发布功能上加上异步发送消息和发送失败备份到本地文件的功能 +*/ + +const ( + BACKUP_PATH = "backup" + TIME_FORMAT = "20060102150405" +) + +var ERR_CLOSED = errors.New("publisher is closed") + +type item struct { + topic string + msg interface{} +} + +type RabbitMQPublisher struct { + b broker.Broker + exchange rabbitmq.Exchange + url string + que chan *item + closed bool + waitor sync.WaitGroup +} + +func NewRabbitMQPublisher(url string, exchange rabbitmq.Exchange, backlog int) *RabbitMQPublisher { + if backlog <= 0 { + backlog = 1 + } + mq := &RabbitMQPublisher{ + url: url, + exchange: exchange, + que: make(chan *item, backlog), + } + + rabbitmq.DefaultRabbitURL = mq.url + rabbitmq.DefaultExchange = mq.exchange + + mq.b = rabbitmq.NewBroker() + mq.b.Init() + return mq +} + +func (p *RabbitMQPublisher) Start() (err error) { + if ok, _ := common.PathExists(BACKUP_PATH); !ok { + err = os.MkdirAll(BACKUP_PATH, os.ModePerm) + if err != nil { + return + } + } + + err = p.b.Connect() + if err != nil { + return + } + + go p.workerRoutine() + + return nil +} + +func (p *RabbitMQPublisher) Stop() error { + if p.closed { + return ERR_CLOSED + } + + p.closed = true + close(p.que) + for item := range p.que { + p.publish(item.topic, item.msg) + } + + //等待所有投递出去的任务全部完成 + p.waitor.Wait() + + return p.b.Disconnect() +} + +// Send 发布消息,异步 +func (p *RabbitMQPublisher) Send(topic string, msg interface{}) (err error) { + if p.closed { + return ERR_CLOSED + } + + i := &item{topic: topic, msg: msg} + select { + case p.que <- i: + default: + //会不会情况更糟糕 + go p.concurrentPublish(topic, msg) + } + return nil +} + +func (p *RabbitMQPublisher) concurrentPublish(topic string, msg interface{}) (err error) { + p.waitor.Add(1) + defer p.waitor.Done() + return p.publish(topic, msg) +} + +// 发布消息,同步 +func (p *RabbitMQPublisher) publish(topic string, msg interface{}) (err error) { + defer func() { + if err != nil { + p.backup(topic, msg, err) + } + + recover() + }() + + buf, err := json.Marshal(msg) + if err != nil { + return err + } + + err = p.b.Publish(topic, &broker.Message{Body: buf}) + if err != nil { + logger.Logger.Error("RabbitMQPublisher.publish err:", err) + return + } + return nil +} + +func (p *RabbitMQPublisher) workerRoutine() { + p.waitor.Add(1) + defer p.waitor.Done() + + for { + select { + case item, ok := <-p.que: + if ok { + p.publish(item.topic, item.msg) + } else { + return + } + } + } +} + +func (p *RabbitMQPublisher) backup(topic string, msg interface{}, err error) { + buf, err := json.Marshal(msg) + if err != nil { + return + } + tNow := time.Now() + filePath := fmt.Sprintf("%s/%s_%s_%09d_%04d.dat", BACKUP_PATH, topic, tNow.Format(TIME_FORMAT), tNow.Nanosecond(), rand.Int31n(10000)) + f, err := os.Create(filePath) + if err != nil { + logger.Logger.Errorf("RabbitMQPublisher.public(%s,%v) err:%v", topic, msg, err) + return + } + defer f.Close() + var reason string + if err != nil { + reason = err.Error() + } + f.WriteString("reason:" + reason + "\n") + f.WriteString("data:" + string(buf) + "\n") +} diff --git a/proto/wrappers.go b/proto/wrappers.go new file mode 100644 index 0000000..8f4917e --- /dev/null +++ b/proto/wrappers.go @@ -0,0 +1,47 @@ +package proto + +import ( + "google.golang.org/protobuf/proto" +) + +/* + * Helper routines for simplifying the creation of optional fields of basic type. + */ + +// Bool is a helper routine that return it self +func Bool(v bool) bool { return v } + +// Int32 is a helper routine that return it self +func Int32(v int32) int32 { return v } + +// Int is a helper routine that return it self +func Int(v int) int32 { return int32(v) } + +// Int64 is a helper routine that return it self +func Int64(v int64) int64 { return v } + +// Float32 is a helper routine that return it self +func Float32(v float32) float32 { return v } + +// Float64 is a helper routine that return it self +func Float64(v float64) float64 { return v } + +// Uint32 is a helper routine that return it self +func Uint32(v uint32) uint32 { return v } + +// Uint64 is a helper routine that return it self +func Uint64(v uint64) uint64 { return v } + +// String is a helper routine that return it self +func String(v string) string { return v } + +// SetDefaults sets unset protocol buffer fields to their default values. +// do nothing +func SetDefaults(pb interface{}) {} + +// Marshal returns the wire-format encoding of m. +func Marshal(m proto.Message) ([]byte, error) { return proto.Marshal(m) } + +func Unmarshal(b []byte, m proto.Message) error { return proto.Unmarshal(b, m) } + +type Message = proto.Message diff --git a/ranksrv/action_gatesrv.go b/ranksrv/action_gatesrv.go new file mode 100644 index 0000000..52b6b14 --- /dev/null +++ b/ranksrv/action_gatesrv.go @@ -0,0 +1,383 @@ +package main + +import ( + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + rankproto "mongo.games.com/game/protocol/rank" + "mongo.games.com/game/ranksrv/com" + "mongo.games.com/game/ranksrv/rank" +) + +func init() { + // 排位赛排行榜 + com.Register(int(rankproto.Rank_PACKET_RANK_CSRankMatch), rankproto.CSRankMatch{}, CSRankMatch) + // 金币榜 + com.Register(int(rankproto.Rank_PACKET_RANK_CSCoin), rankproto.CSCoin{}, CSCoin) + // 邀请积分排行榜 + com.Register(int(rankproto.Rank_PACKET_RANK_CSInvite), rankproto.CSInvite{}, CSInvite) + // 邀请记录 + com.Register(int(rankproto.Rank_PACKET_CSInviteLog), rankproto.CSInviteLog{}, CSInviteLog) + // 收入榜 + com.Register(int(rankproto.Rank_PACKET_RANK_CSWinCoin), rankproto.CSWinCoin{}, CSWinCoin) +} + +func CSRankMatch(s *netlib.Session, d *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRankMatch data:", data) + msg, ok := data.(*rankproto.CSRankMatch) + if !ok { + return nil + } + + rank.RankLevelMgrInstance.Take(d.Platform, msg.GetId(), func(list []*model.PlayerRankScore, err error) { + if err != nil { + logger.Logger.Errorf("CSRankMatch error: %v", err) + return + } + + page := msg.GetPage() + if page < 0 { + page = 0 + } + pageSize := msg.GetPageSize() + if pageSize < 1 { + pageSize = 10 + } + + start := page * pageSize + end := start + pageSize + if end >= int32(len(list)) { + end = int32(len(list)) + } + + var i int32 + var ranks []*rankproto.SeasonRank + if end > start && int(start) < len(list) { + for _, v := range list[start:end] { + r := &rankproto.SeasonRank{ + Snid: v.SnId, + Name: v.Name, + Lv: v.Lv, + Rank: start + i, + Sex: v.Sex, + HeadUrl: v.HeadUrl, + Coin: v.Coin, + Score: v.Score, + ModId: v.ModId, + } + ranks = append(ranks, r) + i++ + } + } + + var me *rankproto.SeasonRank + for k, v := range list { + if v.SnId == d.Snid { + me = &rankproto.SeasonRank{ + Snid: v.SnId, + Name: v.Name, + Lv: v.Lv, + Rank: int32(k), + Sex: v.Sex, + HeadUrl: v.HeadUrl, + Coin: v.Coin, + Score: v.Score, + ModId: v.ModId, + } + break + } + } + + pack := &rankproto.SCRankMatch{ + Ranks: ranks, + Me: me, + Page: page, + PageSize: pageSize, + Total: int32(len(list)), + } + + common.SendToGate(sid, int(rankproto.Rank_PACKET_RANK_SCRankMatch), pack, s) + logger.Logger.Tracef("SCRankMatch: %v", pack) + }) + return nil +} + +func CSCoin(s *netlib.Session, d *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSCoin data:", data) + msg, ok := data.(*rankproto.CSCoin) + if !ok { + return nil + } + + rank.PlayerCoinMgrInstance.Take(d.Platform, 0, func(list []*model.RankPlayerCoin, err error) { + if err != nil { + logger.Logger.Errorf("CSCoin error: %v", err) + return + } + + page := msg.GetPage() + if page < 0 { + page = 0 + } + pageSize := msg.GetPageSize() + if pageSize < 1 { + pageSize = 10 + } + + start := page * pageSize + end := start + pageSize + if end >= int32(len(list)) { + end = int32(len(list)) + } + + var i int32 + var ranks []*rankproto.PlayerCoin + if end > start && int(start) < len(list) { + for _, v := range list[start:end] { + r := &rankproto.PlayerCoin{ + Snid: v.SnId, + Name: v.Name, + Sex: v.Sex, + HeadUrl: v.HeadUrl, + Coin: v.Coin, + Rank: start + i, + ModId: v.ModId, + } + ranks = append(ranks, r) + i++ + } + } + + var me *rankproto.PlayerCoin + for k, v := range list { + if v.SnId == d.Snid { + me = &rankproto.PlayerCoin{ + Snid: v.SnId, + Name: v.Name, + Sex: v.Sex, + HeadUrl: v.HeadUrl, + Coin: v.Coin, + Rank: int32(k), + ModId: v.ModId, + } + break + } + } + + pack := &rankproto.SCCoin{ + Ranks: ranks, + Me: me, + Page: page, + PageSize: pageSize, + Total: int32(len(list)), + } + + common.SendToGate(sid, int(rankproto.Rank_PACKET_RANK_SCCoin), pack, s) + logger.Logger.Tracef("SCCoin: %v", pack) + }) + return nil +} + +func CSInvite(s *netlib.Session, d *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSInvite data:", data) + msg, ok := data.(*rankproto.CSInvite) + if !ok { + return nil + } + + rank.RankInviteMgrInstance.Take(d.Platform, msg.GetId(), func(list []*model.PlayerRankInvite, err error) { + if err != nil { + logger.Logger.Errorf("CSInvite error: %v", err) + return + } + + start, end := com.SkipLimitToStartEnd(msg.GetSkip(), msg.GetLimit(), len(list)) + + IsEndNum := false + if end == int32(len(list)) { + IsEndNum = true + } + + var i int32 + var ranks []*rankproto.InviteRank + if end > start && int(start) < len(list) { + for _, v := range list[start:end] { + r := &rankproto.InviteRank{ + Snid: v.SnId, + Name: v.Name, + Rank: start + i, + Score: v.Score, + InviteNum: v.InviteNum, + ModId: v.ModId, + } + ranks = append(ranks, r) + i++ + } + } + + var me *rankproto.InviteRank + for k, v := range list { + if v.SnId == d.Snid { + me = &rankproto.InviteRank{ + Snid: v.SnId, + Name: v.Name, + Score: v.Score, + Rank: int32(k), + InviteNum: v.InviteNum, + ModId: v.ModId, + } + break + } + } + + pack := &rankproto.SCInvite{ + Id: msg.GetId(), + Ranks: ranks, + Me: me, + Skip: msg.GetSkip(), + IsEndNum: IsEndNum, + RankMaxNum: model.GameParamData.RankInviteMaxNum, + } + + common.SendToGate(sid, int(rankproto.Rank_PACKET_RANK_SCInvite), pack, s) + logger.Logger.Tracef("SCInvite: %v", pack) + }) + return nil +} + +func CSInviteLog(s *netlib.Session, d *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSInviteLog data:", data) + msg, ok := data.(*rankproto.CSInviteLog) + if !ok { + return nil + } + + rank.InviteLogMgrInstance.Take(d.Platform, d.Snid, func(list []*model.InviteInfo, err error) { + if err != nil { + logger.Logger.Errorf("CSInviteLog error: %v", err) + return + } + + start, end := com.SkipLimitToStartEnd(msg.GetSkip(), msg.GetLimit(), len(list)) + + var ranks []*rankproto.InviteInfo + for _, v := range list[start:end] { + r := &rankproto.InviteInfo{ + Name: v.Name, + SnId: v.SnId, + CreateTs: v.CreateTs, + Score: v.Score, + ModId: v.ModId, + } + ranks = append(ranks, r) + } + + pack := &rankproto.SCInviteLog{ + Skip: start, + Limit: msg.GetLimit(), + List: ranks, + } + + common.SendToGate(sid, int(rankproto.Rank_PACKET_SCInviteLog), pack, s) + logger.Logger.Tracef("SCInviteLog: %v", pack) + }) + + return nil +} + +func CSWinCoin(s *netlib.Session, d *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWinCoin data:", data) + msg, ok := data.(*rankproto.CSWinCoin) + if !ok { + return nil + } + + ret := &rankproto.SCWinCoin{} + var ranks []*rankproto.WinCoinInfo + var me *rankproto.WinCoinInfo + + send := func() { + ret.Ranks = ranks + ret.Me = me + common.SendToGate(sid, int(rankproto.Rank_PACKET_RANK_SCWinCoin), ret, s) + logger.Logger.Tracef("SCWinCoin: %v", ret) + } + + rank.WinCoinMgrInstance.Get(d.Platform, 0, func(list []*model.WinCoinInfo, err error) { + if err != nil { + logger.Logger.Errorf("CSWinCoin error: %v", err) + return + } + + start, end := com.SkipLimitToStartEnd(msg.GetSkip(), msg.GetLimit(), len(list)) + ret.Skip = start + ret.Limit = msg.GetLimit() + + var i int32 + for _, v := range list[start:end] { + r := &rankproto.WinCoinInfo{ + SnId: v.SnId, + Name: v.Name, + Rank: start + i, + Coin: v.Coin, + ModId: v.ModId, + } + ranks = append(ranks, r) + i++ + if v.SnId == d.Snid { + me = r + } + } + + if len(list) > 0 && me == nil { + for k, v := range list { + if v.SnId == d.Snid { + me = &rankproto.WinCoinInfo{ + SnId: v.SnId, + Name: v.Name, + Rank: int32(k), + Coin: v.Coin, + ModId: v.ModId, + } + break + } + } + } + + if me == nil { + var err error + var ret *model.FindWinCoinReply + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + startTs, endTs := rank.StartEndTs() + ret, err = model.FindWinCoinTienlen(&model.FindWinCoinArgs{ + Platform: d.Platform, + StartTs: startTs, + EndTs: endTs, + SnId: d.Snid, + }) + if err != nil { + logger.Logger.Errorf("FindWinCoinTienlen error: %v", err) + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err == nil && ret.List != nil { + me = &rankproto.WinCoinInfo{ + SnId: ret.List.SnId, + Name: ret.List.Name, + Rank: -1, + Coin: ret.List.Coin, + ModId: ret.List.ModId, + } + } + send() + })).Start() + } else { + send() + } + }) + return nil +} diff --git a/ranksrv/com/etcd.go b/ranksrv/com/etcd.go new file mode 100644 index 0000000..1da2b97 --- /dev/null +++ b/ranksrv/com/etcd.go @@ -0,0 +1,20 @@ +package com + +import ( + "go.etcd.io/etcd/client/v3" + + "mongo.games.com/game/etcd" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + // 平台信息 + etcd.Register(etcd.ETCDKEY_PLATFORM_PREFIX, webapi.Platform{}, PlatformConfigEtcd) +} + +func PlatformConfigEtcd(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + PlatformConfigSingleton.Update(data) +} diff --git a/ranksrv/com/listmgr.go b/ranksrv/com/listmgr.go new file mode 100644 index 0000000..511254c --- /dev/null +++ b/ranksrv/com/listmgr.go @@ -0,0 +1,121 @@ +package com + +import ( + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" +) + +func NewListMgr[T any](cacheTime func() int64, loadFunc func(platform string, index int32) ([]T, error)) *ListMgr[T] { + return &ListMgr[T]{ + platform: make(map[string]map[int32]*DataItem[T]), + LoadFunc: loadFunc, + CacheTime: cacheTime, + } +} + +type funcCall[T any] func([]T, error) + +type DataItem[T any] struct { + Ts int64 // 更新时间 + List []T + cb []funcCall[T] +} + +type ListMgr[T any] struct { + platform map[string]map[int32]*DataItem[T] + + // 数据库查询方法 + // 参数 平台id,类型 + LoadFunc func(string, int32) ([]T, error) + CacheTime func() int64 +} + +// Get 从缓存中获取数据 +func (r *ListMgr[T]) Get(platform string, index int32, f func([]T, error)) { + tp := r.platform[platform] + if tp == nil { + r.platform[platform] = map[int32]*DataItem[T]{} + } + item := r.platform[platform][index] + if item != nil { + f(item.List, nil) + return + } +} + +// Take 从缓存中获取数据,如果缓存中没有数据或缓存过期,则从数据库中获取数据 +func (r *ListMgr[T]) Take(platform string, index int32, f func([]T, error)) { + tp := r.platform[platform] + if tp == nil { + r.platform[platform] = map[int32]*DataItem[T]{} + } + item := r.platform[platform][index] + if item != nil && time.Now().Unix()-item.Ts < r.CacheTime() { + f(item.List, nil) + return + } + + if item == nil { + item = &DataItem[T]{ + cb: []funcCall[T]{f}, + } + r.platform[platform][index] = item + } else { + if len(item.cb) > 0 { + item.cb = append(item.cb, f) + return + } else { + item.cb = []funcCall[T]{f} + } + } + + var list []T + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + list, err = r.LoadFunc(platform, index) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + r.platform[platform][index] = &DataItem[T]{ + List: list, + Ts: time.Now().Unix(), + } + for _, v := range item.cb { + v(list, err) + } + })).Start() +} + +// UpdateCache 更新缓存 +func (r *ListMgr[T]) UpdateCache(platform string, index int32) { + tp := r.platform[platform] + if tp == nil { + r.platform[platform] = map[int32]*DataItem[T]{} + } + item := r.platform[platform][index] + if item == nil { + item = &DataItem[T]{ + cb: []funcCall[T]{}, + } + r.platform[platform][index] = item + } + + var list []T + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + list, err = r.LoadFunc(platform, index) + if err != nil { + logger.Logger.Errorf("UpdateCache LoadFunc error:%v", err) + } + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err == nil { + r.platform[platform][index] = &DataItem[T]{ + List: list, + Ts: time.Now().Unix(), + } + } + })).Start() +} diff --git a/ranksrv/com/platform.go b/ranksrv/com/platform.go new file mode 100644 index 0000000..b886aee --- /dev/null +++ b/ranksrv/com/platform.go @@ -0,0 +1,37 @@ +package com + +import ( + "mongo.games.com/game/protocol/webapi" +) + +var PlatformConfigSingleton = &PlatformConfigMgr{ + Platforms: make(map[string]*PlatformConfig), +} + +type PlatformConfig struct { +} + +type PlatformConfigMgr struct { + Platforms map[string]*PlatformConfig //key 是平台标记 +} + +func (this *PlatformConfigMgr) Get(plt string) *PlatformConfig { + ret, ok := this.Platforms[plt] + if !ok { + ret = new(PlatformConfig) + this.Platforms[plt] = ret + } + return ret +} + +func (this *PlatformConfigMgr) Update(config any) { + if config == nil { + return + } + + switch v := config.(type) { + case *webapi.Platform: + this.Get(v.GetPlatformName()) + default: + } +} diff --git a/ranksrv/com/register.go b/ranksrv/com/register.go new file mode 100644 index 0000000..faef73f --- /dev/null +++ b/ranksrv/com/register.go @@ -0,0 +1,44 @@ +package com + +import ( + "reflect" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + rankproto "mongo.games.com/game/protocol/rank" +) + +func Register(mainId int, msgType interface{}, h func(s *netlib.Session, g *rankproto.GateTransmit, packetId int, data interface{}, sid int64) error) { + f := func() interface{} { + tp := reflect.TypeOf(msgType) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return reflect.New(tp).Interface() + } + + common.RegisterHandler(mainId, common.HandlerWrapper(func(s *netlib.Session, packetId int, data interface{}, sid int64) error { + d, ok := data.(*rankproto.GateTransmit) + if !ok { + return nil + } + + msg := f() + if err := netlib.UnmarshalPacketNoPackId(d.GetPacketData(), msg); err != nil { + logger.Logger.Errorf("PacketId %v Unmarshal error: %v", mainId, err) + return err + } + + if d == nil || d.Platform == "" || d.Platform == "0" || d.Snid == 0 { + return nil + } + + return h(s, d, packetId, msg, sid) + })) + + netlib.RegisterFactory(mainId, netlib.PacketFactoryWrapper(func() interface{} { + return &rankproto.GateTransmit{} + })) +} diff --git a/ranksrv/com/util.go b/ranksrv/com/util.go new file mode 100644 index 0000000..3657a62 --- /dev/null +++ b/ranksrv/com/util.go @@ -0,0 +1,20 @@ +package com + +func SkipLimitToStartEnd(skip, limit int32, maxLimit int) (start int32, end int32) { + if skip < 0 { + skip = 0 + } + if limit < 1 { + limit = 10 + } + + start = skip + end = skip + limit + if end >= int32(maxLimit) { + end = int32(maxLimit) + if start > end { + start = end + } + } + return +} diff --git a/ranksrv/config.json b/ranksrv/config.json new file mode 100644 index 0000000..f1871d1 --- /dev/null +++ b/ranksrv/config.json @@ -0,0 +1,149 @@ +{ + "netlib": { + "SrvInfo":{ + "Name": "RankServer", + "Type": 8, + "Id": 801, + "AreaID": 1, + "Banner": [ + "=================", + "rank server", + "=================" + ] + }, + + "IoServices": [ + { + "Id": 1501, + "Type": 15, + "AreaId": 1, + "Name": "RankServer", + "Ip": "127.0.0.1", + "Port": 0, + "MaxDone": 40000, + "MaxPend": 2000000, + "MaxPacket": 6553500, + "MaxConn": 200, + "RcvBuff": 4096000, + "SndBuff": 4096000, + "WriteTimeout": 300, + "ReadTimeout": 300, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": ["session-filter-auth"], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler", + "handler-ranksrv-close" + ] + }, + { + "Id": 501, + "Type": 5, + "AreaId": 1, + "Name": "ManagerService", + "Ip": "127.0.0.1", + "Port": 3002, + "MaxDone": 20000, + "MaxPend": 20000, + "MaxPacket": 6553500, + "MaxConn": 100, + "RcvBuff": 819200, + "SndBuff": 819200, + "WriteTimeout": 300, + "ReadTimeout": 300, + "IsInnerLink": true, + "NoDelay": true, + "IsClient": true, + "IsAutoReconn": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": ["session-filter-auth"], + "HandlerChain": ["session-srv-registe","srv-service-handler"] + } + ] + }, + + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + }, + "Worker": { + "WorkerCnt": 64, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + + "costum": { + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog": 1024, + + "etcdurl":[ + "127.0.0.1:2379" + ], + "etcduser": "root", + "etcdpwd": "win88", + + "GameIdFilter": [] + }, + + "webapi": { + "GameApiURL": "http://103.39.231.66:1099/api/game_srv/platform_config" + }, + + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + + "core": { + "MaxProcs": 4 + }, + + "signal":{ + "SupportSignal": true + }, + + "common":{ + "AppId": "5c56d1644966f078bfb90c71", + "IsDevMode":false + }, + + "data":{ + "RootPath":"../data" + }, + "gameconfig":{ + "RootPath":"../data/gameconfig" + }, + "i18n":{ + "RootPath":"../data/i18n" + } +} \ No newline at end of file diff --git a/ranksrv/init.go b/ranksrv/init.go new file mode 100644 index 0000000..f2dce93 --- /dev/null +++ b/ranksrv/init.go @@ -0,0 +1,35 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/broker/rabbitmq" + "time" +) + +var RabbitMQPublisher *mq.RabbitMQPublisher +var RabbitMqConsumer *mq.RabbitMQConsumer + +func init() { + //首先加载游戏配置 + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + //初始化rpc + model.StartupRPClient(common.CustomConfig.GetString("MgoRpcCliNet"), common.CustomConfig.GetString("MgoRpcCliAddr"), time.Duration(common.CustomConfig.GetInt("MgoRpcCliReconnInterV"))*time.Second) + + //rabbitmq打开链接 + RabbitMqConsumer = mq.NewRabbitMQConsumer(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}) + if RabbitMqConsumer != nil { + RabbitMqConsumer.Start() + } + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + if RabbitMqConsumer != nil { + RabbitMqConsumer.Stop() + } + return nil + }) +} diff --git a/ranksrv/logger.xml b/ranksrv/logger.xml new file mode 100644 index 0000000..f39aee3 --- /dev/null +++ b/ranksrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ranksrv/main.go b/ranksrv/main.go new file mode 100644 index 0000000..79dd8e2 --- /dev/null +++ b/ranksrv/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "mongo.games.com/game/model" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/module" + + _ "games.yol.com/win88" + "mongo.games.com/game/common" +) + +func main() { + core.RegisterConfigEncryptor(common.ConfigFE) + core.LoadPackages("config.json") + defer core.ClosePackages() + + model.InitGameParam() + + waitor := module.Start() + waitor.Wait("ranksrv") +} diff --git a/ranksrv/rank/invitelog.go b/ranksrv/rank/invitelog.go new file mode 100644 index 0000000..88d7e0a --- /dev/null +++ b/ranksrv/rank/invitelog.go @@ -0,0 +1,22 @@ +package rank + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" + "mongo.games.com/game/ranksrv/com" +) + +var InviteLogMgrInstance = com.NewListMgr[*model.InviteInfo]( + func() int64 { + return 5 // 缓存5秒 + }, + func(platform string, index int32) ([]*model.InviteInfo, error) { + logger.Logger.Tracef("load invitelog platform:%s snid:%d", platform, index) + ret, err := model.GetInviteList(platform, index) + if err != nil { + logger.Logger.Errorf("load invitelog err:%s", err.Error()) + return nil, err + } + return ret, nil + }) diff --git a/ranksrv/rank/playercoin.go b/ranksrv/rank/playercoin.go new file mode 100644 index 0000000..fd2d805 --- /dev/null +++ b/ranksrv/rank/playercoin.go @@ -0,0 +1,23 @@ +package rank + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" + "mongo.games.com/game/ranksrv/com" +) + +var PlayerCoinMgrInstance = com.NewListMgr[*model.RankPlayerCoin]( + func() int64 { + return int64(model.GameParamData.RankTimeout) + }, + func(platform string, index int32) ([]*model.RankPlayerCoin, error) { + logger.Logger.Tracef("load rank player coin platform:%s", platform) + list, err := model.FindPlayerCoinList(&model.FindPlayerCoinListArgs{ + Platform: platform, + }) + if err != nil { + return nil, err + } + return list.List, nil + }) diff --git a/ranksrv/rank/rankinvite.go b/ranksrv/rank/rankinvite.go new file mode 100644 index 0000000..88e7c37 --- /dev/null +++ b/ranksrv/rank/rankinvite.go @@ -0,0 +1,24 @@ +package rank + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" + "mongo.games.com/game/ranksrv/com" +) + +var RankInviteMgrInstance = com.NewListMgr[*model.PlayerRankInvite]( + func() int64 { + return int64(model.GameParamData.RankTimeout) + }, + func(platform string, index int32) ([]*model.PlayerRankInvite, error) { + logger.Logger.Tracef("load rank invite platform:%s rankType:%d", platform, index) + InviteList, err := model.FindPlayerRankInviteList(&model.FindPlayerRankInviteListArgs{ + Platform: platform, + RankType: index, + }) + if err != nil { + return nil, err + } + return InviteList.List, nil + }) diff --git a/ranksrv/rank/ranklevel.go b/ranksrv/rank/ranklevel.go new file mode 100644 index 0000000..0a35e93 --- /dev/null +++ b/ranksrv/rank/ranklevel.go @@ -0,0 +1,24 @@ +package rank + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" + "mongo.games.com/game/ranksrv/com" +) + +var RankLevelMgrInstance = com.NewListMgr[*model.PlayerRankScore]( + func() int64 { + return int64(model.GameParamData.RankTimeout) + }, + func(platform string, index int32) ([]*model.PlayerRankScore, error) { + logger.Logger.Tracef("load rank level platform:%s rankType:%d", platform, index) + seasonList, err := model.FindPlayerRankSeasonList(&model.FindPlayerRankSeasonListArgs{ + Platform: platform, + RankType: index, + }) + if err != nil { + return nil, err + } + return seasonList.List, nil + }) diff --git a/ranksrv/rank/wincoin.go b/ranksrv/rank/wincoin.go new file mode 100644 index 0000000..66d5c6f --- /dev/null +++ b/ranksrv/rank/wincoin.go @@ -0,0 +1,68 @@ +package rank + +import ( + "math" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + + "mongo.games.com/game/model" + "mongo.games.com/game/ranksrv/com" +) + +var WinCoinMgrInstance = com.NewListMgr[*model.WinCoinInfo]( + func() int64 { + return math.MaxInt32 + }, + func(platform string, index int32) ([]*model.WinCoinInfo, error) { + // 每天21点刷新,获取前一天的排行 + startTs, endTs := StartEndTs() + logger.Logger.Tracef("load wincoin platform:%s startTs:%v endTs:%v", platform, startTs, endTs) + ret, err := model.FindWinCoinListTienlen(&model.FindWinCoinListArgs{ + Platform: platform, + StartTs: startTs, + EndTs: endTs, + }) + return ret.List, err + }) + +func StartEndTs() (startTs int64, endTs int64) { + now := time.Now() + year, month, day := now.Date() + lastTs := time.Date(year, month, day, model.GameParamData.WinCoinUpdateTime, 0, 0, 0, now.Location()) + if now.Before(lastTs) { + lastTs = lastTs.AddDate(0, 0, -1) + } + endTs = lastTs.Unix() + return endTs - 24*int64(time.Hour.Seconds()), endTs +} + +type WinCoinMgr struct { +} + +func (w *WinCoinMgr) ModuleName() string { + return "WinCoinMgr" +} + +func (w *WinCoinMgr) Init() { + for k := range com.PlatformConfigSingleton.Platforms { + WinCoinMgrInstance.UpdateCache(k, 0) + } +} + +func (w *WinCoinMgr) Update() { + h := time.Now().Hour() + if h == model.GameParamData.WinCoinUpdateTime { + for k := range com.PlatformConfigSingleton.Platforms { + WinCoinMgrInstance.UpdateCache(k, 0) + } + } +} + +func (w *WinCoinMgr) Shutdown() { +} + +func init() { + module.RegisteModule(new(WinCoinMgr), time.Hour, 0) +} diff --git a/robot.bat b/robot.bat new file mode 100644 index 0000000..e3c42e6 --- /dev/null +++ b/robot.bat @@ -0,0 +1,3 @@ +set GODEBUG=gctrace=1 +cd robot +start robot.exe \ No newline at end of file diff --git a/robot/base/clientmgr.go b/robot/base/clientmgr.go new file mode 100644 index 0000000..ae3cab7 --- /dev/null +++ b/robot/base/clientmgr.go @@ -0,0 +1,184 @@ +package base + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/rand" + "os" + "strconv" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + loginproto "mongo.games.com/game/protocol/login" + serverproto "mongo.games.com/game/protocol/server" +) + +/* + 登录及机器人账号更新 +*/ + +var ClientMgrSingleton = &ClientMgr{ + sessionPool: make(map[string]*netlib.Session), + Running: true, + CycleTimeEvent: [24][]HourEvent{}, +} + +type HourEvent struct { + newAcc string //wait to open + oldAcc string //wait to close + session *netlib.Session +} +type InvalidAcc struct { + Acc string + Session *netlib.Session +} +type ClientMgr struct { + sessionPool map[string]*netlib.Session + Running bool + CycleTimeEvent [24][]HourEvent +} + +// 发送登录消息 +func (this *ClientMgr) startLogin(acc string, s *netlib.Session) { + ts := time.Now().UnixNano() + csLogin := &loginproto.CSLogin{ + Username: proto.String(acc), + TimeStamp: proto.Int64(ts), + Platform: proto.String(common.Platform_Rob), + Channel: proto.String(common.Channel_Rob), + } + params := &model.PlayerParams{ + Ip: fmt.Sprintf("%v.%v.%v.%v", 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255)), + City: RandZone(), + Platform: 1, + Logininmodel: "app", + } + data, err := json.Marshal(params) + if err == nil { + csLogin.Params = proto.String(string(data[:])) + } + h := md5.New() + io.WriteString(h, fmt.Sprintf("%v%v", acc, Config.AppId)) + pwd := hex.EncodeToString(h.Sum(nil)) + h.Reset() + io.WriteString(h, fmt.Sprintf("%v%v%v", pwd, Config.AppId, ts)) + pwd = hex.EncodeToString(h.Sum(nil)) + csLogin.Password = pwd + csLogin.LoginType = proto.Int32(0) + csLogin.Sign = proto.String(common.MakeMd5String(csLogin.GetUsername(), csLogin.GetPassword(), + strconv.Itoa(int(csLogin.GetTimeStamp())), csLogin.GetParams(), Config.AppId)) + s.Send(int(loginproto.LoginPacketID_PACKET_CS_LOGIN), csLogin) + logger.Logger.Infof("账号 [%v] 开始登录", acc) +} + +func (this *ClientMgr) RegisterSession(acc string, s *netlib.Session) { + this.sessionPool[acc] = s + StartSessionLoginTimer(s, timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + if !s.IsConned() || !this.Running { + StopSessionLoginTimer(s) + return false + } + this.startLogin(acc, s) + return true + }), nil, time.Second*time.Duration(2+rand.Int31n(3)), -1) +} + +func (this *ClientMgr) UnRegisterSession(acc string) { + delete(this.sessionPool, acc) +} + +func (this *ClientMgr) HourChange() { + fileModify := false + eventArr := this.CycleTimeEvent[time.Now().Hour()] + for _, event := range eventArr { + accChan[event.newAcc] = true //使用新的账号 + cfg := NewSessionConfig() + netlib.Connect(cfg) //创建新的连接 + if session, ok := this.sessionPool[event.oldAcc]; ok && session != nil { + //删除旧有账号数据 + pack := &serverproto.RWAccountInvalid{ + Acc: proto.String(event.oldAcc), + } + session.Send(int(loginproto.LoginPacketID_PACKET_CS_ACCOUNTINVALID), pack) // 删除机器人账号 + //删号标记,不要在断线重连了 + session.SetAttribute(SessionAttributeDelAccount, true) + //关闭连接 + session.Close() + } + //更新本地账号数据信息 + for key, value := range accPool { + if value.Acc == event.oldAcc { + accPool[key] = &AccountData{ + Acc: event.newAcc, + Create: time.Now().UnixNano(), + Time: time.Now(), + } + fileModify = true + break + } + } + } + this.CycleTimeEvent[time.Now().Hour()] = []HourEvent{} + if fileModify == true { + //持久化本次的账号数据 + buff, err := json.Marshal(accPool) + if err != nil { + logger.Logger.Error("Marshal account data error:", err) + } else { + err := os.WriteFile(accountFileName, buff, os.ModePerm) + if err != nil { + logger.Logger.Error("Write robot account file error:", err) + } + } + } +} + +func (this *ClientMgr) DayChange() { + invalidCount := 0 //过期账号数量 + updateLimit := len(accPool) * model.GameParamData.InvalidRobotAccRate / 100 //可更新的账号数量 + invalidAccs := []InvalidAcc{} + if updateLimit > 0 { + invalideTime := time.Now().AddDate(0, 0, -model.GameParamData.InvalidRobotDay).UnixNano() + for _, value := range accPool { + //检查过期账号 + if value.Create < invalideTime { + //过期账号 + invalidAccs = append(invalidAccs, InvalidAcc{ + Acc: value.Acc, + Session: this.sessionPool[value.Acc], + }) + invalidCount++ + j := rand.Intn(invalidCount) + i := invalidCount - 1 + if i != j { + invalidAccs[i], invalidAccs[j] = invalidAccs[j], invalidAccs[i] + } + } + } + if len(invalidAccs) >= updateLimit { + invalidAccs = invalidAccs[:updateLimit] + } + } + //本次需要生成的新账号 + cnt := len(invalidAccs) + for i := 0; i < cnt; i++ { + timePoint := i % 24 + eventArr := this.CycleTimeEvent[timePoint] + eventArr = append(eventArr, HourEvent{ + newAcc: bson.NewObjectId().Hex(), + oldAcc: invalidAccs[i].Acc, + session: invalidAccs[i].Session, + }) + this.CycleTimeEvent[timePoint] = eventArr + } +} diff --git a/robot/base/clock.go b/robot/base/clock.go new file mode 100644 index 0000000..a4cc3a5 --- /dev/null +++ b/robot/base/clock.go @@ -0,0 +1,90 @@ +package base + +import ( + "time" + + "mongo.games.com/goserver/core/module" +) + +/* + 计时器 +*/ + +var ClockMgrSingleton = &ClockMgr{ + LastHour: -1, + LastDay: -1, +} + +type ClockMgr struct { + LastTime time.Time + LastMonth time.Month + LastWeek int + LastDay int + LastHour int + LastMini int + LastSec int +} + +func (this *ClockMgr) ModuleName() string { + return "ClockMgr" +} + +func (this *ClockMgr) Init() { + tNow := time.Now().Local() + this.LastTime = tNow + _, this.LastMonth, this.LastDay = tNow.Date() + this.LastHour, this.LastMini, this.LastSec = tNow.Hour(), tNow.Minute(), tNow.Second() + _, this.LastWeek = tNow.ISOWeek() +} + +func (this *ClockMgr) Update() { + tNow := time.Now().Local() + sec := tNow.Second() + if sec != this.LastSec { + this.LastSec = sec + // 秒 + PlayerMgrSingleton.OnSecondTimer() + + min := tNow.Minute() + if min != this.LastMini { + this.LastMini = min + // 分 + PlayerMgrSingleton.OnMiniTimer() + + hour := tNow.Hour() + if hour != this.LastHour { + // 时 + ClientMgrSingleton.HourChange() + + this.LastHour = hour + day := tNow.Day() + if day != this.LastDay { + // 天 + ClientMgrSingleton.DayChange() + + this.LastDay = day + _, week := tNow.ISOWeek() + if week != this.LastWeek { + // 周 + + this.LastWeek = week + } + month := tNow.Month() + if month != this.LastMonth { + // 月 + + this.LastMonth = month + } + } + } + } + } +} + +func (this *ClockMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(ClockMgrSingleton, time.Millisecond*500, 0) +} diff --git a/robot/base/config.go b/robot/base/config.go new file mode 100644 index 0000000..a8d0433 --- /dev/null +++ b/robot/base/config.go @@ -0,0 +1,31 @@ +package base + +import ( + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/netlib" +) + +var Config = Configuration{} + +type Configuration struct { + Count int // 机器人总数 + AppId string // appID + Connects netlib.SessionConfig // 网络连接配置 +} + +func (this *Configuration) Name() string { + return "benchmark" +} + +func (this *Configuration) Init() error { + //logger.Logger.Tracef("%+v", *this) + return nil +} + +func (this *Configuration) Close() error { + return nil +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/robot/base/connect.go b/robot/base/connect.go new file mode 100644 index 0000000..9804c34 --- /dev/null +++ b/robot/base/connect.go @@ -0,0 +1,64 @@ +package base + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" +) + +const ( + // RobotSessionStartId 机器人session开始id + RobotSessionStartId = 100000000 +) + +var ( + BenchMarkModule = &BenchMark{} + WaitConnectSessions []*netlib.SessionConfig +) + +func NewSessionConfig() *netlib.SessionConfig { + BenchMarkModule.idx++ + cfg := Config.Connects + cfg.Id = BenchMarkModule.idx + cfg.Init() + return &cfg +} + +type BenchMark struct { + idx int +} + +func (m *BenchMark) ModuleName() string { + return "benchmark-module" +} + +func (m *BenchMark) Init() { + m.idx = RobotSessionStartId + for i := 0; i < Config.Count; i++ { + cfg := NewSessionConfig() + logger.Logger.Info("waite connect session id=", cfg.Id) + WaitConnectSessions = append(WaitConnectSessions, cfg) + } +} + +// Update 机器开始连接游戏服务器 +func (m *BenchMark) Update() { + n := len(WaitConnectSessions) + if n > 0 { + config := WaitConnectSessions[n-1] + WaitConnectSessions = WaitConnectSessions[:n-1] + if err := netlib.Connect(config); err != nil { + logger.Logger.Error("netlib.Connect error", err) + } + } +} + +func (m *BenchMark) Shutdown() { + module.UnregisteModule(m) +} + +func init() { + module.RegisteModule(BenchMarkModule, time.Millisecond, 1) +} diff --git a/robot/base/constant.go b/robot/base/constant.go new file mode 100644 index 0000000..e8e9dfb --- /dev/null +++ b/robot/base/constant.go @@ -0,0 +1,46 @@ +package base + +// 标记 +const ( + SessionAttributeClientAccountId int = iota // 账号 + SessionAttributeDelAccount // 待删除账号 + SessionAttributePingTimer // 心跳定时器 + SessionAttributeLoginTimer // 登录定时器 + SessionAttributeEnteringMatchScene // 进入比赛场中 + SessionAttributeEnteringScene // 进入非比赛场中 + SessionAttributeUser // 玩家信息 + SessionAttributeScene // 房间对象 + SessionAttributeSceneId // 房间ID +) + +// 玩家状态标记 +const ( + PlayerState_Online int32 = 1 << iota //在线标记 1 + PlayerState_Ready //准备标记 2 + PlayerState_SceneOwner //房主标记 3 + PlayerState_Choke //呛标记 被复用于金花,是否被动弃牌 4 + PlayerState_Ting //听牌标记 5 金花复用,标记最后押注时,是否看牌 + PlayerState_NoisyBanker //闹庄标记 6 金花复用,标记allin时,是否看牌 + PlayerState_WaitOp //等待操作标记 7 + PlayerState_Auto //托管状态 8 + PlayerState_Check //已看牌状态 9 + PlayerState_Fold //弃牌状态 10 + PlayerState_Lose //输状态 11 + PlayerState_Win //赢状态 12 + PlayerState_WaitNext //等待下一局游戏 13 + PlayerState_GameBreak //不能继续游戏 14 + PlayerState_Leave //暂离状态 15 + PlayerState_Audience //观众标记 16 + PlayerState_AllIn //allin标记 17 + PlayerState_FinalAllIn //最后一圈,最后一个人allin标记 18 + PlayerState_Show //亮牌标记 19 + PlayerState_EnterSceneFailed //进场失败 20 + PlayerState_PKLost //发起Pk,失败 21 + PlayerState_IsChangeCard //牛牛标识是否换牌 22 + PlayerState_IsPayChangeCard //牛牛标识是否充值换牌 23 + PlayerState_Bankruptcy //玩家破产 24 + PlayerState_MatchQuit //退赛标记 25 + PlayerState_AllFollow //跟到底状态 26 + PlayerState_SAdjust //单控状态 27 + PlayerState_Max +) diff --git a/robot/base/function.go b/robot/base/function.go new file mode 100644 index 0000000..fb1468c --- /dev/null +++ b/robot/base/function.go @@ -0,0 +1,125 @@ +package base + +import ( + "math/rand" + "time" + + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/proto" + player_proto "mongo.games.com/game/protocol/player" +) + +func GetUser(s *netlib.Session) *player_proto.SCPlayerData { + if user, ok := s.GetAttribute(SessionAttributeUser).(*player_proto.SCPlayerData); ok { + return user + } + return nil +} + +func GetScene(s *netlib.Session) IScene { + if sceneId, ok := s.GetAttribute(SessionAttributeSceneId).(int32); ok { + return SceneMgrSingleton.GetScene(sceneId) + } + ss := s.GetAttribute(SessionAttributeScene) + if ss != nil { + if scene, ok := ss.(IScene); ok { + return scene + } + } + return nil +} + +func HadScene(s *netlib.Session) bool { + if sceneId, ok := s.GetAttribute(SessionAttributeSceneId).(int32); ok { + return sceneId > 0 + } + if scene := s.GetAttribute(SessionAttributeScene); scene != nil { + return true + } + return false +} + +func StartSessionPingTimer(s *netlib.Session, act timer.TimerAction, ud interface{}, interval time.Duration, times int) bool { + StopSessionPingTimer(s) + if hTimer, ok := timer.StartTimer(act, ud, interval, times); ok { + s.SetAttribute(SessionAttributePingTimer, hTimer) + return true + } + return false +} + +func StopSessionPingTimer(s *netlib.Session) { + if h, ok := s.GetAttribute(SessionAttributePingTimer).(timer.TimerHandle); ok { + if h != timer.TimerHandle(0) { + timer.StopTimer(h) + s.RemoveAttribute(SessionAttributePingTimer) + } + } +} + +// StartSessionLoginTimer 登录定时器 +func StartSessionLoginTimer(s *netlib.Session, act timer.TimerAction, ud interface{}, interval time.Duration, times int) bool { + StopSessionLoginTimer(s) + if hTimer, ok := timer.StartTimer(act, ud, interval, times); ok { + s.SetAttribute(SessionAttributeLoginTimer, hTimer) + return true + } + return false +} + +func StopSessionLoginTimer(s *netlib.Session) { + if h, ok := s.GetAttribute(SessionAttributeLoginTimer).(timer.TimerHandle); ok { + if h != timer.TimerHandle(0) { + timer.StopTimer(h) + s.RemoveAttribute(SessionAttributeLoginTimer) + } + } +} + +// ExePMCmd 机器人特殊指令 +func ExePMCmd(s *netlib.Session, cmd string) { + CSPMCmd := &player_proto.CSPMCmd{ + Cmd: proto.String(cmd), + } + proto.SetDefaults(CSPMCmd) + s.Send(int(player_proto.PlayerPacketID_PACKET_CS_PMCMD), CSPMCmd) +} + +// DelaySendSecond 延迟消息发送 +// DelaySendSecond(s, packetid, pack, []int{1, 2}...) 1到2秒随机延迟发送消息 +func DelaySendSecond(s *netlib.Session, packetid int, pack interface{}, params ...int) { + DelaySendDuration(s, packetid, pack, time.Second, params...) +} + +// DelaySendMillisecond 延迟消息发送 +// DelaySendMillisecond(s, packetid, pack, []int{1, 2}...) 1到2毫秒随机延迟发送消息 +func DelaySendMillisecond(s *netlib.Session, packetid int, pack interface{}, params ...int) { + DelaySendDuration(s, packetid, pack, time.Millisecond, params...) +} + +// DelaySendDuration 在某个时间区间随机延迟发送一次消息 +// unit 时间单位 +// params 延时区间 +func DelaySendDuration(s *netlib.Session, packetid int, pack interface{}, unit time.Duration, params ...int) { + min := 1 + if len(params) > 0 { + min = params[0] + } + max := min + 3 + if len(params) > 1 { + max = params[1] + } + if max < min { + min, max = max, min + } else if max == min { + max = min + 1 + } + thinkSec := time.Duration(min + rand.Intn(max-min)) + + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + s.Send(packetid, pack) + return true + }), nil, unit*thinkSec, 1) +} diff --git a/robot/base/gatesessionhandler.go b/robot/base/gatesessionhandler.go new file mode 100644 index 0000000..9d88c07 --- /dev/null +++ b/robot/base/gatesessionhandler.go @@ -0,0 +1,76 @@ +package base + +import ( + player_proto "mongo.games.com/game/protocol/player" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +/* + 添加到客户端管理器,管理器负责登录 + 当连接断开时,从管理器中移除,判断是否需要重连 +*/ + +const ( + GateSessionHandlerName = "handler-gate-session" +) + +type GateSessionHandler struct { + netlib.BasicSessionHandler +} + +func (g *GateSessionHandler) GetName() string { + return GateSessionHandlerName +} + +func (g *GateSessionHandler) GetInterestOps() uint { + return 1< 0 { + // logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats) + // if logLogic != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("logic", logLogic) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////net session stats + //netStats := netlib.Stats() + //if len(netStats) > 0 { + // logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats) + // if logNet != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("net", logNet) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////schedule stats + //jobStats := schedule.Stats() + //if len(jobStats) > 0 { + // logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats) + // if logJob != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("job", logJob) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////trans stats + //transStats := transact.Stats() + //if len(transStats) > 0 { + // logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats) + // if logTrans != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("transact", logTrans) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////panic stats + //panicStats := utils.GetPanicStats() + //if len(panicStats) > 0 { + // for key, stats := range panicStats { + // logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats) + // if logPanic != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("panic", logPanic) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + // } + //} + // + ////object command quene stats + //objStats := core.AppCtx.GetStats() + //if len(objStats) > 0 { + // logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats) + // if logCmd != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpsertMonitorData("cmdque", logCmd) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + // } + //} + // + ////gorouting count, eg. system info + //runtimeStats := utils.StatsRuntime() + //logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats) + //if logRuntime != nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.InsertMonitorData("runtime", logRuntime) + // }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + //} +} + +func (this *MonitorMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + //module.RegisteModule(MonitorMgrSington, time.Minute*5, 0) +} diff --git a/robot/base/player.go b/robot/base/player.go new file mode 100644 index 0000000..1d7c766 --- /dev/null +++ b/robot/base/player.go @@ -0,0 +1,96 @@ +package base + +import ( + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" +) + +var NilPlayer IPlayer = nil + +type IPlayer interface { + Clear() + GetSnId() int32 + GetPos() int32 + GetFlag() int32 + SetFlag(flag int32) + GetCoin() int64 + GetLastOp() int32 + SetLastOp(op int32) + MarkFlag(flag int32) + UnmarkFlag(flag int32) + IsMarkFlag(flag int32) bool + IsOnLine() bool + IsReady() bool + IsSceneOwner() bool + IsRobot() bool + UpdateCards(cards []int32) + UpdateBasePlayers(gameId string, basePlayer *server_proto.RobotData) + GetBasePlayers(gameId string) *server_proto.RobotData + LeaveGameMsg(snid int32) bool + GetOutLimitCoin() int + GetGameCount() int + GetLastWinOrLoss() int + GetTakeCoin() int64 +} + +type BasePlayer struct { + BasePlayer map[string]*server_proto.RobotData + GameCount int + LastWinOrLoss int //0 平局 1 赢取 -1 失败 + TakeCoin int64 + Scene interface{} +} + +func (this *BasePlayer) UpdateBasePlayers(gameId string, basePlayer *server_proto.RobotData) { + if this.BasePlayer == nil { + this.BasePlayer = make(map[string]*server_proto.RobotData) + } + this.BasePlayer[gameId] = basePlayer +} + +func (this *BasePlayer) GetBasePlayers(gameId string) *server_proto.RobotData { + if this.BasePlayer == nil { + this.BasePlayer = make(map[string]*server_proto.RobotData) + } + if this.BasePlayer[gameId] == nil { + this.BasePlayer[gameId] = &server_proto.RobotData{} + } + return this.BasePlayer[gameId] +} + +func (this *BasePlayer) GetLastWinOrLoss() int { + return this.LastWinOrLoss +} + +func (this *BasePlayer) GetGameCount() int { + return this.GameCount +} + +func (this *BasePlayer) GetTakeCoin() int64 { + return this.TakeCoin +} + +func (this *BasePlayer) GetOutLimitCoin() int { + return -1 +} + +func (this *BasePlayer) SendMsg(snid int32, packetid int, data interface{}) bool { + s := PlayerMgrSingleton.GetPlayerSession(snid) + if s != nil { + return s.Send(packetid, data) + } + return false +} + +func (this *BasePlayer) LeaveGameMsg(snid int32) bool { + s := PlayerMgrSingleton.GetPlayerSession(snid) + if s != nil { + pack := &hall_proto.CSLeaveRoom{ + Mode: proto.Int32(0), + } + proto.SetDefaults(pack) + return s.Send(int(hall_proto.GameHallPacketID_PACKET_CS_LEAVEROOM), pack) + } + return false +} diff --git a/robot/base/playermgr.go b/robot/base/playermgr.go new file mode 100644 index 0000000..3f2b596 --- /dev/null +++ b/robot/base/playermgr.go @@ -0,0 +1,167 @@ +package base + +import ( + "math/rand" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + player_proto "mongo.games.com/game/protocol/player" +) + +var PlayerMgrSingleton = &PlayerMgr{ + playersMapSnId: make(map[int32]*player_proto.SCPlayerData), + playersSession: make(map[int32]*netlib.Session), + playersMatchSession: make(map[int32]*netlib.Session), +} + +type PlayerMgr struct { + playersMapSnId map[int32]*player_proto.SCPlayerData // 玩家id:玩家信息 + playersSession map[int32]*netlib.Session // 非比赛场; 玩家id:session + playersMatchSession map[int32]*netlib.Session // 比赛场;玩家id:session + playerEnterType bool +} + +func (pm *PlayerMgr) AddPlayer(data *player_proto.SCPlayerData, s *netlib.Session) { + snid := data.GetData().GetSnId() + pm.playersMapSnId[snid] = data + + // 给比赛场预留50%的机器人 + if pm.playerEnterType { + pm.playerEnterType = false + pm.playersMatchSession[snid] = s + } else { + pm.playerEnterType = true + pm.playersSession[snid] = s + } +} + +func (pm *PlayerMgr) DelPlayer(snid int32) { + delete(pm.playersMapSnId, snid) + if s, ok := pm.playersSession[snid]; ok { + delete(pm.playersSession, snid) + if s != nil { + return + } + } + if s, ok := pm.playersMatchSession[snid]; ok { + delete(pm.playersMatchSession, snid) + if s != nil { + } + } +} + +func (pm *PlayerMgr) GetPlayer(snid int32) *player_proto.SCPlayerData { + if data, ok := pm.playersMapSnId[snid]; ok { + return data + } + return nil +} + +func (pm *PlayerMgr) GetPlayerSession(snid int32) *netlib.Session { + if data, ok := pm.playersSession[snid]; ok { + return data + } + if data, ok := pm.playersMatchSession[snid]; ok { + return data + } + return nil +} + +func (pm *PlayerMgr) ProcessCheckRobotNum() { + var freePlayers, enteringPlayers []*netlib.Session + var inScene int + for _, s := range pm.playersSession { + if HadScene(s) { + inScene++ + } else { + if s.GetAttribute(SessionAttributeEnteringScene) != nil { + enteringPlayers = append(enteringPlayers, s) + } else { + freePlayers = append(freePlayers, s) + } + } + } + var freeMatchPlayers, enteringMatchPlayers []*netlib.Session + var inMatchScene int + for _, s := range pm.playersMatchSession { + if HadScene(s) { + inMatchScene++ + } else { + if s.GetAttribute(SessionAttributeEnteringMatchScene) != nil { + enteringMatchPlayers = append(enteringMatchPlayers, s) + } else { + freeMatchPlayers = append(freeMatchPlayers, s) + } + } + } + + logger.Logger.Infof("机器人总数:%d 普通场空闲:%d 普通场入场中:%d 普通场游戏中:%d 比赛场空闲:%d 比赛场入场中:%d 比赛场游戏中:%d", + len(pm.playersMapSnId), len(freePlayers), len(enteringPlayers), inScene, len(pm.playersMatchSession), len(enteringMatchPlayers), inMatchScene) +} + +// ProcessInvite 机器人进入房间 +// roomId 房间id +// id 场次id +// platform 平台id +// isMatch 是否比赛场 +func (pm *PlayerMgr) ProcessInvite(roomId, id, cnt int32, platform string, IsMatch bool) { + f := func(sessions map[int32]*netlib.Session, tp int) { + var freePlayers, enteringPlayers []*netlib.Session + var inScene int + for _, s := range sessions { + if HadScene(s) { + inScene++ + } else { + if s.GetAttribute(tp) != nil { + enteringPlayers = append(enteringPlayers, s) + } else { + freePlayers = append(freePlayers, s) + } + } + } + freeCnt := len(freePlayers) + if freeCnt <= 0 { + return + } + if cnt <= 0 { + return + } + if roomId == 0 { + return + } + + if cnt > int32(freeCnt) { + cnt = int32(freeCnt) + } + id := rand.Perm(freeCnt) + for i := 0; i < int(cnt) && i < len(id); i++ { + s := freePlayers[id[i]] + CSEnterRoom := &hall_proto.CSEnterRoom{ + RoomId: proto.Int32(roomId), + GameId: proto.Int(0), + } + proto.SetDefaults(CSEnterRoom) + s.Send(int(hall_proto.GameHallPacketID_PACKET_CS_ENTERROOM), CSEnterRoom) + if i != 0 { + s.SetAttribute(i, struct{}{}) + } + } + } + + if IsMatch { + f(pm.playersMatchSession, SessionAttributeEnteringMatchScene) + } else { + f(pm.playersSession, SessionAttributeEnteringScene) + } +} + +func (pm *PlayerMgr) OnSecondTimer() { + +} + +func (pm *PlayerMgr) OnMiniTimer() { + pm.ProcessCheckRobotNum() +} diff --git a/robot/base/randrobot.go b/robot/base/randrobot.go new file mode 100644 index 0000000..268504d --- /dev/null +++ b/robot/base/randrobot.go @@ -0,0 +1,119 @@ +package base + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" +) + +const HEAD_URL = "http://cdnres.doudoubei.com/doubeires/head/" + +type Zone struct { + Area string + Weight int +} + +var Zones []Zone +var ZonesWeight int + +type HeadPool struct { + Boy []string + Girl []string +} + +var HEADIMG_POOL = &HeadPool{} + +func init() { + buf, err := os.ReadFile("../data/icon_rob.json") + if err != nil { + panic(err) + } + + err = json.Unmarshal(buf, HEADIMG_POOL) + if err != nil { + panic(err) + } + + buf, err = os.ReadFile("../data/zone_rob.json") + if err != nil { + panic(err) + } + err = json.Unmarshal(buf, &Zones) + if err != nil { + panic(err) + } + + for i := 0; i < len(Zones); i++ { + ZonesWeight += Zones[i].Weight + Zones[i].Weight = ZonesWeight + } +} + +func CalcuHashCode(str string) int64 { + data := []byte(str) + cnt := len(data) + code := int64(0) + for i := 0; i < cnt; i++ { + code = 31*code + int64(data[i]) + } + return code +} + +func RandRobotInfo(accId string) (name, icon, sex, ip string) { + //ts := time.Now().Unix() + //code := CalcuHashCode(fmt.Sprintf("%v-%v", ts, accId)) + //rand.Seed(code) + //nSex := 1 + rand.Int31n(2) + //sex = strconv.Itoa(int(nSex)) + //var iconCnt int + //var iconPool []string + //switch nSex { + //case 1: + // namePool := srvdata.PBDB_NameBoyMgr.Datas.GetArr() + // namePoolLen := int32(len(namePool)) + // if namePoolLen > 0 { + // name = namePool[rand.Int31n(namePoolLen)].GetName() + // } + // iconPool = HEADIMG_POOL.Boy + // iconCnt = len(iconPool) + //case 2: + // namePool := srvdata.PBDB_NameGirlMgr.Datas.GetArr() + // namePoolLen := int32(len(namePool)) + // if namePoolLen > 0 { + // name = namePool[rand.Int31n(namePoolLen)].GetName() + // } + // iconPool = HEADIMG_POOL.Girl + // iconCnt = len(iconPool) + //} + // + //if iconCnt > 0 { + // icon = Config.HeadUrl + iconPool[rand.Int31n(int32(iconCnt))] + //} + ip = fmt.Sprintf("%v.%v.%v.%v", 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255)) + return +} + +func RandLongitudeAndLatitude() (longitude, latitude int32) { + //中国范围经纬度 + //113216260,34963671 + //114103930,34592702 + longitude = rand.Int31n(120212509-110241174) + 110241174 + latitude = rand.Int31n(40960003-21558402) + 21558402 + return longitude, latitude +} + +func RandZone() string { + idx := -1 + weight := rand.Intn(ZonesWeight) + for i := 0; i < len(Zones); i++ { + if Zones[i].Weight >= weight { + idx = i + break + } + } + if idx != -1 { + return Zones[idx].Area + } + return "深圳" +} diff --git a/robot/base/scene.go b/robot/base/scene.go new file mode 100644 index 0000000..90c931b --- /dev/null +++ b/robot/base/scene.go @@ -0,0 +1,32 @@ +package base + +import "mongo.games.com/goserver/core/netlib" + +type IScene interface { + GetAIMode() int32 + SetAIMode(aiMode int32) + GetRoomId() int32 + GetRoomMode() int32 + GetGameId() int32 + GetPlayerByPos(pos int32) IPlayer + GetPlayerBySnid(snid int32) IPlayer + GetMe(s *netlib.Session) IPlayer + AddPlayer(p IPlayer) + DelPlayer(snid int32) + IsFull() bool + IsMatchScene() bool + Update(ts int64) +} + +type BaseScene struct { + AIMode int32 + RobotTypeAIName map[int]string +} + +func (this *BaseScene) GetAIMode() int32 { + return this.AIMode +} + +func (this *BaseScene) SetAIMode(aiMode int32) { + this.AIMode = aiMode +} diff --git a/robot/base/scenemgr.go b/robot/base/scenemgr.go new file mode 100644 index 0000000..ce2c62f --- /dev/null +++ b/robot/base/scenemgr.go @@ -0,0 +1,73 @@ +package base + +import ( + "time" + + "mongo.games.com/goserver/core/module" + + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +var SceneMgrSingleton = &SceneMgr{ + Scenes: make(map[int32]IScene), + sceneDBGameFree: make(map[int32]*server_proto.DB_GameFree), +} + +type SceneMgr struct { + Scenes map[int32]IScene + sceneDBGameFree map[int32]*server_proto.DB_GameFree +} + +func (sm *SceneMgr) AddScene(s IScene) { + sm.Scenes[s.GetRoomId()] = s +} + +func (sm *SceneMgr) DelScene(sceneId int32) { + delete(sm.Scenes, sceneId) + delete(sm.sceneDBGameFree, sceneId) +} + +func (sm *SceneMgr) GetScene(sceneId int32) IScene { + if s, exist := sm.Scenes[sceneId]; exist { + return s + } + return nil +} + +func (sm *SceneMgr) UpdateSceneDBGameFree(sceneId int32, dbGameFree *server_proto.DB_GameFree) { + sm.sceneDBGameFree[sceneId] = dbGameFree +} + +func (sm *SceneMgr) GetSceneDBGameFree(sceneId, gamefreeId int32) *server_proto.DB_GameFree { + if data, exist := sm.sceneDBGameFree[sceneId]; exist { + return data + } + + return srvdata.PBDB_GameFreeMgr.GetData(gamefreeId) +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (sm *SceneMgr) Update() { + for _, s := range sm.Scenes { + s.Update(time.Now().UnixMilli()) // 毫秒时间戳 + } + return +} + +func (sm *SceneMgr) Init() { +} + +func (sm *SceneMgr) ModuleName() string { + return "SceneMgr-module" +} + +func (sm *SceneMgr) Shutdown() { + module.UnregisteModule(sm) +} + +func init() { + module.RegisteModule(SceneMgrSingleton, time.Millisecond*100, 0) +} diff --git a/robot/base/sclogin.go b/robot/base/sclogin.go new file mode 100644 index 0000000..4ccce05 --- /dev/null +++ b/robot/base/sclogin.go @@ -0,0 +1,60 @@ +package base + +import ( + "encoding/json" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + loginproto "mongo.games.com/game/protocol/login" + playerproto "mongo.games.com/game/protocol/player" +) + +func init() { + // 心跳 + netlib.Register(int(loginproto.GatePacketID_PACKET_SC_PONG), loginproto.SCPong{}, SCPong) + // 登录 + netlib.Register(int(loginproto.LoginPacketID_PACKET_SC_LOGIN), loginproto.SCLogin{}, SCLogin) +} + +func SCPong(s *netlib.Session, packetid int, data interface{}) error { + return nil +} + +func SCLogin(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("SCLogin ", data) + + msg, ok := data.(*loginproto.SCLogin) + if !ok { + return nil + } + + if msg.GetOpRetCode() != loginproto.OpResultCode_OPRC_Sucess { + accountID := s.GetAttribute(SessionAttributeClientAccountId) + logger.Logger.Error("登录失败 ", accountID) + s.Close() + return nil + } + + StopSessionLoginTimer(s) + _, _, _, ip := RandRobotInfo(msg.GetAccId()) + csPlayerData := &playerproto.CSPlayerData{ + AccId: msg.GetAccId(), + } + pp := &model.PlayerParams{ + Platform: 1, + Ip: ip, + City: RandZone(), + Logininmodel: "app", + } + d, err := json.Marshal(pp) + if err == nil { + csPlayerData.Params = proto.String(string(d)) + } + proto.SetDefaults(csPlayerData) + s.Send(int(playerproto.PlayerPacketID_PACKET_CS_PLAYERDATA), csPlayerData) + logger.Logger.Info("登录成功 ", msg.GetAccId()) + return nil +} diff --git a/robot/base/scplayerinfo.go b/robot/base/scplayerinfo.go new file mode 100644 index 0000000..1d9df25 --- /dev/null +++ b/robot/base/scplayerinfo.go @@ -0,0 +1,109 @@ +package base + +import ( + "fmt" + "math/rand" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + hallproto "mongo.games.com/game/protocol/gamehall" + loginproto "mongo.games.com/game/protocol/login" + playerproto "mongo.games.com/game/protocol/player" +) + +func init() { + // 玩家信息 + netlib.Register(int(playerproto.PlayerPacketID_PACKET_SC_PLAYERDATA), playerproto.SCPlayerData{}, SCPlayerData) + // 玩家数据更新 + netlib.Register(int(playerproto.PlayerPacketID_PACKET_SC_PLAYERDATAUPDATE), playerproto.SCPlayerDataUpdate{}, SCPlayerDataUpdate) + // 玩家状态更新 + netlib.Register(int(playerproto.PlayerPacketID_PACKET_SC_PLAYERFLAG), playerproto.SCPlayerFlag{}, SCPlayerFlag) +} + +func SCPlayerData(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("SCPlayerData ", data) + msg, ok := data.(*playerproto.SCPlayerData) + if !ok { + return nil + } + + if msg.GetOpRetCode() != playerproto.OpResultCode_OPRC_Sucess { + accountID := s.GetAttribute(SessionAttributeClientAccountId) + logger.Logger.Errorf("获取玩家信息失败 %v", accountID) + s.Close() + return nil + } + + s.SetAttribute(SessionAttributeUser, msg) + PlayerMgrSingleton.AddPlayer(msg, s) + if msg.GetData() != nil { + coin := msg.GetData().GetCoin() + if coin < 1000000 { + ExePMCmd(s, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, 1000000)) + } + } + + if msg.GetRoomId() != 0 { + // 返回房间 + s.SetAttribute(SessionAttributeSceneId, msg.GetRoomId()) + scReturnRoom := &hallproto.CSReturnRoom{} + proto.SetDefaults(scReturnRoom) + s.Send(int(hallproto.GameHallPacketID_PACKET_CS_RETURNROOM), scReturnRoom) + } + + StartSessionPingTimer(s, timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + if !s.IsConned() { + StopSessionPingTimer(s) + return false + } + pack := &loginproto.CSPing{} + s.Send(int(loginproto.GatePacketID_PACKET_CS_PING), pack) + return true + }), nil, time.Second*time.Duration(60+rand.Int31n(100)), -1) + + return nil +} + +func SCPlayerDataUpdate(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("SCPlayerDataUpdate ", data) + msg, ok := data.(*playerproto.SCPlayerDataUpdate) + if !ok { + return nil + } + + playerData := s.GetAttribute(SessionAttributeUser) + if playerData == nil { + logger.Logger.Errorf("SCPlayerDataUpdate playerData is nil") + return nil + } + + SCPlayerData, ok := playerData.(*playerproto.SCPlayerData) + if !ok { + logger.Logger.Errorf("SCPlayerDataUpdate SCPlayerData is nil") + return nil + } + SCPlayerData.GetData().Coin = msg.GetCoin() + return nil +} + +func SCPlayerFlag(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("SCPlayerFlag ", data) + + msg, ok := data.(*playerproto.SCPlayerFlag) + if !ok { + return nil + } + + if scene, ok := GetScene(s).(IScene); ok && scene != nil { + p := scene.GetPlayerBySnid(msg.GetPlayerId()) + if p != nil { + p.SetFlag(msg.GetFlag()) + } + } + return nil +} diff --git a/robot/base/scroom.go b/robot/base/scroom.go new file mode 100644 index 0000000..a542fc7 --- /dev/null +++ b/robot/base/scroom.go @@ -0,0 +1,131 @@ +package base + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + gamehallproto "mongo.games.com/game/protocol/gamehall" +) + +func init() { + //SCEnterRoom + netlib.Register(int(gamehallproto.GameHallPacketID_PACKET_SC_ENTERROOM), gamehallproto.SCEnterRoom{}, SCEnterRoom) + //SCDestroyRoom + netlib.Register(int(gamehallproto.GameHallPacketID_PACKET_SC_DESTROYROOM), gamehallproto.SCDestroyRoom{}, SCDestroyRoom) + //SCLeaveRoom + netlib.Register(int(gamehallproto.GameHallPacketID_PACKET_SC_LEAVEROOM), gamehallproto.SCLeaveRoom{}, SCLeaveRoom) + //SCReturnRoom + netlib.Register(int(gamehallproto.GameHallPacketID_PACKET_SC_RETURNROOM), gamehallproto.SCReturnRoom{}, SCReturnRoom) + //SCQuitGame + netlib.Register(int(gamehallproto.GameHallPacketID_PACKET_SC_QUITGAME), gamehallproto.SCQuitGame{}, SCQuitGame) +} + +func SCEnterRoom(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("SCEnterRoom ", pack) + msg, ok := pack.(*gamehallproto.SCEnterRoom) + if !ok { + return nil + } + + if msg.GetOpRetCode() == gamehallproto.OpResultCode_Game_OPRC_Sucess_Game { + s.SetAttribute(SessionAttributeSceneId, msg.GetRoomId()) + } + s.RemoveAttribute(SessionAttributeEnteringScene) + s.RemoveAttribute(SessionAttributeEnteringMatchScene) + return nil +} + +func SCDestroyRoom(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("SCDestroyRoom ", pack) + + msg, ok := pack.(*gamehallproto.SCDestroyRoom) + if !ok { + return nil + } + + if msg.GetOpRetCode() == gamehallproto.OpResultCode_Game_OPRC_Sucess_Game { + s.RemoveAttribute(SessionAttributeScene) + s.RemoveAttribute(SessionAttributeSceneId) + s.RemoveAttribute(SessionAttributeEnteringScene) + s.RemoveAttribute(SessionAttributeEnteringMatchScene) + return nil + } + + logger.Logger.Error("SCDestroyRoom failed") + + return nil +} + +func SCLeaveRoom(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("SCLeaveRoom ", pack) + + msg, ok := pack.(*gamehallproto.SCLeaveRoom) + if !ok { + return nil + } + + if msg.GetOpRetCode() == gamehallproto.OpResultCode_Game_OPRC_Sucess_Game { + if scene, ok := GetScene(s).(IScene); ok && scene != nil { + p := scene.GetMe(s) + if p != nil { + logger.Logger.Trace("(this *SCLeaveRoomHandler) snid [%v].", p.GetSnId()) + scene.DelPlayer(p.GetSnId()) + } + } + s.RemoveAttribute(SessionAttributeScene) + s.RemoveAttribute(SessionAttributeSceneId) + s.RemoveAttribute(SessionAttributeEnteringScene) + s.RemoveAttribute(SessionAttributeEnteringMatchScene) + return nil + } + + logger.Logger.Error("SCLeaveRoom failed") + + return nil +} + +func SCReturnRoom(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("SCReturnRoom ", pack) + + msg, ok := pack.(*gamehallproto.SCReturnRoom) + if !ok { + return nil + } + + if msg.GetOpRetCode() == gamehallproto.OpResultCode_Game_OPRC_Sucess_Game { + logger.Logger.Info("SCReturnRoom success") + return nil + } + + logger.Logger.Error("SCReturnRoom failed") + + return nil +} + +func SCQuitGame(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("SCQuitGame ", pack) + + msg, ok := pack.(*gamehallproto.SCQuitGame) + if !ok { + return nil + } + + if msg.GetOpCode() == gamehallproto.OpResultCode_Game_OPRC_Sucess_Game { + if scene, ok := GetScene(s).(IScene); ok && scene != nil { + p := scene.GetMe(s) + if p != nil { + logger.Logger.Trace("(this *SCQuitGameHandler) snid [%v].", p.GetSnId()) + scene.DelPlayer(p.GetSnId()) + } + } + s.RemoveAttribute(SessionAttributeScene) + s.RemoveAttribute(SessionAttributeSceneId) + s.RemoveAttribute(SessionAttributeEnteringScene) + s.RemoveAttribute(SessionAttributeEnteringMatchScene) + return nil + } + + logger.Logger.Error("SCQuitGame failed") + + return nil +} diff --git a/robot/base/scserver.go b/robot/base/scserver.go new file mode 100644 index 0000000..5f54ef8 --- /dev/null +++ b/robot/base/scserver.go @@ -0,0 +1,49 @@ +package base + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + serverproto "mongo.games.com/game/protocol/server" +) + +func init() { + // 邀请机器人 + netlib.Register(int(serverproto.SSPacketID_PACKET_WR_INVITEROBOT), serverproto.WRInviteRobot{}, WRInviteRobot) + + //GameFree数据同步 + netlib.Register(int(serverproto.SSPacketID_PACKET_GR_GameFreeData), serverproto.GRGameFreeData{}, func(s *netlib.Session, packetId int, data interface{}) error { + logger.Logger.Trace("GRGameFreeData ", data) + if msg, ok := data.(*serverproto.GRGameFreeData); ok { + roomId := msg.GetRoomId() + dbGameFree := msg.GetDBGameFree() + SceneMgrSingleton.UpdateSceneDBGameFree(roomId, dbGameFree) + } + return nil + }) + + //房间销毁 + netlib.Register(int(serverproto.SSPacketID_PACKET_GR_DESTROYSCENE), serverproto.GRDestroyScene{}, func(s *netlib.Session, packetId int, data interface{}) error { + logger.Logger.Trace("GRDestroyScene ", data) + if msg, ok := data.(*serverproto.GRDestroyScene); ok { + sceneId := msg.GetSceneId() + SceneMgrSingleton.DelScene(sceneId) + } + return nil + }) +} + +func WRInviteRobot(s *netlib.Session, packetId int, data interface{}) error { + logger.Logger.Trace("WRInviteRobot ", data) + + PlayerMgrSingleton.ProcessCheckRobotNum() + + if msg, ok := data.(*serverproto.WRInviteRobot); ok { + roomId := msg.GetRoomId() + gamefreeId := msg.GetMatchId() + cnt := msg.GetCnt() + platform := msg.GetPlatform() + PlayerMgrSingleton.ProcessInvite(roomId, gamefreeId, cnt, platform, msg.IsMatch) + } + return nil +} diff --git a/robot/base/trascate_stop.go b/robot/base/trascate_stop.go new file mode 100644 index 0000000..2d40333 --- /dev/null +++ b/robot/base/trascate_stop.go @@ -0,0 +1,36 @@ +package base + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/timer" + "mongo.games.com/goserver/core/transact" + "time" +) + +func init() { + transact.RegisteHandler(common.TransType_StopServer, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnExecuteWrapper %x", tNode.MyTnp.TId) + ClientMgrSingleton.Running = false + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + module.Stop() + return true + }), nil, time.Second*10, 1) + return transact.TransExeResult_Success + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnCommitWrapper") + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnRollBackWrapper") + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + return transact.TransExeResult(retCode) + }), + }) +} diff --git a/robot/chess/chesstitiansHttpHelper.go b/robot/chess/chesstitiansHttpHelper.go new file mode 100644 index 0000000..2b8d7c7 --- /dev/null +++ b/robot/chess/chesstitiansHttpHelper.go @@ -0,0 +1,245 @@ +package chess + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "math/rand" + "net/http" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/chess" + "mongo.games.com/game/proto" + proto_chesstitians "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/game/robot/base" +) + +type StockfishReq struct { + Fen string `json:"fen"` + Time int `json:"time"` + SkillLevel int `json:"skillLevel"` +} + +type StockfishResp struct { + Fen string + Time int64 + Bestmove string + Ponder string + Error int +} + +func Testjson() { + // test 发送http请求 + client := &http.Client{} + + // 创建HTTP请求 + req, err := http.NewRequest("GET", "http://127.0.0.1:23988/chess/jsontest", nil) + if err != nil { + panic(err) + } + + // 发送HTTP请求 + resp, err := client.Do(req) + if err != nil { + panic(err) + } + // 处理HTTP响应 + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + //fmt.Println(string(body)) + logger.Logger.Infof("@jsonTest %s", string(body)) + var stockfishResp StockfishResp + err = json.Unmarshal(body, &stockfishResp) + if err != nil { + panic(err) + } + logger.Logger.Infof("@bestmove=%s,ponder=%s", stockfishResp.Bestmove, stockfishResp.Ponder) +} + +func UCIPost(fen string, timeScore int) StockfishResp { + var stockfishResp = StockfishResp{ + Error: 1, + } + client := &http.Client{Timeout: time.Second * 5} + reqBody := StockfishReq{ + Fen: fen, + Time: timeScore, + } + reqJsonString, err := json.Marshal(reqBody) + if err != nil { + stockfishResp.Error = 2 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@UCIPost %v", string(reqJsonString)) + // 创建HTTP请求 + req, err := http.NewRequest("POST", common.CustomConfig.GetString("UCIURL"), bytes.NewBuffer(reqJsonString)) + if err != nil { + panic(err) + } + req.Header.Set("Content-Type", "application/json") + // 发送HTTP请求 + resp, err := client.Do(req) + if err != nil { + stockfishResp.Error = 3 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + // 处理HTTP响应 + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + stockfishResp.Error = 4 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@jsonTest %s", string(body)) + err = json.Unmarshal(body, &stockfishResp) + if err != nil { + stockfishResp.Error = 5 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + stockfishResp.Error = 0 + logger.Logger.Infof("@bestmove=%s,ponder=%s", stockfishResp.Bestmove, stockfishResp.Ponder) + return stockfishResp +} + +func MakrukPost(fen string, timeScore int) StockfishResp { + var stockfishResp = StockfishResp{ + Error: 1, + } + client := &http.Client{Timeout: time.Second * 5} + reqBody := StockfishReq{ + Fen: fen, + Time: timeScore, + } + reqJsonString, err := json.Marshal(reqBody) + if err != nil { + stockfishResp.Error = 2 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@UCIPost %v", string(reqJsonString)) + // 创建HTTP请求 + req, err := http.NewRequest("POST", common.CustomConfig.GetString("MakrukURL"), bytes.NewBuffer(reqJsonString)) + if err != nil { + panic(err) + } + req.Header.Set("Content-Type", "application/json") + // 发送HTTP请求 + resp, err := client.Do(req) + if err != nil { + stockfishResp.Error = 3 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + // 处理HTTP响应 + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + stockfishResp.Error = 4 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@jsonTest %s", string(body)) + err = json.Unmarshal(body, &stockfishResp) + if err != nil { + stockfishResp.Error = 5 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + stockfishResp.Error = 0 + logger.Logger.Infof("@bestmove=%s,ponder=%s", stockfishResp.Bestmove, stockfishResp.Ponder) + return stockfishResp +} + +func CambodianPost(fen string, timeScore int, skillLevel int) StockfishResp { + var stockfishResp = StockfishResp{ + Error: 1, + } + client := &http.Client{Timeout: time.Second * 5} + reqBody := StockfishReq{ + Fen: fen, + Time: timeScore, + SkillLevel: skillLevel, + } + reqJsonString, err := json.Marshal(reqBody) + if err != nil { + stockfishResp.Error = 2 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@UCIPost %v", string(reqJsonString)) + // 创建HTTP请求 + req, err := http.NewRequest("POST", common.CustomConfig.GetString("CambodianURL"), bytes.NewBuffer(reqJsonString)) + if err != nil { + panic(err) + } + req.Header.Set("Content-Type", "application/json") + // 发送HTTP请求 + resp, err := client.Do(req) + if err != nil { + stockfishResp.Error = 3 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + // 处理HTTP响应 + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + stockfishResp.Error = 4 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + logger.Logger.Infof("@jsonTest %s", string(body)) + err = json.Unmarshal(body, &stockfishResp) + if err != nil { + stockfishResp.Error = 5 + logger.Logger.Errorf("@UCIPost %v %v", stockfishResp, err.Error()) + return stockfishResp + } + stockfishResp.Error = 0 + logger.Logger.Infof("@bestmove=%s,ponder=%s", stockfishResp.Bestmove, stockfishResp.Ponder) + return stockfishResp +} + +func AiRandom(s *netlib.Session) { + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + p := scene.GetMe(s) + if me, ok2 := p.(*ChesstitiansPlayer); ok2 && me != ChesstitiansNilPlayer { + if scene.State == int32(rule.SceneStatePlayerOp) { + if me.IsBlack == 1 && scene.GetAct() == "b" || me.IsBlack != 1 && scene.GetAct() == "w" { + // 轮到当前行棋方 + chess := scene.chess + fen := chess.GenFen() + logger.Logger.Infof("fen: %s", fen) + + // 随机走一步 + + logger.Logger.Info("随机走一步") + // 随机走一步 + ms := chess.GetMoveAllPositions(me.IsBlack != 1) + if len(ms) > 0 { + m := ms[rand.Intn(len(ms))] + packOp := &proto_chesstitians.SCChesstitiansPlayerOp{ + OpCode: proto.Int32(rule.PlayerOpPlay), + OpParam: []int64{int64(m[0].Index), int64(m[1].Index)}, + } + base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{1, 2}...) + logger.Logger.Infof("CSChesstitiansPlayerOp random %v", packOp.String()) + } else { + logger.Logger.Errorf("随机一步失败") + } + } + } + } + } +} diff --git a/robot/chess/chesstitiansplayer.go b/robot/chess/chesstitiansplayer.go new file mode 100644 index 0000000..e7b4ce8 --- /dev/null +++ b/robot/chess/chesstitiansplayer.go @@ -0,0 +1,84 @@ +package chess + +import ( + chesstitiansApi "mongo.games.com/game/api3th/smart/chesstitians" + "mongo.games.com/game/proto" + proto_chesstitians "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/game/robot/base" +) + +var ChesstitiansNilPlayer *ChesstitiansPlayer = nil + +type ChesstitiansPlayer struct { + base.BasePlayer + *proto_chesstitians.ChesstitiansPlayerData + data *chesstitiansApi.PredictRequest +} + +func NewChesstitiansPlayer(data *proto_chesstitians.ChesstitiansPlayerData) *ChesstitiansPlayer { + p := &ChesstitiansPlayer{ChesstitiansPlayerData: data} + p.Init() + return p +} + +func (p *ChesstitiansPlayer) Init() { + p.Clear() +} + +func (p *ChesstitiansPlayer) Clear() { + +} + +func (p *ChesstitiansPlayer) MarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag |= flag + p.Flag = proto.Int32(myFlag) +} + +func (p *ChesstitiansPlayer) UnmarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag &= ^flag + p.Flag = proto.Int32(myFlag) +} + +func (p *ChesstitiansPlayer) IsMarkFlag(flag int32) bool { + if (p.GetFlag() & flag) != 0 { + return true + } + return false +} + +func (p *ChesstitiansPlayer) IsOnLine() bool { + return p.IsMarkFlag(base.PlayerState_Online) +} + +func (p *ChesstitiansPlayer) IsReady() bool { + return true +} + +func (p *ChesstitiansPlayer) IsSceneOwner() bool { + return false +} + +func (p *ChesstitiansPlayer) CanOp() bool { + return true +} + +func (p *ChesstitiansPlayer) IsRobot() bool { + player := base.PlayerMgrSingleton.GetPlayer(p.GetSnId()) + return player != nil +} + +func (p *ChesstitiansPlayer) SetFlag(flag int32) { + p.Flag = proto.Int32(flag) +} + +func (p *ChesstitiansPlayer) GetLastOp() int32 { + return 0 +} + +func (p *ChesstitiansPlayer) SetLastOp(op int32) { +} + +func (p *ChesstitiansPlayer) UpdateCards(cards []int32) { +} diff --git a/robot/chess/chesstitiansscene.go b/robot/chess/chesstitiansscene.go new file mode 100644 index 0000000..311db09 --- /dev/null +++ b/robot/chess/chesstitiansscene.go @@ -0,0 +1,125 @@ +package chess + +import ( + rule "mongo.games.com/game/gamerule/chess" + proto_chesstitians "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/game/protocol/player" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/netlib" +) + +type ChesstitiansScene struct { + base.BaseScene + *proto_chesstitians.SCChesstitiansRoomInfo + players map[int32]*ChesstitiansPlayer + chess rule.Logic +} +type filterFunc func(*ChesstitiansScene, *ChesstitiansPlayer, []int32, []int32, []int32, bool) (bool, []int32) + +var FilterMgr []filterFunc + +func NewChesstitiansScene(info *proto_chesstitians.SCChesstitiansRoomInfo) *ChesstitiansScene { + s := &ChesstitiansScene{ + SCChesstitiansRoomInfo: info, + players: make(map[int32]*ChesstitiansPlayer), + } + s.Init() + return s +} + +func (s *ChesstitiansScene) Init() { + s.players = make(map[int32]*ChesstitiansPlayer) + for _, mpd := range s.GetPlayers() { + p := NewChesstitiansPlayer(mpd) + if p != nil { + s.AddPlayer(p) + } + } + s.chess = rule.NewChess(int(s.Variant)) + s.chess.Init() + s.makeChessStruct(s.chess) +} +func (s *ChesstitiansScene) GetIsAllAi() bool { + var i int + for _, p := range s.players { + if p.IsRobot() { + i++ + } + } + return i == 4 +} +func (s *ChesstitiansScene) Clear() { + for _, p := range s.players { + p.Clear() + } +} + +func (s *ChesstitiansScene) AddPlayer(p base.IPlayer) { + if mp, ok := p.(*ChesstitiansPlayer); ok { + s.players[p.GetSnId()] = mp + } +} + +func (s *ChesstitiansScene) DelPlayer(snid int32) { + if p, exist := s.players[snid]; exist && p != nil { + delete(s.players, snid) + } +} + +func (s *ChesstitiansScene) GetPlayerByPos(pos int32) base.IPlayer { + return nil +} + +func (s *ChesstitiansScene) GetPlayerBySnid(snid int32) base.IPlayer { + if p, exist := s.players[snid]; exist { + return p + } + return nil +} + +func (s *ChesstitiansScene) GetPlayerByNotSnid(snid int32) base.IPlayer { + for _, chesstitiansPlayer := range s.players { + if chesstitiansPlayer.GetSnId() != snid { + return chesstitiansPlayer + } + } + return nil +} + +func (this *ChesstitiansScene) GetMe(s *netlib.Session) base.IPlayer { + if user, ok := s.GetAttribute(base.SessionAttributeUser).(*player.SCPlayerData); ok { + return this.GetPlayerBySnid(user.GetData().GetSnId()) + } + return nil +} +func (this *ChesstitiansScene) GetOp(s *netlib.Session) base.IPlayer { + if user, ok := s.GetAttribute(base.SessionAttributeUser).(*player.SCPlayerData); ok { + return this.GetPlayerByNotSnid(user.GetData().GetSnId()) + } + return nil +} + +func (s *ChesstitiansScene) IsFull() bool { + return len(s.players) >= int(s.MaxPlayerNum) +} + +func (s *ChesstitiansScene) IsMatchScene() bool { + return s.IsMatch == 1 || s.IsMatch == 2 //锦标赛和冠军赛 +} + +func (this *ChesstitiansScene) Update(ts int64) { +} + +func (this *ChesstitiansScene) makeChessStruct(chess rule.Logic) { + chess.Set(this.GetChess(), nil, this.GetAct(), this.GetCastling(), this.GetCambodianMove(), int(this.GetEnpassant()), int(this.GetChessRound())) +} + +func (this *ChesstitiansScene) PlayOp(fromIdx int, toIdx int) { + this.chess.Move(fromIdx, toIdx) + this.Chess[fromIdx] = string(this.chess.GetPiece(fromIdx)) + this.Chess[toIdx] = string(this.chess.GetPiece(toIdx)) + this.chess.NextAct() + this.Act = this.chess.GetAct() + this.Round = int32(this.chess.GetRound()) + this.CambodianMove = this.chess.GetCambodianMove() +} diff --git a/robot/chess/scchesstitians.go b/robot/chess/scchesstitians.go new file mode 100644 index 0000000..bfa47c2 --- /dev/null +++ b/robot/chess/scchesstitians.go @@ -0,0 +1,481 @@ +package chess + +import ( + "math/rand" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/timer" + + rule "mongo.games.com/game/gamerule/chess" + "mongo.games.com/game/proto" + proto_chesstitians "mongo.games.com/game/protocol/chesstitians" + "mongo.games.com/game/robot/base" +) + +type SCChesstitiansRoomInfoPacketFactory struct { +} + +type SCChesstitiansRoomInfoHandler struct { +} + +func (this *SCChesstitiansRoomInfoPacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansRoomInfo{} + return pack +} + +func (this *SCChesstitiansRoomInfoHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCChesstitiansRoomInfoHandler) Process [%v].", s.GetSessionConfig().Id) + if msg, ok := pack.(*proto_chesstitians.SCChesstitiansRoomInfo); ok { + scene := base.SceneMgrSingleton.GetScene(msg.GetRoomId()) + if scene == nil { + scene = NewChesstitiansScene(msg) + base.SceneMgrSingleton.AddScene(scene) + } + if scene != nil { + for _, pd := range msg.GetPlayers() { + if scene.GetPlayerBySnid(pd.GetSnId()) == nil { + p := NewChesstitiansPlayer(pd) + if p != nil { + scene.AddPlayer(p) + } + } + } + //logger.Logger.Infof("(this *SCChesstitiansRoomInfoHandler) SetAttribute(base.SessionAttributeSceneId) %v %v", scene.GetRoomId(), msg) + operateTask(s, 3) + } + } else { + logger.Logger.Error("SCChesstitiansRoomInfo package data error.") + } + return nil +} + +type SCChesstitiansPlayerOpPacketFactory struct { +} + +type SCChesstitiansPlayerOpHandler struct { +} + +func (this *SCChesstitiansPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansPlayerOp{} + return pack +} + +func (this *SCChesstitiansPlayerOpHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCChesstitiansPlayerOpHandler) Process [%v].", s.GetSessionConfig().Id) + if scChesstitiansOp, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerOp); ok { + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + player := scene.GetMe(s) + + if player == base.NilPlayer { + //logger.Logger.Errorf("(this *SCChesstitiansPlayerOpHandler) nilPlayer") + return nil + } + me, ok2 := player.(*ChesstitiansPlayer) + //logger.Logger.Infof("(this *SCChesstitiansPlayerOpHandler) player ok:%v me:%v", ok2, me) + if ok2 && me != ChesstitiansNilPlayer { + //logger.Logger.Tracef("SCChesstitiansPlayerOpHandler msg:%v", scChesstitiansOp) + isMeOp := me.GetSnId() == scChesstitiansOp.GetSnId() + if isMeOp { + if int(scChesstitiansOp.GetOpRetCode()) == 0 { + switch scChesstitiansOp.GetOpCode() { + case rule.PlayerOpPlay: + } + } else { //操作失败 + logger.Logger.Infof("TODO: 如果操作失败,应改为操作") + switch scChesstitiansOp.GetOpCode() { + case rule.PlayerOpPlay: + // ai请求失败,随便走一步 + AiRandom(s) + } + } + } + + if scChesstitiansOp.GetOpCode() == rule.PlayerOpPlay && scChesstitiansOp.GetOpRetCode() == 0 { + param := scChesstitiansOp.GetOpParam() + //scene.chess.PlayOp(int(param[0]), int(param[1])) + //scene.chess.NextAct() + scene.PlayOp(int(param[0]), int(param[1])) + } + + if !isMeOp { + //todo 对方求和,机器人暂时都同意求和 + // 机器人模拟真人,求和,认输,悔棋等操作 + switch scChesstitiansOp.GetOpCode() { + case rule.PlayerOpDraw: + pack := &proto_chesstitians.SCChesstitiansPlayerOp{ + OpCode: proto.Int32(rule.PlayerOpPlay), + } + if rand.Int31n(100) < 80 { + pack.OpParam = []int64{rule.RefuseDraw} + } else { + pack.OpParam = []int64{rule.AgreeDraw} + } + base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), + pack, []int{2, 5}...) + } + + operateTask(s, 3) + } + + } else { + logger.Logger.Errorf("(this *SCChesstitiansPlayerOpHandler) nilPlayer") + } + } else { + logger.Logger.Error("SCChesstitiansPlayerOp base.GetScene(s).(*ChesstitiansScene) error") + } + } else { + logger.Logger.Error("SCChesstitiansPlayerOp package data error.") + } + return nil +} + +type SCChesstitiansRoomStatePacketFactory struct { +} + +type SCChesstitiansRoomStateHandler struct { +} + +func (this *SCChesstitiansRoomStatePacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansRoomState{} + return pack +} + +func (this *SCChesstitiansRoomStateHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) Process [%v].", s.GetSessionConfig().Id) + if scChesstitiansRoomState, ok := pack.(*proto_chesstitians.SCChesstitiansRoomState); ok { + //logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) base.GetScene(s).(*ChesstitiansScene)") + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + scene.State = scChesstitiansRoomState.State + p := scene.GetMe(s) + me, ok := p.(*ChesstitiansPlayer) + //logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) get player snId:%v ok:%v me:%v", p.GetSnId(), ok, me) + if ok && me != ChesstitiansNilPlayer { + //logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) to roomState state:%v", scChesstitiansRoomState.GetState()) + switch scChesstitiansRoomState.GetState() { + case int32(rule.SceneStateWaitPlayer): + scene.Clear() + case int32(rule.SceneStateWaitStart): //等待开始 + scene.Clear() + if me.GetSnId() == scene.GetMasterSnid() { + packOp := &proto_chesstitians.CSChesstitiansPlayerOp{ + OpCode: proto.Int32(3), + } + proto.SetDefaults(packOp) + //if scene.GetIsAllAi() { + // base.DelayAISend(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp) + //} else { + // base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{3, 7}...) + //} + base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{2, 5}...) + } + case int32(rule.SceneStatePlayerOp): + logger.Logger.Infof("检查是否机器人先开始 %d", scene.CurOpIdx) + operateTask(s, 3) + case int32(rule.SceneStateBilled): + scene.Clear() + me.Clear() + } + } else { + logger.Logger.Errorf("(this *SCChesstitiansRoomStateHandler) get player snId:%v", me.GetSnId()) + } + } else { + logger.Logger.Errorf("(this *SCChesstitiansRoomStateHandler) base.GetScene(s).(*ChesstitiansScene)") + } + } else { + logger.Logger.Error("SCChesstitiansRoomState package data error.") + } + return nil +} + +func operateTask(s *netlib.Session, times int) { + logger.Logger.Info("@@CallableWrapper") + var array []int64 + var code int + + if s == nil { + return + } + + scene, ok := base.GetScene(s).(*ChesstitiansScene) + if !ok { + return + } + + p := scene.GetMe(s) + me, ok := p.(*ChesstitiansPlayer) + if !ok || me == ChesstitiansNilPlayer || me == nil { + return + } + + if scene.State != int32(rule.SceneStatePlayerOp) { + return + } + + if me.IsBlack == 1 && scene.GetAct() == "b" || me.IsBlack != 1 && scene.GetAct() == "w" { + // 轮到当前行棋方 + chess := scene.chess + fen := chess.GenFen() + logger.Logger.Infof("fen: %s", fen) + + skillLevel := 20 + switch scene.SceneType { + case 1: + skillLevel = -6 + case 2: + skillLevel = -3 + case 3: + skillLevel = 0 + case 4: + skillLevel = 3 + case 5: + skillLevel = 6 + } + + var resp StockfishResp + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if scene.GetVariant() == int32(rule.ChessVarChess) { + resp = UCIPost(fen, 500) + } else if scene.GetVariant() == int32(rule.ChessVarMakruk) { + resp = MakrukPost(fen, 500) + } else if scene.GetVariant() == int32(rule.ChessVarCambodian) { + resp = CambodianPost(fen, 500, skillLevel) + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if resp.Error == 0 && len(resp.Bestmove) >= 4 { + array = chess.UciTwoPosToIdxList(resp.Bestmove) + logger.Logger.Infof("fen: %v, Bestmove: %v, Ponder: %v", fen, resp.Bestmove, resp.Ponder) + packOp := &proto_chesstitians.SCChesstitiansPlayerOp{ + OpCode: proto.Int32(rule.PlayerOpPlay), + OpParam: array, + } + base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{1, 2}...) + //logger.Logger.Infof("CSChesstitiansPlayerOp %v", packOp.String()) + } else { + logger.Logger.Errorf("AI错误 %v", code) + if times > 0 { + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + operateTask(s, times-1) + return true + }), nil, time.Second*5, 1) + } else { + // ai请求失败,随便走一步 + AiRandom(s) + } + } + }), "UCI_Action").Start() + } +} + +type SCChesstitiansGameBilledPacketFactory struct { +} + +type SCChesstitiansGameBilledHandler struct { +} + +func (this *SCChesstitiansGameBilledPacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansGameBilled{} + return pack +} + +func (this *SCChesstitiansGameBilledHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCChesstitiansGameBilledHandler) Process [%v].", s.GetSessionConfig().Id) + if scChesstitiansBilled, ok := pack.(*proto_chesstitians.SCChesstitiansGameBilled); ok { + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + //logger.Logger.Trace(scChesstitiansBilled) + billData := scChesstitiansBilled.GetDatas() + for _, data := range billData { + p := scene.GetMe(s) + if p == base.NilPlayer { + continue + } + if me, ok2 := p.(*ChesstitiansPlayer); ok2 && me != ChesstitiansNilPlayer { + if data.GetSnId() == me.GetSnId() { //自己的数据 + me.Coin = proto.Int64(data.GetGameCoin()) + me.IsWin = data.GetIsWin() + me.ChessGrade = int64(data.Score) + me.WinTimes = data.WinTimes + me.WinScore = data.WinScore + me.OtherScore = data.OtherScore + me.NextRank = data.NextRank + me.WinCoin = data.WinCoin + } + } + } + } + } else { + logger.Logger.Error("SCChesstitiansGameBilled package data error.") + } + return nil +} + +type SCChesstitiansCardPacketFactory struct { +} + +type SCChesstitiansCardHandler struct { +} + +func (this *SCChesstitiansCardPacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansCard{} + return pack +} + +func (this *SCChesstitiansCardHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCChesstitiansCardHandler) Process [%v].", s.GetSessionConfig().Id) + if scChesstitiansCard, ok := pack.(*proto_chesstitians.SCChesstitiansCard); ok { + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + scene.Chess = scChesstitiansCard.GetChess() + scene.Act = scChesstitiansCard.GetAct() + scene.Castling = scChesstitiansCard.GetCastling() + scene.Enpassant = scChesstitiansCard.GetEnpassant() + scene.ChessRound = scChesstitiansCard.GetChessRound() + cards := scChesstitiansCard.GetCards() + p := scene.GetMe(s) + if me, ok2 := p.(*ChesstitiansPlayer); ok2 && me != ChesstitiansNilPlayer { + if scene.GetState() == int32(rule.SceneStateChessInit) { + op := scene.GetOp(s).(*ChesstitiansPlayer) // TODO: + if cards[0] == 1 { + me.IsBlack = 1 + op.IsBlack = 0 + } else { + me.IsBlack = 0 + op.IsBlack = 1 + } + scene.makeChessStruct(scene.chess) + } + } + } + } else { + logger.Logger.Error("SCChesstitiansCardHandler package SCChesstitiansCard error.") + } + return nil +} + +type SCChesstitiansUpdateMasterSnidPacketFactory struct { +} + +type SCChesstitiansUpdateMasterSnidHandler struct { +} + +func (this *SCChesstitiansUpdateMasterSnidPacketFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansUpdateMasterSnid{} + return pack +} + +func (this *SCChesstitiansUpdateMasterSnidHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCChesstitiansUpdateMasterSnidHandler) Process [%v].", s.GetSessionConfig().Id) + if scChesstitiansUpdateMasterSnid, ok := pack.(*proto_chesstitians.SCChesstitiansUpdateMasterSnid); ok { + if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok { + masterSnid := scChesstitiansUpdateMasterSnid.GetMasterSnid() + scene.MasterSnid = masterSnid //更新房主 + } + } else { + logger.Logger.Error("SCChesstitiansUpdateMasterSnidHandler package SCChesstitiansUpdateMasterSnid error.") + } + return nil +} + +// 玩家进入 + +type SCChesstitiansPlayerEnterFactory struct { +} + +func (this *SCChesstitiansPlayerEnterFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansPlayerEnter{} + return pack +} + +type SCChesstitiansPlayerEnterHandler struct { +} + +func (this *SCChesstitiansPlayerEnterHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + msg, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerEnter) + if !ok { + logger.Logger.Error("SCChesstitiansPlayerEnterHandler error") + return nil + } + logger.Logger.Trace("Recover SCChesstitiansPlayerEnterHandler ", msg.String()) + scene, ok := base.GetScene(s).(*ChesstitiansScene) + if !ok { + return nil + } + if scene == nil { + return nil + } + if player := NewChesstitiansPlayer(msg.GetData()); player != nil { + scene.AddPlayer(player) + } + return nil +} + +// 玩家离开 + +type SCChesstitiansPlayerLeaveFactory struct { +} + +func (this *SCChesstitiansPlayerLeaveFactory) CreatePacket() interface{} { + pack := &proto_chesstitians.SCChesstitiansPlayerLeave{} + return pack +} + +type SCChesstitiansPlayerLeaveHandler struct { +} + +func (this *SCChesstitiansPlayerLeaveHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + msg, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerLeave) + if !ok { + logger.Logger.Error("SCChesstitiansPlayerLeaveHandler error") + return nil + } + logger.Logger.Trace("Recover SCChesstitiansPlayerLeaveHandler ", msg.String()) + scene, ok := base.GetScene(s).(*ChesstitiansScene) + if !ok { + return nil + } + if scene == nil { + return nil + } + if player, ok := scene.GetPlayerByPos(msg.GetPos()).(*ChesstitiansPlayer); ok && player != nil { + scene.DelPlayer(player.GetSnId()) + } + return nil +} + +func init() { + //SCChesstitiansRoomInfo + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), &SCChesstitiansRoomInfoHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), &SCChesstitiansRoomInfoPacketFactory{}) + //SCChesstitiansPlayerOp + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), &SCChesstitiansPlayerOpHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), &SCChesstitiansPlayerOpPacketFactory{}) + //SCChesstitiansRoomState + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), &SCChesstitiansRoomStateHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), &SCChesstitiansRoomStatePacketFactory{}) + //SCChesstitiansFinalBilled + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), &SCChesstitiansGameBilledHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), &SCChesstitiansGameBilledPacketFactory{}) + //SCChesstitiansCard + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), &SCChesstitiansCardHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), &SCChesstitiansCardPacketFactory{}) + //SCChesstitiansUpdateMasterSnid + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), &SCChesstitiansUpdateMasterSnidHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), &SCChesstitiansUpdateMasterSnidPacketFactory{}) + //ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), &SCChesstitiansPlayerEnterHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), &SCChesstitiansPlayerEnterFactory{}) + //ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave + netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), &SCChesstitiansPlayerLeaveHandler{}) + netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), &SCChesstitiansPlayerLeaveFactory{}) + + ////Testjson() + //task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // logger.Logger.Info("@@CallableWrapper") + // stockfishResp := UCIPost("fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 500) + // logger.Logger.Infof("stockfishResp %v", stockfishResp) + // return nil + //}), nil, "UCIPost_Action").Start() +} diff --git a/robot/config.json b/robot/config.json new file mode 100644 index 0000000..10467c2 --- /dev/null +++ b/robot/config.json @@ -0,0 +1,129 @@ +{ + "netlib": { + "SrvInfo": { + "Name": "RobotServer", + "Type": 9, + "Id": 901, + "AreaID": 1, + "Banner": [ + "=================", + "robot server", + "=================" + ] + }, + "IoServices": [ + { + "Id": 901, + "Type": 9, + "AreaId": 1, + "Name": "RobotService", + "Ip": "", + "Port": 9003, + "MaxDone": 200000, + "MaxPend": 200000, + "MaxPacket": 65535, + "MaxConn": 2, + "RcvBuff": 8192000, + "SndBuff": 8192000, + "WriteTimeout": 300, + "ReadTimeout": 300, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler", + "handler-world-close" + ] + } + ] + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal": { + "SupportSignal": true + }, + "benchmark": { + "Count": 20, + "AppId": "5c56d1644966f078bfb90c71", + "Connects": { + "Id": 10000, + "Type": 1, + "AreaId": 1, + "Name": "ClientService", + "Ip": "127.0.0.1", + "Port": 11001, + "Protocol": "tcp", + "Path": "/", + "MaxDone": 200, + "MaxPend": 200, + "MaxPacket": 65535, + "MaxConn": 2000, + "RcvBuff": 4096, + "SndBuff": 4096, + "WriteTimeout": 3600, + "ReadTimeout": 3600, + "SoLinger": 10, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "www.jxjy.games.cn", + "IsClient": true, + "AllowMultiConn": true, + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "handler-gate-session" + ] + } + }, + "costum": { + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + "UseTIENLENSmartApi3th": false, + "TIENLENApi3thTimeout": 3, + "TIENLENApi3thAddr": "http://127.0.0.1:5000", + "TIENLEN2Api3thAddr": "http://127.0.0.1:5001", + "TIENLENApiKey": "", + "CambodianURL": "http://127.0.0.1:23988/chess/cambodian" + }, + "data": { + "RootPath": "../data" + } +} \ No newline at end of file diff --git a/robot/fishing/fish.go b/robot/fishing/fish.go new file mode 100644 index 0000000..c72c7a6 --- /dev/null +++ b/robot/fishing/fish.go @@ -0,0 +1,31 @@ +package fishing + +import "sync" + +type Fish struct { + id int32 //实例id + tempId int32 //模板id + coin int32 //价值 + birthTick int32 //出生时间 + dieTick int32 //死亡时间 +} + +var fishPool = sync.Pool{ + New: func() interface{} { + return &Fish{} + }, +} + +func NewFish(id, tempId, coin, birthTick, dieTick int32) *Fish { + f := fishPool.Get().(*Fish) + f.id = id + f.tempId = tempId + f.coin = coin + f.birthTick = birthTick + f.dieTick = dieTick + return f +} + +func DestoryFish(f *Fish) { + fishPool.Put(f) +} diff --git a/robot/fishing/fishplayer.go b/robot/fishing/fishplayer.go new file mode 100644 index 0000000..23a4151 --- /dev/null +++ b/robot/fishing/fishplayer.go @@ -0,0 +1,320 @@ +package fishing + +import ( + "fmt" + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + fish_proto "mongo.games.com/game/protocol/fishing" + hall_proto "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/logger" + "time" +) + +type FishingPlayer struct { + base.BasePlayer + *fish_proto.FishingPlayerData + dbGameFree *server_proto.DB_GameFree //自由场数据 + scene *FishingScene + changePowerTs int64 + changePower2Ts int64 //倍率递归变化时间间隔 + lifeTimeTs int64 + fireTimeTs int64 + leaveTimeTs int64 + changeVIPTimeTs int64 + changeVIPTime int64 + curPower int32 //当前倍率 + targetPower int32 //目标倍率 + lockFish int32 //当前锁定攻击的鱼 + logicTick int32 //逻辑时钟 + bulletSeq int32 //子弹增长因子 + bulletId int32 //子弹编号 +} + +func NewFishingPlayer(data *fish_proto.FishingPlayerData, sceneEx *FishingScene, gameFreeId, roomId int32) *FishingPlayer { + p := &FishingPlayer{ + FishingPlayerData: data, + scene: sceneEx, + dbGameFree: base.SceneMgrSingleton.GetSceneDBGameFree(roomId, gameFreeId), + changePowerTs: 0, + changePower2Ts: 0, + lifeTimeTs: 0, + fireTimeTs: 0, + leaveTimeTs: 0, + changeVIPTimeTs: 0, + changeVIPTime: 0, + curPower: 0, + targetPower: 0, + lockFish: 0, + } + p.Init() + return p +} + +func (this *FishingPlayer) RandomVipGun() { + if this.IsRobot() == false { + return + } + + sceneType := this.dbGameFree.GetSceneType() + + minValue := int32(0) + maxValue := int32(1) + switch sceneType { + case 1: //1,初级场 0至2中随机1个 + minValue = 0 + maxValue = 2 + case 2: //2,中级场 1至4中随机1个 + minValue = 1 + maxValue = 4 + case 3: //3,高级场 2至6中随机1个 + minValue = 2 + maxValue = 6 + default: + minValue = 0 + maxValue = 1 + } + vipLevel := this.GetVIP() + if vipLevel < maxValue { + maxValue = vipLevel + if maxValue <= minValue { + minValue = 0 + maxValue = 1 + } + } + + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil { + vipLevelSelected := rand.Int31n(maxValue-minValue) + minValue + pack := fish_proto.CSFishingOp{ + OpCode: proto.Int32(FishingPlayerOpSelVip), + } + pack.Params = append(pack.Params, int64(vipLevelSelected)) + s.Send(int(fish_proto.FIPacketID_FISHING_CS_OP), pack) + } + +} + +func (this *FishingPlayer) RandomPower() { + if this.IsRobot() == false { + logger.Logger.Trace("RandomPower not robot", this.GetSnId()) + return + } + + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil { + logger.Logger.Trace("RandomPower ", this.GetSnId()) + + otherParams := this.dbGameFree.GetOtherIntParams() + powerIndex := rand.Int31n(int32(len(otherParams))) + powerValue := otherParams[powerIndex] + if common.InSliceInt64(otherParams, powerValue) { + this.targetPower = int32(powerValue) + } + logger.Logger.Trace("RandomPower ", this.GetSnId(), powerValue) + + this.changePower2Ts = int64(time.Duration(common.RandInt(500, 1000)) * time.Millisecond) + } +} + +func (this *FishingPlayer) CheckInitGameCoin() { + if this.IsRobot() == false { + logger.Logger.Trace("CheckInitGameCoin not robot", this.GetSnId()) + return + } + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil { + minValue := int(this.dbGameFree.GetRobotTakeCoin()[0]) + maxValue := int(this.dbGameFree.GetRobotTakeCoin()[1]) + if maxValue == minValue { + minValue = int(this.dbGameFree.GetBaseScore()) + maxValue = minValue * 100 + } + if this.GetCoin() < int64(minValue) || this.GetCoin() > int64(maxValue) { + coins := rand.Intn(maxValue-minValue) + minValue - int(this.GetCoin()) + base.ExePMCmd(s, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, coins)) + } + + //ExePMCmd(s, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, 100)) + } +} + +func (this *FishingPlayer) Init() { + //在场内时间 + this.leaveTimeTs = int64(time.Duration(common.RandInt(5, 10)) * time.Minute) + //换炮间隔 + this.changeVIPTime = int64(time.Duration(common.RandInt(5, 10)) * time.Minute) + + this.RandomVipGun() + this.RandomPower() + this.CheckInitGameCoin() +} + +func (this *FishingPlayer) Clear() { + this.logicTick = 0 + this.bulletId = 0 +} + +func (this *FishingPlayer) UpdatePlayerData(data *fish_proto.FishingPlayerData) { + this.FishingPlayerData = data +} + +func (this *FishingPlayer) MarkFlag(flag int32) { + myFlag := this.GetFlag() + myFlag |= flag + this.Flag = proto.Int32(myFlag) +} + +func (this *FishingPlayer) UnmarkFlag(flag int32) { + myFlag := this.GetFlag() + myFlag &= ^flag + this.Flag = proto.Int32(myFlag) +} + +func (this *FishingPlayer) IsMarkFlag(flag int32) bool { + if (this.GetFlag() & flag) != 0 { + return true + } + return false +} + +func (this *FishingPlayer) IsOnLine() bool { + return this.IsMarkFlag(base.PlayerState_Online) +} + +func (this *FishingPlayer) IsReady() bool { + return this.IsMarkFlag(base.PlayerState_Ready) +} + +func (this *FishingPlayer) IsSceneOwner() bool { + return this.IsMarkFlag(base.PlayerState_SceneOwner) +} + +func (this *FishingPlayer) IsRobot() bool { + player := base.PlayerMgrSingleton.GetPlayer(this.GetSnId()) + return player != nil +} + +func (this *FishingPlayer) SetFlag(flag int32) { + this.Flag = proto.Int32(flag) +} + +func (this *FishingPlayer) GetLastOp() int32 { + return 0 +} + +func (this *FishingPlayer) SetLastOp(op int32) { +} + +func (this *FishingPlayer) UpdateCards(cards []int32) { +} + +func (this *FishingPlayer) Update(ts int64) { + if !this.IsRobot() { + return + } + //切换倍率 + if this.targetPower == this.curPower { + this.changePowerTs += ts + if this.changePowerTs >= int64(2*time.Minute) { + this.changePowerTs = 0 + + this.RandomPower() + } + } else { + this.changePower2Ts = this.changePower2Ts - ts + if this.changePower2Ts <= int64(0*time.Millisecond) { + if this.targetPower < this.curPower { + this.curPower = this.curPower - this.dbGameFree.GetBaseScore() + } else if this.targetPower > this.curPower { + this.curPower = this.curPower + this.dbGameFree.GetBaseScore() + } + + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil { + pack := &fish_proto.CSFishingOp{ + OpCode: proto.Int32(FishingPlayerOpSetPower), + } + pack.Params = append(pack.Params, int64(this.curPower), int64(this.targetPower)) + s.Send(int(fish_proto.FIPacketID_FISHING_CS_OP), pack) + logger.Logger.Tracef("this.curPower =", this.curPower) + } + + this.changePower2Ts = int64(time.Duration(common.RandInt(500, 1000)) * time.Millisecond) + } + } + + //换炮 + this.changeVIPTimeTs += ts + if this.changeVIPTimeTs >= this.changeVIPTime { + this.changeVIPTimeTs = 0 + this.RandomVipGun() + } + + //离场 + this.lifeTimeTs += ts + if this.lifeTimeTs >= this.leaveTimeTs { + this.lifeTimeTs = 0 + + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil { + pack := &hall_proto.CSQuitGame{ + Id: proto.Int32(this.dbGameFree.Id), + } + proto.SetDefaults(pack) + s.Send(int(hall_proto.GameHallPacketID_PACKET_CS_QUITGAME), pack) + } + } + + //一发子弹一发命中,交替执行 + if this.scene.logicTick%2 == 0 { + this.Fire() + } else { + this.Hit() + } +} + +func (this *FishingPlayer) Fire() { + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil && this.scene != nil { + f := this.scene.RandGetOneFish() + if f != nil { + this.lockFish = f.id + this.bulletSeq++ + this.bulletId = this.bulletSeq + pack := &fish_proto.CSFishingOp{ + OpCode: proto.Int32(FishingPlayerOpFire), + Params: []int64{rand.Int63n(1280), rand.Int63n(720), int64(this.bulletId), int64(this.curPower)}, + } + proto.SetDefaults(pack) + s.Send(int(fish_proto.FIPacketID_FISHING_CS_OP), pack) + logger.Logger.Infof("FishingPlayer.Fire(snid:%v, bullet:%v)", this.GetSnId(), this.bulletId) + } + } +} + +func (this *FishingPlayer) Hit() { + if this.lockFish != 0 && this.bulletId != 0 { + s := base.PlayerMgrSingleton.GetPlayerSession(this.GetSnId()) + if s != nil && this.scene != nil { + f := this.scene.GetFish(this.lockFish) + if f == nil { + f = this.scene.RandGetOneFish() + } + if f == nil { + return + } + + pack := &fish_proto.CSFishingOp{ + OpCode: proto.Int32(FishingPlayerOpHitFish), + Params: []int64{int64(this.bulletId), int64(f.id)}, + } + proto.SetDefaults(pack) + s.Send(int(fish_proto.FIPacketID_FISHING_CS_OP), pack) + logger.Logger.Infof("FishingPlayer.Hit(snid:%v, bullet:%v)", this.GetSnId(), this.bulletId) + this.bulletId = 0 + } + } +} diff --git a/robot/fishing/fishpolicymgr.go b/robot/fishing/fishpolicymgr.go new file mode 100644 index 0000000..de8fce9 --- /dev/null +++ b/robot/fishing/fishpolicymgr.go @@ -0,0 +1,257 @@ +package fishing + +import ( + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" +) + +type Policy struct { + Data map[int32][]IPolicyData + MaxTick int32 +} + +type IPolicyData interface { + GetId() int32 + GetTime() int32 + GetFishId() int32 + GetPaths() []int32 + GetCount() int32 + GetSpeed() int32 + GetEvent() int32 + GetRefreshInterval() int32 + GetTimeToLive() int32 +} + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + logger.Logger.Info("初始化鱼灯[S]") + defer logger.Logger.Info("初始化鱼灯[E]") + /* + * 101,102,103-151,152,153 + */ + data := []IPolicyData{} + for _, value := range srvdata.PBDB_Policy101Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(101, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy102Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(102, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy103Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(103, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy151Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(151, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy152Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(152, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy153Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(153, data) + /* + * 201,202,203-251,252,253 + */ + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy201Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(201, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy202Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(202, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy203Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(203, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy251Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(251, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy252Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(252, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy253Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(253, data) + /* + * 301,302,303-351,352,353 + */ + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy301Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(301, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy302Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(302, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy303Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(303, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy351Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(351, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy352Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(352, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy353Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(353, data) + /* + * 401,402,403-451,452,453 + */ + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy401Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(401, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy402Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(402, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy403Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(403, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy451Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(451, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy452Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(452, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy453Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(453, data) + /* + * 501,502-811,812,813,814,815,816 + */ + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy501Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(501, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy502Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(502, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy811Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(811, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy812Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(812, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy813Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(813, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy814Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(814, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy815Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(815, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy816Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(816, data) + /* + * 701,702 + */ + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy701Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(701, data) + data = []IPolicyData{} + for _, value := range srvdata.PBDB_Policy702Mgr.Datas.Arr { + data = append(data, value) + } + FishPolicyMgrSington.AddPolicyData(702, data) + + return nil + }) +} + +// FishPolicyMgrSington 机器人使用 +var FishPolicyMgrSington = &FishPolicyMgr{ + data: make(map[int32]*Policy), +} + +type FishPolicyMgr struct { + data map[int32]*Policy +} + +func (this *FishPolicyMgr) GetFishByTime(policyId int32, tick int32) []IPolicyData { + if data, ok := this.data[policyId]; ok { + return data.Data[tick] + } + return nil +} + +func (this *FishPolicyMgr) AddPolicyData(id int32, data []IPolicyData) { + p := &Policy{ + Data: make(map[int32][]IPolicyData), + MaxTick: 0, + } + for _, value := range data { + tick := value.GetTime() + fishsArr := p.Data[tick] + if fishsArr == nil { + p.Data[tick] = []IPolicyData{value} + } else { + p.Data[tick] = append(fishsArr, value) + } + if tick > p.MaxTick { + p.MaxTick = tick + } + } + this.data[id] = p +} diff --git a/robot/fishing/fishscene.go b/robot/fishing/fishscene.go new file mode 100644 index 0000000..7948626 --- /dev/null +++ b/robot/fishing/fishscene.go @@ -0,0 +1,244 @@ +package fishing + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/fishing" + fish_proto "mongo.games.com/game/protocol/fishing" + player_proto "mongo.games.com/game/protocol/player" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/robot/base" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type FishingScene struct { + base.BaseScene + *fish_proto.SCFishingRoomInfo + s *netlib.Session + dbGameFree *server_proto.DB_GameFree //自由场数据 + seats [fishing.MaxPlayer]*FishingPlayer + players map[int32]*FishingPlayer + fishes map[int32]*Fish + syncedPolicy map[int64]struct{} + logicTick int32 //当前刷鱼逻辑时钟 + willExpiredMap map[int32]map[int32]*Fish //即将超期的鱼 +} + +func NewFishingScene(info *fish_proto.SCFishingRoomInfo) *FishingScene { + s := &FishingScene{ + SCFishingRoomInfo: info, + players: make(map[int32]*FishingPlayer), + fishes: make(map[int32]*Fish), + syncedPolicy: make(map[int64]struct{}), + willExpiredMap: make(map[int32]map[int32]*Fish), //即将超期的鱼 + logicTick: 0, + } + s.Init() + + logger.Logger.Trace("NewFishingScene ") + + return s +} + +func (this *FishingScene) Init() { + this.dbGameFree = base.SceneMgrSingleton.GetSceneDBGameFree(this.GetRoomId(), this.GetGameFreeId()) +} + +func (this *FishingScene) Clear() { + for _, player := range this.players { + player.Clear() + } + + for _, f := range this.fishes { + this.DelFish(f) + } + this.syncedPolicy = make(map[int64]struct{}) + this.fishes = make(map[int32]*Fish) + this.willExpiredMap = make(map[int32]map[int32]*Fish) +} + +func (this *FishingScene) AddPlayer(p base.IPlayer) { + if mp, ok := p.(*FishingPlayer); ok { + this.players[p.GetSnId()] = mp + this.seats[p.GetPos()] = mp + + logger.Logger.Trace("playernum ", len(this.players)) + } +} + +func (this *FishingScene) DelPlayer(snid int32) { + if p, exist := this.players[snid]; exist && p != nil { + delete(this.players, snid) + this.seats[p.GetPos()] = nil + } +} + +func (this *FishingScene) GetPlayerByPos(pos int32) base.IPlayer { + if pos >= 0 && pos < fishing.MaxPlayer { + return this.seats[pos] + } + return nil +} + +func (this *FishingScene) GetPlayerBySnid(snid int32) base.IPlayer { + if p, exist := this.players[snid]; exist { + return p + } + return nil +} + +func (this *FishingScene) GetMe(s *netlib.Session) base.IPlayer { + if user, ok := s.GetAttribute(base.SessionAttributeUser).(*player_proto.SCPlayerData); ok { + player := this.GetPlayerBySnid(user.GetData().GetSnId()) + return player + } + return nil +} + +func (this *FishingScene) GetPlayerCount() int32 { + return int32(len(this.players)) +} + +func (this *FishingScene) IsFull() bool { + return len(this.players) >= int(fishing.MaxPlayer) +} + +func (this *FishingScene) IsMatchScene() bool { + return this.GetRoomId() >= common.MatchSceneStartId && this.GetRoomId() <= common.MatchSceneMaxId +} + +func (this *FishingScene) IsCoinScene() bool { + return this.GetRoomId() >= common.CoinSceneStartId && this.GetRoomId() <= common.CoinSceneMaxId +} + +func (this *FishingScene) InitPlayer(s *netlib.Session, p *FishingPlayer) { + +} + +// 玩家操作 +const ( + FishingPlayerOpFire int32 = iota //开炮 + FishingPlayerOpHitFish //命中 + FishingPlayerOpSetPower //切换倍率 + FishingPlayerOpSelVip //切换VIP鱼炮 + FishingPlayerOpRobotFire + FishingPlayerOpRobotHitFish + FishingPlayerOpLeave + FishingPlayerOpEnter + FishingPlayerOpAuto + FishingPlayerOpSelTarget + FishingPlayerOpFireRate + FishingRobotOpAuto + FishingRobotOpSetPower + FishingPlayerHangup + FishingRobotWantLeave +) + +func (this *FishingScene) UpdateOnlinePlayers(onlinePlayers []int32) { + for iPos := 0; iPos < fishing.MaxPlayer; iPos++ { + seat := this.seats[iPos] + if seat != nil { + if common.InSliceInt32(onlinePlayers, seat.GetSnId()) == false { + this.DelPlayer(seat.GetSnId()) + } + } + } +} + +func (this *FishingScene) AllIsRobot() bool { + bAllIsRobot := true + for _, player := range this.players { + if base.PlayerMgrSingleton.GetPlayer(player.GetSnId()) == nil { + bAllIsRobot = false + } + } + return bAllIsRobot +} + +func (this *FishingScene) Update(ts int64) { + //logger.Logger.Trace("FishingScene Update GetPlayerCount ", this.GetPlayerCount()) + + //清理过期的鱼 + //this.DelExpiredFish(this.logicTick) + //this.logicTick++ + // + //for _, player := range this.players { + // player.Update(ts) + //} +} + +func (this *FishingScene) flushFish(policyId, logicTick int32) { + if _, exist := this.syncedPolicy[common.MakeI64(policyId, logicTick)]; exist { + //这波鱼已经刷过了 + return + } + + this.logicTick = logicTick + policyData := FishPolicyMgrSington.GetFishByTime(policyId, logicTick) + if len(policyData) <= 0 { + return + } + + for _, value := range policyData { + fishRateData := srvdata.PBDB_FishRateMgr.GetData(value.GetFishId()) + var coin int32 + gold := fishRateData.GetGold() + if len(gold) > 0 { + coin = gold[0] + if len(gold) > 1 { + coin = int32(common.RandInt(int(gold[0]), int(gold[1]))) + } + } + count := value.GetCount() // 获取当前的数量 + for index := int32(0); index < int32(count); index++ { + id := policyId*1000000 + int32(value.GetId())*100 + int32(index+1) // 作为Fish的唯一标识 + var birthTick = logicTick + index*value.GetRefreshInterval() // 这个条鱼出生的时间 + var liveTick = birthTick + value.GetTimeToLive()*10 // 这条鱼存货的时间 + fish := NewFish(id, value.GetFishId(), coin, birthTick, liveTick) // 根据参数生成Fish对象 + if fish != nil { + this.AddFish(fish) + } + } + } +} + +func (this FishingScene) GetFish(id int32) *Fish { + return this.fishes[id] +} + +func (this *FishingScene) AddFish(f *Fish) { + this.fishes[f.id] = f + willExpired := this.willExpiredMap[f.dieTick] + if willExpired == nil { + willExpired = make(map[int32]*Fish) + this.willExpiredMap[f.dieTick] = willExpired + } + willExpired[f.id] = f +} + +func (this *FishingScene) DelFish(f *Fish) { + delete(this.fishes, f.id) + if willExpired, exist := this.willExpiredMap[f.dieTick]; exist { + delete(willExpired, f.id) + } +} + +func (this *FishingScene) DelExpiredFish(logicTick int32) { + if expired, exist := this.willExpiredMap[logicTick]; exist { + for _, f := range expired { + this.DelFish(f) + } + } +} + +func (this *FishingScene) RandGetOneFish() *Fish { + for _, f := range this.fishes { + if f.birthTick > this.logicTick || f.dieTick < this.logicTick { //还未出生|已经死亡 + continue + } + + return f + } + return nil +} diff --git a/robot/fishing/scfish.go b/robot/fishing/scfish.go new file mode 100644 index 0000000..1e85415 --- /dev/null +++ b/robot/fishing/scfish.go @@ -0,0 +1,289 @@ +package fishing + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + fish_proto "mongo.games.com/game/protocol/fishing" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +// FIPacketID_FISHING_SC_ROOMINFO +type SCFishingRoomInfoPacketFactory struct { +} + +type SCFishingRoomInfoHandler struct { +} + +func (this *SCFishingRoomInfoPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFishingRoomInfo{} + return pack +} + +func (this *SCFishingRoomInfoHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("(this *SCFishingRoomInfoHandler) Process ", s.GetSessionConfig().Id, pack) + if msg, ok := pack.(*fish_proto.SCFishingRoomInfo); ok { + sceneEx, _ := base.SceneMgrSingleton.GetScene(msg.GetRoomId()).(*FishingScene) + if sceneEx == nil { + sceneEx = NewFishingScene(msg) + base.SceneMgrSingleton.AddScene(sceneEx) + } + if sceneEx != nil { + for _, pd := range msg.GetPlayers() { + if oldPlayer, ok := sceneEx.GetPlayerBySnid(pd.GetSnId()).(*FishingPlayer); ok { + oldPlayer.UpdatePlayerData(pd) + } else { + p := NewFishingPlayer(pd, sceneEx, sceneEx.GetGameFreeId(), sceneEx.GetRoomId()) + if p != nil { + sceneEx.AddPlayer(p) + } + } + } + //logger.Logger.Trace(msg) + var me *FishingPlayer + if sceneEx.GetMe(s) != nil { + me = sceneEx.GetMe(s).(*FishingPlayer) + sceneEx.InitPlayer(s, me) + } + + } + } else { + logger.Logger.Error("SCFishingRoomInfo package data error.") + } + return nil +} + +type SCFishingRoomStatePacketFactory struct { +} + +type SCFishingRoomStateHandler struct { +} + +func (this *SCFishingRoomStatePacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFishingRoomState{} + return pack +} + +func (this *SCFishingRoomStateHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + if _, ok := pack.(*fish_proto.SCFishingRoomState); ok { + if sceneEx, ok := base.GetScene(s).(*FishingScene); ok { + sceneEx.Clear() + } + } else { + logger.Logger.Error("SCFishingRoomStateHandler package data error.") + } + return nil +} + +// FIPacketID_FISHING_SC_SEATS +type SCFishingSeatsPacketFactory struct { +} + +type SCFishingSeatsHandler struct { +} + +func (this *SCFishingSeatsPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFishingSeats{} + return pack +} + +func (this *SCFishingSeatsHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Trace("(this *SCFishingSeatsHandler) Process ", s.GetSessionConfig().Id, pack) + if scFishingSeats, ok := pack.(*fish_proto.SCFishingSeats); ok { + if sceneEx, ok := base.GetScene(s).(*FishingScene); ok { + var leftPlayers []int32 + for _, pd := range scFishingSeats.GetData() { + if oldPlayer, ok := sceneEx.GetPlayerBySnid(pd.GetSnId()).(*FishingPlayer); ok { + oldPlayer.UpdatePlayerData(pd) + } else { + p := NewFishingPlayer(pd, sceneEx, sceneEx.GetGameFreeId(), sceneEx.GetRoomId()) + if p != nil { + sceneEx.AddPlayer(p) + } + } + + leftPlayers = append(leftPlayers, pd.GetSnId()) + } + + sceneEx.UpdateOnlinePlayers(leftPlayers) + + //if sceneEx.AllIsRobot() { + // sceneEx.Clear() + // base.SceneMgrSingleton.DelScene(sceneEx.GetRoomId()) + //} + } + } else { + logger.Logger.Error("SCFishingSeatsHandler package data error.") + } + return nil +} + +// FIPacketID_FISHING_SC_FIREPOWER +type SCFishingFirePowerPacketFactory struct { +} + +type SCFishingFirePowerHandler struct { +} + +func (this *SCFishingFirePowerPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFirePower{} + return pack +} + +func (this *SCFishingFirePowerHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + //logger.Logger.Trace("(this *SCFishingFirePowerHandler) Process ", s.GetSessionConfig().Id, pack) + if _, ok := pack.(*fish_proto.SCFirePower); ok { + //logger.Logger.Trace(scPack) + } else { + logger.Logger.Error("SCFishingSeatsHandler package data error.") + } + return nil +} + +// FIPacketID_FISHING_SC_FIREPOWER +type SCFishingSynFishPacketFactory struct { +} + +type SCFishingSynFishHandler struct { +} + +func (this *SCFishingSynFishPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCSyncRefreshFish{} + return pack +} + +func (this *SCFishingSynFishHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + //logger.Logger.Trace("(this *SCFishingSynFishHandler) Process ", s.GetSessionConfig().Id, pack) + if msg, ok := pack.(*fish_proto.SCSyncRefreshFish); ok { + //logger.Logger.Trace(scPack) + if sceneEx, ok := base.GetScene(s).(*FishingScene); ok { + sceneEx.flushFish(msg.GetPolicyId(), msg.GetTimePoint()) + } + } else { + logger.Logger.Error("SCFishingSynFishHandler package data error.") + } + return nil +} + +// FIPacketID_FISHING_SC_FIREPOWER +type SCFishingOpPacketFactory struct { +} + +type SCFishingOpHandler struct { +} + +func (this *SCFishingOpPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFishingOp{} + return pack +} + +func (this *SCFishingOpHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + if scPack, ok := pack.(*fish_proto.SCFishingOp); ok { + if scPack.GetOpCode() == FishingPlayerOpRobotFire { + logger.Logger.Infof("SCFishingSynFishHandler package data ==> snid %v opcode %v opRetCode %v ", scPack.GetSnId(), scPack.GetOpCode(), scPack.GetOpRetCode()) + if scPack.GetOpRetCode() == fish_proto.OpResultCode_OPRC_CoinNotEnough || scPack.GetOpRetCode() == fish_proto.OpResultCode_OPRC_Sucess { + //退出 + logger.Logger.Infof("SCFishingSynFishHandler package data ==> snid %v opcode %v opRetCode %v ", scPack.GetSnId(), scPack.GetOpCode(), scPack.GetOpRetCode()) + pack := &hall_proto.CSCoinSceneOp{ + Id: proto.Int32(0), + OpType: proto.Int32(common.CoinSceneOp_Leave), + } + proto.SetDefaults(pack) + //s.Send(int(fish_proto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack) + base.DelaySendSecond(s, int(hall_proto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack, 3, 10) + //ExePMCmd(s, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, 50000)) + } + } else if scPack.GetOpCode() == FishingRobotWantLeave { + pack := &hall_proto.CSCoinSceneOp{ + Id: proto.Int32(0), + OpType: proto.Int32(common.CoinSceneOp_Leave), + } + proto.SetDefaults(pack) + base.DelaySendSecond(s, int(hall_proto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack, 3, 10) + } + } else { + logger.Logger.Error("SCFishingSynFishHandler package data error.") + } + return nil +} + +// FIPacketID_FISHING_SC_FIREPOWER +type SCFishingFirePacketFactory struct { +} + +type SCFishingFireHandler struct { +} + +func (this *SCFishingFirePacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFire{} + return pack +} + +func (this *SCFishingFireHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + if _, ok := pack.(*fish_proto.SCFire); ok { + + } else { + logger.Logger.Error("SCFishingFireHandler package data error.") + } + return nil +} + +type SCFishingFireHitPacketFactory struct { +} + +type SCFishingFireHitHandler struct { +} + +func (this *SCFishingFireHitPacketFactory) CreatePacket() interface{} { + pack := &fish_proto.SCFireHit{} + return pack +} + +func (this *SCFishingFireHitHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + if msg, ok := pack.(*fish_proto.SCFireHit); ok { + if sceneEx, ok := base.GetScene(s).(*FishingScene); ok { + ids := msg.GetFishId() + for _, id := range ids { + f := sceneEx.GetFish(id) + if f != nil { + sceneEx.DelFish(f) + } + } + } + } else { + logger.Logger.Error("SCFishingFireHitHandler package data error.") + } + return nil +} + +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +func init() { + //SCFishingRoomInfo + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_ROOMINFO), &SCFishingRoomInfoHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_ROOMINFO), &SCFishingRoomInfoPacketFactory{}) + //SCFishingSeats + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_SEATS), &SCFishingSeatsHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_SEATS), &SCFishingSeatsPacketFactory{}) + + //SCFishingSeats + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_FIREPOWER), &SCFishingFirePowerHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_FIREPOWER), &SCFishingFirePowerPacketFactory{}) + + //SCFishingSeats + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_SYNCFISH), &SCFishingSynFishHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_SYNCFISH), &SCFishingSynFishPacketFactory{}) + + //SCFishingOp + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_OP), &SCFishingOpHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_OP), &SCFishingOpPacketFactory{}) + + //SCFire + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_FIRE), &SCFishingFireHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_FIRE), &SCFishingFirePacketFactory{}) + + //SCFireHit + netlib.RegisterHandler(int(fish_proto.FIPacketID_FISHING_SC_FIREHIT), &SCFishingFireHitHandler{}) + netlib.RegisterFactory(int(fish_proto.FIPacketID_FISHING_SC_FIREHIT), &SCFishingFireHitPacketFactory{}) +} diff --git a/robot/logger.xml b/robot/logger.xml new file mode 100644 index 0000000..e859e4e --- /dev/null +++ b/robot/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robot/main.go b/robot/main.go new file mode 100644 index 0000000..e417004 --- /dev/null +++ b/robot/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/module" + + _ "games.yol.com/win88" + _ "mongo.games.com/game/common" + _ "mongo.games.com/game/robot/base" + _ "mongo.games.com/game/robot/chess" + _ "mongo.games.com/game/robot/thirteen" + _ "mongo.games.com/game/robot/tienlen" + _ "mongo.games.com/game/srvdata" +) + +func main() { + defer core.ClosePackages() + core.LoadPackages("config.json") + + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/robot/robotaccount.json b/robot/robotaccount.json new file mode 100644 index 0000000..1f6a781 --- /dev/null +++ b/robot/robotaccount.json @@ -0,0 +1 @@ +[{"Acc":"65e049882878d93184ac99d3","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d4","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d5","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d6","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d7","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d8","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99d9","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99da","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99db","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99dc","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99dd","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99de","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99df","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e0","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e1","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e2","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e3","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e4","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e5","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e6","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e7","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e8","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99e9","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ea","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99eb","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ec","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ed","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ee","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ef","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f0","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f1","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f2","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f3","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f4","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f5","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f6","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f7","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f8","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99f9","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99fa","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99fb","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99fc","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99fd","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99fe","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac99ff","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a00","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a01","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a02","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a03","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a04","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a05","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a06","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a07","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a08","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a09","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0a","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0b","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0c","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0d","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0e","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a0f","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a10","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a11","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a12","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a13","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a14","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a15","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a16","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a17","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a18","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a19","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1a","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1b","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1c","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1d","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1e","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a1f","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a20","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a21","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a22","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a23","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a24","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a25","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a26","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a27","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a28","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a29","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2a","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2b","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2c","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2d","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2e","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a2f","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a30","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a31","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a32","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a33","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a34","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a35","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a36","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a37","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a38","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a39","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3a","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3b","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3c","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3d","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3e","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a3f","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a40","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a41","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a42","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a43","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a44","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a45","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a46","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a47","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a48","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a49","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"},{"Acc":"65e049882878d93184ac9a4a","Create":1709197704454143900,"Time":"2024-02-29T17:08:24.4541439+08:00"}] \ No newline at end of file diff --git a/robot/thirteen/scthirteenwater.go b/robot/thirteen/scthirteenwater.go new file mode 100644 index 0000000..77565e2 --- /dev/null +++ b/robot/thirteen/scthirteenwater.go @@ -0,0 +1,299 @@ +package thirteen + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/robot/base" + + "mongo.games.com/game/common" + rule "mongo.games.com/game/gamerule/thirteen" + "mongo.games.com/game/protocol/thirteen" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type SCThirteenWaterRoomInfoPacketFactory struct { +} + +type SCThirteenWaterRoomInfoHandler struct { +} + +func (this *SCThirteenWaterRoomInfoPacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenRoomInfo{} + return pack +} + +func (this *SCThirteenWaterRoomInfoHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterRoomInfoHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenRoomInfo); ok { + scene, ok := base.SceneMgrSingleton.GetScene(msg.GetRoomId()).(*ThirteenWaterScene) + if scene == nil || ok == false { + scene = NewThirteenWaterScene(msg) + base.SceneMgrSingleton.AddScene(scene) + } + if scene != nil { + for _, pd := range msg.GetPlayers() { + if scene.GetPlayerBySnid(pd.GetSnId()) == nil { + p := NewThirteenWaterPlayer(pd) + if p != nil { + scene.AddPlayer(p) + } + } + } + switch int(msg.GetState()) { + case rule.ThirteenWaterSceneStateWait, rule.ThirteenWaterSceneStateStart: + } + } + + } else { + logger.Logger.Error("SCThirteenWaterRoomInfo package data error.") + } + return nil +} + +type SCThirteenWaterRoomStatePacketFactory struct { +} + +type SCThirteenWaterRoomStateHandler struct { +} + +func (this *SCThirteenWaterRoomStatePacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenRoomState{} + return pack +} + +func (this *SCThirteenWaterRoomStateHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterRoomStateHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenRoomState); ok { + if scene, ok := base.GetScene(s).(*ThirteenWaterScene); ok { + + p := scene.GetMe(s) + if p == base.NilPlayer { + return nil + } + if me, ok := p.(*ThirteenWaterPlayer); ok && me != nil { + //logger.Logger.Trace(msg) + switch int(msg.GetState()) { + case rule.ThirteenWaterSceneStateOptCard: + //十三水选牌 + player := p.(*ThirteenWaterPlayer) + if player == nil { + return nil + } + n := int32(0) + if len(player.allcardsO) != 0 { + //rands := rand.Intn(len(player.allcardsO)) + //n = player.allcardsO[rands] + player.GetMaxCardsO() + n = player.maxcardsO + } + if n == 0 { + n = -1 + } + pack := &thirteen.CSThirteenPlayerOp{ + OpCode: proto.Int32(int32(rule.ThirteenWaterPlayerOpMS)), + OpParam: []int64{int64(n), -2}, + } + proto.SetDefaults(pack) + base.DelaySendSecond(s, int(thirteen.TWMmoPacketID_PACKET_CSThirteenPlayerOp), pack, 5, 10) + } + } + } + } else { + logger.Logger.Error("SCThirteenWaterRoomState package data error.") + } + return nil +} + +type SCThirteenWaterPlayerEnterPacketFactory struct { +} + +type SCThirteenWaterPlayerEnterHandler struct { +} + +func (this *SCThirteenWaterPlayerEnterPacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenPlayerEnter{} + return pack +} + +func (this *SCThirteenWaterPlayerEnterHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterPlayerEnterHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenPlayerEnter); ok { + if scene, ok := base.GetScene(s).(base.IScene); ok && scene != nil { + //logger.Logger.Trace(msg) + player := NewThirteenWaterPlayer(msg.GetData()) + if player != nil { + scene.AddPlayer(player) + } + } + } else { + logger.Logger.Error("SCThirteenWaterPlayerEnter package data error.") + } + return nil +} + +type SCThirteenWaterPlayerLeavePacketFactory struct { +} + +type SCThirteenWaterPlayerLeaveHandler struct { +} + +func (this *SCThirteenWaterPlayerLeavePacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenPlayerLeave{} + return pack +} + +func (this *SCThirteenWaterPlayerLeaveHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterPlayerLeaveHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenPlayerLeave); ok { + if scene, ok := base.GetScene(s).(*ThirteenWaterScene); ok && scene != nil { + //logger.Logger.Trace(msg) + p := scene.GetPlayerByPos(msg.GetPos()) + if p != nil { + if player, ok := p.(*ThirteenWaterPlayer); ok && player != nil { + scene.DelPlayer(player.GetSnId()) + } + } + } + } else { + logger.Logger.Error("SCThirteenWaterPlayerLeave package data error.") + } + return nil +} + +type SCThirteenWaterBilledPacketFactory struct { +} + +type SCThirteenWaterBilledHandler struct { +} + +func (this *SCThirteenWaterBilledPacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenBilled{} + return pack +} + +func (this *SCThirteenWaterBilledHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterBilledHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenBilled); ok { + if scene, ok := base.GetScene(s).(*ThirteenWaterScene); ok && scene != nil { + for _, v := range msg.GetAllBilled() { + p := scene.GetPlayerByPos(v.GetPos()) + if player, ok := p.(*ThirteenWaterPlayer); ok && player != nil { + player.Coin = v.GetCoin() + } + } + scene.Clear() + } + } else { + logger.Logger.Error("SCThirteenWaterBilled package data error.") + } + return nil +} + +type SCThirteenWaterPlayerOpPacketFactory struct { +} + +type SCThirteenWaterPlayerOpHandler struct { +} + +func (this *SCThirteenWaterPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenPlayerOp{} + return pack +} + +func (this *SCThirteenWaterPlayerOpHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterPlayerOpHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenPlayerOp); ok { + if scene, ok := base.GetScene(s).(*ThirteenWaterScene); ok && scene != nil { + p := scene.GetMe(s) + if p == base.NilPlayer { + return nil + } + player := p.(*ThirteenWaterPlayer) + if me, ok := p.(*ThirteenWaterPlayer); ok && me != nil { + if me.Pos == msg.Pos { + if player.cardsO.PokerType == -1 { + copy(common.CopySliceIntToInt32(player.cardsO.Head[:]), msg.GetCards().GetHead()) + copy(common.CopySliceIntToInt32(player.cardsO.Mid[:]), msg.GetCards().GetMid()) + copy(common.CopySliceIntToInt32(player.cardsO.End[:]), msg.GetCards().GetEnd()) + player.cardsO.PokerType = int(msg.GetCards().GetIndexType()) + } + } + } + } + } else { + logger.Logger.Error("SCThirteenWaterPokerType package data error.") + } + return nil +} + +type SCThirteenWaterPlayerCardsPacketFactory struct { +} + +type SCThirteenWaterPlayerCardsHandler struct { +} + +func (this *SCThirteenWaterPlayerCardsPacketFactory) CreatePacket() interface{} { + pack := &thirteen.SCThirteenPlayerCards{} + return pack +} + +func (this *SCThirteenWaterPlayerCardsHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + me := base.GetUser(s) + logger.Logger.Tracef("(this *SCThirteenWaterPlayerOpHandler) Process [%v].", me.GetData().GetSnId()) + if msg, ok := pack.(*thirteen.SCThirteenPlayerCards); ok { + if scene, ok := base.GetScene(s).(*ThirteenWaterScene); ok && scene != nil { + //logger.Logger.Trace(msg) + p := scene.GetPlayerByPos(msg.GetPos()) + if p != nil { + if player, ok := p.(*ThirteenWaterPlayer); ok && player != nil { + if player.GetSnId() == me.GetData().GetSnId() { + player.Cards = msg.GetCards() + for k, v := range msg.GetAllCardsO() { + player.allcardsO[k] = v.GetIndexType() + } + } + } + } + } + } else { + logger.Logger.Error("SCThirteenWaterPlayerCards package data error.") + } + return nil +} +func init() { + //SCThirteenWaterRoomInfo + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomInfo), &SCThirteenWaterRoomInfoHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomInfo), &SCThirteenWaterRoomInfoPacketFactory{}) + + //SCThirteenWaterRoomState + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomState), &SCThirteenWaterRoomStateHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomState), &SCThirteenWaterRoomStatePacketFactory{}) + + //SCThirteenWaterPlayerEnter + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), &SCThirteenWaterPlayerEnterHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), &SCThirteenWaterPlayerEnterPacketFactory{}) + + //SCThirteenWaterPlayerLeave + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerLeave), &SCThirteenWaterPlayerLeaveHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerLeave), &SCThirteenWaterPlayerLeavePacketFactory{}) + + //SCThirteenWaterBilled + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenBilled), &SCThirteenWaterBilledHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenBilled), &SCThirteenWaterBilledPacketFactory{}) + + //SCThirteenPlayerOp + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), &SCThirteenWaterPlayerOpHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), &SCThirteenWaterPlayerOpPacketFactory{}) + + //SCThirteenWaterPlayerCards + netlib.RegisterHandler(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerCards), &SCThirteenWaterPlayerCardsHandler{}) + netlib.RegisterFactory(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerCards), &SCThirteenWaterPlayerCardsPacketFactory{}) + +} diff --git a/robot/thirteen/thirteenwaterplayer.go b/robot/thirteen/thirteenwaterplayer.go new file mode 100644 index 0000000..8ab9403 --- /dev/null +++ b/robot/thirteen/thirteenwaterplayer.go @@ -0,0 +1,119 @@ +package thirteen + +import ( + rule "mongo.games.com/game/gamerule/thirteen" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/thirteen" + "mongo.games.com/game/robot/base" +) + +type ThirteenWaterPlayer struct { + base.BasePlayer + *thirteen.ThirteenPlayerData + oldCoin int64 + OpDelayTimes int32 //本局延迟操作次数 + allcardsO map[int]int32 //玩家所有牌型 + cardsO *rule.Group //确定的牌型信息 + maxcardsO int32 //最大牌的牌型 +} + +func NewThirteenWaterPlayer(data *thirteen.ThirteenPlayerData) *ThirteenWaterPlayer { + p := &ThirteenWaterPlayer{ThirteenPlayerData: data} + p.Init() + return p +} + +func (p *ThirteenWaterPlayer) Init() { + p.oldCoin = p.GetCoin() + p.Clear() +} + +func (p *ThirteenWaterPlayer) GetMaxCardsO() { + max := -1000 + for _, v := range p.allcardsO { + sum := 0 + + if v >= 1000000 { + p.maxcardsO = v + break + } + a := v / 10000 + b := (v - a*10000) / 100 + c := v - a*10000 - b*100 + s := []int32{a, b, c} + for _, k := range s { + if k > rule.PokersTypeZero && k <= rule.PokersTypeOne { + sum += rule.PokersTypeOne - int(k) + } + } + if max <= sum { + max = sum + p.maxcardsO = v + } + } +} +func (p *ThirteenWaterPlayer) Clear() { + p.UnmarkFlag(base.PlayerState_Ready) + p.UnmarkFlag(base.PlayerState_WaitNext) + p.UnmarkFlag(base.PlayerState_GameBreak) + p.OpDelayTimes = 0 + p.allcardsO = make(map[int]int32) + p.cardsO = &rule.Group{Head: [3]int{-1, -1, -1}, Mid: [5]int{-1, -1, -1, -1, -1}, End: [5]int{-1, -1, -1, -1, -1}, PokerType: -1} + p.maxcardsO = -1 +} + +func (p *ThirteenWaterPlayer) MarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag |= flag + p.Flag = proto.Int32(myFlag) +} + +func (p *ThirteenWaterPlayer) UnmarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag &= ^flag + p.Flag = proto.Int32(myFlag) +} + +func (p *ThirteenWaterPlayer) IsMarkFlag(flag int32) bool { + if (p.GetFlag() & flag) != 0 { + return true + } + return false +} + +func (p *ThirteenWaterPlayer) IsOnLine() bool { + return p.IsMarkFlag(base.PlayerState_Online) +} + +func (p *ThirteenWaterPlayer) IsReady() bool { + return p.IsMarkFlag(base.PlayerState_Ready) +} + +func (p *ThirteenWaterPlayer) IsSceneOwner() bool { + return p.IsMarkFlag(base.PlayerState_SceneOwner) +} +func (this *ThirteenWaterPlayer) IsGameing() bool { + return !this.IsMarkFlag(base.PlayerState_WaitNext) && !this.IsMarkFlag(base.PlayerState_GameBreak) +} + +func (p *ThirteenWaterPlayer) IsRobot() bool { + player := base.PlayerMgrSingleton.GetPlayer(p.GetSnId()) + return player != nil +} + +func (p *ThirteenWaterPlayer) SetFlag(flag int32) { + p.Flag = proto.Int32(flag) +} + +func (p *ThirteenWaterPlayer) GetLastOp() int32 { + return 0 +} + +func (p *ThirteenWaterPlayer) SetLastOp(op int32) { +} + +func (p *ThirteenWaterPlayer) UpdateCards(cards []int32) { + if p != nil { + p.Cards = cards + } +} diff --git a/robot/thirteen/thirteenwaterscene.go b/robot/thirteen/thirteenwaterscene.go new file mode 100644 index 0000000..05ba78f --- /dev/null +++ b/robot/thirteen/thirteenwaterscene.go @@ -0,0 +1,89 @@ +package thirteen + +import ( + "mongo.games.com/game/common" + protoplayer "mongo.games.com/game/protocol/player" + "mongo.games.com/game/protocol/thirteen" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/netlib" +) + +type ThirteenWaterScene struct { + base.BaseScene + *thirteen.SCThirteenRoomInfo + seats [8]*ThirteenWaterPlayer + players map[int32]*ThirteenWaterPlayer +} + +func NewThirteenWaterScene(info *thirteen.SCThirteenRoomInfo) *ThirteenWaterScene { + s := &ThirteenWaterScene{ + SCThirteenRoomInfo: info, + players: make(map[int32]*ThirteenWaterPlayer), + } + s.Init() + return s +} + +func (s *ThirteenWaterScene) Init() { + for _, mpd := range s.GetPlayers() { + p := NewThirteenWaterPlayer(mpd) + if p != nil { + s.AddPlayer(p) + } + } +} +func (s *ThirteenWaterScene) Clear() { + for _, player := range s.players { + player.Clear() + } +} + +func (s *ThirteenWaterScene) AddPlayer(p base.IPlayer) { + if mp, ok := p.(*ThirteenWaterPlayer); ok { + s.players[p.GetSnId()] = mp + s.seats[p.GetPos()] = mp + } +} + +func (s *ThirteenWaterScene) DelPlayer(snid int32) { + if p, exist := s.players[snid]; exist && p != nil { + delete(s.players, snid) + s.seats[p.GetPos()] = nil + } +} + +func (s *ThirteenWaterScene) GetPlayerByPos(pos int32) base.IPlayer { + if pos >= 0 && pos < 8 { + return s.seats[pos] + } + return nil +} + +func (s *ThirteenWaterScene) GetPlayerBySnid(snid int32) base.IPlayer { + if p, exist := s.players[snid]; exist { + return p + } + return nil +} + +func (this *ThirteenWaterScene) GetMe(s *netlib.Session) base.IPlayer { + if user, ok := s.GetAttribute(base.SessionAttributeUser).(*protoplayer.SCPlayerData); ok { + player := this.GetPlayerBySnid(user.GetData().GetSnId()) + return player + } + return nil +} + +func (s *ThirteenWaterScene) IsFull() bool { + return len(s.players) >= 4 +} + +func (s *ThirteenWaterScene) IsMatchScene() bool { + return s.GetRoomId() >= common.MatchSceneStartId +} + +func (s *ThirteenWaterScene) Update(ts int64) { + +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/robot/tienlen/sctienlen.go b/robot/tienlen/sctienlen.go new file mode 100644 index 0000000..e52c3b6 --- /dev/null +++ b/robot/tienlen/sctienlen.go @@ -0,0 +1,601 @@ +package tienlen + +import ( + "encoding/json" + "fmt" + "math/rand" + tienlenApi "mongo.games.com/game/api3th/smart/tienlen" + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/proto" + proto_tienlen "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "sort" + "strconv" + "strings" +) + +type SCTienLenRoomInfoPacketFactory struct { +} + +type SCTienLenRoomInfoHandler struct { +} + +func (this *SCTienLenRoomInfoPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenRoomInfo{} + return pack +} + +func (this *SCTienLenRoomInfoHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenRoomInfoHandler) Process [%v].", s.GetSessionConfig().Id) + if msg, ok := pack.(*proto_tienlen.SCTienLenRoomInfo); ok { + scene := base.SceneMgrSingleton.GetScene(msg.GetRoomId()) + if scene == nil { + scene = NewTienLenScene(msg) + base.SceneMgrSingleton.AddScene(scene) + } + if scene != nil { + for _, pd := range msg.GetPlayers() { + if scene.GetPlayerBySnid(pd.GetSnId()) == nil { + p := NewTienLenPlayer(pd) + if p != nil { + scene.AddPlayer(p) + } + } + } + } + } else { + logger.Logger.Error("SCTienLenRoomInfo package data error.") + } + return nil +} + +type SCTienLenPlayerOpPacketFactory struct { +} + +type SCTienLenPlayerOpHandler struct { +} + +func (this *SCTienLenPlayerOpPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenPlayerOp{} + return pack +} + +func (this *SCTienLenPlayerOpHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenPlayerOpHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenOp, ok := pack.(*proto_tienlen.SCTienLenPlayerOp); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + player := scene.GetMe(s) + + if player == base.NilPlayer { + return nil + } + if me, ok2 := player.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + if me.GetSnId() == scTienLenOp.GetSnId() { + if int(scTienLenOp.GetOpRetCode()) == 0 { + switch scTienLenOp.GetOpCode() { + case tienlen.TienLenPlayerOpPlay: + delCards := scTienLenOp.GetOpParam() + for _, delcard := range delCards { + for i, card := range me.Cards { + if card != tienlen.InvalideCard && card == int32(delcard) { + me.Cards[i] = tienlen.InvalideCard + } + } + } + case tienlen.TienLenPlayerOpPass: + } + } else { //操作失败 + switch scTienLenOp.GetOpCode() { + case tienlen.TienLenPlayerOpPlay: //出牌操作失败,改为过 + if scTienLenOp.GetSnId() == me.GetSnId() { + packOp := &proto_tienlen.CSTienLenPlayerOp{ + OpCode: proto.Int32(2), + } + proto.SetDefaults(packOp) + s.Send(int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp) + } + case int32(tienlen.TienLenPlayerOpPass): //过牌操作失败,改为出 + } + } + } + } + } + } else { + logger.Logger.Error("SCTienLenPlayerOp package data error.") + } + return nil +} + +type SCTienLenRoomStatePacketFactory struct { +} + +type SCTienLenRoomStateHandler struct { +} + +func (this *SCTienLenRoomStatePacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenRoomState{} + return pack +} + +func (this *SCTienLenRoomStateHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenRoomStateHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenRoomState, ok := pack.(*proto_tienlen.SCTienLenRoomState); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + scene.State = scTienLenRoomState.State + p := scene.GetMe(s) + if me, ok2 := p.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + switch scTienLenRoomState.GetState() { + case int32(tienlen.TienLenSceneStateWaitPlayer): + scene.Clear() + case int32(tienlen.TienLenSceneStateWaitStart): //等待开始 + scene.Clear() + if me.GetSnId() == scene.GetMasterSnid() { + packOp := &proto_tienlen.CSTienLenPlayerOp{ + OpCode: proto.Int32(3), + } + proto.SetDefaults(packOp) + //if scene.GetIsAllAi() { + // base.DelayAISend(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp) + //} else { + // base.DelaySendSecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{3, 7}...) + //} + base.DelaySendSecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{3, 7}...) + } + case int32(tienlen.TienLenSceneStatePlayerOp): + case int32(tienlen.TienLenSceneStateBilled): + scene.Clear() + me.Clear() + } + } + } + } else { + logger.Logger.Error("SCTienLenRoomState package data error.") + } + return nil +} + +type SCTienLenGameBilledPacketFactory struct { +} + +type SCTienLenGameBilledHandler struct { +} + +func (this *SCTienLenGameBilledPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenGameBilled{} + return pack +} + +func (this *SCTienLenGameBilledHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenGameBilledHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenBilled, ok := pack.(*proto_tienlen.SCTienLenGameBilled); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + //logger.Logger.Trace(scTienLenBilled) + billData := scTienLenBilled.GetDatas() + for _, data := range billData { + p := scene.GetMe(s) + if p == base.NilPlayer { + continue + } + if me, ok2 := p.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + if data.GetSnId() == me.GetSnId() { //自己的数据 + me.Coin = proto.Int64(data.GetGameCoin()) + } + } + } + } + } else { + logger.Logger.Error("SCTienLenGameBilled package data error.") + } + return nil +} + +type SCTienLenCurOpPosPacketFactory struct { +} + +type SCTienLenCurOpPosHandler struct { +} + +func (this *SCTienLenCurOpPosPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenCurOpPos{} + return pack +} + +func (this *SCTienLenCurOpPosHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenCurOpPosHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenCurOpPos, ok := pack.(*proto_tienlen.SCTienLenCurOpPos); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + curPos := scTienLenCurOpPos.GetPos() + p := scene.GetMe(s) + if me, ok2 := p.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + if scene.GetState() == int32(tienlen.TienLenSceneStatePlayerOp) { + if me.GetPos() == curPos { + packOp := &proto_tienlen.CSTienLenPlayerOp{ + OpCode: proto.Int32(1), + } + cpCards := []int32{} + for _, card := range me.Cards { + if card != tienlen.InvalideCard { + cpCards = append(cpCards, card) + } + } + recmCards := []int32{} + + exDelayTs := int(scTienLenCurOpPos.GetExDelay()) * 1000 + minS := 1500 + maxS := 3000 + if scene.IsMatchScene() && scene.GetIsAllAi() { + exDelayTs = 1 + minS = 1 + maxS = 2 + } + notExDelay := true //立即出牌 + notExDelayTs := 1 + notExDelayminS := 1 + notExDelaymaxS := 2 + if len(cpCards) > 0 { + logger.Logger.Trace("tienlenApi.Config.Switch() = ", tienlenApi.Config.Switch(), "scene.GetIsAllAi() = ", scene.GetIsAllAi()) + if tienlenApi.Config.Switch() && (!scene.IsMatchScene() || (scene.IsMatchScene() && !scene.GetIsAllAi())) { //开启 不是比赛场或者是比赛场但不是纯ai + if me.data != nil && me.data.Num_cards_left_0 == 13 && + me.data.Num_cards_left_1 == 13 && + me.data.Num_cards_left_2 == 13 && + me.data.Num_cards_left_3 == 13 && + me.data.Card_play_action_seq == "" { + //根据最小牌值去推荐出牌:顺子>2连对>三张>对子>单张 + recmCards = tienlen.GetOutCards(cpCards, me.data, me.GetPos()) + if len(recmCards) == 0 { + logger.Logger.Error("RecommendCardsWithMinCard error1: ", me.GetSnId(), " recmCards:", recmCards) + } + if len(recmCards) > 0 { + for _, card := range recmCards { + packOp.OpParam = append(packOp.OpParam, int64(card)) + } + } else { + packOp.OpCode = proto.Int32(2) + //压不住的时候,90%概率1s后出牌 + if rand.Intn(100) < 90 { + if exDelayTs == 0 { + exDelayTs = 1000 + } + minS = 1 + maxS = 2 + } + } + proto.SetDefaults(packOp) + if notExDelay && len(recmCards) == len(cpCards) { //最后一手牌立即打出 + exDelayTs = notExDelayTs + minS = notExDelayminS + maxS = notExDelaymaxS + } + base.DelaySendMillisecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{exDelayTs + minS, exDelayTs + maxS}...) + } else { + var err error + var res []byte + if me == nil { + return nil + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if scene.IsTienLenYule() { + tienlenApi.Config.IPAddr = common.CustomConfig.GetString(fmt.Sprintf("%s2Api3thAddr", tienlenApi.Config.Name)) + } else { + tienlenApi.Config.IPAddr = common.CustomConfig.GetString(fmt.Sprintf("%sApi3thAddr", tienlenApi.Config.Name)) + } + res, err = tienlenApi.Config.Do(tienlenApi.Predict, me.data) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if scene.GetState() != int32(tienlen.TienLenSceneStatePlayerOp) || me.GetPos() != curPos { + tienlenApi.Config.Log().Errorf("TienLen TienLenSmart return too later state[%v-%v] pos[%v-%v] GameId[%v] RoomId[%v]", scene.GetState(), + tienlen.TienLenSceneStatePlayerOp, me.GetPos(), curPos, scene.GameId, scene.RoomId) + return + } + logger.Logger.Infof("AI返回要出的牌!!!!!!! err = %v,,,,res = %v", err, res) + if err != nil || res == nil { + tienlenApi.Config.Log().Errorf("TienLen TienLenSmart Err:%v", err) + lastDelCards := scTienLenCurOpPos.GetCards() + if len(lastDelCards) != 0 && !scTienLenCurOpPos.GetIsNew() { //根据上手牌出牌 + logger.Logger.Trace("根据上手牌出牌: ", me.GetSnId(), " lastDelCards:", lastDelCards, " IsNew:", scTienLenCurOpPos.GetIsNew()) + //压牌 + recmCards = tienlen.GetPressCards(cpCards, lastDelCards, me.data, me.GetPos()) + } else { //自由出牌 + logger.Logger.Trace("自由出牌: ", me.GetSnId(), " lastDelCards:", lastDelCards, " IsNew:", scTienLenCurOpPos.GetIsNew()) + //根据最小牌值去推荐出牌:顺子>2连对>三张>对子>单张 + //手牌 + recmCards = tienlen.GetOutCards(cpCards, me.data, me.GetPos()) + if len(recmCards) == 0 { + logger.Logger.Error("RecommendCardsWithMinCard error2: ", me.GetSnId(), " lastDelCards:", lastDelCards, " recmCards:", recmCards) + } + } + if len(recmCards) > 0 { + + for _, card := range recmCards { + packOp.OpParam = append(packOp.OpParam, int64(card)) + } + } else { + packOp.OpCode = proto.Int32(2) + //压不住的时候,90%概率1s后出牌 + if rand.Intn(100) < 90 { + if exDelayTs == 0 { + exDelayTs = 1000 + } + minS = 1 + maxS = 2 + } + } + proto.SetDefaults(packOp) + if notExDelay && len(recmCards) == len(cpCards) { //最后一手牌立即打出 + exDelayTs = notExDelayTs + minS = notExDelayminS + maxS = notExDelaymaxS + } + base.DelaySendMillisecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{exDelayTs + minS, exDelayTs + maxS}...) + return + } + PredictResponse := new(tienlenApi.PredictResponse) + if err = json.Unmarshal(res, PredictResponse); err != nil { + logger.Logger.Error("AI返回要出的牌 解析报错!!!!!!!!!!!!!!!!!!!") + tienlenApi.Config.Log().Errorf("TienLen TienLenSmart json.Unmarshal() error: %v", err) + return + } + logger.Logger.Info("-------------------AI返回要出的牌----------------", PredictResponse) + tienlenApi.Config.Log().Infof("PredictResponse:%v", *PredictResponse) + cardstr := "" + maxwinrate := 0.0 + for k, v := range PredictResponse.WinRates { + v_rate, errrate := strconv.ParseFloat(v, 32) + if errrate != nil { + tienlenApi.Config.Log().Errorf("TienLen TienLenSmart strconv.ParseFloat() error: %v", err) + continue + } + if v_rate >= maxwinrate { + maxwinrate = v_rate + cardstr = k + } + } + recmCardss := strings.Split(cardstr, ",") + for _, v := range recmCardss { + card, errcard := strconv.Atoi(v) + if errcard == nil { + tmp := tienlenApi.AiCardToCard[int32(card)] + recmCards = append(recmCards, int32(tmp)) + } + } + tienlenApi.Config.Log().Infof("--> %v Start TienLen:%+v", me.GetSnId(), cardstr) + lastDelCards := scTienLenCurOpPos.GetCards() + recmCards = scene.FilterRobotAICard(me, cpCards, lastDelCards, recmCards, scTienLenCurOpPos.GetIsNew()) + + if len(recmCards) > 0 { + for _, card := range recmCards { + packOp.OpParam = append(packOp.OpParam, int64(card)) + } + } else { + lastDelCards := scTienLenCurOpPos.GetCards() + if len(lastDelCards) != 0 && !scTienLenCurOpPos.GetIsNew() { //根据上手牌出牌 + packOp.OpCode = proto.Int32(2) + //压不住的时候,90%概率1s后出牌 + if rand.Intn(100) < 90 { + if exDelayTs == 0 { + exDelayTs = 1000 + } + minS = 1 + maxS = 2 + } + } else { //自由出牌 + logger.Logger.Trace("自由出牌: ", me.GetSnId(), " lastDelCards:", lastDelCards, " IsNew:", scTienLenCurOpPos.GetIsNew()) + //根据最小牌值去推荐出牌:顺子>2连对>三张>对子>单张 + recmCards = tienlen.GetOutCards(cpCards, me.data, me.GetPos()) + logger.Logger.Trace("根据最小牌值去推荐出牌: ", recmCards) + if len(recmCards) > 0 { + for _, card := range recmCards { + packOp.OpParam = append(packOp.OpParam, int64(card)) + } + } + } + } + tienlenApi.Config.Log().Tracef("--> %v Start TienLen:%+v", me.GetSnId(), packOp.GetOpParam()) + proto.SetDefaults(packOp) + if notExDelay && len(recmCards) == len(cpCards) { //最后一手牌立即打出 + exDelayTs = notExDelayTs + minS = notExDelayminS + maxS = notExDelaymaxS + } + logger.Logger.Info("AI通知游戏服 出牌!!!!!!!!!!!! packOp = ", packOp) + base.DelaySendMillisecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{exDelayTs + minS, exDelayTs + maxS}...) + }), "SmartStart_TienLen_action").Start() + + } + } else { + //不是用远程机器人的情况下走下面逻辑 + lastDelCards := scTienLenCurOpPos.GetCards() + if len(lastDelCards) != 0 && !scTienLenCurOpPos.GetIsNew() { //根据上手牌出牌 + logger.Logger.Trace("根据上手牌出牌: ", me.GetSnId(), " lastDelCards:", lastDelCards, " IsNew:", scTienLenCurOpPos.GetIsNew()) + //压牌 + recmCards = tienlen.GetPressCards(cpCards, lastDelCards, me.data, me.GetPos()) + } else { //出首牌 + logger.Logger.Trace("自由出牌: ", me.GetSnId(), " lastDelCards:", lastDelCards, " IsNew:", scTienLenCurOpPos.GetIsNew()) + //根据最小牌值去推荐出牌:顺子>2连对>三张>对子>单张 + recmCards = tienlen.GetOutCards(cpCards, me.data, me.GetPos()) + } + if len(recmCards) > 0 { + for _, card := range recmCards { + packOp.OpParam = append(packOp.OpParam, int64(card)) + } + } else { + packOp.OpCode = proto.Int32(2) + //压不住的时候,90%概率1s后出牌 + if rand.Intn(100) < 90 { + if exDelayTs == 0 { + exDelayTs = 1000 + } + minS = 1 + maxS = 2 + } + } + proto.SetDefaults(packOp) + if notExDelay && len(recmCards) == len(cpCards) { //最后一手牌立即打出 + exDelayTs = notExDelayTs + minS = notExDelayminS + maxS = notExDelaymaxS + } + base.DelaySendMillisecond(s, int(proto_tienlen.TienLenPacketID_PACKET_CSTienLenPlayerOp), packOp, []int{exDelayTs + minS, exDelayTs + maxS}...) + } + } + } + } + } + } + } else { + logger.Logger.Error("SCTienLenCurOpPosHandler package data error.") + } + return nil +} + +type SCTienLenCardPacketFactory struct { +} + +type SCTienLenCardHandler struct { +} + +func (this *SCTienLenCardPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenCard{} + return pack +} + +func (this *SCTienLenCardHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenCardHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenCard, ok := pack.(*proto_tienlen.SCTienLenCard); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + cards := scTienLenCard.GetCards() + p := scene.GetMe(s) + if me, ok2 := p.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + if scene.GetState() == int32(tienlen.TienLenSceneStateHandCard) { + me.Cards = []int32{} + for _, card := range cards { + me.Cards = append(me.Cards, card) + } + sort.Slice(me.Cards, func(i, j int) bool { + if me.Cards[i] > me.Cards[j] { + return false + } + return true + }) + } + } + } + } else { + logger.Logger.Error("SCTienLenCardHandler package SCTienLenCard error.") + } + return nil +} + +type SCTienLenUpdateMasterSnidPacketFactory struct { +} + +type SCTienLenUpdateMasterSnidHandler struct { +} + +func (this *SCTienLenUpdateMasterSnidPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenUpdateMasterSnid{} + return pack +} + +func (this *SCTienLenUpdateMasterSnidHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenUpdateMasterSnidHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenUpdateMasterSnid, ok := pack.(*proto_tienlen.SCTienLenUpdateMasterSnid); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + masterSnid := scTienLenUpdateMasterSnid.GetMasterSnid() + scene.MasterSnid = masterSnid //更新房主 + } + } else { + logger.Logger.Error("SCTienLenUpdateMasterSnidHandler package SCTienLenUpdateMasterSnid error.") + } + return nil +} + +type SCTienLenAIPacketFactory struct { +} + +type SCTienLenAIHandler struct { +} + +func (this *SCTienLenAIPacketFactory) CreatePacket() interface{} { + pack := &proto_tienlen.SCTienLenAIData{} + return pack +} + +func (this *SCTienLenAIHandler) Process(s *netlib.Session, packid int, pack interface{}) error { + logger.Logger.Tracef("(this *SCTienLenAIHandler) Process [%v].", s.GetSessionConfig().Id) + if scTienLenAI, ok := pack.(*proto_tienlen.SCTienLenAIData); ok { + if scene, ok := base.GetScene(s).(*TienLenScene); ok { + //logger.Logger.Tracef("(this *SCTienLenAIHandler) Process [%v] %v", scene.GetRoomId(), scTienLenAI) + p := scene.GetMe(s) + if me, ok2 := p.(*TienLenPlayer); ok2 && me != TienLenNilPlayer { + if me.GetPos() == scTienLenAI.GetPlayerPosition() { + me.data = &tienlenApi.PredictRequest{ + Bomb_num: 0, + Card_play_action_seq: scTienLenAI.GetCardPlayActionSeq(), + Last_move_0: scTienLenAI.GetLastMove_0(), + Last_move_1: scTienLenAI.GetLastMove_1(), + Last_move_2: scTienLenAI.GetLastMove_2(), + Last_move_3: scTienLenAI.GetLastMove_3(), + Num_cards_left_0: int(scTienLenAI.GetNumCardsLeft_0()), + Num_cards_left_1: int(scTienLenAI.GetNumCardsLeft_1()), + Num_cards_left_2: int(scTienLenAI.GetNumCardsLeft_2()), + Num_cards_left_3: int(scTienLenAI.GetNumCardsLeft_3()), + Other_hand_cards: scTienLenAI.GetOtherHandCards(), + Played_cards_0: scTienLenAI.GetPlayedCards_0(), + Played_cards_1: scTienLenAI.GetPlayedCards_1(), + Played_cards_2: scTienLenAI.GetPlayedCards_2(), + Played_cards_3: scTienLenAI.GetPlayedCards_3(), + Player_hand_cards: scTienLenAI.GetPlayerHandCards(), + Player_position: int(scTienLenAI.GetPlayerPosition()), + IsTienLenYule: scTienLenAI.IsTienLenYule, + IsFirstHand: scTienLenAI.IsFirstHand, + Cards_left_0: scTienLenAI.CardsLeft_0, + Cards_left_1: scTienLenAI.CardsLeft_1, + Cards_left_2: scTienLenAI.CardsLeft_2, + Cards_left_3: scTienLenAI.CardsLeft_3, + Last_pos: scTienLenAI.LastPos, + IsEnd: scTienLenAI.IsEnd, + WinSnids: scTienLenAI.WinSnids, + IsWin: scTienLenAI.IsWin, + } + } + } + } + } else { + logger.Logger.Error("SCTienLenAIHandler package SCTienLenAIData error.") + } + return nil +} + +func init() { + //SCTienLenRoomInfo + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenRoomInfo), &SCTienLenRoomInfoHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenRoomInfo), &SCTienLenRoomInfoPacketFactory{}) + //SCTienLenPlayerOp + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenPlayerOp), &SCTienLenPlayerOpHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenPlayerOp), &SCTienLenPlayerOpPacketFactory{}) + //SCTienLenRoomState + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenRoomState), &SCTienLenRoomStateHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenRoomState), &SCTienLenRoomStatePacketFactory{}) + //SCTienLenFinalBilled + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenGameBilled), &SCTienLenGameBilledHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenGameBilled), &SCTienLenGameBilledPacketFactory{}) + //SCTienLenCurOpPos + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenCurOpPos), &SCTienLenCurOpPosHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenCurOpPos), &SCTienLenCurOpPosPacketFactory{}) + //SCTienLenCard + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenCard), &SCTienLenCardHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenCard), &SCTienLenCardPacketFactory{}) + //SCTienLenUpdateMasterSnid + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenUpdateMasterSnid), &SCTienLenUpdateMasterSnidHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenUpdateMasterSnid), &SCTienLenUpdateMasterSnidPacketFactory{}) + //SCTienLenAIData + netlib.RegisterHandler(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenAI), &SCTienLenAIHandler{}) + netlib.RegisterFactory(int(proto_tienlen.TienLenPacketID_PACKET_SCTienLenAI), &SCTienLenAIPacketFactory{}) +} diff --git a/robot/tienlen/tienlenplayer.go b/robot/tienlen/tienlenplayer.go new file mode 100644 index 0000000..81045d4 --- /dev/null +++ b/robot/tienlen/tienlenplayer.go @@ -0,0 +1,84 @@ +package tienlen + +import ( + tienlenApi "mongo.games.com/game/api3th/smart/tienlen" + "mongo.games.com/game/proto" + proto_tienlen "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/game/robot/base" +) + +var TienLenNilPlayer *TienLenPlayer = nil + +type TienLenPlayer struct { + base.BasePlayer + *proto_tienlen.TienLenPlayerData + data *tienlenApi.PredictRequest +} + +func NewTienLenPlayer(data *proto_tienlen.TienLenPlayerData) *TienLenPlayer { + p := &TienLenPlayer{TienLenPlayerData: data} + p.Init() + return p +} + +func (p *TienLenPlayer) Init() { + p.Clear() +} + +func (p *TienLenPlayer) Clear() { + +} + +func (p *TienLenPlayer) MarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag |= flag + p.Flag = proto.Int32(myFlag) +} + +func (p *TienLenPlayer) UnmarkFlag(flag int32) { + myFlag := p.GetFlag() + myFlag &= ^flag + p.Flag = proto.Int32(myFlag) +} + +func (p *TienLenPlayer) IsMarkFlag(flag int32) bool { + if (p.GetFlag() & flag) != 0 { + return true + } + return false +} + +func (p *TienLenPlayer) IsOnLine() bool { + return p.IsMarkFlag(base.PlayerState_Online) +} + +func (p *TienLenPlayer) IsReady() bool { + return true +} + +func (p *TienLenPlayer) IsSceneOwner() bool { + return false +} + +func (p *TienLenPlayer) CanOp() bool { + return true +} + +func (p *TienLenPlayer) IsRobot() bool { + player := base.PlayerMgrSingleton.GetPlayer(p.GetSnId()) + return player != nil +} + +func (p *TienLenPlayer) SetFlag(flag int32) { + p.Flag = proto.Int32(flag) +} + +func (p *TienLenPlayer) GetLastOp() int32 { + return 0 +} + +func (p *TienLenPlayer) SetLastOp(op int32) { +} + +func (p *TienLenPlayer) UpdateCards(cards []int32) { +} diff --git a/robot/tienlen/tienlenscene.go b/robot/tienlen/tienlenscene.go new file mode 100644 index 0000000..201aefd --- /dev/null +++ b/robot/tienlen/tienlenscene.go @@ -0,0 +1,161 @@ +package tienlen + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/protocol/player" + proto_tienlen "mongo.games.com/game/protocol/tienlen" + "mongo.games.com/game/robot/base" + "mongo.games.com/goserver/core/netlib" + "sort" +) + +type TienLenScene struct { + base.BaseScene + *proto_tienlen.SCTienLenRoomInfo + players map[int32]*TienLenPlayer +} +type filterFunc func(*TienLenScene, *TienLenPlayer, []int32, []int32, []int32, bool) (bool, []int32) + +var FilterMgr []filterFunc + +func NewTienLenScene(info *proto_tienlen.SCTienLenRoomInfo) *TienLenScene { + s := &TienLenScene{ + SCTienLenRoomInfo: info, + players: make(map[int32]*TienLenPlayer), + } + s.Init() + return s +} + +func (s *TienLenScene) Init() { + s.players = make(map[int32]*TienLenPlayer) + for _, mpd := range s.GetPlayers() { + p := NewTienLenPlayer(mpd) + if p != nil { + s.AddPlayer(p) + } + } +} +func (s *TienLenScene) GetIsAllAi() bool { + var i int + for _, p := range s.players { + if p.IsRobot() { + i++ + } + } + return i == 4 +} +func (s *TienLenScene) Clear() { + for _, p := range s.players { + p.Clear() + } +} + +func (s *TienLenScene) AddPlayer(p base.IPlayer) { + if mp, ok := p.(*TienLenPlayer); ok { + s.players[p.GetSnId()] = mp + } +} + +func (s *TienLenScene) DelPlayer(snid int32) { + if p, exist := s.players[snid]; exist && p != nil { + delete(s.players, snid) + } +} + +func (s *TienLenScene) GetPlayerByPos(pos int32) base.IPlayer { + return nil +} + +func (s *TienLenScene) GetPlayerBySnid(snid int32) base.IPlayer { + if p, exist := s.players[snid]; exist { + return p + } + return nil +} + +func (this *TienLenScene) GetMe(s *netlib.Session) base.IPlayer { + if user, ok := s.GetAttribute(base.SessionAttributeUser).(*player.SCPlayerData); ok { + return this.GetPlayerBySnid(user.GetData().GetSnId()) + } + return nil +} + +func (s *TienLenScene) IsFull() bool { + return len(s.players) >= int(s.MaxPlayerNum) +} + +func (s *TienLenScene) IsMatchScene() bool { + return s.IsMatch == 1 || s.IsMatch == 2 //锦标赛和冠军赛 +} + +// 娱乐版 +func (this *TienLenScene) IsTienLenYule() bool { + return common.IsTienLenYuLe(int(this.GetGameId())) +} + +func (this *TienLenScene) GetMaxCardInScene(player *TienLenPlayer, cards []int32) []int32 { + if player == nil { + return nil + } + ret := make([]int32, len(cards)) + copy(ret, cards) + + sort.Slice(ret, func(i, j int) bool { + if tienlen.CmpCard(cards[i], cards[j]) { + return true + } + return false + }) + + for k, v := range this.players { + if k == player.SnId { + continue + } + cpCards := []int32{} + for _, card := range v.Cards { + if card != tienlen.InvalideCard { + cpCards = append(cpCards, card) + } + } + + for i := 0; i < len(cpCards); i++ { + for j := 0; j < len(ret); j++ { + if tienlen.Value(cpCards[i]) > tienlen.Value(ret[j]) { + ret = ret[:j] + break + } else if tienlen.Value(cpCards[i]) == tienlen.Value(ret[j]) { + if tienlen.Color(cpCards[i]) > tienlen.Color(ret[j]) { + ret = ret[:j] + break + } + } + } + } + } + return ret +} + +func (s *TienLenScene) IsCoinScene() bool { + return true +} +func (this *TienLenScene) Update(ts int64) { +} + +// 处理特殊规则情况下,需要处理的牌型调整 +func (this *TienLenScene) FilterRobotAICard(player *TienLenPlayer, cards, lastCards, aiCards []int32, isNew bool) []int32 { + + for i := 0; i < len(FilterMgr); i++ { + isOk, arr := FilterMgr[i](this, player, cards, lastCards, aiCards, isNew) + if isOk { + return arr + } + } + + return aiCards +} + +func init() { + +} diff --git a/rpc/rpclient.go b/rpc/rpclient.go new file mode 100644 index 0000000..3b810a2 --- /dev/null +++ b/rpc/rpclient.go @@ -0,0 +1,170 @@ +package rpc + +import ( + "errors" + "io" + "net/rpc" + "sync/atomic" + "time" + + "mongo.games.com/goserver/core/utils" +) + +var ( + ErrRPClientNoConn = errors.New("RPClient no connect.") + ErrRPClientShutdown = errors.New("RPClient is shutdown.") + ErrRPClientCallTimeout = errors.New("RPClient call timeout.") +) + +type RPClient struct { + client *rpc.Client + reconnInterv time.Duration + net string + addr string + connecting int32 + ready bool + shutdown bool +} + +func NewRPClient(net, addr string, d time.Duration) *RPClient { + if d < time.Second { + d = time.Second + } + + c := &RPClient{ + net: net, + addr: addr, + ready: false, + shutdown: false, + reconnInterv: d, + } + c.init() + return c +} + +func (this *RPClient) IsReady() bool { + return this.ready +} + +func (this *RPClient) IsConnecting() bool { + return atomic.LoadInt32(&this.connecting) == 1 +} + +func (this *RPClient) IsShutdown() bool { + return this.shutdown +} + +func (this *RPClient) init() { +} + +func (this *RPClient) Start() error { + if atomic.CompareAndSwapInt32(&this.connecting, 0, 1) { + this.dialRoutine() + } + return nil +} + +func (this *RPClient) Stop() error { + if !this.shutdown { + this.shutdown = true + if this.client != nil { + return this.client.Close() + } + } + return nil +} + +func (this *RPClient) Call(serviceMethod string, args interface{}, reply interface{}) error { + return this.CallWithTimeout(serviceMethod, args, reply, time.Hour*24) +} + +func (this *RPClient) CallWithTimeout(serviceMethod string, args interface{}, reply interface{}, d time.Duration) error { + if this.client == nil { + return ErrRPClientNoConn + } + + if d <= time.Second { + d = time.Second + } + + start := time.Now() + var err error + call := this.client.Go(serviceMethod, args, reply, make(chan *rpc.Call, 1)) + select { + case <-time.After(d): + return ErrRPClientCallTimeout + case call = <-call.Done: + err = call.Error + } + if err != nil && (err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF) { + var dailed chan struct{} + if atomic.CompareAndSwapInt32(&this.connecting, 0, 1) { + dailed = make(chan struct{}) + _ = this.client.Close() + go utils.CatchPanic(func() { + this.dialRoutine() + dailed <- struct{}{} + }) + } + + dd := d - time.Now().Sub(start) + if dd <= 0 { + return ErrRPClientCallTimeout + } + + select { + case <-time.After(dd): + return ErrRPClientCallTimeout + case <-dailed: + dd = d - time.Now().Sub(start) + if dd <= 0 { + return ErrRPClientCallTimeout + } + call = this.client.Go(serviceMethod, args, reply, make(chan *rpc.Call, 1)) + dd = d - time.Now().Sub(start) + if dd <= 0 { + return ErrRPClientCallTimeout + } + select { + case <-time.After(dd): + return ErrRPClientCallTimeout + case call = <-call.Done: + err = call.Error + } + return err + } + } + return err +} + +func (this *RPClient) dialRoutine() { + defer func() { + atomic.StoreInt32(&this.connecting, 0) + }() + dial := func() error { + client, err := rpc.DialHTTP(this.net, this.addr) + if err == nil { + if this.shutdown { + client.Close() + return ErrRPClientShutdown + } + this.client = client + this.ready = true + return nil + } + return err + } + + err := dial() + if err != nil { + for !this.shutdown { + select { + case <-time.After(this.reconnInterv): + err = dial() + if err == nil { + return + } + } + } + } +} diff --git a/schedulesrv/config.json b/schedulesrv/config.json new file mode 100644 index 0000000..44fc1d2 --- /dev/null +++ b/schedulesrv/config.json @@ -0,0 +1,54 @@ +{ + "netlib":{}, + "module":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "tx":{ + "TxSkeletonName":"mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "executor":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + }, + "Worker":{ + "WorkerCnt":8, + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + } + } + }, + "timer":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "core":{ + "MaxProcs":4 + }, + "cmdline":{ + "SupportCmdLine":true + }, + "signal":{ + "SupportSignal":true + }, + "common":{ + "AppId":"5c56d1644966f078bfb90c71", + "IsDevMode":true + }, + "costum":{ + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3 + }, + "webapi":{} +} \ No newline at end of file diff --git a/schedulesrv/init.go b/schedulesrv/init.go new file mode 100644 index 0000000..b256615 --- /dev/null +++ b/schedulesrv/init.go @@ -0,0 +1,53 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/schedule" + + "mongo.games.com/game/model" +) + +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + //每日凌晨4点执行清理任务 + //0 0 4 * * * + task := schedule.NewTask("定期清理cardlog", "0 0 4 * * *", func() error { + logger.Logger.Info("@@@执行定期清理任务@@@") + tNow := time.Now() + tStart := tNow.AddDate(0, -3, 0) + changeInfo, err := model.RemoveCoinLog(tStart.Unix()) + if err != nil { + logger.Logger.Warnf("定期清理coinlog失败: %v", err) + } else { + logger.Logger.Warnf("定期清理coinlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) + } + //APIlog + changeInfo, err = model.RemoveAPILog(tStart.Unix()) + if err != nil { + logger.Logger.Warnf("定期清理APIlog失败: %v", err) + } else { + logger.Logger.Warnf("定期清理APIlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) + } + return nil + }) + if task != nil { + logger.Logger.Info("@@@执行定期清理任务@@@加入调度") + schedule.AddTask(task.Taskname, task) + } + + //每日凌晨执行汇总任务 + //0 0 0 * * * + //taskDay := schedule.NewTask("定期汇总玩家金币总额", "0 0 0 * * *", func() error { + // logger.Logger.Info("@@@执行定期汇总任务@@@") + // return model.AggregatePlayerCoin() + //}) + //if taskDay != nil { + // logger.Logger.Info("@@@执行定期汇总任务@@@加入调度") + // schedule.AddTask(taskDay.Taskname, taskDay) + //} + return nil + }) +} diff --git a/schedulesrv/logger.xml b/schedulesrv/logger.xml new file mode 100644 index 0000000..f6eb37b --- /dev/null +++ b/schedulesrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schedulesrv/main.go b/schedulesrv/main.go new file mode 100644 index 0000000..42219c0 --- /dev/null +++ b/schedulesrv/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "math/rand" + "time" + + _ "games.yol.com/win88" + "mongo.games.com/goserver/core" + _ "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/schedule" +) + +func main() { + rand.Seed(time.Now().Unix()) + defer core.ClosePackages() + core.LoadPackages("config.json") + + //启动定时任务 + schedule.StartTask() + + //启动业务模块 + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/shell/build.sh b/shell/build.sh new file mode 100644 index 0000000..0671757 --- /dev/null +++ b/shell/build.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +workDir="" +cd $workDir + +# 编译 +cd ../dbproxy +go build + +cd ../mgrsrv +go build + +cd ../gatesrv +go build + +cd ../worldsrv +go build + +cd ../gamesrv +go build + +cd ../robot +go build \ No newline at end of file diff --git a/shell/build_copy.sh b/shell/build_copy.sh new file mode 100644 index 0000000..81c5870 --- /dev/null +++ b/shell/build_copy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +targetDir="/home/win88" +targetPrefix="win88" + +#cp -f ../dbproxy targetDir/dbproxy/$targetPrefix_dbproxy +#cp -f ../mgrsrv targetDir/mgrsrv/$targetPrefix_mgrsrv +#cp -f ../gatesrv targetDir/gatesrv/$targetPrefix_gatesrv +cp -f ../worldsrv targetDir/worldsrv/$targetPrefix_worldsrv +cp -f ../gamesrv targetDir/gamesrv/$targetPrefix_gamesrv +cp -f ../robot targetDir/robot/$targetPrefix_robot diff --git a/shell/close.sh b/shell/close.sh new file mode 100644 index 0000000..dff83d3 --- /dev/null +++ b/shell/close.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +workDir="/home/win88" + +pkill -9 win88_gatesrv +pkill -9 win88_mgrsrv +pkill -9 win88_robot + +pkill -2 win88_gamesrv +echo "================ gamesrv ===================" +echo "check gamesrv srv close state" +ps -ef | grep "win88" +echo "try close gamesrv..." +tail -f workDirgamesrv/all.log | awk '/shutdown/ {if(NR%10==0) {exit}; print}' +echo "Kill gamesrv" + +pkill -2 win88_worldsrv +echo "================ worldsrv ===================" +echo "check worldsrv srv close state" +ps -ef | grep "win88" +echo "try close worldsrv..." +tail -f worldsrv/all.log | awk '/shutdown/ {if(NR%20==0) {exit}; print}' +echo "Kill worldsrv" + +pkill -2 win88_dbproxy +echo "================ dbproxy ===================" +echo "check dbproxy srv close state" +ps -ef | grep "win88" +echo "try close dbproxy..." +tail -f dbproxy/all.log | awk '/shutdown/ {if(NR%1==0) {exit}; print}' +echo "Kill dbproxy" + +pkill -9 win88_gamesrv +pkill -9 win88_worldsrv +pkill -9 win88_dbproxy +echo "check srv close state" +ps -ef | grep "win88" diff --git a/shell/mongo_backup.sh b/shell/mongo_backup.sh new file mode 100644 index 0000000..fdcfed3 --- /dev/null +++ b/shell/mongo_backup.sh @@ -0,0 +1,29 @@ +#!/bin/bash +DUMP=/home/mongodb/bin/mongodump +#备份文件临时目录 +OUT_DIR=/home/mongodb/backup/tmp +#备份文件正式目录 +TAR_DIR=/home/mongodb/backup +#备份文件将以备份时间保存 +DATE=`date +%Y_%m_%d_%H_%M_%S` +#数据库操作员 +DB_USER=hjgame +#密码 +DB_PASS=hj888 +#保留最新14天的备份 +DAYS=14 +#备份文件命名格式 +TAR_BAK="mongodb_bak_$DATE.tar.gz" +#创建文件夹 +cd $OUT_DIR +#清空临时目录 +rm -rf $OUT_DIR/* +#创建本次备份的文件夹 +mkdir -p $OUT_DIR/$DATE +#执行备份命令 +$DUMP --host 192.168.1.91 --port 3017 -u $DB_USER -p $DB_PASS -o $OUT_DIR/$DATE +#将备份文件打包放入正式目录 +tar -zcvf $TAR_DIR/$TAR_BAK $OUT_DIR/$DATE +#删除14天前的旧备份 +find $TAR_DIR/ -mtime +$DAYS -delete + diff --git a/shell/mysql_backup.sh b/shell/mysql_backup.sh new file mode 100644 index 0000000..183f7c7 --- /dev/null +++ b/shell/mysql_backup.sh @@ -0,0 +1,41 @@ +#!/bin/bash +#数据库IP +dbserver='192.168.1.91' +#数据库用户名 +dbuser='root' +#数据密码 +dbpasswd='gbNiHao1.1dbmis@#' +#数据库,如有多个库用空格分开 +dbname='dbmis' +#备份时间 +backtime=`date +%Y%m%d` +#备份输出日志路径 +logpath='/data/mysqlbak/' +datapath='/data/mysqlbak/' + +echo "################## ${backtime} #############################" +echo "开始备份" +#日志记录头部 +echo "" >> ${logpath}/mysqlback.log +echo "-------------------------------------------------" >> ${logpath}/mysqlback.log +echo "备份时间为${backtime},备份数据库表 ${dbname} 开始" >> ${logpath}/mysqlback.log +#正式备份数据库 +for table in $dbname; do +source=`mysqldump -h ${dbserver} -u ${dbuser} -p${dbpasswd} ${table} > ${datapath}/${backtime}.sql` 2>> ${logpath}/mysqlback.log; +#备份成功以下操作 +if [ "$?" == 0 ];then +cd $datapath +#为节约硬盘空间,将数据库压缩 +tar zcf ${table}${backtime}.tar.gz ${backtime}.sql > /dev/null +#删除原始文件,只留压缩后文件 +rm -f ${datapath}/${backtime}.sql +#删除七天前备份,也就是只保存7天内的备份 +find $datapath -name "*.tar.gz" -type f -mtime +7 -exec rm -rf {} \; > /dev/null 2>&1 +echo "数据库表 ${dbname} 备份成功!!" >> ${logpath}/mysqlback.log +else +#备份失败则进行以下操作 +echo "数据库表 ${dbname} 备份失败!!" >> ${logpath}/mysqlback.log +fi +done +echo "完成备份" +echo "################## ${backtime} #############################" \ No newline at end of file diff --git a/shell/start.sh b/shell/start.sh new file mode 100644 index 0000000..9bf0e99 --- /dev/null +++ b/shell/start.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +workDir="/home/win88" +cd $workDir + +cd dbproxy +nohup ./win88_dbproxy >/dev/null & +cd $workDir/mgrsrv +nohup ./win88_mgrsrv >/dev/null & +cd $workDir/gatesrv +nohup ./win88_gatesrv >/dev/null & +cd $workDir/gamesrv +nohup ./win88_gamesrv >/dev/null & +cd $workDir/worldsrv +nohup ./win88_worldsrv >/dev/null & + +echo " ȴ3 AI." +sleep 3 +cd $workDir/robot +nohup ./win88_robot >/dev/null & +echo "check srv states..." +ps -ef | grep win88 diff --git a/simple_startup.bat b/simple_startup.bat new file mode 100644 index 0000000..3f1e931 --- /dev/null +++ b/simple_startup.bat @@ -0,0 +1,11 @@ +set GODEBUG=gctrace=1 +cd dbproxy +start dbproxy.exe +cd ../mgrsrv +start mgrsrv.exe +cd ../gatesrv +start gatesrv.exe +rem cd ../worldsrv +rem start worldsrv.exe +rem cd ../gamesrv +rem start gamesrv.exe \ No newline at end of file diff --git a/srvdata/client_ver.go b/srvdata/client_ver.go new file mode 100644 index 0000000..8a041db --- /dev/null +++ b/srvdata/client_ver.go @@ -0,0 +1,56 @@ +package srvdata + +import "encoding/json" + +type ClientVer struct { + MinApkVer int32 + LatestApkVer int32 + MinResVer int32 + LatestResVer int32 +} + +var ClientVersSingeton = &ClientVers{ + packVers: make(map[string]map[string]ClientVer), + gameVers: make(map[string]map[string]ClientVer), +} + +type ClientVers struct { + packVers map[string]map[string]ClientVer + gameVers map[string]map[string]ClientVer +} + +func updateClientVers() error { + packVers := make(map[string]map[string]ClientVer) + gameVers := make(map[string]map[string]ClientVer) + for _, clientVer := range PBDB_ClientVerMgr.Datas.Arr { + packVerStr := clientVer.GetPackVers() + ver := make(map[string]ClientVer) + err := json.Unmarshal([]byte(packVerStr), &ver) + if err == nil { + packVers[clientVer.GetPackageFlag()] = ver + } + gameVerStr := clientVer.GetGameVers() + ver = make(map[string]ClientVer) + err = json.Unmarshal([]byte(gameVerStr), &ver) + if err == nil { + gameVers[clientVer.GetPackageFlag()] = ver + } + } + ClientVersSingeton.packVers = packVers + ClientVersSingeton.gameVers = gameVers + return nil +} + +func GetPackVers(platformTag string) map[string]ClientVer { + if vers, exist := ClientVersSingeton.packVers[platformTag]; exist { + return vers + } + return nil +} + +func GetGameVers(platformTag string) map[string]ClientVer { + if vers, exist := ClientVersSingeton.gameVers[platformTag]; exist { + return vers + } + return nil +} diff --git a/srvdata/config.go b/srvdata/config.go new file mode 100644 index 0000000..cdb52c4 --- /dev/null +++ b/srvdata/config.go @@ -0,0 +1,236 @@ +package srvdata + +import ( + "os" + "path/filepath" + "strings" + + "github.com/howeyc/fsnotify" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" +) + +func IsLibFile(name string) bool { + strArr := strings.Split(name, "_") + if len(strArr) < 1 { + return false + } + if strArr[0] == "LB" { + return true + } else { + return false + } +} + +// DatFileModifiedCommand dat文件加载 +type DatFileModifiedCommand struct { + fileName string +} + +func (fmc *DatFileModifiedCommand) Done(o *basic.Object) error { + fn := filepath.Base(fmc.fileName) + logger.Logger.Info("modified file name ======", fn) + + // loader + loader := DataMgr.GetLoader(fn) + if loader != nil { + err := loader.Reload(fmc.fileName) + if err != nil { + logger.Logger.Warn(fn, " loader err:", err) + } + } + // loaderAfter + loaderAfter := DataMgrAfter.GetLoader(fn) + if loaderAfter != nil { + for _, v := range loaderAfter { + err := v.Reload(fmc.fileName) + if err != nil { + logger.Logger.Warn(fn, " loaderAfter err:", err) + } + } + } + + //todo 实现DataLoader + switch fn { + case "DB_ClientVer.dat": + err := updateClientVers() + if err != nil { + return err + } + case "DB_Createroom.dat": + CreateRoomMgrSington.Init() + case "DB_Game_Drop.dat": + GameDropMgrSington.Init() + case "DB_Game_Role.dat", "DB_Game_Pet.dat": + RolePetMgrSington.Init() + } + + return nil +} + +// JsonFileModifiedCommand json文件加载 +type JsonFileModifiedCommand struct { + fileName string +} + +func (gmc *JsonFileModifiedCommand) Done(o *basic.Object) error { + fn := filepath.Base(gmc.fileName) + switch fn { + case "gameparam.json": + model.InitGameParam() + case "gmac.json": + model.InitGMAC() + case "thrconfig.json": + model.InitGameConfig() + case "fishingparam.json": + model.InitFishingParam() + //case "bullfightparam.json": + // model.InitBullFightParam() + //case "winthreeparam.json": + // model.InitWinThreeParam() + //case "mahjongparam.json": + // model.InitMahJongParam() + case "normalparam.json": + model.InitNormalParam() + case "clientparam.json": + model.InitClientParam() + } + return nil +} + +var Config = Configuration{} + +type Configuration struct { + RootPath string //文件目录 + LoadLib bool //是否装载牌库文件 + watcher *fsnotify.Watcher +} + +func (this *Configuration) Name() string { + return "data" +} + +func (this *Configuration) Init() error { + workDir, workingDirError := os.Getwd() + if workingDirError != nil { + return workingDirError + } + this.RootPath = filepath.Join(workDir, this.RootPath) + var err error + this.watcher, err = fsnotify.NewWatcher() + if err != nil { + logger.Logger.Warn(" fsnotify.NewWatcher err:", err) + } + + // 监听文件变化 + // Process events + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Warn("watch data director modify goroutine err:", err) + } + }() + for { + select { + case ev, ok := <-this.watcher.Event: + if ok && ev != nil { + if ev.IsModify() || ev.IsRename() { + obj := core.CoreObject() + if filepath.Ext(ev.Name) == ".dat" { + if obj != nil { + obj.SendCommand(&DatFileModifiedCommand{fileName: ev.Name}, false) + } + logger.Logger.Info("fsnotify event:", ev) + } else if filepath.Ext(ev.Name) == ".json" { + if obj != nil { + obj.SendCommand(&JsonFileModifiedCommand{fileName: ev.Name}, false) + } + logger.Logger.Info("fsnotify event:", ev) + } + } + } else { + return + } + case err := <-this.watcher.Error: + logger.Logger.Warn("fsnotify err:", err) + } + } + logger.Logger.Warnf("watcher quit! %v", this.RootPath) + }() + + // 收集 DataLoader 处理的文件 + loaderFiles := []string{} // 文件地址,dat文件 + this.watcher.Watch(this.RootPath) + filepath.Walk(this.RootPath, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + this.watcher.Watch(path) // 监听文件夹下的文件修改 + } + name := info.Name() + if filepath.Ext(name) != ".dat" { + return nil + } + + loaderFiles = append(loaderFiles, name) + return nil + }) + + // 使用 DataLoader 加载 + for _, fileName := range loaderFiles { + if IsLibFile(fileName) && this.LoadLib == false { //是牌库文件,但是不安排装载的话,就不加载库文件了 + continue + } + + // loader + loader := DataMgr.GetLoader(fileName) + if loader != nil { + fullPath := filepath.Join(this.RootPath, fileName) + err := loader.Load(fullPath) + if err != nil { + logger.Logger.Warnf("%v loader err: %v", fileName, err) + } else { + logger.Logger.Infof("%v loader success", fileName) + } + } else { + logger.Logger.Warnf("%v no loader", fileName) + } + + // loaderAfter 依赖xlsx数据 + loaderAfter := DataMgrAfter.GetLoader(fileName) + if loaderAfter != nil { + fullPath := filepath.Join(this.RootPath, fileName) + for _, v := range loaderAfter { + err := v.Load(fullPath) + if err != nil { + logger.Logger.Warnf("%v loaderAfter err: %v", fileName, err) + } else { + logger.Logger.Infof("%v loaderAfter success", fileName) + } + } + } + } + + //todo 实现DataLoader + err = updateClientVers() + if err != nil { + return err + } + //DB_Createroom + CreateRoomMgrSington.Init() + GameDropMgrSington.Init() + //role pet + RolePetMgrSington.Init() + return nil +} + +func (this *Configuration) Close() error { + this.watcher.Close() + return nil +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/srvdata/create_room.go b/srvdata/create_room.go new file mode 100644 index 0000000..c3ff3b3 --- /dev/null +++ b/srvdata/create_room.go @@ -0,0 +1,96 @@ +package srvdata + +import ( + "mongo.games.com/game/protocol/server" +) + +var CreateRoomMgrSington = &CreateRoomMgr{ + GameIdDatas: make(map[int32][]*server.DB_Createroom), + Datas: make(map[int32][]*server.DB_Createroom), +} + +type CreateRoomMgr struct { + GameIdDatas map[int32][]*server.DB_Createroom + Datas map[int32][]*server.DB_Createroom +} + +func (this *CreateRoomMgr) Init() { + this.Datas = make(map[int32][]*server.DB_Createroom) + this.GameIdDatas = make(map[int32][]*server.DB_Createroom) + for _, v := range PBDB_CreateroomMgr.Datas.GetArr() { + gameid := v.GameId + key := gameid*10000 + v.GameSite + this.Datas[key] = append(this.Datas[key], v) + this.GameIdDatas[gameid] = append(this.GameIdDatas[gameid], v) + } +} + +func (this *CreateRoomMgr) GetGameSiteByGameId(gameid int32, takeCoin int64) int32 { + datas := this.GameIdDatas[gameid] + if datas != nil && len(datas) != 0 { + for i := len(datas) - 1; i >= 0; i-- { + goldRange := datas[i].GoldRange + if len(goldRange) != 0 && goldRange[0] != 0 { + if takeCoin >= int64(goldRange[0]) { + return datas[i].GetGameSite() + } + } + } + } + return 0 +} + +func (this *CreateRoomMgr) GetDataByGameIdWithTakeCoin(gameid int32, takeCoin int64) *server.DB_Createroom { + data := this.GameIdDatas[gameid] + if data != nil && len(data) != 0 { + for i := len(data) - 1; i >= 0; i-- { + goldRange := data[i].GoldRange + if len(goldRange) != 0 && goldRange[0] != 0 { + if takeCoin >= int64(goldRange[0]) { + return data[i] + } + } + } + } + return nil +} + +func (this *CreateRoomMgr) GetDataByGameIdWithSite(gameid, gamesite int32) []*server.DB_Createroom { + return this.Datas[gameid*10000+gamesite] +} + +func (this *CreateRoomMgr) GetLimitCoinByBaseScore(gameid, gamesite, baseScore int32) int64 { + limitCoin := int64(0) + datas := this.GetDataByGameIdWithSite(gameid, gamesite) + if datas != nil && len(datas) != 0 { + tmpIds := []int32{} + for i := 0; i < len(datas); i++ { + data := datas[i] + betRange := data.GetBetRange() + if len(betRange) == 0 { + continue + } + for j := 0; j < len(betRange); j++ { + if betRange[j] == baseScore && len(data.GetGoldRange()) > 0 && data.GetGoldRange()[0] != 0 { + tmpIds = append(tmpIds, data.GetId()) + break + } + } + } + if len(tmpIds) > 0 { + goldRange := PBDB_CreateroomMgr.GetData(tmpIds[0]).GetGoldRange() + if len(goldRange) != 0 && goldRange[0] != 0 { + limitCoin = int64(goldRange[0]) + } + if limitCoin != 0 { + for _, id := range tmpIds { + tmp := PBDB_CreateroomMgr.GetData(id).GetGoldRange() + if int64(tmp[0]) < limitCoin && tmp[0] != 0 { + limitCoin = int64(tmp[0]) + } + } + } + } + } + return limitCoin +} diff --git a/srvdata/dataloader.go b/srvdata/dataloader.go new file mode 100644 index 0000000..d7fc87b --- /dev/null +++ b/srvdata/dataloader.go @@ -0,0 +1,122 @@ +package srvdata + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "os" +) + +var fileSignMap = make(map[string]string) + +type DataLoader interface { + Load(fileFullPath string) error + Reload(fileFullPath string) error +} + +type DataHolder interface { + unmarshal([]byte) error + reunmarshal([]byte) error +} + +type JsonDataLoader struct { + dh DataHolder +} + +func (this *JsonDataLoader) Load(fileFullPath string) error { + buf, err := os.ReadFile(fileFullPath) + if err != nil { + return err + } + + h := md5.New() + _, err = h.Write(buf) + if err != nil { + return err + } + fileSign := hex.EncodeToString(h.Sum(nil)) + fileSignMap[fileFullPath] = fileSign + + if this.dh != nil { + err = this.dh.unmarshal(buf) + } + + return err +} + +func (this *JsonDataLoader) Reload(fileFullPath string) error { + buf, err := os.ReadFile(fileFullPath) + if err != nil { + return err + } + + h := md5.New() + _, err = h.Write(buf) + if err != nil { + return err + } + fileSign := hex.EncodeToString(h.Sum(nil)) + if preSign, exist := fileSignMap[fileFullPath]; exist { + if preSign == fileSign { + return nil + } + } + fileSignMap[fileFullPath] = fileSign + + if this.dh != nil { + err = this.dh.reunmarshal(buf) + } + + return err +} + +type ProtobufDataLoader struct { + dh DataHolder +} + +func (this *ProtobufDataLoader) Load(fileFullPath string) error { + buf, err := os.ReadFile(fileFullPath) + if err != nil { + return err + } + + h := md5.New() + _, err = h.Write(buf) + if err != nil { + return err + } + fileSign := hex.EncodeToString(h.Sum(nil)) + fileSignMap[fileFullPath] = fileSign + + if this.dh != nil { + err = this.dh.unmarshal(buf) + } + + return err +} + +func (this *ProtobufDataLoader) Reload(fileFullPath string) error { + buf, err := os.ReadFile(fileFullPath) + if err != nil { + return err + } + + h := md5.New() + _, err = h.Write(buf) + if err != nil { + return err + } + fileSign := hex.EncodeToString(h.Sum(nil)) + if preSign, exist := fileSignMap[fileFullPath]; exist { + if preSign == fileSign { + return errors.New("content no modify") + } + } + fileSignMap[fileFullPath] = fileSign + + if this.dh != nil { + err = this.dh.reunmarshal(buf) + } + + return err +} diff --git a/srvdata/datamgr.go b/srvdata/datamgr.go new file mode 100644 index 0000000..e856b15 --- /dev/null +++ b/srvdata/datamgr.go @@ -0,0 +1,66 @@ +package srvdata + +import ( + "strings" +) + +var DataMgr = &dataMgr{ + loaders: make(map[string]DataLoader), + cacheGameFreeId: make(map[int32]CacheGameType), +} + +type dataMgr struct { + loaders map[string]DataLoader + cacheGameFreeId map[int32]CacheGameType +} +type CacheGameType struct { + Ids []int32 + GameType int32 +} + +func (this *dataMgr) RegisteLoader(name string, loader DataLoader) { + this.loaders[strings.ToLower(name)] = loader +} + +func (this *dataMgr) GetLoader(name string) DataLoader { + if loader, exist := this.loaders[strings.ToLower(name)]; exist { + return loader + } + return nil +} + +func (this *dataMgr) GetGameFreeIds(gameId, gameMode int32) (ids []int32, gameType int32) { + key := gameId<<16 | gameMode + if data, exist := this.cacheGameFreeId[key]; exist { + return data.Ids, data.GameType + } else { + for _, dbGameFree := range PBDB_GameFreeMgr.Datas.Arr { + if dbGameFree.GetGameId() == gameId && dbGameFree.GetGameMode() == gameMode { + ids = append(ids, dbGameFree.GetId()) + gameType = dbGameFree.GetGameType() + } + } + this.cacheGameFreeId[key] = CacheGameType{Ids: ids, GameType: gameType} + } + return +} + +// DataMgrAfter 配置文件监听器,这个加载数据会在DataMgr之后 +var DataMgrAfter = &dataMgrAfter{ + loaders: make(map[string][]DataLoader), +} + +type dataMgrAfter struct { + loaders map[string][]DataLoader +} + +func (this *dataMgrAfter) RegisterLoader(name string, loader DataLoader) { + this.loaders[strings.ToLower(name)] = append(this.loaders[strings.ToLower(name)], loader) +} + +func (this *dataMgrAfter) GetLoader(name string) []DataLoader { + if loader, exist := this.loaders[strings.ToLower(name)]; exist { + return loader + } + return nil +} diff --git a/srvdata/db_activity1.go b/srvdata/db_activity1.go new file mode 100644 index 0000000..9c43dc9 --- /dev/null +++ b/srvdata/db_activity1.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Activity1Mgr = &DB_Activity1Mgr{pool: make(map[int32]*server.DB_Activity1), Datas: &server.DB_Activity1Array{}} + +type DB_Activity1Mgr struct { + Datas *server.DB_Activity1Array + pool map[int32]*server.DB_Activity1 +} + +func (this *DB_Activity1Mgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Activity1Mgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Activity1Array{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Activity1Mgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Activity1Mgr) GetData(id int32) *server.DB_Activity1 { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Activity1.dat", &ProtobufDataLoader{dh: PBDB_Activity1Mgr}) +} diff --git a/srvdata/db_actsign.go b/srvdata/db_actsign.go new file mode 100644 index 0000000..e8e060d --- /dev/null +++ b/srvdata/db_actsign.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ActSignMgr = &DB_ActSignMgr{pool: make(map[int32]*server.DB_ActSign), Datas: &server.DB_ActSignArray{}} + +type DB_ActSignMgr struct { + Datas *server.DB_ActSignArray + pool map[int32]*server.DB_ActSign +} + +func (this *DB_ActSignMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ActSignMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ActSignArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ActSignMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ActSignMgr) GetData(id int32) *server.DB_ActSign { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ActSign.dat", &ProtobufDataLoader{dh: PBDB_ActSignMgr}) +} diff --git a/srvdata/db_animalcolor.go b/srvdata/db_animalcolor.go new file mode 100644 index 0000000..7ec544e --- /dev/null +++ b/srvdata/db_animalcolor.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_AnimalColorMgr = &DB_AnimalColorMgr{pool: make(map[int32]*server.DB_AnimalColor), Datas: &server.DB_AnimalColorArray{}} + +type DB_AnimalColorMgr struct { + Datas *server.DB_AnimalColorArray + pool map[int32]*server.DB_AnimalColor +} + +func (this *DB_AnimalColorMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_AnimalColorMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_AnimalColorArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_AnimalColorMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_AnimalColorMgr) GetData(id int32) *server.DB_AnimalColor { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_AnimalColor.dat", &ProtobufDataLoader{dh: PBDB_AnimalColorMgr}) +} diff --git a/srvdata/db_artilleryrate.go b/srvdata/db_artilleryrate.go new file mode 100644 index 0000000..0bfaae0 --- /dev/null +++ b/srvdata/db_artilleryrate.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ArtilleryRateMgr = &DB_ArtilleryRateMgr{pool: make(map[int32]*server.DB_ArtilleryRate), Datas: &server.DB_ArtilleryRateArray{}} + +type DB_ArtilleryRateMgr struct { + Datas *server.DB_ArtilleryRateArray + pool map[int32]*server.DB_ArtilleryRate +} + +func (this *DB_ArtilleryRateMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ArtilleryRateMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ArtilleryRateArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ArtilleryRateMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ArtilleryRateMgr) GetData(id int32) *server.DB_ArtilleryRate { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ArtilleryRate.dat", &ProtobufDataLoader{dh: PBDB_ArtilleryRateMgr}) +} diff --git a/srvdata/db_artilleryskin.go b/srvdata/db_artilleryskin.go new file mode 100644 index 0000000..fae0d8f --- /dev/null +++ b/srvdata/db_artilleryskin.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ArtillerySkinMgr = &DB_ArtillerySkinMgr{pool: make(map[int32]*server.DB_ArtillerySkin), Datas: &server.DB_ArtillerySkinArray{}} + +type DB_ArtillerySkinMgr struct { + Datas *server.DB_ArtillerySkinArray + pool map[int32]*server.DB_ArtillerySkin +} + +func (this *DB_ArtillerySkinMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ArtillerySkinMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ArtillerySkinArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ArtillerySkinMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ArtillerySkinMgr) GetData(id int32) *server.DB_ArtillerySkin { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ArtillerySkin.dat", &ProtobufDataLoader{dh: PBDB_ArtillerySkinMgr}) +} diff --git a/srvdata/db_blackwhite.go b/srvdata/db_blackwhite.go new file mode 100644 index 0000000..f0c4dab --- /dev/null +++ b/srvdata/db_blackwhite.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_BlackWhiteMgr = &DB_BlackWhiteMgr{pool: make(map[int32]*server.DB_BlackWhite), Datas: &server.DB_BlackWhiteArray{}} + +type DB_BlackWhiteMgr struct { + Datas *server.DB_BlackWhiteArray + pool map[int32]*server.DB_BlackWhite +} + +func (this *DB_BlackWhiteMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_BlackWhiteMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_BlackWhiteArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_BlackWhiteMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_BlackWhiteMgr) GetData(id int32) *server.DB_BlackWhite { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_BlackWhite.dat", &ProtobufDataLoader{dh: PBDB_BlackWhiteMgr}) +} diff --git a/srvdata/db_cardsjd.go b/srvdata/db_cardsjd.go new file mode 100644 index 0000000..84a10f7 --- /dev/null +++ b/srvdata/db_cardsjd.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_CardsJDMgr = &DB_CardsJDMgr{pool: make(map[int32]*server.DB_CardsJD), Datas: &server.DB_CardsJDArray{}} + +type DB_CardsJDMgr struct { + Datas *server.DB_CardsJDArray + pool map[int32]*server.DB_CardsJD +} + +func (this *DB_CardsJDMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_CardsJDMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_CardsJDArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_CardsJDMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_CardsJDMgr) GetData(id int32) *server.DB_CardsJD { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_CardsJD.dat", &ProtobufDataLoader{dh: PBDB_CardsJDMgr}) +} diff --git a/srvdata/db_cardsyule.go b/srvdata/db_cardsyule.go new file mode 100644 index 0000000..25b5881 --- /dev/null +++ b/srvdata/db_cardsyule.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_CardsYuLeMgr = &DB_CardsYuLeMgr{pool: make(map[int32]*server.DB_CardsYuLe), Datas: &server.DB_CardsYuLeArray{}} + +type DB_CardsYuLeMgr struct { + Datas *server.DB_CardsYuLeArray + pool map[int32]*server.DB_CardsYuLe +} + +func (this *DB_CardsYuLeMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_CardsYuLeMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_CardsYuLeArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_CardsYuLeMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_CardsYuLeMgr) GetData(id int32) *server.DB_CardsYuLe { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_CardsYuLe.dat", &ProtobufDataLoader{dh: PBDB_CardsYuLeMgr}) +} diff --git a/srvdata/db_chessbilledrules.go b/srvdata/db_chessbilledrules.go new file mode 100644 index 0000000..cead345 --- /dev/null +++ b/srvdata/db_chessbilledrules.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ChessBilledRulesMgr = &DB_ChessBilledRulesMgr{pool: make(map[int32]*server.DB_ChessBilledRules), Datas: &server.DB_ChessBilledRulesArray{}} + +type DB_ChessBilledRulesMgr struct { + Datas *server.DB_ChessBilledRulesArray + pool map[int32]*server.DB_ChessBilledRules +} + +func (this *DB_ChessBilledRulesMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ChessBilledRulesMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ChessBilledRulesArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ChessBilledRulesMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ChessBilledRulesMgr) GetData(id int32) *server.DB_ChessBilledRules { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ChessBilledRules.dat", &ProtobufDataLoader{dh: PBDB_ChessBilledRulesMgr}) +} diff --git a/srvdata/db_chessmatchrules.go b/srvdata/db_chessmatchrules.go new file mode 100644 index 0000000..0fa4b90 --- /dev/null +++ b/srvdata/db_chessmatchrules.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ChessMatchRulesMgr = &DB_ChessMatchRulesMgr{pool: make(map[int32]*server.DB_ChessMatchRules), Datas: &server.DB_ChessMatchRulesArray{}} + +type DB_ChessMatchRulesMgr struct { + Datas *server.DB_ChessMatchRulesArray + pool map[int32]*server.DB_ChessMatchRules +} + +func (this *DB_ChessMatchRulesMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ChessMatchRulesMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ChessMatchRulesArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ChessMatchRulesMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ChessMatchRulesMgr) GetData(id int32) *server.DB_ChessMatchRules { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ChessMatchRules.dat", &ProtobufDataLoader{dh: PBDB_ChessMatchRulesMgr}) +} diff --git a/srvdata/db_chessrank.go b/srvdata/db_chessrank.go new file mode 100644 index 0000000..7d9b188 --- /dev/null +++ b/srvdata/db_chessrank.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ChessRankMgr = &DB_ChessRankMgr{pool: make(map[int32]*server.DB_ChessRank), Datas: &server.DB_ChessRankArray{}} + +type DB_ChessRankMgr struct { + Datas *server.DB_ChessRankArray + pool map[int32]*server.DB_ChessRank +} + +func (this *DB_ChessRankMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ChessRankMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ChessRankArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ChessRankMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ChessRankMgr) GetData(id int32) *server.DB_ChessRank { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ChessRank.dat", &ProtobufDataLoader{dh: PBDB_ChessRankMgr}) +} diff --git a/srvdata/db_clientver.go b/srvdata/db_clientver.go new file mode 100644 index 0000000..26532be --- /dev/null +++ b/srvdata/db_clientver.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ClientVerMgr = &DB_ClientVerMgr{pool: make(map[int32]*server.DB_ClientVer), Datas: &server.DB_ClientVerArray{}} + +type DB_ClientVerMgr struct { + Datas *server.DB_ClientVerArray + pool map[int32]*server.DB_ClientVer +} + +func (this *DB_ClientVerMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ClientVerMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ClientVerArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ClientVerMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ClientVerMgr) GetData(id int32) *server.DB_ClientVer { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ClientVer.dat", &ProtobufDataLoader{dh: PBDB_ClientVerMgr}) +} diff --git a/srvdata/db_crashsearch.go b/srvdata/db_crashsearch.go new file mode 100644 index 0000000..0969f66 --- /dev/null +++ b/srvdata/db_crashsearch.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_CrashSearchMgr = &DB_CrashSearchMgr{pool: make(map[int32]*server.DB_CrashSearch), Datas: &server.DB_CrashSearchArray{}} + +type DB_CrashSearchMgr struct { + Datas *server.DB_CrashSearchArray + pool map[int32]*server.DB_CrashSearch +} + +func (this *DB_CrashSearchMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_CrashSearchMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_CrashSearchArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_CrashSearchMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_CrashSearchMgr) GetData(id int32) *server.DB_CrashSearch { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_CrashSearch.dat", &ProtobufDataLoader{dh: PBDB_CrashSearchMgr}) +} diff --git a/srvdata/db_createroom.go b/srvdata/db_createroom.go new file mode 100644 index 0000000..a559518 --- /dev/null +++ b/srvdata/db_createroom.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_CreateroomMgr = &DB_CreateroomMgr{pool: make(map[int32]*server.DB_Createroom), Datas: &server.DB_CreateroomArray{}} + +type DB_CreateroomMgr struct { + Datas *server.DB_CreateroomArray + pool map[int32]*server.DB_Createroom +} + +func (this *DB_CreateroomMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_CreateroomMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_CreateroomArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_CreateroomMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_CreateroomMgr) GetData(id int32) *server.DB_Createroom { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Createroom.dat", &ProtobufDataLoader{dh: PBDB_CreateroomMgr}) +} diff --git a/srvdata/db_fish.go b/srvdata/db_fish.go new file mode 100644 index 0000000..1ad512f --- /dev/null +++ b/srvdata/db_fish.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FishMgr = &DB_FishMgr{pool: make(map[int32]*server.DB_Fish), Datas: &server.DB_FishArray{}} + +type DB_FishMgr struct { + Datas *server.DB_FishArray + pool map[int32]*server.DB_Fish +} + +func (this *DB_FishMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FishMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FishArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FishMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FishMgr) GetData(id int32) *server.DB_Fish { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Fish.dat", &ProtobufDataLoader{dh: PBDB_FishMgr}) +} diff --git a/srvdata/db_fishout.go b/srvdata/db_fishout.go new file mode 100644 index 0000000..cd22c6c --- /dev/null +++ b/srvdata/db_fishout.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FishOutMgr = &DB_FishOutMgr{pool: make(map[int32]*server.DB_FishOut), Datas: &server.DB_FishOutArray{}} + +type DB_FishOutMgr struct { + Datas *server.DB_FishOutArray + pool map[int32]*server.DB_FishOut +} + +func (this *DB_FishOutMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FishOutMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FishOutArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FishOutMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FishOutMgr) GetData(id int32) *server.DB_FishOut { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FishOut.dat", &ProtobufDataLoader{dh: PBDB_FishOutMgr}) +} diff --git a/srvdata/db_fishpath.go b/srvdata/db_fishpath.go new file mode 100644 index 0000000..72dfafb --- /dev/null +++ b/srvdata/db_fishpath.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FishPathMgr = &DB_FishPathMgr{pool: make(map[int32]*server.DB_FishPath), Datas: &server.DB_FishPathArray{}} + +type DB_FishPathMgr struct { + Datas *server.DB_FishPathArray + pool map[int32]*server.DB_FishPath +} + +func (this *DB_FishPathMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FishPathMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FishPathArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FishPathMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FishPathMgr) GetData(id int32) *server.DB_FishPath { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FishPath.dat", &ProtobufDataLoader{dh: PBDB_FishPathMgr}) +} diff --git a/srvdata/db_fishroom.go b/srvdata/db_fishroom.go new file mode 100644 index 0000000..2c6dcab --- /dev/null +++ b/srvdata/db_fishroom.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FishRoomMgr = &DB_FishRoomMgr{pool: make(map[int32]*server.DB_FishRoom), Datas: &server.DB_FishRoomArray{}} + +type DB_FishRoomMgr struct { + Datas *server.DB_FishRoomArray + pool map[int32]*server.DB_FishRoom +} + +func (this *DB_FishRoomMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FishRoomMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FishRoomArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FishRoomMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FishRoomMgr) GetData(id int32) *server.DB_FishRoom { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FishRoom.dat", &ProtobufDataLoader{dh: PBDB_FishRoomMgr}) +} diff --git a/srvdata/db_fishskill.go b/srvdata/db_fishskill.go new file mode 100644 index 0000000..28e4864 --- /dev/null +++ b/srvdata/db_fishskill.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FishSkillMgr = &DB_FishSkillMgr{pool: make(map[int32]*server.DB_FishSkill), Datas: &server.DB_FishSkillArray{}} + +type DB_FishSkillMgr struct { + Datas *server.DB_FishSkillArray + pool map[int32]*server.DB_FishSkill +} + +func (this *DB_FishSkillMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FishSkillMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FishSkillArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FishSkillMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FishSkillMgr) GetData(id int32) *server.DB_FishSkill { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FishSkill.dat", &ProtobufDataLoader{dh: PBDB_FishSkillMgr}) +} diff --git a/srvdata/db_fortunegod_odds.go b/srvdata/db_fortunegod_odds.go new file mode 100644 index 0000000..6cd3a49 --- /dev/null +++ b/srvdata/db_fortunegod_odds.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FortuneGod_OddsMgr = &DB_FortuneGod_OddsMgr{pool: make(map[int32]*server.DB_FortuneGod_Odds), Datas: &server.DB_FortuneGod_OddsArray{}} + +type DB_FortuneGod_OddsMgr struct { + Datas *server.DB_FortuneGod_OddsArray + pool map[int32]*server.DB_FortuneGod_Odds +} + +func (this *DB_FortuneGod_OddsMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FortuneGod_OddsMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FortuneGod_OddsArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FortuneGod_OddsMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FortuneGod_OddsMgr) GetData(id int32) *server.DB_FortuneGod_Odds { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FortuneGod_Odds.dat", &ProtobufDataLoader{dh: PBDB_FortuneGod_OddsMgr}) +} diff --git a/srvdata/db_fortunegod_turnrate.go b/srvdata/db_fortunegod_turnrate.go new file mode 100644 index 0000000..90ede8c --- /dev/null +++ b/srvdata/db_fortunegod_turnrate.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FortuneGod_TurnRateMgr = &DB_FortuneGod_TurnRateMgr{pool: make(map[int32]*server.DB_FortuneGod_TurnRate), Datas: &server.DB_FortuneGod_TurnRateArray{}} + +type DB_FortuneGod_TurnRateMgr struct { + Datas *server.DB_FortuneGod_TurnRateArray + pool map[int32]*server.DB_FortuneGod_TurnRate +} + +func (this *DB_FortuneGod_TurnRateMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FortuneGod_TurnRateMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FortuneGod_TurnRateArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FortuneGod_TurnRateMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FortuneGod_TurnRateMgr) GetData(id int32) *server.DB_FortuneGod_TurnRate { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FortuneGod_TurnRate.dat", &ProtobufDataLoader{dh: PBDB_FortuneGod_TurnRateMgr}) +} diff --git a/srvdata/db_fortunegod_weight.go b/srvdata/db_fortunegod_weight.go new file mode 100644 index 0000000..24598dc --- /dev/null +++ b/srvdata/db_fortunegod_weight.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FortuneGod_WeightMgr = &DB_FortuneGod_WeightMgr{pool: make(map[int32]*server.DB_FortuneGod_Weight), Datas: &server.DB_FortuneGod_WeightArray{}} + +type DB_FortuneGod_WeightMgr struct { + Datas *server.DB_FortuneGod_WeightArray + pool map[int32]*server.DB_FortuneGod_Weight +} + +func (this *DB_FortuneGod_WeightMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FortuneGod_WeightMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FortuneGod_WeightArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FortuneGod_WeightMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FortuneGod_WeightMgr) GetData(id int32) *server.DB_FortuneGod_Weight { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FortuneGod_Weight.dat", &ProtobufDataLoader{dh: PBDB_FortuneGod_WeightMgr}) +} diff --git a/srvdata/db_fortunegod_weightcondition.go b/srvdata/db_fortunegod_weightcondition.go new file mode 100644 index 0000000..81b6710 --- /dev/null +++ b/srvdata/db_fortunegod_weightcondition.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_FortuneGod_WeightConditionMgr = &DB_FortuneGod_WeightConditionMgr{pool: make(map[int32]*server.DB_FortuneGod_WeightCondition), Datas: &server.DB_FortuneGod_WeightConditionArray{}} + +type DB_FortuneGod_WeightConditionMgr struct { + Datas *server.DB_FortuneGod_WeightConditionArray + pool map[int32]*server.DB_FortuneGod_WeightCondition +} + +func (this *DB_FortuneGod_WeightConditionMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_FortuneGod_WeightConditionMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_FortuneGod_WeightConditionArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_FortuneGod_WeightConditionMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_FortuneGod_WeightConditionMgr) GetData(id int32) *server.DB_FortuneGod_WeightCondition { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_FortuneGod_WeightCondition.dat", &ProtobufDataLoader{dh: PBDB_FortuneGod_WeightConditionMgr}) +} diff --git a/srvdata/db_game_drop.go b/srvdata/db_game_drop.go new file mode 100644 index 0000000..c4be220 --- /dev/null +++ b/srvdata/db_game_drop.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Game_DropMgr = &DB_Game_DropMgr{pool: make(map[int32]*server.DB_Game_Drop), Datas: &server.DB_Game_DropArray{}} + +type DB_Game_DropMgr struct { + Datas *server.DB_Game_DropArray + pool map[int32]*server.DB_Game_Drop +} + +func (this *DB_Game_DropMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Game_DropMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Game_DropArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Game_DropMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Game_DropMgr) GetData(id int32) *server.DB_Game_Drop { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Game_Drop.dat", &ProtobufDataLoader{dh: PBDB_Game_DropMgr}) +} diff --git a/srvdata/db_game_introduction.go b/srvdata/db_game_introduction.go new file mode 100644 index 0000000..0168ad2 --- /dev/null +++ b/srvdata/db_game_introduction.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Game_IntroductionMgr = &DB_Game_IntroductionMgr{pool: make(map[int32]*server.DB_Game_Introduction), Datas: &server.DB_Game_IntroductionArray{}} + +type DB_Game_IntroductionMgr struct { + Datas *server.DB_Game_IntroductionArray + pool map[int32]*server.DB_Game_Introduction +} + +func (this *DB_Game_IntroductionMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Game_IntroductionMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Game_IntroductionArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Game_IntroductionMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Game_IntroductionMgr) GetData(id int32) *server.DB_Game_Introduction { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Game_Introduction.dat", &ProtobufDataLoader{dh: PBDB_Game_IntroductionMgr}) +} diff --git a/srvdata/db_game_pet.go b/srvdata/db_game_pet.go new file mode 100644 index 0000000..5774e40 --- /dev/null +++ b/srvdata/db_game_pet.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Game_PetMgr = &DB_Game_PetMgr{pool: make(map[int32]*server.DB_Game_Pet), Datas: &server.DB_Game_PetArray{}} + +type DB_Game_PetMgr struct { + Datas *server.DB_Game_PetArray + pool map[int32]*server.DB_Game_Pet +} + +func (this *DB_Game_PetMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Game_PetMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Game_PetArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Game_PetMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Game_PetMgr) GetData(id int32) *server.DB_Game_Pet { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Game_Pet.dat", &ProtobufDataLoader{dh: PBDB_Game_PetMgr}) +} diff --git a/srvdata/db_game_role.go b/srvdata/db_game_role.go new file mode 100644 index 0000000..ebeb004 --- /dev/null +++ b/srvdata/db_game_role.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Game_RoleMgr = &DB_Game_RoleMgr{pool: make(map[int32]*server.DB_Game_Role), Datas: &server.DB_Game_RoleArray{}} + +type DB_Game_RoleMgr struct { + Datas *server.DB_Game_RoleArray + pool map[int32]*server.DB_Game_Role +} + +func (this *DB_Game_RoleMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Game_RoleMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Game_RoleArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Game_RoleMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Game_RoleMgr) GetData(id int32) *server.DB_Game_Role { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Game_Role.dat", &ProtobufDataLoader{dh: PBDB_Game_RoleMgr}) +} diff --git a/srvdata/db_gamecoinpool.go b/srvdata/db_gamecoinpool.go new file mode 100644 index 0000000..23e25f8 --- /dev/null +++ b/srvdata/db_gamecoinpool.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameCoinPoolMgr = &DB_GameCoinPoolMgr{pool: make(map[int32]*server.DB_GameCoinPool), Datas: &server.DB_GameCoinPoolArray{}} + +type DB_GameCoinPoolMgr struct { + Datas *server.DB_GameCoinPoolArray + pool map[int32]*server.DB_GameCoinPool +} + +func (this *DB_GameCoinPoolMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameCoinPoolMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameCoinPoolArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameCoinPoolMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameCoinPoolMgr) GetData(id int32) *server.DB_GameCoinPool { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameCoinPool.dat", &ProtobufDataLoader{dh: PBDB_GameCoinPoolMgr}) +} diff --git a/srvdata/db_gamefree.go b/srvdata/db_gamefree.go new file mode 100644 index 0000000..c32f2b6 --- /dev/null +++ b/srvdata/db_gamefree.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameFreeMgr = &DB_GameFreeMgr{pool: make(map[int32]*server.DB_GameFree), Datas: &server.DB_GameFreeArray{}} + +type DB_GameFreeMgr struct { + Datas *server.DB_GameFreeArray + pool map[int32]*server.DB_GameFree +} + +func (this *DB_GameFreeMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameFreeMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameFreeArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameFreeMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameFreeMgr) GetData(id int32) *server.DB_GameFree { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameFree.dat", &ProtobufDataLoader{dh: PBDB_GameFreeMgr}) +} diff --git a/srvdata/db_gameitem.go b/srvdata/db_gameitem.go new file mode 100644 index 0000000..02243df --- /dev/null +++ b/srvdata/db_gameitem.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameItemMgr = &DB_GameItemMgr{pool: make(map[int32]*server.DB_GameItem), Datas: &server.DB_GameItemArray{}} + +type DB_GameItemMgr struct { + Datas *server.DB_GameItemArray + pool map[int32]*server.DB_GameItem +} + +func (this *DB_GameItemMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameItemMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameItemArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameItemMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameItemMgr) GetData(id int32) *server.DB_GameItem { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameItem.dat", &ProtobufDataLoader{dh: PBDB_GameItemMgr}) +} diff --git a/srvdata/db_gamematchlevel.go b/srvdata/db_gamematchlevel.go new file mode 100644 index 0000000..e7afd09 --- /dev/null +++ b/srvdata/db_gamematchlevel.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameMatchLevelMgr = &DB_GameMatchLevelMgr{pool: make(map[int32]*server.DB_GameMatchLevel), Datas: &server.DB_GameMatchLevelArray{}} + +type DB_GameMatchLevelMgr struct { + Datas *server.DB_GameMatchLevelArray + pool map[int32]*server.DB_GameMatchLevel +} + +func (this *DB_GameMatchLevelMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameMatchLevelMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameMatchLevelArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameMatchLevelMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameMatchLevelMgr) GetData(id int32) *server.DB_GameMatchLevel { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameMatchLevel.dat", &ProtobufDataLoader{dh: PBDB_GameMatchLevelMgr}) +} diff --git a/srvdata/db_gamerule.go b/srvdata/db_gamerule.go new file mode 100644 index 0000000..335b72b --- /dev/null +++ b/srvdata/db_gamerule.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameRuleMgr = &DB_GameRuleMgr{pool: make(map[int32]*server.DB_GameRule), Datas: &server.DB_GameRuleArray{}} + +type DB_GameRuleMgr struct { + Datas *server.DB_GameRuleArray + pool map[int32]*server.DB_GameRule +} + +func (this *DB_GameRuleMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameRuleMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameRuleArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameRuleMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameRuleMgr) GetData(id int32) *server.DB_GameRule { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameRule.dat", &ProtobufDataLoader{dh: PBDB_GameRuleMgr}) +} diff --git a/srvdata/db_gamesubsidy.go b/srvdata/db_gamesubsidy.go new file mode 100644 index 0000000..06718c2 --- /dev/null +++ b/srvdata/db_gamesubsidy.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GameSubsidyMgr = &DB_GameSubsidyMgr{pool: make(map[int32]*server.DB_GameSubsidy), Datas: &server.DB_GameSubsidyArray{}} + +type DB_GameSubsidyMgr struct { + Datas *server.DB_GameSubsidyArray + pool map[int32]*server.DB_GameSubsidy +} + +func (this *DB_GameSubsidyMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GameSubsidyMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GameSubsidyArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GameSubsidyMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GameSubsidyMgr) GetData(id int32) *server.DB_GameSubsidy { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GameSubsidy.dat", &ProtobufDataLoader{dh: PBDB_GameSubsidyMgr}) +} diff --git a/srvdata/db_gammatchlv.go b/srvdata/db_gammatchlv.go new file mode 100644 index 0000000..c92049d --- /dev/null +++ b/srvdata/db_gammatchlv.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GamMatchLVMgr = &DB_GamMatchLVMgr{pool: make(map[int32]*server.DB_GamMatchLV), Datas: &server.DB_GamMatchLVArray{}} + +type DB_GamMatchLVMgr struct { + Datas *server.DB_GamMatchLVArray + pool map[int32]*server.DB_GamMatchLV +} + +func (this *DB_GamMatchLVMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GamMatchLVMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GamMatchLVArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GamMatchLVMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GamMatchLVMgr) GetData(id int32) *server.DB_GamMatchLV { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GamMatchLV.dat", &ProtobufDataLoader{dh: PBDB_GamMatchLVMgr}) +} diff --git a/srvdata/db_giftbox.go b/srvdata/db_giftbox.go new file mode 100644 index 0000000..5dbca79 --- /dev/null +++ b/srvdata/db_giftbox.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_GiftBoxMgr = &DB_GiftBoxMgr{pool: make(map[int32]*server.DB_GiftBox), Datas: &server.DB_GiftBoxArray{}} + +type DB_GiftBoxMgr struct { + Datas *server.DB_GiftBoxArray + pool map[int32]*server.DB_GiftBox +} + +func (this *DB_GiftBoxMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_GiftBoxMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_GiftBoxArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_GiftBoxMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_GiftBoxMgr) GetData(id int32) *server.DB_GiftBox { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_GiftBox.dat", &ProtobufDataLoader{dh: PBDB_GiftBoxMgr}) +} diff --git a/srvdata/db_iceageelementrate.go b/srvdata/db_iceageelementrate.go new file mode 100644 index 0000000..500718f --- /dev/null +++ b/srvdata/db_iceageelementrate.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_IceAgeElementRateMgr = &DB_IceAgeElementRateMgr{pool: make(map[int32]*server.DB_IceAgeElementRate), Datas: &server.DB_IceAgeElementRateArray{}} + +type DB_IceAgeElementRateMgr struct { + Datas *server.DB_IceAgeElementRateArray + pool map[int32]*server.DB_IceAgeElementRate +} + +func (this *DB_IceAgeElementRateMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_IceAgeElementRateMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_IceAgeElementRateArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_IceAgeElementRateMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_IceAgeElementRateMgr) GetData(id int32) *server.DB_IceAgeElementRate { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_IceAgeElementRate.dat", &ProtobufDataLoader{dh: PBDB_IceAgeElementRateMgr}) +} diff --git a/srvdata/db_legend_odds.go b/srvdata/db_legend_odds.go new file mode 100644 index 0000000..1ac0dcb --- /dev/null +++ b/srvdata/db_legend_odds.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Legend_OddsMgr = &DB_Legend_OddsMgr{pool: make(map[int32]*server.DB_Legend_Odds), Datas: &server.DB_Legend_OddsArray{}} + +type DB_Legend_OddsMgr struct { + Datas *server.DB_Legend_OddsArray + pool map[int32]*server.DB_Legend_Odds +} + +func (this *DB_Legend_OddsMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Legend_OddsMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Legend_OddsArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Legend_OddsMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Legend_OddsMgr) GetData(id int32) *server.DB_Legend_Odds { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Legend_Odds.dat", &ProtobufDataLoader{dh: PBDB_Legend_OddsMgr}) +} diff --git a/srvdata/db_legend_turnrate.go b/srvdata/db_legend_turnrate.go new file mode 100644 index 0000000..12c4939 --- /dev/null +++ b/srvdata/db_legend_turnrate.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Legend_TurnRateMgr = &DB_Legend_TurnRateMgr{pool: make(map[int32]*server.DB_Legend_TurnRate), Datas: &server.DB_Legend_TurnRateArray{}} + +type DB_Legend_TurnRateMgr struct { + Datas *server.DB_Legend_TurnRateArray + pool map[int32]*server.DB_Legend_TurnRate +} + +func (this *DB_Legend_TurnRateMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Legend_TurnRateMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Legend_TurnRateArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Legend_TurnRateMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Legend_TurnRateMgr) GetData(id int32) *server.DB_Legend_TurnRate { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Legend_TurnRate.dat", &ProtobufDataLoader{dh: PBDB_Legend_TurnRateMgr}) +} diff --git a/srvdata/db_legend_weight.go b/srvdata/db_legend_weight.go new file mode 100644 index 0000000..7a645ca --- /dev/null +++ b/srvdata/db_legend_weight.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Legend_WeightMgr = &DB_Legend_WeightMgr{pool: make(map[int32]*server.DB_Legend_Weight), Datas: &server.DB_Legend_WeightArray{}} + +type DB_Legend_WeightMgr struct { + Datas *server.DB_Legend_WeightArray + pool map[int32]*server.DB_Legend_Weight +} + +func (this *DB_Legend_WeightMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Legend_WeightMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Legend_WeightArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Legend_WeightMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Legend_WeightMgr) GetData(id int32) *server.DB_Legend_Weight { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Legend_Weight.dat", &ProtobufDataLoader{dh: PBDB_Legend_WeightMgr}) +} diff --git a/srvdata/db_legend_weightcondition.go b/srvdata/db_legend_weightcondition.go new file mode 100644 index 0000000..0ee01d9 --- /dev/null +++ b/srvdata/db_legend_weightcondition.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Legend_WeightConditionMgr = &DB_Legend_WeightConditionMgr{pool: make(map[int32]*server.DB_Legend_WeightCondition), Datas: &server.DB_Legend_WeightConditionArray{}} + +type DB_Legend_WeightConditionMgr struct { + Datas *server.DB_Legend_WeightConditionArray + pool map[int32]*server.DB_Legend_WeightCondition +} + +func (this *DB_Legend_WeightConditionMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Legend_WeightConditionMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Legend_WeightConditionArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Legend_WeightConditionMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Legend_WeightConditionMgr) GetData(id int32) *server.DB_Legend_WeightCondition { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Legend_WeightCondition.dat", &ProtobufDataLoader{dh: PBDB_Legend_WeightConditionMgr}) +} diff --git a/srvdata/db_matchrank.go b/srvdata/db_matchrank.go new file mode 100644 index 0000000..08d98d3 --- /dev/null +++ b/srvdata/db_matchrank.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_MatchRankMgr = &DB_MatchRankMgr{pool: make(map[int32]*server.DB_MatchRank), Datas: &server.DB_MatchRankArray{}} + +type DB_MatchRankMgr struct { + Datas *server.DB_MatchRankArray + pool map[int32]*server.DB_MatchRank +} + +func (this *DB_MatchRankMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_MatchRankMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_MatchRankArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_MatchRankMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_MatchRankMgr) GetData(id int32) *server.DB_MatchRank { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_MatchRank.dat", &ProtobufDataLoader{dh: PBDB_MatchRankMgr}) +} diff --git a/srvdata/db_name.go b/srvdata/db_name.go new file mode 100644 index 0000000..d97ed6e --- /dev/null +++ b/srvdata/db_name.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_NameMgr = &DB_NameMgr{pool: make(map[int32]*server.DB_Name), Datas: &server.DB_NameArray{}} + +type DB_NameMgr struct { + Datas *server.DB_NameArray + pool map[int32]*server.DB_Name +} + +func (this *DB_NameMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_NameMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_NameArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_NameMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_NameMgr) GetData(id int32) *server.DB_Name { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Name.dat", &ProtobufDataLoader{dh: PBDB_NameMgr}) +} diff --git a/srvdata/db_nameboy.go b/srvdata/db_nameboy.go new file mode 100644 index 0000000..3e54bee --- /dev/null +++ b/srvdata/db_nameboy.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_NameBoyMgr = &DB_NameBoyMgr{pool: make(map[int32]*server.DB_NameBoy), Datas: &server.DB_NameBoyArray{}} + +type DB_NameBoyMgr struct { + Datas *server.DB_NameBoyArray + pool map[int32]*server.DB_NameBoy +} + +func (this *DB_NameBoyMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_NameBoyMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_NameBoyArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_NameBoyMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_NameBoyMgr) GetData(id int32) *server.DB_NameBoy { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_NameBoy.dat", &ProtobufDataLoader{dh: PBDB_NameBoyMgr}) +} diff --git a/srvdata/db_namegirl.go b/srvdata/db_namegirl.go new file mode 100644 index 0000000..4810d6d --- /dev/null +++ b/srvdata/db_namegirl.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_NameGirlMgr = &DB_NameGirlMgr{pool: make(map[int32]*server.DB_NameGirl), Datas: &server.DB_NameGirlArray{}} + +type DB_NameGirlMgr struct { + Datas *server.DB_NameGirlArray + pool map[int32]*server.DB_NameGirl +} + +func (this *DB_NameGirlMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_NameGirlMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_NameGirlArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_NameGirlMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_NameGirlMgr) GetData(id int32) *server.DB_NameGirl { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_NameGirl.dat", &ProtobufDataLoader{dh: PBDB_NameGirlMgr}) +} diff --git a/srvdata/db_newplayer.go b/srvdata/db_newplayer.go new file mode 100644 index 0000000..2947998 --- /dev/null +++ b/srvdata/db_newplayer.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_NewPlayerMgr = &DB_NewPlayerMgr{pool: make(map[int32]*server.DB_NewPlayer), Datas: &server.DB_NewPlayerArray{}} + +type DB_NewPlayerMgr struct { + Datas *server.DB_NewPlayerArray + pool map[int32]*server.DB_NewPlayer +} + +func (this *DB_NewPlayerMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_NewPlayerMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_NewPlayerArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_NewPlayerMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_NewPlayerMgr) GetData(id int32) *server.DB_NewPlayer { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_NewPlayer.dat", &ProtobufDataLoader{dh: PBDB_NewPlayerMgr}) +} diff --git a/srvdata/db_phonelottery.go b/srvdata/db_phonelottery.go new file mode 100644 index 0000000..f01bf1d --- /dev/null +++ b/srvdata/db_phonelottery.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_PhoneLotteryMgr = &DB_PhoneLotteryMgr{pool: make(map[int32]*server.DB_PhoneLottery), Datas: &server.DB_PhoneLotteryArray{}} + +type DB_PhoneLotteryMgr struct { + Datas *server.DB_PhoneLotteryArray + pool map[int32]*server.DB_PhoneLottery +} + +func (this *DB_PhoneLotteryMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_PhoneLotteryMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_PhoneLotteryArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_PhoneLotteryMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_PhoneLotteryMgr) GetData(id int32) *server.DB_PhoneLottery { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_PhoneLottery.dat", &ProtobufDataLoader{dh: PBDB_PhoneLotteryMgr}) +} diff --git a/srvdata/db_playerexp.go b/srvdata/db_playerexp.go new file mode 100644 index 0000000..6b24880 --- /dev/null +++ b/srvdata/db_playerexp.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_PlayerExpMgr = &DB_PlayerExpMgr{pool: make(map[int32]*server.DB_PlayerExp), Datas: &server.DB_PlayerExpArray{}} + +type DB_PlayerExpMgr struct { + Datas *server.DB_PlayerExpArray + pool map[int32]*server.DB_PlayerExp +} + +func (this *DB_PlayerExpMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_PlayerExpMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_PlayerExpArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_PlayerExpMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_PlayerExpMgr) GetData(id int32) *server.DB_PlayerExp { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_PlayerExp.dat", &ProtobufDataLoader{dh: PBDB_PlayerExpMgr}) +} diff --git a/srvdata/db_playerinfo.go b/srvdata/db_playerinfo.go new file mode 100644 index 0000000..8f6a953 --- /dev/null +++ b/srvdata/db_playerinfo.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_PlayerInfoMgr = &DB_PlayerInfoMgr{pool: make(map[int32]*server.DB_PlayerInfo), Datas: &server.DB_PlayerInfoArray{}} + +type DB_PlayerInfoMgr struct { + Datas *server.DB_PlayerInfoArray + pool map[int32]*server.DB_PlayerInfo +} + +func (this *DB_PlayerInfoMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_PlayerInfoMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_PlayerInfoArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_PlayerInfoMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_PlayerInfoMgr) GetData(id int32) *server.DB_PlayerInfo { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_PlayerInfo.dat", &ProtobufDataLoader{dh: PBDB_PlayerInfoMgr}) +} diff --git a/srvdata/db_playertype.go b/srvdata/db_playertype.go new file mode 100644 index 0000000..2ccdd96 --- /dev/null +++ b/srvdata/db_playertype.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_PlayerTypeMgr = &DB_PlayerTypeMgr{pool: make(map[int32]*server.DB_PlayerType), Datas: &server.DB_PlayerTypeArray{}} + +type DB_PlayerTypeMgr struct { + Datas *server.DB_PlayerTypeArray + pool map[int32]*server.DB_PlayerType +} + +func (this *DB_PlayerTypeMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_PlayerTypeMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_PlayerTypeArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_PlayerTypeMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_PlayerTypeMgr) GetData(id int32) *server.DB_PlayerType { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_PlayerType.dat", &ProtobufDataLoader{dh: PBDB_PlayerTypeMgr}) +} diff --git a/srvdata/db_potodd.go b/srvdata/db_potodd.go new file mode 100644 index 0000000..c37d83b --- /dev/null +++ b/srvdata/db_potodd.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_PotOddMgr = &DB_PotOddMgr{pool: make(map[int32]*server.DB_PotOdd), Datas: &server.DB_PotOddArray{}} + +type DB_PotOddMgr struct { + Datas *server.DB_PotOddArray + pool map[int32]*server.DB_PotOdd +} + +func (this *DB_PotOddMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_PotOddMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_PotOddArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_PotOddMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_PotOddMgr) GetData(id int32) *server.DB_PotOdd { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_PotOdd.dat", &ProtobufDataLoader{dh: PBDB_PotOddMgr}) +} diff --git a/srvdata/db_rankcycle.go b/srvdata/db_rankcycle.go new file mode 100644 index 0000000..854360c --- /dev/null +++ b/srvdata/db_rankcycle.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_RankCycleMgr = &DB_RankCycleMgr{pool: make(map[int32]*server.DB_RankCycle), Datas: &server.DB_RankCycleArray{}} + +type DB_RankCycleMgr struct { + Datas *server.DB_RankCycleArray + pool map[int32]*server.DB_RankCycle +} + +func (this *DB_RankCycleMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_RankCycleMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_RankCycleArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_RankCycleMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_RankCycleMgr) GetData(id int32) *server.DB_RankCycle { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_RankCycle.dat", &ProtobufDataLoader{dh: PBDB_RankCycleMgr}) +} diff --git a/srvdata/db_ranklevel.go b/srvdata/db_ranklevel.go new file mode 100644 index 0000000..53cf6f7 --- /dev/null +++ b/srvdata/db_ranklevel.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_RankLevelMgr = &DB_RankLevelMgr{pool: make(map[int32]*server.DB_RankLevel), Datas: &server.DB_RankLevelArray{}} + +type DB_RankLevelMgr struct { + Datas *server.DB_RankLevelArray + pool map[int32]*server.DB_RankLevel +} + +func (this *DB_RankLevelMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_RankLevelMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_RankLevelArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_RankLevelMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_RankLevelMgr) GetData(id int32) *server.DB_RankLevel { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_RankLevel.dat", &ProtobufDataLoader{dh: PBDB_RankLevelMgr}) +} diff --git a/srvdata/db_rankreward.go b/srvdata/db_rankreward.go new file mode 100644 index 0000000..5dcaa90 --- /dev/null +++ b/srvdata/db_rankreward.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_RankRewardMgr = &DB_RankRewardMgr{pool: make(map[int32]*server.DB_RankReward), Datas: &server.DB_RankRewardArray{}} + +type DB_RankRewardMgr struct { + Datas *server.DB_RankRewardArray + pool map[int32]*server.DB_RankReward +} + +func (this *DB_RankRewardMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_RankRewardMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_RankRewardArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_RankRewardMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_RankRewardMgr) GetData(id int32) *server.DB_RankReward { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_RankReward.dat", &ProtobufDataLoader{dh: PBDB_RankRewardMgr}) +} diff --git a/srvdata/db_sensitive_words.go b/srvdata/db_sensitive_words.go new file mode 100644 index 0000000..5e981f2 --- /dev/null +++ b/srvdata/db_sensitive_words.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_Sensitive_WordsMgr = &DB_Sensitive_WordsMgr{pool: make(map[int32]*server.DB_Sensitive_Words), Datas: &server.DB_Sensitive_WordsArray{}} + +type DB_Sensitive_WordsMgr struct { + Datas *server.DB_Sensitive_WordsArray + pool map[int32]*server.DB_Sensitive_Words +} + +func (this *DB_Sensitive_WordsMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_Sensitive_WordsMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_Sensitive_WordsArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_Sensitive_WordsMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_Sensitive_WordsMgr) GetData(id int32) *server.DB_Sensitive_Words { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Sensitive_Words.dat", &ProtobufDataLoader{dh: PBDB_Sensitive_WordsMgr}) +} diff --git a/srvdata/db_slotrateweight.go b/srvdata/db_slotrateweight.go new file mode 100644 index 0000000..b26d260 --- /dev/null +++ b/srvdata/db_slotrateweight.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_SlotRateWeightMgr = &DB_SlotRateWeightMgr{pool: make(map[int32]*server.DB_SlotRateWeight), Datas: &server.DB_SlotRateWeightArray{}} + +type DB_SlotRateWeightMgr struct { + Datas *server.DB_SlotRateWeightArray + pool map[int32]*server.DB_SlotRateWeight +} + +func (this *DB_SlotRateWeightMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_SlotRateWeightMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_SlotRateWeightArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_SlotRateWeightMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_SlotRateWeightMgr) GetData(id int32) *server.DB_SlotRateWeight { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_SlotRateWeight.dat", &ProtobufDataLoader{dh: PBDB_SlotRateWeightMgr}) +} diff --git a/srvdata/db_systemchance.go b/srvdata/db_systemchance.go new file mode 100644 index 0000000..c2b1033 --- /dev/null +++ b/srvdata/db_systemchance.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_SystemChanceMgr = &DB_SystemChanceMgr{pool: make(map[int32]*server.DB_SystemChance), Datas: &server.DB_SystemChanceArray{}} + +type DB_SystemChanceMgr struct { + Datas *server.DB_SystemChanceArray + pool map[int32]*server.DB_SystemChance +} + +func (this *DB_SystemChanceMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_SystemChanceMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_SystemChanceArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_SystemChanceMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_SystemChanceMgr) GetData(id int32) *server.DB_SystemChance { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_SystemChance.dat", &ProtobufDataLoader{dh: PBDB_SystemChanceMgr}) +} diff --git a/srvdata/db_task.go b/srvdata/db_task.go new file mode 100644 index 0000000..cb4e3c4 --- /dev/null +++ b/srvdata/db_task.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_TaskMgr = &DB_TaskMgr{pool: make(map[int32]*server.DB_Task), Datas: &server.DB_TaskArray{}} + +type DB_TaskMgr struct { + Datas *server.DB_TaskArray + pool map[int32]*server.DB_Task +} + +func (this *DB_TaskMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_TaskMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_TaskArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_TaskMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_TaskMgr) GetData(id int32) *server.DB_Task { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Task.dat", &ProtobufDataLoader{dh: PBDB_TaskMgr}) +} diff --git a/srvdata/db_thirdplatformgamemapping.go b/srvdata/db_thirdplatformgamemapping.go new file mode 100644 index 0000000..f85f92f --- /dev/null +++ b/srvdata/db_thirdplatformgamemapping.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_ThirdPlatformGameMappingMgr = &DB_ThirdPlatformGameMappingMgr{pool: make(map[int32]*server.DB_ThirdPlatformGameMapping), Datas: &server.DB_ThirdPlatformGameMappingArray{}} + +type DB_ThirdPlatformGameMappingMgr struct { + Datas *server.DB_ThirdPlatformGameMappingArray + pool map[int32]*server.DB_ThirdPlatformGameMapping +} + +func (this *DB_ThirdPlatformGameMappingMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_ThirdPlatformGameMappingMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_ThirdPlatformGameMappingArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_ThirdPlatformGameMappingMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_ThirdPlatformGameMappingMgr) GetData(id int32) *server.DB_ThirdPlatformGameMapping { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_ThirdPlatformGameMapping.dat", &ProtobufDataLoader{dh: PBDB_ThirdPlatformGameMappingMgr}) +} diff --git a/srvdata/db_tips.go b/srvdata/db_tips.go new file mode 100644 index 0000000..cca568f --- /dev/null +++ b/srvdata/db_tips.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_TipsMgr = &DB_TipsMgr{pool: make(map[int32]*server.DB_Tips), Datas: &server.DB_TipsArray{}} + +type DB_TipsMgr struct { + Datas *server.DB_TipsArray + pool map[int32]*server.DB_Tips +} + +func (this *DB_TipsMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_TipsMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_TipsArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_TipsMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_TipsMgr) GetData(id int32) *server.DB_Tips { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_Tips.dat", &ProtobufDataLoader{dh: PBDB_TipsMgr}) +} diff --git a/srvdata/db_vip.go b/srvdata/db_vip.go new file mode 100644 index 0000000..cb4962f --- /dev/null +++ b/srvdata/db_vip.go @@ -0,0 +1,67 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PBDB_VIPMgr = &DB_VIPMgr{pool: make(map[int32]*server.DB_VIP), Datas: &server.DB_VIPArray{}} + +type DB_VIPMgr struct { + Datas *server.DB_VIPArray + pool map[int32]*server.DB_VIP +} + +func (this *DB_VIPMgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *DB_VIPMgr) reunmarshal(data []byte) error { + newDatas := &server.DB_VIPArray{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *DB_VIPMgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *DB_VIPMgr) GetData(id int32) *server.DB_VIP { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("DB_VIP.dat", &ProtobufDataLoader{dh: PBDB_VIPMgr}) +} diff --git a/srvdata/gamedropmgr.go b/srvdata/gamedropmgr.go new file mode 100644 index 0000000..10c36af --- /dev/null +++ b/srvdata/gamedropmgr.go @@ -0,0 +1,67 @@ +package srvdata + +import ( + "strconv" +) + +var GameDropMgrSington = &GameDropMgr{ + GameDropData: make(map[string][]*GameDropData), +} + +type GameDropMgr struct { + GameDropData map[string][]*GameDropData +} + +type GameDropData struct { + ItemId int32 + Rate int32 + MinAmount int32 + MaxAmount int32 +} + +func (this *GameDropMgr) ModuleName() string { + return "GameDropMgr" +} + +func (this *GameDropMgr) GetKey(gameid, basescore int32) string { + return strconv.FormatInt(int64(gameid), 10) + "_" + strconv.FormatInt(int64(basescore), 10) +} + +func (this *GameDropMgr) Init() { + gdArr := PBDB_Game_DropMgr.Datas.Arr + if gdArr != nil { + for _, drop := range gdArr { + key := this.GetKey(drop.GameId, drop.Bet) + //道具1 + if drop.Amount1 == nil || len(drop.Amount1) != 2 { + continue + } + gdd1 := &GameDropData{ + ItemId: drop.ItemId1, + Rate: drop.Rate1, + MinAmount: drop.Amount1[0], + MaxAmount: drop.Amount1[1], + } + this.GameDropData[key] = append(this.GameDropData[key], gdd1) + //道具2 + //if drop.Amount2 == nil || len(drop.Amount2) != 2 { + // continue + //} + //gdd2 := &GameDropData{ + // ItemId: drop.ItemId2, + // Rate: drop.Rate2, + // MinAmount: drop.Amount2[0], + // MaxAmount: drop.Amount2[1], + //} + //this.GameDropData[key] = append(this.GameDropData[key], gdd2) + } + } +} + +func (this *GameDropMgr) GetDropInfoByBaseScore(gameid, basescore int32) []*GameDropData { + key := this.GetKey(gameid, basescore) + if gdds, exist := this.GameDropData[key]; exist { + return gdds + } + return nil +} diff --git a/srvdata/matchlevel.go b/srvdata/matchlevel.go new file mode 100644 index 0000000..1022a20 --- /dev/null +++ b/srvdata/matchlevel.go @@ -0,0 +1,52 @@ +package srvdata + +import "mongo.games.com/game/protocol/server" + +var MatchLevelMgr = &MatchLevel{ + DBGameFreeId: map[int32][]*server.DB_GameMatchLevel{}, +} + +type MatchLevel struct { + DBGameFreeId map[int32][]*server.DB_GameMatchLevel +} + +func (m *MatchLevel) Load(fileFullPath string) error { + m.DBGameFreeId = make(map[int32][]*server.DB_GameMatchLevel) + for _, v := range PBDB_GameMatchLevelMgr.Datas.GetArr() { + m.DBGameFreeId[v.GameFreeId] = append(m.DBGameFreeId[v.GameFreeId], v) + } + return nil +} + +func (m *MatchLevel) Reload(fileFullPath string) error { + return m.Load(fileFullPath) +} + +func (m *MatchLevel) Get(gamefreeid, level int32) *server.DB_GameMatchLevel { + arr, ok := m.DBGameFreeId[gamefreeid] + if !ok { + arr = m.DBGameFreeId[0] // 默认配置 + } + if len(arr) > 0 { + for _, v := range arr { + if v.GetMatchLevel() == level { + return v + } + } + } + // 给个默认值 + return &server.DB_GameMatchLevel{ + Id: 0, + GameFreeId: 0, + MatchLevel: 0, + RobotUpRatio: 50, + UpGrade: []int32{40, 70, 100, 130, 160, 190, 420, 480, 540, 600}, + UpGradeOdds: []int32{200, 220, 90, 85, 80, 75, 70, 65, 60, 55}, + DownGrade: []int32{20, 40, 60, 80, 100, 120, 140, 160, 180, 200}, + DownGradeOdds: []int32{300, 300, 85, 60, 55, 50, 45, 40, 35, 30}, + } +} + +func init() { + DataMgrAfter.RegisterLoader("DB_GameMatchLevel.dat", MatchLevelMgr) +} diff --git a/srvdata/playerExp.go b/srvdata/playerExp.go new file mode 100644 index 0000000..b264c83 --- /dev/null +++ b/srvdata/playerExp.go @@ -0,0 +1,39 @@ +package srvdata + +var PlayerExpMgr = &DB_PlayerExpMgrEx{ArtilleryRateMap: make(map[int32]*ArtilleryRateData)} + +type ArtilleryRateData struct { + Shell int32 + Level int32 +} + +type DB_PlayerExpMgrEx struct { + ArtilleryRateMap map[int32]*ArtilleryRateData +} + +func (this *DB_PlayerExpMgrEx) Load(fileFullPath string) error { + this.ArtilleryRateMap = make(map[int32]*ArtilleryRateData) + for _, data := range PBDB_ArtilleryRateMgr.Datas.Arr { + this.ArtilleryRateMap[data.Level] = &ArtilleryRateData{ + Shell: data.Shell, + Level: data.Level, + } + } + return nil +} + +// 获取解锁的炮倍 +func (this *DB_PlayerExpMgrEx) GetUnPower(level int32) int32 { + if this.ArtilleryRateMap[level] != nil { + return this.ArtilleryRateMap[level].Shell + } + return 0 +} + +func (this *DB_PlayerExpMgrEx) Reload(fileFullPath string) error { + return this.Load(fileFullPath) +} + +func init() { + DataMgrAfter.RegisterLoader("DB_PlayerExp.dat", PlayerExpMgr) +} diff --git a/srvdata/playertype.go b/srvdata/playertype.go new file mode 100644 index 0000000..3a58f67 --- /dev/null +++ b/srvdata/playertype.go @@ -0,0 +1,39 @@ +package srvdata + +import "mongo.games.com/game/protocol/server" + +var PlayerTypeMgrSington = &PlayerTypeMgr{ + types: make(map[int32][]*server.DB_PlayerType), +} + +type PlayerTypeMgr struct { + types map[int32][]*server.DB_PlayerType +} + +func (this *PlayerTypeMgr) Load(fileFullPath string) error { + this.updateData() + return nil +} + +func (this *PlayerTypeMgr) Reload(fileFullPath string) error { + return this.Load(fileFullPath) +} + +func (this *PlayerTypeMgr) updateData() { + types := make(map[int32][]*server.DB_PlayerType) + for _, data := range PBDB_PlayerTypeMgr.Datas.Arr { + types[data.GetGameFreeId()] = append(types[data.GetGameFreeId()], data) + } + this.types = types +} + +func (this *PlayerTypeMgr) GetPlayerType(id int32) []*server.DB_PlayerType { + if data, exist := this.types[id]; exist { + return data + } + return nil +} + +func init() { + DataMgrAfter.RegisterLoader("DB_PlayerType.dat", PlayerTypeMgrSington) +} diff --git a/srvdata/rankmatch.go b/srvdata/rankmatch.go new file mode 100644 index 0000000..391189b --- /dev/null +++ b/srvdata/rankmatch.go @@ -0,0 +1,67 @@ +package srvdata + +import ( + "sort" + + "mongo.games.com/game/protocol/server" +) + +var RankLevelMgr = &RankLevel{ + RankType: map[int32][]*server.DB_RankLevel{}, +} + +type RankLevel struct { + RankType map[int32][]*server.DB_RankLevel +} + +func (r *RankLevel) Load(fileFullPath string) error { + r.RankType = make(map[int32][]*server.DB_RankLevel) + for _, v := range PBDB_RankLevelMgr.Datas.GetArr() { + r.RankType[v.RankType] = append(r.RankType[v.RankType], v) + } + + for _, v := range r.RankType { + sort.Slice(v, func(i, j int) bool { + return v[i].Score < v[j].Score + }) + } + + return nil +} + +func (r *RankLevel) Reload(fileFullPath string) error { + return r.Load(fileFullPath) +} + +func (r *RankLevel) GetRankLevel(rankType int32, score int64) int32 { + // 没有配置排名 + if len(r.RankType) == 0 { + return 0 + } + + // 没有配置该类型的排名 + if _, ok := r.RankType[rankType]; !ok { + return 0 + } + + // 遍历排名 + var level int32 + for _, v := range r.RankType[rankType] { + if score < v.Score { + return v.Level + } + level = v.Level + } + return level +} + +func (r *RankLevel) GetNewScore(score int64) int64 { + if score <= 10000 { + return score + } + return score/2 + 5000 +} + +func init() { + DataMgrAfter.RegisterLoader("DB_RankLevel.dat", RankLevelMgr) +} diff --git a/srvdata/rolepetmgr.go b/srvdata/rolepetmgr.go new file mode 100644 index 0000000..68dc1f0 --- /dev/null +++ b/srvdata/rolepetmgr.go @@ -0,0 +1,60 @@ +package srvdata + +import ( + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/server" +) + +var RolePetMgrSington = &RolePetMgr{ + Roles: make(map[int32][]*server.DB_Game_Role), + Pets: make(map[int32][]*server.DB_Game_Pet), +} + +type RolePetMgr struct { + Roles map[int32][]*server.DB_Game_Role + Pets map[int32][]*server.DB_Game_Pet +} + +func (this *RolePetMgr) Init() { + this.Roles = make(map[int32][]*server.DB_Game_Role) + this.Pets = make(map[int32][]*server.DB_Game_Pet) + for _, v := range PBDB_Game_RoleMgr.Datas.GetArr() { + this.Roles[v.RoleId] = append(this.Roles[v.RoleId], v) + } + for _, v := range PBDB_Game_PetMgr.Datas.GetArr() { + this.Pets[v.PetId] = append(this.Pets[v.PetId], v) + } +} +func (this *RolePetMgr) GetRoleByRoleIdAndLevel(roleId, level int32) *server.DB_Game_Role { + if roleInfo, ok := this.Roles[roleId]; ok { + if len(roleInfo) > int(level) { + return roleInfo[level] + } + } + return nil +} +func (this *RolePetMgr) GetPetByPetIdAndLevel(petId, level int32) *server.DB_Game_Pet { + if petInfo, ok := this.Pets[petId]; ok { + if len(petInfo) > int(level) { + return petInfo[level] + } + } + return nil +} + +// GetRoleAdd 获取角色加成 +// roleId 角色id +// tp 加成类型 +// 返回 角色id, 加成值 +// 获得角色,就有加成,不用使用 +func (this *RolePetMgr) GetRoleAdd(p *model.PlayerData, tp int32) (int32, int32) { + if p.Roles != nil && p.Roles.ModUnlock != nil { + for k, v := range p.Roles.ModUnlock { + info := this.GetRoleByRoleIdAndLevel(k, v) + if info.AwardType == tp { + return k, info.Award + } + } + } + return 0, 0 +} diff --git a/srvdata/sensitive_word.go b/srvdata/sensitive_word.go new file mode 100644 index 0000000..6cb4d76 --- /dev/null +++ b/srvdata/sensitive_word.go @@ -0,0 +1,124 @@ +package srvdata + +var sensitiveWordsTree = make(map[rune]*Word) + +type Word struct { + w []rune + next map[rune]*Word +} + +func initSensitiveWordTree() error { + for _, word := range PBDB_Sensitive_WordsMgr.Datas.Arr { + w := []rune(word.GetSensitive_Words()) + if len(w) == 0 { + continue + } + AddSensitiveWord(w) + } + return nil +} + +func AddSensitiveWord(w []rune) bool { + if len(w) == 0 { + return false + } + node := sensitiveWordsTree + for i, c := range w { + if _, exist := node[c]; !exist { + if i == len(w)-1 { + node[c] = &Word{w: w[i:], next: make(map[rune]*Word)} + } else { + node[c] = &Word{w: nil, next: make(map[rune]*Word)} + } + } else { + if i == len(w)-1 { + if _, exist := node[c]; exist { + if node[c].w == nil { + node[c].w = w[i:] + } + return true + } + } + } + node = node[c].next + } + return true +} + +//func DelSensitiveWord(w []rune) bool { +// if len(w) == 0 { +// return false +// } +// node := sensitiveWordsTree +// for i, c := range w { +// if _, exist := node[c]; !exist { +// return false +// } +// if i == len(w)-1 { +// if _, exist := node[c]; exist { +// node[c].w = nil +// return true +// } +// } +// node = node[c].next +// } +// return false +//} + +func HasSensitiveWord(words []rune) bool { + cnt := len(words) + for m := 0; m < cnt; m++ { + node := sensitiveWordsTree + for n := m; n < cnt; n++ { + c := words[n] + if content, exist := node[c]; exist { + if content.w != nil { + return true + } else { + node = content.next + } + } else { + break + } + } + } + return false +} + +//func ReplaceSensitiveWord(words []rune) []rune { +// cnt := len(words) +// for m := 0; m < cnt; m++ { +// node := sensitiveWordsTree +// for n := m; n < cnt; n++ { +// c := words[n] +// if content, exist := node[c]; exist { +// if content.w != nil { +// for i := m; i <= n; i++ { +// words[i] = rune('*') +// } +// } else { +// node = content.next +// } +// } else { +// break +// } +// } +// } +// return words +//} + +type SensitiveWordTreeMgr struct { +} + +func (s *SensitiveWordTreeMgr) Load(fileFullPath string) error { + return initSensitiveWordTree() +} + +func (s *SensitiveWordTreeMgr) Reload(fileFullPath string) error { + sensitiveWordsTree = make(map[rune]*Word) + return initSensitiveWordTree() +} + +func init() { + DataMgrAfter.RegisterLoader("DB_Sensitive_Words.dat", &SensitiveWordTreeMgr{}) +} diff --git a/srvdata/sensitive_word_test.go b/srvdata/sensitive_word_test.go new file mode 100644 index 0000000..b623ed0 --- /dev/null +++ b/srvdata/sensitive_word_test.go @@ -0,0 +1,38 @@ +package srvdata + +import ( + "path/filepath" + "testing" +) + +func initSensitiveWordEnv(t *testing.T) { + fileName := "DB_Sensitive_Words.dat" + loader := DataMgr.GetLoader(fileName) + if loader != nil { + fullPath := filepath.Join("../data", fileName) + err := loader.Load(fullPath) + if err != nil { + t.Error(fileName, " loader err:", err) + } + } + err := initSensitiveWordTree() + if err != nil { + t.Error(fileName, " init tree err:", err) + } +} + +func TestSensitiveWord(t *testing.T) { + //initSensitiveWordEnv(t) + // + //testWords := "18岁以下勿看00000000" + //if !HasSensitiveWord([]rune(testWords)) { + // t.Error("HasSensitiveWord") + //} + // + //ret := ReplaceSensitiveWord([]rune(testWords)) + //t.Log(string(ret[:])) + //ret = ReplaceSensitiveWord([]rune("go-vern-menthjdhfjdfhdj")) + //t.Log(string(ret[:])) + //ret = ReplaceSensitiveWord([]rune("【③肖中特】中国爱神之传奇00爱液")) + //t.Log(string(ret[:])) +} diff --git a/startup.bat b/startup.bat new file mode 100644 index 0000000..f3aa47e --- /dev/null +++ b/startup.bat @@ -0,0 +1,13 @@ +set GODEBUG=gctrace=1 +cd dbproxy +start dbproxy.exe +cd ../mgrsrv +start mgrsrv.exe +cd ../gatesrv +start gatesrv.exe +cd ../worldsrv +start worldsrv.exe +cd ../gamesrv +start gamesrv.exe +cd ../ranksrv +start ranksrv.exe \ No newline at end of file diff --git a/tools/bucketidgen/config.json b/tools/bucketidgen/config.json new file mode 100644 index 0000000..f323d67 --- /dev/null +++ b/tools/bucketidgen/config.json @@ -0,0 +1,28 @@ +{ + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + + "core": { + "MaxProcs": 1 + }, + + "costum": { + "PlayerIdCount": 801999 + }, + + "mongo": { + "Dbs": { + "user": { + "Host": "127.0.0.1", + "Database": "win88_global", + "User": "", + "Password": "" + } + } + } +} \ No newline at end of file diff --git a/tools/bucketidgen/genbucketid.go b/tools/bucketidgen/genbucketid.go new file mode 100644 index 0000000..9ce6824 --- /dev/null +++ b/tools/bucketidgen/genbucketid.go @@ -0,0 +1,75 @@ +package main + +import ( + "github.com/globalsign/mgo/bson" + "math/rand" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/mongo" + "time" +) + +var ( + c_playerids *mongo.Collection + PlayerIdsDBName = "user" + PlayerIdsCollName = "user_bucketids" +) + +type PlayerId struct { + Id bson.ObjectId `bson:"_id"` + StartPos int32 + EndPos int32 + Used bool +} + +func PlayerIdCollection() *mongo.Collection { + if c_playerids == nil || !c_playerids.IsValid() { + c_playerids = mongo.DatabaseC(PlayerIdsDBName, PlayerIdsCollName) + if c_playerids != nil { + c_playerids.Hold() + } + } + return c_playerids +} +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + var ( + minV = 10000000 + maxV = 99999999 + bucketSize = 100 + bucketArr []*PlayerId + gSeedV = time.Now().UnixNano() + ) + cnt := ((maxV - minV) / bucketSize) + 1 + bucketArr = make([]*PlayerId, 0, cnt) + for i := minV; i < maxV; i = i + bucketSize { + bucketArr = append(bucketArr, &PlayerId{ + Id: bson.NewObjectId(), + StartPos: int32(i), + EndPos: int32(i + bucketSize - 1), + Used: false, + }) + } + gSeedV++ + rand.Seed(gSeedV) + for i, _ := range bucketArr { + rnd := rand.Intn(len(bucketArr)) + bucketArr[i], bucketArr[rnd] = bucketArr[rnd], bucketArr[i] + } + c := PlayerIdCollection() + docs := make([]interface{}, 0, len(bucketArr)) + for _, log := range bucketArr { + docs = append(docs, log) + } + if c != nil { + for len(docs) > 0 { + cnt := len(docs) + if cnt > 1000 { + cnt = 1000 + } + c.Insert(docs[:cnt]...) + docs = docs[cnt:] + } + } + return nil + }) +} diff --git a/tools/bucketidgen/logger.xml b/tools/bucketidgen/logger.xml new file mode 100644 index 0000000..cb66204 --- /dev/null +++ b/tools/bucketidgen/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/bucketidgen/main.go b/tools/bucketidgen/main.go new file mode 100644 index 0000000..891bc81 --- /dev/null +++ b/tools/bucketidgen/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/module" +) + +func main() { + defer core.ClosePackages() + core.LoadPackages("config.json") + + waiter := module.Start() + waiter.Wait("") +} diff --git a/tools/cydattoclibin/config.json b/tools/cydattoclibin/config.json new file mode 100644 index 0000000..07da7cf --- /dev/null +++ b/tools/cydattoclibin/config.json @@ -0,0 +1,4 @@ +{ + "srcPath": "/Users/ethan/Proj/trunk/src/mongo.games.com/game/data", + "destPath":"/Users/ethan/Proj/trunk/src/mongo.games.com/game/binary" +} \ No newline at end of file diff --git a/tools/cydattoclibin/config_ori.json b/tools/cydattoclibin/config_ori.json new file mode 100644 index 0000000..9b09431 --- /dev/null +++ b/tools/cydattoclibin/config_ori.json @@ -0,0 +1,4 @@ +{ + "srcPath": "D:/gocode/trunk/src/mongo.games.com/game/data", + "destPath":"D:/Vietnammongo.games.com/game/trunk/binary" +} \ No newline at end of file diff --git a/tools/cydattoclibin/cydattoclibin b/tools/cydattoclibin/cydattoclibin new file mode 100644 index 0000000..c82c4df Binary files /dev/null and b/tools/cydattoclibin/cydattoclibin differ diff --git a/tools/cydattoclibin/cydattoclibin.go b/tools/cydattoclibin/cydattoclibin.go new file mode 100644 index 0000000..4c1fbba --- /dev/null +++ b/tools/cydattoclibin/cydattoclibin.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path" + "strings" +) + +type Conf struct { + SrcPath string `json:"srcPath"` + DestPath string `json:"destPath"` +} + +// 判断路径是否存在 +func pathExist(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +func main() { + fileBuffer, err := os.ReadFile("./config.json") + if err != nil { + fmt.Println("获取srcpath和destpath发生错误") + return + } + var c Conf + + e := json.Unmarshal(fileBuffer, &c) + if e != nil { + fmt.Println("从配置文件中获取srcpath和destpath发生错误") + } + + fmt.Println("源路径为:%s", c.SrcPath) + fmt.Println("目标路径为:%s", c.DestPath) + + //判定源路径是否存在 + srcstat, _ := pathExist(c.SrcPath) + if !srcstat { + fmt.Println("源路径不存在,无法执行转换") + } + deststate, _ := pathExist(c.DestPath) + if !deststate { + fmt.Println("目标路径不存在,请修改配置文件") + } + + copyDataToBin(c.SrcPath, c.DestPath) +} + +// 将 srcPath目录中的文件copy到 目标对应文件中 并且要修改后缀名 +func copyDataToBin(srcPath string, targetPath string) { + rd, err := os.ReadDir(srcPath) + if err != nil { + fmt.Println("遍历文件是发生错误") + return + } + for _, fi := range rd { + if fi.IsDir() { + //copyDataToBin(srcPath + fi.Name(),targetPath + fi.Name()) + copyDataToBin(path.Join(srcPath, fi.Name()), path.Join(targetPath, fi.Name())) + } else { + + fullPath := path.Join(srcPath, fi.Name()) + ext := path.Ext(fullPath) + //判定文件的后缀名为.bat则copy + if ext == ".dat" { + createTargetDir(targetPath) + + //判定目标文件是否已经存在。如果存在删除 + //fulleTargetPath := targetPath + fi.Name() + strArr := strings.Split(fi.Name(), ".") + baseName := strArr[0] + tFileName := path.Join(targetPath, baseName) + ".bin" + targetState, _ := pathExist(tFileName) + if targetState { + os.Remove(tFileName) + } + copyFile(fullPath, tFileName) + } + } + } +} + +func copyFile(srcFile string, targetFile string) { + src, err := os.Open(srcFile) + if err != nil { + fmt.Println("Open file err = %v/n", err) + return + } + defer src.Close() + + dst, err := os.OpenFile(targetFile, os.O_WRONLY|os.O_CREATE, 0777) + if err != nil { + fmt.Println("Open file err = %c/n", err) + return + } + defer dst.Close() + io.Copy(dst, src) +} + +// 创建目录 +func createTargetDir(destDir string) { + dirState, _ := pathExist(destDir) + if !dirState { + os.Mkdir(destDir, os.ModePerm) + } +} diff --git a/tools/etcdtool/build_linux.bat b/tools/etcdtool/build_linux.bat new file mode 100644 index 0000000..c8f39db --- /dev/null +++ b/tools/etcdtool/build_linux.bat @@ -0,0 +1,7 @@ +set CGO_ENABLED=0 +set GOOS=linux +set GOARCH=amd64 +go fmt +go build +@echo "complete" +pause diff --git a/tools/etcdtool/build_windows.bat b/tools/etcdtool/build_windows.bat new file mode 100644 index 0000000..502a403 --- /dev/null +++ b/tools/etcdtool/build_windows.bat @@ -0,0 +1,7 @@ +set CGO_ENABLED=0 +set GOOS=windows +set GOARCH=amd64 +go fmt +go build +@echo "complete" +pause diff --git a/tools/etcdtool/config.json b/tools/etcdtool/config.json new file mode 100644 index 0000000..693166d --- /dev/null +++ b/tools/etcdtool/config.json @@ -0,0 +1,75 @@ +{ + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "core": { + "MaxProcs": 4 + }, + "costum":{ + "GameIds":"23,24,25,26,27", + "gamesrv":"http://192.168.88.111:9595", + "etcdurl":["127.0.0.1:2379"], + "etcduser":"root", + "etcdpwd":"123456", + "UseBlacklistBindPlayerinfo":true + }, + "common": { + "AppId": "5a7553560c9cf51c105678f3", + "IsDevMode": true + }, + "webapi": { + "GameApiURL": "http://96.9.70.209/api/game_srv", + "DgApiUrl":"http://dg.oceaneet.com", + "HboApiUrl":"http://96.9.70.209:8088", + "WwgAddr":"http://96.9.70.209:8089/", + "WwgReqTimeout":30, + "FgAddr":"http://96.9.70.209:8089/", + "FgReqTimeout":30, + "AmebaAddr":"http://96.9.70.209:8089/", + "AmebaReqTimeout":30 + }, + "data": { + "RootPath": "../../data", + "Files": [ + "DB_Sensitive_Words.dat", + "DB_MatchFree.dat", + "DB_MatchRule.dat", + "DB_Task.dat", + "DB_GameFree.dat", + "DB_GameRule.dat", + "DB_SystemChance.dat", + "DB_AnimalColor.dat", + "DB_Fish.dat" + ], + "LoadLib": true + }, + "signal":{ + "SupportSignal": true + } +} \ No newline at end of file diff --git a/tools/etcdtool/logger.xml b/tools/etcdtool/logger.xml new file mode 100644 index 0000000..f6eb37b --- /dev/null +++ b/tools/etcdtool/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/etcdtool/main.go b/tools/etcdtool/main.go new file mode 100644 index 0000000..b71e5d0 --- /dev/null +++ b/tools/etcdtool/main.go @@ -0,0 +1,1966 @@ +package main + +import ( + "encoding/json" + "fmt" + _ "games.yol.com/win88" + "go.etcd.io/etcd/client/v3" + "mongo.games.com/game/common" + "mongo.games.com/game/etcd" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core" + _ "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "net" + "strconv" + "time" +) + +//获取平台数据 platform_list + +// 排行榜开关 +type RankSwitch struct { + Asset int32 // 财富榜 + Recharge int32 // 充值榜 + Exchange int32 // 兑换榜 + Profit int32 // 盈利榜 +} + +// 俱乐部配置 +type ClubConfig struct { + CreationCoin int64 //创建俱乐部金额 + IncreaseCoin int64 //升级俱乐部金额 + ClubInitPlayerNum int32 //俱乐部初始人数 + IncreasePlayerNum int32 //升级人数增加 + IsOpenClub bool //是否开放俱乐部 + CreateClubCheckByManual bool //创建俱乐部人工审核,true=手动 + EditClubNoticeByManual bool //修改公告人工审核,true=手动 + CreateRoomAmount int64 //创建房间金额(分/局) + GiveCoinRate []int64 //会长充值额外赠送比例 +} + +type PlatformInfoApi struct { + PlatformName string + Isolated bool + Disabled bool + ConfigId int32 + CustomService string + BindOption int32 + ServiceFlag int32 + UpgradeAccountGiveCoin int32 //升级账号奖励金币 + NewAccountGiveCoin int32 //新账号奖励金币 + PerBankNoLimitAccount int32 //同一银行卡号绑定用户数量限制 + ExchangeMin int32 //最低兑换金额 + ExchangeLimit int32 //兑换限制 + ExchangeTax int32 //兑换税收(万分比) + ExchangeForceTax int32 //强制兑换税收 + ExchangeFlow int32 //兑换流水比例 + ExchangeGiveFlow int32 //赠送兑换流水比例 + ExchangeFlag int32 //兑换标记 二进制 第一位:兑换税收 第二位:流水比例 + ExchangeVer int32 //兑换版本 + ExchangeMultiple int32 //兑换基数 + VipRange []int32 //VIP充值区间 + OtherParams string //其他参数json串 + SpreadConfig int32 + Leaderboard RankSwitch + ClubConfig *ClubConfig //俱乐部配置 + VerifyCodeType int32 //验证码方式 + ThirdGameMerchant map[int32]int32 //三方游戏平台状态 + CustomType int32 + NeedDeviceInfo bool + NeedSameName bool //绑定的银行卡和支付宝用户名字需要相同 + ExchangeBankMax int32 //银行卡最大兑换金额 0不限制 + ExchangeAlipayMax int32 //支付宝最大兑换金额 0不限制 + DgHboConfig int32 //dg hbo配置,默认0,dg 1 hbo 2 + PerBankNoLimitName int32 //银行卡名字数量限制 + IsCanUserBindPromoter bool //是否允许用户手动绑定推广员 + UserBindPromoterPrize int32 //手动绑定奖励 +} + +type PackageListApi struct { + Tag string //android包名或者ios标记 + Platform int32 //所属平台 + ChannelId int32 //渠道ID + PromoterId int32 //推广员ID + PromoterTree int32 //无级推广树 + SpreadTag int32 //全民包标识 0:普通包 1:全民包 + OpenInstallTag int32 //是否是openinstall包 0:不是 1:是 + Status int32 //状态 + AppStore int32 //是否是苹果商店包 0:不是 1:是 + ExchangeFlag int32 //兑换标记 0 关闭包返利 1打开包返利 受平台配置影响 + ExchangeFlow int32 //兑换比例 + IsForceBind int32 +} + +type PlatformCoinfig struct { + Id int32 + Params []PlatConDataDetail + Parent_id int32 + Platform int32 +} + +// 游戏平台配置 +type PlatConDataDetail struct { + LogicId int32 ////对应DB_GameFree.xlsx中的id + Param string //参数 + State int32 //开关 0:关 1:开 + GroupId int32 //组id + DBGameFree *server.DB_GameFree //游戏配置 +} + +// 游戏平台状态 +type PlatConfState struct { + LogicId int32 //对应GameGlobalState中的LogicId + Param string //参数 + State int32 //开关 0:关 1:开 + GroupId int32 //组id + DBGameFree *server.DB_GameFree //配置参数 +} + +var PlatformList = make(map[string]*PlatformInfoApi) +var PackageList = make(map[string]*PackageListApi) +var PlatConList = make(map[int32]*PlatformCoinfig) +var etcdCli = &etcd.Client{} +var waitCnt int +var isClearEtcd = "n" //是否清理etcd数据,默认是不清理 +var isPutToEtcd = "n" //是否拉取数据到etcd,默认是不拉取 +var isWatchEtcd = "n" //是否监控etcd,默认是不监控 +func main() { + logger.Trace(`==================== Tips: Submit Your Choice =======================`) + + logger.Tracef(`Do you want delete etcd only key=%v data (y/n) ?`, etcd.ETCDKEY_ROOT_PREFIX) + fmt.Scan(&isClearEtcd) + + logger.Tracef(`Do you want put data to etcd by WebAPI (y/n) ?`) + fmt.Scan(&isPutToEtcd) + + logger.Tracef(`Do you want watch etcd (y/n) ?`) + fmt.Scan(&isWatchEtcd) + + var err error + if isClearEtcd != "y" && isPutToEtcd != "y" && isWatchEtcd != "y" { + logger.Errorf(`you don't have select any action, bye! `) + goto END + } + defer core.ClosePackages() + core.LoadPackages("config.json") + logger.Trace("etcdurl=", common.CustomConfig.GetStrings("etcdurl")) + logger.Trace("etcduser=", common.CustomConfig.GetString("etcduser")) + logger.Trace("etcdpwd=", common.CustomConfig.GetString("etcdpwd")) + logger.Trace("正在连接ETCD服务...") + err = etcdCli.Open(common.CustomConfig.GetStrings("etcdurl"), common.CustomConfig.GetString("etcduser"), common.CustomConfig.GetString("etcdpwd"), time.Minute) + if err != nil { + logger.Error("etcd connect fail because : ", err) + return + } + logger.Trace("连接ETCD服务成功!") + + if isClearEtcd == "y" { + logger.Trace("delete etcd data...") + rep, err := etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ROOT_PREFIX) + if err != nil { + logger.Logger.Errorf("delete etcd date err:%v", err) + } else { + logger.Tracef("delete %v item in key=%v", rep.Deleted, etcd.ETCDKEY_ROOT_PREFIX) + } + } + + if isPutToEtcd == "y" { + logger.Trace("是否只获取公共黑名单数据=", common.CustomConfig.GetBool("UseBlacklistBindPlayerinfo")) + //PutTestPlatform2Etcd() + //return + LoadActGoldCome() + PutActGoldCome2Etcd() + return + LoadRebateData() + PutRebateTaskEtcd() + + //获取平台列表 + GetPlatformList() + PutPlatform2Etcd() + + //包 + //LoadPlatformPackage() + PutPlatformPackage2Etcd() + + // + LoadPlatformConfig() + PutPlatformConfig2Etcd() + + // + LoadGameGroup() + PutPlatformGroup2Etcd() + + // + LoadBullet() + PutBullet2Etcd() + + // + LoadCustomer() + PutCustomer2Etcd() + + LoadBlackList() + PutBlackList2Etcd() + + LoadActSign() + PutActSign2Etcd() + + // + LoadActGoldTask() + PutActGoldTask2Etcd() + + // + + LoadActOnlineReward() + PutActOnlineReward2Etcd() + + LoadLuckyTurntableConfig() + PutLuckyTurntableConfig2Etcd() + + LoadYebConfig() + PutYeb2ConfigEtcd() + + LoadPromoterData() + PutPromoterEtcd() + + LoadActVipData() + PutActVipEtcd() + + LoadActWxShareData() + PutActWxShareEtcd() + + LoadActData() + PutAcEtcd() + + LoadRandomPrizeData() + PutRandomPrizeEtcd() + + LoadActFPayData() + PutAcFPayEtcd() + + } + + if isWatchEtcd == "y" { + logger.Trace("Tip:watching etcd ,you can get/put keys....") + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Errorf("etcd watch WithPrefix(%v) panic:%v", etcd.ETCDKEY_ROOT_PREFIX, err) + } + logger.Logger.Warnf("etcd watch WithPrefix(%v) quit!!!", etcd.ETCDKEY_ROOT_PREFIX) + }() + var times int64 + for { + times++ + logger.Logger.Warnf("etcd watch WithPrefix(%v) start[%v]!!!", etcd.ETCDKEY_ROOT_PREFIX, times) + rch := etcdCli.WatchWithPrefix(etcd.ETCDKEY_ROOT_PREFIX, 0) + for wresp := range rch { + if wresp.Canceled { + logger.Logger.Warnf("etcd watch WithPrefix(%v) be closed, reason:%v", etcd.ETCDKEY_ROOT_PREFIX, wresp.Err()) + continue + } + for _, ev := range wresp.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + logger.Logger.Infof("etcd desc WithPrefix(%v) delete data:%v", string(ev.Kv.Key), string(ev.Kv.Value)) + case clientv3.EventTypePut: + logger.Logger.Infof("etcd desc WithPrefix(%v) put data:%v", string(ev.Kv.Key), string(ev.Kv.Value)) + } + } + } + } + }() + } +END: + //启动业务模块 + waiter := module.Start() + waiter.Wait("main()") +} + +// 平台列表 +func GetPlatformList() { + type ApiResult struct { + Tag int + Msg []PlatformInfoApi + } + logger.Logger.Trace("start API_GetPlatformList") + platformBuff, err := webapi.API_GetPlatformData(common.GetAppId()) + if err == nil { + //logger.Logger.Trace("API_GetPlatformData:", string(platformBuff)) + ar := ApiResult{} + err = json.Unmarshal(platformBuff, &ar) + if err == nil && ar.Tag == 0 { + for i := 0; i < len(ar.Msg); i++ { + PlatformList[ar.Msg[i].PlatformName] = &ar.Msg[i] + } + } else { + logger.Logger.Error("Unmarshal platform data error:", err, string(platformBuff)) + } + } else { + logger.Logger.Error("Get platfrom data error:", err) + } +} + +func PutTestPlatform2Etcd() { + + for i := 1; i < 10000; i++ { + s := "{\"PlatformName\":\"9\",\"Isolated\":true,\"Disabled\":true,\"ConfigId\":9,\"CustomService\":\"\",\"BindOption\":1,\"ServiceFlag\":1,\"UpgradeAccountGiveCoin\":300,\"NewAccountGiveCoin\":0,\"PerBankNoLimitAccount\":1,\"ExchangeMin\":10000,\"ExchangeLimit\":300,\"ExchangeTax\":100,\"ExchangeForceTax\":0,\"ExchangeMultiple\":0,\"ExchangeFlow\":0,\"ExchangeGiveFlow\":0,\"ExchangeFlag\":1,\"ExchangeVer\":0,\"VipRange\":[10000,50000,100000,300000,500000,1000000],\"OtherParams\":\"\",\"SpreadConfig\":1,\"Leaderboard\":{\"Asset\":0,\"Recharge\":0,\"Exchange\":0,\"Profit\":0},\"ClubConfig\":{\"CreationCoin\":10000,\"IncreaseCoin\":100000,\"ClubInitPlayerNum\":50,\"IncreasePlayerNum\":50,\"IsOpenClub\":false,\"CreateClubCheckByManual\":false,\"EditClubNoticeByManual\":false,\"CreateRoomAmount\":1,\"GiveCoinRate\":[1,2,3,4,5,6,7,8,9,10]},\"VerifyCodeType\":0,\"ThirdGameMerchant\":{\"38\":1,\"39\":1,\"41\":0,\"43\":1,\"46\":0,\"47\":0,\"48\":0},\"CustomType\":0,\"NeedDeviceInfo\":false,\"NeedSameName\":false,\"ExchangeBankMax\":0,\"ExchangeAlipayMax\":0,\"DgHboConfig\":0}" + data, err := json.Marshal(s) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_PLATFORM_PREFIX, "7"), string(s)) + if err != nil { + logger.Logger.Tracef("PutPlatform2Etcd err:%v data:%v", err, string(s)) + } + } else { + logger.Logger.Errorf("PutPlatform2Etcd err:%v data:%v", err, string(data)) + } + } + +} + +func PutPlatform2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_PLATFORM_PREFIX) + + logger.Logger.Trace("ETCD_PutPlatform2Etcd") + for name, p := range PlatformList { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_PLATFORM_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutPlatform2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutPlatform2Etcd err:%v data:%v", err, string(data)) + } + } +} + +// 包列表 +//func LoadPlatformPackage() { +// //获取包对应关系 package_list +// logger.Logger.Trace("start API_PackageList") +// packageBuff, err := webapi.API_PackageList(common.GetAppId()) +// if err == nil { +// type ApiResult struct { +// Tag int +// Msg []PackageListApi +// } +// ar := ApiResult{} +// err = json.Unmarshal(packageBuff, &ar) +// if err == nil && ar.Tag == 0 { +// for i := 0; i < len(ar.Msg); i++ { +// if ar.Msg[i].Platform == 0 { +// ar.Msg[i].Platform = 1 +// } +// PackageList[ar.Msg[i].Tag] = &ar.Msg[i] +// //logger.Logger.Trace("PlatformPackage data:", ar.Msg[i]) +// } +// } else { +// logger.Logger.Error("Unmarshal package list data error:", err, string(packageBuff)) +// } +// } else { +// logger.Logger.Error("Get package list data error:", err) +// } +//} + +func PutPlatformPackage2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_PACKAGE_PREFIX) + logger.Logger.Trace("ETCD_PutPlatformPackage2Etcd") + //type ETCDPackageInfo struct { + // Tag string //android包名或者ios标记 + // Platform int32 //所属平台 + // Channel int32 //渠道ID + // Promoter int32 //推广员ID + // PromoterTree int32 //无级推广树 + // SpreadTag int32 //全民包标识 0:普通包 1:全民包 + // Status int32 //状态 + //} + count := len(PackageList) + cur := 0 + for name, p := range PackageList { + //value := &ETCDPackageInfo{ + // Tag: p.Tag, + // Platform: p.Platform, + // Channel: p.ChannelId, + // Promoter: p.PromoterId, + // PromoterTree: p.PromoterTree, + // SpreadTag: p.SpreadTag, + // Status: p.Status, + //} + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_PACKAGE_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutPlatformPackage2Etcd err:%v data:%v", err, string(data)) + } else { + //logger.Logger.Tracef("PutPlatformPackage2Etcd succes:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutPlatformPackage2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +func LoadPlatformConfig() { + //获取平台详细信息 game_config_list + type PlatformConfigApi struct { + Id int32 + Params []PlatConfState + Parent_id int32 + Platform int32 + } + type PlatformConfigDataResult struct { + Tag int + Msg []PlatformConfigApi + } + //logger.Logger.Trace("LoadPlatformConfig") + configBuff, err := webapi.API_GetPlatformConfigData(common.GetAppId()) + //logger.Trace(string(configBuff)) + if err == nil { + pcdr := PlatformConfigDataResult{} + err = json.Unmarshal(configBuff, &pcdr) + if err == nil && pcdr.Tag == 0 { + for _, config := range pcdr.Msg { + pc := &PlatformCoinfig{ + Id: config.Id, + Parent_id: config.Parent_id, + Platform: config.Platform, + } + println(pc.Id) + for _, config := range config.Params { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(config.LogicId) + if dbGameFree == nil { + logger.Logger.Error("Platform config data error logic id:", config.LogicId) + continue + } + pcd := PlatConDataDetail{ + LogicId: config.LogicId, + Param: config.Param, + State: config.State, + GroupId: config.GroupId, + DBGameFree: config.DBGameFree, + } + if pcd.DBGameFree == nil { //数据容错 + pcd.DBGameFree = dbGameFree + } else { + + CopyDBGameFreeField(dbGameFree, pcd.DBGameFree) + } + pc.Params = append(pc.Params, pcd) + } + //logger.Logger.Info("PlatformCoinfig data:", pc) + PlatConList[config.Id] = pc + } + } else { + logger.Logger.Error("Unmarshal platform config data error:", err, string(configBuff)) + } + } else { + logger.Logger.Error("Get platfrom config data error:", err) + } +} + +func PutPlatformConfig2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_GAMECONFIG_PREFIX) + logger.Logger.Trace("ETCD_PutPlatformConfig2Etcd") + count := len(PlatConList) + cur := 0 + for name, p := range PlatConList { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_GAMECONFIG_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutPlatformConfig2Etcd err:%v data:%v", err, string(data)) + } else { + //logger.Logger.Tracef("PutPlatformConfig2Etcd succes:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutPlatformConfig2Etcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type PlatformGameGroup struct { + GroupId int32 `json:"id"` + LogicId int32 `json:"LogicId"` + State int32 `json:"State"` + DBGameFree *server.DB_GameFree `json:"DBGameFree"` //游戏配置 +} + +var GameGroups = make(map[int32]*PlatformGameGroup) + +func LoadGameGroup() { + //获取包对应关系 package_list + logger.Logger.Trace("API_GetGameGroupData") + packageBuff, err := webapi.API_GetGameGroupData(common.GetAppId()) + if err == nil { + type ApiResult struct { + Tag int + Msg []*PlatformGameGroup + } + ar := ApiResult{} + err = json.Unmarshal(packageBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, value := range ar.Msg { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(value.LogicId) + if dbGameFree == nil { + continue + } + if value.DBGameFree == nil { + value.DBGameFree = dbGameFree + } else { + CopyDBGameFreeField(dbGameFree, value.DBGameFree) + } + GameGroups[value.GroupId] = value + //logger.Logger.Trace("PlatformGameGroup data:", value) + } + } else { + logger.Logger.Error("Unmarshal PlatformGameGroup data error:", err, string(packageBuff)) + } + } else { + logger.Logger.Error("Get PlatformGameGroup data error:", err) + } +} + +func PutPlatformGroup2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_GROUPCONFIG_PREFIX) + logger.Logger.Trace("ETCD_PutPlatformGroup2Etcd") + count := len(GameGroups) + cur := 0 + for name, p := range GameGroups { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_GROUPCONFIG_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutPlatformGroup2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutPlatformGroup2Etcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type Bullet struct { + Id int32 + Sort int32 //排序 + Platform string + NoticeTitle string + NoticeContent string + UpdateTime string + State int //0 关闭 1开启 +} //声明与world中一样的结构体 +type ApiBulletResult struct { + Tag int + Msg []Bullet +} + +var BulletMsgList = make(map[int32]*Bullet) + +func LoadBullet() { + logger.Logger.Trace("LoadBullet") + buff, err := webapi.API_GetBulletData(common.GetAppId()) + //logger.Logger.Warn("bulletin buff: ", string(buff)) + if err == nil { + info := ApiBulletResult{} + err = json.Unmarshal([]byte(buff), &info) + if err == nil { + for i := 0; i < len(info.Msg); i++ { + BulletMsgList[info.Msg[i].Id] = &info.Msg[i] + } + } else { + logger.Logger.Error("Unmarshal Bullet data error:", err, string(buff)) + } + } else { + logger.Logger.Error("Get Bullet data error:", err) + } +} + +func PutBullet2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_BULLETIN_PREFIX) + logger.Logger.Trace("ETCD_PutBullet2Etcd") + count := len(BulletMsgList) + cur := 0 + for name, p := range BulletMsgList { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_BULLETIN_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutCustomer2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutCustomer2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +func CompBullet() { + logger.Logger.Trace("LoadBullet") + buff, err := webapi.API_GetBulletData(common.GetAppId()) + //logger.Logger.Warn("bulletin buff: ", string(buff)) + if err == nil { + info := ApiBulletResult{} + err = json.Unmarshal([]byte(buff), &info) + if err == nil { + for i := 0; i < len(info.Msg); i++ { + BulletMsgList[info.Msg[i].Id] = &info.Msg[i] + } + + res, err := etcdCli.GetValueWithPrefix(etcd.ETCDKEY_BULLETIN_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var info Bullet + err = json.Unmarshal(res.Kvs[i].Value, &info) + if err == nil { + if BulletMsgList[info.Id] == nil { + etcdCli.DelValue(string(res.Kvs[i].Key)) + //logger.Logger.Errorf("etcd json pasre : ", string(res.Kvs[i].Key)) + } + } else { + logger.Logger.Errorf("etcd json pasre err: ", res.Kvs[i].Key, err) + } + + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_BULLETIN_PREFIX, err) + return + } + + } else { + logger.Logger.Error("Unmarshal Bullet data error:", err, string(buff)) + return + } + } else { + logger.Logger.Error("Get Bullet data error:", err) + return + } + +} + +type Customer struct { + Id int32 + Platform string + Weixin_account string + Qq_account string + Headurl string + Nickname string + Status int + Ext string +} //声明与world中一样的结构体 +type ApiCustomerResult struct { + Tag int + Msg []Customer +} + +var CustomerMsgList = make(map[int32]*Customer) + +func LoadCustomer() { + logger.Logger.Trace("LoadCustomer") + buff, err := webapi.API_GetCustomerData(common.GetAppId()) + //logger.Logger.Trace("customer buff:", string(buff)) + if err == nil { + c_info := ApiCustomerResult{} + err = json.Unmarshal([]byte(buff), &c_info) + if err == nil { + for i := 0; i < len(c_info.Msg); i++ { + CustomerMsgList[c_info.Msg[i].Id] = &c_info.Msg[i] + } + } else { + logger.Logger.Trace("CustomerMgr is Unmarshal error.", err, string(buff)) + } + } else { + logger.Logger.Trace("API_GetCustomerData is error. ", err) + } +} + +func PutCustomer2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_AGENTCUSTOMER_PREFIX) + logger.Logger.Trace("ETCD_PutCustomer2Etcd") + count := len(CustomerMsgList) + cur := 0 + for name, p := range CustomerMsgList { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_AGENTCUSTOMER_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutCustomer2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutCustomer2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type BlackInfo struct { + Id int32 + BlackType int //1.游戏2.兑换3.充值 + MemberId int32 // + AlipayAccount string + AlipayName string + Bankcard string + Ip string //support like "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291. + Platform string + PackageTag string + ipNet *net.IPNet +} +type BlackInfoApi struct { + Id int32 + Space int32 + Snid int32 + Alipay_account string + Alipay_name string + Bankcard string + Ip string + Platform string + PackageTag string + Explain string + Creator int32 + Create_time string + Update_time string +} //声明与world一样结构体 +var BlackList = make(map[int32]BlackInfoApi) + +func LoadBlackList() { + type ApiResult struct { + Tag int + Msg []BlackInfoApi + } + logger.Logger.Trace("LoadBlackList") + page := 1 + for { + logger.Logger.Trace("LoadBlackList req page:%v", page) + var buff []byte + var err error + if common.CustomConfig.GetBool("UseBlacklistBindPlayerinfo") { + buff, err = webapi.API_GetCommonBlackData(common.GetAppId(), page) + } else { + buff, err = webapi.API_GetBlackData(common.GetAppId(), page) + } + + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, value := range ar.Msg { + BlackList[value.Id] = value + } + if len(ar.Msg) < 5000 { + break + } else { + page++ + } + } else { + logger.Logger.Error("Unmarshal black data error:", err, string(buff)) + break + } + } else { + logger.Logger.Error("Init black list failed.", err, string(buff)) + break + } + } +} + +func PutBlackList2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_BLACKLIST_PREFIX) + logger.Logger.Trace("ETCD_PutBlackList2Etcd") + count := len(BlackList) + cur := 0 + for name, p := range BlackList { + data, err := json.Marshal(&p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_BLACKLIST_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutBlackList2Etcd err:%v data:%v", err, string(data)) + } else { + //logger.Logger.Tracef("PutBlackList2Etcd succes:%v data:%v", err, string(data)) + } + //time.Sleep(time.Millisecond * 200) + } else { + logger.Logger.Errorf("PutBlackList2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type SignConfig struct { + ConfigTickets int64 //tickets + Platform string + StartAct int32 //活动是否开启 + StartTickets int64 //tickets + Reward [][]int64 // [vipLevel][dayIndex] +} //声明与world中一样的结构体 +var SignConfigs = make(map[string]SignConfig) + +func LoadActSign() { + type SignConfigApi struct { + Params []SignConfig + } + type ApiResult struct { + Tag int + Msg SignConfigApi + } + logger.Logger.Trace("LoadActSign") + buff, err := webapi.API_GetPlatformSignConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, value := range ar.Msg.Params { + if value.StartAct > 0 { + SignConfigs[value.Platform] = value + //logger.Logger.Trace("value:", value) + } + } + } else { + logger.Logger.Error("Unmarshal ActSign data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("Init ActSign list failed.") + } + +} + +func PutActSign2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_SIGNIN_PREFIX) + logger.Logger.Trace("ETCD_PutActSign2Etcd") + count := len(SignConfigs) + cur := 0 + for name, p := range SignConfigs { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_SIGNIN_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutActSign2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutActSign2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type GoldTaskConfig struct { + TaskId int32 // + TaskDsc string + StartAct int32 //活动是否开启, 0:关闭 1:开启 + GameId int32 // + TaskSort int32 //0,游戏对局; 1,打死某类鱼 + TaskParam []int64 //活动参数 0;[count]; 1[fishid, count] + LimitTimes int32 //限制次数 + Reward int64 //奖励 + DurationSort int32 //持续时间 0:永久有效, 1:每天清除; 2,... +} + +type APIGoldTaskConfigs struct { + Datas []GoldTaskConfig //[taskid] + Platform string //平台 + ConfigVer int64 //时间戳 + StartAct int32 //开关 +} //声明与world中一样的结构体 + +var ActGoldTaskMap = make(map[string]APIGoldTaskConfigs) + +func LoadActGoldTask() { + type ApiResult struct { + Tag int + Msg []APIGoldTaskConfigs + } + logger.Logger.Trace("LoadActGoldTask") + buff, err := webapi.API_GetGoldTaskConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + //logger.Logger.Trace("API_GetGoldTaskConfig response:", string(buff)) + for _, plateformConfig := range ar.Msg { + //logger.Logger.Trace("Platform:", plateformConfig.Platform, " ConfigVer:", plateformConfig.ConfigVer) + ActGoldTaskMap[plateformConfig.Platform] = plateformConfig + } + } else { + logger.Logger.Error("Unmarshal ActGoldTaskMgr data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("Init ActGoldTaskMgr list failed.") + } +} + +func PutActGoldTask2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_GOLDTASK_PREFIX) + logger.Logger.Trace("ETCD_PutActGoldTask2Etcd") + count := len(ActGoldTaskMap) + cur := 0 + for name, p := range ActGoldTaskMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_GOLDTASK_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutActGoldTask2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutActGoldTask2Etcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type GoldComeConfig struct { + TaskId int32 // + TaskDsc string + StartAct int32 //活动是否开启, 0:关闭 1:开启 + GameId int32 // + WinType int32 //1,输赢分数; 2下注流水 + MinTimes int64 //责任局数 + BeginHour int32 + BeginMinute int32 + EndHour int32 + EndMinute int32 + Reward []int64 //奖励1~20名奖励 +} //声明与world中一样的结构体 +type RobotType int32 + +const ( + ROBOTTYPE_ROBOT_CLOSE RobotType = iota //0:不使用机器人 + ROBOTTYPE_ROBOT_OPEN = 1 //1:使用机器人 +) + +type APIGoldComeConfig struct { + Datas []GoldComeConfig //[taskid] + Platform string //平台 + ConfigVer int64 //版本号 + StartAct int32 //开启标记 + RobotType RobotType //机器人类型 +} //声明与world中一样的结构体 + +var ActGoldComeMap = make(map[string]APIGoldComeConfig) + +func LoadActGoldCome() { + type ApiResult struct { + Tag int + Msg []APIGoldComeConfig + } + logger.Logger.Trace("LoadActGoldCome") + buff, err := webapi.API_GetGoldComeConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, plateformConfig := range ar.Msg { + ActGoldComeMap[plateformConfig.Platform] = plateformConfig + } + } else { + logger.Logger.Error("Unmarshal ActGoldComeMgr data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("Init ActGoldComeMgr list failed.") + } + +} + +func PutActGoldCome2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_GOLDCOME_PREFIX) + logger.Logger.Trace("ETCD_PutActGoldCome2Etcd") + count := len(ActGoldComeMap) + cur := 0 + for name, p := range ActGoldComeMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutActGoldCome2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutActGoldCome2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type OnlineRewardConfig struct { + Id int32 + OnlineDuration int32 + RewardAmount int64 +} + +type PlatformOnlineRewardConfig struct { + Platform string + StartAct int32 //活动是否开启 + StartTickets int64 //tickets + Version int32 // 活动版本 + PayNeed int32 //充值需要的 + Reward []OnlineRewardConfig `json:"Data,omitempty"` +} //声明与world中一样的结构体 + +type ApiOnlineRewardDatas struct { + Params []PlatformOnlineRewardConfig +} + +var ActOnlineRewardMap = make(map[string]PlatformOnlineRewardConfig) + +type LuckyTurntableConfig struct { + TurntableType int32 `json:"Type"` // 转盘类型 + PoolInitAmount int64 `json:"PoolCoin"` // 水池初始额度 + ScoreCost int64 // 积分消耗 + Reward []int64 // 奖励:依次为小奖1、小奖2、中奖1、中奖2、大奖1、大奖2、特大奖1、特大奖2 + PoolModify int64 `json:"PoolChange"` // 水池变化额度 +} + +type PlatformLuckyTurntableConfig struct { + Platform string // 平台名 + StartAct int32 // 活动是否开启 + StartTickets int64 // tickets + Version int32 // 活动版本 + Turntables []*LuckyTurntableConfig `json:"Data"` + + MapTurntables map[int32]*LuckyTurntableConfig +} //声明与world一样的结构体 + +type ApiResultDatas struct { + Params []PlatformLuckyTurntableConfig +} + +var LuckyTurntableMap = make(map[string]PlatformLuckyTurntableConfig) + +// 平台幸运转盘活动 +func LoadLuckyTurntableConfig() { + type ApiResult struct { + Tag int + Msg ApiResultDatas + } + logger.Logger.Trace("LoadLuckyTurntableConfig") + buff, err := webapi.API_GetLuckyTurntableConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, value := range ar.Msg.Params { + //if value.StartAct > 0 { + LuckyTurntableMap[value.Platform] = value + //logger.Logger.Trace("value:", value) + //} + } + } else { + logger.Logger.Error("LuckyTurntableConfig.Init Unmarshal LuckyTurntableConfig data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("LuckyTurntableConfig.Init webapi.API_GetLuckyTurntableConfig failed.") + } +} + +func PutLuckyTurntableConfig2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX) + logger.Logger.Trace("ETCD_PutLuckyTurntableConfig2Etcd") + count := len(LuckyTurntableMap) + cur := 0 + for name, p := range LuckyTurntableMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutLuckyTurntableConfig2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutLuckyTurntableConfig2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} +func LoadActOnlineReward() { + type ApiResult struct { + Tag int + Msg ApiOnlineRewardDatas + } + logger.Logger.Trace("LoadActOnlineReward") + buff, err := webapi.API_GetOnlineRewardConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, value := range ar.Msg.Params { + //if value.StartAct > 0 { + ActOnlineRewardMap[value.Platform] = value + //logger.Logger.Trace("value:", value) + //} + } + } else { + logger.Logger.Error("ActOnlineRewardMgr.Init Unmarshal ActOnlineReward data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("ActOnlineRewardMgr.Init webapi.API_GetOnlineRewardConfig failed.") + } +} + +func PutActOnlineReward2Etcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX) + logger.Logger.Trace("ETCD_PutActOnlineReward2Etcd") + count := len(ActOnlineRewardMap) + cur := 0 + for name, p := range ActOnlineRewardMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutActOnlineReward2Etcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutActOnlineReward2Etcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type YebConfig struct { + Platform string // 平台名 + StartAct int32 // 活动是否开启 + StartTickets int64 // tickets + Version int32 // 活动版本 + + Rate int64 // 配置利率(每万元收益,单位:分) +} //与world中一样 + +var YebConfigMap = make(map[string]YebConfig) + +// 平台余额宝活动 +func LoadYebConfig() { + type ApiResultDatas struct { + Params []YebConfig + } + + type ApiResult struct { + Tag int + Msg ApiResultDatas + } + logger.Logger.Trace("LoadYebConfig") + buff, err := webapi.API_GetYebConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, value := range ar.Msg.Params { + //if value.StartAct > 0 { + YebConfigMap[value.Platform] = value + //logger.Logger.Trace("value:", value) + //} + } + } else { + logger.Logger.Error("API_GetYebConfig.Init Unmarshal API_GetYebConfig data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("API_GetYebConfig.Init webapi.API_GetYebConfig failed.") + } +} + +func PutYeb2ConfigEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_YEB_PREFIX) + logger.Logger.Trace("ETCD_PutYeb2ConfigEtcd") + count := len(YebConfigMap) + cur := 0 + for name, p := range YebConfigMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_YEB_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutYeb2ConfigEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutYeb2ConfigEtcd err:%v data:%v", err, string(data)) + } + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type RebateGameCfg struct { + BaseCoin [3]int32 //返利基准 + RebateRate [3]int32 //返利比率 + GameId int32 //游戏id + GameMode int32 //游戏类型 + MaxRebateCoin int64 //最高返利 + +} +type RebateGameThirdCfg struct { + BaseCoin [3]int32 //返利基准 + RebateRate [3]int32 //返利比率 + ThirdName string //第三方key + ThirdShowName string //前端显示name + MaxRebateCoin int64 //最高返利 + ThirdId string //三方游戏id +} +type RebateInfo struct { + Platform string //平台名称 + RebateSwitch bool //返利开关 + RebateManState int //返利是开启个人返利 0 关闭 1 开启 + ReceiveMode int //领取方式 0实时领取 1次日领取 + NotGiveOverdue int //0不过期 1过期不给 2过期邮件给 + RebateGameCfg []*RebateGameCfg //key为"gameid"+"gamemode" + RebateGameThirdCfg []*RebateGameThirdCfg //第三方key + Version int //活动版本 后台控制 +} +type RebateTask struct { + Platform string //平台名称 + RebateSwitch bool //返利开关 + RebateManState int //返利是开启个人返利 0 关闭 1 开启 + ReceiveMode int //领取方式 0实时领取 1次日领取 + NotGiveOverdue int //0不过期 1过期不给 2过期邮件给 + RebateGameCfg map[string]*RebateGameCfg //key为"gameid"+"gamemode" + RebateGameThirdCfg map[string]*RebateGameThirdCfg //第三方key + Version int //活动版本 后台控制 +} + +var RebateTaskMap = make(map[string]*RebateTask) + +func LoadRebateData() { + logger.Logger.Trace("LoadRebateData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []RebateInfo + } + rebateBuff, err := webapi.API_GetGameRebateConfig(common.GetAppId()) + if err == nil { + logger.Logger.Trace("API_GetGameRebateConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + RebateTaskMap[v.Platform] = &RebateTask{ + Platform: v.Platform, + RebateSwitch: v.RebateSwitch, + ReceiveMode: v.ReceiveMode, + RebateManState: v.RebateManState, + NotGiveOverdue: v.NotGiveOverdue, + Version: v.Version, + } + RebateTaskMap[v.Platform].RebateGameCfg = make(map[string]*RebateGameCfg) + RebateTaskMap[v.Platform].RebateGameThirdCfg = make(map[string]*RebateGameThirdCfg) + for _, cfg := range v.RebateGameCfg { + for _, dfm := range srvdata.PBDB_GameFreeMgr.Datas.Arr { + + if dfm.GetGameId() == cfg.GameId && dfm.GetGameMode() == cfg.GameMode { + RebateTaskMap[v.Platform].RebateGameCfg[dfm.GetGameDif()] = cfg + break + } + } + } + + for _, cfg := range v.RebateGameThirdCfg { + RebateTaskMap[v.Platform].RebateGameThirdCfg[cfg.ThirdId] = cfg + } + } + } else { + logger.Logger.Error("Unmarshal RebateTask data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get RebateTask data error:", err) + } +} +func PutRebateTaskEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_CONFIG_REBATE) + logger.Logger.Trace("ETCD_PutRebateTaskEtcd") + count := len(RebateTaskMap) + cur := 0 + for name, p := range RebateTaskMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_CONFIG_REBATE, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutRebateTaskEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutRebateTaskEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type PromoterConfig struct { + PromoterID string //代理ID + Platform string //平台 + PromoterType int32 //代理类型 0 全民 1无限代理 2 渠道 3 推广员 + UpgradeAccountGiveCoin int32 //升级账号奖励金币 + NewAccountGiveCoin int32 //新账号奖励金币 + ExchangeTax int32 //兑换税收(万分比) + ExchangeForceTax int32 //强制兑换税收 + ExchangeFlow int32 //兑换流水比例 + ExchangeGiveFlow int32 //赠送兑换流水比例 + ExchangeFlag int32 //兑换标记 + IsInviteRoot int32 //是否绑定全民用户 +} + +var PromoterMap = make(map[string]*PromoterConfig) + +func LoadPromoterData() { + logger.Logger.Trace("LoadPromoterData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []PromoterConfig + } + rebateBuff, err := webapi.API_GetPromoterConfig(common.GetAppId()) + if err == nil { + logger.Logger.Trace("API_GetPromoterConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + temp := v + PromoterMap[v.PromoterID+"_"+strconv.Itoa(int(v.PromoterType))] = &temp + } + } else { + logger.Logger.Error("Unmarshal Promoter data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get Promoter data error:", err) + } +} +func PutPromoterEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_PROMOTER_PREFIX) + logger.Logger.Trace("ETCD_PutPromoterEtcd") + count := len(PromoterMap) + cur := 0 + for name, p := range PromoterMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_PROMOTER_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("ETCD_PutPromoterEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("ETCD_PutPromoterEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type ActVipRewardConfig struct { + LevelCoin int32 //等级奖励金币 + DayCoin int32 //每日奖励 + WeekCoin int32 //每周奖励 + MonthCoin int32 //每月奖励 +} + +type ActVipPlateformConfig struct { + VipBonusInfo []ActVipRewardConfig //奖励信息 + Platform string //平台 + StartAct int32 //活动开启标记 0:关闭 1:开启 +} + +var ActVipMap = make(map[string]*ActVipPlateformConfig) + +func LoadActVipData() { + logger.Logger.Trace("LoadActVipData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []ActVipPlateformConfig + } + + rebateBuff, err := webapi.API_GetActVipConfig(common.GetAppId()) + if err == nil { + logger.Logger.Trace("API_GetPromoterConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + temp := v + ActVipMap[temp.Platform] = &temp + } + } else { + logger.Logger.Error("Unmarshal ActVip data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get ActVip data error:", err) + } +} +func PutActVipEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_VIP_PREFIX) + logger.Logger.Trace("ETCD_PutActVipEtcd") + count := len(ActVipMap) + cur := 0 + for name, p := range ActVipMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_VIP_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("ETCD_ActVipMapEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("ETCD_ActVipMapEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type ShareConfig struct { + Platform string // 平台id + Status int32 // 开关 + Number int64 // 分享彩金 + Times int64 // 每日领取次数 + FriendUrl string + SpaceUrl string + VipLevel int32 // vip等级下限 + Recharge int64 // 充值下限 +} + +var ActWxShareMap = make(map[string]*ShareConfig) + +func LoadActWxShareData() { + logger.Logger.Trace("LoadActWxShareData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []ShareConfig + } + + rebateBuff, err := webapi.API_GetWeiXinShareConfig(common.GetAppId()) + if err == nil { + //logger.Logger.Trace("API_GetPromoterConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + temp := v + ActWxShareMap[temp.Platform] = &temp + } + } else { + logger.Logger.Error("Unmarshal ActWxShare data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get ActWxShare data error:", err) + } +} +func PutActWxShareEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX) + logger.Logger.Trace("ETCD_PutActEtcd") + count := len(ActWxShareMap) + cur := 0 + for name, p := range ActWxShareMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("ETCD_PutActWxShareEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("ETCD_PutActWxShareEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type ActGiveConfig struct { + Tag int32 //赠与类型 + IsStop int32 //是否阻止 1阻止 0放开 + NeedFlow int32 +} + +type ActGivePlateformConfig struct { + ActInfo map[string]*ActGiveConfig //奖励信息 + Platform string //平台 +} + +var ActMap = make(map[string]*ActGivePlateformConfig) + +func LoadActData() { + logger.Logger.Trace("LoadActData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []ActGivePlateformConfig + } + + rebateBuff, err := webapi.API_GetActConfig(common.GetAppId()) + if err == nil { + //logger.Logger.Trace("API_GetPromoterConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + temp := v + ActMap[temp.Platform] = &temp + } + } else { + logger.Logger.Error("Unmarshal Act data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get Act data error:", err) + } +} +func PutAcEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_GIVE_PREFIX) + logger.Logger.Trace("ETCD_PutActEtcd") + count := len(ActMap) + cur := 0 + for name, p := range ActMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_GIVE_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("ETCD_PutActEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("ETCD_PutActEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type RandRange struct { + Low int32 + High int32 +} + +type RandCoinData struct { + Id int //红包活动编号 + Platform string + IsOpen bool + DType int //红包类型 0:全量红包 1:随机红包 2:五福红包 + StartTs int64 //开始时间 + EndTs int64 //结束时间 + CoinTs int64 //可以领奖的时间(五福红包有特殊的时间,其他的和开始时间是一样的) + Title string //红包标题 + Context string //红包文案 + Count int32 //红包数量 + TotleCoin int64 //红包总额度(写错成了totlecoin,想修改成coin,但是这个需要后端配合修改,后端改起来麻烦,所以不要纠结这个变量名) + VipSel []int //红包VIP选项 勾选的VIP等级 + ExtCoin int64 //红包额外条件 + RandCoin []RandRange //VIP随机范围(随机红包特有) + NewYear []int //五福参数(五福红包特有)邀请好友数量,好友流水,每日充值,游戏局数,个人流水 +} + +type ActConfig struct { + Platform string + IsOpen bool + ActivityArr []*RandCoinData +} + +var RandomPrizeMap = make(map[string]*ActConfig) + +func LoadRandomPrizeData() { + logger.Logger.Trace("LoadRandomPrizeData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []*ActConfig + } + + rebateBuff, err := webapi.API_GetRandCoinData(common.GetAppId()) + if err == nil { + //logger.Logger.Trace("API_GetPromoterConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + RandomPrizeMap[v.Platform] = v + } + } else { + logger.Logger.Error("Unmarshal RandomPrize data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get RandomPrize data error:", err) + } +} + +func PutRandomPrizeEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_RANDCOIN_PREFIX) + logger.Logger.Trace("ETCD_PutActEtcd") + count := len(RandomPrizeMap) + cur := 0 + for name, p := range RandomPrizeMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_RANDCOIN_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("ETCD_PutActEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("ETCD_PutActEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +type ActFPayWinConfig struct { + WinType int32 //类型 + WinRate int32 //赢取比例 +} + +type ActFPayConfig struct { + StartAct int32 //活动开启标记 0:关闭 1:开启 + ConfigVer int32 //版本 + PlayerAddMax int32 //玩家数量 //每分钟增长最大 + PlayerAddMin int32 //玩家数量 //每分钟增长最小 + StartTime int32 //开启时间 //秒 + EndTime int32 //结束时间 //秒 + FPayCoin int32 //首充金额 + FPayGiveType int32 //赠送类型 0 金额 1 比例 + FPayGiveCoin int32 //赠送金额 + NeedWinCoin int32 //需要完成赢取金额 + Remark string //备注 + WinConfig map[string]*ActFPayWinConfig //赢取比例配置 +} + +type ActFPayPlateformConfig struct { + FPayInfo *ActFPayConfig //奖励信息 + Platform string //平台 +} + +var ActFPayMap = make(map[string]*ActFPayPlateformConfig) + +func LoadActFPayData() { + logger.Logger.Trace("LoadActData") + + //获取平台返利数据 + type ApiResult struct { + Tag int + Msg []ActFPayPlateformConfig + } + + rebateBuff, err := webapi.API_GetActFPayConfig(common.GetAppId()) + if err == nil { + logger.Logger.Trace("API_GetActFPayConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + temp := v + ActFPayMap[temp.Platform] = &temp + } + } else { + logger.Logger.Error("Unmarshal ActFPay data error:", err, string(rebateBuff)) + } + } else { + logger.Logger.Error("Get ActFPay data error:", err) + } +} +func PutAcFPayEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_FPAY_PREFIX) + logger.Logger.Trace("PutAcFPayEtcd") + count := len(ActFPayMap) + cur := 0 + for name, p := range ActFPayMap { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_FPAY_PREFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +func CopyDBGameFreeField(src, dst *server.DB_GameFree) { + dst.Id = src.Id + dst.Name = src.Name + dst.Title = src.Title + dst.ShowType = src.ShowType + dst.SubShowType = src.SubShowType + dst.Flag = src.Flag + dst.GameRule = src.GameRule + dst.TestTakeCoin = src.TestTakeCoin + dst.SceneType = src.SceneType + dst.GameType = src.GameType + dst.GameId = src.GameId + dst.GameMode = src.GameMode + dst.ShowId = src.ShowId + dst.ServiceFee = src.ServiceFee + dst.Turn = src.Turn + dst.BetDec = src.BetDec + //dst.CorrectNum = src.CorrectNum + //dst.CorrectRate = src.CorrectRate + //dst.Deviation = src.Deviation + //dst.Ready = src.Ready + dst.Ai = src.Ai + dst.Jackpot = src.Jackpot + //dst.ElementsParams = src.ElementsParams + //dst.OtherElementsParams = src.OtherElementsParams + //dst.DownRiceParams = src.DownRiceParams + //dst.InitValue = src.InitValue + //dst.LowerLimit = src.LowerLimit + //dst.UpperLimit = src.UpperLimit + //dst.UpperOffsetLimit = src.UpperOffsetLimit + //dst.MaxOutValue = src.MaxOutValue + //dst.ChangeRate = src.ChangeRate + //dst.MinOutPlayerNum = src.MinOutPlayerNum + //dst.UpperLimitOfOdds = src.UpperLimitOfOdds + if len(dst.RobotNumRng) < 2 { + dst.RobotNumRng = src.RobotNumRng + } + //dst.SameIpLimit = src.SameIpLimit + //dst.BaseRate = src.BaseRate + //dst.CtroRate = src.CtroRate + //dst.HardTimeMin = src.HardTimeMin + //dst.HardTimeMax = src.HardTimeMax + //dst.NormalTimeMin = src.NormalTimeMin + //dst.NormalTimeMax = src.NormalTimeMax + //dst.EasyTimeMin = src.EasyTimeMin + //dst.EasyTimeMax = src.EasyTimeMax + //dst.EasrierTimeMin = src.EasrierTimeMin + //dst.EasrierTimeMax = src.EasrierTimeMax + dst.GameType = src.GameType + dst.GameDif = src.GameDif + dst.GameClass = src.GameClass + dst.PlatformName = src.PlatformName + if len(dst.MaxBetCoin) == 0 { + dst.MaxBetCoin = src.MaxBetCoin + } + + //预创建房间数量走后台配置 + //dst.CreateRoomNum = src.CreateRoomNum + //后台功能做上后,优先使用后台的配置,默认直接用表格种配好的 + //if !model.GameParamData.MatchTrueManUseWeb { + // dst.MatchTrueMan = src.MatchTrueMan + //} + + ////默认走本地配置 + //if dst.PlayerWaterRate == nil { + // tv := src.GetPlayerWaterRate() + // dst.PlayerWaterRate = &tv + //} + // + //if dst.BetWaterRate == nil { + // tv := src.GetBetWaterRate() + // dst.BetWaterRate = &tv + //} + if dst.Id == 10000001 { + println(dst.GetMatchTrueMan()) + } +} + +// 赛事奖励配置 +type ActMatchRewardConfig struct { + RankRange []int32 //名次区间 + Reward int32 //奖励数量 +} + +// 活动配置 +type ActTicketConfig struct { + Id int32 //配置编号 + Platform string //平台编号 + MatchId int32 //比赛编号 + StartTime int64 //开始时间 + EndTime int64 //结束时间 + Enable bool //是否开启 + AutoAgree bool //自动同意 + PlayerType int32 //玩家类型 0:受邀请的玩家 + AchievType int32 //成绩类型 0:首日最好成绩 + Rewards []*ActMatchRewardConfig //奖励配置 +} + +var ActTicketConfigData = make(map[string]*ActTicketConfig) + +func LoadActTicketConfig() { + type ActTicketConfigMsg struct { + Tag int + Msg []*ActTicketConfig + } + logger.Logger.Trace("API_GetActTicketConfigData") + buff, err := webapi.API_GetActTicketConfigData(common.GetAppId()) + if err == nil { + var data ActTicketConfigMsg + err = json.Unmarshal(buff, &data) + if err == nil && data.Tag == 0 { + for _, cfg := range data.Msg { + ActTicketConfigData[cfg.Platform] = cfg + } + } else { + logger.Logger.Error("Unmarshal ActTicketConfig config data error:", err, string(buff)) + } + } else { + logger.Logger.Error("Get ActTicketConfig config data error:", err) + } +} +func PutActTicketEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_ACT_TICKET_PROFIX) + logger.Logger.Trace("PutActTicketEtcd") + count := len(ActTicketConfigData) + cur := 0 + for name, p := range ActTicketConfigData { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_ACT_TICKET_PROFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} + +// 比赛配置 +type MatchConfig struct { + Id int32 //比赛id + Platform string //平台编号 + Ver int32 //数据版本号 + Name string //名称 + Desc string //比赛说明 + Turn int32 //显示排序 + Enable bool //开启标记 false:未开启 true:开启 + StartMatchType int32 //开赛类型 1:流水赛(人满即开) 2:定点赛(固定时间开赛) + GameFreeId int32 //游戏id 对应gamefreeid + Onumber int32 //最低开赛人数 + Gnumber int32 //分组人数 + NumberLimit int32 //最高报名数人数 + Rot bool //是否使用机器人 false:不使用 true:使用 + RobCnt []int32 //邀请机器人数量 5,8 => 5~8 + SameIpLimit bool //同ip限制 + GuestLimit bool //正式玩家限制 false:游客也可报名 true:只能正式玩家报名 + PrizeWashLimit bool //奖金是否有打码要求 false:无 true:需要打码 + VipLimit int32 //vip限制 二进制模式 + PrizeLimit int32 //单日奖金发放限制 0:不限制 >0:发完停赛 + ShowPrize int32 //给客户端展示的奖励限制 + RobOccupyPrize bool //机器人是否占用奖励 + SignupCost MatchSignupConfig //报名费 + StartTime []MatchDateTimeConfig //开赛时间 + MatchProcess []*MatchProcessConfig //赛程阶段 + Reward []*MatchRewardConfig //奖励配置 +} + +// 赛程配置 +type MatchProcessConfig struct { + Name string //赛程名称 + Desc string //赛程描述 + InitGrade int32 //初始积分 + GradeDiscount int32 //积分折算 0.使用初始积分 1:使用上个赛程的积分 2:上一轮积分进行折算 + MPPType int32 //赛段赛制类型 1:定局积分 2:打立出局 3:瑞士移位 4:定时积分 + NumOfGames int32 //局数 + BaseScore int32 //初始底分 + Filter int32 //晋级人数 + Params []int32 //具体规则参数 打立出局:X,Y,Z 每X秒增加Y底分,低于Z分淘汰 + PhaseFlag int32 //0:海选 1:预赛 2:淘汰赛 3:半决赛 4.决赛 +} + +// 比赛奖励配置 +type MatchRewardConfig struct { + RankRange []int32 //名次区间 + Reward int32 //奖励金币 +} + +// 比赛报名配置 +type MatchSignupConfig struct { + SupportFreeTimes bool //是否支持免费 + FreeTimes int32 //免费次数 + SupportTicket bool //是否支持入场券 + TicketCnt int32 //入场券数量 + SupportCoin bool //是否支持金币报名 + Coin int32 //所需金币 +} + +// 比赛时间配置 +type MatchDateTimeConfig struct { + StartDate int32 //开始日期 如:20200801 0表示不限开始日期 + EndDate int32 //结束日期 如:20200808 0表示不限结束日期 + StartMini int32 //开始时间 如:1000 10点开赛 0表示不限开始时间 + EndMini int32 //结束时间 如:1200 12点结束 0表示不限结束时间 +} + +var MatchConfigData = make(map[int32]*MatchConfig) + +func LoadMatchConfig() { + type ActTicketConfigMsg struct { + Tag int + Msg []*MatchConfig + } + logger.Logger.Trace("API_GetMatchConfigData") + buff, err := webapi.API_GetMatchConfigData(common.GetAppId()) + if err == nil { + var data ActTicketConfigMsg + err = json.Unmarshal(buff, &data) + if err == nil && data.Tag == 0 { + for _, cfg := range data.Msg { + MatchConfigData[cfg.Id] = cfg + } + } else { + logger.Logger.Error("Unmarshal ActTicketConfig config data error:", err, string(buff)) + } + } else { + logger.Logger.Error("Get ActTicketConfig config data error:", err) + } +} +func PutMatchConfigEtcd() { + etcdCli.DelValueWithPrefix(etcd.ETCDKEY_MATCH_PROFIX) + logger.Logger.Trace("PutMatchConfigEtcd") + count := len(MatchConfigData) + cur := 0 + for name, p := range MatchConfigData { + data, err := json.Marshal(p) + if err == nil { + _, err := etcdCli.PutValue(fmt.Sprintf("%v%v", etcd.ETCDKEY_MATCH_PROFIX, name), string(data)) + if err != nil { + logger.Logger.Tracef("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + } else { + logger.Logger.Errorf("PutAcFPayEtcd err:%v data:%v", err, string(data)) + } + + cur++ + fmt.Printf("\r") + fmt.Printf("%d%%", int(float64(cur)/float64(count)*100)) + } +} diff --git a/tools/etcdtool/mainmgo b/tools/etcdtool/mainmgo new file mode 100644 index 0000000..2b25b8f --- /dev/null +++ b/tools/etcdtool/mainmgo @@ -0,0 +1,33 @@ +package main + +import ( + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "fmt" + "time" +) +var playerinfo *mgo.Collection +func main() { + session, err := mgo.Dial("mongodb://hjgame:123456@192.168.0.242:27017/") + if err != nil { + fmt.Println(err) + return + } + db:=session.DB("jxjy_game_hj") + playerinfo=db.C("user_playerinfo") + + go find(1420) + go find(1421) + time.Sleep(time.Second*5) +} +func find(snid int32) { + fmt.Printf("head:%d;start at:%d \n",snid,time.Now().UnixNano()) + var result interface{} + err := playerinfo.Find(bson.M{"head": snid}).One(&result) + if err != nil { + fmt.Println(err) + }else { + fmt.Println(result) + } + fmt.Printf("head:%d;end at:%d \n",snid,time.Now().UnixNano()) +} \ No newline at end of file diff --git a/tools/etcdtool/使用工具前请先读我.txt b/tools/etcdtool/使用工具前请先读我.txt new file mode 100644 index 0000000..2aea2fc --- /dev/null +++ b/tools/etcdtool/使用工具前请先读我.txt @@ -0,0 +1,3 @@ +1��ִ��֮ǰ������config.json��ȷ��API��ַ��etcd������Ϣ�����û��������룻 +2��������гɹ��������ɵ�error.log��־��Ϣ��error_when_succes.logһ���������һ���������г��ִ�����ʱ���ʹ��etcd��ȡ���õĻ��ϲ��������η��� +3��ע��etcdtool����ļ������ڵ�·����config.json�������õ�·�����������д��� \ No newline at end of file diff --git a/tools/httpProto3/config.json b/tools/httpProto3/config.json new file mode 100644 index 0000000..f736cfc --- /dev/null +++ b/tools/httpProto3/config.json @@ -0,0 +1,53 @@ +{ + "netlib": { + }, + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 31002 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + + "core": { + "MaxProcs": 4 + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal":{ + "SupportSignal": true + }, + "admin": { + "SupportAdmin": true, + "AdminHttpPort": 80 + }, + "profile": {}, + "common": {}, + "costum": {} +} \ No newline at end of file diff --git a/tools/httpProto3/httpProto3.go b/tools/httpProto3/httpProto3.go new file mode 100644 index 0000000..dc8ca5b --- /dev/null +++ b/tools/httpProto3/httpProto3.go @@ -0,0 +1,429 @@ +package main + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/qpapi" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/admin" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "net/http" + "time" +) + +//const TestIp = "http://192.168.10.240:9899" + +const TestIp = "http://127.0.0.1:9899" + +var ApiKey = []string{ + "/api/mongo.games.com/game/AddCoinByIdAndPT", + "/api/mongo.games.com/game/AddCoinById", + "/api/Cache/ListRoom", + "/api/Cache/DestroyRoom", + "/api/Ctrl/ListServerStates", + "/api/Ctrl/ServerStateSwitch", + "/api/Ctrl/SrvCtrlClose", + "/api/mongo.games.com/game/CreateShortMessage", + "/api/mongo.games.com/game/DeleteShortMessage", + "/api/mongo.games.com/game/SinglePlayerAdjust", + "/api/Message/QueryHorseRaceLampList", + "/api/mongo.games.com/game/QueryGamePoolByGameId", + "/api/Report/OnlineReportTotal", + "/api/Message/EditHorseRaceLamp", + "/api/Message/CreateHorseRaceLamp", + "/api/Message/GetHorseRaceLampById", + "/api/Report/QueryOnlineReportList", + "/api/Member/QPAPIRegisterOrLogin", + "/api/Member/QPGetMemberGoldById", + "/api/Member/QPGetGameHistory", + "/api/mongo.games.com/game/QPAPIAddSubCoinById", + "/api/Player/WhiteBlackControl", + "/api/Player/BlackBySnId", +} + +func (clm *APIHttpData) Init() { + clm.RouteMap["/api/Player/BlackBySnId"] = APIData{ + M: &webapi.ASBlackBySnId{ + Platform: "1", + SnId: 90887600, + BlacklistType: 1, + }, + Pack: &webapi.SABlackBySnId{}, + } + clm.RouteMap["/api/Player/WhiteBlackControl"] = APIData{ + M: &webapi.ASWhiteBlackControl{ + Platform: "1", + SnId: 29274000, + WBLevel: 10, + WBCoinLimit: 0, + ResetTotalCoin: 0, + MaxNum: 0, + SnIds: []int32{}, + State: 3, + }, + Pack: &webapi.SAWhiteBlackControl{}, + } + clm.RouteMap["/api/mongo.games.com/game/AddCoinByIdAndPT"] = APIData{ + M: &webapi.ASAddCoinByIdAndPT{ + Platform: "1", + ID: 24953700, + Gold: 10000, + Oper: "test", + Desc: "", + BillNo: int64(rand.Int31n(100000)), + LogType: 1, + }, + Pack: &webapi.SAAddCoinByIdAndPT{}, + } + + clm.RouteMap["/api/Message/GetHorseRaceLampById"] = APIData{ + M: &webapi.ASGetHorseRaceLampById{ + Platform: "1", + NoticeId: "614d2d43e1382378b76be9a5", + }, + Pack: &webapi.SAGetHorseRaceLampById{}, + } + clm.RouteMap["/api/Message/CreateHorseRaceLamp"] = APIData{ + M: &webapi.ASCreateHorseRaceLamp{ + Platform: "1", + Title: "", + Content: "dsfsdfff", + Footer: "", + Count: 1, + State: 1, + StartTime: 1, + Priority: 1, + MsgType: 1, + StandSec: 1, + Target: []int32{}, + }, + Pack: &webapi.SACreateHorseRaceLamp{}, + } + clm.RouteMap["/api/Message/EditHorseRaceLamp"] = APIData{ + M: &webapi.ASEditHorseRaceLamp{ + HorseRaceLamp: &webapi.HorseRaceLamp{ + Id: "614d2f500d96096240c27c70", + Platform: "1", + Title: "", + Content: "dsfsdfff1111", + Footer: "", + Count: 1, + State: 1, + StartTime: 1, + Priority: 1, + MsgType: 1, + StandSec: 1, + Target: []int32{}, + }, + }, + Pack: &webapi.SAEditHorseRaceLamp{}, + } + clm.RouteMap["/api/mongo.games.com/game/QueryGamePoolByGameId"] = APIData{ + M: &webapi.ASQueryGamePoolByGameId{ + GameId: 306, + GameMode: 0, + Platform: "1", + GroupId: 0, + }, + Pack: &webapi.SAQueryGamePoolByGameId{}, + } + clm.RouteMap["/api/mongo.games.com/game/SinglePlayerAdjust"] = APIData{ + M: &webapi.ASSinglePlayerAdjust{ + Opration: 3, + PlayerSingleAdjust: &webapi.PlayerSingleAdjust{ + Platform: "1", + SnId: 35153500, + GameFreeId: 6040001, + GameId: 604, + TotalTime: 10, + }, + }, + Pack: &webapi.SASinglePlayerAdjust{}, + } + clm.RouteMap["/api/Ctrl/SrvCtrlClose"] = APIData{ + M: &webapi.ASSrvCtrlClose{ + SrvType: 0, + }, + Pack: &webapi.SASrvCtrlClose{}, + } + clm.RouteMap["/api/Ctrl/ServerStateSwitch"] = APIData{ + M: &webapi.ASServerStateSwitch{ + SrvId: 777, + SrvType: 7, + }, + Pack: &webapi.SAServerStateSwitch{}, + } + clm.RouteMap["/api/Ctrl/ListServerStates"] = APIData{ + M: &webapi.ASListServerStates{}, + Pack: &webapi.SAListServerStates{}, + } + clm.RouteMap["/api/mongo.games.com/game/AddCoinById"] = APIData{ + M: &webapi.ASAddCoinById{ + Platform: "", + ID: 13931000, + Gold: 1000000, + Oper: "test", + Desc: "", + BillNo: int64(rand.Int31n(10000)), + LogType: 0, + }, + Pack: &webapi.SAAddCoinById{}, + } + clm.RouteMap["/api/Cache/ListRoom"] = APIData{ + M: &webapi.ASListRoom{ + PageNo: 1, + PageSize: 50, + RoomType: -1, + }, + Pack: &webapi.SAListRoom{}, + } + clm.RouteMap["/api/Cache/DestroyRoom"] = APIData{ + M: &webapi.ASDestroyRoom{ + Platform: "1", + SceneIds: []int32{1000000002}, + DestroyType: 0, + }, + Pack: &webapi.SADestroyRoom{}, + } + clm.RouteMap["/api/mongo.games.com/game/CreateShortMessage"] = APIData{ + M: &webapi.ASCreateShortMessage{ + Platform: "1", + SrcSnid: 35153500, + DestSnid: 35153500, + NoticeTitle: "221", + NoticeContent: "qwqewqe", + }, + Pack: &webapi.SACreateShortMessage{}, + } + clm.RouteMap["/api/mongo.games.com/game/DeleteShortMessage"] = APIData{ + M: &webapi.ASDeleteShortMessage{ + Platform: "1", + Id: "6143f8b30d9609caec249b1a", + }, + Pack: &webapi.SADeleteShortMessage{}, + } + clm.RouteMap["/api/Message/QueryHorseRaceLampList"] = APIData{ + M: &webapi.ASQueryHorseRaceLampList{ + Platform: "1", + PageNo: 1, + PageSize: 20, + MsgType: 0, + }, + Pack: &webapi.SAQueryHorseRaceLampList{}, + } + clm.RouteMap["/api/Report/OnlineReportTotal"] = APIData{ + M: &webapi.ASOnlineReportTotal{ + Platform: "1", + }, + Pack: &webapi.SAOnlineReportTotal{}, + } + clm.RouteMap["/api/Report/QueryOnlineReportList"] = APIData{ + M: &webapi.ASQueryOnlineReportList{ + Platform: "1", + PageNo: 1, + PageSize: 20, + }, + Pack: &webapi.SAQueryOnlineReportList{}, + } + + ml := &qpapi.ASLogin{ + MerchantTag: "1", + UserName: "abcd", + Ts: time.Now().Unix(), + Sign: "", + } + + rawl := fmt.Sprintf("%v%v%v%v", ml.GetMerchantTag(), ml.GetUserName(), "", ml.GetTs()) + hl := md5.New() + io.WriteString(hl, rawl) + newsignl := hex.EncodeToString(hl.Sum(nil)) + ml.Sign = newsignl + + //创建用户 + clm.RouteMap["/api/Member/QPAPIRegisterOrLogin"] = APIData{ + M: ml, + Pack: &qpapi.SALogin{}, + } + //clm.RouteMap["/api/mongo.games.com/game/CrashVerifier"] = APIData{ + // M: &qpapi.ASCrachHash{ + // Hash:"583f8e896a5a333e2eb532c31adeffda430d7121e1d4c44914972b5070f7881c", + // Wheel:33, + // }, + // Pack: &qpapi.SACrachHash{}, + //} + + mu := &qpapi.ASMemberGold{ + Username: "abcd", + MerchantTag: "1", + Ts: time.Now().Unix(), + Sign: "", + } + + rawu := fmt.Sprintf("%v%v%v%v", mu.GetUsername(), mu.GetMerchantTag(), "", mu.GetTs()) + hu := md5.New() + io.WriteString(hu, rawu) + newsignu := hex.EncodeToString(hu.Sum(nil)) + mu.Sign = newsignu + + //获取用户金币 + clm.RouteMap["/api/Member/QPGetMemberGoldById"] = APIData{ + M: mu, + Pack: &qpapi.SAMemberGold{}, + } + + ma := &qpapi.ASAddCoinById{ + Username: "abcd", + Gold: 10000, + BillNo: time.Now().Unix(), + MerchantTag: "1", + Ts: time.Now().Unix(), + Sign: "", + } + + rawa := fmt.Sprintf("%v%v%v%v%v%v", ma.GetUsername(), ma.GetGold(), ma.GetBillNo(), ma.GetMerchantTag(), "", ma.GetTs()) + ha := md5.New() + io.WriteString(ha, rawa) + newsigna := hex.EncodeToString(ha.Sum(nil)) + ma.Sign = newsigna + + //加减币 + clm.RouteMap["/api/mongo.games.com/game/QPAPIAddSubCoinById"] = APIData{ + M: ma, + Pack: &qpapi.SAAddCoinById{}, + } + + m := &qpapi.ASPlayerHistory{ + //Username:"111111", + MerchantTag: "1", + GameHistoryModel: 1, + StartTime: 1654587626, + EndTime: 1655809512, + PageNo: 2, + PageSize: 50, + Ts: time.Now().Unix(), + Sign: "", + } + + raw := fmt.Sprintf("%v%v%v%v%v%v%v%v%v", m.GetUsername(), m.GetMerchantTag(), m.GetGameHistoryModel(), + m.GetStartTime(), m.GetEndTime(), m.GetPageNo(), m.GetPageSize(), "", m.GetTs()) + h := md5.New() + io.WriteString(h, raw) + newsign := hex.EncodeToString(h.Sum(nil)) + m.Sign = newsign + clm.RouteMap["/api/Member/QPGetGameHistory"] = APIData{ + M: m, + Pack: &qpapi.SAPlayerHistory{}, + } +} + +func init() { + for _, key := range ApiKey { + admin.MyAdminApp.Route(key, WorldSrvApi) + } + module.RegisteModule(APIHttpSington, time.Second, 1) +} + +var APIHttpSington = &APIHttpData{ + RouteMap: make(map[string]APIData), +} + +type APIData struct { + M proto.Message + Pack proto.Message +} +type APIHttpData struct { + RouteMap map[string]APIData +} + +func (clm *APIHttpData) ModuleName() string { + return "APIHttp" +} +func (clm *APIHttpData) Update() { +} + +func (clm *APIHttpData) Shutdown() { + module.UnregisteModule(clm) +} +func WorldSrvApi(rw http.ResponseWriter, req *http.Request) { + Path := req.URL.Path + Method := req.Method + fmt.Println(Path, Method) + if Method == "POST" { + data, err := io.ReadAll(req.Body) + if err != nil { + logger.Logger.Info("Body err.", err) + WebApiResponseByte(rw, nil) + return + } + fmt.Println(data) + } else { + fmt.Println("path......", Path) + if apiData, ok := APIHttpSington.RouteMap[Path]; ok { + WebApiResponseByte(rw, SendData(Path, apiData.M, apiData.Pack)) + } else { + WebApiResponseByte(rw, nil) + } + } + return +} +func WebApiResponseByte(rw http.ResponseWriter, data []byte) bool { + dataLen := len(data) + rw.Header().Set("Content-Length", fmt.Sprintf("%v", dataLen)) + rw.WriteHeader(http.StatusOK) + pos := 0 + for pos < dataLen { + writeLen, err := rw.Write(data[pos:]) + if err != nil { + logger.Logger.Info("webApiResponse SendData error:", err, " data=", string(data[:]), " pos=", pos, " writelen=", writeLen, " dataLen=", dataLen) + return false + } + pos += writeLen + } + return true +} + +func SendData(url string, m, pack proto.Message) []byte { + a, err := proto.Marshal(m) + startTime := time.Now().UnixNano() + args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, url, string(a), startTime) + h := md5.New() + io.WriteString(h, args) + realSign := hex.EncodeToString(h.Sum(nil)) + url = fmt.Sprintf("%v?nano=%v&sign=%v", TestIp+url, startTime, realSign) + + new_str := bytes.NewBuffer(a) + req, err := http.NewRequest("POST", url, new_str) + // req.Header.Set("X-Custom-Header", "myvalue") + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + fmt.Println("status", resp.Status) + //fmt.Println("response:", resp.Header) + body, _ := io.ReadAll(resp.Body) + //fmt.Println("response Body:", string(body)) + proto.Unmarshal(body, pack) + fmt.Println("=============", pack) + type Api struct { + Info string + } + info, _ := json.Marshal(pack) + api := Api{ + Info: string(info), + } + b, _ := json.Marshal(api) + return b +} diff --git a/tools/httpProto3/logger.xml b/tools/httpProto3/logger.xml new file mode 100644 index 0000000..4ecfab8 --- /dev/null +++ b/tools/httpProto3/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/httpProto3/main.go b/tools/httpProto3/main.go new file mode 100644 index 0000000..afabf18 --- /dev/null +++ b/tools/httpProto3/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/module" + "time" +) + +func main() { + rand.Seed(time.Now().Unix()) + core.RegisterConfigEncryptor(common.ConfigFE) + defer core.ClosePackages() + core.LoadPackages("config.json") + + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/tools/json2binary/config.json b/tools/json2binary/config.json new file mode 100644 index 0000000..decdace --- /dev/null +++ b/tools/json2binary/config.json @@ -0,0 +1,5 @@ +{ + "srcPath": "D:/Vietnammongo.games.com/game/trunk/fishpath/json", + "destPath":"D:/Vietnammongo.games.com/game/trunk/fishpath/binary", + "cmdPath":"D:/Vietnammongo.games.com/game/trunk/fishpath" +} \ No newline at end of file diff --git a/tools/json2binary/config_fish.json b/tools/json2binary/config_fish.json new file mode 100644 index 0000000..decdace --- /dev/null +++ b/tools/json2binary/config_fish.json @@ -0,0 +1,5 @@ +{ + "srcPath": "D:/Vietnammongo.games.com/game/trunk/fishpath/json", + "destPath":"D:/Vietnammongo.games.com/game/trunk/fishpath/binary", + "cmdPath":"D:/Vietnammongo.games.com/game/trunk/fishpath" +} \ No newline at end of file diff --git a/tools/json2binary/json2binary.go b/tools/json2binary/json2binary.go new file mode 100644 index 0000000..8d749a1 --- /dev/null +++ b/tools/json2binary/json2binary.go @@ -0,0 +1,152 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/klauspost/compress/zip" + "io" + "os" + "os/exec" + "path" +) + +type Conf struct { + SrcPath string `json:"srcPath"` + DesPath string `json:"destPath"` + CmdPath string `json:"cmdPath"` +} + +func pathExist(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +func main() { + fileBuffer, err := os.ReadFile("./config.json") + if err != nil { + fmt.Println("获取srcPath desPath失败") + return + } + var c Conf + e := json.Unmarshal(fileBuffer, &c) + if e != nil { + fmt.Println("从配置文件中获取srcPath desPath失败") + return + } + fmt.Println("源路径为", c.SrcPath) + fmt.Println("目标路径为", c.DesPath) + + srcstate, _ := pathExist(c.SrcPath) + if !srcstate { + fmt.Println("源路径不存在无法进行转换") + return + } + desState, _ := pathExist(c.DesPath) + if !desState { + fmt.Println("目标路径,请修改配置文件") + return + } + + //直接将目录下的文件全部进行转换 + fileInfo, dirErr := os.ReadDir(c.SrcPath) + if dirErr != nil { + fmt.Println("遍历源路径出现错误") + return + } + for _, v := range fileInfo { + name := v.Name() + + if path.Ext(name) == ".json" { + //将json文件进行转换 + fileAllName := path.Base(name) + fileSufix := path.Ext(name) + filePrefix := fileAllName[0 : len(fileAllName)-len(fileSufix)] + cvt2Bin(c.SrcPath, c.DesPath, filePrefix, c.CmdPath) + } + } + +} + +// 将单个文件转换 +func cvt2Bin(src string, des string, name string, cmdPath string) { + srcPath := path.Join(src, name+".json") + desPath := path.Join(des, name+".bin") + fmt.Println("压缩文件开始", srcPath, desPath) + + //如果目标文件已经存在则删除 + if exist, _ := pathExist(desPath); exist { + os.Remove(desPath) + } + + //f,err := os.Open(srcPath) + //if err != nil{ + // return + //} + //defer f.Close() + //d,_ := os.Create(desPath) + //defer d.Close() + //w := zip.NewWriter(d) + //defer w.Close() + //err = compress(f,"",w) + //if err != nil{ + // fmt.Println(err) + // return + //} + + //直接使用7z 进行压缩就可以 + //7z a -tzip D:\VietnamGame\trunk\fishpath\json\custom1.bin custom1.json + cmdStr := "%s/7z.exe a -tzip %s %s" + cmdStr = fmt.Sprintf(cmdStr, cmdPath, desPath, srcPath) + cmd := exec.Command("cmd.exe", "/c", cmdStr) + + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + err := cmd.Run() + if err != nil { + fmt.Println(fmt.Sprint(err) + ": " + stderr.String()) + return + } + fmt.Println("Result: " + out.String()) + + //cmd := exec.Command(cmdStr,"/c") + //if err := cmd.Run(); err != nil { + // fmt.Println("压缩文件是失败", desPath) + // return + //} + fmt.Println("压缩文件到成功", desPath) +} + +func compress(file *os.File, prefix string, zw *zip.Writer) error { + info, err := file.Stat() + if err != nil { + return err + } + header, err := zip.FileInfoHeader(info) + if len(prefix) == 0 { + header.Name = header.Name + } else { + header.Name = prefix + "/" + header.Name + } + if err != nil { + return err + } + writer, err := zw.CreateHeader(header) + if err != nil { + return err + } + _, err = io.Copy(writer, file) + file.Close() + if err != nil { + return err + } + return nil +} diff --git a/tools/language/config.go b/tools/language/config.go new file mode 100644 index 0000000..3ab936a --- /dev/null +++ b/tools/language/config.go @@ -0,0 +1,50 @@ +// config +package main + +import ( + "os" + "path/filepath" + "text/template" + + "mongo.games.com/goserver/core" +) + +var Config = Configuration{} +var templates *template.Template + +type Configuration struct { + WorkPath string + XlsxPath string + TsPath string + LanguageType []string +} + +func (this *Configuration) Name() string { + return "language" +} + +func (this *Configuration) Init() (err error) { + wd, err := os.Getwd() + if err != nil { + return + } + + pattern := filepath.Join(wd, "templ", "*.templ") + funcMap := template.FuncMap{ + "inc": func(n int) int { + n++ + return n + }, + } + templates, err = template.Must(template.New("mytempl").Funcs(funcMap).ParseGlob(pattern)).Parse("") + + return +} + +func (this *Configuration) Close() (err error) { + return +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/tools/language/config.json b/tools/language/config.json new file mode 100644 index 0000000..a107b58 --- /dev/null +++ b/tools/language/config.json @@ -0,0 +1,8 @@ +{ + "language": { + "WorkPath": "E:/gocode/trunk/src", + "XlsxPath": "mongo.games.com/game/tools/language/data", + "TsPath": "mongo.games.com/game/tools/language/data", + "LanguageType": ["cfg_zh","cfg_en","cfg_kh","cfg_vi"] + } +} \ No newline at end of file diff --git a/tools/language/copy2cc.bat b/tools/language/copy2cc.bat new file mode 100644 index 0000000..f4dd86d --- /dev/null +++ b/tools/language/copy2cc.bat @@ -0,0 +1,7 @@ +@echo off + +cd data +dir *.ts +copy *.ts E:\ccc_client\vietnam\assets\Lobby\i18n\*.ts +del *.ts +pause \ No newline at end of file diff --git a/tools/language/data/Language.xlsx b/tools/language/data/Language.xlsx new file mode 100644 index 0000000..dbbcd4c Binary files /dev/null and b/tools/language/data/Language.xlsx differ diff --git a/tools/language/logger.xml b/tools/language/logger.xml new file mode 100644 index 0000000..17f5274 --- /dev/null +++ b/tools/language/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/language/main.go b/tools/language/main.go new file mode 100644 index 0000000..0d94b02 --- /dev/null +++ b/tools/language/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/tealeg/xlsx" + "mongo.games.com/goserver/core" + "os" + "path/filepath" + "strconv" + "strings" +) + +type Language struct { + Excel string + Data map[string][]string +} + +func main() { + defer core.ClosePackages() + core.LoadPackages("config.json") + + file := filepath.Join(Config.WorkPath, Config.XlsxPath, "Language.xlsx") + xlsxFile, err := xlsx.OpenFile(file) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", file) + return + } + sheet1 := xlsxFile.Sheets[0] + var languages = make(map[string]*Language) + var language = new(Language) + var excelName string + var total = len(sheet1.Rows[0].Cells) + for _, row := range sheet1.Rows[2:] { + key := strings.Replace(row.Cells[1].Value, "_", "", -1) + if excelName != key { + if excelName != "" { + if _, ok := languages[excelName]; !ok { + languages[excelName] = language + } else { + for s, i := range language.Data { + languages[excelName].Data[s] = i + } + } + } + //excelName = row.Cells[1].Value + excelName = key + language = &Language{Data: map[string][]string{}} + language.Excel = excelName + } + var val []string + for i := 3; i < total; i++ { + var val2 = "\"\"" + if i < len(row.Cells) { + val2 = strconv.Quote(row.Cells[i].Value) + } + val = append(val, val2) + } + + language.Data[row.Cells[2].Value] = val + } + + if _, ok := languages[language.Excel]; !ok { + languages[language.Excel] = language + } else { + for s, i := range language.Data { + languages[language.Excel].Data[s] = i + } + } + + fmt.Println(len(languages), "excel.") + //for _, v := range languages { + // fmt.Println(v.Excel) + // for m, n := range v.Data { + // fmt.Println(m, "===", n) + // } + //} + var needLang []Language + for _, v := range languages { + needLang = append(needLang, *v) + } + for k, v := range Config.LanguageType { + if k < total-3 { + str1 := strings.Split(v, "_") + if len(str1) == 2 { + geneLanguageHelper(needLang, k, str1[1]) + } + } + } + fmt.Println("success.") +} + +func geneLanguageHelper(languages []Language, num int, str string) { + filename := Config.LanguageType[num] + dm := map[string]interface{}{ + "Languages": languages, + "Idx": num, + "Name": str, + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "languagets", dm) + if err != nil { + fmt.Println("geneLanguageHelper ExecuteTemplate error:", err) + return + } + helperGoFile := filepath.Join(Config.WorkPath, Config.TsPath, filename+".ts") + err = os.WriteFile(helperGoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneLanguageHelper WriteFile error:", err) + return + } + //for k, v := range languages { + // for m, n := range v.Data { + // if len(n) > 0 { + // languages[k].Data[m] = n[1:] + // } + // } + //} +} diff --git a/tools/language/templ/languagets.templ b/tools/language/templ/languagets.templ new file mode 100644 index 0000000..e65f0af --- /dev/null +++ b/tools/language/templ/languagets.templ @@ -0,0 +1,20 @@ +{{define "languagets"}}const win = window as any; + +export let languages:any = { + // Data +{{range $index, $Language := .Languages}} "{{$Language.Excel}}": { +{{range $key, $val := .Data}} "{{$key}}": {{index . $.Idx}}, +{{end}} }, +{{end}}}; + +if (!win.languages) { + win.languages = {}; +} +if (win.languages.{{$.Name}}) { + for (let item in languages) { + win.languages.{{$.Name}}[item] = languages[item]; + } +} else { + win.languages.{{$.Name}} = languages; +} +{{end}} \ No newline at end of file diff --git a/tools/msg2bin/msg2bin.go b/tools/msg2bin/msg2bin.go new file mode 100644 index 0000000..8082912 --- /dev/null +++ b/tools/msg2bin/msg2bin.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "mongo.games.com/game/proto" + webapi_proto "mongo.games.com/game/protocol/webapi" + "os" +) + +func main() { + msg := &webapi_proto.ASUpdatePlatform{} + platform := &webapi_proto.Platform{ + PlatformName: "官方平台X", + Isolated: false, + Disabled: false, + //ConfigId: 1, + CustomService: "", + BindOption: 0, + ServiceFlag: false, + UpgradeAccountGiveCoin: 0, + NewAccountGiveCoin: 0, + PerBankNoLimitAccount: 0, + ExchangeMin: 0, + ExchangeLimit: 0, + ExchangeTax: 0, + ExchangeForceTax: 0, + ExchangeFlow: 0, + ExchangeGiveFlow: 0, + ExchangeFlag: 0, + ExchangeVer: 0, + ExchangeMultiple: 0, + VipRange: nil, + SpreadConfig: 0, + Leaderboard: nil, + ClubConfig: nil, + VerifyCodeType: 0, + ThirdGameMerchant: nil, + CustomType: 0, + NeedSameName: false, + ExchangeBankMax: 0, + ExchangeAlipayMax: 0, + PerBankNoLimitName: 0, + IsCanUserBindPromoter: false, + UserBindPromoterPrize: 0, + } + + msg.Platforms = append(msg.Platforms, platform) + proto.SetDefaults(msg) + + file, err := os.Create("output.bin") + if err != nil { + fmt.Println("文件创建失败 ", err.Error()) + return + } + defer file.Close() + b, errors := proto.Marshal(msg) + if errors != nil { + fmt.Println("编码失败", errors.Error()) + return + } + _, err = file.Write(b) + if err != nil { + fmt.Println("编码失败", err.Error()) + return + } + fmt.Println("编码成功") +} diff --git a/tools/proto2js/config.json b/tools/proto2js/config.json new file mode 100644 index 0000000..2c9c52e --- /dev/null +++ b/tools/proto2js/config.json @@ -0,0 +1,35 @@ +{ + "srcPath": "E:/gocode/trunk/src/mongo.games.com/game/protocol", + "destPath":"E:/ccc_client/protocol", + "cmdPath": "C:/Users/Administrator/AppData/Roaming/npm", + "mapCc": [ + "activity", + "bag", + "chat", + "fishing", + "friend", + "fruits", + "gamehall", + "gradeshop", + "login", + "message", + "pets", + "player", + "qpapi", + "richblessed", + "server", + "shop", + "tala", + "task", + "telegramapi", + "tienlen", + "tournament", + "welfare", + "caishen", + "tamquoc", + "iceage", + "easterisland", + "samloc", + "avengers" + ] +} \ No newline at end of file diff --git a/tools/proto2js/protoconvert2ts.go b/tools/proto2js/protoconvert2ts.go new file mode 100644 index 0000000..e223196 --- /dev/null +++ b/tools/proto2js/protoconvert2ts.go @@ -0,0 +1,209 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path" + "strings" +) + +type Conf struct { + SrcPath string `json:"srcPath"` + DestPath string `json:"destPath"` + CmdPath string `json:cmdPath` + MapCc []string `json:mapCc` +} + +// 判断路径是否存在 +func pathExist(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +/* +传入参数 all 则转换 protocol目录下的所有文件 +如果传入模块名字 则转换模块名字的 proto文件 +*/ +func main() { + + //args := os.Args + //if len(args) < 1 { + // fmt.Println("[all | module name]") + // return + //} + + //读取当前目录下的配置文件 + fileBuffer, err := os.ReadFile("./config.json") + if err != nil { + fmt.Println("获取srcpath和destpath发生错误") + return + } + var c Conf + + e := json.Unmarshal(fileBuffer, &c) + if e != nil { + fmt.Println("从配置文件中获取srcpath和destpath发生错误") + } + + //fmt.Println("源路径为:%s", c.SrcPath) + //fmt.Println("目标路径为:%s", c.DestPath) + + //判定源路径是否存在 + srcstat, _ := pathExist(c.SrcPath) + if !srcstat { + fmt.Println("源路径不存在,无法执行转换") + } + deststate, _ := pathExist(c.DestPath) + if !deststate { + fmt.Println("目标路径不存在,请修改配置文件") + } + var mapCc = make(map[string]int) + if len(c.MapCc) > 0 { + for _, v := range c.MapCc { + mapCc[v] = 0 + } + } + moduleName := "all" + if moduleName == "all" { + //遍历目录 所有目录都执行转换 + fileInfo, dirErr := os.ReadDir(c.SrcPath) + if dirErr != nil { + fmt.Println("遍历源目录出现错误") + } + for _, v := range fileInfo { + if _, ok := mapCc[v.Name()]; ok && v.IsDir() { + doConvert(c.CmdPath, c.DestPath, c.SrcPath, v.Name()) + } + } + } else { + //转换单个模块 + doConvert(c.CmdPath, c.DestPath, c.SrcPath, moduleName) + } + +} + +func doConvert(cmdPath string, dPath string, sPath string, name string) { + destPath := path.Join(dPath, name) + srcPath := path.Join(sPath, name) + if isExist, _ := pathExist(destPath); isExist { + os.RemoveAll(destPath) + } + os.Mkdir(destPath, os.ModePerm) + + if isExist, _ := pathExist(srcPath); !isExist { + fmt.Println("要转换的模块不存在") + return + } + //str := "%s/pbjs --target static-module --no-convert --no-delimited --wrap closure --no-beautify --no-create --force-number --no-verify -o %s/%s.js %s/*.proto" + str := "%s/pbjs --dependency protobufjs/minimal.js --no-service --no-convert --no-delimited --no-create --no-beautify --no-verify --target static-module --wrap commonjs --out %s/%s.js %s/*.proto" + cmdStr := fmt.Sprintf(str, cmdPath, destPath, name, srcPath) + //fmt.Println("转换为js") + //fmt.Println(cmdStr) + + cmd := exec.Command("cmd.exe", "/C", cmdStr) + + if err := cmd.Run(); err != nil { + fmt.Printf("转换%s proto to js 错误", name) + } + + //这个地方需要将js中内容以字符串的形式读取出来,然后 + coverJsFile(destPath, name) + + //fmt.Println("转化ts") + //str = "%s/pbts --no-comments --main -o %s/%s.d.ts %s/%s.js" + str = "%s/pbts.cmd --main --out %s/" + name + ".d.ts %s/" + name + ".js" + cmdStr = fmt.Sprintf(str, cmdPath, destPath, destPath) + cmd = exec.Command("cmd.exe", "/C", cmdStr) + // + if err := cmd.Run(); err != nil { + fmt.Println("转换 js to ts 错误", name) + } + + convrTsFile(destPath, name) + + //重新生成js。为了生成ts则必须有注释。在生成ts后重新生成一下js去掉注释 + //str = "%s/pbjs --target static-module --no-convert --no-delimited --no-comments --wrap closure --no-beautify --no-create --force-number --no-verify -o %s/%s.js %s/*.proto" + str = "%s/pbjs --dependency protobufjs/minimal.js --no-service --no-convert --no-comments --no-delimited --no-create --no-beautify --no-verify --target static-module --wrap commonjs --out %s/%s.js %s/*.proto" + cmdStr = fmt.Sprintf(str, cmdPath, destPath, name, srcPath) + //fmt.Println("重新转换为js") + //fmt.Println(cmdStr) + + cmd = exec.Command("cmd.exe", "/C", cmdStr) + + if err := cmd.Run(); err != nil { + fmt.Println("重新去掉注释转换%s proto to js 错误", name) + } + + //copy *.proto + str = "copy %s/*.proto %s/" + cmdStr = fmt.Sprintf(str, srcPath, destPath) + cmd = exec.Command("powershell.exe", "/C", cmdStr) + if err := cmd.Run(); err != nil { + fmt.Println("copy err:", err) + } + + //这个地方需要将js中内容以字符串的形式读取出来,然后 + coverJsFile(destPath, name) + + fmt.Printf("cc %-15s to js 完成 \n", name) +} + +// 覆盖filePath对应的文本文件 +func coverJsFile(filePath string, name string) { + by, err := os.ReadFile(filePath + "/" + name + ".js") + if err != nil { + fmt.Printf("覆盖文件%s出现错误/n", name) + return + } + + s := string(by) + + startStr := `(function($protobuf)` + + //targetStr := fmt.Sprintf("(window || global).%s = (function($protobuf)", name) + targetStr := fmt.Sprintf("module.exports.%s=(function($protobuf)", name) + s = strings.Replace(s, startStr, targetStr, 1) + + endStr := `(protobuf)` + endTargetStr := fmt.Sprintf("%s.%s", "(protobuf)", name) + s = strings.Replace(s, endStr, endTargetStr, 1) + f, err := os.OpenFile(filePath+"/"+name+".js", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + fmt.Println("覆盖写文件出现错误") + return + } + defer f.Close() + f.WriteString(s) +} + +func convrTsFile(filePath string, name string) { + by, err := os.ReadFile(filePath + "/" + name + ".d.ts") + if err != nil { + fmt.Println("覆盖TS文件%s错误", name) + return + } + s := string(by) + //temple := `declare global { + // %s + //} + //export {}` + //temple = fmt.Sprintf(temple, s) + + temple := s + + f, err := os.OpenFile(filePath+"/"+name+".d.ts", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + fmt.Println("覆盖TS文件发生错误%s", name) + return + } + defer f.Close() + f.WriteString(temple) +} diff --git a/tools/rabbitmqReceiver/main.go b/tools/rabbitmqReceiver/main.go new file mode 100644 index 0000000..a11f702 --- /dev/null +++ b/tools/rabbitmqReceiver/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "log" + + "github.com/streadway/amqp" +) + +func failOnError(err error, msg string) { + if err != nil { + log.Fatalf("%s: %s", msg, err) + } +} + +func main() { + conn, err := amqp.Dial("amqp://guest:guest@192.168.1.160:5672/") + failOnError(err, "Failed to connect to RabbitMQ") + defer conn.Close() + + ch, err := conn.Channel() + failOnError(err, "Failed to open a channel") + defer ch.Close() + + q, err := ch.QueueDeclare( + "Member_Register", // name + true, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + failOnError(err, "Failed to declare a queue") + + msgs, err := ch.Consume( + q.Name, // queue + "", // consumer + true, // auto-ack + false, // exclusive + false, // no-local + false, // no-wait + nil, // args + ) + failOnError(err, "Failed to register a consumer") + + forever := make(chan bool) + + go func() { + for d := range msgs { + log.Printf("Received a message: %s", d.Body) + } + }() + + log.Printf(" [*] Waiting for messages. To exit press CTRL+C") + <-forever +} diff --git a/tools/rabbitmqSender/main.go b/tools/rabbitmqSender/main.go new file mode 100644 index 0000000..d7aa1ac --- /dev/null +++ b/tools/rabbitmqSender/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + "log" + "time" + + "github.com/streadway/amqp" +) + +func failOnError(err error, msg string) { + if err != nil { + log.Fatalf("%s: %s", msg, err) + } +} + +func main() { + conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/") + failOnError(err, "Failed to connect to RabbitMQ") + defer conn.Close() + + ch, err := conn.Channel() + failOnError(err, "Failed to open a channel") + defer ch.Close() + + q, err := ch.QueueDeclare( + "hello", // name + true, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + failOnError(err, "Failed to declare a queue") + + isok := true + c := make(chan *amqp.Error, 1) + ch.NotifyClose(c) + go func() { + for { + select { + case e, ok := <-c: + fmt.Println("NotifyClose", ok, e) + isok = false + return + } + } + }() + i := 0 + for isok { + i++ + body := fmt.Sprintf("hello_lyk%v", i) + err = ch.Publish( + "", // exchange + q.Name, // routing key + false, // mandatory + false, // immediate + amqp.Publishing{ + ContentType: "text/plain", + Body: []byte(body), + DeliveryMode: amqp.Persistent, + }) + log.Printf(" [x] Sent %s", body) + //failOnError(err, "Failed to publish a message") + if i == 1000 { + break + } + time.Sleep(time.Second) + } + +} diff --git a/tools/statisticsrv/backend.go b/tools/statisticsrv/backend.go new file mode 100644 index 0000000..752f778 --- /dev/null +++ b/tools/statisticsrv/backend.go @@ -0,0 +1,46 @@ +package main + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "strconv" +) + +func apiTopNFileDownFile(c *gin.Context) { + strPageNo := c.DefaultQuery("pageno", "1") + strPageSize := c.DefaultQuery("pagesize", "50") + pageNo, _ := strconv.Atoi(strPageNo) + pageSize, _ := strconv.Atoi(strPageSize) + datas, cnt := SelectTopNFileDownFailed(pageSize, pageNo) + d, _ := json.MarshalIndent(datas, "", "\t") + c.JSON(200, gin.H{ + "status": "ok", + "pageno": pageNo, + "count": cnt, + "data": string(d), + }) +} + +func apiFileDownFailDetail(c *gin.Context) { + fileName := c.Query("filename") + strPageNo := c.DefaultQuery("pageno", "1") + strPageSize := c.DefaultQuery("pagesize", "50") + pageNo, _ := strconv.Atoi(strPageNo) + pageSize, _ := strconv.Atoi(strPageSize) + datas, cnt, _ := SelectFileDownFailedDetail(fileName, pageSize, pageNo) + d, _ := json.MarshalIndent(datas, "", "\t") + c.JSON(200, gin.H{ + "status": "ok", + "pageno": pageNo, + "count": cnt, + "data": string(d), + }) +} + +func RegisteBackEndAPI() { + //http://127.0.0.1:8080/api/topn_filedown_fail?pageno=1&pagesize=10 + GinEngine.Any("/api/topn_filedown_fail", apiTopNFileDownFile) + + //http://127.0.0.1:8080/api/filedown_fail_detail?filename=a.mp3&pageno=1&pagesize=10 + GinEngine.Any("/api/filedown_fail_detail", apiFileDownFailDetail) +} diff --git a/tools/statisticsrv/config.go b/tools/statisticsrv/config.go new file mode 100644 index 0000000..bb80678 --- /dev/null +++ b/tools/statisticsrv/config.go @@ -0,0 +1,74 @@ +package main + +import ( + "github.com/go-ini/ini" +) + +const ( + DEFAULT_CONFIGFILE_NAME = "my.ini" + DEFAULT_SERVER_PORT = 8080 + DEFAULT_MGO_MAXDONE = 1024 + DEFAULT_MGO_MAXINTERVAL = 5 +) + +var AppCfg Config + +type ServerConfig struct { + Port int +} + +type MongoConfig struct { + Host string + Database string + UserName string + Password string + MaxDone int + MaxInterval int +} + +type Config struct { + GinMode string + SC ServerConfig + MC MongoConfig +} + +func LoadConfig() error { + cfg, err := ini.Load(DEFAULT_CONFIGFILE_NAME) + if err != nil { + return err + } + + AppCfg.GinMode = cfg.Section("").Key("app_mode").String() + srvsec := cfg.Section("server") + if srvsec != nil { + port, err := srvsec.Key("http_port").Int() + if err == nil { + AppCfg.SC.Port = port + } else { + AppCfg.SC.Port = DEFAULT_SERVER_PORT + } + } else { + AppCfg.SC.Port = DEFAULT_SERVER_PORT + } + + mgosec := cfg.Section("mongo") + if mgosec != nil { + AppCfg.MC.Database = mgosec.Key("database").String() + AppCfg.MC.Host = mgosec.Key("host").String() + AppCfg.MC.UserName = mgosec.Key("username").String() + AppCfg.MC.Password = mgosec.Key("password").String() + maxdone, err := mgosec.Key("maxdone").Int() + if err == nil { + AppCfg.MC.MaxDone = maxdone + } else { + AppCfg.MC.MaxDone = DEFAULT_MGO_MAXDONE + } + maxinterval, err := mgosec.Key("maxinterval").Int() + if err == nil { + AppCfg.MC.MaxInterval = maxinterval + } else { + AppCfg.MC.MaxInterval = DEFAULT_MGO_MAXINTERVAL + } + } + return nil +} diff --git a/tools/statisticsrv/frontend.go b/tools/statisticsrv/frontend.go new file mode 100644 index 0000000..4af1e71 --- /dev/null +++ b/tools/statisticsrv/frontend.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/gin-gonic/gin" + "github.com/globalsign/mgo/bson" + "strconv" + "time" +) + +func ResisteFrontEndAPI() { + //http://127.0.0.1:8080/api/report_filedown_fail?platform=7&package=com.qipai.xiaoxin.a001&filename=1.mp4&url=http://cdn.x.com/1.mp4?ver=1001&statuscode=404&errorcode=2&errormsg=xxx + GinEngine.GET("/api/report_filedown_fail", func(c *gin.Context) { + pkg := c.DefaultQuery("package", "com.x.qipai") + filename := c.Query("filename") + url := c.Query("url") + md5 := c.Query("md5") + errormsg := c.Query("errormsg") + statuscode := c.Query("statuscode") + errorcode := c.Query("errorcode") + _size := c.Query("size") + size, _ := strconv.Atoi(_size) + tNow := time.Now() + ip := c.ClientIP() + fdfdd := &FileDownLoadFailDetailData{ + Id: bson.NewObjectId(), + FileName: filename, + Url: url, + StatusCode: statuscode, + ErrorCode: errorcode, + ErrorMsg: errormsg, + Size: int64(size), + Ip: ip, + Package: pkg, + Md5: md5, + Time: tNow, + } + WriteFileDownLog(fdfdd) + + c.JSON(200, gin.H{ + "status": "ok", + }) + }) +} diff --git a/tools/statisticsrv/log.go b/tools/statisticsrv/log.go new file mode 100644 index 0000000..6523710 --- /dev/null +++ b/tools/statisticsrv/log.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/cihub/seelog" +) + +var ( + Log seelog.LoggerInterface +) + +func init() { + Log, _ = seelog.LoggerFromConfigAsFile("logger.xml") + seelog.ReplaceLogger(Log) +} + +func Reload(fileName string) error { + newLogger, err := seelog.LoggerFromConfigAsFile(fileName) + if err != nil { + return err + } + if newLogger != nil { + Log = newLogger + seelog.ReplaceLogger(Log) + } + return nil +} diff --git a/tools/statisticsrv/logger.xml b/tools/statisticsrv/logger.xml new file mode 100644 index 0000000..cb66204 --- /dev/null +++ b/tools/statisticsrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/statisticsrv/main.go b/tools/statisticsrv/main.go new file mode 100644 index 0000000..bc6039c --- /dev/null +++ b/tools/statisticsrv/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "github.com/gin-gonic/gin" + "io" + "os" + "runtime" +) + +var GinEngine *gin.Engine + +func main() { + + runtime.GOMAXPROCS(runtime.NumCPU()) + + err := LoadConfig() + if err != nil { + fmt.Printf("Fail to LoadConfig: %v", err) + os.Exit(1) + } + + // Force log's color + gin.ForceConsoleColor() + gin.SetMode(AppCfg.GinMode) + GinEngine = gin.Default() + GinEngine.Use(gin.Recovery()) + // Logging to a file. + f, _ := os.Create("gin.log") + gin.DefaultWriter = io.MultiWriter(f) + + MgoSession, err = newDBSession() + if err != nil { + fmt.Println("mgo session create err:", err) + os.Exit(1) + } + + //启动mongo定时ping + StartMgoPing() + //启动日志发布 + StartPublishLog() + //注册前端api + ResisteFrontEndAPI() + //注册后端api + RegisteBackEndAPI() + + GinEngine.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + GinEngine.Run(fmt.Sprintf(":%d", AppCfg.SC.Port)) +} diff --git a/tools/statisticsrv/model.go b/tools/statisticsrv/model.go new file mode 100644 index 0000000..dd797da --- /dev/null +++ b/tools/statisticsrv/model.go @@ -0,0 +1,188 @@ +package main + +import ( + "errors" + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + "time" +) + +var c_filedownloadfaildetail *mgo.Collection + +var ChanFileDownFailed chan *FileDownLoadFailDetailData + +type FileDownLoadFailDetailData struct { + Id bson.ObjectId `bson:"_id"` + FileName string + Url string + Ip string + Package string + Md5 string + StatusCode string + ErrorCode string + ErrorMsg string + Size int64 + Time time.Time +} + +func FileDetailCollection() *mgo.Collection { + if c_filedownloadfaildetail == nil { + c_filedownloadfaildetail = MgoSession.DB(AppCfg.MC.Database).C("log_filedownfail") + if c_filedownloadfaildetail != nil { + c_filedownloadfaildetail.EnsureIndex(mgo.Index{Key: []string{"filename"}, Background: true, Sparse: true}) + c_filedownloadfaildetail.EnsureIndex(mgo.Index{Key: []string{"url"}, Background: true, Sparse: true}) + c_filedownloadfaildetail.EnsureIndex(mgo.Index{Key: []string{"package"}, Background: true, Sparse: true}) + } + } + return c_filedownloadfaildetail +} + +func InsertFileDownloadDetail(logs ...*FileDownLoadFailDetailData) (err error) { + clog := FileDetailCollection() + if clog == nil { + return + } + switch len(logs) { + case 0: + return errors.New("no data") + case 1: + err = clog.Insert(logs[0]) + default: + docs := make([]interface{}, 0, len(logs)) + for _, log := range logs { + docs = append(docs, log) + } + err = clog.Insert(docs...) + } + if err != nil { + Log.Warn("InsertCoinLogs error:", err) + return + } + return +} + +func SelectFileDownFailedDetail(filename string, pageSize, pageNo int) (datas []*FileDownLoadFailDetailData, cnt int, err error) { + clog := FileDetailCollection() + if clog == nil { + return nil, 0, nil + } + start := (pageNo - 1) * pageSize + q := clog.Find(bson.M{"filename": filename}).Sort("time:-1") + if q != nil { + cnt, err = q.Count() + if err != nil { + return + } + err = q.Skip(start).Limit(pageSize).All(&datas) + } + return +} + +func SelectTopNFileDownFailed(pageSize, pageNo int) (datas []*TopNFileMsg, cnt int) { + clog := FileDetailCollection() + if clog == nil { + return nil, 0 + } + + pipeline := []bson.M{ + {"$group": bson.M{"_id": "$filename", "total": bson.M{"$sum": 1}, "size": bson.M{"$avg": "$size"}}}, + {"$sort": bson.M{"total": -1}}, + } + pipe := clog.Pipe(pipeline) + if pipe != nil { + pipe.AllowDiskUse() + err := pipe.All(&datas) + if err != nil { + Log.Warnf("SelectTopNFileDownFailed err:%v", err) + return nil, 0 + } + + start := (pageNo - 1) * pageSize + + cnt = len(datas) + if start < 0 { + start = 0 + } + if start > cnt { + start = cnt + } + end := start + pageSize + if end > cnt { + end = cnt + } + datas = datas[start:end] + } + //if start != 0 { + // pipeline = append(pipeline, bson.M{"$skip": start}) + //} + //if pageSize != 0 { + // pipeline = append(pipeline, bson.M{"$limit": pageSize}) + //} + + //pipe := clog.Pipe(pipeline) + //if pipe != nil { + // pipe.AllowDiskUse() + // err := pipe.All(&datas) + // if err != nil { + // Log.Warnf("SelectTopNFileDownFailed err:%v", err) + // return nil + // } + //} + return +} + +func AsyncBatchInsertFileDownloadDetail(logs []*FileDownLoadFailDetailData) { + go func() { + defer func() { + err := recover() + if err != nil { + Log.Warnf("InsertFileDownloadDetail panic: %s", err) + } + }() + err := InsertFileDownloadDetail(logs...) + if err != nil { + Log.Warn("InsertCoinLogs error:", err) + } + }() +} + +func WriteFileDownLog(log *FileDownLoadFailDetailData) { + select { + case ChanFileDownFailed <- log: + default: + Log.Warn("WriteFileDownLog channel full, then drop") + } +} + +func StartPublishLog() { + ChanFileDownFailed = make(chan *FileDownLoadFailDetailData, AppCfg.MC.MaxDone*10) + go func() { + defer func() { + err := recover() + if err != nil { + Log.Warnf("StartPublishLog panic: %s", err) + } + }() + var logs []*FileDownLoadFailDetailData + for { + select { + case log, ok := <-ChanFileDownFailed: + if !ok { + return + } + logs = append(logs, log) + if len(logs) >= AppCfg.MC.MaxDone { + wlogs := logs + logs = nil + AsyncBatchInsertFileDownloadDetail(wlogs) + } + case <-time.After(time.Millisecond * time.Duration(AppCfg.MC.MaxInterval)): + if len(logs) != 0 { + wlogs := logs + logs = nil + AsyncBatchInsertFileDownloadDetail(wlogs) + } + } + } + }() +} diff --git a/tools/statisticsrv/mongo.go b/tools/statisticsrv/mongo.go new file mode 100644 index 0000000..5056763 --- /dev/null +++ b/tools/statisticsrv/mongo.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "github.com/globalsign/mgo" + "time" +) + +var autoPingInterval time.Duration = time.Minute +var MgoSession *mgo.Session + +func newDBSession() (s *mgo.Session, err error) { + login := "" + if AppCfg.MC.UserName != "" { + login = AppCfg.MC.UserName + ":" + AppCfg.MC.Password + "@" + } + host := "localhost" + if AppCfg.MC.Host != "" { + host = AppCfg.MC.Host + } + + // http://goneat.org/pkg/labix.org/v2/mgo/#Session.Mongo + // [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options] + url := fmt.Sprintf("mongodb://%s%s/admin", login, host) + //fmt.Println(url) + session, err := mgo.Dial(url) + if err != nil { + return + } + + s = session + return +} + +func Ping() { + var err error + if MgoSession != nil { + err = MgoSession.Ping() + if err != nil { + Log.Errorf("mongo.Ping err:%v", err) + MgoSession.Refresh() + } else { + Log.Tracef("mongo.Ping suc") + } + } +} + +func StartMgoPing() { + go func() { + defer func() { + err := recover() + if err != nil { + Log.Warnf("StartMgoPing panic: %s", err) + } + }() + for { + select { + case <-time.After(autoPingInterval): + Ping() + } + } + }() +} + +func SetAutoPing(interv time.Duration) { + autoPingInterval = interv +} diff --git a/tools/statisticsrv/my.ini b/tools/statisticsrv/my.ini new file mode 100644 index 0000000..d634f51 --- /dev/null +++ b/tools/statisticsrv/my.ini @@ -0,0 +1,21 @@ + +# possible values : release, debug +app_mode = release + +[server] + +# The http port to use +http_port = 8080 + +[mongo] + +database = hjstatistics +host = 127.0.0.1 +username = +password = +# batch insert mongo +maxdone = 128 +# log insert max interval(ms) +maxinterval = 5000 + + diff --git a/tools/statisticsrv/protocol.go b/tools/statisticsrv/protocol.go new file mode 100644 index 0000000..b1e82a4 --- /dev/null +++ b/tools/statisticsrv/protocol.go @@ -0,0 +1,16 @@ +package main + +type FileReqMsg struct { + FileName string `json:"filename"` + Size int64 `json:"size"` + Url string `json:"url"` + StatusCode string `json:"statuscode"` + ErrorCode string `json:"errorcode"` + ErrorMsg string `json:"errormsg"` +} + +type TopNFileMsg struct { + Id string `bson:"_id"` + Total int64 `bson:"total"` + Size int64 `bson:"size"` +} diff --git a/tools/test/build.bat b/tools/test/build.bat new file mode 100644 index 0000000..9d08775 --- /dev/null +++ b/tools/test/build.bat @@ -0,0 +1,3 @@ +set GOOS=linux +set GOARCH=amd64 +go build \ No newline at end of file diff --git a/tools/test/main.go b/tools/test/main.go new file mode 100644 index 0000000..ee54560 --- /dev/null +++ b/tools/test/main.go @@ -0,0 +1,391 @@ +package main + +import ( + "fmt" + "math/rand" + "mongo.games.com/game/gamerule/ninelineking" + "time" +) + +func main() { + println(time.Now().UnixNano()) + println(fmt.Sprintf("%v", float64(1002340))) + + rand.Seed(time.Now().UnixNano()) + min := 1000 + max := 0 + total := 0 + times := 100 + fmt.Println("---没干扰的情况下的击中次数") + for n := 0; n < times; n++ { + cnt := 0 + for i := 0; i < 1000; i++ { + cnt++ + if rand.Intn(100) == 1 { + total += cnt + if cnt < min { + min = cnt + } + if cnt > max { + max = cnt + } + fmt.Println("第", n+1, "次1%的概率,击中时的次数:", cnt) + break + } + } + } + fmt.Println("1%的概率,平均击杀次数", total/times, "最小", min, "最大", max) + fmt.Println("=========================") + fmt.Println("+++有干扰的情况下的击中次数") + total = 0 + min = 1000 + max = 0 + for n := 0; n < times; n++ { + for m := 0; m < rand.Intn(1000); m++ { + rand.Float64() + } + cnt := 0 + for i := 0; i < 1000; i++ { + cnt++ + if rand.Intn(100) == 1 { + total += cnt + if cnt < min { + min = cnt + } + if cnt > max { + max = cnt + } + fmt.Println("第", n+1, "次1%的概率,击中时的次数:", cnt) + break + } + } + } + fmt.Println("1%的概率,平均击杀次数", total/times, "最小", min, "最大", max) + + if false { + var lhPoker [52]int32 + for i := 0; i < 52; i++ { + lhPoker[i] = int32(i) + } + lhDesc := []string{"0", "-", "1"} + lhWeight := []int32{47059, 5882, 47059} + RandSliceIndexByWight := func(s1 []int32) int { + total := int32(0) + for _, v := range s1 { + total += v + } + if total <= 0 { + return 0 + } + random := rand.Int31n(total) + total = 0 + for i, v := range s1 { + total += v + if random < total { + return i + } + } + return 0 + } + _ = lhWeight + _ = RandSliceIndexByWight + var lhtotal [3]int + var lhTrend [100]int + for i := 0; i < 10000; i++ { + //fmt.Printf("---开始第[%d]局模拟---\r\n", i+1) + for j := 0; j < 100; j++ { + lhRand := rand.Perm(52) + //lhTrend[j] = RandSliceIndexByWight(lhWeight) + //fmt.Print(lhDesc[lhTrend[j]], " ") + lPoker := lhPoker[lhRand[0]]%13 + 1 + hPoker := lhPoker[lhRand[1]]%13 + 1 + if lPoker == hPoker { + lhTrend[j] = 1 + } else if lPoker > hPoker { + lhTrend[j] = 0 + } else { + lhTrend[j] = 2 + } + lhtotal[lhTrend[j]]++ + } + //fmt.Print("\r\n") + } + fmt.Println(lhtotal) + sum := 0 + for i := 0; i < 3; i++ { + sum += lhtotal[i] + } + for i := 0; i < 3; i++ { + fmt.Print(lhDesc[i], ":", float64(lhtotal[i])/float64(sum), "% ") + } + fmt.Println() + } + //百牛路单模拟测试 + if false { + var rd [4]*rand.Rand + for i := 0; i < 4; i++ { + rd[i] = rand.New(rand.NewSource(rand.Int63())) + } + + var flagstr = []string{"输", "赢"} + for i := 0; i < 100; i++ { + fmt.Println("===第", i+1, "组路单") + var ld [4][20]int + for j := 0; j < 20; j++ { + for k := 0; k < 4; k++ { + ld[k][j] = rand.Intn(2) //rd[k].Intn(2) + } + } + var total int + var sum [4]int + for k := 0; k < 4; k++ { + var flag int + var maxlong int + var long int + v := ld[k][0] + for j := 0; j < 20; j++ { + sum[k] += ld[k][j] + if v == ld[k][j] { + long++ + if long > maxlong { + maxlong = long + flag = v + } + } else { + v = ld[k][j] + long = 1 + } + } + total += sum[k] + fmt.Println(ld[k], "胜率:", sum[k]*5, "%,", "长龙:", maxlong, "连", flagstr[flag]) + } + + var alllost int + var allwin int + for j := 0; j < 20; j++ { + flag := ld[0][j] + allsame := true + for k := 1; k < 4; k++ { + if ld[k][j] != flag { + allsame = false + break + } + } + if allsame { + if flag == 0 { + alllost++ + } else { + allwin++ + } + } + } + fmt.Println("===20局总胜率", total*100/80, "%,", "通杀次数:", alllost, ",通赔次数", allwin) + } + } + + //九线拉王模拟测试 + if false { + type NineLintItem struct { + cards [ninelineking.NINELINEKINF_CARD_NUM]int32 + allscore int32 + allline int32 + freeTimes int32 + jackaptRatio float64 + } + var freepool [180][]*NineLintItem + var pool [20000][]*NineLintItem + start := time.Now() + for i := 0; i < 1000000; i++ { + var cards [ninelineking.NINELINEKINF_CARD_NUM]int32 + for i := 0; i < ninelineking.NINELINEKINF_CARD_NUM; i++ { + cards[i] = rand.Int31n(ninelineking.KindOfCard_KingMax) + } + allscore, allline, freeTimes, jackaptRatio, _, _ := ninelineking.CalcuNineLineKingScore(cards[:], 9) + pool[allscore] = append(pool[allscore], &NineLintItem{ + cards: cards, + allscore: allscore, + allline: allline, + freeTimes: freeTimes, + jackaptRatio: jackaptRatio, + }) + if freeTimes > 0 { + freepool[freeTimes] = append(freepool[freeTimes], &NineLintItem{ + cards: cards, + allscore: allscore, + allline: allline, + freeTimes: freeTimes, + jackaptRatio: jackaptRatio, + }) + } + } + fmt.Println("NineLine take:", time.Now().Sub(start)) + //for k, v := range pool { + // if len(v) > 0 { + // fmt.Println("allscore:", k, "count:", len(v)) + // } + //} + + fmt.Println("freetimes :", len(freepool)) + sumline := int32(0) + for k, v := range freepool { + if len(v) > 0 { + fmt.Println("freetimes:", k) + for i := 0; i < len(v); i++ { + fmt.Println("freetime:", v[i].freeTimes, "score:", v[i].allscore, "lines:", v[i].allline) + sumline += v[i].allline + } + } + } + fmt.Println("total line:", sumline) + } + // start := time.Now() + // var t *time.Timer + // t = time.AfterFunc(randomDuration(), func() { + // fmt.Println(time.Now().Sub(start)) + // t.Reset(randomDuration()) + // }) + // time.Sleep(5 * time.Second) + // for i := 0; i < 100; i++ { + // fmt.Println(rand.Int31n(4)) + // } + // m := make(map[int][]int) + // fmt.Println(len(m[1])) + // var mm []int32 + // fmt.Println(len(mm)) + // Comb(100, 5) + // for dayIdx := 0; dayIdx <= 6; dayIdx++ { + // tNow := time.Now() + // tStart := tNow.AddDate(0, 0, int(-dayIdx)) + // tStart = time.Date(tStart.Year(), tStart.Month(), tStart.Day(), 0, 0, 0, 0, tStart.Location()) + // tEnd := tStart.AddDate(0, 0, 1) + // fmt.Println(tStart, "-", tEnd) + // } + + // type TestCase struct { + // hands [5]int32 + // kind int32 + // ok *bullfight.CardsInfoO + // } + // p := bullfight.NewPoker() + // for i := 0; i < 100; i++ { + // p.Reset() + // var players [5]TestCase + // for j := 0; j < 5; j++ { + // players[j].kind = rand.Int31n(bullfight.KindOfCard_Max - 3) + // for m := 0; m < 4; m++ { + // players[j].hands[m] = int32(p.Next()) + // } + // old := int32(p.Next()) + // players[j].hands[4] = old + // players[j].ok = bullfight.KindOfCardFigureUpSington.FigureUpByCard(players[j].hands[:]) + // if !(players[j].ok != nil && players[j].ok.GetKind() >= players[j].kind) { + // restCnt := p.Count() + // for k := 0; k < restCnt; k++ { + // players[j].hands[4] = int32(p.TryNextN(k)) + // ok := bullfight.KindOfCardFigureUpSington.FigureUpByCard(players[j].hands[:]) + // if ok != nil && ok.GetKind() >= players[j].kind { + // players[j].ok = ok + // p.ChangeNextN(k, bullfight.Card(old)) + // break + // } + // if ok != nil && ok.GetKind() > players[j].ok.GetKind() { + // players[j].ok = ok + // } + // } + // } + // if players[j].ok != nil && players[j].ok.GetKind() >= players[j].kind { + // //fmt.Println("[ok]", i, j, players[j].hands, players[j].kind, players[j].ok) + // } else { + // fmt.Println("[ng]", i, j, players[j].hands, players[j].kind, players[j].ok) + // } + // } + // } + + // dbConfig := mongo.DbConfig{ + // Host: "127.0.0.1", + // Database: "jxjy_game", + // User: "mongouser", + // Password: "888", + // } + // dbConfig := mongo.DbConfig{ + // Host: "192.168.1.50", + // Database: "superstar_game", + // User: "", + // Password: "", + // } + //mongo.Config.Dbs["user"] = dbConfig + //mongo.Config.Init() + //type PlayerData struct { + // Id bson.ObjectId `bson:"_id"` + // SnId int32 //数字唯一id + // Params []string //外部参数 + // PlatformNickName string //平台昵称(base64,规避特殊字符) + // PlatformHead string //平台头像 + // PlatformSex int //平台性别 + //} + //playerrec := mongo.DatabaseC("user", "user_playerinfo") + //if playerrec != nil { + // var recs []model.PlayerData + // err := playerrec.Find(nil).All(&recs) + // if err == nil { + // fmt.Println(len(recs)) + // for i := 0; i < len(recs); i++ { + // if recs[i].Params != nil && len(recs[i].Params) > 0 && recs[i].PlatformNickName == "" { + // if len(recs[i].Params) > 2 { + // recs[i].PlatformSex, _ = strconv.Atoi(recs[i].Params[2]) + // } + // recs[i].PlatformNickName = base64.StdEncoding.EncodeToString([]byte(recs[i].Params[0])) + // if len(recs[i].Params) > 1 { + // recs[i].PlatformHead = recs[i].Params[1] + // } + // //err := playerrec.Update(bson.M{"snid": recs[i].SnId}, bson.D{{"$set", bson.D{{"platformnickname", name}, {"platformhead", head}, {"platformsex", sex}}}}) + // err := playerrec.Update(bson.M{"_id": recs[i].Id}, &recs[i]) + // //, bson.D{{"$set", bson.D{{"logintimes", acc.LoginTimes}, {"lastlogouttime", acc.LastLogoutTime}}}} + // if err != nil { + // fmt.Println(recs[i].SnId, "update failed", err) + // } else { + // fmt.Println(recs[i].SnId, "update success") + // } + // } + // } + // } + //} + + sInt := []int32{1, 1, 2, 4, 5, 1, 7, 8, 1} + for i := 0; i < len(sInt); i++ { + if sInt[i] == 1 { + sInt = append(sInt[:i], sInt[i+1:]...) + i-- + } + } + + fmt.Println(sInt) +} + +func randomDuration() time.Duration { + return time.Duration(rand.Int63n(1e9)) +} + +func Comb(m, n int) [][]int { + comb := make([]int, m, m) + var all [][]int + comb[0] = n + var combination func(m, n int) + combination = func(m, n int) { + for i := m; i >= n; i-- { + comb[n] = i + if n > 1 { + combination(i-1, n-1) + } else { + var t []int + for j := comb[0]; j > 0; j-- { + t = append(t, comb[j]) + } + all = append(all, t) + } + } + } + combination(m, comb[0]) + fmt.Println(all) + return all +} diff --git a/tools/test/player.json b/tools/test/player.json new file mode 100644 index 0000000..3470532 --- /dev/null +++ b/tools/test/player.json @@ -0,0 +1 @@ +[{"SnId":217978,"Name":"Null"},{"SnId":205792,"Name":"丽泽绍高风"},{"SnId":213456,"Name":"新波"},{"SnId":217073,"Name":"凌薇"},{"SnId":209400,"Name":"浩阔"},{"SnId":216697,"Name":"彤瑞"},{"SnId":213243,"Name":"夏彤"},{"SnId":215066,"Name":"弘深"},{"SnId":217327,"Name":"弘深"},{"SnId":217868,"Name":"原谅我不能原谅你"},{"SnId":211025,"Name":"心痛之后,泪水淹没了回忆"},{"SnId":216986,"Name":"平淡而不平凡"},{"SnId":217484,"Name":"靈魂的洗禮っ"},{"SnId":217651,"Name":"小年轻"},{"SnId":216418,"Name":"尔贪恋某某的情ヽ"},{"SnId":216108,"Name":"∥我的专属、已找到"},{"SnId":217919,"Name":"狗逼到哪都彷徨"},{"SnId":216705,"Name":"雨落﹏思念起"},{"SnId":216255,"Name":"觅荷"},{"SnId":217713,"Name":"雅柏"},{"SnId":215782,"Name":"放下那份伪装"},{"SnId":217544,"Name":"你什么货色,姐就什么脸色"},{"SnId":214737,"Name":"抢房子!"},{"SnId":217170,"Name":"尾奏坏☉天使"},{"SnId":217899,"Name":"余妍"},{"SnId":217142,"Name":"听云"},{"SnId":217787,"Name":"雅昶"},{"SnId":213179,"Name":"知爱——?"},{"SnId":216741,"Name":"逸秀"},{"SnId":216791,"Name":"鸿雪"},{"SnId":217934,"Name":"展鹏"},{"SnId":212227,"Name":"低头浅笑、那情如歌如泣"},{"SnId":217804,"Name":"英华"},{"SnId":217171,"Name":"女人也要归宿。"},{"SnId":215505,"Name":"回忆尽头的青橘酸"},{"SnId":212668,"Name":"冠玉"},{"SnId":212237,"Name":"浩慨"},{"SnId":216460,"Name":"元武"},{"SnId":212464,"Name":"那一年的光景"},{"SnId":217450,"Name":"如果你也听说会不会相信我"},{"SnId":215570,"Name":"离开我以后,有没有更快乐"},{"SnId":217090,"Name":"别过来你胖到我了"},{"SnId":214431,"Name":"/右手╭╯无名指的等待"},{"SnId":217748,"Name":"姐丶独领风骚"},{"SnId":217829,"Name":"景澄"},{"SnId":215968,"Name":"飞航"},{"SnId":213646,"Name":"玉宸"},{"SnId":216351,"Name":"单曲循环入戏太深"},{"SnId":209236,"Name":"淑蕊"},{"SnId":217454,"Name":"恨到全世界都歉疚"},{"SnId":215396,"Name":"浩轩"},{"SnId":217100,"Name":"唱着、无旋律的曲"},{"SnId":210939,"Name":"与你乐"},{"SnId":217284,"Name":"鸿涛"},{"SnId":217187,"Name":"几分伤心几分痴"},{"SnId":216410,"Name":"童话中、青蛙会变成王子"},{"SnId":214666,"Name":"文心"},{"SnId":211486,"Name":"温纶"},{"SnId":215600,"Name":"建柏"},{"SnId":215952,"Name":"信鸿"},{"SnId":215383,"Name":"狗逼到哪都彷徨"},{"SnId":217272,"Name":"鹏煊"},{"SnId":216143,"Name":"浩涆"},{"SnId":215041,"Name":"情难懂"},{"SnId":211781,"Name":"我家闺蜜高端霸气上档次。"},{"SnId":216641,"Name":"氣質型\u0026#8226;"},{"SnId":217552,"Name":"花开花落、谁还在乎谁"},{"SnId":216853,"Name":"清雅"},{"SnId":213546,"Name":"紧身╮小西装"},{"SnId":209498,"Name":"新语"},{"SnId":215054,"Name":"泰宁"},{"SnId":217811,"Name":"没有、liberty"},{"SnId":214127,"Name":"花有百样红人与狗不同"},{"SnId":217657,"Name":"我喜欢飞在天上绕圈圈"},{"SnId":216577,"Name":"安静忘记"},{"SnId":216812,"Name":"英秀"},{"SnId":212774,"Name":"明辉"},{"SnId":213592,"Name":"小三会对冷漠柳眼泪吗"},{"SnId":216662,"Name":"丁辰"},{"SnId":217627,"Name":"修为"},{"SnId":215451,"Name":"指尖依稀缠绕着你飘渺的思"},{"SnId":217831,"Name":"新知"},{"SnId":215365,"Name":"英资"},{"SnId":217799,"Name":"语堂"},{"SnId":217688,"Name":"打我也不松手"},{"SnId":215340,"Name":"修永"},{"SnId":216721,"Name":"可以输但绝不能哭"},{"SnId":214475,"Name":"俊远"},{"SnId":214148,"Name":"豆蔻年华谁呢能许谁地老天"},{"SnId":216212,"Name":"zh1属于伱一人的醋坛子"},{"SnId":217647,"Name":"知道太少"},{"SnId":217193,"Name":"音乐女"},{"SnId":216092,"Name":"那天 ,你说会对我好。"},{"SnId":217944,"Name":"漫长时光里无限温柔"},{"SnId":214890,"Name":"一头牙"},{"SnId":210878,"Name":"明天,你好"},{"SnId":215750,"Name":"哈恰"},{"SnId":217428,"Name":"无尽甜蜜里旳悠悠记忆° ゞ"},{"SnId":216071,"Name":"等牛粪的鲜花〃"},{"SnId":213461,"Name":"采珊"},{"SnId":210154,"Name":"景钥"},{"SnId":213579,"Name":"建德"},{"SnId":216017,"Name":"电台少年痴迷少女 ゞ"},{"SnId":213123,"Name":"现在、有我们来持续"},{"SnId":215210,"Name":"u400001"},{"SnId":217503,"Name":"u400002"},{"SnId":213604,"Name":"u400003"},{"SnId":215886,"Name":"u400004"},{"SnId":217015,"Name":"u400005"},{"SnId":217908,"Name":"u400006"},{"SnId":215912,"Name":"u400007"},{"SnId":212329,"Name":"u400008"},{"SnId":217117,"Name":"u400009"},{"SnId":214043,"Name":"u400010"},{"SnId":217581,"Name":"u400011"},{"SnId":212214,"Name":"u400012"},{"SnId":216795,"Name":"u400013"},{"SnId":212034,"Name":"u400014"},{"SnId":216723,"Name":"u400015"},{"SnId":213847,"Name":"u400016"},{"SnId":217251,"Name":"u400017"},{"SnId":216505,"Name":"u400018"},{"SnId":211089,"Name":"u400019"},{"SnId":214425,"Name":"u400020"},{"SnId":217105,"Name":"u400021"},{"SnId":214476,"Name":"u400022"},{"SnId":216682,"Name":"u400023"},{"SnId":217524,"Name":"u400024"},{"SnId":209735,"Name":"u400025"},{"SnId":217607,"Name":"u400026"},{"SnId":216770,"Name":"u400027"},{"SnId":217680,"Name":"u400028"},{"SnId":211436,"Name":"u400029"},{"SnId":212978,"Name":"u400030"},{"SnId":215398,"Name":"u400031"},{"SnId":214683,"Name":"u400032"},{"SnId":217018,"Name":"u400033"},{"SnId":217903,"Name":"u400034"},{"SnId":213827,"Name":"u400035"},{"SnId":214303,"Name":"u400036"},{"SnId":207279,"Name":"uug"},{"SnId":204774,"Name":"Happy灬丨龙"},{"SnId":213858,"Name":"毒刺骨"}] \ No newline at end of file diff --git a/tools/upload/build_upload.bat b/tools/upload/build_upload.bat new file mode 100644 index 0000000..5e80dc4 --- /dev/null +++ b/tools/upload/build_upload.bat @@ -0,0 +1,24 @@ +cd ../../ + +set CGO_ENABLED=0 +set GOOS=linux +set GOARCH=amd64 +cd ./mgrsrv +go build +cd ../gatesrv +go build +cd ../worldsrv +go build +cd ../gamesrv +go build +cd ../minigame +go build +cd ../robot +go build +cd ../dbproxy +go build + +cd .. +cd ./tools/upload + +WinSCP.exe /console /script=upload.txt /log=upload.log \ No newline at end of file diff --git a/tools/upload/config.json b/tools/upload/config.json new file mode 100644 index 0000000..3c1c17d --- /dev/null +++ b/tools/upload/config.json @@ -0,0 +1,7 @@ +{ + "Name": "gamer", + "Passwd": "123$654", + "Addr": "192.168.10.240", + "RemoteDir": "/home/gamer/win88", + "LocalDir": "E:\\gocode\\trunk\\src\\games.yol.com\\win88" +} \ No newline at end of file diff --git a/tools/upload/doc b/tools/upload/doc new file mode 100644 index 0000000..671af4f --- /dev/null +++ b/tools/upload/doc @@ -0,0 +1,8 @@ +1.安装 WinSCP +2.执行 build_linux.bat +3.执行 winscp_upload.bat + +copy data: + +cd /home/gamer/win88/data +put E:\gocode\trunk\src\games.yol.com\win88\data\* \ No newline at end of file diff --git a/tools/upload/main.go b/tools/upload/main.go new file mode 100644 index 0000000..c07f6fd --- /dev/null +++ b/tools/upload/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "text/template" +) + +type Config struct { + Name string + Passwd string + Addr string + RemoteDir string + LocalDir string +} + +func main() { + data, err := os.ReadFile("./config.json") + if err != nil { + log.Fatal(err) + } + + var cfg Config + if err = json.Unmarshal(data, &cfg); err != nil { + log.Fatal(err) + } + + tmpl, err := template.ParseFiles("./upload.temp") + if err != nil { + log.Fatal(err) + } + + f, err := os.OpenFile("./upload.txt", os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666) + if err != nil { + log.Fatal(err) + } + + if err = tmpl.Execute(f, cfg); err != nil { + log.Fatal(err) + } + fmt.Println("success.") +} diff --git a/tools/upload/upload.temp b/tools/upload/upload.temp new file mode 100644 index 0000000..a9e74d1 --- /dev/null +++ b/tools/upload/upload.temp @@ -0,0 +1,32 @@ +echo 连接服务器 +open scp://{{.Name}}:{{.Passwd}}@{{.Addr}} + +cd {{.RemoteDir}}/mgrsrv +put {{.LocalDir}}\mgrsrv\mgrsrv +call mv mgrsrv win88_mgrsrv +chmod 755 win88_mgrsrv + +cd {{.RemoteDir}}/gatesrv +put {{.LocalDir}}\gatesrv\gatesrv +call mv gatesrv win88_gatesrv +chmod 755 win88_gatesrv + +cd {{.RemoteDir}}/worldsrv +put {{.LocalDir}}\worldsrv\worldsrv +call mv worldsrv win88_worldsrv +chmod 755 win88_worldsrv + +cd {{.RemoteDir}}/gamesrv +put {{.LocalDir}}\gamesrv\gamesrv +call mv gamesrv win88_gamesrv +chmod 755 win88_gamesrv + +cd {{.RemoteDir}}/robot +put {{.LocalDir}}\robot\robot +call mv robot win88_robot +chmod 755 win88_robot + +cd {{.RemoteDir}}/dbproxy +put {{.LocalDir}}\dbproxy\dbproxy +call mv dbproxy win88_dbproxy +chmod 755 win88_dbproxy \ No newline at end of file diff --git a/tools/upload/upload.txt b/tools/upload/upload.txt new file mode 100644 index 0000000..e1b449f --- /dev/null +++ b/tools/upload/upload.txt @@ -0,0 +1,32 @@ +echo 连接服务器 +open scp://gamer:123$654@192.168.10.240 + +cd /home/gamer/win88/mgrsrv +put E:\gocode\trunk\src\games.yol.com\win88\mgrsrv\mgrsrv +call mv mgrsrv win88_mgrsrv +chmod 755 win88_mgrsrv + +cd /home/gamer/win88/gatesrv +put E:\gocode\trunk\src\games.yol.com\win88\gatesrv\gatesrv +call mv gatesrv win88_gatesrv +chmod 755 win88_gatesrv + +cd /home/gamer/win88/worldsrv +put E:\gocode\trunk\src\games.yol.com\win88\worldsrv\worldsrv +call mv worldsrv win88_worldsrv +chmod 755 win88_worldsrv + +cd /home/gamer/win88/gamesrv +put E:\gocode\trunk\src\games.yol.com\win88\gamesrv\gamesrv +call mv gamesrv win88_gamesrv +chmod 755 win88_gamesrv + +cd /home/gamer/win88/robot +put E:\gocode\trunk\src\games.yol.com\win88\robot\robot +call mv robot win88_robot +chmod 755 win88_robot + +cd /home/gamer/win88/dbproxy +put E:\gocode\trunk\src\games.yol.com\win88\dbproxy\dbproxy +call mv dbproxy win88_dbproxy +chmod 755 win88_dbproxy \ No newline at end of file diff --git a/tools/upload/winscp_upload.bat b/tools/upload/winscp_upload.bat new file mode 100644 index 0000000..7ed618c --- /dev/null +++ b/tools/upload/winscp_upload.bat @@ -0,0 +1 @@ +WinSCP.exe /console /script=upload.txt /log=upload.log \ No newline at end of file diff --git a/tools/xlsx2binary/agc.go b/tools/xlsx2binary/agc.go new file mode 100644 index 0000000..e7422ec --- /dev/null +++ b/tools/xlsx2binary/agc.go @@ -0,0 +1,8567 @@ +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package main + +import ( + "encoding/json" + "fmt" + "os" + "runtime" + "strconv" + "strings" + + "github.com/tealeg/xlsx" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var _ = strings.Split + +func AgcConvertDB_ActSign(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ActSignArray{ + Arr: make([]*server.DB_ActSign, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ActSign{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Name = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Item_Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Grade = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Activity1(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Activity1Array{ + Arr: make([]*server.DB_Activity1, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Activity1{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Parameter = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Turn = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + data.Title = proto.String(row.Cells[3].String()) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Costype = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Costp = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Cost = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Typee = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Propid = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Value = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Getype = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_AnimalColor(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_AnimalColorArray{ + Arr: make([]*server.DB_AnimalColor, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_AnimalColor{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Desc = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.ColorChance = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ArtilleryRate(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ArtilleryRateArray{ + Arr: make([]*server.DB_ArtilleryRate, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ArtilleryRate{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Shell = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Level = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + data.Desc = proto.String(row.Cells[3].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ArtillerySkin(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ArtillerySkinArray{ + Arr: make([]*server.DB_ArtillerySkin, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ArtillerySkin{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.CannonId = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.ExprieTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Show = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Order = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + data.NameIcon = proto.String(row.Cells[7].String()) + + if len(row.Cells) < 8+1 { + break + } + + data.PicIcon = proto.String(row.Cells[8].String()) + + if len(row.Cells) < 9+1 { + break + } + + data.BaseIcon = proto.String(row.Cells[9].String()) + + if len(row.Cells) < 10+1 { + break + } + + data.ShellIcon = proto.String(row.Cells[10].String()) + + if len(row.Cells) < 11+1 { + break + } + + data.NetIcon = proto.String(row.Cells[11].String()) + + if len(row.Cells) < 12+1 { + break + } + + arrStr = strings.Split(row.Cells[12].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[12].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Vip = arrInt + + if len(row.Cells) < 13+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[13].String(), 10, 32) + data.Gold = proto.Int32(int32(temp)) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Diamond = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.Income = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.Speed = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[17].String(), 10, 32) + data.Caught = proto.Int32(int32(temp)) + + if len(row.Cells) < 18+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[18].String(), 10, 32) + data.Introduce = proto.Int32(int32(temp)) + + if len(row.Cells) < 19+1 { + break + } + + data.Source = proto.String(row.Cells[19].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_BlackWhite(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_BlackWhiteArray{ + Arr: make([]*server.DB_BlackWhite, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_BlackWhite{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.BlackOdds = arrInt + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.WhiteOdds = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_CardsJD(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_CardsJDArray{ + Arr: make([]*server.DB_CardsJD, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_CardsJD{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Card1 = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Card1Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Card1HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + data.Change1Cards = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + data.Card2 = proto.String(row.Cells[5].String()) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Card2Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Card2HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + data.Change2Cards = proto.String(row.Cells[8].String()) + + if len(row.Cells) < 9+1 { + break + } + + data.Card3 = proto.String(row.Cells[9].String()) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Card3Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.Card3HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + data.Change3Cards = proto.String(row.Cells[12].String()) + + if len(row.Cells) < 13+1 { + break + } + + data.Card4 = proto.String(row.Cells[13].String()) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Card4Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.Card4HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + data.Change4Cards = proto.String(row.Cells[16].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_CardsYuLe(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_CardsYuLeArray{ + Arr: make([]*server.DB_CardsYuLe, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_CardsYuLe{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Card1 = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Card1Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Card1HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + data.Change1Cards = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + data.Card2 = proto.String(row.Cells[5].String()) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Card2Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Card2HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + data.Change2Cards = proto.String(row.Cells[8].String()) + + if len(row.Cells) < 9+1 { + break + } + + data.Card3 = proto.String(row.Cells[9].String()) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Card3Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.Card3HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + data.Change3Cards = proto.String(row.Cells[12].String()) + + if len(row.Cells) < 13+1 { + break + } + + data.Card4 = proto.String(row.Cells[13].String()) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Card4Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.Card4HandNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + data.Change4Cards = proto.String(row.Cells[16].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ChessBilledRules(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ChessBilledRulesArray{ + Arr: make([]*server.DB_ChessBilledRules, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ChessBilledRules{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.TypeId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.WinScore = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.LoseScore = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.DrawScore = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.WinTimes = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.OtherScore = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ChessMatchRules(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ChessMatchRulesArray{ + Arr: make([]*server.DB_ChessMatchRules, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ChessMatchRules{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.ScoreMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ScoreMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.MatchScoreMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.MatchScoreMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MatchScoreLowStep = arrInt + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MatchScoreHightStep = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ChessRank(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ChessRankArray{ + Arr: make([]*server.DB_ChessRank, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ChessRank{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Score = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ClientVer(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ClientVerArray{ + Arr: make([]*server.DB_ClientVer, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ClientVer{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.PackageFlag = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + data.PackVers = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + data.GameVers = proto.String(row.Cells[3].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_CrashSearch(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_CrashSearchArray{ + Arr: make([]*server.DB_CrashSearch, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_CrashSearch{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Time = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Price = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Createroom(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_CreateroomArray{ + Arr: make([]*server.DB_Createroom, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Createroom{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.GameId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.GameSite = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.GoldRange = arrInt + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.BetRange = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Fish(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FishArray{ + Arr: make([]*server.DB_Fish, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Fish{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + data.NameE = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Gold = arrInt + + if len(row.Cells) < 4+1 { + break + } + + data.Icon = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Speed = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Exp = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.FrameCnt = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.FrameDelay = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Rate = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.ShowType = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.Show = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[12].String(), 10, 32) + data.ShowScale = proto.Int32(int32(temp)) + + if len(row.Cells) < 13+1 { + break + } + + arrStr = strings.Split(row.Cells[13].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[13].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.ShowPos = arrInt + + if len(row.Cells) < 14+1 { + break + } + + data.DieSound = proto.String(row.Cells[14].String()) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.DieFrame = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.DieRotate = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[17].String(), 10, 32) + data.DieEffect = proto.Int32(int32(temp)) + + if len(row.Cells) < 18+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[18].String(), 10, 32) + data.DieShake = proto.Int32(int32(temp)) + + if len(row.Cells) < 19+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[19].String(), 10, 32) + data.ShakeRange = proto.Int32(int32(temp)) + + if len(row.Cells) < 20+1 { + break + } + + data.Shape = proto.String(row.Cells[20].String()) + + if len(row.Cells) < 21+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[21].String(), 10, 32) + data.IsBoss = proto.Int32(int32(temp)) + + if len(row.Cells) < 22+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[22].String(), 10, 32) + data.ResId = proto.Int32(int32(temp)) + + if len(row.Cells) < 23+1 { + break + } + + data.DieParticle = proto.String(row.Cells[23].String()) + + if len(row.Cells) < 24+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[24].String(), 10, 32) + data.GroupShape = proto.Int32(int32(temp)) + + if len(row.Cells) < 25+1 { + break + } + + arrStr = strings.Split(row.Cells[25].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[25].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.GroupFishes = arrInt + + if len(row.Cells) < 26+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[26].String(), 10, 32) + data.Zorder = proto.Int32(int32(temp)) + + if len(row.Cells) < 27+1 { + break + } + + data.ResPng = proto.String(row.Cells[27].String()) + + if len(row.Cells) < 28+1 { + break + } + + data.ResPlist = proto.String(row.Cells[28].String()) + + if len(row.Cells) < 29+1 { + break + } + + data.ExportJson = proto.String(row.Cells[29].String()) + + if len(row.Cells) < 30+1 { + break + } + + data.AimIcon = proto.String(row.Cells[30].String()) + + if len(row.Cells) < 31+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[31].String(), 10, 32) + data.GameId = proto.Int32(int32(temp)) + + if len(row.Cells) < 32+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[32].String(), 10, 32) + data.Sort = proto.Int32(int32(temp)) + + if len(row.Cells) < 33+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[33].String(), 10, 32) + data.FishType = proto.Int32(int32(temp)) + + if len(row.Cells) < 34+1 { + break + } + + data.RandomCoin = proto.String(row.Cells[34].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FishOut(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FishOutArray{ + Arr: make([]*server.DB_FishOut, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FishOut{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.SceneType = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Exp = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Multiple = arrInt + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Path = arrInt + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Count = arrInt + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.RefreshInterval = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Speed = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Event = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FishPath(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FishPathArray{ + Arr: make([]*server.DB_FishPath, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FishPath{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.AppearTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.DisappearTime = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FishRoom(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FishRoomArray{ + Arr: make([]*server.DB_FishRoom, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FishRoom{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.RoomId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + data.SumGold1 = proto.String(row.Cells[3].String()) + + if len(row.Cells) < 4+1 { + break + } + + data.SumGold2 = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + data.SumGold3 = proto.String(row.Cells[5].String()) + + if len(row.Cells) < 6+1 { + break + } + + data.SumGold4 = proto.String(row.Cells[6].String()) + + if len(row.Cells) < 7+1 { + break + } + + data.SumGold5 = proto.String(row.Cells[7].String()) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.BossCDTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.LittleBossCDTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + data.EnableBoss = proto.String(row.Cells[10].String()) + + if len(row.Cells) < 11+1 { + break + } + + data.EnableLittleBoss = proto.String(row.Cells[11].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FishSkill(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FishSkillArray{ + Arr: make([]*server.DB_FishSkill, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FishSkill{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Vip = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Consume = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Item = arrInt + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.OtherConsumer = arrInt + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Multiple = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Duration = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.SkillGroups = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.GCD = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Cooldown = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + data.Hidden = proto.String(row.Cells[11].String()) + + if len(row.Cells) < 12+1 { + break + } + + data.Describe = proto.String(row.Cells[12].String()) + + if len(row.Cells) < 13+1 { + break + } + + data.Boss = proto.String(row.Cells[13].String()) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.Limit = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.Mutex = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[17].String(), 10, 32) + data.MutexTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 18+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[18].String(), 10, 32) + data.Fury = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FortuneGod_Odds(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FortuneGod_OddsArray{ + Arr: make([]*server.DB_FortuneGod_Odds, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FortuneGod_Odds{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Rateodds3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Rateodds4 = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Rateodds5 = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FortuneGod_TurnRate(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FortuneGod_TurnRateArray{ + Arr: make([]*server.DB_FortuneGod_TurnRate, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FortuneGod_TurnRate{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.ReturnRateMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ReturnRateMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Chance = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FortuneGod_Weight(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FortuneGod_WeightArray{ + Arr: make([]*server.DB_FortuneGod_Weight, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FortuneGod_Weight{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Weight = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_FortuneGod_WeightCondition(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_FortuneGod_WeightConditionArray{ + Arr: make([]*server.DB_FortuneGod_WeightCondition, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_FortuneGod_WeightCondition{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.IsNew = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.BetScope = arrInt + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.TrueCalcRate = arrInt + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.WeightId = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GamMatchLV(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GamMatchLVArray{ + Arr: make([]*server.DB_GamMatchLV, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GamMatchLV{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + arrStr = strings.Split(row.Cells[1].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[1].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Star = arrInt + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Star2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.AwardType1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.AwardId1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Number1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.AwardType2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.AwardId2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Number2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.AwardType3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.AwardId3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[12].String(), 10, 32) + data.Number3 = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameCoinPool(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameCoinPoolArray{ + Arr: make([]*server.DB_GameCoinPool, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameCoinPool{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 64) + data.InitValue = proto.Int64(int64(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 64) + data.LowerLimit = proto.Int64(int64(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 64) + data.UpperLimit = proto.Int64(int64(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 64) + data.QuDu = proto.Int64(int64(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.UpperOdds = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.UpperOddsMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.LowerOdds = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.LowerOddsMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.ProfitRate = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameFree(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameFreeArray{ + Arr: make([]*server.DB_GameFree, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameFree{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + data.Title = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.GameId = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.GameMode = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.FreeMode = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.GameRule = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.GameType = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.SceneType = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.RankType = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.SceneAdd = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + data.Desc = proto.String(row.Cells[11].String()) + + if len(row.Cells) < 12+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[12].String(), 10, 32) + data.ShowType = proto.Int32(int32(temp)) + + if len(row.Cells) < 13+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[13].String(), 10, 32) + data.SubShowType = proto.Int32(int32(temp)) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Flag = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.TestTakeCoin = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.ShowId = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[17].String(), 10, 64) + data.LimitCoin = proto.Int64(int64(temp)) + + if len(row.Cells) < 18+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[18].String(), 10, 64) + data.MaxCoinLimit = proto.Int64(int64(temp)) + + if len(row.Cells) < 19+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[19].String(), 10, 32) + data.ServiceFee = proto.Int32(int32(temp)) + + if len(row.Cells) < 20+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[20].String(), 10, 64) + data.LowerThanKick = proto.Int64(int64(temp)) + + if len(row.Cells) < 21+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[21].String(), 10, 32) + data.BaseScore = proto.Int32(int32(temp)) + + if len(row.Cells) < 22+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[22].String(), 10, 32) + data.Turn = proto.Int32(int32(temp)) + + if len(row.Cells) < 23+1 { + break + } + + data.BetDec = proto.String(row.Cells[23].String()) + + if len(row.Cells) < 24+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[24].String(), 10, 32) + data.Bot = proto.Int32(int32(temp)) + + if len(row.Cells) < 25+1 { + break + } + + arrStr = strings.Split(row.Cells[25].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[25].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Ai = arrInt + + if len(row.Cells) < 26+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[26].String(), 10, 32) + data.Banker = proto.Int32(int32(temp)) + + if len(row.Cells) < 27+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[27].String(), 10, 32) + data.MaxChip = proto.Int32(int32(temp)) + + if len(row.Cells) < 28+1 { + break + } + + arrStr = strings.Split(row.Cells[28].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[28].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.OtherIntParams = arrInt64 + + if len(row.Cells) < 29+1 { + break + } + + arrStr = strings.Split(row.Cells[29].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[29].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.ChessScoreParams = arrInt64 + + if len(row.Cells) < 30+1 { + break + } + + arrStr = strings.Split(row.Cells[30].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[30].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.RankScoreParams = arrInt64 + + if len(row.Cells) < 31+1 { + break + } + + arrStr = strings.Split(row.Cells[31].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[31].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Jackpot = arrInt + + if len(row.Cells) < 32+1 { + break + } + + arrStr = strings.Split(row.Cells[32].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[32].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.RobotNumRng = arrInt + + if len(row.Cells) < 33+1 { + break + } + + arrStr = strings.Split(row.Cells[33].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[33].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.RobotTakeCoin = arrInt64 + + if len(row.Cells) < 34+1 { + break + } + + arrStr = strings.Split(row.Cells[34].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[34].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.RobotLimitCoin = arrInt64 + + if len(row.Cells) < 35+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[35].String(), 10, 32) + data.BetLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 36+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[36].String(), 10, 32) + data.TaxRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 37+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[37].String(), 10, 32) + data.SameIpLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 38+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[38].String(), 10, 32) + data.SamePlaceLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 39+1 { + break + } + + data.GameDif = proto.String(row.Cells[39].String()) + + if len(row.Cells) < 40+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[40].String(), 10, 32) + data.GameClass = proto.Int32(int32(temp)) + + if len(row.Cells) < 41+1 { + break + } + + data.PlatformName = proto.String(row.Cells[41].String()) + + if len(row.Cells) < 42+1 { + break + } + + arrStr = strings.Split(row.Cells[42].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[42].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MaxBetCoin = arrInt + + if len(row.Cells) < 43+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[43].String(), 10, 32) + data.PlayNumLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 44+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[44].String(), 10, 32) + data.CreateRoomNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 45+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[45].String(), 10, 32) + data.MatchTrueMan = proto.Int32(int32(temp)) + + if len(row.Cells) < 46+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[46].String(), 10, 32) + data.PlayerWaterRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 47+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[47].String(), 10, 32) + data.MatchMode = proto.Int32(int32(temp)) + + if len(row.Cells) < 48+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[48].String(), 10, 32) + data.KillingRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 49+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[49].String(), 10, 32) + data.BetWaterRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 50+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[50].String(), 10, 32) + data.Lottery = proto.Int32(int32(temp)) + + if len(row.Cells) < 51+1 { + break + } + + data.LotteryConfig = proto.String(row.Cells[51].String()) + + if len(row.Cells) < 52+1 { + break + } + + arrStr = strings.Split(row.Cells[52].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[52].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.BalanceLine = arrInt + + if len(row.Cells) < 53+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[53].String(), 10, 32) + data.JackpotRatio = proto.Int32(int32(temp)) + + if len(row.Cells) < 54+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[54].String(), 10, 32) + data.JackpotMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 55+1 { + break + } + + arrStr = strings.Split(row.Cells[55].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[55].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.ChessGradeLimit = arrInt + + if len(row.Cells) < 56+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[56].String(), 10, 32) + data.LeaveDeduct = proto.Int32(int32(temp)) + + if len(row.Cells) < 57+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[57].String(), 10, 32) + data.LeaveCombat = proto.Int32(int32(temp)) + + if len(row.Cells) < 58+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[58].String(), 10, 32) + data.IntuseCannonMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 59+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[59].String(), 10, 32) + data.IntuseCannonMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 60+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[60].String(), 10, 32) + data.BossDrainageBet = proto.Int32(int32(temp)) + + if len(row.Cells) < 61+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[61].String(), 10, 32) + data.Draw = proto.Int32(int32(temp)) + + if len(row.Cells) < 62+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[62].String(), 10, 64) + data.Fluctuate = proto.Int64(int64(temp)) + + if len(row.Cells) < 63+1 { + break + } + + data.FluctuateMax = proto.String(row.Cells[63].String()) + + if len(row.Cells) < 64+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[64].String(), 10, 32) + data.Ratio = proto.Int32(int32(temp)) + + if len(row.Cells) < 65+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[65].String(), 10, 64) + data.MinValue = proto.Int64(int64(temp)) + + if len(row.Cells) < 66+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[66].String(), 10, 64) + data.MaxValue = proto.Int64(int64(temp)) + + if len(row.Cells) < 67+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[67].String(), 10, 32) + data.DrainageBet = proto.Int32(int32(temp)) + + if len(row.Cells) < 68+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[68].String(), 10, 32) + data.DiamondDrop = proto.Int32(int32(temp)) + + if len(row.Cells) < 69+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[69].String(), 10, 32) + data.NegativeMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 70+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[70].String(), 10, 32) + data.RatioMax = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameItem(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameItemArray{ + Arr: make([]*server.DB_GameItem, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameItem{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.ShowLocation = arrInt + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Classify = arrInt + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Effect0 = arrInt + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Effect = arrInt + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.SaleType = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.SaleGold = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Composition = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.CompositionMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.Time = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + data.Location = proto.String(row.Cells[12].String()) + + if len(row.Cells) < 13+1 { + break + } + + data.Describe = proto.String(row.Cells[13].String()) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 64) + data.Num = proto.Int64(int64(temp)) + + if len(row.Cells) < 15+1 { + break + } + + data.Value = proto.String(row.Cells[15].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameMatchLevel(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameMatchLevelArray{ + Arr: make([]*server.DB_GameMatchLevel, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameMatchLevel{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.GameFreeId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.MatchLevel = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.RobotUpRatio = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.UpGrade = arrInt + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.UpGradeOdds = arrInt + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.DownGrade = arrInt + + if len(row.Cells) < 7+1 { + break + } + + arrStr = strings.Split(row.Cells[7].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[7].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.DownGradeOdds = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameRule(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameRuleArray{ + Arr: make([]*server.DB_GameRule, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameRule{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.GameId = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.GameMode = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Params = arrInt + + if len(row.Cells) < 5+1 { + break + } + + data.RuleDesc = proto.String(row.Cells[5].String()) + + if len(row.Cells) < 6+1 { + break + } + + data.GameDif = proto.String(row.Cells[6].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GameSubsidy(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GameSubsidyArray{ + Arr: make([]*server.DB_GameSubsidy, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GameSubsidy{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.LimitNum = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Get = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Times = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Game_Drop(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Game_DropArray{ + Arr: make([]*server.DB_Game_Drop, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Game_Drop{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.GameId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Bet = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.ItemName1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.ItemId1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Rate1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Amount1 = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Game_Introduction(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Game_IntroductionArray{ + Arr: make([]*server.DB_Game_Introduction, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Game_Introduction{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + data.Story = proto.String(row.Cells[3].String()) + + if len(row.Cells) < 4+1 { + break + } + + data.AwardTitle = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.LevelMax = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Game_Pet(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Game_PetArray{ + Arr: make([]*server.DB_Game_Pet, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Game_Pet{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.PetId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Grade = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Level = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Fragment = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Amount = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.AwardType = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Award = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.AwardRate = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Game_Role(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Game_RoleArray{ + Arr: make([]*server.DB_Game_Role, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Game_Role{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.RoleId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Grade = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Level = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Fragment = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Amount = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.AwardType = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Award = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.AwardRate = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_GiftBox(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_GiftBoxArray{ + Arr: make([]*server.DB_GiftBox, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_GiftBox{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Rate = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + if row.Cells[2].String() != "" { + pairs := strings.Split(row.Cells[2].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.ItemID = resultMap + } + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_IceAgeElementRate(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_IceAgeElementRateArray{ + Arr: make([]*server.DB_IceAgeElementRate, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_IceAgeElementRate{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.ModeName = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ModeType = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Params = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Legend_Odds(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Legend_OddsArray{ + Arr: make([]*server.DB_Legend_Odds, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Legend_Odds{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Rateodds3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Rateodds4 = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Rateodds5 = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Legend_TurnRate(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Legend_TurnRateArray{ + Arr: make([]*server.DB_Legend_TurnRate, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Legend_TurnRate{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.ReturnRateMin = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ReturnRateMax = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Chance = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Legend_Weight(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Legend_WeightArray{ + Arr: make([]*server.DB_Legend_Weight, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Legend_Weight{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Weight = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Legend_WeightCondition(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Legend_WeightConditionArray{ + Arr: make([]*server.DB_Legend_WeightCondition, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Legend_WeightCondition{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.IsNew = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + arrStr = strings.Split(row.Cells[2].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[2].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.BetScope = arrInt + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.TrueCalcRate = arrInt + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.WeightId = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_MatchRank(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_MatchRankArray{ + Arr: make([]*server.DB_MatchRank, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_MatchRank{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + arrStr = strings.Split(row.Cells[1].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[1].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.RankStar = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Name(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_NameArray{ + Arr: make([]*server.DB_Name, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Name{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_NameBoy(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_NameBoyArray{ + Arr: make([]*server.DB_NameBoy, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_NameBoy{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_NameGirl(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_NameGirlArray{ + Arr: make([]*server.DB_NameGirl, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_NameGirl{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_NewPlayer(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_NewPlayerArray{ + Arr: make([]*server.DB_NewPlayer, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_NewPlayer{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Condition1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 64) + data.ConditionValue1 = proto.Int64(int64(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Condition2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 64) + data.ConditionValue2 = proto.Int64(int64(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Bond = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.AddType = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 64) + data.AddMax = proto.Int64(int64(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 64) + data.AddMin = proto.Int64(int64(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.TianHuRate = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_PhoneLottery(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_PhoneLotteryArray{ + Arr: make([]*server.DB_PhoneLottery, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_PhoneLottery{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Name = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Item_Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Grade = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Odd = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Oddrate1 = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Odd2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Oddrate2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Odd3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Oddrate3 = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.Oddrate4 = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_PlayerExp(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_PlayerExpArray{ + Arr: make([]*server.DB_PlayerExp, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_PlayerExp{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Exp = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_PlayerInfo(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_PlayerInfoArray{ + Arr: make([]*server.DB_PlayerInfo, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_PlayerInfo{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + data.City = proto.String(row.Cells[0].String()) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.Head = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.Name = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Sex = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_PlayerType(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_PlayerTypeArray{ + Arr: make([]*server.DB_PlayerType, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_PlayerType{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.GameFreeId = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.PayLowerLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.PayUpperLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.GameTimeLowerLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.GameTimeUpperLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.TotalInLowerLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.TotalInUpperLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.OddsLowerLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.OddsUpperLimit = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.LuckyRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[12].String(), 10, 32) + data.ChangeCardRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 13+1 { + break + } + + arrStr = strings.Split(row.Cells[13].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[13].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.CardValueRange = arrInt + + if len(row.Cells) < 14+1 { + break + } + + arrStr = strings.Split(row.Cells[14].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[14].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MatchPriority = arrInt + + if len(row.Cells) < 15+1 { + break + } + + arrStr = strings.Split(row.Cells[15].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[15].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.ExcludeMatch = arrInt + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.CardLibRate = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + arrStr = strings.Split(row.Cells[17].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[17].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.CardLibArr = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_PotOdd(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_PotOddArray{ + Arr: make([]*server.DB_PotOdd, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_PotOdd{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + data.Title = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.VipOdd = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_RankCycle(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_RankCycleArray{ + Arr: make([]*server.DB_RankCycle, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_RankCycle{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Start = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + data.End = proto.String(row.Cells[2].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_RankLevel(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_RankLevelArray{ + Arr: make([]*server.DB_RankLevel, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_RankLevel{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.RankType = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Level = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + data.Name = proto.String(row.Cells[3].String()) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 64) + data.Score = proto.Int64(int64(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_RankReward(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_RankRewardArray{ + Arr: make([]*server.DB_RankReward, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_RankReward{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.RankType = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Level = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Award1Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Award1Num = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Award2Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.Award2Num = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[7].String(), 10, 32) + data.Award3Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Award3Num = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Sensitive_Words(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_Sensitive_WordsArray{ + Arr: make([]*server.DB_Sensitive_Words, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Sensitive_Words{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Sensitive_Words = proto.String(row.Cells[1].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Shop(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ShopArray{ + Arr: make([]*server.DB_Shop, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Shop{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.VipLevel = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ItemId = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Page = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Order = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.Type = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Location = arrInt + + if len(row.Cells) < 7+1 { + break + } + + data.Picture = proto.String(row.Cells[7].String()) + + if len(row.Cells) < 8+1 { + break + } + + data.Name = proto.String(row.Cells[8].String()) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Ad = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.AdTime = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[11].String(), 10, 32) + data.RepeatTimes = proto.Int32(int32(temp)) + + if len(row.Cells) < 12+1 { + break + } + + arrStr = strings.Split(row.Cells[12].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[12].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.CoolingTime = arrInt + + if len(row.Cells) < 13+1 { + break + } + + arrStr = strings.Split(row.Cells[13].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[13].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Label = arrInt + + if len(row.Cells) < 14+1 { + break + } + + arrStr = strings.Split(row.Cells[14].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[14].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.AddArea = arrInt + + if len(row.Cells) < 15+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[15].String(), 10, 32) + data.Ratio = proto.Int32(int32(temp)) + + if len(row.Cells) < 16+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[16].String(), 10, 32) + data.Amount = proto.Int32(int32(temp)) + + if len(row.Cells) < 17+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[17].String(), 10, 32) + data.CostType = proto.Int32(int32(temp)) + + if len(row.Cells) < 18+1 { + break + } + + arrStr = strings.Split(row.Cells[18].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[18].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.CostArea = arrInt + + if len(row.Cells) < 19+1 { + break + } + + if row.Cells[19].String() != "" { + pairs := strings.Split(row.Cells[19].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.Award = resultMap + } + + if len(row.Cells) < 20+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[20].String(), 10, 32) + data.EndTime = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_SlotRateWeight(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_SlotRateWeightArray{ + Arr: make([]*server.DB_SlotRateWeight, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_SlotRateWeight{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.GameFreeId = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Pos = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + arrStr = strings.Split(row.Cells[3].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[3].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.NormCol1 = arrInt + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.NormCol2 = arrInt + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.NormCol3 = arrInt + + if len(row.Cells) < 6+1 { + break + } + + arrStr = strings.Split(row.Cells[6].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[6].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.NormCol4 = arrInt + + if len(row.Cells) < 7+1 { + break + } + + arrStr = strings.Split(row.Cells[7].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[7].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.NormCol5 = arrInt + + if len(row.Cells) < 8+1 { + break + } + + arrStr = strings.Split(row.Cells[8].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[8].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.FreeCol1 = arrInt + + if len(row.Cells) < 9+1 { + break + } + + arrStr = strings.Split(row.Cells[9].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[9].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.FreeCol2 = arrInt + + if len(row.Cells) < 10+1 { + break + } + + arrStr = strings.Split(row.Cells[10].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[10].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.FreeCol3 = arrInt + + if len(row.Cells) < 11+1 { + break + } + + arrStr = strings.Split(row.Cells[11].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[11].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.FreeCol4 = arrInt + + if len(row.Cells) < 12+1 { + break + } + + arrStr = strings.Split(row.Cells[12].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[12].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.FreeCol5 = arrInt + + if len(row.Cells) < 13+1 { + break + } + + arrStr = strings.Split(row.Cells[13].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[13].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MaryOut = arrInt + + if len(row.Cells) < 14+1 { + break + } + + arrStr = strings.Split(row.Cells[14].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[14].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.MaryMid = arrInt + + if len(row.Cells) < 15+1 { + break + } + + arrStr = strings.Split(row.Cells[15].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[15].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.JackPot = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_SystemChance(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_SystemChanceArray{ + Arr: make([]*server.DB_SystemChance, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_SystemChance{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Desc = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.ChanceType = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.Coin = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.Rate = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Task(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_TaskArray{ + Arr: make([]*server.DB_Task, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Task{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.ActivityType = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[4].String(), 10, 32) + data.TaskType = proto.Int32(int32(temp)) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 64) + data.TargetTimes = proto.Int64(int64(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 64) + data.FinishTimes = proto.Int64(int64(temp)) + + if len(row.Cells) < 7+1 { + break + } + + if row.Cells[7].String() != "" { + pairs := strings.Split(row.Cells[7].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.Award = resultMap + } + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.GameType = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + arrStr = strings.Split(row.Cells[9].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[9].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Position = arrInt + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_ThirdPlatformGameMapping(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_ThirdPlatformGameMappingArray{ + Arr: make([]*server.DB_ThirdPlatformGameMapping, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_ThirdPlatformGameMapping{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[1].String(), 10, 32) + data.SystemGameID = proto.Int32(int32(temp)) + + if len(row.Cells) < 2+1 { + break + } + + data.ThirdPlatformName = proto.String(row.Cells[2].String()) + + if len(row.Cells) < 3+1 { + break + } + + data.ThirdGameID = proto.String(row.Cells[3].String()) + + if len(row.Cells) < 4+1 { + break + } + + data.Desc = proto.String(row.Cells[4].String()) + + if len(row.Cells) < 5+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[5].String(), 10, 32) + data.ScreenOrientationType = proto.Int32(int32(temp)) + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.ThirdID = proto.Int32(int32(temp)) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_Tips(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_TipsArray{ + Arr: make([]*server.DB_Tips, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_Tips{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + arrStr = strings.Split(row.Cells[1].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[1].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.GameId = arrInt + + if len(row.Cells) < 2+1 { + break + } + + data.Des = proto.String(row.Cells[2].String()) + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func AgcConvertDB_VIP(fi, fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr := &server.DB_VIPArray{ + Arr: make([]*server.DB_VIP, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.DB_VIP{} + + for { + + if len(row.Cells) < 0+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data.Id = proto.Int32(int32(temp)) + + if len(row.Cells) < 1+1 { + break + } + + data.Name = proto.String(row.Cells[1].String()) + + if len(row.Cells) < 2+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[2].String(), 10, 32) + data.Count = proto.Int32(int32(temp)) + + if len(row.Cells) < 3+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[3].String(), 10, 32) + data.VipExp = proto.Int32(int32(temp)) + + if len(row.Cells) < 4+1 { + break + } + + arrStr = strings.Split(row.Cells[4].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[4].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Privilege1 = arrInt + + if len(row.Cells) < 5+1 { + break + } + + arrStr = strings.Split(row.Cells[5].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[5].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Privilege2 = arrInt + + if len(row.Cells) < 6+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[6].String(), 10, 32) + data.ShopId2 = proto.Int32(int32(temp)) + + if len(row.Cells) < 7+1 { + break + } + + arrStr = strings.Split(row.Cells[7].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[7].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Privilege3 = arrInt + + if len(row.Cells) < 8+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[8].String(), 10, 32) + data.Privilege4 = proto.Int32(int32(temp)) + + if len(row.Cells) < 9+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[9].String(), 10, 32) + data.Privilege5 = proto.Int32(int32(temp)) + + if len(row.Cells) < 10+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[10].String(), 10, 32) + data.Privilege6 = proto.Int32(int32(temp)) + + if len(row.Cells) < 11+1 { + break + } + + if row.Cells[11].String() != "" { + pairs := strings.Split(row.Cells[11].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.Privilege7 = resultMap + } + + if len(row.Cells) < 12+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[12].String(), 10, 32) + data.Privilege7Price = proto.Int32(int32(temp)) + + if len(row.Cells) < 13+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[13].String(), 10, 32) + data.ShopId7 = proto.Int32(int32(temp)) + + if len(row.Cells) < 14+1 { + break + } + + temp, _ = strconv.ParseInt(row.Cells[14].String(), 10, 32) + data.Privilege8 = proto.Int32(int32(temp)) + + if len(row.Cells) < 15+1 { + break + } + + arrStr = strings.Split(row.Cells[15].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[15].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.Param = arrInt + + if len(row.Cells) < 16+1 { + break + } + + arrStr = strings.Split(row.Cells[16].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[16].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.RewardOutlineID = arrInt + + if len(row.Cells) < 17+1 { + break + } + + if row.Cells[17].String() != "" { + pairs := strings.Split(row.Cells[17].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.Award = resultMap + } + + if len(row.Cells) < 18+1 { + break + } + + if row.Cells[18].String() != "" { + arrStr = strings.Split(row.Cells[18].String(), "|") + data.ParamName = arrStr + } + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil { + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + +func main() { + + AgcConvertDB_ActSign(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ActSign.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ActSign.dat`) + + AgcConvertDB_Activity1(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Activity1.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Activity1.dat`) + + AgcConvertDB_AnimalColor(`D:\trunk\src\games.yol.com\win88\xlsx\DB_AnimalColor.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_AnimalColor.dat`) + + AgcConvertDB_ArtilleryRate(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ArtilleryRate.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ArtilleryRate.dat`) + + AgcConvertDB_ArtillerySkin(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ArtillerySkin.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ArtillerySkin.dat`) + + AgcConvertDB_BlackWhite(`D:\trunk\src\games.yol.com\win88\xlsx\DB_BlackWhite.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_BlackWhite.dat`) + + AgcConvertDB_CardsJD(`D:\trunk\src\games.yol.com\win88\xlsx\DB_CardsJD.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_CardsJD.dat`) + + AgcConvertDB_CardsYuLe(`D:\trunk\src\games.yol.com\win88\xlsx\DB_CardsYuLe.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_CardsYuLe.dat`) + + AgcConvertDB_ChessBilledRules(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ChessBilledRules.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ChessBilledRules.dat`) + + AgcConvertDB_ChessMatchRules(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ChessMatchRules.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ChessMatchRules.dat`) + + AgcConvertDB_ChessRank(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ChessRank.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ChessRank.dat`) + + AgcConvertDB_ClientVer(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ClientVer.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ClientVer.dat`) + + AgcConvertDB_CrashSearch(`D:\trunk\src\games.yol.com\win88\xlsx\DB_CrashSearch.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_CrashSearch.dat`) + + AgcConvertDB_Createroom(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Createroom.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Createroom.dat`) + + AgcConvertDB_Fish(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Fish.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Fish.dat`) + + AgcConvertDB_FishOut(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FishOut.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FishOut.dat`) + + AgcConvertDB_FishPath(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FishPath.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FishPath.dat`) + + AgcConvertDB_FishRoom(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FishRoom.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FishRoom.dat`) + + AgcConvertDB_FishSkill(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FishSkill.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FishSkill.dat`) + + AgcConvertDB_FortuneGod_Odds(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FortuneGod_Odds.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FortuneGod_Odds.dat`) + + AgcConvertDB_FortuneGod_TurnRate(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FortuneGod_TurnRate.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FortuneGod_TurnRate.dat`) + + AgcConvertDB_FortuneGod_Weight(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FortuneGod_Weight.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FortuneGod_Weight.dat`) + + AgcConvertDB_FortuneGod_WeightCondition(`D:\trunk\src\games.yol.com\win88\xlsx\DB_FortuneGod_WeightCondition.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_FortuneGod_WeightCondition.dat`) + + AgcConvertDB_GamMatchLV(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GamMatchLV.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GamMatchLV.dat`) + + AgcConvertDB_GameCoinPool(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameCoinPool.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameCoinPool.dat`) + + AgcConvertDB_GameFree(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameFree.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameFree.dat`) + + AgcConvertDB_GameItem(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameItem.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameItem.dat`) + + AgcConvertDB_GameMatchLevel(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameMatchLevel.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameMatchLevel.dat`) + + AgcConvertDB_GameRule(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameRule.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameRule.dat`) + + AgcConvertDB_GameSubsidy(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GameSubsidy.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GameSubsidy.dat`) + + AgcConvertDB_Game_Drop(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Game_Drop.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Game_Drop.dat`) + + AgcConvertDB_Game_Introduction(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Game_Introduction.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Game_Introduction.dat`) + + AgcConvertDB_Game_Pet(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Game_Pet.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Game_Pet.dat`) + + AgcConvertDB_Game_Role(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Game_Role.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Game_Role.dat`) + + AgcConvertDB_GiftBox(`D:\trunk\src\games.yol.com\win88\xlsx\DB_GiftBox.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_GiftBox.dat`) + + AgcConvertDB_IceAgeElementRate(`D:\trunk\src\games.yol.com\win88\xlsx\DB_IceAgeElementRate.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_IceAgeElementRate.dat`) + + AgcConvertDB_Legend_Odds(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Legend_Odds.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Legend_Odds.dat`) + + AgcConvertDB_Legend_TurnRate(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Legend_TurnRate.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Legend_TurnRate.dat`) + + AgcConvertDB_Legend_Weight(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Legend_Weight.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Legend_Weight.dat`) + + AgcConvertDB_Legend_WeightCondition(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Legend_WeightCondition.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Legend_WeightCondition.dat`) + + AgcConvertDB_MatchRank(`D:\trunk\src\games.yol.com\win88\xlsx\DB_MatchRank.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_MatchRank.dat`) + + AgcConvertDB_Name(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Name.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Name.dat`) + + AgcConvertDB_NameBoy(`D:\trunk\src\games.yol.com\win88\xlsx\DB_NameBoy.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_NameBoy.dat`) + + AgcConvertDB_NameGirl(`D:\trunk\src\games.yol.com\win88\xlsx\DB_NameGirl.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_NameGirl.dat`) + + AgcConvertDB_NewPlayer(`D:\trunk\src\games.yol.com\win88\xlsx\DB_NewPlayer.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_NewPlayer.dat`) + + AgcConvertDB_PhoneLottery(`D:\trunk\src\games.yol.com\win88\xlsx\DB_PhoneLottery.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_PhoneLottery.dat`) + + AgcConvertDB_PlayerExp(`D:\trunk\src\games.yol.com\win88\xlsx\DB_PlayerExp.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_PlayerExp.dat`) + + AgcConvertDB_PlayerInfo(`D:\trunk\src\games.yol.com\win88\xlsx\DB_PlayerInfo.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_PlayerInfo.dat`) + + AgcConvertDB_PlayerType(`D:\trunk\src\games.yol.com\win88\xlsx\DB_PlayerType.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_PlayerType.dat`) + + AgcConvertDB_PotOdd(`D:\trunk\src\games.yol.com\win88\xlsx\DB_PotOdd.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_PotOdd.dat`) + + AgcConvertDB_RankCycle(`D:\trunk\src\games.yol.com\win88\xlsx\DB_RankCycle.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_RankCycle.dat`) + + AgcConvertDB_RankLevel(`D:\trunk\src\games.yol.com\win88\xlsx\DB_RankLevel.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_RankLevel.dat`) + + AgcConvertDB_RankReward(`D:\trunk\src\games.yol.com\win88\xlsx\DB_RankReward.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_RankReward.dat`) + + AgcConvertDB_Sensitive_Words(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Sensitive_Words.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Sensitive_Words.dat`) + + AgcConvertDB_Shop(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Shop.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Shop.dat`) + + AgcConvertDB_SlotRateWeight(`D:\trunk\src\games.yol.com\win88\xlsx\DB_SlotRateWeight.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_SlotRateWeight.dat`) + + AgcConvertDB_SystemChance(`D:\trunk\src\games.yol.com\win88\xlsx\DB_SystemChance.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_SystemChance.dat`) + + AgcConvertDB_Task(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Task.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Task.dat`) + + AgcConvertDB_ThirdPlatformGameMapping(`D:\trunk\src\games.yol.com\win88\xlsx\DB_ThirdPlatformGameMapping.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_ThirdPlatformGameMapping.dat`) + + AgcConvertDB_Tips(`D:\trunk\src\games.yol.com\win88\xlsx\DB_Tips.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_Tips.dat`) + + AgcConvertDB_VIP(`D:\trunk\src\games.yol.com\win88\xlsx\DB_VIP.xlsx`, `D:\trunk\src\games.yol.com\win88\data\DB_VIP.dat`) + +} diff --git a/tools/xlsx2proto/config.go b/tools/xlsx2proto/config.go new file mode 100644 index 0000000..3e616e0 --- /dev/null +++ b/tools/xlsx2proto/config.go @@ -0,0 +1,83 @@ +// config +package main + +import ( + "os" + "path/filepath" + "strings" + "text/template" + + "mongo.games.com/goserver/core" +) + +var XlsxFiles = make(map[string]string) + +var Config = Configuration{} + +type Configuration struct { + WorkPath string + XlsxPath string + ProtoPath string + ProtoLuaPath string + GoFilePath string + TsFilePath string + ConvertToolPath string + CppFilePath string + DataPath string + ProtoFile string +} + +func (this *Configuration) Name() string { + return "proto" +} + +func (this *Configuration) Init() (err error) { + + protoAbsPath := filepath.Join(this.WorkPath, this.ProtoPath) + err = os.MkdirAll(protoAbsPath, os.ModePerm) + if err != nil { + return + } + + xlsxAbsPath := filepath.Join(this.WorkPath, this.XlsxPath) + var fis []os.DirEntry + fis, err = os.ReadDir(xlsxAbsPath) + if err != nil { + return + } + + for _, v := range fis { + if !v.IsDir() { + if !strings.HasSuffix(v.Name(), ".xlsx") { + continue + } + + pfAbs := filepath.Join(xlsxAbsPath, v.Name()) + XlsxFiles[v.Name()] = pfAbs + } + } + + wd, err := os.Getwd() + if err != nil { + return + } + + pattern := filepath.Join(wd, "templ", "*.templ") + funcMap := template.FuncMap{ + "inc": func(n int) int { + n++ + return n + }, + } + templates, err = template.Must(template.New("mytempl").Funcs(funcMap).ParseGlob(pattern)).Parse("") + + return +} + +func (this *Configuration) Close() (err error) { + return +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/tools/xlsx2proto/config.json b/tools/xlsx2proto/config.json new file mode 100644 index 0000000..6c48934 --- /dev/null +++ b/tools/xlsx2proto/config.json @@ -0,0 +1,14 @@ +{ + "proto": { + "WorkPath": "D:/trunk/src", + "XlsxPath": "mongo.games.com/game/xlsx", + "ProtoPath": "mongo.games.com/game/protocol/server", + "ProtoFile": "pbdata.proto", + "ProtoLuaPath": "mongo.games.com/game/protocol_lua/pbdata", + "GoFilePath": "mongo.games.com/game/srvdata", + "TsFilePath": "D:/proj/ccc_client/tsdata", + "DataPath": "mongo.games.com/game/data", + "ConvertToolPath": "mongo.games.com/game/tools/xlsx2binary", + "CppFilePath": "temp" + } +} \ No newline at end of file diff --git a/tools/xlsx2proto/covert_xlsx_2_pbd.bat b/tools/xlsx2proto/covert_xlsx_2_pbd.bat new file mode 100644 index 0000000..974eda4 --- /dev/null +++ b/tools/xlsx2proto/covert_xlsx_2_pbd.bat @@ -0,0 +1,20 @@ +@echo off +set path=%path%;"C:\Program Files\Go\bin" +set work_path="C:\trunk\src\games.yol.com\win88" +set protoc=%work_path%\bin\protoc-3.19.4-win64\bin\protoc.exe +set protoc-gen-go-plugin-path="%work_path%\bin\protoc-gen-go.exe" +if not exist xlsx2proto.exe ( + go build +) +xlsx2proto.exe +cd ../../protocol/server +%protoc% --plugin=protoc-gen-go=%protoc-gen-go-plugin-path% --go_out=. pbdata.proto +cd ../../tools/xlsx2binary +if not exist xlsx2proto.exe ( + go fmt + go build +) +xlsx2binary.exe +cd ../../ +copydata.bat +pause diff --git a/tools/xlsx2proto/covert_xlsx_2_pbds.bat b/tools/xlsx2proto/covert_xlsx_2_pbds.bat new file mode 100644 index 0000000..65a6ceb --- /dev/null +++ b/tools/xlsx2proto/covert_xlsx_2_pbds.bat @@ -0,0 +1,14 @@ +set path=%path%;E:\GO\go1.15.15\bin +if not exist xlsx2proto.exe ( + go build +) +xlsx2proto.exe +cd ../../protocol/pbdata +protoc --go_out=. pbdata.proto +cd ../../tools/xlsx2binary +go fmt +go build +xlsx2binary.exe +cd ../../ + +pause diff --git a/tools/xlsx2proto/logger.xml b/tools/xlsx2proto/logger.xml new file mode 100644 index 0000000..17f5274 --- /dev/null +++ b/tools/xlsx2proto/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/xlsx2proto/templ/agc.templ b/tools/xlsx2proto/templ/agc.templ new file mode 100644 index 0000000..f818065 --- /dev/null +++ b/tools/xlsx2proto/templ/agc.templ @@ -0,0 +1,162 @@ +{{define "agc"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package main +import ( + "encoding/json" + "fmt" + "os" + "runtime" + "strconv" + "strings" + + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "github.com/tealeg/xlsx" +) + +var _ = strings.Split + +{{ $a := .data }}{{ range $a }}{{ $elem := . }} +func AgcConvert{{ $elem.ProtoName }}(fi,fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr:=&server.{{$elem.ProtoName}}Array{ + Arr:make([]*server.{{$elem.ProtoName}}, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.{{$elem.ProtoName}}{} + + for { + {{ range $index, $col := $elem.Cols }} + if len(row.Cells)<{{$col.ColIndex}}+1{ + break + } + {{if eq $col.ColType 1 }} + temp, _ = strconv.ParseInt(row.Cells[{{$col.ColIndex}}].String(), 10, 32) + data.{{$col.ColName}} = proto.Int32(int32(temp)) + {{end}} + {{if eq $col.ColType 5 }} + temp, _ = strconv.ParseInt(row.Cells[{{$col.ColIndex}}].String(), 10, 64) + data.{{$col.ColName}} = proto.Int64(int64(temp)) + {{end}} + {{if eq $col.ColType 2 }} + data.{{$col.ColName}} = proto.String(row.Cells[{{$col.ColIndex}}].String()) + {{end}} + {{if eq $col.ColType 3 }} + arrStr = strings.Split(row.Cells[{{$col.ColIndex}}].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[{{$col.ColIndex}}].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.{{$col.ColName}} = arrInt + {{end}} + {{if eq $col.ColType 4 }} + if row.Cells[{{$col.ColIndex}}].String() != "" { + arrStr = strings.Split(row.Cells[{{$col.ColIndex}}].String(), "|") + data.{{$col.ColName}} = arrStr + } + {{end}} + {{if eq $col.ColType 6 }} + arrStr = strings.Split(row.Cells[{{$col.ColIndex}}].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[{{$col.ColIndex}}].String() != "" { + arrInt64 = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.{{$col.ColName}} = arrInt64 + {{end}} + {{if eq $col.ColType 7 }} + if row.Cells[{{$col.ColIndex}}].String() != ""{ + pairs := strings.Split(row.Cells[{{$col.ColIndex}}].String(), ";") + resultMap := make(map[int64]int64) + for _, pair := range pairs { + kv := strings.Split(pair, ",") + key, err := strconv.ParseInt(kv[0], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[0]) + continue + } + value, err := strconv.ParseInt(kv[1], 10, 64) + if err != nil { + // 错误处理 + fmt.Println("无法转换为int64:", kv[1]) + continue + } + resultMap[key] = value + } + data.{{$col.ColName}} = resultMap + } + {{end}} + {{end}} + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil{ + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + {{end}} + +func main(){ +{{ $a := .data }}{{ $opath := .opath }}{{ range $a }}{{ $elem := . }} + AgcConvert{{$elem.ProtoName}}(`{{$elem.AbsPath}}`,`{{$opath}}\{{$elem.ProtoName}}.dat`) +{{end}} +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2proto/templ/gpb.templ b/tools/xlsx2proto/templ/gpb.templ new file mode 100644 index 0000000..ef0c0f0 --- /dev/null +++ b/tools/xlsx2proto/templ/gpb.templ @@ -0,0 +1,22 @@ +{{define "gpb"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +syntax = "proto3"; +package server; +option go_package = ".;server"; +{{ $a := . }}{{ range $a }}{{ $elem := . }} +message {{$elem.ProtoName}} { +{{ range $index, $col := $elem.Cols }}{{if $col.IsArray }} + repeated {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{else if $col.IsMap }} + map<{{"int64"}}, {{"int64"}}> {{$col.ColName}} = {{ inc $index }}; +{{else}} + {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{end}}{{end}} +} + +message {{$elem.ProtoName}}Array { + repeated {{$elem.ProtoName}} Arr = 1; +} +{{end}} +{{end}} diff --git a/tools/xlsx2proto/templ/gpb_mgr.templ b/tools/xlsx2proto/templ/gpb_mgr.templ new file mode 100644 index 0000000..8d8d0df --- /dev/null +++ b/tools/xlsx2proto/templ/gpb_mgr.templ @@ -0,0 +1,69 @@ +{{define "gpb_mgr"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PB{{.ProtoName}}Mgr = &{{.ProtoName}}Mgr{pool: make(map[int32]*server.{{.ProtoName}}), Datas: &server.{{.ProtoName}}Array{}} + +type {{.ProtoName}}Mgr struct { + Datas *server.{{.ProtoName}}Array + pool map[int32]*server.{{.ProtoName}} +} + +func (this *{{.ProtoName}}Mgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *{{.ProtoName}}Mgr) reunmarshal(data []byte) error { + newDatas := &server.{{.ProtoName}}Array{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *{{.ProtoName}}Mgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *{{.ProtoName}}Mgr) GetData(id int32) *server.{{.ProtoName}} { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("{{.ProtoName}}.dat", &ProtobufDataLoader{dh: PB{{.ProtoName}}Mgr}) +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2proto/templ/gpb_mgr_c.templ b/tools/xlsx2proto/templ/gpb_mgr_c.templ new file mode 100644 index 0000000..148ae4a --- /dev/null +++ b/tools/xlsx2proto/templ/gpb_mgr_c.templ @@ -0,0 +1,20 @@ +{{define "gpb_mgr_c"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +#include "DataMgrAgc.h" + +void DB_Loader::loadAll() +{ +{{ $a := .data }}{{ range $a }}{{ $elem := . }} + DataAutoLoader {{$elem.ProtoName}}AutoLoader("data/{{$elem.ProtoName}}.dat", {{$elem.ProtoName}}DataMgr::getInstance()); +{{end}} +} + +void DB_Loader::purgeAll() +{ +{{ $a := .data }}{{ range $a }}{{ $elem := . }} + {{$elem.ProtoName}}DataMgr::getInstance()->purge(); +{{end}} +} +{{end}} + diff --git a/tools/xlsx2proto/templ/gpb_mgr_h.templ b/tools/xlsx2proto/templ/gpb_mgr_h.templ new file mode 100644 index 0000000..e370e3a --- /dev/null +++ b/tools/xlsx2proto/templ/gpb_mgr_h.templ @@ -0,0 +1,81 @@ +{{define "gpb_mgr_h"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +#ifndef __DATA_MGR_AGC_H__ +#define __DATA_MGR_AGC_H__ +#include "DataMgr.h" +#include "protocol/protocol.pb.h" +#include "protocol/pbdata.pb.h" +class DB_Loader +{ +public: + static DB_Loader& getInstance() + { + static DB_Loader inst; + return inst; + } + + void loadAll(); + void purgeAll(); +}; + +{{ $a := .data }}{{ range $a }}{{ $elem := . }} +class {{$elem.ProtoName}}DataMgr : public IDataMgr +{ +public: + static {{$elem.ProtoName}}DataMgr* getInstance() + { + static {{$elem.ProtoName}}DataMgr* s_instance; + if (!s_instance) + { + s_instance = new {{$elem.ProtoName}}DataMgr(); + } + return s_instance; + } + + void purge() + { + if (m_pDataArray) + { + delete m_pDataArray; + } + m_dataPool.clear(); + } + +protected: + virtual void mapData(void* pData, int size) + { + if (!pData || size<=0) + { + return; + } + + if (m_pDataArray) + { + delete m_pDataArray; + } + m_dataPool.clear(); + + m_pDataArray = protocol::{{$elem.ProtoName}}Array::default_instance().New(); + google::protobuf::io::CodedInputStream cis((google::protobuf::uint8*)pData, size); + if (m_pDataArray->MergeFromCodedStream(&cis)) + { + for (int i=0; iarr_size(); ++i) + { + const protocol::{{$elem.ProtoName}}& data = m_pDataArray->arr(i); + m_dataPool[data.id()] = (google::protobuf::Message*)&data; + } + } + + return; + } +protected: + {{$elem.ProtoName}}DataMgr():m_pDataArray(0){} + virtual ~{{$elem.ProtoName}}DataMgr(){} + + protocol::{{$elem.ProtoName}}Array* m_pDataArray; +}; +{{end}} + +#endif +{{end}} \ No newline at end of file diff --git a/tools/xlsx2proto/templ/gpb_mgr_ts.templ b/tools/xlsx2proto/templ/gpb_mgr_ts.templ new file mode 100644 index 0000000..d226652 --- /dev/null +++ b/tools/xlsx2proto/templ/gpb_mgr_ts.templ @@ -0,0 +1,44 @@ +{{define "gpb_mgr_ts"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +import { ConfHolder } from "./ConfLoader"; + +export class {{.ProtoName}} implements ConfHolder { + public datas:server.{{.ProtoName}}Array | null = null; + public pool:Map = new Map(); + private static instacne:{{.ProtoName}} | null = null; + + public static getInstance():{{.ProtoName}}{ + if (this.instacne == null){ + this.instacne = new {{.ProtoName}}(); + } + return this.instacne; + } + + //反序列化 + decode(buff: Uint8Array): Error|null { + this.datas = server.{{.ProtoName}}Array.decode(buff); + this.arrangeData(); + return null; + } + + // + public clear():void{ + this.datas =null; + this.pool.clear(); + {{.ProtoName}}.instacne = null; + } + + //将数组转换成为字典 + private arrangeData(){ + if (this.datas != null){ + for (let k of this.datas.Arr.keys()){ + let id = this.datas.Arr[k].Id; + this.pool.set(id!,this.datas.Arr[k] as server.{{.ProtoName}}) + } + } + } +} +{{end}} + diff --git a/tools/xlsx2proto/templ/gpb_single.templ b/tools/xlsx2proto/templ/gpb_single.templ new file mode 100644 index 0000000..ee17258 --- /dev/null +++ b/tools/xlsx2proto/templ/gpb_single.templ @@ -0,0 +1,19 @@ +{{define "gpb_single"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +syntax = "proto3"; +package server; +option go_package = ".;server"; + +message {{.ProtoName}} { +{{ range $index, $col := .Cols }}{{if $col.IsArray }} + repeated {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{else}} + {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{end}}{{end}} +} + +message {{.ProtoName}}Array { + repeated {{.ProtoName}} Arr = 1; +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2proto/xlsx2proto.go b/tools/xlsx2proto/xlsx2proto.go new file mode 100644 index 0000000..6717d69 --- /dev/null +++ b/tools/xlsx2proto/xlsx2proto.go @@ -0,0 +1,283 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/tealeg/xlsx" + "mongo.games.com/goserver/core" +) + +type SheetColumnMetaStruct struct { + ColIndex int + ColName string + ColType int + CTString string + IsArray bool + IsMap bool +} + +type SheetMetaStruct struct { + AbsPath string + FileName string + ProtoName string + Cols []*SheetColumnMetaStruct +} + +const ( + INTTYPE string = "(int)" + INT64TYPE string = "(int64)" + STRTYPE = "(str)" + ARRINTTYPE = "(arrint)" + ARRINT64TYPE = "(arrint64)" + ARRSTRTYPE = "(arrstr)" + INTTYPE_PROTO = "int32" + INT64TYPE_PROTO = "int64" + STRTYPE_PROTO = "string" + MAPTYPE string = "(map)" + MAP_PROTO = "map" +) + +var templates *template.Template + +func main() { + + defer core.ClosePackages() + core.LoadPackages("config.json") + + smsMap := make(map[string]*SheetMetaStruct) + for xlsxFileName, xlsxFilePath := range XlsxFiles { + xlsxFile, err := xlsx.OpenFile(xlsxFilePath) + if err != nil { + fmt.Println("excel file open error:", err, " filePath:", xlsxFilePath) + continue + } + + for _, sheet := range xlsxFile.Sheets { + sms := &SheetMetaStruct{ + AbsPath: xlsxFilePath, + FileName: xlsxFileName, + ProtoName: strings.TrimSuffix(xlsxFileName, ".xlsx"), + Cols: make([]*SheetColumnMetaStruct, 0, sheet.MaxCol)} + + for _, row := range sheet.Rows { + for i, cell := range row.Cells { + s := cell.String() + if strings.HasSuffix(s, INTTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, INTTYPE), 1, INTTYPE_PROTO, false, false}) + } else if strings.HasSuffix(s, STRTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, STRTYPE), 2, STRTYPE_PROTO, false, false}) + } else if strings.HasSuffix(s, ARRINTTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ARRINTTYPE), 3, INTTYPE_PROTO, true, false}) + } else if strings.HasSuffix(s, ARRSTRTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ARRSTRTYPE), 4, STRTYPE_PROTO, true, false}) + } else if strings.HasSuffix(s, INT64TYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, INT64TYPE), 5, INT64TYPE_PROTO, false, false}) + } else if strings.HasSuffix(s, ARRINT64TYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ARRINT64TYPE), 6, INT64TYPE_PROTO, true, false}) + } else if strings.HasSuffix(s, MAPTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, MAPTYPE), 7, MAP_PROTO, false, true}) + } + } + break //only fetch first row + } + + smsMap[sms.ProtoName] = sms + break //only fetch first sheet + } + } + + geneProto(smsMap) +} + +func geneProto(xlsxs map[string]*SheetMetaStruct) { + if xlsxs == nil { + return + } + + geneAgcHelper(xlsxs) //生成 xlsx转二进制文件(.json,.dat) + geneDataStructProto(xlsxs) //生成 xlsx转protobuf结构文件(pbdata.proto) + //geneDataSingleStructProto(xlsxs) + //geneChhDataMgrHeader(xlsxs)//生成 c头文件 + //geneCppDataMgrHeader(xlsxs)//生成 c源文件 + //copyNoDataProtoToLua() + for _, val := range xlsxs { + genGoDataMgr(val) // 生成Go对象文件 + genTsDataMgr(val) // 生成TypeScript对象文件 + } +} + +// geneAgcHelper 生成go代码,读取xlsx数据 +func geneAgcHelper(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + "opath": filepath.Join(Config.WorkPath, Config.DataPath), + "isMapCol": isMapColumn(xlsxs), + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "agc", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperGoFile := filepath.Join(Config.WorkPath, Config.ConvertToolPath, "agc.go") + err = os.WriteFile(helperGoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +// geneDataStructProto 生成xlsx的proto文件 +func geneDataStructProto(xlsxs map[string]*SheetMetaStruct) { + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb", xlsxs) + if err != nil { + fmt.Println("geneDataStructProto ExecuteTemplate error:", err) + return + } + + protoFile := filepath.Join(Config.WorkPath, Config.ProtoPath, Config.ProtoFile) + err = os.WriteFile(protoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneDataStructProto error:", err) + } +} + +func geneDataSingleStructProto(xlsxs map[string]*SheetMetaStruct) { + for k, v := range xlsxs { + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_single", v) + if err != nil { + fmt.Println("geneDataSingleStructProto ExecuteTemplate error:", err) + return + } + + protoFile := filepath.Join(Config.WorkPath, Config.ProtoLuaPath, strings.ToLower(k)+".proto") + err = os.WriteFile(protoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneDataSingleStructProto error:", err) + } + } +} + +// genGoDataMgr 生成go代码,读取.dat文件数据 +func genGoDataMgr(sms *SheetMetaStruct) { + if sms == nil { + return + } + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr", sms) + if err != nil { + fmt.Println("genGoDataMgr ExecuteTemplate error:", err) + return + } + opath := filepath.Join(Config.WorkPath, Config.GoFilePath, strings.ToLower(fmt.Sprintf("%v.go", sms.ProtoName))) + err = os.WriteFile(opath, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("genGoDataMgr WriteFile error:", err) + return + } +} + +// genTsDataMgr 生成ts类模板 +func genTsDataMgr(sms *SheetMetaStruct) { + if sms == nil { + return + } + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_ts", sms) + if err != nil { + fmt.Println("genTsDataMgr ExecuteTemplate error:", err) + return + } + //opath := filepath.Join(Config.WorkPath, Config.TsFilePath, strings.ToLower(fmt.Sprintf("%v.ts", sms.ProtoName))) + opath := fmt.Sprintf("%v/%v", Config.TsFilePath, strings.ToLower(fmt.Sprintf("%v.ts", sms.ProtoName))) + err = os.WriteFile(opath, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("genTsDataMgr WriteFile error:", err) + return + } +} + +func geneChhDataMgrHeader(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_h", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperChhFile := filepath.Join(Config.WorkPath, Config.CppFilePath, "DataMgrAgc.h") + err = os.WriteFile(helperChhFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +func geneCppDataMgrHeader(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_c", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperChhFile := filepath.Join(Config.WorkPath, Config.CppFilePath, "DataMgrAgc.cpp") + err = os.WriteFile(helperChhFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +func copyNoDataProtoToLua() { + protoFile := filepath.Join(Config.WorkPath, Config.ProtoPath) + rootdir, pathopen := os.Open(protoFile) + if pathopen != nil { + fmt.Println("Path open error.") + } else { + files, _ := rootdir.Readdir(0) + for _, fi := range files { + if strings.HasSuffix(fi.Name(), ".proto") && !strings.Contains(fi.Name(), "pbdata.proto") { + datas, err := os.ReadFile(filepath.Join(Config.WorkPath, Config.ProtoPath, fi.Name())) + if err == nil { + strContent := string(datas[:]) + strContent = strings.Replace(strContent, `"protocol/`, `"`, -1) + dest := filepath.Join(Config.WorkPath, Config.ProtoLuaPath, fi.Name()) + fmt.Println("==========dest", dest) + os.WriteFile(dest, []byte(strContent), os.ModePerm) + } else { + fmt.Println(err) + } + } + + } + } +} + +func isMapColumn(xlsxs map[string]*SheetMetaStruct) bool { + for _, sheet := range xlsxs { + for _, col := range sheet.Cols { + if col.IsMap { + return true + } + } + } + return false +} diff --git a/tools/xlsx2protomac/config.go b/tools/xlsx2protomac/config.go new file mode 100644 index 0000000..3e616e0 --- /dev/null +++ b/tools/xlsx2protomac/config.go @@ -0,0 +1,83 @@ +// config +package main + +import ( + "os" + "path/filepath" + "strings" + "text/template" + + "mongo.games.com/goserver/core" +) + +var XlsxFiles = make(map[string]string) + +var Config = Configuration{} + +type Configuration struct { + WorkPath string + XlsxPath string + ProtoPath string + ProtoLuaPath string + GoFilePath string + TsFilePath string + ConvertToolPath string + CppFilePath string + DataPath string + ProtoFile string +} + +func (this *Configuration) Name() string { + return "proto" +} + +func (this *Configuration) Init() (err error) { + + protoAbsPath := filepath.Join(this.WorkPath, this.ProtoPath) + err = os.MkdirAll(protoAbsPath, os.ModePerm) + if err != nil { + return + } + + xlsxAbsPath := filepath.Join(this.WorkPath, this.XlsxPath) + var fis []os.DirEntry + fis, err = os.ReadDir(xlsxAbsPath) + if err != nil { + return + } + + for _, v := range fis { + if !v.IsDir() { + if !strings.HasSuffix(v.Name(), ".xlsx") { + continue + } + + pfAbs := filepath.Join(xlsxAbsPath, v.Name()) + XlsxFiles[v.Name()] = pfAbs + } + } + + wd, err := os.Getwd() + if err != nil { + return + } + + pattern := filepath.Join(wd, "templ", "*.templ") + funcMap := template.FuncMap{ + "inc": func(n int) int { + n++ + return n + }, + } + templates, err = template.Must(template.New("mytempl").Funcs(funcMap).ParseGlob(pattern)).Parse("") + + return +} + +func (this *Configuration) Close() (err error) { + return +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/tools/xlsx2protomac/config.json b/tools/xlsx2protomac/config.json new file mode 100644 index 0000000..27bd0bc --- /dev/null +++ b/tools/xlsx2protomac/config.json @@ -0,0 +1,14 @@ +{ + "proto": { + "WorkPath": "/Users/ethan/Proj/trunk/Proj/trunk", + "XlsxPath": "mongo.games.com/game/xlsx", + "ProtoPath": "mongo.games.com/game/protocol/server", + "ProtoLuaPath": "mongo.games.com/game/protocol_lua/pbdata", + "GoFilePath": "mongo.games.com/game/srvdata", + "TsFilePath": "/Users/ethan/Proj/yuenan/ccc_client/tsdata", + "ConvertToolPath": "mongo.games.com/game/tools/xlsx2binary", + "CppFilePath": "temp", + "DataPath": "mongo.games.com/game/data", + "ProtoFile": "pbdata.proto" + } +} \ No newline at end of file diff --git a/tools/xlsx2protomac/convert_xlsx_2_pbd.sh b/tools/xlsx2protomac/convert_xlsx_2_pbd.sh new file mode 100644 index 0000000..5a7ca93 --- /dev/null +++ b/tools/xlsx2protomac/convert_xlsx_2_pbd.sh @@ -0,0 +1,16 @@ +work_path=$WIN88_DIR +protoc=$work_path/bin/protoc-3.19.4-osx-x86_64/bin/protoc +protocgengo=$work_path/bin/protoc-gen-go +[ -d "$work_path" ] && echo "$work_path is dir" +[ -f "$protoc" ] && echo "$protoc is file" +[ -f "$protocgengo" ] && echo "$protocgengo is file" + +#if [ ! -f xlsx2protomac ]; +#then + go build + [ -f xlsx2protomac ] && echo "xlsx2proto可执行文件成功生成" +#else +# echo "xlsx2proto已经存在" +#fi + +./xlsx2protomac \ No newline at end of file diff --git a/tools/xlsx2protomac/logger.xml b/tools/xlsx2protomac/logger.xml new file mode 100644 index 0000000..17f5274 --- /dev/null +++ b/tools/xlsx2protomac/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/xlsx2protomac/templ/agc.templ b/tools/xlsx2protomac/templ/agc.templ new file mode 100644 index 0000000..68d18c3 --- /dev/null +++ b/tools/xlsx2protomac/templ/agc.templ @@ -0,0 +1,139 @@ +{{define "agc"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package main +import ( + "encoding/json" + "fmt" + "os" + "runtime" + "strconv" + "strings" + + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "github.com/tealeg/xlsx" +) + +var _ = strings.Split + +{{ $a := .data }}{{ range $a }}{{ $elem := . }} +func AgcConvert{{ $elem.ProtoName }}(fi,fo string) { + xlsxFile, err := xlsx.OpenFile(fi) + if err != nil { + fmt.Println("excel file open error:", err, "filename:", fi) + return + } + + if err := recover(); err != nil { + fmt.Println(" panic,error=", err) + var buf [4096]byte + lens := runtime.Stack(buf[:], false) + fmt.Println("stack--->", string(buf[:lens])) + } + for _, sheet := range xlsxFile.Sheets { + arr:=&server.{{$elem.ProtoName}}Array{ + Arr:make([]*server.{{$elem.ProtoName}}, 0, len(sheet.Rows)), + } + + for i, row := range sheet.Rows { + if i <= 1 { + continue + } + + if len(row.Cells) == 0 { + break + } + + temp := int64(0) + var arrInt []int32 + var arrInt64 []int64 + var arrStr []string + var _ = arrInt + var _ = arrStr + var _ = arrInt64 + + temp, _ = strconv.ParseInt(row.Cells[0].String(), 10, 32) + data := &server.{{$elem.ProtoName}}{} + + for { + {{ range $index, $col := $elem.Cols }} + if len(row.Cells)<{{$index}}+1{ + break + } + {{if eq $col.ColType 1 }} + temp, _ = strconv.ParseInt(row.Cells[{{$index}}].String(), 10, 32) + data.{{$col.ColName}} = proto.Int32(int32(temp)) + {{end}} + {{if eq $col.ColType 5 }} + temp, _ = strconv.ParseInt(row.Cells[{{$index}}].String(), 10, 64) + data.{{$col.ColName}} = proto.Int64(int64(temp)) + {{end}} + {{if eq $col.ColType 2 }} + data.{{$col.ColName}} = proto.String(row.Cells[{{$index}}].String()) + {{end}} + {{if eq $col.ColType 3 }} + arrStr = strings.Split(row.Cells[{{$index}}].String(), "|") + arrInt = nil + if len(arrStr) > 0 && row.Cells[{{$index}}].String() != "" { + arrInt = make([]int32, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 32) + arrInt[i] = int32(temp) + } + } + data.{{$col.ColName}} = arrInt + {{end}} + {{if eq $col.ColType 4 }} + if row.Cells[{{$index}}].String() != "" { + arrStr = strings.Split(row.Cells[{{$index}}].String(), "|") + data.{{$col.ColName}} = arrStr + } + {{end}} + {{if eq $col.ColType 6 }} + arrStr = strings.Split(row.Cells[{{$index}}].String(), "|") + arrInt64 = nil + if len(arrStr) > 0 && row.Cells[{{$index}}].String() != "" { + arrInt = make([]int64, len(arrStr), len(arrStr)) + for i, v := range arrStr { + temp, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) + arrInt64[i] = int64(temp) + } + } + data.{{$col.ColName}} = arrInt64 + {{end}} + {{end}} + + break + } + arr.Arr = append(arr.Arr, data) + } + + byteData, err := proto.Marshal(arr) + if err == nil{ + err := os.WriteFile(fo, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + + byteData, err = json.MarshalIndent(arr, "", "\t") + if err == nil { + foJson := strings.Replace(fo, ".dat", ".json", -1) + err := os.WriteFile(foJson, byteData, os.ModePerm) + if err != nil { + fmt.Println(err) + } + } + break //only fetch first sheet + } +} + {{end}} + +func main(){ +{{ $a := .data }}{{ $opath := .opath }}{{ range $a }}{{ $elem := . }} + AgcConvert{{$elem.ProtoName}}(`{{$elem.AbsPath}}`,`{{$opath}}\{{$elem.ProtoName}}.dat`) +{{end}} +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2protomac/templ/gpb.templ b/tools/xlsx2protomac/templ/gpb.templ new file mode 100644 index 0000000..2a2d6b4 --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb.templ @@ -0,0 +1,20 @@ +{{define "gpb"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +syntax = "proto3"; +package server; +option go_package = ".;server"; +{{ $a := . }}{{ range $a }}{{ $elem := . }} +message {{$elem.ProtoName}} { +{{ range $index, $col := $elem.Cols }}{{if $col.IsArray }} + repeated {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{else}} + {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{end}}{{end}} +} + +message {{$elem.ProtoName}}Array { + repeated {{$elem.ProtoName}} Arr = 1; +} +{{end}} +{{end}} diff --git a/tools/xlsx2protomac/templ/gpb_mgr.templ b/tools/xlsx2protomac/templ/gpb_mgr.templ new file mode 100644 index 0000000..8d8d0df --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb_mgr.templ @@ -0,0 +1,69 @@ +{{define "gpb_mgr"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +package srvdata + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var PB{{.ProtoName}}Mgr = &{{.ProtoName}}Mgr{pool: make(map[int32]*server.{{.ProtoName}}), Datas: &server.{{.ProtoName}}Array{}} + +type {{.ProtoName}}Mgr struct { + Datas *server.{{.ProtoName}}Array + pool map[int32]*server.{{.ProtoName}} +} + +func (this *{{.ProtoName}}Mgr) unmarshal(data []byte) error { + err := proto.Unmarshal(data, this.Datas) + if err == nil { + this.arrangeData() + } + return err +} + +func (this *{{.ProtoName}}Mgr) reunmarshal(data []byte) error { + newDatas := &server.{{.ProtoName}}Array{} + err := proto.Unmarshal(data, newDatas) + if err == nil { + for _, item := range newDatas.Arr { + existItem := this.GetData(item.GetId()) + if existItem == nil { + this.pool[item.GetId()] = item + this.Datas.Arr = append(this.Datas.Arr, item) + } else { + *existItem = *item + } + } + } + return err +} + +func (this *{{.ProtoName}}Mgr) arrangeData() { + if this.Datas == nil { + return + } + + dataArr := this.Datas.GetArr() + if dataArr == nil { + return + } + + for _, data := range dataArr { + this.pool[data.GetId()] = data + } +} + +func (this *{{.ProtoName}}Mgr) GetData(id int32) *server.{{.ProtoName}} { + if data, ok := this.pool[id]; ok { + return data + } + return nil +} + +func init() { + DataMgr.RegisteLoader("{{.ProtoName}}.dat", &ProtobufDataLoader{dh: PB{{.ProtoName}}Mgr}) +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2protomac/templ/gpb_mgr_c.templ b/tools/xlsx2protomac/templ/gpb_mgr_c.templ new file mode 100644 index 0000000..148ae4a --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb_mgr_c.templ @@ -0,0 +1,20 @@ +{{define "gpb_mgr_c"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +#include "DataMgrAgc.h" + +void DB_Loader::loadAll() +{ +{{ $a := .data }}{{ range $a }}{{ $elem := . }} + DataAutoLoader {{$elem.ProtoName}}AutoLoader("data/{{$elem.ProtoName}}.dat", {{$elem.ProtoName}}DataMgr::getInstance()); +{{end}} +} + +void DB_Loader::purgeAll() +{ +{{ $a := .data }}{{ range $a }}{{ $elem := . }} + {{$elem.ProtoName}}DataMgr::getInstance()->purge(); +{{end}} +} +{{end}} + diff --git a/tools/xlsx2protomac/templ/gpb_mgr_h.templ b/tools/xlsx2protomac/templ/gpb_mgr_h.templ new file mode 100644 index 0000000..e370e3a --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb_mgr_h.templ @@ -0,0 +1,81 @@ +{{define "gpb_mgr_h"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +#ifndef __DATA_MGR_AGC_H__ +#define __DATA_MGR_AGC_H__ +#include "DataMgr.h" +#include "protocol/protocol.pb.h" +#include "protocol/pbdata.pb.h" +class DB_Loader +{ +public: + static DB_Loader& getInstance() + { + static DB_Loader inst; + return inst; + } + + void loadAll(); + void purgeAll(); +}; + +{{ $a := .data }}{{ range $a }}{{ $elem := . }} +class {{$elem.ProtoName}}DataMgr : public IDataMgr +{ +public: + static {{$elem.ProtoName}}DataMgr* getInstance() + { + static {{$elem.ProtoName}}DataMgr* s_instance; + if (!s_instance) + { + s_instance = new {{$elem.ProtoName}}DataMgr(); + } + return s_instance; + } + + void purge() + { + if (m_pDataArray) + { + delete m_pDataArray; + } + m_dataPool.clear(); + } + +protected: + virtual void mapData(void* pData, int size) + { + if (!pData || size<=0) + { + return; + } + + if (m_pDataArray) + { + delete m_pDataArray; + } + m_dataPool.clear(); + + m_pDataArray = protocol::{{$elem.ProtoName}}Array::default_instance().New(); + google::protobuf::io::CodedInputStream cis((google::protobuf::uint8*)pData, size); + if (m_pDataArray->MergeFromCodedStream(&cis)) + { + for (int i=0; iarr_size(); ++i) + { + const protocol::{{$elem.ProtoName}}& data = m_pDataArray->arr(i); + m_dataPool[data.id()] = (google::protobuf::Message*)&data; + } + } + + return; + } +protected: + {{$elem.ProtoName}}DataMgr():m_pDataArray(0){} + virtual ~{{$elem.ProtoName}}DataMgr(){} + + protocol::{{$elem.ProtoName}}Array* m_pDataArray; +}; +{{end}} + +#endif +{{end}} \ No newline at end of file diff --git a/tools/xlsx2protomac/templ/gpb_mgr_ts.templ b/tools/xlsx2protomac/templ/gpb_mgr_ts.templ new file mode 100644 index 0000000..d226652 --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb_mgr_ts.templ @@ -0,0 +1,44 @@ +{{define "gpb_mgr_ts"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! + +import { ConfHolder } from "./ConfLoader"; + +export class {{.ProtoName}} implements ConfHolder { + public datas:server.{{.ProtoName}}Array | null = null; + public pool:Map = new Map(); + private static instacne:{{.ProtoName}} | null = null; + + public static getInstance():{{.ProtoName}}{ + if (this.instacne == null){ + this.instacne = new {{.ProtoName}}(); + } + return this.instacne; + } + + //反序列化 + decode(buff: Uint8Array): Error|null { + this.datas = server.{{.ProtoName}}Array.decode(buff); + this.arrangeData(); + return null; + } + + // + public clear():void{ + this.datas =null; + this.pool.clear(); + {{.ProtoName}}.instacne = null; + } + + //将数组转换成为字典 + private arrangeData(){ + if (this.datas != null){ + for (let k of this.datas.Arr.keys()){ + let id = this.datas.Arr[k].Id; + this.pool.set(id!,this.datas.Arr[k] as server.{{.ProtoName}}) + } + } + } +} +{{end}} + diff --git a/tools/xlsx2protomac/templ/gpb_single.templ b/tools/xlsx2protomac/templ/gpb_single.templ new file mode 100644 index 0000000..ee17258 --- /dev/null +++ b/tools/xlsx2protomac/templ/gpb_single.templ @@ -0,0 +1,19 @@ +{{define "gpb_single"}} +// Code generated by xlsx2proto. +// DO NOT EDIT! +syntax = "proto3"; +package server; +option go_package = ".;server"; + +message {{.ProtoName}} { +{{ range $index, $col := .Cols }}{{if $col.IsArray }} + repeated {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{else}} + {{$col.CTString}} {{$col.ColName}} = {{ inc $index }}; +{{end}}{{end}} +} + +message {{.ProtoName}}Array { + repeated {{.ProtoName}} Arr = 1; +} +{{end}} \ No newline at end of file diff --git a/tools/xlsx2protomac/xlsx2proto.go b/tools/xlsx2protomac/xlsx2proto.go new file mode 100644 index 0000000..c99cd10 --- /dev/null +++ b/tools/xlsx2protomac/xlsx2proto.go @@ -0,0 +1,262 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/tealeg/xlsx" + "mongo.games.com/goserver/core" +) + +type SheetColumnMetaStruct struct { + ColName string + ColType int + CTString string + IsArray bool +} + +type SheetMetaStruct struct { + AbsPath string + FileName string + ProtoName string + Cols []*SheetColumnMetaStruct +} + +const ( + INTTYPE string = "(int)" + INT64TYPE string = "(int64)" + STRTYPE = "(str)" + ARRINTTYPE = "(arrint)" + ARRINT64TYPE = "(arrint64)" + ARRSTRTYPE = "(arrstr)" + INTTYPE_PROTO = "int32" + INT64TYPE_PROTO = "int64" + STRTYPE_PROTO = "string" +) + +var templates *template.Template + +func main() { + + defer core.ClosePackages() + core.LoadPackages("config.json") + + smsMap := make(map[string]*SheetMetaStruct) + for xlsxFileName, xlsxFilePath := range XlsxFiles { + xlsxFile, err := xlsx.OpenFile(xlsxFilePath) + if err != nil { + fmt.Println("excel file open error:", err, " filePath:", xlsxFilePath) + continue + } + + for _, sheet := range xlsxFile.Sheets { + sms := &SheetMetaStruct{ + AbsPath: xlsxFilePath, + FileName: xlsxFileName, + ProtoName: strings.TrimSuffix(xlsxFileName, ".xlsx"), + Cols: make([]*SheetColumnMetaStruct, 0, sheet.MaxCol)} + + for _, row := range sheet.Rows { + for _, cell := range row.Cells { + s := cell.String() + if strings.HasSuffix(s, INTTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, INTTYPE), 1, INTTYPE_PROTO, false}) + } else if strings.HasSuffix(s, STRTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, STRTYPE), 2, STRTYPE_PROTO, false}) + } else if strings.HasSuffix(s, ARRINTTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, ARRINTTYPE), 3, INTTYPE_PROTO, true}) + } else if strings.HasSuffix(s, ARRSTRTYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, ARRSTRTYPE), 4, STRTYPE_PROTO, true}) + } else if strings.HasSuffix(s, INT64TYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, INT64TYPE), 5, INT64TYPE_PROTO, false}) + } else if strings.HasSuffix(s, ARRINT64TYPE) { + sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{strings.TrimSuffix(s, ARRINT64TYPE), 6, INT64TYPE_PROTO, true}) + } + } + break //only fetch first row + } + + smsMap[sms.ProtoName] = sms + break //only fetch first sheet + } + } + + geneProto(smsMap) +} + +func geneProto(xlsxs map[string]*SheetMetaStruct) { + if xlsxs == nil { + return + } + + geneAgcHelper(xlsxs) //生成 xlsx转二进制文件 + geneDataStructProto(xlsxs) //生成 xlsx转protobuf结构文件 + //geneDataSingleStructProto(xlsxs) + //geneChhDataMgrHeader(xlsxs)//生成 c头文件 + //geneCppDataMgrHeader(xlsxs)//生成 c源文件 + //copyNoDataProtoToLua() + for _, val := range xlsxs { + genGoDataMgr(val) //生成 go对象文件 + genTsDataMgr(val) //生成 ts对象文件 + } +} + +func geneAgcHelper(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + "opath": filepath.Join(Config.WorkPath, Config.DataPath), + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "agc", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperGoFile := filepath.Join(Config.WorkPath, Config.ConvertToolPath, "agc.go") + err = os.WriteFile(helperGoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +func geneDataStructProto(xlsxs map[string]*SheetMetaStruct) { + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb", xlsxs) + if err != nil { + fmt.Println("geneDataStructProto ExecuteTemplate error:", err) + return + } + + protoFile := filepath.Join(Config.WorkPath, Config.ProtoPath, Config.ProtoFile) + err = os.WriteFile(protoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneDataStructProto error:", err) + } +} + +func geneDataSingleStructProto(xlsxs map[string]*SheetMetaStruct) { + for k, v := range xlsxs { + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_single", v) + if err != nil { + fmt.Println("geneDataSingleStructProto ExecuteTemplate error:", err) + return + } + + protoFile := filepath.Join(Config.WorkPath, Config.ProtoLuaPath, strings.ToLower(k)+".proto") + err = os.WriteFile(protoFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneDataSingleStructProto error:", err) + } + } +} + +func genGoDataMgr(sms *SheetMetaStruct) { + if sms == nil { + return + } + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr", sms) + if err != nil { + fmt.Println("genGoDataMgr ExecuteTemplate error:", err) + return + } + opath := filepath.Join(Config.WorkPath, Config.GoFilePath, strings.ToLower(fmt.Sprintf("%v.go", sms.ProtoName))) + err = os.WriteFile(opath, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("genGoDataMgr WriteFile error:", err) + return + } +} + +// 生成ts类模板 +func genTsDataMgr(sms *SheetMetaStruct) { + if sms == nil { + return + } + + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_ts", sms) + if err != nil { + fmt.Println("genTsDataMgr ExecuteTemplate error:", err) + return + } + //opath := filepath.Join(Config.WorkPath, Config.TsFilePath, strings.ToLower(fmt.Sprintf("%v.ts", sms.ProtoName))) + opath := fmt.Sprintf("%v/%v", Config.TsFilePath, strings.ToLower(fmt.Sprintf("%v.ts", sms.ProtoName))) + err = os.WriteFile(opath, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("genTsDataMgr WriteFile error:", err) + return + } +} + +func geneChhDataMgrHeader(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_h", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperChhFile := filepath.Join(Config.WorkPath, Config.CppFilePath, "DataMgrAgc.h") + err = os.WriteFile(helperChhFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +func geneCppDataMgrHeader(xlsxs map[string]*SheetMetaStruct) { + dm := map[string]interface{}{ + "data": xlsxs, + } + outputHelper := bytes.NewBuffer(nil) + err := templates.ExecuteTemplate(outputHelper, "gpb_mgr_c", dm) + if err != nil { + fmt.Println("geneProto ExecuteTemplate error:", err) + return + } + + helperChhFile := filepath.Join(Config.WorkPath, Config.CppFilePath, "DataMgrAgc.cpp") + err = os.WriteFile(helperChhFile, outputHelper.Bytes(), os.ModePerm) + if err != nil { + fmt.Println("geneProto WriteFile error:", err) + return + } +} + +func copyNoDataProtoToLua() { + protoFile := filepath.Join(Config.WorkPath, Config.ProtoPath) + rootdir, pathopen := os.Open(protoFile) + if pathopen != nil { + fmt.Println("Path open error.") + } else { + files, _ := rootdir.Readdir(0) + for _, fi := range files { + if strings.HasSuffix(fi.Name(), ".proto") && !strings.Contains(fi.Name(), "pbdata.proto") { + datas, err := os.ReadFile(filepath.Join(Config.WorkPath, Config.ProtoPath, fi.Name())) + if err == nil { + strContent := string(datas[:]) + strContent = strings.Replace(strContent, `"protocol/`, `"`, -1) + dest := filepath.Join(Config.WorkPath, Config.ProtoLuaPath, fi.Name()) + fmt.Println("==========dest", dest) + os.WriteFile(dest, []byte(strContent), os.ModePerm) + } else { + fmt.Println(err) + } + } + + } + } +} diff --git a/tools/xlsx2protomac/xlsx2protomac b/tools/xlsx2protomac/xlsx2protomac new file mode 100644 index 0000000..cf2847f Binary files /dev/null and b/tools/xlsx2protomac/xlsx2protomac differ diff --git a/update_deploy.bat b/update_deploy.bat new file mode 100644 index 0000000..918d388 --- /dev/null +++ b/update_deploy.bat @@ -0,0 +1,27 @@ +xcopy .\data D:\gocode\ReadyUpdate\data /s /e /y + +xcopy .\dbproxy\dbproxy D:\gocode\ReadyUpdate /y +del .\dbproxy\dbproxy +xcopy .\mgrsrv\mgrsrv D:\gocode\ReadyUpdate /y +del .\mgrsrv\mgrsrv +xcopy .\gatesrv\gatesrv D:\gocode\ReadyUpdate /y +del .\gatesrv\gatesrv +xcopy .\worldsrv\worldsrv D:\gocode\ReadyUpdate /y +del .\worldsrv\worldsrv +xcopy .\gamesrv\gamesrv D:\gocode\ReadyUpdate /y +del .\gamesrv\gamesrv +xcopy .\robot\robot D:\gocode\ReadyUpdate /y +del .\robot\robot +xcopy .\ranksrv\ranksrv D:\gocode\ReadyUpdate /y +del .\ranksrv\ranksrv + +if exist "D:\gocode\ReadyUpdate\data\gameparam.json" (del D:\gocode\ReadyUpdate\data\gameparam.json) +if exist "D:\gocode\ReadyUpdate\data\clientparam.json" (del D:\gocode\ReadyUpdate\data\clientparam.json) +if exist "D:\gocode\ReadyUpdate\data\thrconfig.json" (del D:\gocode\ReadyUpdate\data\thrconfig.json) +if exist "D:\gocode\ReadyUpdate\data\gamedata.json" (del D:\gocode\ReadyUpdate\data\gamedata.json) +if exist "D:\gocode\ReadyUpdate\data\fishingparam.json" (del D:\gocode\ReadyUpdate\data\fishingparam.json) +if exist "D:\gocode\ReadyUpdate\data\normalparam.json" (del D:\gocode\ReadyUpdate\data\normalparam.json) +if exist "D:\gocode\ReadyUpdate\data\gmac.json" (del D:\gocode\ReadyUpdate\data\gmac.json) +if exist "D:\gocode\ReadyUpdate\data\zone_rob.json" (del D:\gocode\ReadyUpdate\data\zone_rob.json) +if exist "D:\gocode\ReadyUpdate\data\icon_rob.json" (del D:\gocode\ReadyUpdate\data\icon_rob.json) +pause \ No newline at end of file diff --git a/webapi/api.go b/webapi/api.go new file mode 100644 index 0000000..7650356 --- /dev/null +++ b/webapi/api.go @@ -0,0 +1,217 @@ +package webapi + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/url" + "strings" + "sync" + "sync/atomic" + "time" + + "google.golang.org/protobuf/proto" +) + +const DEFAULT_TIMEOUT = time.Duration(time.Second * 30) + +var WebApiStats = new(sync.Map) + +type ApiStats struct { + RunTimes int64 //执行次数 + TotalRuningTime int64 //总执行时间 + MaxRuningTime int64 //最长执行时间 + TimeoutTimes int64 //执行超时次数 +} + +func DeviceOs(os string) string { + switch os { + case "ios": + return "2" + case "android": + return "1" + default: + return "0" + } +} + +// API调用 +// action 为需要调用的api, 例如 /api/Send/SendCode +// params 为需发送的参数 +// protocol = https || http ||Post +func API_OP(url string, params url.Values) (map[string]interface{}, error) { + //res, err := http.PostForm("http://192.168.1.160:9090/api/Sms/SendCaptcha", params) + res, err := http.PostForm(Config.GameApiURL+url, params) + if err != nil { + return nil, err + } + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + //println(url, " ret:", string(body[:])) + + result := make(map[string]interface{}) + json.Unmarshal(body, &result) + + return result, nil +} + +// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// GET方式请求 +func getRequest(appId, action string, params map[string]string, body proto.Message, protocol string, dura time.Duration) ([]byte, error) { + var client *http.Client + if strings.ToUpper(protocol) == "HTTPS" { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client = &http.Client{Transport: tr} + } else { + client = &http.Client{} + } + data, err := proto.Marshal(body) + if err != nil { + return nil, err + } + + callUrl := MakeURL(appId, action, params, data) + //println("callurl=", callUrl) + req, err := http.NewRequest("GET", callUrl, nil) + if err != nil { + return nil, err + } + + var stats *ApiStats + if v, exist := WebApiStats.Load(action); exist { + stats = v.(*ApiStats) + } else { + stats = &ApiStats{} + WebApiStats.Store(action, stats) + } + + var isTimeout bool + start := time.Now() + defer func() { + ps := int64(time.Now().Sub(start) / time.Millisecond) + if stats != nil { + if isTimeout { + atomic.AddInt64(&stats.TimeoutTimes, 1) + } + atomic.AddInt64(&stats.RunTimes, 1) + atomic.AddInt64(&stats.TotalRuningTime, ps) + if atomic.LoadInt64(&stats.MaxRuningTime) < ps { + atomic.StoreInt64(&stats.MaxRuningTime, ps) + } + } + }() + + //设置超时 + client.Timeout = dura + req.Close = true + resp, err := client.Do(req) + if err != nil { + if uerr, ok := err.(net.Error); ok { + isTimeout = uerr.Timeout() + } + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode == 200 { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, err + } + println("callurl=", callUrl, "code=", resp.StatusCode) + return nil, fmt.Errorf("StatusCode:%d", resp.StatusCode) +} + +// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// POST方式请求 +func postRequest(appId, action string, params map[string]string, body proto.Message, protocol string, dura time.Duration) ([]byte, error) { + var client *http.Client + if strings.ToUpper(protocol) == "HTTPS" { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client = &http.Client{Transport: tr} + } else { + client = &http.Client{} + } + + data, err := proto.Marshal(body) + if err != nil { + return nil, err + } + + callUrl := MakeURL(appId, action, params, data) + //println("callurl=", callUrl) + req, err := http.NewRequest("POST", callUrl, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + + var stats *ApiStats + if v, exist := WebApiStats.Load(action); exist { + stats = v.(*ApiStats) + } else { + stats = &ApiStats{} + WebApiStats.Store(action, stats) + } + + var isTimeout bool + start := time.Now() + defer func() { + ps := int64(time.Now().Sub(start) / time.Millisecond) + if stats != nil { + if isTimeout { + atomic.AddInt64(&stats.TimeoutTimes, 1) + } + atomic.AddInt64(&stats.RunTimes, 1) + atomic.AddInt64(&stats.TotalRuningTime, ps) + if atomic.LoadInt64(&stats.MaxRuningTime) < ps { + atomic.StoreInt64(&stats.MaxRuningTime, ps) + } + } + }() + + //设置超时 + client.Timeout = dura + req.Close = true + resp, err := client.Do(req) + if err != nil { + if uerr, ok := err.(net.Error); ok { + isTimeout = uerr.Timeout() + } + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, err + } + println("callurl=", callUrl, "code=", resp.StatusCode) + return nil, fmt.Errorf("StatusCode:%d", resp.StatusCode) +} + +func Stats() map[string]ApiStats { + stats := make(map[string]ApiStats) + WebApiStats.Range(func(k, v interface{}) bool { + if s, ok := v.(*ApiStats); ok { + ss := *s //计数可能不精准 + stats[k.(string)] = ss + } + return true + }) + return stats +} diff --git a/webapi/clubapi.go b/webapi/clubapi.go new file mode 100644 index 0000000..781290a --- /dev/null +++ b/webapi/clubapi.go @@ -0,0 +1,129 @@ +package webapi + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + + "mongo.games.com/goserver/core/logger" +) + +// 俱乐部创建审核,阻塞协程 +// 如果调用失败只会有日志记录,后台也没有这个俱乐部的信息。 +// 出现该问题后,后台可以指定(也就是手动输入)相关信息调用审核结果接口即可。 +// 下面的俱乐部公告审核和该接口一样 +// 注意注意:如果给后台发送成功的话,err==nil那么请把这个俱乐部的v.CreateCheckPosted标记为true +func API_ClubCreateWaitCheck(appId string, ClubID, ClubOwner int32, PltID, ClubName, ClubNotice string) error { + params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + params["platform"] = PltID + params["snid"] = strconv.Itoa(int(ClubOwner)) + params["club_name"] = ClubName + params["club_id"] = strconv.Itoa(int(ClubID)) + params["club_notice"] = ClubNotice + buff, err := getRequest(appId, "/push_club_create_review", params, nil, "http", DEFAULT_TIMEOUT) + //fmt.Println(string(buff)) + //fmt.Println(err) + if err != nil { + //后台没有俱乐部信息的话,游服人员可以在这里查日志!!! + logger.Logger.Errorf("ClubID=v% ClubOwner=v% at API_ClubCreateWaitCheck() req failed, err:v%", ClubID, ClubOwner, err) + return err + } + fmt.Println(string(buff)) + type ApiResult struct { + Tag int32 + Msg string + } + result := ApiResult{} + err = json.Unmarshal(buff, &result) + if err != nil { + return err + } + if result.Tag != 0 { + errMsg := fmt.Sprintf("Create ClubCreateWaitCheck result failed._%v", result.Msg) + return errors.New(errMsg) + } else { + return nil + } +} + +// 俱乐部公告审核,阻塞协程 +// 注意注意:如果给后台发送成功的话,err==nil那么请把这个俱乐部的v.NoticeCheckPosted标记为true +// OpSnid为操作者的snid,注意:操作者不一定是俱乐部创建者 +func API_ClubNoticeWaitCheck(appId string, ClubID, OpSnId int32, PltID, ClubName, ClubNotice string) error { + params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + params["platform"] = PltID + params["snid"] = strconv.Itoa(int(OpSnId)) + params["club_name"] = ClubName + params["club_id"] = strconv.Itoa(int(ClubID)) + params["club_notice"] = ClubNotice + buff, err := getRequest(appId, "/push_club_notice_review", params, nil, "http", DEFAULT_TIMEOUT) + //fmt.Println(string(buff)) + if err != nil { + //后台没有俱乐部信息的话,游服人员可以在这里查日志!!! + logger.Logger.Errorf("ClubID=v% ClubOwner=v% at API_ClubNoticeWaitCheck() req failed, err:v%", ClubID, OpSnId, err) + return err + } + type ApiResult struct { + Tag int32 + Msg string + } + result := ApiResult{} + err = json.Unmarshal(buff, &result) + if err != nil { + return err + } + if result.Tag != 0 { + errMsg := fmt.Sprintf("Create ClubNoticeWaitCheck result failed._%v", result.Msg) + return errors.New(errMsg) + } else { + return nil + } +} + +// 请求后台俱乐部的流水返给客户端用,阻塞协程 +func ReqClubTurnover(appId string, clubID int32, DateTs int64) ([]byte, error) { + params := make(map[string]string) + params["club_id"] = strconv.Itoa(int(clubID)) + params["date_ts"] = strconv.Itoa(int(DateTs)) + buff, err := getRequest(appId, "/club_room_statistics", params, nil, "http", DEFAULT_TIMEOUT) + //fmt.Println(string(buff)) + if err != nil { + logger.Logger.Errorf("ReqClubTurnover failed err:", err) + return nil, err + } + return buff, nil +} + +// 请求后台俱乐部的抽水,阻塞协程 +func ReqClubPump(appId string, pltID string, DateTs int64) ([]byte, error) { + params := make(map[string]string) + params["platform"] = pltID + params["date_ts"] = strconv.Itoa(int(DateTs)) + buff, err := getRequest(appId, "/platform_club_statistics", params, nil, "http", DEFAULT_TIMEOUT) + //fmt.Println(string(buff)) + if err != nil { + logger.Logger.Errorf("ReqClubPump failed err:", err) + return nil, err + } + return buff, nil +} + +// 请求后台俱乐部包间详细的抽水,阻塞协程 +func ReqClubRoomPumpDetail(appId string, clubID int32, clubRoomID string, PageSize, PageNum int32, DateTs int64) ([]byte, error) { + params := make(map[string]string) + params["club_id"] = strconv.Itoa(int(clubID)) + params["room_id"] = clubRoomID + params["date_ts"] = strconv.Itoa(int(DateTs)) + params["PageSize"] = strconv.Itoa(int(PageSize)) + params["PageNum"] = strconv.Itoa(int(PageNum)) + buff, err := getRequest(appId, "/clubroom_statistics", params, nil, "http", DEFAULT_TIMEOUT) + //fmt.Println(string(buff)) + if err != nil { + logger.Logger.Errorf("ReqClubRoomPumpDetail failed err:", err) + return nil, err + } + return buff, nil +} diff --git a/webapi/config.go b/webapi/config.go new file mode 100644 index 0000000..889eed9 --- /dev/null +++ b/webapi/config.go @@ -0,0 +1,32 @@ +// config +package webapi + +import ( + "fmt" + "mongo.games.com/goserver/core" +) + +var ( + Config = Configuration{} +) + +type Configuration struct { + GameApiURL string +} + +func (this *Configuration) Name() string { + return "webapi" +} + +func (this *Configuration) Init() (err error) { + fmt.Println(*this) + return nil +} + +func (this *Configuration) Close() error { + return nil +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/webapi/deprecated.go b/webapi/deprecated.go new file mode 100644 index 0000000..0ed0b2e --- /dev/null +++ b/webapi/deprecated.go @@ -0,0 +1,1053 @@ +package webapi + +import ( + "errors" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" + "time" +) + +type PlayerStatement struct { + Platform string `json:"platform"` //平台id + PackageId string `json:"packageTag"` //包标识 + SnId int32 `json:"snid"` //玩家id + IsBind int32 `json:"is_bind"` //是否是正式账号 1:正式用户 0:非正式 + TotalWin int32 `json:"total_win"` //折算后赢钱数 + TotalLose int32 `json:"total_lose"` //折算后输钱数 + TotaSrclWin int32 `json:"total_src_win"` //赢钱数 + TotaSrclLose int32 `json:"total_src_lose"` //输钱数 +} + +type PlayerStatementSrc struct { + Platform string `json:"platform"` //平台id + PackageId string `json:"packageTag"` //包标识 + SnId int32 `json:"snid"` //玩家id + IsBind int32 `json:"is_bind"` //是否是正式账号 1:正式用户 0:非正式 + TotalWin float64 `json:"total_win"` //折算后赢钱数 + TotalLose float64 `json:"total_lose"` //折算后输钱数 + TotaSrclWin float64 `json:"total_src_win"` //赢钱数 + TotaSrclLose float64 `json:"total_src_lose"` //输钱数 +} + +// 推广信息 +type PromoterData struct { + Platform int32 `json:"platform"` //平台id + PromoterId int32 `json:"promoter_id"` //推广员id + ChannelId int32 `json:"channel_id"` //渠道id + Spreader int32 `json:"spreader"` //推广人id + PromoterTree int32 `json:"promoter_tree"` //无级推广树id + Tag string `json:"tag"` //包标识 +} + +type ImgVerifyMsg struct { + ImgBase string `json:"img_base"` + Length int32 `json:"length"` + Code string `json:"code"` +} + +// ApiSendSMS 发送短信验证码 +func ApiSendSMS(appId string, body proto.Message) ([]byte, error) { + return postRequest(appId, "/send_sms", nil, body, "http", DEFAULT_TIMEOUT) +} + +// ApiGetImageVerify 获取图片验证码 +func ApiGetImageVerify(appId string, body proto.Message) ([]byte, error) { + return getRequest(appId, "/get_img_verify", nil, body, "http", DEFAULT_TIMEOUT) +} + +// 获取包对应的平台和上级关系 +//func API_PackageList(appId string) ([]byte, error) { +// //params := make(map[string]string) +// //params["ts"] = strconv.Itoa(int(time.Now().Unix())) +// //return getRequest(appId, "/package_list", params, "http", DEFAULT_TIMEOUT) +// return nil, nil +//} + +// 游戏返利配置列表 +func API_GetGameRebateConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/getGameRebateConfig", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +////获取游戏分组列表 +//func API_GetGameGroupData(appId string) ([]byte, error) { +// //params := make(map[string]string) +// //params["ts"] = strconv.Itoa(int(time.Now().Unix())) +// //return getRequest(appId, "/game_config_group_list", params, "http", DEFAULT_TIMEOUT) +// return nil, nil +//} + +// 获取公告列表 +func API_GetBulletData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/notice_list", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取招商列表 +func API_GetCustomerData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/agent_customer_list", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +////平台详细配置 +//func API_GetPlatformConfigData(appId string) ([]byte, error) { +// //params := make(map[string]string) +// //params["ts"] = strconv.Itoa(int(time.Now().Unix())) +// //return getRequest(appId, "/game_config_list", params, "http", time.Duration(time.Second*120)) +// return nil, nil +//} + +// 获得代理配置 +func API_GetPromoterConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/game_promoter_list", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 黑名单列表 +func API_GetBlackData(appId string, page int) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["page"] = strconv.Itoa(page) + //return getRequest(appId, "/black_list", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 团队信息 +func API_GetSpreadPlayer(appId string, SnId int32, platform string) ([]byte, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(SnId)) + //params["platform"] = platform + //return getRequest(appId, "/spread_player", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取公共黑名单信息 +func API_GetCommonBlackData(appId string, page int) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["page"] = strconv.Itoa(page) + //return getRequest(appId, "/black_list_common", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 支付方式 +func API_GetPayList(appId string, platform string, guestUser, newUser, userVip int, logicLevels []int32, os, snid int32) ([]byte, error) { + //params := make(map[string]string) + //params["platform"] = platform + //params["guest"] = strconv.Itoa(guestUser) + //params["new"] = strconv.Itoa(newUser) + //params["vip"] = strconv.Itoa(userVip) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["os"] = strconv.Itoa(int(os)) + //params["snid"] = strconv.Itoa(int(snid)) + // + //var logiclevel string + //for _, v := range logicLevels { + // logiclevel = logiclevel + fmt.Sprintf("%v,", v) + //} + //if len(logiclevel) > 0 { + // logiclevel = logiclevel[:len(logiclevel)-1] + // params["logiclevel"] = logiclevel + //} + //return getRequest(appId, "/pay_platform_list", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// CToAPITransfer +func API_Transfer(appId string, info string) ([]byte, error) { + //params := make(map[string]string) + //params["info"] = info + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/c2api_transfer", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +/* + int32 Snid = 1;//用户id + int32 Platform = 2;//用户id + int32 Type = 3;//商品类型 + int32 GoodsId = 4;//商品ID + int32 VCard = 5;//消耗V卡 + string GoodsName = 6;//兑换物品名称 + string UserName = 7;//兑换人姓名 + string Mobile = 8;//兑换人手机号 + string Comment = 9; //备注信息 +*/ + +// 兑换订单 +func API_CreateExchange(appId string, body proto.Message) ([]byte, error) { + + return postRequest(appId, "/create_exchange_order", nil, body, "http", DEFAULT_TIMEOUT) +} + +// 获取兑换列表 +func API_ExchangeList(appId string, body proto.Message) ([]byte, error) { + + return postRequest(appId, "/get_exchange_shop", nil, body, "http", DEFAULT_TIMEOUT) +} + +// 获取兑换记录 +func API_ExchangeRecord(appId string, body proto.Message) ([]byte, error) { + + return postRequest(appId, "/get_exchange_order", nil, body, "http", DEFAULT_TIMEOUT) +} + +// APIGetMatchAwardCode 获取比赛奖品话费兑换码 +func APIGetMatchAwardCode(appId string, body proto.Message) ([]byte, error) { + // test + //return proto.Marshal(&webapi.SAGetMatchAwardCode{ + // Tag: webapi.TagCode_SUCCESS, + // Code: "ADCDEFGHIJKLMN", + // Money: "1", + // Msg: "", + //}) + // test + + return postRequest(appId, "/get_match_award_code", nil, body, "http", DEFAULT_TIMEOUT) +} + +// 积分商城兑换订单 +func API_GradeShopExchangeList(appId, platform string, snid, page, count int32) ([]byte, error) { + //params := make(map[string]string) + //params["Platform"] = platform + //params["SnId"] = strconv.Itoa(int(snid)) + //params["Page"] = strconv.Itoa(int(page)) + //if page > 0 { + // params["Count"] = strconv.Itoa(int(count)) + //} + //params["Ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/list_exchange_shop_order", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 积分商城重置商品库存 +func API_GradeShopInitShop(appId string, InitShopIds string) ([]byte, error) { + //params := make(map[string]string) + //params["InitShopIds"] = InitShopIds + //params["Ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/zero_to_reset_exshop", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 发送短信 +func API_SendSMSCode(appId string, tel string, code string) error { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["phone"] = tel + //params["code"] = code + //buff, err := getRequest(appId, "/send_sms", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return err + //} + //type ApiResult struct { + // Tag int32 + // Msg string + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // return err + //} + //if result.Tag != 0 { + // return errors.New(result.Msg) + //} else { + // return nil + //} + return nil +} + +// 支付订单 商城 +func API_CreateOrder(appId, orderId string, configid, SnId, shopId int32, platform string, packageTag string, os, deviceId, shopname string, + amount [3]int32, consumptionamount int32, itemInfo []*webapi.ItemInfo, exchangeOrderId string) *webapi.ASCreateOrder { + body := &webapi.SACreateOrder{ + OrderId: orderId, + SnId: SnId, + ShopId: shopId, + Platform: platform, + PackageTag: packageTag, + Os: DeviceOs(os), + DeviceId: deviceId, + ConfigId: configid, + ShopName: shopname, + OrderAmount: amount[:], + ItemInfo: itemInfo, + DollarAmount: consumptionamount, + Ts: time.Now().Unix(), + ExchangeOrderId: exchangeOrderId, + } + ret := &webapi.ASCreateOrder{} + buff, err := postRequest(appId, "/create_order", nil, body, "http", DEFAULT_TIMEOUT) + if err != nil { + logger.Logger.Error("API_CreateOrder Request err:", err) + return nil + } + logger.Logger.Trace("API_CreateOrder:", string(buff)) + err = proto.Unmarshal(buff, ret) + if err != nil { + logger.Logger.Error("API_CreateOrder Unmarshal err:", err) + return nil + } + return ret +} + +// 兑换订单 +func API_CreateExchangeOrder(snid int32, platform string, before_coin, before_safe, exchange_count, tag, bank_id int64, + account, username, ip string, win_times, lose_times, win_total, lose_total int64, os, appId, channel, agent string, + promoterTree int32, packageTag string, giveGold int64, forceTax int64, needFlow int64, curFlow int64, payts int64, + payEndTs int64, newUser int, telephonePromoter int32, deviceId string) (error, int64) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["before_coin"] = strconv.Itoa(int(before_coin)) + //params["before_safe"] = strconv.Itoa(int(before_safe)) + //params["exchange_count"] = strconv.Itoa(int(exchange_count)) + //params["tag"] = strconv.Itoa(int(tag)) + //params["bank_id"] = strconv.Itoa(int(bank_id)) + //params["account"] = account + //params["username"] = username + //params["ip"] = ip + //params["win_times"] = strconv.Itoa(int(win_times)) + //params["lose_times"] = strconv.Itoa(int(lose_times)) + //params["win_total"] = strconv.Itoa(int(win_total)) + //params["lose_total"] = strconv.Itoa(int(lose_total)) + //params["os"] = DeviceOs(os) + //params["channel"] = channel + //params["promoter"] = agent + //params["promoter_tree"] = strconv.Itoa(int(promoterTree)) + //params["packageTag"] = packageTag + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["force_tax"] = strconv.Itoa(int(forceTax)) + //params["give_gold"] = strconv.Itoa(int(giveGold)) + //params["need_flow"] = strconv.Itoa(int(needFlow)) + //params["cur_flow"] = strconv.Itoa(int(curFlow)) + //params["new"] = strconv.Itoa(newUser) + //params["pay_ts"] = strconv.Itoa(int(payts)) + //params["pay_endts"] = strconv.Itoa(int(payEndTs)) + //params["telephone_promoter"] = strconv.Itoa(int(telephonePromoter)) + //params["deviceid"] = deviceId + //buff, err := getRequest(appId, "/create_exchange_order", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return err, 0 + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // return err, 0 + //} + //if result.Tag != 0 { + // errMsg := fmt.Sprintf("Create exchange order result failed._%v", result.Msg) + // return errors.New(errMsg), 0 + //} else { + // var id int64 + // switch result.Msg.(type) { + // case string: + // c, cr := strconv.Atoi(result.Msg.(string)) + // if cr == nil { + // id = int64(c) + // } + // case float64: + // id = int64(result.Msg.(float64)) + // } + // return nil, id + //} + return nil, 0 +} + +// 商品兑换订单 +func API_CreateGradeShopExchangeOrder(LogId, appId, platform string, snid, ShopId int32, Ip, ReceiveName, ReceiveTel, ReceiveAddr string, + LastGrade int64) error { + //params := make(map[string]string) + //params["Platform"] = platform //平台号 + //params["SnId"] = strconv.Itoa(int(snid)) //玩家Id + //params["LogId"] = LogId //订单id + //params["ShopId"] = strconv.Itoa(int(ShopId)) //商品Id + //params["Ip"] = Ip //IP地址 + //params["ReceiveName"] = ReceiveName //收货人名字 + //params["ReceiveTel"] = ReceiveTel //收货人电话 + //params["ReceiveAddr"] = ReceiveAddr //收货人地址 + //params["LastGrade"] = strconv.Itoa(int(LastGrade)) //玩家兑换完剩余积分 + //params["Ts"] = strconv.Itoa(int(time.Now().Unix())) //创建时间 + //buff, err := getRequest(appId, "/create_exchange_shop_order", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return err + //} + //type ApiResult struct { + // Tag int32 + // Msg string + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // return err + //} + //if result.Tag != 0 { + // errMsg := fmt.Sprintf("Create GradeShop exchange order result failed._%v", result.Msg) + // return errors.New(errMsg) + //} else { + // return nil + //} + return nil +} + +// 税收分成 +func API_TaxDivide(snid int32, platform, channel, promoter, packageTag string, tax int64, appId string, gameid, gamemode int, gamefreeid, promoterTree int32) (int32, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["tax"] = fmt.Sprintf("%v", tax) + //params["channel"] = channel + //params["promoter"] = promoter + //params["promoter_tree"] = strconv.Itoa(int(promoterTree)) + //params["packageTag"] = packageTag + //params["gameId"] = strconv.Itoa(gameid) + //params["modeId"] = strconv.Itoa(gamemode) + //params["gamefreeId"] = strconv.Itoa(int(gamefreeid)) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, "/tax_divide", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 流水推送 +func API_SpreadAccount(appId string, gamefreeId int32, data []*PlayerStatement) (int32, error) { + //d, err := json.Marshal(data) + //if err != nil { + // return -1, err + //} + //params := make(map[string]string) + //params["gamefreeId"] = strconv.Itoa(int(gamefreeId)) + //params["data"] = string(d[:]) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, "/spread_account_push", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 系统赠送 +func API_SystemGive(snid int32, platform, channel, promoter string, ammount, tag int32, appId string, packageTag string) (int32, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["channel"] = channel + //params["promoter"] = promoter + //params["package_tag"] = packageTag + //params["amount"] = fmt.Sprintf("%v", ammount) + //params["tg"] = strconv.Itoa(int(tag)) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, "/system_give", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 首次登录通知 +func API_PlayerEvent(event int, platform, packageTag string, snid int32, channel string, promoter string, promoterTree int32, isCreate int, isNew int, isBind int, appId string) (int32, error) { + //params := make(map[string]string) + //params["event"] = strconv.Itoa(event) + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["channel"] = channel + //params["promoter"] = promoter + //params["promoter_tree"] = strconv.Itoa(int(promoterTree)) + //params["packageTag"] = packageTag + //params["isCreate"] = strconv.Itoa(isCreate) + //params["isNew"] = strconv.Itoa(isNew) + //params["isBind"] = strconv.Itoa(isBind) + //params["create_time"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, "/player_event", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 推送全民推广关系链 +func API_PushSpreadLink(snid int32, platform, packageTag string, inviterId int, isBind, isForce int, appId string) (int32, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["packageTag"] = packageTag + //params["inviter"] = strconv.Itoa(inviterId) + //params["is_bind"] = strconv.Itoa(isBind) + //params["is_force"] = strconv.Itoa(isForce) + //buff, err := getRequest(appId, "/push_spread_link", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + ////fmt.Println("push_spread_link Response:", string(buff[:])) + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 推送全民推广关系链 +func API_PushInviterIp(snid, inviterId, promoterTree int32, promoter string, bundleId, ip string, os string, appId string) (int32, *PromoterData, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["bundle_id"] = bundleId + //params["ip"] = ip + //params["os"] = DeviceOs(os) + //params["inviter"] = strconv.Itoa(int(inviterId)) + //params["promoter_tree"] = strconv.Itoa(int(promoterTree)) + //params["promoter"] = promoter + // + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, "/push_inviter_ip", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, nil, err + //} + //type ApiResult struct { + // Tag int32 + // Msg *PromoterData + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // type ErrResult struct { + // Tag int32 + // Msg string + // } + // errRes := ErrResult{} + // errTag := json.Unmarshal(buff, &errRes) + // if errTag == nil { + // logger.Logger.Warnf("API_PushInviterIp response tag:%v msg:%v err:%v snid:%v packagetag:%v ip:%v ", errRes.Tag, errRes.Msg, snid, bundleId, ip) + // } + //} + //return result.Tag, result.Msg, err + return 0, nil, nil +} + +// 推送全民推广关系链 +func API_PushInvitePromoter(snid int32, promoter string, appId string) (int32, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["promoter"] = promoter + // + //buff, err := getRequest(appId, "/bind_pay_exchange", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + ////fmt.Println("push_spread_link Response:", string(buff[:])) + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 无限代信息校验 +func API_ValidPromoterTree(snid int32, packageTag string, promoterTree int32, appId string) (int32, error) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["package_tag"] = packageTag + //params["promoter_tree"] = strconv.Itoa(int(promoterTree)) + //buff, err := getRequest(appId, "/valid_promoter_tree", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return -1, err + //} + //type ApiResult struct { + // Tag int32 + // Msg interface{} + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //return result.Tag, err + return 0, nil +} + +// 玩家透传API +func API_PlayerPass(snid int32, platform, channel, promoter, apiName, param, appId string, logicLvls []int32) (string, error) { + //params := make(map[string]string) + //if param != "" { + // cparam := make(map[string]interface{}) + // err := json.Unmarshal([]byte(param), &cparam) + // if err == nil { + // for k, v := range cparam { + // switch v.(type) { + // case float64: + // params[k] = fmt.Sprintf("%v", int64(v.(float64))) + // default: + // params[k] = fmt.Sprintf("%v", v) + // } + // } + // } + //} + //params["snid"] = strconv.Itoa(int(snid)) + //params["platform"] = platform + //params["channel"] = channel + //params["promoter"] = promoter + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, apiName, params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return "", err + //} + //return string(buff[:]), err + return "", nil +} + +// 系统透传API +func API_SystemPass(apiName, param, cBData, appId string) (string, error) { + body := &webapi.SAWebAPISystemPass{ + Params: param, + CBData: cBData, + Ts: time.Now().Unix(), + } + buff, err := postRequest(appId, apiName, nil, body, "http", DEFAULT_TIMEOUT) + if err != nil { + return "", err + } + ret := &webapi.ASWebAPISystemPass{} + err = proto.Unmarshal(buff, ret) + if err != nil { + return "", err + } + if ret.OpRetCode == 1 { + return "", errors.New(ret.ErrMsg) + } + return ret.Response, err +} + +// 定时任务 +func API_ScheduleDo(appId, action string, dura time.Duration) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //buff, err := getRequest(appId, action, params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return buff, err + //} + //return buff, nil + return nil, nil +} + +// 平台详细配置 +func API_GetPlatformSignConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_sign_config", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 活跃任务 +func API_GetTaskConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_task_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 财神任务 +func API_GetGoldTaskConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_goldtask_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 财神降临 +func API_GetGoldComeConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_goldcome_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// vip活动配置 +func API_GetActVipConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_vip_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 首充奖励活动配置 +func API_GetActFPayConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_fpay_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 活动相关统一配置 +func API_GetActConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_act_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 支付活动配置 +func API_GetPayActConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/active_payact_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取在线奖励活动配置 +func API_GetOnlineRewardConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + ////params["ts"] = strconv.Itoa(int(time.Now().Unix())) + ////params["platform"] = platform + //return getRequest(appId, "/online_reward_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取幸运转盘活动配置 +func API_GetLuckyTurntableConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + ////params["ts"] = strconv.Itoa(int(time.Now().Unix())) + ////params["platform"] = platform + //return getRequest(appId, "/luckly_turntable_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取微信分享彩金配置 +func API_GetWeiXinShareConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //return getRequest(appId, "/weixin_share_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取余额宝配置 +func API_GetYebConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + ////params["ts"] = strconv.Itoa(int(time.Now().Unix())) + ////params["platform"] = platform + //return getRequest(appId, "/yeb_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取周卡月卡配置 +func API_GetCardConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //return getRequest(appId, "/card_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取阶梯充值配置 +func API_GetStepRechargeConfig(appId string) ([]byte, error) { + //params := make(map[string]string) + //return getRequest(appId, "/step_recharge_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取自动黑白名单配置 +func API_GetAutoBWConfig(appId string, page int) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //params["page"] = strconv.Itoa(page) + //return getRequest(appId, "/autobw_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 获取包对应的平台和上级关系 +func API_GetImgVerify(appId string, phone string) (*ImgVerifyMsg, error) { + //params := make(map[string]string) + //params["phone"] = phone + //buff, err := getRequest(appId, "/get_img_verify", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return nil, err + //} + // + //type ApiResult struct { + // Tag int32 `json:"Tag"` + // Msg ImgVerifyMsg `json:"Msg"` + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // return nil, err + //} + //if result.Tag != 0 { + // return nil, errors.New("Get Image Verify Failed.") + //} else { + // return &result.Msg, nil + //} + return nil, nil +} + +type RebateImgUrlMsg struct { + Wx string `json:"wx"` + Image string `json:"image"` +} + +// 获取包对应的平台和上级关系 +func API_GetRebateImgUrl(appId string, platform string) (string, string, error) { + //params := make(map[string]string) + //params["platform"] = platform + //buff, err := getRequest(appId, "/get_weixin_by_range", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return "", "", err + //} + // + //type ApiResult struct { + // Tag int32 `json:"Tag"` + // Msg RebateImgUrlMsg `json:"Msg"` + //} + //result := ApiResult{} + //err = json.Unmarshal(buff, &result) + //if err != nil { + // return "", "", err + //} + //if result.Tag != 0 { + // return "", "", errors.New("Get Image Url Failed.") + //} else { + // return result.Msg.Image, result.Msg.Wx, nil + //} + return "", "", nil +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +//请求参数如下: +//int32 snid 玩家id +//==================================================================== +//int64 showTypeId = 2 棋牌游戏 +// showTypeId = 3 捕鱼游戏(废弃,不在使用) +// showTypeId = 4 电子游艺 +// showTypeId = 5 真人视讯 +// showTypeId = 6 彩票游戏 +// showTypeId = 380720001 WWG 大师捕鱼 在三方中的游戏id=3 +// showTypeId = 391590001 FG 捕鱼嘉年华3D 在三方中的游戏id=fish_3D +// showTypeId = 391600001 FG 捕鸟达人 在三方中的游戏id=fish_bn +// showTypeId = 391610001 FG 欢乐捕鱼 在三方中的游戏id=fish_hl +// showTypeId = 391620001 FG 美人捕鱼 在三方中的游戏id=fish_mm +// showTypeId = 391630001 FG 天天捕鱼 在三方中的游戏id=fish_tt +// showTypeId = 391640001 FG 雷霆战警 在三方中的游戏id=fish_zj +// showTypeId = 391650001 FG 魔法王者 在三方中的游戏id=fish_mfwz +//====================================================================== +//int64 timeIndex 0.全部 1.今天 2.昨天 3.一个月内 +//int64 thirdId 那个第三方 0=全部 WWG平台=38 FG平台=39 体育赛事=41 VR彩票=43 真人视讯=28 +//int32 pageNo 当前页 +//int32 pageCount 共几页 + +// API返回的每条格式如下: +// int64 Ts //注单时间戳 +// string ThirdPltName //三方平台名字 +// string ThirdGameId //三方游戏id +// string ThirdGameName //三方游戏名字 +// string SysGamefreeid //我们系统的游戏id,这个不返回占位 +// string RecordId //注单号 +// int64 BetCoin //投注金额 +// int64 ReceivedCoin //已派奖 +func API_GetThirdDetail(appId, pltform string, snid, pageNo, pageCount, showTypeId, timeIndex, thirdId int32) (error, []byte) { + return nil, nil + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["pageNo"] = strconv.Itoa(int(pageNo)) + //params["pageCount"] = strconv.Itoa(int(pageCount)) + //params["showTypeId"] = strconv.Itoa(int(showTypeId)) + //params["timeIndex"] = strconv.Itoa(int(timeIndex)) + //params["thirdId"] = strconv.Itoa(int(thirdId)) + //params["platform"] = pltform + //buff, err := getRequest(appId, "/third_detail", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return err, buff + //} + //return nil, buff + + //下面是以post方式请求,备用 + //var client = &http.Client{} + //var signupDataBuff []byte + //var err error + //ts := time.Now().Unix() + //if params != nil { + // signupDataBuff, err = json.Marshal(params) + // if err != nil { + // return err, nil + // } + //} + // + ////fmt.Println(string(signupDataBuff)) + //sign := MakeMd5String(fmt.Sprintf("%v;%v;%v", ts, string(signupDataBuff), appId)) + //url := fmt.Sprintf("%v?ts=%v&sign=%v", Config.GameApiURL+"/third_detail", ts, sign) + //logger.Trace("API_GetThirdDetail request url:", url) + //request, err := http.NewRequest("POST", url, bytes.NewReader(signupDataBuff)) + //if err != nil { + // return err, nil + //} + //client.Timeout = DEFAULT_TIMEOUT + //resp, err := client.Do(request) + //if err != nil { + // logger.Errorf("Snid=%v API_GetThirdDetail api :%v", snid, err) + // return err, nil + //} + //defer resp.Body.Close() + // + //if resp.StatusCode == 200 { + // buff, err := io.ReadAll(resp.Body) + // if err != nil { + // return err, nil + // } + // return nil, buff + //} + //io.Copy(ioutil.Discard, resp.Body) + //return fmt.Errorf("API_GetThirdDetail HttpStatusCode:%d", resp.StatusCode), nil +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////// +//请求参数如下: +//int32 snid 玩家id +//==================================================================== +//string hotGameNameSet = "狂欢派对","复仇者联盟","百人牛牛" +//在mysql 中使用in来查询 +//====================================================================== +//int64 timeIndex 0.全部 1.今天 2.昨天 3.一个月内 +//int64 thirdId 那个第三方 WWG平台=38 FG平台=39 体育赛事=41 VR彩票=43 真人视讯=28 +//int32 pageNo 当前页 +//int32 pageCount 共几页 + +// API返回的每条格式如下: +// int64 Ts //注单时间戳 +// string ThirdPltName //三方平台名字 +// string ThirdGameId //三方游戏id +// string ThirdGameName //三方游戏名字 +// string SysGamefreeid //我们系统的游戏id,这个不返回占位 +// string RecordId //注单号 +// int64 BetCoin //投注金额 +// int64 ReceivedCoin //已派奖 +func API_GetThirdHotGameDetail(appId, pltform string, snid, pageNo, pageCount, timeIndex, thirdId int32, hotGameNameSet []string) (error, []byte) { + //params := make(map[string]string) + //params["snid"] = strconv.Itoa(int(snid)) + //params["pageNo"] = strconv.Itoa(int(pageNo)) + //params["pageCount"] = strconv.Itoa(int(pageCount)) + //params["hotGameNameSet"] = strings.Join(hotGameNameSet, ",") + //params["timeIndex"] = strconv.Itoa(int(timeIndex)) + //params["thirdId"] = strconv.Itoa(int(thirdId)) + //params["platform"] = pltform + //buff, err := getRequest(appId, "/hot_third_game", params, "http", DEFAULT_TIMEOUT) + //if err != nil { + // return err, buff + //} + //return nil, buff + return nil, nil +} + +// //////////////////////////////////////////////////////////////////////////////////////////////// +func API_GetRandCoinData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/activity_read_envelope_config", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// //////////////////////////////////////////////////////////////////////////////////////////////// +func API_GetChildData(appId string, platform string, snid int32, ts1, ts2 int64) ([]byte, error) { + //params := make(map[string]string) + //params["platform"] = platform + //params["snid"] = strconv.Itoa(int(snid)) + //params["start_time"] = strconv.Itoa(int(ts1)) + //params["end_time"] = strconv.Itoa(int(ts2)) + //return getRequest(appId, "/spread_child_list", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +func API_PushPlayerSingleAdjustCount(appId string, id int32, count int32) ([]byte, error) { + //params := make(map[string]string) + //params["id"] = strconv.Itoa(int(id)) + //params["times"] = strconv.Itoa(int(count)) + //return getRequest(appId, "/game_ctrl_alone_times_push", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} +func API_PlayerSingleAdjustData(appId string, page int32, pagecount int32) ([]byte, error) { + //params := make(map[string]string) + //params["page"] = strconv.Itoa(int(page)) + //params["limit"] = strconv.Itoa(int(pagecount)) + //return getRequest(appId, "/game_ctrl_alone_config", params, "http", DEFAULT_TIMEOUT) + return nil, nil +} + +// 平台杀率配置 +func API_GetPlatformProfitControlConfigData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/profitcontrol_config_list", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 比赛配置 +func API_GetMatchConfigData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/match_list", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 比赛报名券活动配置 +func API_GetActTicketConfigData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/activity_ticket_config", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 比赛积分商城配置 +func API_GetGradeShopConfigData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/query_exchange_shop", params, "http", time.Duration(time.Second*120)) + return nil, nil +} + +// 用户分层配置 +func API_GetLogicLevelConfigData(appId string) ([]byte, error) { + //params := make(map[string]string) + //params["ts"] = strconv.Itoa(int(time.Now().Unix())) + //return getRequest(appId, "/logic_level_config", params, "http", time.Duration(time.Second*120)) + return nil, nil +} diff --git a/webapi/reqrep.go b/webapi/reqrep.go new file mode 100644 index 0000000..29fa18d --- /dev/null +++ b/webapi/reqrep.go @@ -0,0 +1,152 @@ +package webapi + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +const ( + STATE_OK = 1 + STATE_ERR = 0 +) + +const ( + RESPONSE_STATE = "State" + RESPONSE_ERRMSG = "ErrMes" + RESPONSE_PAGECOUNT = "PageCount" + RESPONSE_PAGENO = "PageNo" + RESPONSE_TOTAL = "Total" + RESPONSE_DATA = "Data" +) + +type RequestBody map[string]interface{} + +func NewRequestBody(data []byte) (RequestBody, error) { + m := make(map[string]interface{}) + var err error + if len(data) > 0 { + err = json.Unmarshal(data, &m) + } + return RequestBody(m), err +} + +func (rp RequestBody) GetStr(key string) (string, bool) { + if val, ok := rp[key]; ok { + if str, ok := val.(string); ok { + return str, true + } + + return fmt.Sprintf("%v", val), false + } + return "", false +} + +func (rp RequestBody) GetInt(key string) (int, bool) { + if val, ok := rp[key]; ok { + if fval, ok := val.(float64); ok { + return int(fval), true + } + + if sval, ok := val.(string); ok { + v, err := strconv.Atoi(sval) + if err == nil { + return v, true + } + } + } + return 0, false +} + +func (rp RequestBody) GetInt64(key string) (int64, bool) { + if val, ok := rp[key]; ok { + if fval, ok := val.(float64); ok { + return int64(fval), true + } + + if sval, ok := val.(string); ok { + v, err := strconv.ParseInt(sval, 10, 64) + if err == nil { + return v, true + } + } + } + return 0, false +} + +func (rp RequestBody) GetFloat32(key string) (float32, bool) { + if val, ok := rp[key]; ok { + if fval, ok := val.(float64); ok { + return float32(fval), true + } + + if sval, ok := val.(string); ok { + v, err := strconv.ParseInt(sval, 10, 32) + if err == nil { + return float32(v), true + } + } + } + return 0, false +} + +func (rp RequestBody) GetFloat64(key string) (float64, bool) { + if val, ok := rp[key]; ok { + if fval, ok := val.(float64); ok { + return fval, true + } + + if sval, ok := val.(string); ok { + v, err := strconv.ParseInt(sval, 10, 64) + if err == nil { + return float64(v), true + } + } + } + return 0, false +} + +func (rp RequestBody) GetBool(key string) (bool, bool) { + if val, ok := rp[key]; ok { + if bval, ok := val.(bool); ok { + return bval, true + } + if sval, ok := val.(string); ok { + lows := strings.ToLower(sval) + if strings.Compare(lows, "true") == 0 { + return true, true + } else if strings.Compare(lows, "false") == 0 { + return false, true + } + } + } + return false, false +} + +func (rp RequestBody) GetData(key string) (interface{}, bool) { + if val, ok := rp[key]; ok { + return val, true + } + return nil, false +} + +func (rp RequestBody) GetRequestBody(key string) (RequestBody, bool) { + if val, ok := rp[key]; ok { + if bval, ok := val.(map[string]interface{}); ok { + return RequestBody(bval), true + } + } + return nil, false +} + +type ResponseBody map[string]interface{} + +func NewResponseBody() ResponseBody { + m := make(map[string]interface{}) + return ResponseBody(m) +} + +func (rb ResponseBody) Marshal() ([]byte, error) { + return json.Marshal(rb) +} diff --git a/webapi/sign.go b/webapi/sign.go new file mode 100644 index 0000000..2b6d0c0 --- /dev/null +++ b/webapi/sign.go @@ -0,0 +1,67 @@ +// 生成签名用 +package webapi + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "io" + "net/url" + "sort" + "strconv" + "time" +) + +// 生成签名 +func MakeSig(appId, action string, params map[string]string, data []byte, ts int) string { + h := md5.New() + io.WriteString(h, action) + io.WriteString(h, strconv.Itoa(ts)) + io.WriteString(h, appId) + if len(params) > 0 { + vals := []string{} + for _, v := range params { + vals = append(vals, v) + } + //sort + sort.Strings(vals) + + cnt := len(vals) + for i := 0; i < cnt; i++ { + io.WriteString(h, vals[i]) + } + } + if len(data) > 0 { + h.Write(data) + } + //println() + return hex.EncodeToString(h.Sum(nil)) +} + +// 生成请求串 +func MakeURL(appId, action string, params map[string]string, data []byte) string { + var buf bytes.Buffer + buf.WriteString(Config.GameApiURL) + buf.WriteString(action) + buf.WriteString("?") + if len(params) > 0 { + for k, v := range params { + buf.WriteString(k) + buf.WriteString("=") + buf.WriteString(url.QueryEscape(v)) + buf.WriteString("&") + } + } + ts := time.Now().Nanosecond() + buf.WriteString("nano=" + strconv.Itoa(ts) + "&") + buf.WriteString("sign=") + buf.WriteString(url.QueryEscape(MakeSig(appId, action, params, data, ts))) + return buf.String() +} +func MakeMd5String(strs ...string) string { + buff := md5.New() + for _, value := range strs { + io.WriteString(buff, value) + } + return hex.EncodeToString(buff.Sum(nil)) +} diff --git a/webapi/thridplatformdef.go b/webapi/thridplatformdef.go new file mode 100644 index 0000000..56eec5a --- /dev/null +++ b/webapi/thridplatformdef.go @@ -0,0 +1,97 @@ +package webapi + +import ( + "errors" + "fmt" + "net/http" + + "mongo.games.com/game/common" +) + +var ReqCgAddr string + +// 没有单独创建账号的接口 +var ( + ErrNoCreated = errors.New("no create account") + ErrRequestTimeout = errors.New(fmt.Sprintf("%v", http.StatusRequestTimeout)) +) + +type ThirdError struct { + err error + isColse bool +} + +func (e ThirdError) Error() string { + return e.err.Error() +} + +func (e ThirdError) IsClose() bool { + return e.isColse +} + +const ( + ThridAccountStatus_OK int32 = 200 //三方账户状态正常 + ThridAccountStatus_Exception int32 = 403 //三方账户状态异常 + CgReqThirdApiTimeOutCode int = 12345 //cg工程请求三方接口超时返回Code约定 +) +const ( + Req_MD5_Salt = "me13ekihf9FGipFMnd56wqqRtfgd2DrqsG" +) + +type WebAPI_ThirdPlatformGameMapping struct { + GameFreeID int32 + ThirdPlatformName string + ThirdGameID string + Desc string + ScreenOrientationType int32 + ThirdID int32 +} + +// 第三方平台基类 +type ThirdPlatformBase struct { + Id int32 + Name string + Sort int32 //排序,在客户端要变现的顺序,暂时没有用到这个 + BaseURL string + Tag string //获取平台标记(没有中文,纯小写字母) + SceneId int //场景id,与SceneMgr中的sceneID对应 + VultGameID int32 //虚拟游戏ID + BaseGameID int //场景ID + IsNeedCheckQuota bool //是否需要检查平台配额 + CurrentPltQuota int64 //当前配额剩余的金额 + ReqTimeOut int32 //请求超时设置 + GamefreeIdMappingInfo map[int32]*WebAPI_ThirdPlatformGameMapping + ThirdgameIdMappingInfo map[string]*WebAPI_ThirdPlatformGameMapping + TransferInteger bool //转账现金必须为整数金额 +} + +// 定义第三方平台接口,后续新加的平台则实现如下接口就可以 +type IThirdPlatform interface { + GetPlatformBase() ThirdPlatformBase + MappingGameName(snId int32) string //映射游戏名 + ReqCreateAccount(snId int32, platform, channel, ip string) error //创建账户 + ReqUserBalance(snId int32, platform, channel, ip string) (err error, balance int64) //用户余额 + ReqIsAllowTransfer(snId int32, platform, channel string) bool //是否允许转账 + ReqTransfer(snId int32, amount int64, transferId string, platform, channel, ip string) (e error, timeout bool) //转账 + ReqEnterGame(snId int32, gameId string, clientIP string, platform, channel string, amount int64) (err error, ret_url string) //进入游戏,返回进入游戏的URL + ReqLeaveGame(snId int32, gameId string, clientIP string, platform, channel string) (err error, amount int64) //退出游戏,返回金币 + InitMappingRelation(db map[int32]*WebAPI_ThirdPlatformGameMapping) //初始化映射关系 + GamefreeId2ThirdGameInfo(gamefreeid int32) (thirdInfo *WebAPI_ThirdPlatformGameMapping) //我方gamefreeid映射到三方 + ThirdGameInfo2GamefreeId(thirdInfo *WebAPI_ThirdPlatformGameMapping) (gamefreeid int32) //三方信息映射到我方gamefreeid +} + +// 将定义的三方平台注册到管理器中,在这里统一管理,避免在其它地方做多分支判断 +func init() { + ThridPlatformMgrSington.register(&XHJThridPlatform{ + ThirdPlatformBase{ + Id: 1, + Name: "XHJ平台", + BaseURL: "", + Tag: "xhj", + IsNeedCheckQuota: false, + BaseGameID: common.GameId_Thr_XHJ, + SceneId: 901, + VultGameID: 9010001, + }, + }) +} diff --git a/webapi/thridplatformmgr.go b/webapi/thridplatformmgr.go new file mode 100644 index 0000000..797d3a6 --- /dev/null +++ b/webapi/thridplatformmgr.go @@ -0,0 +1,81 @@ +package webapi + +import ( + "sync" + "sync/atomic" + "time" +) + +var ThridPlatformMgrSington = &ThridPlatformMgr{ + billno: time.Now().UnixNano(), +} + +type ThridPlatformMgr struct { + ThridPlatformMap sync.Map + billno int64 +} + +func (this *ThridPlatformMgr) register(p IThirdPlatform) { + this.ThridPlatformMap.Store(p.GetPlatformBase().Name, p) +} + +// 为什么要这样做? +// 避免并发量大的话出现订单号重复的情况 +// 但非三方游戏代码中有使用微秒时间戳作为billno,仅仅修改三方游戏的话必然存在碰撞问题,暂时先不要加了 +func (this *ThridPlatformMgr) GenerateUniqueBillno() int64 { + return atomic.AddInt64(&this.billno, 1) +} + +// 根据平台ID获得平台map,id=-1则返回全部的平台 +func (this *ThridPlatformMgr) FindPlatformByPlatformId(id int32) []IThirdPlatform { + set := make([]IThirdPlatform, 0) + if id == -1 { + this.ThridPlatformMap.Range(func(key, value interface{}) bool { + set = append(set, value.(IThirdPlatform)) + return true + }) + return set + } + + this.ThridPlatformMap.Range(func(key, value interface{}) bool { + if value.(IThirdPlatform).GetPlatformBase().Id == id { + set = append(set, value.(IThirdPlatform)) + return false + } + return true + }) + return set +} + +// 根据场景ID查找属于哪个平台 +func (this *ThridPlatformMgr) FindPlatformByPlatformBaseGameId(baseGameId int) (plt IThirdPlatform) { + this.ThridPlatformMap.Range(func(key, value interface{}) bool { + if value.(IThirdPlatform).GetPlatformBase().BaseGameID == baseGameId { + plt = value.(IThirdPlatform) + return false + } + return true + }) + return +} +func (this *ThridPlatformMgr) PlatformIsExist(id int32) (isExist bool) { + isExist = false + this.ThridPlatformMap.Range(func(key, value interface{}) bool { + if value.(IThirdPlatform).GetPlatformBase().Id == id { + isExist = true + return false + } + return true + }) + return isExist +} + +// 获得平台数量 +func (this *ThridPlatformMgr) AllPlatformCount() int32 { + count := int32(0) + this.ThridPlatformMap.Range(func(key, value interface{}) bool { + count++ + return true + }) + return count +} diff --git a/webapi/tostring.go b/webapi/tostring.go new file mode 100644 index 0000000..cee575f --- /dev/null +++ b/webapi/tostring.go @@ -0,0 +1,28 @@ +package webapi + +import ( + . "reflect" + "strconv" +) + +func valueToString(val Value) string { + typ := val.Type() + switch val.Kind() { + case Int, Int8, Int16, Int32, Int64: + return strconv.FormatInt(val.Int(), 10) + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return strconv.FormatUint(val.Uint(), 10) + case Float32, Float64: + return strconv.FormatFloat(val.Float(), 'g', -1, 64) + case String: + return val.String() + case Bool: + if val.Bool() { + return "true" + } else { + return "false" + } + default: + panic("valueToString: can't print type " + typ.String()) + } +} diff --git a/webapi/webapi.go b/webapi/webapi.go new file mode 100644 index 0000000..7a29f34 --- /dev/null +++ b/webapi/webapi.go @@ -0,0 +1,23 @@ +package webapi + +import "mongo.games.com/game/protocol/webapi" + +// 平台配置列表 +func API_GetPlatformData(appId string) ([]byte, error) { + return postRequest(appId, "/game_srv/platform_list", nil, &webapi.SAPlatformInfo{PlatformId: 0}, "http", DEFAULT_TIMEOUT) +} + +// 平台游戏配置 +func API_GetPlatformConfigData(appId string) ([]byte, error) { + return postRequest(appId, "/game_srv/game_config_list", nil, nil, "http", DEFAULT_TIMEOUT) +} + +// 游戏分组列表 +func API_GetGameGroupData(appId string) ([]byte, error) { + return postRequest(appId, "/game_srv/game_config_group", nil, nil, "http", DEFAULT_TIMEOUT) +} + +// 全局游戏开关 +func API_GetGlobalGameStatus(appId string) ([]byte, error) { + return postRequest(appId, "/game_srv/game_config_global", nil, nil, "http", DEFAULT_TIMEOUT) +} diff --git a/webapi/xhj.go b/webapi/xhj.go new file mode 100644 index 0000000..292e0ae --- /dev/null +++ b/webapi/xhj.go @@ -0,0 +1,238 @@ +package webapi + +import ( + "bytes" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "sync/atomic" + "time" + + "google.golang.org/protobuf/proto" + "mongo.games.com/game/common" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" +) + +const ( + XHJ_StartGameUrl = "rocket/game_srv_third/register_or_login" + XHJ_ExitGameUrl = "rocket/game_srv_third/login_out" +) + +type XHJThridPlatform struct { + ThirdPlatformBase +} + +func (this *XHJThridPlatform) InitMappingRelation(db map[int32]*WebAPI_ThirdPlatformGameMapping) { + this.GamefreeIdMappingInfo = make(map[int32]*WebAPI_ThirdPlatformGameMapping) + this.ThirdgameIdMappingInfo = make(map[string]*WebAPI_ThirdPlatformGameMapping) + for _, v := range db { + if v.ThirdPlatformName == this.Name { + this.GamefreeIdMappingInfo[v.GameFreeID] = v + this.ThirdgameIdMappingInfo[v.ThirdGameID] = v + } + } +} +func (this *XHJThridPlatform) GamefreeId2ThirdGameInfo(gamefreeid int32) (thirdInfo *WebAPI_ThirdPlatformGameMapping) { + if v, exist := this.GamefreeIdMappingInfo[gamefreeid]; exist { + return v + } + return +} +func (this *XHJThridPlatform) ThirdGameInfo2GamefreeId(thirdInfo *WebAPI_ThirdPlatformGameMapping) (gamefreeid int32) { + if thirdInfo != nil { + for _, v := range this.GamefreeIdMappingInfo { + if thirdInfo.Desc == v.Desc { + return v.GameFreeID + } + } + } + return 0 +} + +func (this *XHJThridPlatform) GetPlatformBase() ThirdPlatformBase { + return this.ThirdPlatformBase +} +func (this *XHJThridPlatform) ReqCreateAccount(Snid int32, Platform, Channel, ip string) error { + + return nil +} +func (this *XHJThridPlatform) ReqUserBalance(Snid int32, Platform, Channel, ip string) (err error, balance int64) { + pack := &webapi_proto.SARocketLoginOut{ + Snid: int64(Snid), + Platform: Platform, + } + + buff, err := this.postRequest(common.GetAppId(), XHJ_ExitGameUrl, nil, pack, "http", DEFAULT_TIMEOUT) + + if err != nil { + return err, 0 + } + + ar := webapi_proto.ASRocketLoginOut{} + err = proto.Unmarshal(buff, &ar) + if err != nil { + return errors.New(ar.Msg), 0 + } + logger.Trace("XHJReqUserBalance Return:", Snid, ar.Amount) + return err, int64(ar.Amount) + // return nil, 0 +} +func (this *XHJThridPlatform) ReqIsAllowTransfer(Snid int32, Platform, Channel string) bool { + + return true +} +func (this *XHJThridPlatform) ReqCheckTransferIsSuccess(Snid int32, TransferId string, Platform, Channel string) error { + + return nil +} +func (this *XHJThridPlatform) ReqTransfer(Snid int32, Amount int64, TransferId string, Platform, Channel, ip string) (e error, timeout bool) { + + return nil, false +} + +func (this *XHJThridPlatform) ReqEnterGame(Snid int32, gameid string, clientIP string, Platform, Channel string, amount int64) (err error, ret_url string) { + pack := &webapi_proto.SARocketLogin{ + Snid: int64(Snid), + Amount: amount, + Platform: Platform, + } + + buff, err := this.postRequest(common.GetAppId(), XHJ_StartGameUrl, nil, pack, "http", DEFAULT_TIMEOUT) //DEFAULT_TIMEOUT + + if err != nil { + return err, "" + } + + ar := webapi_proto.ASRocketLogin{} + err = proto.Unmarshal(buff, &ar) + if err == nil && ar.Url == "" { + return errors.New(ar.Msg), "" + } + logger.Trace("XHJReqEnterGame Return:", Snid, ar.Url) + return err, ar.Url +} +func (this *XHJThridPlatform) ReqLeaveGame(Snid int32, gameid string, clientIP string, Platform, Channel string) (err error, amount int64) { + pack := &webapi_proto.SARocketLoginOut{ + Snid: int64(Snid), + Platform: Platform, + } + + buff, err := this.postRequest(common.GetAppId(), XHJ_ExitGameUrl, nil, pack, "http", DEFAULT_TIMEOUT) + if err != nil { + return err, 0 + } + ar := webapi_proto.ASRocketLoginOut{} + err = proto.Unmarshal(buff, &ar) + if err != nil { + return errors.New("proto.Unmarshal error"), 0 + } + if ar.Tag == 2 { + //请求失败 + return errors.New(ar.Msg), 0 + } + logger.Trace("XHJReqLeaveGame Return:", Snid, ar.Amount) + return err, int64(ar.Amount) +} +func (this *XHJThridPlatform) MappingGameName(snid int32) string { + + return strconv.Itoa(int(snid)) + "_XHJ" +} + +func (this *XHJThridPlatform) postRequest(appId, action string, params map[string]string, body proto.Message, protocol string, dura time.Duration) ([]byte, error) { + var client *http.Client + if strings.ToUpper(protocol) == "HTTPS" { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client = &http.Client{Transport: tr} + } else { + client = &http.Client{} + } + + data, err := proto.Marshal(body) + if err != nil { + return nil, err + } + + callUrl := makeURL(appId, action, params, data) + //println("callurl=", callUrl) + req, err := http.NewRequest("POST", callUrl, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + + var stats *ApiStats + if v, exist := WebApiStats.Load(action); exist { + stats = v.(*ApiStats) + } else { + stats = &ApiStats{} + WebApiStats.Store(action, stats) + } + + var isTimeout bool + start := time.Now() + defer func() { + ps := int64(time.Now().Sub(start) / time.Millisecond) + if stats != nil { + if isTimeout { + atomic.AddInt64(&stats.TimeoutTimes, 1) + } + atomic.AddInt64(&stats.RunTimes, 1) + atomic.AddInt64(&stats.TotalRuningTime, ps) + if atomic.LoadInt64(&stats.MaxRuningTime) < ps { + atomic.StoreInt64(&stats.MaxRuningTime, ps) + } + } + }() + + //设置超时 + client.Timeout = dura + req.Close = true + resp, err := client.Do(req) + + if err != nil { + if uerr, ok := err.(net.Error); ok { + isTimeout = uerr.Timeout() + } + logger.Logger.Errorf(" postRequest Timeout: %v", err) + return nil, ErrRequestTimeout + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, err + } + //println("callurl=", callUrl, "code=", resp.StatusCode) + return nil, fmt.Errorf("StatusCode:%d", resp.StatusCode) +} + +// 生成请求串 +func makeURL(appId, action string, params map[string]string, data []byte) string { + var buf bytes.Buffer + buf.WriteString(ReqCgAddr) + buf.WriteString(action) + buf.WriteString("?") + if len(params) > 0 { + for k, v := range params { + buf.WriteString(k) + buf.WriteString("=") + buf.WriteString(url.QueryEscape(v)) + buf.WriteString("&") + } + } + ts := time.Now().Nanosecond() + buf.WriteString("nano=" + strconv.Itoa(ts) + "&") + buf.WriteString("sign=") + buf.WriteString(url.QueryEscape(MakeSig(appId, action, params, data, ts))) + return buf.String() +} diff --git a/worldsrv/action_bag.go b/worldsrv/action_bag.go new file mode 100644 index 0000000..b53a797 --- /dev/null +++ b/worldsrv/action_bag.go @@ -0,0 +1,270 @@ +package main + +import ( + "fmt" + "math/rand" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/bag" + "mongo.games.com/game/srvdata" +) + +// 查看背包 +type CSBagInfoPacketFactory struct { +} + +type CSBagInfoHandler struct { +} + +func (this *CSBagInfoPacketFactory) CreatePacket() interface{} { + pack := &bag.CSBagInfo{} + return pack +} + +func (this *CSBagInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBagInfoHandler Process recv ", data) + if _, ok := data.(*bag.CSBagInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSBagInfoHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + //nowLocation := int(msg.NowLocation - 1) + playbag := BagMgrSingleton.GetBagInfo(p.SnId) + pack := &bag.SCBagInfo{RetCode: bag.OpResultCode_OPRC_Sucess, BagNumMax: BagItemMax} + if playbag != nil { + for _, v := range playbag.BagItem { + item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if item != nil && v.ItemNum > 0 /*&& (nowLocation == -1 || (nowLocation < len(item.ShowLocation) && item.ShowLocation[nowLocation] == 1))*/ { + pack.Infos = append(pack.Infos, &bag.ItemInfo{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + //Name: item.Name, + //ShowLocation: item.ShowLocation, + //Classify: item.Classify, + //Type: item.Type, + //Effect0: item.Effect0, + //Effect: item.Effect, + //SaleType: item.SaleType, + //SaleGold: item.SaleGold, + //Composition: item.Composition, + //CompositionMax: item.CompositionMax, + //Time: item.Time, + //Location: item.Location, + //Describe: item.Describe, + ObtainTime: v.ObtainTime, + }) + } + } + } + logger.Logger.Trace("SCBagInfo:", pack) + p.SendToClient(int(bag.SPacketID_PACKET_ALL_BAG_INFO), pack) + } + return nil +} + +// 使用/获取道具 PS严格来说客户端只存在消耗道具 服务器增加 上线要禁止该接口增加道具 +type CSUpBagInfoPacketFactory struct { +} + +type CSUpBagInfoHandler struct { +} + +func (this *CSUpBagInfoPacketFactory) CreatePacket() interface{} { + pack := &bag.CSUpBagInfo{} + return pack +} + +func (this *CSUpBagInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSUpBagInfoHandler Process recv ", data) + msg, ok := data.(*bag.CSUpBagInfo) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSUpBagInfoHandler p == nil") + return nil + } + + platform := p.GetPlatform() + if platform == nil { + return nil + } + + pack := &bag.SCUpBagInfo{RetCode: bag.OpResultCode_OPRC_Error} + send := func() { + p.SendToClient(int(bag.SPacketID_PACKET_ALL_BAG_USE), pack) + logger.Logger.Tracef("SCUpBagInfo: %v", pack) + } + + item := BagMgrSingleton.GetItem(p.SnId, msg.ItemId) + if item == nil || msg.ItemNum <= 0 || item.ItemNum < int64(msg.ItemNum) || len(item.Effect) != ItemMax || p.SnId == msg.AcceptSnId { + send() + return nil + } + + var isCanOp int32 + if msg.NowEffect == 0 { //竖版 + isCanOp = item.Effect0[msg.Opt] + } else if msg.NowEffect == 1 { //横版 + isCanOp = item.Effect[msg.Opt] + } + + if isCanOp == 0 { + logger.Logger.Trace("道具没有操作权限", msg.ItemId) + send() + return nil + } + + ts := time.Now().Unix() + + switch msg.Opt { + case ItemCanUse: + logger.Logger.Trace("道具使用", msg.ItemId) + + f := func() { + // 使用道具,减少道具 + BagMgrSingleton.SalePlayerItem(p, item, int64(msg.ItemNum)) + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(msg.ItemNum), "道具使用") + pack.RetCode = bag.OpResultCode_OPRC_Sucess + pack.NowItemId = item.ItemId + pack.NowItemNum = item.ItemNum + } + + switch item.ItemId { + case common.ItemIDGiftBox: + f() + useFunc := func() { + sum := 0 + for _, v := range srvdata.PBDB_GiftBoxMgr.Datas.GetArr() { + sum += int(v.GetRate()) + } + if sum > 0 { + n := 0 + i := rand.Intn(sum) + for _, v := range srvdata.PBDB_GiftBoxMgr.Datas.GetArr() { + n += int(v.GetRate()) + if i < n { + var items []*Item + for k, vv := range v.ItemID { + if vv > 0 { + items = append(items, &Item{ + ItemId: int32(k), + ItemNum: vv, + ObtainTime: ts, + }) + } + } + if len(items) > 0 { + BagMgrSingleton.AddJybBagInfo(p, items, 0, common.GainWay_ItemUse, "player", "道具使用") + for _, v := range items { + data := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if data != nil { + // 背包变更记录 + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, v.ItemId, data.Name, v.ItemNum, "碎片礼盒获得") + } + pack.Infos = append(pack.Infos, &bag.ItemInfo{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + ObtainTime: v.ObtainTime, + }) + } + } + break + } + } + } + } + for i := 0; i < int(msg.ItemNum); i++ { + useFunc() + } + + default: + logger.Logger.Warnf("道具使用未定义", msg.ItemId) + } + + case ItemCanGive: + logger.Logger.Trace("道具赠送", msg.ItemId) + acceptPlayer := PlayerMgrSington.GetPlatformPlayerBySnId(p.Platform, msg.AcceptSnId) + if acceptPlayer != nil { + BagMgrSingleton.AddMailByItem(p.Platform, p.SnId, p.Name, msg.AcceptSnId, msg.ShowId, []int32{msg.ItemId, msg.ItemNum}) + BagMgrSingleton.SalePlayerItem(p, item, int64(msg.ItemNum)) + remark := fmt.Sprintf("赠送给玩家(%v)", msg.AcceptSnId) + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(msg.ItemNum), remark) + logger.Logger.Trace("道具赠送成功", msg.ItemId) + pack.RetCode = bag.OpResultCode_OPRC_Sucess + pack.NowItemId = msg.ItemId + pack.NowItemNum = item.ItemNum + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.GetPlayerBaseInfo(p.Platform, msg.AcceptSnId) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + aPlayer := data.(*model.PlayerBaseInfo) + if data != nil && aPlayer != nil { + BagMgrSingleton.AddMailByItem(p.Platform, p.SnId, p.Name, msg.AcceptSnId, msg.ShowId, []int32{msg.ItemId, msg.ItemNum}) + BagMgrSingleton.SalePlayerItem(p, item, int64(msg.ItemNum)) + remark := fmt.Sprintf("赠送给玩家(%v)", msg.AcceptSnId) + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(msg.ItemNum), remark) + logger.Logger.Trace("道具赠送成功", msg.ItemId) + pack.RetCode = bag.OpResultCode_OPRC_Sucess + pack.NowItemId = msg.ItemId + pack.NowItemNum = item.ItemNum + } else { + pack.RetCode = bag.OpResultCode_OPRC_NotPlayer + } + send() + }), "GetPlayerBaseInfo").Start() + return nil + } + + case ItemCanSell: + logger.Logger.Trace("道具出售", msg.ItemId) + if msg.ItemNum > 0 { + isF := BagMgrSingleton.SaleItem(p, msg.ItemId, int64(msg.ItemNum)) + if isF { + pack.RetCode = bag.OpResultCode_OPRC_Sucess + if item.SaleGold > 0 { + if item.SaleType == 1 { + remark := "道具出售" + fmt.Sprintf("%v-%v", msg.ItemId, msg.ItemNum) + p.AddCoin(int64(item.SaleGold*msg.ItemNum), 0, common.GainWay_Item_Sale, "sys", remark) + pack.Coin = int64(item.SaleGold * msg.ItemNum) + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(msg.ItemNum), "道具出售") + } else if item.SaleType == 2 { + remark := "道具出售" + fmt.Sprintf("%v-%v", msg.ItemId, msg.ItemNum) + p.AddDiamond(int64(item.SaleGold*msg.ItemNum), 0, common.GainWay_Item_Sale, "sys", remark) + pack.Diamond = int64(item.SaleGold * msg.ItemNum) + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(msg.ItemNum), "道具出售") + } + } + pack.NowItemId = item.ItemId + pack.NowItemNum = item.ItemNum + } + } + } + + send() + return nil +} + +func init() { + // 查看背包 + common.RegisterHandler(int(bag.SPacketID_PACKET_ALL_BAG_INFO), &CSBagInfoHandler{}) + netlib.RegisterFactory(int(bag.SPacketID_PACKET_ALL_BAG_INFO), &CSBagInfoPacketFactory{}) + + // 道具操作(赠送,出售...) + common.RegisterHandler(int(bag.SPacketID_PACKET_ALL_BAG_USE), &CSUpBagInfoHandler{}) + netlib.RegisterFactory(int(bag.SPacketID_PACKET_ALL_BAG_USE), &CSUpBagInfoPacketFactory{}) +} diff --git a/worldsrv/action_chat.go b/worldsrv/action_chat.go new file mode 100644 index 0000000..b990839 --- /dev/null +++ b/worldsrv/action_chat.go @@ -0,0 +1,379 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/chat" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "time" +) + +// 用户发送消息 +type CSChatMsgPacketFactory struct { +} +type CSChatMsgHandler struct { +} + +func (this *CSChatMsgPacketFactory) CreatePacket() interface{} { + pack := &chat.CSChatMsg{} + return pack +} + +func (this *CSChatMsgHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSChatMsgHandler Process recv ", data) + if msg, ok := data.(*chat.CSChatMsg); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSChatMsgHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSChatMsgHandler platform == nil") + return nil + } + var content = msg.GetContent() + if len(content) > 150 { + logger.Logger.Warn("CSChatMsgHandler len(content) > 150") + return nil + } + pack := &chat.SCChatMsg{ + Msg2Snid: proto.Int32(msg.Msg2Snid), + Snid: proto.Int32(p.SnId), + Name: proto.String(p.Name), + Head: proto.Int32(p.Head), + HeadUrl: proto.String(p.HeadUrl), + Ts: proto.Int64(time.Now().Unix()), + OpRetCode: chat.OpResultCode_OPRC_Sucess, + } + if msg.GetMsg2Snid() == 0 { //只有大厅消息走关键字过滤 + if !ChatMgrSington.CanSendToPlatform(p.SnId) { //大厅聊天CD + return nil + } + //has := srvdata.HasSensitiveWord([]rune(msg.GetContent())) + //if has { + // content = string(srvdata.ReplaceSensitiveWord([]rune(msg.GetContent()))[:]) + // logger.Logger.Trace("CSChatMsgHandler find HasSensitiveWord then after ReplaceSensitiveWord content=", content) + //} + //content = html.EscapeString(content) + pack.Content = content + proto.SetDefaults(pack) + logger.Logger.Trace("SCChatMsg -> Platform", pack) + PlayerMgrSington.BroadcastMessageToPlatformWithHall(p.Platform, p.SnId, int(chat.ChatPacketID_PACKET_SCChatMsg), pack) + } else { //私聊 + FriendUnreadMgrSington.DelFriendUnread(p.Platform, p.SnId, msg.GetMsg2Snid()) + pack.Content = content + if !FriendMgrSington.IsFriend(p.Platform, p.SnId, msg.GetMsg2Snid()) { + pack.OpRetCode = chat.OpResultCode_OPRC_Chat_NotFriend + logger.Logger.Warn("CSChatMsgHandler not friend") + } else { + if FriendMgrSington.IsShield(p.Platform, msg.GetMsg2Snid(), p.SnId) { //对方有没有屏蔽发送者 + pack.OpRetCode = chat.OpResultCode_OPRC_Chat_IsShield + logger.Logger.Warn("CSChatMsgHandler IsShield") + } + if FriendMgrSington.IsShield(p.Platform, p.SnId, msg.GetMsg2Snid()) { //有没有屏蔽对方 + pack.OpRetCode = chat.OpResultCode_OPRC_Chat_Shield + logger.Logger.Warn("CSChatMsgHandler Shield") + } + } + if pack.OpRetCode == chat.OpResultCode_OPRC_Sucess { + msg2p := PlayerMgrSington.GetPlayerBySnId(msg.GetMsg2Snid()) + if msg2p != nil { //在线 + proto.SetDefaults(pack) + msg2p.SendToClient(int(chat.ChatPacketID_PACKET_SCChatMsg), pack) + logger.Logger.Trace("SCChatMsg SnId", p.SnId, " -> Msg2Snid:", msg.GetMsg2Snid(), pack) + FriendUnreadMgrSington.AddFriendUnread(p.Platform, msg.GetMsg2Snid(), p.SnId) + } else { //不在线 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertFriendUnread(p.Platform, msg.GetMsg2Snid(), p.SnId) + }), nil).StartByFixExecutor("UpsertFriendUnread") + + } + ChatMgrSington.AddChat(p.Platform, p.SnId, msg.GetMsg2Snid(), content) + ChatMgrSington.SaveChatData(p.Platform, p.SnId, msg.GetMsg2Snid()) + } + proto.SetDefaults(pack) + p.SendToClient(int(chat.ChatPacketID_PACKET_SCChatMsg), pack) + logger.Logger.Trace("SCChatMsg ", pack) + } + + } + return nil +} + +// 聊天记录 +type CSGetChatLogPacketFactory struct { +} +type CSGetChatLogHandler struct { +} + +func (this *CSGetChatLogPacketFactory) CreatePacket() interface{} { + pack := &chat.CSGetChatLog{} + return pack +} + +func (this *CSGetChatLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetChatLogHandler Process recv ", data) + if msg, ok := data.(*chat.CSGetChatLog); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetChatLogHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSGetChatLogHandler platform == nil") + return nil + } + if !FriendMgrSington.IsFriend(p.Platform, p.SnId, msg.GetSnid()) { + logger.Logger.Warn("CSGetChatLogHandler not friend") + return nil + } + friendInfo := FriendMgrSington.GetFriendList(p.Platform, p.SnId) + var bf *model.BindFriend + if friendInfo != nil && len(friendInfo) != 0 { + for _, friend := range friendInfo { + if friend.SnId == msg.GetSnid() { + bf = friend + } + } + } + FriendUnreadMgrSington.DelFriendUnread(p.Platform, p.SnId, msg.GetSnid()) + chatLogs := ChatMgrSington.GetChat(p.Platform, p.SnId, msg.GetSnid()) + if chatLogs != nil { + pack := &chat.SCGetChatLog{ + Snid: proto.Int32(msg.GetSnid()), + } + if chatLogs.ChatContent != nil && len(chatLogs.ChatContent) != 0 { + for _, content := range chatLogs.ChatContent { + if content.Content != "" { + if bf != nil { + srcSnid := int32(0) + srcName := "" + srcHead := int32(0) + srcHeadUrl := "" + toSnId := int32(0) + toName := "" + toHead := int32(0) + ToHeadUrl := "" + if content.SrcSnId == p.SnId { //我说的 + srcSnid = p.SnId + srcName = p.Name + srcHead = p.Head + srcHeadUrl = p.HeadUrl + toSnId = bf.SnId + toName = bf.Name + toHead = bf.Head + ToHeadUrl = bf.HeadUrl + } else { //对方说的 + srcSnid = bf.SnId + srcName = bf.Name + srcHead = bf.Head + srcHeadUrl = bf.HeadUrl + toSnId = p.SnId + toName = p.Name + toHead = p.Head + ToHeadUrl = p.HeadUrl + } + + log := &chat.ChatLog{ + SrcSnId: proto.Int32(srcSnid), + SrcName: proto.String(srcName), + SrcHead: proto.Int32(srcHead), + SrcHeadUrl: proto.String(srcHeadUrl), + ToSnId: proto.Int32(toSnId), + ToName: proto.String(toName), + ToHead: proto.Int32(toHead), + ToHeadUrl: proto.String(ToHeadUrl), + Content: proto.String(content.Content), + Ts: proto.Int64(content.Ts), + } + pack.ChatLogs = append(pack.ChatLogs, log) + } + } + } + } + proto.SetDefaults(pack) + p.SendToClient(int(chat.ChatPacketID_PACKET_SCGetChatLog), pack) + logger.Logger.Trace("SCGetChatLog ", pack) + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + bindSnid := ChatMgrSington.getBindSnid(p.SnId, msg.GetSnid()) + ret, err := model.QueryChatByBindSnid(p.Platform, bindSnid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + pack := &chat.SCGetChatLog{ + Snid: proto.Int32(msg.GetSnid()), + } + if ret, ok := data.(*model.Chat); ok && ret != nil { + ChatMgrSington.SetChat(p.Platform, ret.BindSnid, ret) + if ret.ChatContent != nil && len(ret.ChatContent) != 0 { + for _, content := range ret.ChatContent { + if content.Content != "" { + if bf != nil { + srcSnid := int32(0) + srcName := "" + srcHead := int32(0) + srcHeadUrl := "" + toSnId := int32(0) + toName := "" + toHead := int32(0) + ToHeadUrl := "" + if content.SrcSnId == p.SnId { //我说的 + srcSnid = p.SnId + srcName = p.Name + srcHead = p.Head + srcHeadUrl = p.HeadUrl + toSnId = bf.SnId + toName = bf.Name + toHead = bf.Head + ToHeadUrl = bf.HeadUrl + } else { //对方说的 + srcSnid = bf.SnId + srcName = bf.Name + srcHead = bf.Head + srcHeadUrl = bf.HeadUrl + toSnId = p.SnId + toName = p.Name + toHead = p.Head + ToHeadUrl = p.HeadUrl + } + + log := &chat.ChatLog{ + SrcSnId: proto.Int32(srcSnid), + SrcName: proto.String(srcName), + SrcHead: proto.Int32(srcHead), + SrcHeadUrl: proto.String(srcHeadUrl), + ToSnId: proto.Int32(toSnId), + ToName: proto.String(toName), + ToHead: proto.Int32(toHead), + ToHeadUrl: proto.String(ToHeadUrl), + Content: proto.String(content.Content), + Ts: proto.Int64(content.Ts), + } + pack.ChatLogs = append(pack.ChatLogs, log) + } + } + } + } + } + proto.SetDefaults(pack) + p.SendToClient(int(chat.ChatPacketID_PACKET_SCGetChatLog), pack) + logger.Logger.Trace("SCGetChatLog ", pack) + })).StartByFixExecutor("LoadChatData") + } + } + return nil +} + +// 读消息 +type CSReadChatMsgPacketFactory struct { +} +type CSReadChatMsgHandler struct { +} + +func (this *CSReadChatMsgPacketFactory) CreatePacket() interface{} { + pack := &chat.CSReadChatMsg{} + return pack +} + +func (this *CSReadChatMsgHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSReadChatMsgHandler Process recv ", data) + if msg, ok := data.(*chat.CSReadChatMsg); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSReadChatMsgHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSReadChatMsgHandler platform == nil") + return nil + } + if !FriendMgrSington.IsFriend(p.Platform, p.SnId, msg.GetSnid()) { + logger.Logger.Warn("CSReadChatMsgHandler not friend") + return nil + } + FriendUnreadMgrSington.DelFriendUnread(p.Platform, p.SnId, msg.GetSnid()) + } + return nil +} + +// 屏蔽 +type CSShieldMsgPacketFactory struct { +} +type CSShieldMsgHandler struct { +} + +func (this *CSShieldMsgPacketFactory) CreatePacket() interface{} { + pack := &chat.CSShieldMsg{} + return pack +} + +func (this *CSShieldMsgHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSShieldMsgHandler Process recv ", data) + if msg, ok := data.(*chat.CSShieldMsg); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSShieldMsgHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSShieldMsgHandler platform == nil") + return nil + } + + pack := &chat.SCShieldMsg{ + Snid: proto.Int32(p.SnId), + ShieldSnid: proto.Int32(msg.GetShieldSnid()), + Shield: proto.Bool(msg.GetShield()), + Ts: proto.Int64(proto.Int64(time.Now().Unix())), + OpRetCode: chat.OpResultCode_OPRC_Sucess, + } + if msg.GetShield() { + if FriendMgrSington.IsShield(p.Platform, p.SnId, msg.GetShieldSnid()) { + logger.Logger.Warn("重复屏蔽") + pack.OpRetCode = chat.OpResultCode_OPRC_Chat_ReShield + } + } else { + if !FriendMgrSington.IsShield(p.Platform, p.SnId, msg.GetShieldSnid()) { + logger.Logger.Warn("重复解除") + pack.OpRetCode = chat.OpResultCode_OPRC_Chat_ReUnShield + } + } + if pack.OpRetCode == chat.OpResultCode_OPRC_Sucess { + if msg.GetShield() { + FriendMgrSington.AddShield(p.Platform, p.SnId, msg.GetShieldSnid()) + } else { + FriendMgrSington.DelShield(p.Platform, p.SnId, msg.GetShieldSnid()) + } + } + proto.SetDefaults(pack) + p.SendToClient(int(chat.ChatPacketID_PACKET_SCShieldMsg), pack) + logger.Logger.Trace("SCShieldMsg ", pack) + } + return nil +} + +func init() { + //聊天消息 + common.RegisterHandler(int(chat.ChatPacketID_PACKET_CSChatMsg), &CSChatMsgHandler{}) + netlib.RegisterFactory(int(chat.ChatPacketID_PACKET_CSChatMsg), &CSChatMsgPacketFactory{}) + //聊天记录 + common.RegisterHandler(int(chat.ChatPacketID_PACKET_CSGetChatLog), &CSGetChatLogHandler{}) + netlib.RegisterFactory(int(chat.ChatPacketID_PACKET_CSGetChatLog), &CSGetChatLogPacketFactory{}) + //读消息 + common.RegisterHandler(int(chat.ChatPacketID_PACKET_CSReadChatMsg), &CSReadChatMsgHandler{}) + netlib.RegisterFactory(int(chat.ChatPacketID_PACKET_CSReadChatMsg), &CSReadChatMsgPacketFactory{}) + //屏蔽玩家 + common.RegisterHandler(int(chat.ChatPacketID_PACKET_CSShieldMsg), &CSShieldMsgHandler{}) + netlib.RegisterFactory(int(chat.ChatPacketID_PACKET_CSShieldMsg), &CSShieldMsgPacketFactory{}) +} diff --git a/worldsrv/action_coinscene.go b/worldsrv/action_coinscene.go new file mode 100644 index 0000000..25282fb --- /dev/null +++ b/worldsrv/action_coinscene.go @@ -0,0 +1,301 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" +) + +type CSCoinSceneGetPlayerNumPacketFactory struct { +} +type CSCoinSceneGetPlayerNumHandler struct { +} + +func (this *CSCoinSceneGetPlayerNumPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSCoinSceneGetPlayerNum{} + return pack +} + +func (this *CSCoinSceneGetPlayerNumHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSCoinSceneGetPlayerNumHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSCoinSceneGetPlayerNum); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + nums := CoinSceneMgrSingleton.GetPlayerNums(p, msg.GetGameId(), msg.GetGameModel()) + pack := &gamehall.SCCoinSceneGetPlayerNum{ + Nums: nums, + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.CoinSceneGamePacketID_PACKET_SC_COINSCENE_GETPLAYERNUM), pack) + } + } + return nil +} + +//type CSCoinSceneOpPacketFactory struct { +//} +//type CSCoinSceneOpHandler struct { +//} +// +//func (this *CSCoinSceneOpPacketFactory) CreatePacket() interface{} { +// pack := &gamehall.CSCoinSceneOp{} +// return pack +//} +// +//func (this *CSCoinSceneOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSCoinSceneOpHandler Process recv ", data) +// if msg, ok := data.(*gamehall.CSCoinSceneOp); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// var ret gamehall.OpResultCode +// pack := &gamehall.SCCoinSceneOp{ +// Id: msg.Id, +// OpType: msg.OpType, +// } +// +// oldPlatform := p.Platform +// switch msg.GetOpType() { +// case common.CoinSceneOp_Enter: +// //pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) +// //if pt != nil && pt.IsForceBind == 1 { +// // if p.BeUnderAgentCode == "" || p.BeUnderAgentCode == "0" { +// // ret = gamehall.OpResultCode_OPRC_MustBindPromoter +// // goto done +// // } +// //} +// +// //已经在房间里了直接返回 +// if p.scene != nil { +// logger.Logger.Warnf("CSCoinSceneOpHandler CoinSceneOp_Enter found snid:%v had in scene:%v gameid:%v", p.SnId, p.scene.sceneId, p.scene.gameId) +// p.ReturnScene(false) +// return nil +// } +// +// var roomId int32 +// params := msg.GetOpParams() +// if len(params) != 0 { +// roomId = params[0] +// platformName := CoinSceneMgrSingleton.GetPlatformBySceneId(int(roomId)) +// if p.IsRob { +// p.Platform = platformName +// } else if p.GMLevel > 0 && p.Platform == platformName { //允许GM直接按房间ID进场 +// roomId = params[0] +// } +// } +// if len(msg.GetPlatform()) > 0 && p.IsRob { +// p.Platform = msg.GetPlatform() +// } +// //检测房间状态是否开启 +// gps := PlatformMgrSingleton.GetGameFree(p.Platform, msg.GetId()) +// if gps == nil { +// ret = gamehall.OpResultCode_OPRC_RoomHadClosed +// goto done +// } +// +// dbGameFree := gps.DbGameFree +// if dbGameFree == nil { +// ret = gamehall.OpResultCode_OPRC_RoomHadClosed +// goto done +// } +// +// if len(params) != 0 && (p.GMLevel > 0 || dbGameFree.GetCreateRoomNum() != 0) { //允许GM|或者可选房间的游戏直接按房间ID进场 +// s := SceneMgrSingleton.GetScene(int(params[0])) +// if s != nil { +// if s.limitPlatform.IdStr == p.Platform || (s.groupId != 0 && s.groupId == gps.GroupId) { +// roomId = params[0] +// } +// } +// } +// +// if dbGameFree.GetLimitCoin() != 0 && int64(dbGameFree.GetLimitCoin()) > p.Coin { +// ret = gamehall.OpResultCode_OPRC_CoinNotEnough +// goto done +// } +// +// if dbGameFree.GetMaxCoinLimit() != 0 && int64(dbGameFree.GetMaxCoinLimit()) < p.Coin && !p.IsRob { +// ret = gamehall.OpResultCode_OPRC_CoinTooMore +// goto done +// } +// +// //检查游戏次数限制 +// if !p.IsRob { +// todayData, _ := p.GetDaliyGameData(int(dbGameFree.GetId())) +// if dbGameFree.GetPlayNumLimit() != 0 && +// todayData != nil && +// todayData.GameTimes >= int64(dbGameFree.GetPlayNumLimit()) { +// ret = gamehall.OpResultCode_OPRC_RoomGameTimes +// goto done +// } +// } +// excludeSceneIds := p.lastSceneId[msg.GetId()] +// //临时修改 +// var NewSceneIds = make([]int32, len(excludeSceneIds)) +// copy(NewSceneIds, excludeSceneIds) +// if len(NewSceneIds) > 1 { +// NewSceneIds = NewSceneIds[len(NewSceneIds)-1:] +// } +// //-------------------------- +// ret = CoinSceneMgrSingleton.PlayerEnter(p, msg.GetId(), roomId, NewSceneIds, false) +// if p.scene != nil { +// pack.OpParams = append(pack.OpParams, int32(p.scene.sceneId)) +// //TODO 有房间还进入失败,尝试returnroom +// if ret != gamehall.OpResultCode_OPRC_Sucess { +// p.ReturnScene(false) +// } +// } +// case common.CoinSceneOp_Leave: +// ret = CoinSceneMgrSingleton.PlayerTryLeave(p, msg.GetId(), false) +// if gamehall.OpResultCode_OPRC_OpYield == ret { +// return nil +// } +// case common.CoinSceneOp_Change: +// if p.scene == nil { +// ret = gamehall.OpResultCode_OPRC_RoomHadClosed +// goto done +// } +// var exclude = int32(p.scene.sceneId) +// params := msg.GetOpParams() +// if len(params) != 0 { +// exclude = params[0] +// } +// if p.scene.IsPrivateScene() { +// //if ClubSceneMgrSington.PlayerInChanging(p) { +// // return nil +// //} +// //ret = ClubSceneMgrSington.PlayerTryChange(p, msg.GetId(), []int32{exclude}, false) +// } else { +// if CoinSceneMgrSingleton.PlayerInChanging(p) { //换桌中 +// return nil +// } +// excludeSceneIds := p.lastSceneId[msg.GetId()] +// if exclude != 0 { +// excludeSceneIds = append(excludeSceneIds, exclude) +// } +// ret = CoinSceneMgrSingleton.PlayerTryChange(p, msg.GetId(), excludeSceneIds, false) +// } +// +// case common.CoinSceneOp_AudienceEnter: +// var roomId int32 +// params := msg.GetOpParams() +// if len(params) != 0 && !p.IsRob { +// roomId = params[0] +// } +// ret = CoinSceneMgrSingleton.AudienceEnter(p, msg.GetId(), roomId, nil, false) +// case common.CoinSceneOp_AudienceLeave: +// ret = CoinSceneMgrSingleton.PlayerTryLeave(p, msg.GetId(), true) +// if gamehall.OpResultCode_OPRC_OpYield == ret { +// return nil +// } +// case common.CoinSceneOp_AudienceChange: +// var exclude int32 +// if p.scene != nil { +// exclude = int32(p.scene.sceneId) +// } +// params := msg.GetOpParams() +// if len(params) != 0 { +// exclude = params[0] +// } +// if CoinSceneMgrSingleton.PlayerInChanging(p) { //换桌中 +// return nil +// } +// excludeSceneIds := p.lastSceneId[msg.GetId()] +// if exclude != 0 { +// excludeSceneIds = append(excludeSceneIds, exclude) +// } +// ret = CoinSceneMgrSingleton.PlayerTryChange(p, msg.GetId(), excludeSceneIds, true) +// case common.CoinSceneOP_Server: +// if p.scene == nil { +// ret = gamehall.OpResultCode_OPRC_RoomHadClosed +// goto done +// } +// gameFreeId := p.scene.dbGameFree.GetId() +// gameConfig := PlatformMgrSingleton.GetGameFree(p.Platform, gameFreeId) +// if gameConfig != nil && gameConfig.DbGameFree.GetMatchMode() == 1 { +// return nil +// } +// var exclude = int32(p.scene.sceneId) +// params := msg.GetOpParams() +// if len(params) != 0 { +// exclude = params[0] +// } +// if p.scene.IsPrivateScene() { +// //if ClubSceneMgrSington.PlayerInChanging(p) { +// // return nil +// //} +// //ret = ClubSceneMgrSington.PlayerTryChange(p, msg.GetId(), []int32{exclude}, false) +// } else { +// if CoinSceneMgrSingleton.PlayerInChanging(p) { //换桌中 +// return nil +// } +// excludeSceneIds := p.lastSceneId[msg.GetId()] +// if exclude != 0 { +// excludeSceneIds = append(excludeSceneIds, exclude) +// } +// ret = CoinSceneMgrSingleton.PlayerTryChange(p, msg.GetId(), excludeSceneIds, false) +// } +// } +// done: +// //机器人要避免身上的平台标记被污染 +// if p.IsRob { +// if !(ret == gamehall.OpResultCode_OPRC_Sucess || +// ret == gamehall.OpResultCode_OPRC_CoinSceneEnterQueueSucc) { +// p.Platform = oldPlatform +// } +// } +// pack.OpCode = ret +// proto.SetDefaults(pack) +// p.SendToClient(int(gamehall.CoinSceneGamePacketID_PACKET_SC_COINSCENE_OP), pack) +// if msg.GetOpType() == common.CoinSceneOp_Enter && ret == gamehall.OpResultCode_OPRC_Sucess && p.scene != nil { +// gameName := p.scene.dbGameFree.GetName() + p.scene.dbGameFree.GetTitle() +// ActMonitorMgrSington.SendActMonitorEvent(ActState_Game, p.SnId, p.Name, p.Platform, +// 0, 0, gameName, 0) +// } +// } +// } +// return nil +//} + +//type CSCoinSceneListRoomPacketFactory struct { +//} +//type CSCoinSceneListRoomHandler struct { +//} +// +//func (this *CSCoinSceneListRoomPacketFactory) CreatePacket() interface{} { +// pack := &gamehall.CSCoinSceneListRoom{} +// return pack +//} +// +//func (this *CSCoinSceneListRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSCoinSceneListRoomHandler Process recv ", data) +// if msg, ok := data.(*gamehall.CSCoinSceneListRoom); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// if !CoinSceneMgrSingleton.ListRooms(p, msg.GetId()) { // 场次id +// pack := &gamehall.SCCoinSceneListRoom{ +// Id: msg.Id, +// } +// proto.SetDefaults(pack) +// p.SendToClient(int(gamehall.CoinSceneGamePacketID_PACKET_SC_COINSCENE_LISTROOM), pack) +// } +// } +// } +// +// return nil +//} + +func init() { + // 获取金币场房间人数 + common.RegisterHandler(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_GETPLAYERNUM), &CSCoinSceneGetPlayerNumHandler{}) + netlib.RegisterFactory(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_GETPLAYERNUM), &CSCoinSceneGetPlayerNumPacketFactory{}) + + // 金币场操作;进入场次,离开场次等 + //common.RegisterHandler(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), &CSCoinSceneOpHandler{}) + //netlib.RegisterFactory(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), &CSCoinSceneOpPacketFactory{}) + + // 获取金币场房间列表 + //common.RegisterHandler(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_LISTROOM), &CSCoinSceneListRoomHandler{}) + //netlib.RegisterFactory(int(gamehall.CoinSceneGamePacketID_PACKET_CS_COINSCENE_LISTROOM), &CSCoinSceneListRoomPacketFactory{}) +} diff --git a/worldsrv/action_datasrv.go b/worldsrv/action_datasrv.go new file mode 100644 index 0000000..d40cfc2 --- /dev/null +++ b/worldsrv/action_datasrv.go @@ -0,0 +1,295 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/srvlib" + "strconv" + "time" +) + +type DWThirdRebateMessagePacketFactory struct { +} +type DWThirdRebateMessageHandler struct { +} + +func (this *DWThirdRebateMessagePacketFactory) CreatePacket() interface{} { + pack := &server.DWThirdRebateMessage{} + return pack +} +func (this *DWThirdRebateMessageHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("DWThirdRebateMessageHandler Process recv ", data) + if msg, ok := data.(*server.DWThirdRebateMessage); ok { + //TODO + SendAckToDataSrv(msg.GetTag(), 2) + if msg.GetAvailableBet() <= 0 { + logger.Logger.Warn("DWThirdRebateMessageHandler is error: AvailableBet= ", msg.GetAvailableBet()) + return nil + } + + //p := PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) + //if p != nil { + // p.dirty = true + // //actRandCoinMgr.OnPlayerLiuShui(p, msg.GetAvailableBet()) + //} + // + //thirdId := strconv.Itoa(int(ThirdPltGameMappingConfig.FindThirdIdByThird(msg.GetThird()))) + //rebateTask := RebateInfoMgrSington.rebateTask[strconv.Itoa(int(msg.GetPlt()))] + //if rebateTask != nil { + // Third := rebateTask.RebateGameThirdCfg[thirdId] + // if Third != nil { + // p := PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) + // if p == nil { + // logger.Logger.Trace("DWThirdRebateMessageHandler p == nil ", msg.GetSnid()) + // OfflinePlayerMgrSington.GetOfflinePlayer(msg.GetSnid(), func(op *OfflinePlayer, asyn bool) { + // if op == nil { + // return + // } + // if op.IsRob { + // return + // } + // + // if data, ok := op.RebateData[thirdId]; ok { + // data.ValidBetTotal += msg.GetAvailableBet() + // } else { + // op.RebateData[thirdId] = &model.RebateData{ + // ValidBetTotal: msg.GetAvailableBet(), + // } + // } + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // //注意流控,防止该任务过渡占用登陆队列,可以在datasrv上配置心跳和maxdone来控制 + // return model.SavePlayerRebate(op.PlayerData, thirdId) + // }), task.CompleteNotifyWrapper(func(data interface{}, tt *task.Task) { + // if data != nil { + // logger.Logger.Errorf("SavePlayerRebate error:%v snid:%v platform:%v AvailableBet:%v", data, msg.GetSnid(), msg.GetThird(), msg.GetAvailableBet()) + // } else { + // p = PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) //说明更新任务排在了玩家登陆的后面(造成了脏读,重新应用下该次下注) + // if p != nil { + // if data, ok := p.RebateData[thirdId]; ok { + // data.ValidBetTotal += msg.GetAvailableBet() + // } else { + // p.RebateData[thirdId] = &model.RebateData{ + // ValidBetTotal: msg.GetAvailableBet(), + // } + // } + // p.dirty = true + // } + // } + // }), "SavePlayerRebate").StartByExecutor(op.AccountId) //保证和玩家存取在一条线程内(避免脏读或者脏写) + // }, false) + // return nil + // } + // if p.IsRob { + // logger.Logger.Trace("DWThirdRebateMessageHandler p is rob ", msg.GetSnid()) + // return nil + // } + // if data, ok := p.RebateData[thirdId]; ok { + // data.ValidBetTotal += msg.GetAvailableBet() + // } else { + // p.RebateData[thirdId] = &model.RebateData{ + // ValidBetTotal: msg.GetAvailableBet(), + // } + // } + // + // p.dirty = true + // //p.CountRebate(thirdId, 1) + // } else { + // logger.Logger.Trace("DWThirdRebateMessageHandler Third is nil. ", msg.GetPlt(), msg.GetThird()) + // } + //} else { + // logger.Logger.Trace("DWThirdRebateMessageHandler rebateTask is nil. ", msg.GetPlt(), msg.GetThird()) + //} + } + return nil +} + +type DWThirdRoundMessagePacketFactory struct { +} +type DWThirdRoundMessageHandler struct { +} + +func (this *DWThirdRoundMessagePacketFactory) CreatePacket() interface{} { + pack := &server.DWThirdRoundMessage{} + return pack +} +func (this *DWThirdRoundMessageHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("DWThirdRoundMessageHandler Process recv ", data) + + if msg, ok := data.(*server.DWThirdRoundMessage); ok { + //todo + //获取到对应的gamefreeid,三方的特殊处理了,只寻找对应大类的第一个gamefreeid,因为这个游戏都在不停的变,很多都不一致 + if thirdID := ThirdPltGameMappingConfig.FindThirdIdByThird(msg.GetThird()); thirdID != 0 { + var dbGamefreeInfo *server.DB_GameFree + platform := msg.GetPlatform() + if platform != 0 { + pltGameInfo := PlatformMgrSingleton.GetGameFree(strconv.Itoa(int(platform)), thirdID) + if pltGameInfo != nil { + dbGamefreeInfo = pltGameInfo.DbGameFree + } + } + + player := PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) + if player != nil { + str := strconv.Itoa(int(thirdID)) + //处理三方全民流水问题 + totalOut := int32(0) + totalIn := int32(0) + if dbGamefreeInfo != nil { + isBind := int32(0) + if player.Tel != "" { + isBind = 1 + } + + if msg.GetProfitCoinInTime() < 0 { + totalIn = msg.GetBetCoinInTime() + } else { + totalOut = msg.GetBetCoinInTime() + } + isQuMin := false + //pt := PlatformMgrSingleton.GetPackageTag(player.PackageID) + //if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + //} + if isQuMin || !model.GameParamData.QMOptimization { + QMFlowMgr.AddPlayerStatement(player.SnId, isBind, totalOut, totalIn, thirdID, + player.Platform, player.PackageID, dbGamefreeInfo) + } + + availableBet := int64(totalOut + totalIn) + availableBet = availableBet * int64(dbGamefreeInfo.GetBetWaterRate()) / 100 + if availableBet > 0 { + player.TotalConvertibleFlow += availableBet + player.TotalFlow += availableBet + player.dirty = true + //今日流水增加 + player.TodayGameData.TodayConvertibleFlow += availableBet + } + } + if gd, ok := player.GDatas[str]; ok { + gd.Statics.GameTimes += int64(msg.GetAccRoundsInTime()) + gd.Statics.TotalOut += int64(totalOut) + gd.Statics.TotalIn += int64(totalIn) + if gd.Statics.MaxSysOut < int64(msg.GetOneroundMaxwin()) { + gd.Statics.MaxSysOut = int64(msg.GetOneroundMaxwin()) + } + } else { + gs := model.NewPlayerGameStatics() + gs.GameTimes = int64(msg.GetAccRoundsInTime()) + gs.MaxSysOut = int64(msg.GetOneroundMaxwin()) + gs.TotalOut = int64(totalOut) + gs.TotalIn = int64(totalIn) + player.GDatas[str] = &model.PlayerGameInfo{ + FirstTime: time.Now(), + Statics: *gs, + } + } + + if player.TotalGameData == nil { + player.TotalGameData = make(map[int][]*model.PlayerGameTotal) + } + showId := 9 + if len(player.TotalGameData[showId]) == 0 { + player.TotalGameData[showId] = []*model.PlayerGameTotal{new(model.PlayerGameTotal)} + } + cnt := len(player.TotalGameData[showId]) + if cnt > 0 { + td := player.TotalGameData[showId][cnt-1] + if td == nil { + td = &model.PlayerGameTotal{} + player.TotalGameData[showId][cnt-1] = td + } + if td != nil { + td.ProfitCoin += int64(msg.GetProfitCoinInTime()) + td.BetCoin += int64(msg.GetBetCoinInTime()) + td.FlowCoin += int64(msg.GetFlowCoinInTime()) + } + } + + //洗码 + //三方游戏,通过进出场的营收差洗码 + washingCoin := msg.GetProfitCoinInTime() + if washingCoin < 0 { + washingCoin = -washingCoin + } + washedCoin := player.WashingCoin(int64(washingCoin)) + if washedCoin > 0 { + logger.Logger.Tracef("三方游戏洗码:snid=%v,washingCoin=%v,gamefreeid=%v", player.SnId, washedCoin, thirdID) + } + //五福红包游戏局数检测 + //actRandCoinMgr.OnPlayerGameTimes(player, int64(msg.GetAccRoundsInTime())) + } else { + if dbGamefreeInfo != nil { + totalOut := int32(0) + totalIn := int32(0) + if msg.GetProfitCoinInTime() < 0 { + totalIn = msg.GetBetCoinInTime() + } else { + totalOut = msg.GetBetCoinInTime() + } + QMFlowMgr.AddOffPlayerStatement(msg.GetSnid(), totalOut, totalIn, thirdID, dbGamefreeInfo) + availableBet := int64(totalOut + totalIn) + availableBet = availableBet * int64(dbGamefreeInfo.GetBetWaterRate()) / 100 + if availableBet > 0 { + PlayerCacheMgrSingleton.Get(strconv.Itoa(int(msg.GetPlatform())), msg.GetSnid(), func(op *PlayerCacheItem, asyn, isnew bool) { + if op == nil { + return + } + if op.IsRob { + return + } + //总流水累加 + op.TotalConvertibleFlow += availableBet + op.TotalFlow += availableBet + //今日流水增加,todo 这个地方没有考虑彩票导致的跨天,或者注单延迟导致的今日流水问题 + op.TodayGameData.TodayConvertibleFlow += availableBet + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + //注意流控,防止该任务过渡占用登陆队列,可以在datasrv上配置心跳和maxdone来控制 + return model.UpdatePlayerExchageFlow(op.Platform, op.SnId, op.TotalConvertibleFlow, op.TotalFlow) + }), nil).StartByExecutor(strconv.Itoa(int(op.SnId))) + }, false) + } + } + } + } + } + + return nil +} +func init() { + netlib.RegisterHandler(int(server.SSPacketID_PACKET_DW_ThirdRebateMessage), &DWThirdRebateMessageHandler{}) + netlib.RegisterFactory(int(server.SSPacketID_PACKET_DW_ThirdRebateMessage), &DWThirdRebateMessagePacketFactory{}) + netlib.RegisterHandler(int(server.SSPacketID_PACKET_DW_ThirdRoundMessage), &DWThirdRoundMessageHandler{}) + netlib.RegisterFactory(int(server.SSPacketID_PACKET_DW_ThirdRoundMessage), &DWThirdRoundMessagePacketFactory{}) +} + +// 暂时约定为:result=1正常,result=-1错误 +func SendAckToDataSrv(snid uint64, result int32) bool { + datasrvSess := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), common.DataServerType, common.DataServerId) + if datasrvSess != nil { + pack := &server.WDACKThirdRebateMessage{ + Tag: proto.Uint64(snid), + Result: proto.Int32(result), + } + proto.SetDefaults(pack) + datasrvSess.Send(int(server.SSPacketID_PACKET_WD_ACKThirdRebateMessage), pack) + return true + } else { + logger.Logger.Error("datasrv server not found.") + } + return false +} + +//func init() { +// go func() { +// for { +// time.Sleep(time.Second) +// SendAckToDataSrv(205656,1) +// } +// }() +//} diff --git a/worldsrv/action_friend.go b/worldsrv/action_friend.go new file mode 100644 index 0000000..6cfde42 --- /dev/null +++ b/worldsrv/action_friend.go @@ -0,0 +1,646 @@ +package main + +import ( + "strconv" + "strings" + "time" + "unicode/utf8" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/friend" + "mongo.games.com/game/srvdata" +) + +type CSFriendListPacketFactory struct { +} + +type CSFriendListHandler struct { +} + +func (this *CSFriendListPacketFactory) CreatePacket() interface{} { + pack := &friend.CSFriendList{} + return pack +} + +func (this *CSFriendListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFriendListHandler Process recv ", data) + if msg, ok := data.(*friend.CSFriendList); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSFriendListHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + pack := &friend.SCFriendList{ + ListType: proto.Int32(msg.GetListType()), + OpRetCode: friend.OpResultCode_OPRC_Sucess, + } + switch msg.GetListType() { + case ListType_Friend: + dfl := FriendMgrSington.GetFriendList(p.Platform, p.GetSnId()) + if dfl != nil { + for _, bf := range dfl { + if bf.SnId == p.SnId { + continue + } + fi := &friend.FriendInfo{ + SnId: proto.Int32(bf.SnId), + Name: proto.String(bf.Name), + Sex: proto.Int32(bf.Sex), + Head: proto.Int32(bf.Head), + HeadUrl: proto.String(bf.HeadUrl), + CreateTs: proto.Int64(bf.CreateTime), + LogoutTs: proto.Int64(bf.LogoutTime), + LastChatTs: proto.Int64(bf.CreateTime), + IsShield: proto.Bool(FriendMgrSington.IsShield(p.Platform, p.SnId, bf.SnId)), + RoleId: bf.RoleId, + } + fi.Online = false + bfp := PlayerMgrSington.GetPlayerBySnId(bf.SnId) + if bfp != nil { + fi.Online = bfp.IsOnLine() + } + chat := ChatMgrSington.GetChat(p.Platform, p.SnId, bf.SnId) + if chat != nil { + fi.LastChatTs = chat.LastChatTs + } + pack.FriendArr = append(pack.FriendArr, fi) + } + } + proto.SetDefaults(pack) + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendList), pack) + logger.Logger.Trace("SCFriendListHandler:", pack) + case ListType_Apply: + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryFriendApplyBySnid(p.Platform, p.SnId) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if ret, ok := data.(*model.FriendApply); ok && ret != nil && ret.ApplySnids != nil { + for _, as := range ret.ApplySnids { + if as.SnId == p.SnId { + continue + } + fi := &friend.FriendInfo{ + SnId: proto.Int32(as.SnId), + Name: proto.String(as.Name), + Head: proto.Int32(as.Head), + HeadUrl: proto.String(as.HeadUrl), + CreateTs: proto.Int64(as.CreateTs), + RoleId: as.RoleId, + } + fi.Online = false + bfp := PlayerMgrSington.GetPlayerBySnId(as.SnId) + if bfp != nil { + fi.Online = bfp.IsOnLine() + } + pack.FriendArr = append(pack.FriendArr, fi) + } + } + proto.SetDefaults(pack) + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendList), pack) + logger.Logger.Trace("SCFriendListHandler: ", pack) + })).Start() + case ListType_Recommend: + friends := PlayerMgrSington.RecommendFriendRule(p.Platform, p.SnId) + for _, f := range friends { + if f.Snid == p.SnId { + continue + } + fi := &friend.FriendInfo{ + SnId: proto.Int32(f.Snid), + Name: proto.String(f.Name), + Head: proto.Int32(f.Head), + HeadUrl: proto.String(f.HeadUrl), + RoleId: f.RoleId, + } + pack.FriendArr = append(pack.FriendArr, fi) + } + proto.SetDefaults(pack) + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendList), pack) + logger.Logger.Trace("SCFriendListHandler: ", pack) + } + } + return nil +} + +type CSFriendOpPacketFactory struct { +} + +type CSFriendOpHandler struct { +} + +func (this *CSFriendOpPacketFactory) CreatePacket() interface{} { + pack := &friend.CSFriendOp{} + return pack +} + +func (this *CSFriendOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFriendOpHandler Process recv ", data) + if msg, ok := data.(*friend.CSFriendOp); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSFriendOpHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + destP := PlayerMgrSington.GetPlayerBySnId(msg.SnId) + if destP != nil { + roleId := common.DefaultRoleId + if destP.Roles != nil { + roleId = int(destP.Roles.ModId) + } + FriendMgrSington.FriendOp(msg.OpCode, p, &model.BindFriend{ + SnId: destP.SnId, + Platform: destP.Platform, + Name: destP.Name, + Head: destP.Head, + HeadUrl: destP.HeadUrl, + Sex: destP.Sex, + LogoutTime: destP.LastLogoutTime.Unix(), + CreateTime: time.Now().Unix(), + RoleId: int32(roleId), + }) + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.GetPlayerBaseInfo(p.Platform, msg.SnId) + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data == nil { + pack := &friend.SCFriendOp{ + OpCode: proto.Int32(msg.OpCode), + SnId: proto.Int32(msg.SnId), + OpRetCode: friend.OpResultCode_OPRC_Error, + } + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + return + } + if destPlayer, ok := data.(*model.PlayerBaseInfo); ok && destPlayer != nil { + roleId := common.DefaultRoleId + if destPlayer.Roles != nil { + roleId = int(destPlayer.Roles.ModId) + } + FriendMgrSington.FriendOp(msg.OpCode, p, &model.BindFriend{ + SnId: destPlayer.SnId, + Platform: destPlayer.Platform, + Name: destPlayer.Name, + Head: destPlayer.Head, + HeadUrl: destPlayer.HeadUrl, + Sex: destPlayer.Sex, + LogoutTime: destPlayer.LastLogoutTime.Unix(), + CreateTime: time.Now().Unix(), + RoleId: int32(roleId), + }) + } + })).Start() + } + } + return nil +} + +type CSQueryPlayerGameLogPacketFactory struct { +} +type CSQueryPlayerGameLogHandler struct { +} + +func (this *CSQueryPlayerGameLogPacketFactory) CreatePacket() interface{} { + pack := &friend.CSQueryPlayerGameLog{} + return pack +} + +func (this *CSQueryPlayerGameLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSQueryPlayerGameLogHandler Process recv ", data) + if msg, ok := data.(*friend.CSQueryPlayerGameLog); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSQueryPlayerGameLogHandler p == nil") + return nil + } + snid := msg.GetSnid() + gameId := msg.GetGameId() + size := msg.GetSize() + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret := model.GetFriendRecordLogBySnid(p.Platform, snid, gameId, int(size)) + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + pack := &friend.SCQueryPlayerGameLog{ + Snid: snid, + GameId: gameId, + Size: size, + } + if ret, ok := data.([]*model.FriendRecord); ok && ret != nil { + for _, gpl := range ret { + gl := &friend.PlayerGameLog{ + GameId: proto.Int32(gpl.GameId), + BaseScore: proto.Int32(gpl.BaseScore), + IsWin: proto.Int32(gpl.IsWin), + Ts: proto.Int64(gpl.Ts), + BillCoin: proto.Int64(gpl.BillCoin), + MatchType: proto.Int32(gpl.MatchType), + } + pack.GameLogs = append(pack.GameLogs, gl) + } + } + proto.SetDefaults(pack) + p.SendToClient(int(friend.FriendPacketID_PACKET_SCQueryPlayerGameLog), pack) + logger.Logger.Trace("SCQueryPlayerGameLogHandler: ", pack) + })).Start() + } + return nil +} + +type CSInviteFriendPacketFactory struct { +} + +type CSInviteFriendHandler struct { +} + +func (this *CSInviteFriendPacketFactory) CreatePacket() interface{} { + pack := &friend.CSInviteFriend{} + return pack +} + +func (this *CSInviteFriendHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSInviteFriendHandler Process recv ", data) + if msg, ok := data.(*friend.CSInviteFriend); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSInviteFriendHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSInviteFriendHandler platform == nil") + return nil + } + friendSnid := msg.GetToSnId() + var opRetCode = friend.OpResultCode_OPRC_Sucess + send := func(player *Player) { + roleId := common.DefaultRoleId + if p.Roles != nil { + roleId = int(p.Roles.ModId) + } + pack := &friend.SCInviteFriend{ + SrcSnId: proto.Int32(p.SnId), + SrcName: proto.String(p.Name), + SrcHead: proto.Int32(p.Head), + SrcHeadUrl: proto.String(p.HeadUrl), + OpRetCode: opRetCode, + GameId: proto.Int(p.scene.gameId), + RoomId: proto.Int(p.scene.sceneId), + Pos: proto.Int32(msg.GetPos()), + RoleId: int32(roleId), + } + proto.SetDefaults(pack) + player.SendToClient(int(friend.FriendPacketID_PACKET_SCInviteFriend), pack) + logger.Logger.Trace("SCInviteFriendHandler: ", pack) + } + //不能邀请自己 + if p.SnId == friendSnid { + logger.Logger.Warn("CSInviteFriendHandler invite self") + opRetCode = friend.OpResultCode_OPRC_Friend_NotOpMyself + send(p) + return nil + } + //不是好友 + if !FriendMgrSington.IsFriend(p.Platform, p.SnId, friendSnid) { + logger.Logger.Warn("CSInviteFriendHandler not friend") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_NotFriend + send(p) + return nil + } + fp := PlayerMgrSington.GetPlayerBySnId(friendSnid) + //不在线 + if fp == nil || !fp.IsOnLine() { + logger.Logger.Warn("CSInviteFriendHandler not online") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_NoOnline + send(p) + return nil + } + //CD + if !FriendMgrSington.CanInvite(p.Platform, p.SnId, friendSnid) { + logger.Logger.Warn("CSInviteFriendHandler in cd time") + opRetCode = friend.OpResultCode_OPRC_Error + send(p) + return nil + } + //scene + if p.scene == nil { + logger.Logger.Warn("CSInviteFriendHandler scene is nil") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_SceneNotExist + send(p) + return nil + } + //私有房间 + if p.scene.sceneMode != common.SceneMode_Private { + logger.Logger.Warn("CSInviteFriendHandler scene is common.SceneMode_Private") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_RoomLimit + send(p) + return nil + } + //好友已在游戏中 + if fp.scene != nil { + logger.Logger.Warn("CSInviteFriendHandler scene is common.SceneMode_Private") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_Gaming + send(p) + return nil + } + pos := int(msg.GetPos()) + if pos < 0 || pos >= p.scene.playerNum { + logger.Logger.Trace("CSInviteFriendHandler pos is fail") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_PosIsError //座位不存在 + send(p) + return nil + } + if opRetCode == friend.OpResultCode_OPRC_Sucess { //成功都通知下 + send(p) + send(fp) + } + } + return nil +} + +type CSInviteFriendOpPacketFactory struct { +} + +type CSInviteFriendOpHandler struct { +} + +func (this *CSInviteFriendOpPacketFactory) CreatePacket() interface{} { + pack := &friend.CSInviteFriendOp{} + return pack +} + +func (this *CSInviteFriendOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSInviteFriendOpHandler Process recv ", data) + if msg, ok := data.(*friend.CSInviteFriendOp); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSInviteFriendOpHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + logger.Logger.Warn("CSInviteFriendOpHandler platform == nil") + return nil + } + srcSnid := msg.GetSnId() + opcode := msg.GetOpCode() + var opRetCode = friend.OpResultCode_OPRC_Sucess + send := func(player *Player) { + pack := &friend.SCInviteFriendOp{ + SnId: proto.Int32(p.SnId), + Name: proto.String(p.Name), + OpCode: proto.Int32(opcode), + OpRetCode: opRetCode, + Pos: proto.Int32(msg.GetPos()), + } + proto.SetDefaults(pack) + isok := player.SendToClient(int(friend.FriendPacketID_PACKET_SCInviteFriendOp), pack) + logger.Logger.Trace("SCInviteFriendOpHandler isok: ", isok, " pack: ", pack) + } + fp := PlayerMgrSington.GetPlayerBySnId(srcSnid) + //不能操作自己 + if p.SnId == srcSnid { + logger.Logger.Warn("CSInviteFriendHandler invite self") + opRetCode = friend.OpResultCode_OPRC_Friend_NotOpMyself + send(p) + return nil + } + //不在线 + if fp == nil || !fp.IsOnLine() { + logger.Logger.Warn("CSInviteFriendHandler not online") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_NoOnline + send(p) + return nil + } + //不是好友 + if !FriendMgrSington.IsFriend(p.Platform, p.SnId, srcSnid) { + logger.Logger.Warn("CSInviteFriendHandler not friend") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_NotFriend + send(p) + return nil + } + + switch int(opcode) { + case Invite_Agree: + logger.Logger.Trace("同意邀请") + if p.scene != nil { + logger.Logger.Warn("CSInviteFriendHandler scene is not nil") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_HadInRoom //已在房间中 + send(p) + return nil + } + scene := fp.scene + if scene == nil { + logger.Logger.Warn("CSInviteFriendHandler scene is nil") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_SceneNotExist //场景不存在 + send(p) + return nil + } + //私有房间 + if scene.sceneMode != common.SceneMode_Private { + logger.Logger.Warn("CSInviteFriendHandler scene is common.SceneMode_Private") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_RoomLimit //只能进入私有房间 + send(p) + return nil + } + //进入房间 + if scene.limitPlatform != nil { + if scene.limitPlatform.Isolated && p.Platform != scene.limitPlatform.IdStr { + logger.Logger.Warn("CSInviteFriendHandler scene room not find") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_RoomNotExist //房间不存在 + send(p) + return nil + } + } + if scene.deleting { + logger.Logger.Warn("CSInviteFriendHandler scene is deleting") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_SceneDeleting //场景正在删除 + send(p) + return nil + } + + if scene.closed { + logger.Logger.Warn("CSInviteFriendHandler scene is closed") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_SceneClosed //场景已关闭 + send(p) + return nil + } + + dbGameFree := scene.dbGameFree + if dbGameFree != nil { + limitCoin := srvdata.CreateRoomMgrSington.GetLimitCoinByBaseScore(int32(scene.gameId), int32(scene.gameSite), scene.BaseScore) + if p.Coin < limitCoin { + logger.Logger.Warn("CSInviteFriendHandler player limitCoin") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_CoinLimit //金币不足 + send(p) + return nil + } + } + + sp := GetScenePolicy(scene.gameId, scene.gameMode) + if sp == nil { + logger.Logger.Warn("CSInviteFriendHandler game not exist") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_GameNotExist //游戏不存在 + send(p) + return nil + } + if reason := sp.CanEnter(scene, p); reason != 0 { + logger.Logger.Trace("CSInviteFriendHandler CanEnter reason ", reason) + opRetCode = friend.OpResultCode_OPRC_InviteFriend_GameNotCanEnter //游戏已开始 + send(p) + return nil + } + if scene.IsFull() { + logger.Logger.Trace("CSInviteFriendHandler sp is full") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_RoomFull //房间已满员 + send(p) + return nil + } + pos := int(msg.GetPos()) + if pos < 0 || pos >= scene.playerNum { + logger.Logger.Trace("CSInviteFriendHandler pos is fail") + opRetCode = friend.OpResultCode_OPRC_InviteFriend_PosIsError //座位不存在 + send(p) + return nil + } + if !p.EnterScene(scene, true, pos) { + logger.Logger.Trace("CSInviteFriendHandler EnterScene fail") + opRetCode = friend.OpResultCode_OPRC_Error //进入房间失败 + send(p) + return nil + } + case Invite_Refuse: + logger.Logger.Trace("拒绝邀请") + send(fp) //通知邀请者 + } + } + return nil +} + +type CSFuzzyQueryPlayerPacketFactory struct { +} + +type CSFuzzyQueryPlayerHandler struct { +} + +func (this *CSFuzzyQueryPlayerPacketFactory) CreatePacket() interface{} { + pack := &friend.CSFuzzyQueryPlayer{} + return pack +} + +func (this *CSFuzzyQueryPlayerHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFuzzyQueryPlayerHandler Process recv ", data) + if msg, ok := data.(*friend.CSFuzzyQueryPlayer); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSFuzzyQueryPlayerHandler p == nil") + return nil + } + queryContent := msg.GetQueryContent() + if utf8.RuneCountInString(queryContent) < 3 { + return nil + } + pack := &friend.SCFuzzyQueryPlayer{ + QueryContent: proto.String(queryContent), + } + + // 优先搜索在线玩家 + for _, player := range PlayerMgrSington.snidMap { + if player != nil && player.IsOnLine() /*&& !player.IsRobot() && !FriendMgrSington.IsFriend(p.SnId, player.SnId)*/ { //在线 + if !player.IsRob && player.Platform != p.Platform { //不同平台的真人不能匹配 + continue + } + snidStr := strconv.FormatInt(int64(player.SnId), 10) + if strings.Contains(player.Name, queryContent) || strings.Contains(snidStr, queryContent) { + roleId := common.DefaultRoleId + if player.Roles != nil { + roleId = int(player.Roles.ModId) + } + pi := &friend.PlayerInfo{ + SnId: proto.Int32(player.SnId), + Name: proto.String(player.Name), + Sex: proto.Int32(player.Sex), + Head: proto.Int32(player.Head), + HeadUrl: proto.String(player.HeadUrl), + RoleId: int32(roleId), + } + pack.Players = append(pack.Players, pi) + } + } + } + + if len(pack.Players) > 0 { + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFuzzyQueryPlayer), pack) + logger.Logger.Tracef("CSFuzzyQueryPlayerHandler %v", pack) + return nil + } + + var err error + var ls []*model.PlayerBaseInfo + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + snid, _ := strconv.Atoi(queryContent) + ls = model.FindPlayerList(snid, "", queryContent, 0, 0, "", "", 0, 0, p.Platform, "", 10) + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + logger.Logger.Errorf("CSFuzzyQueryPlayerHandler err:%v", err) + } + for _, v := range ls { + roleId := common.DefaultRoleId + if v.Roles != nil { + roleId = int(v.Roles.ModId) + } + pi := &friend.PlayerInfo{ + SnId: proto.Int32(v.SnId), + Name: proto.String(v.Name), + Sex: proto.Int32(v.Sex), + Head: proto.Int32(v.Head), + HeadUrl: proto.String(v.HeadUrl), + RoleId: int32(roleId), + } + pack.Players = append(pack.Players, pi) + } + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFuzzyQueryPlayer), pack) + logger.Logger.Tracef("CSFuzzyQueryPlayerHandler %v", pack) + })).Start() + } + return nil +} + +func init() { + //好友列表 申请列表 推荐列表 + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSFriendList), &CSFriendListHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSFriendList), &CSFriendListPacketFactory{}) + //申请 同意 拒绝 删除 + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSFriendOp), &CSFriendOpHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSFriendOp), &CSFriendOpPacketFactory{}) + //查看别人战绩 + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSQueryPlayerGameLog), &CSQueryPlayerGameLogHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSQueryPlayerGameLog), &CSQueryPlayerGameLogPacketFactory{}) + //邀请好友对战 + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSInviteFriend), &CSInviteFriendHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSInviteFriend), &CSInviteFriendPacketFactory{}) + //同意、拒绝好友邀请 + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSInviteFriendOp), &CSInviteFriendOpHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSInviteFriendOp), &CSInviteFriendOpPacketFactory{}) + //根据id或者昵称查询玩家: + common.RegisterHandler(int(friend.FriendPacketID_PACKET_CSFuzzyQueryPlayer), &CSFuzzyQueryPlayerHandler{}) + netlib.RegisterFactory(int(friend.FriendPacketID_PACKET_CSFuzzyQueryPlayer), &CSFuzzyQueryPlayerPacketFactory{}) +} diff --git a/worldsrv/action_game.go b/worldsrv/action_game.go new file mode 100644 index 0000000..1fe3db3 --- /dev/null +++ b/worldsrv/action_game.go @@ -0,0 +1,2280 @@ +package main + +import ( + "errors" + "fmt" + "math/rand" + "time" + + webapi_proto "mongo.games.com/game/protocol/webapi" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/player" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" +) + +type CSEnterRoomPacketFactory struct { +} +type CSEnterRoomHandler struct { +} + +func (this *CSEnterRoomPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSEnterRoom{} + return pack +} +func (this *CSEnterRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSEnterRoomHandler Process recv ", data) + msg, ok := data.(*gamehall.CSEnterRoom) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + var code = gamehall.OpResultCode_Game_OPRC_Sucess_Game + var sp ScenePolicy + var dbGameFree *server.DB_GameFree + var roomId int + var gameId int + var gameMode int + var cfg *webapi_proto.GameFree + oldPlatform := p.Platform + + // 进入原来的房间 + scene := p.scene + if scene != nil { + code = gamehall.OpResultCode_Game_OPRC_GameStarting_Game + roomId = scene.sceneId + gameId = scene.gameId + gameMode = scene.gameMode + logger.Logger.Tracef("CSEnterRoomHandler had scene(%d)", scene.sceneId) + goto failed + } + + // 房间查询 + scene = SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene == nil { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Trace("CSEnterRoomHandler scene == nil") + goto failed + } + + gameId = scene.gameId + gameMode = scene.gameMode + roomId = scene.sceneId + if p.IsRob { + p.Platform = scene.limitPlatform.IdStr + } + + cfg = PlatformMgrSingleton.GetGameFree(p.Platform, scene.dbGameFree.Id) + if cfg != nil && (cfg.GroupId != scene.groupId || cfg.GroupId == 0) { + if scene.limitPlatform != nil { + if scene.limitPlatform.Isolated && p.Platform != scene.limitPlatform.IdStr { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Tracef("CSEnterRoomHandler ScenePolicy(gameid:%v mode:%v) scene.limitPlatform.Isolated && p.Platform != scene.limitPlatform.Name", scene.gameId, scene.gameMode) + goto failed + } + } + } + if scene.deleting { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Trace("CSEnterRoomHandler scene is deleting") + goto failed + } + if scene.closed { + code = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + logger.Logger.Trace("CSEnterRoomHandler scene is closed") + goto failed + } + + dbGameFree = scene.dbGameFree + if dbGameFree != nil { + if common.IsLocalGame(scene.gameId) { + if !p.IsRob { + limitCoin := srvdata.CreateRoomMgrSington.GetLimitCoinByBaseScore(int32(scene.gameId), int32(scene.gameSite), scene.BaseScore) + if p.Coin < limitCoin { + code = gamehall.OpResultCode_Game_OPRC_CoinNotEnough_Game + logger.Logger.Trace("CSEnterRoomHandler scene is closed") + goto failed + } + } + } + } + + //检测房间状态是否开启 + if !scene.IsMatchScene() && !PlatformMgrSingleton.CheckGameState(scene.limitPlatform.IdStr, dbGameFree.Id) { + code = gamehall.OpResultCode_Game_OPRC_GameHadClosed + logger.Logger.Tracef("CSEnterRoomHandler SnId:%v GameFreeId:%v GameHadClosed", p.SnId, dbGameFree.Id) + goto failed + } + + sp = GetScenePolicy(scene.gameId, scene.gameMode) + if sp == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSEnterRoomHandler ScenePolicy(gameid:%v mode:%v) not registe", scene.gameId, scene.gameMode) + goto failed + } + + if reason := sp.CanEnter(scene, p); reason != 0 { + code = gamehall.OpResultCode_Game(reason) + logger.Logger.Trace("CSEnterRoomHandler sp.CanEnter(scene, p) reason ", reason) + goto failed + } + + if scene.IsFull() { + code = gamehall.OpResultCode_Game_OPRC_RoomIsFull_Game + logger.Logger.Trace("CSEnterRoomHandler ScenePolicy.IsFull = true") + goto failed + } + + if scene.IsMatchScene() && p.IsRob { + grade := int32(1000) + snid := p.SnId + ms := MatchSeasonMgrSington.GetMatchSeason(snid) // 玩家赛季信息 + lv := MatchSeasonRankMgrSington.CreateRobotLv() // + if ms != nil { + lv = ms.Lv + } + roleId := int32(2000001) + if p.Roles != nil { + roleId = p.Roles.ModId + } + var tm *TmMatch + if len(scene.params) > 3 { + sortId := scene.params[0] + tm = TournamentMgr.GetTm(sortId) + if tm != nil && tm.copyRobotGrades != nil && len(tm.copyRobotGrades) > 0 { + randIndex := rand.Intn(len(tm.copyRobotGrades)) + grade = tm.copyRobotGrades[randIndex].grade + snid = tm.copyRobotGrades[randIndex].copySnid + lv = tm.copyRobotGrades[randIndex].copyLv + roleId = tm.copyRobotGrades[randIndex].copyRoleId + tm.copyRobotGrades = append(tm.copyRobotGrades[:randIndex], tm.copyRobotGrades[randIndex+1:]...) + } + } + mc := NewMatchContext(p, tm, grade, snid, lv, roleId, 0) + if mc != nil { + mc.gaming = true + p.matchCtx = mc + } + } + + if !p.EnterScene(scene, true, -1) { + code = gamehall.OpResultCode_Game_OPRC_Error_Game + } + +failed: + if code != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + resp := &gamehall.SCEnterRoom{ + GameId: proto.Int(gameId), + ModeType: proto.Int(gameMode), + RoomId: proto.Int(roomId), + OpRetCode: code, + } + p.Platform = oldPlatform + proto.SetDefaults(resp) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERROOM), resp) + } + return nil +} + +type CSAudienceEnterRoomHandler struct { +} + +func (this *CSAudienceEnterRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSAudienceEnterRoomHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSEnterRoom); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + var code = gamehall.OpResultCode_Game_OPRC_Sucess_Game + var sp ScenePolicy + var dbGameFree *server.DB_GameFree + var cfg *webapi_proto.GameFree + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene == nil { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Trace("CSAudienceEnterRoomHandler scene == nil") + goto failed + } + if scene.IsMatchScene() { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Tracef("CSAudienceEnterRoomHandler scene.IsMatchScene() %v", scene.sceneId) + goto failed + } + if p.scene != nil { + code = gamehall.OpResultCode_Game_OPRC_CannotWatchReasonInOther_Game + logger.Logger.Trace("CSAudienceEnterRoomHandler p.scene != nil") + goto failed + } + cfg = PlatformMgrSingleton.GetGameFree(p.Platform, scene.dbGameFree.Id) + if cfg != nil && (cfg.GroupId != scene.groupId || cfg.GroupId == 0) { + if scene.limitPlatform != nil { + if scene.limitPlatform.Isolated && p.Platform != scene.limitPlatform.IdStr { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Tracef("CSEnterRoomHandler ScenePolicy(gameid:%v mode:%v) scene.limitPlatform.Isolated && p.Platform != scene.limitPlatform.Name", scene.gameId, scene.gameMode) + goto failed + } + } + } + //if !scene.starting { + // code = gamehall.OpResultCode_Game_OPRC_CannotWatchReasonRoomNotStart_Game + // logger.Logger.Trace("CSAudienceEnterRoomHandler !scene.starting") + // goto failed + //} + if scene.deleting { + code = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + logger.Logger.Trace("CSAudienceEnterRoomHandler scene is deleting") + goto failed + } + if scene.closed { + code = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + logger.Logger.Trace("CSAudienceEnterRoomHandler scene is closed") + goto failed + } + //if scene.IsCoinScene() || scene.IsHundredScene() { + // code = gamehall.OpResultCode_Game_OPRC_Error_Game + // logger.Logger.Trace("CSAudienceEnterRoomHandler scene is IsCoinScene IsHundredScene") + // goto failed + //} + + sp = GetScenePolicy(scene.gameId, scene.gameMode) + if sp == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSAudienceEnterRoomHandler ScenePolicy(gameid:%v mode:%v) not registe", scene.gameId, scene.gameMode) + goto failed + } + dbGameFree = scene.dbGameFree + code = gamehall.OpResultCode_Game(CoinSceneMgrSingleton.AudienceEnter(p, dbGameFree.GetId(), msg.GetRoomId(), nil, true)) + + failed: + if code != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + resp := &gamehall.SCEnterRoom{ + OpRetCode: code, + } + proto.SetDefaults(resp) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERROOM), resp) + } + } + return nil +} + +type CSReturnRoomPacketFactory struct { +} +type CSReturnRoomHandler struct { +} + +func (this *CSReturnRoomPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSReturnRoom{} + return pack +} +func (this *CSReturnRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSReturnRoomHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSReturnRoom); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + scene := p.scene + pack := &gamehall.SCReturnRoom{} + if scene == nil { + //miniGameScene := MiniGameMgrSington.GetAllSceneByPlayer(p) + isExist := false + //for _, s := range miniGameScene { + // if s.sceneId == int(msg.GetRoomId()) { + // isExist = true + // } + //} + if isExist { + //如果存在这尝试返回到房间内 + //MiniGameMgrSington.OnPlayerReturnScene(p) + } else { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + //如果不存在则直接返回 + goto done + } + } else { + pack.RoomId = proto.Int(scene.sceneId) + pack.GameId = proto.Int(scene.gameId) + pack.ModeType = proto.Int(scene.gameMode) + pack.Params = scene.params + pack.HallId = proto.Int32(scene.hallId) + gameVers := srvdata.GetGameVers(p.PackageID) + if ver, ok := gameVers[fmt.Sprintf("%v,%v", scene.gameId, p.Channel)]; ok { + pack.MinApkVer = proto.Int32(ver.MinApkVer) + pack.MinResVer = proto.Int32(ver.MinResVer) + pack.LatestApkVer = proto.Int32(ver.LatestApkVer) + pack.LatestResVer = proto.Int32(ver.LatestResVer) + + if msg.GetApkVer() < ver.MinApkVer { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_YourAppVerIsLow_Game + goto done + } + + if msg.GetResVer() < ver.MinResVer { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_YourResVerIsLow_Game + goto done + } + } + scene = p.ReturnScene(msg.GetIsLoaded()) + if scene == nil { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_RoomNotExist_Game + } else { + //成功返回房间的消息在gamesrv上发送,为了确保房间消息的顺序 + return nil + //pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + } + } + done: + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_RETURNROOM), pack) + } + return nil +} + +type CSJoinGamePacketFactory struct { +} +type CSJoinGameHandler struct { +} + +func (this *CSJoinGamePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSJoinGame{} + return pack +} +func (this *CSJoinGameHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSJoinGameHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSJoinGame); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSJoinGameHandler p == nil") + return nil + } + if p.scene == nil { + logger.Logger.Warn("CSJoinGameHandler scene == nil") + return nil + } + + newPlayer := PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if newPlayer != nil { + pack := &gamehall.SCJoinGame{ + MsgType: proto.Int32(msg.GetMsgType()), + OpRetCode: gamehall.OpResultCode_Game(0), + } + if !p.scene.IsTestScene() { + // 入场限额检查 + if p.scene.dbGameFree.GetLimitCoin() != 0 && int64(p.scene.dbGameFree.GetLimitCoin()) > p.Coin { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_MoneyNotEnough_Game + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_JOINGAME), pack) + return nil + } + // 携带金币检查 + if p.scene.dbGameFree.GetMaxCoinLimit() != 0 && int64(p.scene.dbGameFree.GetMaxCoinLimit()) < p.Coin && !p.IsRob { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_CoinTooMore_Game + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_JOINGAME), pack) + return nil + } + } + // 是否还有空座位 + if p.scene.IsFull() { + pack.OpRetCode = gamehall.OpResultCode_Game(1) + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_JOINGAME), pack) + return nil + } + p.scene.AudienceSit(newPlayer, -1) + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_JOINGAME), pack) + } + } + return nil +} + +type CSGetDataLogPacketFactory struct { +} +type CSGetDataLogHandler struct { +} + +func (this *CSGetDataLogPacketFactory) CreatePacket() interface{} { + pack := &player.CSGetDataLog{} + return pack +} + +func (this *CSGetDataLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetDataLogHandler Process recv ", data) + if msg, ok := data.(*player.CSGetDataLog); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetDataLogHandler p == nil") + return nil + } + ts := int64(msg.GetVer()) + if ts == 0 { + ts = time.Now().Unix() + } + type LogData struct { + datas []model.CoinLog + err error + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + datas, err := model.GetCoinLogBySnidAndLessTs(p.Platform, p.SnId, ts) + return &LogData{datas: datas, err: err} + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if d, ok := data.(*LogData); ok { + if d != nil && d.err == nil { + pack := &player.SCGetDataLog{ + DataType: msg.DataType, + } + ver := msg.GetVer() + for i := 0; i < len(d.datas); i++ { + ts := d.datas[i].Time.Unix() + if int32(ts) < ver { + ver = int32(ts) + } + pack.Datas = append(pack.Datas, &player.DataLog{ + LogType: proto.Int32(d.datas[i].LogType), + ChangeCount: proto.Int64(d.datas[i].Count), + RestCount: proto.Int64(d.datas[i].RestCount), + Remark: proto.String(d.datas[i].Remark), + Ts: proto.Int32(int32(ts)), + }) + } + pack.Ver = proto.Int32(ver) + proto.SetDefaults(pack) + p.SendToClient(int(player.PlayerPacketID_PACKET_SC_GETDATALOG), pack) + } + } + }), "GetCoinLogBySnidAndLessTs").StartByFixExecutor("coinlog_r") + } + return nil +} + +// 第三方-->系统 +func _LeaveTransferThird2SystemTask(p *Player) { + + if p.thrscene == 0 { + logger.Logger.Tracef("player snid=%v TransferThird2SystemTask p.thrscene == 0 return", p.SnId) + return + } + + //if p.thrscene.sceneMode != int(common.SceneMode_Thr) { + // logger.Logger.Infof("player snid=%v TransferThird2SystemTask p.scene == thrID return", p.SnId) + // return + //} + + if p.thridBalanceRefreshReqing { + logger.Logger.Tracef("player snid=%v TransferThird2SystemTask p.thridBalanceRefreshReqing == true return", p.SnId) + return + } + gainway := common.GainWay_Transfer_Thrid2System + plt := webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(p.thrscene) + if plt == nil { + logger.Logger.Tracef("player snid=%v TransferThird2SystemTask plt == nil return", p.SnId) + return + } + + oper := plt.GetPlatformBase().Name + "2System" + amount := int64(0) + timeStamp := time.Now().UnixNano() + p.thridBalanceRefreshReqing = true + //timeout := false + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + for i := int32(0); i < model.GameParamData.ThirdPltTransferMaxTry; { + var err error + var coinLog *model.PayCoinLog + var coinlogex *model.CoinLog + //var apiHasTransfer = false + remark := plt.GetPlatformBase().Name + "转出到系统" + //allow := plt.ReqIsAllowTransfer(p.SnId, p.Platform, p.Channel) + //if !allow { + // goto Rollback + //} + err, amount = plt.ReqLeaveGame(p.SnId, fmt.Sprintf("%v", p.thrscene), p.Ip, p.Platform, p.Channel) + if err != nil { + goto Rollback + } + if amount <= 0 { + return nil + } + if plt.GetPlatformBase().TransferInteger { + amount = (amount / 100) * 100 + if amount <= 0 { + return nil + } + } + timeStamp = time.Now().UnixNano() + //如果請求超時的話資金就不能從三方出來,玩家的錢只會少 + //err, timeout = plt.ReqTransfer(p.SnId, -amount, strconv.FormatInt(timeStamp, 10), p.Platform, p.Channel, p.Ip) + //if err != nil || timeout { + // goto Rollback + //} + //apiHasTransfer = true + coinLog = model.NewPayCoinLog(timeStamp, int32(p.SnId), amount, int32(gainway), oper, model.PayCoinLogType_Coin, 0) + timeStamp = coinLog.TimeStamp + err = model.InsertPayCoinLogs(p.Platform, coinLog) + if err != nil { + goto Rollback + } + coinlogex = model.NewCoinLogEx(&model.CoinLogParam{ + Platform: p.Platform, + SnID: p.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: amount, + RemainNum: p.Coin + amount, + Add: 0, + LogType: int32(gainway), + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + goto Rollback + } + return nil + Rollback: + if coinLog != nil { + model.RemovePayCoinLog(p.Platform, coinLog.LogId) + } + if coinlogex != nil { + model.RemoveCoinLogOne(coinlogex.Platform, coinlogex.LogId) + } + //如果發現有任何一個超時,則就不在往下執行,因為不知道數據是否正確 + /* + if timeout { + logger.Logger.Errorf("player snid=%v third->system transfer %v timeout at try %v times,then stop try!", p.SnId, -amount, i+1) + break + } + if apiHasTransfer { + err, timeout = plt.ReqTransfer(p.SnId, amount, strconv.FormatInt(time.Now().UnixNano(), 10), p.Platform, p.Channel, p.Ip) + if err != nil { + logger.Logger.Errorf("player snid=%v third->system transfer rollback fail at try %v times", p.SnId, i+1) + } + } + //如果发现有任何一個超时,則就不在往下执行,因为不知道数据是否在三方已经处理 + if timeout { + logger.Logger.Errorf("player snid=%v third->system rollback transfer %v timeout at try %v times,then stop try!", p.SnId, amount, i+1) + break + } + */ + logger.Logger.Tracef("player snid=%v third->system transfer rollback at try %v times", p.SnId, i+1) + i++ + if i < model.GameParamData.ThirdPltTransferMaxTry { + time.Sleep(time.Duration(model.GameParamData.ThirdPltTransferInterval) * time.Second) + } + } + return errors.New("third->system transfer error >max try times!") + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + statePack := &gamehall.SCThridGameBalanceUpdateState{} + if data != nil { + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = false + p.thridBalanceReqIsSucces = false + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + logger.Logger.Trace("SCThridAccountTransferHandler third->system transfer error:", data) + } else { + p.thridBalanceReqIsSucces = true + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + // p.thrscene = nil + p.Coin += amount + p.SetPayTs(timeStamp) + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = true + //ThirdPlatformMgrSington.AddThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag, thirdBalance) + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + p.dirty = true + + if statePack.OpRetCode == gamehall.OpResultCode_Game_OPRC_Sucess_Game { + //退出成功 重置场景状态 + if p.thrscene != 0 { + p.thrscene = 0 + p.scene = nil + } + } + + p.thridBalanceRefreshReqing = false + p.diffData.Coin = -1 //强制更新金币 + p.SendDiffData() + return + }), "ThridAccountTransfer").Start() +} + +// 进入三方 +type CSEnterThridGamePacketFactory struct { +} +type CSEnterThridGameHandler struct { +} + +func (this *CSEnterThridGamePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSEnterThridGame{} + return pack +} + +func (this *CSEnterThridGameHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSEnterThridGameHandler Process recv ", data) + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSEnterThridGameHandler p == nil") + return nil + } + + if msg, ok := data.(*gamehall.CSEnterThridGame); ok { + // msg.ThridGameId = proto.Int32(9010001) + returnErrorCodeFunc := func(code gamehall.OpResultCode_Game) { + pack := &gamehall.SCEnterThridGame{} + pack.OpRetCode = code + pack.ThridGameId = msg.ThridGameId + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERTHRIDGAME), pack) + logger.Trace(pack) + } + //正在请求刷新余额中不能进入三方 + if p.thridBalanceRefreshReqing { + logger.Logger.Warn("CSEnterThridGameHandler client req thridBalanceRefreshReqing", p.SnId) + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_ThirdPltProcessing_Game) + return nil + } + //msg.ThridGameId=proto.Int32(430010001) + + //找到对应的平台 + thridPltGameItem, plt := ThirdPltGameMappingConfig.FindThirdInfoBySystemGameId(msg.ThridGameId) + if thridPltGameItem == nil || plt == nil { + logger.Logger.Infof("Player %v no platform", p.SnId) + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + return nil + } + + if p.thrscene != 0 && p.thrscene != plt.GetPlatformBase().BaseGameID { + logger.Logger.Infof("Player %v in game.", p.SnId) + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_ThirdPltProcessing_Game) + return nil + } + if p.isDelete { + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + return nil + } + //pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) + //if pt != nil && pt.IsForceBind == 1 { + // if p.BeUnderAgentCode == "" || p.BeUnderAgentCode == "0" { + // returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_MustBindPromoter_Game) + // return nil + // } + //} + + //检测房间状态是否开启 + gps := PlatformMgrSingleton.GetGameFree(p.Platform, msg.GetThridGameId()) + if gps == nil { + logger.Logger.Infof("Player %v no cfg room close", p.SnId) + + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + return nil + } + dbGameFree := gps.DbGameFree + if dbGameFree == nil { + logger.Logger.Infof("Player %v no gamefree", p.SnId) + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + return nil + } + pfConfig := PlatformMgrSingleton.GetPlatform(p.Platform) + if pfConfig == nil || pfConfig.ThirdGameMerchant == nil || pfConfig.ThirdGameMerchant[int32(plt.GetPlatformBase().BaseGameID)] == 0 { + logger.Logger.Infof("Player %v no pfcfg", p.SnId) + + // returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + // return nil + } + + //检查限额,金额不足 + if dbGameFree.GetLimitCoin() != 0 && p.GetCoin() < int64(dbGameFree.GetLimitCoin()) { + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_CoinNotEnough_Game) + return nil + } + + //检查平台配额是否足够 + /*if plt.GetPlatformBase().IsNeedCheckQuota { + dgQuota := ThirdPlatformMgrSington.GetThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag) + if dgQuota <= 0 || dgQuota <= p.GetCoin() { + logger.Logger.Infof("Player snid %v %v platfrom Quota of game not enough.", p.SnId, plt.GetPlatformBase().Name) + returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_Dg_QuotaNotEnough_Game) + return nil + } + }*/ + + //检查场景是否开放或者存在,预设数据 + //scene := SceneMgrSingleton.GetThirdScene(plt) + //if scene != nil { + p.thrscene = plt.GetPlatformBase().BaseGameID + //检查场景是否开放或者存在,预设数据 + scene := SceneMgrSingleton.GetThirdScene(plt) //仅用于占位 + if scene != nil { + p.scene = scene + } + //} else { + // logger.Logger.Infof("Player %v no scene", p.SnId) + + // returnErrorCodeFunc(gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game) + // return nil + //} + + AskEnterThridGame(p, plt, thridPltGameItem, s) + } + return nil +} +func AskEnterThridGame(p *Player, plt webapi.IThirdPlatform, thridPltGameItem *server.DB_ThirdPlatformGameMapping, + s *netlib.Session) { + pack := &gamehall.SCEnterThridGame{} + pack.ThridGameId = thridPltGameItem.SystemGameID + amount := p.GetCoin() + if plt.GetPlatformBase().TransferInteger { + amount = (amount / 100) * 100 + } + p.Coin = p.GetCoin() - amount + gainway := common.GainWay_Transfer_System2Thrid + oper := "System2" + plt.GetPlatformBase().Name + timeStamp := time.Now().UnixNano() + p.thridBalanceRefreshReqing = true + transferTimeOut := false + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var err error + //var ok bool + url := "" + remark := "转入" + plt.GetPlatformBase().Name + thridPltGameItem.GetDesc() + //thridPlatformCoin := int64(0) + var coinLog *model.PayCoinLog + var coinlogex *model.CoinLog + /*err = ensureThridPltUserName(plt, p, amount, s.RemoteAddr()) + if err != nil && err != webapi.ErrNoCreated { + goto Rollback + }*/ + //ok = utils.RunPanicless(func() { + err, url = enterThridPltUserName(plt, p, amount, thridPltGameItem.GetThirdGameID(), s.RemoteAddr()) + //err, url = plt.ReqEnterGame(p.SnId, thridGameId, s.RemoteAddr(), p.Platform, p.Channel, p.Ip) + //}) + if err == webapi.ErrRequestTimeout { // 超时 + transferTimeOut = true + } + //ok = true + if err != nil && !transferTimeOut { + logger.Logger.Errorf("plt.ReqEnterGame() snid:%v error: %v", p.SnId, err) + if thrErr, ok := err.(webapi.ThirdError); ok { + if thrErr.IsClose() { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Thr_GameClose_Game + } else { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + } + } else { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + } + goto Rollback + } + coinLog = model.NewPayCoinLog(timeStamp, int32(p.SnId), -amount, int32(gainway), oper, model.PayCoinLogType_Coin, 0) + timeStamp = coinLog.TimeStamp + err = model.InsertPayCoinLogs(p.Platform, coinLog) + if err != nil { + goto Rollback + } + coinlogex = model.NewCoinLogEx(&model.CoinLogParam{ + Platform: p.Platform, + SnID: p.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: -amount, + RemainNum: p.Coin, + Add: 0, + LogType: int32(gainway), + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + goto Rollback + } + // + //err, transferTimeOut = plt.ReqTransfer(p.SnId, amount, strconv.FormatInt(timeStamp, 10), p.Platform, p.Channel, p.Ip) + if transferTimeOut { + logger.Logger.Errorf("plt.ReqTransfer() snid:%v error:%v", p.SnId, err) + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + goto Rollback + } + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + pack.ScreenOrientationType = proto.Int32(thridPltGameItem.GetScreenOrientationType()) + pack.EnterUrl = proto.String(url) + return nil + Rollback: + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + if coinLog != nil { + model.RemovePayCoinLog(p.Platform, coinLog.LogId) + } + if coinlogex != nil { + if transferTimeOut { + err2 := model.UpdateCoinLogRemark(coinlogex.Platform, coinlogex.LogId, plt.GetPlatformBase().Name+"需人工处理") + if err2 != nil { + logger.Logger.Errorf("thr UpdateCoinLogRemark(%v) error: %v", coinlogex.LogId, err2) + } + } else { + model.RemoveCoinLogOne(coinlogex.Platform, coinlogex.LogId) + } + + } + return errors.New("system->third transfer rollback!") + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if pack.GetOpRetCode() == gamehall.OpResultCode_Game_OPRC_Sucess_Game { + // ThirdPlatformMgrSington.AddThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag, -amount) + p.SetPayTs(timeStamp) + } else { + //如帐变出现问题,就在日志里面查下面的输出信息!!! + //如果转账超时,三方的转账是否成功就是未知的,这时不能将金币再加到玩家身上。 + //如果出现超时问题,就需要人工对账。 + //注意:这个地方说的超时已经包含CG工程Check订单后的超时 + if transferTimeOut { + logger.Logger.Errorf("CSEnterThridGameHandler player snid:%v transfer %v to %v timeout:", p.SnId, amount, plt.GetPlatformBase().Name) + } else { + p.Coin += amount + } + p.thrscene = 0 + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + logger.Logger.Trace("enterThridPltUserName system->third transfer error:", data) + } + p.dirty = true + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = false + + p.SendDiffData() + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERTHRIDGAME), pack) + p.thridBalanceRefreshReqing = false + logger.Logger.Trace("CSEnterThridGameHandler send client:", pack) + return + }), "CSEnterThridGameHandler").Start() +} + +func ensureThridPltUserName(pltform webapi.IThirdPlatform, p *Player, amount int64, ip string) error { + var err error + err = pltform.ReqCreateAccount(p.SnId, p.Platform, p.Channel, p.GetIP()) + if err != nil { + if err != webapi.ErrNoCreated { + logger.Logger.Errorf("Snid=%v Plt=%v ReqCreateAccount error:%v", p.SnId, pltform.GetPlatformBase().Name, err) + } + return err + } + return nil +} + +func enterThridPltUserName(pltform webapi.IThirdPlatform, p *Player, amount int64, gameId, ip string) (err error, url string) { + // (snId int32, gameId string, clientIP string, platform, channel string, amount int64) + err, url = pltform.ReqEnterGame(p.SnId, "", p.GetIP(), p.Platform, p.Channel, amount) + if err != nil { + if err != webapi.ErrNoCreated { + logger.Logger.Errorf("Snid=%v Plt=%v ReqEnterGame error:%v", p.SnId, pltform.GetPlatformBase().Name, err) + } + return err, "" + } + return +} + +// 离开三方 +type CSLeaveThridGamePacketFactory struct { +} +type CSLeaveThridGameHandler struct { +} + +func (this *CSLeaveThridGamePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSLeaveThridGame{} + return pack +} + +func (this *CSLeaveThridGameHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + if _, ok := data.(*gamehall.CSLeaveThridGame); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSLeaveThridGameHandler p == nil") + return nil + } + logger.Logger.Trace("CSLeaveThridGameHandler Process recv ", p.SnId) + _LeaveTransferThird2SystemTask(p) + pack := &gamehall.SCLeaveThridGame{} + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_LEAVETHRIDGAME), pack) + } + return nil +} + +// 刷新 +type CSThridBalanceRefreshPacketFactory struct { +} +type CSThridBalanceRefreshHandler struct { +} + +func (this *CSThridBalanceRefreshPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSThridGameBalanceUpdate{} + return pack +} +func (this *CSThridBalanceRefreshHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSThridBalanceRefreshHandler Process recv ", data) + if _, ok := data.(*gamehall.CSThridGameBalanceUpdate); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSThridBalanceRefreshHandler p == nil") + return nil + } + //if p.scene != nil { + // logger.Logger.Tracef("player snid=%v CSThridBalanceRefreshHandler p.scene != nil return", p.SnId) + // return nil + //} + + //请求太快,不做处理,给API减轻一些压力 + if p.thridBalanceRefreshReqing { + logger.Logger.Warn("CSThridBalanceRefreshHandler client req too fast") + pack := &gamehall.SCThridGameBalanceUpdate{ + OpRetCode: gamehall.OpResultCode_Game_OPRC_Error_Game, + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATE), pack) + return nil + } + // p.thridBalanceRefreshReqing = true + RefreshTransferThird2SystemTask(p) + /* + gainway := common.GainWay_Transfer_Thrid2System + waitgroup := webapi.ThridPlatformMgrSington.AllPlatformCount() + isSucces := true + timeout := false + + webapi.ThridPlatformMgrSington.ThridPlatformMap.Range(func(key, value interface{}) bool { + plt := value.(webapi.IThirdPlatform) + if stateOk, exist := p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name]; exist && stateOk { + waitgroup-- + if 0 == waitgroup { + statePack := &gamehall.SCThridGameBalanceUpdateState{} + pack := &gamehall.SCThridGameBalanceUpdate{} + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + p.thridBalanceReqIsSucces = true + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + pack.Coin = proto.Int64(p.Coin) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATE), pack) + p.thridBalanceRefreshReqing = false + } + return true + } + var tsk task.Task + tsk = task.New(nil, + task.CallableWrapper(func(o *basic.Object) interface{} { + plt := tsk.GetEnv("plt").(webapi.IThirdPlatform) + tsk.PutEnv("timeStamp", time.Now().UnixNano()) + var err error + var coinLog *model.PayCoinLog + var coinlogex *model.CoinLog + var apiHasTransfer = false + remark := "刷新" + plt.GetPlatformBase().Name + "转出到系统" + thirdBalance := int64(0) + //allow := false + if plt == nil { + logger.Logger.Tracef("player snid=%v CSThridBalanceRefreshHandler plt == nil return", p.SnId) + return int64(-1) + } + + pfConfig := PlatformMgrSingleton.GetPlatform(p.Platform) + if pfConfig == nil { + return int64(0) + } + + if pfConfig.ThirdGameMerchant == nil || pfConfig.ThirdGameMerchant[int32(plt.GetPlatformBase().BaseGameID)] == 0 { + return int64(0) + } + + oper := plt.GetPlatformBase().Name + "2System" + err = ensureThridPltUserName(plt, p, thirdBalance, s.RemoteAddr()) + if err != nil { + if err == webapi.ErrNoCreated { + return int64(0) + } + logger.Logger.Tracef("player snid=%v at %v ensureThridPltUserName() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + //allow = plt.ReqIsAllowTransfer(p.SnId, p.Platform, p.Channel) + //if !allow { + // logger.Logger.Tracef("player snid=%v at %v is not allow Transfer", p.SnId, plt.GetPlatformBase().Name) + // goto Rollback + //} + err, thirdBalance = plt.ReqUserBalance(p.SnId, p.Platform, p.Channel, p.Ip) + if err != nil { + logger.Logger.Tracef("player snid=%v at %v plt.ReqUserBalance() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + if thirdBalance <= 0 { + return int64(0) + } + if plt.GetPlatformBase().TransferInteger { + thirdBalance = (thirdBalance / 100) * 100 + if thirdBalance <= 0 { + return int64(0) + } + } + err, timeout = plt.ReqTransfer(p.SnId, -thirdBalance, strconv.FormatInt(time.Now().UnixNano(), 10), p.Platform, p.Channel, p.Ip) + if err != nil || timeout { + logger.Logger.Tracef("player snid=%v at %v plt.ReqTransfer() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + apiHasTransfer = true + coinLog = model.NewPayCoinLog(time.Now().UnixNano(), int32(p.SnId), thirdBalance, int32(gainway), oper, model.PayCoinLogType_Coin, 0) + tsk.PutEnv("timeStamp", coinLog.TimeStamp) + err = model.InsertPayCoinLogs(p.Platform, coinLog) + if err != nil { + logger.Logger.Tracef("player snid=%v at %v model.InsertPayCoinLogs() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + coinlogex = model.NewCoinLogEx(p.SnId, thirdBalance, p.Coin+thirdBalance, p.SafeBoxCoin, 0, int32(gainway), + 0, oper, remark, p.Platform, p.Channel, p.BeUnderAgentCode, 0, p.PackageID, int32(plt.GetPlatformBase().VultGameID)) + err = model.InsertCoinLog(coinlogex) + if err != nil { + logger.Logger.Tracef("player snid=%v at %v model.InsertCoinLogs() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + tsk.PutEnv("plt", plt) + return thirdBalance + Rollback: + if coinLog != nil { + model.RemovePayCoinLog(p.Platform, coinLog.LogId) + } + if coinlogex != nil { + model.RemoveCoinLogOne(coinlogex.Platform, coinlogex.LogId) + } + if timeout { + logger.Logger.Errorf("player snid=%v CSThridBalanceRefreshHandler transfer %v to %v timeout!", p.SnId, -thirdBalance, plt.GetPlatformBase().Name) + return int64(-1) + } + if apiHasTransfer { + err, timeout = plt.ReqTransfer(p.SnId, thirdBalance, strconv.FormatInt(time.Now().UnixNano(), 10), p.Platform, p.Channel, p.Ip) + if timeout { + logger.Logger.Errorf("player snid=%v CSThridBalanceRefreshHandler transfer rollback %v to %v timeout!", p.SnId, thirdBalance, plt.GetPlatformBase().Name) + } + } + return int64(-1) + }), + task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + thirdBalance := data.(int64) + plt := tsk.GetEnv("plt").(webapi.IThirdPlatform) + timeStamp := tsk.GetEnv("timeStamp").(int64) + if thirdBalance < 0 { + isSucces = false + logger.Logger.Tracef("player snid=%v at platform=%v CSThridBalanceRefreshHandler third->system transfer fail", p.SnId, plt.GetPlatformBase().Name) + } else if thirdBalance > 0 { + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = true + p.Coin += thirdBalance + p.SetPayTs(timeStamp) + //ThirdPlatformMgrSington.AddThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag, thirdBalance) + p.dirty = true + logger.Logger.Tracef("player snid=%v at platform=%v CSThridBalanceRefreshHandler third->system transfer succes", p.SnId, plt.GetPlatformBase().Name) + } else { + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = true + } + if atomic.AddInt32(&waitgroup, -1) == 0 { + p.diffData.Coin = -1 + p.SendDiffData() + statePack := &gamehall.SCThridGameBalanceUpdateState{} + pack := &gamehall.SCThridGameBalanceUpdate{} + if isSucces { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + p.thridBalanceReqIsSucces = true + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + } else { + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + p.thridBalanceReqIsSucces = false + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + pack.Coin = proto.Int64(p.Coin) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATE), pack) + p.thridBalanceRefreshReqing = false + logger.Logger.Tracef("SendToClient() player snid=%v at CSThridBalanceRefreshHandler() pack:%v", p.SnId, pack.String()) + } + }), "ThridAccount") + tsk.PutEnv("plt", value) + tsk.Start() + return true + })*/ + } + return nil +} + +// 刷新 第三方-->系统 +func RefreshTransferThird2SystemTask(p *Player) { + + /*if p.thrscene == 0 { + logger.Logger.Tracef("player snid=%v RefreshTransferThird2SystemTask p.thrscene == 0 return", p.SnId) + return + }*/ + + if p.thridBalanceRefreshReqing { + logger.Logger.Tracef("player snid=%v RefreshTransferThird2SystemTask p.thridBalanceRefreshReqing == true return", p.SnId) + return + } + gainway := common.GainWay_Transfer_Thrid2System + + amount := int64(0) + timeStamp := time.Now().UnixNano() + p.thridBalanceRefreshReqing = true + //timeout := false + webapi.ThridPlatformMgrSington.ThridPlatformMap.Range(func(key, value interface{}) bool { + // plt := value.(webapi.IThirdPlatform) + + var tsk task.Task + tsk = task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // plt := webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(p.thrscene) + plt := tsk.GetEnv("plt").(webapi.IThirdPlatform) + if plt == nil { + logger.Logger.Tracef("player snid=%v RefreshTransferThird2SystemTask plt == nil return", p.SnId) + return errors.New("third->system plt is nil") + } + for i := int32(0); i < model.GameParamData.ThirdPltTransferMaxTry; { + var err error + var coinLog *model.PayCoinLog + var coinlogex *model.CoinLog + + oper := plt.GetPlatformBase().Name + "2System" + remark := "刷新" + plt.GetPlatformBase().Name + "转出到系统" + err, amount = plt.ReqUserBalance(p.SnId, p.Platform, p.Channel, p.Ip) + + // err, amount = plt.ReqLeaveGame(p.SnId, fmt.Sprintf("%v", p.thrscene), p.Ip, p.Platform, p.Channel) + if err != nil { + goto Rollback + } + if amount <= 0 { + return nil + } + if plt.GetPlatformBase().TransferInteger { + amount = (amount / 100) * 100 + if amount <= 0 { + return nil + } + } + timeStamp = time.Now().UnixNano() + + coinLog = model.NewPayCoinLog(timeStamp, int32(p.SnId), amount, int32(gainway), oper, model.PayCoinLogType_Coin, 0) + timeStamp = coinLog.TimeStamp + err = model.InsertPayCoinLogs(p.Platform, coinLog) + if err != nil { + goto Rollback + } + coinlogex = model.NewCoinLogEx(&model.CoinLogParam{ + Platform: p.Platform, + SnID: p.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: amount, + RemainNum: p.Coin + amount, + Add: 0, + LogType: int32(gainway), + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + goto Rollback + } + return nil + Rollback: + if coinLog != nil { + model.RemovePayCoinLog(p.Platform, coinLog.LogId) + } + if coinlogex != nil { + model.RemoveCoinLogOne(coinlogex.Platform, coinlogex.LogId) + } + + logger.Logger.Tracef("player snid=%v third->system transfer rollback at try %v times", p.SnId, i+1) + i++ + if i < model.GameParamData.ThirdPltTransferMaxTry { + time.Sleep(time.Duration(model.GameParamData.ThirdPltTransferInterval) * time.Duration(time.Second)) + } + } + return errors.New("third->system transfer error >max try times!") + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + statePack := &gamehall.SCThridGameBalanceUpdateState{} + pack := &gamehall.SCThridGameBalanceUpdate{} + plt := tsk.GetEnv("plt").(webapi.IThirdPlatform) + if data != nil { + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = false + p.thridBalanceReqIsSucces = false + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + logger.Logger.Trace("SCThridAccountTransferHandler third->system transfer error:", data) + } else { + p.thridBalanceReqIsSucces = true + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + // p.thrscene = nil + p.Coin += amount + p.SetPayTs(timeStamp) + p.thirdBalanceRefreshMark[plt.GetPlatformBase().Name] = true + //ThirdPlatformMgrSington.AddThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag, thirdBalance) + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + pack.Coin = proto.Int64(p.Coin) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATE), pack) + p.dirty = true + + //这个地方虽然说拉取失败,但是为了不影响玩家玩其他的游戏,还是可以进入其它场景 + //后面玩家自己通过手动刷新余额 + if p.thrscene != 0 { + p.thrscene = 0 + p.scene = nil + } + p.thridBalanceRefreshReqing = false + p.diffData.Coin = -1 //强制更新金币 + p.SendDiffData() + return + }), "ThridAccountRefresh") + tsk.PutEnv("plt", value) + tsk.Start() + return true + }) +} + +type CSGetPrivateRoomHistoryPacketFactory struct { +} +type CSGetPrivateRoomHistoryHandler struct { +} + +func (this *CSGetPrivateRoomHistoryPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSGetPrivateRoomHistory{} + return pack +} + +func (this *CSGetPrivateRoomHistoryHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetPrivateRoomHistoryHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSGetPrivateRoomHistory); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetPrivateRoomHistoryHandler p == nil") + return nil + } + + pps := PrivateSceneMgrSington.GetOrCreatePlayerPrivateScene(p) + if pps == nil { + logger.Logger.Warnf("CSGetPrivateRoomHistoryHandler PrivateSceneMgrSington.GetOrCreatePlayerPrivateScene(%v)", p.SnId) + return nil + } + + pps.LoadLogs(p, msg.GetQueryTime()) + } + return nil +} + +type CSQueryRoomInfoPacketFactory struct { +} +type CSQueryRoomInfoHandler struct { +} + +func (this *CSQueryRoomInfoPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSQueryRoomInfo{} + return pack +} + +func (this *CSQueryRoomInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + //msg, ok := data.(*gamehall.CSQueryRoomInfo) + //if !ok { + // return nil + //} + //if len(msg.GetId()) > 0 { + // return this.ProcessId(s, packetid, data, sid) + //} + return this.ProcessLocalGame(s, packetid, data, sid) +} + +func (this *CSQueryRoomInfoHandler) ProcessLocalGame(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSQueryRoomInfoHandler Process recv ProcessLocalGame", data) + if msg, ok := data.(*gamehall.CSQueryRoomInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSQueryRoomInfoHandler p == nil") + return nil + } + pack := &gamehall.SCQueryRoomInfo{ + GameSite: proto.Int32(msg.GetGameSite()), + } + mapGameCfg := PlatformMgrSingleton.GetGameFrees(p.Platform) + for _, gameid := range msg.GetGameIds() { + pack.GameIds = append(pack.GameIds, gameid) + scenes := SceneMgrSingleton.GetScenesByGame(int(gameid)) + for _, scene := range scenes { + var isShow bool + if mapGameCfg != nil { + if cfg, have := mapGameCfg[scene.dbGameFree.Id]; have && cfg.GroupId != 0 && cfg.GroupId == scene.groupId { + isShow = true + } + } + if p.Platform == scene.limitPlatform.IdStr || isShow { + if scene != nil && scene.sceneMode == common.SceneMode_Public && len(scene.players) != 0 { + if scene.gameId == int(gameid) && scene.gameSite == int(msg.GetGameSite()) { + roomInfo := &gamehall.QRoomInfo{ + GameFreeId: proto.Int32(scene.dbGameFree.GetId()), + GameId: proto.Int32(scene.dbGameFree.GetGameId()), + RoomId: proto.Int(scene.sceneId), + BaseCoin: proto.Int64(int64(scene.BaseScore)), + LimitCoin: proto.Int64(scene.dbGameFree.GetLimitCoin()), + CurrNum: proto.Int(scene.GetPlayerCnt()), + MaxPlayer: proto.Int(scene.playerNum), + Creator: proto.Int32(scene.creator), + CreateTs: proto.Int32(int32(scene.createTime.Unix())), + Params: scene.params, + } + pack.RoomInfo = append(pack.RoomInfo, roomInfo) + } + } + } + } + } + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_QUERYROOMINFO), pack) + logger.Logger.Trace("SCQueryRoomInfo: ", pack) + } + return nil +} + +func (this *CSQueryRoomInfoHandler) ProcessId(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSQueryRoomInfoHandler Process recv ProcessId", data) + if msg, ok := data.(*gamehall.CSQueryRoomInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSQueryRoomInfoHandler p == nil") + return nil + } + + pack := &gamehall.SCQueryRoomInfo{} + mapGameCfg := PlatformMgrSingleton.GetGameFrees(p.Platform) + for _, v := range msg.GetId() { + gameid := v / 10000 + pack.GameIds = append(pack.GameIds, gameid) + scenes := SceneMgrSingleton.GetScenesByGame(int(gameid)) + for _, scene := range scenes { + var isShow bool + if mapGameCfg != nil { + if cfg, have := mapGameCfg[scene.dbGameFree.Id]; have && cfg.GroupId != 0 && cfg.GroupId == scene.groupId { + isShow = true + } + } + if p.Platform == scene.limitPlatform.IdStr || isShow { + if scene != nil && scene.sceneMode == common.SceneMode_Public && len(scene.players) != 0 { + if scene.dbGameFree.Id == v { + roomInfo := &gamehall.QRoomInfo{ + GameFreeId: proto.Int32(scene.dbGameFree.GetId()), + GameId: proto.Int32(scene.dbGameFree.GetGameId()), + RoomId: proto.Int(scene.sceneId), + BaseCoin: proto.Int64(int64(scene.BaseScore)), + LimitCoin: proto.Int64(scene.dbGameFree.GetLimitCoin()), + CurrNum: proto.Int(scene.GetPlayerCnt()), + MaxPlayer: proto.Int(scene.playerNum), + Creator: proto.Int32(scene.creator), + CreateTs: proto.Int32(int32(scene.createTime.Unix())), + Params: scene.params, + } + pack.RoomInfo = append(pack.RoomInfo, roomInfo) + } + } + } + } + } + + pack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_QUERYROOMINFO), pack) + logger.Logger.Trace("SCQueryRoomInfo: ", pack) + } + return nil +} + +type CSLotteryLogPacketFactory struct { +} +type CSLotteryLogHandler struct { +} + +func (this *CSLotteryLogPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSLotteryLog{} + return pack +} + +func (this *CSLotteryLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSLotteryLogHandler Process recv ", data) + //if msg, ok := data.(*gamehall.CSLotteryLog); ok { + // p := PlayerMgrSington.GetPlayer(sid) + // if p == nil { + // logger.Logger.Warn("CSLotteryLogHandler p == nil") + // return nil + // } + // + // LotteryMgrSington.FetchLotteryLog(p, msg.GetGameFreeId()) + //} + return nil +} + +// 获取指定游戏配置 包括分场信息 +type CSGetGameConfigPacketFactory struct { +} +type CSGetGameConfigHandler struct { +} + +func (this *CSGetGameConfigPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSGetGameConfig{} + return pack +} + +func (this *CSGetGameConfigHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetGameConfigHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSGetGameConfig); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetGameConfigHandler p == nil") + return nil + } + + plf := msg.GetPlatform() + if plf == "" { + logger.Logger.Warn("CSGetGameConfigHandler plf == ''") + return nil + } + + chl := msg.GetChannel() + if chl == "" { + logger.Logger.Warn("CSGetGameConfigHandler chl == ''") + return nil + } + + gameId := msg.GetGameId() + if gameId == 0 { + logger.Logger.Warn("CSGetGameConfigHandler gameId == 0") + return nil + } + p.SendGameConfig(gameId, plf, chl) + } + return nil +} + +// 进入游戏操作 +type CSEnterGamePacketFactory struct { +} +type CSEnterGameHandler struct { +} + +func (this *CSEnterGamePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSEnterGame{} + return pack +} + +func (this *CSEnterGameHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSEnterGameHandler Process recv ", data) + msg, ok := data.(*gamehall.CSEnterGame) + if !ok { + return nil + } + + gameId := msg.GetId() //gameid + if gameId > 10000 { + gameId = msg.GetId() / 10000 //gamefreeid + } + + if common.IsLocalGame(int(gameId)) { + return this.ProcessLocal(s, packetid, data, sid) + } + return this.ProcessNormal(s, packetid, data, sid) +} + +func (this *CSEnterGameHandler) ProcessLocal(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSEnterGameHandler ProcessLocal recv ", data) + msg, ok := data.(*gamehall.CSEnterGame) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + // 返回消息 + var ret gamehall.OpResultCode_Game + pack := &gamehall.SCEnterGame{ + Id: msg.Id, + } + oldPlatform := p.Platform + sendEnterGame := func() { + //机器人要避免身上的平台标记被污染 + if p.IsRob { + if ret != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + p.Platform = oldPlatform + } + } + pack.OpCode = ret + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERGAME), pack) + } + + // 进入原房间 + if p.scene != nil { + if p.thrscene > 0 { + ret = gamehall.OpResultCode_Game_OPRC_InOtherGameIng_Game + sendEnterGame() + return nil + } + logger.Logger.Warnf("CSEnterGameHandler found snid:%v had in scene:%v gameid:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + p.ReturnScene(true) + return nil + } + + // 获取gameId + gameId := msg.GetId() //gameid + if gameId > 10000 { + gameId = msg.GetId() / 10000 //gamefreeid + } + + // 本地游戏入场规则 + playerTakeCoin := p.Coin + gameSite := srvdata.CreateRoomMgrSington.GetGameSiteByGameId(gameId, playerTakeCoin) + if gameSite == 0 { + ret = gamehall.OpResultCode_Game_OPRC_CoinNotEnough_Game + sendEnterGame() + return nil + } + + gamefreeid := gameId*10000 + gameSite + gps := PlatformMgrSingleton.GetGameFree(p.Platform, gamefreeid) + if gps == nil { + ret = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + sendEnterGame() + return nil + } + + dbGameFree := gps.DbGameFree + var roomId int32 + params := msg.GetOpParams() + + if dbGameFree == nil { + ret = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + sendEnterGame() + return nil + } + + if len(params) != 0 { + roomId = params[0] + platformName := CoinSceneMgrSingleton.GetPlatformBySceneId(int(roomId)) + if p.IsRob { + p.Platform = platformName + } else if p.GMLevel > 0 && p.Platform == platformName { //允许GM直接按房间ID进场 + roomId = params[0] + } + } + + if len(msg.GetPlatform()) > 0 && p.IsRob { + p.Platform = msg.GetPlatform() + } + + if len(params) != 0 && (p.GMLevel > 0 || dbGameFree.GetCreateRoomNum() != 0) { //允许GM|或者可选房间的游戏直接按房间ID进场 + s := SceneMgrSingleton.GetScene(int(params[0])) + if s != nil { + if s.limitPlatform.IdStr == p.Platform || (gps.GroupId != 0 && s.groupId == gps.GroupId) { + roomId = params[0] + } + } + } + + excludeSceneIds := p.lastSceneId[gamefreeid] + ret = gamehall.OpResultCode_Game(CoinSceneMgrSingleton.PlayerEnter(p, gamefreeid, roomId, excludeSceneIds, true)) + if p.scene != nil { + pack.OpParams = append(pack.OpParams, int32(p.scene.sceneId)) + // 有房间还进入失败,尝试returnroom + if ret != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + p.ReturnScene(true) + return nil + } + } + sendEnterGame() + return nil +} + +func (this *CSEnterGameHandler) ProcessNormal(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSEnterGameHandler ProcessNormal recv ", data) + msg, ok := data.(*gamehall.CSEnterGame) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + // 返回消息 + var ret gamehall.OpResultCode_Game + pack := &gamehall.SCEnterGame{ + Id: msg.Id, + } + oldPlatform := p.Platform + sendEnterGame := func() { + //机器人要避免身上的平台标记被污染 + if p.IsRob { + if ret != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + p.Platform = oldPlatform + } + } + pack.OpCode = ret + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_ENTERGAME), pack) + } + + // 进入原房间 + if p.scene != nil { + if p.thrscene > 0 { + ret = gamehall.OpResultCode_Game_OPRC_InOtherGameIng_Game + sendEnterGame() + return nil + } + logger.Logger.Warnf("CSEnterGameHandler found snid:%v had in scene:%v gameid:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + p.ReturnScene(true) + return nil + } + + // 其他游戏入场规则 + gps := PlatformMgrSingleton.GetGameFree(p.Platform, msg.GetId()) + if gps == nil { + ret = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + sendEnterGame() + return nil + } + + dbGameFree := gps.DbGameFree + if dbGameFree == nil { + ret = gamehall.OpResultCode_Game_OPRC_RoomHadClosed_Game + sendEnterGame() + return nil + } + + gameType := dbGameFree.GetGameType() + var roomId int32 + params := msg.GetOpParams() // 0:房间id + switch { + case common.IsHundredType(gameType): + if len(params) != 0 { + roomId = params[0] + name, ok := HundredSceneMgrSington.GetPlatformNameBySceneId(roomId) + if p.IsRob { + //机器人先伪装成对应平台的用户 + if ok { + p.Platform = name + } + } else if p.GMLevel > 0 && p.Platform == name { //允许GM直接按房间ID进场 + roomId = params[0] + } + } + + if len(params) != 0 && p.GMLevel > 0 { //允许GM直接按房间ID进场 + s := SceneMgrSingleton.GetScene(int(params[0])) + if s != nil { + if s.limitPlatform.IdStr == p.Platform || (s.groupId != 0 && s.groupId == gps.GroupId) { + roomId = params[0] + } + } + } + ret = gamehall.OpResultCode_Game(HundredSceneMgrSington.PlayerEnter(p, msg.GetId())) + if p.scene != nil { + pack.OpParams = append(pack.OpParams, msg.GetId()) + if ret != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + p.ReturnScene(true) + return nil + } + } + + case common.IsCoinSceneType(gameType): + if len(params) != 0 { + roomId = params[0] + platformName := CoinSceneMgrSingleton.GetPlatformBySceneId(int(roomId)) + if p.IsRob { + p.Platform = platformName + } else if p.GMLevel > 0 && p.Platform == platformName { //允许GM直接按房间ID进场 + roomId = params[0] + } + } + if len(msg.GetPlatform()) > 0 && p.IsRob { + p.Platform = msg.GetPlatform() + } + + if len(params) != 0 && (p.GMLevel > 0 || dbGameFree.GetCreateRoomNum() != 0) { //允许GM|或者可选房间的游戏直接按房间ID进场 + s := SceneMgrSingleton.GetScene(int(params[0])) + if s != nil { + if s.limitPlatform.IdStr == p.Platform || (gps.GroupId != 0 && s.groupId == gps.GroupId) { + roomId = params[0] + } + } + } + + excludeSceneIds := p.lastSceneId[msg.GetId()] + ret = gamehall.OpResultCode_Game(CoinSceneMgrSingleton.PlayerEnter(p, msg.GetId(), roomId, excludeSceneIds, true)) + if p.scene != nil { + pack.OpParams = append(pack.OpParams, int32(p.scene.sceneId)) + if ret != gamehall.OpResultCode_Game_OPRC_Sucess_Game { + p.ReturnScene(true) + return nil + } + } + default: + + } + sendEnterGame() + return nil +} + +// 退出游戏操作 +type CSQuitGamePacketFactory struct { +} +type CSQuitGameHandler struct { +} + +func (this *CSQuitGamePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSQuitGame{} + return pack +} + +func (this *CSQuitGameHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSQuitGameHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSQuitGame); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + var ret gamehall.OpResultCode_Game + pack := &gamehall.SCQuitGame{ + Id: msg.Id, + } + + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(msg.GetId()) + gameType := dbGameFree.GetGameType() + if common.IsHundredType(gameType) { + ret = gamehall.OpResultCode_Game(HundredSceneMgrSington.PlayerTryLeave(p)) + } else if common.IsCoinSceneType(gameType) { + ret = gamehall.OpResultCode_Game(CoinSceneMgrSingleton.PlayerTryLeave(p, msg.IsAudience)) + if gamehall.OpResultCode_Game_OPRC_OpYield_Game == ret { + return nil + } + } + + pack.OpCode = ret + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_QUITGAME), pack) + + } + } + return nil +} + +type CSCreateRoomPacketFactory struct { +} +type CSCreateRoomHandler struct { +} + +func (this *CSCreateRoomPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSCreateRoom{} + return pack +} + +func (this *CSCreateRoomHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSCreateRoomHandler Process recv ", data) + msg, ok := data.(*gamehall.CSCreateRoom) + if !ok { + return nil + } + + switch { + case common.IsLocalGame(int(msg.GetGameId())): + return this.ProcessLocalGame(s, packetid, data, sid) + default: + logger.Logger.Errorf("CSCreateRoomHandler no create function %v", msg.GetGameId()) + return nil + } +} + +func (this *CSCreateRoomHandler) ProcessLocalGame(s *netlib.Session, packetid int, data interface{}, sid int64) error { + msg, ok := data.(*gamehall.CSCreateRoom) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + var code gamehall.OpResultCode_Game + var betRange []int32 + var inRange bool + var dbCreateRoom *server.DB_Createroom + var dbGameFree *server.DB_GameFree + var dbGameRule *server.DB_GameRule + var playerTakeCoin = p.GetCoin() + var maxPlayerNum = int(msg.GetMaxPlayerNum()) + var gameId = msg.GetGameId() + var params = msg.GetParams() + var roomId int + var scene *Scene + var sp ScenePolicy + var gamefreeId int32 + var gameSite int32 + var csp *CoinScenePool + var baseScore int32 + var gps *webapi_proto.GameFree + + //根据携带金额取可创房间 DB_Createroom + arrs := srvdata.PBDB_CreateroomMgr.Datas.Arr + for i := len(arrs) - 1; i >= 0; i-- { + if arrs[i].GetGameId() == msg.GetGameId() { + goldRange := arrs[i].GoldRange + if len(goldRange) == 0 { + continue + } + if playerTakeCoin >= int64(goldRange[0]) { + dbCreateRoom = arrs[i] + break + } + } + } + if dbCreateRoom == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, dbCreateRoom) + goto failed + } + + //校验下gameid + if dbCreateRoom.GetGameId() != gameId { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler PBDB_Createroom Id:%v GameId:%v not exist", dbCreateRoom.GetGameId(), gameId) + goto failed + } + + gameSite = dbCreateRoom.GetGameSite() + gamefreeId = gameId*10000 + gameSite + gps = PlatformMgrSingleton.GetGameFree(p.Platform, gamefreeId) + if gps == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, gamefreeId) + goto failed + } + + dbGameFree = gps.DbGameFree + //dbGameFree = srvdata.PBDB_GameFreeMgr.GetData(gamefreeId) + if dbGameFree == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, gamefreeId) + goto failed + } + + //检测房间状态是否开启 + if !PlatformMgrSingleton.CheckGameState(p.Platform, dbGameFree.Id) { + code = gamehall.OpResultCode_Game_OPRC_GameHadClosed + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v GameHadClosed", p.SnId, gamefreeId) + goto failed + } + + dbGameRule = srvdata.PBDB_GameRuleMgr.GetData(dbGameFree.GetGameRule()) + if dbGameRule == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v gamerule not exist", p.SnId, gamefreeId) + goto failed + } + + if len(params) == 0 { + params = common.CopySliceInt32(dbGameRule.GetParams()) + } + sp = GetScenePolicy(int(dbGameFree.GetGameId()), int(dbGameFree.GetGameMode())) + if sp == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v ScenePolicy(gameid:%v mode:%v) not registe", p.SnId, dbGameFree.GetGameId(), dbGameFree.GetGameMode()) + goto failed + } + + if p.scene != nil { + code = gamehall.OpResultCode_Game_OPRC_RoomHadExist_Game + logger.Logger.Tracef("CSCreateRoomHandler had scene(%d)", p.scene.sceneId) + goto failed + } + + //校验底分 + betRange = dbCreateRoom.GetBetRange() + inRange = false + for _, bet := range betRange { + if msg.GetBaseCoin() == bet { + inRange = true + break + } + } + if !inRange || msg.GetBaseCoin() == 0 { + code = gamehall.OpResultCode_Game_OPRC_Error_Game + logger.Logger.Tracef("CSCreateRoomHandler BaseCoin:%v not in BetRange ", msg.GetBaseCoin()) + goto failed + } + //修正底注 + baseScore = msg.GetBaseCoin() + + // 如果人数为0,人数从建房参数中获取,如果没有就用gameconfig中的默认最大人数 + // 再添加新游戏人数放在建房参数params中 + //校验人数 + maxPlayerNum = int(msg.GetMaxPlayerNum()) + if maxPlayerNum != 4 && maxPlayerNum != 2 { + code = gamehall.OpResultCode_Game_OPRC_Error_Game + logger.Logger.Tracef("CSCreateRoomHandler GameId_TienLen_maxPlayerNum:%v ", maxPlayerNum) + maxPlayerNum = 0 + } + + //创建房间 + csp = CoinSceneMgrSingleton.GetCoinScenePool(p.GetPlatform().IdStr, dbGameFree.GetId()) + roomId = SceneMgrSingleton.GenOneCoinSceneId() + if roomId == common.RANDID_INVALID { + code = gamehall.OpResultCode_Game_OPRC_AllocRoomIdFailed_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameId:%v sceneId == -1 ", p.SnId, gameId) + goto failed + } + scene, code = p.CreateLocalGameScene(roomId, int(gameId), int(gameSite), int(msg.GetSceneMode()), maxPlayerNum, + params, dbGameFree, baseScore, csp.groupId) + if scene != nil { + if code == gamehall.OpResultCode_Game_OPRC_Sucess_Game { + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v Create Sucess GameId:%v", p.SnId, gameId) + // try enter scene + csp.scenes[scene.sceneId] = scene + scene.csp = csp + if !p.EnterScene(scene, true, -1) { + code = gamehall.OpResultCode_Game_OPRC_Error_Game + } + } + } + +failed: + resp := &gamehall.SCCreateRoom{ + GameId: msg.GetGameId(), + BaseCoin: msg.GetBaseCoin(), + SceneMode: msg.GetSceneMode(), + MaxPlayerNum: msg.GetMaxPlayerNum(), + Params: msg.GetParams(), + OpRetCode: code, + } + proto.SetDefaults(resp) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_CREATEROOM), resp) + return nil +} + +func (this *CSCreateRoomHandler) ProcessThirteen(s *netlib.Session, packetid int, data interface{}, sid int64) error { + msg, ok := data.(*gamehall.CSCreateRoom) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + var code gamehall.OpResultCode_Game + var dbGameFree *server.DB_GameFree + var dbGameRule *server.DB_GameRule + var params = msg.GetParams() + var baseScore = msg.GetBaseCoin() + var sp ScenePolicy + var gamefreeId = msg.GetId() + var gps *webapi_proto.GameFree + var maxPlayerNum = int(msg.GetMaxPlayerNum()) + var csp *CoinScenePool + var roomId int + var scene *Scene + var gameId = gamefreeId / 10000 + var spd *ScenePolicyData + + gps = PlatformMgrSingleton.GetGameFree(p.Platform, gamefreeId) + if gps == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, gamefreeId) + goto failed + } + + dbGameFree = gps.DbGameFree + if dbGameFree == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, gamefreeId) + goto failed + } + + //检测房间状态是否开启 + if !PlatformMgrSingleton.CheckGameState(p.Platform, dbGameFree.Id) { + code = gamehall.OpResultCode_Game_OPRC_GameHadClosed + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v GameHadClosed", p.SnId, gamefreeId) + goto failed + } + + dbGameRule = srvdata.PBDB_GameRuleMgr.GetData(dbGameFree.GetGameRule()) + if dbGameRule == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v gamerule not exist", p.SnId, gamefreeId) + goto failed + } + + sp = GetScenePolicy(int(gameId), 0) + if sp == nil { + code = gamehall.OpResultCode_Game_OPRC_GameNotExist_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameFreeId:%v not exist", p.SnId, gamefreeId) + goto failed + } + spd, ok = sp.(*ScenePolicyData) + if ok { + //todo 参数校验 + _ = spd + + } + if p.scene != nil { + code = gamehall.OpResultCode_Game_OPRC_RoomHadExist_Game + logger.Logger.Tracef("CSCreateRoomHandler had scene(%d)", p.scene.sceneId) + goto failed + } + + //创建房间 + csp = CoinSceneMgrSingleton.GetCoinScenePool(p.GetPlatform().IdStr, dbGameFree.GetId()) + roomId = SceneMgrSingleton.GenOneCoinSceneId() + if roomId == common.RANDID_INVALID { + code = gamehall.OpResultCode_Game_OPRC_AllocRoomIdFailed_Game + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v GameId:%v sceneId == -1 ", p.SnId, gameId) + goto failed + } + scene, code = p.CreateLocalGameScene(roomId, int(gameId), int(dbGameFree.GetSceneType()), int(msg.GetSceneMode()), + maxPlayerNum, params, dbGameFree, baseScore, csp.groupId) + if scene != nil { + if code == gamehall.OpResultCode_Game_OPRC_Sucess_Game { + logger.Logger.Tracef("CSCreateRoomHandler SnId:%v Create Sucess GameId:%v", p.SnId, gameId) + // try enter scene + csp.scenes[scene.sceneId] = scene + scene.csp = csp + if !p.EnterScene(scene, true, -1) { + code = gamehall.OpResultCode_Game_OPRC_Error_Game + } + } + } + +failed: + resp := &gamehall.SCCreateRoom{ + GameId: msg.GetGameId(), + BaseCoin: msg.GetBaseCoin(), + SceneMode: msg.GetSceneMode(), + MaxPlayerNum: msg.GetMaxPlayerNum(), + Params: msg.GetParams(), + OpRetCode: code, + } + proto.SetDefaults(resp) + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_CREATEROOM), resp) + return nil +} + +// 观众坐下 +type CSAudienceSitPacketFactory struct { +} +type CSAudienceSitHandler struct { +} + +func (this *CSAudienceSitPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSAudienceSit{} + return pack +} + +func (this *CSAudienceSitHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSAudienceSitHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSAudienceSit); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSAudienceSitHandler p == nil") + return nil + } + if p.scene == nil { + logger.Logger.Warn("CSAudienceSitHandler scene == nil") + return nil + } + if int32(p.scene.sceneId) != msg.GetRoomId() { + logger.Logger.Warn("CSAudienceSitHandler sceneId != roomId") + return nil + } + newPlayer := PlayerMgrSington.GetPlayerBySnId(p.SnId) + if newPlayer != nil { + pack := &gamehall.SCAudienceSit{ + RoomId: proto.Int32(msg.GetRoomId()), + } + if !p.scene.IsTestScene() { + // 入场限额检查 + limitCoin := srvdata.CreateRoomMgrSington.GetLimitCoinByBaseScore(int32(p.scene.gameId), int32(p.scene.gameSite), p.scene.BaseScore) + if p.Coin < limitCoin { + pack.OpCode = gamehall.OpResultCode_Game_OPRC_MoneyNotEnough_Game + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_AUDIENCESIT), pack) + return nil + } + } + // 是否还有空座位 + if p.scene.IsFull() { + pack.OpCode = gamehall.OpResultCode_Game_OPRC_RoomIsFull_Game + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_AUDIENCESIT), pack) + return nil + } + p.scene.AudienceSit(newPlayer, -1) + pack.OpCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + newPlayer.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_AUDIENCESIT), pack) + } + } + return nil +} + +// 我的游戏信息及平台公告 +type CSRecordAndNoticePacketFactory struct { +} +type CSRecordAndNoticeHandler struct { +} + +func (this *CSRecordAndNoticePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSRecordAndNotice{} + return pack +} + +func (this *CSRecordAndNoticeHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRecordAndNoticeHandler Process recv ", data) + msg, ok := data.(*gamehall.CSRecordAndNotice) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSRecordAndNoticeHandler p == nil") + return nil + } + + pack := &gamehall.SCRecordAndNotice{ + OpCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + } + + switch msg.GetOpt() { + case 0: + // 公告 + list := PlatformMgrSingleton.GetCommonNotice(p.Platform) + if list != nil { + for _, v := range list.List { + pack.List = append(pack.List, &gamehall.CommonNotice{ + Sort: v.Sort, // 排序 第几位 + Title: v.Title, // 标题 + Content: v.Content, // 内容 + TypeName: v.TypeName, // 大标题 + Type: v.Type, // 大标题类型 + StartTime: int32(v.StartTime), // 开始显示时间 + EndTime: int32(v.EndTime), // 结束显示时间 + CategoryType: v.CategoryType, + ImgUrl: v.ImgUrl, + NoticeId: v.NoticeId, + IsLoop: v.IsLoop, + LoopTime: v.LoopTime, + }) + } + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_COMNOTICE), pack) + logger.Logger.Trace("CSRecordAndNoticeHandler Process send ", pack) + + case 1: + // 战绩日期列表 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + gdi := model.GetPlayerExistListByTs(p.SnId, p.Platform, msg.StartTime, 7) + return &gdi + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil && data.(*[]int64) != nil { + gamets := data.(*[]int64) + pack.GlistTs = append(pack.GlistTs, *gamets...) + + } else { + pack.OpCode = gamehall.OpResultCode_Game_OPRC_Error_Game + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_COMNOTICE), pack) + }), "SCRecordAndNotice").Start() + + case 2: + // 当日战绩 + starttime := msg.StartTime + time := time.Unix(starttime, 0) + endtime := time.AddDate(0, 0, 1).Unix() - 1 //23:59:59 + var list model.GamePlayerListType + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + list = model.GetPlayerListByHallExAPI(p.SnId, p.Platform, starttime, endtime, int(msg.PageNo), int(msg.PageSize)) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + for _, v := range list.Data { + pack.Glist = append(pack.Glist, &gamehall.PlayerRecord{ + GameFreeid: v.GameFreeid, + GameDetailedLogId: v.GameDetailedLogId, + TotalIn: v.TotalIn, + TotalOut: v.TotalOut, + Ts: v.Ts, + MatchType: proto.Int32(v.MatchType), + }) + } + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_COMNOTICE), pack) + }), "SCRecordAndNotice").Start() + } + + return nil +} + +func init() { + // 观众进入房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCE_ENTERROOM), &CSAudienceEnterRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCE_ENTERROOM), &CSEnterRoomPacketFactory{}) + // 返回房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_RETURNROOM), &CSReturnRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_RETURNROOM), &CSReturnRoomPacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_JOINGAME), &CSJoinGameHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_JOINGAME), &CSJoinGamePacketFactory{}) + + common.RegisterHandler(int(player.PlayerPacketID_PACKET_CS_GETDATALOG), &CSGetDataLogHandler{}) + netlib.RegisterFactory(int(player.PlayerPacketID_PACKET_CS_GETDATALOG), &CSGetDataLogPacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_ENTERTHRIDGAME), &CSEnterThridGameHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_ENTERTHRIDGAME), &CSEnterThridGamePacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_LEAVETHRIDGAME), &CSLeaveThridGameHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_LEAVETHRIDGAME), &CSLeaveThridGamePacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_THRIDGAMEBALANCEUPDATE), &CSThridBalanceRefreshHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_THRIDGAMEBALANCEUPDATE), &CSThridBalanceRefreshPacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_GETPRIVATEROOMHISTORY), &CSGetPrivateRoomHistoryHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_GETPRIVATEROOMHISTORY), &CSGetPrivateRoomHistoryPacketFactory{}) + + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_LOTTERYLOG), &CSLotteryLogHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_LOTTERYLOG), &CSLotteryLogPacketFactory{}) + //获取指定游戏配置 包括分场信息 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_GETGAMECONFIG), &CSGetGameConfigHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_GETGAMECONFIG), &CSGetGameConfigPacketFactory{}) + //玩家进入游戏 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_ENTERGAME), &CSEnterGameHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_ENTERGAME), &CSEnterGamePacketFactory{}) + //玩家进入房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_ENTERROOM), &CSEnterRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_ENTERROOM), &CSEnterRoomPacketFactory{}) + //玩家退出游戏 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_QUITGAME), &CSQuitGameHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_QUITGAME), &CSQuitGamePacketFactory{}) + //创建房间 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_CREATEROOM), &CSCreateRoomHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_CREATEROOM), &CSCreateRoomPacketFactory{}) + //查询公共房间列表 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_QUERYROOMINFO), &CSQueryRoomInfoHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_QUERYROOMINFO), &CSQueryRoomInfoPacketFactory{}) + //观众坐下 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCESIT), &CSAudienceSitHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_AUDIENCESIT), &CSAudienceSitPacketFactory{}) + //我的游戏信息及平台公告 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_COMNOTICE), &CSRecordAndNoticeHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_COMNOTICE), &CSRecordAndNoticePacketFactory{}) +} diff --git a/worldsrv/action_hundredscene.go b/worldsrv/action_hundredscene.go new file mode 100644 index 0000000..e8a25de --- /dev/null +++ b/worldsrv/action_hundredscene.go @@ -0,0 +1,487 @@ +package main + +import ( + "fmt" + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSHundredSceneGetPlayerNumPacketFactory struct { +} +type CSHundredSceneGetPlayerNumHandler struct { +} + +func (this *CSHundredSceneGetPlayerNumPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSHundredSceneGetPlayerNum{} + return pack +} + +func (this *CSHundredSceneGetPlayerNumHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSHundredSceneGetPlayerNumHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSHundredSceneGetPlayerNum); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + nums := HundredSceneMgrSington.GetPlayerNums(p, msg.GetGameId(), msg.GetGameModel()) + pack := &gamehall.SCHundredSceneGetPlayerNum{ + Nums: nums, + } + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_HUNDREDSCENE_GETPLAYERNUM), pack) + logger.Logger.Trace("SCHundredSceneGetPlayerNum:", pack) + } + } + + return nil +} + +type CSHundredSceneOpPacketFactory struct { +} +type CSHundredSceneOpHandler struct { +} + +func (this *CSHundredSceneOpPacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSHundredSceneOp{} + return pack +} + +func (this *CSHundredSceneOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSHundredSceneOpHandler Process recv ", data) + if msg, ok := data.(*gamehall.CSHundredSceneOp); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + var ret gamehall.OpResultCode_Hundred + pack := &gamehall.SCHundredSceneOp{ + Id: msg.Id, + OpType: msg.OpType, + } + oldPlatform := p.Platform + switch msg.GetOpType() { + case HundredSceneOp_Enter: + //pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) + //if pt != nil && pt.IsForceBind == 1 { + // if p.BeUnderAgentCode == "" || p.BeUnderAgentCode == "0" { + // ret = gamehall.OpResultCode_Hundred_OPRC_MustBindPromoter_Hundred + // goto done + // } + //} + if p.scene != nil { + logger.Logger.Warnf("CSHundredSceneOpHandler CoinSceneOp_Enter found snid:%v had in scene:%v gameid:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + p.ReturnScene(false) + return nil + } + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(msg.GetId()) + gameId := dbGameFree.GetGameId() + var roomId int32 + params := msg.GetOpParams() + if len(params) != 0 { + roomId = params[0] + name, ok := HundredSceneMgrSington.GetPlatformNameBySceneId(roomId) + if p.IsRob { + //机器人先伪装成对应平台的用户 + if ok { + p.Platform = name + } + } else if p.GMLevel > 0 && p.Platform == name { //允许GM直接按房间ID进场 + roomId = params[0] + } + } + //检测房间状态是否开启 + gps := PlatformMgrSingleton.GetGameFree(p.Platform, msg.GetId()) + if gps == nil { + ret = gamehall.OpResultCode_Hundred_OPRC_RoomHadClosed_Hundred + goto done + } + + dbGameFree = gps.DbGameFree + if len(params) != 0 && p.GMLevel > 0 { //允许GM直接按房间ID进场 + s := SceneMgrSingleton.GetScene(int(params[0])) + if s != nil { + if s.limitPlatform.IdStr == p.Platform || (gps.GroupId != 0 && s.groupId == gps.GroupId) { + roomId = params[0] + } + } + } + + if dbGameFree == nil { + ret = gamehall.OpResultCode_Hundred_OPRC_RoomHadClosed_Hundred + goto done + } + + if dbGameFree.GetLimitCoin() != 0 && int64(dbGameFree.GetLimitCoin()) > p.Coin { + ret = gamehall.OpResultCode_Hundred_OPRC_CoinNotEnough_Hundred + goto done + } + + if dbGameFree.GetMaxCoinLimit() != 0 && int64(dbGameFree.GetMaxCoinLimit()) < p.Coin && !p.IsRob { + ret = gamehall.OpResultCode_Hundred_OPRC_CoinTooMore_Hundred + goto done + } + + //检查游戏次数限制 + if !p.IsRob { + //todayData, _ := p.GetDaliyGameData(int(dbGameFree.GetId())) + //if dbGameFree.GetPlayNumLimit() != 0 && + // todayData != nil && + // todayData.GameTimes >= int64(dbGameFree.GetPlayNumLimit()) { + // ret = gamehall.OpResultCode_Hundred_OPRC_RoomGameTimes_Hundred + // goto done + //} + } + + gameVers := srvdata.GetGameVers(p.PackageID) + if gameVers != nil { + if ver, ok := gameVers[fmt.Sprintf("%v,%v", gameId, p.Channel)]; ok { + pack.MinApkVer = proto.Int32(ver.MinApkVer) + pack.MinResVer = proto.Int32(ver.MinResVer) + pack.LatestApkVer = proto.Int32(ver.LatestApkVer) + pack.LatestResVer = proto.Int32(ver.LatestResVer) + + if msg.GetApkVer() < ver.MinApkVer { + ret = gamehall.OpResultCode_Hundred_OPRC_YourAppVerIsLow_Hundred + goto done + } + + if msg.GetResVer() < ver.MinResVer { + ret = gamehall.OpResultCode_Hundred_OPRC_YourResVerIsLow_Hundred + goto done + } + } + } + + ret = HundredSceneMgrSington.PlayerEnter(p, msg.GetId()) + if p.scene != nil { + pack.OpParams = append(pack.OpParams, msg.GetId()) + //TODO 有房间还进入失败,尝试returnroom + if ret != gamehall.OpResultCode_Hundred_OPRC_Sucess_Hundred { + p.ReturnScene(false) + return nil + } + } + case HundredSceneOp_Leave: + ret = HundredSceneMgrSington.PlayerTryLeave(p) + case HundredSceneOp_Change: + /*var exclude int32 + if p.scene != nil { + exclude = int32(p.scene.sceneId) + } + params := msg.GetOpParams() + if len(params) != 0 { + exclude = params[0] + } + if HundredSceneMgrSington.PlayerInChanging(p) { //换桌中 + return nil + } + ret = HundredSceneMgrSington.PlayerTryChange(p, msg.GetId(), exclude)*/ + } + done: + //机器人要避免身上的平台标记被污染 + if p.IsRob { + if ret != gamehall.OpResultCode_Hundred_OPRC_Sucess_Hundred { + p.Platform = oldPlatform + } + } + pack.OpCode = ret + proto.SetDefaults(pack) + p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_HUNDREDSCENE_OP), pack) + //} + //if msg.GetOpType() == common.CoinSceneOp_Enter && ret == gamehall.OpResultCode_Hundred_OPRC_Sucess_Hundred && p.scene != nil { + // gameName := p.scene.dbGameFree.GetName() + p.scene.dbGameFree.GetTitle() + // ActMonitorMgrSington.SendActMonitorEvent(ActState_Game, p.SnId, p.Name, p.Platform, + // 0, 0, gameName, 0) + //} + } + } + return nil +} + +type CSGameObservePacketFactory struct { +} +type CSGameObserveHandler struct { +} + +func (this *CSGameObservePacketFactory) CreatePacket() interface{} { + pack := &gamehall.CSGameObserve{} + return pack +} + +func (this *CSGameObserveHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGameObserveHandler Process recv ", data) + if _, ok := data.(*gamehall.CSGameObserve); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + //if msg.GetStartOrEnd() { + // gameStateMgr.PlayerRegiste(p, msg.GetGameId(), msg.GetStartOrEnd()) + // pack := &gamehall.SCGameSubList{} + // statePack := &gamehall.SCGameState{} + // scenes := HundredSceneMgrSington.GetPlatformScene(p.Platform, msg.GetGameId()) + // for _, value := range scenes { + // pack.List = append(pack.List, &gamehall.GameSubRecord{ + // GameFreeId: proto.Int32(value.dbGameFree.GetId()), + // NewLog: proto.Int32(-1), + // LogCnt: proto.Int(len(value.GameLog)), + // TotleLog: value.GameLog, + // }) + // leftTime := int64(value.StateSec) - (time.Now().Unix() - value.StateTs) + // if leftTime < 0 { + // leftTime = 0 + // } + // statePack.List = append(statePack.List, &gamehall.GameState{ + // GameFreeId: proto.Int32(value.dbGameFree.GetId()), + // Ts: proto.Int64(leftTime), + // Sec: proto.Int32(value.StateSec), + // }) + // } + // p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_GAMESUBLIST), pack) + // logger.Logger.Trace("SCGameSubList:", pack) + // p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_GAMESTATE), statePack) + // logger.Logger.Trace("SCGameState:", statePack) + //} else { + // gameStateMgr.PlayerClear(p) + //} + } + } + return nil +} + +//type CSHundredSceneGetGameJackpotPacketFactory struct { +//} +//type CSHundredSceneGetGameJackpotHandler struct { +//} +// +//func (this *CSHundredSceneGetGameJackpotPacketFactory) CreatePacket() interface{} { +// pack := &gamehall.CSHundredSceneGetPlayerNum{} +// return pack +//} +// +//func (this *CSHundredSceneGetGameJackpotHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSHundredSceneGetGameJackpotHandler Process recv ", data) +// if _, ok := data.(*gamehall.CSHundredSceneGetPlayerNum); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// //gameid := int(msg.GetGameId()) +// //// 冰河世纪, 百战成神, 财神, 复仇者联盟, 复活岛 +// //if gameid == common.GameId_IceAge || gameid == common.GameId_TamQuoc || gameid == common.GameId_CaiShen || +// // gameid == common.GameId_Avengers || gameid == common.GameId_EasterIsland { +// // gameStateMgr.PlayerRegiste(p, msg.GetGameId(), true) +// // pack := &gamehall.SCHundredSceneGetGameJackpot{} +// // scenes := HundredSceneMgrSington.GetPlatformScene(p.Platform, msg.GetGameId()) +// // for _, v := range scenes { +// // jpfi := &gamehall.GameJackpotFundInfo{ +// // GameFreeId: proto.Int32(v.dbGameFree.GetId()), +// // JackPotFund: proto.Int64(v.JackPotFund), +// // } +// // pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) +// // } +// // proto.SetDefaults(pack) +// // p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack) +// // logger.Logger.Trace("SCHundredSceneGetGameJackpot:", pack) +// //} +// } +// } +// return nil +//} +// +//type CSHundredSceneGetGameHistoryInfoPacketFactory struct { +//} +//type CSHundredSceneGetGameHistoryInfoHandler struct { +//} +// +//func (this *CSHundredSceneGetGameHistoryInfoPacketFactory) CreatePacket() interface{} { +// pack := &gamehall.CSHundredSceneGetHistoryInfo{} +// return pack +//} +// +//func (this *CSHundredSceneGetGameHistoryInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("World CSHundredSceneGetGameHistoryInfoHandler Process recv ", data) +// if msg, ok := data.(*gamehall.CSHundredSceneGetHistoryInfo); ok { +// gameid := int(msg.GetGameId()) +// historyModel := msg.GetGameHistoryModel() +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// switch historyModel { +// case PLAYER_HISTORY_MODEL: // 历史记录 +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// var genPlayerHistoryInfo = func(spinID string, isFree bool, createdTime, totalBetValue, totalPriceValue, totalBonusValue, multiple int64, player *gamehall.PlayerHistoryInfo) { +// player.SpinID = proto.String(spinID) +// player.CreatedTime = proto.Int64(createdTime) +// player.TotalBetValue = proto.Int64(totalBetValue) +// player.TotalPriceValue = proto.Int64(totalPriceValue) +// player.IsFree = proto.Bool(isFree) +// player.TotalBonusValue = proto.Int64(totalBonusValue) +// player.Multiple = proto.Int64(multiple) +// } +// +// var genPlayerHistoryInfoMsg = func(spinid string, v *model.NeedGameRecord, gdl *model.GameDetailedLog, player *gamehall.PlayerHistoryInfo) { +// switch gameid { +// //case common.GameId_IceAge: +// // data, err := model.UnMarshalIceAgeGameNote(gdl.GameDetailedNote) +// // if err != nil { +// // logger.Logger.Errorf("World UnMarshalIceAgeGameNote error:%v", err) +// // } +// // gnd := data.(*model.IceAgeType) +// // genPlayerHistoryInfo(spinid, gnd.IsFree, int64(v.Ts), int64(gnd.Score), gnd.TotalPriceValue, gnd.TotalBonusValue, player) +// //case common.GameId_TamQuoc: +// // data, err := model.UnMarshalTamQuocGameNote(gdl.GameDetailedNote) +// // if err != nil { +// // logger.Logger.Errorf("World UnMarshalTamQuocGameNote error:%v", err) +// // } +// // gnd := data.(*model.TamQuocType) +// // genPlayerHistoryInfo(spinid, gnd.IsFree, int64(v.Ts), int64(gnd.Score), gnd.TotalPriceValue, gnd.TotalBonusValue, player) +// //case common.GameId_CaiShen: +// // data, err := model.UnMarshalCaiShenGameNote(gdl.GameDetailedNote) +// // if err != nil { +// // logger.Logger.Errorf("World UnMarshalCaiShenGameNote error:%v", err) +// // } +// // gnd := data.(*model.CaiShenType) +// // genPlayerHistoryInfo(spinid, gnd.IsFree, int64(v.Ts), int64(gnd.Score), gnd.TotalPriceValue, gnd.TotalBonusValue, player) +// case common.GameId_Crash: +// data, err := model.UnMarshalGameNoteByHUNDRED(gdl.GameDetailedNote) +// if err != nil { +// logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) +// } +// jsonString, _ := json.Marshal(data) +// +// // convert json to struct +// gnd := model.CrashType{} +// json.Unmarshal(jsonString, &gnd) +// +// //gnd := data.(*model.CrashType) +// for _, curplayer := range gnd.PlayerData { +// if curplayer.UserId == p.SnId { +// genPlayerHistoryInfo(spinid, false, int64(v.Ts), int64(curplayer.UserBetTotal), curplayer.ChangeCoin, 0, int64(curplayer.UserMultiple), player) +// break +// } +// } +// case common.GameId_Avengers: +// data, err := model.UnMarshalAvengersGameNote(gdl.GameDetailedNote) +// if err != nil { +// logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) +// } +// gnd := data.(*model.GameResultLog) +// genPlayerHistoryInfo(spinid, gnd.BaseResult.IsFree, int64(v.Ts), int64(gnd.BaseResult.TotalBet), gnd.BaseResult.WinTotal, gnd.BaseResult.WinSmallGame, 0, player) +// //case common.GameId_EasterIsland: +// // data, err := model.UnMarshalEasterIslandGameNote(gdl.GameDetailedNote) +// // if err != nil { +// // logger.Logger.Errorf("World UnMarshalEasterIslandGameNote error:%v", err) +// // } +// // gnd := data.(*model.EasterIslandType) +// // genPlayerHistoryInfo(spinid, gnd.IsFree, int64(v.Ts), int64(gnd.Score), gnd.TotalPriceValue, gnd.TotalBonusValue, player) +// default: +// logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive gameid(%v) error", gameid) +// } +// } +// +// gameclass := int32(2) +// spinid := strconv.FormatInt(int64(p.SnId), 10) +// dbGameFrees := srvdata.PBDB_GameFreeMgr.Datas.Arr //.GetData(data.DbGameFree.Id) +// roomtype := int32(0) +// for _, v := range dbGameFrees { +// if int32(gameid) == v.GetGameId() { +// gameclass = v.GetGameClass() +// roomtype = v.GetSceneType() +// break +// } +// } +// +// gpl := model.GetPlayerListByHallEx(p.SnId, p.Platform, 0, 50, 0, 0, roomtype, gameclass, gameid) +// pack := &gamehall.SCPlayerHistory{} +// for _, v := range gpl.Data { +// if v.GameDetailedLogId == "" { +// logger.Logger.Error("World PlayerHistory GameDetailedLogId is nil") +// break +// } +// gdl := model.GetPlayerHistory(p.Platform, v.GameDetailedLogId) +// player := &gamehall.PlayerHistoryInfo{} +// genPlayerHistoryInfoMsg(spinid, v, gdl, player) +// pack.PlayerHistory = append(pack.PlayerHistory, player) +// } +// proto.SetDefaults(pack) +// logger.Logger.Infof("World gameid:%v PlayerHistory:%v ", gameid, pack) +// return pack +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// if data == nil { +// logger.Logger.Error("World PlayerHistory data is nil") +// return +// } +// p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEPLAYERHISTORY), data) +// }), "CSGetPlayerHistoryHandlerWorld").Start() +// case BIGWIN_HISTORY_MODEL: // 爆奖记录 +// jackpotList := JackpotListMgrSington.GetJackpotList(gameid) +// //if len(jackpotList) < 1 { +// // JackpotListMgrSington.GenJackpot(gameid) // 初始化爆奖记录 +// // JackpotListMgrSington.after(gameid) // 开启定时器 +// // jackpotList = JackpotListMgrSington.GetJackpotList(gameid) +// //} +// pack := JackpotListMgrSington.GetStoCMsg(jackpotList) +// pack.GameId = msg.GetGameId() +// logger.Logger.Infof("World BigWinHistory: %v %v", gameid, pack) +// p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEBIGWINHISTORY), pack) +// case GAME_HISTORY_MODEL: +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// var genGameHistoryInfo = func(gameNumber string, createdTime, multiple int64, hash string, gamehistory *gamehall.GameHistoryInfo) { +// gamehistory.GameNumber = proto.String(gameNumber) +// gamehistory.CreatedTime = proto.Int64(createdTime) +// gamehistory.Hash = proto.String(hash) +// gamehistory.Multiple = proto.Int64(multiple) +// } +// +// gls := model.GetAllGameDetailedLogsByGameIdAndTs(p.Platform, gameid, 20) +// +// pack := &gamehall.SCPlayerHistory{} +// for _, v := range gls { +// +// gamehistory := &gamehall.GameHistoryInfo{} +// +// data, err := model.UnMarshalGameNoteByHUNDRED(v.GameDetailedNote) +// if err != nil { +// logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) +// } +// jsonString, _ := json.Marshal(data) +// +// // convert json to struct +// gnd := model.CrashType{} +// json.Unmarshal(jsonString, &gnd) +// +// genGameHistoryInfo(v.LogId, int64(v.Ts), int64(gnd.Rate), gnd.Hash, gamehistory) +// pack.GameHistory = append(pack.GameHistory, gamehistory) +// } +// proto.SetDefaults(pack) +// logger.Logger.Infof("World gameid:%v History:%v ", gameid, pack) +// return pack +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// if data == nil { +// logger.Logger.Error("World GameHistory data is nil") +// return +// } +// p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEPLAYERHISTORY), data) +// }), "CSGetGameHistoryHandlerWorld").Start() +// default: +// logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive historyModel(%v) error", historyModel) +// } +// } +// } +// return nil +//} + +func init() { + common.RegisterHandler(int(gamehall.HundredScenePacketID_PACKET_CS_HUNDREDSCENE_GETPLAYERNUM), &CSHundredSceneGetPlayerNumHandler{}) + netlib.RegisterFactory(int(gamehall.HundredScenePacketID_PACKET_CS_HUNDREDSCENE_GETPLAYERNUM), &CSHundredSceneGetPlayerNumPacketFactory{}) + + common.RegisterHandler(int(gamehall.HundredScenePacketID_PACKET_CS_HUNDREDSCENE_OP), &CSHundredSceneOpHandler{}) + netlib.RegisterFactory(int(gamehall.HundredScenePacketID_PACKET_CS_HUNDREDSCENE_OP), &CSHundredSceneOpPacketFactory{}) + //请求游戏列表 + common.RegisterHandler(int(gamehall.GameHallPacketID_PACKET_CS_GAMEOBSERVE), &CSGameObserveHandler{}) + netlib.RegisterFactory(int(gamehall.GameHallPacketID_PACKET_CS_GAMEOBSERVE), &CSGameObservePacketFactory{}) + + //// 请求奖池信息 + //common.RegisterHandler(int(gamehall.HundredScenePacketID_PACKET_CS_GAMEJACKPOT), &CSHundredSceneGetGameJackpotHandler{}) + //netlib.RegisterFactory(int(gamehall.HundredScenePacketID_PACKET_CS_GAMEJACKPOT), &CSHundredSceneGetGameJackpotPacketFactory{}) + + ////// 请求历史记录和爆奖记录 + //common.RegisterHandler(int(gamehall.HundredScenePacketID_PACKET_CS_GAMEHISTORYINFO), &CSHundredSceneGetGameHistoryInfoHandler{}) + //netlib.RegisterFactory(int(gamehall.HundredScenePacketID_PACKET_CS_GAMEHISTORYINFO), &CSHundredSceneGetGameHistoryInfoPacketFactory{}) +} diff --git a/worldsrv/action_login.go b/worldsrv/action_login.go new file mode 100644 index 0000000..758c5de --- /dev/null +++ b/worldsrv/action_login.go @@ -0,0 +1,696 @@ +package main + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + login_proto "mongo.games.com/game/protocol/login" + "mongo.games.com/game/srvdata" +) + +/* +登录功能: +记录账号登录状态和连接状态(一个玩家可能多客户端登录,会有多个连接,实现顶号) +连接有两种状态,登录中,登录完成 +登录流程: +1.判断连接是否已经存在(存在说明已经登录过了),存在则不用处理,这是单连接重复登录 +2.判断账号是否有正在登录的连接,有的话则断开当前连接,从账号登录的角度讲只能一个客户端在登录中 +3.判断是否有已经登录完成并且正在游戏中的连接,如果有则断开当前连接(游戏中的连接不会被断开) +4.判断是否有已经登录完成的连接但是没有在游戏中,则当前连接可以登录(因为登录是异步的,登录完成后可以再判断一下3),记录账号信息和连接状态,登录完成断开其它连接(顶号) +5.没有其他连接,正常登录 +*/ + +type CSLoginPacketFactory struct { +} + +type CSLoginHandler struct { +} + +func (this *CSLoginPacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSLogin{} + return pack +} + +func (this *CSLoginHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSLoginHandler Process recv ", data) + csl, ok := data.(*login_proto.CSLogin) + if !ok { + return nil + } + + sendSCLogin := func(code login_proto.OpResultCode, str ...string) { + sclogin := &login_proto.SCLogin{ + OpRetCode: code, + } + if len(str) == 2 { + sclogin.ApkUrl = str[0] + sclogin.IpaUrl = str[1] + } + proto.SetDefaults(sclogin) + common.SendToGate(sid, int(login_proto.LoginPacketID_PACKET_SC_LOGIN), sclogin, s) + } + + sendSCDisconnect := func(code int32) { + ssDis := &login_proto.SSDisconnect{ + SessionId: proto.Int64(sid), + Type: proto.Int32(code), + } + proto.SetDefaults(ssDis) + s.Send(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), ssDis) + } + + if csl.GetUsername() == "" || csl.GetPassword() == "" { + sendSCLogin(login_proto.OpResultCode_OPRC_Error) + sendSCDisconnect(common.KickReason_Freeze) + return nil + } + + // 根据PlatformTag包标识,获取平台信息 + // 机器人没有包标识,根据csl.GetPlatform()判断 + platform, _, promoter, _, tagkey := PlatformMgrSingleton.GetPlatformByPackageTag(csl.GetPlatformTag()) + if platform == 0 && csl.GetPlatform() != common.Platform_Rob { + sendSCLogin(login_proto.OpResultCode_OPRC_Error) + sendSCDisconnect(common.KickReason_Freeze) + return nil + } + + // 替换为后台拿到的配置数据,不再使用客户端的发送数据 + backupPromoter := "" + if csl.GetChannel() != common.Channel_Rob { + // platform + if platform != 0 { + csl.Platform = proto.String(strconv.Itoa(int(platform))) + } else { + csl.Platform = proto.String(DefaultPlatform) + platform = DefaultPlatformInt + } + // channel + //if channel != 0 { + // csl.Channel = proto.String(strconv.Itoa(int(channel))) + //} else { + // csl.Channel = proto.String("") + //} + // promoter + if len(csl.GetPromoter()) <= 0 { + if promoter != 0 { + csl.Promoter = proto.String(strconv.Itoa(int(promoter))) + } else { + csl.Promoter = proto.String("") + } + } else { + backupPromoter = csl.GetPromoter() + } + //InviterId 和 PromoterTree客户端是从剪贴板上读取出来的,所以不以包上的为准 + } + + logger.Logger.Tracef("CSLoginHandler %v", csl.String()) + + // 数据校验 + raw := fmt.Sprintf("%v%v%v%v%v", csl.GetUsername(), csl.GetPassword(), csl.GetTimeStamp(), csl.GetParams(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + hashsum := hex.EncodeToString(h.Sum(nil)) + if hashsum != csl.GetSign() { + logger.Logger.Tracef("ClientSessionAttribute_State hashsum not fit!!! get:%v expect:%v rawstr:%v", csl.GetSign(), hashsum, raw) + sendSCLogin(login_proto.OpResultCode_OPRC_Error) + sendSCDisconnect(common.KickReason_CheckCodeErr) + return nil + } + + // 平台维护 + pt := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(platform))) + if pt == nil || pt.Disable { + sendSCLogin(login_proto.OpResultCode_OPRC_SceneServerMaintain) + sendSCDisconnect(common.KickReason_Freeze) + return nil + } + + // 是否正在维护 + if model.GameParamData.SrvMaintain && SrvIsMaintaining { + inWhiteList := false + for i := 0; i < len(model.GMACData.WhiteList); i++ { + if model.GMACData.WhiteList[i] == csl.GetUsername() { + inWhiteList = true + break + } + } + //排除白名单里的玩家 + if !inWhiteList { + sendSCLogin(login_proto.OpResultCode_OPRC_SceneServerMaintain) + sendSCDisconnect(common.KickReason_Freeze) + return nil + } + } + + // 检查版本号 + if model.GameParamData.VerifyClientVersion { + deviceOs := csl.GetDeviceOs() + packVers := srvdata.GetPackVers(csl.GetPlatformTag()) + if deviceOs != "" && packVers != nil { + if cvers, ok := packVers[deviceOs]; ok { + if csl.GetApkVer() < cvers.MinApkVer { + appInfo := PlatformMgrSingleton.GetPackageTag(csl.PlatformTag) + sendSCLogin(login_proto.OpResultCode_OPRC_YourAppVerIsLow, appInfo.ApkUrl, appInfo.IpaUrl) + sendSCDisconnect(common.KickReason_AppLow) + return nil + } + if csl.GetResVer() < cvers.MinResVer { + appInfo := PlatformMgrSingleton.GetPackageTag(csl.PlatformTag) + sendSCLogin(login_proto.OpResultCode_OPRC_YourResVerIsLow, appInfo.ApkUrl, appInfo.IpaUrl) + sendSCDisconnect(common.KickReason_ResLow) + return nil + } + } + } + } + + // 手机验证码登录,验证码是否正确 + var codeValid bool + if csl.GetLoginType() == common.LoginTypeTelCode { + if csl.GetCode() != "" { + code := CacheMemory.Get(common.GetTelLoginCodeKey(csl.GetPlatform(), csl.GetUsername())) + // 验证码错误 + if code != csl.GetCode() { + sendSCLogin(login_proto.OpResultCode_OPRC_TelCodeError) + sendSCDisconnect(common.KickReason_Freeze) + return nil + } + codeValid = true + } + } + + // 连接正在登录 + state := LoginStateMgrSington.GetLoginStateBySid(sid) + if state != nil { + logger.Logger.Warnf("CSLoginHandler relogining (%v) repeated (%v) ", csl.GetUsername(), sid) + return nil + } + + // 玩家是否有在登录的连接 + if LoginStateMgrSington.IsLogining(csl.GetUsername(), csl.GetPlatform(), tagkey) { + logger.Logger.Warnf("CSLoginHandler logining (%v) disconnect current(%v) ", csl.GetUsername(), sid) + sendSCDisconnect(common.KickReason_Logining) // 登录中重复登录会被断开连接 + return nil + } + + // 玩家已经登录完成并且在游戏中,断开当前连接 + ls := LoginStateMgrSington.GetLoginStateByName(UserKey(csl.GetUsername(), csl.GetPlatform(), tagkey)) + if ls != nil && ls.acc != nil { + // lss 其它连接 + lss := LoginStateMgrSington.LoginFinish(csl.GetUsername(), csl.GetPlatform(), sid, ls.acc, tagkey) + player := PlayerMgrSington.GetPlayerBySnId(ls.acc.SnId) + if len(lss) > 0 && (player != nil && (player.scene != nil || player.thrscene != 0)) { + sendSCLogin(login_proto.OpResultCode_OPRC_LoginOtherPlace) + sendSCDisconnect(common.KickReason_Logining) + return nil + } + } + + clog := &model.ClientLoginInfo{ + LoginType: csl.GetLoginType(), + ApkVer: csl.GetApkVer(), + ResVer: csl.GetResVer(), + InviterId: csl.GetInviterId(), + PromoterTree: csl.GetPromoterTree(), + UserName: csl.GetUsername(), + PlatformTag: csl.GetPlatformTag(), + Promoter: csl.GetPromoter(), + Sid: sid, + HeadUrl: csl.GetHeadUrl(), + DeviceOS: csl.GetDeviceOs(), + } + + // 玩家开始登录 + if LoginStateMgrSington.StartLogin(csl.GetUsername(), csl.GetPlatform(), sid, s, clog, tagkey) { + tl := &TaskLogin{CSLogin: csl, Session: s, Sid: sid, BackupPromoter: backupPromoter, tagkey: tagkey, codeValid: codeValid} + t := task.New(nil, tl, tl, "TaskLogin") + if b := t.StartByExecutor(csl.GetUsername()); !b { + logger.Logger.Trace("login task launch failed") + sendSCDisconnect(common.KickReason_TaskErr) + } + return nil + } + + // 用缓存信息做登录,有account + // 根据StartLogin代码,这里ls.acc一定不为nil + ls = LoginStateMgrSington.GetLoginStateByName(UserKey(csl.GetUsername(), csl.GetPlatform(), tagkey)) + if ls != nil { + acc := ls.acc + // 账号冻结 + if acc.State > time.Now().Unix() { + sendSCLogin(login_proto.OpResultCode_OPRC_AccountBeFreeze) + sendSCDisconnect(common.KickReason_Freeze) + //清理登录状态 + LoginStateMgrSington.LogoutBySid(sid) + return nil + } + + pwdIsErr := true + switch csl.GetLoginType() { + case common.LoginTypeGuest: //游客登录 + if acc.UserName == csl.GetUsername() && acc.Platform == csl.GetPlatform() && acc.TagKey == tagkey { + raw := fmt.Sprintf("%v%v%v", acc.PassWord, common.GetAppId(), csl.GetTimeStamp()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + if pwd != csl.GetPassword() { + pwdIsErr = true + } else { + pwdIsErr = false + } + } + case common.LoginTypeAccount: //帐号登录 + if acc.UserName == csl.GetUsername() && acc.Platform == csl.GetPlatform() && acc.TagKey == tagkey { + raw := fmt.Sprintf("%v%v%v", acc.TelPassWord, common.GetAppId(), csl.GetTimeStamp()) + ht := md5.New() + io.WriteString(ht, raw) + pwd := hex.EncodeToString(ht.Sum(nil)) + if pwd != csl.GetPassword() { + pwdIsErr = true + } else { + pwdIsErr = false + } + } + case common.LoginTypeTelCode: // 手机验证码登录 + if acc.Tel == csl.GetUsername() && acc.Platform == csl.GetPlatform() && acc.TagKey == tagkey { + if codeValid { + // 更新密码 + raw := fmt.Sprintf("%v%v", bson.NewObjectId().Hex(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + acc.TelPassWord = pwd + acc.TelPassWordExpire = time.Now().AddDate(0, 0, common.TelLoginValidDays).Unix() + pwdIsErr = false + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err = model.UpdateAccount(acc) + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + logger.Logger.Errorf("UpdateAccount error:%v", err) + } + }), "UpdateAccount").Start() + } else { + raw := fmt.Sprintf("%v%v%v", acc.TelPassWord, common.GetAppId(), csl.GetTimeStamp()) + ht := md5.New() + io.WriteString(ht, raw) + pwd := hex.EncodeToString(ht.Sum(nil)) + if pwd != csl.GetPassword() { + pwdIsErr = true + } else { + pwdIsErr = false + } + } + } + } + + //密码错误 + if pwdIsErr { + // 缓存登录失败再从数据库查询一次 + if csl.GetChannel() != common.Channel_Rob { + //try from db login + tl := &TaskLogin{CSLogin: csl, Session: s, Sid: sid} + t := task.New(nil, tl, tl, "TaskLogin") + if b := t.StartByExecutor(csl.GetUsername()); !b { + logger.Logger.Trace("login task launch failed") + sendSCDisconnect(common.KickReason_DBLoadAcc) + } + return nil + } + sendSCLogin(login_proto.OpResultCode_OPRC_LoginPassError) + //清理登录状态 + LoginStateMgrSington.LogoutBySid(sid) + return nil + } + + SCLogin(s, sid, csl, acc, login_proto.OpResultCode_OPRC_Sucess) + + // 标记当前玩家登录成功,断开其它连接 + lss := LoginStateMgrSington.LoginFinish(csl.GetUsername(), csl.GetPlatform(), sid, acc, tagkey) + if len(lss) != 0 { + for k, ls := range lss { + ssDis := &login_proto.SSDisconnect{ + SessionId: proto.Int64(k), + Type: proto.Int32(common.KickReason_OtherLogin), + } + proto.SetDefaults(ssDis) + s.Send(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), ssDis) + LoginStateMgrSington.Logout(ls) + logger.Logger.Warnf("==========顶号 oldsid:%v newsid:%v", k, sid) + } + } + } + return nil +} + +type CSCustomServicePacketFactory struct { +} + +type CSCustomServiceHandler struct { +} + +func (this *CSCustomServicePacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSCustomService{} + return pack +} + +func (this *CSCustomServiceHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSCustomService Process recv ", data) + if _, ok := data.(*login_proto.CSCustomService); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSCustomServiceHandler p == nil") + return nil + } + var Url string + var customType int32 + flag := int32(0) + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform != nil { + Url = platform.ServiceUrl + customType = platform.CustomType + if platform.ServiceFlag { + flag = 1 + } + } + pack := &login_proto.SCCustomService{ + Url: proto.String(Url), + OpenFlag: proto.Int32(flag), + CustomType: proto.Int32(customType), + } + p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_CUSTOMSERVICE), pack) + logger.Logger.Trace("SCCustomService:", pack) + } + return nil +} + +type CSPlatFormPacketFactory struct { +} + +type CSPlatFormHandler struct { +} + +func (this *CSPlatFormPacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSPlatFormConfig{} + return pack +} + +func (this *CSPlatFormHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlatFormHandler Process recv ", data) + if _, ok := data.(*login_proto.CSPlatFormConfig); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlatFormHandler p == nil") + return nil + } + + //platformId, _, _ := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform == nil { + scPlatForm := &login_proto.SCPlatFormConfig{ + OpRetCode: login_proto.OpResultCode_OPRC_Error, + } + proto.SetDefaults(scPlatForm) + p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_PLATFORMCFG), scPlatForm) + return nil + } + + scPlatForm := &login_proto.SCPlatFormConfig{ + Platform: proto.String(p.Platform), + OpRetCode: login_proto.OpResultCode_OPRC_Sucess, + UpgradeAccountGiveCoin: proto.Int32(p.GetUpdateAccPrize()), + VipRange: platform.VipRange, + ExchangeMin: proto.Int32(platform.ExchangeMin), + ExchangeLimit: proto.Int32(platform.ExchangeLimit), + OtherParams: proto.String(platform.OtherParams), + SpreadConfig: proto.Int32(platform.SpreadConfig), + ExchangeTax: proto.Int32(platform.ExchangeTax), + ExchangeFlow: proto.Int32(GetExchangeFlow(p.PlayerData)), + ExchangeBankMax: proto.Int32(platform.ExchangeBankMax), + ExchangeAlipayMax: proto.Int32(platform.ExchangeAlipayMax), + ExchangeMultiple: proto.Int32(platform.ExchangeMultiple), + } + rebateTask := RebateInfoMgrSington.rebateTask[p.Platform] + if rebateTask != nil { + scPlatForm.Rebate = &login_proto.RebateCfg{ + RebateSwitch: proto.Bool(rebateTask.RebateSwitch), + ReceiveMode: proto.Int32(int32(rebateTask.ReceiveMode)), + NotGiveOverdue: proto.Int32(int32(rebateTask.NotGiveOverdue)), + } + } + if platform.ClubConfig != nil { //俱乐部配置 + scPlatForm.Club = &login_proto.ClubCfg{ + IsOpenClub: proto.Bool(platform.ClubConfig.IsOpenClub), + CreationCoin: proto.Int64(platform.ClubConfig.CreationCoin), + IncreaseCoin: proto.Int64(platform.ClubConfig.IncreaseCoin), + ClubInitPlayerNum: proto.Int32(platform.ClubConfig.ClubInitPlayerNum), + CreateClubCheckByManual: proto.Bool(platform.ClubConfig.CreateClubCheckByManual), + EditClubNoticeByManual: proto.Bool(platform.ClubConfig.EditClubNoticeByManual), + CreateRoomAmount: proto.Int64(platform.ClubConfig.CreateRoomAmount), + GiveCoinRate: platform.ClubConfig.GiveCoinRate, + } + } + + proto.SetDefaults(scPlatForm) + p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_PLATFORMCFG), scPlatForm) + } + return nil +} + +// +//// 公告信息 +//type CSBulletionInfoPacketFactory struct { +//} +// +//type CSBulletionInfoHandler struct { +//} +// +//func (this *CSBulletionInfoPacketFactory) CreatePacket() interface{} { +// pack := &login_proto.CSBulletionInfo{} +// return pack +//} +//func (this *CSBulletionInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSBulletionInfoHandler Process recv ", data) +// if msg, ok := data.(*login_proto.CSBulletionInfo); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p == nil { +// logger.Logger.Trace("CSBulletionInfoHandler p == nil ") +// return nil +// } +// pf, _, _, _, _ := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) +// platform := strconv.Itoa(int(pf)) +// i := 0 +// var bulletList []*login_proto.Bulletion +// for _, v := range BulletMgrSington.BulletMsgList { +// if v != nil && platform == v.Platform && v.State == 1 { +// bulletList = append(bulletList, &login_proto.Bulletion{}) +// bulletList[i].Id = proto.Int32(v.Id) +// bulletList[i].NoticeTitle = proto.String(v.NoticeTitle) +// bulletList[i].NoticeContent = proto.String(v.NoticeContent) +// bulletList[i].UpdateTime = proto.String(v.UpdateTime) +// bulletList[i].Sort = proto.Int32(v.Sort) +// +// i++ +// } +// } +// +// var rawpack = &login_proto.SCBulletionInfo{ +// Id: proto.Int(0), +// BulletionList: bulletList, +// } +// proto.SetDefaults(rawpack) +// p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_BULLETIONINFO), rawpack) +// +// } +// return nil +//} +// +//// 招商信息 CSCustomerInfoList +//type CSCustomerInfoListPacketFactory struct { +//} +// +//type CSCustomerInfoListHandler struct { +//} +// +//func (this *CSCustomerInfoListPacketFactory) CreatePacket() interface{} { +// pack := &login_proto.CSCustomerInfoList{} +// return pack +//} +//func (this *CSCustomerInfoListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSCustomerInfoListHandler Process recv ", data) +// if _, ok := data.(*login_proto.CSCustomerInfoList); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p == nil { +// logger.Logger.Trace("CSBulletionInfoHandler p == nil ") +// return nil +// } +// var customerList []*login_proto.Customer +// i := 0 +// for _, v := range CustomerMgrSington.CustomerMsgList { +// if v != nil && v.Platform == p.Platform && v.Status == 1 { +// customerList = append(customerList, &login_proto.Customer{}) +// customerList[i].Id = proto.Int32(v.Id) +// customerList[i].Nickname = proto.String(v.Nickname) +// customerList[i].Headurl = proto.String(v.Headurl) +// customerList[i].Ext = proto.String(v.Ext) +// customerList[i].WeixinAccount = proto.String(v.Weixin_account) +// customerList[i].QqAccount = proto.String(v.Qq_account) +// i++ +// } +// } +// var rawpack = &login_proto.SCCustomerInfoList{ +// CustomerList: customerList, +// } +// proto.SetDefaults(rawpack) +// p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_CUSTOMERINFOLIST), rawpack) +// +// } +// return nil +//} + +type CSVerifyTypePacketFactory struct { +} + +type CSVerifyTypeHandler struct { +} + +func (this *CSVerifyTypePacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSVerifyType{} + return pack +} +func (this *CSVerifyTypeHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVerifyTypeHandler Process recv ", data) + if msg, ok := data.(*login_proto.CSVerifyType); ok { + pf, _, _, _, _ := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) + platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(pf))) + pack := &login_proto.SCVerifyType{} + if platform == nil { + pack.OpRetCode = login_proto.OpResultCode_OPRC_Error + } else { + pack.VerifyType = proto.Int32(platform.VerifyCodeType) + pack.OpRetCode = login_proto.OpResultCode_OPRC_Sucess + if platform.VerifyCodeType != common.CodeTypeNo && !reTelRule.MatchString(msg.GetTel()) { + pack := &login_proto.SCVerifyType{ + OpRetCode: login_proto.OpResultCode_OPRC_TelError, + } + common.SendToGate(sid, int(login_proto.LoginPacketID_PACKET_SC_VERIFYTYPE), pack, s) + return nil + } + } + common.SendToGate(sid, int(login_proto.LoginPacketID_PACKET_SC_VERIFYTYPE), pack, s) + } + return nil +} + +type CSRegisterVerifyTypePacketFactory struct { +} + +type CSRegisterVerifyTypeHandler struct { +} + +func (this *CSRegisterVerifyTypePacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSRegisterVerifyType{} + return pack +} +func (this *CSRegisterVerifyTypeHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRegisterVerifyTypeHandler Process recv ", data) + if msg, ok := data.(*login_proto.CSRegisterVerifyType); ok { + pf, _, _, _, _ := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) + platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(pf))) + pack := &login_proto.SCRegisterVerifyType{} + if platform == nil { + pack.OpRetCode = login_proto.OpResultCode_OPRC_Error + } else { + if platform.RegisterVerifyCodeSwitch { + pack.VerifyType = proto.Int32(common.CodeTypeNo) + } else { + pack.VerifyType = proto.Int32(platform.VerifyCodeType) + } + pack.OpRetCode = login_proto.OpResultCode_OPRC_Sucess + } + common.SendToGate(sid, int(login_proto.LoginPacketID_PACKET_SC_REGISTERVERIFYTYPE), pack, s) + } + return nil +} + +// 获取三方游戏配置 +type CSGetThrGameCfgPacketFactory struct { +} + +type CSGetThrGameCfgHandler struct { +} + +func (this *CSGetThrGameCfgPacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSGetThrGameCfg{} + return pack +} +func (this *CSGetThrGameCfgHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetThrGameCfgHandler Process recv ", data) + if msg, ok := data.(*login_proto.CSGetThrGameCfg); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetThrGameCfgHandler p == nil") + return nil + } + pack := &login_proto.SCGetThrGameCfg{} + plf := msg.GetPlatform() + //加载配置 + gps := PlatformMgrSingleton.GetGameFrees(plf) + for _, v := range gps { + if v.Status { + if v.DbGameFree.GetGameRule() == 0 { + pack.ThrGameCfg = append(pack.ThrGameCfg, &login_proto.LoginThrGameConfig{ + LogicId: proto.Int32(v.DbGameFree.Id), + LimitCoin: proto.Int64(v.DbGameFree.GetLimitCoin()), + }) + } + } + } + p.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_GETTHRGAMECFG), pack) + } + return nil +} + +func init() { + //登录 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_LOGIN), &CSLoginHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_LOGIN), &CSLoginPacketFactory{}) + //客服地址 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_CUSTOMSERVICE), &CSCustomServiceHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_CUSTOMSERVICE), &CSCustomServicePacketFactory{}) + //平台配置信息 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_PLATFORMCFG), &CSPlatFormHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_PLATFORMCFG), &CSPlatFormPacketFactory{}) + ////公告信息 + //common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_BULLETIONINFO), &CSBulletionInfoHandler{}) + //netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_BULLETIONINFO), &CSBulletionInfoPacketFactory{}) + ////招商信息 + //common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_CUSTOMERINFOLIST), &CSCustomerInfoListHandler{}) + //netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_CUSTOMERINFOLIST), &CSCustomerInfoListPacketFactory{}) + + // 获取验证码配置 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_VERIFYTYPE), &CSVerifyTypeHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_VERIFYTYPE), &CSVerifyTypePacketFactory{}) + + // 获取登录验证码类型 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_REGISTERVERIFYTYPE), &CSRegisterVerifyTypeHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_REGISTERVERIFYTYPE), &CSRegisterVerifyTypePacketFactory{}) + + //获取三方游戏配置 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_GETTHRGAMECFG), &CSGetThrGameCfgHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_GETTHRGAMECFG), &CSGetThrGameCfgPacketFactory{}) +} diff --git a/worldsrv/action_logout.go b/worldsrv/action_logout.go new file mode 100644 index 0000000..82b3851 --- /dev/null +++ b/worldsrv/action_logout.go @@ -0,0 +1,80 @@ +package main + +import ( + "mongo.games.com/game/common" + login_proto "mongo.games.com/game/protocol/login" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +func SessionLogout(sid int64, drop bool) bool { + ls := LoginStateMgrSington.GetLoginStateBySid(sid) + if ls == nil { + logger.Logger.Trace("SessionLogout ls == nil") + return false + } + + ls.state = LoginStateLogout + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + p.ThirdGameLogout() + if drop { + p.DropLine() + } else { + p.Logout() + } + } else { + logger.Logger.Trace("SessionLogout p == nil") + } + + LoginStateMgrSington.Logout(ls) + return true +} + +type CSLogoutPacketFactory struct { +} + +type CSLogoutHandler struct { +} + +func (this *CSLogoutPacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSLogout{} + return pack +} + +func (this *CSLogoutHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + + logger.Logger.Trace("CSLogoutHandler Process recv ", data) + SessionLogout(sid, false) + return nil +} + +type SSDisconnectPacketFactory struct { +} + +func (this *SSDisconnectPacketFactory) CreatePacket() interface{} { + pack := &login_proto.SSDisconnect{} + return pack +} + +type SSDisconnectHandler struct { +} + +func (this *SSDisconnectHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + logger.Logger.Trace("SSDisconnectHandler Process recv ", data) + if ssd, ok := data.(*login_proto.SSDisconnect); ok { + sid := ssd.GetSessionId() + if sid != 0 { + SessionLogout(sid, true) + } + } + return nil +} +func init() { + // 玩家主动登出 + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_LOGOUT), &CSLogoutHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_LOGOUT), &CSLogoutPacketFactory{}) + // 玩家掉线 + netlib.RegisterHandler(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), &SSDisconnectHandler{}) + netlib.RegisterFactory(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), &SSDisconnectPacketFactory{}) +} diff --git a/worldsrv/action_message.go b/worldsrv/action_message.go new file mode 100644 index 0000000..8cccbad --- /dev/null +++ b/worldsrv/action_message.go @@ -0,0 +1,120 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/protocol/message" +) + +// 读取邮件 +type CSReadMessagePacketFactory struct { +} +type CSReadMessageHandler struct { +} + +func (this *CSReadMessagePacketFactory) CreatePacket() interface{} { + pack := &message.CSMessageRead{} + return pack +} + +func (this *CSReadMessageHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSReadMessageHandler Process recv ", data) + if csMessageRead, ok := data.(*message.CSMessageRead); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSReadMessageHandler p == nil") + return nil + } + + p.ReadMessage(csMessageRead.GetId()) + } + return nil +} + +// 删除邮件 +type CSDelMessagePacketFactory struct { +} +type CSDelMessageHandler struct { +} + +func (this *CSDelMessagePacketFactory) CreatePacket() interface{} { + pack := &message.CSMessageDel{} + return pack +} + +func (this *CSDelMessageHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSDelMessageHandler Process recv ", data) + if csMessageDel, ok := data.(*message.CSMessageDel); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSDelMessageHandler p == nil") + return nil + } + + p.DelMessage(csMessageDel.GetId(), 1) + } + return nil +} + +// 提取邮件附件 +type CSGetMessageAttachPacketFactory struct { +} +type CSGetMessageAttachHandler struct { +} + +func (this *CSGetMessageAttachPacketFactory) CreatePacket() interface{} { + pack := &message.CSGetMessageAttach{} + return pack +} + +func (this *CSGetMessageAttachHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetMessageAttachHandler Process recv ", data) + if csGetMessageAttach, ok := data.(*message.CSGetMessageAttach); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetMessageAttachHandler p == nil") + return nil + } + + p.GetMessageAttach(csGetMessageAttach.GetId()) + } + return nil +} + +// 查看邮件 +type SCMessageListPacketFactory struct { +} + +type SCMessageListHandler struct { +} + +func (this *SCMessageListPacketFactory) CreatePacket() interface{} { + pack := &message.CSMessageList{} + return pack +} + +func (this *SCMessageListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("(this *SCMessageListHandler) Process [%v].", s.GetSessionConfig().Id) + if msg, ok := data.(*message.CSMessageList); ok { + p := PlayerMgrSington.GetPlayer(sid) + p.SendMessage(msg.GetShowId()) + } + return nil +} + +func init() { + common.RegisterHandler(int(message.MSGPacketID_PACKET_CS_MESSAGEREAD), &CSReadMessageHandler{}) + netlib.RegisterFactory(int(message.MSGPacketID_PACKET_CS_MESSAGEREAD), &CSReadMessagePacketFactory{}) + + common.RegisterHandler(int(message.MSGPacketID_PACKET_CS_MESSAGEDEL), &CSDelMessageHandler{}) + netlib.RegisterFactory(int(message.MSGPacketID_PACKET_CS_MESSAGEDEL), &CSDelMessagePacketFactory{}) + + common.RegisterHandler(int(message.MSGPacketID_PACKET_CS_GETMESSAGEATTACH), &CSGetMessageAttachHandler{}) + netlib.RegisterFactory(int(message.MSGPacketID_PACKET_CS_GETMESSAGEATTACH), &CSGetMessageAttachPacketFactory{}) + + // 获取邮件列表 + common.RegisterHandler(int(message.MSGPacketID_PACKET_CS_MESSAGELIST), &SCMessageListHandler{}) + netlib.RegisterFactory(int(message.MSGPacketID_PACKET_CS_MESSAGELIST), &SCMessageListPacketFactory{}) +} diff --git a/worldsrv/action_minigame.go b/worldsrv/action_minigame.go new file mode 100644 index 0000000..43dc094 --- /dev/null +++ b/worldsrv/action_minigame.go @@ -0,0 +1,144 @@ +package main + +// +//import ( +// "mongo.games.com/game/common" +// "mongo.games.com/game/proto" +// "mongo.games.com/game/protocol/mngame" +// "mongo.games.com/game/protocol/server" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +//) +// +//type CSMNGameEnterPacketFactory struct { +//} +//type CSMNGameEnterHandler struct { +//} +// +//func (this *CSMNGameEnterPacketFactory) CreatePacket() interface{} { +// pack := &mngame.CSMNGameEnter{} +// return pack +//} +// +//func (this *CSMNGameEnterHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSMNGameEnterHandler Process recv ", data) +// if msg, ok := data.(*mngame.CSMNGameEnter); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// code := MiniGameMgrSington.PlayerEnter(p, msg.GetId()) +// pack := &mngame.SCMNGameEnter{ +// Id: msg.GetId(), +// OpRetCode: code, +// } +// proto.SetDefaults(pack) +// logger.Logger.Tracef("CSMNGameEnterHandler Process recv %v ", pack) +// p.SendToClient(int(mngame.MNGamePacketID_PACKET_SC_MNGAME_ENTER), pack) +// } +// } +// +// return nil +//} +// +//type CSMNGameLeavePacketFactory struct { +//} +//type CSMNGameLeaveHandler struct { +//} +// +//func (this *CSMNGameLeavePacketFactory) CreatePacket() interface{} { +// pack := &mngame.CSMNGameLeave{} +// return pack +//} +// +//func (this *CSMNGameLeaveHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSMNGameLeaveHandler Process recv ", data) +// if msg, ok := data.(*mngame.CSMNGameLeave); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// code := MiniGameMgrSington.PlayerLeave(p, msg.GetId()) +// pack := &mngame.SCMNGameLeave{ +// Id: msg.GetId(), +// OpRetCode: code, +// Reason: int32(common.PlayerLeaveReason_Normal), +// } +// proto.SetDefaults(pack) +// p.SendToClient(int(mngame.MNGamePacketID_PACKET_SC_MNGAME_LEAVE), pack) +// } +// } +// +// return nil +//} +// +//type CSMNGameDispatcherPacketFactory struct { +//} +//type CSMNGameDispatcherHandler struct { +//} +// +//func (this *CSMNGameDispatcherPacketFactory) CreatePacket() interface{} { +// pack := &mngame.CSMNGameDispatcher{} +// return pack +//} +// +//func (this *CSMNGameDispatcherHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSMNGameDispatcherHandler Process recv ", data) +// if msg, ok := data.(*mngame.CSMNGameDispatcher); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p != nil { +// MiniGameMgrSington.PlayerMsgDispatcher(p, msg) +// } +// } +// +// return nil +//} +// +//type GWPlayerLeaveMiniGamePacketFactory struct { +//} +//type GWPlayerLeaveMiniGameHandler struct { +//} +// +//func (this *GWPlayerLeaveMiniGamePacketFactory) CreatePacket() interface{} { +// pack := &server.GWPlayerLeaveMiniGame{} +// return pack +//} +// +//func (this *GWPlayerLeaveMiniGameHandler) Process(s *netlib.Session, packetid int, data interface{}) error { +// logger.Logger.Trace("GWPlayerLeaveMiniGameHandler Process recv ", data) +// if msg, ok := data.(*server.GWPlayerLeaveMiniGame); ok { +// p := PlayerMgrSington.GetPlayerBySnId(msg.SnId) +// if p != nil { +// plt := p.GetPlatform() +// s := MiniGameMgrSington.GetScene(plt, msg.GetGameFreeId()) +// if s != nil { +// delete(s.players, p.SnId) +// } +// +// gamings, ok := MiniGameMgrSington.playerGaming[p.SnId] +// if ok { +// delete(gamings, msg.GetGameFreeId()) +// } +// +// pack := &mngame.SCMNGameLeave{ +// Id: msg.GetGameFreeId(), +// Reason: msg.GetReason(), +// OpRetCode: mngame.MNGameOpResultCode_MNGAME_OPRC_Sucess, +// } +// proto.SetDefaults(pack) +// p.SendToClient(int(mngame.MNGamePacketID_PACKET_SC_MNGAME_LEAVE), pack) +// } +// } +// +// return nil +//} +// +//func init() { +// common.RegisterHandler(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_ENTER), &CSMNGameEnterHandler{}) +// netlib.RegisterFactory(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_ENTER), &CSMNGameEnterPacketFactory{}) +// +// common.RegisterHandler(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_LEAVE), &CSMNGameLeaveHandler{}) +// netlib.RegisterFactory(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_LEAVE), &CSMNGameLeavePacketFactory{}) +// +// common.RegisterHandler(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_DISPATCHER), &CSMNGameDispatcherHandler{}) +// netlib.RegisterFactory(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_DISPATCHER), &CSMNGameDispatcherPacketFactory{}) +// +// netlib.RegisterHandler(int(server.SSPacketID_PACKET_GW_PLAYERLEAVE_MINIGAME), &GWPlayerLeaveMiniGameHandler{}) +// netlib.RegisterFactory(int(server.SSPacketID_PACKET_GW_PLAYERLEAVE_MINIGAME), &GWPlayerLeaveMiniGamePacketFactory{}) +//} diff --git a/worldsrv/action_pets.go b/worldsrv/action_pets.go new file mode 100644 index 0000000..e5c3cf7 --- /dev/null +++ b/worldsrv/action_pets.go @@ -0,0 +1,348 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/protocol/pets" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSRoleInfoPacketFactory struct { +} + +type CSRoleInfoHandler struct { +} + +func (this *CSRoleInfoPacketFactory) CreatePacket() interface{} { + pack := &pets.CSRoleInfo{} + return pack +} + +func (this *CSRoleInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRoleInfoHandler Process recv ", data) + if _, ok := data.(*pets.CSRoleInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSRoleInfoHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + roleInfos := PetMgrSington.GetRoleInfos(p) + pack := &pets.SCRoleInfo{ + Infos: roleInfos, + } + logger.Logger.Trace("SCRoleInfo:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_ROLE_INFO), pack) + } + + return nil +} + +type CSPetInfoPacketFactory struct { +} + +type CSPetInfoHandler struct { +} + +func (this *CSPetInfoPacketFactory) CreatePacket() interface{} { + pack := &pets.CSPetInfo{} + return pack +} + +func (this *CSPetInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPetInfoHandler Process recv ", data) + if _, ok := data.(*pets.CSPetInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPetInfoHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + + petInfos := PetMgrSington.GetPetInfos(p) + pack := &pets.SCPetInfo{ + Infos: petInfos, + } + logger.Logger.Trace("SCPetInfo:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_PET_INFO), pack) + } + return nil +} + +type CSRisingStarPacketFactory struct { +} + +type CSRisingStarHandler struct { +} + +func (this *CSRisingStarPacketFactory) CreatePacket() interface{} { + pack := &pets.CSRisingStar{} + return pack +} + +func (this *CSRisingStarHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRisingStarHandler Process recv ", data) + if msg, ok := data.(*pets.CSRisingStar); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSRisingStarHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + SendInfoRole := func(retCode pets.OpResultCode, roleInfo *pets.RoleInfo) { + pack := &pets.SCRoleRisingStar{ + RetCode: retCode, + RoleInfo: roleInfo, + } + logger.Logger.Trace("SCPetRisingStar:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_ROLE_RISINGSTAR), pack) + } + SendInfoPet := func(retCode pets.OpResultCode, petInfo *pets.PetInfo) { + pack := &pets.SCPetRisingStar{ + RetCode: retCode, + PetInfo: petInfo, + } + logger.Logger.Trace("SCPetRisingStar:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_PET_RISINGSTAR), pack) + } + if msg.RisingModId == 0 { + logger.Logger.Warn("CSRisingStarHandler UseModId:", msg.RisingModId) + SendInfoRole(pets.OpResultCode_OPRC_Error, PetMgrSington.GetRoleInfo(p, msg.RisingModId)) + return nil + } + if msg.RisingType == 0 { + roleInfo := PetMgrSington.GetIntroductionByModId(msg.RisingModId) + if roleInfo == nil { + SendInfoRole(pets.OpResultCode_OPRC_Error, PetMgrSington.GetRoleInfo(p, msg.RisingModId)) + return nil + } + if roleInfo.MaxLevel == p.Roles.ModUnlock[msg.RisingModId] { + logger.Logger.Trace("人物已经达到最大等级") + SendInfoRole(pets.OpResultCode_OPRC_Error, PetMgrSington.GetRoleInfo(p, msg.RisingModId)) + return nil + } + role := PetMgrSington.GetRoleInfo(p, msg.RisingModId) + if role != nil { + if role.HaveAmount < role.Amount { + logger.Logger.Trace("人物碎片道具数量不够", role.HaveAmount, role.Amount) + return nil + } + } + //背包数据处理 + item := BagMgrSingleton.GetItem(p.SnId, role.Fragment) + if item != nil { + // item.ItemNum -= role.Amount + role.HaveAmount -= role.Amount + BagMgrSingleton.SalePlayerItem(p, item, int64(role.Amount)) + //人物模型状态处理 + p.Roles.ModUnlock[msg.RisingModId]++ + FriendMgrSington.UpdateInfo(p.Platform, p.SnId) + p.dirty = true + //人物 + SendInfoRole(pets.OpResultCode_OPRC_Sucess, PetMgrSington.GetRoleInfo(p, msg.RisingModId)) + remark := role.Name + "升星" + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(role.Amount), remark) + + // BagMgrSingleton.SyncBagData(p, item.ItemId) + } + } else if msg.RisingType == 1 { + petInfo := PetMgrSington.GetIntroductionByModId(msg.RisingModId) + if petInfo == nil { + SendInfoPet(pets.OpResultCode_OPRC_Error, PetMgrSington.GetPetInfo(p, msg.RisingModId)) + return nil + } + if petInfo.MaxLevel == p.Pets.ModUnlock[msg.RisingModId] { + logger.Logger.Trace("宠物已经达到最大等级") + SendInfoPet(pets.OpResultCode_OPRC_Error, PetMgrSington.GetPetInfo(p, msg.RisingModId)) + return nil + } + + pet := PetMgrSington.GetPetInfo(p, msg.RisingModId) + if pet != nil { + if pet.HaveAmount < pet.Amount { + logger.Logger.Trace("宠物碎片道具数量不够", pet.HaveAmount, pet.Amount) + return nil + } + } + //背包数据处理 + item := BagMgrSingleton.GetItem(p.SnId, pet.Fragment) + if item != nil { + // item.ItemNum -= pet.Amount + pet.HaveAmount -= pet.Amount + + BagMgrSingleton.SalePlayerItem(p, item, int64(pet.Amount)) + + p.Pets.ModUnlock[msg.RisingModId]++ + FriendMgrSington.UpdateInfo(p.Platform, p.SnId) + p.dirty = true + //宠物 + SendInfoPet(pets.OpResultCode_OPRC_Sucess, PetMgrSington.GetPetInfo(p, msg.RisingModId)) + remark := pet.Name + "升星" + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(pet.Amount), remark) + + //BagMgrSingleton.SyncBagData(p, item.ItemId) + } + } + + } + return nil +} + +type CSRolePetUseOpPacketFactory struct { +} + +type CSRolePetUseOpHandler struct { +} + +func (this *CSRolePetUseOpPacketFactory) CreatePacket() interface{} { + pack := &pets.CSRolePetUseOp{} + return pack +} + +func (this *CSRolePetUseOpHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRolePetUseOpHandler Process recv ", data) + if msg, ok := data.(*pets.CSRolePetUseOp); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSRolePetUseOpHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + if msg.UseModId == 0 { + logger.Logger.Warn("CSRolePetUseOpHandler UseModId:", msg.UseModId) + return nil + } + if msg.UseModType == 0 { + if p.Roles.ModId == msg.UseModId { + logger.Logger.Trace("人物使用中 不能直接取消人物使用") + return nil + } + p.Roles.ModId = msg.UseModId + p.dirty = true + logger.Logger.Trace("使用人物:", msg.UseModId) + } else { + if p.Pets.ModId == msg.UseModId { + p.Pets.ModId = 0 + logger.Logger.Trace("取消宠物跟随:", msg.UseModId) + p.dirty = true + } else { + logger.Logger.Trace("设置宠物跟随:", msg.UseModId) + p.Pets.ModId = msg.UseModId + p.dirty = true + } + } + pack := &pets.SCRolePetUseOp{ + RetCode: pets.OpResultCode_OPRC_Sucess, + UseModType: msg.UseModType, + UseModId: msg.UseModId, + } + logger.Logger.Trace("SCRolePetUseOp:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_ROLEPETUSEOP), pack) + } + return nil +} + +type CSRolePetUnlockPacketFactory struct { +} + +type CSRolePetUnlockHandler struct { +} + +func (this *CSRolePetUnlockPacketFactory) CreatePacket() interface{} { + pack := &pets.CSRolePetUnlock{} + return pack +} + +func (this *CSRolePetUnlockHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRolePetUnlockHandler Process recv ", data) + if msg, ok := data.(*pets.CSRolePetUnlock); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSRolePetUnlockHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + SendMsg := func(retCode pets.OpResultCode, roleInfo *pets.RoleInfo, petInfo *pets.PetInfo) { + pack := &pets.SCRolePetUnlock{ + RetCode: retCode, + UseModType: msg.UseModType, + UseModId: msg.UseModId, + RoleInfo: roleInfo, + PetInfo: petInfo, + } + logger.Logger.Trace("SCRolePetUnlock:", pack) + p.SendToClient(int(pets.PetsPacketID_PACKET_SC_ROLEPETUNLOCK), pack) + } + if msg.UseModId == 0 { + logger.Logger.Warn("CSRolePetUnlockHandler UseModId:", msg.UseModId) + SendMsg(pets.OpResultCode_OPRC_Error, nil, nil) + return nil + } + if msg.UseModType == 0 { + if _, ok1 := p.Roles.ModUnlock[msg.UseModId]; !ok1 { + roleInfo := PetMgrSington.GetRoleInfo(p, msg.UseModId) + if roleInfo != nil { + item := BagMgrSingleton.GetItem(p.SnId, roleInfo.Fragment) + if item != nil && item.ItemNum >= int64(roleInfo.Amount) { + item.ItemNum -= int64(roleInfo.Amount) + p.Roles.ModUnlock[msg.UseModId] = 1 + FriendMgrSington.UpdateInfo(p.Platform, p.SnId) + p.dirty = true + logger.Logger.Trace("解锁人物", msg.UseModId) + SendMsg(pets.OpResultCode_OPRC_Sucess, PetMgrSington.GetRoleInfo(p, msg.UseModId), nil) + remark := roleInfo.Name + "解锁" + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(roleInfo.Amount), remark) + return nil + } + } + } + } else if msg.UseModType == 1 { + if _, ok1 := p.Pets.ModUnlock[msg.UseModId]; !ok1 { + petInfo := PetMgrSington.GetPetInfo(p, msg.UseModId) + if petInfo != nil { + item := BagMgrSingleton.GetItem(p.SnId, petInfo.Fragment) + if item != nil && item.ItemNum >= int64(petInfo.Amount) { + item.ItemNum -= int64(petInfo.Amount) + p.Pets.ModUnlock[msg.UseModId] = 1 + FriendMgrSington.UpdateInfo(p.Platform, p.SnId) + p.dirty = true + logger.Logger.Trace("解锁宠物", msg.UseModId) + SendMsg(pets.OpResultCode_OPRC_Sucess, nil, PetMgrSington.GetPetInfo(p, msg.UseModId)) + remark := petInfo.Name + "解锁" + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, int64(petInfo.Amount), remark) + return nil + } + } + } + } + SendMsg(pets.OpResultCode_OPRC_Error, nil, nil) + } + return nil +} +func init() { + common.RegisterHandler(int(pets.PetsPacketID_PACKET_CS_ROLE_INFO), &CSRoleInfoHandler{}) + netlib.RegisterFactory(int(pets.PetsPacketID_PACKET_CS_ROLE_INFO), &CSRoleInfoPacketFactory{}) + common.RegisterHandler(int(pets.PetsPacketID_PACKET_CS_PET_INFO), &CSPetInfoHandler{}) + netlib.RegisterFactory(int(pets.PetsPacketID_PACKET_CS_PET_INFO), &CSPetInfoPacketFactory{}) + common.RegisterHandler(int(pets.PetsPacketID_PACKET_CS_PET_RISINGSTAR), &CSRisingStarHandler{}) + netlib.RegisterFactory(int(pets.PetsPacketID_PACKET_CS_PET_RISINGSTAR), &CSRisingStarPacketFactory{}) + common.RegisterHandler(int(pets.PetsPacketID_PACKET_CS_ROLEPETUSEOP), &CSRolePetUseOpHandler{}) + netlib.RegisterFactory(int(pets.PetsPacketID_PACKET_CS_ROLEPETUSEOP), &CSRolePetUseOpPacketFactory{}) + common.RegisterHandler(int(pets.PetsPacketID_PACKET_CS_ROLEPETUNLOCK), &CSRolePetUnlockHandler{}) + netlib.RegisterFactory(int(pets.PetsPacketID_PACKET_CS_ROLEPETUNLOCK), &CSRolePetUnlockPacketFactory{}) +} diff --git a/worldsrv/action_phonelottery.go b/worldsrv/action_phonelottery.go new file mode 100644 index 0000000..bfeba67 --- /dev/null +++ b/worldsrv/action_phonelottery.go @@ -0,0 +1,207 @@ +package main + +import ( + "encoding/json" + "math/rand" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + player_proto "mongo.games.com/game/protocol/player" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +type CSPhoneLotteryInfoPacketFactory struct { +} + +type CSPhoneLotteryInfoHandler struct { +} + +func (this *CSPhoneLotteryInfoPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPhoneLotteryInfo{} + return pack +} + +// 获取抽奖信息 +func (this *CSPhoneLotteryInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + + if _, ok := data.(*player_proto.CSPhoneLotteryInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + logger.Logger.Trace("客户端请求抽奖信息!snid = ", p.SnId) + if p == nil { + logger.Logger.Warn("CSPhoneLotteryInfo p == nil") + return nil + } + if WelfareMgrSington.GetPhoneLotteryStatus(p.Platform) == WelfareClose { + return nil + } + pack := &player_proto.SCPhoneLotteryInfo{} + pack.PhoneScore = p.PhoneScore + pack.Count = p.LotteryCount + pool := srvdata.PBDB_PhoneLotteryMgr.Datas.GetArr() + for _, lottery := range pool { + if p.AppChannel == common.ChannelGooglePlay { + if lottery.Type == 1 { + continue + } + } else { + if lottery.Type == 2 { + continue + } + } + date := &player_proto.LotteryItem{ + Id: lottery.Id, + ItemId: lottery.Item_Id, + ItemNum: int64(lottery.Grade), + } + pack.Item = append(pack.Item, date) + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PhoneLotteryInfo), pack) + logger.Logger.Trace("返回抽奖信息:", pack.String()) + } + return nil +} + +// 请求抽奖 +type CSPhoneLotteryPacketFactory struct { +} + +type CSPhoneLotteryHandler struct { +} + +func (this *CSPhoneLotteryPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPhoneLottery{} + return pack +} +func (this *CSPhoneLotteryHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + if msg, ok := data.(*player_proto.CSPhoneLottery); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPhoneLottery p == nil") + return nil + } + if WelfareMgrSington.GetPhoneLotteryStatus(p.Platform) == WelfareClose { + return nil + } + countType := msg.GetLotteryType() + count := int32(0) + if countType == 1 { + count = 1 + } else { + count = 10 + } + logger.Logger.Tracef("玩家请求抽奖,snid =%d,count = %d ", p.SnId, count) + if p == nil { + logger.Logger.Warn("CSPhoneLottery p == nil") + return nil + } + if p.LotteryCount < count { + logger.Logger.Trace("剩余抽奖次数不足,无法抽奖 count = ", count) + return nil + } + //p.LotteryCount -= count + p.addLotteryCount(-count) + pool := srvdata.PBDB_PhoneLotteryMgr.Datas.GetArr() + pack := &player_proto.SCPhoneLottery{} + items := make([]*Item, 0) + for i := 1; i <= int(count); i++ { + //抽奖 + rate := 0 + weight := 0 + for _, lottery := range pool { + if p.AppChannel == common.ChannelGooglePlay { + if lottery.Type == 1 { + continue + } + } else { + if lottery.Type == 2 { + continue + } + } + if p.PhoneScore < int64(lottery.Odd) { + rate = 1 + weight += int(lottery.Oddrate1) + } else if p.PhoneScore >= int64(lottery.Odd) && p.PhoneScore < int64(lottery.Odd2) { + rate = 2 + weight += int(lottery.Oddrate2) + } else if p.PhoneScore >= int64(lottery.Odd2) && p.PhoneScore < int64(lottery.Odd3) { + rate = 3 + weight += int(lottery.Oddrate3) + } else if p.PhoneScore >= int64(lottery.Odd3) { + rate = 4 + weight += int(lottery.Oddrate4) + } + } + random := rand.Intn(weight) + 1 + num := 0 + logger.Logger.Tracef("玩家抽奖 当前权重区间 rate = %d,weight = %d ,随机到的值:%d", rate, weight, random) + for _, lottery := range pool { + if p.AppChannel == common.ChannelGooglePlay { + if lottery.Type == 1 { + continue + } + } else { + if lottery.Type == 2 { + continue + } + } + if rate == 1 { + if lottery.Oddrate1 == 0 { + continue + } + num += int(lottery.Oddrate1) + } else if rate == 2 { + if lottery.Oddrate2 == 0 { + continue + } + num += int(lottery.Oddrate2) + } else if rate == 3 { + if lottery.Oddrate3 == 0 { + continue + } + num += int(lottery.Oddrate3) + } else if rate == 4 { + if lottery.Oddrate4 == 0 { + continue + } + num += int(lottery.Oddrate4) + } + if random <= num { + itemArr := &player_proto.LotteryItem{} + itemArr.ItemId = lottery.Item_Id + itemArr.ItemNum = int64(lottery.Grade) + itemArr.Id = lottery.Id + items = append(items, &Item{ + ItemId: lottery.Item_Id, // 物品id + ItemNum: int64(lottery.Grade), // 数量 + }) + logger.Logger.Tracef("玩家抽奖获得物品 itemId = %d,itemNum = %d", lottery.Item_Id, lottery.Grade) + pack.Item = append(pack.Item, itemArr) + break + } + } + } + //增加到玩家背包 + BagMgrSingleton.AddJybBagInfo(p, items, 0, common.GainWay_PhoneScore, "system", "玩游戏积分") + pack.Count = p.LotteryCount + pack.PhoneScore = p.PhoneScore + logger.Logger.Tracef("获取玩家抽奖权重 score = %d,抽奖获得的物品:%v", p.PhoneScore, pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PhoneLottery), pack) + //抽奖统计 + jsonData, err := json.Marshal(items) + if err != nil { + return err + } + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(p.SnId, p.Platform, string(jsonData), 1, 0, 0, 0)) + } + return nil +} + +func init() { + // 抽奖信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_PhoneLotteryInfo), &CSPhoneLotteryInfoHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_PhoneLotteryInfo), &CSPhoneLotteryInfoPacketFactory{}) + // 抽奖 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_PhoneLottery), &CSPhoneLotteryHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_PhoneLottery), &CSPhoneLotteryPacketFactory{}) +} diff --git a/worldsrv/action_player.go b/worldsrv/action_player.go new file mode 100644 index 0000000..9fed6b2 --- /dev/null +++ b/worldsrv/action_player.go @@ -0,0 +1,3117 @@ +package main + +import ( + "crypto/md5" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/rand" + "net/url" + "regexp" + "strconv" + "time" + "unicode/utf8" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + gamehall_proto "mongo.games.com/game/protocol/gamehall" + player_proto "mongo.games.com/game/protocol/player" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/webapi" +) + +var reTelRule, _ = regexp.Compile(`^(1[3|4|5|6|7|8|9][0-9]\d{4,8})$`) + +// 获取邀请码奖励 +type CSInviteCodePlayerPacketFactory struct { +} +type CSInviteCodePlayerHandler struct { +} + +func (this *CSInviteCodePlayerPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerInviteCode{} + return pack +} + +func (this *CSInviteCodePlayerHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSInviteCodePlayerHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerInviteCode); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSInviteCodePlayerHandler p == nil") + return nil + } + inviteCode := msg.GetCode() + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + + var ret webapi.RequestBody + var err error + data := make(url.Values) + data["MemberId"] = []string{strconv.Itoa(int(p.SnId))} + data["Key"] = []string{inviteCode} + + ret, err = webapi.API_OP("/api/ExchangeCode/MemberExchangeCode", data) + logger.Logger.Trace("webapi返回数据: ", ret) + if err != nil { + return nil + } + if ret == nil { + return nil + } + + state, _ := ret.GetInt("State") + if state == 0 { + return nil + } + + ret_data, _ := ret.GetRequestBody("Data") + if ret_data == nil { + return nil + } + coin, _ := ret_data.GetInt64("Price") + if coin <= 0 { + return nil + } + + //发送邮件 + var otherParams []int32 + newMsg := model.NewMessage("", p.SnId, "", p.SnId, model.MSGTYPE_INVITECODE, p.Name, inviteCode, coin, 0, + model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", otherParams, p.Platform, model.HallAll) + err = model.InsertMessage(p.Platform, newMsg) + if err == nil { + + //执行成功后通知后端 + data := make(url.Values) + data["Snid"] = []string{strconv.Itoa(int(p.SnId))} + data["Code"] = []string{inviteCode} + data["State"] = []string{strconv.Itoa(1)} + _, err_res := webapi.API_OP("/api/ExchangeCode/MemberExchangeCodeSuccess", data) + if err_res != nil { + logger.Logger.Error("/api/ExchangeCode/MemberExchangeCodeSuccess err ", err_res) + } + + return newMsg + } else { + //执行失败后通知后端 + data := make(url.Values) + data["Snid"] = []string{strconv.Itoa(int(p.SnId))} + data["Code"] = []string{inviteCode} + data["State"] = []string{strconv.Itoa(0)} + _, err_res := webapi.API_OP("/api/ExchangeCode/MemberExchangeCodeSuccess", data) + if err_res != nil { + logger.Logger.Error("/api/ExchangeCode/MemberExchangeCodeSuccess err ", err_res) + } + + return nil + } + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + retCode := player_proto.OpResultCode_OPRC_Sucess + if newMsg, OK := data.(*model.Message); OK { + p.AddMessage(newMsg) + } else { + retCode = player_proto.OpResultCode_OPRC_Error + } + + //错误提示 + pack := &player_proto.SCPlayerInviteCode{ + OpRetCode: retCode, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_INVITECODE), pack) + + }), "InviteCode").Start() + + } + return nil +} + +// 修改昵称 +type CSPlayerChangeNickPacketFactory struct { +} +type CSPlayerChangeNickHandler struct { +} + +func (this *CSPlayerChangeNickPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSChangeNick{} + return pack +} + +func (this *CSPlayerChangeNickHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + //logger.Logger.Trace("CSPlayerChangeNickHandler Process recv ", data) + //if msg, ok := data.(*player_proto.CSChangeNick); ok { + // //logger.Logger.Trace("修改昵称,获得IP",s.RemoteAddr()) + // p := PlayerMgrSington.GetPlayer(sid) + // if p == nil { + // logger.Logger.Warn("CSPlayerChangeNickHandler p == nil") + // return nil + // } + // + // sendPack := func(code *player_proto.OpResultCode) { + // pack := &player_proto.SCChangeNick{ + // OpRetCode: code, + // Nick: msg.Nick, + // } + // proto.SetDefaults(pack) + // p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_CHANGENICK), pack) + // } + // + // nick := msg.GetNick() + // //昵称不能为空 + // if nick == "" { + // sendPack(player_proto.OpResultCode_OPRC_NickIsNull) + // return nil + // } + // + // if !utf8.ValidString(nick) { + // sendPack(player_proto.OpResultCode_OPRC_NickIsIllegal) + // return nil + // } + // //昵称已占用,昵称允许重复 + // //if model.GetPlayerNickIsExist(nick) { + // // sendPack(player_proto.OpResultCode_OPRC_NickIsExist) + // // return nil + // //} + // + // //昵称超出长度限制 + // if len(nick) > 21 { + // sendPack(player_proto.OpResultCode_OPRC_NickIsTooLen) + // return nil + // } + // + // if p.IsStopRename == 1 { + // sendPack(player_proto.OpResultCode_OPRC_NickIsCantRename) + // return nil + // } + // + // //TODO 校验昵称的合法性 + // //var agentName, agentKey, thirdPlf = model.GetDgConfigByPlatform(p.Platform) + // var agentDgName, agentDgKey, thirdDgPlf = model.OnlyGetDgConfigByPlatform(p.Platform) + // var agentHboName, agentHboKey, thirdHboPlf = model.OnlyGetHboConfigByPlatform(p.Platform) + // //为了兼容以前的数据 + // var hboGame, hboPass, dgGame, dgPass string + // if len(p.StoreHboGame) > 0 { + // hboGame = p.StoreHboGame + // hboPass = p.StoreHboPass + // } + // if len(p.StoreDgGame) > 0 { + // dgGame = p.StoreDgGame + // dgPass = p.StoreDgPass + // } + // if len(p.DgGame) > 0 && strings.Contains(p.DgGame, "dg") { + // dgGame = p.DgGame + // dgPass = p.DgPass + // } + // if len(p.DgGame) > 0 && strings.Contains(p.DgGame, "hbo") { + // hboGame = p.DgGame + // hboPass = p.DgPass + // } + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // code := model.UpdatePlayerNick(p.AccountId, msg.GetNick()) + // if code == 0 { + // + // if len(hboGame) > 0 && len(agentHboName) > 0 { + // err, codeid, _, _ := webapi.API_DgUpdate(thirdHboPlf, common.GetAppId(), + // hboGame, hboPass, nick, agentHboName, agentHboKey) + // if err != nil { + // logger.Logger.Error("Update hbo name error:", err) + // } + // if codeid != 0 { + // logger.Logger.Error("Update hbo code:", codeid) + // } + // } + // + // if len(dgGame) > 0 && len(agentDgName) > 0 { + // err, codeid, _, _ := webapi.API_DgUpdate(thirdDgPlf, common.GetAppId(), + // dgGame, dgPass, nick, agentDgName, agentDgKey) + // if err != nil { + // logger.Logger.Error("Update dg name error:", err) + // } + // if codeid != 0 { + // logger.Logger.Error("Update dg code:", codeid) + // } + // } + // + // } + // return code + // }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { + // if i, ok := data.(int); ok { + // if i == 0 { + // PlayerSubjectSign.UpdateName(p.SnId, nick) + // sendPack(player_proto.OpResultCode_OPRC_Sucess) + // } else { + // sendPack(player_proto.OpResultCode_OPRC_Error) + // } + // } + // }), "UpdatePlayerNick").Start() + //} + return nil +} + +// 修改头像 +type CSPlayerChangeIconPacketFactory struct { +} +type CSPlayerChangeIconHandler struct { +} + +func (this *CSPlayerChangeIconPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerChangeIcon{} + return pack +} + +func (this *CSPlayerChangeIconHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerChangeIconHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerChangeIcon); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerChangeIconHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCPlayerChangeIcon{ + OpRetCode: code, + Icon: msg.Icon, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_CHANGEICON), pack) + } + + if int32(time.Now().Sub(p.changeIconTime)/time.Second) > 30 { + PlayerSubjectSign.UpdateHead(p.SnId, msg.GetIcon()) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Frequently) + } + } + return nil +} + +// 修改头像框 +type CSPlayerChangeHeadOutLinePacketFactory struct { +} +type CSPlayerChangeHeadOutLineHandler struct { +} + +func (this *CSPlayerChangeHeadOutLinePacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerChangeHeadOutLine{} + return pack +} + +func (this *CSPlayerChangeHeadOutLineHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerChangeHeadOutLineHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerChangeHeadOutLine); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerChangeHeadOutLineHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCPlayerChangeHeadOutLine{ + OpRetCode: code, + HeadOutLine: msg.HeadOutLine, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_HEADOUTLINE), pack) + } + + headOutline := msg.GetHeadOutLine() + can := false + for vip := int32(0); vip <= p.VIP; vip++ { + dbVip := VipMgrSington.GetVIPLevelCfg(p.Platform, vip) + if dbVip != nil { + if common.InSliceInt32(dbVip.GetRewardOutlineID(), headOutline) { + can = true + break + } + } + } + if !can { + sendPack(player_proto.OpResultCode_OPRC_IconError) + return nil + } + if msg.GetHeadOutLine() != 0 { + PlayerSubjectSign.UpdateHeadOutline(p.SnId, headOutline) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Frequently) + } + } + return nil +} + +// 修改性别 +type CSPlayerChangeSexPacketFactory struct { +} +type CSPlayerChangeSexHandler struct { +} + +func (this *CSPlayerChangeSexPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerChangeSex{} + return pack +} + +func (this *CSPlayerChangeSexHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerChangeSexHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerChangeSex); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerChangeSexHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCPlayerChangeSex{ + OpRetCode: code, + Sex: msg.Sex, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_CHANGESEX), pack) + } + if msg.GetSex() < 0 || msg.GetSex() > 2 { + sendPack(player_proto.OpResultCode_OPRC_SexError) + return nil + } + + p.Sex = msg.GetSex() + p.dirty = true + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } + return nil +} + +//// 帐号绑定,帐号密码找回,保险箱密码找回 +//type CSUpgradeAccountPacketFactory struct { +//} +//type CSUpgradeAccountHandler struct { +//} +// +//func (this *CSUpgradeAccountPacketFactory) CreatePacket() interface{} { +// pack := &player_proto.CSUpgradeAccount{} +// return pack +//} +// +//func (this *CSUpgradeAccountHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSUpgradeAccountHandler Process recv ", data) +// if msg, ok := data.(*player_proto.CSUpgradeAccount); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// +// sendPack := func(code player_proto.OpResultCode) { +// pack := &player_proto.SCUpgradeAccount{ +// OpRetCode: code, +// Tel: msg.Tel, +// ChangeType: msg.ChangeType, +// } +// proto.SetDefaults(pack) +// //p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_UPGRADEACCOUNT), pack) +// common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_UPGRADEACCOUNT), pack, s) +// } +// +// //验证手机号 +// tel := msg.GetTel() +// if !reTelRule.MatchString(tel) { +// sendPack(player_proto.OpResultCode_OPRC_TelError) +// return nil +// } +// platformId, _, _, _, tagkey := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) +// platformID := strconv.Itoa(int(platformId)) +// platform := PlatformMgrSingleton.GetPlatform(platformID) +// if msg.GetChangeType() == 0 { +// if !platform.RegisterVerifyCodeSwitch { +// code := CacheMemory.Get(fmt.Sprintf("%v_%v", tel, tagkey)) +// if code == nil { +// sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) +// return nil +// } +// if verifyCode, ok := code.(string); ok && verifyCode != "" { +// if verifyCode != msg.GetVerificationCode() { +// sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) +// return nil +// } +// } +// } +// } else { +// if platform.VerifyCodeType != common.CodeTypeNo { +// code := CacheMemory.Get(fmt.Sprintf("%v_%v", tel, tagkey)) +// if code == nil { +// sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) +// return nil +// } +// if verifyCode, ok := code.(string); ok && verifyCode != "" { +// if verifyCode != msg.GetVerificationCode() { +// sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) +// return nil +// } +// } +// } +// } +// +// //验证密码 +// if msg.GetPassword() == "" || len(msg.GetPassword()) < 32 { +// sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) +// return nil +// } +// +// var upgradeaccountcount int32 +// //升级账号 +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// //绑定手机 +// if msg.GetChangeType() == 0 { +// if p != nil { +// //手机号是否已经注册 +// if model.PlayerTelIsExist(msg.GetTel(), platformID, tagkey) { +// sendPack(player_proto.OpResultCode_OPRC_TelIsExist) +// return errors.New("TelIsExist is Error") +// } +// //帐号绑定 +// //更新Account数据 +// err := model.UpgradeAccount(p.AccountId, msg.GetTel(), msg.GetPassword(), platformID, tagkey) +// if err != nil { +// return err +// } +// +// //当前IP和日期已经赠送的金币总额 +// upgradeaccountcount = model.GetUpgradeAccountCoinLogsByIPAndDate(p.RegIp, p.Platform, time.Now()) +// //更新玩家信息表 +// return model.UpdatePlayerTel(p.Platform, p.SnId, msg.GetTel()) +// } else { +// //账号密码找回 +// return model.GetBackPlayerPassword(msg.GetTel(), msg.GetPassword(), platformID, tagkey) +// } +// } +// //设置保险箱密码 +// /*if msg.GetChangeType() == 1 { +// raw := fmt.Sprintf("111111%v", common.GetAppId()) +// h := md5.New() +// io.WriteString(h, raw) +// pwd := hex.EncodeToString(h.Sum(nil)) +// return model.UpdateSafeBoxPassword(p.AccountId, pwd, msg.GetPassword()) +// }*/ +// //帐号密码找回 +// if msg.GetChangeType() == 2 { +// //账号密码找回 +// return model.GetBackPlayerPassword(msg.GetTel(), msg.GetPassword(), platformID, tagkey) +// } +// //保险箱密码找回 +// if msg.GetChangeType() == 3 { +// if p != nil { +// p.SafeBoxPassword = msg.GetPassword() +// } +// return model.GetBackSafeBoxPassword(msg.GetTel(), msg.GetPassword(), platformID, tagkey) +// } +// return fmt.Errorf("GetChangeType is Error:%v", msg.GetChangeType()) +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// if data == nil { +// if msg.GetChangeType() == 0 || msg.GetChangeType() == 2 { +// als := LoginStateMgrSington.GetLoginStateByTelAndPlatform(msg.GetTel(), platformID) +// if als != nil && als.acc != nil { +// als.acc.Tel = msg.GetTel() +// als.acc.TelPassWord = msg.GetPassword() +// } +// } +// if p != nil { +// oldTel := p.Tel +// p.Tel = msg.GetTel() +// if msg.GetChangeType() == 0 { +// if !p.IsRob { +// //用户绑定事件 +// if oldTel == "" { //首次绑定账号 +// p.UpgradeTime = time.Now() +// p.ReportBindPhoneEvent() +// //p.ReportWebEvent(model.WEBEVENT_UPGRADEACC, 0, 0, 1) +// +// //升级账号赠送 +// upgradeAccountGiveCoin := p.GetUpdateAccPrize() +// if upgradeAccountGiveCoin > 0 && !p.layered[common.ActId_UpgradeAccount] { +// if model.GameParamData.UpgradeAccountGiveCoinLimit != 0 && upgradeaccountcount < model.GameParamData.UpgradeAccountGiveCoinLimit { +// p.AddCoin(int64(upgradeAccountGiveCoin), common.GainWay_UpgradeAccount, "", "") +// //增加泥码 +// p.AddDirtyCoin(0, int64(upgradeAccountGiveCoin)) +// p.AddPayCoinLog(int64(upgradeAccountGiveCoin), model.PayCoinLogType_Coin, "system") +// p.ReportSystemGiveEvent(upgradeAccountGiveCoin, common.GainWay_UpgradeAccount, true) +// } else { +// upgradeAccountGiveCoin = 0 +// sendPack(player_proto.OpResultCode_OPRC_Account_IP_TooManyReg) +// } +// } +// +// //记录赠送日志 +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// model.InsertUpgradeAccountCoinLog(p.RegIp, time.Now(), upgradeAccountGiveCoin, p.SnId, p.Channel, p.Platform, p.BeUnderAgentCode, p.PackageID, p.City, p.InviterId) +// return nil +// }), nil, "InsertUpgradeAccountCoinLog").StartByFixExecutor("InsertUpgradeAccountCoinLog") +// //正式用户的登录红包 +// //actRandCoinMgr.OnRandOnlineCoin(p) +// } +// } +// } +// } +// sendPack(player_proto.OpResultCode_OPRC_Sucess) +// } else { +// logger.Logger.Warnf("UpgradeAccount err:%v", data) +// sendPack(player_proto.OpResultCode_OPRC_Error) +// } +// }), "UpgradeAccount").StartByExecutor(msg.GetTel()) +// } +// +// return nil +//} + +// 绑定支付宝 +type CSBindAlipayPacketFactory struct { +} +type CSBindAlipayHandler struct { +} + +func (this *CSBindAlipayPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSBindAlipay{} + return pack +} + +func (this *CSBindAlipayHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBindAlipayHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSBindAlipay); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSBindAlipayHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCBindAlipay{ + OpRetCode: code, + AlipayAccount: msg.AlipayAccount, + AlipayAccName: msg.AlipayAccName, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_BINDALIPAY), pack) + } + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform != nil { + if !platform.IsMarkFlag(int32(BindAlipay)) { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_PlatformError) + return nil + } + } + alipayAccount := msg.GetAlipayAccount() + if alipayAccount == "" { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_AccountEmpty) + return nil + } + + if !utf8.ValidString(alipayAccount) { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_AccountIllegal) + return nil + } + + alipayAccName := msg.GetAlipayAccName() + if alipayAccName == "" { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_AccNameEmpty) + return nil + } + + if !utf8.ValidString(alipayAccName) { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_AccNameIllegal) + return nil + } + + if platform.NeedSameName { + if p.BankAccName != "" { + if p.BankAccName != alipayAccName { + sendPack(player_proto.OpResultCode_OPRC_BankAndAli_NotSame) + return nil + } + } + } + + if msg.GetPassword() == "" || len(msg.GetPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + + raw := fmt.Sprintf("%v%v%v", p.SafeBoxPassword, common.GetAppId(), msg.GetTimeStamp()) + h := md5.New() + io.WriteString(h, raw) + hashsum := hex.EncodeToString(h.Sum(nil)) + if hashsum != msg.GetPassword() { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + + var limitCount int32 + if platform != nil && platform.PerBankNoLimitAccount != 0 { + limitCount = platform.PerBankNoLimitAccount + } + var prohibit bool + var isLimitNum bool + //绑定支付宝账号 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + cnt := model.CountAlipayAccountCount(p.Platform, msg.GetAlipayAccount()) + if limitCount > 0 && cnt >= int(limitCount) { //没有事务,存在容差,认为可以容忍 + prohibit = true + return nil + } + if platform.PerBankNoLimitName > 0 { + cnt := model.CountBankAlipayNameCount(p.Platform, alipayAccName, p.SnId) + if cnt >= int(platform.PerBankNoLimitName) { + isLimitNum = true + return nil + } + } + + err := model.UpdatePlayerAlipay(p.Platform, p.SnId, alipayAccount, alipayAccName) + if err == nil { + model.NewBankBindLog(p.SnId, p.Platform, model.BankBindLogType_Ali, msg.GetAlipayAccName(), + msg.GetAlipayAccount(), 1) + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + if prohibit { + sendPack(player_proto.OpResultCode_OPRC_BindAlipay_CountLimit) + } else if isLimitNum { + sendPack(player_proto.OpResultCode_OPRC_BindBankAlipay_NameCountLimit) + } else { + p.AlipayAccount = msg.GetAlipayAccount() + p.AlipayAccName = msg.GetAlipayAccName() + //用户绑定支付宝事件 + p.ReportBindAlipayEvent() + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + }), "UpdatePlayerAlipay").StartByExecutor(strconv.Itoa(int(p.SnId))) + + } + return nil +} + +// 绑定银行卡 +type CSBindBankPacketFactory struct { +} +type CSBindBankHandler struct { +} + +func (this *CSBindBankPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSBindBank{} + return pack +} + +func (this *CSBindBankHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBindBankHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSBindBank); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSBindBankHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCBindBank{ + OpRetCode: code, + Bank: msg.Bank, + BankAccount: msg.BankAccount, + BankAccName: msg.BankAccName, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_BINDBANK), pack) + } + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform != nil { + if !platform.IsMarkFlag(int32(BindBackCard)) { + sendPack(player_proto.OpResultCode_OPRC_BindBank_PlatformError) + return nil + } + } + bank := msg.GetBank() + if bank == "" { + sendPack(player_proto.OpResultCode_OPRC_BindBank_NameEmpty) + return nil + } + + if !utf8.ValidString(bank) { + sendPack(player_proto.OpResultCode_OPRC_BindBank_NameIllegal) + return nil + } + + bankAccount := msg.GetBankAccount() + if bankAccount == "" { + sendPack(player_proto.OpResultCode_OPRC_BindBank_AccountEmpty) + return nil + } + + if !utf8.ValidString(bankAccount) { + sendPack(player_proto.OpResultCode_OPRC_BindBank_AccountIllegal) + return nil + } + + bankAccName := msg.GetBankAccName() + if bankAccName == "" { + sendPack(player_proto.OpResultCode_OPRC_BindBank_AccNameEmpty) + return nil + } + + if !utf8.ValidString(bankAccName) { + sendPack(player_proto.OpResultCode_OPRC_BindBank_AccNameIllegal) + return nil + } + + if platform.NeedSameName { + if p.AlipayAccName != "" { + if p.AlipayAccName != bankAccName { + sendPack(player_proto.OpResultCode_OPRC_BankAndAli_NotSame) + return nil + } + } + } + + if msg.GetPassword() == "" || len(msg.GetPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + + raw := fmt.Sprintf("%v%v%v", p.SafeBoxPassword, common.GetAppId(), msg.GetTimeStamp()) + h := md5.New() + io.WriteString(h, raw) + hashsum := hex.EncodeToString(h.Sum(nil)) + if hashsum != msg.GetPassword() { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + + if p.BankAccount != "" { + logger.Logger.Errorf("player had bank,now modify:%v", p.SnId) + } + + var limitCount int32 + if platform != nil && platform.PerBankNoLimitAccount != 0 { + limitCount = platform.PerBankNoLimitAccount + } + var prohibit bool + var isLimitNum bool + //绑定银行账号 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + cnt := model.CountBankAccountCount(p.Platform, bankAccount) + if limitCount > 0 && cnt >= int(limitCount) { //没有事务,存在容差,认为可以容忍 + prohibit = true + return nil + } + if platform.PerBankNoLimitName > 0 { + cnt := model.CountBankAlipayNameCount(p.Platform, bankAccName, p.SnId) + if cnt >= int(platform.PerBankNoLimitName) { + isLimitNum = true + return nil + } + } + + err := model.UpdatePlayerBank(p.Platform, p.SnId, bank, bankAccount, bankAccName) + if err == nil { + model.NewBankBindLog(p.SnId, p.Platform, model.BankBindLogType_Bank, bankAccName, bankAccount, 1) + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + if prohibit { + sendPack(player_proto.OpResultCode_OPRC_BindBank_CountLimit) + } else if isLimitNum { + sendPack(player_proto.OpResultCode_OPRC_BindBankAlipay_NameCountLimit) + } else { + p.Bank = bank + p.BankAccount = bankAccount + p.BankAccName = bankAccName + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + }), "UpdatePlayerBank").StartByExecutor(strconv.Itoa(int(p.SnId))) + } + return nil +} + +// 修改密码 +type CSChangePasswordPacketFactory struct { +} +type CSChangePasswordHandler struct { +} + +func (this *CSChangePasswordPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSChangePassword{} + return pack +} + +func (this *CSChangePasswordHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSChangePasswordHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSChangePassword); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSChangePasswordHandler p == nil") + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCChangePassword{ + OpRetCode: code, + ChangeType: msg.ChangeType, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_CHANGEPASSWORD), pack) + } + + //修改类型不匹配 + if msg.GetChangeType() < 0 || msg.GetChangeType() > 2 { + sendPack(player_proto.OpResultCode_OPRC_Error) + return nil + } + + //旧密码 + if msg.GetOldPassword() == "" || len(msg.GetOldPassword()) < 32 { + if msg.GetChangeType() == 0 { + sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) + } else { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + } + return nil + } + + //新密码 + if msg.GetNewPassword() == "" || len(msg.GetNewPassword()) < 32 { + if msg.GetChangeType() == 0 { + sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) + } else { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + } + return nil + } + + if msg.GetNewPassword() == msg.GetOldPassword() && msg.GetChangeType() != 2 { + sendPack(player_proto.OpResultCode_OPRC_PasswordEqual) + return nil + } + + switch msg.GetChangeType() { //0:帐号密码 1:保险箱密码 2:设置保险箱密码 + case 0: + //修改帐号密码 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpdatePlayerPassword(p.Platform, p.AccountId, msg.GetOldPassword(), msg.GetNewPassword()) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + als := LoginStateMgrSington.GetLoginStateByAccId(p.AccountId) + if als != nil && als.acc != nil { + als.acc.TelPassWord = msg.GetNewPassword() + } + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + }), "UpdatePlayerPassword").StartByExecutor(strconv.Itoa(int(p.SnId))) + case 1: + //验证保险箱密码 + + if p.SafeBoxPassword != msg.GetOldPassword() { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + //修改帐号密码 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpdateSafeBoxPassword(p.Platform, p.AccountId, msg.GetOldPassword(), msg.GetNewPassword()) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if i, ok := data.(int); ok { + if i == 0 { + p.SafeBoxPassword = msg.GetNewPassword() + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + } + }), "UpdatePlayerPassword").StartByExecutor(strconv.Itoa(int(p.SnId))) + case 2: + //验证保险箱密码 + /*raw := fmt.Sprintf("%v%v%v", p.SafeBoxPassword, common.GetAppId(), msg.GetTimeStamp()) + h := md5.New() + io.WriteString(h, raw) + hashsum := hex.EncodeToString(h.Sum(nil)) + if hashsum != msg.GetOldPassword() { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + }*/ + raw := fmt.Sprintf("%v%v", model.DEFAULT_PLAYER_SAFEBOX_PWD, common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + //修改帐号密码 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpdateSafeBoxPassword(p.Platform, p.AccountId, pwd, msg.GetNewPassword()) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if i, ok := data.(int); ok { + if i == 0 { + p.SafeBoxPassword = msg.GetNewPassword() + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + } + }), "UpdatePlayerPassword").StartByExecutor(strconv.Itoa(int(p.SnId))) + } + //sendPack(player_proto.OpResultCode_OPRC_Sucess) + } + return nil +} + +// 操作保险箱 +type CSPlayerSafeBoxPacketFactory struct { +} +type CSPlayerSafeBoxHandler struct { +} + +func (this *CSPlayerSafeBoxPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerOpCoin{} + return pack +} + +func (this *CSPlayerSafeBoxHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerSafeBoxHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerOpCoin); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerSafeBoxHandler p == nil") + return nil + } + if p.scene != nil { + logger.Logger.Warn("CSPlayerSafeBoxHandler p.scene != nil") + return nil + } + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCPlayerOpCoin{ + OpRetCode: code, + Op: msg.Op, + Coin: proto.Int64(p.Coin), + SafeBoxCoin: proto.Int64(p.SafeBoxCoin), + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PLAYEROPCOIN), pack) + } + if msg.GetCoin() <= 0 { + sendPack(player_proto.OpResultCode_OPRC_Error) + return nil + } + snid := p.SnId + beforeCoin := p.Coin + beforeBank := p.SafeBoxCoin + afterCoin := p.Coin + afterBank := p.SafeBoxCoin + if msg.GetOp() == model.SafeBoxLogType_Save { + //验证金币 + if p.Coin < msg.GetCoin() { + sendPack(player_proto.OpResultCode_OPRC_CoinNotEnough) + return nil + } + //计算日志数据 + afterCoin = p.Coin - msg.GetCoin() + afterBank = p.SafeBoxCoin + msg.GetCoin() + //操作数据 + p.SafeBoxCoin += msg.GetCoin() + p.AddCoin(-msg.GetCoin(), 0, common.GainWay_SafeBoxSave, "system", "保险箱存入") + p.dirty = true + } else if msg.GetOp() == model.SafeBoxLogType_TakeOut { + //验证金币 + if p.SafeBoxCoin < msg.GetCoin() { + sendPack(player_proto.OpResultCode_OPRC_CoinNotEnough) + return nil + } + //验证密码 + if msg.GetPassword() == "" || len(msg.GetPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_Safebox_PasswordIllegal) + return nil + } + hashsum := common.MakeMd5String(p.SafeBoxPassword, common.GetAppId(), strconv.Itoa(int(msg.GetTimeStamp()))) + if hashsum != msg.GetPassword() { + sendPack(player_proto.OpResultCode_OPRC_SafeBoxPasswordError) + return nil + } + //计算日志数据 + afterCoin = p.Coin + msg.GetCoin() + afterBank = p.SafeBoxCoin - msg.GetCoin() + //操作数据 + p.SafeBoxCoin -= msg.GetCoin() + p.AddCoin(msg.GetCoin(), 0, common.GainWay_SafeBoxTakeOut, "system", "保险箱取出") + p.dirty = true + } + + p.SendDiffData() + sendPack(player_proto.OpResultCode_OPRC_Sucess) + var oper string + var logs []*model.PayCoinLog + var coinPayts int64 + var safePayts int64 + billNo := time.Now().UnixNano() + if msg.GetOp() == model.SafeBoxLogType_Save { + log := model.NewPayCoinLog(billNo, p.SnId, msg.GetCoin(), 0, "system", model.PayCoinLogType_SafeBoxCoin, 0) + if log != nil { + safePayts = log.TimeStamp + logs = append(logs, log) + } + billNo += 1 + log = model.NewPayCoinLog(billNo, p.SnId, -msg.GetCoin(), 0, "system", model.PayCoinLogType_Coin, 0) + if log != nil { + coinPayts = log.TimeStamp + logs = append(logs, log) + } + oper = "保险箱存入" + } else if msg.GetOp() == model.SafeBoxLogType_TakeOut { + log := model.NewPayCoinLog(billNo, p.SnId, -msg.GetCoin(), 0, "system", model.PayCoinLogType_SafeBoxCoin, 0) + if log != nil { + safePayts = log.TimeStamp + logs = append(logs, log) + } + billNo += 1 + log = model.NewPayCoinLog(billNo, p.SnId, msg.GetCoin(), 0, "system", model.PayCoinLogType_Coin, 0) + if log != nil { + coinPayts = log.TimeStamp + logs = append(logs, log) + } + oper = "保险箱取出" + } + isNeedRollBack := true + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + + err := model.InsertPayCoinLogs(p.Platform, logs...) + if err != nil { + logger.Logger.Trace("InsertPayCoinLogs err:", err) + return err + } + err, _ = model.InsertSafeBox(p.SnId, msg.GetCoin(), beforeBank, afterBank, beforeCoin, afterCoin, + msg.GetOp(), time.Now(), p.Ip, oper, p.Platform, p.Channel, p.BeUnderAgentCode) + if err != nil { + isNeedRollBack = false + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil && isNeedRollBack { + //如果保存失败,并不会回滚玩家的数据,主要原因时无法回滚,如果回滚就需要先扣后补,因为只是在玩家身上流转, + //所以可以可以不考虑回滚问题 + logger.Logger.Errorf("Player %v box coin op log error.", snid) + } else { + p.SetPayTs(coinPayts) + p.SetSafeBoxPayTs(safePayts) + p.dirty = true + } + }), oper).StartByExecutor(strconv.Itoa(int(p.SnId))) + } + return nil +} + +// 读取保险箱记录 +type CSPlayerSafeBoxCoinLogPacketFactory struct { +} +type CSPlayerSafeBoxCoinLogHandler struct { +} + +func (this *CSPlayerSafeBoxCoinLogPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSGetSafeBoxCoinLog{} + return pack +} + +func (this *CSPlayerSafeBoxCoinLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("GetSafeBoxCoinLog Process recv ", data) + if _, ok := data.(*player_proto.CSGetSafeBoxCoinLog); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("GetSafeBoxCoinLog p == nil") + return nil + } + + pack := &player_proto.SCGetSafeBoxCoinLog{} + + sendPack := func(code player_proto.OpResultCode) { + pack.OpRetCode = code + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_GETSAFEBOXCOINLOG), pack) + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if sblist, err := model.GetSafeBoxs(p.Platform, p.SnId); err == nil { + return sblist + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + return nil + } + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if safeboxrec, ok := data.([]model.SafeBoxRec); ok { + for i := 0; i < len(safeboxrec); i++ { + safeBoxInfo := &player_proto.SafeBoxCoinLog{ + LogType: proto.Int32(safeboxrec[i].LogType), + OPCoin: proto.Int64(safeboxrec[i].Count), + OPCoinFront: proto.Int64(safeboxrec[i].BeforeSafeBox), + OPCoinLast: proto.Int64(safeboxrec[i].AfterSafeBox), + Ts: proto.Int64(safeboxrec[i].Time.Unix()), + } + pack.Logs = append(pack.Logs, safeBoxInfo) + } + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + sendPack(player_proto.OpResultCode_OPRC_Error) + } + }), "GetSafeBoxCoinLog").StartByExecutor(strconv.Itoa(int(p.SnId))) + } + return nil +} + +// +////读取游戏记录 +//type CSPlayerGameCoinLogPacketFactory struct { +//} +//type CSPlayerGameCoinLogHandler struct { +//} +// +//func (this *CSPlayerGameCoinLogPacketFactory) CreatePacket() interface{} { +// pack := &player_proto.CSGetGameCoinLog{} +// return pack +//} +// +//func (this *CSPlayerGameCoinLogHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSPlayerGameCoinLogHandler Process recv ", data) +// if _, ok := data.(*player_proto.CSGetGameCoinLog); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p == nil { +// logger.Logger.Warn("CSPlayerGameCoinLogHandler p == nil") +// return nil +// } +// //model.InsertGameRecList(p.SnId, 0, 20, p.entercoin, p.Coin, p.enterts, time.Now()) +// pack := &player_proto.SCGetGameCoinLog{} +// sendPack := func(code player_proto.OpResultCode) { +// pack.OpRetCode = code +// proto.SetDefaults(pack) +// p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_GETGAMECOINLOG), pack) +// } +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// if glist, err := model.GetGameRecLogs(p.SnId); err == nil { +// return glist +// } else { +// sendPack(player_proto.OpResultCode_OPRC_Error) +// return nil +// } +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// if gamelist, ok := data.([]model.GameRecLog); ok { +// for i := 0; i < len(gamelist); i++ { +// gameInfo := &player_proto.GameCoinLog{ +// GameId: proto.Int32(gamelist[i].GameId), +// EnterCount: proto.Int64(gamelist[i].EnterCount), +// LeaveCount: proto.Int64(gamelist[i].LeaveCount), +// EnterTs: proto.Int64(gamelist[i].EnterTs.Unix()), +// LeaveTs: proto.Int64(gamelist[i].LeaveTs.Unix()), +// } +// pack.Logs = append(pack.Logs, gameInfo) +// } +// sendPack(player_proto.OpResultCode_OPRC_Sucess) +// } else { +// sendPack(player_proto.OpResultCode_OPRC_Error) +// } +// }), "GetSafeBoxCoinLog").StartByExecutor(p.AccountId) +// } +// return nil +//} + +// 注册帐号 +type CSPlayerRegisterPacketFactory struct { +} +type CSPlayerRegisterHandler struct { +} + +func (this *CSPlayerRegisterPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSRegister{} + return pack +} + +func (this *CSPlayerRegisterHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerRegisterHandler Process recv ", data) + if t, ok := data.(*player_proto.CSRegister); ok { + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCRegister{ + OpRetCode: code, + } + proto.SetDefaults(pack) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_REGISTER), pack, s) + } + + //替换为后台拿到的配置数据,不再使用客户端的发送数据, + platformID, _, promoter, _, tagkey := PlatformMgrSingleton.GetPlatformByPackageTag(t.GetPlatformTag()) + if platformID == 0 { + sendPack(player_proto.OpResultCode_OPRC_Error) + return nil + } + var loginType int32 + //手机号注册,验证参数 + if t.GetRegistType() == 0 { + loginType = common.RegisterTypeTel + //检验手机号合法 + tel := t.GetTel() + if !reTelRule.MatchString(tel) { + sendPack(player_proto.OpResultCode_OPRC_TelError) + return nil + } + + //验证验证码 + platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(platformID))) + if !platform.RegisterVerifyCodeSwitch { + if platform.VerifyCodeType != common.CodeTypeNo { // 不使用验证码注册 + code := CacheMemory.Get(fmt.Sprintf("%v_%v", tel, tagkey)) + if code == nil { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + if verifyCode, ok := code.(string); ok && verifyCode != "" { + if verifyCode != t.GetVerificationCode() { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + } + } + } + + //验证密码 + if t.GetTelPassword() == "" || len(t.GetTelPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) + return nil + } + if t.GetPassword() == "" || len(t.GetPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) + return nil + } + } else { + // username,telpassword + loginType = common.RegisterTypeName + if t.GetUsername() == "" { + sendPack(player_proto.OpResultCode_OPRC_UserNameError) + return nil + } + //验证密码 + if t.GetTelPassword() == "" || len(t.GetTelPassword()) < 32 { + sendPack(player_proto.OpResultCode_OPRC_UpgradeAccount_PasswordIllegal) + return nil + } + } + + //backupPromoter := "" + + strPlatform := strconv.Itoa(int(platformID)) + if t.GetChannel() != common.Channel_Rob { + t.Platform = proto.String(strPlatform) + //因为后台需要,修改此方法,修改为客户端传递的数据 + if len(t.GetPromoter()) <= 0 { + t.Promoter = proto.String(strconv.Itoa(int(promoter))) + } else { + //backupPromoter = t.GetPromoter() + } + } + + amount := int32(0) + plt := PlatformMgrSingleton.GetPlatform(strPlatform) + //if plt != nil { + // amount += plt.NewAccountGiveCoin + // amount += plt.UpgradeAccountGiveCoin + //} + + var temp []byte + var ee error + di := t.GetDeviceInfo() + if plt.NeedDeviceInfo && di == "" { //maybe res ver is low + sendPack(player_proto.OpResultCode_OPRC_YourResVerIsLow) + return nil + } + + if di != "" { + var e common.Encryptor + e.Init(common.GetAppId(), t.GetPlatformTag(), int32(t.GetTimeStamp())) + temp, ee = base64.StdEncoding.DecodeString(di) + if ee != nil { + sendPack(player_proto.OpResultCode_OPRC_YourResVerIsLow) + return nil + } + + e.Encrypt(temp, len(temp)) + + if model.GameParamData.ValidDeviceInfo && !json.Valid(temp) { + sendPack(player_proto.OpResultCode_OPRC_YourResVerIsLow) + return nil + } + } + + deviceInfo := string(temp) + if bi, ok := BlackListMgrSington.CheckDeviceInBlack(deviceInfo, BlackState_Login, t.GetPlatform()); ok { + sendPack(player_proto.OpResultCode_OPRC_InBlackList) + if bi != nil { + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_SRVMSG), common.CreateSrvMsg(common.SRVMSG_CODE_DEFAULT, i18n.Tr("languages", "BlackListLimit1Args", bi.Id, bi.Id, bi.Id, bi.Id)), s) + } + return nil + } + + var pi *model.PlayerData + var acc *model.Account + //var promoterCfg *PromoterConfig + //var invitePromoterID string + var tf bool + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + _, code := model.AccountIsExist(t.GetUsername(), t.GetTel(), t.GetPassword(), t.GetPlatform(), t.GetTimeStamp(), + loginType, tagkey, false, false) + if code == common.LoginTelExist { + //手机号已经注册 + return player_proto.OpResultCode_OPRC_TelIsRegister + } + if code == common.RegisterExist { + //账号密码已经注册 + return player_proto.OpResultCode_OPRC_Login_CreateFailed + } + if code == common.RegisterNotExist { + //新帐号,游客 + raw := fmt.Sprintf("%v%v", t.GetUsername(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + acc = &model.Account{} + //可以帐号密码登录,也可以游客登录 + acc, code = model.InsertTelAccount(t.GetUsername(), pwd, t.GetPlatform(), t.GetChannel(), t.GetPromoter(), t.GetParams(), t.GetInviterId(), + t.GetPromoterTree(), t.GetTel(), t.GetTelPassword(), t.GetPlatformTag(), t.GetPackage(), deviceInfo, tagkey, 0, "") + + if code != common.InsertAccountOk { + return player_proto.OpResultCode_OPRC_Error + } + //生成玩家数据 + pi, tf = model.CreatePlayerDataOnRegister(acc.Platform, acc.AccountId.Hex(), amount, "", niceIdMgr.GetRobHeadUrlIdx()) + if pi == nil || tf == false { + return player_proto.OpResultCode_OPRC_Error + } + } else { + logger.Logger.Tracef("CSPlayerRegister Player code:%v", code) + return player_proto.OpResultCode_OPRC_Error + } + + if pi != nil && pi.InviterId != 0 { + invitePd := model.GetPlayerBaseInfo(pi.Platform, pi.InviterId) + if invitePd != nil { + //invitePromoterID = invitePd.BeUnderAgentCode + } + } + + //新号赠送初始金额 + err2 := model.UpdatePlayerCoin(pi.Platform, pi.SnId, pi.Coin+int64(model.GameParamData.NewPlayerCoin), 0, 0, time.Now().Unix(), time.Now().Unix(), 0, pi.ShopID) + if err2 == nil { + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(pi.SnId, pi.Name, pi.Platform, model.SystemFreeGive_GiveType_NewPlayer, model.SystemFreeGive_CoinType_Coin, int64(model.GameParamData.NewPlayerCoin))) + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: pi.Platform, + SnID: pi.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: int64(model.GameParamData.NewPlayerCoin), + RemainNum: pi.Coin + int64(model.GameParamData.NewPlayerCoin), + Add: 0, + LogType: common.GainWay_NewPlayer, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "", + Remark: "", + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + return player_proto.OpResultCode_OPRC_Sucess + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if code, ok := data.(player_proto.OpResultCode); ok { + if code != player_proto.OpResultCode_OPRC_Sucess { + sendPack(code) + return + } + } + //清理掉cache中的无效id + PlayerCacheMgrSingleton.UnCacheInvalidPlayerId(pi.SnId) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + }), "PlayerRegister").StartByExecutor(t.GetUsername()) + } + return nil +} + +type CSWebAPIPlayerPassPacketFactory struct { +} + +type CSWebAPIPlayerPassHandler struct { +} + +func (this *CSWebAPIPlayerPassPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSWebAPIPlayerPass{} + return pack +} + +func (this *CSWebAPIPlayerPassHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWebAPIPlayerPassHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSWebAPIPlayerPass); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSWebAPIPlayerPassHandler p == nil") + return nil + } + opCode := player_proto.OpResultCode_OPRC_Sucess + errString := "" + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + errString, err = webapi.API_PlayerPass(p.SnId, p.Platform, p.Channel, p.BeUnderAgentCode, msg.GetApiName(), msg.GetParams(), common.GetAppId(), p.LogicLevels) + if err != nil { + logger.Logger.Errorf("API_PlayerPass error:%v api:%v params:%v", err, msg.GetApiName(), msg.GetParams()) + opCode = player_proto.OpResultCode_OPRC_Error + return nil + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + pack := &player_proto.SCWebAPIPlayerPass{ + OpRetCode: opCode, + ApiName: msg.ApiName, + CBData: msg.CBData, + Response: proto.String(errString), + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_WEBAPI_PLAYERPASS), pack) + logger.Logger.Trace("CSWebAPIPlayerPass:", pack) + }), "API_PlayerPass").Start() + } + return nil +} + +type CSWebAPISystemPassPacketFactory struct { +} + +type CSWebAPISystemPassHandler struct { +} + +func (this *CSWebAPISystemPassPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSWebAPISystemPass{} + return pack +} + +func (this *CSWebAPISystemPassHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWebAPISystemPassHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSWebAPISystemPass); ok { + opCode := player_proto.OpResultCode_OPRC_Sucess + errString := "" + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + errString, err = webapi.API_SystemPass(msg.GetApiName(), msg.GetParams(), msg.GetCBData(), common.GetAppId()) + if err != nil { + logger.Logger.Errorf("API_SystemPass error:%v apiname=%v params=%v", err, msg.GetApiName(), msg.GetParams()) + opCode = player_proto.OpResultCode_OPRC_Error + return nil + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + pack := &player_proto.SCWebAPISystemPass{ + OpRetCode: opCode, + ApiName: msg.ApiName, + CBData: msg.CBData, + Response: proto.String(errString), + } + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_WEBAPI_SYSTEMPASS), pack, s) + logger.Logger.Trace("CSWebAPISystemPass:", pack) + }), "API_SystemPass").Start() + } + return nil +} + +type CSSpreadBindPacketFactory struct { +} + +type CSSpreadBindHandler struct { +} + +func (this *CSSpreadBindPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSSpreadBind{} + return pack +} + +func (this *CSSpreadBindHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSSpreadBindHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSSpreadBind); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSSpreadBindHandler p == nil") + return nil + } + + sendPack := func(opCode player_proto.OpResultCode, parentId int32) { + pack := &player_proto.SCSpreadBind{ + OpRetCode: opCode, + ParentId: msg.ParentId, + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_SPREADBIND), pack) + return + } + + parentId := msg.GetParentId() + parent := PlayerMgrSington.GetPlayerBySnId(parentId) + if parent != nil { + if parent.Platform != p.Platform { + sendPack(player_proto.OpResultCode_OPRC_InviterIdNotExist, 0) + return nil + } + if parent.Tel == "" { + sendPack(player_proto.OpResultCode_OPRC_InviterNoBind, 0) + return nil + } + } + + if model.GameParamData.InvitePromoterBind && p.BeUnderAgentCode != "" && p.BeUnderAgentCode != "0" { + sendPack(player_proto.OpResultCode_OPRC_HadSpreadInviterId, 0) + return nil + } + + if p.InviterId != 0 { + sendPack(player_proto.OpResultCode_OPRC_HadSpreadInviterId, 0) + return nil + } + var promoterCfg *PromoterConfig + upPromoterID := "" + opCode := player_proto.OpResultCode_OPRC_Sucess + tag := int32(0) + pAgentCode := "" + PlayerCacheMgrSingleton.Get(p.Platform, parentId, func(ppd *PlayerCacheItem, asyn, isnew bool) { + if ppd == nil || ppd.Platform != p.Platform { + opCode = player_proto.OpResultCode_OPRC_InviterIdNotExist + sendPack(opCode, parentId) + return + } + + if ppd.Tel == "" { + opCode = player_proto.OpResultCode_OPRC_InviterNoBind + sendPack(opCode, parentId) + return + } + + pAgentCode = ppd.BeUnderAgentCode + key, err := GetPromoterKey(0, pAgentCode, "") + if err == nil { + upPromoterID = pAgentCode + promoterCfg = PromoterMgrSington.GetConfig(key) + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + tag, err = webapi.API_PushSpreadLink(p.SnId, p.Platform, p.PackageID, int(parentId), 1, 1, common.GetAppId()) + if (err != nil || tag != 0) && !common.Config.IsDevMode { + logger.Logger.Errorf("API_PushSpreadLink error: %v tag: %v", err, tag) + if tag == 101 { //闭环 + opCode = player_proto.OpResultCode_OPRC_SpreadBindClosedLoop + } else { + opCode = player_proto.OpResultCode_OPRC_SpreadBindFailed + } + return nil + } else { + p.InviterId = parentId + p.dirty = true + if promoterCfg != nil { + if promoterCfg.IsInviteRoot > 0 { + p.BeUnderAgentCode = upPromoterID + } + } + + err = model.UpdatePlayerPackageIdByStr(p.SnId, p.PackageID, p.Platform, p.Channel, p.BeUnderAgentCode, p.InviterId, p.PromoterTree) + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if opCode == player_proto.OpResultCode_OPRC_Sucess { + //actRandCoinMgr.OnPlayerInvite(p.Platform, parentId) + //actRandCoinMgr.tempBind[p.SnId] = time.Now().Unix() + + } + sendPack(opCode, parentId) + }), "API_PushSpreadLink").Start() + + }, false) + } + return nil +} + +//type CSBindPromoterPacketFactory struct { +//} +// +//type CSBindPromoterHandler struct { +//} +// +//func (this *CSBindPromoterPacketFactory) CreatePacket() interface{} { +// pack := &player_proto.CSBindPromoter{} +// return pack +//} +// +//func (this *CSBindPromoterHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { +// logger.Logger.Trace("CSBindPromoterHandler Process recv ", data) +// if msg, ok := data.(*player_proto.CSBindPromoter); ok { +// p := PlayerMgrSington.GetPlayer(sid) +// if p == nil { +// logger.Logger.Warn("CSBindPromoterHandler p == nil") +// return nil +// } +// +// sendPack := func(opCode player_proto.OpResultCode, parentId int32) { +// pack := &player_proto.SCBindPromoter{ +// OpRetCode: opCode, +// Promoter: msg.Promoter, +// } +// p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_BINDPROMOTER), pack) +// return +// } +// +// promoter := msg.GetPromoter() +// +// info, ok := PlatformMgrSingleton.PromoterList[promoter] +// if !ok { +// sendPack(player_proto.OpResultCode_OPRC_NoPromotor, 0) +// return nil +// } +// +// plt := PlatformMgrSingleton.GetPlatform(p.Platform) +// if plt == nil { +// sendPack(player_proto.OpResultCode_OPRC_NoPlatform, 0) +// return nil +// } +// +// if !plt.IsCanUserBindPromoter { +// sendPack(player_proto.OpResultCode_OPRC_CantUserBind, 0) +// return nil +// } +// +// if p.BeUnderAgentCode != "" && p.BeUnderAgentCode != "0" { +// sendPack(player_proto.OpResultCode_OPRC_PromoterHasBind, 0) +// return nil +// } +// +// if promoter == "" || promoter == "0" { +// sendPack(player_proto.OpResultCode_OPRC_CantUserBind, 0) +// return nil +// } +// +// if info.Platform != p.Platform { +// sendPack(player_proto.OpResultCode_OPRC_PlatformNoPromoter, 0) +// return nil +// } +// if plt.UserBindPromoterPrize > 0 && !p.layered[common.ActId_PromoterBind] { +// p.AddCoin(int64(plt.UserBindPromoterPrize), common.GainWay_PromoterBind, "system", promoter) +// p.ReportSystemGiveEvent(plt.UserBindPromoterPrize, common.GainWay_PromoterBind, true) +// } +// +// p.BeUnderAgentCode = promoter +// p.PackageID = info.Tag +// p.dirty = true +// sendPack(player_proto.OpResultCode_OPRC_Sucess, 0) +// +// p.SendPlatformCanUsePromoterBind() +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// _, _ = webapi.API_PushInvitePromoter(p.SnId, promoter, common.GetAppId()) +// return nil +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// +// }), "API_PushInviteLinkPromoter").Start() +// +// } +// return nil +//} + +type CSGenCustomerTokenPacketFactory struct { +} + +type CSGenCustomerTokenHandler struct { +} + +func (this *CSGenCustomerTokenPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSGenCustomerToken{} + return pack +} + +func (this *CSGenCustomerTokenHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGenCustomerTokenHandler Process recv ", data) + if _, ok := data.(*player_proto.CSGenCustomerToken); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGenCustomerTokenHandler p == nil") + return nil + } + + token := p.GenCustomerToken() + PlayerMgrSington.UpdatePlayerToken(p, token) + pack := &player_proto.SCGenCustomerToken{Token: proto.String(token)} + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_GENCUSTOMTOKEN), pack) + } + return nil +} + +type CSCustomerNewMsgAckPacketFactory struct { +} + +type CSCustomerNewMsgAckHandler struct { +} + +func (this *CSCustomerNewMsgAckPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSCustomerNewMsgAck{} + return pack +} + +func (this *CSCustomerNewMsgAckHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSCustomerNewMsgAckHandler Process recv ", data) + //if msg, ok := data.(*player_proto.CSCustomerNewMsgAck); ok { + // p := PlayerMgrSington.GetPlayer(sid) + // if p == nil { + // logger.Logger.Warn("CSCustomerNewMsgAckHandler p == nil") + // return nil + // } + // + // CustomerOfflineMsgMgrSington.OnOfflineMsgAck(p.SnId, msg.GetMsgIds()) + //} + return nil +} + +type CSIosInstallStablePacketFactory struct { +} + +type CSIosInstallStableHandler struct { +} + +func (this *CSIosInstallStablePacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSIosInstallStable{} + return pack +} + +func (this *CSIosInstallStableHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSIosInstallStableHandler Process recv ", data) + //if _, ok := data.(*player_proto.CSIosInstallStable); ok { + // p := PlayerMgrSington.GetPlayer(sid) + // if p == nil { + // logger.Logger.Warn("CSIosInstallStableHandler p == nil") + // return nil + // } + // + // if p.CoinPayTotal < int64(model.GameParamData.IosStablePrizeMinRecharge) { //不满足最低充值需求 + // return nil + // } + // if p.DeviceOS != "ios" { //不是苹果系统,直接忽略 + // return nil + // } + // if p.IosStableState != model.IOSSTABLESTATE_NIL { //未标注过才行 + // return nil + // } + // + // p.IosStableState = model.IOSSTABLESTATE_MARK + // p.dirty = true + //} + return nil +} + +// 查询中奖 +type CSFishJackpotPacketFactory struct { +} +type CSFishJackpotHandler struct { +} + +func (this *CSFishJackpotPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSJackpotList{} + return pack +} +func (this *CSFishJackpotHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishJackpotHandler Process recv ", data) + if _, ok := data.(*player_proto.CSJackpotList); ok { + player := PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishJackpotHandler p == nil") + return nil + } + + pack := &player_proto.SCJackpotList{} + + datas := FishJackListMgr.GetJackInfo(player.Platform) + for _, v := range datas { + info := &player_proto.FishJackpotInfo{ + Name: proto.String(v.Name), + Coin: proto.Int64(v.JackpotCoin), + Type: proto.Int32(v.JackpotType), + Ts: proto.Int64(v.Ts), + } + pack.JackpotList = append(pack.JackpotList, info) + } + logger.Logger.Trace("CSFishJackpotHandler Pb ", pack) + player.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_FISHJACKPOTCOIN), pack) + + return nil + } + return nil +} + +// 查询奖池金额 +type CSFishJackpotCoinPacketFactory struct { +} +type CSFishJackpotCoinHandler struct { +} + +func (this *CSFishJackpotCoinPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSFishJackpotCoin{} + return pack +} +func (this *CSFishJackpotCoinHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSFishJackpotCoinHandler Process recv ", data) + if _, ok := data.(*player_proto.CSFishJackpotCoin); ok { + player := PlayerMgrSington.GetPlayer(sid) + if player == nil { + logger.Logger.Warn("CSFishJackpotCoinHandler p == nil") + return nil + } + + pack := &player_proto.SCFishJackpotCoin{} + if datas, exist := FishJackpotCoinMgr.Jackpot[player.Platform]; exist { + pack.Coin = proto.Int64(datas + 234982125) + //pack.Coin = proto.Int64(datas) + } else { + pack.Coin = proto.Int64(234982125) // 假数据 + //pack.Coin = proto.Int64(0) // 假数据 + } + logger.Logger.Trace("CSFishJackpotCoinHandler Pb ", pack) + player.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_FISHJACKPOTDATA), pack) + + return nil + } + return nil +} + +// 全民是否打开 +type CSGetSpreadIsOpenPacketFactory struct { +} +type CSGetSpreadIsOpenHandler struct { +} + +func (this *CSGetSpreadIsOpenPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSGetSpreadLWIsOpen{} + return pack +} + +func (this *CSGetSpreadIsOpenHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetSpreadIsOpenHandler Process recv ", data) + if _, ok := data.(*player_proto.CSGetSpreadLWIsOpen); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetSpreadIsOpenHandler p == nil") + return nil + } + plt := PlatformMgrSingleton.GetPlatform(p.Platform) + if plt == nil { + + return nil + } + + pack := &player_proto.SCGetSpreadLWIsOpen{} + if plt.SpreadWinLose { + pack.IsOpen = proto.Int32(1) + } else { + pack.IsOpen = proto.Int32(0) + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_GetSpreadLWIsOpen), pack) + + //sendPack(player_proto.OpResultCode_OPRC_Sucess) + } + return nil +} + +// 玩家设置 音乐音效客户端修改 目前功能只有兑换码 +type CSPlayerSettingPacketFactory struct { +} +type CSPlayerSettingHandler struct { +} + +func (this *CSPlayerSettingPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPlayerSetting{} + return pack +} + +func (this *CSPlayerSettingHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerSettingHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPlayerSetting); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerSettingHandler p == nil") + return nil + } + + //p.SCVIPInfo() + //p.SCVIPBuy(int64(10)) + //p.VIPDraw(3) + pack := &player_proto.SCPlayerSetting{ + OpRetCode: player_proto.OpResultCode_OPRC_Jyb_CodeErr, + } + + if msg.PackageCode != "" { + args := &model.VerifyUpJybInfoArgs{ + UseCode: msg.PackageCode, + Plt: p.Platform, + SnId: p.SnId, + } + if len(msg.PackageCode) < 12 { // 通用 + args.CodeType = 1 + } else { + key, err := model.Code2Id(msg.PackageCode) + if err != nil || key < uint64(model.Keystart) { + pack.OpRetCode = player_proto.OpResultCode_OPRC_Jyb_CodeErr + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_ALL_SETTING), pack) + return nil + } + + start := uint64(model.Keystart) + args.CodeStart = int64(key / start * start) // 过滤 + args.CodeType = 2 + } + BagMgrSingleton.VerifyUpJybInfo(p, args) + + } else { + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_ALL_SETTING), pack) + } + } + return nil +} + +// 充值VIP 后续要删掉 +type CSPlayerVIPBuyPacketFactory struct { +} +type CSPlayerVIPBuyHandler struct { +} + +func (this *CSPlayerVIPBuyPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSVIPBuy{} + return pack +} + +func (this *CSPlayerVIPBuyHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVIPBuy Process recv ", data) + if msg, ok := data.(*player_proto.CSVIPBuy); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerVIPBuyHandler p == nil") + return nil + } + + p.SCVIPBuy(int64(msg.GetMoney())) + } + return nil +} + +// VIP信息 +type CSPlayerVIPInfoPacketFactory struct { +} +type CSPlayerVIPInfoHandler struct { +} + +func (this *CSPlayerVIPInfoPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSVIPInfo{} + return pack +} + +func (this *CSPlayerVIPInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVIPInfo Process recv ", data) + if _, ok := data.(*player_proto.CSVIPInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerVIPInfoHandler p == nil") + return nil + } + p.SCVIPInfo() + } + return nil +} + +// 玩家领取VIP礼包 +type CSPlayerVIPDrawPacketFactory struct { +} +type CSPlayerVIPDrawHandler struct { +} + +func (this *CSPlayerVIPDrawPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSVIPDraw{} + return pack +} + +func (this *CSPlayerVIPDrawHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVIPDraw Process recv ", data) + if msg, ok := data.(*player_proto.CSVIPDraw); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerVIPDrawHandler p == nil") + return nil + } + + p.VIPDraw(msg.GetId()) + } + return nil +} + +// 玩家领取VIP礼包 +type CSVIPPrivilegeInfoPacketFactory struct { +} +type CSVIPPrivilegeInfoHandler struct { +} + +func (this *CSVIPPrivilegeInfoPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSVIPPrivilegeInfo{} + return pack +} + +func (this *CSVIPPrivilegeInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVIPPrivilegeInfoHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSVIPPrivilegeInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSVIPPrivilegeInfoHandler p == nil") + return nil + } + code := msg.GetCode() + pack := &player_proto.SCVIPPrivilegeInfo{ + Code: proto.Int32(code), + Vip: proto.Int32(p.VIP), + } + switch code { + case 1: //1.VIP比赛场免费次数 + pack.Value = p.GetVIPPrivilege1() + pack.NowValue = p.VipMatchTimes + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_VIPPrivilegeInfo), pack) + } + return nil +} + +func init() { + //修改昵称信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_CHANGENICK), &CSPlayerChangeNickHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_CHANGENICK), &CSPlayerChangeNickPacketFactory{}) + //修改头像信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_CHANGEICON), &CSPlayerChangeIconHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_CHANGEICON), &CSPlayerChangeIconPacketFactory{}) + //修改头像框信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_HEADOUTLINE), &CSPlayerChangeHeadOutLineHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_HEADOUTLINE), &CSPlayerChangeHeadOutLinePacketFactory{}) + //修改性别信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_CHANGESEX), &CSPlayerChangeSexHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_CHANGESEX), &CSPlayerChangeSexPacketFactory{}) + //绑定支付宝账号 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_BINDALIPAY), &CSBindAlipayHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_BINDALIPAY), &CSBindAlipayPacketFactory{}) + //绑定银行卡账号 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_BINDBANK), &CSBindBankHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_BINDBANK), &CSBindBankPacketFactory{}) + //更改密码 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_CHANGEPASSWORD), &CSChangePasswordHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_CHANGEPASSWORD), &CSChangePasswordPacketFactory{}) + //保险箱存取 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_PLAYEROPCOIN), &CSPlayerSafeBoxHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_PLAYEROPCOIN), &CSPlayerSafeBoxPacketFactory{}) + //读取保险箱记录 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_GETSAFEBOXCOINLOG), &CSPlayerSafeBoxCoinLogHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_GETSAFEBOXCOINLOG), &CSPlayerSafeBoxCoinLogPacketFactory{}) + //注册帐号 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_REGISTER), &CSPlayerRegisterHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_REGISTER), &CSPlayerRegisterPacketFactory{}) + //获取邀请码 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_INVITECODE), &CSInviteCodePlayerHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_INVITECODE), &CSInviteCodePlayerPacketFactory{}) + //生成客服会话token + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_GENCUSTOMTOKEN), &CSGenCustomerTokenHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_GENCUSTOMTOKEN), &CSGenCustomerTokenPacketFactory{}) + //客服离线消息接收ack + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_CUSTOMNEWMSGACK), &CSCustomerNewMsgAckHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_CUSTOMNEWMSGACK), &CSCustomerNewMsgAckPacketFactory{}) + //ios稳定版升级标注 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_IOSINSTALLSTABLE), &CSIosInstallStableHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_IOSINSTALLSTABLE), &CSIosInstallStablePacketFactory{}) + //查询中奖 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_FISHJACKPOTCOIN), &CSFishJackpotHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_FISHJACKPOTCOIN), &CSFishJackpotPacketFactory{}) + //查询奖池金额 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_FISHJACKPOTDATA), &CSFishJackpotCoinHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_FISHJACKPOTDATA), &CSFishJackpotCoinPacketFactory{}) + //查询客损是否打开 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_GetSpreadLWIsOpen), &CSGetSpreadIsOpenHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_GetSpreadLWIsOpen), &CSGetSpreadIsOpenPacketFactory{}) + //玩家设置 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_ALL_SETTING), &CSPlayerSettingHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_ALL_SETTING), &CSPlayerSettingPacketFactory{}) + //充值VIP + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_VIPBUY), &CSPlayerVIPBuyHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_VIPBUY), &CSPlayerVIPBuyPacketFactory{}) + //VIP信息 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_VIPINFO), &CSPlayerVIPInfoHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_VIPINFO), &CSPlayerVIPInfoPacketFactory{}) + //玩家领取VIP礼包 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_DRAWVIPGIFT), &CSPlayerVIPDrawHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_DRAWVIPGIFT), &CSPlayerVIPDrawPacketFactory{}) + //VIP特权 + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_VIPPrivilegeInfo), &CSVIPPrivilegeInfoHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_VIPPrivilegeInfo), &CSVIPPrivilegeInfoPacketFactory{}) + +} + +func init() { + // 用户信息 + common.Register(int(player_proto.PlayerPacketID_PACKET_CS_PLAYERDATA), player_proto.CSPlayerData{}, CSPlayerData) + // 查看别人信息 + common.Register(int(player_proto.PlayerPacketID_PACKET_CS_QUERYPLAYER), player_proto.CSQueryPlayer{}, CSQueryPlayer) + // 修改个人信息 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSSavePlayerInfo), player_proto.CSSavePlayerInfo{}, CSSavePlayerInfo) + // 修改头像url + common.Register(int(player_proto.PlayerPacketID_PACKET_CS_HeadUrl), player_proto.CSHeadUrl{}, CSHeadUrl) + // 获取绑定手机号奖励 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSBindTelInfo), player_proto.CSBindTelInfo{}, CSBindTelInfo) + // 获取短信验证码 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSPlayerSMSCode), player_proto.CSPlayerSMSCode{}, CSPlayerSMSCode) + // 绑定手机号 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSBindTel), player_proto.CSBindTel{}, CSBindTel) + // 获取图片验证码 + common.Register(int(player_proto.PlayerPacketID_PACKET_CS_GETIMAGEVERIFYCODE), player_proto.CSGetImageVerifyCode{}, CSGetImageVerifyCodeLocal) + // 验证图片验证码 + common.Register(int(player_proto.PlayerPacketID_PACKET_CS_ImageVerifyCode), player_proto.CSImageVerifyCode{}, CSImageVerifyCode) + // 查询游戏账变记录 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSBillList), player_proto.CSBillList{}, CSBillList) + // 看广告一次 + common.Register(int(player_proto.PlayerPacketID_PACKET_CSADV), player_proto.CSADV{}, CSADV) +} + +func CSPlayerData(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerDataHandler Process recv ", data) + cspl, ok := data.(*player_proto.CSPlayerData) + if !ok { + return nil + } + + ls := LoginStateMgrSington.GetLoginStateBySid(sid) + if ls == nil { + logger.Logger.Warnf("CSPlayerDataHandler sid:%v not login 1!!!", sid) + return nil + } + if ls.als == nil { + logger.Logger.Warnf("CSPlayerDataHandler sid:%v not login 2!!!", sid) + return nil + } + if ls.als.acc == nil { + logger.Logger.Warnf("CSPlayerDataHandler sid:%v not login 3!!!", sid) + return nil + } + + if cspl.GetAccId() != ls.als.acc.AccountId.Hex() { + cspl.AccId = proto.String(ls.als.acc.AccountId.Hex()) + logger.Logger.Warnf("CSPlayerDataHandler player(%v) try to hit db!!!", cspl.GetAccId()) + } + + // 公共方法 + playerFunc := func(p *Player, msg *player_proto.CSPlayerData) { + p.dirty = true + if p.GMLevel > 2 { + //伪造IP地址 + p.Ip = fmt.Sprintf("%v.%v.%v.%v", 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255)) + } + //p.params = p.UpdateParams(msg.GetParams()) + //player.DeviceInfo = deviceInfo + p.DeviceId = msg.GetDeviceId() + p.DeviceName = msg.GetDeviceName() + p.PackageName = msg.GetPackageName() + p.AppVersion = msg.GetAppVersion() + p.BuildVersion = msg.GetBuildVersion() + p.AppChannel = msg.GetAppChannel() + p.Channel = msg.AppChannel + + if ls.clog != nil { + PlayerSubjectSign.UpdateHeadUrl(p.SnId, ls.clog.HeadUrl) + } + } + + // 有缓存数据 + player := PlayerMgrSington.GetPlayerByAccount(cspl.GetAccId()) + if player != nil { + // 当前玩家登录,踢掉其它客户端连接 + if player.IsOnLine() { + if player.sid != 0 && player.sid != sid { + //Kick the exist player disconnect + player.Kickout(common.KickReason_OtherLogin) + } + } + + var temp []byte + var err error + di := cspl.GetDeviceInfo() + + // 获取不到平台信息,登录失败 + pt := PlatformMgrSingleton.GetPlatform(player.Platform) + if !player.IsRob && pt == nil { + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: player_proto.OpResultCode_OPRC_Error, + } + logger.Logger.Trace("player no platform:", player.Platform, player.SnId) + proto.SetDefaults(scPlayerData) + player.SendRawToClientIncOffLine(sid, s, int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + //Kick the exist player disconnect + player.Kickout(common.KickReason_Freeze) + return nil + } + + // 没有设备信息,登录失败 + if !player.IsRob && pt.NeedDeviceInfo && di == "" { //maybe res ver is low + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: player_proto.OpResultCode_OPRC_LoginFailed, + } + logger.Logger.Trace("player no deviceinfo:", player.SnId) + + proto.SetDefaults(scPlayerData) + player.SendRawToClientIncOffLine(sid, s, int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + //Kick the exist player disconnect + player.Kickout(common.KickReason_Freeze) + return nil + } + + // 设备验证失败,登录失败 + if !player.IsRob && di != "" && cspl.GetPlatformTag() != "" { + var e common.Encryptor + e.Init(common.GetAppId(), cspl.GetPlatformTag(), int32(cspl.GetTimeStamp())) + temp, err = base64.StdEncoding.DecodeString(di) + if err == nil { + e.Encrypt(temp, len(temp)) + } else { + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: player_proto.OpResultCode_OPRC_NotLogin, + } + logger.Logger.Trace("player no decode deviceinfo:", player.SnId) + + proto.SetDefaults(scPlayerData) + player.SendRawToClientIncOffLine(sid, s, int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + //Kick the exist player disconnect + player.Kickout(common.KickReason_Freeze) + return nil + } + + if !player.IsRob && model.GameParamData.ValidDeviceInfo && !json.Valid(temp) { + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: player_proto.OpResultCode_OPRC_NotLogin, + } + logger.Logger.Trace("player no json deviceinfo:", player.SnId, temp) + proto.SetDefaults(scPlayerData) + player.SendRawToClientIncOffLine(sid, s, int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + //Kick the exist player disconnect + player.Kickout(common.KickReason_Freeze) + return nil + } + } + + if !player.IsRob { + if _, ok := BlackListMgrSington.CheckLogin(player.PlayerData); !ok { + //var msg string + //if bi != nil { + // msg = i18n.Tr("languages", "BlackListLimit2Args", player.SnId, bi.Id, player.SnId, bi.Id, player.SnId, bi.Id, player.SnId, bi.Id) + //} else { + // msg = i18n.Tr("languages", "BlackListLimit1Args", player.SnId, player.SnId, player.SnId, player.SnId) + //} + //common.SendSrvMsg(player, common.SRVMSG_CODE_DEFAULT, msg) + //scPlayerData := &player_proto.SCPlayerData{ + // OpRetCode: player_proto.OpResultCode_OPRC_InBlackList, + //} + //proto.SetDefaults(scPlayerData) + //player.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + //Kick the exist player disconnect + player.Kickout(common.KickReason_Freeze) + return nil + } + } + + // 给玩家发送三方余额状态 + statePack := &gamehall_proto.SCThridGameBalanceUpdateState{} + if player.thridBalanceReqIsSucces { + statePack.OpRetCode = gamehall_proto.OpResultCode_Game_OPRC_Sucess_Game + } else { + statePack.OpRetCode = gamehall_proto.OpResultCode_Game_OPRC_Error_Game + } + player.SendRawToClientIncOffLine(sid, s, int(gamehall_proto.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + + //抽奖次数兼容老玩家 + if !player.IsRob && !player.InitLotteryStatus && WelfareMgrSington.GetPhoneLotteryStatus(player.Platform) == WelfareOpen { + player.addLotteryCount(20) + player.InitLotteryStatus = true + player.dirty = true + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(player.SnId, player.Platform, "", 3, 20, 5, 0)) + } + + // rehold player + playerFunc(player, cspl) + PlayerMgrSington.ReholdPlayer(player, sid, s) + player.OnRehold() + player.SendPlayerInfo() + return nil + } + + // 没有缓存数据 + if PlayerMgrSington.StartLoading(cspl.GetAccId(), sid) { + return nil + } + + PlayerCacheMgrSingleton.UnCacheInvalidPlayerId(ls.als.acc.SnId) + PlayerCacheMgrSingleton.Get(ls.als.acc.Platform, ls.als.acc.SnId, func(pd *PlayerCacheItem, async, isnew bool) { + send := func(code player_proto.OpResultCode) { + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: code, + } + proto.SetDefaults(scPlayerData) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData, s) + } + + if pd == nil { + send(player_proto.OpResultCode_OPRC_Login_CreateFailed) + return + } + + sid := PlayerMgrSington.EndPlayerLoading(cspl.GetAccId()) + + var promoterID string + var promoterCfg *PromoterConfig + f := func() { + PlayerMgrSington.AddPlayer(sid, pd.PlayerData, s) + player := PlayerMgrSington.GetPlayer(sid) + if player == nil { + return + } + + if !player.IsRob { + if _, ok := BlackListMgrSington.CheckLogin(pd.PlayerData); !ok { + //黑名单用户也需要调用一下onlogin,否则会导致数据无法刷新 + player.OnLogined() + player.Kickout(common.KickReason_Freeze) + return + } + } + + if promoterID != "" { + key, err := GetPromoterKey(0, promoterID, "") + if err == nil { + promoterCfg = PromoterMgrSington.GetConfig(key) + if promoterCfg != nil && promoterCfg.IsInviteRoot > 0 { + player.BeUnderAgentCode = promoterID + } + } + } + + var temp []byte + var ee error + di := cspl.GetDeviceInfo() + if di != "" { + var e common.Encryptor + e.Init(common.GetAppId(), player.PackageID, int32(cspl.GetTimeStamp())) + temp, ee = base64.StdEncoding.DecodeString(di) + if ee == nil { + e.Encrypt(temp, len(temp)) + } + } + + if isnew { //新用户赠送金币 + //首次创建账号事件 + //isBind := 0 + //if pd.Tel != "" { + // isBind = 1 + //} + //LogChannelSingleton.WriteMQData(model.GeneratePlayerEvent(model.WEBEVENT_LOGIN, pd.Platform, pd.PackageID, pd.SnId, pd.Channel, pd.BeUnderAgentCode, pd.PromoterTree, 1, 1, isBind, common.GetAppId())) + + //newbieCoin := player.GetRegisterPrize() + //if newbieCoin > 0 { + // player.AddCoin(int64(newbieCoin), common.GainWay_NewPlayer, "system", "") + // //增加泥码 + // player.AddDirtyCoin(0, int64(newbieCoin)) + // player.ReportSystemGiveEvent(newbieCoin, common.GainWay_NewPlayer, true) + // player.AddPayCoinLog(int64(newbieCoin), model.PayCoinLogType_Coin, "NewPlayer") + // LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(pd.SnId, pd.Name, pd.Platform, model.SystemFreeGive_GiveType_NewPlayer, model.SystemFreeGive_CoinType_Coin, int64(newbieCoin))) + //} + //if player.InviterId > 0 { + // //actRandCoinMgr.OnPlayerInvite(player.Platform, player.InviterId) + //} + } + //测试代码 + + playerFunc(player, cspl) + player.OnLogined() + player.SendPlayerInfo() + return + } + + if pd.InviterId != 0 { + // 获取推广人 + PlayerCacheMgrSingleton.UnCacheInvalidPlayerId(pd.InviterId) + PlayerCacheMgrSingleton.Get(pd.Platform, pd.InviterId, func(inviter *PlayerCacheItem, async, isnew bool) { + if inviter != nil { + promoterID = inviter.BeUnderAgentCode + } + f() + }, false) + } else { + f() + } + }, true) + + return nil +} + +func CSQueryPlayer(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSQueryPlayerHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSQueryPlayer); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSQueryPlayerHandler p == nil") + return nil + } + destSnId := msg.GetSnId() + send := func(f *model.Friend) { + pack := &player_proto.SCQueryPlayer{ + SnId: f.SnId, + Name: f.Name, + Head: f.Head, + Sex: f.Sex, + Coin: f.Coin, + Diamond: f.Diamond, + VCard: f.VCard, + HeadUrl: f.HeadUrl, + Signature: f.Signature, + Age: f.Age, + GameID: f.GameID, + } + //IsFriend + pack.IsFriend = FriendMgrSington.IsFriend(p.Platform, p.SnId, f.SnId) + //IsShield + pack.IsShield = FriendMgrSington.IsShield(p.Platform, p.SnId, f.SnId) + //Role + roleInfo := f.Roles + if roleInfo != nil { + for id, level := range roleInfo { + role := &player_proto.RoleOrPet{ + Id: id, + Level: level, + } + roleData := PetMgrSington.GetIntroductionByModId(id) + if roleData != nil { + role.Name = roleData.Name + } + pack.Roles = append(pack.Roles, role) + } + } + //Pet + petInfo := f.Pets + if petInfo != nil { + for id, level := range petInfo { + pet := &player_proto.RoleOrPet{ + Id: id, + Level: level, + } + petData := PetMgrSington.GetIntroductionByModId(id) + if petData != nil { + pet.Name = petData.Name + } + pack.Pets = append(pack.Pets, pet) + } + } + ok := p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_QUERYPLAYER), pack) + logger.Logger.Trace("SCQueryPlayerHandler ok: ", ok, " pack: ", pack) + } + + player := PlayerMgrSington.GetPlayerBySnId(destSnId) + if player != nil && player.IsRob { + pack := &player_proto.SCQueryPlayer{ + SnId: player.SnId, + Name: player.Name, + Head: player.Head, + Sex: player.Sex, + Coin: player.Coin, + Diamond: player.Diamond, + HeadUrl: player.HeadUrl, + Signature: player.Signature, + Age: player.Age, + GameID: player.GameID, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_QUERYPLAYER), pack) + logger.Logger.Trace("SCQueryPlayerHandler ok: ", ok, " pack: ", pack) + } else { // 真人去friend取信息 + friend := FriendMgrSington.GetPlayer(p.Platform, destSnId) + if friend != nil { + send(friend) + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryFriendBySnid(p.Platform, destSnId) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if ret, ok := data.(*model.Friend); ok && ret != nil { + send(ret) + } + })).Start() + } + } + } + return nil +} + +func CSSavePlayerInfo(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSSavePlayerInfo %v", data) + msg, ok := data.(*player_proto.CSSavePlayerInfo) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + if msg.GetName() != "" && msg.GetName() != p.Name { + PlayerSubjectSign.UpdateName(p.SnId, msg.GetName()) + } + + if msg.GetHead() != 0 && msg.GetHeadUrl() != p.HeadUrl { + PlayerSubjectSign.UpdateHead(p.SnId, msg.GetHead()) + } + + if msg.GetHeadUrl() != "" && msg.GetHeadUrl() != p.HeadUrl { + PlayerSubjectSign.UpdateHeadUrl(p.SnId, msg.GetHeadUrl()) + } + + if msg.GetSex() != 0 && msg.GetSex() != p.Sex { + p.Sex = msg.GetSex() + } + + if msg.GetAge() > 0 && msg.GetAge() != p.Age { + p.Age = msg.GetAge() + } + + if msg.GetSignature() != "" && msg.GetSignature() != p.Signature && len([]rune(msg.GetSignature())) < 100 { + p.Signature = msg.GetSignature() + } + + if msg.GetRoleID() > 0 && msg.GetRoleID() != p.Roles.ModId { + if _, ok := p.Roles.ModUnlock[msg.GetRoleID()]; ok { + p.Roles.ModId = msg.GetRoleID() + } + } + + if msg.GetPetID() > 0 { + if _, ok := p.Pets.ModUnlock[msg.GetPetID()]; ok { + if msg.GetPetID() == p.Pets.ModId { + p.Pets.ModId = 0 + } else { + p.Pets.ModId = msg.GetPetID() + } + } + } + + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCSavePlayerInfo), &player_proto.SCSavePlayerInfo{ + OpRetCode: player_proto.OpResultCode_OPRC_Sucess, + }) + + return nil +} + +func CSHeadUrl(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSHeadUrl Process recv ", data) + msg, ok := data.(*player_proto.CSHeadUrl) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSHeadUrl p == nil") + return nil + } + + if len(msg.GetUrl()) >= 500 { + logger.Logger.Warn("CSHeadUrl msg.GetUrl() >= 500") + return nil + } + + PlayerSubjectSign.UpdateHeadUrl(p.SnId, msg.GetUrl()) + + pack := &player_proto.SCHeadUrl{ + OpRetCode: player_proto.OpResultCode_OPRC_Sucess, + Url: p.HeadUrl, + } + + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_HeadUrl), pack) + return nil +} + +func CSBindTelInfo(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBindTelInfo Process ", data) + _, ok := data.(*player_proto.CSBindTelInfo) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Error("CSBindTelInfo p == nil", sid) + return nil + } + + plt := PlatformMgrSingleton.GetPlatform(p.Platform) + if plt == nil { + logger.Logger.Error("CSBindTelInfo platform is nil", sid) + return nil + } + + pack := &player_proto.SCBindTelInfo{ + BindTelReward: make(map[int32]int64), + } + for k, v := range plt.BindTelReward { + pack.BindTelReward[common.ToItemId(k)] = v + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCBindTelInfo), pack) + return nil +} + +var telRule, _ = regexp.Compile(`^(\d{2,15})$`) + +func CSPlayerSMSCode(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerSMSCode Process ", data) + msg, ok := data.(*player_proto.CSPlayerSMSCode) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCPlayerSMSCode{ + Code: code, + } + proto.SetDefaults(pack) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SCPlayerSMSCode), pack, s) + logger.Logger.Trace("SCPlayerSMSCode send pack ", code) + } + + if msg.GetTel() == "" || msg.GetAreaCode() == "" { + sendPack(player_proto.OpResultCode_OPRC_TelError) + return nil + } + + tel := fmt.Sprint(msg.GetAreaCode(), msg.GetTel()) + // tel + if !telRule.MatchString(tel) { + sendPack(player_proto.OpResultCode_OPRC_TelError) + return nil + } + + // 验证图片验证码 + imageCodeKey := common.GetImageCodeKey(strconv.FormatInt(sid, 10)) + imageCode := CacheMemory.Get(imageCodeKey) + if imageCode != nil { + if imageCode != msg.GetImageCode() { + sendPack(player_proto.OpResultCode_OPRC_ImageVerifyCodeFailed) + return nil + } + CacheMemory.Delete(imageCodeKey) + } + + var telKey string + var snid int32 + if p != nil { + msg.Platform = p.Platform + snid = p.SnId + } + + var validTime time.Duration + switch msg.GetTypeID() { + case 0, common.SMSCodeTelBind: + msg.TypeID = common.SMSCodeTelBind // 旧接口是0 + telKey = common.GetBindTelCodeKey(msg.GetPlatform(), tel) + validTime = common.SMSCodeValidTimeTelBind + + case common.SMSCodeTelLogin: + telKey = common.GetTelLoginCodeKey(msg.GetPlatform(), tel) + validTime = common.SMSCodeValidTimeTelLogin + + default: + logger.Logger.Errorf("CSPlayerSMSCode typeID error %d", msg.GetTypeID()) + sendPack(player_proto.OpResultCode_OPRC_Error) + return nil + } + + key := fmt.Sprintf("key%s", telKey) + code := CacheMemory.Get(key) // 频率限制 + if code != nil { + sendPack(player_proto.OpResultCode_OPRC_Frequently) + return nil + } + + if model.GameParamData.FakeVerifyCode != "" { + CacheMemory.Put(telKey, model.GameParamData.FakeVerifyCode, int64(validTime)) + CacheMemory.Put(key, model.GameParamData.FakeVerifyCode, common.SMSCodeValidTime) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + return nil + } + + //先设置注册码,防止多次注册 + smsCode := common.RandSmsCode() + CacheMemory.Put(telKey, smsCode, int64(validTime)) + CacheMemory.Put(key, smsCode, common.SMSCodeValidTime) + logger.Logger.Trace("CSPlayerSMSCode smsCode ", smsCode) + + var res []byte + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + param := &webapi_proto.ASSendSms{ + Snid: snid, + Phone: tel, + Code: smsCode, + Platform: msg.GetPlatform(), + TypeID: msg.GetTypeID(), + } + res, err = webapi.ApiSendSMS(common.GetAppId(), param) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err != nil || res == nil { + logger.Logger.Errorf("API_SendSms err %v", err) + sendPack(player_proto.OpResultCode_OPRC_Error) + return + } + + var info webapi_proto.SASendSms + proto.Unmarshal(res, &info) + + if info.Tag == webapi_proto.TagCode_SUCCESS { + CacheMemory.Put(telKey, smsCode, int64(validTime)) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + } else { + if info.Tag == webapi_proto.TagCode_Limit { + logger.Logger.Warnf("API_SendSms Limit tel:%s", tel) + sendPack(player_proto.OpResultCode_OPRC_SMSCodeLimit) + return + } + logger.Logger.Errorf("API_SendSms err %v", err) + sendPack(player_proto.OpResultCode_OPRC_Error) + } + }), "API_SendSms").Start() + return nil +} + +func CSBindTel(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBindTel Process ", data) + msg, ok := data.(*player_proto.CSBindTel) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Error("CSBindTel p == nil", sid) + return nil + } + + //test + //if model.GameParamData.FakeVerifyCode != "" && msg.GetTel() == "0" { + // CacheMemory.Delete(fmt.Sprintf("%v-%v", p.Tel, p.Platform)) + // a := PlayerMgrSington.GetPlayerByAccount(p.AccountId) + // if a != nil { + // a.Tel = "" + // } + // p.Tel = "" + // return nil + //} + //test + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCBindTel{ + Code: code, + } + proto.SetDefaults(pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCBindTel), pack) + logger.Logger.Tracef("SCBindTel send pack %v", pack) + } + + if p.Tel != "" { + sendPack(player_proto.OpResultCode_OPRC_TelIsExist) + return nil + } + + if msg.GetTel() == "" || msg.GetAreaCode() == "" { + sendPack(player_proto.OpResultCode_OPRC_TelError) + return nil + } + + //验证手机号 + //tel := msg.GetTel() + //if !reTelRule.MatchString(tel) { + // sendPack(player_proto.OpResultCode_OPRC_TelError.Enum()) + // return nil + //} + + tel := fmt.Sprint(msg.GetAreaCode(), msg.GetTel()) + telKey := common.GetBindTelCodeKey(p.Platform, tel) + + //验证验证码 + code := CacheMemory.Get(telKey) + if code == nil { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + + if msg.GetCode() == "" { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + + verifyCode, ok := code.(string) + if !ok || verifyCode == "" { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + + if verifyCode != msg.GetCode() { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return nil + } + + var snid int32 + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + //if model.GameParamData.FakeVerifyCode == "" { + // 手机号是否已经被绑定 + snid, _ = model.GetPlayerTel(tel, p.Platform, 0) // tagKey没用了,给0就行 + if snid != 0 { + return nil + } + // 绑定手机号 + if err = model.BindTelAccount(p.AccountId, tel, p.Platform); err != nil { + logger.Logger.Errorf("BindTelAccount err: %v", err) + return nil + } + + if err = model.UpdatePlayerTel(p.Platform, p.SnId, tel); err != nil { + logger.Logger.Errorf("UpdatePlayerTel err: %v", err) + return nil + } + //} + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + sendPack(player_proto.OpResultCode_OPRC_VerificationCodeError) + return + } + + if snid != 0 { + sendPack(player_proto.OpResultCode_OPRC_TelIsExist) + return + } + + // 首次绑定赠送奖励 + if p.Tel == "" { + a := PlayerMgrSington.GetPlayerByAccount(p.AccountId) + if a != nil { + a.Tel = tel + } + p.Tel = tel + p.BindTelReward() + } + + // 删除验证码 + //CacheMemory.Delete(telKey) + sendPack(player_proto.OpResultCode_OPRC_Sucess) + })).StartByFixExecutor(fmt.Sprintf("Platform%v", p.Platform)) + return nil +} + +func CSGetImageVerifyCode(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSGetImageVerifyCode ", data) + if _, ok := data.(*player_proto.CSGetImageVerifyCode); ok { + sendPack := func(code player_proto.OpResultCode, data string) { + pack := &player_proto.SCGetImageVerifyCode{ + OpRetCode: code, + ImageData: proto.String(data), + } + proto.SetDefaults(pack) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_GETIMAGEVERIFYCODE), pack, s) + logger.Logger.Tracef("SCGetImageVerifyCode ", pack) + } + + var err error + var b []byte + imgVerifyMsg := &webapi_proto.SAGetImgVerify{} + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + param := &webapi_proto.ASGetImgVerify{ + //Tel: msg.GetTel(), + } + b, err = webapi.ApiGetImageVerify(common.GetAppId(), param) + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err != nil || len(b) == 0 { + sendPack(player_proto.OpResultCode_OPRC_Error, "") + return + } + err = proto.Unmarshal(b, imgVerifyMsg) + if err != nil || imgVerifyMsg.GetTag() != webapi_proto.TagCode_SUCCESS || + imgVerifyMsg.GetImageData() == "" || imgVerifyMsg.GetCode() == "" { + sendPack(player_proto.OpResultCode_OPRC_Error, "") + return + } + key := common.GetImageCodeKey(strconv.FormatInt(sid, 10)) + CacheMemory.Put(key, imgVerifyMsg.Code, 600) + logger.Logger.Infof("@CacheMemory.Put(key:%v, val:%v)", key, imgVerifyMsg.Code) + sendPack(player_proto.OpResultCode_OPRC_Sucess, imgVerifyMsg.ImageData) + }), "API_GetImgVerify").Start() + } + return nil +} + +func CSGetImageVerifyCodeLocal(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSGetImageVerifyCode ", data) + if _, ok := data.(*player_proto.CSGetImageVerifyCode); ok { + sendPack := func(code player_proto.OpResultCode, data string) { + pack := &player_proto.SCGetImageVerifyCode{ + OpRetCode: code, + ImageData: proto.String(data), + } + proto.SetDefaults(pack) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_GETIMAGEVERIFYCODE), pack, s) + logger.Logger.Tracef("SCGetImageVerifyCode ", pack) + } + + var err error + var b64, answer string + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + b64, answer, err = common.ImageCode() + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err != nil { + sendPack(player_proto.OpResultCode_OPRC_Error, "") + return + } + + key := common.GetImageCodeKey(strconv.FormatInt(sid, 10)) + CacheMemory.Put(key, answer, 600) + logger.Logger.Infof("@CacheMemory.Put(key:%v, val:%v)", key, answer) + sendPack(player_proto.OpResultCode_OPRC_Sucess, b64) + }), "API_GetImgVerify").Start() + } + return nil +} + +func CSImageVerifyCode(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSImageVerifyCode Process ", data) + msg, ok := data.(*player_proto.CSImageVerifyCode) + if !ok { + return nil + } + + sendPack := func(code player_proto.OpResultCode) { + pack := &player_proto.SCImageVerifyCode{ + OpRetCode: code, + } + proto.SetDefaults(pack) + common.SendToGate(sid, int(player_proto.PlayerPacketID_PACKET_SC_ImageVerifyCode), pack, s) + logger.Logger.Trace("SCImageVerifyCode send pack ", code) + } + + imageCodeKey := common.GetImageCodeKey(strconv.FormatInt(sid, 10)) + imageCode := CacheMemory.Get(imageCodeKey) + if imageCode != nil { + if imageCode != msg.GetCode() { + sendPack(player_proto.OpResultCode_OPRC_ImageVerifyCodeFailed) + return nil + } + CacheMemory.Delete(imageCodeKey) + } + + sendPack(player_proto.OpResultCode_OPRC_Sucess) + return nil +} + +func CSBillList(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSBillList Process %v", data) + msg, ok := data.(*player_proto.CSBillList) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + ret := &player_proto.SCBillList{} + + if msg.GetTs() <= 0 { + msg.Ts = time.Now().Unix() + } + startTs := msg.GetTs() - 24*60*60 + if startTs <= 0 { + return nil + } + endTs := msg.GetTs() + + if msg.GetPageNo() < 0 { + msg.PageNo = 0 + } + if msg.GetPageSize() > 100 { + msg.PageSize = 100 + } + if msg.GetPageSize() <= 0 { + msg.PageSize = 10 + } + + fromIndex := msg.GetPageNo() * msg.GetPageSize() + toIndex := fromIndex + msg.GetPageSize() + + logs, _, err := model.GetCoinLogGame(p.Platform, p.SnId, common.BillTypeCoin, startTs, endTs, int(fromIndex), int(toIndex)) + if err != nil { + logger.Logger.Errorf("GetCoinLogGame err:%v", err) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCBillList), ret) + return err + } + + for _, v := range logs { + ret.Items = append(ret.Items, &player_proto.BillItem{ + Ts: v.Ts, + Id: v.LogId.Hex(), + BillType: int64(v.LogType), + Amount: v.Count, + Balance: v.RestCount, + GameID: int64(v.InGame), + GameFreeID: v.GameFreeId, + BaseCoin: v.BaseCoin, + }) + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCBillList), ret) + logger.Logger.Tracef("SCBillList pageNo:%d, pageSize:%d %v", msg.GetPageNo(), msg.GetPageSize(), ret) + return nil +} + +func CSADV(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSADV Process %v", data) + msg, ok := data.(*player_proto.CSADV) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + TaskSubjectSingleton.Touch(common.TaskTypeAdv, &TaskData{ + SnId: p.SnId, + Num: 1, + }) + + ret := &player_proto.SCADV{ + Param: msg.GetParam(), + } + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SCADV), ret) + logger.Logger.Tracef("SCADV %v", ret) + return nil +} diff --git a/worldsrv/action_rankmatch.go b/worldsrv/action_rankmatch.go new file mode 100644 index 0000000..ac60bb8 --- /dev/null +++ b/worldsrv/action_rankmatch.go @@ -0,0 +1,278 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/rankmatch" + "mongo.games.com/game/srvdata" +) + +func init() { + // 赛季信息 + common.Register(int(rankmatch.RankMatch_PACKET_RM_CSRMSeasonInfo), rankmatch.CSRMSeasonInfo{}, CSRMSeasonInfo) + // 段位表 + common.Register(int(rankmatch.RankMatch_PACKET_RM_CSRMRankConfig), rankmatch.CSRMRankConfig{}, CSRMRankConfig) + // 段位奖励 + common.Register(int(rankmatch.RankMatch_PACKET_RM_CSRMAwardConfig), rankmatch.CSRMAwardConfig{}, CSRMAwardConfig) + // 领取段位奖励 + common.Register(int(rankmatch.RankMatch_PACKET_RM_CSRMAward), rankmatch.CSRMAward{}, CSRMAward) +} + +// CSRMSeasonInfo 赛季信息 +func CSRMSeasonInfo(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRMSeasonInfo") + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSRMSeasonInfo p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSRMSeasonInfo Platform == Default_Platform.") + return nil + } + + pack := &rankmatch.SCRMSeasonInfo{} + + cfg := RankMgrSingleton.GetSeasonConfig(p.Platform) + if cfg == nil { + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMSeasonInfo), pack) + return nil + } + + pack = &rankmatch.SCRMSeasonInfo{ + Id: cfg.SeasonId, + TimeStamp: []int64{cfg.StartTs, cfg.EndTs}, + } + + rank := RankMgrSingleton.GetPlayerSeason(p.SnId) + if rank != nil && rank.RankType != nil { + for k, v := range rank.RankType { + if v == nil { + continue + } + info := &rankmatch.SeasonInfo{Id: k} + info.Lv, info.Score = RankMgrSingleton.GetPlayerRankLV(k, p.SnId) + info.LastLv, info.LastScore = RankMgrSingleton.GetPlayerLastRankLV(k, p.SnId) + pack.Seasons = append(pack.Seasons, info) + } + } + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMSeasonInfo), pack) + logger.Logger.Trace("SCTMSeasonInfo:", pack) + return nil +} + +// CSRMRankConfig 段位列表 +func CSRMRankConfig(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSRMRankConfig data:%v", data) + msg, ok := data.(*rankmatch.CSRMRankConfig) + if !ok { + return nil + } + + if msg.GetId() <= 0 { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSRMRankConfig p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSRMRankConfig Platform == Default_Platform.") + return nil + } + + pack := &rankmatch.SCRMRankConfig{} + for _, v := range RankMgrSingleton.GetRankList(msg.GetId()) { + pack.Items = append(pack.Items, &rankmatch.RankItem{ + Id: v.Id, + Lv: v.Lv, + Score: v.Score, + }) + } + + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMRankConfig), pack) + logger.Logger.Trace("SCRMRankConfig:", pack) + return nil +} + +// CSRMAwardConfig 段位奖励 +func CSRMAwardConfig(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSRMAwardConfig data:%v", data) + msg, ok := data.(*rankmatch.CSRMAwardConfig) + if !ok { + return nil + } + + if msg.GetId() <= 0 { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSRMAwardConfig p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSRMAwardConfig Platform == Default_Platform.") + return nil + } + + rank := RankMgrSingleton.GetPlayerSeason(p.SnId) + + lv, _ := RankMgrSingleton.GetPlayerRankLV(msg.GetId(), p.SnId) + + pack := &rankmatch.SCRMAwardConfig{} + + for _, item := range RankMgrSingleton.GetRankAwardList(msg.GetId()) { + // 1 可领取 2已领取 + if lv >= item.Lv && rank != nil { + item.ReceiveType = 1 // 可领取 + // 是否已领取 + if info := rank.RankType[msg.GetId()]; info != nil { + for _, v := range info.Awards { + if v.Id == item.Id { + if v.Ts > 0 { + item.ReceiveType = 2 // 已领取 + } + break + } + } + } + } + + pack.List = append(pack.List, item) + } + + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMAwardConfig), pack) + logger.Logger.Trace("SCRMAwardConfig:", pack) + return nil +} + +// CSRMAward 领取段位奖励 +func CSRMAward(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSRMAward data:%v", data) + msg, ok := data.(*rankmatch.CSRMAward) + if !ok { + return nil + } + + if msg.GetId() <= 0 { + return nil + } + + d := srvdata.PBDB_RankRewardMgr.GetData(msg.AwardId) + if d == nil { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSRMAward p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSRMAward Platform == Default_Platform.") + return nil + } + + rank := RankMgrSingleton.GetPlayerSeason(p.SnId) + if rank == nil { + return nil + } + + pack := &rankmatch.SCRMAward{ + Code: 1, // 1 失败 + Id: msg.GetAwardId(), + } + + var isAward bool + if info := rank.RankType[msg.GetId()]; info != nil { + for _, v := range info.Awards { + if v.Id == msg.GetAwardId() { + // 是否已领取 + if v.Ts > 0 { + isAward = true + } + break + } + } + } + + if isAward { + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMAward), pack) + logger.Logger.Trace("SCRMAward already receive:", pack) + return nil + } + + // 是否可领取 + lv, _ := RankMgrSingleton.GetPlayerRankLV(msg.GetId(), p.SnId) + if lv >= d.GetLevel() { + // 可以领取 + isAward = true + } + + // 领取奖励 + if isAward { + pack.Code = 0 // 领取成功 + rank.RankType[msg.GetId()].Awards = append(rank.RankType[msg.GetId()].Awards, &model.RankAward{ + Id: msg.AwardId, + Ts: time.Now().Unix(), + }) + rank.Dirty = true + p.dirty = true + + type Param struct { + Id int32 + Num int32 + } + f := func(v *Param) { + if v.Num > 0 { + switch { + case v.Id <= 0: + case v.Id == 1: //金币 + p.AddCoin(int64(v.Num), 0, common.GainWay_RankMatch, "system", "段位奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, + model.SystemFreeGive_GiveType_RankMatch, model.SystemFreeGive_CoinType_Coin, int64(v.Num))) + case v.Id == 2: //钻石 + p.AddDiamond(int64(v.Num), 0, common.GainWay_RankMatch, "system", "段位奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, + model.SystemFreeGive_GiveType_RankMatch, model.SystemFreeGive_CoinType_Diamond, int64(v.Num))) + default: + //道具 + item := &Item{ + ItemId: v.Id, + ItemNum: int64(v.Num), + } + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_RankReward, "system", "段位奖励") + itemData := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, itemData.Name, int64(item.ItemNum), "段位奖励") + } + } + } + } + f(&Param{ + Id: d.Award1Id, + Num: d.Award1Num, + }) + f(&Param{ + Id: d.Award2Id, + Num: d.Award2Num, + }) + f(&Param{ + Id: d.Award3Id, + Num: d.Award3Num, + }) + } + + p.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMAward), pack) + logger.Logger.Trace("SCRMAward:", pack) + return nil +} diff --git a/worldsrv/action_server.go b/worldsrv/action_server.go new file mode 100644 index 0000000..1c7d713 --- /dev/null +++ b/worldsrv/action_server.go @@ -0,0 +1,730 @@ +package main + +import ( + "strconv" + "strings" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + gamehall_proto "mongo.games.com/game/protocol/gamehall" + login_proto "mongo.games.com/game/protocol/login" + player_proto "mongo.games.com/game/protocol/player" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +func init() { + // 销毁房间 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_DESTROYSCENE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWDestroyScene{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_DESTROYSCENE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive GWDestroyScene:", pack) + if msg, ok := pack.(*server_proto.GWDestroyScene); ok { + SceneMgrSingleton.DestroyScene(int(msg.GetSceneId()), msg.GetIsCompleted()) + } + return nil + })) + + // 离开房间 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_PLAYERLEAVE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWPlayerLeave{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_PLAYERLEAVE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + if msg, ok := pack.(*server_proto.GWPlayerLeave); ok { + logger.Logger.Trace("receive GWPlayerLeave:", msg.GetPlayerId()) + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene != nil { + p := PlayerMgrSington.GetPlayerBySnId(msg.GetPlayerId()) + if p != nil { + data := msg.GetPlayerData() + if len(data) != 0 { + logger.Logger.Trace("GWPlayerLeave p.UnmarshalData(data)") + p.UnmarshalData(data, scene) + } + switch { + case scene.IsCoinScene(): + if !CoinSceneMgrSingleton.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerLeave snid:%v sceneid:%v gameid:%v modeid:%v [coinscene]", + p.SnId, scene.sceneId, scene.gameId, scene.gameMode) + } + case scene.IsMatchScene(): + if !MatchSceneMgrSingleton.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerLeave snid:%v sceneid:%v gameid:%v modeid:%v matchid:%v [matchscene]", + p.SnId, scene.sceneId, scene.gameId, scene.gameMode, scene.matchId) + } else { + //结算积分 + if !p.IsRob { + TournamentMgr.UpdateMatchInfo(p, msg.MatchId, int32(msg.GetReturnCoin()), int32(msg.GetCurIsWin()), msg.GetMatchRobotGrades()) + } else { + p.matchCtx = nil + } + } + case scene.IsHundredScene(): + if !HundredSceneMgrSington.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerLeave snid:%v sceneid:%v gameid:%v modeid:%v [hundredcene]", + p.SnId, scene.sceneId, scene.gameId, scene.gameMode) + } + default: + if scene.ClubId > 0 { + //if club, ok := clubManager.clubList[scene.ClubId]; ok { + // if cp, ok1 := club.memberList[p.SnId]; ok1 { + // cp.GameCount += msg.GetGameTimes() + // cp.DayCoin += msg.GetTotalConvertibleFlow() - p.TotalConvertibleFlow + // } + //} + //if !ClubSceneMgrSington.PlayerLeave(p, int(msg.GetReason())) { + // logger.Logger.Warnf("Club leave room msg snid:%v sceneid:%v gameid:%v modeid:%v [coinscene]", + // p.SnId, scene.sceneId, scene.gameId, scene.mode) + // scene.PlayerLeave(p, int(msg.GetReason())) + //} + } else { + scene.PlayerLeave(p, int(msg.GetReason())) + } + } + + if p.scene != nil { + logger.Logger.Warnf("after GWPlayerLeave found snid:%v sceneid:%v gameid:%v modeid:%v", p.SnId, p.scene.sceneId, p.scene.gameId, p.scene.gameMode) + } + + // 同步排位积分 + if scene.IsRankMatch() { + if p.IsRob { + RankMgrSingleton.UpdateRobotSeason(p.Platform, p.SnId, scene.dbGameFree.GetRankType(), + msg.GetRankScore()[scene.dbGameFree.GetRankType()], p.Name, p.Sex, p.HeadUrl, p.Coin, p.PlayerData.GetRoleId()) + } else { + RankMgrSingleton.UpdatePlayerSeason(p.SnId, msg.GetRankScore()) + } + } + + //比赛场不处理下面的内容 + if !scene.IsMatchScene() { + // 破产检测 + sdata := srvdata.PBDB_GameSubsidyMgr.GetData(GameSubsidyid) + if sdata != nil { + if !p.IsRob && p.takeCoin > msg.GetReturnCoin() && p.takeCoin >= int64(sdata.LimitNum) && msg.GetReturnCoin() < int64(sdata.LimitNum) { + CostCoin := p.takeCoin - msg.GetReturnCoin() + + logger.Logger.Infof("NewBankruptLogEx: snid:%v GetReturnCoin:%v coin:%v CostCoin:%v", p.SnId, msg.GetReturnCoin(), p.takeCoin, CostCoin) + log := model.NewBankruptLogEx(p.SnId, scene.dbGameFree.GetId(), p.CreateTime.Unix(), CostCoin, p.Platform, scene.gameId) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } + // 破产检测 + + oldCoin := p.Coin + + //带回金币 + if p.Coin != msg.GetReturnCoin() { + p.Coin = msg.GetReturnCoin() + if p.Coin < 0 { + p.Coin = 0 + } + + p.dirty = true + } + + logger.Logger.Infof("SSPacketID_PACKET_GW_PLAYERLEAVE: snid:%v oldcoin:%v coin:%v", p.SnId, oldCoin, p.Coin) + p.diffData.Coin = -1 //强制更新金币 + p.diffData.TotalConvertibleFlow = -1 //强制更新流水 + p.SendDiffData() //只是把差异发给前端 + + gameCoinTs := msg.GetGameCoinTs() + if !p.IsRob && !scene.IsTestScene() { + // 同步背包数据 + diffItems := []*Item{} + dbItemArr := srvdata.PBDB_GameItemMgr.Datas.Arr + if dbItemArr != nil { + items := msg.GetItems() + if items != nil { + for _, dbItem := range dbItemArr { + if itemNum, exist := items[dbItem.Id]; exist { + oldItem := BagMgrSingleton.GetItem(p.SnId, dbItem.Id) + diffNum := int64(itemNum) + if oldItem != nil { + diffNum = int64(itemNum) - oldItem.ItemNum + } + if diffNum != 0 { + item := &Item{ + ItemId: dbItem.Id, + ItemNum: diffNum, + } + diffItems = append(diffItems, item) + } + } + } + } + } + if diffItems != nil && len(diffItems) != 0 { + BagMgrSingleton.AddJybBagInfo(p, diffItems, 0, 0, "", "") + } + + //对账点同步 + if p.GameCoinTs < gameCoinTs { + p.GameCoinTs = gameCoinTs + p.dirty = true + } + + //破产统计 + //if int(msg.GetReason()) == common.PlayerLeaveReason_Bekickout { + // if len(scene.paramsEx) > 0 { + // gameIdEx := scene.paramsEx[0] + // gps := PlatformMgrSingleton.GetGameFree(scene.limitPlatform.IdStr, scene.paramsEx[0]) + // if gps != nil { + // lowLimit := gps.DbGameFree.GetLowerThanKick() + // if lowLimit != 0 && p.Coin+p.SafeBoxCoin < int64(lowLimit) { + // p.ReportBankRuptcy(int32(scene.gameId), int32(scene.gameMode), gameIdEx) + // } + // } + // } + //} + } + } + } else { + logger.Logger.Tracef("GWPlayerLeave LocalRobotIdMgrSington %v", msg.GetPlayerId()) + //LocalRobotIdMgrSington.FreeId(msg.GetPlayerId()) + } + } + } + return nil + })) + + // 观众离开房间 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_AUDIENCELEAVE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWPlayerLeave{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_AUDIENCELEAVE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive PACKET_GW_AUDIENCELEAVE GWPlayerLeave:", pack) + if msg, ok := pack.(*server_proto.GWPlayerLeave); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene != nil { + p := PlayerMgrSington.GetPlayerBySnId(msg.GetPlayerId()) + if p != nil { + gameCoinTs := msg.GetGameCoinTs() + //带回的金币 + if p.Coin != msg.GetReturnCoin() && !scene.IsMatchScene() { + p.Coin = msg.GetReturnCoin() + if p.Coin < 0 { + p.Coin = 0 + } + p.dirty = true + } + + //对账点同步 + if p.GameCoinTs != gameCoinTs { + p.GameCoinTs = gameCoinTs + p.dirty = true + } + + CoinSceneMgrSingleton.PlayerLeave(p, int(msg.GetReason())) + + //变化金币 + p.dirty = true + p.diffData.Coin = -1 //强制更新金币 + p.diffData.TotalConvertibleFlow = -1 //强制更新流水 + p.SendDiffData() + } else { + //LocalRobotIdMgrSington.FreeId(msg.GetPlayerId()) + } + } + } + return nil + })) + + // 房间游戏开始 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_SCENESTART), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWSceneStart{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_SCENESTART), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_SCENESTART GWSceneStart:", pack) + if msg, ok := pack.(*server_proto.GWSceneStart); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene != nil { + scene.starting = msg.GetStart() + scene.currRound = msg.GetCurrRound() + scene.totalRound = msg.GetMaxRound() + scene.lastTime = time.Now() + if scene.starting { + if scene.currRound == 1 { + scene.startTime = time.Now() + //p := PlayerMgrSington.GetPlayer(s.Sid) + } + } + if scene.starting { + logger.Logger.Trace("游戏开始------", scene.gameId, scene.sceneId, scene.replayCode, scene.currRound) + } else { + logger.Logger.Trace("游戏结束------", scene.gameId, scene.sceneId, scene.replayCode, scene.currRound) + } + PlatformMgrSingleton.OnChangeSceneState(scene, scene.starting) + } + } + return nil + })) + + // 房间游戏状态 + // 捕鱼 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_SCENESTATE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWSceneState{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_SCENESTATE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_SCENESTATE GWSceneState:", pack) + if msg, ok := pack.(*server_proto.GWSceneState); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene != nil { + scene.state = msg.GetCurrState() + scene.fishing = msg.GetFishing() + PlatformMgrSingleton.OnChangeSceneState(scene, scene.starting) + } + } + return nil + })) + + // 用户状态同步 flag + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_PLAYERSTATE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWPlayerFlag{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_PLAYERSTATE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive GWPlayerFlag:", pack) + if msg, ok := pack.(*server_proto.GWPlayerFlag); ok { + player := PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if player != nil { + player.flag = msg.GetFlag() + } + } + return nil + })) + + // 房间服务器状态切换 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GB_STATE_SWITCH), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.ServerStateSwitch{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GB_STATE_SWITCH), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GB_STATE_SWITCH ServerStateSwitch:", pack) + if sr, ok := pack.(*server_proto.ServerStateSwitch); ok { + srvid := int(sr.GetSrvId()) + gameSess := GameSessMgrSington.GetGameSess(srvid) + if gameSess != nil { + if gameSess.state == common.GAME_SESS_STATE_ON { + gameSess.SwitchState(common.GAME_SESS_STATE_OFF) + } else { + gameSess.SwitchState(common.GAME_SESS_STATE_ON) + } + } else { + gateSess := GameSessMgrSington.GetGateSess(srvid) + if gateSess != nil { + if gateSess.state == common.GAME_SESS_STATE_ON { + gateSess.SwitchState(common.GAME_SESS_STATE_OFF) + } else { + gateSess.SwitchState(common.GAME_SESS_STATE_ON) + } + } + } + } + return nil + })) + + // 游戏服务器的系统广播 + // 捕鱼 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_NEWNOTICE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWNewNotice{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_NEWNOTICE), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("receive GWNewNotice:", pack) + if msg, ok := pack.(*server_proto.GWNewNotice); ok { + //立即发送改为定期发送,控制下广播包的频度 + HorseRaceLampMgrSington.PushGameHorseRaceLamp(msg.GetCh(), msg.GetPlatform(), msg.GetContent(), int32(msg.GetMsgtype()), msg.GetIsrob(), msg.GetPriority()) + } + return nil + })) + + // 同步每局游戏那些玩家一起玩的 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_SCENEPLAYERLOG), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWScenePlayerLog{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_SCENEPLAYERLOG), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("receive GWScenePlayerLog:", pack) + if msg, ok := pack.(*server_proto.GWScenePlayerLog); ok { + sceneLimitMgr.ReceiveData(msg.GetGameId(), msg.GetSnids()) + } + return nil + })) + + // 强制离开房间 + // 返回房间失败 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_PLAYERFORCELEAVE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWPlayerForceLeave{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_PLAYERFORCELEAVE), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + if msg, ok := pack.(*server_proto.GWPlayerForceLeave); ok { + logger.Logger.Warn("receive GWPlayerForceLeave:", msg) + scene := SceneMgrSingleton.GetScene(int(msg.GetRoomId())) + if scene != nil { + p := PlayerMgrSington.GetPlayerBySnId(msg.GetPlayerId()) + if p != nil { + ctx := scene.GetPlayerGameCtx(p.SnId) + if ctx != nil && p.scene == scene && scene.HasPlayer(p) && ctx.enterTs == msg.GetEnterTs() { + switch { + case scene.IsCoinScene(): + if !CoinSceneMgrSingleton.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerForceLeave snid:%v sceneid:%v gameid:%v modeid:%v [coinscene]", p.SnId, scene.sceneId, scene.gameId, scene.gameMode) + } + case scene.IsHundredScene(): + if !HundredSceneMgrSington.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerForceLeave snid:%v sceneid:%v gameid:%v modeid:%v [hundredcene]", p.SnId, scene.sceneId, scene.gameId, scene.gameMode) + } + case scene.IsMatchScene(): + if !MatchSceneMgrSingleton.PlayerLeave(p, int(msg.GetReason())) { + logger.Logger.Warnf("GWPlayerLeave snid:%v sceneid:%v gameid:%v modeid:%v matchid:%v [matchscene]", + p.SnId, scene.sceneId, scene.gameId, scene.gameMode, scene.matchId) + } + default: + scene.PlayerLeave(p, int(msg.GetReason())) + } + + if p.scene != nil { + logger.Logger.Warnf("after GWPlayerForceLeave found snid:%v sceneid:%v gameid:%v modeid:%v", p.SnId, p.scene.sceneId, p.scene.gameId, p.scene.gameMode) + p.scene.PlayerLeave(p, int(msg.GetReason())) + } + } else { + logger.Logger.Warnf("GWPlayerForceLeave snid:%v sceneid:%v gameid:%v modeid:%v fount not p.scene==scene && scene.HasPlayer(p)", p.SnId, scene.sceneId, scene.gameId, scene.gameMode) + } + } + } else { + logger.Logger.Warnf("GWPlayerForceLeave snid:%v scene:%v had closed", msg.GetPlayerId(), msg.GetRoomId()) + } + } + return nil + })) + + //一局游戏一结算 + //可处理以下业务 + //1.同步玩家游戏场内的实时金币 + //2.触发相关任务:统计有效下注金币数,赢取金币数,牌局次数等 + //3.黑名单处理 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_PLAYERDATA), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWPlayerData{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_PLAYERDATA), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive GWPlayerBet:", pack) + if msg, ok := pack.(*server_proto.GWPlayerData); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetSceneId())) + if scene == nil { + return nil + } + for _, playerBet := range msg.GetDatas() { + player := PlayerMgrSington.GetPlayerBySnId(playerBet.GetSnId()) + if player != nil && !player.IsRob { + // task 赢次数 + if playerBet.GetGain() > 0 || playerBet.WinState == 1 { + TaskSubjectSingleton.Touch(common.TaskTypeWinTimes, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: 1, + }) + } + // task 游戏局数 + TaskSubjectSingleton.Touch(common.TaskTypePlayTimes, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: 1, + }) + // task 排位赛次数 + if scene.IsRankMatch() { + TaskSubjectSingleton.Touch(common.TaskTypeRankMatchTimes, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: 1, + }) + } + if !scene.IsMatchScene() { // 比赛没金币是积分 + n := playerBet.GetGain() + if n < 0 { + n = -n + } + //游戏中输赢金币 + TaskSubjectSingleton.Touch(common.TaskTypeWinOrLose, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: n, + }) + // 游戏中赢金币 + if playerBet.GetGain() > 0 || playerBet.WinState == 1 { + TaskSubjectSingleton.Touch(common.TaskTypeWinCoin, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: playerBet.GetGain(), + }) + } + } + //tienlen 游戏场次 + if common.IsTienLen(scene.gameId) { + TaskSubjectSingleton.Touch(common.TaskTypeTienlenCount, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: 1, + }) + if playerBet.GetGain() > 0 || playerBet.WinState == 1 { + TaskSubjectSingleton.Touch(common.TaskTypeTienlenWinTimes, &TaskData{ + SnId: player.SnId, + GameID: scene.gameId, + GameFreeID: scene.dbGameFree.GetId(), + Num: 1, + }) + } + } + + if !scene.IsMatchScene() { // 比赛没金币是积分 + player.Coin = playerBet.GetCoin() + player.GameCoinTs = playerBet.GetGameCoinTs() + player.GameTax += playerBet.GetTax() + player.dirty = true + player.WBUpdate(playerBet.GetWBGain()) + } + } + } + } + return nil + })) + + //推送游戏的状态 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_GAMESTATE), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWGameState{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_GAMESTATE), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_GAMESTATE GWGameState:", pack) + if msg, ok := pack.(*server_proto.GWGameState); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetSceneId())) + if scene != nil { + scene.State = msg.GetState() + scene.StateSec = msg.GetSec() + scene.BankerListNum = msg.GetBankerListNum() + if scene.State == scene.sp.GetBetState() { + scene.StateTs = msg.GetTs() + leftTime := int64(scene.StateSec) - (time.Now().Unix() - scene.StateTs) + if leftTime < 0 { + leftTime = 0 + } + pack := &gamehall_proto.SCGameState{} + pack.List = append(pack.List, &gamehall_proto.GameState{ + GameFreeId: proto.Int32(scene.dbGameFree.GetId()), + Ts: proto.Int64(leftTime), + Sec: proto.Int32(scene.StateSec), + }) + gameStateMgr.BrodcastGameState(int32(scene.gameId), scene.limitPlatform.IdStr, + int(gamehall_proto.GameHallPacketID_PACKET_SC_GAMESTATE), pack) + } + } + } + return nil + })) + + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_JACKPOTLIST), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWGameJackList{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_JACKPOTLIST), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_JACKPOTLIST GWGameJackList:", pack) + if msg, ok := pack.(*server_proto.GWGameJackList); ok { + FishJackListMgr.Insert(msg.GetCoin(), msg.GetSnId(), msg.GetRoomId(), msg.GetJackType(), msg.GetGameId(), + msg.GetPlatform(), msg.GetChannel(), msg.GetName()) + } + return nil + })) + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_JACKPOTCOIN), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWGameJackCoin{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_JACKPOTCOIN), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_JACKPOTCOIN GWGameJackCoin:", pack) + if msg, ok := pack.(*server_proto.GWGameJackCoin); ok { + for i, pl := range msg.Platform { + FishJackpotCoinMgr.Jackpot[pl] = msg.Coin[i] + } + } + return nil + })) + + //强制换桌 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_GW_CHANGESCENEEVENT), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.GWChangeSceneEvent{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_GW_CHANGESCENEEVENT), netlib.HandlerWrapper(func(s *netlib.Session, packetid int, pack interface{}) error { + logger.Logger.Trace("receive SSPacketID_PACKET_GW_CHANGESCENEEVENT GWChangeSceneEvent:", pack) + if msg, ok := pack.(*server_proto.GWChangeSceneEvent); ok { + scene := SceneMgrSingleton.GetScene(int(msg.GetSceneId())) + if scene != nil { + scene.PlayerTryChange() + } + } + return nil + })) + + //玩家中转消息 + netlib.RegisterFactory(int(server_proto.SSPacketID_PACKET_SS_REDIRECTTOPLAYER), netlib.PacketFactoryWrapper(func() interface{} { + return &server_proto.SSRedirectToPlayer{} + })) + netlib.RegisterHandler(int(server_proto.SSPacketID_PACKET_SS_REDIRECTTOPLAYER), netlib.HandlerWrapper(func(s *netlib.Session, + packetid int, pack interface{}) error { + logger.Logger.Trace("SSRedirectToPlayer Process recv ", pack) + if msg, ok := pack.(*server_proto.SSRedirectToPlayer); ok { + p := PlayerMgrSington.GetPlayerBySnId(msg.GetSnId()) + if p == nil { + return nil + } + p.SendToClient(int(msg.GetPacketId()), msg.GetData()) + } + return nil + })) +} + +// 机器人服务器向worldsrv发送 +type CSPMCmdPacketFactory struct { +} +type CSPMCmdHandler struct { +} + +func (this *CSPMCmdPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSPMCmd{} + return pack +} + +func (this *CSPMCmdHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPMCmdHandler Process recv ", data) + if msg, ok := data.(*player_proto.CSPMCmd); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Trace("CSPMCmdHandler p == nil") + return nil + } + + if !p.IsRob && p.GMLevel < 3 { + logger.Logger.Trace("CSPMCmdHandler p.Channel != common.Channel_Rob && p.GMLevel < 3") + return nil + } + + cmd := msg.GetCmd() + logger.Logger.Infof("CSPMCmdHandler %v %v", p.SnId, cmd) + args := strings.Split(cmd, common.PMCmd_SplitToken) + argsCnt := len(args) + if argsCnt != 0 { + switch args[0] { + case common.PMCmd_AddCoin: + if argsCnt > 1 { + coin, err := strconv.ParseInt(args[1], 10, 64) //strconv.Atoi(args[1]) + if err != nil { + logger.Logger.Warnf("CSPMCmdHandler %v parse %v err:%v", p.SnId, cmd, err) + return nil + } + if coin != 0 { + p.CoinPayTotal += int64(coin) + p.dirty = true + p.AddCoin(coin, 0, common.GainWay_ByPMCmd, p.GetName(), cmd) + p.ReportSystemGiveEvent(int32(coin), common.GainWay_ByPMCmd, true) + p.SendDiffData() + } + } + case common.PMCmd_Privilege: + if p.GMLevel >= 3 { + if p.Flags&model.PLAYER_FLAGS_PRIVILEGE == 0 { + p.Flags |= model.PLAYER_FLAGS_PRIVILEGE + } else { + p.Flags &= ^model.PLAYER_FLAGS_PRIVILEGE + } + } + } + } + } + return nil +} + +type CSRobotChgDataPacketFactory struct { +} +type CSRobotChgDataHandler struct { +} + +func (this *CSRobotChgDataPacketFactory) CreatePacket() interface{} { + pack := &player_proto.CSRobotChgData{} + return pack +} + +func (this *CSRobotChgDataHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSRobotChgDataHandler Process recv ", data) + if _, ok := data.(*player_proto.CSRobotChgData); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Trace("CSRobotChgDataHandler p == nil") + return nil + } + + if !p.IsRob { + logger.Logger.Trace("CSRobotChgDataHandler !p.IsRob") + return nil + } + } + return nil +} + +type CSAccountInvalidPacketFactory struct { +} +type CSAccountInvalidHandler struct { +} + +func (this *CSAccountInvalidPacketFactory) CreatePacket() interface{} { + pack := &login_proto.CSAccountInvalid{} + return pack +} + +func (this *CSAccountInvalidHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSAccountInvalidHandler Process recv ", data) + if _, ok := data.(*login_proto.CSAccountInvalid); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p != nil && p.IsRobot() { + snid := p.SnId + acc := p.AccountId + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.RemoveAccount(p.Platform, acc) + if err != nil { + logger.Logger.Error("Remove robot account data error:", err) + } + err = model.RemovePlayerByAcc(p.Platform, acc) + if err != nil { + logger.Logger.Error("Remove robot player data error:", err) + } + logger.Logger.Trace("CSAccountInvalid message remove :", acc) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + PlayerMgrSington.DelPlayer(snid) + LoginStateMgrSington.DelAccountByAccid(acc) + return + }), "RemoveAccount").StartByFixExecutor("RemoveAccount") + } + } + return nil +} + +func init() { + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_PMCMD), &CSPMCmdHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_PMCMD), &CSPMCmdPacketFactory{}) + + common.RegisterHandler(int(player_proto.PlayerPacketID_PACKET_CS_ROBOTCHGDATA), &CSRobotChgDataHandler{}) + netlib.RegisterFactory(int(player_proto.PlayerPacketID_PACKET_CS_ROBOTCHGDATA), &CSRobotChgDataPacketFactory{}) + + common.RegisterHandler(int(login_proto.LoginPacketID_PACKET_CS_ACCOUNTINVALID), &CSAccountInvalidHandler{}) + netlib.RegisterFactory(int(login_proto.LoginPacketID_PACKET_CS_ACCOUNTINVALID), &CSAccountInvalidPacketFactory{}) +} diff --git a/worldsrv/action_shop.go b/worldsrv/action_shop.go new file mode 100644 index 0000000..45551f9 --- /dev/null +++ b/worldsrv/action_shop.go @@ -0,0 +1,652 @@ +package main + +import ( + "math/rand" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/shop" +) + +type CSShopInfoPacketFactory struct { +} + +type CSShopInfoHandler struct { +} + +func (this *CSShopInfoPacketFactory) CreatePacket() interface{} { + pack := &shop.CSShopInfo{} + return pack +} + +func (this *CSShopInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSShopInfoHandler Process recv ", data) + if msg, ok := data.(*shop.CSShopInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSShopInfoHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + shopInfos := ShopMgrSington.GetShopInfos(p, int(msg.NowLocation-1)) + pack := &shop.SCShopInfo{ + Infos: shopInfos, + } + logger.Logger.Trace("SCShopInfo:", pack, len(shopInfos)) + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_INFO), pack) + } + return nil +} + +type CSAdLookedPacketFactory struct { +} + +type CSAdLookedHandler struct { +} + +func (this *CSAdLookedPacketFactory) CreatePacket() interface{} { + pack := &shop.CSAdLooked{} + return pack +} + +func (this *CSAdLookedHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSAdLookedHandler Process recv ", data) + if msg, ok := data.(*shop.CSAdLooked); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSAdLookedHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + pack := &shop.SCAdLooked{ + RetCode: shop.OpResultCode_OPRC_Error, + } + shopInfo := ShopMgrSington.GetShopInfo(msg.ShopId, p) + if shopInfo == nil { + return nil + } + if shopInfo.Ad == 0 { + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_ADLOOKED), pack) + logger.Logger.Warnf("CSAdLookedHandler shopId(%v) Ad(%v)", msg.ShopId, shopInfo.Ad == 0) + return nil + } + if shopInfo.RemainingTime >= 5 { // 冷却时间大于5秒 + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_ADLOOKED), pack) + logger.Logger.Warn("冷却中ing RemainingTime", shopInfo.RemainingTime) + return nil + } + if shopTotal, ok1 := p.ShopTotal[msg.ShopId]; ok1 { + if shopInfo.RepeatTimes <= shopTotal.AdReceiveNum { + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_ADLOOKED), pack) + logger.Logger.Warn("次数已满 已经领取的次数AdReceiveNum", shopInfo.AdReceiveNum) + return nil + } + shopTotal.AdLookedNum++ + } else { + p.ShopTotal[msg.ShopId] = &model.ShopTotal{AdLookedNum: 1} + } + b := ShopMgrSington.LookAdReceive(msg.ShopId, p, msg.GetPosition()) + pack.RetCode = b + if b == shop.OpResultCode_OPRC_Sucess { + p.ShopLastLookTime[msg.ShopId] = time.Now().Unix() + } + + si := ShopMgrSington.GetShopInfo(msg.ShopId, p) + if si != nil { + pack.ShopInfo = ShopMgrSington.GetShopInfoProto(si, p, 0) + } + logger.Logger.Trace("ShopTotal:", p.ShopTotal[msg.ShopId]) + logger.Logger.Trace("SCAdLooked:", pack) + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_ADLOOKED), pack) + + if pack.RetCode == shop.OpResultCode_OPRC_Sucess { + ShopMgrSington.ShopCheckShowRed(p) + ShopMgrSington.StartTimer(p.SnId, si.RemainingTime) + } + } + return nil +} + +type CSVCPayShopPacketFactory struct { +} + +type CSVCPayShopHandler struct { +} + +func (this *CSVCPayShopPacketFactory) CreatePacket() interface{} { + pack := &shop.CSVCPayShop{} + return pack +} + +func (this *CSVCPayShopHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSVCPayShopHandler Process recv ", data) + if msg, ok := data.(*shop.CSVCPayShop); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSVCPayShopHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + shopInfo := ShopMgrSington.GetShopInfo(msg.ShopId, p) + if shopInfo == nil { + return nil + } + var lastLookTime int64 + if shopInfo.Ad > 0 { + lastLookTime = p.ShopLastLookTime[msg.ShopId] + adLookedNum := p.ShopTotal[msg.ShopId].AdLookedNum + coolingTime := int64(shopInfo.CoolingTime[adLookedNum]) + if coolingTime != 0 && time.Now().Unix()-lastLookTime < coolingTime { + logger.Logger.Error("时间差:", time.Now().Unix()-lastLookTime, int64(shopInfo.CoolingTime[adLookedNum])) + return nil + } + } + var pack = &shop.SCVCPayShop{} + SendClient := func(ret shop.OpResultCode) { + pack.RetCode = ret + if ret == shop.OpResultCode_OPRC_Sucess { + si := ShopMgrSington.GetShopInfo(msg.ShopId, p) + if si != nil { + pack.ShopInfo = ShopMgrSington.GetShopInfoProto(si, p, msg.VipShopId) + } + pack.ShopInfo.VipShopId = msg.VipShopId + } + logger.Logger.Trace("SCVCPayShop:", pack) + proto.SetDefaults(pack) + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_VCPAYSHOP), pack) + } + + if p.VIP < shopInfo.VipLevel { + logger.Logger.Errorf("VIP等级不足!snid = %v,Vip = %v,shopInfo.VipLevel = %v,Id = %v", p.SnId, p.VIP, shopInfo.VipLevel, shopInfo.Id) + SendClient(shop.OpResultCode_OPRC_VipLevelNotEnough) + return nil + } + + if shopInfo.Page == ShopPageVip { + if p.VipShopData[msg.VipShopId] == nil || p.VipShopData[msg.VipShopId].IsBuy { + SendClient(shop.OpResultCode_OPRC_ExchangeLimit) + logger.Logger.Tracef("玩家VIP商城购买 当前物品无法购买! snid = %v,p.VipShopData[shopInfo.Id] = %v", p.SnId, p.GetVipShopData(shopInfo.Id, msg.VipShopId)) + return nil + } + } + //不需要观看广告的 + var money int32 = rand.Int31n(shopInfo.CostArea[1]-shopInfo.CostArea[0]+1) + shopInfo.CostArea[0] + if shopInfo.Page == ShopPageVip { + money = p.VipShopData[msg.VipShopId].CostArea + } + switch shopInfo.ConstType { + case ShopConsumeCoin: + //金币 + if int64(money) > p.Coin { + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + case ShopConsumeDiamond: + //钻石 + if int64(money) > p.Diamond { + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + case ShopConsumeMoney: + SendClient(shop.OpResultCode_OPRC_Error) + return nil + case ShopConsumePhoneScore: + if int64(money) > p.PhoneScore { + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + default: + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + op := ShopMgrSington.ReceiveVCPayShop(shopInfo, p, msg.VipShopId, msg.GetPosition()) + SendClient(op) + } + return nil +} + +type CSShopExchangeRecordPacketFactory struct { +} + +type CSShopExchangeRecordHandler struct { +} + +func (this *CSShopExchangeRecordPacketFactory) CreatePacket() interface{} { + pack := &shop.CSShopExchangeRecord{} + return pack +} + +func (this *CSShopExchangeRecordHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSShopExchangeRecordHandler Process recv ", data) + if msg, ok := data.(*shop.CSShopExchangeRecord); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSShopExchangeRecordHandler p == nil") + return nil + } + platform := p.GetPlatform() + if platform == nil { + return nil + } + ShopMgrSington.GetExchangeRecord(p, msg.PageNo) + + } + return nil +} + +type CSShopExchangePacketFactory struct { +} + +type CSShopExchangeHandler struct { +} + +func (this *CSShopExchangePacketFactory) CreatePacket() interface{} { + pack := &shop.CSShopExchange{} + return pack +} + +func (this *CSShopExchangeHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSShopExchangeHandler Process recv ", data) + if msg, ok := data.(*shop.CSShopExchange); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSShopExchangeHandler p == nil") + return nil + } + _, f := BlackListMgrSington.CheckExchange(p.PlayerData) + if !f { + pack := &shop.SCShopExchange{ + RetCode: shop.OpResultCode_OPRC_ExchangeLimitAcc, + } + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGE), pack) + return nil + } + if msg.Amount <= 0 || msg.Amount > 100 { + return nil + } + ShopMgrSington.Exchange(p, msg.GoodsId, msg.UserName, msg.Mobile, msg.Comment, msg.Id, msg.Amount) + } + return nil +} + +// 兑换列表 +type CSShopExchangeListPacketFactory struct { +} + +type CSShopExchangeListHandler struct { +} + +func (this *CSShopExchangeListPacketFactory) CreatePacket() interface{} { + pack := &shop.CSShopExchangeList{} + return pack +} + +func (this *CSShopExchangeListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSShopExchangeListHandler Process recv ", data) + if _, ok := data.(*shop.CSShopExchangeList); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSShopExchangeListHandler p == nil") + return nil + } + + ShopMgrSington.ExchangeList(p) + } + return nil +} + +// 充值 +type CSPayInfoPacketFactory struct { +} + +type CSPayInfoHandler struct { +} + +func (this *CSPayInfoPacketFactory) CreatePacket() interface{} { + pack := &shop.CSPayInfo{} + return pack +} + +func (this *CSPayInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPayInfoHandler Process recv ", data) + if msg, ok := data.(*shop.CSPayInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPayInfoHandler p == nil") + return nil + } + + SendClient := func(ret shop.OpResultCode) { + pack := &shop.SCPayInfo{ + RetCode: ret, + } + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + } + //充值限制 + _, f := BlackListMgrSington.CheckRecharge(p.PlayerData) + if !f { + SendClient(shop.OpResultCode_OPRC_ExchangeLimitAcc) + return nil + } + if msg.GoodsId >= ShopTypeStart { + //商城id + shopInfo := ShopMgrSington.GetShopInfo(msg.GoodsId, p) + if shopInfo == nil { + SendClient(shop.OpResultCode_OPRC_ExchangeSoldOut) + return nil + } + if shopInfo.ConstType != ShopConsumeMoney { + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + if shopInfo.Page == ShopPageVip { + //判断是否购买过了 + vipLevel := p.VIP + if shopInfo.Type == 1 { + vipCfg := VipMgrSington.GetVipCfg(p.Platform, p.VIP) + if vipCfg == nil || vipCfg.ShopId2 != msg.GoodsId { + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + } else { + vipLevel = shopInfo.VipLevel + } + if p.GetPlayerVipBagStatus(shopInfo.Id, vipLevel) == 1 { + logger.Logger.Error("充值购买VIP礼包失败!!!!!!!!!!!!!") + SendClient(shop.OpResultCode_OPRC_Error) + return nil + } + /* //VIP金币礼包 判断当前vip等级的礼包是否领取过 + if p.WelfData.VIPBag == nil { + p.WelfData.VIPBag = make(map[int32]map[int32]int32) + } + if p.WelfData.VIPBag[vipLevel] != nil && p.WelfData.VIPBag[vipLevel][msg.GoodsId] == shopInfo.Type { + logger.Logger.Error("充值购买VIP礼包失败!!!!!!!!!!!!!") + SendClient(shop.OpResultCode_OPRC_Error) + return nil + }*/ + } + ShopMgrSington.SendAPICreateOrder(p, msg.ConfigPayId, shopInfo, "shop_goods_xj") + + } else { + //活动 + switch msg.GoodsId { + case BlindBox: + WelfareMgrSington.BuyBlindBox(p, msg.BuyId, msg.ConfigPayId) + case FirstPay: + WelfareMgrSington.BuyFirstPay(p, msg.ConfigPayId) + case ContinuousPay: + WelfareMgrSington.BuyContinuousPay(p, msg.ConfigPayId) + case Exchange: + cdata := ShopMgrSington.GetExchangeData(p.Platform, msg.BuyId) + if cdata == nil { + SendClient(shop.OpResultCode_OPRC_ExchangeSoldOut) + return nil + } + if msg.ExchangeNum <= 0 { + return nil + } + cdata.OrderId = msg.ExchangeOrderId + cdata.ExchangeTypeId = msg.ExchangeId + cdata.ExchangeNum = msg.ExchangeNum + ShopMgrSington.SendAPICreateOrder(p, msg.ConfigPayId, cdata, "shop_exchange") + } + } + + } + return nil +} + +// 获取充值记录 +type CSGetPayInfoListPacketFactory struct { +} + +type CSGetPayInfoListHandler struct { +} + +func (this *CSGetPayInfoListPacketFactory) CreatePacket() interface{} { + pack := &shop.CSGetPayInfoList{} + return pack +} + +func (this *CSGetPayInfoListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetPayInfoListHandler Process recv ", data) + if msg, ok := data.(*shop.CSGetPayInfoList); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSGetPayInfoListHandler p == nil") + return nil + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.GetDbShopLogsByPage(p.Platform, p.SnId, msg.OpType) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + logger.Logger.Trace("model.GetDbShopLogsByPage", data) + pack := &shop.SCGetPayInfoList{} + if data != nil { + info := data.([]*model.DbShop) + for _, dbShop := range info { + var itemInfo []*shop.ItemInfo + for _, i2 := range dbShop.ItemInfo { + itemInfo = append(itemInfo, &shop.ItemInfo{ + ItemId: i2.ItemId, + ItemNum: i2.ItemNum, + }) + } + pack.Info = append(pack.Info, &shop.PayInfoList{ + OrderId: dbShop.LogId.Hex(), + ConsumeType: dbShop.Consume, + ConsumeNum: dbShop.ConsumeNum, + Amount: dbShop.Amount, + ItemInfo: itemInfo, + State: dbShop.State, + Ts: dbShop.CreateTs.Unix(), + }) + } + + } + proto.SetDefaults(pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCGETPAYINFOLIST), pack) + logger.Logger.Tracef("SCGetPayInfoListHandler sid:%v", pack) + }), "model.GetDbShopLogsByPage").Start() + } + return nil +} + +// 1.增加 2.查询 3.删除 +const ( + AddAddr int32 = 1 + SelectAddr int32 = 2 + DelAddr int32 = 3 + UpdateAddr int32 = 4 +) + +// 添加玩家地址 +type CSPlayerAddrPacketFactory struct { +} + +type CSPlayerAddrHandler struct { +} + +func (this *CSPlayerAddrPacketFactory) CreatePacket() interface{} { + pack := &shop.CSPlayerAddr{} + return pack +} + +func (this *CSPlayerAddrHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSPlayerAddr Process recv ", data) + if msg, ok := data.(*shop.CSPlayerAddr); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPlayerAddr p == nil") + return nil + } + switch msg.OpType { + case AddAddr: + addr := msg.Addr + if len(addr) > 200 { + logger.Logger.Warnf("玩家添加地址 地址长度过长len = %v,SnId = &v,addr = %v", len(addr), p.SnId, addr) + return nil + } + if len(p.PlayerData.Addr) >= 3 { + logger.Logger.Warn("最多添加3条地址!") + return nil + } + logger.Logger.Trace("添加的地址:", addr) + + p.PlayerData.Addr = append(p.PlayerData.Addr, addr) + case DelAddr: + id := msg.Id + p.PlayerData.Addr = append(p.PlayerData.Addr[:id], p.PlayerData.Addr[id+1:]...) + case UpdateAddr: + id := msg.Id + if id >= 3 { + return nil + } + addr := msg.Addr + if len(addr) > 200 { + return nil + } + if len(p.PlayerData.Addr) != 0 && len(p.PlayerData.Addr) < int(id) { + return nil + } + p.PlayerData.Addr[id] = addr + case SelectAddr: + + } + //返回消息 + pack := &shop.SCGetPlayerAddrs{} + for k, v := range p.PlayerData.Addr { + pack.Addrs = append(pack.Addrs, &shop.AddrData{ + Id: int32(k), + Addr: v, + }) + } + p.SendToClient(int(shop.SPacketID_PACKET_SCPLAYERADDR), pack) + } + return nil +} + +// 添加玩家地址 +type CSUpdateVipShopPacketFactory struct { +} + +type CSUpdateVipShopHandler struct { +} + +func (this *CSUpdateVipShopPacketFactory) CreatePacket() interface{} { + pack := &shop.CSUpdateVipShop{} + return pack +} + +func (this *CSUpdateVipShopHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSPayInfoHandler p == nil") + return nil + } + if p.VIP == 0 { + return nil + } + vipConfig := VipMgrSington.GetVIPLevelCfg(p.Platform, p.VIP) + if vipConfig == nil { + return nil + } + + if p.VipShopRefreshCount >= vipConfig.Privilege3[0] { + logger.Logger.Error("玩家VIP商城刷新次数不足 snid = ", p.SnId) + return nil + } + ShopMgrSington.UpdateVipShopData(p) + p.VipShopRefreshCount += 1 + pack := &shop.SCUpdateVipShop{ + RefreshCount: p.VipShopRefreshCount, //玩家当前刷新次数 + } + for i, info := range p.VipShopData { + si := ShopMgrSington.ShopInfos[p.Platform][info.Id] + data := &shop.ShopInfo{ + Id: info.Id, + AdLookedNum: si.AdLookedNum, + AdReceiveNum: si.AdReceiveNum, + LastLookTime: si.LastLookTime, + RoleAdded: si.RoleAdded, + PetAdded: si.PetAdded, + ItemId: si.ItemId, + Order: si.Order, + Page: si.Page, + Type: si.Type, + Location: si.Location, + Picture: si.Picture, + Name: si.Name, + Ad: si.Ad, + AdTime: si.AdTime, + RepeatTimes: si.RepeatTimes, + CoolingTime: si.CoolingTime, + Label: si.Label, + Added: info.AddArea, + Amount: si.Amount * int64(info.CostArea), + Consume: si.ConstType, + ConsumptionAmount: info.CostArea, + AddItemInfo: si.AddItemInfo, + VipLevel: si.VipLevel, + EndTime: si.EndTime, + IsBuy: info.IsBuy, + VipShopId: i, + AmountFinal: ShopMgrSington.GetAmountFinal(p, si.Id, i), + } + + pack.Info = append(pack.Info, data) + } + p.SendToClient(int(shop.SPacketID_PACKET_SC_UPDATE_VIP_SHOP), pack) + return nil +} +func init() { + // 获取商城商品信息列表 + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_INFO), &CSShopInfoHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_INFO), &CSShopInfoPacketFactory{}) + // 看广告领取商品,校验冷却时间和领取次数(免费) + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_ADLOOKED), &CSAdLookedHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_ADLOOKED), &CSAdLookedPacketFactory{}) + // 看广告领取商品,校验冷却时间(非免费) + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_VCPAYSHOP), &CSVCPayShopHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_VCPAYSHOP), &CSVCPayShopPacketFactory{}) + // 获取订单记录(非现金:金币,道具;现金的走透传后台获取) + common.RegisterHandler(int(shop.SPacketID_PACKET_CSGETPAYINFOLIST), &CSGetPayInfoListHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CSGETPAYINFOLIST), &CSGetPayInfoListPacketFactory{}) + + // 获取商城商品兑换列表 + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGELIST), &CSShopExchangeListHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGELIST), &CSShopExchangeListPacketFactory{}) + // 兑换商品(非现金) + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGE), &CSShopExchangeHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGE), &CSShopExchangePacketFactory{}) + // 兑换商品(现金,创建订单) + common.RegisterHandler(int(shop.SPacketID_PACKET_CSPAYINFO), &CSPayInfoHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CSPAYINFO), &CSPayInfoPacketFactory{}) + // 获取兑换记录 + //todo 客户端走透传后台获取 + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGERECORD), &CSShopExchangeRecordHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_SHOP_EXCHANGERECORD), &CSShopExchangeRecordPacketFactory{}) + + //玩家地址操作 + common.RegisterHandler(int(shop.SPacketID_PACKET_CSPLAYERADDR), &CSPlayerAddrHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CSPLAYERADDR), &CSPlayerAddrPacketFactory{}) + //VIP商城刷新 + common.RegisterHandler(int(shop.SPacketID_PACKET_CS_UPDATE_VIP_SHOP), &CSUpdateVipShopHandler{}) + netlib.RegisterFactory(int(shop.SPacketID_PACKET_CS_UPDATE_VIP_SHOP), &CSUpdateVipShopPacketFactory{}) +} diff --git a/worldsrv/action_sign.go b/worldsrv/action_sign.go new file mode 100644 index 0000000..55bb774 --- /dev/null +++ b/worldsrv/action_sign.go @@ -0,0 +1,90 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/activity" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" +) + +// ------------------------------------------------ +type CSSignPacketFactory struct { +} + +type CSSignHandler struct { +} + +func (this *CSSignPacketFactory) CreatePacket() interface{} { + pack := &activity.CSSign{} + return pack +} + +func (this *CSSignHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSSignHandler Process recv ", data) + if msg, ok := data.(*activity.CSSign); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSSignHandler p == nil") + return nil + } + + pack := &activity.SCSign{} + pack.SignIndex = proto.Int32(msg.GetSignIndex()) + pack.SignType = proto.Int32(msg.GetSignType()) + + retCode := ActSignMgrSington.CanSign(p, int(msg.GetSignIndex())) + if retCode != activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess { + pack.OpRetCode = retCode + proto.SetDefaults(pack) + p.SendToClient(int(activity.ActSignPacketID_PACKET_SCSign), pack) + return nil + } + retCode = ActSignMgrSington.Sign(p, int(msg.GetSignIndex()), msg.GetSignType()) + if retCode != activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess { + pack.OpRetCode = retCode + proto.SetDefaults(pack) + p.SendToClient(int(activity.ActSignPacketID_PACKET_SCSign), pack) + return nil + } + + pack.OpRetCode = activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess + proto.SetDefaults(pack) + p.SendToClient(int(activity.ActSignPacketID_PACKET_SCSign), pack) + } + return nil +} + +// ------------------------------------------------ +type CSSignDataPacketFactory struct { +} + +type CSSignDataHandler struct { +} + +func (this *CSSignDataPacketFactory) CreatePacket() interface{} { + pack := &activity.CSSignData{} + return pack +} + +func (this *CSSignDataHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSSignDataHandler Process recv ", data) + if _, ok := data.(*activity.CSSignData); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warn("CSSignDataHandler p == nil") + return nil + } + ActSignMgrSington.SendSignDataToPlayer(p) + } + return nil +} + +func init() { + //签到 + common.RegisterHandler(int(activity.ActSignPacketID_PACKET_CSSign), &CSSignHandler{}) + netlib.RegisterFactory(int(activity.ActSignPacketID_PACKET_CSSign), &CSSignPacketFactory{}) + //签到数据 + common.RegisterHandler(int(activity.ActSignPacketID_PACKET_CSSignData), &CSSignDataHandler{}) + netlib.RegisterFactory(int(activity.ActSignPacketID_PACKET_CSSignData), &CSSignDataPacketFactory{}) +} diff --git a/worldsrv/action_task.go b/worldsrv/action_task.go new file mode 100644 index 0000000..de5e86c --- /dev/null +++ b/worldsrv/action_task.go @@ -0,0 +1,203 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + + "mongo.games.com/game/common" + taskproto "mongo.games.com/game/protocol/task" + "mongo.games.com/game/srvdata" +) + +const ( + TaskStateFinish = 1 // 任务完成 + TaskStateReward = 2 // 任务奖励已领取 +) + +func init() { + // 获取任务列表 + common.Register(int(taskproto.TaskPacketID_PACKET_CSTaskList), taskproto.CSTaskList{}, CSTaskList) + // 领取任务奖励 + common.Register(int(taskproto.TaskPacketID_PACKET_CSTaskReward), taskproto.CSTaskReward{}, CSTaskReward) + // 测试用:直接增加任务计数 + common.Register(int(taskproto.TaskPacketID_PACKET_CSDebugInc), taskproto.CSTaskDebugInc{}, CSTaskDebugInc) +} + +// GetTaskTimes 获取任务完成进度 +func GetTaskTimes(p *Player, id int32) int64 { + if p.WelfData != nil && p.WelfData.Task[id] != nil { + return p.WelfData.Task[id].N + } + return 0 +} + +// IsTaskFinish 是否任务完成 +func IsTaskFinish(p *Player, id int32) bool { + data := srvdata.PBDB_TaskMgr.GetData(id) + if data == nil { + return false + } + if p.WelfData != nil && p.WelfData.Task[id] != nil { + return p.WelfData.Task[id].N >= data.GetTargetTimes() + } + return false +} + +// IsTaskReward 是否任务奖励已领取 +func IsTaskReward(p *Player, id int32) bool { + if p.WelfData != nil && p.WelfData.Task != nil { + if data := p.WelfData.Task[id]; data != nil && data.Ts > 0 { + t := srvdata.PBDB_TaskMgr.GetData(id) + switch t.ActivityType { + case common.TaskActivityTypeEveryDay: + if common.TsInSameDay(time.Now().Unix(), data.Ts) { + return true + } + + case common.TaskActivityTypeWeek: + if common.TsInSameWeek(time.Now().Unix(), data.Ts) { + return true + } + + case common.TaskActivityTypeNovice, common.TaskActivityTypeInvite: + if data.Ts > 0 { + return true + } + } + } + } + return false +} + +func SendReward(p *Player, m map[int64]int64) { + var items []*Item + for k, v := range m { + items = append(items, &Item{ + ItemId: int32(k), + ItemNum: v, + }) + } + BagMgrSingleton.AddJybBagInfo(p, items, 0, common.GainWay_TaskReward, "system", "任务奖励") + for _, v := range items { + data := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if data != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, v.ItemId, data.Name, v.ItemNum, "任务获得") + } + } +} + +func CSTaskList(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSTaskList %v", data) + msg, ok := data.(*taskproto.CSTaskList) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + ret := &taskproto.SCTaskList{ + Tp: msg.GetTp(), + } + + // todo 优化 + tp := msg.GetTp() + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.GetActivityType() != tp { + continue + } + item := &taskproto.TaskData{ + Id: v.Id, + N: GetTaskTimes(p, v.Id), + TargetN: v.GetTargetTimes(), + Status: 0, + Reward: v.GetAward(), + TaskType: v.GetTaskType(), + } + if item.N > item.TargetN { + item.N = item.TargetN + } + if IsTaskReward(p, v.Id) { + item.Status = TaskStateReward + } else if IsTaskFinish(p, v.Id) { + item.Status = TaskStateFinish + } + ret.List = append(ret.List, item) + } + p.SendToClient(int(taskproto.TaskPacketID_PACKET_SCTaskList), ret) + logger.Logger.Tracef("SCTaskList %v", ret) + return nil +} + +func CSTaskReward(s *netlib.Session, packetId int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSTaskReward %v", data) + msg, ok := data.(*taskproto.CSTaskReward) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + ret := &taskproto.SCTaskReward{ + OpCode: taskproto.OpResultCode_OPRC_Error, + Tp: msg.GetTp(), + Id: msg.GetId(), + } + + if IsTaskFinish(p, msg.GetId()) && !IsTaskReward(p, msg.GetId()) { + p.WelfData.Task[msg.GetId()].Ts = time.Now().Unix() + data := srvdata.PBDB_TaskMgr.GetData(msg.GetId()) + if data != nil { + SendReward(p, data.Award) + } + ret.OpCode = taskproto.OpResultCode_OPRC_Success + } + + p.SendToClient(int(taskproto.TaskPacketID_PACKET_SCTaskReward), ret) + logger.Logger.Tracef("SCTaskReward %v", ret) + return nil +} + +func CSTaskDebugInc(s *netlib.Session, packetId int, data interface{}, sid int64) error { + if !common.Config.IsDevMode { + return nil + } + + logger.Logger.Tracef("CSTaskDebugInc %v", data) + msg, ok := data.(*taskproto.CSTaskDebugInc) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + id := msg.GetId() + taskType := int32(-1) + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.Id == id { + taskType = v.TaskType + break + } + } + + p.ResetTaskN(taskType) + TaskSubjectSingleton.Touch(int(taskType), &TaskData{ + SnId: p.SnId, + Num: int64(msg.GetAddNum()), + Debug: true, + }) + + ret := &taskproto.SCTaskDebugInc{ + OpCode: taskproto.OpResultCode_OPRC_Success, + } + p.SendToClient(int(taskproto.TaskPacketID_PACKET_SCDebugInc), ret) + logger.Logger.Tracef("CSTaskDebugInc %v", ret) + return nil +} diff --git a/worldsrv/action_tournament.go b/worldsrv/action_tournament.go new file mode 100644 index 0000000..4d36517 --- /dev/null +++ b/worldsrv/action_tournament.go @@ -0,0 +1,465 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tournament" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "sort" + "strconv" + "time" +) + +// 比赛场信息 +type CSTMInfoPacketFactory struct { +} +type CSTMInfoHandler struct { +} + +func (this *CSTMInfoPacketFactory) CreatePacket() interface{} { + pack := &tournament.CSTMInfo{} + return pack +} + +func (this *CSTMInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTMInfoHandler Process recv ", data) + if _, ok := data.(*tournament.CSTMInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSTMInfo p == nil.") + return nil + } + pack := TournamentMgr.GetSCTMInfosPack(p.Platform, p.AppChannel) + proto.SetDefaults(pack) + logger.Logger.Trace("SCTMInfos++++++++++++:", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack) + } + return nil +} + +// 排行榜 +type CSTMRankListPacketFactory struct { +} +type CSTMRankListHandler struct { +} + +func (this *CSTMRankListPacketFactory) CreatePacket() interface{} { + pack := &tournament.CSTMRankList{} + return pack +} + +func (this *CSTMRankListHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTMRankListHandler Process recv ", data) + if msg, ok := data.(*tournament.CSTMRankList); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSTMRankList p == nil.") + return nil + } + pack := &tournament.SCTMRankList{ + TMId: msg.TMId, + TimeRange: "9.1-9.30", + TMRank: []*tournament.TMRank{ + {RankId: 1, RankName: "rankNo.1", WinnerNum: 5}, + {RankId: 2, RankName: "rankNo.2", WinnerNum: 4}, + {RankId: 3, RankName: "rankNo.3", WinnerNum: 2}}, + } + proto.SetDefaults(pack) + logger.Logger.Trace("CSTMRankList:", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMRankList), pack) + } + return nil +} + +// 报名 +type CSSignRacePacketFactory struct { +} +type CSSignRaceHandler struct { +} + +func (this *CSSignRacePacketFactory) CreatePacket() interface{} { + pack := &tournament.CSSignRace{} + return pack +} + +func (this *CSSignRaceHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSSignRaceHandler Process recv ", data) + if msg, ok := data.(*tournament.CSSignRace); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSSignRace p == nil.") + return nil + } + if p.scene != nil { + logger.Logger.Warnf("CSSignRace p.scene != nil.") + return nil + } + if p.IsRob { + logger.Logger.Warnf("CSSignRace p.IsRob.") + return nil + } + platform := p.Platform + tmid := msg.TMId + pack := &tournament.SCSignRace{} + switch msg.GetOpCode() { + case 0: + ok, code := TournamentMgr.SignUp(tmid, p) + if !ok { + logger.Logger.Infof("player(%v) match(%v) SignUp is fail.", p.SnId, tmid) + pack.RetCode = code //0成功 1重复报名 2比赛没有开启 3道具不足 4不在报名时间段 5金币不足 6钻石不足 + } + + if code == int32(tournament.SignRaceCode_OPRC_Close) || + code == int32(tournament.SignRaceCode_OPRC_Time) { + TournamentMgr.CancelSignUpAll(platform, tmid) + } + + waitStart := TournamentMgr.playerWaitStart[p.SnId] + if pack.RetCode == 0 && waitStart != 0 { + pack.WaitStartTime = waitStart + } + + proto.SetDefaults(pack) + logger.Logger.Trace("SCSignRace:", pack) + signSucc := p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) + // 检查是否可以开始比赛(关闭机器人时,比赛开赛) + if msg.GetOpCode() == 0 && pack.RetCode == 0 && signSucc && !TournamentMgr.IsUseRobot(platform, tmid) { + if TournamentMgr.CanStart(platform, tmid) { + TournamentMgr.Start(platform, tmid) + } + } + + default: + if TournamentMgr.IsMatching(p.SnId) { + logger.Logger.Infof("player(%v) IsMatching.", p.SnId) + } else { + //取消报名 + TournamentMgr.CancelSignUp(platform, tmid, p.SnId) + } + return nil + } + } + return nil +} + +// 赛季信息 +type CSTMSeasonInfoPacketFactory struct { +} +type CSTMSeasonInfoHandler struct { +} + +func (this *CSTMSeasonInfoPacketFactory) CreatePacket() interface{} { + pack := &tournament.CSTMSeasonInfo{} + return pack +} + +func (this *CSTMSeasonInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTMSeasonInfoHandler Process recv ", data) + if _, ok := data.(*tournament.CSTMSeasonInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSTMSeasonInfoHandler p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSTMSeasonInfoHandler Platform == Default_Platform.") + return nil + } + msid := MatchSeasonMgrSington.GetMatchSeasonId(p.Platform) + if msid == nil { + logger.Logger.Warnf("CSTMSeasonInfoHandler msid == nil.") + return nil + } + send := func(ms *MatchSeason) { + pack := &tournament.SCTMSeasonInfo{ + Id: msid.SeasonId, + SeasonTimeStamp: []int64{msid.StartStamp, msid.EndStamp}, + Lv: ms.Lv, + LastLv: ms.LastLv, + IsAward: ms.IsAward, + } + proto.SetDefaults(pack) + logger.Logger.Trace("CSTMSeasonInfoHandler:", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMSeasonInfo), pack) + } + snid := p.SnId + ms := MatchSeasonMgrSington.GetMatchSeason(snid) + if ms == nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryMatchSeasonBySnid(p.Platform, snid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + var ret *model.MatchSeason + dirty := false + if data == nil || data.(*model.MatchSeason) == nil { //新数据 + player := PlayerMgrSington.GetPlayerBySnId(snid) + if player != nil { + ret = model.NewMatchSeason(player.Platform, snid, player.Name, msid.SeasonId, 1) + dirty = true + } else { + logger.Logger.Trace("CSTMSeasonInfoHandler error: player==nil ", snid) + } + } else { + ret = data.(*model.MatchSeason) + if ret.SeasonId < msid.SeasonId { //不同赛季段位继承 + num := msid.SeasonId - ret.SeasonId + finalLv := ret.Lv + for i := 0; i < int(num); i++ { //继承几次 + if i == int(num)-1 { //上个赛季 + ret.LastLv = finalLv + } + finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) + } + ret.Lv = finalLv + ret.SeasonId = msid.SeasonId + ret.IsAward = false + ret.UpdateTs = time.Now().Unix() + dirty = true + } + } + ms = MatchSeasonMgrSington.exchangeModel2Cache(ret) + ms.dirty = dirty + MatchSeasonMgrSington.SetMatchSeason(ms) + send(ms) + })).StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) + } else { + if ms.SeasonId < msid.SeasonId { //不同赛季段位继承 + num := msid.SeasonId - ms.SeasonId + finalLv := ms.Lv + for i := 0; i < int(num); i++ { //继承几次 + if i == int(num)-1 { //上个赛季 + ms.LastLv = finalLv + } + finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) + } + ms.Lv = finalLv + ms.SeasonId = msid.SeasonId + ms.IsAward = false + ms.UpdateTs = time.Now().Unix() + ms.dirty = true + MatchSeasonMgrSington.SetMatchSeason(ms) + } + send(ms) + } + } + return nil +} + +// 赛季排行榜 +type CSTMSeasonRankPacketFactory struct { +} +type CSTMSeasonRankHandler struct { +} + +func (this *CSTMSeasonRankPacketFactory) CreatePacket() interface{} { + pack := &tournament.CSTMSeasonRank{} + return pack +} + +func (this *CSTMSeasonRankHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTMSeasonRankHandler Process recv ", data) + if _, ok := data.(*tournament.CSTMSeasonRank); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSTMSeasonRankHandler p == nil.") + return nil + } + platform := p.Platform + if platform == DefaultPlatform { + logger.Logger.Warnf("CSTMSeasonRankHandler Platform == Default_Platform.") + return nil + } + pack := &tournament.SCTMSeasonRank{} + tmpMsrs := []*MatchSeasonRank{} + msr := MatchSeasonRankMgrSington.GetMatchSeasonRank(platform) + if msr != nil { + for _, ms := range msr { + sr := &MatchSeasonRank{ + SnId: ms.SnId, + Name: ms.Name, + Lv: ms.Lv, + } + tmpMsrs = append(tmpMsrs, sr) + } + } + robotmsr := MatchSeasonRankMgrSington.GetRobotMatchSeasonRank(platform) + if robotmsr != nil { + for _, ms := range robotmsr { + sr := &MatchSeasonRank{ + SnId: ms.SnId, + Name: ms.Name, + Lv: ms.Lv, + } + tmpMsrs = append(tmpMsrs, sr) + } + } + if tmpMsrs != nil && len(tmpMsrs) > 0 { + sort.Slice(tmpMsrs, func(i, j int) bool { + return tmpMsrs[i].Lv > tmpMsrs[j].Lv + }) + if len(tmpMsrs) > model.GameParamData.MatchSeasonRankMaxNum { + tmpMsrs = append(tmpMsrs[:model.GameParamData.MatchSeasonRankMaxNum]) + } + for i := 0; i < len(tmpMsrs); i++ { + ms := tmpMsrs[i] + sr := &tournament.SeasonRank{ + Snid: ms.SnId, + Name: ms.Name, + Lv: ms.Lv, + Rank: int32(i) + 1, + } + pack.ReasonRanks = append(pack.ReasonRanks, sr) + } + } + proto.SetDefaults(pack) + logger.Logger.Trace("CSTMSeasonRankHandler:", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMSeasonRank), pack) + } + return nil +} + +// 领取赛季奖励 +type CSTMSeasonAwardPacketFactory struct { +} +type CSTMSeasonAwardHandler struct { +} + +func (this *CSTMSeasonAwardPacketFactory) CreatePacket() interface{} { + pack := &tournament.CSTMSeasonAward{} + return pack +} + +func (this *CSTMSeasonAwardHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSTMSeasonAwardHandler Process recv ", data) + if msg, ok := data.(*tournament.CSTMSeasonAward); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSTMSeasonAwardHandler p == nil.") + return nil + } + if p.Platform == DefaultPlatform { + logger.Logger.Warnf("CSTMSeasonInfoHandler Platform == Default_Platform.") + return nil + } + lv := msg.GetLv() + logger.Logger.Trace("CSTMSeasonAwardHandler lv: ", lv) + pack := &tournament.SCTMSeasonAward{ + Lv: lv, + Code: 1, + } + ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId) + msi := MatchSeasonMgrSington.GetMatchSeasonId(p.Platform) + if ms != nil && msi != nil { + if !ms.IsAward && ms.LastLv == lv && msi.SeasonId > 1 { //领取上赛季奖励 + for _, v := range srvdata.PBDB_GamMatchLVMgr.Datas.GetArr() { + if v.Star != nil && len(v.Star) > 1 { + startStar := v.Star[0] + endStar := v.Star[1] + if lv >= startStar && lv <= endStar { //匹配段位 + pack.Code = 0 + MatchSeasonMgrSington.UpdateMatchSeasonAward(p.SnId) + if v.Number1 > 0 { + switch v.AwardType1 { + case 1: //金币 + p.AddCoin(int64(v.Number1), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Coin, int64(v.Number1))) + case 2: //钻石 + p.AddDiamond(int64(v.Number1), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Diamond, int64(v.Number1))) + case 3: //道具 + item := &Item{ + ItemId: v.AwardId1, + ItemNum: int64(v.Number1), + } + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_MatchSeason, "system", "赛季奖励") + itemData := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, itemData.Name, item.ItemNum, "赛季奖励") + } + } + } + if v.Number2 > 0 { + switch v.AwardType2 { + case 1: //金币 + p.AddCoin(int64(v.Number2), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Coin, int64(v.Number2))) + case 2: //钻石 + p.AddDiamond(int64(v.Number2), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Diamond, int64(v.Number2))) + case 3: //道具 + item := &Item{ + ItemId: v.AwardId2, + ItemNum: int64(v.Number2), + } + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_MatchSeason, "system", "赛季奖励") + itemData := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, itemData.Name, item.ItemNum, "赛季奖励") + } + } + } + if v.Number3 > 0 { + switch v.AwardType3 { + case 1: //金币 + p.AddCoin(int64(v.Number3), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Coin, int64(v.Number3))) + case 2: //钻石 + p.AddDiamond(int64(v.Number3), 0, common.GainWay_MatchSeason, "system", "赛季奖励") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_MatchSeason, model.SystemFreeGive_CoinType_Diamond, int64(v.Number3))) + case 3: //道具 + item := &Item{ + ItemId: v.AwardId3, + ItemNum: int64(v.Number3), + } + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_MatchSeason, "system", "赛季奖励") + itemData := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, itemData.Name, item.ItemNum, "赛季奖励") + } + } + } + break + } + } + } + } + } + if pack.Code != 0 { + logger.Logger.Trace("CSTMSeasonAwardHandler ms: ", ms) + logger.Logger.Trace("CSTMSeasonAwardHandler msi: ", msi) + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCTMSeasonAward:", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMSeasonAward), pack) + } + return nil +} + +func init() { + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSTMInfo), &CSTMInfoHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSTMInfo), &CSTMInfoPacketFactory{}) + + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSTMRankList), &CSTMRankListHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSTMRankList), &CSTMRankListPacketFactory{}) + + // 比赛报名 + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSSignRace), &CSSignRaceHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSSignRace), &CSSignRacePacketFactory{}) + + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonInfo), &CSTMSeasonInfoHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonInfo), &CSTMSeasonInfoPacketFactory{}) + + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonRank), &CSTMSeasonRankHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonRank), &CSTMSeasonRankPacketFactory{}) + + common.RegisterHandler(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonAward), &CSTMSeasonAwardHandler{}) + netlib.RegisterFactory(int(tournament.TOURNAMENTID_PACKET_TM_CSTMSeasonAward), &CSTMSeasonAwardPacketFactory{}) +} diff --git a/worldsrv/action_welfare.go b/worldsrv/action_welfare.go new file mode 100644 index 0000000..2a05db7 --- /dev/null +++ b/worldsrv/action_welfare.go @@ -0,0 +1,455 @@ +package main + +import ( + "encoding/base64" + "fmt" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/welfare" + "mongo.games.com/game/srvdata" +) + +// 救济金 +type CSGetReliefFundPacketFactory struct { +} + +type CSGetReliefFundHandler struct { +} + +func (this *CSGetReliefFundPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSGetReliefFund{} + return pack +} + +func (this *CSGetReliefFundHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetReliefFund Process recv ", data) + if msg, ok := data.(*welfare.CSGetReliefFund); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSGetReliefFundHandler p == nil p.SnId = %v", p.SnId) + return nil + } + WelfareMgrSington.GetReliefFund(p, msg.GetIsVideo()) + } + return nil +} + +// 转动转盘 +type CSGetTurnplatePacketFactory struct { +} + +type CSGetTurnplateHandler struct { +} + +func (this *CSGetTurnplatePacketFactory) CreatePacket() interface{} { + pack := &welfare.CSGetTurnplate{} + return pack +} + +func (this *CSGetTurnplateHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetTurnplate Process recv ", data) + if msg, ok := data.(*welfare.CSGetTurnplate); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSGetTurnplateHandler p == nil p.SnId = %v", p.SnId) + return nil + } + if msg.GetIsVideo() { + // 看视频可以再领一次 + WelfareMgrSington.GetTurnplteVideo(p) + return nil + } + WelfareMgrSington.GetTurnplate(p) + } + return nil +} + +// 累计签到 +type CSGetAddupSignPacketFactory struct { +} + +type CSGetAddupSignHandler struct { +} + +func (this *CSGetAddupSignPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSGetAddupSign{} + return pack +} + +func (this *CSGetAddupSignHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetAddupSign Process recv ", data) + if msg, ok := data.(*welfare.CSGetAddupSign); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSGetAddupSignHandler p == nil") + return nil + } + + WelfareMgrSington.GetAddupSign(p, msg.GetAddUpDay()) + } + return nil +} + +// 福利信息 +type CSWelfaredInfoPacketFactory struct { +} + +type CSWelfaredInfoHandler struct { +} + +func (this *CSWelfaredInfoPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSWelfaredInfo{} + return pack +} + +func (this *CSWelfaredInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWelfaredInfo Process recv ", data) + if _, ok := data.(*welfare.CSWelfaredInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSWelfaredInfoHandler p == nil p.SnId = %v", p.SnId) + return nil + } + WelfareMgrSington.WelfaredInfo(p) + } + return nil +} + +// 查看盲盒 +type CSBlindBoxInfoPacketFactory struct { +} + +type CSBlindBoxInfoHandler struct { +} + +func (this *CSBlindBoxInfoPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSBlindBoxInfo{} + return pack +} + +func (this *CSBlindBoxInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSBlindBoxInfo Process recv ", data) + if msg, ok := data.(*welfare.CSBlindBoxInfo); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSBlindBoxInfoHandler p == nil p.SnId = %v", p.SnId) + return nil + } + WelfareMgrSington.BlindBoxInfo(p, msg.GetId()) + } + return nil +} + +// 购买盲盒 +type CSBuyBlindBoxPacketFactory struct { +} + +type CSBuyBlindBoxHandler struct { +} + +func (this *CSBuyBlindBoxPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSGetBlindBox{} + return pack +} + +func (this *CSBuyBlindBoxHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSGetBlindBox Process recv ", data) + if _, ok := data.(*welfare.CSGetBlindBox); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSBuyBlindBoxHandler p == nil p.SnId = %v", p.SnId) + return nil + } + //WelfareMgrSington.BuyBlindBox(p, msg.GetId(), msg.ConfigPayId) + } + return nil +} + +// 查看首充 +type CSFirstPayInfoPacketFactory struct { +} + +type CSFirstPayInfoHandler struct { +} + +func (this *CSFirstPayInfoPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSWelfareFirstPayData{} + return pack +} + +func (this *CSFirstPayInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWelfareFirstPayData Process recv ", data) + if _, ok := data.(*welfare.CSWelfareFirstPayData); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSFirstPayInfoHandler p == nil p.SnId = %v", p.SnId) + return nil + } + WelfareMgrSington.FirstPayInfo(p) + } + return nil +} + +// 领取首充 +type CSBuyFirstPayPacketFactory struct { +} + +type CSBuyFirstPayHandler struct { +} + +func (this *CSBuyFirstPayPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSWelfareFirstPay{} + return pack +} + +func (this *CSBuyFirstPayHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWelfareFirstPay Process recv ", data) + if _, ok := data.(*welfare.CSWelfareFirstPay); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSBuyFirstPayHandler p == nil p.SnId = %v", p.SnId) + return nil + } + //WelfareMgrSington.BuyFirstPay(p, msg.ConfigPayId) + } + return nil +} + +// 查看连续充值 +type CSContinuousPayInfoPacketFactory struct { +} + +type CSContinuousPayInfoHandler struct { +} + +func (this *CSContinuousPayInfoPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSWelfareContinuousPayData{} + return pack +} + +func (this *CSContinuousPayInfoHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWelfareContinuousPayData Process recv ", data) + if _, ok := data.(*welfare.CSWelfareContinuousPayData); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSContinuousPayInfoHandler p == nil p.SnId = %v", p.SnId) + return nil + } + WelfareMgrSington.ContinuousPayInfo(p) + } + return nil +} + +// 领取连续充值 +type CSBuyContinuousPayPacketFactory struct { +} + +type CSBuyContinuousPayHandler struct { +} + +func (this *CSBuyContinuousPayPacketFactory) CreatePacket() interface{} { + pack := &welfare.CSWelfareContinuousPay{} + return pack +} + +func (this *CSBuyContinuousPayHandler) Process(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Trace("CSWelfareContinuousPay Process recv ", data) + if _, ok := data.(*welfare.CSWelfareContinuousPay); ok { + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + logger.Logger.Warnf("CSBuyContinuousPayHandler p == nil p.SnId = %v", p.SnId) + return nil + } + //WelfareMgrSington.BuyContinuousPay(p, msg.ConfigPayId) + } + return nil +} + +func CSWelfRelief(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSWelfRelief Process recv %v", data) + _, ok := data.(*welfare.CSWelfareRelief) + if !ok { + return nil + } + + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + conf := srvdata.PBDB_GameSubsidyMgr.GetData(GameSubsidyid) + + ret := &welfare.SCWelfareRelief{ + LimitNum: conf.GetLimitNum(), + Get: conf.GetGet(), + Times: conf.GetTimes(), + } + + p.SendToClient(int(welfare.SPacketID_PACKET_SCWelfRelief), ret) + logger.Logger.Tracef("SCWelfRelief %v", ret) + return nil +} + +func CSInviteInfo(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSInviteInfo Process recv %v", data) + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + + ul := fmt.Sprintf("%s?user=%s", model.GameParamData.InviteUrl, base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s|%d", p.Platform, p.SnId)))) + + ret := &welfare.SCInviteInfo{ + Num: p.InviteNum, + Code: p.InviteCode, + InviteUrl: ul, + Score: p.InviteScore, + OtherCode: p.OtherCode, + } + + p.SendToClient(int(welfare.SPacketID_PACKET_SCInviteInfo), ret) + logger.Logger.Tracef("SCInviteInfo %v", ret) + return nil +} + +func CSBindInvite(s *netlib.Session, packetid int, data interface{}, sid int64) error { + logger.Logger.Tracef("CSBindInvite Process recv %v", data) + msg, ok := data.(*welfare.CSBindInvite) + if !ok { + return nil + } + p := PlayerMgrSington.GetPlayer(sid) + if p == nil { + return nil + } + ret := &welfare.SCBindInvite{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + + var inviteSnId int32 + var err error + + send := func() { + p.SendToClient(int(welfare.SPacketID_PACKET_SCBindInvite), ret) + logger.Logger.Tracef("SCBindInvite %v", ret) + + if ret.OpRetCode == welfare.OpResultCode_OPRC_Sucess { + // 更新绑定关系 + LogChannelSingleton.WriteLog(&model.BindInvite{ + Platform: p.Platform, + SnId: p.SnId, + InviteSnId: inviteSnId, + }) + + TaskSubjectSingleton.Touch(common.TaskTypeBindInviter, &TaskData{SnId: p.SnId, Num: 1}) + p.InviteTask(common.InviteScoreTypeLogin, 0, 1) + } + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + inviteSnId, err = model.GetSnIdByCode(p.Platform, msg.GetCode()) + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil || inviteSnId == 0 { + logger.Logger.Errorf("GetSnIdByCode err %v %v", err, inviteSnId) + ret.OpRetCode = welfare.OpResultCode_OPRC_NotExist + send() + return + } + + if p.InviteSnId != 0 { + ret.OpRetCode = welfare.OpResultCode_OPRC_AlreadyBind + send() + return + } + if inviteSnId == p.SnId { + ret.OpRetCode = welfare.OpResultCode_OPRC_BindSelf + send() + return + } + pp := PlayerMgrSington.GetPlayerBySnId(inviteSnId) + if pp != nil { + if pp.Platform != p.Platform { + send() + return + } + if pp.InviteSnId == p.SnId { + ret.OpRetCode = welfare.OpResultCode_OPRC_MyInvite + send() + return + } + p.InviteSnId = pp.SnId + p.OtherCode = pp.InviteCode + ret.OpRetCode = welfare.OpResultCode_OPRC_Sucess + send() + return + } + + inviteSnId2 := int32(0) + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + inviteSnId2, err = model.GetPlayerInviteSnid(p.Platform, inviteSnId) + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + send() + return + } + if inviteSnId2 == p.SnId { + ret.OpRetCode = welfare.OpResultCode_OPRC_MyInvite + send() + return + } + p.InviteSnId = inviteSnId + p.OtherCode = msg.GetCode() + ret.OpRetCode = welfare.OpResultCode_OPRC_Sucess + send() + })).Start() + })).Start() + return nil +} + +func init() { + // 领取救济金 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_GETRELIEFFUND), &CSGetReliefFundHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_GETRELIEFFUND), &CSGetReliefFundPacketFactory{}) + //转动转盘 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_GETTURNPLATE), &CSGetTurnplateHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_GETTURNPLATE), &CSGetTurnplatePacketFactory{}) + //累计签到 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_GETADDUPSIGN), &CSGetAddupSignHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_GETADDUPSIGN), &CSGetAddupSignPacketFactory{}) + //福利信息 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_WELFAREINFO), &CSWelfaredInfoHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_WELFAREINFO), &CSWelfaredInfoPacketFactory{}) + //查看盲盒 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_BLINBOXINFO), &CSBlindBoxInfoHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_BLINBOXINFO), &CSBlindBoxInfoPacketFactory{}) + //购买盲盒 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_GETBLINBOX), &CSBuyBlindBoxHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_GETBLINBOX), &CSBuyBlindBoxPacketFactory{}) + //查看首充 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_FIRSTPAYINFO), &CSFirstPayInfoHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_FIRSTPAYINFO), &CSFirstPayInfoPacketFactory{}) + //领取首充 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_FIRSTPAY), &CSBuyFirstPayHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_FIRSTPAY), &CSBuyFirstPayPacketFactory{}) + //查看连续充值 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_CONTINPAYINFO), &CSContinuousPayInfoHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_CONTINPAYINFO), &CSContinuousPayInfoPacketFactory{}) + //领取连续充值 + common.RegisterHandler(int(welfare.SPacketID_PACKET_CS_WELF_CONTINPAY), &CSBuyContinuousPayHandler{}) + netlib.RegisterFactory(int(welfare.SPacketID_PACKET_CS_WELF_CONTINPAY), &CSBuyContinuousPayPacketFactory{}) + // 破产补助信息 + common.Register(int(welfare.SPacketID_PACKET_CSWelfRelief), welfare.CSWelfareRelief{}, CSWelfRelief) + // 邀请信息 + common.Register(int(welfare.SPacketID_PACKET_CSInviteInfo), welfare.CSInviteInfo{}, CSInviteInfo) + // 绑定信息 + common.Register(int(welfare.SPacketID_PACKET_CSBindInvite), welfare.CSBindInvite{}, CSBindInvite) +} diff --git a/worldsrv/actmgr.go b/worldsrv/actmgr.go new file mode 100644 index 0000000..efce62e --- /dev/null +++ b/worldsrv/actmgr.go @@ -0,0 +1,146 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/webapi" + + "encoding/json" + "time" + + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" +) + +const ACT_LOGGER = "actmgr" + +var ActMgrSington = &ActMgr{ + ConfigByPlateform: make(map[string]*ActGivePlateformConfig), +} + +// config -------------------------------------------------------- +type ActGiveConfig struct { + Tag int32 //赠与类型 + IsStop int32 //是否阻止 1阻止 0放开 + NeedFlow int32 //需要流水的倍数 0 不计算 其他计算 +} + +type ActGivePlateformConfig struct { + ActInfo map[string]*ActGiveConfig //奖励信息 + findInfo map[int32]*ActGiveConfig //奖励信息 + Platform string //平台 +} + +type ActMgr struct { + ConfigByPlateform map[string]*ActGivePlateformConfig + LastTicket int64 +} + +func (this *ActMgr) AddGiveConfig(actComeConfig *ActGivePlateformConfig, plateFrom string) { + actComeConfig.findInfo = make(map[int32]*ActGiveConfig) + for _, v := range actComeConfig.ActInfo { + actComeConfig.findInfo[v.Tag] = v + } + this.ConfigByPlateform[plateFrom] = actComeConfig + + this.OnConfigChanged(actComeConfig) +} + +func (this *ActMgr) RemovePlateFormConfig(plateFrom string) { + _, ok := this.ConfigByPlateform[plateFrom] + if ok { + delete(this.ConfigByPlateform, plateFrom) + } +} + +func (this *ActMgr) GetIsNeedGive(plateFrom string, tag int32) bool { + info := this.GetGiveConfig(plateFrom) + if info == nil { + return true + } + + if v, ok := info.findInfo[tag]; ok { + if v.IsStop == 1 { + return false + } + } + return true +} + +func (this *ActMgr) GetExchangeFlow(plateFrom string, tag int32) int32 { + info := this.GetGiveConfig(plateFrom) + if info == nil { + return 0 + } + + if v, ok := info.findInfo[tag]; ok { + return v.NeedFlow + } + return 0 +} + +func (this *ActMgr) GetGiveConfig(plateForm string) *ActGivePlateformConfig { + plateFormConfig, ok := this.ConfigByPlateform[plateForm] + if ok { + return plateFormConfig + } + return nil +} + +func (this *ActMgr) OnConfigChanged(actComeConfig *ActGivePlateformConfig) { + +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *ActMgr) ModuleName() string { + return "ActMgr" +} + +func (this *ActMgr) Init() { + this.LastTicket = time.Now().Unix() + + if this.ConfigByPlateform == nil { + this.ConfigByPlateform = make(map[string]*ActGivePlateformConfig) + } + + type ApiResult struct { + Tag int + Msg []ActGivePlateformConfig + } + + //不使用etcd的情况下走api获取 + if !model.GameParamData.UseEtcd { + buff, err := webapi.API_GetActConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil { + for _, plateformConfig := range ar.Msg { + t := plateformConfig + this.AddGiveConfig(&t, plateformConfig.Platform) + } + } else { + logger.Logger.Error("Unmarshal ActMgr data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("Init ActMgr list failed.") + } + } else { + EtcdMgrSington.InitPlatformAct() + } + +} + +func (this *ActMgr) Update() { + +} + +func (this *ActMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(ActMgrSington, time.Minute, 0) +} diff --git a/worldsrv/actmonitormgr.go b/worldsrv/actmonitormgr.go new file mode 100644 index 0000000..01a8120 --- /dev/null +++ b/worldsrv/actmonitormgr.go @@ -0,0 +1,177 @@ +package main + +import ( + "math" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "sort" +) + +const ( + ActState_Login int32 = 1 << iota //登录.1 + ActState_Exchange //兑换.2 + ActState_Game //游戏.3 + ActState_Max +) + +var ActMonitorMgrSington = &ActMonitorMgr{ + ActMonitorList: make(map[int64]*ActMonitorInfo), +} + +type ActMonitorInfo struct { + SeqNo int64 + SnId int32 + Platform string //平台 + MonitorType int32 //二进制 1.登录 2.兑换 3.游戏 + CreateTime int64 //创建时间 + Creator string //创建者 + ReMark string //备注 + GameName string //当前所在游戏名字 + State int //玩家状态 0.全部 1.不在线 2.在线 3.游戏中 +} +type ActMonitorMgr struct { + ActMonitorList map[int64]*ActMonitorInfo + NowActSeqNo int64 +} + +// monitorType 自己的类型 flag 当前触发的类型 +func (u *ActMonitorMgr) IsMarkFlag(monitorType, flag int32) bool { + if (monitorType & flag) != 0 { + return true + } + return false +} +func (u *ActMonitorMgr) Init() { + actMonitorData := model.GetAllActMonitorData() + for _, info := range actMonitorData { + ami := &ActMonitorInfo{ + SeqNo: info.SeqNo, + SnId: info.SnId, + Platform: info.Platform, + MonitorType: info.MonitorType, + CreateTime: info.CreateTime, + Creator: info.Creator, + ReMark: info.ReMark, + } + if u.NowActSeqNo < info.SeqNo { + u.NowActSeqNo = info.SeqNo + } + u.ActMonitorList[info.SeqNo] = ami + } +} + +type ActMonitorList struct { + PageNo int + PageSize int + PageSum int + TotalSum int + Data []*ActMonitorInfo +} + +func (u *ActMonitorMgr) QueryAMIList(pageNo, pageSize int, platform string, snid, startTs, endTs, state int) *ActMonitorList { + if len(u.ActMonitorList) == 0 { + return nil + } + var amiList = make([]*ActMonitorInfo, 0) + for _, v := range u.ActMonitorList { + if len(platform) != 0 && v.Platform != platform { + continue + } + if snid != 0 && v.SnId != int32(snid) { + continue + } + if startTs != 0 && endTs != 0 && (v.CreateTime < int64(startTs) || v.CreateTime > int64(endTs)) { + continue + } + if state != 0 && v.State != state { + continue + } + amiList = append(amiList, v) + } + sort.Slice(amiList, func(i, j int) bool { + if amiList[i].SeqNo > amiList[j].SeqNo { + return true + } + return false + }) + totalNum := len(amiList) //总条目 + pageSum := int(math.Ceil(float64(totalNum) / float64(pageSize))) //总页数 + if pageNo <= 0 || pageNo > pageSum { + pageNo = 1 //当前页 + } + start := (pageNo - 1) * pageSize + end := start + pageSize + if totalNum > start { + if totalNum < end { + end = totalNum + } + amiList = amiList[start:end] + } + for k, v := range amiList { + actPlayer := amiList[k] + actPlayer.GameName = "" + p := PlayerMgrSington.GetPlayerBySnId(v.SnId) + if p != nil { + if p.IsOnLine() { + actPlayer.State = 2 + } else { + actPlayer.State = 1 + } + if p.scene != nil { + actPlayer.State = 3 + actPlayer.GameName = p.scene.dbGameFree.GetName() + p.scene.dbGameFree.GetTitle() + } + } else { + actPlayer.State = 1 + } + } + return &ActMonitorList{pageNo, pageSize, pageSum, totalNum, amiList} +} +func (u *ActMonitorMgr) Edit(amt *ActMonitorInfo) { + u.ActMonitorList[amt.SeqNo] = amt +} +func (u *ActMonitorMgr) Del(seqNo int64) { + delete(u.ActMonitorList, seqNo) +} +func (u *ActMonitorMgr) AddSeqNo() int64 { + u.NowActSeqNo++ + return u.NowActSeqNo +} +func (u *ActMonitorMgr) GetSeqNo(snid int32, platform string) int64 { + for _, v := range u.ActMonitorList { + if v.SnId == snid && v.Platform == platform { + return v.SeqNo + } + } + return -1 +} +func (u *ActMonitorMgr) SendActMonitorEvent(eventType, snid int32, name, platform string, billNo, exchangeCoin int64, + gameSceneName string, state int32) { + logger.Logger.Tracef("SendActMonitorEvent eventType:%v snid:%v name:%v platform:%v billNo:%v exchangeCoin:%v "+ + "gameSceneName:%v state:%v", eventType, snid, name, platform, billNo, exchangeCoin, gameSceneName, state) + //seqNo := u.GetSeqNo(snid, platform) + //if data, ok := u.ActMonitorList[seqNo]; ok { + // if u.IsMarkFlag(eventType, data.MonitorType) { + // var flag int32 + // if eventType == ActState_Login { + // flag = 1 + // } else if eventType == ActState_Exchange { + // flag = 2 + // } else if eventType == ActState_Game { + // flag = 3 + // } + // logger.Logger.Tracef("GenerateActMonitorEvent "+ + // "flag:%v eventType:%v snid:%v name:%v platform:%v billNo:%v exchangeCoin:%v "+ + // "gameSceneName:%v state:%v reMark:%v", + // flag, eventType, snid, name, platform, billNo, exchangeCoin, gameSceneName, state, data.ReMark) + // LogChannelSingleton.WriteMQData(model.GenerateActMonitorEvent(flag, snid, name, platform, + // time.Now().Unix(), billNo, exchangeCoin, gameSceneName, state, data.ReMark)) + // } + //} +} +func init() { + //RegisterParallelLoadFunc("用户行为监控列表", func() error { + // ActMonitorMgrSington.Init() + // return nil + //}) +} diff --git a/worldsrv/actsignmgr.go b/worldsrv/actsignmgr.go new file mode 100644 index 0000000..49e08c0 --- /dev/null +++ b/worldsrv/actsignmgr.go @@ -0,0 +1,163 @@ +package main + +import ( + "github.com/globalsign/mgo" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/activity" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "strconv" + "time" +) + +var ActSignMgrSington = &ActSignMgr{ + SignConfigs: make(map[int]*server.DB_ActSign), +} + +type ActSignMgr struct { + SignConfigs map[int]*server.DB_ActSign +} + +func (this *ActSignMgr) Init() { + if this.SignConfigs == nil { + this.SignConfigs = make(map[int]*server.DB_ActSign) + } + for _, v := range srvdata.PBDB_ActSignMgr.Datas.GetArr() { + this.SignConfigs[int(v.Id)] = v + } +} + +func (this *ActSignMgr) GetConfig(id int) *server.DB_ActSign { + signConfig, ok := this.SignConfigs[id] + if ok { + return signConfig + } + return nil +} + +func (this *ActSignMgr) OnPlayerLogin(player *Player) error { + return this.RefixedPlayerData(player) +} + +func (this *ActSignMgr) OnDayChanged(player *Player) error { + //跨天不需要 + //this.RefixedPlayerData(player) + //this.SendSignDataToPlayer(player) + return nil +} + +func (this *ActSignMgr) RefixedPlayerData(player *Player) error { + if player.IsRob { + return nil + } + if player.SignData == nil { + player.SignData = &model.SignData{ + SignIndex: 0, + LastSignTickets: 0, + } + } + return nil +} + +func (this *ActSignMgr) SendSignDataToPlayer(player *Player) { + if player.IsRob { + return + } + pack := &activity.SCSignData{} + //已经领取第几个 + pack.SignCount = proto.Int(player.SignData.SignIndex) + if player.SignData.LastSignTickets != 0 { + lastSignTime := time.Unix(player.SignData.LastSignTickets, 0) + dayDiff := int32(common.DiffDay(time.Now(), lastSignTime)) + if dayDiff == 0 { + pack.TodaySign = proto.Int32(1) + } else { + pack.TodaySign = proto.Int32(0) + } + } else { + pack.TodaySign = proto.Int32(0) + } + proto.SetDefaults(pack) + player.SendToClient(int(activity.ActSignPacketID_PACKET_SCSignData), pack) + logger.Logger.Trace("SCSignData: ", pack) +} + +func (this *ActSignMgr) CanSign(player *Player, signIndex int) activity.OpResultCode_ActSign { + signConfig := this.GetConfig(signIndex) + if signConfig == nil { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Error + } + + if player.SignData.LastSignTickets != 0 { + lastSignTime := time.Unix(player.SignData.LastSignTickets, 0) + dayDiff := int32(common.DiffDay(time.Now(), lastSignTime)) + if dayDiff == 0 { + if player.SignData.SignIndex == signIndex { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Repeat + } else { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Config_Day_Error + } + } + + if player.SignData.SignIndex != (signIndex - 1) { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Config_Day_Error + } + } else { + if signIndex != 1 { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Config_Day_Error + } + } + + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess +} + +func (this *ActSignMgr) Sign(player *Player, signIndex int, signType int32) activity.OpResultCode_ActSign { + errCode := this.CanSign(player, signIndex) + if errCode != activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess { + return errCode + } + + signConfig := this.GetConfig(signIndex) + if signConfig == nil { + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Error + } + + player.SignData.LastSignTickets = time.Now().Unix() + player.SignData.SignIndex = signIndex + + logger.Logger.Info("签到成功: ", signConfig) + grade := signConfig.Grade + switch signType { + case 0: //普通签到 + case 1: //双倍签到 + grade *= 2 + } + switch signConfig.Type { + case 1: //金币 + player.AddCoin(int64(grade), 0, common.GainWay_ActSign, strconv.Itoa(signIndex), time.Now().Format("2006-01-02 15:04:05")) + case 2: //钻石 + player.AddDiamond(int64(grade), 0, common.GainWay_ActSign, strconv.Itoa(signIndex), time.Now().Format("2006-01-02 15:04:05")) + case 3: //道具 + item := &Item{ + ItemId: signConfig.Item_Id, + ItemNum: int64(grade), + } + BagMgrSingleton.AddJybBagInfo(player, []*Item{item}, 0, common.GainWay_ActSign, strconv.Itoa(signIndex), time.Now().Format("2006-01-02 15:04:05")) + data := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if data != nil { + BagMgrSingleton.RecordItemLog(player.Platform, player.SnId, ItemObtain, item.ItemId, data.Name, item.ItemNum, "14日签到获得") + } + } + return activity.OpResultCode_ActSign_OPRC_Activity_Sign_Sucess +} + +func init() { + mgo.SetStats(true) + RegisterParallelLoadFunc("14日签到", func() error { + ActSignMgrSington.Init() + return nil + }) +} diff --git a/worldsrv/bagmgr.go b/worldsrv/bagmgr.go new file mode 100644 index 0000000..ac5f851 --- /dev/null +++ b/worldsrv/bagmgr.go @@ -0,0 +1,534 @@ +package main + +import ( + "mongo.games.com/game/worldsrv/internal" + "strconv" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/bag" + playerproto "mongo.games.com/game/protocol/player" + "mongo.games.com/game/srvdata" +) + +const ( + BagItemMax int32 = 200 +) + +// 道具功能 Function +const ( + ItemCanUse = iota //可以使用 + ItemCanGive //可以赠送 + ItemCanSell //可以出售 + ItemMax +) + +type Item struct { + ItemId int32 // 物品ID + ItemNum int64 // 物品数量 + ////数据表数据 + Name string // 名称 + //ShowLocation []int32 // 显示位置 + //Classify []int32 // 分页类型 1,道具类 2,资源类 3,兑换类 + //Type int32 // 道具种类 1,宠物碎片 2,角色碎片 + Effect0 []int32 // 竖版道具功能 1,使用 2,赠送 3,出售 + Effect []int32 // 横版道具功能 1,使用 2,赠送 3,出售 + SaleType int32 // 出售类型 + SaleGold int32 // 出售金额 + //Composition int32 // 能否叠加 1,能 2,不能 + //CompositionMax int32 // 叠加上限 + //Time int32 // 道具时效 0为永久 + //Location string // 跳转页面 + //Describe string // 道具描述 + //数据库数据 + ObtainTime int64 //获取的时间 +} + +type BagInfo struct { + SnId int32 //玩家账号直接在这里生成 + Platform string //平台 + BagItem map[int32]*Item //背包数据 key为itemId + dirty bool +} + +// BagMgrSingleton 背包管理器 +var BagMgrSingleton = &BagMgr{ + PlayerBag: make(map[int32]*BagInfo), +} + +type BagMgr struct { + PlayerBag map[int32]*BagInfo // snid:背包 +} + +func (this *BagMgr) ModuleName() string { + return "BagMgr" +} + +func (this *BagMgr) Init() { +} + +func (this *BagMgr) GetBagInfo(snid int32) *BagInfo { + if v, exist := this.PlayerBag[snid]; exist { + return v + } + return nil +} + +// GetItem 获取个人的指定道具信息 +func (this *BagMgr) GetItem(snid, itemId int32) *Item { + if bagItem, ok := this.PlayerBag[snid]; ok { + if bagItem != nil { + item := bagItem.BagItem[itemId] + if item != nil { + itemX := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemX != nil { + item.Name = itemX.Name + //item.ShowLocation = itemX.ShowLocation + //item.Classify = itemX.Classify + //item.Type = itemX.Type + item.Effect0 = itemX.Effect + item.Effect = itemX.Effect + item.SaleType = itemX.SaleType + item.SaleGold = itemX.SaleGold + //item.Composition = itemX.Composition + //item.CompositionMax = itemX.CompositionMax + //item.Time = itemX.Time + //item.Location = itemX.Location + //item.Describe = itemX.Describe + } + return item + } + } + } + return nil +} + +// AddJybBagInfo 给玩家背包添加道具 +// add 加成数量 +// gainWay 记录类型 +// oper 操作人 +// remark 备注 +func (this *BagMgr) AddJybBagInfo(p *Player, addItems []*Item, add int64, gainWay int32, oper, remark string) (*BagInfo, bag.OpResultCode) { + var items []*Item + for _, v := range addItems { + if v == nil || v.ItemNum == 0 { + continue + } + item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + switch item.Type { + case common.ItemTypeCoin: + //增加金币 + if item.Id == common.ItemIDCoin { + p.AddCoin(v.ItemNum, 0, gainWay, oper, remark) + } + case common.ItemTypeDiamond: + //增加钻石 + if item.Id == common.ItemIDDiamond { + p.AddDiamond(v.ItemNum, 0, gainWay, oper, remark) + } + case common.ItemTypeFishPower: + //增加炮台 + p.ItemUnPlayerPowerListEx(v.ItemId) + case common.ItemTypeMoneyPond: + //增加个人金币池 + if v.ItemId == common.ItemIDMoneyPond { + p.MoneyPond += v.ItemNum + } + case common.ItemTypeVipExp: + //增加玩家VIP经验 + if v.ItemId == common.ItemIDVipExp { + p.AddMoneyPayTotal(v.ItemNum) + } + case common.ItemTypeShopScore: + if v.ItemId == common.ItemIDPhoneScore { + p.AddPhoneScore(v.ItemNum, 0, gainWay, oper, remark) + } + default: + // 道具变化 + items = append(items, v) + } + } + + // 添加道具到背包 + + var changeItems []int32 + var newBagInfo *BagInfo + if _, exist := this.PlayerBag[p.SnId]; !exist { + newBagInfo = &BagInfo{ + SnId: p.SnId, + Platform: p.Platform, + BagItem: make(map[int32]*Item), + } + } else { + newBagInfo = this.PlayerBag[p.SnId] + } + + if len(items) == 0 { + return newBagInfo, bag.OpResultCode_OPRC_Sucess + } + + var code = bag.OpResultCode_OPRC_Sucess + for _, v := range items { + if v == nil || v.ItemNum == 0 { + continue + } + item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if item == nil { + code = bag.OpResultCode_OPRC_IdErr + continue + } + + changeItems = append(changeItems, v.ItemId) + if itm, exist := newBagInfo.BagItem[v.ItemId]; exist { + itm.ItemNum += v.ItemNum + } else { + newBagInfo.BagItem[v.ItemId] = &Item{ + ItemId: item.Id, // 物品id + ItemNum: v.ItemNum, // 数量 + ObtainTime: time.Now().Unix(), + } + } + if v.ItemId == common.ItemIDWeekScore && v.ItemNum != 0 { + TaskSubjectSingleton.Touch(common.TaskTypeActivityScore, &TaskData{ + SnId: p.SnId, + Num: v.ItemNum, + }) + } + } + if len(changeItems) > 0 { + newBagInfo.dirty = true + p.dirty = true + this.PlayerBag[p.SnId] = newBagInfo + this.SyncBagData(p.SnId, changeItems...) + } + + return newBagInfo, code +} + +// SaleItem 出售道具,减少玩家道具数量 +func (this *BagMgr) SaleItem(p *Player, itemId int32, num int64) bool { + if bagInfo, ok := this.PlayerBag[p.SnId]; ok { + if item, ok1 := bagInfo.BagItem[itemId]; ok1 { + if item.ItemNum >= num { + //可以出售 + return this.SalePlayerItem(p, item, num) + } + } + } + return false +} + +// SalePlayerItem 出售道具,减少玩家道具数量 +func (this *BagMgr) SalePlayerItem(p *Player, item *Item, num int64) bool { + item.ItemNum -= num + p.dirty = true + this.SyncBagData(p.SnId, item.ItemId) + return true +} + +// RecordItemLog 道具操作记录(获得,消耗) +func (this *BagMgr) RecordItemLog(platform string, snid, logType, itemId int32, itemName string, count int64, remark string) { + //logger.Logger.Trace("RecordItemLog:", platform, snid, logType, itemId, itemName, count, remark) + log := model.NewItemLogEx(platform, snid, logType, itemId, itemName, count, remark) + if log != nil { + LogChannelSingleton.WriteLog(log) + } +} + +// AddMailByItem 赠送道具到邮件 +// srcId 发送人 srcName发送人名字 +// items[0]:道具id items[1]:道具数量 items[2]:道具id items[3]:道具数量 +func (this *BagMgr) AddMailByItem(platform string, srcId int32, srcName string, snid int32, showId int64, items []int32) { + logger.Logger.Trace("AddMailByItem:", srcId, srcName, items) + var newMsg *model.Message + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + content := i18n.Tr("languages", "GiftMail", srcName, srcName, srcName, srcName) + title := i18n.Tr("languages", "GiftMailTitle") + newMsg = model.NewMessageByPlayer("", 1, srcId, srcName, snid, model.MSGTYPE_ITEM, title, content, + 0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", items, platform, showId) + return model.InsertMessage(platform, newMsg) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil { + p.AddMessage(newMsg) + } + } + }), "AddMailByItem").Start() +} + +// VerifyUpJybInfo 兑换礼包 +func (this *BagMgr) VerifyUpJybInfo(p *Player, args *model.VerifyUpJybInfoArgs) { + + type VerifyInfo struct { + jyb *model.JybInfo + err error + } + pack := &playerproto.SCPlayerSetting{ + OpRetCode: playerproto.OpResultCode_OPRC_Sucess, + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + jyb, err := model.VerifyUpJybInfo(args) + info := &VerifyInfo{ + jyb: jyb, + err: err, + } + return info + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil && data.(*VerifyInfo) != nil { + jyb := data.(*VerifyInfo).jyb + err := data.(*VerifyInfo).err + if jyb != nil && jyb.Award != nil { // 领取到礼包 + pack.GainItem = &playerproto.JybInfoAward{} + if jyb.Award.Item != nil { + if len(jyb.Award.Item) > 0 { + items := make([]*Item, 0) + for _, v := range jyb.Award.Item { + items = append(items, &Item{ + ItemId: v.ItemId, // 物品id + ItemNum: v.ItemNum, // 数量 + ObtainTime: time.Now().Unix(), + }) + } + if _, code := this.AddJybBagInfo(p, items, 0, common.GainWay_ActJybAward, "system", "礼包码兑换"); code != bag.OpResultCode_OPRC_Sucess { //TODO 添加失败 要回退礼包 + logger.Logger.Errorf("CSPlayerSettingHandler AddJybBagInfo err", code) + pack.OpRetCode = playerproto.OpResultCode_OPRC_Error + proto.SetDefaults(pack) + p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack) + } else { + for _, v := range items { + itemData := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, v.ItemId, itemData.Name, int64(v.ItemNum), "礼包领取") + } + } + PetMgrSington.CheckShowRed(p) + } + p.dirty = true + } + } + for _, v := range jyb.Award.Item { + //if _, code := BagMgrSingleton.UpBagInfo(p.SnId, p.Platform, v.ItemId, v.ItemNum); code == bag.OpResultCode_OPRC_Sucess { // 需修改 + pack.GainItem.ItemId = append(pack.GainItem.ItemId, &playerproto.ItemInfo{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + }) + } + if jyb.Award.Coin > 0 { + p.AddCoin(jyb.Award.Coin, 0, common.GainWay_ActJybAward, "system", "礼包码兑换") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Coin, int64(jyb.Award.Coin))) + } + if jyb.Award.Diamond > 0 { + p.AddDiamond(jyb.Award.Diamond, 0, common.GainWay_ActJybAward, "system", "礼包码兑换") + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Diamond, int64(jyb.Award.Diamond))) + } + p.dirty = true + pack.GainItem.Coin = jyb.Award.Coin + pack.GainItem.Diamond = jyb.Award.Diamond + } else { + switch err.Error() { + case model.ErrJybISReceive.Error(): + pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_Receive + case model.ErrJYBPlCode.Error(): + pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeExist + case model.ErrJybTsTimeErr.Error(): + pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_TimeErr + default: + pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeErr + } + } + } else { + proto.SetDefaults(pack) + p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack) + } + proto.SetDefaults(pack) + p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack) + }), "VerifyUpJybInfo").Start() + // 先检查玩家背包是否足够 + +} + +// SyncBagData 通知玩家背包数据变化 +func (this *BagMgr) SyncBagData(snid int32, changeItemIds ...int32) { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p == nil || p.IsRob { + return + } + + var itemInfos []*bag.ItemInfo + for _, itemId := range changeItemIds { + itemInfo := this.GetItem(snid, itemId) + if itemInfo != nil { + itemInfos = append(itemInfos, &bag.ItemInfo{ + ItemId: itemInfo.ItemId, + ItemNum: itemInfo.ItemNum, + //Name: itemInfo.Name, + //Classify: itemInfo.Classify, + //Type: itemInfo.Type, + //Effect0: itemInfo.Effect0, + //Effect: itemInfo.Effect, + //SaleType: itemInfo.SaleType, + //SaleGold: itemInfo.SaleGold, + //Composition: itemInfo.Composition, + //CompositionMax: itemInfo.CompositionMax, + //Time: itemInfo.Time, + //Location: itemInfo.Location, + //Describe: itemInfo.Describe, + ObtainTime: itemInfo.ObtainTime, + }) + if itemInfo.ItemId == VCard { + FriendMgrSington.UpdateInfo(p.Platform, p.SnId) + } + } + } + p.SyncBagData(itemInfos) +} + +func (this *BagMgr) Update() { +} + +func (this *BagMgr) Shutdown() { + module.UnregisteModule(this) +} + +//========================implement IPlayerLoad ============================== + +func (this *BagMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay { + data := model.GetBagInfo(snid, platform) + return &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + Err: nil, + Data: data, + } +} + +func (this *BagMgr) Callback(player any, ret *internal.PlayerLoadReplay) { + if ret == nil || ret.Data == nil { + return + } + + bagInfo, ok := ret.Data.(*model.BagInfo) + if !ok || bagInfo == nil { + return + } + + //数据表 数据库数据相结合 + newBagInfo := &BagInfo{ + SnId: ret.Snid, + Platform: ret.Platform, + BagItem: make(map[int32]*Item), + } + for k, bi := range bagInfo.BagItem { + item := srvdata.PBDB_GameItemMgr.GetData(bi.ItemId) + if item != nil { + if bi.ItemNum > 0 { + newBagInfo.BagItem[k] = &Item{ + ItemId: bi.ItemId, + ItemNum: bi.ItemNum, + ObtainTime: bi.ObtainTime, + } + } + } else { + logger.Logger.Error("InitBagInfo err: item is nil. ItemId:", bi.ItemId) + } + } + this.PlayerBag[ret.Snid] = newBagInfo +} + +func (this *BagMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay { + // 查询最近游戏 + gameID := model.GetRecentGame(platform, snid) + return &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + Err: nil, + Data: gameID, + } +} + +func (this *BagMgr) CallbackAfter(ret *internal.PlayerLoadReplay) { + if ret == nil { + return + } + if ret.Err != nil { + logger.Logger.Error("BagMgr LoadAfter err:", ret.Err) + return + } + p := PlayerMgrSington.GetPlayerBySnId(ret.Snid) + if p != nil { + p.GameID = ret.Data.([]int32) + } +} + +func (this *BagMgr) Save(platform string, snid int32, isSync, force bool) { + bagInfo := this.PlayerBag[snid] + logger.Logger.Trace("SaveBagData:", bagInfo) + + if bagInfo == nil || (!bagInfo.dirty && !force) { + return + } + + type BagInfoMap struct { + SnId int32 //玩家账号直接在这里生成 + Platform string //平台 + BagItem []*Item //背包数据 key为itemId + } + // biMap 数据拷贝 + var biMap = BagInfoMap{ + SnId: bagInfo.SnId, + Platform: bagInfo.Platform, + } + for _, v := range bagInfo.BagItem { + biMap.BagItem = append(biMap.BagItem, &Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime}) + } + + var err error + f := func() { + newBagInfo := &model.BagInfo{ + SnId: biMap.SnId, + Platform: biMap.Platform, + BagItem: make(map[int32]*model.Item), + } + for _, v := range biMap.BagItem { + newBagInfo.BagItem[v.ItemId] = &model.Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime} + } + err = model.UpBagItem(newBagInfo) + } + + cf := func() { + if err == nil { + bagInfo.dirty = false + } + } + + if isSync { + f() + cf() + return + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + f() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + cf() + }), "SaveBagData").StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) +} + +func (this *BagMgr) Release(platform string, snid int32) { + delete(this.PlayerBag, snid) +} + +func init() { + module.RegisteModule(BagMgrSingleton, time.Second, 0) + internal.RegisterPlayerLoad(BagMgrSingleton) +} diff --git a/worldsrv/blacklistmgr.go b/worldsrv/blacklistmgr.go new file mode 100644 index 0000000..a542cfd --- /dev/null +++ b/worldsrv/blacklistmgr.go @@ -0,0 +1,598 @@ +package main + +import ( + "github.com/globalsign/mgo" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/logger" + "net" + "strings" +) + +const ( + BlackState_Login uint = 1 << iota //登录 + BlackState_Exchange //兑换 + BlackState_Recharge //充值 + BlackState_Match //比赛 + BlackState_Max +) + +var BlackListMgrSington = &BlackListMgr{ + BlackList: make(map[int32]*BlackInfo), +} + +//type BlackListObserver interface { +// OnAddBlackInfo(blackinfo *BlackInfo) +// OnEditBlackInfo(blackinfo *BlackInfo) +// OnRemoveBlackInfo(blackinfo *BlackInfo) +//} + +type BlackListMgr struct { + BlackList map[int32]*BlackInfo + BlackListByPlatform [BlackState_Max]map[string]map[int32]*BlackInfo + AlipayAccByPlatform [BlackState_Max]map[string]map[string]*BlackInfo + AlipayNameByPlatform [BlackState_Max]map[string]map[string]*BlackInfo + BankcardByPlatform [BlackState_Max]map[string]map[string]*BlackInfo + IpByPlatform [BlackState_Max]map[string]map[string]*BlackInfo + IpNetByPlatform [BlackState_Max]map[string][]*BlackInfo + PackageTagByPlatform [BlackState_Max]map[string]*BlackInfo + DeviceByPlatform [BlackState_Max]map[string]map[string]*BlackInfo + //Observers []BlackListObserver +} + +type BlackInfo struct { + Id int32 + BlackType int //1.游戏2.兑换3.充值4.比赛 + Alipay_account string + Alipay_name string + Bankcard string + Ip string //support like "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291. + Platform string + PackageTag string + DeviceId string //设备ID + ipNet *net.IPNet +} +type BlackInfoApi struct { + Id int32 + Space int32 + Snid int32 + Alipay_account string + Alipay_name string + Bankcard string + Ip string + Platform string + PackageTag string + Explain string + Creator int32 + Create_time string + Update_time string + DeviceId string //设备ID +} + +//func (this *BlackListMgr) RegisterObserver(observer BlackListObserver) { +// for _, ob := range this.Observers { +// if ob == observer { +// return +// } +// } +// this.Observers = append(this.Observers, observer) +//} +// +//func (this *BlackListMgr) UnregisterObserver(observer BlackListObserver) { +// for i, ob := range this.Observers { +// if ob == observer { +// count := len(this.Observers) +// if i == 0 { +// this.Observers = this.Observers[1:] +// } else if i == count-1 { +// this.Observers = this.Observers[:count-1] +// } else { +// arr := this.Observers[:i] +// arr = append(arr, this.Observers[i+1:]...) +// this.Observers = arr +// } +// } +// } +//} + +func (this *BlackListMgr) Init() { + if this.BlackList == nil { + this.BlackList = make(map[int32]*BlackInfo) + } + for i := uint(0); i < BlackState_Max; i++ { + this.BlackListByPlatform[i] = make(map[string]map[int32]*BlackInfo) + this.AlipayAccByPlatform[i] = make(map[string]map[string]*BlackInfo) + this.AlipayNameByPlatform[i] = make(map[string]map[string]*BlackInfo) + this.BankcardByPlatform[i] = make(map[string]map[string]*BlackInfo) + this.IpByPlatform[i] = make(map[string]map[string]*BlackInfo) + this.IpNetByPlatform[i] = make(map[string][]*BlackInfo) + this.PackageTagByPlatform[i] = make(map[string]*BlackInfo) + this.DeviceByPlatform[i] = make(map[string]map[string]*BlackInfo) + } + if !model.GameParamData.UseEtcd { + + } else { + EtcdMgrSington.InitBlackList() + } + +} + +func (this *BlackListMgr) DivBlackInfo(blackInfo *BlackInfo) { + for i := uint(0); i < BlackState_Max; i++ { + if blackInfo.BlackType&(1< 0 { + aabp := this.AlipayAccByPlatform[i] + if aabp != nil { + if pool, exist := aabp[blackInfo.Platform]; exist { + pool[blackInfo.Alipay_account] = blackInfo + } else { + pool = make(map[string]*BlackInfo) + if pool != nil { + pool[blackInfo.Alipay_account] = blackInfo + aabp[blackInfo.Platform] = pool + } + } + } + } + + //alipay name + if len(blackInfo.Alipay_name) > 0 { + anbp := this.AlipayNameByPlatform[i] + if anbp != nil { + if pool, exist := anbp[blackInfo.Platform]; exist { + pool[blackInfo.Alipay_name] = blackInfo + } else { + pool = make(map[string]*BlackInfo) + if pool != nil { + pool[blackInfo.Alipay_name] = blackInfo + anbp[blackInfo.Platform] = pool + } + } + } + } + + //bank + if len(blackInfo.Bankcard) > 0 { + bankbp := this.BankcardByPlatform[i] + if bankbp != nil { + if pool, exist := bankbp[blackInfo.Platform]; exist { + pool[blackInfo.Bankcard] = blackInfo + } else { + pool = make(map[string]*BlackInfo) + if pool != nil { + pool[blackInfo.Bankcard] = blackInfo + bankbp[blackInfo.Platform] = pool + } + } + } + } + + //ip + if len(blackInfo.Ip) > 0 { + ipbp := this.IpByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + pool[blackInfo.Ip] = blackInfo + } else { + pool = make(map[string]*BlackInfo) + if pool != nil { + pool[blackInfo.Ip] = blackInfo + ipbp[blackInfo.Platform] = pool + } + } + } + } + + //ipnet + if blackInfo.ipNet != nil { + ipbp := this.IpNetByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + pool = append(pool, blackInfo) + ipbp[blackInfo.Platform] = pool + } else { + ipbp[blackInfo.Platform] = []*BlackInfo{blackInfo} + } + } + } + + //packageid + if len(blackInfo.PackageTag) > 0 { + packbp := this.PackageTagByPlatform[i] + if packbp != nil { + packbp[blackInfo.PackageTag] = blackInfo + } + } + + //deviceinfo + if len(blackInfo.DeviceId) > 0 { + ipbp := this.DeviceByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + pool[blackInfo.DeviceId] = blackInfo + } else { + pool = make(map[string]*BlackInfo) + if pool != nil { + pool[blackInfo.DeviceId] = blackInfo + ipbp[blackInfo.Platform] = pool + } + } + } + } + } + } +} + +func (this *BlackListMgr) UnDivBlackInfo(blackInfo *BlackInfo) { + for i := uint(0); i < BlackState_Max; i++ { + if blackInfo.BlackType&(1< 0 { + aabp := this.AlipayAccByPlatform[i] + if aabp != nil { + if pool, exist := aabp[blackInfo.Platform]; exist { + delete(pool, blackInfo.Alipay_account) + } + } + } + + //alipay name + if len(blackInfo.Alipay_name) > 0 { + anbp := this.AlipayNameByPlatform[i] + if anbp != nil { + if pool, exist := anbp[blackInfo.Platform]; exist { + delete(pool, blackInfo.Alipay_name) + } + } + } + + //bank + if len(blackInfo.Bankcard) > 0 { + bankbp := this.BankcardByPlatform[i] + if bankbp != nil { + if pool, exist := bankbp[blackInfo.Platform]; exist { + delete(pool, blackInfo.Bankcard) + } + } + } + + //ip + if len(blackInfo.Ip) > 0 { + ipbp := this.IpByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + delete(pool, blackInfo.Ip) + } + } + } + + //ipnet + if blackInfo.ipNet != nil { + ipbp := this.IpNetByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + index := -1 + for i, bi := range pool { + if bi.ipNet == blackInfo.ipNet { + index = i + break + } + } + if index != -1 { + pool = append(pool[:index], pool[index+1:]...) + } + ipbp[blackInfo.Platform] = pool + } + } + } + + //packageid + if len(blackInfo.PackageTag) > 0 { + packbp := this.PackageTagByPlatform[i] + if packbp != nil { + delete(packbp, blackInfo.PackageTag) + } + } + + //deviceinfo + if len(blackInfo.DeviceId) > 0 { + ipbp := this.DeviceByPlatform[i] + if ipbp != nil { + if pool, exist := ipbp[blackInfo.Platform]; exist { + delete(pool, blackInfo.DeviceId) + } + } + } + } + } +} + +// 初始化 +func (this *BlackListMgr) InitBlackInfo(bia *BlackInfoApi) { + blackInfo := &BlackInfo{ + Id: bia.Id, + BlackType: int(bia.Space), + Alipay_account: bia.Alipay_account, + Alipay_name: bia.Alipay_name, + Bankcard: bia.Bankcard, + Ip: bia.Ip, + Platform: bia.Platform, + PackageTag: bia.PackageTag, + DeviceId: bia.DeviceId, + } + if strings.Contains(blackInfo.Ip, "/") { + _, blackInfo.ipNet, _ = net.ParseCIDR(blackInfo.Ip) + } + this.BlackList[blackInfo.Id] = blackInfo + this.DivBlackInfo(blackInfo) +} +func (this *BlackListMgr) UpsertBlackInfo(bia *BlackInfoApi) { + blackInfo := &BlackInfo{ + Id: bia.Id, + BlackType: int(bia.Space), + Alipay_account: bia.Alipay_account, + Alipay_name: bia.Alipay_name, + Bankcard: bia.Bankcard, + Ip: bia.Ip, + Platform: bia.Platform, + PackageTag: bia.PackageTag, + DeviceId: bia.DeviceId, + } + if strings.Contains(blackInfo.Ip, "/") { + _, blackInfo.ipNet, _ = net.ParseCIDR(blackInfo.Ip) + } + if old, exist := this.BlackList[blackInfo.Id]; exist { + this.UnDivBlackInfo(old) + } + this.BlackList[blackInfo.Id] = blackInfo + this.DivBlackInfo(blackInfo) +} + +func (this *BlackListMgr) RemoveBlackInfo(Id int32, platform string) { + if org, exist := this.BlackList[Id]; exist { + if len(platform) == 0 || org.Platform == platform { + logger.Logger.Infof("(this *BlackListMgr) RemoveBlackInfo(Id:%v, platform:%v)", Id, platform) + delete(this.BlackList, Id) + this.UnDivBlackInfo(org) + } + } +} + +// func (this *BlackListMgr) RemoveBlackInfoByUser(blackinfo *BlackInfo) { +// this.OnRemoveBlackInfo(blackinfo) +// } +// +// func (this *BlackListMgr) OnAddBlackInfo(blackinfo *BlackInfo) { +// for _, ob := range this.Observers { +// ob.OnAddBlackInfo(blackinfo) +// } +// } +// +// func (this *BlackListMgr) OnEditBlackInfo(blackinfo *BlackInfo) { +// for _, ob := range this.Observers { +// ob.OnEditBlackInfo(blackinfo) +// } +// } +// +// func (this *BlackListMgr) OnRemoveBlackInfo(blackinfo *BlackInfo) { +// for _, ob := range this.Observers { +// ob.OnRemoveBlackInfo(blackinfo) +// } +// } +func (this *BlackListMgr) CheckPlayerInvalid(blackinfo *BlackInfo, data *model.PlayerData) bool { + if len(blackinfo.Platform) > 0 && (data.Platform != blackinfo.Platform && blackinfo.Platform != "0") { + return false + } + switch { + //case blackinfo.Snid == data.SnId: //根据用户的ID找到了对应的用户 + // return true + case len(blackinfo.Ip) > 0 && blackinfo.Ip == data.Ip: //根据用户的IP找到了对应的用户 + return true + case len(blackinfo.Alipay_name) > 0 && blackinfo.Alipay_name == data.AlipayAccName: //根据用户的支付宝找到了对应的用户 + return true + case len(blackinfo.Alipay_account) > 0 && blackinfo.Alipay_account == data.AlipayAccount: //根据用户的支付宝账户找到了对应的用户 + return true + case len(blackinfo.Bankcard) > 0 && blackinfo.Bankcard == data.BankAccount: //根据用户的银行卡找到了对应的用户 + return true + case len(blackinfo.PackageTag) > 0 && strings.HasPrefix(data.PackageID, blackinfo.PackageTag): //根据包标识找对应的用户 + return true + } + if blackinfo.ipNet != nil { + ip := net.ParseIP(data.Ip) + if ip != nil { + if blackinfo.ipNet.Contains(ip) { + return true + } + } + } + return false +} +func (this *BlackListMgr) CheckLogin(data *model.PlayerData) (*BlackInfo, bool) { + if bi, ok := this.CheckPlayerInBlack(data, BlackState_Login); ok { + return bi, false + } + return nil, true +} +func (this *BlackListMgr) CheckExchange(data *model.PlayerData) (*BlackInfo, bool) { + if bi, ok := this.CheckPlayerInBlack(data, BlackState_Exchange); ok { + return bi, false + } + return nil, true +} +func (this *BlackListMgr) CheckRecharge(data *model.PlayerData) (*BlackInfo, bool) { + if bi, ok := this.CheckPlayerInBlack(data, BlackState_Recharge); ok { + return bi, false + } + return nil, true +} +func (this *BlackListMgr) CheckMatch(data *model.PlayerData) (*BlackInfo, bool) { + if bi, ok := this.CheckPlayerInBlack(data, BlackState_Match); ok { + return bi, false + } + return nil, true +} +func (this *BlackListMgr) CheckPlayerInBlack(data *model.PlayerData, blackType uint) (bi *BlackInfo, ok bool) { + if data.IsRob { + return nil, false + } + if bi, ok := this.CheckPlayerInBlackByPlatfromUsePlayerBind(data, blackType, data.Platform); ok { + return bi, true + } + return nil, false +} + +func (this *BlackListMgr) CheckPlayerInBlackByPlatfromUsePlayerBind(data *model.PlayerData, blackType uint, platform string) (*BlackInfo, bool) { + if blackType >= 0 && blackType < BlackState_Max { + //先检查snid + if uint(data.BlacklistType)&blackType != 0 { + logger.Logger.Infof("found platform:%v player:%d snid in blacklist", platform, data.SnId) + return nil, true + } + + //支付宝账号 + //if len(data.AlipayAccount) > 0 { + // aabp := this.AlipayAccByPlatform[blackType] + // if aabp != nil { + // if pool, exist := aabp[platform]; exist { + // if bi, exist := pool[data.AlipayAccount]; exist { + // logger.Logger.Infof("found platform:%v player:%d AlipayAccount:%v in blacklist", platform, data.SnId, data.AlipayAccount) + // return bi, true + // } + // } + // } + //} + + //支付宝用户名 + //if len(data.AlipayAccName) > 0 { + // aabp := this.AlipayNameByPlatform[blackType] + // if aabp != nil { + // if pool, exist := aabp[platform]; exist { + // if bi, exist := pool[data.AlipayAccName]; exist { + // logger.Logger.Infof("found platform:%v player:%d AlipayAccName:%v in blacklist", platform, data.SnId, data.AlipayAccName) + // return bi, true + // } + // } + // } + //} + + //银行卡号 + //if len(data.BankAccount) > 0 { + // bankbp := this.BankcardByPlatform[blackType] + // if bankbp != nil { + // if pool, exist := bankbp[platform]; exist { + // if bi, exist := pool[data.BankAccount]; exist { + // logger.Logger.Infof("found platform:%v player:%d BankAccount:%v in blacklist", platform, data.SnId, data.BankAccount) + // return bi, true + // } + // } + // } + //} + + //ip检查 + if len(data.Ip) > 0 { + //精准ip + ipbp := this.IpByPlatform[blackType] + if ipbp != nil { + if pool, exist := ipbp[platform]; exist { + if bi, exist := pool[data.Ip]; exist { + logger.Logger.Infof("found platform:%v player:%d Ip:%v in blacklist", platform, data.SnId, data.Ip) + return bi, true + } + } + } + + //ip段 + ipnetbp := this.IpNetByPlatform[blackType] + if ipnetbp != nil { + if pool, exist := ipnetbp[platform]; exist { + for _, bi := range pool { + ip := net.ParseIP(data.Ip) + if ip != nil { + if bi.ipNet.Contains(ip) { + logger.Logger.Infof("found platform:%v player:%d Ip:%v in blacklist ipnet:%v", platform, data.SnId, data.Ip, bi.ipNet.String()) + return bi, true + } + } + } + } + } + } + + //包标识 + if len(data.PackageID) > 0 { + packbp := this.PackageTagByPlatform[blackType] + if packbp != nil { + if bi, exist := packbp[data.PackageID]; exist { + logger.Logger.Infof("found platform:%v player:%d PackageID:%v in blacklist", platform, data.SnId, data.PackageID) + return bi, true + } + } + } + //设备号 + if len(data.DeviceId) > 0 { + deviceByPlatform := this.DeviceByPlatform[blackType] + if deviceByPlatform != nil { + if devices, exist := deviceByPlatform[data.Platform]; exist { + if bi, ok := devices[data.DeviceId]; ok { + logger.Logger.Infof("found platform:%v player:%d PackageID:%v in blacklist", platform, data.SnId, data.PackageID) + return bi, true + } + } + } + } + } + return nil, false +} + +// 设备号验证 +func (this *BlackListMgr) CheckDeviceInBlack(deviceId string, blackType uint, platform string) (*BlackInfo, bool) { + if bi, ok := this.CheckDeviceInBlackByPlatfrom(deviceId, blackType, platform); ok { + return bi, true + } + return nil, false +} + +func (this *BlackListMgr) CheckDeviceInBlackByPlatfrom(deviceId string, blackType uint, platform string) (*BlackInfo, bool) { + if blackType >= 0 && blackType < BlackState_Max { + if len(deviceId) > 0 { + devicebp := this.DeviceByPlatform[blackType] + if devicebp != nil { + if pool, exist := devicebp[platform]; exist { + if bi, ok := pool[deviceId]; ok { + logger.Logger.Infof("found platform:%v device:%s in blacklist", platform, deviceId) + return bi, true + } + } + } + } + } + return nil, false +} + +func init() { + mgo.SetStats(true) + RegisterParallelLoadFunc("平台黑名单", func() error { + BlackListMgrSington.Init() + return nil + }) +} diff --git a/worldsrv/broadcasthandler.go b/worldsrv/broadcasthandler.go new file mode 100644 index 0000000..38650cd --- /dev/null +++ b/worldsrv/broadcasthandler.go @@ -0,0 +1,62 @@ +package main + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + BroadcastMaker = &BroadcastPacketFactory{} +) + +type BroadcastPacketFactory struct { +} + +type BroadcastHandler struct { +} + +func init() { + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), &BroadcastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_BROADCAST), BroadcastMaker) +} + +func (this *BroadcastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketBroadcast{} + return pack +} + +func (this *BroadcastPacketFactory) CreateBroadcastPacket(sp *protocol.BCSessionUnion, packetid int, data interface{}) (rawproto.Message, error) { + pack := &protocol.SSPacketBroadcast{ + SessParam: sp, + PacketId: proto.Int(packetid), + } + + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Warn("BroadcastPacketFactory.CreateBroadcastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *BroadcastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if bp, ok := data.(*protocol.SSPacketBroadcast); ok { + pd := bp.GetData() + sp := bp.GetSessParam() + if bcss := sp.GetBcss(); bcss != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(bp.GetPacketId()), pd, int(bcss.GetSArea()), int(bcss.GetSType())) + } + } + return nil +} diff --git a/worldsrv/bulletin.go b/worldsrv/bulletin.go new file mode 100644 index 0000000..b2cea9b --- /dev/null +++ b/worldsrv/bulletin.go @@ -0,0 +1,207 @@ +package main + +// +//import ( +// "encoding/json" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/module" +// "time" +//) +// +////公告模块 +// +//var BulletMgrSington = &BulletMgr{ +// BulletMsgList: make(map[int32]*Bullet), +//} +// +//type BulletMgr struct { +// BulletMsgList map[int32]*Bullet +//} +// +//type Bullet struct { +// Id int32 +// Sort int32 //排序 +// Platform string +// NoticeTitle string +// NoticeContent string +// UpdateTime string +// State int //0 关闭 1开启 +//} +//type ApiBulletResult struct { +// Tag int +// Msg []Bullet +//} +// +//func (this *BulletMgr) query() { +// //不使用etcd的情况下走api获取 +// if model.GameParamData.UseEtcd { +// EtcdMgrSington.InitPlatformBulletin() +// } else { +// buff, err := webapi.API_GetBulletData(common.GetAppId()) +// //logger.Logger.Warn("bulletin buff: ", string(buff)) +// if err == nil { +// info := ApiBulletResult{} +// err = json.Unmarshal([]byte(buff), &info) +// if err == nil { +// for i := 0; i < len(info.Msg); i++ { +// BulletMgrSington.BulletMsgList[info.Msg[i].Id] = &info.Msg[i] +// } +// } else { +// logger.Logger.Error("Unmarshal Bullet data error:", err, string(buff)) +// } +// } else { +// logger.Logger.Error("Get Bullet data error:", err) +// } +// } +//} +// +//func (this *BulletMgr) clearPlatformBullet(Platform string) { +// for k, v := range this.BulletMsgList { +// if v.Platform == Platform { +// delete(this.BulletMsgList, k) +// } +// } +//} +// +//func (this *BulletMgr) updateBullet(id int32, info string) (map[int32]*Bullet, string) { +// platform := "" +// if info == "" { +// delete(this.BulletMsgList, id) +// platform = "delete" +// } else { +// bt := this.Unmarshal(info) +// if bt != nil { +// this.BulletMsgList[id] = bt +// platform = bt.Platform +// } +// } +// return this.BulletMsgList, platform +//} +//func (this *BulletMgr) Unmarshal(info string) (bt *Bullet) { +// err := json.Unmarshal([]byte(info), &bt) +// if err != nil { +// logger.Logger.Trace("Unmarshal Bullet is error :", err) +// return nil +// } +// return +//} +//func (this *BulletMgr) ModuleName() string { +// return "BulletMgr" +//} +//func (this *BulletMgr) Init() { +//} +//func (this *BulletMgr) Update() { +//} +//func (this *BulletMgr) Shutdown() { +// module.UnregisteModule(this) +//} +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////招商列表 +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +//var CustomerMgrSington = &CustomerMgr{ +// CustomerMsgList: make(map[int32]*Customer), +//} +// +//type CustomerMgr struct { +// CustomerMsgList map[int32]*Customer +//} +//type Customer struct { +// Id int32 +// Platform string +// Weixin_account string +// Qq_account string +// Headurl string +// Nickname string +// Status int +// Ext string +//} +//type ApiCustomerResult struct { +// Tag int +// Msg []Customer +//} +// +//func (this *CustomerMgr) ModuleName() string { +// return "CustomerMgr" +//} +//func (this *CustomerMgr) Init() { +//} +//func (this *CustomerMgr) Update() { +//} +//func (this *CustomerMgr) Shutdown() { +// module.UnregisteModule(this) +//} +//func (this *CustomerMgr) query() { +// //不使用etcd的情况下走api获取 +// if model.GameParamData.UseEtcd { +// EtcdMgrSington.InitPlatformAgent() +// } else { +// buff, err := webapi.API_GetCustomerData(common.GetAppId()) +// logger.Logger.Trace("customer buff:", string(buff)) +// if err == nil { +// c_info := ApiCustomerResult{} +// err = json.Unmarshal([]byte(buff), &c_info) +// if err == nil { +// for i := 0; i < len(c_info.Msg); i++ { +// CustomerMgrSington.CustomerMsgList[c_info.Msg[i].Id] = &c_info.Msg[i] +// } +// } else { +// logger.Logger.Trace("CustomerMgr is Unmarshal error.", err) +// } +// } else { +// logger.Logger.Trace("API_GetCustomerData is error. ", err) +// } +// } +//} +//func (this *CustomerMgr) updateCustomer(id int32, info string) (map[int32]*Customer, string) { +// platform := "" +// if info == "" { +// delete(this.CustomerMsgList, id) +// platform = "delete" +// } else { +// bt := this.Unmarshal(info) +// if bt != nil { +// this.CustomerMsgList[id] = bt +// platform = bt.Platform +// } +// } +// return this.CustomerMsgList, platform +//} +//func (this *CustomerMgr) Unmarshal(info string) (bt *Customer) { +// err := json.Unmarshal([]byte(info), &bt) +// if err != nil { +// logger.Logger.Trace("Unmarshal Customer is error :", err) +// return nil +// } +// return +//} +// +//func init() { +// module.RegisteModule(BulletMgrSington, time.Second*2, 0) +// module.RegisteModule(CustomerMgrSington, time.Second*2, 0) +// +// RegisterParallelLoadFunc("平台公告", func() error { +// BulletMgrSington.query() +// return nil +// }) +// +// ////不使用并发加载,因为并发太快,用到GameParam里面的数据还未来的及加载,下同 +// //core.RegisteHook(core.HOOK_BEFORE_START, func() error { +// // BulletMgrSington.query() +// // return nil +// //}) +// +// RegisterParallelLoadFunc("平台代理", func() error { +// CustomerMgrSington.query() +// return nil +// }) +// +// //core.RegisteHook(core.HOOK_BEFORE_START, func() error { +// // CustomerMgrSington.query() +// // return nil +// //}) +//} diff --git a/worldsrv/cachedata.go b/worldsrv/cachedata.go new file mode 100644 index 0000000..76ab4b5 --- /dev/null +++ b/worldsrv/cachedata.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "sync" + "time" +) + +const ( + AfterMini = 1 //数据缓存1分钟 + AfterHour = 2 //数据缓存1小时 + AfterDay = 3 //数据缓存1天 + AfterWeek = 4 //数据缓存1周 + AfterOver = 5 //数据缓存到服务器关闭 +) + +var CacheDataMgr = &CacheDataManager{ + MiniCache: new(sync.Map), + HourCache: new(sync.Map), + DayCache: new(sync.Map), + WeekCache: new(sync.Map), + OverCache: new(sync.Map), +} + +type CacheDataManager struct { + BaseClockSinker + MiniCache *sync.Map + HourCache *sync.Map + DayCache *sync.Map + WeekCache *sync.Map + OverCache *sync.Map +} +type CacheData struct { + Data interface{} + Ts int64 +} + +func (this *CacheDataManager) addCacheData(timeRange int, key string, data interface{}) { + switch timeRange { + case AfterMini: + this.MiniCache.Store(key, &CacheData{ + Data: data, + Ts: time.Now().Add(time.Minute).Unix(), + }) + case AfterHour: + this.HourCache.Store(key, &CacheData{ + Data: data, + Ts: time.Now().Add(time.Hour).Unix(), + }) + case AfterDay: + this.DayCache.Store(key, &CacheData{ + Data: data, + Ts: time.Now().Add(time.Hour * 24).Unix(), + }) + case AfterWeek: + this.DayCache.Store(key, &CacheData{ + Data: data, + Ts: time.Now().Add(time.Hour * 24 * 7).Unix(), + }) + case AfterOver: + this.DayCache.Store(key, &CacheData{ + Data: data, + Ts: time.Now().Add(time.Hour * 24 * 999).Unix(), + }) + } +} + +// 感兴趣所有clock event +func (this *CacheDataManager) InterestClockEvent() int { + return 1 << CLOCK_EVENT_MINUTE +} + +func (this *CacheDataManager) OnMiniTimer() { + now := time.Now().Unix() + this.MiniCache.Range(func(key, value interface{}) bool { + if data, ok := value.(*CacheData); ok { + if data.Ts < now { + this.MiniCache.Delete(key) + } + } + return true + }) + this.HourCache.Range(func(key, value interface{}) bool { + if data, ok := value.(*CacheData); ok { + if data.Ts < now { + this.HourCache.Delete(key) + } + } + return true + }) + this.DayCache.Range(func(key, value interface{}) bool { + if data, ok := value.(*CacheData); ok { + if data.Ts < now { + this.DayCache.Delete(key) + } + } + return true + }) + this.WeekCache.Range(func(key, value interface{}) bool { + if data, ok := value.(*CacheData); ok { + if data.Ts < now { + this.WeekCache.Delete(key) + } + } + return true + }) +} + +/* + * 缓存账单号,避免有重复的账单号提交 + */ +func (this *CacheDataManager) CacheBillNumber(billNo int, platform string) { + key := fmt.Sprintf("BillNo-%v-%v", billNo, platform) + this.addCacheData(AfterHour, key, key) +} +func (this *CacheDataManager) CacheBillCheck(billNo int, platform string) bool { + key := fmt.Sprintf("BillNo-%v-%v", billNo, platform) + if _, ok := this.HourCache.Load(key); ok { + return true + } else { + return false + } +} +func (this *CacheDataManager) ClearCacheBill(billNo int, platform string) { + key := fmt.Sprintf("BillNo-%v-%v", billNo, platform) + this.HourCache.Delete(key) +} + +func init() { + ClockMgrSington.RegisteSinker(CacheDataMgr) +} diff --git a/worldsrv/chatmgr.go b/worldsrv/chatmgr.go new file mode 100644 index 0000000..f2babfe --- /dev/null +++ b/worldsrv/chatmgr.go @@ -0,0 +1,205 @@ +package main + +import ( + "strconv" + "time" + + "github.com/globalsign/mgo/bson" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/model" +) + +var ChatMgrSington = &ChatMgr{ + ChatList: make(map[string]map[string]*Chat), + TsChat: make(map[int32]int64), +} + +type ChatMgr struct { + ChatList map[string]map[string]*Chat // platform:会话(snid,toSnid):聊天内容 + TsChat map[int32]int64 // snid:时间戳 +} + +type Chat struct { + Id bson.ObjectId `bson:"_id"` + BindSnid string //snid1 < snid2 + ChatContent []*ChatContent + LastChatTs int64 + Dirty bool +} + +type ChatContent struct { + SrcSnId int32 + Content string + Ts int64 +} + +func (this *ChatMgr) ModuleName() string { + return "ChatMgr" +} + +func (this *ChatMgr) Init() { +} + +// CanSendToPlatform 大厅聊天cd(5秒) +func (this *ChatMgr) CanSendToPlatform(snid int32) bool { + if this.TsChat[snid] == 0 { + this.TsChat[snid] = time.Now().Unix() + return true + } + if time.Now().Unix()-this.TsChat[snid] > 5 { + this.TsChat[snid] = time.Now().Unix() + return true + } + return false +} + +func (this *ChatMgr) getBindSnid(snid, tosnid int32) string { + ret := "" + if snid <= tosnid { + ret = strconv.FormatInt(int64(snid), 10) + "_" + strconv.FormatInt(int64(tosnid), 10) + } else { + ret = strconv.FormatInt(int64(tosnid), 10) + "_" + strconv.FormatInt(int64(snid), 10) + } + return ret +} + +// 新增聊天信息 +func (this *ChatMgr) AddChat(platform string, snid, tosnid int32, content string) { + bindSnid := this.getBindSnid(snid, tosnid) + logger.Logger.Trace("(this *ChatMgr) AddChat ", bindSnid) + if this.ChatList[platform] == nil { + this.ChatList[platform] = make(map[string]*Chat) + } + var cl *Chat + if _, exist := this.ChatList[platform][bindSnid]; !exist { + cacheCl := this.db2cache(model.NewChat(bindSnid)) + cl = &cacheCl + this.ChatList[platform][bindSnid] = cl + } + cl = this.ChatList[platform][bindSnid] + cc := &ChatContent{ + SrcSnId: snid, + Content: content, + Ts: time.Now().Unix(), + } + cl.ChatContent = append(cl.ChatContent, cc) + cl.LastChatTs = time.Now().Unix() + cl.Dirty = true +} + +// 删除聊天信息 +func (this *ChatMgr) DelChat(platform string, snid, tosnid int32) { + bindSnid := this.getBindSnid(snid, tosnid) + logger.Logger.Trace("(this *ChatMgr) DelChat ", bindSnid) + if this.ChatList[platform] == nil { + return + } + if _, exist := this.ChatList[platform][bindSnid]; exist { + delete(this.ChatList[platform], bindSnid) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.DelChat(platform, bindSnid) + return nil + }), nil).StartByFixExecutor("DelChat") + } +} + +// 聊天信息 +func (this *ChatMgr) SetChat(platform string, bindSnid string, chat *model.Chat) { + logger.Logger.Trace("(this *ChatMgr) SetChat ", bindSnid) + if this.ChatList[platform] == nil { + this.ChatList[platform] = make(map[string]*Chat) + } + if _, exist := this.ChatList[platform][bindSnid]; !exist { + cacheChat := this.db2cache(chat) + this.ChatList[platform][bindSnid] = &cacheChat + } +} + +// 获取聊天信息 +func (this *ChatMgr) GetChat(platform string, snid, tosnid int32) *Chat { + bindSnid := this.getBindSnid(snid, tosnid) + logger.Logger.Trace("(this *ChatMgr) GetChat ", bindSnid) + if this.ChatList[platform] == nil { + return nil + } + return this.ChatList[platform][bindSnid] +} + +func (this *ChatMgr) SaveChatData(platform string, snid, tosnid int32) { + bindSnid := this.getBindSnid(snid, tosnid) + logger.Logger.Trace("(this *ChatMgr) SaveChatData ", bindSnid) + if this.ChatList[platform] == nil { + return + } + if chat, exist := this.ChatList[platform][bindSnid]; exist { + if chat != nil && chat.Dirty { + chat.Dirty = false + dbchat := this.cache2db(chat) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertChat(platform, bindSnid, &dbchat) + }), nil, "SaveChatData").StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) + } + } +} + +// db -> cache +func (this *ChatMgr) db2cache(chat *model.Chat) (c Chat) { + if chat != nil { + c.Id = chat.Id + c.BindSnid = chat.BindSnid + if chat.ChatContent != nil { + for _, content := range chat.ChatContent { + cc := &ChatContent{ + SrcSnId: content.SrcSnId, + Content: content.Content, + Ts: content.Ts, + } + c.ChatContent = append(c.ChatContent, cc) + } + } + c.LastChatTs = chat.LastChatTs + } + return c +} + +// cache -> db +func (this *ChatMgr) cache2db(chat *Chat) (c model.Chat) { + if chat != nil { + c.Id = chat.Id + c.BindSnid = chat.BindSnid + if chat.ChatContent != nil { + for _, content := range chat.ChatContent { + cc := &model.ChatContent{ + SrcSnId: content.SrcSnId, + Content: content.Content, + Ts: content.Ts, + } + c.ChatContent = append(c.ChatContent, cc) + } + } + c.LastChatTs = chat.LastChatTs + } + return c +} + +func (this *ChatMgr) Update() { +} + +func (this *ChatMgr) Shutdown() { + for platform, chatList := range this.ChatList { + for bindSnid, chat := range chatList { + dbchat := this.cache2db(chat) + model.UpsertChat(platform, bindSnid, &dbchat) + } + } + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(ChatMgrSington, time.Second, 0) +} diff --git a/worldsrv/chessrank.go b/worldsrv/chessrank.go new file mode 100644 index 0000000..2b658c0 --- /dev/null +++ b/worldsrv/chessrank.go @@ -0,0 +1,71 @@ +package main + +import ( + "mongo.games.com/game/model" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/module" + "sort" + "time" +) + +var ChessRankMgrSington = &ChessRankMgr{ + ChessRankcfg: make(map[string]map[int32]*webapi_proto.ChessRankcfgData), + ChessRankArr: make(map[string]map[int32][]int32), +} + +type ChessRankMgr struct { + ChessRankcfg map[string]map[int32]*webapi_proto.ChessRankcfgData + ChessRankArr map[string]map[int32][]int32 +} + +func (this *ChessRankMgr) ModuleName() string { + return "ChessRankMgr" +} + +func (this *ChessRankMgr) Init() { + if !model.GameParamData.UseEtcd { + // 后台说现在没有不走ETCD情况~ + } else { + EtcdMgrSington.InitUpdateChessRankcfg() + } +} + +func (this *ChessRankMgr) UpdateChessRankConfig(cfg *webapi_proto.ChessRankcfgData) { + if this.ChessRankcfg[cfg.GetPlatform()] == nil { + this.ChessRankcfg[cfg.GetPlatform()] = make(map[int32]*webapi_proto.ChessRankcfgData) + } + this.ChessRankcfg[cfg.GetPlatform()][cfg.GetGameId()] = cfg + + sort.Slice(cfg.Datas, func(i, j int) bool { + return cfg.GetDatas()[i].Score < cfg.GetDatas()[j].Score + }) + + if this.ChessRankArr[cfg.GetPlatform()] == nil { + this.ChessRankArr[cfg.GetPlatform()] = make(map[int32][]int32) + } + var arr []int32 + for _, v := range cfg.GetDatas() { + arr = append(arr, v.GetScore()) + } + this.ChessRankArr[cfg.GetPlatform()][cfg.GetGameId()] = arr +} + +func (this *ChessRankMgr) GetChessRankConfig(platform string, gameId int32) *webapi_proto.ChessRankcfgData { + return this.ChessRankcfg[platform][gameId] +} + +func (this *ChessRankMgr) GetChessRankArr(platform string, gameId int32) []int32 { + return this.ChessRankArr[platform][gameId] +} + +func (this *ChessRankMgr) Update() { + +} + +func (this *ChessRankMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(ChessRankMgrSington, time.Second, 0) +} diff --git a/worldsrv/clock.go b/worldsrv/clock.go new file mode 100644 index 0000000..7bd48d7 --- /dev/null +++ b/worldsrv/clock.go @@ -0,0 +1,187 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/module" +) + +var ClockMgrSington = &ClockMgr{ + LastHour: -1, + LastDay: -1, + Notifying: false, +} + +const ( + CLOCK_EVENT_SECOND int = iota + CLOCK_EVENT_MINUTE + CLOCK_EVENT_HOUR + CLOCK_EVENT_DAY + CLOCK_EVENT_WEEK + CLOCK_EVENT_MONTH + CLOCK_EVENT_SHUTDOWN + CLOCK_EVENT_MAX +) + +type ClockSinker interface { + InterestClockEvent() int + OnSecTimer() + OnMiniTimer() + OnHourTimer() + OnDayTimer() + OnWeekTimer() + OnMonthTimer() + OnShutdown() +} + +type BaseClockSinker struct { +} + +func (s *BaseClockSinker) InterestClockEvent() int { return (1 << CLOCK_EVENT_MAX) - 1 } +func (s *BaseClockSinker) OnSecTimer() {} +func (s *BaseClockSinker) OnMiniTimer() {} +func (s *BaseClockSinker) OnHourTimer() {} +func (s *BaseClockSinker) OnDayTimer() {} +func (s *BaseClockSinker) OnWeekTimer() {} +func (s *BaseClockSinker) OnMonthTimer() {} +func (s *BaseClockSinker) OnShutdown() {} + +type ClockMgr struct { + sinkers [CLOCK_EVENT_MAX][]ClockSinker + LastTickTime time.Time + LastMonth time.Month + LastWeek int + LastDay int + LastHour int + LastMini int + LastSec int + Notifying bool + LastFiveMin int +} + +func (this *ClockMgr) RegisteSinker(sinker ClockSinker) { + interest := sinker.InterestClockEvent() + for i := 0; i < CLOCK_EVENT_MAX; i++ { + if (1< 0 && p.ChessGrade < int64(chessGradeLimit[0]) { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter find snid:%v %v grade limit:%v", p.SnId, p.ChessGrade, chessGradeLimit) + return gamehallproto.OpResultCode_OPRC_ChessGradeLimit + } + if chessGradeLimit[1] > 0 && p.ChessGrade >= int64(chessGradeLimit[1]) { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter find snid:%v %v grade limit:%v", p.SnId, p.ChessGrade, chessGradeLimit) + return gamehallproto.OpResultCode_OPRC_ChessGradeLimit + } + } + return gamehallproto.OpResultCode_OPRC_Sucess +} + +//todo DB_Createroom 入场检测 diff --git a/worldsrv/coinscenematch.go b/worldsrv/coinscenematch.go new file mode 100644 index 0000000..f38b4bb --- /dev/null +++ b/worldsrv/coinscenematch.go @@ -0,0 +1,24 @@ +package main + +import "mongo.games.com/game/common" + +type FuncCoinSceneMatch func(csp *CoinScenePool, p *Player, scenes map[int]*Scene, sameIpLimit bool, exclude []int32) *Scene + +var coinSceneMatchFuncPool = make(map[int]FuncCoinSceneMatch) + +// RegisterCoinSceneMatchFunc 注册游戏匹配房间的方式 +func RegisterCoinSceneMatchFunc(gameId int, f FuncCoinSceneMatch) { + coinSceneMatchFuncPool[gameId] = f +} + +func GetCoinSceneMatchFunc(gameId int) FuncCoinSceneMatch { + if f, exist := coinSceneMatchFuncPool[gameId]; exist { + return f + } + return nil +} + +func init() { + RegisterCoinSceneMatchFunc(common.GameId_ChesstitiansCambodian, ChessMatchFunc) + RegisterCoinSceneMatchFunc(common.GameId_ChesstitiansCambodianRobot, ChessMatchFunc) +} diff --git a/worldsrv/coinscenematch_chess.go b/worldsrv/coinscenematch_chess.go new file mode 100644 index 0000000..9cf2f1b --- /dev/null +++ b/worldsrv/coinscenematch_chess.go @@ -0,0 +1,286 @@ +package main + +import ( + "fmt" + "sort" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +type ChessMatchItem struct { + Index int + Id int32 + Score int64 + SceneId int +} + +type ChessMatchPool struct { + List []*ChessMatchItem + IdItem map[int32]*ChessMatchItem +} + +func (c *ChessMatchPool) Len() int { + return len(c.List) +} + +func (c *ChessMatchPool) Less(i, j int) bool { + return c.List[i].Score < c.List[j].Score +} + +func (c *ChessMatchPool) Swap(i, j int) { + c.List[i].Index = j + c.List[j].Index = i + c.List[i], c.List[j] = c.List[j], c.List[i] +} + +// Add . +// id snid +func (c *ChessMatchPool) Add(id int32, score int64, sceneId int) { + if _, ok := c.IdItem[id]; ok { + return + } + + item := &ChessMatchItem{ + Index: len(c.List), + Id: id, + Score: score, + SceneId: sceneId, + } + c.List = append(c.List, item) + c.IdItem[id] = item + + sort.Sort(c) +} + +func (c *ChessMatchPool) Del(id int32) { + item, ok := c.IdItem[id] + if !ok { + return + } + + c.List = append(c.List[:item.Index], c.List[item.Index:]...) + + for i := item.Index; i < len(c.List); i++ { + c.List[i].Index = i + } +} + +// ChessMatchFunc 国际象棋匹配规则 +func ChessMatchFunc(csp *CoinScenePool, p *Player, scenes map[int]*Scene, sameIpLimit bool, exclude []int32) *Scene { + if csp == nil || p == nil { + return nil + } + + var cmp *ChessMatchPool + var ok bool + if csp.extraData != nil { + cmp, ok = csp.extraData.(*ChessMatchPool) + } + if !ok || cmp == nil { + logger.Logger.Errorf("ChessMatchFunc not found ChessMatchPool") + return nil + } + + if p.IsRobot() || len(cmp.List) == 0 { + return nil + } + + var rule *server.DB_ChessMatchRules + for _, v := range srvdata.PBDB_ChessMatchRulesMgr.Datas.Arr { + switch { + case p.ChessGrade >= int64(v.ScoreMin) && p.ChessGrade <= int64(v.ScoreMax): + rule = v + } + } + + if rule == nil { + logger.Logger.Tracef("ChessMatchFunc ChessMatchRule not found %d", p.ChessGrade) + return nil + } + + if cmp == nil { + logger.Logger.Tracef("ChessMatchFunc ChessMatchPool is nil") + return nil + } + + if rule.MatchScoreMin > rule.MatchScoreMax { + logger.Logger.Tracef("ChessMatchFunc rule.MatchScoreMin > rule.MatchScoreMax") + return nil + } + + var scene *Scene + + i := sort.Search(len(cmp.List), func(i int) bool { + return cmp.List[i].Score >= int64(rule.MatchScoreMin) + }) + j := sort.Search(len(cmp.List), func(i int) bool { + return cmp.List[i].Score > int64(rule.MatchScoreMax) + }) + if i > j { + return nil + } + + logger.Logger.Tracef("ChessMatchFunc 基础匹配范围 %d,%d", rule.MatchScoreMin, rule.MatchScoreMax) + + f := func(sceneId int) bool { + s := SceneMgrSingleton.GetScene(sceneId) + // 满人,解散中,排除 + if s == nil || s.IsFull() || s.deleting || common.InSliceInt32(exclude, int32(s.sceneId)) { + return false + } + // 同IP限制 + if sameIpLimit && p.GMLevel == 0 && s.HasSameIp(p.Ip) { + return false + } + // 同局限制 + if s.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlace(p, s) { + return false + } + + return true + } + + // 基础匹配 + if i < len(cmp.List) { + for _, v := range cmp.List[i:j] { + if f(v.SceneId) { + logger.Logger.Tracef("ChessMatchFunc 基础匹配成功 %d playerScore:%d, other:%d", v.SceneId, p.ChessGrade, v.Score) + scene = SceneMgrSingleton.GetScene(v.SceneId) + break + } + } + } + + // 扩展匹配 + if scene == nil { + for k, v := range rule.MatchScoreLowStep { + m := sort.Search(len(cmp.List), func(i int) bool { + return cmp.List[i].Score >= int64(rule.MatchScoreMin-v) + }) + n := sort.Search(len(cmp.List), func(i int) bool { + return cmp.List[i].Score > int64(rule.MatchScoreMax+rule.MatchScoreHightStep[k]) + }) + logger.Logger.Tracef("ChessMatchFunc 扩展匹配范围 %d,%d", rule.MatchScoreMin-v, rule.MatchScoreMax+rule.MatchScoreHightStep[k]) + logger.Logger.Tracef("ChessMatchFunc 范围索引 m:%d, i:%d, j:%d, n:%d", m, i, j, n) + // [m,n]的范围应该是包含[i,j] + if m > i || j > n { + // 配置错误 + break + } + + if m >= 0 && m < i { + for _, v := range cmp.List[m:i] { + if f(v.SceneId) { + logger.Logger.Tracef("ChessMatchFunc 扩展匹配成功 %d playerScore:%d, other:%d", v.SceneId, p.ChessGrade, v.Score) + scene = SceneMgrSingleton.GetScene(v.SceneId) + break + } + } + } + if scene == nil { + if n >= j { + for _, v := range cmp.List[j:n] { + if f(v.SceneId) { + logger.Logger.Tracef("ChessMatchFunc 扩展匹配成功 %d playerScore:%d, other:%d", v.SceneId, p.ChessGrade, v.Score) + scene = SceneMgrSingleton.GetScene(v.SceneId) + break + } + } + } + } + if scene == nil { + i = m + j = n + } else { + break + } + } + } + + return scene +} + +func ChessMatchFuncTest(chessMatchPool *ChessMatchPool, chessScore int32) *Scene { + var rule *server.DB_ChessMatchRules + for _, v := range srvdata.PBDB_ChessMatchRulesMgr.Datas.Arr { + switch { + case chessScore >= v.ScoreMin && chessScore <= v.ScoreMax: + rule = v + } + } + + if rule == nil { + fmt.Printf("ChessMatchFunc ChessMatchRule not found %d\n", chessScore) + return nil + } + + if rule.MatchScoreMin > rule.MatchScoreMax { + fmt.Printf("ChessMatchFunc rule.MatchScoreMin > rule.MatchScoreMax\n") + return nil + } + + var scene *Scene + + i := sort.Search(len(chessMatchPool.List), func(i int) bool { + return chessMatchPool.List[i].Score >= int64(rule.MatchScoreMin) + }) + j := sort.Search(len(chessMatchPool.List), func(i int) bool { + return chessMatchPool.List[i].Score > int64(rule.MatchScoreMax) + }) + if i > j { + return nil + } + + fmt.Printf("ChessMatchFunc 基础匹配范围 %d,%d\n", rule.MatchScoreMin, rule.MatchScoreMax) + fmt.Printf("ChessMatchFunc 基础匹配范围 i:%d,j:%d\n", i, j) + + // 基础匹配 + if i < len(chessMatchPool.List) { + for _, v := range chessMatchPool.List[i:j] { + fmt.Printf("ChessMatchFunc 基础匹配成功 %d playerScore:%d, other:%d\n", v.SceneId, chessScore, v.Score) + } + } + + // 扩展匹配 + if scene == nil { + for k, v := range rule.MatchScoreLowStep { + m := sort.Search(len(chessMatchPool.List), func(i int) bool { + return chessMatchPool.List[i].Score >= int64(rule.MatchScoreMin-v) + }) + n := sort.Search(len(chessMatchPool.List), func(i int) bool { + return chessMatchPool.List[i].Score > int64(rule.MatchScoreMax+rule.MatchScoreHightStep[k]) + }) + fmt.Printf("ChessMatchFunc 扩展匹配范围 %d,%d\n", rule.MatchScoreMin-v, rule.MatchScoreMax+rule.MatchScoreHightStep[k]) + fmt.Printf("ChessMatchFunc 范围索引 m:%d, i:%d, j:%d, n:%d\n", m, i, j, n) + // [m,n]的范围应该是包含[i,j] + if m > i || j > n { + // 配置错误 + break + } + + if m >= 0 && m < i { + for _, v := range chessMatchPool.List[m:i] { + fmt.Printf("ChessMatchFunc 扩展匹配成功 %d playerScore:%d, other:%d\n", v.SceneId, chessScore, v.Score) + } + } + if scene == nil { + if n >= j { + for _, v := range chessMatchPool.List[j:n] { + fmt.Printf("ChessMatchFunc 扩展匹配成功 %d playerScore:%d, other:%d\n", v.SceneId, chessScore, v.Score) + } + } + } + if scene == nil { + i = m + j = n + } else { + break + } + } + } + return nil +} diff --git a/worldsrv/coinscenematch_chess_test.go b/worldsrv/coinscenematch_chess_test.go new file mode 100644 index 0000000..911c69c --- /dev/null +++ b/worldsrv/coinscenematch_chess_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "testing" +) + +func TestChessMatchFunc(t *testing.T) { + + m := &ChessMatchPool{IdItem: make(map[int32]*ChessMatchItem)} + + for i := 0; i < 200; i++ { + m.Add(int32(i), int64(i), i) + } + + ChessMatchFuncTest(m, 50) +} diff --git a/worldsrv/coinscenemgr.go b/worldsrv/coinscenemgr.go new file mode 100644 index 0000000..5f098ad --- /dev/null +++ b/worldsrv/coinscenemgr.go @@ -0,0 +1,703 @@ +package main + +import ( + "math/rand" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/server" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" +) + +var CoinSceneMgrSingleton = &CoinSceneMgr{ + playerChanging: make(map[int32]int32), + //按平台管理 + scenesOfPlatform: make(map[string]map[int32]*CoinScenePool), + platformOfScene: make(map[int32]string), + //按组管理 + scenesOfGroup: make(map[int32]map[int32]*CoinScenePool), + groupOfScene: make(map[int32]int32), +} + +type CreateRoomCache struct { + gameFreeId int32 + platformName string +} + +type CoinSceneMgr struct { + playerChanging map[int32]int32 // snid:gamefreeid 换桌中的玩家 + + //按平台管理 + scenesOfPlatform map[string]map[int32]*CoinScenePool // platform:gamefreeid + platformOfScene map[int32]string // sceneid:platform; 创建房间后记录房间所在平台 + + //按组管理 + scenesOfGroup map[int32]map[int32]*CoinScenePool // groupid:gamefreeid + groupOfScene map[int32]int32 // sceneid:groupid; + + //延迟创建房间列表 + delayCache []CreateRoomCache // 待预创建房间 +} + +// GetCoinScenePool 获取一个场景池 +// plt 平台id +// id 场次id +func (csm *CoinSceneMgr) GetCoinScenePool(plt string, id int32) *CoinScenePool { + gf := PlatformMgrSingleton.GetGameFree(plt, id) + if gf == nil { + return nil + } + groupId := gf.GetGroupId() + if groupId != 0 { + if _, ok := csm.scenesOfGroup[groupId]; ok { + if csp, ok := csm.scenesOfGroup[groupId][id]; ok { + return csp + } + } + } else { + if ss, ok := csm.scenesOfPlatform[plt]; ok { + if csp, ok := ss[id]; ok && csp != nil { + return csp + } + } + } + + var dbGameFree *server_proto.DB_GameFree + if groupId != 0 { + conf := PlatformGameGroupMgrSington.GetGameGroup(groupId) + if conf != nil { + dbGameFree = conf.GetDbGameFree() + } + } + if dbGameFree == nil { + dbGameFree = gf.GetDbGameFree() + } + if dbGameFree == nil { + return nil + } + + // 创建了一个新的 + // 应该是走不到这里,因为模块启动时所有场次都创建了房间池 + return NewCoinScenePool(plt, groupId, dbGameFree) +} + +func (csm *CoinSceneMgr) findCoinScenePool(platform string, id int32) (pools map[int32]*CoinScenePool, groupID int32, + dbGameFree *server.DB_GameFree) { + gf := PlatformMgrSingleton.GetGameFree(platform, id) + if gf == nil { + return nil, 0, nil + } + groupId := gf.GetGroupId() + + var ss map[int32]*CoinScenePool + var ok bool + if groupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(groupId) + if pgg != nil { + ss, ok = csm.scenesOfGroup[groupId] + if !ok { + ss = make(map[int32]*CoinScenePool) + csm.scenesOfGroup[groupId] = ss + } + return ss, groupId, pgg.GetDbGameFree() + } + } + + if ss == nil { + ss, ok = csm.scenesOfPlatform[platform] + if !ok { + ss = make(map[int32]*CoinScenePool) + csm.scenesOfPlatform[platform] = ss + } + return ss, 0, gf.GetDbGameFree() + } + + return nil, 0, nil +} + +// PlayerEnter 玩家进入房间池 +func (csm *CoinSceneMgr) PlayerEnter(p *Player, id int32, roomId int32, exclude []int32, ischangeroom bool) hall_proto.OpResultCode { + logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerEnter snid:%v id:%v roomid:%v exclude:%v", p.SnId, id, roomId, exclude) + if p.isDelete { //删档用户不让进游戏 + return hall_proto.OpResultCode_OPRC_RoomHadClosed + } + + //多平台支持 + platform := p.GetPlatform() + if platform == nil { + return hall_proto.OpResultCode_OPRC_RoomHadClosed + } + + // 玩家已经在房间里了 + if p.scene != nil { + logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in gameId:%v gamefreeid:%v", p.SnId, p.scene.gameId, id) + return hall_proto.OpResultCode_OPRC_Error + } + + pools, groupID, free := csm.findCoinScenePool(platform.IdStr, id) + if pools == nil { + return hall_proto.OpResultCode_OPRC_RoomHadClosed + } + + csp, ok := pools[id] + if !ok || csp == nil { + csp = NewCoinScenePool(platform.IdStr, groupID, free) + if csp == nil { + logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in id:%v exclude:%v NewCoinScenePool failed", p.SnId, id, exclude) + return hall_proto.OpResultCode_OPRC_Error + } + pools[id] = csp + } + + ret := csp.PlayerEnter(p, roomId, exclude, ischangeroom) + + logger.Logger.Warnf("(csm *CoinSceneMgr) PlayerEnter snid:%v find in id:%v exclude:%v return false", p.SnId, id, exclude) + return ret +} + +// AudienceEnter 观众进入房间 +func (csm *CoinSceneMgr) AudienceEnter(p *Player, id int32, roomId int32, exclude []int32, ischangeroom bool) hall_proto.OpResultCode { + //多平台支持 + platform := p.GetPlatform() + if platform == nil { + return hall_proto.OpResultCode_OPRC_RoomHadClosed + } + + pools, _, _ := csm.findCoinScenePool(platform.IdStr, id) + if pools == nil { + return hall_proto.OpResultCode_OPRC_RoomHadClosed + } + + if len(pools) == 0 { + return hall_proto.OpResultCode_OPRC_NoFindDownTiceRoom + } + + if csp, ok := pools[id]; ok && csp != nil { + ret := csp.AudienceEnter(p, roomId, exclude, ischangeroom) + logger.Logger.Warnf("(csm *CoinSceneMgr) AudienceEnter snid:%v find in id:%v exclude:%v return false", p.SnId, id, exclude) + return ret + } + logger.Logger.Warnf("(csm *CoinSceneMgr) AudienceEnter snid:%v find in id:%v exclude:%v csp.AudienceEnter return false", p.SnId, id, exclude) + return hall_proto.OpResultCode_OPRC_Error +} + +func (csm *CoinSceneMgr) playerLeave(p *Player, reason int) bool { + if p == nil || p.scene == nil { + return true + } + + s := p.scene + if s.groupId != 0 { + if ss, ok := csm.scenesOfGroup[s.groupId]; ok && ss != nil { + if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { + if !csp.PlayerLeave(p, reason) { + csp.AudienceLeave(p, reason) + } + } + return true + } + } + + // 玩家身上平台 + if platform := p.GetPlatform(); platform != nil { + if ss, ok := csm.scenesOfPlatform[platform.IdStr]; ok && ss != nil { + if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { + if !csp.PlayerLeave(p, reason) { + csp.AudienceLeave(p, reason) + } + } + return true + } + } + + // 房间所在平台 + if s.limitPlatform != nil { + if ss, ok := csm.scenesOfPlatform[s.limitPlatform.IdStr]; ok && ss != nil { + if csp, ok := ss[s.dbGameFree.GetId()]; ok && csp != nil { + if !csp.PlayerLeave(p, reason) { + csp.AudienceLeave(p, reason) + } + } + return true + } + } + + return true +} + +// PlayerLeave 玩家离开 +func (csm *CoinSceneMgr) PlayerLeave(p *Player, reason int) bool { + logger.Logger.Tracef("玩家离开: snid %v, reason %v isAudience %v", p.SnId, reason) + return csm.playerLeave(p, reason) +} + +// OnDestroyScene 解散房间 +// sceneId 房间id +func (csm *CoinSceneMgr) OnDestroyScene(sceneId int) { + if platformName, ok := csm.platformOfScene[int32(sceneId)]; ok { + if ss, ok := csm.scenesOfPlatform[platformName]; ok { + for _, csp := range ss { + csp.OnDestroyScene(sceneId) + } + } + delete(csm.platformOfScene, int32(sceneId)) + } + + if groupId, ok := csm.groupOfScene[int32(sceneId)]; ok { + if ss, ok := csm.scenesOfGroup[groupId]; ok { + for _, csp := range ss { + csp.OnDestroyScene(sceneId) + } + } + delete(csm.groupOfScene, int32(sceneId)) + } +} + +// GetPlatformBySceneId 获取房间所在平台 +func (csm *CoinSceneMgr) GetPlatformBySceneId(sceneId int) string { + if platformName, ok := csm.platformOfScene[int32(sceneId)]; ok { + return platformName + } + s := SceneMgrSingleton.GetScene(sceneId) + if s != nil && s.limitPlatform != nil { + return s.limitPlatform.IdStr + } + return DefaultPlatform +} + +// GetPlayerNums 获取场次人数 +func (csm *CoinSceneMgr) GetPlayerNums(p *Player, gameId, gameMode int32) []int32 { + //多平台支持 + platform := p.GetPlatform() + var nums [10]int32 + wantNum := [10]int32{80, 50, 30, 20, 10, 10, 10, 10, 10, 10} + for i := 0; i < 10; i++ { + if wantNum[i]/2 > 0 { + nums[i] = rand.Int31n(wantNum[i]/2) + wantNum[i] + } + } + if platform == nil { + return nums[:] + } + ids, _ := srvdata.DataMgr.GetGameFreeIds(gameId, gameMode) + for _, id := range ids { + gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, id) + if gps != nil { + if gps.GroupId != 0 { + if ss, exist := csm.scenesOfGroup[gps.GroupId]; exist { + if csp, exist := ss[id]; exist { + sceneType := csp.GetSceneType() - 1 + if sceneType >= 0 && sceneType < len(nums) { + nums[sceneType] += csp.GetPlayerNum() + csp.GetFakePlayerNum() + } + } + } + } else { + if ss, ok := csm.scenesOfPlatform[platform.IdStr]; ok { + if csp, exist := ss[id]; exist { + sceneType := csp.GetSceneType() - 1 + if sceneType >= 0 && sceneType < len(nums) { + nums[sceneType] += csp.GetPlayerNum() + csp.GetFakePlayerNum() + } + } + } + } + } + } + + if len(ids) <= 10 { + return nums[:len(ids)] + } + return nums[:] +} + +// InCoinScene 是否在场次中 +func (csm *CoinSceneMgr) InCoinScene(p *Player) bool { + if p == nil { + logger.Logger.Tracef("(csm *CoinSceneMgr) InCoinScene p == nil snid:%v ", p.SnId) + return false + } + + if p.scene == nil { + return false + } + + if p.scene.csp == nil { + return false + } + return true +} + +// PlayerTryLeave 通知游戏服务玩家要离开房间 +func (csm *CoinSceneMgr) PlayerTryLeave(p *Player, isAudience bool) hall_proto.OpResultCode { + if !csm.InCoinScene(p) { + logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerTryLeave !csm.InCoinScene(p) snid:%v ", p.SnId) + return hall_proto.OpResultCode_OPRC_Sucess + } + + // 通知gamesrv + op := common.CoinSceneOp_Leave + if isAudience { + op = common.CoinSceneOp_AudienceLeave + } + pack := &hall_proto.CSCoinSceneOp{ + Id: proto.Int32(p.scene.csp.id), + OpType: proto.Int32(op), + } + proto.SetDefaults(pack) + common.TransmitToServer(p.sid, int(hall_proto.CoinSceneGamePacketID_PACKET_CS_COINSCENE_OP), pack, p.scene.gameSess.Session) + logger.Logger.Tracef("(csm *CoinSceneMgr) PlayerTryLeave snid:%v id:%v", p.SnId, pack.GetId()) + return hall_proto.OpResultCode_OPRC_OpYield +} + +func (csm *CoinSceneMgr) PlayerInChanging(p *Player) bool { + _, exist := csm.playerChanging[p.SnId] + return exist +} + +func (csm *CoinSceneMgr) ClearPlayerChanging(p *Player) { + delete(csm.playerChanging, p.SnId) +} + +func (csm *CoinSceneMgr) PlayerTryChange(p *Player, id int32, exclude []int32, isAudience bool) hall_proto.OpResultCode { + if csm.InCoinScene(p) { + return csm.StartChangeCoinSceneTransact(p, id, exclude, isAudience) + } + + if isAudience { + return csm.AudienceEnter(p, id, 0, exclude, true) + } + return csm.PlayerEnter(p, id, 0, exclude, true) +} + +func (csm *CoinSceneMgr) StartChangeCoinSceneTransact(p *Player, id int32, exclude []int32, isAudience bool) hall_proto.OpResultCode { + if p == nil || p.scene == nil { + logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact p == nil || p.scene == nil snid:%v id:%v", p.SnId, id) + return hall_proto.OpResultCode_OPRC_Error + } + + tNow := time.Now() + if !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second { + logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact !p.lastChangeScene.IsZero() && tNow.Sub(p.lastChangeScene) < time.Second snid:%v id:%v", p.SnId, id) + return hall_proto.OpResultCode_OPRC_ChangeRoomTooOften + } + + tnp := &transact.TransNodeParam{ + Tt: common.TransType_CoinSceneChange, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + ctx := &CoinSceneChangeCtx{ + id: id, + isClub: false, + snid: p.SnId, + sceneId: int32(p.scene.sceneId), + exclude: exclude, + isAudience: isAudience, + } + tNode := transact.DTCModule.StartTrans(tnp, ctx, CoinSceneChangeTimeOut) + if tNode != nil { + tNode.Go(core.CoreObject()) + csm.playerChanging[p.SnId] = id + p.lastChangeScene = tNow + return hall_proto.OpResultCode_OPRC_Sucess + } + logger.Logger.Warnf("(csm *CoinSceneMgr) StartChangeCoinSceneTransact tNode == nil snid:%v id:%v", p.SnId, id) + return hall_proto.OpResultCode_OPRC_Error +} + +// ListRooms 给客户端发送场次房间列表 +// id 场次id +func (csm *CoinSceneMgr) ListRooms(p *Player, id int32) bool { + //多平台支持 + platform := p.GetPlatform() + if platform == nil { + return false + } + + pools, groupID, free := csm.findCoinScenePool(platform.IdStr, id) + if pools == nil { + return false + } + + if csp, ok := pools[id]; ok && csp != nil { + return csp.ListRoom(p) + } + csp := NewCoinScenePool(platform.IdStr, groupID, free) + if csp == nil { + return false + } + pools[id] = csp + return csp.ListRoom(p) +} + +// CreateRoomByCache 创建预创建房间 +// 每2秒创建一次 +func (csm *CoinSceneMgr) CreateRoomByCache() { + cnt := len(csm.delayCache) + if cnt > 0 { + data := csm.delayCache[cnt-1] + csm.delayCache = csm.delayCache[:cnt-1] + pdd := PlatformMgrSingleton.GetGameFree(data.platformName, data.gameFreeId) + //对战场和捕鱼场 预创建 + if pdd != nil && pdd.DbGameFree != nil { + var ss map[int32]*CoinScenePool + var ok bool + //分组模式 + if pdd.GroupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(pdd.GroupId) + if pgg != nil { + ss, ok = csm.scenesOfGroup[pdd.GroupId] + if !ok { + ss = make(map[int32]*CoinScenePool) + csm.scenesOfGroup[pdd.GroupId] = ss + } + } + } + //独立模式 + if ss == nil { + ss, ok = csm.scenesOfPlatform[data.platformName] + if !ok { + ss = make(map[int32]*CoinScenePool) + csm.scenesOfPlatform[data.platformName] = ss + } + } + if ss == nil { + return + } + if csp, ok := ss[pdd.DbGameFree.Id]; ok && csp != nil { + csp.PreCreateRoom() + return + } + csp := NewCoinScenePool(data.platformName, 0, pdd.DbGameFree) + if csp != nil { + ss[pdd.DbGameFree.Id] = csp + csp.PreCreateRoom() + return + } + } + } +} + +func (csm *CoinSceneMgr) ModuleName() string { + return "CoinSceneMgr" +} + +func (csm *CoinSceneMgr) Init() { + // 场次池预创建 + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.Isolated || platform.IdStr == "" { + arr := srvdata.PBDB_GameFreeMgr.Datas.GetArr() + for _, dbGame := range arr { + gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, dbGame.GetId()) + if gps != nil { + // dbGameFree + dbGameFree := gps.DbGameFree + if gps.GroupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + dbGameFree = pgg.DbGameFree + } + } + // CoinScenePool + csp := NewCoinScenePool(platform.IdStr, gps.GroupId, dbGameFree) + if csp != nil { + if gps.GroupId != 0 { + if ss, exist := csm.scenesOfGroup[gps.GroupId]; exist { + ss[dbGame.GetId()] = csp + } else { + ss = make(map[int32]*CoinScenePool) + ss[dbGame.GetId()] = csp + csm.scenesOfGroup[gps.GroupId] = ss + } + } else { + if ss, exist := csm.scenesOfPlatform[platform.IdStr]; exist { + ss[dbGame.GetId()] = csp + } else { + ss = make(map[int32]*CoinScenePool) + ss[dbGame.GetId()] = csp + csm.scenesOfPlatform[platform.IdStr] = ss + } + } + } + } + } + } + } +} + +func (csm *CoinSceneMgr) Update() { + // 预创建房间 + csm.CreateRoomByCache() +} + +func (csm *CoinSceneMgr) Shutdown() { + module.UnregisteModule(csm) +} + +//=====================PlatformObserver====================== + +func (this *CoinSceneMgr) OnPlatformCreate(p *Platform) { + if p.IdStr == DefaultPlatform { + return + } + //获取配置 + gps := PlatformMgrSingleton.GetGameFrees(p.IdStr) + for _, v := range gps { + if v.Status && v.DbGameFree.GetCreateRoomNum() > 0 { + this.delayCache = append(this.delayCache, CreateRoomCache{gameFreeId: v.DbGameFree.Id, platformName: p.IdStr}) + } + } +} + +func (this *CoinSceneMgr) OnPlatformDestroy(p *Platform) { + if p == nil { + return + } + if csps, ok := this.scenesOfPlatform[p.IdStr]; ok { + for _, csp := range csps { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range csp.scenes { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + // 房间中记录的有游服连接,广播的方式也可以 + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } +} + +func (this *CoinSceneMgr) OnPlatformChangeIsolated(p *Platform, isolated bool) { + if !isolated { + this.OnPlatformDestroy(p) + } +} + +func (this *CoinSceneMgr) OnPlatformChangeDisabled(p *Platform, disabled bool) { + if disabled { + this.OnPlatformDestroy(p) + } +} + +func (this *CoinSceneMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapi.GameFree) { + if p == nil || newCfg == nil { + return + } + + var ss map[int32]*CoinScenePool + var ok bool + if oldCfg.GroupId != newCfg.GroupId || oldCfg.GroupId != 0 { + ss, ok = this.scenesOfGroup[oldCfg.GroupId] + } else { + ss, ok = this.scenesOfPlatform[p.IdStr] + } + if !ok || ss == nil { + return + } + + if cps, ok := ss[newCfg.DbGameFree.Id]; ok { + cps.dbGameFree = newCfg.DbGameFree + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range cps.scenes { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), + pack, common.GetSelfAreaId(), srvlib.GameServerType) + + //预创建房间配置更新 + if newCfg.DbGameFree.GetCreateRoomNum() != 0 && p.IdStr != DefaultPlatform && newCfg.Status { + logger.Logger.Tracef(">>>预创建房间 platform:%v %v_%v gamefreeid:%v CreateRoomNum:%v", p.Name, + newCfg.DbGameFree.GetName(), newCfg.DbGameFree.GetTitle(), newCfg.DbGameFree.GetId(), newCfg.DbGameFree.GetCreateRoomNum()) + this.delayCache = append(this.delayCache, CreateRoomCache{gameFreeId: newCfg.DbGameFree.GetId(), platformName: p.IdStr}) + } + } +} + +func (this *CoinSceneMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { + if p == nil { + return + } + if csps, ok := this.scenesOfPlatform[p.IdStr]; ok { + for _, csp := range csps { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range csp.scenes { + if scene.dbGameFree.Id == gameFreeId { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } +} + +//=========================PlatformGameGroupObserver============================== + +func (this *CoinSceneMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi.GameConfigGroup) { + if newCfg == nil { + return + } + if scenes, exist := this.scenesOfGroup[newCfg.Id]; exist { + if cps, ok := scenes[newCfg.DbGameFree.Id]; ok { + needDestroy := false + if cps.dbGameFree.GetBot() != newCfg.DbGameFree.GetBot() || + cps.dbGameFree.GetBaseScore() != newCfg.DbGameFree.GetBaseScore() || + cps.dbGameFree.GetLimitCoin() != newCfg.DbGameFree.GetLimitCoin() || + cps.dbGameFree.GetMaxCoinLimit() != newCfg.DbGameFree.GetMaxCoinLimit() || + cps.dbGameFree.GetTaxRate() != newCfg.DbGameFree.GetTaxRate() || + !common.SliceInt64Equal(cps.dbGameFree.GetOtherIntParams(), newCfg.DbGameFree.GetOtherIntParams()) || + !common.SliceInt64Equal(cps.dbGameFree.GetRobotTakeCoin(), newCfg.DbGameFree.GetRobotTakeCoin()) || + !common.SliceInt64Equal(cps.dbGameFree.GetRobotLimitCoin(), newCfg.DbGameFree.GetRobotLimitCoin()) { + needDestroy = true + } + //TODO 预创建房间配置更新,unsupport group model + cps.dbGameFree = newCfg.DbGameFree + if needDestroy { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range cps.scenes { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } + } +} + +//=========================GameSessionListener====================================== + +func (csm *CoinSceneMgr) OnGameSessionRegiste(gs *GameSession) { + wildGs := len(gs.gameIds) == 0 || common.InSliceInt32(gs.gameIds, 0) // 是否所有游戏都支持 + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + //获取配置 + gps := PlatformMgrSingleton.GetGameFrees(platform.IdStr) + for _, v := range gps { + if v.Status && v.DbGameFree.GetCreateRoomNum() > 0 && (wildGs || common.InSliceInt32(gs.gameIds, v.DbGameFree.GetGameId())) { + csm.delayCache = append(csm.delayCache, CreateRoomCache{gameFreeId: v.DbGameFree.Id, platformName: platform.IdStr}) + } + } + } +} + +func (this *CoinSceneMgr) OnGameSessionUnregiste(gs *GameSession) { + //todo 游戏服务断开,是否解散房间? +} + +func init() { + module.RegisteModule(CoinSceneMgrSingleton, time.Second*2, 0) + + PlatformMgrSingleton.RegisterObserver(CoinSceneMgrSingleton) + PlatformGameGroupMgrSington.RegisteObserver(CoinSceneMgrSingleton) + RegisteGameSessionListener(CoinSceneMgrSingleton) +} diff --git a/worldsrv/coinscenepool.go b/worldsrv/coinscenepool.go new file mode 100644 index 0000000..1882309 --- /dev/null +++ b/worldsrv/coinscenepool.go @@ -0,0 +1,467 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + gamehall_proto "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +// CoinScenePool 房间池 +type CoinScenePool struct { + platform string // 平台id + groupId int32 // 组id + id int32 // 场次id + dbGameFree *server_proto.DB_GameFree // 场次配置 + dbGameRule *server_proto.DB_GameRule // 场次配置 + scenes map[int]*Scene // 所有房间,房间id + players map[int32]struct{} // 玩家id + + // 扩展数据 + extraData interface{} + // 匹配规则 + policy ICoinScenePool +} + +func NewCoinScenePool(platform string, groupId int32, dbGameFree *server_proto.DB_GameFree) *CoinScenePool { + if dbGameFree == nil { + return nil + } + + dbGameRule := srvdata.PBDB_GameRuleMgr.GetData(dbGameFree.GetGameRule()) + if dbGameRule == nil { + logger.Logger.Errorf("Coin scene pool init failed,%v game rule data not find.", dbGameFree.GetGameRule()) + return nil + } + + csp := &CoinScenePool{ + platform: platform, + groupId: groupId, + id: dbGameFree.GetId(), + dbGameFree: dbGameFree, + dbGameRule: dbGameRule, + scenes: make(map[int]*Scene), + players: make(map[int32]struct{}), + policy: new(BaseCoinScenePool), // 默认方式 + } + + policy := GetCoinScenePool(dbGameFree.GetGameId()) + if policy != nil { + csp.policy = policy + csp.extraData = policy.New() + } + return csp +} + +func (csp *CoinScenePool) GetPlayerNum() int32 { + return int32(len(csp.players)) +} + +func (csp *CoinScenePool) GetFakePlayerNum() int32 { + //if csp.dbGameFree != nil { + // correctNum := csp.dbGameFree.GetCorrectNum() + // correctRate := csp.dbGameFree.GetCorrectRate() + // count := csp.GetPlayerNum() + // return correctNum + count*correctRate/100 + csp.numDeviation + //} + return 0 +} + +// GetSceneType 获取场次id +func (csp *CoinScenePool) GetSceneType() int { + if csp.dbGameFree != nil { + return int(csp.dbGameFree.GetSceneType()) + } + return 0 +} + +// CanInviteRob 能否邀请机器人 +func (csp *CoinScenePool) CanInviteRob() bool { + if csp.dbGameFree != nil { + return csp.dbGameFree.GetBot() != 0 + } + return false +} + +// CanEnter 检查入场条件 +func (csp *CoinScenePool) CanEnter(p *Player) gamehall_proto.OpResultCode { + if csp.dbGameFree == nil || p == nil { + return gamehall_proto.OpResultCode_OPRC_Error + } + + //检测房间状态是否开启 + gps := PlatformMgrSingleton.GetGameFree(p.Platform, csp.id) + if gps == nil || !gps.Status { + return gamehall_proto.OpResultCode_OPRC_RoomHadClosed + } + + dbGameFree := csp.dbGameFree + if dbGameFree == nil { + return gamehall_proto.OpResultCode_OPRC_RoomHadClosed + } + + //检查游戏次数限制 + if !p.IsRob { + todayData, _ := p.GetDaliyGameData(int(dbGameFree.GetId())) + if dbGameFree.GetPlayNumLimit() != 0 && + todayData != nil && + todayData.GameTimes >= int64(dbGameFree.GetPlayNumLimit()) { + return gamehall_proto.OpResultCode_OPRC_RoomGameTimes + } + } + + return csp.policy.CanEnter(csp, p) +} + +// CanAudienceEnter 检查观众入场条件 +func (csp *CoinScenePool) CanAudienceEnter(p *Player) gamehall_proto.OpResultCode { + if csp.dbGameFree == nil || p == nil { + return gamehall_proto.OpResultCode_OPRC_Error + } + + //检测房间状态是否开启 + gps := PlatformMgrSingleton.GetGameFree(p.Platform, csp.id) + if gps == nil { + return gamehall_proto.OpResultCode_OPRC_RoomHadClosed + } + + dbGameFree := csp.dbGameFree + if dbGameFree == nil { + return gamehall_proto.OpResultCode_OPRC_RoomHadClosed + } + + return csp.policy.CanAudienceEnter(csp, p) +} + +// PlayerEnter 玩家进入房间池 +func (csp *CoinScenePool) PlayerEnter(p *Player, roomId int32, exclude []int32, ischangeroom bool) gamehall_proto.OpResultCode { + if ret := csp.CanEnter(p); ret != gamehall_proto.OpResultCode_OPRC_Sucess { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter find snid:%v csp.CanEnter coin:%v ret:%v id:%v", p.SnId, + p.Coin, ret, csp.dbGameFree.GetId()) + return ret + } + + if p.scene != nil { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter[p.scene != nil] find snid:%v in scene:%v gameId:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + return gamehall_proto.OpResultCode_OPRC_Error + } + + var scene *Scene + // 进入房间 + if roomId != 0 && (p.IsRob || p.GMLevel > 0 || csp.dbGameFree.GetCreateRoomNum() != 0) { + if s, ok := csp.scenes[int(roomId)]; ok { + if s != nil && !s.deleting { //指定房间id进入,那么忽略掉排除id + if s.IsFull() { + return gamehall_proto.OpResultCode_OPRC_RoomIsFull + } + if sp, ok := s.sp.(*ScenePolicyData); ok { + if !s.starting || sp.EnterAfterStart { + scene = s + } else { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter[!s.starting || sp.EnterAfterStart] snid:%v sceneid:%v starting:%v EnterAfterStart:%v", p.SnId, s.sceneId, s.starting, sp.EnterAfterStart) + return gamehall_proto.OpResultCode_OPRC_Error + } + } + } + } else { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter(robot:%v,roomid:%v, exclude:%v, ischangeroom:%v) no found scene", p.SnId, roomId, exclude, ischangeroom) + return gamehall_proto.OpResultCode_OPRC_Error + } + } + + if scene == nil { + var ret gamehall_proto.OpResultCode + ret, scene = csp.policy.PlayerEnter(csp, p, exclude, ischangeroom) + if ret != gamehall_proto.OpResultCode_OPRC_Sucess { + return ret + } + } + + if scene == nil { + scene = csp.policy.NewScene(csp, p) + if scene != nil { + csp.scenes[scene.sceneId] = scene + scene.csp = csp + } else { + logger.Logger.Errorf("Create %v scene failed.", csp.id) + } + } + + if scene != nil { + if p.EnterScene(scene, ischangeroom, -1) { + csp.OnPlayerEnter(p, scene) + return gamehall_proto.OpResultCode_OPRC_Sucess + } + } + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter snid:%v not found scene", p.SnId) + return gamehall_proto.OpResultCode_OPRC_SceneServerMaintain +} + +// AudienceEnter 观众入场 +func (csp *CoinScenePool) AudienceEnter(p *Player, roomId int32, exclude []int32, ischangeroom bool) gamehall_proto.OpResultCode { + if ret := csp.CanAudienceEnter(p); ret != gamehall_proto.OpResultCode_OPRC_Sucess { + logger.Logger.Warnf("(csp *CoinScenePool) AudienceEnter find snid:%v csp.CanEnter coin:%v ret:%v id:%v", p.SnId, p.Coin, ret, csp.dbGameFree.GetId()) + return ret + } + + if p.scene != nil { + logger.Logger.Warnf("(csp *CoinScenePool) AudienceEnter[p.scene != nil] find snid:%v in scene:%v gameId:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + return gamehall_proto.OpResultCode_OPRC_Error + } + + var scene *Scene + if roomId != 0 { + if s, ok := csp.scenes[int(roomId)]; ok { + if s != nil && !s.deleting /*&& s.sceneId != int(exclude)*/ { + scene = s + } else { + logger.Logger.Warnf("(csp *CoinScenePool) AudienceEnter[!s.starting || sp.EnterAfterStart] snid:%v sceneid:%v starting:%v EnterAfterStart:%v", p.SnId, s.sceneId, s.starting) + } + } + } + + if scene == nil { + var ret gamehall_proto.OpResultCode + ret, scene = csp.policy.AudienceEnter(csp, p, exclude, ischangeroom) + if ret != gamehall_proto.OpResultCode_OPRC_Sucess { + return ret + } + } + + if scene == nil { + return gamehall_proto.OpResultCode_OPRC_NoFindDownTiceRoom + } + + if scene != nil { + // 预创建房间检查观众数量 + if scene.IsPreCreateScene() && scene.GetAudienceCnt() >= model.GameParamData.MaxAudienceNum { + return gamehall_proto.OpResultCode_OPRC_RoomIsFull + } + if scene.AudienceEnter(p, ischangeroom) { + csp.OnPlayerEnter(p, scene) + return gamehall_proto.OpResultCode_OPRC_Sucess + } + } + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter snid:%v not found scene", p.SnId) + return gamehall_proto.OpResultCode_OPRC_NoFindDownTiceRoom +} + +// OnPlayerEnter 玩家进入房间完成 +func (csp *CoinScenePool) OnPlayerEnter(p *Player, scene *Scene) { + csp.players[p.SnId] = struct{}{} + csp.policy.OnPlayerEnter(csp, p, scene) +} + +// PlayerLeave 玩家离开房间 +func (csp *CoinScenePool) PlayerLeave(p *Player, reason int) bool { + if p.scene == nil { + return true + } + if p.scene.csp != csp && p.scene == csp.scenes[p.scene.sceneId] { + logger.Logger.Error("bug") + } + if p.scene.csp != csp { + return false + } + if p.scene != csp.scenes[p.scene.sceneId] { + logger.Logger.Error("bug") + } + s, ok := csp.scenes[p.scene.sceneId] + if !ok || s == nil { + return false + } + if !s.HasPlayer(p) { + return false + } + + if !csp.policy.PlayerLeave(csp, p, reason) { + return false + } + + s.PlayerLeave(p, reason) + logger.Logger.Tracef("(csp *CoinScenePool) PlayerLeave snid:%v in scene:%v", p.SnId, s.sceneId) + + csp.policy.OnPlayerLeave(csp, s, p) + + csp.OnPlayerLeave(s, p) + return true +} + +// AudienceLeave 观众离开房间 +func (csp *CoinScenePool) AudienceLeave(p *Player, reason int) bool { + if p.scene == nil { + return true + } + if p.scene.csp != csp { + return false + } + s, ok := csp.scenes[p.scene.sceneId] + if !ok || s == nil { + return false + } + if !s.HasAudience(p) { + return false + } + + if !csp.policy.AudienceLeave(csp, p, reason) { + return false + } + + s.AudienceLeave(p, reason) + logger.Logger.Tracef("(csp *CoinScenePool) AudienceLeave snid:%v in scene:%v", p.SnId, s.sceneId) + + csp.policy.OnPlayerLeave(csp, s, p) + + csp.OnPlayerLeave(s, p) + return true +} + +// OnPlayerLeave 离开房间完成 +func (csp *CoinScenePool) OnPlayerLeave(s *Scene, p *Player) { + if s == nil || p == nil { + return + } + delete(csp.players, p.SnId) + + // 玩家离开结算空房间的私人房 + if s.IsPrivateScene() { + if s.IsEmpty() { + s.ForceDelete(false) + } + return + } + + // 解散空房间并且房间数量大于预创建房间数量 + if s.IsPreCreateScene() { + if s.IsEmpty() { + var hasCnt int + for _, scene := range csp.scenes { + if s.limitPlatform.IdStr == scene.limitPlatform.IdStr { + hasCnt++ + } + } + if hasCnt > int(csp.dbGameFree.GetCreateRoomNum()) { + s.ForceDelete(false) + } + } + } +} + +// OnDestroyScene 解散房间 +// todo 是否需要优化 +func (csp *CoinScenePool) OnDestroyScene(sceneId int) { + scene, ok := csp.scenes[sceneId] + if !ok { + return + } + + for id := range scene.players { + player := PlayerMgrSington.GetPlayerBySnId(id) + if player != nil { + if !player.IsRob { + ctx := scene.GetPlayerGameCtx(player.SnId) + if ctx != nil { + //发送一个探针,等待ack后同步金币 + player.TryRetrieveLostGameCoin(sceneId) + } + } + } + } + for id := range scene.audiences { + player := PlayerMgrSington.GetPlayerBySnId(id) + if player != nil { + if !player.IsRob { + ctx := scene.GetPlayerGameCtx(player.SnId) + if ctx != nil { + //发送一个探针,等待ack后同步金币 + player.TryRetrieveLostGameCoin(sceneId) + } + } + } + } + + csp.policy.OnDestroyScene(csp, sceneId) + scene.csp = nil // 解除关联 + delete(csp.scenes, sceneId) +} + +// PreCreateRoom 预创建房间 +func (csp *CoinScenePool) PreCreateRoom() { + if csp.platform == DefaultPlatform { + return + } + preCreateNum := int(csp.dbGameFree.GetCreateRoomNum()) + if preCreateNum == 0 || model.GameParamData.ClosePreCreateRoom { + return + } + if p := PlatformMgrSingleton.GetPlatform(csp.platform); p == nil || p.Disable { + return + } + var num int + for _, scene := range csp.scenes { + if scene.limitPlatform.IdStr == csp.platform { + num++ + } + } + if num < preCreateNum { + inc := preCreateNum - num + for i := 0; i < inc; i++ { + scene := csp.policy.NewPreCreateScene(csp) + if scene != nil { + csp.scenes[scene.sceneId] = scene + scene.csp = csp + } + } + } +} + +// ListRoom 房间列表 +func (csp *CoinScenePool) ListRoom(p *Player) bool { + if p.scene != nil { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerListRoom[p.scene != nil] find snid:%v in scene:%v gameId:%v", p.SnId, p.scene.sceneId, p.scene.gameId) + return false + } + + csp.PreCreateRoom() + + if len(csp.scenes) == 0 { + return false + } + + pack := &gamehall_proto.SCCoinSceneListRoom{ + Id: csp.dbGameFree.Id, + LimitCoin: csp.dbGameFree.LimitCoin, + MaxCoinLimit: csp.dbGameFree.MaxCoinLimit, + BaseScore: csp.dbGameFree.BaseScore, + MaxScore: csp.dbGameFree.MaxChip, + OtherIntParams: csp.dbGameFree.OtherIntParams, + } + + maxPlayerNum := 0 + for sceneId, s := range csp.scenes { + data := &gamehall_proto.CoinSceneInfo{ + SceneId: proto.Int(sceneId), + PlayerNum: proto.Int(len(s.players)), + } + pack.Datas = append(pack.Datas, data) + maxPlayerNum = s.playerNum + } + pack.MaxPlayerNum = proto.Int(maxPlayerNum) + proto.SetDefaults(pack) + p.SendToClient(int(gamehall_proto.CoinSceneGamePacketID_PACKET_SC_COINSCENE_LISTROOM), pack) + return true +} + +// GetHasTruePlayerSceneCnt 有真人的房间数量 +func (csp *CoinScenePool) GetHasTruePlayerSceneCnt() int { + cnt := 0 + for _, s := range csp.scenes { + if s.GetTruePlayerCnt() != 0 { + cnt++ + } + } + return cnt +} diff --git a/worldsrv/coinscenepool_base.go b/worldsrv/coinscenepool_base.go new file mode 100644 index 0000000..991c646 --- /dev/null +++ b/worldsrv/coinscenepool_base.go @@ -0,0 +1,258 @@ +package main + +import ( + "sort" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/gamehall" +) + +const ( + MatchTrueManForbid int32 = -1 //禁止匹配真人 + MatchTrueManPriority = 1 //优先匹配真人 +) + +type BaseCoinScenePool struct { +} + +func (this *BaseCoinScenePool) New() interface{} { + return nil +} + +// CanEnter 检查入场条件 +func (this *BaseCoinScenePool) CanEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode { + // 具体场次的入场规则 + canEnterFunc := GetCoinSceneCanEnterFunc(int(pool.dbGameFree.GetGameId())) + if canEnterFunc != nil { + ret := canEnterFunc(pool, p) + if ret != gamehall.OpResultCode_OPRC_Sucess { + return ret + } + } + + // 通用入场检测 + if pool.dbGameFree.GetLimitCoin() != 0 && int64(pool.dbGameFree.GetLimitCoin()) > p.Coin { + return gamehall.OpResultCode_OPRC_CoinNotEnough + } + if pool.dbGameFree.GetMaxCoinLimit() != 0 && int64(pool.dbGameFree.GetMaxCoinLimit()) < p.Coin && !p.IsRob { + return gamehall.OpResultCode_OPRC_CoinTooMore + } + + return gamehall.OpResultCode_OPRC_Sucess +} + +func (this *BaseCoinScenePool) CanAudienceEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode { + return gamehall.OpResultCode_OPRC_Sucess +} + +// PlayerEnter 玩家进入房间池 +func (this *BaseCoinScenePool) PlayerEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) { + // 配房规则 + // 如果全局为限制则看具体的游戏是否限制,如果全局为不限制则不考虑具体的游戏。 + sameIpLimit := !model.GameParamData.SameIpNoLimit + if sameIpLimit && pool.dbGameFree.GetSameIpLimit() == 0 { + sameIpLimit = false + } + + // 通用匹配规则过滤 + matchTrueManRule := pool.dbGameFree.GetMatchTrueMan() + + scenes := make(map[int]*Scene) + for sceneId, s := range pool.scenes { + if s == nil || s.IsFull() || s.deleting { + continue + } + + if !common.Config.IsDevMode { + // 禁止真人匹配 + if matchTrueManRule == MatchTrueManForbid && !p.IsRob && s.GetTruePlayerCnt() != 0 { + continue + } + // 排除不能进的房间 + if common.InSliceInt32(exclude, int32(s.sceneId)) { + continue + } + // 规避同ip的用户在一个房间内(GM除外) + if sameIpLimit && p.GMLevel == 0 && s.HasSameIp(p.Ip) { + continue + } + // 多少局只能禁止再配对 + if s.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlace(p, s) { + continue + } + // 牌局开始后禁止进入 + if sp, ok := s.sp.(*ScenePolicyData); ok { + if s.starting && !sp.EnterAfterStart { + continue + } + } + } + scenes[sceneId] = s + } + + //优先黑白名单 + if scene == nil && len(scenes) != 0 { + gameId := pool.dbGameFree.GetGameId() + if p.WBLevel < 0 { //黑名单玩家 + var cntWhite int + var cntLose int + var whiteScene *Scene + var loseScene *Scene + for _, s := range scenes { + if s != nil { + if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) { + continue + } + cnt := s.GetWhitePlayerCnt() + if cnt > cntWhite { + cntWhite = cnt + whiteScene = s + } + cnt = s.GetLostPlayerCnt() + if cnt > cntLose { + cntLose = cnt + loseScene = s + } + } + } + if whiteScene != nil { + scene = whiteScene + } else if loseScene != nil { + scene = loseScene + } + } else if p.WBLevel > 0 { //白名单玩家 + var cntBlack int + var cntWin int + var blackScene *Scene + var winScene *Scene + for _, s := range scenes { + if s != nil { + if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) { + continue + } + cnt := s.GetBlackPlayerCnt() + if cnt > cntBlack { + cntBlack = cnt + blackScene = s + } + cnt = s.GetWinPlayerCnt() + if cnt > cntWin { + cntWin = cnt + winScene = s + } + + } + } + if blackScene != nil { + scene = blackScene + } else if winScene != nil { + scene = winScene + } + } else { //按类型匹配 + //优先真人 + if scene == nil && len(scenes) != 0 && matchTrueManRule == MatchTrueManPriority { + var selScene []*Scene + for _, value := range scenes { + if value != nil { + if value.GetTruePlayerCnt() > 0 && !value.IsFull() && !value.deleting { + selScene = append(selScene, value) + } + } + } + if len(selScene) > 0 { + sort.Slice(selScene, func(i, j int) bool { + return selScene[i].GetTruePlayerCnt() > selScene[j].GetTruePlayerCnt() + }) + scene = selScene[0] + } + } + + if scene == nil && len(scenes) != 0 { + //1.其次游戏的配桌规则 + if scene == nil { + matchFunc := GetCoinSceneMatchFunc(int(gameId)) + if matchFunc != nil { + scene = matchFunc(pool, p, scenes, sameIpLimit, exclude) + return gamehall.OpResultCode_OPRC_Sucess, scene + } + } + } + } + // 随机 + if scene == nil { + for _, v := range scenes { + scene = v + break + } + } + } + + return gamehall.OpResultCode_OPRC_Sucess, scene +} + +func (this *BaseCoinScenePool) AudienceEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) { + for _, s := range pool.scenes { + if s != nil && len(s.players) != 0 && !s.deleting && !common.InSliceInt32(exclude, int32(s.sceneId)) { + scene = s + break + } + } + return gamehall.OpResultCode_OPRC_Sucess, scene +} + +func (this *BaseCoinScenePool) OnPlayerEnter(pool *CoinScenePool, p *Player, scene *Scene) {} + +func (this *BaseCoinScenePool) PlayerLeave(pool *CoinScenePool, p *Player, reason int) bool { + return true +} + +func (this *BaseCoinScenePool) AudienceLeave(pool *CoinScenePool, p *Player, reason int) bool { + return true +} + +func (this *BaseCoinScenePool) OnPlayerLeave(pool *CoinScenePool, s *Scene, p *Player) {} + +func (this *BaseCoinScenePool) NewScene(pool *CoinScenePool, p *Player) *Scene { + gameId := int(pool.dbGameRule.GetGameId()) + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Errorf("Get %v game min session failed.", gameId) + return nil + } + + gameMode := pool.dbGameRule.GetGameMode() + params := pool.dbGameRule.GetParams() + var platformName string + limitPlatform := PlatformMgrSingleton.GetPlatform(pool.platform) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + platformName = DefaultPlatform + } else { + platformName = limitPlatform.IdStr + } + + sceneId := SceneMgrSingleton.GenOneCoinSceneId() + + scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, gameId, int(gameMode), int(common.SceneMode_Public), + 1, -1, params, gs, limitPlatform, pool.groupId, pool.dbGameFree, int32(pool.id)) + if scene != nil { + scene.hallId = pool.id + scene.csp = pool + if pool.groupId != 0 { + CoinSceneMgrSingleton.groupOfScene[int32(sceneId)] = pool.groupId + } else { + CoinSceneMgrSingleton.platformOfScene[int32(sceneId)] = platformName + } + return scene + } + return nil +} + +func (this *BaseCoinScenePool) NewPreCreateScene(pool *CoinScenePool) *Scene { + return this.NewScene(pool, nil) +} + +func (this *BaseCoinScenePool) OnDestroyScene(pool *CoinScenePool, sceneId int) {} diff --git a/worldsrv/coinscenepool_chess.go b/worldsrv/coinscenepool_chess.go new file mode 100644 index 0000000..6b53590 --- /dev/null +++ b/worldsrv/coinscenepool_chess.go @@ -0,0 +1,57 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" +) + +// 象棋特殊匹配实现 + +func init() { + RegisterCoinScenePool(common.GameId_ChesstitiansCambodian, new(CoinScenePoolChess)) + RegisterCoinScenePool(common.GameId_ChesstitiansCambodianRobot, new(CoinScenePoolChess)) +} + +type CoinScenePoolChess struct { + BaseCoinScenePool +} + +func (this *CoinScenePoolChess) New() interface{} { + return &ChessMatchPool{ + IdItem: make(map[int32]*ChessMatchItem), + } +} + +func (this *CoinScenePoolChess) OnPlayerEnter(pool *CoinScenePool, p *Player, scene *Scene) { + var cmp *ChessMatchPool + var ok bool + if pool != nil && pool.extraData != nil { + cmp, ok = pool.extraData.(*ChessMatchPool) + } + if !ok || cmp == nil { + logger.Logger.Errorf("(this *CoinScenePoolChess) OnPlayerEnter not found ChessMatchPool") + return + } + + if scene.IsFull() { + for k := range scene.players { + cmp.Del(k) + } + } else { + cmp.Add(p.SnId, p.ChessGrade, scene.sceneId) + } +} + +func (this *CoinScenePoolChess) OnPlayerLeave(pool *CoinScenePool, s *Scene, p *Player) { + var cmp *ChessMatchPool + var ok bool + if pool != nil && pool.extraData != nil { + cmp, ok = pool.extraData.(*ChessMatchPool) + } + if !ok || cmp == nil { + logger.Logger.Errorf("(this *CoinScenePoolChess) OnPlayerLeave not found ChessMatchPool") + return + } + cmp.Del(p.SnId) +} diff --git a/worldsrv/coinscenepool_interface.go b/worldsrv/coinscenepool_interface.go new file mode 100644 index 0000000..4e64993 --- /dev/null +++ b/worldsrv/coinscenepool_interface.go @@ -0,0 +1,35 @@ +package main + +import ( + "mongo.games.com/game/protocol/gamehall" +) + +var coinScenePools = map[int32]ICoinScenePool{} + +// RegisterCoinScenePool 注册游戏匹配规则 +func RegisterCoinScenePool(gameId int32, policy ICoinScenePool) { + coinScenePools[gameId] = policy +} + +func GetCoinScenePool(gameId int32) ICoinScenePool { + return coinScenePools[gameId] +} + +type ICoinScenePool interface { + New() interface{} + CanEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode + CanAudienceEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode + + PlayerEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) + AudienceEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) + OnPlayerEnter(pool *CoinScenePool, p *Player, scene *Scene) + + PlayerLeave(pool *CoinScenePool, p *Player, reason int) bool + AudienceLeave(pool *CoinScenePool, p *Player, reason int) bool + OnPlayerLeave(pool *CoinScenePool, s *Scene, p *Player) + + NewScene(pool *CoinScenePool, p *Player) *Scene + NewPreCreateScene(pool *CoinScenePool) *Scene + + OnDestroyScene(pool *CoinScenePool, sceneId int) +} diff --git a/worldsrv/coinscenepool_local.go b/worldsrv/coinscenepool_local.go new file mode 100644 index 0000000..bc8f9b4 --- /dev/null +++ b/worldsrv/coinscenepool_local.go @@ -0,0 +1,318 @@ +package main + +import ( + "sort" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +// 根据 DB_Createroom 规则匹配房间,创建房间 + +func init() { + local := new(CoinScenePoolLocal) + RegisterCoinScenePool(common.GameId_TienLen, local) + RegisterCoinScenePool(common.GameId_TienLen_yl, local) + RegisterCoinScenePool(common.GameId_TienLen_toend, local) + RegisterCoinScenePool(common.GameId_TienLen_yl_toend, local) + RegisterCoinScenePool(common.GameId_TaLa, local) + RegisterCoinScenePool(common.GameId_SamLoc, local) + RegisterCoinScenePool(common.GameID_ThirteenFree, local) + RegisterCoinScenePool(common.GameID_ThirteenFreeLaiZi, local) +} + +type CoinScenePoolLocal struct { + BaseCoinScenePool +} + +func (this *CoinScenePoolLocal) PlayerEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) { + // 配房规则 + // 如果全局为限制则看具体的游戏是否限制,如果全局为不限制则不考虑具体的游戏。 + sameIpLimit := !model.GameParamData.SameIpNoLimit + if sameIpLimit && pool.dbGameFree.GetSameIpLimit() == 0 { + sameIpLimit = false + } + + // 通用匹配规则过滤 + matchTrueManRule := pool.dbGameFree.GetMatchTrueMan() + + //根据携带金额取进房间底注 DB_Createroom + var dbCreateRoom *server_proto.DB_Createroom + gameId := pool.dbGameFree.GetGameId() + dbCreateRoom = srvdata.CreateRoomMgrSington.GetDataByGameIdWithTakeCoin(gameId, p.Coin) + if dbCreateRoom == nil { + logger.Logger.Warnf("(csp *CoinScenePool) PlayerEnter(robot:%v, exclude:%v, ischangeroom:%v) no found scene from DB_Createroom", + p.SnId, exclude, ischangeroom) + return gamehall.OpResultCode_OPRC_Error, nil + } + + scenes := make(map[int]*Scene) + for sceneId, s := range pool.scenes { + if s == nil || s.IsFull() || s.deleting { + continue + } + + if !common.Config.IsDevMode { + // 禁止真人匹配 + if matchTrueManRule == MatchTrueManForbid && !p.IsRob && s.GetTruePlayerCnt() != 0 { + continue + } + // 排除不能进的房间 + if common.InSliceInt32(exclude, int32(s.sceneId)) { + continue + } + // 规避同ip的用户在一个房间内(GM除外) + if sameIpLimit && p.GMLevel == 0 && s.HasSameIp(p.Ip) { + continue + } + // 多少局只能禁止再配对 + if s.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlace(p, s) { + continue + } + // 牌局开始后禁止进入 + if sp, ok := s.sp.(*ScenePolicyData); ok { + if s.starting && !sp.EnterAfterStart { + continue + } + } + //根据携带金额取可进房间 + if dbCreateRoom != nil && len(dbCreateRoom.GetBetRange()) != 0 { + betRange := dbCreateRoom.GetBetRange() + if common.InSliceInt32(betRange, s.BaseScore) { + scenes[sceneId] = s + } + } + } + scenes[sceneId] = s + } + + //优先黑白名单 + if scene == nil && len(scenes) != 0 { + gameId := pool.dbGameFree.GetGameId() + if p.WBLevel < 0 { //黑名单玩家 + var cntWhite int + var cntLose int + var whiteScene *Scene + var loseScene *Scene + for _, s := range scenes { + if s != nil { + if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) { + continue + } + cnt := s.GetWhitePlayerCnt() + if cnt > cntWhite { + cntWhite = cnt + whiteScene = s + } + cnt = s.GetLostPlayerCnt() + if cnt > cntLose { + cntLose = cnt + loseScene = s + } + } + } + if whiteScene != nil { + scene = whiteScene + } else if loseScene != nil { + scene = loseScene + } + } else if p.WBLevel > 0 { //白名单玩家 + var cntBlack int + var cntWin int + var blackScene *Scene + var winScene *Scene + for _, s := range scenes { + if s != nil { + if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) { + continue + } + cnt := s.GetBlackPlayerCnt() + if cnt > cntBlack { + cntBlack = cnt + blackScene = s + } + cnt = s.GetWinPlayerCnt() + if cnt > cntWin { + cntWin = cnt + winScene = s + } + + } + } + if blackScene != nil { + scene = blackScene + } else if winScene != nil { + scene = winScene + } + } else { //按类型匹配 + //优先真人 + if scene == nil && len(scenes) != 0 && matchTrueManRule == MatchTrueManPriority { + var selScene []*Scene + for _, value := range scenes { + if value != nil { + if value.GetTruePlayerCnt() > 0 && !value.IsFull() && !value.deleting { + selScene = append(selScene, value) + } + } + } + if len(selScene) > 0 { + sort.Slice(selScene, func(i, j int) bool { + return selScene[i].GetTruePlayerCnt() > selScene[j].GetTruePlayerCnt() + }) + scene = selScene[0] + } + } + + if scene == nil && len(scenes) != 0 { + //1.其次游戏的配桌规则 + if scene == nil { + matchFunc := GetCoinSceneMatchFunc(int(gameId)) + if matchFunc != nil { + scene = matchFunc(pool, p, scenes, sameIpLimit, exclude) + } + } + } + } + } + + return gamehall.OpResultCode_OPRC_Sucess, scene +} + +func (this *CoinScenePoolLocal) NewScene(pool *CoinScenePool, p *Player) *Scene { + gameId := int(pool.dbGameFree.GetGameId()) + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Errorf("Get %v game min session failed.", gameId) + return nil + } + + sceneId := SceneMgrSingleton.GenOneCoinSceneId() + + params := pool.dbGameRule.GetParams() + var platformName string + limitPlatform := PlatformMgrSingleton.GetPlatform(pool.platform) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + platformName = DefaultPlatform + } else { + platformName = limitPlatform.IdStr + } + + //根据携带金额取可创房间 DB_Createroom + baseScore := int32(0) + gameSite := 0 + playerTakeCoin := p.Coin + var dbCreateRoom *server_proto.DB_Createroom + arrs := srvdata.PBDB_CreateroomMgr.Datas.Arr + for i := len(arrs) - 1; i >= 0; i-- { + if arrs[i].GetGameId() == int32(gameId) { + goldRange := arrs[i].GoldRange + if len(goldRange) == 0 { + continue + } + if playerTakeCoin >= int64(goldRange[0]) { + dbCreateRoom = arrs[i] + break + } + } + } + if dbCreateRoom == nil { + logger.Logger.Tracef("CoinScenePool CreateLocalGameNewScene failed! playerTakeCoin:%v ", playerTakeCoin) + return nil + } + if len(dbCreateRoom.GetBetRange()) != 0 && dbCreateRoom.GetBetRange()[0] != 0 { + baseScore = common.RandInt32Slice(dbCreateRoom.GetBetRange()) + gameSite = int(dbCreateRoom.GetGameSite()) + } + if baseScore == 0 { + logger.Logger.Tracef("CoinScenePool CreateLocalGameNewScene failed! baseScore==0") + return nil + } + + scene := SceneMgrSingleton.CreateLocalGameScene(p.SnId, sceneId, gameId, gameSite, common.SceneMode_Public, 1, params, + gs, limitPlatform, 0, pool.dbGameFree, baseScore, pool.groupId, pool.id) + if scene != nil { + scene.hallId = pool.id + scene.groupId = pool.groupId + CoinSceneMgrSingleton.platformOfScene[int32(sceneId)] = platformName + return scene + } + return nil +} + +func (this *CoinScenePoolLocal) NewPreCreateScene(pool *CoinScenePool) *Scene { + gameId := int(pool.dbGameRule.GetGameId()) + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Errorf("Get %v game min session failed.", gameId) + return nil + } + + sceneId := SceneMgrSingleton.GenOneCoinSceneId() + + params := pool.dbGameRule.GetParams() + var platformName string + limitPlatform := PlatformMgrSingleton.GetPlatform(pool.platform) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + platformName = DefaultPlatform + } else { + platformName = limitPlatform.IdStr + } + + var scene *Scene + tmpIdx := common.RandInt(100) + playerNum := 4 + if tmpIdx < 20 { //20%创建两人房间 + playerNum = 2 + } + //根据SceneType随机可创房间 DB_Createroom + baseScore := int32(0) + gameSite := 0 + var dbCreateRooms []*server_proto.DB_Createroom + arrs := srvdata.PBDB_CreateroomMgr.Datas.Arr + for i := len(arrs) - 1; i >= 0; i-- { + if arrs[i].GetGameId() == int32(gameId) && arrs[i].GetGameSite() == pool.dbGameFree.GetSceneType() { + goldRange := arrs[i].GoldRange + if len(goldRange) == 0 { + continue + } + if goldRange[0] == 0 { + continue + } + dbCreateRooms = append(dbCreateRooms, arrs[i]) + } + } + if len(dbCreateRooms) != 0 { + randIdx := common.RandInt(len(dbCreateRooms)) + dbCreateRoom := dbCreateRooms[randIdx] + if len(dbCreateRoom.GetBetRange()) != 0 && dbCreateRoom.GetBetRange()[0] != 0 { + baseScore = common.RandInt32Slice(dbCreateRoom.GetBetRange()) + gameSite = int(dbCreateRoom.GetGameSite()) + } + if baseScore != 0 { + scene = SceneMgrSingleton.CreateLocalGameScene(0, sceneId, gameId, gameSite, common.SceneMode_Public, 1, params, + gs, limitPlatform, playerNum, pool.dbGameFree, baseScore, pool.groupId, pool.id) + if scene != nil { + logger.Logger.Tracef("CreateLocalGameScene success.gameId:%v gameSite:%v baseScore:%v randIdx:%v", scene.gameId, scene.gameSite, baseScore, randIdx) + } + } + } + + if scene != nil { + scene.hallId = pool.id + scene.csp = pool + if pool.groupId != 0 { + CoinSceneMgrSingleton.groupOfScene[int32(sceneId)] = pool.groupId + } else { + CoinSceneMgrSingleton.platformOfScene[int32(sceneId)] = platformName + } + return scene + } + return nil +} diff --git a/worldsrv/config-local.json b/worldsrv/config-local.json new file mode 100644 index 0000000..be3d4b9 --- /dev/null +++ b/worldsrv/config-local.json @@ -0,0 +1,159 @@ +{ + "netlib":{ + "SrvInfo":{ + "Name":"WorldServer", + "Type":6, + "Id":601, + "AreaID":1, + "Banner":[ + "=================", + "world server", + "=================" + ] + }, + "IoServices":[ + { + "Id":601, + "Type":6, + "AreaId":1, + "Name":"WorldService", + "Ip":"127.0.0.1", + "Port":0, + "MaxDone":40000, + "MaxPend":2000000, + "MaxPacket":6553500, + "MaxConn":200, + "RcvBuff":4096000, + "SndBuff":10240000, + "WriteTimeout":30, + "ReadTimeout":30, + "IsInnerLink":true, + "NoDelay":true, + "SupportFragment":true, + "AuthKey":"1234567890", + "FilterChain":[ + "session-filter-auth" + ], + "HandlerChain":[ + "session-srv-registe", + "handler-world-close" + ] + }, + { + "Id":501, + "Type":5, + "AreaId":1, + "Name":"ManagerService", + "Ip":"127.0.0.1", + "Port":3002, + "MaxDone":20, + "MaxPend":20, + "MaxPacket":6553500, + "MaxConn":10, + "RcvBuff":8192, + "SndBuff":8192, + "WriteTimeout":30, + "ReadTimeout":30, + "IsInnerLink":true, + "NoDelay":true, + "IsClient":true, + "IsAutoReconn":true, + "SupportFragment":true, + "AuthKey":"1234567890", + "FilterChain":[ + "session-filter-auth" + ], + "HandlerChain":[ + "session-srv-registe", + "srv-service-handler" + ] + } + ] + }, + "module":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "tx":{ + "TxSkeletonName":"mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "executor":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + }, + "Worker":{ + "WorkerCnt":8, + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + } + } + }, + "timer":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "core":{ + "MaxProcs":4 + }, + "cmdline":{ + "SupportCmdLine":true + }, + "signal":{ + "SupportSignal":true + }, + "common":{ + "AppId":"5c56d1644966f078bfb90c71", + "IsDevMode":true + }, + "costum":{ + "PromotersUrl":"http://192.168.0.65/jxjy/tg/mongo.games.com/game/push_order", + "etcdurl":["127.0.0.1:2379"], + "etcduser":"root", + "etcdpwd":"win88", + "PltName":"xiaoxin", + "LogicLevelUrl":"http://127.0.0.1:8000", + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog":1024 + }, + "webapi":{ + "GameApiURL":"http://127.0.0.1:8000/api/game_srv" + }, + "data":{ + "RootPath":"../data", + "Files":[ + "DB_Sensitive_Words.dat", + "DB_MatchFree.dat", + "DB_MatchRule.dat", + "DB_Task.dat", + "DB_GameFree.dat", + "DB_GameRule.dat", + "DB_Shop.dat", + "DB_Condition.dat", + "DB_Limit_Times.dat", + "DB_Sign.dat", + "DB_Activity1.dat", + "DB_VIP.dat" + ] + }, + "gameconfig":{ + "RootPath":"../data/gameconfig" + }, + "i18n":{ + "RootPath":"../data/i18n" + } +} \ No newline at end of file diff --git a/worldsrv/config.json b/worldsrv/config.json new file mode 100644 index 0000000..2ace58f --- /dev/null +++ b/worldsrv/config.json @@ -0,0 +1,140 @@ +{ + "netlib": { + "SrvInfo": { + "Name": "WorldServer", + "Type": 6, + "Id": 601, + "AreaID": 1, + "Banner": [ + "=================", + "world server", + "=================" + ] + }, + "IoServices": [ + { + "Id": 601, + "Type": 6, + "AreaId": 1, + "Name": "WorldService", + "Ip": "127.0.0.1", + "Port": 0, + "MaxDone": 40000, + "MaxPend": 2000000, + "MaxPacket": 6553500, + "MaxConn": 200, + "RcvBuff": 4096000, + "SndBuff": 10240000, + "WriteTimeout": 30, + "ReadTimeout": 30, + "IsInnerLink": true, + "NoDelay": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "handler-world-close" + ] + }, + { + "Id": 501, + "Type": 5, + "AreaId": 1, + "Name": "ManagerService", + "Ip": "127.0.0.1", + "Port": 3002, + "MaxDone": 20, + "MaxPend": 20, + "MaxPacket": 6553500, + "MaxConn": 10, + "RcvBuff": 8192, + "SndBuff": 8192, + "WriteTimeout": 30, + "ReadTimeout": 30, + "IsInnerLink": true, + "NoDelay": true, + "IsClient": true, + "IsAutoReconn": true, + "SupportFragment": true, + "AuthKey": "1234567890", + "FilterChain": [ + "session-filter-auth" + ], + "HandlerChain": [ + "session-srv-registe", + "srv-service-handler" + ] + } + ] + }, + "module": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "tx": { + "TxSkeletonName": "mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "executor": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + }, + "Worker": { + "WorkerCnt": 8, + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 0 + } + } + }, + "timer": { + "Options": { + "QueueBacklog": 1024, + "MaxDone": 1024, + "Interval": 100 + } + }, + "cmdline": { + "SupportCmdLine": true + }, + "signal": { + "SupportSignal": true + }, + "common": { + "AppId": "5c56d1644966f078bfb90c71", + "IsDevMode": true + }, + "costum": { + "etcdurl": [ + "127.0.0.1:2379" + ], + "etcduser": "root", + "etcdpwd": "win88", + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + "RabbitMQURL": "amqp://win88:123456@127.0.0.1:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog": 1024 + }, + "webapi": { + "GameApiURL": "http://127.0.0.1:8000/api/game_srv" + }, + "data": { + "RootPath": "../data" + }, + "gameconfig": { + "RootPath": "../data/gameconfig" + }, + "i18n": { + "RootPath": "../data/i18n" + } +} \ No newline at end of file diff --git a/worldsrv/config_18.json b/worldsrv/config_18.json new file mode 100644 index 0000000..f2cc7e7 --- /dev/null +++ b/worldsrv/config_18.json @@ -0,0 +1,159 @@ +{ + "netlib":{ + "SrvInfo":{ + "Name":"WorldServer", + "Type":6, + "Id":601, + "AreaID":1, + "Banner":[ + "=================", + "world server", + "=================" + ] + }, + "IoServices":[ + { + "Id":601, + "Type":6, + "AreaId":1, + "Name":"WorldService", + "Ip":"127.0.0.1", + "Port":0, + "MaxDone":40000, + "MaxPend":2000000, + "MaxPacket":6553500, + "MaxConn":200, + "RcvBuff":4096000, + "SndBuff":10240000, + "WriteTimeout":30, + "ReadTimeout":30, + "IsInnerLink":true, + "NoDelay":true, + "SupportFragment":true, + "AuthKey":"1234567890", + "FilterChain":[ + "session-filter-auth" + ], + "HandlerChain":[ + "session-srv-registe", + "handler-world-close" + ] + }, + { + "Id":501, + "Type":5, + "AreaId":1, + "Name":"ManagerService", + "Ip":"127.0.0.1", + "Port":3002, + "MaxDone":20, + "MaxPend":20, + "MaxPacket":6553500, + "MaxConn":10, + "RcvBuff":8192, + "SndBuff":8192, + "WriteTimeout":30, + "ReadTimeout":30, + "IsInnerLink":true, + "NoDelay":true, + "IsClient":true, + "IsAutoReconn":true, + "SupportFragment":true, + "AuthKey":"1234567890", + "FilterChain":[ + "session-filter-auth" + ], + "HandlerChain":[ + "session-srv-registe", + "srv-service-handler" + ] + } + ] + }, + "module":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "tx":{ + "TxSkeletonName":"mongo.games.com/goserver/srvlib/txcommskeleton" + }, + "executor":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + }, + "Worker":{ + "WorkerCnt":8, + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":0 + } + } + }, + "timer":{ + "Options":{ + "QueueBacklog":1024, + "MaxDone":1024, + "Interval":100 + } + }, + "core":{ + "MaxProcs":4 + }, + "cmdline":{ + "SupportCmdLine":true + }, + "signal":{ + "SupportSignal":true + }, + "common":{ + "AppId":"5c56d1644966f078bfb90c71", + "IsDevMode":true + }, + "costum":{ + "PromotersUrl":"http://192.168.0.65/jxjy/tg/mongo.games.com/game/push_order", + "etcdurl":["192.168.31.18:2379"], + "etcduser":"root", + "etcdpwd":"win88", + "PltName":"xiaoxin", + "LogicLevelUrl":"http://192.168.31.18:8000", + "MgoRpcCliNet": "tcp", + "MgoRpcCliAddr": "127.0.0.1:8999", + "MgoRpcCliReconnInterV": 3, + + "RabbitMQURL": "amqp://win88:123456@192.168.31.18:5672/win88", + "RMQExchange": "win88", + "RMQPublishBacklog":1024 + }, + "webapi":{ + "GameApiURL":"http://192.168.31.18:8000/api/game_srv" + }, + "data":{ + "RootPath":"../data", + "Files":[ + "DB_Sensitive_Words.dat", + "DB_MatchFree.dat", + "DB_MatchRule.dat", + "DB_Task.dat", + "DB_GameFree.dat", + "DB_GameRule.dat", + "DB_Shop.dat", + "DB_Condition.dat", + "DB_Limit_Times.dat", + "DB_Sign.dat", + "DB_Activity1.dat", + "DB_VIP.dat" + ] + }, + "gameconfig":{ + "RootPath":"../data/gameconfig" + }, + "i18n":{ + "RootPath":"../data/i18n" + } +} \ No newline at end of file diff --git a/worldsrv/dbsaver.go b/worldsrv/dbsaver.go new file mode 100644 index 0000000..663c86e --- /dev/null +++ b/worldsrv/dbsaver.go @@ -0,0 +1,148 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" +) + +type SaveTaskHandler interface { + Time2Save() +} + +var SaverSliceNumber = 600 + +var DbSaver_Inst = &DbSaver{ + Tick: int32(SaverSliceNumber), + index: 0, + init: false, + list: make([]*SaverArray, SaverSliceNumber), + queue: make([]*BalanceQueue, 10), + pool: make(map[SaveTaskHandler]*SaverArray), +} + +type DbSaver struct { + Tick int32 // 最大索引 + index int32 // 循环索引 + list []*SaverArray + queue []*BalanceQueue + init bool + pool map[SaveTaskHandler]*SaverArray +} + +// pushBalanceSaverArray 向队列中添加SaveTaskHandler +func (this *DbSaver) pushBalanceSaverArray(sth SaveTaskHandler) { + if sth == nil { + return + } + if _, exist := this.pool[sth]; exist { + return + } + for pos, bq := range this.queue { + size := len(bq.queue) + if size > 0 { + arr := bq.queue[size-1] + if pos+1 >= len(this.queue) { + this.queue = append(this.queue, &BalanceQueue{}) + } + this.queue[pos+1].queue = append(this.queue[pos+1].queue, arr) + this.queue[pos].queue = bq.queue[:size-1] + arr.bqPos = len(this.queue[pos+1].queue) - 1 + arr.Array = append(arr.Array, sth) + this.pool[sth] = arr + return + } + } + return +} + +// RegisterDbSaverTask 向队列中添加SaveTaskHandler +func (this *DbSaver) RegisterDbSaverTask(i interface{}) { + if st, ok := i.(SaveTaskHandler); ok { + this.pushBalanceSaverArray(st) + } +} + +// UnregisteDbSaveTask 从队列中移除SaveTaskHandler +func (this *DbSaver) UnregisteDbSaveTask(i interface{}) { + if sth, ok := i.(SaveTaskHandler); ok { + if arr, exist := this.pool[sth]; exist { + delete(this.pool, sth) + count := len(arr.Array) + for i := 0; i < count; i++ { + if arr.Array[i] == sth { + arr.Array[i] = arr.Array[count-1] + arr.Array = arr.Array[:count-1] + + bqPos := arr.bqPos + queCount := len(this.queue[count].queue) + this.queue[count].queue[bqPos] = this.queue[count].queue[queCount-1] + this.queue[count].queue[bqPos].bqPos = bqPos + this.queue[count].queue = this.queue[count].queue[:queCount-1] + this.queue[count-1].queue = append(this.queue[count-1].queue, arr) + arr.bqPos = len(this.queue[count-1].queue) - 1 + return + } + } + } else { + logger.Logger.Info("Player not in dbsaver") + } + } +} + +// SaverArray 保存SaveTaskHandler的数组 +type SaverArray struct { + Array []SaveTaskHandler + bqPos int +} + +// BalanceQueue 保存SaveTaskHandler的队列 +type BalanceQueue struct { + queue []*SaverArray +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *DbSaver) ModuleName() string { + return "dbsaver" +} + +func (this *DbSaver) Init() { + if this.init == false { + for i := 0; i < len(this.queue); i++ { + this.queue[i] = &BalanceQueue{} + } + //初始化平衡数组,所有平衡队列容量为0 + for i := 0; i < int(this.Tick); i++ { + this.list[i] = &SaverArray{bqPos: i} + this.queue[0].queue = append(this.queue[0].queue, this.list[i]) + } + this.init = true + } +} + +func (this *DbSaver) Update() { + if this.index == this.Tick { + this.index = 0 + } + sa := this.list[this.index] + for _, sth := range sa.Array { + sth.Time2Save() + } + this.index = this.index + 1 +} + +func (this *DbSaver) Shutdown() { + module.UnregisteModule(this) +} + +//////////////////////////////////////////////////////////////////// +/// Module Implement [end] +//////////////////////////////////////////////////////////////////// + +// 注册模块 +func init() { + module.RegisteModule(DbSaver_Inst, time.Second, 0) +} diff --git a/worldsrv/doc.go b/worldsrv/doc.go new file mode 100644 index 0000000..0af1cf0 --- /dev/null +++ b/worldsrv/doc.go @@ -0,0 +1,6 @@ +package main + +// Game world server(The whole world only). +// Responsibilities: +// 1:Responsible for the overall business in the game. +// For example: social relations diff --git a/worldsrv/etcd.go b/worldsrv/etcd.go new file mode 100644 index 0000000..a95b531 --- /dev/null +++ b/worldsrv/etcd.go @@ -0,0 +1,45 @@ +package main + +import ( + "go.etcd.io/etcd/client/v3" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/etcd" + "mongo.games.com/game/protocol/webapi" +) + +func init() { + // 个人水池配置 + etcd.Register(etcd.ETCDKEY_PLAYERPOOL, webapi.PlayerPool{}, PlatformConfigEtcd) + // 商品兑换 + etcd.Register(etcd.ETCDKEY_SHOP_EXCHANGE, webapi.ExchangeShopList{}, func(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + config, ok := data.(*webapi.ExchangeShopList) + if !ok { + logger.Logger.Errorf("etcd completeKey:%s, data type error", completeKey) + return + } + ShopMgrSington.UpExShop(config) + }) + // 商城商品 + etcd.Register(etcd.ETCDKEY_SHOP_ITEM, webapi.ItemShopList{}, func(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + config, ok := data.(*webapi.ItemShopList) + if !ok { + logger.Logger.Errorf("etcd completeKey:%s, data type error", completeKey) + return + } + ShopMgrSington.UpdateItemShop(config) + }) +} + +func PlatformConfigEtcd(completeKey string, isInit bool, event *clientv3.Event, data interface{}) { + if event.Type == clientv3.EventTypeDelete { + return + } + PlatformMgrSingleton.UpdateConfig(data) +} diff --git a/worldsrv/etcdmgr.go b/worldsrv/etcdmgr.go new file mode 100644 index 0000000..445de97 --- /dev/null +++ b/worldsrv/etcdmgr.go @@ -0,0 +1,2436 @@ +package main + +import ( + "context" + "encoding/json" + "strconv" + "strings" + "time" + + "go.etcd.io/etcd/client/v3" + "mongo.games.com/game/common" + "mongo.games.com/game/etcd" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + login_proto "mongo.games.com/game/protocol/login" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" +) + +// EtcdMgrSington etcd数据读取 +// Deprecated: use [etcd] instead +// 使用 etcd.Register 代替 +// todo EtcdMgrSington 用新方法替换 +var EtcdMgrSington = &EtcdMgr{ + Client: &etcd.Client{}, +} + +type EtcdMgr struct { + *etcd.Client +} + +// 初始化平台数据 +func (this *EtcdMgr) InitPlatform() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_PLATFORM_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_PLATFORM_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var value webapi_proto.Platform + err = proto.Unmarshal(res.Kvs[i].Value, &value) + if err == nil { + PlatformMgrSingleton.UpsertPlatform(value.PlatformName, value.Isolated, value.Disabled, value.Id, + value.CustomService, value.BindOption, value.ServiceFlag, value.UpgradeAccountGiveCoin, + value.NewAccountGiveCoin, value.PerBankNoLimitAccount, value.ExchangeMin, value.ExchangeLimit, + value.ExchangeTax, value.ExchangeFlow, value.ExchangeFlag, value.SpreadConfig, value.VipRange, "", + nil, value.VerifyCodeType, nil /*value.ThirdGameMerchant,*/, value.CustomType, + false, value.NeedSameName, value.ExchangeForceTax, value.ExchangeGiveFlow, value.ExchangeVer, + value.ExchangeBankMax, value.ExchangeAlipayMax, 0, value.PerBankNoLimitName, value.IsCanUserBindPromoter, + value.UserBindPromoterPrize, false, value.ExchangeMultiple, false, value.MerchantKey, + value.BindTelReward) + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(res.Kvs[i].Key), err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_PLATFORM_PREFIX, err) + } + } + return -1 + } + + watchFunc := func(ctx context.Context, revision int64) { + // 监控数据变动 + this.GoWatch(ctx, revision, etcd.ETCDKEY_PLATFORM_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var value webapi_proto.Platform + err := proto.Unmarshal(ev.Kv.Value, &value) + if err == nil { + platform := PlatformMgrSingleton.UpsertPlatform(value.PlatformName, value.Isolated, + value.Disabled, value.Id, value.CustomService, value.BindOption, + value.ServiceFlag, value.UpgradeAccountGiveCoin, value.NewAccountGiveCoin, + value.PerBankNoLimitAccount, value.ExchangeMin, value.ExchangeLimit, + value.ExchangeTax, value.ExchangeFlow, value.ExchangeFlag, value.SpreadConfig, + value.VipRange, "", nil, value.VerifyCodeType, + nil, value.CustomType, true, value.NeedSameName, + value.ExchangeForceTax, value.ExchangeGiveFlow, value.ExchangeVer, + value.ExchangeBankMax, value.ExchangeAlipayMax, 0, value.PerBankNoLimitName, + value.IsCanUserBindPromoter, value.UserBindPromoterPrize, false, value.ExchangeMultiple, + false, value.MerchantKey, value.BindTelReward) + if platform != nil { + //通知客户端平台配置发生改变 + scPlatForm := &login_proto.SCPlatFormConfig{ + Platform: proto.String(platform.IdStr), + OpRetCode: login_proto.OpResultCode_OPRC_Sucess, + UpgradeAccountGiveCoin: proto.Int32(platform.UpgradeAccountGiveCoin), + ExchangeMin: proto.Int32(platform.ExchangeMin), + ExchangeLimit: proto.Int32(platform.ExchangeLimit), + VipRange: platform.VipRange, + OtherParams: proto.String(platform.OtherParams), + SpreadConfig: proto.Int32(platform.SpreadConfig), + ExchangeTax: proto.Int32(platform.ExchangeTax), + ExchangeFlow: proto.Int32(platform.ExchangeFlow), + ExchangeBankMax: proto.Int32(platform.ExchangeBankMax), + ExchangeAlipayMax: proto.Int32(platform.ExchangeAlipayMax), + ExchangeMultiple: proto.Int32(platform.ExchangeMultiple), + } + rebateTask := RebateInfoMgrSington.rebateTask[platform.IdStr] + if rebateTask != nil { + scPlatForm.Rebate = &login_proto.RebateCfg{ + RebateSwitch: proto.Bool(rebateTask.RebateSwitch), + ReceiveMode: proto.Int32(int32(rebateTask.ReceiveMode)), + NotGiveOverdue: proto.Int32(int32(rebateTask.NotGiveOverdue)), + } + } + if platform.ClubConfig != nil { //俱乐部配置 + scPlatForm.Club = &login_proto.ClubCfg{ + IsOpenClub: proto.Bool(platform.ClubConfig.IsOpenClub), + CreationCoin: proto.Int64(platform.ClubConfig.CreationCoin), + IncreaseCoin: proto.Int64(platform.ClubConfig.IncreaseCoin), + ClubInitPlayerNum: proto.Int32(platform.ClubConfig.ClubInitPlayerNum), + CreateClubCheckByManual: proto.Bool(platform.ClubConfig.CreateClubCheckByManual), + EditClubNoticeByManual: proto.Bool(platform.ClubConfig.EditClubNoticeByManual), + CreateRoomAmount: proto.Int64(platform.ClubConfig.CreateRoomAmount), + GiveCoinRate: platform.ClubConfig.GiveCoinRate, + } + } + proto.SetDefaults(scPlatForm) + PlayerMgrSington.BroadcastMessageToPlatform(platform.IdStr, int(login_proto.LoginPacketID_PACKET_SC_PLATFORMCFG), scPlatForm) + } + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(ev.Kv.Key), err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// InitPlatformGameConfig 初始化平台游戏配置 +func (this *EtcdMgr) InitPlatformGameConfig() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_GAMECONFIG_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_GAMECONFIG_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var config webapi_proto.GameFree + err = proto.Unmarshal(res.Kvs[i].Value, &config) + if err == nil { + s := strings.TrimPrefix(string(res.Kvs[i].Key), etcd.ETCDKEY_GAMECONFIG_PREFIX) + arr := strings.Split(s, "/") + if len(arr) > 1 { + pltId := arr[0] + if err == nil { + PlatformMgrSingleton.UpsertGameFree(pltId, &config) + } + } + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(res.Kvs[i].Key), err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAMECONFIG_PREFIX, err) + } + } + return -1 + } + + watchFunc := func(ctx context.Context, revision int64) { + // 监控数据变动 + this.GoWatch(ctx, revision, etcd.ETCDKEY_GAMECONFIG_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var config webapi_proto.GameFree + err := proto.Unmarshal(ev.Kv.Value, &config) + if err == nil { + s := strings.TrimPrefix(string(ev.Kv.Key), etcd.ETCDKEY_GAMECONFIG_PREFIX) + arr := strings.Split(s, "/") + if len(arr) > 1 { + pltId := arr[0] + if err == nil { + PlatformMgrSingleton.UpsertGameFree(pltId, &config) + } + } + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(ev.Kv.Key), err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 初始化平台包数据 +func (this *EtcdMgr) InitPlatformPackage() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_PACKAGE_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_PACKAGE_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var value webapi_proto.AppInfo + err = proto.Unmarshal(res.Kvs[i].Value, &value) + if err == nil { + if value.PlatformId == 0 { + value.PlatformId = int32(DefaultPlatformInt) + } + + PlatformMgrSingleton.PackageList[value.PackageName] = &value + PlatformMgrSingleton.PackageList[value.BundleId] = &value + //if _, ok := PlatformMgrSingleton.PromoterList[strconv.Itoa(int(value.PromoterId))]; !ok { + // PlatformMgrSingleton.PromoterList[strconv.Itoa(int(value.PromoterId))] = PlatformPromoter{ + // Platform: strconv.Itoa(int(value.Platform)), + // Promoter: strconv.Itoa(int(value.PromoterId)), + // Tag: value.Tag, + // } + //} + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(res.Kvs[i].Key), err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_PACKAGE_PREFIX, err) + } + } + return -1 + } + + watchFunc := func(ctx context.Context, revision int64) { + // 监控数据变动 + this.GoWatch(ctx, revision, etcd.ETCDKEY_PACKAGE_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var value webapi_proto.AppInfo + err := proto.Unmarshal(ev.Kv.Value, &value) + if err == nil { + if value.PlatformId == 0 { + value.PlatformId = int32(DefaultPlatformInt) + } + + PlatformMgrSingleton.PackageList[value.PackageName] = &value + PlatformMgrSingleton.PackageList[value.BundleId] = &value + //if _, ok := PlatformMgrSingleton.PromoterList[strconv.Itoa(int(value.PromoterId))]; !ok { + // PlatformMgrSingleton.PromoterList[strconv.Itoa(int(value.PromoterId))] = PlatformPromoter{ + // Platform: strconv.Itoa(int(value.Platform)), + // Promoter: strconv.Itoa(int(value.PromoterId)), + // Tag: value.Tag, + // } + //} + // + //var strPlatorm string + //var strChannel string + //var strPromoter string + //if value.Platform != 0 { + // strPlatorm = strconv.Itoa(int(value.Platform)) + //} + //if value.ChannelId != 0 { + // strChannel = strconv.Itoa(int(value.ChannelId)) + //} + //if value.PromoterId != 0 { + // strPromoter = strconv.Itoa(int(value.PromoterId)) + //} + //if value.AppStore != 1 { + // uptCnt := PlayerMgrSington.UpdateAllPlayerPackageTag(value.Tag, strPlatorm, strChannel, strPromoter, int32(value.PromoterTree), value.TagKey) + // logger.Logger.Infof("/api/mongo.games.com/game/UpsertPackageTag update(tag:%v, platform:%v, channel:%v, promoter:%v), updated cnt=%v", value.Tag, strPlatorm, strChannel, strPromoter, uptCnt) + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return model.UpdateAllPlayerPackageTag(value.Tag, strPlatorm, strChannel, strPromoter, int32(value.PromoterTree), value.TagKey) + // }), nil, "UpdateAllPlayerPackageTag").Start() + //} + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PACKAGE_PREFIX, err) + } + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载组配置 +func (this *EtcdMgr) InitGameGroup() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_GROUPCONFIG_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_GROUPCONFIG_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var value webapi_proto.GameConfigGroup + err = proto.Unmarshal(res.Kvs[i].Value, &value) + if err == nil { + PlatformGameGroupMgrSington.UpsertGameGroup(&value) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GROUPCONFIG_PREFIX, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_GROUPCONFIG_PREFIX, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_GROUPCONFIG_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var value webapi_proto.GameConfigGroup + err := proto.Unmarshal(ev.Kv.Value, &value) + if err == nil { + PlatformGameGroupMgrSington.UpsertGameGroup(&value) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GROUPCONFIG_PREFIX, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载黑名单配置 +func (this *EtcdMgr) InitBlackList() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_BLACKLIST_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_BLACKLIST_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var value BlackInfoApi + err = json.Unmarshal(res.Kvs[i].Value, &value) + if err == nil { + BlackListMgrSington.InitBlackInfo(&value) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_BLACKLIST_PREFIX, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_BLACKLIST_PREFIX, err) + } + } + return -1 + } + + //@test code + //go func() { + // for { + // i := int32(1) + // data := BlackInfoApi{ + // Id: i, + // Snid: i, + // Creator: rand.Int31(), + // } + // buf, err := json.Marshal(data) + // if err == nil { + // key := fmt.Sprintf("%s%d", etcd.ETCDKEY_BLACKLIST_PREFIX, i) + // putResp, err := this.PutValue(key, string(buf)) + // if err == nil { + // if putResp.PrevKv != nil { + // logger.Logger.Trace("@etcdtest put", string(putResp.PrevKv.Key), string(putResp.PrevKv.Value)) + // } + // //delResp, err := this.DelValue(key) + // //if err == nil { + // // logger.Logger.Trace("@etcdtest del", delResp.Deleted) + // //} + // } + // } + // } + //}() + //@test code + + //ETCD中现在只有公共黑名单信息 + //如果删除公共黑名单信息使用ETCD删除 + //如果删除个人玩家身上的黑名单信息使用API删除 + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_BLACKLIST_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + dirs := strings.Split(string(ev.Kv.Key), "/") + n := len(dirs) + if n > 0 { + last := dirs[n-1] + id, err := strconv.Atoi(last) + if err == nil { + if value, exist := BlackListMgrSington.BlackList[int32(id)]; exist { + BlackListMgrSington.RemoveBlackInfo(value.Id, value.Platform) + } + } + } + case clientv3.EventTypePut: + var value BlackInfoApi + err := json.Unmarshal(ev.Kv.Value, &value) + if err == nil { + BlackListMgrSington.UpsertBlackInfo(&value) + if (value.Space & int32(BlackState_Login)) != 0 { + var targetPlayer []*Player //确定用户是否在线 + for _, value := range PlayerMgrSington.players { + _, ok := BlackListMgrSington.CheckPlayerInBlack(value.PlayerData, BlackState_Login) + if ok { + targetPlayer = append(targetPlayer, value) + } + } + for _, p := range targetPlayer { + if p.sid != 0 { + p.Kickout(int32(login_proto.SSDisconnectTypeCode_SSDTC_BlackList)) + } else { + LoginStateMgrSington.LogoutByAccount(p.AccountId) + } + } + } + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_BLACKLIST_PREFIX, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// +//// 加载平台公告配置 +//func (this *EtcdMgr) InitPlatformBulletin() { +// initFunc := func() int64 { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_BULLETIN_PREFIX) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_BULLETIN_PREFIX) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// var info Bullet +// err = json.Unmarshal(res.Kvs[i].Value, &info) +// if err == nil { +// BulletMgrSington.BulletMsgList[info.Id] = &info +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_BULLETIN_PREFIX, err) +// } +// } +// if res.Header != nil { +// return res.Header.Revision +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_BULLETIN_PREFIX, err) +// } +// } +// return -1 +// } +// +// // 监控数据变动 +// watchFunc := func(ctx context.Context, revision int64) { +// this.GoWatch(ctx, revision, etcd.ETCDKEY_BULLETIN_PREFIX, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// dirs := strings.Split(string(ev.Kv.Key), "/") +// n := len(dirs) +// if n > 0 { +// last := dirs[n-1] +// id, err := strconv.Atoi(last) +// if err == nil { +// delete(BulletMgrSington.BulletMsgList, int32(id)) +// } +// } +// case clientv3.EventTypePut: +// var value Bullet +// err := json.Unmarshal(ev.Kv.Value, &value) +// if err == nil { +// BulletMgrSington.BulletMsgList[value.Id] = &value +// var bulletList []*login_proto.Bulletion +// bulletList = append(bulletList, &login_proto.Bulletion{ +// Id: proto.Int32(value.Id), +// NoticeTitle: proto.String(value.NoticeTitle), +// NoticeContent: proto.String(value.NoticeContent), +// UpdateTime: proto.String(value.UpdateTime), +// Sort: proto.Int32(value.Sort), +// }) +// var rawpack = &login_proto.SCBulletionInfo{ +// Id: proto.Int32(value.Id), +// BulletionList: bulletList, +// } +// proto.SetDefaults(rawpack) +// PlayerMgrSington.BroadcastMessageToPlatform(value.Platform, int(login_proto.LoginPacketID_PACKET_SC_BULLETIONINFO), rawpack) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_BULLETIN_PREFIX, err) +// } +// } +// } +// return nil +// }) +// } +// +// this.InitAndWatch(initFunc, watchFunc) +//} +// +//// 加载平台招商代理配置 +//func (this *EtcdMgr) InitPlatformAgent() { +// initFunc := func() int64 { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_AGENTCUSTOMER_PREFIX) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_AGENTCUSTOMER_PREFIX) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// var info Customer +// err = json.Unmarshal(res.Kvs[i].Value, &info) +// if err == nil { +// CustomerMgrSington.CustomerMsgList[info.Id] = &info +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_AGENTCUSTOMER_PREFIX, err) +// } +// } +// if res.Header != nil { +// return res.Header.Revision +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_AGENTCUSTOMER_PREFIX, err) +// } +// } +// return -1 +// } +// +// // 监控数据变动 +// watchFunc := func(ctx context.Context, revision int64) { +// this.GoWatch(ctx, revision, etcd.ETCDKEY_AGENTCUSTOMER_PREFIX, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// dirs := strings.Split(string(ev.Kv.Key), "/") +// n := len(dirs) +// if n > 0 { +// last := dirs[n-1] +// id, err := strconv.Atoi(last) +// if err == nil { +// delete(CustomerMgrSington.CustomerMsgList, int32(id)) +// } +// } +// case clientv3.EventTypePut: +// var value Customer +// err := json.Unmarshal(ev.Kv.Value, &value) +// if err == nil { +// CustomerMgrSington.CustomerMsgList[value.Id] = &value +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_AGENTCUSTOMER_PREFIX, err) +// } +// } +// } +// return nil +// }) +// } +// +// this.InitAndWatch(initFunc, watchFunc) +//} + +// 加载平台签到活动配置 +func (this *EtcdMgr) InitPlatformActSignin() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_SIGNIN_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_SIGNIN_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var value SignConfig + // err = json.Unmarshal(res.Kvs[i].Value, &value) + // if err == nil { + // if value.StartAct > 0 { + // ActSignMgrSington.SignConfigs[value.Platform] = value + // //logger.Logger.Trace("value:", value) + // } + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_SIGNIN_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_SIGNIN_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_SIGNIN_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + dirs := strings.Split(string(ev.Kv.Key), "/") + n := len(dirs) + if n > 0 { + //platform := dirs[n-1] + //delete(ActSignMgrSington.SignConfigs, platform) + } + case clientv3.EventTypePut: + //var value SignConfig + //err := json.Unmarshal(ev.Kv.Value, &value) + //if err == nil { + // ActSignMgrSington.ModifyConfig(value) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_SIGNIN_PREFIX, err) + //} + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载平台任务活动配置 +func (this *EtcdMgr) InitPlatformActTask() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_TASK_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_TASK_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var taskConfig APITaskConfigs + // err = json.Unmarshal(res.Kvs[i].Value, &taskConfig) + // if err == nil { + // gtpf := &PlatformTaskConfig{ + // ConfigByTaskId: make(map[int32]TaskConfig), + // StartAct: taskConfig.StartAct, + // Platform: taskConfig.Platform, + // ConfigVer: taskConfig.ConfigVer, + // } + // ActTaskMgrSington.ConfigByPlatform[gtpf.Platform] = gtpf + // for _, config := range taskConfig.Datas { + // if config.StartAct > 0 { + // gtpf.ConfigByTaskId[config.Id] = config + // } + // } + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TASK_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TASK_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_TASK_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var taskConfig APITaskConfigs + //err := json.Unmarshal(ev.Kv.Value, &taskConfig) + //if err == nil { + // gtpf := PlatformTaskConfig{ + // ConfigByTaskId: make(map[int32]TaskConfig), + // StartAct: taskConfig.StartAct, + // Platform: taskConfig.Platform, + // ConfigVer: taskConfig.ConfigVer, + // } + // gtpf.ConfigByTaskId = make(map[int32]TaskConfig) + // for _, v := range taskConfig.Datas { + // gtpf.ConfigByTaskId[v.Id] = v + // } + // ActTaskMgrSington.ModifyConfig(gtpf) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TASK_PREFIX, err) + //} + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载平台财神任务活动配置 +func (this *EtcdMgr) InitPlatformActGoldTask() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_GOLDTASK_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_GOLDTASK_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var taskConfig APIGoldTaskConfigs + // err = json.Unmarshal(res.Kvs[i].Value, &taskConfig) + // if err == nil { + // gtpf := &GoldTaskPlateformConfig{ + // ConfigByTaskId: make(map[int32]GoldTaskConfig), + // StartAct: taskConfig.StartAct, + // Platform: taskConfig.Platform, + // ConfigVer: taskConfig.ConfigVer, + // } + // ActGoldTaskMgrSington.ConfigByPlateform[gtpf.Platform] = gtpf + // for _, config := range taskConfig.Datas { + // if config.StartAct > 0 { + // ActGoldTaskMgrSington.AddConfig(config, taskConfig.Platform, false) + // } + // } + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDTASK_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDTASK_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_GOLDTASK_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var taskConfig APIGoldTaskConfigs + //err := json.Unmarshal(ev.Kv.Value, &taskConfig) + //if err == nil { + // gtpf := GoldTaskPlateformConfig{ + // ConfigByTaskId: make(map[int32]GoldTaskConfig), + // StartAct: taskConfig.StartAct, + // Platform: taskConfig.Platform, + // ConfigVer: taskConfig.ConfigVer, + // } + // gtpf.ConfigByTaskId = make(map[int32]GoldTaskConfig) + // for _, v := range taskConfig.Datas { + // gtpf.ConfigByTaskId[v.TaskId] = v + // } + // ActGoldTaskMgrSington.ModifyConfig(gtpf) + // if curPlateformConfig, exist := ActGoldTaskMgrSington.ConfigByPlateform[gtpf.Platform]; exist { + // //移除task + // for curTaskId, curTaskConfig := range curPlateformConfig.ConfigByTaskId { + // bFindTask := false + // if _, exist := gtpf.ConfigByTaskId[curTaskId]; exist { + // bFindTask = true + // } + // if !bFindTask { + // ActGoldTaskMgrSington.RemoveConfig(curTaskConfig.TaskId, gtpf.Platform, true) + // } + // } + // } + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDTASK_PREFIX, err) + //} + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载平台财神降临活动配置 +func (this *EtcdMgr) InitPlatformActGoldCome() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_GOLDCOME_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var taskConfig APIGoldComeConfig + // err = json.Unmarshal(res.Kvs[i].Value, &taskConfig) + // if err == nil { + // gcpf := &GoldComePlateformConfig{ + // ConfigByTaskId: make(map[int32]GoldComeConfig), + // StartAct: taskConfig.StartAct, + // Platform: taskConfig.Platform, + // ConfigVer: taskConfig.ConfigVer, + // RobotType: taskConfig.RobotType, + // MinPlayerCount: taskConfig.MinPlayerCount, + // } + // ActGoldComeMgrSington.ConfigByPlateform[gcpf.Platform] = gcpf + // for _, config := range taskConfig.Datas { + // if config.StartAct > 0 { + // ActGoldComeMgrSington.AddConfig(config, taskConfig.Platform, false) + // } + // } + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var taskConfig APIGoldComeConfig + //err := json.Unmarshal(ev.Kv.Value, &taskConfig) + //if err == nil { + // //1, 修改成最新配置 + // var config GoldComePlateformConfig + // config.Platform = taskConfig.Platform + // config.ConfigVer = taskConfig.ConfigVer + // config.StartAct = taskConfig.StartAct + // config.RobotType = taskConfig.RobotType + // config.MinPlayerCount = taskConfig.MinPlayerCount + // config.ConfigByTaskId = make(map[int32]GoldComeConfig) + // for _, v := range taskConfig.Datas { + // config.ConfigByTaskId[v.TaskId] = v + // } + // ActGoldComeMgrSington.ModifyConfig(config) + // + // if curPlateformConfig, exist := ActGoldTaskMgrSington.ConfigByPlateform[taskConfig.Platform]; exist { + // //移除task + // for curTaskId, curTaskConfig := range curPlateformConfig.ConfigByTaskId { + // bFindTask := false + // if _, exist := config.ConfigByTaskId[curTaskId]; exist { + // bFindTask = true + // } + // if !bFindTask { + // ActGoldComeMgrSington.RemoveConfig(curTaskConfig.TaskId, config.Platform, true) + // } + // } + // } + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, err) + //} + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载平台在线奖励活动配置 +func (this *EtcdMgr) InitPlatformActOnlineReward() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var value PlatformOnlineRewardConfig + // err = json.Unmarshal(res.Kvs[i].Value, &value) + // if err == nil { + // ActOnlineRewardMgrSington.Configs[value.Platform] = value + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var value PlatformOnlineRewardConfig + //err := json.Unmarshal(ev.Kv.Value, &value) + //if err == nil { + // ActOnlineRewardMgrSington.ModifyConfig(value) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_ONLINEREWARD_PREFIX, err) + //} + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载平台转盘活动配置 +func (this *EtcdMgr) InitPlatformActLuckyTurntable() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var value PlatformLuckyTurntableConfig + // err = json.Unmarshal(res.Kvs[i].Value, &value) + // if err == nil { + // ActLuckyTurntableMgrSington.AdjustForBackgroundConfig(value) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var value PlatformLuckyTurntableConfig + //err := json.Unmarshal(ev.Kv.Value, &value) + //if err == nil { + // ActLuckyTurntableMgrSington.ModifyConfig(value) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_LUCKLYTURNTABLE_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载余额宝配置 +func (this *EtcdMgr) InitPlatformActYeb() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_YEB_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_YEB_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var value YebConfig + // err = json.Unmarshal(res.Kvs[i].Value, &value) + // if err == nil { + // ActYebMgrSington.AdjustForBackgroundConfig(value, true, nil) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_YEB_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_YEB_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_YEB_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var cfg YebConfig + //err := json.Unmarshal(ev.Kv.Value, &cfg) + //if err == nil { + // ActYebMgrSington.ModifyConfig(cfg) + // if cfg.StartAct <= 0 { + // type tempObject struct { + // newMsgs []*model.Message + // playerDatas []model.PlayerData + // } + // + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // tmpObj := &tempObject{ + // newMsgs: make([]*model.Message, 0), + // playerDatas: make([]model.PlayerData, 0), + // } + // + // players := model.GetYebPlayersDatas(cfg.Platform) + // for _, p := range players { + // pp := PlayerMgrSington.GetPlayerBySnId(p.SnId) + // if pp == nil { + // // 计算离线用户利息 + // ActYebMgrSington.CalcYebInterest(nil, &p) + // } + // + // var otherParams []int32 + // strTitle := i18n.Tr("cn", "YebTurnOffTitle") + // strContent := i18n.Tr("cn", "YebTurnOffContent", float64(p.YebData.Balance)/100) + // newMsg := model.NewMessage("", int32(0), p.SnId, int32(model.MSGTYPE_YEB), strTitle, strContent, p.YebData.Balance, + // model.MSGSTATE_UNREAD, time.Now().Unix(), model.MSGATTACHSTATE_DEFAULT, "", otherParams, cfg.Platform) + // err := model.InsertMessage(newMsg) + // if err != nil { + // logger.Logger.Errorf("UpdateYebConfig model.InsertMessage snid %v err %v", p.SnId, err) + // continue + // } + // + // tmpObj.newMsgs = append(tmpObj.newMsgs, newMsg) + // tmpObj.playerDatas = append(tmpObj.playerDatas, p) + // + // err = model.ResetYeb(p.SnId, "") + // if err != nil { + // logger.Logger.Errorf("UpdateYebConfig err %v", err) + // continue + // } + // + // if pp != nil { + // pp.YebData.TotalIncome = 0 + // pp.YebData.PrevIncome = 0 + // pp.YebData.Balance = 0 + // pp.YebData.InterestTs = 0 + // } + // } + // + // model.RemoveActYebLog(cfg.Platform) + // return tmpObj + // }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { + // if obj, ok := data.(*tempObject); ok { + // for i, p := range obj.playerDatas { + // pp := PlayerMgrSington.GetPlayerBySnId(p.SnId) + // if pp != nil { + // pp.AddMessage(obj.newMsgs[i]) + // } + // } + // } else { + // logger.Logger.Error("UpdateYebConfig task result data covert failed.") + // } + // }), "UpdateYebConfig").Start() + // } + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_YEB_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 初始化返利数据 +func (this *EtcdMgr) InitRebateConfig() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 初始化返利数据 拉取数据:", etcd.ETCDKEY_CONFIG_REBATE) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_CONFIG_REBATE) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var RebateTasks *RebateTask + err = json.Unmarshal(res.Kvs[i].Value, &RebateTasks) + if err == nil { + RebateInfoMgrSington.rebateTask[RebateTasks.Platform] = RebateTasks + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_REBATE, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_REBATE, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_CONFIG_REBATE, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var RebateTasks *RebateTask + err := json.Unmarshal(ev.Kv.Value, &RebateTasks) + if err == nil { + RebateInfoMgrSington.rebateTask[RebateTasks.Platform] = RebateTasks + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_REBATE, err) + } + } + } + return nil + }) + } + this.InitAndWatch(initFunc, watchFunc) +} + +// 初始化代理数据 +func (this *EtcdMgr) InitPromoterConfig() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 初始化代理数据 拉取数据:", etcd.ETCDKEY_PROMOTER_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_PROMOTER_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var promoterConfig *PromoterConfig + err = json.Unmarshal(res.Kvs[i].Value, &promoterConfig) + if err == nil { + PromoterMgrSington.AddConfig(promoterConfig) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PROMOTER_PREFIX, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_PROMOTER_PREFIX, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_PROMOTER_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + dirs := strings.Split(string(ev.Kv.Key), "/") + n := len(dirs) + if n > 0 { + promoterConfig := dirs[n-1] + PromoterMgrSington.RemoveConfigByKey(promoterConfig) + } + /* + var promoterConfig *PromoterConfig + err := json.Unmarshal(ev.Kv.Value, &promoterConfig) + if err == nil { + PromoterMgrSington.RemoveConfigByKey(promoterConfig) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PROMOTER_PREFIX, err) + } + */ + case clientv3.EventTypePut: + var promoterConfig *PromoterConfig + err := json.Unmarshal(ev.Kv.Value, &promoterConfig) + if err == nil { + PromoterMgrSington.AddConfig(promoterConfig) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PROMOTER_PREFIX, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 初始化微信分享彩金配置 +func (this *EtcdMgr) InitWeiXinShare() { + initFunc := func() int64 { + if !model.GameParamData.UseEtcd { + return 0 + } + logger.Logger.Info("ETCD 初始化分享彩金配置 拉取数据:", etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX) + //if err != nil { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX, err) + // return + //} + //for i := int64(0); i < res.Count; i++ { + // var cfg ShareConfig + // if err = json.Unmarshal(res.Kvs[i].Value, &cfg); err != nil { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX, err) + // continue + // } + // weiXinShareMgr.AddConfig(&cfg) + //} + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + //dirs := strings.Split(string(ev.Kv.Key), "/") + //if len(dirs) > 1 { + // weiXinShareMgr.RemoveConfig(dirs[len(dirs)-1]) + //} + + case clientv3.EventTypePut: + //var cfg ShareConfig + //err := json.Unmarshal(ev.Kv.Value, &cfg) + //if err == nil { + // weiXinShareMgr.AddConfig(&cfg) + // weiXinShareMgr.BroadcastMessageToPlatform(cfg.Platform, cfg.Status) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_WEIXIN_SHARE_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载vip活动配置 +func (this *EtcdMgr) InitPlatformActVip() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_VIP_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_VIP_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var vipConfig ActVipPlateformConfig + // err = json.Unmarshal(res.Kvs[i].Value, &vipConfig) + // if err == nil { + // ActVipMgrSington.AddConfig(&vipConfig, vipConfig.Platform) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_VIP_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_VIP_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_VIP_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var vipConfig ActVipPlateformConfig + //err := json.Unmarshal(ev.Kv.Value, &vipConfig) + //if err == nil { + // ActVipMgrSington.AddConfig(&vipConfig, vipConfig.Platform) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载活动give配置 +func (this *EtcdMgr) InitPlatformAct() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_GIVE_PREFIX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_GIVE_PREFIX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var vipConfig ActGivePlateformConfig + err = json.Unmarshal(res.Kvs[i].Value, &vipConfig) + if err == nil { + ActMgrSington.AddGiveConfig(&vipConfig, vipConfig.Platform) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GIVE_PREFIX, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GIVE_PREFIX, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_GIVE_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var vipConfig ActGivePlateformConfig + err := json.Unmarshal(ev.Kv.Value, &vipConfig) + if err == nil { + ActMgrSington.AddGiveConfig(&vipConfig, vipConfig.Platform) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_GOLDCOME_PREFIX, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +//加载活动give配置 +//func (this *EtcdMgr) InitPlatformPayAct() { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_PAY_PREFIX) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_PAY_PREFIX) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// var vipConfig PayActPlateformConfig +// err = json.Unmarshal(res.Kvs[i].Value, &vipConfig) +// if err == nil { +// PayActMgrSington.AddPayConfig(&vipConfig, vipConfig.Platform) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_PAY_PREFIX, err) +// } +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_PAY_PREFIX, err) +// } +// } +// +// // 监控数据变动 +// this.GoWatch(etcd.ETCDKEY_ACT_PAY_PREFIX, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// case clientv3.EventTypePut: +// var vipConfig PayActPlateformConfig +// err := json.Unmarshal(ev.Kv.Value, &vipConfig) +// if err == nil { +// PayActMgrSington.AddPayConfig(&vipConfig, vipConfig.Platform) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_PAY_PREFIX, err) +// } +// } +// } +// return nil +// }) +//} + +func (this *EtcdMgr) InitActLoginRandCoinConfig() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 红包数据 拉取数据:", etcd.ETCDKEY_ACT_RANDCOIN_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_RANDCOIN_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var actConfig = &ActConfig{} + // err = json.Unmarshal(res.Kvs[i].Value, actConfig) + // if err != nil { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_RANDCOIN_PREFIX, err) + // continue + // } + // actRandCoinMgr.SetConfig(actConfig) + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_RANDCOIN_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_RANDCOIN_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypePut: + //var actConfig = &ActConfig{} + //err := json.Unmarshal(ev.Kv.Value, &actConfig) + //if err == nil { + // actRandCoinMgr.SetConfig(actConfig) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_RANDCOIN_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载活动fpay配置 +func (this *EtcdMgr) InitPlatformActFPay() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_FPAY_PREFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_FPAY_PREFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var vipConfig ActFPayPlateformConfig + // err = json.Unmarshal(res.Kvs[i].Value, &vipConfig) + // if err == nil { + // ActFPayMgrSington.AddConfig(&vipConfig, vipConfig.Platform) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_FPAY_PREFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_FPAY_PREFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_FPAY_PREFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var vipConfig ActFPayPlateformConfig + //err := json.Unmarshal(ev.Kv.Value, &vipConfig) + //if err == nil { + // ActFPayMgrSington.AddConfig(&vipConfig, vipConfig.Platform) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_FPAY_PREFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +////加载杀率配置 +//func (this *EtcdMgr) InitPlatformProfitControl() { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_PLATFORM_PROFITCONTROL) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_PLATFORM_PROFITCONTROL) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// var cfg ProfitControlConfig +// err = json.Unmarshal(res.Kvs[i].Value, &cfg) +// if err == nil { +// ProfitControlMgrSington.UpdateConfig(&cfg, true) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PLATFORM_PROFITCONTROL, err) +// } +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_PLATFORM_PROFITCONTROL, err) +// } +// } +// +// // 监控数据变动 +// this.GoWatch(etcd.ETCDKEY_PLATFORM_PROFITCONTROL, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// case clientv3.EventTypePut: +// var cfg ProfitControlConfig +// err := json.Unmarshal(ev.Kv.Value, &cfg) +// if err == nil { +// ProfitControlMgrSington.UpdateConfig(&cfg, false) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PLATFORM_PROFITCONTROL, err) +// } +// } +// } +// return nil +// }) +//} + +// 加载比赛配置 +//func (this *EtcdMgr) InitMatchConfig() { +// initFunc := func() int64 { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_MATCH_PROFIX) +// //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_MATCH_PROFIX) +// //if err == nil { +// // for i := int64(0); i < res.Count; i++ { +// // var cfg MatchConfig +// // err = json.Unmarshal(res.Kvs[i].Value, &cfg) +// // if err == nil { +// // MatchMgrSington.UpdateConfig(&cfg, true) +// // } else { +// // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) +// // } +// // } +// //} else { +// // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) +// //} +// } +// return -1 +// } +// +// // 监控数据变动 +// watchFunc := func(ctx context.Context, revision int64) { +// this.GoWatch(ctx, revision, etcd.ETCDKEY_MATCH_PROFIX, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// case clientv3.EventTypePut: +// //var cfg MatchConfig +// //err := json.Unmarshal(ev.Kv.Value, &cfg) +// //if err == nil { +// // MatchMgrSington.UpdateConfig(&cfg, false) +// //} else { +// // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) +// //} +// } +// } +// return nil +// }) +// } +// +// this.InitAndWatch(initFunc, watchFunc) +//} + +// 加载比赛券活动配置 +func (this *EtcdMgr) InitActTicketConfig() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_TICKET_PROFIX) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_TICKET_PROFIX) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var cfg ActTicketConfig + // err = json.Unmarshal(res.Kvs[i].Value, &cfg) + // if err == nil { + // ActTicketMgrSington.UpdateConfig(&cfg, true) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TICKET_PROFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TICKET_PROFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_TICKET_PROFIX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var cfg ActTicketConfig + //err := json.Unmarshal(ev.Kv.Value, &cfg) + //if err == nil { + // ActTicketMgrSington.UpdateConfig(&cfg, false) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TICKET_PROFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载积分商城配置 +func (this *EtcdMgr) InitGradeShopConfig() { + initFunc := func() int64 { + + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_MATCH_GRADESHOP) + //res, err := this.GetValueWithPrefix(etcd.ETCDKEY_MATCH_GRADESHOP) + //if err == nil { + // for i := int64(0); i < res.Count; i++ { + // var cfg GradeShopConfig + // err = json.Unmarshal(res.Kvs[i].Value, &cfg) + // if err == nil { + // GradeShopMgrSington.UpdateConfig(&cfg, true) + // } else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) + // } + // } + //} else { + // logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) + //} + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_MATCH_GRADESHOP, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + //var cfg GradeShopConfig + //err := json.Unmarshal(ev.Kv.Value, &cfg) + //if err == nil { + // GradeShopMgrSington.UpdateConfig(&cfg, false) + //} else { + // logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_MATCH_PROFIX, err) + //} + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +//// 加载用户分层配置 +//func (this *EtcdMgr) InitLogicLevelConfig() { +// +// initFunc := func() int64 { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_CONFIG_LOGICLEVEL) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_CONFIG_LOGICLEVEL) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// var cfg LogicLevelConfig +// err = json.Unmarshal(res.Kvs[i].Value, &cfg) +// if err == nil { +// LogicLevelMgrSington.UpdateConfig(&cfg) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_LOGICLEVEL, err) +// } +// } +// if res.Header != nil { +// return res.Header.Revision +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_LOGICLEVEL, err) +// } +// } +// return -1 +// } +// +// // 监控数据变动 +// watchFunc := func(ctx context.Context, revision int64) { +// this.GoWatch(ctx, revision, etcd.ETCDKEY_CONFIG_LOGICLEVEL, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// case clientv3.EventTypePut: +// var cfg LogicLevelConfig +// err := json.Unmarshal(ev.Kv.Value, &cfg) +// if err == nil { +// LogicLevelMgrSington.UpdateConfig(&cfg) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_CONFIG_LOGICLEVEL, err) +// } +// } +// } +// return nil +// }) +// } +// +// this.InitAndWatch(initFunc, watchFunc) +//} + +// 加载全局游戏开关 +func (this *EtcdMgr) InitGameGlobalStatus() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_GAME_CONFIG_GLOBAL) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_GAME_CONFIG_GLOBAL) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var cfg webapi_proto.GameConfigGlobal + err = proto.Unmarshal(res.Kvs[i].Value, &cfg) + if err == nil { + for _, v := range cfg.GetGameStatus() { + gameId := v.GetGameId() + status := v.GetStatus() + PlatformMgrSingleton.GameStatus[gameId] = status + } + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_CONFIG_GLOBAL, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_CONFIG_GLOBAL, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_GAME_CONFIG_GLOBAL, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var cfg webapi_proto.GameConfigGlobal + err := proto.Unmarshal(ev.Kv.Value, &cfg) + if err == nil { + cfgs := make([]*hall_proto.GameConfig1, 0) + for _, v := range cfg.GetGameStatus() { + gameId := v.GetGameId() // gamefreeid + status := v.GetStatus() + + if PlatformMgrSingleton.GameStatus[gameId] != status { + cfgs = append(cfgs, &hall_proto.GameConfig1{ + LogicId: gameId, + Status: status, + }) + } + PlatformMgrSingleton.GameStatus[gameId] = status + } + PlatformMgrSingleton.ChangeGameStatus(cfgs) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_CONFIG_GLOBAL, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 界面入口开关 +func (this *EtcdMgr) InitEntrySwitch() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_PACKAGE_ENTRYSWITCH) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_PACKAGE_ENTRYSWITCH) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var cfg webapi_proto.EntrySwitch + err = proto.Unmarshal(res.Kvs[i].Value, &cfg) + if err == nil { + PlatformMgrSingleton.UpdateEntrySwitch(&cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_PACKAGE_ENTRYSWITCH, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_PACKAGE_ENTRYSWITCH, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_PACKAGE_ENTRYSWITCH, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var config webapi_proto.EntrySwitch + err := proto.Unmarshal(ev.Kv.Value, &config) + if err == nil { + PlatformMgrSingleton.UpdateEntrySwitch(&config) + PlatformMgrSingleton.ChangeEntrySwitch(config.Platform, &config) + } else { + logger.Logger.Errorf("etcd read(%v) proto.Unmarshal err:%v", string(ev.Kv.Key), err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载兑换 +func (this *EtcdMgr) InitExchangeShop() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_SHOP_EXCHANGE) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_SHOP_EXCHANGE) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.ExchangeShopList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil { + ShopMgrSington.UpExShop(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_SHOP_EXCHANGE, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_SHOP_EXCHANGE, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_SHOP_EXCHANGE, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.ExchangeShopList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil { + ShopMgrSington.UpExShop(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_SHOP_EXCHANGE, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载公告 +func (this *EtcdMgr) InitCommonNotice() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_GAME_NOTICE) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_GAME_NOTICE) + if err == nil { + for i := int64(0); i < res.Count; i++ { + + var cfgs webapi_proto.CommonNoticeList + err = proto.Unmarshal(res.Kvs[i].Value, &cfgs) + + if err == nil && len(cfgs.List) != 0 { + //now := time.Now().Unix() + //for i := 0; i < len(cfgs.List); { + // if cfgs.List[i].GetEndTime() < now || cfgs.List[i].GetStartTime() > now { + // cfgs.List = append(cfgs.List[:i], cfgs.List[i+1:]...) + // } else { + // i++ + // } + //} + + PlatformMgrSingleton.UpdateCommonNotices(&cfgs) + + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_NOTICE, err) + } + + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_GAME_NOTICE, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var cfgs webapi_proto.CommonNoticeList + err := proto.Unmarshal(ev.Kv.Value, &cfgs) + + if err == nil { + + //now := time.Now().Unix() + //for i := 0; i < len(cfgs.List); { + // if cfgs.List[i].GetEndTime() < now || cfgs.List[i].GetStartTime() > now { + // cfgs.List = append(cfgs.List[:i], cfgs.List[i+1:]...) + // } else { + // i++ + // } + //} + + PlatformMgrSingleton.UpdateCommonNotices(&cfgs) + // 通知公共变更 + for _, v := range PlayerMgrSington.playerOfPlatform[cfgs.Platform] { + if v != nil && v.IsOnLine() { + v.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_NoticeChange), &hall_proto.SCNoticeChange{}) + } + } + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_NOTICE, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// InitGameMatchDate 加载比赛配置 +func (this *EtcdMgr) InitGameMatchDate() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_GAME_MATCH) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_GAME_MATCH) + if err == nil { + for i := int64(0); i < res.Count; i++ { + var cfgs webapi_proto.GameMatchDateList + err = proto.Unmarshal(res.Kvs[i].Value, &cfgs) + if err == nil && len(cfgs.List) != 0 { + TournamentMgr.UpdateData(true, &cfgs) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_MATCH, err) + } + + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_GAME_MATCH, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + var cfgs webapi_proto.GameMatchDateList + err := proto.Unmarshal(ev.Kv.Value, &cfgs) + if err == nil && len(cfgs.List) != 0 { + TournamentMgr.UpdateData(false, &cfgs) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) panic:%v", etcd.ETCDKEY_GAME_MATCH, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载七日签到 +func (this *EtcdMgr) InitSign7() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_7SIGN) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_7SIGN) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.Welfare7SignDateList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateSign7(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_7SIGN, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_7SIGN, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_7SIGN, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.Welfare7SignDateList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateSign7(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_7SIGN, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载轮盘 +func (this *EtcdMgr) InitTurnplate() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_TURNPLATE) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_TURNPLATE) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.WelfareTurnplateDateList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateTurnplate(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_TURNPLATE, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_TURNPLATE, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_TURNPLATE, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.WelfareTurnplateDateList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateTurnplate(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_TURNPLATE, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载盲盒 +func (this *EtcdMgr) InitBlindBox() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_BLINDBOX) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_BLINDBOX) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.WelfareBlindBoxDataList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateBlindBox(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_BLINDBOX, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_BLINDBOX, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_BLINDBOX, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.WelfareBlindBoxDataList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateBlindBox(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_BLINDBOX, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载首充 +func (this *EtcdMgr) InitFirstPay() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_FIRSTPAY) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_FIRSTPAY) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.WelfareFirstPayDataList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateFirstPay(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_FIRSTPAY, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_FIRSTPAY, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_FIRSTPAY, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.WelfareFirstPayDataList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateFirstPay(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_FIRSTPAY, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载连续充值 +func (this *EtcdMgr) InitContinuousPay() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_CONTINUOUSPAY) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_CONTINUOUSPAY) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.WelfareContinuousPayDataList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateContinuousPay(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_CONTINUOUSPAY, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_CONTINUOUSPAY, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_CONTINUOUSPAY, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.WelfareContinuousPayDataList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdateContinuousPay(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_CONTINUOUSPAY, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载VIP +func (this *EtcdMgr) InitUpdateVIPcfg() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_VIP_CFG) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_VIP_CFG) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.VIPcfgDataList{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + VipMgrSington.UpdateVIPcfg(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_VIP_CFG, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_VIP_CFG, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_VIP_CFG, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.VIPcfgDataList{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + VipMgrSington.UpdateVIPcfg(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_VIP_CFG, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载段位配置 +func (this *EtcdMgr) InitUpdateChessRankcfg() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_CHESSRANK_CFG) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_CHESSRANK_CFG) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.ChessRankcfgData{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + ChessRankMgrSington.UpdateChessRankConfig(cfg) + logger.Logger.Tracef("ChessRankConfig %v", cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_CHESSRANK_CFG, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_CHESSRANK_CFG, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_CHESSRANK_CFG, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.ChessRankcfgData{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + ChessRankMgrSington.UpdateChessRankConfig(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_CHESSRANK_CFG, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载抽手机活动 +func (this *EtcdMgr) InitPhoneLottery() { + initFunc := func() int64 { + if model.GameParamData.UseEtcd { + logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_ACT_PHONELOTTERY) + res, err := this.GetValueWithPrefix(etcd.ETCDKEY_ACT_PHONELOTTERY) + if err == nil { + for i := int64(0); i < res.Count; i++ { + cfg := &webapi_proto.WelfarePhoneLotteryStatus{} + //msg := &webapi.ASSrvCtrlClose{} + err = proto.Unmarshal(res.Kvs[i].Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdatePhoneLotteryStatus(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_PHONELOTTERY, cfg.Platform, err) + } + } + if res.Header != nil { + return res.Header.Revision + } + } else { + logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_ACT_PHONELOTTERY, err) + } + } + return -1 + } + + // 监控数据变动 + watchFunc := func(ctx context.Context, revision int64) { + this.GoWatch(ctx, revision, etcd.ETCDKEY_ACT_PHONELOTTERY, func(res clientv3.WatchResponse) error { + for _, ev := range res.Events { + switch ev.Type { + case clientv3.EventTypeDelete: + case clientv3.EventTypePut: + cfg := &webapi_proto.WelfarePhoneLotteryStatus{} + err := proto.Unmarshal(ev.Kv.Value, cfg) + if err == nil && cfg.Platform != "" { + WelfareMgrSington.UpdatePhoneLotteryStatus(cfg) + } else { + logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_ACT_PHONELOTTERY, cfg.Platform, err) + } + } + } + return nil + }) + } + + this.InitAndWatch(initFunc, watchFunc) +} + +// 加载 调控黑白名单 +//func (this *EtcdMgr) InitWBCtrlCFG() { +// initFunc := func() int64 { +// if model.GameParamData.UseEtcd { +// logger.Logger.Info("ETCD 拉取数据:", etcd.ETCDKEY_WBCtrl_CFG) +// res, err := this.GetValueWithPrefix(etcd.ETCDKEY_WBCtrl_CFG) +// if err == nil { +// for i := int64(0); i < res.Count; i++ { +// cfg := &webapi_proto.WbCtrlCfg{} +// err = proto.Unmarshal(res.Kvs[i].Value, cfg) +// if err == nil && cfg.Platform != "" { +// WBCtrlCfgMgr.UpdateConfig(cfg) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_WBCtrl_CFG, cfg.Platform, err) +// } +// } +// if res.Header != nil { +// return res.Header.Revision +// } +// } else { +// logger.Logger.Errorf("etcd get WithPrefix(%v) panic:%v", etcd.ETCDKEY_WBCtrl_CFG, err) +// } +// } +// return -1 +// } +// +// // 监控数据变动 +// watchFunc := func(ctx context.Context, revision int64) { +// this.GoWatch(ctx, revision, etcd.ETCDKEY_WBCtrl_CFG, func(res clientv3.WatchResponse) error { +// for _, ev := range res.Events { +// switch ev.Type { +// case clientv3.EventTypeDelete: +// case clientv3.EventTypePut: +// cfg := &webapi_proto.WbCtrlCfg{} +// err := proto.Unmarshal(ev.Kv.Value, cfg) +// if err == nil && cfg.Platform != "" { +// WBCtrlCfgMgr.UpdateConfig(cfg) +// } else { +// logger.Logger.Errorf("etcd desc WithPrefix(%v) Platform %v panic:%v", etcd.ETCDKEY_WBCtrl_CFG, cfg.Platform, err) +// } +// } +// } +// return nil +// }) +// } +// +// this.InitAndWatch(initFunc, watchFunc) +//} + +func (this *EtcdMgr) Init() { + if model.GameParamData.UseEtcd { + logger.Logger.Infof("EtcdClient开始连接url:%v;etcduser:%v;etcdpwd:%v", common.CustomConfig.GetStrings("etcdurl"), common.CustomConfig.GetString("etcduser"), common.CustomConfig.GetString("etcdpwd")) + err := this.Open(common.CustomConfig.GetStrings("etcdurl"), common.CustomConfig.GetString("etcduser"), common.CustomConfig.GetString("etcdpwd"), time.Minute) + if err != nil { + logger.Logger.Tracef("EtcdMgr.Open err:%v", err) + return + } + } +} + +func (this *EtcdMgr) Shutdown() { + this.Close() +} + +func (this *EtcdMgr) Reset() { + this.Close() + this.Init() + this.ReInitAndWatchAll() +} diff --git a/worldsrv/fishJacklistmgr.go b/worldsrv/fishJacklistmgr.go new file mode 100644 index 0000000..7fa9604 --- /dev/null +++ b/worldsrv/fishJacklistmgr.go @@ -0,0 +1,112 @@ +package main + +import ( + "time" + + "mongo.games.com/game/model" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" +) + +var FishJackpotCoinMgr = &FishJackpotCoin{ + Jackpot: make(map[string]int64), +} + +type FishJackpotCoin struct { + Jackpot map[string]int64 //捕鱼奖池 +} + +type FishJackListManager struct { + FishJackList map[string][]*FishJackInfo +} + +type FishJackInfo struct { + Ts int64 + Name string + JackpotCoin int64 + JackpotType int32 +} + +var FishJackListMgr = &FishJackListManager{ + FishJackList: make(map[string][]*FishJackInfo), +} + +func (this *FishJackListManager) ModuleName() string { + return "FishJackListManager" +} + +func (this *FishJackListManager) Init() { + //data := model. +} + +func (this *FishJackListManager) Update() { +} + +func (this *FishJackListManager) Shutdown() { + module.UnregisteModule(this) +} + +func (this *FishJackListManager) InitJackInfo(platform string) { + + datas, err := model.GetFishJackpotLogByPlatform(platform) // 必须要先得到 不能走协程 + if err == nil { + for i, v := range datas { + if i == model.FishJackpotLogMaxLimitPerQuery { + break + } + data := &FishJackInfo{ + Ts: v.Ts, + Name: v.Name, + JackpotCoin: v.Coin, + JackpotType: v.JackType, + } + this.FishJackList[platform] = append(this.FishJackList[platform], data) + } + } +} + +func (this *FishJackListManager) GetJackInfo(platform string) []*FishJackInfo { + if _, exist := this.FishJackList[platform]; !exist { + this.InitJackInfo(platform) + } + return this.FishJackList[platform] +} + +func (this *FishJackListManager) Insert(coin int64, snid, roomid, jackType, inGame int32, platform, channel, name string) { + + log := model.NewFishJackpotLogEx(snid, coin, roomid, jackType, inGame, platform, channel, name) + this.InsertLog(log) + if _, exist := this.FishJackList[platform]; !exist /*|| len(datas) == 0 */ { + this.InitJackInfo(platform) + } + data := &FishJackInfo{ + Ts: log.Ts, + Name: log.Name, + JackpotCoin: log.Coin, + JackpotType: log.JackType, + } + d1 := append([]*FishJackInfo{}, this.FishJackList[platform][0:]...) + this.FishJackList[platform] = append(this.FishJackList[platform][:0], data) + this.FishJackList[platform] = append(this.FishJackList[platform], d1...) + if len(this.FishJackList[platform]) > model.FishJackpotLogMaxLimitPerQuery { + this.FishJackList[platform] = this.FishJackList[platform][:model.FishJackpotLogMaxLimitPerQuery] + } + //logger.Logger.Info("FishJackListManager Insert ", d1, this.FishJackList[platform]) +} + +func (this *FishJackListManager) InsertLog(log *model.FishJackpotLog) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertSignleFishJackpotLog(log) + if err != nil { + logger.Logger.Error("FishJackListManager Insert ", err) + } + return err + }), nil, "InsertFishJack").Start() +} + +func init() { + module.RegisteModule(FishJackListMgr, time.Hour, 0) +} diff --git a/worldsrv/friendmgr.go b/worldsrv/friendmgr.go new file mode 100644 index 0000000..618418e --- /dev/null +++ b/worldsrv/friendmgr.go @@ -0,0 +1,925 @@ +package main + +import ( + "errors" + "fmt" + "strconv" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/friend" + "mongo.games.com/game/worldsrv/internal" +) + +const ( + ListType_Friend int32 = iota // 0.好友列表 + ListType_Apply // 1.申请列表 + ListType_Recommend // 2.推荐列表 +) + +const ( + OpType_Apply int32 = iota // 0.申请 + OpType_Agree // 1.同意 + OpType_Refuse // 2.拒绝 + OpType_Delete // 3.删除 +) + +const ( + Invite_Agree int = iota // 0.同意 + Invite_Refuse // 1.拒绝 +) + +const FriendMaxNum = 200 +const ShieldMaxNum = 10000 +const FriendApplyMaxNum = 1000 //好友申请上限 +const FriendWrite = "FriendWrite" + +var FriendMgrSington = &FriendMgr{ + FriendList: make(map[string]map[int32]*model.Friend), + TsInviteCd: make(map[string]map[string]int64), +} + +type FriendMgr struct { + FriendList map[string]map[int32]*model.Friend // 平台id:snid:好友信息 + TsInviteCd map[string]map[string]int64 // 平台id:snid:邀请cd时间 +} + +func (this *FriendMgr) ModuleName() string { + return "FriendMgr" +} + +func (this *FriendMgr) Init() { +} + +// Add 缓存玩家信息 +func (this *FriendMgr) Add(platform string, snid int32) { + logger.Logger.Trace("(this *FriendMgr) Add ", snid) + if this.FriendList[platform] == nil { + this.FriendList[platform] = make(map[int32]*model.Friend) + } + if _, exist := this.FriendList[platform][snid]; !exist { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil { + this.FriendList[platform][snid] = model.NewFriend(p.Platform, p.SnId) + this.UpdateInfo(p.Platform, p.SnId) + } + } +} + +// Del 删除玩家缓存 +func (this *FriendMgr) Del(platform string, snid int32) { + if this.FriendList[platform] == nil { + return + } + if _, exist := this.FriendList[platform][snid]; exist { + delete(this.FriendList[platform], snid) + } +} + +// CanInvite 是否可以邀请,邀请冷却 +func (this *FriendMgr) CanInvite(platform string, snid, fsnid int32) bool { + if this.TsInviteCd[platform] == nil { + this.TsInviteCd[platform] = make(map[string]int64) + } + strSnid := strconv.FormatInt(int64(snid), 10) + "_" + strconv.FormatInt(int64(fsnid), 10) + if _, exist := this.TsInviteCd[platform][strSnid]; !exist { + this.TsInviteCd[platform][strSnid] = time.Now().Unix() + return true + } + if this.TsInviteCd[platform][strSnid] == 0 { + this.TsInviteCd[platform][strSnid] = time.Now().Unix() + return true + } + if time.Now().Unix()-this.TsInviteCd[platform][strSnid] > 30 { + this.TsInviteCd[platform][strSnid] = time.Now().Unix() + return true + } + return false +} + +// AgreeApply 同意好友申请 +func (this *FriendMgr) AgreeApply(platform string, snid int32, bf *model.BindFriend) friend.OpResultCode { + logger.Logger.Trace("(this *FriendMgr) AgreeApply ", snid, " BindFriend: ", bf) + if bf == nil || bf.Platform != platform { + return friend.OpResultCode_OPRC_Friend_NoPlayer + } + if this.FriendList[platform] == nil { + this.FriendList[platform] = make(map[int32]*model.Friend) + } + + // 是否为好友 + if this.IsFriend(platform, snid, bf.SnId) { + return friend.OpResultCode_OPRC_Friend_AlreadyAdd + } + + // 检查好友数量 + dflDest := this.GetFriendList(platform, snid) + if dflDest != nil { + if len(dflDest) >= FriendMaxNum { + return friend.OpResultCode_OPRC_Friend_DestFriendMax + } + } + dfl := this.GetFriendList(platform, bf.SnId) + if dfl != nil { + if len(dfl) >= FriendMaxNum { + return friend.OpResultCode_OPRC_Friend_FriendMax + } + } + + bf.CreateTime = time.Now().Unix() + this.FriendList[platform][snid].BindFriend = append(this.FriendList[platform][snid].BindFriend, bf) + this.FriendList[platform][snid].Dirty = true + return friend.OpResultCode_OPRC_Sucess +} + +// RemoveFriend 删除好友 +func (this *FriendMgr) RemoveFriend(platform string, snid, destSnid int32) bool { + logger.Logger.Trace("(this *FriendMgr) RemoveFriend ", snid, " destSnid: ", destSnid) + if this.FriendList[platform] == nil { + return false + } + if fl, exist := this.FriendList[platform][snid]; exist { + for i, f := range fl.BindFriend { + if f.SnId == destSnid { + fl.BindFriend = append(fl.BindFriend[:i], fl.BindFriend[i+1:]...) + fl.Dirty = true + return true + } + } + } + + return false +} + +func (this *FriendMgr) UpdateInfo(platform string, snid int32) { + logger.Logger.Trace("(this *FriendMgr) UpdateInfo ", snid) + if this.FriendList[platform] == nil { + return + } + + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p == nil { + return + } + + info, ok := this.FriendList[platform][snid] + if !ok { + return + } + info.Name = p.Name + info.Head = p.Head + info.HeadUrl = p.HeadUrl + info.Sex = p.Sex + info.Coin = p.Coin + info.Diamond = p.Diamond + item := BagMgrSingleton.GetItem(p.SnId, VCard) + if item != nil { + info.VCard = item.ItemNum + } + info.Roles = p.Roles.ModUnlock + info.Pets = p.Pets.ModUnlock + info.Age = p.Age + info.Signature = p.Signature + info.GameID = p.GameID + info.UpdateTime = time.Now().Unix() + info.Dirty = true + + for _, v := range info.BindFriend { + if v == nil { + continue + } + in := this.FriendList[platform][v.SnId] + if in == nil { + continue + } + for _, vv := range in.BindFriend { + if vv != nil && vv.SnId == snid { + vv.Name = p.Name + vv.Head = p.Head + vv.HeadUrl = p.HeadUrl + vv.Sex = p.Sex + vv.LogoutTime = p.LastLogoutTime.Unix() + vv.RoleId = p.Roles.ModId + in.Dirty = true + break + } + } + } +} + +// GetPlayer 获取玩家信息 +func (this *FriendMgr) GetPlayer(platform string, snid int32) *model.Friend { + if this.FriendList[platform] == nil { + return nil + } + if fi, exist := this.FriendList[platform][snid]; exist { + this.UpdateInfo(platform, snid) + return fi + } + return nil +} + +// GetFriendList 获取好友列表 +func (this *FriendMgr) GetFriendList(platform string, snid int32) []*model.BindFriend { + var allFriend []*model.BindFriend + if this.FriendList[platform] == nil { + return nil + } + if f, exist := this.FriendList[platform][snid]; exist { + if f != nil && f.BindFriend != nil { + for _, bindFriend := range f.BindFriend { + allFriend = append(allFriend, bindFriend) + } + } + } + return allFriend +} + +// IsFriend 是否好友关系 +func (this *FriendMgr) IsFriend(platform string, snid, destSnid int32) bool { + logger.Logger.Trace("(this *FriendMgr) IsFriend ", snid, " -> ", destSnid) + dfl := this.GetFriendList(platform, snid) + if dfl != nil && len(dfl) != 0 { + for _, df := range dfl { + if df.SnId == destSnid { + return true + } + } + } + return false +} + +// ApplyList 查询好友申请列表 +func (this *FriendMgr) ApplyList(platform string, snid int32) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryFriendApplyBySnid(platform, snid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if ret, ok := data.(*model.FriendApply); ok && ret != nil && ret.ApplySnids != nil { + if ret.ApplySnids != nil { + pack := &friend.SCFriendApplyData{} + for _, as := range ret.ApplySnids { + fa := &friend.FriendApply{ + Snid: proto.Int32(as.SnId), + Name: proto.String(as.Name), + CreateTs: proto.Int64(as.CreateTs), + } + pack.FriendApplys = append(pack.FriendApplys, fa) + } + if len(pack.FriendApplys) > 0 { + proto.SetDefaults(pack) + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil { + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendApplyData), pack) + logger.Logger.Trace("SCFriendApplyData: 好友申请列表 pack: ", pack) + } + } + } + } + })).StartByFixExecutor("QueryFriendApplyBySnid") +} + +// AddShield 屏蔽好友消息 +func (this *FriendMgr) AddShield(platform string, snid, ssnid int32) { + if this.FriendList[platform] == nil { + return + } + if fl, exist := this.FriendList[platform][snid]; exist { + if fl.Shield == nil { + fl.Shield = []int32{} + } + if len(fl.Shield) > ShieldMaxNum { + return + } + fl.Shield = append(fl.Shield, ssnid) + fl.Dirty = true + } +} + +// DelShield 解除消息屏蔽 +func (this *FriendMgr) DelShield(platform string, snid, ssnid int32) { + if this.FriendList[platform] == nil { + return + } + if fl, exist := this.FriendList[platform][snid]; exist { + if fl.Shield == nil { + return + } + if len(fl.Shield) == 0 { + return + } + fl.Shield = common.DelSliceInt32(fl.Shield, ssnid) + fl.Dirty = true + } +} + +// IsShield 是否屏蔽消息 +// snid的玩屏蔽ssnid的消息 +func (this *FriendMgr) IsShield(platform string, snid, ssnid int32) bool { + if this.FriendList[platform] == nil { + return false + } + if fl, exist := this.FriendList[platform][snid]; exist { + if fl.Shield == nil { + return false + } + if len(fl.Shield) == 0 { + return false + } + return common.InSliceInt32(fl.Shield, ssnid) + } + return false +} + +// UpdateLogoutTime 更新玩家下线时间 +func (this *FriendMgr) UpdateLogoutTime(platform string, snid int32) { + if this.FriendList[platform] == nil { + return + } + if fl, exist := this.FriendList[platform][snid]; exist { + fl.LogoutTime = time.Now().Unix() + fl.Dirty = true + if fl.BindFriend != nil { + for _, bf := range fl.BindFriend { + if this.FriendList[bf.Platform] != nil { + if data, ok := this.FriendList[bf.Platform][bf.SnId]; ok { + if data.BindFriend != nil { + for _, bindFriend := range data.BindFriend { + if bindFriend.SnId == snid { + bindFriend.LogoutTime = time.Now().Unix() + break + } + } + } + } + } + } + } + } +} + +func (this *FriendMgr) FriendOp(opcode int32, p *Player, destP *model.BindFriend) { + switch opcode { + case OpType_Apply: + logger.Logger.Trace("@Apply friend", p.SnId, " -> ", destP.SnId) + this.FriendApply(p, destP) + case OpType_Agree: + logger.Logger.Trace("@AgreeApply friend", p.SnId, " -> ", destP.SnId) + this.FriendAgree(p, destP) + case OpType_Refuse: + logger.Logger.Trace("@Refuse friend", p.SnId, " -> ", destP.SnId) + this.FriendRefuse(p, destP) + case OpType_Delete: + logger.Logger.Trace("@Delete friend", p.SnId, " -> ", destP.SnId) + this.FriendDelete(p, destP) + } +} + +// FriendApply 好友申请 +// 记录在数据库 +func (this *FriendMgr) FriendApply(p *Player, destP *model.BindFriend) { + SendToClick := func(retCode friend.OpResultCode, self ...bool) { + pack := &friend.SCFriendOp{ + OpCode: proto.Int32(OpType_Apply), + SnId: proto.Int32(destP.SnId), + OpRetCode: retCode, + } + if len(self) == 0 { + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } else { + destPs := PlayerMgrSington.GetPlayerBySnId(destP.SnId) + if destPs != nil && destPs.IsOnLine() { + roleId := common.DefaultRoleId + if p.Roles != nil { + roleId = int(p.Roles.ModId) + } + pack.Friend = &friend.FriendInfo{ + SnId: proto.Int32(p.SnId), + Name: proto.String(p.Name), + Sex: proto.Int32(p.Sex), + Head: proto.Int32(p.Head), + HeadUrl: proto.String(p.HeadUrl), + CreateTs: proto.Int64(time.Now().Unix()), + LogoutTs: proto.Int64(p.LastLogoutTime.Unix()), + RoleId: int32(roleId), + } + destPs.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } + } + logger.Logger.Tracef(">>FriendApply %d -> %d, %v", p.SnId, destP.SnId, pack) + } + if FriendMgrSington.IsFriend(p.Platform, p.SnId, destP.SnId) { + SendToClick(friend.OpResultCode_OPRC_Friend_AlreadyAdd) + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryFriendApplyBySnid(p.Platform, destP.SnId) + if err != nil { + return friend.OpResultCode_OPRC_Error + } + if ret != nil { + //在申请列表 + if ret.ApplySnids != nil { + if len(ret.ApplySnids) > FriendApplyMaxNum { + return friend.OpResultCode_OPRC_Friend_DestApplyFriendMax //对方好友申请已达上限 + } + for _, as := range ret.ApplySnids { + if as.SnId == p.SnId { + return friend.OpResultCode_OPRC_Friend_AlreadyApply //已经申请过好友 + } + } + } + } else { + //不在申请列表 新增 + ret = model.NewFriendApply(destP.SnId) + } + roleId := common.DefaultRoleId + if p.Roles != nil { + roleId = int(p.Roles.ModId) + } + ret.ApplySnids = append(ret.ApplySnids, &model.FriendApplySnid{ + SnId: p.SnId, + Name: p.Name, + Head: p.Head, + HeadUrl: p.HeadUrl, + CreateTs: time.Now().Unix(), + RoleId: int32(roleId), + }) + model.UpsertFriendApply(p.Platform, destP.SnId, ret) + return friend.OpResultCode_OPRC_Sucess + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + SendToClick(friend.OpResultCode_OPRC_Sucess, false) // 对方 + SendToClick(data.(friend.OpResultCode)) // 自己 + })).StartByFixExecutor(FriendWrite) +} + +// FriendAgree 同意好友申请 +func (this *FriendMgr) FriendAgree(p *Player, destP *model.BindFriend) { + SendToClick := func(retCode friend.OpResultCode, self ...bool) { + pack := &friend.SCFriendOp{ + OpCode: proto.Int32(OpType_Agree), + SnId: proto.Int32(destP.SnId), + OpRetCode: retCode, + } + if len(self) == 0 { + roleId := common.DefaultRoleId + if destP.RoleId != 0 { + roleId = int(destP.RoleId) + } + pack.Friend = &friend.FriendInfo{ + SnId: proto.Int32(destP.SnId), + Name: proto.String(destP.Name), + Sex: proto.Int32(destP.Sex), + Head: proto.Int32(destP.Head), + HeadUrl: proto.String(destP.HeadUrl), + CreateTs: proto.Int64(time.Now().Unix()), + LogoutTs: proto.Int64(destP.LogoutTime), + RoleId: int32(roleId), + } + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } else { + destPs := PlayerMgrSington.GetPlayerBySnId(destP.SnId) + if destPs != nil && destPs.IsOnLine() { + roleId := common.DefaultRoleId + if p.Roles != nil { + roleId = int(p.Roles.ModId) + } + pack.Friend = &friend.FriendInfo{ + SnId: proto.Int32(p.SnId), + Name: proto.String(p.Name), + Sex: proto.Int32(p.Sex), + Head: proto.Int32(p.Head), + HeadUrl: proto.String(p.HeadUrl), + CreateTs: proto.Int64(time.Now().Unix()), + LogoutTs: proto.Int64(p.LastLogoutTime.Unix()), + RoleId: int32(roleId), + } + destPs.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } + } + logger.Logger.Tracef(">>FriendAgree %d -> %d, %v", p.SnId, destP.SnId, pack) + } + me := FriendMgrSington.GetPlayer(p.Platform, p.SnId) + if me == nil { + SendToClick(friend.OpResultCode_OPRC_Error) + return + } + if FriendMgrSington.IsFriend(p.Platform, p.SnId, destP.SnId) { //已经是好友了 + SendToClick(friend.OpResultCode_OPRC_Friend_AlreadyAdd) + return + } + //验证自己 + if len(me.BindFriend) >= FriendMaxNum { + SendToClick(friend.OpResultCode_OPRC_Friend_FriendMax) + return + } + + destPFriend := FriendMgrSington.GetPlayer(destP.Platform, destP.SnId) + if destPFriend != nil { + if len(destPFriend.BindFriend) >= FriendMaxNum { + SendToClick(friend.OpResultCode_OPRC_Friend_DestFriendMax) + return + } + } + + var friendDB *model.Friend + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if destPFriend == nil { + // 不在线 + friendDB, err = model.QueryFriendBySnid(destP.Platform, destP.SnId) + if err != nil { + logger.Logger.Error("QueryFriendBySnid:", err, destP.SnId) + return friend.OpResultCode_OPRC_Error + } + if friendDB == nil || friendDB.SnId != destP.SnId { + friendDB = model.NewFriend(destP.Platform, destP.SnId) + } + + if len(friendDB.BindFriend) >= FriendMaxNum { + return friend.OpResultCode_OPRC_Friend_FriendMax + } + + for _, v := range friendDB.BindFriend { + if v.SnId == p.SnId { + return friend.OpResultCode_OPRC_Friend_AlreadyAdd + } + } + } + + ret, err := model.QueryFriendApplyBySnid(p.Platform, p.SnId) + if err != nil { + return friend.OpResultCode_OPRC_Error + } + //查看是否在申请列表 + if ret != nil { + if ret.ApplySnids != nil { + for i, as := range ret.ApplySnids { + if as.SnId == destP.SnId { + //在申请列表 删除 + ret.ApplySnids = append(ret.ApplySnids[:i], ret.ApplySnids[i+1:]...) + model.UpsertFriendApply(p.Platform, p.SnId, ret) + // 保存好友关系 + if friendDB != nil { + friendDB.BindFriend = append(friendDB.BindFriend, &model.BindFriend{ + SnId: p.SnId, + CreateTime: time.Now().Unix(), + }) + model.UpsertFriend(friendDB) + } + return nil + } + } + } + } + return friend.OpResultCode_OPRC_Error + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + logger.Logger.Error("FriendAgree data:", data) + SendToClick(data.(friend.OpResultCode)) + return + } + + //同意者加入到被同意者好友里 + if destPlayer := FriendMgrSington.GetPlayer(destP.Platform, destP.SnId); destPlayer != nil { + roleId := common.DefaultRoleId + if p.Roles != nil { + roleId = int(p.Roles.ModId) + } + result := FriendMgrSington.AgreeApply(destP.Platform, destP.SnId, &model.BindFriend{ + SnId: p.SnId, + CreateTime: time.Now().Unix(), + Platform: p.Platform, + Name: p.Name, + Head: p.Head, + HeadUrl: p.HeadUrl, + Sex: p.Sex, + LogoutTime: p.LastLogoutTime.Unix(), + RoleId: int32(roleId), + }) + if result != friend.OpResultCode_OPRC_Sucess && result != friend.OpResultCode_OPRC_Friend_AlreadyAdd { + logger.Logger.Warn("AgreeApply error: ", result) + SendToClick(result) + return + } + } + + // 被同意者加入到同意者好友里 + result := FriendMgrSington.AgreeApply(p.Platform, p.SnId, destP) + if result != friend.OpResultCode_OPRC_Sucess { + SendToClick(result, false) + SendToClick(result) + return + } + SendToClick(friend.OpResultCode_OPRC_Sucess, false) + SendToClick(friend.OpResultCode_OPRC_Sucess) + })).StartByFixExecutor(FriendWrite) +} + +func (this *FriendMgr) FriendRefuse(p *Player, destP *model.BindFriend) { + SendToClick := func(retCode friend.OpResultCode, self ...bool) { + pack := &friend.SCFriendOp{ + OpCode: proto.Int32(OpType_Refuse), + SnId: proto.Int32(destP.SnId), + OpRetCode: retCode, + } + if len(self) == 0 { + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } else { + destPs := PlayerMgrSington.GetPlayerBySnId(destP.SnId) + if destPs != nil && destPs.IsOnLine() { + destPs.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + } + } + logger.Logger.Tracef(">>FriendRefuse %d -> %d, %v", p.SnId, destP.SnId, pack) + } + if FriendMgrSington.IsFriend(p.Platform, p.SnId, destP.SnId) { + SendToClick(friend.OpResultCode_OPRC_Friend_AlreadyAdd) + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, _ := model.QueryFriendApplyBySnid(p.Platform, p.SnId) + if ret != nil { + if ret.ApplySnids != nil { + for i, as := range ret.ApplySnids { + if as.SnId == destP.SnId { + ret.ApplySnids = append(ret.ApplySnids[:i], ret.ApplySnids[i+1:]...) + model.UpsertFriendApply(p.Platform, p.SnId, ret) + return nil + } + } + } + } + return errors.New("not in apply") + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + //拒绝了不提醒 + if data != nil { + logger.Logger.Error("FriendRefuse data:", data) + SendToClick(friend.OpResultCode_OPRC_Error) + return + } + SendToClick(friend.OpResultCode_OPRC_Sucess) + })).StartByFixExecutor(FriendWrite) +} + +func (this *FriendMgr) FriendDelete(p *Player, destP *model.BindFriend) { + SendToClick := func(retCode friend.OpResultCode, self ...bool) { + pack := &friend.SCFriendOp{ + OpCode: proto.Int32(OpType_Delete), + SnId: proto.Int32(destP.SnId), + OpRetCode: retCode, + } + if len(self) == 0 { + pack.SnId = destP.SnId + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + logger.Logger.Tracef(">>FriendDelete %d -> %d, %v", p.SnId, destP.SnId, pack) + } else { + destPs := PlayerMgrSington.GetPlayerBySnId(destP.SnId) + if destPs != nil && destPs.IsOnLine() { + pack.SnId = p.SnId + destPs.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendOp), pack) + logger.Logger.Tracef(">>FriendDelete dest %d -> %d, %v", p.SnId, destP.SnId, pack) + } + } + } + f := FriendMgrSington.GetPlayer(p.Platform, destP.SnId) + if f != nil { + //发起者删除被删除者 + isok1 := FriendMgrSington.RemoveFriend(p.Platform, p.SnId, f.SnId) + if !isok1 { + logger.Logger.Warn("RemoveFriend error: ", p.SnId, " del friend:", f.SnId) + SendToClick(friend.OpResultCode_OPRC_Error) + return + } + //被删除者删除发起者 + isok2 := FriendMgrSington.RemoveFriend(f.Platform, f.SnId, p.SnId) + if !isok2 { + logger.Logger.Warn("RemoveFriend error: ", f.SnId, " del friend:", p.SnId) + //删除失败不用通知 + } else { + SendToClick(friend.OpResultCode_OPRC_Sucess, false) + } + SendToClick(friend.OpResultCode_OPRC_Sucess) + ChatMgrSington.DelChat(p.Platform, p.SnId, f.SnId) + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, _ := model.QueryFriendBySnid(p.Platform, destP.SnId) + if ret != nil { + //被删除者删除发起者 + if ret.BindFriend != nil { + for i, bindFriend := range ret.BindFriend { + if bindFriend.SnId == p.SnId { + ret.BindFriend = append(ret.BindFriend[:i], ret.BindFriend[i+1:]...) + model.UpsertFriend(ret) + SendToClick(friend.OpResultCode_OPRC_Sucess, false) + return nil + } + } + } + } + return errors.New("not in apply") + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + logger.Logger.Error("FriendDelete data:", data) + SendToClick(friend.OpResultCode_OPRC_Error) + return + } else { + //发起者删除被删除者 + isok1 := FriendMgrSington.RemoveFriend(p.Platform, p.SnId, destP.SnId) + if !isok1 { + logger.Logger.Warn("RemoveFriend error: ", p.SnId, " del friend:", destP.SnId) + //SendToClick(friend.OpResultCode_OPRC_Error) + //return + } + SendToClick(friend.OpResultCode_OPRC_Sucess) + } + ChatMgrSington.DelChat(p.Platform, p.SnId, destP.SnId) + })).StartByFixExecutor(FriendWrite) + } +} + +func (this *FriendMgr) Update() { +} + +func (this *FriendMgr) Shutdown() { + module.UnregisteModule(this) +} + +func (this *FriendMgr) UpdateName(snId int32, name string) { + p := PlayerMgrSington.GetPlayerBySnId(snId) + if p != nil { + this.UpdateInfo(p.Platform, p.SnId) + } +} + +func (this *FriendMgr) UpdateHead(snId, head int32) { + p := PlayerMgrSington.GetPlayerBySnId(snId) + if p != nil { + this.UpdateInfo(p.Platform, p.SnId) + } +} + +//========================implement IPlayerLoad ============================== + +func (this *FriendMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay { + return nil +} + +func (this *FriendMgr) Callback(player any, ret *internal.PlayerLoadReplay) { +} + +func (this *FriendMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay { + ret := &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + } + + friendDB, err := model.QueryFriendBySnid(platform, snid) + if err != nil { + logger.Logger.Error("QueryFriendBySnid:", err, snid) + ret.Err = err + return ret + } + + if friendDB == nil { + return ret + } + + var offSnID []int32 + for _, v := range friendDB.BindFriend { + roleId := common.DefaultRoleId + if v.RoleId != 0 { + roleId = int(v.RoleId) + } + p := PlayerMgrSington.GetPlayerBySnId(v.SnId) + if p != nil { + v.Platform = p.Platform + v.Name = p.Name + v.Head = p.Head + v.HeadUrl = p.HeadUrl + v.Sex = p.Sex + if !p.IsOnLine() { + v.LogoutTime = p.LastLogoutTime.Unix() + } + v.RoleId = int32(roleId) + } else { + offSnID = append(offSnID, v.SnId) + } + } + + if len(offSnID) > 0 { + offFriends, err := model.QueryFriendsBySnids(platform, offSnID) + if err != nil { + logger.Logger.Error("QueryFriendsBySnids is err:", err) + ret.Err = err + return ret + } + for _, offFriend := range offFriends { + for _, v := range friendDB.BindFriend { + roleId := common.DefaultRoleId + if v.RoleId != 0 { + roleId = int(v.RoleId) + } + if v.SnId == offFriend.SnId { + v.Platform = offFriend.Platform + v.Name = offFriend.Name + v.Head = offFriend.Head + v.HeadUrl = offFriend.HeadUrl + v.Sex = offFriend.Sex + v.LogoutTime = offFriend.LogoutTime + v.RoleId = int32(roleId) + break + } + } + } + } + + ret.Data = friendDB + + return ret +} + +func (this *FriendMgr) CallbackAfter(ret *internal.PlayerLoadReplay) { + if ret == nil || ret.Platform == "" || ret.Snid <= 0 { + return + } + + if ret.Err != nil { + logger.Logger.Error("(this *FriendMgr) CallbackAfter err:", ret.Err) + return + } + if ret.Data == nil { + this.Add(ret.Platform, ret.Snid) + logger.Logger.Infof("(this *FriendMgr) LoadFriendData New: %+v", *this.GetPlayer(ret.Platform, ret.Snid)) + return + } + + data, ok := ret.Data.(*model.Friend) + if !ok { + logger.Logger.Error("bug") + return + } + + if this.FriendList[ret.Platform] == nil { + this.FriendList[ret.Platform] = make(map[int32]*model.Friend) + } + + this.FriendList[ret.Platform][ret.Snid] = data + logger.Logger.Infof("(this *FriendMgr) LoadFriendData db: %+v", *data) + p := PlayerMgrSington.GetPlayerBySnId(ret.Snid) + if p != nil { + this.UpdateInfo(ret.Platform, ret.Snid) + } + FriendUnreadMgrSington.LoadFriendUnreadData(ret.Platform, ret.Snid) + this.ApplyList(ret.Platform, ret.Snid) +} + +func (this *FriendMgr) Save(platform string, snid int32, isSync, force bool) { + logger.Logger.Trace("(this *FriendMgr) SaveFriendData ", snid) + ret := this.GetPlayer(platform, snid) + if ret == nil || (!ret.Dirty && !force) { + return + } + + var res bool + f := func() { + res = model.UpsertFriend(ret) != nil + } + cf := func() { + if res { + ret.Dirty = false + } + } + + if isSync { + f() + cf() + return + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + f() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + cf() + })).StartByFixExecutor(fmt.Sprintf("player%v", ret.SnId)) +} + +func (this *FriendMgr) Release(platform string, snid int32) { + this.Del(platform, snid) +} + +func init() { + module.RegisteModule(FriendMgrSington, time.Hour, 0) + internal.RegisterPlayerLoad(FriendMgrSington) +} diff --git a/worldsrv/friendunreadmgr.go b/worldsrv/friendunreadmgr.go new file mode 100644 index 0000000..7a51f43 --- /dev/null +++ b/worldsrv/friendunreadmgr.go @@ -0,0 +1,178 @@ +package main + +import ( + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/friend" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "time" +) + +var FriendUnreadMgrSington = &FriendUnreadMgr{ + FriendUnreadList: make(map[string]map[int32]map[int32]int), + FriendUnreadDirty: make(map[string]map[int32]bool), +} + +type FriendUnreadMgr struct { + FriendUnreadList map[string]map[int32]map[int32]int + FriendUnreadDirty map[string]map[int32]bool +} + +func (this *FriendUnreadMgr) AddFriendUnread(platform string, snid, chatsnid int32) { + if this.FriendUnreadList[platform] != nil { + if ful, exist := this.FriendUnreadList[platform][snid]; !exist { + ful = make(map[int32]int) + ful[chatsnid] = 1 + this.FriendUnreadList[platform][snid] = ful + } else { + had := false + for cSnid, unreadNum := range ful { + if cSnid == chatsnid { + unreadNum++ + had = true + break + } + } + if !had { + ful[chatsnid] = 1 + } + } + if this.FriendUnreadDirty[platform] == nil { + this.FriendUnreadDirty[platform] = make(map[int32]bool) + } + this.FriendUnreadDirty[platform][snid] = true + } +} + +func (this *FriendUnreadMgr) DelFriendUnread(platform string, snid, chatsnid int32) { + if this.FriendUnreadList[platform] == nil { + return + } + if ful, exist := this.FriendUnreadList[platform][snid]; exist { + delete(ful, chatsnid) + if this.FriendUnreadDirty[platform] == nil { + this.FriendUnreadDirty[platform] = make(map[int32]bool) + } + this.FriendUnreadDirty[platform][snid] = true + } +} + +func (this *FriendUnreadMgr) LoadFriendUnreadData(platform string, snid int32) { + if this.FriendUnreadList[platform] == nil { + this.FriendUnreadList[platform] = make(map[int32]map[int32]int) + } + if _, exist := this.FriendUnreadList[platform][snid]; exist { + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryFriendUnreadBySnid(platform, snid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if ret, ok := data.(*model.FriendUnread); ok && ret != nil { + if ret.UnreadSnids != nil && len(ret.UnreadSnids) > 0 { + this.FriendUnreadList[platform][ret.Snid] = make(map[int32]int) + for _, us := range ret.UnreadSnids { + this.FriendUnreadList[platform][ret.Snid][us.SnId] = int(us.UnreadNum) + } + if this.FriendUnreadDirty[platform] == nil { + this.FriendUnreadDirty[platform] = make(map[int32]bool) + } + this.FriendUnreadDirty[platform][ret.Snid] = false + this.CheckSendFriendUnreadData(platform, ret.Snid) + } + } + })).StartByFixExecutor("QueryFriendUnreadBySnid") +} + +func (this *FriendUnreadMgr) CheckSendFriendUnreadData(platform string, snid int32) { + if this.FriendUnreadList[platform] == nil { + return + } + if ful, exist := this.FriendUnreadList[platform][snid]; exist { + if ful == nil || len(ful) == 0 { + return + } + pack := &friend.SCFriendUnreadData{} + for cSnid, unreadNum := range ful { + isFriend := FriendMgrSington.IsFriend(platform, snid, cSnid) + if isFriend { + fu := &friend.FriendUnread{ + Snid: proto.Int32(cSnid), + UnreadNum: proto.Int(unreadNum), + } + pack.FriendUnreads = append(pack.FriendUnreads, fu) + } else { + delete(ful, cSnid) + if this.FriendUnreadDirty[platform] == nil { + this.FriendUnreadDirty[platform] = make(map[int32]bool) + } + this.FriendUnreadDirty[platform][snid] = true + } + } + if len(pack.FriendUnreads) > 0 { + proto.SetDefaults(pack) + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil { + p.SendToClient(int(friend.FriendPacketID_PACKET_SCFriendUnreadData), pack) + logger.Logger.Trace("SCFriendUnreadData: 未读消息列表 pack: ", pack) + } + } + } +} + +func (this *FriendUnreadMgr) SaveFriendUnreadData(platform string, snid int32) { + if this.FriendUnreadDirty[platform] == nil || this.FriendUnreadList[platform] == nil { + return + } + if dirty, exist := this.FriendUnreadDirty[platform][snid]; exist { + if dirty { + if ful, ok := this.FriendUnreadList[platform][snid]; ok { + fu := model.NewFriendUnread(snid) + for cSnid, unreadNum := range ful { + if unreadNum > 0 && FriendMgrSington.IsFriend(platform, snid, cSnid) { + us := &model.FriendUnreadSnid{ + SnId: cSnid, + UnreadNum: int32(unreadNum), + } + fu.UnreadSnids = append(fu.UnreadSnids, us) + } + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdateFriendUnread(platform, snid, fu) + return nil + }), nil).StartByFixExecutor("UpdateFriendUnread") + } + } + } +} + +func (this *FriendUnreadMgr) ModuleName() string { + return "FriendUnreadMgr" +} + +func (this *FriendUnreadMgr) Init() { +} + +func (this *FriendUnreadMgr) Update() { +} + +func (this *FriendUnreadMgr) Shutdown() { + for platform, friendUnreadDirty := range this.FriendUnreadDirty { + for snid, dirty := range friendUnreadDirty { + if dirty { + this.SaveFriendUnreadData(platform, snid) + } + } + } + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(FriendUnreadMgrSington, time.Hour, 0) +} diff --git a/worldsrv/gameallconfig.go b/worldsrv/gameallconfig.go new file mode 100644 index 0000000..581b32a --- /dev/null +++ b/worldsrv/gameallconfig.go @@ -0,0 +1,88 @@ +package main + +//import ( +// "mongo.games.com/game/model" +// "mongo.games.com/game/srvdata" +// "mongo.games.com/goserver/core/logger" +// "github.com/globalsign/mgo/bson" +//) +// +//func InitGameAllConfigData() error { +// c := model.GameConfigCollection() +// if c != nil { +// var datas []model.GameGlobalState +// err := c.Find(nil).All(&datas) +// if err != nil { +// logger.Logger.Trace("InitGameAllConfigData err:", err) +// return err +// } +// for i := 0; i < len(datas); i++ { +// model.GameAllConfig[datas[i].LogicId] = &datas[i] +// } +// logger.Logger.Trace("InitGameAllConfigData:", model.GameAllConfig) +// +// //把dbFree中的数据写入数据库 +// arr := srvdata.PBDB_GameFreeMgr.Datas.GetArr() +// for _, dbGame := range arr { +// if dbGame.GetGameId() > 0 { +// name := dbGame.GetName() +// if name != dbGame.GetTitle() { +// name = dbGame.GetName() + dbGame.GetTitle() +// } +// if data, exist := model.GameAllConfig[dbGame.GetId()]; exist { +// data.Name = name +// data.GameId = dbGame.GetGameId() +// data.GameMode = dbGame.GetGameMode() +// cu := model.GameConfigCollection() +// if cu != nil { +// info, err := cu.Upsert(bson.M{"logicid": dbGame.GetId()}, data) +// if err != nil { +// logger.Logger.Trace("InitGameAllConfigData :", info, err) +// return err +// } +// } +// } else { +// name := dbGame.GetName() +// if name != dbGame.GetTitle() { +// name = dbGame.GetName() + dbGame.GetTitle() +// } +// data := &model.GameGlobalState{ +// Id: bson.NewObjectId(), +// LogicId: dbGame.GetId(), +// Name: name, +// GameId: dbGame.GetGameId(), +// GameMode: dbGame.GetGameMode(), +// State: 0, +// } +// model.GameAllConfig[dbGame.GetId()] = data +// ci := model.GameConfigCollection() +// if ci != nil { +// info, err := ci.Upsert(bson.M{"logicid": dbGame.GetId()}, data) +// if err != nil { +// logger.Logger.Trace("InitGameAllConfigData :", info, err) +// return err +// } +// } +// } +// } +// } +// +// //dbfree表中删除后操作 +// for k, _ := range model.GameAllConfig { +// gc := srvdata.PBDB_GameFreeMgr.GetData(k) +// if gc == nil { +// cgc := model.GameConfigCollection() +// if cgc != nil { +// err := cgc.Remove(bson.M{"logicid": k}) +// if err != nil { +// logger.Logger.Warn("RemoveGameConfig error:", err) +// return err +// } else { +// delete(model.GameAllConfig, k) +// } +// } +// } +// } +// } +// return nil +//} diff --git a/worldsrv/gameconfig.go b/worldsrv/gameconfig.go new file mode 100644 index 0000000..c2c33b0 --- /dev/null +++ b/worldsrv/gameconfig.go @@ -0,0 +1,139 @@ +package main + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "os" + "path/filepath" + + "github.com/howeyc/fsnotify" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" +) + +var fileSignMap = make(map[string]string) +var Config = Configuration{} + +type Configuration struct { + RootPath string + watcher *fsnotify.Watcher +} + +func (this *Configuration) Name() string { + return "gameconfig" +} + +func (this *Configuration) Init() error { + workDir, err := os.Getwd() + if err != nil { + return err + } + this.RootPath = filepath.Join(workDir, this.RootPath) + this.watcher, err = fsnotify.NewWatcher() + if err != nil { + logger.Logger.Info(" fsnotify.NewWatcher err:", err) + return err + } + + // Process events + go func() { + defer func() { + if err := recover(); err != nil { + logger.Logger.Warn("watch gameconfig director modify goroutine err:", err) + } + }() + for { + select { + case ev, ok := <-this.watcher.Event: + if ok && ev != nil { + if ev.IsModify() { + obj := core.CoreObject() + if filepath.Ext(ev.Name) == ".json" { + if obj != nil { + obj.SendCommand(&fileModifiedCommand{fileName: ev.Name}, false) + } + logger.Logger.Info("fsnotify event:", ev) + } + } + } else { + return + } + case err := <-this.watcher.Error: + logger.Logger.Info("fsnotify error:", err) + } + } + }() + this.watcher.Watch(this.RootPath) + + //遍历所有json + filepath.Walk(this.RootPath, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + if filepath.Ext(info.Name()) == ".json" { + UpdateGameConfigPolicy(path) + } + } + return nil + }) + return nil +} + +func (this *Configuration) Close() error { + this.watcher.Close() + return nil +} + +type fileModifiedCommand struct { + fileName string +} + +func (fmc *fileModifiedCommand) Done(o *basic.Object) error { + fn := filepath.Base(fmc.fileName) + logger.Logger.Info("modified file name ======", fn) + return UpdateGameConfigPolicy(fmc.fileName) +} + +func UpdateGameConfigPolicy(fullPath string) error { + //logger.Logger.Info("UpdateGameConfigPolicy file: ", fullPath) + buf, err := os.ReadFile(fullPath) + if err != nil { + return err + } + + if len(buf) == 0 { + return nil + } + + h := md5.New() + _, err = h.Write(buf) + if err != nil { + return err + } + fileSign := hex.EncodeToString(h.Sum(nil)) + if preSign, exist := fileSignMap[fullPath]; exist { + if preSign == fileSign { + return nil + } + } + fileSignMap[fullPath] = fileSign + spd := &ScenePolicyData{} + err = json.Unmarshal(buf, spd) + if err != nil { + logger.Logger.Info("UpdateGameConfigPolicy json.Unmarshal error", err) + } + if err == nil && spd.Init() { + for _, m := range spd.GameMode { + //logger.Logger.Info("New game config ver:", spd.ConfigVer) + if !CheckGameConfigVer(spd.ConfigVer, spd.GameId, m) { + //TeaHouseMgr.UpdateGameConfigVer(spd.ConfigVer, spd.GameId, m) + } + RegisteScenePolicy(int(spd.GameId), int(m), spd) + } + } + return err +} + +func init() { + core.RegistePackage(&Config) +} diff --git a/worldsrv/gamedata.go b/worldsrv/gamedata.go new file mode 100644 index 0000000..d90df8a --- /dev/null +++ b/worldsrv/gamedata.go @@ -0,0 +1,30 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" +) + +type GameDataMgr struct { +} + +func (this *GameDataMgr) ModuleName() string { + return "GameDataMgr" +} + +func (this *GameDataMgr) Init() { + //model.InitGameData() +} + +func (this *GameDataMgr) Update() { + logger.Logger.Trace("(this *GameDataMgr) Update()") + //model.SaveGameData() +} + +func (this *GameDataMgr) Shutdown() { + //model.SaveGameData() + //module.UnregisteModule(this) +} + +func init() { + //module.RegisteModule(&GameDataMgr{}, time.Minute, 0) +} diff --git a/worldsrv/gamesess.go b/worldsrv/gamesess.go new file mode 100644 index 0000000..f1c2c65 --- /dev/null +++ b/worldsrv/gamesess.go @@ -0,0 +1,299 @@ +package main + +import ( + "fmt" + "mongo.games.com/game/protocol/webapi" + "sync" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + gamehall_proto "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + libproto "mongo.games.com/goserver/srvlib/protocol" +) + +type GameSessionListener interface { + OnGameSessionRegiste(*GameSession) + OnGameSessionUnregiste(*GameSession) +} + +var GameSessionListenerSet = new(sync.Map) + +func RegisteGameSessionListener(listener GameSessionListener) { + GameSessionListenerSet.Store(listener, listener) +} + +func UnregisteGameSessionListener(listener GameSessionListener) { + GameSessionListenerSet.Delete(listener) +} + +type GameSession struct { + *netlib.Session + srvId int + srvType int + state common.GameSessState + players map[int32]*Player + scenes map[int]*Scene + cps map[string]*model.CoinPoolSetting + gameIds []int32 +} + +// 构造函数 +func NewGameSession(srvId, srvType int, s *netlib.Session) *GameSession { + gs := &GameSession{ + Session: s, + srvId: srvId, + srvType: srvType, + state: common.GAME_SESS_STATE_ON, + players: make(map[int32]*Player), + scenes: make(map[int]*Scene), + cps: make(map[string]*model.CoinPoolSetting), + } + return gs +} + +func (this *GameSession) RebindPlayerSnId(oldSnId, newSnId int32) { + if p, exist := this.players[oldSnId]; exist { + delete(this.players, oldSnId) + this.players[newSnId] = p + } +} + +func (this *GameSession) GetSrvId() int32 { + if this.Session == nil { + return 0 + } + attr := this.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*libproto.SSSrvRegiste); ok && srvInfo != nil { + return srvInfo.GetId() + } + } + return 0 +} + +// 关闭其上的所有场景 +func (this *GameSession) CloseAllScene() { + for sceneId, scene := range this.scenes { + if scene.IsMiniGameScene() { + + } else { + scDestroyRoom := &gamehall_proto.SCDestroyRoom{ + RoomId: proto.Int(sceneId), + OpRetCode: gamehall_proto.OpResultCode_Game_OPRC_Sucess_Game, + IsForce: proto.Int(1), + } + proto.SetDefaults(scDestroyRoom) + scene.Broadcast(int(gamehall_proto.GameHallPacketID_PACKET_SC_DESTROYROOM), scDestroyRoom, 0) + SceneMgrSingleton.DestroyScene(sceneId, true) + } + } + this.scenes = nil + this.players = nil +} + +// 注册事件 +func (this *GameSession) OnRegiste() { + GameSessionListenerSet.Range(func(key, val interface{}) bool { + if lis, ok := val.(GameSessionListener); ok { + lis.OnGameSessionRegiste(this) + } + return true + }) +} + +// 注销事件 +func (this *GameSession) OnUnregiste() { + //销毁比赛 + //MatchMgrSington.DestroyAllMatchByGameSession(this) + //解散房间 + this.CloseAllScene() + + GameSessionListenerSet.Range(func(key, val interface{}) bool { + if lis, ok := val.(GameSessionListener); ok { + lis.OnGameSessionUnregiste(this) + } + return true + }) +} + +// 负载因数 +func (this *GameSession) GetLoadFactor() int { + return len(this.scenes)*20 + len(this.players) +} + +// 设置状态 +func (this *GameSession) SwitchState(state common.GameSessState) { + if state == this.state { + return + } + this.state = state + switch state { + case common.GAME_SESS_STATE_ON: + this.OnStateOn() + case common.GAME_SESS_STATE_OFF: + this.OnStateOff() + } +} +func (this *GameSession) OnStateOn() { + pack := &server_proto.ServerState{ + SrvState: proto.Int(int(this.state)), + } + proto.SetDefaults(pack) + this.Send(int(server_proto.SSPacketID_PACKET_WG_SERVER_STATE), pack) +} +func (this *GameSession) OnStateOff() { + pack := &server_proto.ServerState{ + SrvState: proto.Int(int(this.state)), + } + proto.SetDefaults(pack) + this.Send(int(server_proto.SSPacketID_PACKET_WG_SERVER_STATE), pack) +} + +func (this *GameSession) AddScene(s *Scene) { + this.scenes[s.sceneId] = s + //send msg + msg := &server_proto.WGCreateScene{ + SceneId: proto.Int(s.sceneId), + GameId: proto.Int(s.gameId), + GameMode: proto.Int(s.gameMode), + SceneMode: proto.Int(s.sceneMode), + Params: s.params, + ParamsEx: s.paramsEx, + Creator: proto.Int32(s.creator), + Agentor: proto.Int32(s.agentor), + HallId: proto.Int32(s.hallId), + ReplayCode: proto.String(s.replayCode), + GroupId: proto.Int32(s.groupId), + TotalOfGames: proto.Int32(s.totalRound), + BaseScore: proto.Int32(s.BaseScore), + PlayerNum: proto.Int(s.playerNum), + } + var platform *Platform + if s.limitPlatform != nil { + msg.Platform = proto.String(s.limitPlatform.IdStr) + platform = s.limitPlatform + } else { + msg.Platform = proto.String(DefaultPlatform) + platform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + if s.dbGameFree != nil { + msg.DBGameFree = s.dbGameFree + } else if platform != nil { + gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, s.paramsEx[0]) + if gps != nil { + if gps.GroupId == 0 { + msg.DBGameFree = gps.DbGameFree + } else { + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + msg.DBGameFree = pgg.DbGameFree + } + } + } + } + if s.IsCoinScene() { + if sp, ok := s.sp.(*ScenePolicyData); ok { + msg.EnterAfterStart = proto.Bool(sp.EnterAfterStart) + } + } + //if s.ClubId > 0 { + // msg.Club = proto.Int32(s.ClubId) + // msg.ClubRoomId = proto.String(s.clubRoomID) + // msg.ClubRoomPos = proto.Int32(s.clubRoomPos) + // msg.ClubRate = proto.Int32(s.clubRoomTax) + //} + if s.IsHundredScene() { + //msg.RealCtrl = WBCtrlCfgMgr.GetRealCtrl(s.limitPlatform.IdStr) + } + // 象棋游戏添加段位配置 + if common.IsChess(s.gameId) { + msg.ChessRank = ChessRankMgrSington.GetChessRankArr(platform.Name, int32(s.gameId)) + } + proto.SetDefaults(msg) + this.Send(int(server_proto.SSPacketID_PACKET_WG_CREATESCENE), msg) + logger.Logger.Trace("WGCreateScene:", msg) +} + +func (this *GameSession) DelScene(s *Scene) { + delete(this.scenes, s.sceneId) + //from gameserver, so don't need send msg +} + +func (this *GameSession) AddPlayer(p *Player) { + this.players[p.SnId] = p +} + +func (this *GameSession) DelPlayer(p *Player) { + delete(this.players, p.SnId) +} + +func (this *GameSession) GenCoinPoolSettingKey(platform string, groupId, gamefreeid, srvid int32) string { + var key string + if groupId != 0 { + key = fmt.Sprintf("%v+%v_%v", gamefreeid, groupId, srvid) + } else { + key = fmt.Sprintf("%v_%v_%v", gamefreeid, platform, srvid) + } + return key +} + +func (this *GameSession) DetectCoinPoolSetting(platform string, gamefreeid, groupId int32) bool { + srvid := this.GetSrvId() + key := this.GenCoinPoolSettingKey(platform, groupId, gamefreeid, srvid) + if _, exist := this.cps[key]; !exist { + data := model.GetCoinPoolSetting(gamefreeid, srvid, groupId, platform) + if data == nil { + dbGameCoinPool := srvdata.PBDB_GameCoinPoolMgr.GetData(gamefreeid) + if dbGameCoinPool != nil { + data = model.NewCoinPoolSetting(platform, groupId, gamefreeid, srvid, dbGameCoinPool) + if data != nil { + err := model.UpsertCoinPoolSetting(data, nil) // 入库 + if err == nil { + model.ManageCoinPoolSetting(data) + } + } + } + } + if data != nil { + this.cps[key] = data + //send msg + msg := &webapi.CoinPoolSetting{ + Platform: platform, + GameFreeId: gamefreeid, + ServerId: srvid, + GroupId: groupId, + InitValue: data.InitValue, + LowerLimit: data.LowerLimit, + UpperLimit: data.UpperLimit, + QuDu: data.QuDu, + UpperOdds: data.UpperOdds, + UpperOddsMax: data.UpperOddsMax, + LowerOdds: data.LowerOdds, + LowerOddsMax: data.LowerOddsMax, + ProfitRate: data.ProfitRate, + ResetTime: data.ResetTime, + Switch: data.Switch, + } + proto.SetDefaults(msg) + this.Send(int(server_proto.SSPacketID_PACKET_WG_COINPOOLSETTING), msg) + } + } + return true +} + +// SendToGame 给某个游戏服务发消息 +func SendToGame(gameId int, packetId int, pack interface{}) { + gameServers := GameSessMgrSington.GetGameServerSess(gameId) + if len(gameServers) == 0 { + gameServers = GameSessMgrSington.GetGameServerSess(common.GameId_Unknow) + } + for _, value := range gameServers { + value.Send(packetId, pack) + } +} diff --git a/worldsrv/gamesessmgr.go b/worldsrv/gamesessmgr.go new file mode 100644 index 0000000..830ab6d --- /dev/null +++ b/worldsrv/gamesessmgr.go @@ -0,0 +1,319 @@ +package main + +import ( + "math" + "mongo.games.com/game/protocol/webapi" + "strconv" + "strings" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +//const ( +// ReplayServerType int = 8 +// ReplayServerId = 801 +//) + +var GameSessMgrSington = &GameSessMgr{ + servers: make(map[int]*GameSession), + gamesrvs: make(map[int][]*GameSession), + gates: make(map[int]*GameSession), +} + +type GameSessMgr struct { + servers map[int]*GameSession + gamesrvs map[int][]*GameSession + gates map[int]*GameSession +} + +// 注册事件 +func (this *GameSessMgr) OnRegiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.GameServiceType { + logger.Logger.Warn("(this *GameSessMgr) OnRegiste (GameSrv):", s) + srvId := int(srvInfo.GetId()) + gs := NewGameSession(srvId, srvlib.GameServerType, s) + if gs != nil { + this.servers[srvId] = gs + data := srvInfo.GetData() + if data != "" { + gameids := strings.Split(data, ",") + for _, id := range gameids { + if gameid, err := strconv.Atoi(id); err == nil { + if gss, exist := this.gamesrvs[gameid]; exist { + gss = append(gss, gs) + this.gamesrvs[gameid] = gss + } else { + this.gamesrvs[gameid] = []*GameSession{gs} + } + gs.gameIds = append(gs.gameIds, int32(gameid)) + } + } + } else { + if gss, exist := this.gamesrvs[0]; exist { + gss = append(gss, gs) + this.gamesrvs[0] = gss + } else { + this.gamesrvs[0] = []*GameSession{gs} + } + //gs.gameIds = append(gs.gameIds, 0) + } + gs.OnRegiste() + //尝试创建百人场 + HundredSceneMgrSington.TryCreateRoom() + } + } else if srvInfo.GetType() == srvlib.GateServiceType { + logger.Logger.Warn("(this *GameSessMgr) OnRegiste (GateSrv):", s) + srvId := int(srvInfo.GetId()) + gs := NewGameSession(srvId, srvlib.GateServerType, s) + if gs != nil { + this.gates[srvId] = gs + } + + } + } + } +} + +// 注销事件 +func (this *GameSessMgr) OnUnregiste(s *netlib.Session) { + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + if srvInfo.GetType() == srvlib.GameServiceType { + logger.Logger.Warn("(this *GameSessMgr) OnUnregiste (GameSrv):", s) + srvId := int(srvInfo.GetId()) + gs := this.servers[srvId] + if gs != nil { + delete(this.servers, srvId) + data := srvInfo.GetData() + if data != "" { + gameids := strings.Split(data, ",") + for _, id := range gameids { + if gameid, err := strconv.Atoi(id); err == nil { + if gss, exist := this.gamesrvs[gameid]; exist { + cnt := len(gss) + for j := 0; j < cnt; j++ { + if gss[j] == gs { + gss[j] = gss[cnt-1] + gss = gss[:cnt-1] + this.gamesrvs[gameid] = gss + break + } + } + } + } + } + } else { + if gss, exist := this.gamesrvs[0]; exist { + cnt := len(gss) + for j := 0; j < cnt; j++ { + if gss[j] == gs { + gss[j] = gss[cnt-1] + gss = gss[:cnt-1] + this.gamesrvs[0] = gss + break + } + } + } + } + gs.OnUnregiste() + } + } else if srvInfo.GetType() == srvlib.GateServiceType { + logger.Logger.Warn("(this *GameSessMgr) OnUnregiste (GateSrv):", s) + LoginStateMgrSington.LogoutAllBySession(s) + srvId := int(srvInfo.GetId()) + delete(this.gates, srvId) + } + } + } +} +func (this *GameSessMgr) GetGameServerSess(gameid int) []*GameSession { + return this.gamesrvs[gameid] +} + +// 获取最小负载的GameSession +func (this *GameSessMgr) GetMinLoadSess(gameid int) *GameSession { + minLoad := math.MaxInt32 + loadFactor := 0 + var gs *GameSession + if gss, exist := this.gamesrvs[gameid]; exist { + if gss != nil { + for _, s := range gss { + if s.state == common.GAME_SESS_STATE_ON { + loadFactor = s.GetLoadFactor() + if minLoad > loadFactor { + minLoad = loadFactor + gs = s + } + } + } + if gs != nil { + return gs + } + } + } + if gss, exist := this.gamesrvs[0]; exist { + if gss != nil { + for _, s := range gss { + if s.state == common.GAME_SESS_STATE_ON { + loadFactor = s.GetLoadFactor() + if minLoad > loadFactor { + minLoad = loadFactor + gs = s + } + } + } + if gs != nil { + return gs + } + } + } + return gs +} + +func (this *GameSessMgr) GetGameSess(srvId int) *GameSession { + if gs, exist := this.servers[srvId]; exist { + return gs + } + return nil +} +func (this *GameSessMgr) GetAllGameSess() []*GameSession { + servers := make([]*GameSession, 0) + for _, v := range this.servers { + servers = append(servers, v) + } + return servers +} +func (this *GameSessMgr) GetGateSess(srvId int) *GameSession { + if gs, exist := this.gates[srvId]; exist { + return gs + } + return nil +} +func (this *GameSessMgr) RebindPlayerSnId(oldSnId, newSnId int32) { + for _, gs := range this.servers { + gs.RebindPlayerSnId(oldSnId, newSnId) + } +} + +func (this *GameSessMgr) ListServerState(srvId, srvType int) []*webapi.ServerInfo { + _createGateServerInfo := func(srvId, srvType int, s *GameSession) *webapi.ServerInfo { + var robNum, playerNum int + for _, p := range PlayerMgrSington.sidMap { + if p != nil && p.gateSess == s.Session { + if p.IsRob { + robNum++ + } else { + playerNum++ + } + } + } + si := &webapi.ServerInfo{ + SrvId: int32(srvId), + SrvType: int32(srvType), + State: int32(s.state), + PlayerNum: int32(playerNum), + RobotNum: int32(robNum), + SceneNum: 0, + } + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + si.Data = srvInfo.GetData() + } + } + return si + } + + _createGameServerInfo := func(srvId, srvType int, s *GameSession) *webapi.ServerInfo { + var playerNum int + for _, p := range s.players { + if !p.IsRob { + playerNum++ + } + } + si := &webapi.ServerInfo{ + SrvId: int32(srvId), + SrvType: int32(srvType), + State: int32(s.state), + PlayerNum: int32(playerNum), + RobotNum: int32(len(s.players) - playerNum), + SceneNum: int32(len(s.scenes)), + } + attr := s.GetAttribute(srvlib.SessionAttributeServerInfo) + if attr != nil { + if srvInfo, ok := attr.(*protocol.SSSrvRegiste); ok && srvInfo != nil { + si.Data = srvInfo.GetData() + } + } + return si + } + var datas []*webapi.ServerInfo + if srvType != 0 { + switch srvType { + case srvlib.GateServiceType: + if srvId != 0 { + if s, exist := this.gates[srvId]; exist { + si := _createGateServerInfo(srvId, srvlib.GateServiceType, s) + datas = append(datas, si) + } + } + case srvlib.GameServiceType: + if srvId != 0 { + if s, exist := this.servers[srvId]; exist { + si := _createGameServerInfo(srvId, srvlib.GameServiceType, s) + datas = append(datas, si) + } + } + } + } else { + if srvId == 0 { + for sid, s := range this.gates { + si := _createGateServerInfo(sid, srvlib.GateServiceType, s) + datas = append(datas, si) + } + for sid, s := range this.servers { + si := _createGameServerInfo(sid, srvlib.GameServiceType, s) + datas = append(datas, si) + } + } else { + if s, exist := this.gates[srvId]; exist { + si := _createGateServerInfo(srvId, srvlib.GateServiceType, s) + datas = append(datas, si) + } + if s, exist := this.servers[srvId]; exist { + si := _createGameServerInfo(srvId, srvlib.GameServiceType, s) + datas = append(datas, si) + } + } + } + + //worldsrv 自身的信息 + myInfo := &webapi.ServerInfo{ + SrvId: int32(common.GetSelfSrvId()), + SrvType: int32(common.GetSelfSrvType()), + State: int32(common.GAME_SESS_STATE_ON), + PlayerNum: int32(len(PlayerMgrSington.players)), + RobotNum: int32(len(PlayerMgrSington.snidMap) - len(PlayerMgrSington.players)), + SceneNum: int32(len(SceneMgrSingleton.scenes)), + } + if SrvIsMaintaining { + myInfo.State = int32(common.GAME_SESS_STATE_ON) + } else { + myInfo.State = int32(common.GAME_SESS_STATE_OFF) + } + //把自己加进去 + datas = append(datas, myInfo) + return datas +} + +func init() { + srvlib.ServerSessionMgrSington.AddListener(GameSessMgrSington) +} diff --git a/worldsrv/gamestate.go b/worldsrv/gamestate.go new file mode 100644 index 0000000..58c9997 --- /dev/null +++ b/worldsrv/gamestate.go @@ -0,0 +1,110 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/netlib" + srvproto "mongo.games.com/goserver/srvlib/protocol" +) + +var gameStateMgr = &GameStateManager{ + gameList: make(map[int32]map[int32]*Player), + gameIds: make(map[int32][]int32), +} + +type GameStateManager struct { + gameList map[int32]map[int32]*Player //gameid-snid-player 推送消息的用户列表 + gameIds map[int32][]int32 +} + +var ids = []int32{ + int32(common.GameId_RollCoin), + int32(common.GameId_RollColor), + int32(common.GameId_RedVsBlack), + int32(common.GameId_DragonVsTiger), + int32(common.GameId_Baccarat), + int32(common.GameId_Roulette), + int32(common.GameId_RollPoint), + int32(common.GameId_RollAnimals), + //int32(common.GameId_BlackJack), + int32(common.GameId_HundredDZNZ), + int32(common.GameId_HundredYXX), + int32(common.GameId_Crash), +} + +func (gsm *GameStateManager) Init() { + var idsMap = make(map[int32]bool) + for _, v := range ids { + idsMap[v] = true + } + dbGameFree := srvdata.PBDB_GameFreeMgr.Datas.Arr + for _, gfs := range dbGameFree { + if _, ok := idsMap[gfs.GameId]; ok { + gsm.gameIds[gfs.GameId] = append(gsm.gameIds[gfs.GameId], gfs.Id) + } + } +} +func (gsm *GameStateManager) PlayerRegiste(player *Player, gameid int32, b bool) { + playerList := gsm.gameList[gameid] + if playerList == nil { + playerList = make(map[int32]*Player) + gsm.gameList[gameid] = playerList + } + playerList[player.SnId] = player +} +func (gsm *GameStateManager) PlayerClear(player *Player) { + for _, value := range gsm.gameList { + if value == nil { + continue + } + delete(value, player.SnId) + } +} +func (gsm *GameStateManager) BrodcastGameState(gameId int32, platform string, packid int, pack interface{}) { + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + playerList := gsm.gameList[gameId] + for _, p := range playerList { + if p != nil && p.gateSess != nil && p.IsOnLine() && p.Platform == platform { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packid, pack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} +func init() { + //使用并行加载 + RegisterParallelLoadFunc("选场游戏场次配置", func() error { + gameStateMgr.Init() + return nil + }) + //gameStateMgr.gameIds[int32(common.GameId_RollCoin)] = []int32{110030001, 110030002, 110030003, 110030004} + //gameStateMgr.gameIds[int32(common.GameId_RollColor)] = []int32{150010001, 150010002, 150010003, 150010004} + //gameStateMgr.gameIds[int32(common.GameId_RedVsBlack)] = []int32{140010001, 140010002, 140010003, 140010004} + //gameStateMgr.gameIds[int32(common.GameId_DragonVsTiger)] = []int32{120010001, 120010002, 120010003, 120010004} + //gameStateMgr.gameIds[int32(common.GameId_Baccarat)] = []int32{350010001, 350010002, 350010003, 350010004} + //gameStateMgr.gameIds[int32(common.GameId_Roulette)] = []int32{540000001, 540000002, 540000003, 540000004} + //gameStateMgr.gameIds[int32(common.GameId_RollPoint)] = []int32{530000001, 530000002, 530000003, 530000004} + //gameStateMgr.gameIds[int32(common.GameId_RollAnimals)] = []int32{560000001, 560000002, 560000003, 560000004} + //gameStateMgr.gameIds[int32(common.GameId_BlackJack)] = []int32{450000001, 450000002, 450000003, 450000004, 450000005} + //gameStateMgr.gameIds[int32(common.GameId_HundredDZNZ)] = []int32{660000001, 660000002, 660000003, 660000004} + //gameStateMgr.gameIds[int32(common.GameId_HundredYXX)] = []int32{670000001, 670000002, 670000003, 670000004} + // + //// 冰河世纪, 百战成神, 财神, 复仇者联盟, 复活岛 + //gameStateMgr.gameIds[int32(common.GameId_CaiShen)] = []int32{790000001, 790000002, 790000003, 790000004} + //gameStateMgr.gameIds[int32(common.GameId_Avengers)] = []int32{800000001, 800000002, 800000003, 800000004} + //gameStateMgr.gameIds[int32(common.GameId_EasterIsland)] = []int32{810000001, 810000002, 810000003, 810000004} + //gameStateMgr.gameIds[int32(common.GameId_IceAge)] = []int32{820000001, 820000002, 820000003} + //gameStateMgr.gameIds[int32(common.GameId_TamQuoc)] = []int32{830000001, 830000002, 830000003} +} diff --git a/worldsrv/horseracelamp.go b/worldsrv/horseracelamp.go new file mode 100644 index 0000000..574c4f2 --- /dev/null +++ b/worldsrv/horseracelamp.go @@ -0,0 +1,414 @@ +package main + +import ( + "github.com/globalsign/mgo/bson" + "math" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/message" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "strings" + "time" +) + +// 0] "%s" +// 5] "玩家 %s 在 %s 中获得了 %s元 ,进入游戏即可参与!" +// 6] "玩家 在 %s 中获得了爆池奖励 %s元 ,进入游戏即可参与!" +// 10] "恭喜 %s 在 %s 游戏内获得jackpot %s ,中奖金额【 %s】" +// 11] "恭喜 %s 击杀 %s %s 获得 %s奖励" +const ( + //消息类型 0:标示消息内容为服务端拼装好的字符串 + //1-99:根据客户端配置的编号格式化参数组成完整的消息内容(详见:HorseRaceLampMsgType) + //100:弹幕跑马灯 + HorseRaceLampType_ServerStr = 0 + HorseRaceLampType_CoinNormal = 5 + HorseRaceLampType_CoinReward = 6 + HorseRaceLampType_CustomMsg = 100 +) + +const ( + MAX_GAMENOTICE_PER_PLATFORM = 100 +) + +var HorseRaceLampMgrSington = &HorseRaceLampMgr{ + HorseRaceLampMsgList: make(map[string]*HorseRaceLamp), + HorseRaceLampGameList: make(map[string][]*HorseRaceLamp), + HorseRaceLampCastList: make(map[string]*HorseRaceLampCastInfo), + NextGameHorseRaceLamp: make(map[string]int64), +} + +type HorseRaceLampMgr struct { + HorseRaceLampMsgList map[string]*HorseRaceLamp + HorseRaceLampGameList map[string][]*HorseRaceLamp + HorseRaceLampCastList map[string]*HorseRaceLampCastInfo + NextGameHorseRaceLamp map[string]int64 +} + +type HorseRaceLamp struct { + Key string + Channel string + Title string + Content string + Footer string + StartTime int64 + Interval int32 + limitInterval int32 + Count int32 + LastTime int64 + Priority int32 + CreateTime int64 + MsgType int32 + Platform string + State int32 + isRob bool + Target []int32 + StandSec int32 +} + +type HorseRaceLampCastInfo struct { + CurIndex int + CurTime int64 + DealQueue []*HorseRaceLamp + DealList []*HorseRaceLamp +} + +func (this *HorseRaceLampMgr) InitHorseRaceLamp() { + for _, p := range PlatformMgrSingleton.GetPlatforms() { + noticeList, err := model.GetAllHorseRaceLamp(p.IdStr) + if err != nil { + logger.Logger.Error("InitHorseRaceLamp count failed:", err, noticeList) + } + + for _, value := range noticeList { + msg := &HorseRaceLamp{ + Key: value.Id.Hex(), + Channel: value.Channel, + Content: value.Content, + StartTime: value.StartTime, + Interval: value.Interval, + Count: value.Count, + MsgType: value.MsgType, + Priority: value.Priority, + CreateTime: value.CreateTime, + Platform: value.Platform, + State: value.State, + Target: value.Target, + StandSec: value.StandSec, + limitInterval: int32(math.Floor(float64(len(value.Content))*0.3)) + 6, + } + + this.HorseRaceLampMsgList[value.Id.Hex()] = msg + this.insertToCastMsg(msg) + } + } +} + +func (this *HorseRaceLampMgr) AddHorseRaceLampMsg(key, ch, p, title, content, footer string, startTime int64, interval, count, + msgType, state, priority int32, createTime int64, target []int32, standSec int32) string { + msg := &HorseRaceLamp{ + Key: key, + Channel: ch, + Title: title, + Content: content, + Footer: footer, + StartTime: startTime, + Interval: interval, + Count: count, + MsgType: msgType, + Priority: priority, + CreateTime: createTime, + Platform: p, + State: state, + Target: target, + StandSec: standSec, + } + this.HorseRaceLampMsgList[key] = msg + this.insertToCastMsg(msg) + + return key +} + +func (this *HorseRaceLampMgr) insertToCastMsg(msg *HorseRaceLamp) { + pKey := msg.Platform + if this.HorseRaceLampCastList[pKey] == nil { + this.HorseRaceLampCastList[pKey] = &HorseRaceLampCastInfo{ + CurIndex: 0, + CurTime: 0, + DealQueue: []*HorseRaceLamp{}, + DealList: []*HorseRaceLamp{}, + } + } else { + switch msg.MsgType { + case HorseRaceLampType_CustomMsg: + this.HorseRaceLampCastList[pKey].DealList = append(this.HorseRaceLampCastList[pKey].DealList, msg) + default: + this.HorseRaceLampCastList[pKey].DealQueue = append(this.HorseRaceLampCastList[pKey].DealQueue, msg) + } + } +} + +func (this *HorseRaceLampMgr) EditHorseRaceLampMsg(hrl *HorseRaceLamp) bool { + if _, ok := this.HorseRaceLampMsgList[hrl.Key]; !ok { + return false + } + hrl.limitInterval = int32(math.Floor(float64(len(hrl.Content))*0.3)) + 6 + this.HorseRaceLampMsgList[hrl.Key] = hrl + + if pInfo, ok := this.HorseRaceLampCastList[hrl.Platform]; ok && hrl.MsgType != HorseRaceLampType_CustomMsg { + pInfo.CurTime = 0 + pInfo.CurIndex = 0 + } + return true +} + +func (this *HorseRaceLampMgr) DelHorseRaceLampMsg(key string) { + if needDel, ok := this.HorseRaceLampMsgList[key]; ok { + if pInfo, ok := this.HorseRaceLampCastList[needDel.Platform]; ok { + pInfo.CurTime = 0 + pInfo.CurIndex = 0 + for index := range pInfo.DealQueue { + if pInfo.DealQueue[index] == needDel { + pInfo.DealQueue = append(pInfo.DealQueue[:index], pInfo.DealQueue[index+1:]...) + break + } + } + for index := range pInfo.DealList { + if pInfo.DealList[index] == needDel { + pInfo.DealList = append(pInfo.DealList[:index], pInfo.DealList[index+1:]...) + break + } + } + } + } + delete(this.HorseRaceLampMsgList, key) +} + +//func (this *HorseRaceLampMgr) WordCheck(content string) string { +// has := srvdata.HasSensitiveWord([]rune(content)) +// if has { +// content = string(srvdata.ReplaceSensitiveWord([]rune(content))[:]) +// } +// return content +//} + +func (this *HorseRaceLampMgr) IsSensitiveWord(content string) bool { + return srvdata.HasSensitiveWord([]rune(content)) +} + +func (this *HorseRaceLampMgr) PushGameHorseRaceLamp(ch, platform, content string, msgType int32, isRob bool, priority int32) bool { + msg := &HorseRaceLamp{ + Channel: ch, + Content: content, + Count: 1, + MsgType: msgType, + Platform: platform, + Priority: priority, + limitInterval: int32(math.Floor(float64(len(content))*0.3)) + 6, + isRob: isRob, + } + + if pool, exist := this.HorseRaceLampGameList[platform]; exist { + if len(pool) >= model.GameParamData.BacklogGameHorseRaceLamp && len(pool) > 0 { + minRobPriority := priority + minPlayerPriority := priority + bestRobIdx := -1 + bestPlayerIdx := -1 + for k, v := range pool { + if !isRob { + if v.isRob { //优先替换机器人的跑马灯 + if v.Priority <= minRobPriority { + bestRobIdx = k + minRobPriority = v.Priority + } + } else { //其次替换真实玩家的跑马灯 + if v.Priority < minPlayerPriority { + bestPlayerIdx = k + minPlayerPriority = v.Priority + } + } + } else { + if v.isRob { + if v.Priority < minRobPriority { + bestRobIdx = k + minRobPriority = v.Priority + } + } + } + } + if bestRobIdx != -1 { //优先替换机器人的跑马灯 + pool = append(pool[:bestRobIdx], pool[bestRobIdx+1:]...) + } else if bestPlayerIdx != -1 { //其次替换玩家的跑马灯 + pool = append(pool[:bestPlayerIdx], pool[bestPlayerIdx+1:]...) + } else { //没找到要替换的,直接丢弃自己的 + return false + } + } + + if !isRob { + isFindRob := false + if len(pool) > 0 { + for k, v := range pool { + if v.isRob { + pool[k] = msg + isFindRob = true + break + } + } + } + if isFindRob { + this.HorseRaceLampGameList[platform] = pool + } else { + this.HorseRaceLampGameList[platform] = append(pool, msg) + } + } else { + this.HorseRaceLampGameList[platform] = append(pool, msg) + } + } else { + this.HorseRaceLampGameList[platform] = []*HorseRaceLamp{msg} + } + return true +} +func (this *HorseRaceLampMgr) DealHorseRaceLamp(uTime int64, value *HorseRaceLamp) { + if value.Count > 0 { + this.BroadcastHorseRaceLampMsg(value) + value.Count = value.Count - 1 + value.LastTime = uTime + value.StartTime = value.LastTime + int64(value.Interval) + if value.Count <= 0 { + this.DelHorseRaceLampMsg(value.Key) + model.RemoveHorseRaceLamp(value.Key, value.Platform) + } + } else { + this.BroadcastHorseRaceLampMsg(value) + value.LastTime = uTime + value.StartTime = value.LastTime + int64(value.Interval) + } +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [HorseRaceLampMgr] +// ////////////////////////////////////////////////////////////////// +func (this *HorseRaceLampMgr) ModuleName() string { + return "HorseRaceLampMgr" +} + +func (this *HorseRaceLampMgr) Init() { +} + +func (this *HorseRaceLampMgr) Update() { + uTime := time.Now().Unix() + //调整了跑马灯功能,需要排队发送 + for _, v := range this.HorseRaceLampCastList { + if uTime > v.CurTime { + if v.CurIndex < len(v.DealQueue) { + value := v.DealQueue[v.CurIndex] + //需要跳过 + if value.State != 0 { + v.CurIndex += 1 + if v.CurIndex >= len(v.DealQueue) { + v.CurIndex = 0 + } + } else if uTime > value.StartTime && value.State == 0 { + this.DealHorseRaceLamp(uTime, value) + v.CurIndex += 1 + if v.CurIndex >= len(v.DealQueue) { + v.CurIndex = 0 + } + v.CurTime = uTime + int64(value.limitInterval) + } + } + } + } + for _, nc := range this.HorseRaceLampCastList { + for _, value := range nc.DealList { + if uTime > value.StartTime && value.State == 0 { + this.DealHorseRaceLamp(uTime, value) + } + } + } + for name, pool := range this.HorseRaceLampGameList { + if len(pool) > 0 { + msg := pool[0] + nextTs := this.NextGameHorseRaceLamp[name] + if uTime >= nextTs { + this.HorseRaceLampGameList[name] = pool[1:] + if msg != nil { + this.BroadcastHorseRaceLampMsg(msg) + this.NextGameHorseRaceLamp[name] = uTime + int64(msg.limitInterval) + } + } + } + } +} + +func (this *HorseRaceLampMgr) SaveHorseRaceLamp() { + for _, hrl := range this.HorseRaceLampMsgList { + model.EditHorseRaceLamp(&model.HorseRaceLamp{ + Id: bson.ObjectIdHex(hrl.Key), + Channel: "", + Title: hrl.Title, + Content: hrl.Content, + Footer: hrl.Footer, + StartTime: hrl.StartTime, + Interval: hrl.Interval, + Count: hrl.Count, + CreateTime: hrl.CreateTime, + Priority: hrl.Priority, + MsgType: hrl.MsgType, + Platform: hrl.Platform, + State: hrl.State, + Target: hrl.Target, + StandSec: hrl.StandSec, + }) + } +} + +func (this *HorseRaceLampMgr) Shutdown() { + this.SaveHorseRaceLamp() + module.UnregisteModule(this) +} + +func (this *HorseRaceLampMgr) BroadcastHorseRaceLampMsg(horseRaceLamp *HorseRaceLamp) { + if horseRaceLamp.MsgType == HorseRaceLampType_CustomMsg { + logger.Logger.Infof(">>>>>>>弹幕>>>>>>>>(this *HorseRaceLampMgr) BroadcastHorseRaceLampMsg content:%v msgType:%v "+ + "target:%v standSec:%v", horseRaceLamp.Content, horseRaceLamp.MsgType, horseRaceLamp.Target, horseRaceLamp.StandSec) + } + var rawpack = &message.SCNotice{ + Count: proto.Int(1), + MsgType: proto.Int32(horseRaceLamp.MsgType), + Ts: proto.Int64(time.Now().Unix()), //发送时间 + ChannelId: proto.String(horseRaceLamp.Channel), + Platform: proto.String(horseRaceLamp.Platform), + StandSec: proto.Int32(horseRaceLamp.StandSec), + } + if horseRaceLamp.MsgType == 0 { + rawpack.Params = append(rawpack.Params, &message.NoticeParam{StrParam: proto.String(horseRaceLamp.Content)}) + } else if horseRaceLamp.MsgType > 0 && horseRaceLamp.MsgType < 100 { + strArr := strings.Split(horseRaceLamp.Content, "|") + for _, value := range strArr { + rawpack.Params = append(rawpack.Params, &message.NoticeParam{StrParam: proto.String(value)}) + } + } else if horseRaceLamp.MsgType == 100 { + rawpack.Params = append(rawpack.Params, &message.NoticeParam{StrParam: proto.String(horseRaceLamp.Title)}) + rawpack.Params = append(rawpack.Params, &message.NoticeParam{StrParam: proto.String(horseRaceLamp.Content)}) + rawpack.Params = append(rawpack.Params, &message.NoticeParam{StrParam: proto.String(horseRaceLamp.Footer)}) + } + + proto.SetDefaults(rawpack) + if len(horseRaceLamp.Target) == 0 { + PlayerMgrSington.BroadcastMessageToPlatform(horseRaceLamp.Platform, int(message.MSGPacketID_PACKET_SC_NOTICE), rawpack) + } else { + PlayerMgrSington.BroadcastMessageToTarget(horseRaceLamp.Platform, horseRaceLamp.Target, int(message.MSGPacketID_PACKET_SC_NOTICE), rawpack) + } +} + +func init() { + module.RegisteModule(HorseRaceLampMgrSington, time.Second*3, 0) + + RegisterParallelLoadFunc("平台通知", func() error { + HorseRaceLampMgrSington.InitHorseRaceLamp() + return nil + }) +} diff --git a/worldsrv/hundredscenemgr.go b/worldsrv/hundredscenemgr.go new file mode 100644 index 0000000..187c667 --- /dev/null +++ b/worldsrv/hundredscenemgr.go @@ -0,0 +1,742 @@ +package main + +import ( + "math/rand" + "mongo.games.com/game/protocol/webapi" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + gamehall_proto "mongo.games.com/game/protocol/gamehall" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/srvlib" +) + +const ( + HundredSceneType_Primary int = iota //初级 + HundredSceneType_Mid //中级 + HundredSceneType_Senior //高级 + HundredSceneType_Professor //专家 + HundredSceneType_Experience //体验场 + HundredSceneType_Max +) + +const ( + HundredSceneOp_Enter int32 = iota //进入 + HundredSceneOp_Leave //离开 + HundredSceneOp_Change //换桌 + HundredSceneOp_Audience //观战 +) + +var HundredSceneMgrSington = &HundredSceneMgr{ + //分平台管理 + scenesOfPlatform: make(map[string]map[int32]*Scene), + platformOfScene: make(map[int32]string), + //分组管理 + scenesOfGroup: make(map[int32]map[int32]*Scene), + groupOfScene: make(map[int32]int32), + playerIning: make(map[int32]int32), +} + +type HundredSceneMgr struct { + //分平台管理 + scenesOfPlatform map[string]map[int32]*Scene // platform:gamefreeid:房间 + platformOfScene map[int32]string // sceneid:platform + //分组管理 + scenesOfGroup map[int32]map[int32]*Scene // groupid:gamefreeid:房间 + groupOfScene map[int32]int32 // sceneid:groupid + playerIning map[int32]int32 // snid:sceneid +} + +func (this *HundredSceneMgr) GetPlatformNameBySceneId(sceneid int32) (string, bool) { + if name, exist := this.platformOfScene[sceneid]; exist { + return name, exist + } + if _, exist := this.groupOfScene[sceneid]; exist { + s := SceneMgrSingleton.GetScene(int(sceneid)) + if s != nil && s.limitPlatform != nil { + return s.limitPlatform.IdStr, true + } + } + return DefaultPlatform, false +} + +func (this *HundredSceneMgr) RebindPlayerSnId(oldSnId, newSnId int32) { + if id, exist := this.playerIning[oldSnId]; exist { + delete(this.playerIning, oldSnId) + this.playerIning[newSnId] = id + } + for _, ss := range this.scenesOfPlatform { + for _, s := range ss { + s.RebindPlayerSnId(oldSnId, newSnId) + } + } + for _, ss := range this.scenesOfGroup { + for _, s := range ss { + s.RebindPlayerSnId(oldSnId, newSnId) + } + } +} + +func (this *HundredSceneMgr) PlayerEnter(p *Player, id int32) gamehall_proto.OpResultCode_Hundred { + logger.Logger.Tracef("(this *HundredSceneMgr) PlayerEnter snid:%v id:%v", p.SnId, id) + if oid, exist := this.playerIning[p.SnId]; exist { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter:%v snid:%v find in id:%v PlayerEnter return false", id, p.SnId, oid) + return gamehall_proto.OpResultCode_Hundred_OPRC_Error_Hundred + } + + if p.scene != nil { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter:%v snid:%v find in id:%v PlayerEnter return false", id, p.SnId, p.scene.sceneId) + return gamehall_proto.OpResultCode_Hundred_OPRC_Error_Hundred + } + + if p.isDelete { //删档用户不让进游戏 + return gamehall_proto.OpResultCode_Hundred_OPRC_RoomHadClosed_Hundred + } + + //多平台支持 + var limitPlatform *Platform + platformName := DefaultPlatform + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform != nil && platform.Isolated { + platformName = platform.IdStr + limitPlatform = platform + } else { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + + gps := PlatformMgrSingleton.GetGameFree(limitPlatform.IdStr, id) + if gps == nil { + return gamehall_proto.OpResultCode_Hundred_OPRC_RoomHadClosed_Hundred + } + + if gps.GroupId != 0 { //按分组进入场景游戏 + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + if _, ok := this.scenesOfGroup[gps.GroupId]; !ok { + this.scenesOfGroup[gps.GroupId] = make(map[int32]*Scene) + + } + if ss, ok := this.scenesOfGroup[gps.GroupId]; ok { + if s, ok := ss[id]; !ok { + s = this.CreateNewScene(id, gps.GroupId, limitPlatform, pgg.DbGameFree) + if s != nil { + ss[id] = s + this.groupOfScene[int32(s.sceneId)] = gps.GroupId + logger.Logger.Tracef("(this *HundredSceneMgr) PlayerEnter(groupid=%v) Create %v scene success.", gps.GroupId, id) + } else { + logger.Logger.Tracef("(this *HundredSceneMgr) PlayerEnter(groupid=%v) Create %v scene failed.", gps.GroupId, id) + } + } + //尝试进入 + if s, ok := ss[id]; ok && s != nil { + if s.PlayerEnter(p, -1, true) { + this.OnPlayerEnter(p, id) + return gamehall_proto.OpResultCode_Hundred_OPRC_Sucess_Hundred + } else { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(groupid=%v) enter %v scene failed.", gps.GroupId, id) + } + } else { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(groupid=%v) get %v scene failed.", gps.GroupId, id) + } + } + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(groupid=%v) snid:%v find in id:%v csp.PlayerEnter return false", gps.GroupId, p.SnId, id) + return gamehall_proto.OpResultCode_Hundred_OPRC_Error_Hundred + } + } + //没有场景,尝试创建 + if _, ok := this.scenesOfPlatform[platformName]; !ok { + this.scenesOfPlatform[platformName] = make(map[int32]*Scene) + } + if ss, ok := this.scenesOfPlatform[platformName]; ok { + if s, ok := ss[id]; !ok { + s = this.CreateNewScene(id, gps.GroupId, limitPlatform, gps.DbGameFree) + if s != nil { + ss[id] = s + this.platformOfScene[int32(s.sceneId)] = platformName + logger.Logger.Tracef("(this *HundredSceneMgr) PlayerEnter(platform=%v) Create %v scene success.", platformName, id) + } else { + logger.Logger.Tracef("(this *HundredSceneMgr) PlayerEnter(platform=%v) Create %v scene failed.", platformName, id) + } + } + //尝试进入 + if s, ok := ss[id]; ok && s != nil { + if s.PlayerEnter(p, -1, true) { + this.OnPlayerEnter(p, id) + return gamehall_proto.OpResultCode_Hundred_OPRC_Sucess_Hundred + } else { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(platform=%v) enter %v scene failed.", platformName, id) + } + } else { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(platform=%v) get %v scene failed.", platformName, id) + } + } + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerEnter(platform=%v) snid:%v find in id:%v csp.PlayerEnter return false", platformName, p.SnId, id) + return gamehall_proto.OpResultCode_Hundred_OPRC_SceneServerMaintain_Hundred +} + +func (this *HundredSceneMgr) OnPlayerEnter(p *Player, id int32) { + this.playerIning[p.SnId] = id +} + +func (this *HundredSceneMgr) PlayerLeave(p *Player, reason int) bool { + if p == nil { + return false + } + if _, ok := this.playerIning[p.SnId]; ok { + if p.scene != nil { + p.scene.PlayerLeave(p, reason) + } else { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerLeave(%v) found scene=nil", p.SnId) + delete(this.playerIning, p.SnId) + } + return true + } else { + if p.scene != nil && p.scene.IsHundredScene() { + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerLeave(%v) exception scene=%v gameid=%v", p.SnId, p.scene.sceneId, p.scene.gameId) + p.scene.PlayerLeave(p, reason) + return true + } + } + logger.Logger.Warnf("(this *HundredSceneMgr) PlayerLeave(%v) not found in hundred scene", p.SnId) + return false +} + +func (this *HundredSceneMgr) PlayerTryLeave(p *Player) gamehall_proto.OpResultCode_Hundred { + if p.scene == nil || p.scene.gameSess == nil { + logger.Logger.Tracef("(csm *HundredSceneMgr) PlayerTryLeave p.scene == nil || p.scene.gameSess == nil snid:%v ", p.SnId) + return 1 + } + //通知gamesrv托管 + if _, ok := this.playerIning[p.SnId]; ok { + pack := &gamehall_proto.CSLeaveRoom{Mode: proto.Int(0)} + proto.SetDefaults(pack) + common.TransmitToServer(p.sid, int(gamehall_proto.GameHallPacketID_PACKET_CS_LEAVEROOM), pack, p.scene.gameSess.Session) + } + return 0 +} + +func (this *HundredSceneMgr) OnPlayerLeave(p *Player) { + delete(this.playerIning, p.SnId) +} + +func (this *HundredSceneMgr) OnDestroyScene(sceneid int) { + var s *Scene + if platformName, ok := this.platformOfScene[int32(sceneid)]; ok { + if ss, ok := this.scenesOfPlatform[platformName]; ok { + for id, scene := range ss { + if scene.sceneId == sceneid { + s = scene + //删除玩家 + for pid, hid := range this.playerIning { + if hid == id { + delete(this.playerIning, pid) + //TODO 非正常删除房间时,尝试同步金币 + player := PlayerMgrSington.GetPlayerBySnId(pid) + if player != nil { + if !player.IsRob { + ctx := scene.GetPlayerGameCtx(player.SnId) + if ctx != nil { + //发送一个探针,等待ack后同步金币 + player.TryRetrieveLostGameCoin(sceneid) + + logger.Logger.Warnf("(this *HundredSceneMgr) OnDestroyScene(sceneid:%v) snid:%v SyncGameCoin", sceneid, player.SnId) + } + } + } + } + } + delete(ss, id) + break + } + } + } + } + + if groupId, ok := this.groupOfScene[int32(sceneid)]; ok { + if ss, ok := this.scenesOfGroup[groupId]; ok { + for id, scene := range ss { + if scene.sceneId == sceneid { + s = scene + //删除玩家 + for pid, hid := range this.playerIning { + if hid == id { + delete(this.playerIning, pid) + //TODO 非正常删除房间时,尝试同步金币 + player := PlayerMgrSington.GetPlayerBySnId(pid) + if player != nil { + if !player.IsRob { + ctx := scene.GetPlayerGameCtx(player.SnId) + if ctx != nil { + //发送一个探针,等待ack后同步金币 + player.TryRetrieveLostGameCoin(sceneid) + logger.Logger.Warnf("(this *HundredSceneMgr) OnDestroyScene(sceneid:%v) snid:%v SyncGameCoin", sceneid, player.SnId) + } + } + } + } + } + delete(ss, id) + break + } + } + } + } + + this.PreCreateGame(s.limitPlatform.IdStr, []int32{s.dbGameFree.Id}) +} + +func (this *HundredSceneMgr) GetPlayerNums(p *Player, gameId, gameMode int32) []int32 { + //多平台支持 + platformName := DefaultPlatform + platform := PlatformMgrSingleton.GetPlatform(p.Platform) + if platform != nil && platform.Isolated { + platformName = platform.IdStr + } else if p.Platform != DefaultPlatform { + platform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + + var nums [HundredSceneType_Max]int32 + wantNum := []int32{80, 50, 30, 20, 0} + for i := 0; i < HundredSceneType_Max; i++ { + if wantNum[i]/2 > 0 { + nums[i] = rand.Int31n(wantNum[i]/2) + wantNum[i] + } + } + + if platform == nil { + return nums[:] + } + + ids, _ := srvdata.DataMgr.GetGameFreeIds(gameId, gameMode) + for _, id := range ids { + gps := PlatformMgrSingleton.GetGameFree(platform.IdStr, id) + if gps != nil { + if gps.GroupId != 0 { + if ss, exist := this.scenesOfGroup[gps.GroupId]; exist { + for _, s := range ss { + if s.paramsEx[0] == id { + dbGame := srvdata.PBDB_GameFreeMgr.GetData(s.paramsEx[0]) + sceneType := int(dbGame.GetSceneType()) - 1 + if sceneType == -2 { + //体验场 + sceneType = HundredSceneType_Experience + } + truePlayerCount := int32(s.GetPlayerCnt()) + + //获取fake用户数量 + var fakePlayerCount int32 + //if truePlayerCount >= 21 { + // correctNum := dbGame.GetCorrectNum() + // correctRate := dbGame.GetCorrectRate() + // fakePlayerCount = correctNum + truePlayerCount*correctRate/100 + dbGame.GetDeviation() + //} + if sceneType >= 0 && sceneType < HundredSceneType_Max { + nums[sceneType] += int32(truePlayerCount + fakePlayerCount) + } + break + } + } + } + } else { + if ss, ok := this.scenesOfPlatform[platformName]; ok { + for _, s := range ss { + if s.paramsEx[0] == id { + dbGame := srvdata.PBDB_GameFreeMgr.GetData(s.paramsEx[0]) + sceneType := int(dbGame.GetSceneType()) - 1 + if sceneType == -2 { + //体验场 + sceneType = HundredSceneType_Experience + } + truePlayerCount := int32(s.GetPlayerCnt()) + + //获取fake用户数量 + var fakePlayerCount int32 + //if truePlayerCount >= 21 { + // correctNum := dbGame.GetCorrectNum() + // correctRate := dbGame.GetCorrectRate() + // fakePlayerCount = correctNum + truePlayerCount*correctRate/100 + dbGame.GetDeviation() + //} + if sceneType >= 0 && sceneType < HundredSceneType_Max { + nums[sceneType] += int32(truePlayerCount + fakePlayerCount) + } + break + } + } + } + } + } + } + return nums[:] +} + +func (this *HundredSceneMgr) InHundredScene(p *Player) bool { + if p == nil { + logger.Logger.Tracef("(this *HundredSceneMgr) InHundredScene p == nil snid:%v ", p.SnId) + return false + } + if _, ok := this.playerIning[p.SnId]; ok { + return true + } + logger.Logger.Tracef("(csm *HundredSceneMgr) InHundredScene false snid:%v ", p.SnId) + return false +} + +func (this *HundredSceneMgr) CreateNewScene(id, groupId int32, limitPlatform *Platform, dbGameFree *server_proto.DB_GameFree) *Scene { + if dbGameFree != nil { + dbGameRule := srvdata.PBDB_GameRuleMgr.GetData(dbGameFree.GetGameRule()) + if dbGameRule != nil { + gameId := int(dbGameRule.GetGameId()) + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs != nil { + sceneId := SceneMgrSingleton.GenOneHundredSceneId() + gameMode := dbGameRule.GetGameMode() + params := dbGameRule.GetParams() + //SceneType := dbGameFree.GetSceneType() + + scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, gameId, int(gameMode), common.SceneMode_Public, 1, -1, params, gs, limitPlatform, groupId, dbGameFree, id) + if scene != nil { + scene.hallId = id + //移动到SceneMgr中集中处理 + //if !scene.IsMatchScene() { + // //平台水池设置 + // gs.DetectCoinPoolSetting(limitPlatform.Name, scene.hallId, scene.groupId) + //} + return scene + } else { + logger.Logger.Errorf("Create hundred scene %v-%v failed.", gameId, sceneId) + } + } else { + logger.Logger.Errorf("Game %v server session no found.", gameId) + } + } else { + logger.Logger.Errorf("Game rule data %v no found.", dbGameFree.GetGameRule()) + } + } else { + logger.Logger.Errorf("Game free data %v no found.", id) + } + + return nil +} + +func (this *HundredSceneMgr) TryCreateRoom() { + if model.GameParamData.HundredScenePreCreate { + arr := srvdata.PBDB_GameFreeMgr.Datas.GetArr() + for _, dbGame := range arr { + if dbGame.GetGameId() <= 0 { + continue + } + if common.IsHundredType(dbGame.GetGameType()) { //百人场 + id := dbGame.GetId() + for k, ss := range this.scenesOfPlatform { + if _, exist := ss[id]; !exist { + limitPlatform := PlatformMgrSingleton.GetPlatform(k) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + k = DefaultPlatform + continue + } + gps := PlatformMgrSingleton.GetGameFree(limitPlatform.IdStr, id) + if gps != nil && gps.GroupId == 0 && gps.Status { + scene := this.CreateNewScene(id, gps.GroupId, limitPlatform, gps.DbGameFree) + logger.Logger.Trace("(this *HundredSceneMgr) TryCreateRoom(platform) ", id, k, scene) + if scene != nil { + this.platformOfScene[int32(scene.sceneId)] = k + ss[id] = scene + } + } + } + } + } + } + } +} +func (this *HundredSceneMgr) PreCreateGame(platform string, createIds []int32) { + limitPlatform := PlatformMgrSingleton.GetPlatform(platform) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + if this.scenesOfPlatform[platform] == nil { + this.scenesOfPlatform[platform] = make(map[int32]*Scene) + } + //var platformName string + platformData := PlatformMgrSingleton.GetPlatform(platform) + if platformData != nil && platformData.Isolated { + //platformName = platformData.Name + } else if platform != DefaultPlatform { + platformData = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + if platformData.IdStr == DefaultPlatform { + return + } + if model.GameParamData.HundredScenePreCreate { + //不创建已经存在的场景 + for _, id := range createIds { + dbGame := srvdata.PBDB_GameFreeMgr.GetData(id) + if common.IsHundredType(dbGame.GetGameType()) { + gps := PlatformMgrSingleton.GetGameFree(platformData.IdStr, id) + if gps != nil && gps.Status { + if gps.GroupId != 0 { + if this.scenesOfGroup[gps.GroupId] != nil && this.scenesOfGroup[gps.GroupId][id] != nil { + continue + } else { + scene := this.CreateNewScene(dbGame.GetId(), gps.GroupId, limitPlatform, gps.DbGameFree) + if scene != nil { + this.scenesOfGroup[gps.GroupId][id] = scene + this.groupOfScene[int32(scene.sceneId)] = gps.GroupId + } + } + + } else { + if this.scenesOfPlatform[platform] != nil && this.scenesOfPlatform[platform][dbGame.GetId()] != nil { + continue + } else { + scene := this.CreateNewScene(dbGame.GetId(), gps.GroupId, limitPlatform, gps.DbGameFree) + if scene != nil { + this.platformOfScene[int32(scene.sceneId)] = platform + this.scenesOfPlatform[platform][dbGame.GetId()] = scene + } + } + } + } + } + } + } +} +func (this *HundredSceneMgr) OnPlatformCreate(p *Platform) { + if p != nil && p.Isolated && p.IdStr != DefaultPlatform { + if _, exist := this.scenesOfPlatform[p.IdStr]; !exist { + this.scenesOfPlatform[p.IdStr] = make(map[int32]*Scene) + if model.GameParamData.HundredScenePreCreate { + arr := srvdata.PBDB_GameFreeMgr.Datas.GetArr() + for _, dbGame := range arr { + if common.IsHundredType(dbGame.GetGameType()) { //百人场 + id := dbGame.GetId() + gps := PlatformMgrSingleton.GetGameFree(p.IdStr, id) + if gps != nil { + if gps.GroupId != 0 { + if ss, ok := this.scenesOfGroup[gps.GroupId]; ok { + if _, exist := ss[id]; !exist { + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + scene := this.CreateNewScene(id, gps.GroupId, p, pgg.DbGameFree) + logger.Logger.Trace("(this *HundredSceneMgr) TryCreateRoom(group) ", id, gps.GroupId, scene) + if scene != nil { + ss[id] = scene + } + } + } + } + } else { + if ss, ok := this.scenesOfPlatform[p.IdStr]; ok { + if _, exist := ss[id]; !exist { + scene := this.CreateNewScene(id, gps.GroupId, p, gps.DbGameFree) + logger.Logger.Trace("(this *HundredSceneMgr) TryCreateRoom(platform) ", id, p.Name, scene) + if scene != nil { + ss[id] = scene + } + } + } + } + } + } + } + } + } + } +} + +func (this *HundredSceneMgr) OnPlatformDestroy(p *Platform) { + if p == nil { + return + } + if ss, ok := this.scenesOfPlatform[p.IdStr]; ok { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range ss { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } +} + +func (this *HundredSceneMgr) OnPlatformChangeIsolated(p *Platform, isolated bool) { + if p != nil { + if isolated { //孤立 + this.OnPlatformCreate(p) //预创建场景 + } else { + if ss, ok := this.scenesOfPlatform[p.IdStr]; ok { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range ss { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } + } +} + +func (this *HundredSceneMgr) OnPlatformChangeDisabled(p *Platform, disabled bool) { + if p == nil { + return + } + if disabled { + if ss, ok := this.scenesOfPlatform[p.IdStr]; ok { + pack := &server_proto.WGGraceDestroyScene{} + for _, scene := range ss { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } +} + +func (this *HundredSceneMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapi.GameFree) { + if p == nil || newCfg == nil { + return + } + if oldCfg.GroupId != newCfg.GroupId || oldCfg.GroupId != 0 { + if scenes, exist := this.scenesOfGroup[oldCfg.GroupId]; exist { + pack := &server_proto.WGGraceDestroyScene{} + if s, ok := scenes[newCfg.DbGameFree.Id]; ok { + pack.Ids = append(pack.Ids, int32(s.sceneId)) + } + if len(pack.Ids) > 0 { + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), + pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } + return + } + if scenes, exist := this.scenesOfPlatform[p.IdStr]; exist { + pack := &server_proto.WGGraceDestroyScene{} + if s, ok := scenes[newCfg.DbGameFree.Id]; ok { + pack.Ids = append(pack.Ids, int32(s.sceneId)) + } + if len(pack.Ids) > 0 { + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), + pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } +} + +func (this *HundredSceneMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi.GameConfigGroup) { + if newCfg == nil { + return + } + if scenes, exist := this.scenesOfGroup[newCfg.Id]; exist { + if s, ok := scenes[newCfg.DbGameFree.Id]; ok { + needDestroy := false + if s.dbGameFree.GetBot() != newCfg.DbGameFree.GetBot() || + s.dbGameFree.GetBaseScore() != newCfg.DbGameFree.GetBaseScore() || + s.dbGameFree.GetLimitCoin() != newCfg.DbGameFree.GetLimitCoin() || + s.dbGameFree.GetMaxCoinLimit() != newCfg.DbGameFree.GetMaxCoinLimit() || + !common.SliceInt64Equal(s.dbGameFree.GetRobotTakeCoin(), newCfg.DbGameFree.GetRobotTakeCoin()) || + !common.SliceInt64Equal(s.dbGameFree.GetRobotLimitCoin(), newCfg.DbGameFree.GetRobotLimitCoin()) { + needDestroy = true + } + if needDestroy { + pack := &server_proto.WGGraceDestroyScene{} + pack.Ids = append(pack.Ids, int32(s.sceneId)) + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } + } +} +func (this *HundredSceneMgr) GetPlatformSceneByGameFreeId(platform string, gameFreeIds []int32) []*Scene { + platformName := DefaultPlatform + platformData := PlatformMgrSingleton.GetPlatform(platform) + if platformData != nil && platformData.Isolated { + platformName = platformData.IdStr + } else if platform != DefaultPlatform { + platformData = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + gameScenes := []*Scene{} + for _, id := range gameFreeIds { + gps := PlatformMgrSingleton.GetGameFree(platformData.IdStr, id) + if gps != nil { + if gps.GroupId != 0 { + if ss, exist := this.scenesOfGroup[gps.GroupId]; exist { + if s, exist := ss[id]; exist && s != nil { + gameScenes = append(gameScenes, s) + } + } + } else { + if ss, ok := this.scenesOfPlatform[platformName]; ok { + if s, exist := ss[id]; exist && s != nil { + gameScenes = append(gameScenes, s) + } + } + } + } + } + + return gameScenes +} +func (this *HundredSceneMgr) GetPlatformScene(platform string, gameid int32) []*Scene { + gameFreeIds := gameStateMgr.gameIds[gameid] + gameScenes := this.GetPlatformSceneByGameFreeId(platform, gameFreeIds) + if len(gameScenes) != len(gameFreeIds) { + createIds := []int32{} + for _, gfi := range gameFreeIds { + bFind := false + for _, s := range gameScenes { + if s.dbGameFree.GetId() == gfi { + bFind = false + break + } + } + if !bFind { + createIds = append(createIds, gfi) + } + } + if len(createIds) > 0 { + this.PreCreateGame(platform, createIds) + gameScenes = this.GetPlatformSceneByGameFreeId(platform, gameFreeIds) + } + } + return gameScenes +} +func (this *HundredSceneMgr) ModuleName() string { + return "HundredSceneMgr" +} + +func (this *HundredSceneMgr) Init() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.Isolated || platform.IdStr == DefaultPlatform { + this.scenesOfPlatform[platform.IdStr] = make(map[int32]*Scene) + } + } +} + +// 撮合 +func (this *HundredSceneMgr) Update() { + +} + +func (this *HundredSceneMgr) Shutdown() { + module.UnregisteModule(this) +} + +func (this *HundredSceneMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { + if p == nil { + return + } + if scenes, ok := this.scenesOfPlatform[p.IdStr]; ok { + for _, scene := range scenes { + pack := &server_proto.WGGraceDestroyScene{} + if scene.dbGameFree.Id == gameFreeId { + pack.Ids = append(pack.Ids, int32(scene.sceneId)) + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack, common.GetSelfAreaId(), srvlib.GameServerType) + } + } +} +func init() { + module.RegisteModule(HundredSceneMgrSington, time.Second*5, 0) + PlatformMgrSingleton.RegisterObserver(HundredSceneMgrSington) + PlatformGameGroupMgrSington.RegisteObserver(HundredSceneMgrSington) +} diff --git a/worldsrv/init.go b/worldsrv/init.go new file mode 100644 index 0000000..03f6666 --- /dev/null +++ b/worldsrv/init.go @@ -0,0 +1,207 @@ +package main + +import ( + "fmt" + "math/rand" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/crash" + "mongo.games.com/game/mq" + "mongo.games.com/goserver/core/broker/rabbitmq" + + "sync" + + "github.com/astaxie/beego/cache" + "mongo.games.com/game/model" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/utils" +) + +type ParalleFunc func() error + +var CacheMemory cache.Cache +var wgParalleLoad = &sync.WaitGroup{} +var ParalleLoadModules []*ParalleLoadModule +var RabbitMQPublisher *mq.RabbitMQPublisher +var RabbitMqConsumer *mq.RabbitMQConsumer + +type ParalleLoadModule struct { + name string + f ParalleFunc +} + +func RegisterParallelLoadFunc(name string, f ParalleFunc) { + ParalleLoadModules = append(ParalleLoadModules, &ParalleLoadModule{name: name, f: f}) +} + +func init() { + rand.Seed(time.Now().UnixNano()) + + //首先加载游戏配置 + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + model.StartupRPClient(common.CustomConfig.GetString("MgoRpcCliNet"), common.CustomConfig.GetString("MgoRpcCliAddr"), time.Duration(common.CustomConfig.GetInt("MgoRpcCliReconnInterV"))*time.Second) + model.InitGameParam() + model.InitNormalParam() + + hs := model.GetCrashHash(0) + if hs == nil { + for i := 0; i < crash.POKER_CART_CNT; i++ { + model.InsertCrashHash(i, crash.Sha256(fmt.Sprintf("%v%v", i, time.Now().UnixNano()))) + } + hs = model.GetCrashHash(0) + } + model.GameParamData.InitGameHash = []string{} + for _, v := range hs { + model.GameParamData.InitGameHash = append(model.GameParamData.InitGameHash, v.Hash) + } + hsatom := model.GetCrashHashAtom(0) + if hsatom == nil { + for i := 0; i < crash.POKER_CART_CNT; i++ { + model.InsertCrashHashAtom(i, crash.Sha256(fmt.Sprintf("%v%v", i, time.Now().UnixNano()))) + } + hsatom = model.GetCrashHashAtom(0) + } + model.GameParamData.AtomGameHash = []string{} + for _, v := range hsatom { + model.GameParamData.AtomGameHash = append(model.GameParamData.AtomGameHash, v.Hash) + } + //if len(model.GameParamData.AtomGameHash) < crash.POKER_CART_CNT || + // len(model.GameParamData.InitGameHash) < crash.POKER_CART_CNT { + // panic(errors.New("hash is read error")) + //} + return nil + }) + + //RegisterParallelLoadFunc("平台红包数据", func() error { + // actRandCoinMgr.LoadPlatformData() + // return nil + //}) + + RegisterParallelLoadFunc("GMAC", func() error { + model.InitGMAC() + return nil + }) + + RegisterParallelLoadFunc("三方游戏配置", func() error { + model.InitGameConfig() + return nil + }) + + RegisterParallelLoadFunc("GameKVData", func() error { + model.InitGameKVData() + return nil + }) + + RegisterParallelLoadFunc("水池配置", func() error { + return model.GetAllCoinPoolSettingData() + }) + + RegisterParallelLoadFunc("三方平台热载数据设置", func() error { + f := func() { + webapi.ReqCgAddr = model.GameParamData.CgAddr + if plt, ok := webapi.ThridPlatformMgrSington.ThridPlatformMap.Load("XHJ平台"); ok { + //plt.(*webapi.XHJThridPlatform).IsNeedCheckQuota = model.GameParamData.FGCheckPlatformQuota + plt.(*webapi.XHJThridPlatform).ReqTimeOut = model.GameParamData.ThirdPltReqTimeout + } + } + f() + model.GameParamData.Observers = append(model.GameParamData.Observers, f) + return nil + }) + + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + + //for _, v := range data { + // PlatformMgrSingleton.UpsertPlatform(v.Name, v.Isolated, v.GameStatesData) + //} + + //ps := []model.GamePlatformState{model.GamePlatformState{LogicId:130000001,Param:"",State:1},model.GamePlatformState{LogicId:150000001,Param:"",State:1}} + + //model.InsertPlatformGameConfig("360",true,ps) + + var err error + CacheMemory, err = cache.NewCache("memory", `{"interval":60}`) + if err != nil { + return err + } + + //etcd打开连接 + EtcdMgrSington.Init() + + //go func() { + // for { + // time.Sleep(time.Minute) + // EtcdMgrSington.Reset() + // } + //}() + + //rabbitmq打开链接 + RabbitMQPublisher = mq.NewRabbitMQPublisher(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}, common.CustomConfig.GetInt("RMQPublishBacklog")) + if RabbitMQPublisher != nil { + err = RabbitMQPublisher.Start() + if err != nil { + panic(err) + } + } + + RabbitMqConsumer = mq.NewRabbitMQConsumer(common.CustomConfig.GetString("RabbitMQURL"), rabbitmq.Exchange{Name: common.CustomConfig.GetString("RMQExchange"), Durable: true}) + if RabbitMqConsumer != nil { + RabbitMqConsumer.Start() + } + + //开始并行加载数据 + //改为串行加载,后台并发有点扛不住 + if len(ParalleLoadModules) > 0 { + tStart := time.Now() + logger.Logger.Infof("===[开始串行加载]===") + //wgParalleLoad.Add(paralleCnt) + for _, m := range ParalleLoadModules { + /*go*/ func(plm *ParalleLoadModule) { + ts := time.Now() + defer func() { + utils.DumpStackIfPanic(plm.name) + //wgParalleLoad.Done() + logger.Logger.Infof("[串行加载结束][%v] 耗时[%v]", plm.name, time.Now().Sub(ts)) + }() + logger.Logger.Infof("[开始串行加载][%v] ", plm.name) + + err := plm.f() + if err != nil { + logger.Logger.Warnf("[串行加载][%v][error:%v]", plm.name, err) + } + }(m) + } + //wgParalleLoad.Wait() + logger.Logger.Infof("===[串行加载结束,总耗时:%v]===", time.Now().Sub(tStart)) + } + return nil + }) + + core.RegisteHook(core.HOOK_AFTER_STOP, func() error { + //etcd关闭连接 + EtcdMgrSington.Shutdown() + //关闭rabbitmq连接 + if RabbitMQPublisher != nil { + RabbitMQPublisher.Stop() + } + + if RabbitMqConsumer != nil { + RabbitMqConsumer.Stop() + } + + //model.ShutdownRPClient() + return nil + }) + //core.RegisteHook(core.HOOK_BEFORE_START, func() error { + // ThirdPltGameMappingConfig.Init() + // return nil + //}) + //RegisterParallelLoadFunc("分层配置数据", func() error { + // //加载分层配置 + // LogicLevelMgrSington.LoadConfig() + // return nil + //}) +} diff --git a/worldsrv/internal/playercache.go b/worldsrv/internal/playercache.go new file mode 100644 index 0000000..53ac1cd --- /dev/null +++ b/worldsrv/internal/playercache.go @@ -0,0 +1,85 @@ +package internal + +/* + 玩家数据可能保存在多个表中,主表user_playerinfo,如果新加字段比较多可以建立新表 + 然后使用 PlayerLoader 加载玩家数据,实现这个接口 + 使用 RegisterPlayerLoad 注册到框架中 +*/ + +type PlayerLoadReplay struct { + Platform string // 平台id + Snid int32 // 玩家id + Err error // 查询错误信息 + Data interface{} +} + +// IPlayerLoad 玩家登录时首次加载数据 +// 重连不会执行 +type IPlayerLoad interface { + // Load 查询数据库,在task中执行 + Load(platform string, snid int32, player any) *PlayerLoadReplay + // Callback 数据查询成功的回掉方法 + Callback(player any, ret *PlayerLoadReplay) +} + +// IPlayerLogined 玩家登录之后加载其他玩家数据 +// 重连不会执行 +// 在 IPlayerLoad 之后执行 +type IPlayerLogined interface { + // LoadAfter 查询数据库,在task中执行 + LoadAfter(platform string, snid int32) *PlayerLoadReplay + // CallbackAfter 数据查询成功的回掉方法 + CallbackAfter(ret *PlayerLoadReplay) +} + +type IPlayerRelease interface { + // Release 内存释放 + Release(platform string, snid int32) +} + +type IPlayerSave interface { + // Save 保存到数据库 + // isSync 是否同步保存 + // force 是否强制保存 + Save(platform string, snid int32, isSync, force bool) +} + +type PlayerLoader interface { + IPlayerLoad + IPlayerLogined + IPlayerSave + IPlayerRelease +} + +type BasePlayerLoader struct { +} + +func (b *BasePlayerLoader) Load(platform string, snid int32, player any) *PlayerLoadReplay { + return nil +} + +func (b *BasePlayerLoader) Callback(player any, ret *PlayerLoadReplay) { +} + +func (b *BasePlayerLoader) LoadAfter(platform string, snid int32) *PlayerLoadReplay { + return nil +} + +func (b *BasePlayerLoader) CallbackAfter(ret *PlayerLoadReplay) { +} + +func (b *BasePlayerLoader) Save(platform string, snid int32, isSync, force bool) { +} + +func (b *BasePlayerLoader) Release(platform string, snid int32) { +} + +var playerLoads []PlayerLoader + +func RegisterPlayerLoad(i PlayerLoader) { + playerLoads = append(playerLoads, i) +} + +func GetPlayerLoads() []PlayerLoader { + return playerLoads +} diff --git a/worldsrv/invitecode.go b/worldsrv/invitecode.go new file mode 100644 index 0000000..9d17ecd --- /dev/null +++ b/worldsrv/invitecode.go @@ -0,0 +1,109 @@ +package main + +import ( + "fmt" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/worldsrv/internal" +) + +func SaveInviteScore(data *model.InviteScore) { + if data == nil { + return + } + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err = model.SaveInviteScore(data) + if err != nil { + logger.Logger.Errorf("SaveInviteScore error:%v", err) + return err + } + // 后台统计积分记录 + data.Ts /= 1000000000 // 后台需要秒 + LogChannelSingleton.WriteLog(data) + logger.Logger.Tracef("==> php InviteScore %+v", *data) + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + p := PlayerMgrSington.GetPlayerBySnId(data.InviteSnId) + if err == nil && p != nil && data.Score != 0 { + if data.Score < 0 { + if -data.Score > p.InviteScore { + data.Score = -p.InviteScore + } + } + p.InviteScore += data.Score + p.dirty = true + + // 更新邀请任务进度 + if data.Score > 0 { + TaskSubjectSingleton.Touch(common.TaskTypeInviteScore, &TaskData{ + SnId: p.SnId, + Num: data.Score, + }) + } + } + })).StartByFixExecutor(fmt.Sprintf("invite_score_%v", data.InviteSnId)) +} + +func GetInviteScore(platform string, snid int32) { + var n, z int64 + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + n, z, err = model.GetInviteScore(platform, snid) + if err != nil { + logger.Logger.Errorf("GetInviteScore error:%v", err) + return err + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if err == nil && p != nil { + p.InviteScore = n + p.dirty = true + + // 更新邀请任务进度 + p.ResetTaskN(common.TaskTypeInviteScore) + TaskSubjectSingleton.Touch(common.TaskTypeInviteScore, &TaskData{ + SnId: p.SnId, + Num: z, + }) + } + })).StartByFixExecutor(fmt.Sprintf("invite_score_%v", snid)) +} + +type InviteCode struct { + internal.BasePlayerLoader +} + +func (i *InviteCode) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay { + n, err := model.GetInviteNum(platform, snid) + return &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + Err: err, + Data: n, + } +} + +func (i *InviteCode) CallbackAfter(ret *internal.PlayerLoadReplay) { + if ret == nil { + return + } + p := PlayerMgrSington.GetPlayerBySnId(ret.Snid) + if p == nil { + return + } + p.InviteNum = ret.Data.(int32) + p.ResetTaskN(common.TaskTypeInviteNum) + TaskSubjectSingleton.Touch(common.TaskTypeInviteNum, &TaskData{SnId: p.SnId, Num: int64(p.InviteNum)}) + GetInviteScore(ret.Platform, ret.Snid) +} + +func init() { + internal.RegisterPlayerLoad(new(InviteCode)) +} diff --git a/worldsrv/jackpotlist.go b/worldsrv/jackpotlist.go new file mode 100644 index 0000000..5ad2b3b --- /dev/null +++ b/worldsrv/jackpotlist.go @@ -0,0 +1,211 @@ +package main + +// +//import ( +// "mongo.games.com/game/common" +// "mongo.games.com/game/proto" +// "mongo.games.com/game/protocol/gamehall" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/timer" +// "math/rand" +// "strconv" +// "time" +//) +// +//var jackpotInterval = time.Hour * common.SCENE_BIGWINHISTORY_TIMEINTERVAL +// +//var JackpotListMgrSington = &JackpotListMgr{ +// BigWinHistoryByGameID: make(map[int][]*gamehall.BigWinHistoryInfo), +// jackpotListHandle: make(map[int]timer.TimerHandle), // 新的爆奖记录生成 +//} +// +//type JackpotListMgr struct { +// BigWinHistoryByGameID map[int][]*gamehall.BigWinHistoryInfo +// jackpotListHandle map[int]timer.TimerHandle // 新的爆奖记录生成 +//} +// +//func (this *JackpotListMgr) AddJackpotList(gameid int, data *gamehall.BigWinHistoryInfo) { +// this.BigWinHistoryByGameID[gameid] = append(this.BigWinHistoryByGameID[gameid], data) +// if len(this.BigWinHistoryByGameID[gameid]) > common.SCENE_BIGWINHISTORY_MAXNUMBER { +// this.BigWinHistoryByGameID[gameid] = this.BigWinHistoryByGameID[gameid][1:] +// } +//} +// +//func (this *JackpotListMgr) GetJackpotList(gameid int) []*gamehall.BigWinHistoryInfo { +// if this.BigWinHistoryByGameID[gameid] == nil { +// this.BigWinHistoryByGameID[gameid] = make([]*gamehall.BigWinHistoryInfo, 0) +// } +// return this.BigWinHistoryByGameID[gameid] +//} +// +//func genRandTime(sec int, circleTime time.Time) time.Time { +// //随机时间间隔 +// rand.Seed(time.Now().UnixNano() + int64(sec)) +// interval := rand.Intn(60) + 60*sec // 分钟 +// circleTime = circleTime.Add(time.Duration(-interval) * time.Minute) +// s := rand.Intn(60) //随机一个秒数 +// circleTime = circleTime.Add(time.Duration(-s) * time.Second) +// return circleTime +//} +// +//func genRoomIDAndScore(gameid int) (roomID int64, score int64) { +// // 随机从房间内读取一个场数据 +// var scenes = make([]*Scene, 0) +// for _, s := range SceneMgrSington.scenes { +// if s != nil && s.dbGameFree.GetGameId() == int32(gameid) { +// scenes = append(scenes, s) +// } +// } +// if len(scenes) < 1 { +// return +// } +// s := scenes[rand.Intn(len(scenes))] +// jackpot := s.dbGameFree.GetJackpot() +// roomID = int64(s.dbGameFree.GetBaseScore()) +// baseScore := int64(jackpot[0]) * roomID +// score = int64(baseScore) + int64(rand.Int31n(int32(baseScore/2))) +// logger.Logger.Infof("genjackpot %v score %v roomID%v baseScore%v", jackpot[0], score, s.dbGameFree.GetBaseScore(), baseScore) +// return +//} +// +//// 生成爆奖记录 +//func (this *JackpotListMgr) GenJackpot(gameid int) { +// // 首次生成初始化爆奖信息 +// if len(this.BigWinHistoryByGameID[gameid]) == 0 { +// // 直接从大厅取机器人 +// circleTime := time.Now() +// sec := common.SCENE_BIGWINHISTORY_LIMITNUMBER +// for _, p := range PlayerMgrSington.sidMap { +// if len(this.BigWinHistoryByGameID[gameid]) >= common.SCENE_BIGWINHISTORY_LIMITNUMBER { +// break +// } +// if p.IsRob { +// p.RobotRandName() +// genedTime := genRandTime(sec, circleTime).Unix() +// spinid := strconv.FormatInt(int64(p.SnId), 10) // 用户id转换成字符串 +// baseBet, priceValue := genRoomIDAndScore(gameid) +// if baseBet == 0 || priceValue == 0 { +// return +// } +// newJackpot := &gamehall.BigWinHistoryInfo{ +// SpinID: spinid, +// CreatedTime: genedTime, +// BaseBet: baseBet, +// TotalBet: baseBet, +// PriceValue: priceValue, +// UserName: p.Name, +// } +// this.AddJackpotList(gameid, newJackpot) +// sec-- +// } +// } +// } else { +// lastRecord := this.BigWinHistoryByGameID[gameid][len(this.BigWinHistoryByGameID[gameid])-1] // 当中奖纪录>10条时,随机时间差, 满足当前时间-最后一次爆奖记录时间 > 随机时间差 时重新生成一条记录 +// lastTime := time.Unix(lastRecord.GetCreatedTime(), 0) +// genNewJackpotFlag := lastTime.Add(jackpotInterval).Before(time.Now()) +// if genNewJackpotFlag { +// for _, p := range PlayerMgrSington.sidMap { +// if p.IsRob { +// p.RobotRandName() +// genedTime := time.Now().Unix() +// spinid := strconv.FormatInt(int64(p.SnId), 10) // 用户id转换成字符串 +// baseBet, priceValue := genRoomIDAndScore(gameid) +// if baseBet == 0 || priceValue == 0 { +// return +// } +// newJackpot := &gamehall.BigWinHistoryInfo{ +// SpinID: spinid, +// CreatedTime: genedTime, +// BaseBet: baseBet, +// TotalBet: baseBet, +// PriceValue: priceValue, +// UserName: p.Name, +// } +// this.AddJackpotList(gameid, newJackpot) +// break +// } +// } +// this.after(gameid) +// } +// } +//} +// +//// AddVirtualJackpot 添加虚拟爆奖记录(名字+用户id, 使用大厅机器人信息) +//func (this *JackpotListMgr) AddVirtualJackpot(gameid int, data *gamehall.BigWinHistoryInfo) { +// if len(PlayerMgrSington.sidMap) < 1 { +// logger.Logger.Error("AddVirtualJackpot not found robot") +// return +// } +// +// for _, p := range PlayerMgrSington.sidMap { +// if p.IsRob { +// p.RobotRandName() +// spinid := strconv.FormatInt(int64(p.SnId), 10) // 用户id转换成字符串 +// data.SpinID = spinid +// data.UserName = p.Name +// this.AddJackpotList(gameid, data) +// break +// } +// } +//} +// +//func (this *JackpotListMgr) start(gameid int) { +// this.jackpotListHandle[gameid], _ = timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { +// this.GenJackpot(gameid) +// return true +// }), nil, jackpotInterval, 1) +//} +// +//func (this *JackpotListMgr) after(gameid int) { +// interval := jackpotInterval + time.Duration(rand.Intn(2))*time.Hour + time.Duration(rand.Intn(60))*time.Minute + time.Duration(rand.Intn(60)) +// this.jackpotListHandle[gameid], _ = timer.AfterTimer(func(h timer.TimerHandle, ud interface{}) bool { +// this.GenJackpot(gameid) +// +// jackpotList := JackpotListMgrSington.GetJackpotList(gameid) +// msg := this.GetStoCMsg(jackpotList) +// logger.Logger.Infof("jackpotlist timer after gameid(%v) %v", gameid, msg) +// return true +// }, nil, interval) +//} +// +//func (this *JackpotListMgr) StopTimer(gameid int) bool { +// return timer.StopTimer(this.jackpotListHandle[gameid]) +//} +// +//func (this *JackpotListMgr) ResetAfterTimer(gameid int) { +// if this.StopTimer(gameid) { +// this.after(gameid) +// } +//} +// +//func (this *JackpotListMgr) GetStoCMsg(jackpotList []*gamehall.BigWinHistoryInfo) *gamehall.SCBigWinHistory { +// pack := &gamehall.SCBigWinHistory{} +// for i := len(jackpotList) - 1; i >= 0; i-- { +// v := jackpotList[i] +// player := &gamehall.BigWinHistoryInfo{ +// SpinID: proto.String(v.GetSpinID()), +// CreatedTime: proto.Int64(v.GetCreatedTime()), +// BaseBet: proto.Int64(v.GetBaseBet()), +// TotalBet: proto.Int64(v.GetTotalBet()), +// PriceValue: proto.Int64(int64(v.GetPriceValue())), +// UserName: proto.String(v.GetUserName()), +// Cards: v.GetCards(), +// } +// pack.BigWinHistory = append(pack.BigWinHistory, player) +// } +// +// //pack := &avengers.SCAvengersBigWinHistory{} +// //for i := len(jackpotList) - 1; i >= 0; i-- { +// // v := jackpotList[i] +// // player := &avengers.AvengersBigWinHistoryInfo{ +// // SpinID: proto.String(v.GetSpinID()), +// // CreatedTime: proto.Int64(v.GetCreatedTime()), +// // RoomID: proto.Int64(v.GetRoomID()), +// // PriceValue: proto.Int64(int64(v.GetPriceValue())), +// // UserName: proto.String(v.GetUserName()), +// // } +// // pack.BigWinHistory = append(pack.BigWinHistory, player) +// //} +// proto.SetDefaults(pack) +// return pack +//} diff --git a/worldsrv/logchannel.go b/worldsrv/logchannel.go new file mode 100644 index 0000000..6e6f877 --- /dev/null +++ b/worldsrv/logchannel.go @@ -0,0 +1,63 @@ +package main + +import ( + "reflect" + + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +// LogChannelSingleton 日志记录器 +var LogChannelSingleton = &LogChannel{ + cName: make(map[reflect.Type]string), +} + +type LogChannel struct { + cName map[reflect.Type]string +} + +func (c *LogChannel) RegisterLogCName(cname string, log interface{}) { + t := c.getLogType(log) + c.cName[t] = cname +} + +func (c *LogChannel) getLogType(log interface{}) reflect.Type { + return reflect.Indirect(reflect.ValueOf(log)).Type() +} + +func (c *LogChannel) getLogCName(log interface{}) string { + t := c.getLogType(log) + if name, exist := c.cName[t]; exist { + return name + } + return "" +} + +// WriteLog 记录日志,需要提前注册 +func (c *LogChannel) WriteLog(log interface{}) { + cname := c.getLogCName(log) + if cname == "" { + cname = "_null_" + } + RabbitMQPublisher.Send(cname, log) +} + +// WriteMQData rabbitMQ消息 +func (c *LogChannel) WriteMQData(data *model.RabbitMQData) { + RabbitMQPublisher.Send(data.MQName, data.Data) +} + +func init() { + LogChannelSingleton.RegisterLogCName(model.LoginLogCollName, &model.LoginLog{}) + LogChannelSingleton.RegisterLogCName(model.CoinGiveLogCollName, &model.CoinGiveLog{}) + LogChannelSingleton.RegisterLogCName(model.CoinLogCollName, &model.CoinLog{}) + LogChannelSingleton.RegisterLogCName(model.ItemLogCollName, &model.ItemLog{}) + LogChannelSingleton.RegisterLogCName(model.WelfareLogCollName, &model.WelfareLog{}) + LogChannelSingleton.RegisterLogCName(model.OnlineLogCollName, &model.OnlineLog{}) + LogChannelSingleton.RegisterLogCName(model.MQRankSeason, &model.PlayerRankScore{}) + LogChannelSingleton.RegisterLogCName(model.MQRankPlayerCoin, &model.RankPlayerCoin{}) + LogChannelSingleton.RegisterLogCName(mq.BackBankrupt, &model.BankruptLog{}) + LogChannelSingleton.RegisterLogCName(mq.BackReliefund, &model.ReliefFundLog{}) + LogChannelSingleton.RegisterLogCName(model.EvtBindInvite, &model.BindInvite{}) + LogChannelSingleton.RegisterLogCName(mq.BackInviteScore, &model.InviteScore{}) +} diff --git a/worldsrv/logger.xml b/worldsrv/logger.xml new file mode 100644 index 0000000..f6eb37b --- /dev/null +++ b/worldsrv/logger.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/worldsrv/logiclevelmgr.go b/worldsrv/logiclevelmgr.go new file mode 100644 index 0000000..a445c99 --- /dev/null +++ b/worldsrv/logiclevelmgr.go @@ -0,0 +1,121 @@ +package main + +// +//import ( +// "encoding/json" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core/logger" +// "io/ioutil" +// "net/http" +// "net/url" +// "strconv" +// "time" +//) +// +//var LogicLevelMgrSington = &LogicLevelMgr{ +// config: make(map[string]*LogicLevelConfig), +// client: &http.Client{Timeout: 30 * time.Second}, +//} +// +//type LogicLevelMgr struct { +// config map[string]*LogicLevelConfig +// client *http.Client +//} +//type LogicLevelConfig struct { +// Platform string +// LogicLevelInfo map[int32]*LogicLevelInfo +//} +//type LogicLevelInfo struct { +// Id int32 //分层id +// ClusterName string //分层名称 +// StartAct int32 //分层开关 1开启 0关闭 +// CheckActIds []int32 //分层包含的活动id +// CheckPay []string //分层包含的充值类型 +//} +// +//func (this *LogicLevelMgr) GetConfig(platform string) *LogicLevelConfig { +// return this.config[platform] +//} +// +//func (this *LogicLevelMgr) UpdateConfig(cfg *LogicLevelConfig) { +// logger.Logger.Trace("++++++++++++++UpdateConfig++++++++++++++") +// this.config[cfg.Platform] = cfg +// +// if playersOL, ok := PlayerMgrSington.playerOfPlatform[cfg.Platform]; ok { +// for _, player := range playersOL { +// if player != nil && !player.IsRob { +// player.layered = make(map[int]bool) +// for _, v := range player.layerlevels { +// if td, ok := cfg.LogicLevelInfo[int32(v)]; ok { +// if td.StartAct == 1 { +// for _, id := range td.CheckActIds { +// player.layered[int(id)] = true +// } +// } +// } +// } +// //player.ActStateSend2Client() +// } +// } +// } +//} +// +//type NewMsg struct { +// Platform string +// SnId int +// Levels []int +//} +// +//func (this *LogicLevelMgr) SendPostBySnIds(platform string, snids []int32) []NewMsg { +// client := this.client +// form := make(url.Values) +// form.Set("Platform", platform) +// str := "" +// for k, snid := range snids { +// str += strconv.Itoa(int(snid)) +// if k+1 < len(snids) { +// str += "," +// } +// } +// form.Set("SnIds", str) +// form.Set("PltName", common.CustomConfig.GetString("PltName")) +// logicLevelUrl := common.CustomConfig.GetString("LogicLevelUrl") +// resp, err := client.PostForm(logicLevelUrl+"/QueryDataBySnIds", form) +// if resp != nil && resp.Status == "200 OK" && err == nil { +// defer resp.Body.Close() +// body, _ := io.ReadAll(resp.Body) +// logger.Logger.Trace(string(body)) +// var data []NewMsg +// json.Unmarshal(body, &data) +// return data +// } +// return nil +//} +//func (this *LogicLevelMgr) LoadConfig() { +// logger.Logger.Trace("++++++++++++++LoadConfig++++++++++++++") +// type LogicLevelConfigData struct { +// Tag int +// Msg []*LogicLevelConfig +// } +// if !model.GameParamData.UseEtcd { +// logger.Logger.Trace("API_GetGradeShopConfigData") +// buff, err := webapi.API_GetLogicLevelConfigData(common.GetAppId()) +// if err == nil { +// var data LogicLevelConfigData +// err = json.Unmarshal(buff, &data) +// if err == nil && data.Tag == 0 { +// for _, cfg := range data.Msg { +// this.UpdateConfig(cfg) +// } +// } else { +// logger.Logger.Error("Unmarshal LogicLevelConfigData config data error:", err, string(buff)) +// } +// } else { +// logger.Logger.Error("Get LogicLevelConfigData config data error:", err) +// } +// } else { +// EtcdMgrSington.InitLogicLevelConfig() +// } +//} diff --git a/worldsrv/loginstatemgr.go b/worldsrv/loginstatemgr.go new file mode 100644 index 0000000..38c666b --- /dev/null +++ b/worldsrv/loginstatemgr.go @@ -0,0 +1,330 @@ +package main + +import ( + "fmt" + "mongo.games.com/goserver/core/logger" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" +) + +const ( + LoginStateLogin int = iota // 登录中 + LoginStateLoginFinish // 登录完成 + LoginStateLogout // 登出中 + LoginStateLogoutFinish // 登出完成 +) + +var LoginStateMgrSington = &LoginStateMgr{ + statesByName: make(map[string]*AccLoginState), + statesByAccId: make(map[string]*AccLoginState), + statesByPlayer: make(map[string]*AccLoginState), + statesBySid: make(map[int64]*SessionState), +} + +// SessionState 客户端连接状态 +type SessionState struct { + userName string // 玩家唯一标识 + sid int64 // 连接唯一标识 + state int // 登录状态 + gateSess *netlib.Session // 客户端所在gatesrv + als *AccLoginState // 账号状态 + clog *model.ClientLoginInfo // 登录日志 + startLoginTs int64 // 登录时间 +} + +type AccLoginState struct { + acc *model.Account + lss map[int64]*SessionState // sid + lastLoginTs int64 //最后登录时间 + lastLogoutTs int64 //最后登出时间(只是用来判断清理内存缓存) +} + +// LoginStateMgr 玩家登录信息 +// 保存登录中和已经登录的玩家账号信息 +// 玩家登出或断开连接只会清除连接信息,短时间内重复登录会使用之前的账号缓存数据不会频繁访问数据库 +// 玩家长时间离开才会清除缓存信息 +// 注意:玩家登录次数,登录时间,登出时间记录并没有准确同步到数据库 +type LoginStateMgr struct { + statesByName map[string]*AccLoginState // key:玩家唯一标识,玩家登录状态 + statesByAccId map[string]*AccLoginState // key:accountid,所有人登录状态 + statesByPlayer map[string]*AccLoginState // key:accountid,真人登录状态 + statesBySid map[int64]*SessionState // key:连接唯一标识,连接状态 +} + +func UserKey(userName, platform string, tagkey int32) string { + return fmt.Sprintf("%v_%v_%v", userName, platform, tagkey) +} + +func (this *LoginStateMgr) IsLogining(userName, platform string, tagkey int32) bool { + userName = UserKey(userName, platform, tagkey) + if v, exist := this.statesByName[userName]; exist { + if len(v.lss) == 0 { + return false + } + for _, ls := range v.lss { + if ls.state == LoginStateLogin { + return true + } + } + } + return false +} + +func (this *LoginStateMgr) IsLoginFinish(userName, platform string, tagkey int32) bool { + userName = UserKey(userName, platform, tagkey) + if v, exist := this.statesByName[userName]; exist { + if len(v.lss) == 0 { + return false + } + for _, ls := range v.lss { + if ls.state == LoginStateLoginFinish { + return true + } + } + } + return false +} + +func (this *LoginStateMgr) GetLoginStateByName(userName string) *AccLoginState { + if v, exist := this.statesByName[userName]; exist { + return v + } + return nil +} + +func (this *LoginStateMgr) GetLoginStateByAccId(accId string) *AccLoginState { + if v, exist := this.statesByAccId[accId]; exist { + return v + } + return nil +} + +func (this *LoginStateMgr) GetLoginStateByTelAndPlatform(tel, platform string) *AccLoginState { + for _, als := range this.statesByPlayer { + if als != nil && als.acc != nil { + if als.acc.Tel == tel && als.acc.Platform == platform { + return als + } + } + } + + return nil +} + +// GetLoginStateBySid 连接是否登录中 +func (this *LoginStateMgr) GetLoginStateBySid(sid int64) *SessionState { + if v, exist := this.statesBySid[sid]; exist { + return v + } + return nil +} + +// StartLogin 开始登录 +func (this *LoginStateMgr) StartLogin(userName, platform string, sid int64, s *netlib.Session, clog *model.ClientLoginInfo, tagkey int32) bool { + //注意此处的坑,这个地方要增加平台id + userName = UserKey(userName, platform, tagkey) + ts := time.Now().Unix() + if als, exist := this.statesByName[userName]; !exist { + als = &AccLoginState{ + lss: make(map[int64]*SessionState), + lastLoginTs: ts, + } + ls := &SessionState{ + userName: userName, + sid: sid, + gateSess: s, + state: LoginStateLogin, + als: als, + startLoginTs: ts, + clog: clog, + } + this.statesByName[userName] = als + als.lss[sid] = ls + this.statesBySid[sid] = ls + return true + } else { + ls := &SessionState{ + userName: userName, + sid: sid, + gateSess: s, + state: LoginStateLogin, + als: als, + startLoginTs: ts, + clog: clog, + } + als.lss[sid] = ls + this.statesBySid[sid] = ls + if als.acc != nil { + return false + } + return true + } +} + +// LoginFinish 玩家登录完成 +func (this *LoginStateMgr) LoginFinish(userName, platform string, sid int64, acc *model.Account, tagkey int32) (oldState map[int64]*SessionState) { + userName = UserKey(userName, platform, tagkey) + if v, exist := this.statesByName[userName]; exist { + v.acc = acc + v.lastLoginTs = time.Now().Unix() + if acc != nil { + this.statesByAccId[acc.AccountId.Hex()] = v + if acc.Platform != common.Platform_Rob { + this.statesByPlayer[acc.AccountId.Hex()] = v + } + } + if len(v.lss) > 0 { + oldState = make(map[int64]*SessionState) + for k, v := range v.lss { + if k != sid { + oldState[k] = v + } + } + } + } + + if v, exist := this.statesBySid[sid]; exist { + v.state = LoginStateLoginFinish + } + + return +} + +// Logout 删除连接状态 +// 连接断开,正常登出 +func (this *LoginStateMgr) Logout(s *SessionState) { + if s != nil { + delete(this.statesBySid, s.sid) + //缓存下,避免对DB造成冲击 + s.state = LoginStateLogoutFinish + s.als.lastLogoutTs = time.Now().Unix() + if s.als != nil && s.als.acc != nil { + s.als.acc.LastLogoutTime = time.Now() + } + delete(s.als.lss, s.sid) + } +} + +// LogoutBySid 登录失败,删除连接状态 +func (this *LoginStateMgr) LogoutBySid(sid int64) { + if s, exist := this.statesBySid[sid]; exist { + this.Logout(s) + } +} + +func (this *LoginStateMgr) LogoutByAccount(accId string) { + if als, exist := this.statesByAccId[accId]; exist { + if als != nil { + for _, s := range als.lss { + this.Logout(s) + } + } + } +} + +// LogoutAllBySession gatesrv掉线,则上面的所有玩家掉线 +func (this *LoginStateMgr) LogoutAllBySession(session *netlib.Session) { + for sid, s := range this.statesBySid { + if s.gateSess == session { + this.Logout(s) + p := PlayerMgrSington.GetPlayer(sid) + if p != nil { + p.DropLine() + } + } + } +} + +// DelAccountByAccid 特殊处理,负责从statesByName删除,为了处理账号的删除问题 +// 目前的一个使用功能,机器人账号会被删除,但是这里是删除真人账号数据? +func (this *LoginStateMgr) DelAccountByAccid(accid string) { + for name, s := range this.statesByPlayer { + if s != nil && s.acc != nil && s.acc.AccountId.Hex() == accid { + this.DeleteAccount(name, s) + } + } +} + +func (this *LoginStateMgr) DeleteAccount(name string, s *AccLoginState) { + if s != nil && s.acc != nil { + //注意此处的坑,这个地方要增加平台id,为了和上面的匹配相同 + userName := UserKey(s.acc.UserName, s.acc.Platform, s.acc.TagKey) + delete(this.statesByName, userName) + userName = UserKey(s.acc.Tel, s.acc.Platform, s.acc.TagKey) + delete(this.statesByName, userName) + acc := s.acc + if acc != nil { + delete(this.statesByAccId, acc.AccountId.Hex()) + } + delete(this.statesByPlayer, name) + } +} + +func (this *LoginStateMgr) ModuleName() string { + return "LoginStateMgr" +} + +func (this *LoginStateMgr) Init() { + // 预加载机器人数据,是为了让机器人登录时不再访问数据库吗? + if model.GameParamData.PreLoadRobotCount > 0 { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + tsBeg := time.Now() + accounts := model.GetRobotAccounts(model.GameParamData.PreLoadRobotCount) + tsEnd := time.Now() + logger.Logger.Tracef("GetRobotAccounts take:%v total:%v", tsEnd.Sub(tsBeg), len(accounts)) + return accounts + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if accounts, ok := data.([]model.Account); ok { + if accounts != nil { + ts := time.Now().Add(time.Hour).Unix() // 这里加了一小时,延迟数据清理? + for i := 0; i < len(accounts); i++ { + userName := UserKey(accounts[i].UserName, accounts[i].Platform, accounts[i].TagKey) + if _, exist := this.statesByName[userName]; !exist { + als := &AccLoginState{ + acc: &accounts[i], + lss: make(map[int64]*SessionState), + lastLoginTs: ts, + lastLogoutTs: ts, + } + this.statesByName[userName] = als + this.statesByAccId[accounts[i].AccountId.Hex()] = als + } + } + } + } + }), "GetAllRobotAccounts").Start() + } + + PlayerMgrSington.LoadRobots() +} + +func (this *LoginStateMgr) Update() { + // 定时清理已经登出的账号数据 + curTs := time.Now().Unix() + for name, s := range this.statesByPlayer { + if len(s.lss) == 0 && curTs-s.lastLogoutTs > int64(model.GameParamData.LoginStateCacheSec) { + if s != nil && s.acc != nil { + this.DeleteAccount(name, s) + } + } + } +} + +func (this *LoginStateMgr) Shutdown() { + for _, s := range this.statesByName { + if s.acc != nil && s.acc.Platform != common.Platform_Rob { + model.LogoutAccount(s.acc) // 更新登录次数和登出时间 + } + } + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(LoginStateMgrSington, time.Minute, 0) +} diff --git a/worldsrv/main.go b/worldsrv/main.go new file mode 100644 index 0000000..c6927f9 --- /dev/null +++ b/worldsrv/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "math/rand" + "time" + + _ "games.yol.com/win88" + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" + _ "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/schedule" +) + +func main() { + rand.Seed(time.Now().Unix()) + core.RegisterConfigEncryptor(common.ConfigFE) + defer core.ClosePackages() + core.LoadPackages("config.json") + + //启动定时任务 + schedule.StartTask() + + //启动业务模块 + waiter := module.Start() + waiter.Wait("main()") +} diff --git a/worldsrv/matchcontext.go b/worldsrv/matchcontext.go new file mode 100644 index 0000000..6ef3a36 --- /dev/null +++ b/worldsrv/matchcontext.go @@ -0,0 +1,70 @@ +package main + +import "sort" + +type PlayerMatchContext struct { + tm *TmMatch //比赛 + p *Player //玩家数据 + scene *Scene //比赛房间 + round int32 //第几轮 + seq int //报名序号 + grade int32 //比赛积分 + rank int32 //当前第几名 + gaming bool //是否比赛中 + record map[int32]int32 //战绩 + copySnid int32 + copyLv int32 + copyRoleId int32 +} + +type MatchContextSlice []*PlayerMatchContext + +func (p MatchContextSlice) Len() int { return len(p) } +func (p MatchContextSlice) Less(i, j int) bool { + if p[i].grade > p[j].grade { + return true + } else if p[i].grade == p[j].grade { + return p[i].seq < p[j].seq + } else { + return false + } +} + +func (p MatchContextSlice) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p MatchContextSlice) Sort(isFinals bool) { + sort.Sort(p) + for i, mc := range p { + mc.rank = int32(i + 1) + } + if isFinals { + lastRank := int32(0) + lastGrade := int32(0) + for i := 0; i < len(p); i++ { + mc := p[i] + if i > 0 && mc.grade == lastGrade { + mc.rank = lastRank + } + lastRank = mc.rank + lastGrade = mc.grade + } + } +} + +func NewMatchContext(p *Player, tm *TmMatch, grade, snid, lv, roleId int32, seq int) *PlayerMatchContext { + if !p.IsRob { + snid = p.SnId + } + return &PlayerMatchContext{ + tm: tm, + p: p, + grade: grade, + seq: seq, + rank: int32(seq), + copySnid: snid, + copyLv: lv, + copyRoleId: roleId, + } +} diff --git a/worldsrv/matchscenemgr.go b/worldsrv/matchscenemgr.go new file mode 100644 index 0000000..8b83b2f --- /dev/null +++ b/worldsrv/matchscenemgr.go @@ -0,0 +1,180 @@ +package main + +import ( + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" +) + +var MatchSceneMgrSingleton = &MatchSceneMgr{ + scenes: make(map[int]*Scene), +} + +// MatchSceneMgr 比赛场房间管理器 +type MatchSceneMgr struct { + scenes map[int]*Scene // 比赛场房间,房间id:房间数据 +} + +// NewScene 创建比赛场房间 +// tm 一场比赛,数据 +// isFinals 是否决赛 +// round 第几轮 +func (ms *MatchSceneMgr) NewScene(tm *TmMatch, isFinals bool, round int32) *Scene { + sceneId := SceneMgrSingleton.GenOneMatchSceneId() + + gameId := int(tm.dbGameFree.GameId) + gameMode := tm.dbGameFree.GetGameMode() + + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Warn("not found game server, gameid: ", gameId) + return nil + } + + limitPlatform := PlatformMgrSingleton.GetPlatform(tm.Platform) + if limitPlatform == nil || !limitPlatform.Isolated { + limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform) + } + + finals := int32(0) // 是否决赛 + if isFinals { + finals = 1 + } + curPlayerNum := int32(1) // 本轮比赛总人数 + nextNeed := int32(0) // 下一轮比赛总人数 + if tm.gmd != nil && len(tm.gmd.MatchPromotion) >= int(round) { + curPlayerNum = tm.gmd.MatchPromotion[round-1] + if curPlayerNum == 1 { //最后一局特殊处理下,取倒数第二局人数 + curPlayerNum = tm.gmd.MatchPromotion[round-2] + } + if len(tm.gmd.MatchPromotion) > int(round) { + nextNeed = tm.gmd.MatchPromotion[round] + } + } + groupId := PlatformMgrSingleton.GetGameFreeGroup(tm.Platform, tm.dbGameFree.Id) + // 建房参数 + // 比赛唯一索引,是否决赛,第几轮,本轮总人数,下一轮总人数,赛制类型 + params := []int32{tm.SortId, finals, round, curPlayerNum, nextNeed, tm.gmd.MatchType} + + scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, gameId, int(gameMode), common.SceneMode_Match, 1, + 0, params, gs, limitPlatform, groupId, tm.dbGameFree) + if scene != nil { + scene.matchId = tm.SortId + return scene + } + return nil +} + +func (ms *MatchSceneMgr) MatchStart(tm *TmMatch) { + scene := ms.NewScene(tm, false, 1) + if scene != nil { + ms.scenes[scene.sceneId] = scene + } + for _, tmp := range tm.TmPlayer { //先进真人 + if scene == nil || scene.IsFull() { + scene = ms.NewScene(tm, false, 1) + if scene != nil { + ms.scenes[scene.sceneId] = scene + } + } + p := PlayerMgrSington.GetPlayerBySnId(tmp.SnId) + if p != nil && p.scene == nil { + mc := TournamentMgr.CreatePlayerMatchContext(p, tm, tmp.seq) + if mc != nil { + mc.gaming = true + scene.PlayerEnter(mc.p, -1, true) + } + } else { + if p != nil { + logger.Logger.Error("MatchStart error: snid: ", p.SnId, " p.scene: ", p.scene) + } + } + } + + if scene != nil && !scene.IsFull() { //填充机器人 + tm.RobotGradesDecline(1) + needRobotNum := scene.playerNum - len(scene.players) + logger.Logger.Trace("MatchStart 填充机器人", needRobotNum) + pack := &server.WGInviteMatchRob{ + Platform: proto.String(tm.Platform), + MatchId: proto.Int32(tm.TMId), + RobNum: proto.Int(needRobotNum), + NeedAwait: proto.Bool(true), + RoomId: proto.Int(scene.sceneId), + } + SendToGame(int(tm.dbGameFree.GameId), int(server.SSPacketID_PACKET_WG_INVITEMATCHROB), pack) + } +} + +func (ms *MatchSceneMgr) NewRoundStart(tm *TmMatch, mct []*PlayerMatchContext, finals bool, round int32) { + scene := ms.NewScene(tm, finals, round) + if scene != nil { + ms.scenes[scene.sceneId] = scene + } + for _, tmp := range mct { + if scene == nil || scene.IsFull() { + scene = ms.NewScene(tm, finals, round) + if scene != nil { + ms.scenes[scene.sceneId] = scene + } + } + p := tmp.p + if p != nil && p.scene == nil { + if mc, ok := TournamentMgr.players[tm.SortId][p.SnId]; ok { + mc.gaming = true + mc.grade = mc.grade * 75 / 100 //积分衰减 + mc.rank = tmp.rank + scene.PlayerEnter(mc.p, -1, true) + } + } else { + if p != nil { + logger.Logger.Error("NewRoundStart error: snid: ", p.SnId, " p.scene: ", p.scene) + } + } + } + + if scene != nil && !scene.IsFull() { //填充机器人 + tm.RobotGradesDecline(int(round)) + needRobotNum := scene.playerNum - len(scene.players) + logger.Logger.Trace("NewRoundStart 填充机器人", needRobotNum) + pack := &server.WGInviteMatchRob{ + Platform: proto.String(tm.Platform), + MatchId: proto.Int32(tm.TMId), + RobNum: proto.Int(needRobotNum), + NeedAwait: proto.Bool(true), + RoomId: proto.Int(scene.sceneId), + } + SendToGame(int(tm.dbGameFree.GameId), int(server.SSPacketID_PACKET_WG_INVITEMATCHROB), pack) + } +} + +func (ms *MatchSceneMgr) PlayerLeave(p *Player, reason int) bool { + if p == nil || p.scene == nil { + return true + } + if p.scene.matchId == 0 { + return true + } + p.scene.PlayerLeave(p, reason) + return true +} + +func (ms *MatchSceneMgr) OnDestroyScene(sceneId int) { + _, has := ms.scenes[sceneId] + if !has { + return + } + delete(ms.scenes, sceneId) +} + +func (ms *MatchSceneMgr) MatchStop(tm *TmMatch) { + if SceneMgrSingleton.scenes != nil && tm != nil { + for _, scene := range SceneMgrSingleton.scenes { + if scene.IsMatchScene() && scene.matchId == tm.SortId { + scene.ForceDelete(false) + } + } + } +} diff --git a/worldsrv/matchseasonmgr.go b/worldsrv/matchseasonmgr.go new file mode 100644 index 0000000..b8422d0 --- /dev/null +++ b/worldsrv/matchseasonmgr.go @@ -0,0 +1,393 @@ +package main + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tournament" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "sort" + "strconv" + "time" +) + +var MatchSeasonMgrSington = &MatchSeasonMgr{ + MatchSeasonList: make(map[int32]*MatchSeason), + MatchSeasonId: make(map[string]*MatchSeasonId), +} + +type MatchSeasonMgr struct { + BaseClockSinker + MatchSeasonList map[int32]*MatchSeason // snid:玩家赛季信息 + MatchSeasonId map[string]*MatchSeasonId // platform:平台赛季信息 +} + +// MatchSeason 玩家赛季信息 +type MatchSeason struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + Name string + SeasonId int32 //赛季id + Lv int32 //段位 + LastLv int32 //上赛季段位 + IsAward bool //上赛季是否领奖 + AwardTs int64 //领奖时间 + UpdateTs int64 + dirty bool +} + +// MatchSeasonId 赛季信息 +type MatchSeasonId struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SeasonId int32 //赛季id + StartStamp int64 //开始时间戳 + EndStamp int64 //结束时间戳 + UpdateTs int64 //更新时间戳 +} + +func (this *MatchSeasonMgr) exchangeModel2Cache(mms *model.MatchSeason) *MatchSeason { + if mms == nil { + return nil + } + ms := &MatchSeason{ + Id: mms.Id, + Platform: mms.Platform, + SnId: mms.SnId, + Name: mms.Name, + Lv: mms.Lv, + LastLv: mms.LastLv, + IsAward: mms.IsAward, + AwardTs: mms.AwardTs, + SeasonId: mms.SeasonId, + UpdateTs: mms.UpdateTs, + } + return ms +} + +func (this *MatchSeasonMgr) GetMatchSeason(snid int32) *MatchSeason { + return this.MatchSeasonList[snid] +} + +// GetAllMatchSeason 获取所有玩家赛季信息 +func (this *MatchSeasonMgr) GetAllMatchSeason() map[int32]*MatchSeason { + return this.MatchSeasonList +} + +func (this *MatchSeasonMgr) SetMatchSeason(ms *MatchSeason) { + if ms == nil { + return + } + this.MatchSeasonList[ms.SnId] = ms +} + +func (this *MatchSeasonMgr) DelMatchSeasonCache(snid int32) { + if this.MatchSeasonList[snid] == nil { + return + } + delete(this.MatchSeasonList, snid) +} + +// UpdateMatchSeasonLv 修改玩家段位 +// 通知段位变更 +// 更新排行榜 +func (this *MatchSeasonMgr) UpdateMatchSeasonLv(p *Player, addlv int32, dirty bool) { + logger.Logger.Trace("(this *MatchSeasonMgr) UpdateMatchSeasonLv: SnId: ", p.SnId, " addlv: ", addlv) + if p == nil || p.IsRob { + return + } + platform := p.Platform + if platform == DefaultPlatform { + return + } + ms := this.GetMatchSeason(p.SnId) + if ms != nil { + ms.Lv = ms.Lv + addlv + ms.dirty = true + ms.UpdateTs = time.Now().Unix() + msid := this.GetMatchSeasonId(platform) + if msid != nil { + if addlv != 0 || dirty { //段位有变化或者需要继承 + //通知客户端段位更新 + pack := &tournament.SCTMSeasonInfo{ + Id: msid.SeasonId, + SeasonTimeStamp: []int64{msid.StartStamp, msid.EndStamp}, + Lv: ms.Lv, + LastLv: ms.LastLv, + IsAward: ms.IsAward, + } + proto.SetDefaults(pack) + ok := p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMSeasonInfo), pack) + logger.Logger.Trace("SCTMSeasonInfoHandler: ok: ", ok, pack) + } + //更新排行榜 + logger.Logger.Trace("更新排行榜!!!") + msrs := MatchSeasonRankMgrSington.GetMatchSeasonRank(platform) + if msrs == nil { //排行榜没有数据 去缓存中取 + ams := MatchSeasonMgrSington.GetAllMatchSeason() + mss := []*model.MatchSeason{} + if ams != nil { + for _, season := range ams { + if season.Platform == platform { + mms := &model.MatchSeason{ + Id: season.Id, + Platform: season.Platform, + SnId: season.SnId, + Name: season.Name, + SeasonId: season.SeasonId, + Lv: season.Lv, + LastLv: season.LastLv, + IsAward: season.IsAward, + AwardTs: season.AwardTs, + } + mss = append(mss, mms) + } + } + } + if mss != nil && len(mss) > 0 { + cmsrs := []*MatchSeasonRank{} + sort.Slice(mss, func(i, j int) bool { + return mss[i].Lv > mss[j].Lv + }) + if len(mss) > model.GameParamData.MatchSeasonRankMaxNum { + mss = append(mss[:model.GameParamData.MatchSeasonRankMaxNum]) + } + for i := 0; i < len(mss); i++ { + season := mss[i] + msr := &MatchSeasonRank{ + Id: season.Id, + Platform: season.Platform, + SnId: season.SnId, + Name: season.Name, + Lv: season.Lv, + UpdateTs: season.UpdateTs, + } + cmsrs = append(cmsrs, msr) + } + MatchSeasonRankMgrSington.SetMatchSeasonRank(platform, cmsrs) + } + } + MatchSeasonRankMgrSington.UpdateMatchSeasonRank(p, ms.Lv) + } + } +} + +// MatchSeasonInherit 查询段位继承 +func (this *MatchSeasonMgr) MatchSeasonInherit(lv int32) int32 { + logger.Logger.Trace("(this *MatchSeasonMgr) MatchSeasonInherit: lv: ", lv) + destLv := int32(1) + for _, v := range srvdata.PBDB_GamMatchLVMgr.Datas.GetArr() { + if v.Star != nil && len(v.Star) > 1 { + startStar := v.Star[0] + endStar := v.Star[1] + if lv >= startStar && lv <= endStar { //匹配段位 + destLv = v.Star2 //继承后段位 + } + } + } + return destLv +} + +// UpdateMatchSeasonAward 更新领奖时间 +func (this *MatchSeasonMgr) UpdateMatchSeasonAward(snid int32) { + logger.Logger.Trace("(this *MatchSeasonMgr) UpdateMatchSeasonAward ", snid) + ms := this.GetMatchSeason(snid) + if ms != nil { + ms.IsAward = true + ms.AwardTs = time.Now().Unix() + ms.UpdateTs = time.Now().Unix() + ms.dirty = true + } +} + +// SaveMatchSeasonData 保存玩家段位信息 +// logout 删除缓存 +func (this *MatchSeasonMgr) SaveMatchSeasonData(snid int32, logout bool) { + logger.Logger.Trace("(this *MatchSeasonMgr) SaveMatchSeasonData ", snid) + ms := this.MatchSeasonList[snid] + if ms != nil && ms.dirty { + ms.dirty = false + mms := &model.MatchSeason{ + Id: ms.Id, + Platform: ms.Platform, + SnId: ms.SnId, + Name: ms.Name, + Lv: ms.Lv, + LastLv: ms.LastLv, + IsAward: ms.IsAward, + AwardTs: ms.AwardTs, + SeasonId: ms.SeasonId, + UpdateTs: ms.UpdateTs, + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMatchSeason(mms) + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + logger.Logger.Info("SaveMatchSeasonData!!!") + if logout { + this.DelMatchSeasonCache(snid) + } + })).StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) + } +} + +// SaveAllMatchSeasonData 保存所有玩家段位信息 +func (this *MatchSeasonMgr) SaveAllMatchSeasonData() { + for _, msl := range this.MatchSeasonList { + this.SaveMatchSeasonData(msl.SnId, false) + } +} + +// UpdateMatchSeasonId 更新比赛场赛季配置 +// 更新赛季配置 +// 更新在线玩家段位 +// 更新排行榜 +func (this *MatchSeasonMgr) UpdateMatchSeasonId(platform string) { + logger.Logger.Info("(this *MatchSeasonMgr) UpdateMatchSeasonId") + if platform == DefaultPlatform { + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryMatchSeasonId(platform) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + var ret *model.MatchSeasonId + if data == nil || data.(*model.MatchSeasonId) == nil { + sstamp, estamp := this.getNowMonthStartAndEnd() + ret = model.NewMatchSeasonId(platform, int32(1), sstamp, estamp) //初始化赛季 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMatchSeasonId(ret) + }), nil).StartByFixExecutor("UpsertMatchSeasonId") + } else { + ret = data.(*model.MatchSeasonId) + } + logger.Logger.Info("UpdateMatchSeasonId!!!", ret) + if ret != nil { + nowStamp := time.Now().Unix() + if nowStamp < ret.StartStamp { + logger.Logger.Error("赛季开始时间错误!!!") + } + if nowStamp >= ret.EndStamp { //新赛季 + logger.Logger.Info("新赛季!!!", ret) + sstamp, estamp := this.getNowMonthStartAndEnd() + ret.SeasonId++ + ret.StartStamp = sstamp + ret.EndStamp = estamp + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMatchSeasonId(ret) + }), nil).StartByFixExecutor("UpsertMatchSeasonId") + //排行榜内的段位继承 + MatchSeasonRankMgrSington.MatchSeasonRankInherit(platform) + //通知平台玩家继承后的段位数据 + players := PlayerMgrSington.playerOfPlatform[platform] + for _, p := range players { + if p != nil && p.IsOnLine() && !p.IsRob { + ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId) + if ms != nil { + if ms.SeasonId < ret.SeasonId { //不同赛季段位继承 + num := ret.SeasonId - ms.SeasonId + finalLv := ms.Lv + for i := 0; i < int(num); i++ { //继承几次 + if i == int(num)-1 { //上个赛季 + ms.LastLv = finalLv + } + finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) + } + ms.Lv = finalLv + ms.SeasonId = ret.SeasonId + ms.IsAward = false + ms.UpdateTs = time.Now().Unix() + ms.dirty = true + MatchSeasonMgrSington.SetMatchSeason(ms) //更新缓存 + pack := &tournament.SCTMSeasonInfo{ + Id: ret.SeasonId, + SeasonTimeStamp: []int64{ret.StartStamp, ret.EndStamp}, + Lv: ms.Lv, + LastLv: ms.LastLv, + IsAward: ms.IsAward, + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCTMSeasonInfo:", p.SnId, " pack: ", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMSeasonInfo), pack) + } + } + } + } + } + this.MatchSeasonId[platform] = &MatchSeasonId{ + Id: ret.Id, + Platform: ret.Platform, + SeasonId: ret.SeasonId, + StartStamp: ret.StartStamp, + EndStamp: ret.EndStamp, + UpdateTs: ret.UpdateTs, + } + } + })).StartByFixExecutor("platform: " + platform) +} + +// GetMatchSeasonId 获取比赛场赛季配置 +func (this *MatchSeasonMgr) GetMatchSeasonId(platform string) *MatchSeasonId { + logger.Logger.Info("(this *MatchSeasonMgr) GetMatchSeasonId", platform) + return this.MatchSeasonId[platform] +} + +// 获取当月初和月末时间戳 +func (this *MatchSeasonMgr) getNowMonthStartAndEnd() (int64, int64) { + now := time.Now() + first := now.Format("2006-01") + "-01" + start, _ := time.ParseInLocation("2006-01-02", first, time.Local) + last := start.AddDate(0, 1, 0).Format("2006-01-02") + end, _ := time.ParseInLocation("2006-01-02", last, time.Local) + return start.Unix(), end.Unix() - 1 +} + +func (this *MatchSeasonMgr) ModuleName() string { + return "MatchSeasonMgr" +} + +func (this *MatchSeasonMgr) Init() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + this.UpdateMatchSeasonId(platform.IdStr) + } +} + +func (this *MatchSeasonMgr) Update() { + this.SaveAllMatchSeasonData() +} + +func (this *MatchSeasonMgr) Shutdown() { + this.SaveAllMatchSeasonData() + module.UnregisteModule(this) +} + +func (this *MatchSeasonMgr) InterestClockEvent() int { + //TODO implement me + //panic("implement me") + return 1 << CLOCK_EVENT_MONTH +} + +func (this *MatchSeasonMgr) OnMonthTimer() { + logger.Logger.Info("(this *MatchSeasonMgr) OnMonthTimer") + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + this.UpdateMatchSeasonId(platform.IdStr) + } +} + +func init() { + module.RegisteModule(MatchSeasonMgrSington, time.Minute*1, 0) + ClockMgrSington.RegisteSinker(MatchSeasonMgrSington) +} diff --git a/worldsrv/matchseasonrankmgr.go b/worldsrv/matchseasonrankmgr.go new file mode 100644 index 0000000..d570bf8 --- /dev/null +++ b/worldsrv/matchseasonrankmgr.go @@ -0,0 +1,379 @@ +package main + +import ( + "github.com/globalsign/mgo/bson" + "math/rand" + "mongo.games.com/game/model" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "sort" + "time" +) + +var MatchSeasonRankMgrSington = &MatchSeasonRankMgr{ + MatchSeasonRank: make(map[string][]*MatchSeasonRank), + MatchSeasonRankDirty: make(map[string]bool), + RobotMatchSeasonRankInit: make(map[string]bool), + RobotMatchSeasonRank: make(map[string][]*MatchSeasonRank), +} + +type MatchSeasonRankMgr struct { + BaseClockSinker + MatchSeasonRank map[string][]*MatchSeasonRank //平台 + MatchSeasonRankDirty map[string]bool + RobotMatchSeasonRankInit map[string]bool + RobotMatchSeasonRank map[string][]*MatchSeasonRank //平台 +} + +type MatchSeasonRank struct { + Id bson.ObjectId `bson:"_id"` + Platform string + SnId int32 + Name string + Lv int32 //段位 + UpdateTs int64 +} + +func (this *MatchSeasonRankMgr) UpdateMatchSeasonRank(p *Player, lv int32) { + logger.Logger.Trace("(this *MatchSeasonRankMgr) UpdateMatchSeasonRank: SnId: ", p.SnId, " lv: ", lv) + platform := p.Platform + msrs := this.GetMatchSeasonRank(platform) + if msrs == nil { + msrs = []*MatchSeasonRank{} + } + have := false + for _, msr := range msrs { + if msr.SnId == p.SnId { + msr.Lv = lv + msr.UpdateTs = time.Now().Unix() + have = true + break + } + } + if !have { + msr := &MatchSeasonRank{ + Id: bson.NewObjectId(), + Platform: platform, + SnId: p.SnId, + Name: p.Name, + Lv: lv, + UpdateTs: time.Now().Unix(), + } + msrs = append(msrs, msr) + } + + sort.Slice(msrs, func(i, j int) bool { + return msrs[i].Lv > msrs[j].Lv + }) + if len(msrs) > model.GameParamData.MatchSeasonRankMaxNum { + if msrs[len(msrs)-1].SnId != p.SnId { //上榜玩家有变化 + this.MatchSeasonRankDirty[platform] = true + } + msrs = append(msrs[:model.GameParamData.MatchSeasonRankMaxNum]) + } else { + this.MatchSeasonRankDirty[platform] = true + } + this.MatchSeasonRank[platform] = msrs +} + +func (this *MatchSeasonRankMgr) GetMatchSeasonRank(platform string) []*MatchSeasonRank { + logger.Logger.Trace("(this *MatchSeasonRankMgr) GetMatchSeasonRank: platform = ", platform) + return this.MatchSeasonRank[platform] +} + +func (this *MatchSeasonRankMgr) SetMatchSeasonRank(platform string, mss []*MatchSeasonRank) { + logger.Logger.Trace("(this *MatchSeasonRankMgr) SetMatchSeasonRank: mss = ", mss) + this.MatchSeasonRank[platform] = mss + this.MatchSeasonRankDirty[platform] = true +} + +// MatchSeasonRankInherit 段位继承 +func (this *MatchSeasonRankMgr) MatchSeasonRankInherit(platform string) { + msr := this.GetMatchSeasonRank(platform) + logger.Logger.Trace("(this *MatchSeasonRankMgr) MatchSeasonRankInherit: msr = ", msr) + if msr == nil { + return + } + for _, rank := range msr { + rank.Lv = MatchSeasonMgrSington.MatchSeasonInherit(rank.Lv) + } + this.SetMatchSeasonRank(platform, msr) +} + +func (this *MatchSeasonRankMgr) InitMatchSeasonRank(platform string) { + logger.Logger.Trace("(this *MatchSeasonRankMgr) InitMatchSeasonRank: ", platform) + if platform == DefaultPlatform { + return + } + if this.MatchSeasonRank[platform] != nil { + return + } + if this.MatchSeasonRank[platform] == nil { + logger.Logger.Trace("(this *MatchSeasonRankMgr) InitMatchSeasonRank: ", this.MatchSeasonRank[platform]) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryMatchSeasonRank(platform) + logger.Logger.Trace("(this *MatchSeasonRankMgr) 1 QueryMatchSeasonRank: ", ret) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + var ret []*model.MatchSeasonRank + if data == nil || data.([]*model.MatchSeasonRank) == nil { //初始数据去log_matchseason里面取段位前n名 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + retRank, err := model.QueryMatchSeason(platform) + logger.Logger.Trace("(this *MatchSeasonRankMgr) 1 QueryMatchSeason: ", ret) + if err != nil { + return nil + } + return retRank + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + var retRank []*model.MatchSeason + logger.Logger.Trace("(this *MatchSeasonRankMgr) 2 QueryMatchSeason: ", ret) + if data == nil || data.([]*model.MatchSeason) == nil { + ams := MatchSeasonMgrSington.GetAllMatchSeason() + if ams != nil { + for _, season := range ams { + if season.Platform == platform { + mms := &model.MatchSeason{ + Id: season.Id, + Platform: season.Platform, + SnId: season.SnId, + Name: season.Name, + SeasonId: season.SeasonId, + Lv: season.Lv, + LastLv: season.LastLv, + IsAward: season.IsAward, + AwardTs: season.AwardTs, + } + retRank = append(retRank, mms) + } + } + } + } else { + retRank = data.([]*model.MatchSeason) + } + if retRank != nil { + this.MatchSeasonRank[platform] = []*MatchSeasonRank{} + sort.Slice(retRank, func(i, j int) bool { + return retRank[i].Lv > retRank[j].Lv + }) + if len(retRank) > model.GameParamData.MatchSeasonRankMaxNum { + retRank = append(retRank[:model.GameParamData.MatchSeasonRankMaxNum]) + } + for i := 0; i < len(retRank); i++ { + season := retRank[i] + msr := &MatchSeasonRank{ + Id: season.Id, + Platform: season.Platform, + SnId: season.SnId, + Name: season.Name, + Lv: season.Lv, + UpdateTs: season.UpdateTs, + } + this.MatchSeasonRank[platform] = append(this.MatchSeasonRank[platform], msr) + this.MatchSeasonRankDirty[platform] = true + } + logger.Logger.Trace("(this *MatchSeasonRankMgr) 3 QueryMatchSeason: ", this.MatchSeasonRank[platform]) + } + })).StartByFixExecutor("platform:" + platform) + } else { + ret = data.([]*model.MatchSeasonRank) + this.MatchSeasonRank[platform] = []*MatchSeasonRank{} + for _, rank := range ret { + msr := &MatchSeasonRank{ + Id: rank.Id, + Platform: rank.Platform, + SnId: rank.SnId, + Name: rank.Name, + Lv: rank.Lv, + UpdateTs: rank.UpdateTs, + } + this.MatchSeasonRank[platform] = append(this.MatchSeasonRank[platform], msr) + } + logger.Logger.Trace("(this *MatchSeasonRankMgr) 3 QueryMatchSeasonRank: ", this.MatchSeasonRank[platform]) + } + })).StartByFixExecutor("platform:" + platform) + } +} + +// SaveMatchSeasonRank 保存排行榜 +func (this *MatchSeasonRankMgr) SaveMatchSeasonRank(platform string) { + logger.Logger.Trace("(this *MatchSeasonRankMgr) SaveMatchSeasonRank: ", platform) + msrp := this.MatchSeasonRank[platform] + if msrp != nil && this.MatchSeasonRankDirty[platform] { + this.MatchSeasonRankDirty[platform] = false + dirtyMsrs := []*model.MatchSeasonRank{} + for _, rank := range msrp { + msr := &model.MatchSeasonRank{ + Id: rank.Id, + Platform: rank.Platform, + SnId: rank.SnId, + Name: rank.Name, + Lv: rank.Lv, + UpdateTs: rank.UpdateTs, + } + dirtyMsrs = append(dirtyMsrs, msr) + } + if dirtyMsrs != nil && len(dirtyMsrs) > 0 { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpsertMatchSeasonRank(platform, dirtyMsrs) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + })).StartByFixExecutor("platform:" + platform) + } + } +} + +func (this *MatchSeasonRankMgr) SaveAllMatchSeasonRank() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + this.SaveMatchSeasonRank(platform.IdStr) + } +} + +func (this *MatchSeasonRankMgr) CreateRobotLv() int32 { + Lv := int32(1) + now := time.Now() + first := now.Format("2006-01") + "-01" + start, _ := time.ParseInLocation("2006-01-02", first, time.Local) + diffUnix := now.Unix() - start.Unix() + diffDay := diffUnix/int64(24*60*60) + 1 + data := srvdata.PBDB_MatchRankMgr.GetData(int32(diffDay)) + if data != nil && data.RankStar != nil && len(data.RankStar) > 0 { + diff := data.RankStar[1] - data.RankStar[0] + min := data.RankStar[0] + if data.RankStar[0] > data.RankStar[1] { + diff = data.RankStar[0] - data.RankStar[1] + min = data.RankStar[1] + } + Lv = rand.Int31n(diff) + min + } + return Lv +} + +func (this *MatchSeasonRankMgr) CreateRobotMatchSeasonRank(platform string) { + if this.RobotMatchSeasonRankInit[platform] { + return + } + this.RobotMatchSeasonRank[platform] = []*MatchSeasonRank{} + for _, player := range PlayerMgrSington.snidMap { + if player != nil && player.IsRob { + msr := &MatchSeasonRank{ + Platform: platform, + SnId: player.SnId, + Name: player.Name, + Lv: this.CreateRobotLv(), + } + this.RobotMatchSeasonRank[platform] = append(this.RobotMatchSeasonRank[platform], msr) + } + if len(this.RobotMatchSeasonRank[platform]) >= model.GameParamData.MatchSeasonRankMaxNum { + break + } + } + if len(this.RobotMatchSeasonRank[platform]) < model.GameParamData.MatchSeasonRankMaxNum { + localSnids := []int32{102917700, 61096000, 21058800, 47291500, 58562600, 22127000, 80639700, 49475400, 60569500, 54746600, 46797900, 88659800, 61118200, 68773200, 92010700, 13305900, 68143500, 86379100, 76177100, 95050900, 23954400, 52524000, 63618100, 31808400, 26929400, 108083700, 50751500, 92179900, 60327700, 69582700, 80156500, 30808000, 53806700, 53235700, 50049200, 30465400, 76672700, 69638500, 12351800, 48705200, 98920500, 32158900, 33519000, 42915300, 30811200, 77037600, 65779800, 22148100, 59819100, 46374600} + pool := srvdata.PBDB_NameMgr.Datas.GetArr() + cnt := int32(len(pool)) + for _, snid := range localSnids { + msr := &MatchSeasonRank{ + Platform: platform, + SnId: snid, + Name: "Guest", + Lv: this.CreateRobotLv(), + } + if cnt > 0 { + msr.Name = pool[rand.Int31n(cnt)].GetName() + } + this.RobotMatchSeasonRank[platform] = append(this.RobotMatchSeasonRank[platform], msr) + if len(this.RobotMatchSeasonRank[platform]) >= model.GameParamData.MatchSeasonRankMaxNum { + break + } + } + } + this.RobotMatchSeasonRankInit[platform] = true +} + +func (this *MatchSeasonRankMgr) GetRobotMatchSeasonRank(platform string) []*MatchSeasonRank { + logger.Logger.Trace("GetRobotMatchSeasonRank: ", platform) + if !this.RobotMatchSeasonRankInit[platform] { + this.CreateRobotMatchSeasonRank(platform) + } + if this.RobotMatchSeasonRank == nil || this.RobotMatchSeasonRank[platform] == nil || len(this.RobotMatchSeasonRank[platform]) < model.GameParamData.MatchSeasonRankMaxNum { + this.CreateRobotMatchSeasonRank(platform) + } + return this.RobotMatchSeasonRank[platform] +} + +func (this *MatchSeasonRankMgr) UpdateRobotMatchSeasonRank(platform string) { + logger.Logger.Trace("UpdateRobotMatchSeasonRank: ", platform) + rmsr := this.GetRobotMatchSeasonRank(platform) + if rmsr != nil { + for _, rank := range rmsr { + diff := rand.Int31n(7) - 3 + rank.Lv += diff + } + } +} + +func (this *MatchSeasonRankMgr) ModuleName() string { + return "MatchSeasonRankMgr" +} + +func (this *MatchSeasonRankMgr) Init() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + this.InitMatchSeasonRank(platform.IdStr) + this.RobotMatchSeasonRankInit[platform.IdStr] = false + } +} + +func (this *MatchSeasonRankMgr) Update() { + this.SaveAllMatchSeasonRank() +} + +func (this *MatchSeasonRankMgr) Shutdown() { + this.SaveAllMatchSeasonRank() + module.UnregisteModule(this) +} + +func (this *MatchSeasonRankMgr) InterestClockEvent() int { + //TODO implement me + //panic("implement me") + return 1< common.MiniGameSceneMaxId { +// this.autoId = common.MiniGameSceneStartId +// } +// return this.autoId +//} +// +//func (this *MiniGameMgr) PlayerEnter(p *Player, id int32) mngame.MNGameOpResultCode { +// plt := p.GetPlatform() +// s := this.GetScene(plt, id) +// if s == nil { +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Error +// } +// +// if !s.PlayerEnterMiniGame(p) { +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Error +// } +// +// gamings, ok := this.playerGaming[p.SnId] +// if !ok { +// gamings = make(map[int32]*Scene) +// this.playerGaming[p.SnId] = gamings +// } +// gamings[id] = s +// +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Sucess +//} +// +//func (this *MiniGameMgr) PlayerLeave(p *Player, id int32) mngame.MNGameOpResultCode { +// plt := p.GetPlatform() +// s := this.GetScene(plt, id) +// if s == nil { +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Error +// } +// +// if !s.PlayerLeaveMiniGame(p) { +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Error +// } +// +// gamings, ok := this.playerGaming[p.SnId] +// if ok { +// delete(gamings, id) +// } +// +// return mngame.MNGameOpResultCode_MNGAME_OPRC_Sucess +//} +// +//func (this *MiniGameMgr) PlayerMsgDispatcher(p *Player, msg *mngame.CSMNGameDispatcher) { +// plt := p.GetPlatform() +// s := this.GetScene(plt, msg.GetId()) +// if s == nil { +// logger.Logger.Errorf("MiniGameMgr.PlayerMsgDispatcher Can't find scene! plt:%v gameId:%v", plt, msg.GetId()) +// return +// } +// +// //minigamesrv 重启容错 +// if !s.HasPlayer(p) { +// this.PlayerEnter(p, msg.GetId()) +// } +// s.RedirectMiniGameMsg(p, msg) +//} +// +//func (this *MiniGameMgr) GetScene(p *Platform, id int32) *Scene { +// scenes, ok := this.scenesOfPlatform[p.IdStr] +// if !ok { +// scenes = make(map[int32]*Scene) +// this.scenesOfPlatform[p.IdStr] = scenes +// } +// +// s, ok := scenes[id] +// if !ok { +// cfg := PlatformMgrSingleton.GetGameFree(p.IdStr, id) +// if cfg != nil && cfg.Status && cfg.DbGameFree.GetGameType() == common.GameType_Mini { +// s = this.CreateSceneByPlatform(p, cfg) +// if s != nil { +// scenes[cfg.DbGameFree.Id] = s +// } else { +// return nil +// } +// return s +// } else { +// return nil +// } +// } else { +// return s +// } +// //return nil +//} +// +//func (this *MiniGameMgr) CreateSceneByPlatform(p *Platform, cfg *webapi_proto.GameFree) *Scene { +// sceneId := this.GenOneSceneId() +// gameId := int(cfg.DbGameFree.GetGameId()) +// gs := GameSessMgrSington.GetMinLoadSess(gameId) +// if gs == nil { +// logger.Logger.Errorf("MiniGameMgr.CreateSceneByPlatform Get %v game min session failed.", gameId) +// return nil +// } +// if gs != nil { +// gameMode := cfg.DbGameFree.GetGameMode() +// dbGameRule := srvdata.PBDB_GameRuleMgr.GetData(cfg.DbGameFree.GetGameRule()) +// params := dbGameRule.GetParams() +// scene := SceneMgrSington.CreateScene(0, 0, sceneId, gameId, int(gameMode), common.SceneMode_Public, 1, -1, params, +// gs, p, cfg.GroupId, cfg.DbGameFree, cfg.DbGameFree.Id) +// if scene != nil { +// scene.hallId = cfg.DbGameFree.Id +// return scene +// } +// } +// return nil +//} +// +//func (this *MiniGameMgr) OnPlatformCreate(p *Platform) { +// if p == nil { +// return +// } +// scenes := make(map[int32]*Scene) +// this.scenesOfPlatform[p.IdStr] = scenes +// +// gps := PlatformMgrSingleton.GetGameFrees(p.IdStr) +// for _, v := range gps { +// if v.Status && v.DbGameFree.GetGameType() == common.GameType_Mini { +// s := this.CreateSceneByPlatform(p, v) +// if s != nil { +// scenes[v.DbGameFree.Id] = s +// } +// } +// } +//} +// +//func (this *MiniGameMgr) OnPlatformDestroy(p *Platform) { +// if p == nil { +// return +// } +// if scenes, ok := this.scenesOfPlatform[p.IdStr]; ok { +// for _, s := range scenes { +// pack := &server_proto.WGGraceDestroyScene{} +// pack.Ids = append(pack.Ids, int32(s.sceneId)) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack) +// } +// delete(this.scenesOfPlatform, p.IdStr) +// } +//} +// +//func (this *MiniGameMgr) OnPlatformChangeIsolated(p *Platform, isolated bool) { +// if p == nil { +// return +// } +// if !isolated { +// this.OnPlatformDestroy(p) +// } +//} +// +//func (this *MiniGameMgr) OnPlatformChangeDisabled(p *Platform, disabled bool) { +// if p == nil { +// return +// } +// if disabled { +// this.OnPlatformDestroy(p) +// } else { +// this.OnPlatformCreate(p) +// } +//} +// +//func (this *MiniGameMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapi_proto.GameFree) { +// if p == nil { +// return +// } +// if scenes, ok := this.scenesOfPlatform[p.IdStr]; ok { +// if oldCfg != nil { +// if s, ok := scenes[oldCfg.DbGameFree.Id]; ok { +// pack := &server_proto.WGGraceDestroyScene{} +// pack.Ids = append(pack.Ids, int32(s.sceneId)) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack) +// delete(scenes, oldCfg.DbGameFree.Id) +// } +// } else if newCfg != nil { +// if newCfg.Status && newCfg.DbGameFree.GetGameType() == common.GameType_Mini { +// s := this.CreateSceneByPlatform(p, newCfg) +// if s != nil { +// scenes[newCfg.DbGameFree.Id] = s +// } +// } +// } +// } +//} +// +//func (this *MiniGameMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi_proto.GameConfigGroup) { +// //donothing +//} +// +///* +//获取platform下面对应的 player SnId所在的scene +//*/ +//func (this *MiniGameMgr) GetAllSceneByPlayer(p *Player) map[int32]*Scene { +// if gameingScenes, ok := this.playerGaming[p.SnId]; ok { +// return gameingScenes +// } +// return nil +//} +// +//func (this *MiniGameMgr) OnPlayerDropLine(p *Player) { +// this.BasePlayerListener.OnPlayerDropLine(p) +// if gamingScenes, ok := this.playerGaming[p.SnId]; ok { +// for _, s := range gamingScenes { +// pack := &server_proto.WGPlayerDropLine{ +// Id: proto.Int32(p.SnId), +// SceneId: proto.Int(s.sceneId), +// } +// proto.SetDefaults(pack) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERDROPLINE), pack) +// } +// } +//} +// +//func (this *MiniGameMgr) OnPlayerRehold(p *Player) { +// this.BasePlayerListener.OnPlayerRehold(p) +// var gateSid int64 +// if p.gateSess != nil { +// if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { +// sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) +// gateSid = sessionId.Get() +// } +// } +// if gamingScenes, ok := this.playerGaming[p.SnId]; ok { +// for _, s := range gamingScenes { +// pack := &server_proto.WGPlayerRehold{ +// Id: proto.Int32(p.SnId), +// Sid: proto.Int64(p.sid), +// SceneId: proto.Int(s.sceneId), +// GateSid: proto.Int64(gateSid), +// } +// proto.SetDefaults(pack) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERREHOLD), pack) +// } +// } +//} +//func (this *MiniGameMgr) OnPlayerReturnScene(p *Player) { +// this.BasePlayerListener.OnPlayerReturnScene(p) +// if gameingScenes, ok := this.playerGaming[p.SnId]; ok { +// for _, s := range gameingScenes { +// pack := &server_proto.WGPlayerReturn{ +// PlayerId: p.SnId, +// RoomId: int32(s.sceneId), +// } +// proto.SetDefaults(pack) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERRETURN), pack) +// } +// } +//} +// +//func (this *MiniGameMgr) OnDestroyScene(s *Scene) { +// +// if pltScenes, ok := this.scenesOfPlatform[s.limitPlatform.IdStr]; ok { +// delete(pltScenes, s.dbGameFree.Id) +// } +// +// for snid, _ := range s.players { +// if scenes, ok := this.playerGaming[snid]; ok { +// delete(scenes, s.dbGameFree.Id) +// if len(scenes) == 0 { +// delete(this.playerGaming, snid) +// } +// } +// } +//} +// +//func (this *MiniGameMgr) ClrPlayerWhiteBlackState(p *Player) { +// if gamings, ok := this.playerGaming[p.SnId]; ok { +// for _, s := range gamings { +// pack := &server_proto.WGSetPlayerBlackLevel{ +// SnId: proto.Int32(p.SnId), +// SceneId: proto.Int32(int32(s.sceneId)), +// ResetTotalCoin: proto.Bool(true), +// } +// proto.SetDefaults(pack) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_GW_AUTORELIEVEWBLEVEL), pack) +// } +// } +//} +// +//func (this *MiniGameMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { +// if p == nil { +// return +// } +// if scenes, ok := this.scenesOfPlatform[p.IdStr]; ok { +// for _, s := range scenes { +// if s.dbGameFree.Id == gameFreeId { +// pack := &server_proto.WGGraceDestroyScene{} +// pack.Ids = append(pack.Ids, int32(s.sceneId)) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_GRACE_DESTROYSCENE), pack) +// delete(scenes, gameFreeId) +// } +// } +// } +//} +// +//func init() { +// RegistePlayerListener(MiniGameMgrSington) +// PlatformMgrSingleton.RegisterObserver(MiniGameMgrSington) +// PlatformGameGroupMgrSington.RegisterObserver(MiniGameMgrSington) +//} diff --git a/worldsrv/monitormgr.go b/worldsrv/monitormgr.go new file mode 100644 index 0000000..b1a5747 --- /dev/null +++ b/worldsrv/monitormgr.go @@ -0,0 +1,168 @@ +package main + +import ( + "encoding/gob" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/profile" + "mongo.games.com/goserver/core/schedule" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/core/utils" + "time" +) + +var MonitorMgrSington = &MonitorMgr{} + +type MonitorMgr struct { +} + +func (this *MonitorMgr) ModuleName() string { + return "MonitorMgr" +} + +func (this *MonitorMgr) Init() { + +} + +func (this *MonitorMgr) Update() { + //player online stats + olStats := PlayerMgrSington.StatsOnline() + log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", olStats) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertMonitorData("online", log) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + + //api stats + if len(WebAPIStats) > 0 { + log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", WebAPIStats) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("webapi", log) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + apiStats := webapi.Stats() + if len(apiStats) > 0 { + log := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "api", apiStats) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("webapi", log) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //logic stats + logicStats := profile.GetStats() + if len(logicStats) > 0 { + logLogic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", logicStats) + if logLogic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("logic", logLogic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //net session stats + netStats := netlib.Stats() + if len(netStats) > 0 { + logNet := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", netStats) + if logNet != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("net", logNet) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //schedule stats + jobStats := schedule.Stats() + if len(jobStats) > 0 { + logJob := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", jobStats) + if logJob != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("job", logJob) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //trans stats + transStats := transact.Stats() + if len(transStats) > 0 { + logTrans := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", transStats) + if logTrans != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("transact", logTrans) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //panic stats + panicStats := utils.GetPanicStats() + if len(panicStats) > 0 { + for key, stats := range panicStats { + logPanic := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), key, stats) + if logPanic != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("panic", logPanic) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + } + + //object command quene stats + objStats := core.AppCtx.GetStats() + if len(objStats) > 0 { + logCmd := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "obj", objStats) + if logCmd != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpsertMonitorData("cmdque", logCmd) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } + } + + //gorouting count, eg. system info + runtimeStats := utils.StatsRuntime() + logRuntime := model.NewMonitorData(int32(common.GetSelfSrvId()), int32(common.GetSelfSrvType()), "", runtimeStats) + if logRuntime != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertMonitorData("runtime", logRuntime) + }), nil, "InsertMonitorData").StartByFixExecutor("monitor") + } +} + +func (this *MonitorMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + + //gob registe + gob.Register(model.PlayerOLStats{}) + gob.Register(map[string]*model.APITransactStats{}) + gob.Register(webapi.ApiStats{}) + gob.Register(map[string]webapi.ApiStats{}) + gob.Register(profile.TimeElement{}) + gob.Register(map[string]profile.TimeElement{}) + gob.Register(netlib.ServiceStats{}) + gob.Register(map[int]netlib.ServiceStats{}) + gob.Register(schedule.TaskStats{}) + gob.Register(map[string]schedule.TaskStats{}) + gob.Register(transact.TransStats{}) + gob.Register(map[int]transact.TransStats{}) + gob.Register(utils.PanicStackInfo{}) + gob.Register(map[string]utils.PanicStackInfo{}) + gob.Register(basic.CmdStats{}) + gob.Register(map[string]basic.CmdStats{}) + gob.Register(utils.RuntimeStats{}) + //gob registe + + module.RegisteModule(MonitorMgrSington, time.Minute*5, 0) + +} diff --git a/worldsrv/mq_coinlog.go b/worldsrv/mq_coinlog.go new file mode 100644 index 0000000..9425191 --- /dev/null +++ b/worldsrv/mq_coinlog.go @@ -0,0 +1,73 @@ +package main + +import ( + "encoding/json" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/broker" + "mongo.games.com/goserver/core/broker/rabbitmq" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/mq" +) + +func init() { + mq.RegisterSubscriber(model.TopicProbeCoinLogAck, func(e broker.Event) (err error) { + msg := e.Message() + if msg != nil { + defer func() { + e.Ack() + recover() + }() + + var log model.CoinLog + err = json.Unmarshal(msg.Body, &log) + if err != nil { + return + } + + //通知主线程执行后续操作 + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + player := PlayerMgrSington.GetPlayerBySnId(log.SnId) + if player != nil { + player.Coin += log.RestCount + player.SyncGameCoin(int(log.RoomId), log.SeqNo) + } + return nil + }), true) + return + } + return nil + }, broker.Queue(model.TopicProbeCoinLogAck), rabbitmq.DurableQueue()) + + // 绑定数量同步 + mq.RegisterSubscriber(model.AckBindNum, func(e broker.Event) error { + msg := e.Message() + if msg != nil { + defer func() { + e.Ack() + recover() + }() + + var log model.BindNum + err := json.Unmarshal(msg.Body, &log) + if err != nil { + return err + } + + //通知主线程执行后续操作 + core.CoreObject().SendCommand(basic.CommandWrapper(func(o *basic.Object) error { + player := PlayerMgrSington.GetPlayerBySnId(log.SnId) + if player != nil { + player.InviteNum = log.Num + player.ResetTaskN(common.TaskTypeInviteNum) + TaskSubjectSingleton.Touch(common.TaskTypeInviteNum, &TaskData{SnId: player.SnId, Num: int64(player.InviteNum)}) + } + return nil + }), true) + } + return nil + }, broker.Queue(model.AckBindNum), rabbitmq.DurableQueue()) +} diff --git a/worldsrv/msgmgr.go b/worldsrv/msgmgr.go new file mode 100644 index 0000000..90be8f9 --- /dev/null +++ b/worldsrv/msgmgr.go @@ -0,0 +1,59 @@ +package main + +import ( + "mongo.games.com/game/model" +) + +var MsgMgrSington = &MsgMgr{ + subscribeMsgs: make(map[string]*model.Message), +} + +type MsgMgr struct { + subscribeMsgs map[string]*model.Message +} + +func (mm *MsgMgr) InitMsg() { + for _, p := range PlatformMgrSingleton.GetPlatforms() { + msgs, err := model.GetSubscribeMessage(p.IdStr) + if err == nil { + mm.init(msgs) + } + } +} + +func (mm *MsgMgr) init(msgs []model.Message) { + for i := 0; i < len(msgs); i++ { + msg := msgs[i] + if msg.State != model.MSGSTATE_REMOVEED { + mm.subscribeMsgs[msg.Id.Hex()] = &msg + } + } +} + +func (mm *MsgMgr) AddMsg(msg *model.Message) { + if msg != nil { + mm.subscribeMsgs[msg.Id.Hex()] = msg + } +} +func (mm *MsgMgr) RemoveMsg(msg *model.Message) { + if msg != nil { + delete(mm.subscribeMsgs, msg.Id.Hex()) + } +} +func (mm *MsgMgr) GetSubscribeMsgs(platform string, ts int64) (msgs []*model.Message) { + for _, msg := range mm.subscribeMsgs { + if msg.CreatTs > ts { + if platform == "" || msg.Platform == platform { + msgs = append(msgs, msg) + } + } + } + return +} + +func init() { + RegisterParallelLoadFunc("平台邮件", func() error { + MsgMgrSington.InitMsg() + return nil + }) +} diff --git a/worldsrv/multicasthandler.go b/worldsrv/multicasthandler.go new file mode 100644 index 0000000..2a94ceb --- /dev/null +++ b/worldsrv/multicasthandler.go @@ -0,0 +1,67 @@ +package main + +import ( + rawproto "google.golang.org/protobuf/proto" + "mongo.games.com/game/proto" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + "mongo.games.com/goserver/srvlib/protocol" +) + +var ( + MulticastMaker = &MulticastPacketFactory{} +) + +type MulticastPacketFactory struct { +} + +type MulticastHandler struct { +} + +func init() { + netlib.RegisterHandler(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), &MulticastHandler{}) + netlib.RegisterFactory(int(protocol.SrvlibPacketID_PACKET_SS_MULTICAST), MulticastMaker) +} + +func (this *MulticastPacketFactory) CreatePacket() interface{} { + pack := &protocol.SSPacketMulticast{} + return pack +} + +func (this *MulticastPacketFactory) CreateMulticastPacket(packetid int, data interface{}, sis ...*protocol.MCSessionUnion) (rawproto.Message, error) { + pack := &protocol.SSPacketMulticast{ + Sessions: sis, + PacketId: proto.Int(packetid), + } + if byteData, ok := data.([]byte); ok { + pack.Data = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, data) + if err == nil { + pack.Data = byteData + } else { + logger.Logger.Info("MulticastPacketFactory.CreateMulticastPacket err:", err) + return nil, err + } + } + proto.SetDefaults(pack) + return pack, nil +} + +func (this *MulticastHandler) Process(s *netlib.Session, packetid int, data interface{}) error { + if mp, ok := data.(*protocol.SSPacketMulticast); ok { + pd := mp.GetData() + sis := mp.GetSessions() + for _, si := range sis { + ss := si.GetMcss() + if ss != nil { + ns := srvlib.ServerSessionMgrSington.GetSession(int(ss.GetSArea()), int(ss.GetSType()), int(ss.GetSId())) + if ns != nil { + ns.Send(int(mp.GetPacketId()), pd /*, s.GetSessionConfig().IsInnerLink*/) + } + } + } + } + return nil +} diff --git a/worldsrv/niceid.go b/worldsrv/niceid.go new file mode 100644 index 0000000..93689b9 --- /dev/null +++ b/worldsrv/niceid.go @@ -0,0 +1,113 @@ +package main + +import ( + "fmt" + "math/rand" + + "mongo.games.com/game/proto" + player_proto "mongo.games.com/game/protocol/player" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" +) + +var niceIdMgr = &NiceIdManager{ + SnIds: []int32{}, + UsedIds: make(map[int32]int32), +} + +type NiceIdManager struct { + SnIds []int32 + UsedIds map[int32]int32 + RobHeadUrlIdx int32 +} + +func (this *NiceIdManager) init() { + //this.SnIds = model.GetInvalidSnid() + //this.SnIds = make([]int32, 0, len(srvdata.PBDB_PlayerInfoMgr.Datas.Arr)) + //for _, value := range srvdata.PBDB_PlayerInfoMgr.Datas.Arr { + // this.SnIds = append(this.SnIds, value.GetId()) + //} + snidLen := len(this.SnIds) + logger.Logger.Info("NiceIdManager snid lens:", snidLen) + for i := 0; i < snidLen; i++ { + index := rand.Intn(snidLen) + this.SnIds[i], this.SnIds[index] = this.SnIds[index], this.SnIds[i] + } + for _, value := range niceIdMgr.SnIds { + this.UsedIds[value] = 0 + } +} +func (this *NiceIdManager) PopNiceId(user int32) int32 { + if len(this.SnIds) <= 0 { + return 0 + } + selId := this.SnIds[len(this.SnIds)-1] + this.SnIds = this.SnIds[:len(this.SnIds)-1] + this.UsedIds[selId] = user + logger.Logger.Infof("NiceIdManager pop niceid %v to %v", selId, user) + return selId +} +func (this *NiceIdManager) PushNiceId(snid int32) { + if _, ok := this.UsedIds[snid]; ok { + this.SnIds = append(this.SnIds, snid) + snidLen := len(this.SnIds) + index := rand.Intn(snidLen) + this.SnIds[snidLen-1], this.SnIds[index] = this.SnIds[index], this.SnIds[snidLen-1] + this.UsedIds[snid] = 0 + logger.Logger.Infof("NiceIdManager push niceid %v to cache", snid) + } +} +func (this *NiceIdManager) NiceIdCheck(playerid int32) { + logger.Logger.Infof("%v be used in NiceIdManager.", playerid) + if userid, ok := this.UsedIds[playerid]; ok { + delete(this.UsedIds, playerid) + if userid != 0 { + user := PlayerMgrSington.GetPlayerBySnId(userid) + if user != nil { + user.NiceId = this.PopNiceId(userid) + if user.scene != nil { + pack := &server_proto.WGNiceIdRebind{ + User: proto.Int32(userid), + NewId: proto.Int32(user.NiceId), + } + user.SendToGame(int(server_proto.SSPacketID_PACKET_GW_NICEIDREBIND), pack) + packNr := &player_proto.SCNiceIdRebind{ + SnidId: proto.Int32(userid), + NiceId: proto.Int32(user.NiceId), + } + user.scene.Broadcast(int(player_proto.PlayerPacketID_PACKET_SC_NICEIDREBIND), packNr, 0) + } + } + } else { + niceIndex := -1 + for key, value := range this.SnIds { + if value == playerid { + niceIndex = key + break + } + } + if niceIndex != -1 { + curCount := len(this.SnIds) + this.SnIds[niceIndex], this.SnIds[curCount-1] = this.SnIds[curCount-1], this.SnIds[niceIndex] + this.SnIds = this.SnIds[:curCount-1] + } + } + } +} +func (this *NiceIdManager) GetRobHeadUrlIdx() string { + this.RobHeadUrlIdx++ + if this.RobHeadUrlIdx > 1448 { + this.RobHeadUrlIdx = 1 + } + return fmt.Sprintf("https://avatar-icon.oss-cn-guangzhou.aliyuncs.com/user_head/head_%v.jpg", this.RobHeadUrlIdx) +} +func (this *NiceIdManager) GetRobHeadUrl(idx int32) string { + return fmt.Sprintf("https://avatar-icon.oss-cn-guangzhou.aliyuncs.com/user_head/head_%v.jpg", idx) +} +func init() { + core.RegisteHook(core.HOOK_BEFORE_START, func() error { + niceIdMgr.init() + return nil + }) +} diff --git a/worldsrv/npcserveragent.go b/worldsrv/npcserveragent.go new file mode 100644 index 0000000..891ad1f --- /dev/null +++ b/worldsrv/npcserveragent.go @@ -0,0 +1,47 @@ +package main + +//import ( +// "mongo.games.com/game/proto" +// "mongo.games.com/game/common" +// "mongo.games.com/game/protocol" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/srvlib" +//) +// +//var NpcServerAgentSington = &NpcServerAgent{} +// +//type NpcServerAgent struct { +//} +// +//func (nsa *NpcServerAgent) Invite(roomId, cnt int, isAgent bool, p *Player, matchId int32) bool { +// //logger.Logger.Trace("(nsa *NpcServerAgent) Invite", roomId, cnt, isAgent, matchId) +// npcSess := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), common.RobotServerType, common.RobotServerId) +// if npcSess != nil { +// if isAgent { +// cnt = 0 +// } +// pack := &protocol.WRInviteRobot{ +// RoomId: proto.Int(roomId), +// MatchId: proto.Int32(matchId), +// Cnt: proto.Int(cnt), +// } +// proto.SetDefaults(pack) +// npcSess.Send(int(protocol.MmoPacketID_PACKET_WR_INVITEROBOT), pack) +// return true +// } else { +// //logger.Logger.Error("Robot server not found.") +// } +// return false +//} +// +//func (nsa *NpcServerAgent) OnPlayerEnterScene(s *Scene, p *Player) { +// logger.Logger.Trace("(nsa *NpcServerAgent) OnPlayerEnterScene") +//} +// +//func (nsa *NpcServerAgent) OnPlayerLeaveScene(s *Scene, p *Player) { +// logger.Logger.Trace("(nsa *NpcServerAgent) OnPlayerLeaveScene") +//} +// +//func (nsa *NpcServerAgent) OnSceneClose(s *Scene) { +// logger.Logger.Trace("(nsa *NpcServerAgent) OnSceneClose") +//} diff --git a/worldsrv/petmgr.go b/worldsrv/petmgr.go new file mode 100644 index 0000000..b35fe3d --- /dev/null +++ b/worldsrv/petmgr.go @@ -0,0 +1,339 @@ +package main + +import ( + "time" + + "mongo.games.com/goserver/core/module" + + "mongo.games.com/game/common" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/pets" + "mongo.games.com/game/srvdata" +) + +const ( + ItemObtain = iota //得到 + ItemConsume //消耗 +) + +var PetMgrSington = &PetMgr{ + //RPInfos: make(map[int32]*RoleAndPetInfo), +} + +type PetMgr struct { + //RPInfos map[int32]*RoleAndPetInfo +} +type RoleAndPetInfo struct { + ModId int32 //模型id + Name string //名字 + Story string //人物背景介绍 + AwardTitle string //奖励标题 + MaxLevel int32 //最大等级 +} + +func (this *PetMgr) ModuleName() string { + return "PetMgr" +} + +// 人物 +func (this *PetMgr) GetRoleInfos(p *Player) []*pets.RoleInfo { + p.InitRolesAndPets() + + rolesInfo := srvdata.RolePetMgrSington.Roles + if rolesInfo != nil { + var newRoles = make([]*pets.RoleInfo, 0) + for roleId, roles := range rolesInfo { + level := p.Roles.ModUnlock[roleId] + if len(roles) >= int(level) { + rol := roles[level] + role := &pets.RoleInfo{ + Id: rol.Id, + RoleId: rol.RoleId, + Name: rol.Name, + Grade: rol.Grade, + Level: rol.Level, + Fragment: rol.Fragment, + Amount: rol.Amount, + AwardType: rol.AwardType, + Award: rol.Award, + AwardRate: rol.AwardRate, + } + if level > 0 { + role.IsUnlock = true + if p.Roles.ModId == roleId { + role.IsUsing = true + } + } + + if level == 0 { + role.NextAward = roles[1].Award + } else { + if int32(len(roles)) > level+1 { + role.NextAward = roles[level+1].Award + } + } + + roleOther := this.GetIntroductionByModId(role.RoleId) + role.MaxLevel = roleOther.MaxLevel + role.Name = roleOther.Name + role.Story = roleOther.Story + role.AwardTitle = roleOther.AwardTitle + /// + item := BagMgrSingleton.GetItem(p.SnId, role.Fragment) + if item != nil { + role.HaveAmount = int32(item.ItemNum) + } + newRoles = append(newRoles, role) + } + } + return newRoles + } + return nil +} +func (this *PetMgr) GetRoleInfo(p *Player, modId int32) *pets.RoleInfo { + p.InitRolesAndPets() + + level := p.Roles.ModUnlock[modId] + roleInfo := srvdata.RolePetMgrSington.GetRoleByRoleIdAndLevel(modId, level) + if roleInfo != nil { + role := &pets.RoleInfo{ + Id: roleInfo.Id, + RoleId: roleInfo.RoleId, + Name: roleInfo.Name, + Grade: roleInfo.Grade, + Level: roleInfo.Level, + Fragment: roleInfo.Fragment, + Amount: roleInfo.Amount, + AwardType: roleInfo.AwardType, + Award: roleInfo.Award, + AwardRate: roleInfo.AwardRate, + } + if level > 0 { + role.IsUnlock = true + if p.Roles.ModId == roleInfo.RoleId { + role.IsUsing = true + } + } + nextInfo := srvdata.RolePetMgrSington.GetRoleByRoleIdAndLevel(modId, level+1) + if nextInfo != nil { + role.NextAward = nextInfo.Award + } + roleOther := this.GetIntroductionByModId(modId) + role.MaxLevel = roleOther.MaxLevel + role.Name = roleOther.Name + role.Story = roleOther.Story + role.AwardTitle = roleOther.AwardTitle + // + item := BagMgrSingleton.GetItem(p.SnId, role.Fragment) + if item != nil { + role.HaveAmount = int32(item.ItemNum) + } + return role + } + return nil +} + +// //////////////////////////////////////////////////////////////////////// +// 宠物 +func (this *PetMgr) GetPetInfos(p *Player) []*pets.PetInfo { + p.InitRolesAndPets() + + petsInfo := srvdata.RolePetMgrSington.Pets + if petsInfo != nil { + var newPets = make([]*pets.PetInfo, 0) + for petId, petInfo := range petsInfo { + level := p.Pets.ModUnlock[petId] + if len(petInfo) >= int(level) { + pet := petInfo[level] + newPet := &pets.PetInfo{ + Id: pet.Id, + PetId: pet.PetId, + Name: pet.Name, + Grade: pet.Grade, + Level: pet.Level, + Fragment: pet.Fragment, + Amount: pet.Amount, + AwardType: pet.AwardType, + Award: pet.Award, + AwardRate: pet.AwardRate, + } + if level > 0 { + newPet.IsUnlock = true + if p.Pets.ModId == petId { + newPet.IsUsing = true + } + } + + if level == 0 { + newPet.NextAward = petInfo[1].Award + } else { + if int32(len(petInfo)) > level+1 { + newPet.NextAward = petInfo[level+1].Award + } + } + + petOther := this.GetIntroductionByModId(newPet.PetId) + newPet.MaxLevel = petOther.MaxLevel + newPet.Name = petOther.Name + newPet.Story = petOther.Story + newPet.AwardTitle = petOther.AwardTitle + /// + item := BagMgrSingleton.GetItem(p.SnId, newPet.Fragment) + if item != nil { + newPet.HaveAmount = int32(item.ItemNum) + } + newPets = append(newPets, newPet) + } + } + return newPets + } + return nil +} +func (this *PetMgr) GetPetInfo(p *Player, modId int32) *pets.PetInfo { + p.InitRolesAndPets() + + level := p.Pets.ModUnlock[modId] + petInfo := srvdata.RolePetMgrSington.GetPetByPetIdAndLevel(modId, level) + if petInfo != nil { + newPet := &pets.PetInfo{ + Id: petInfo.Id, + PetId: petInfo.PetId, + Name: petInfo.Name, + Grade: petInfo.Grade, + Level: petInfo.Level, + Fragment: petInfo.Fragment, + Amount: petInfo.Amount, + AwardType: petInfo.AwardType, + Award: petInfo.Award, + AwardRate: petInfo.AwardRate, + } + if level > 0 { + newPet.IsUnlock = true + if p.Roles.ModId == petInfo.PetId { + newPet.IsUsing = true + } + } + nextInfo := srvdata.RolePetMgrSington.GetPetByPetIdAndLevel(modId, level+1) + if nextInfo != nil { + newPet.NextAward = nextInfo.Award + } + petOther := this.GetIntroductionByModId(modId) + newPet.MaxLevel = petOther.MaxLevel + newPet.Name = petOther.Name + newPet.Story = petOther.Story + newPet.AwardTitle = petOther.AwardTitle + // + item := BagMgrSingleton.GetItem(p.SnId, newPet.Fragment) + if item != nil { + newPet.HaveAmount = int32(item.ItemNum) + } + return newPet + } + return nil +} + +// 商品人物总加成 人物功能变动需要修改 +func (this *PetMgr) GetShopAward(shopInfo *ShopInfo, p *Player) (award, roleId int32) { + if shopInfo.Ad > 0 && shopInfo.Type == ShopTypeCoin { + id, add := srvdata.RolePetMgrSington.GetRoleAdd(p.PlayerData, common.RoleAddADV) + award += add + roleId = id + } + + if shopInfo.Ad == 0 && shopInfo.Type == ShopTypeCoin { + id, add := srvdata.RolePetMgrSington.GetRoleAdd(p.PlayerData, common.RoleAddCoin) + award += add + roleId = id + } + + //VIP商城没有人物加成 + if shopInfo.Page == ShopPageVip || shopInfo.Page == ShopPagePhoneScore || shopInfo.Page == ShopPagePhoneScore_google { + award = 0 + } + return award, roleId +} + +// 宠物加成 宠物功能变动需要修改 +func (this *PetMgr) GetAwardPetByWelf(p *Player) (award int64) { + petChick := this.GetPetInfo(p, 1000001) + if petChick != nil && petChick.Level > 0 { + award = int64(petChick.Award) + } + return +} +func (this *PetMgr) GetIntroductionByModId(modId int32) *RoleAndPetInfo { + mod := srvdata.PBDB_Game_IntroductionMgr.GetData(modId) + if mod == nil { + return &RoleAndPetInfo{} + } + return &RoleAndPetInfo{ + ModId: mod.Id, + Name: mod.Name, + Story: mod.Story, + AwardTitle: mod.AwardTitle, + MaxLevel: mod.LevelMax, + } +} +func (this *PetMgr) CheckShowRed(p *Player) { + if p == nil { + return + } + var roleRed, petRed bool + if p.Roles != nil { + needAmount := make(map[int32]int32) + for roleId, rol := range srvdata.RolePetMgrSington.Roles { + if level, ok := p.Roles.ModUnlock[roleId]; ok { + needAmount[rol[level].Fragment] = rol[level].Amount + } else { + needAmount[rol[level].Fragment] = rol[level].Amount + } + } + for fragment, amount := range needAmount { + item := BagMgrSingleton.GetItem(p.SnId, fragment) + if item != nil && item.ItemNum >= int64(amount) && amount != 0 { + roleRed = true + break + } + } + } + if p.Pets != nil { + needAmount := make(map[int32]int32) + for petId, pet := range srvdata.RolePetMgrSington.Pets { + if level, ok := p.Pets.ModUnlock[petId]; ok { + needAmount[pet[level].Fragment] = pet[level].Amount + } else { + needAmount[pet[level].Fragment] = pet[level].Amount + } + } + for fragment, amount := range needAmount { + item := BagMgrSingleton.GetItem(p.SnId, fragment) + if item != nil && item.ItemNum >= int64(amount) && amount != 0 { + petRed = true + break + } + } + } + if roleRed { + p.SendShowRed(hall_proto.ShowRedCode_Role, 0, 1) + } + if petRed { + p.SendShowRed(hall_proto.ShowRedCode_Pet, 0, 1) + } +} + +////////////////////////////////// + +func (this *PetMgr) Init() { + +} +func (this *PetMgr) Update() { + +} + +func (this *PetMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(PetMgrSington, time.Second, 0) +} diff --git a/worldsrv/platform.go b/worldsrv/platform.go new file mode 100644 index 0000000..c573fb8 --- /dev/null +++ b/worldsrv/platform.go @@ -0,0 +1,254 @@ +package main + +import ( + "strconv" + + "mongo.games.com/game/common" + webapiproto "mongo.games.com/game/protocol/webapi" +) + +const ( + BindBackCard int = 1 << iota // 银行卡 + BindAlipay // 支付宝 + BindWeiXin // 微信 +) + +const ( + ExchangeFlag_Tax = 1 << iota //兑换税收 + ExchangeFlag_Flow //兑换流水 + ExchangeFlag_Force //强制兑换流水是否开启 + ExchangeFlag_UpAcc //账号注册升级是否开启 + +) + +type PlatformGamePlayerNum struct { + Nums map[int32]int //人数信息 + Dirty bool //变化标记 +} + +// 排行榜开关 +type RankSwitch struct { + Asset int32 // 财富榜 + Recharge int32 // 充值榜 + Exchange int32 // 兑换榜 + Profit int32 // 盈利榜 + Flow int32 // 流水榜 +} + +// 俱乐部配置 +type ClubConfig struct { + CreationCoin int64 //创建俱乐部金额 + IncreaseCoin int64 //升级俱乐部金额 + ClubInitPlayerNum int32 //俱乐部初始人数 + IncreasePlayerNum int32 //升级人数增加 + IsOpenClub bool //是否开放俱乐部 + CreateClubCheckByManual bool //创建俱乐部人工审核,true=手动 + EditClubNoticeByManual bool //修改公告人工审核,true=手动 + CreateRoomAmount int64 //创建房间金额(分/局) + GiveCoinRate []int64 //会长充值额外赠送比例 +} +type Platform struct { + Id int32 // 平台ID + IdStr string // 字符id + Name string // 平台名称 + Isolated bool // 是否孤立(别的平台看不到) + Disable bool // 是否禁用 + Halls map[int32]*PlatformGameHall //厅 + GamePlayerNum map[int32]*PlatformGamePlayerNum //游戏人数 + dirty bool // + ServiceUrl string //客服地址 + BindOption int32 //绑定选项 + ServiceFlag bool //客服标记 是否支持浏览器跳转 false否 true是 + UpgradeAccountGiveCoin int32 //升级账号奖励金币 + NewAccountGiveCoin int32 //新账号奖励金币 + PerBankNoLimitAccount int32 //同一银行卡号绑定用户数量限制 + ExchangeMin int32 //最低兑换金额 + ExchangeLimit int32 //兑换后身上保留最低余额 + ExchangeTax int32 //兑换税收(万分比) + ExchangeFlow int32 //兑换流水比例 + ExchangeForceTax int32 //强制兑换税收 + ExchangeGiveFlow int32 //赠送兑换流水 + ExchangeFlag int32 //兑换标记 二进制 第一位:兑换税收 第二位:流水比例 + ExchangeVer int32 //兑换版本号 + ExchangeMultiple int32 //兑换基数(只能兑换此数的整数倍) + VipRange []int32 //VIP充值区间 + OtherParams string //其他参数json串 + SpreadConfig int32 //0:等级返点 1:保底返佣 + RankSwitch RankSwitch //排行榜开关 + ClubConfig *ClubConfig //俱乐部配置 + VerifyCodeType int32 //注册账号使用验证码方式 0:短信验证码 1:随机字符串 2:滑块验证码 3:不使用 + RegisterVerifyCodeSwitch bool // 关闭注册验证码 + ThirdGameMerchant map[int32]int32 //三方游戏平台状态 + CustomType int32 //客服类型 0:live800 1:美洽 2:cc + NeedDeviceInfo bool //需要获取设备信息 + NeedSameName bool //绑定的银行卡和支付宝用户名字需要相同 + ExchangeBankMax int32 //银行卡最大兑换金额 0不限制 + ExchangeAlipayMax int32 //支付宝最大兑换金额 0不限制 + DgHboConfig int32 //dg hbo配置,默认0,dg 1 hbo 2 + PerBankNoLimitName int32 //银行卡和支付宝 相同名字最大数量 + IsCanUserBindPromoter bool //是否允许用户手动绑定推广员 + UserBindPromoterPrize int32 //手动绑定奖励 + SpreadWinLose bool //是否打开客损开关 + PltGameCfg *PlatformGameConfig //平台游戏配置 + MerchantKey string //商户秘钥 + BindTelReward map[int32]int64 // 绑定手机号奖励 +} + +type PlatformGameConfig struct { + games map[int32]*webapiproto.GameFree //以gamefreeid为key + cache map[int32][]*webapiproto.GameFree //以gameid为key +} + +func (cfg *PlatformGameConfig) RecreateCache() { + if cfg.cache == nil { + cfg.cache = make(map[int32][]*webapiproto.GameFree) + } + for _, val := range cfg.games { + cfg.cache[val.DbGameFree.GetGameId()] = append(cfg.cache[val.DbGameFree.GetGameId()], val) + } +} + +func (cfg *PlatformGameConfig) GetGameCfg(gamefreeId int32) *webapiproto.GameFree { + if cfg.games != nil { + if c, exist := cfg.games[gamefreeId]; exist { + if c.GroupId == 0 { + return c + } else { + groupCfg := PlatformGameGroupMgrSington.GetGameGroup(c.GroupId) + temp := &webapiproto.GameFree{ + GroupId: groupCfg.GetId(), + Status: c.Status, + DbGameFree: groupCfg.GetDbGameFree(), + } + return temp + } + } else { + //logger.Logger.Errorf("PlatformGameConfig GetGameCfg Can't Find GameCfg[%v]", gamefreeId) + } + } + return nil +} + +func CompareGameFreeConfigChged(oldCfg, newCfg *webapiproto.GameFree) bool { + if oldCfg.Status != newCfg.Status || + oldCfg.GroupId != newCfg.GroupId || + oldCfg.DbGameFree.GetBot() != newCfg.DbGameFree.GetBot() || + oldCfg.DbGameFree.GetBaseScore() != newCfg.DbGameFree.GetBaseScore() || + oldCfg.DbGameFree.GetLimitCoin() != newCfg.DbGameFree.GetLimitCoin() || + oldCfg.DbGameFree.GetMaxCoinLimit() != newCfg.DbGameFree.GetMaxCoinLimit() || + oldCfg.DbGameFree.GetSameIpLimit() != newCfg.DbGameFree.GetSameIpLimit() || + oldCfg.DbGameFree.GetSamePlaceLimit() != newCfg.DbGameFree.GetSamePlaceLimit() || + oldCfg.DbGameFree.GetLowerThanKick() != newCfg.DbGameFree.GetLowerThanKick() || + oldCfg.DbGameFree.GetBanker() != newCfg.DbGameFree.GetBanker() || + oldCfg.DbGameFree.GetMaxChip() != newCfg.DbGameFree.GetMaxChip() || + oldCfg.DbGameFree.GetBetLimit() != newCfg.DbGameFree.GetBetLimit() || + oldCfg.DbGameFree.GetLottery() != newCfg.DbGameFree.GetLottery() || + oldCfg.DbGameFree.GetLotteryConfig() != newCfg.DbGameFree.GetLotteryConfig() || + oldCfg.DbGameFree.GetTaxRate() != newCfg.DbGameFree.GetTaxRate() || + !common.SliceInt64Equal(oldCfg.DbGameFree.GetOtherIntParams(), newCfg.DbGameFree.GetOtherIntParams()) || + !common.SliceInt64Equal(oldCfg.DbGameFree.GetRobotTakeCoin(), newCfg.DbGameFree.GetRobotTakeCoin()) || + !common.SliceInt64Equal(oldCfg.DbGameFree.GetRobotLimitCoin(), newCfg.DbGameFree.GetRobotLimitCoin()) || + !common.SliceInt32Equal(oldCfg.DbGameFree.GetRobotNumRng(), newCfg.DbGameFree.GetRobotNumRng()) || + !common.SliceInt32Equal(oldCfg.DbGameFree.GetBalanceLine(), newCfg.DbGameFree.GetBalanceLine()) || + (len(newCfg.DbGameFree.GetMaxBetCoin()) > 0 && + !common.Int32SliceEqual(oldCfg.DbGameFree.GetMaxBetCoin(), newCfg.DbGameFree.GetMaxBetCoin())) || + oldCfg.DbGameFree.GetMatchMode() != newCfg.DbGameFree.GetMatchMode() || + oldCfg.DbGameFree.GetCreateRoomNum() != newCfg.DbGameFree.GetCreateRoomNum() || + oldCfg.DbGameFree.GetJackpotRatio() != newCfg.DbGameFree.GetJackpotRatio() || + oldCfg.DbGameFree.GetJackpotMin() != newCfg.DbGameFree.GetJackpotMin() || + oldCfg.DbGameFree.GetMatchTrueMan() != newCfg.DbGameFree.GetMatchTrueMan() || + oldCfg.DbGameFree.GetLeaveDeduct() != newCfg.DbGameFree.GetLeaveDeduct() || + oldCfg.DbGameFree.GetLeaveCombat() != newCfg.DbGameFree.GetLeaveCombat() || + oldCfg.DbGameFree.GetIntuseCannonMin() != newCfg.DbGameFree.GetIntuseCannonMin() || + oldCfg.DbGameFree.GetIntuseCannonMax() != newCfg.DbGameFree.GetIntuseCannonMax() || + oldCfg.DbGameFree.GetBossDrainageBet() != newCfg.DbGameFree.GetBossDrainageBet() || + oldCfg.DbGameFree.GetDraw() != newCfg.DbGameFree.GetDraw() || + oldCfg.DbGameFree.GetFluctuate() != newCfg.DbGameFree.GetFluctuate() || + oldCfg.DbGameFree.GetFluctuateMax() != newCfg.DbGameFree.GetFluctuateMax() || + oldCfg.DbGameFree.GetRatio() != newCfg.DbGameFree.GetRatio() || + oldCfg.DbGameFree.GetMinValue() != newCfg.DbGameFree.GetMinValue() || + oldCfg.DbGameFree.GetMaxValue() != newCfg.DbGameFree.GetMaxValue() || + oldCfg.DbGameFree.GetDrainageBet() != newCfg.DbGameFree.GetDrainageBet() || + oldCfg.DbGameFree.GetDiamondDrop() != newCfg.DbGameFree.GetDiamondDrop() || + oldCfg.DbGameFree.GetNegativeMax() != newCfg.DbGameFree.GetNegativeMax() || + oldCfg.DbGameFree.GetRatioMax() != newCfg.DbGameFree.GetRatioMax() { + return false + } + return true +} + +func NewPlatform(id int32, isolated bool) *Platform { + p := &Platform{ + Id: id, + IdStr: strconv.Itoa(int(id)), + Isolated: isolated, + Halls: make(map[int32]*PlatformGameHall), + GamePlayerNum: make(map[int32]*PlatformGamePlayerNum), + ClubConfig: &ClubConfig{}, + PltGameCfg: &PlatformGameConfig{ + games: make(map[int32]*webapiproto.GameFree), + cache: make(map[int32][]*webapiproto.GameFree), + }, + } + + return p +} + +// true 有流水标记 false 没有流水标记 +func (p *Platform) IsFlowSwitch() bool { + if (p.ExchangeFlag&ExchangeFlag_Flow) != 0 && p.ExchangeFlow > 0 { + return true + } + return false +} + +func (p *Platform) ChangeIsolated(isolated bool) bool { + if p.Isolated == isolated { + return false + } + p.Isolated = isolated + //if isolated { //开放平台转换成私有平台 + // for _, hall := range p.Halls { + // hall.ConvertToIsolated() + // } + //} else { //私有平台转换为开放平台 + // for _, hall := range p.Halls { + // hall.OpenSceneToPublic() + // } + //} + return true +} + +func (p *Platform) ChangeDisabled(disable bool) bool { + if p.Disable == disable { + return false + } + p.Disable = disable + if disable { //关闭平台,踢掉平台上所有的人 + PlayerMgrSington.KickoutByPlatform(p.IdStr) + } + return true +} + +func (p *Platform) PlayerLogin(player *Player) { + +} + +func (p *Platform) PlayerLogout(player *Player) { + +} + +func (p *Platform) IsMarkFlag(flag int32) bool { + if (p.BindOption & flag) != 0 { + return true + } + return false +} + +func (p *Platform) OnDayTimer() { + +} + +func (p *Platform) OnHourTimer() { + +} diff --git a/worldsrv/platformgamegroup.go b/worldsrv/platformgamegroup.go new file mode 100644 index 0000000..ca35ad2 --- /dev/null +++ b/worldsrv/platformgamegroup.go @@ -0,0 +1,132 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core/logger" +) + +type PlatformGameGroupObserver interface { + OnGameGroupUpdate(oldCfg, newCfg *webapi_proto.GameConfigGroup) +} + +var PlatformGameGroupMgrSington = &PlatformGameGroupMgr{ + groups: make(map[int32]*webapi_proto.GameConfigGroup), +} + +type PlatformGameGroupMgr struct { + groups map[int32]*webapi_proto.GameConfigGroup + observers []PlatformGameGroupObserver +} + +func (this *PlatformGameGroupMgr) RegisteObserver(observer PlatformGameGroupObserver) { + for _, ob := range this.observers { + if ob == observer { + return + } + } + this.observers = append(this.observers, observer) +} + +func (this *PlatformGameGroupMgr) UnregisteObserver(observer PlatformGameGroupObserver) { + for i, ob := range this.observers { + if ob == observer { + count := len(this.observers) + if i == 0 { + this.observers = this.observers[1:] + } else if i == count-1 { + this.observers = this.observers[:count-1] + } else { + arr := this.observers[:i] + arr = append(arr, this.observers[i+1:]...) + this.observers = arr + } + } + } +} + +func (this *PlatformGameGroupMgr) GetGameGroup(groupId int32) *webapi_proto.GameConfigGroup { + if g, exist := this.groups[groupId]; exist { + return g + } + return nil +} + +func (this *PlatformGameGroupMgr) LoadGameGroup() { + //不使用etcd的情况下走api获取 + if model.GameParamData.UseEtcd { + EtcdMgrSington.InitGameGroup() + } else { + //获取平台游戏组信息 + logger.Logger.Trace("API_GetGameGroupData") + buf, err := webapi.API_GetGameGroupData(common.GetAppId()) + if err == nil { + pcdr := &webapi_proto.ASGameConfigGroup{} + err = proto.Unmarshal(buf, pcdr) + if err == nil && pcdr.Tag == webapi_proto.TagCode_SUCCESS { + gameCfgGroup := pcdr.GetGameConfigGroup() + for _, value := range gameCfgGroup { + groupId := value.GetId() + vDbGameFree := value.GetDbGameFree() + + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(value.LogicId) + if dbGameFree == nil { + continue + } + if vDbGameFree == nil { + vDbGameFree = dbGameFree + } else { + CopyDBGameFreeField(dbGameFree, vDbGameFree) + } + + this.groups[groupId] = value + logger.Logger.Trace("PlatformGameGroup data:", value) + } + } else { + logger.Logger.Error("Unmarshal PlatformGameGroup data error:", err) + } + } else { + logger.Logger.Error("Get PlatformGameGroup data error:", err) + } + } +} + +func (this *PlatformGameGroupMgr) UpsertGameGroup(conf *webapi_proto.GameConfigGroup) { + if conf == nil { + return + } + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(conf.GetLogicId()) + if dbGameFree == nil { + return + } + if conf.DbGameFree == nil { + conf.DbGameFree = dbGameFree + } else { + CopyDBGameFreeField(dbGameFree, conf.DbGameFree) + } + old, ok := this.groups[conf.GetId()] + + //更新相关配置 + this.groups[conf.GetId()] = conf + //发布更新事件 + if ok && old != nil { + this.OnGameGroupUpdate(old, conf) + } +} + +func (this *PlatformGameGroupMgr) OnGameGroupUpdate(oldCfg, newCfg *webapi_proto.GameConfigGroup) { + for _, observer := range this.observers { + observer.OnGameGroupUpdate(oldCfg, newCfg) + } +} + +func init() { + RegisterParallelLoadFunc("平台游戏组数据", func() error { + PlatformGameGroupMgrSington.LoadGameGroup() + return nil + }) +} diff --git a/worldsrv/platformgamehall.go b/worldsrv/platformgamehall.go new file mode 100644 index 0000000..dd73000 --- /dev/null +++ b/worldsrv/platformgamehall.go @@ -0,0 +1,71 @@ +package main + +import ( + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/server" +) + +type PlatformGameHall struct { + HallId int32 //游戏厅id + Scenes map[int]*Scene //游戏房间列表 + Players map[int32]*Player //大厅中的玩家 + p *Platform //所属平台 + dbGameFree *server.DB_GameFree //厅配置数据(这里都是模板值,非后台实例数据) + dbGameRule *server.DB_GameRule //游戏配置数据(这里都是模板值,非后台实例数据) +} + +func (pgh *PlatformGameHall) PlayerLeave(p *Player) { + delete(pgh.Players, p.SnId) + if p.hallId == pgh.HallId { + p.hallId = 0 + } +} + +func (p *Player) CreateRoomPlayerInfoProtocol() *gamehall.RoomPlayerInfo { + pack := &gamehall.RoomPlayerInfo{ + SnId: proto.Int32(p.SnId), + Head: proto.Int32(p.Head), + Sex: proto.Int32(p.Sex), + Name: proto.String(p.Name), + Pos: proto.Int(p.pos), + Flag: proto.Int32(p.flag), + HeadOutLine: proto.Int32(p.HeadOutLine), + VIP: proto.Int32(p.VIP), + } + return pack +} + +func (pgh *PlatformGameHall) OnPlayerEnterScene(scene *Scene, player *Player) { + delete(pgh.Players, player.SnId) + pack := &gamehall.SCRoomPlayerEnter{ + RoomId: proto.Int(scene.sceneId), + Player: player.CreateRoomPlayerInfoProtocol(), + } + proto.SetDefaults(pack) + pgh.Broadcast(int(gamehall.GameHallPacketID_PACKET_SC_ROOMPLAYERENTER), pack, player.SnId) +} + +func (pgh *PlatformGameHall) OnPlayerLeaveScene(scene *Scene, player *Player) { + pack := &gamehall.SCRoomPlayerLeave{ + RoomId: proto.Int(scene.sceneId), + Pos: proto.Int(player.pos), + } + proto.SetDefaults(pack) + pgh.Broadcast(int(gamehall.GameHallPacketID_PACKET_SC_ROOMPLAYERLEAVE), pack, player.SnId) +} + +func (pgh *PlatformGameHall) OnDestroyScene(scene *Scene) { + delete(pgh.Scenes, scene.sceneId) + pack := &gamehall.SCDestroyRoom{ + RoomId: proto.Int(scene.sceneId), + OpRetCode: gamehall.OpResultCode_Game_OPRC_Sucess_Game, + IsForce: proto.Int(1), + } + proto.SetDefaults(pack) + pgh.Broadcast(int(gamehall.GameHallPacketID_PACKET_SC_DESTROYROOM), pack, 0) +} + +func (pgh *PlatformGameHall) Broadcast(packetid int, packet interface{}, exclude int32) { + PlatformMgrSingleton.Broadcast(packetid, packet, pgh.Players, exclude) +} diff --git a/worldsrv/platformmgr.go b/worldsrv/platformmgr.go new file mode 100644 index 0000000..53b9343 --- /dev/null +++ b/worldsrv/platformmgr.go @@ -0,0 +1,891 @@ +package main + +import ( + "strconv" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/utils" + srvlibproto "mongo.games.com/goserver/srvlib/protocol" + + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + hallproto "mongo.games.com/game/protocol/gamehall" + loginproto "mongo.games.com/game/protocol/login" + serverproto "mongo.games.com/game/protocol/server" + webapiproto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" +) + +const ( + DefaultPlatform = "0" + DefaultPlatformInt = 0 +) + +// PlatformConfig 所有跟平台相关的配置,不要再定义map数据结构了 +type PlatformConfig struct { + Platform *Platform // 平台配置 + EntrySwitch *webapiproto.EntrySwitch // 客户端游戏入口开关 + CommonNotices *webapiproto.CommonNoticeList // 公告 + PlayerPool *webapiproto.PlayerPool // 个人水池配置 +} + +type PlatformObserver interface { + OnPlatformCreate(p *Platform) + OnPlatformDestroy(p *Platform) + OnPlatformChangeDisabled(p *Platform, disabled bool) + OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapiproto.GameFree) + OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) +} + +var PlatformMgrSingleton = &PlatformMgr{ + configs: make(map[string]*PlatformConfig), + PackageList: make(map[string]*webapiproto.AppInfo), + GameStatus: make(map[int32]bool), //全局游戏开关 +} + +type PlatformMgr struct { + BaseClockSinker + + //todo 所有跟平台相关的配置,不要再定义map数据结构了 + configs map[string]*PlatformConfig + + PackageList map[string]*webapiproto.AppInfo // 包对应的平台,包标识 + GameStatus map[int32]bool // 超管平台游戏开关,游戏id + + Observers []PlatformObserver +} + +func (pm *PlatformMgr) RegisterObserver(observer PlatformObserver) { + for _, ob := range pm.Observers { + if ob == observer { + return + } + } + pm.Observers = append(pm.Observers, observer) +} + +func (pm *PlatformMgr) UnregisterObserver(observer PlatformObserver) { + for i, ob := range pm.Observers { + if ob == observer { + pm.Observers = append(pm.Observers[:i], pm.Observers[i+1:]...) + break + } + } +} + +func (pm *PlatformMgr) GetPlatforms() []*Platform { + var ret []*Platform + for _, v := range pm.configs { + if v != nil && v.Platform != nil { + ret = append(ret, v.Platform) + } + } + return ret +} + +func (pm *PlatformMgr) CreatePlatform(id int32, isolated bool) *Platform { + pltId := strconv.Itoa(int(id)) + p := NewPlatform(id, isolated) + pm.Get(pltId).Platform = p + if p != nil { + pm.OnPlatformCreate(p) + } + return p +} + +// CreateDefaultPlatform 创建默认平台 +func (pm *PlatformMgr) CreateDefaultPlatform() { + //默认平台数据 + defaultPlatform := pm.CreatePlatform(DefaultPlatformInt, false) + defaultPlatform.Disable = false + //默认平台配置 + pgc := defaultPlatform.PltGameCfg + if pgc != nil { + for _, value := range srvdata.PBDB_GameFreeMgr.Datas.Arr { + if value.GetGameId() > 0 { + pgc.games[value.GetId()] = &webapiproto.GameFree{ + Status: true, + DbGameFree: value, + } + } + } + pgc.RecreateCache() + } +} + +// UpsertPlatform 更新或新增平台 +func (pm *PlatformMgr) UpsertPlatform(name string, isolated, disable bool, id int32, url string, + bindOption int32, serviceFlag bool, upgradeAccountGiveCoin, newAccountGiveCoin, perBankNoLimitAccount, exchangeMin, + exchangeLimit, exchangeTax, exchangeFlow, exchangeFlag, spreadConfig int32, vipRange []int32, otherParam string, + ccf *ClubConfig, verifyCodeType int32, thirdState map[int32]int32, customType int32, needDeviceInfo bool, + needSameName bool, exchangeForceTax int32, exchangeGiveFlow int32, exchangeVer int32, exchangeBankMax int32, + exchangeAlipayMax int32, dgHboCfg int32, PerBankNoLimitName int32, isCanUserBindPromoter bool, + userBindPromoterPrize int32, spreadWinLose bool, exchangeMultiple int32, registerVerifyCodeSwitch bool, merchantKey string, + bindTelReward map[int32]int64) *Platform { + pltId := strconv.Itoa(int(id)) + p := pm.Get(pltId).Platform + if p == nil { + p = pm.CreatePlatform(id, isolated) + } + //oldIsolated := p.Isolated + //oldBindPromoter := p.IsCanUserBindPromoter + oldDisabled := p.Disable + //oldBindOption := p.BindOption + p.Id = id + p.Name = name + p.IdStr = pltId + p.ServiceUrl = url + p.BindOption = bindOption + p.ServiceFlag = serviceFlag + p.CustomType = customType + p.UpgradeAccountGiveCoin = upgradeAccountGiveCoin + p.NewAccountGiveCoin = newAccountGiveCoin + p.PerBankNoLimitAccount = perBankNoLimitAccount + p.ExchangeMin = exchangeMin + p.ExchangeLimit = exchangeLimit + p.SpreadConfig = spreadConfig + p.ExchangeTax = exchangeTax + p.ExchangeVer = exchangeVer + p.ExchangeForceTax = exchangeForceTax + p.ExchangeFlow = exchangeFlow + p.ExchangeFlag = exchangeFlag + p.VipRange = vipRange + p.OtherParams = otherParam + p.VerifyCodeType = verifyCodeType + p.RegisterVerifyCodeSwitch = registerVerifyCodeSwitch + p.ThirdGameMerchant = thirdState + //p.NeedDeviceInfo = needDeviceInfo + p.NeedSameName = needSameName + p.PerBankNoLimitName = PerBankNoLimitName + p.ExchangeGiveFlow = exchangeGiveFlow + p.ExchangeBankMax = exchangeBankMax + p.ExchangeAlipayMax = exchangeAlipayMax + p.DgHboConfig = dgHboCfg + p.IsCanUserBindPromoter = isCanUserBindPromoter + p.UserBindPromoterPrize = userBindPromoterPrize + p.SpreadWinLose = spreadWinLose + p.ExchangeMultiple = exchangeMultiple + p.MerchantKey = merchantKey + p.BindTelReward = bindTelReward + if p.ExchangeFlag&ExchangeFlag_Flow == 0 { //修正下 + p.ExchangeFlow = 0 + } + //if oldIsolated != isolated { + // pm.ChangeIsolated(name, isolated) + //} + if oldDisabled != disable { + pm.ChangeDisabled(name, disable) + } + //if oldBindPromoter != isCanUserBindPromoter { + // pm.ChangeIsCanBindPromoter(name, isCanUserBindPromoter) + //} + if ccf != nil { + p.ClubConfig = ccf + } + return p +} + +// Get 获取平台相关配置,不会返回nil +func (pm *PlatformMgr) Get(plt string) *PlatformConfig { + cfg, ok := pm.configs[plt] + if !ok { + cfg = new(PlatformConfig) + pm.configs[plt] = cfg + } + return cfg +} + +func (this *PlatformMgr) UpdateConfig(config any) { + if config == nil { + return + } + switch v := config.(type) { + case *webapiproto.PlayerPool: + this.Get(v.GetPlatform()).PlayerPool = v + default: + logger.Logger.Errorf("unknown config type:%v", config) + } +} + +func (pm *PlatformMgr) UpdateEntrySwitch(config *webapiproto.EntrySwitch) { + if config == nil { + return + } + pm.Get(config.Platform).EntrySwitch = config +} + +func (pm *PlatformMgr) GetEntrySwitch(plt string) *webapiproto.EntrySwitch { + return pm.Get(plt).EntrySwitch +} + +func (pm *PlatformMgr) UpdateCommonNotices(config *webapiproto.CommonNoticeList) { + if config == nil { + return + } + pm.Get(config.Platform).CommonNotices = config +} + +func (pm *PlatformMgr) GetCommonNotices(plt string) *webapiproto.CommonNoticeList { + return pm.Get(plt).CommonNotices +} + +func (pm *PlatformMgr) SyncGameFree(platform string, data *webapiproto.GameFree) { + packSgf := &loginproto.SCSyncGameFree{} + gc := &loginproto.GameConfig{ + GameId: proto.Int32(data.DbGameFree.GetGameId()), + GameMode: proto.Int32(data.DbGameFree.GetGameMode()), + LogicId: proto.Int32(data.DbGameFree.GetId()), + State: proto.Bool(data.Status), + LimitCoin: proto.Int64(data.DbGameFree.GetLimitCoin()), + MaxCoinLimit: proto.Int64(data.DbGameFree.GetMaxCoinLimit()), + BaseScore: proto.Int32(data.DbGameFree.GetBaseScore()), + BetScore: proto.Int32(data.DbGameFree.GetBetLimit()), + OtherIntParams: data.DbGameFree.GetOtherIntParams(), + MaxBetCoin: data.DbGameFree.GetMaxBetCoin(), + MatchMode: proto.Int32(data.DbGameFree.GetMatchMode()), + } + if data.DbGameFree.GetLottery() != 0 { + gc.LotteryCfg = data.DbGameFree.LotteryConfig + } + packSgf.Data = append(packSgf.Data, gc) + if len(packSgf.Data) > 0 { + PlayerMgrSington.BroadcastMessageToPlatform(platform, int(loginproto.LoginPacketID_PACKET_SC_SYNCGAMEFREE), packSgf) + } +} + +// UpsertGameFree 更新场次配置 +func (pm *PlatformMgr) UpsertGameFree(platform string, data *webapiproto.GameFree) { + p := pm.GetPlatform(platform) + if p != nil { + pgc := p.PltGameCfg + if pgc != nil { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(data.DbGameFree.Id) + if dbGameFree == nil { + return + } + if data.GetDbGameFree() == nil { //数据容错 + data.DbGameFree = dbGameFree + } else { + CopyDBGameFreeField(dbGameFree, data.DbGameFree) + } + + old, ok := pgc.games[data.DbGameFree.Id] + pgc.games[data.DbGameFree.Id] = data + found := false + if ok && old != nil { + if c, ok := pgc.cache[data.DbGameFree.Id]; ok { + for i := 0; i < len(c); i++ { + if c[i].DbGameFree.Id == data.DbGameFree.Id { + c[i] = data + found = true + break + } + } + } + } + if !found { + pgc.cache[data.DbGameFree.Id] = append(pgc.cache[data.DbGameFree.Id], data) + } + if ok && old != nil && !CompareGameFreeConfigChged(old, data) { + pm.OnPlatformGameFreeUpdate(p, old, data) + pm.SyncGameFree(p.IdStr, data) + } + } + } +} + +func (pm *PlatformMgr) OnPlatformCreate(p *Platform) { + for _, observer := range pm.Observers { + observer.OnPlatformCreate(p) + } +} + +func (pm *PlatformMgr) OnPlatformGameFreeUpdate(p *Platform, oldCfg, newCfg *webapiproto.GameFree) { + for _, observer := range pm.Observers { + observer.OnPlatformGameFreeUpdate(p, oldCfg, newCfg) + } +} + +func (pm *PlatformMgr) OnPlatformDestroyByGameFreeId(p *Platform, gameFreeId int32) { + for _, observer := range pm.Observers { + observer.OnPlatformDestroyByGameFreeId(p, gameFreeId) + } +} + +func (pm *PlatformMgr) OnPlatformChangeDisabled(p *Platform, disable bool) { + for _, observer := range pm.Observers { + observer.OnPlatformChangeDisabled(p, disable) + } +} + +func (pm *PlatformMgr) ChangeDisabled(name string, disable bool) { + if p := pm.GetPlatform(name); p != nil { + if p.ChangeDisabled(disable) { + pm.OnPlatformChangeDisabled(p, disable) + } + } +} + +func (pm *PlatformMgr) GetPlatform(platform string) *Platform { + return pm.Get(platform).Platform +} + +func (pm *PlatformMgr) OnChangeSceneState(scene *Scene, startclose bool) { + +} +func (pm *PlatformMgr) PlayerLogin(p *Player) { + if p.IsRob { + return + } + platform := pm.GetPlatform(p.Platform) + if platform != nil { + platform.PlayerLogin(p) + } +} + +func (pm *PlatformMgr) Broadcast(packetid int, packet interface{}, players map[int32]*Player, exclude int32) { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range players { + if p != nil && p.gateSess != nil && p.IsOnLine() && exclude != p.SnId { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: p.sid, + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, packet, v...) + if err == nil { + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} + +func (pm *PlatformMgr) PlayerLogout(p *Player) { + platform := pm.GetPlatform(p.Platform) + if platform != nil && platform.Isolated { + platform.PlayerLogout(p) + } else { + + } +} + +func (this *PlatformMgr) GetPackageTag(tag string) *webapiproto.AppInfo { + logger.Logger.Tracef("Get %v platform.", tag) + if pt, ok := this.PackageList[tag]; ok { + return pt + } + return nil +} + +func (this *PlatformMgr) GetPlatformByPackageTag(tag string) (int32, int32, int32, int32, int32) { + logger.Logger.Tracef("Get %v platform.", tag) + if pt, ok := this.PackageList[tag]; ok { + return pt.PlatformId, 0, 0, 0, 0 + } else { + return int32(DefaultPlatformInt), 0, 0, 0, 0 + } +} + +func (this *PlatformMgr) GetCurrencyByPackageTag(tag string) (string, int32) { + logger.Logger.Tracef("Get %v CurrencyRatio.", tag) + if pt, ok := this.PackageList[tag]; ok && pt.CurrencyType != "" && pt.CurrencyRatio != 0 { + return pt.CurrencyType, pt.CurrencyRatio + } + return "USD", 1 +} + +func (this *PlatformMgr) GetPlatformUpgradeAccountGiveCoinByPackageTag(tag string) int32 { + platform, _, _, _, _ := this.GetPlatformByPackageTag(tag) + return this.GetPlatformUpgradeAccountGiveCoinByPlatform(strconv.Itoa(int(platform))) +} + +func (this *PlatformMgr) GetPlatformUpgradeAccountGiveCoinByPlatform(platform string) int32 { + platformData := this.GetPlatform(platform) + if platformData == nil { + return 0 + } + return platformData.UpgradeAccountGiveCoin +} + +func (this *PlatformMgr) GetGameFrees(platform string) map[int32]*webapiproto.GameFree { + p := this.GetPlatform(platform) + if p == nil { + return nil + } + + data := make(map[int32]*webapiproto.GameFree) + for id, val := range p.PltGameCfg.games { + if !val.Status || !this.GameStatus[id] { + continue + } + + if val.GroupId != 0 { + cfg := PlatformGameGroupMgrSington.GetGameGroup(val.GroupId) + if cfg != nil { + temp := &webapiproto.GameFree{ + GroupId: cfg.GetId(), + Status: val.Status, + DbGameFree: cfg.GetDbGameFree(), + } + data[id] = temp + } + } else { + data[id] = val + } + } + return data +} + +func (this *PlatformMgr) GetGameFree(platform string, gameFreeId int32) *webapiproto.GameFree { + p := this.GetPlatform(platform) + if p == nil { + return nil + } + + gf := p.PltGameCfg.GetGameCfg(gameFreeId) + if gf == nil { + return nil + } + + if !this.GameStatus[gameFreeId] || !gf.GetStatus() { + return nil + } + + return gf +} + +// CheckGameState 场次开关状态 +func (this *PlatformMgr) CheckGameState(platform string, gameFreeId int32) bool { + gf := this.GetGameFree(platform, gameFreeId) + if gf == nil { + return false + } + return gf.Status +} + +func (this *PlatformMgr) GetGameFreeGroup(platform string, gameFreeId int32) int32 { + c := this.GetGameFree(platform, gameFreeId) + if c != nil { + return c.GroupId + } + return 0 +} + +func (this *PlatformMgr) GetCommonNotice(plt string) *webapiproto.CommonNoticeList { + logger.Logger.Tracef("GetCommonNotice %v platform.", plt) + if cfg := this.GetCommonNotices(plt); cfg != nil { + //now := time.Now().Unix() + re := &webapiproto.CommonNoticeList{ + Platform: cfg.Platform, + List: cfg.List, + } + //for _, v := range pt.List { + // if v.GetEndTime() > now && v.GetStartTime() < now { + // re.List = append(re.List, v) + // } + //} + return re + } + return nil +} + +// ChangeGameStatus 后台切换超管游戏开关 +func (this *PlatformMgr) ChangeGameStatus(cfgs []*hallproto.GameConfig1) { + //向所有玩家发送状态变化协议 + pack := &hallproto.SCChangeGameStatus{} + + for _, p := range this.GetPlatforms() { + for _, cfg := range cfgs { + gameFreeId := cfg.LogicId + data, ok := p.PltGameCfg.games[gameFreeId] + if data != nil && ok && data.Status { //自身有这个游戏,且处于开启状态 + if !cfg.Status { //全局关,销毁游戏 + pack.GameCfg = append(pack.GameCfg, cfg) + this.OnPlatformDestroyByGameFreeId(p, gameFreeId) + } else { + lgc := &hallproto.GameConfig1{ + LogicId: proto.Int32(data.DbGameFree.Id), + LimitCoin: proto.Int64(data.DbGameFree.GetLimitCoin()), + MaxCoinLimit: proto.Int64(data.DbGameFree.GetMaxCoinLimit()), + BaseScore: proto.Int32(data.DbGameFree.GetBaseScore()), + BetScore: proto.Int32(data.DbGameFree.GetBetLimit()), + OtherIntParams: data.DbGameFree.GetOtherIntParams(), + MaxBetCoin: data.DbGameFree.GetMaxBetCoin(), + MatchMode: proto.Int32(data.DbGameFree.GetMatchMode()), + Status: data.Status, + } + if data.DbGameFree.GetLottery() != 0 { //彩金池 + //lgc.LotteryCfg = data.DBGameFree.LotteryConfig + //_, gl := LotteryMgrSington.FetchLottery(plf, data.DBGameFree.GetId(), data.DBGameFree.GetGameId()) + //if gl != nil { + // lgc.LotteryCoin = proto.Int64(gl.Value) + //} + } + pack.GameCfg = append(pack.GameCfg, lgc) + } + } + } + + if len(pack.GameCfg) > 0 { + PlayerMgrSington.BroadcastMessageToPlatform(p.IdStr, int(hallproto.GameHallPacketID_PACKET_SC_CHANGEGAMESTATUS), pack) + } + } +} + +// ChangeEntrySwitch 修改客户端游戏入口状态 +func (this *PlatformMgr) ChangeEntrySwitch(platform string, cfg *webapiproto.EntrySwitch) { + p := PlatformMgrSingleton.GetPlatform(platform) + if p == nil { + return + } + pack := &hallproto.SCChangeEntrySwitch{} + + for _, v := range cfg.GetDatas() { + pack.GameCfg = append(pack.GameCfg, v) + } + + PlayerMgrSington.BroadcastMessageToPlatform(platform, int(hallproto.GameHallPacketID_PACKET_SC_CHANGEENTRYSWITCH), pack) +} + +func (this *PlatformMgr) ModuleName() string { + return "PlatformMgr" +} + +func (this *PlatformMgr) Init() { + +} + +func (this *PlatformMgr) LoadPlatform() { + //构建默认的平台数据 + this.CreateDefaultPlatform() + //获取平台数据 platform_list + + //不使用etcd的情况下走api获取 + if !model.GameParamData.UseEtcd { + //buf, err := webapi.API_GetPlatformData(common.GetAppId()) + //if err == nil { + // ar := webapi_proto.ASPlatformInfo{} + // err = proto.Unmarshal(buf, &ar) + // if err == nil && ar.Tag == webapi_proto.TagCode_SUCCESS { + // for _, value := range ar.Platforms { + // platform := this.CreatePlatform(value.Id, value.Isolated) + // platform.Name = value.PlatformName + // //platform.ConfigId = value.ConfigId + // platform.Disable = value.Disabled + // platform.ServiceUrl = value.CustomService + // platform.CustomType = value.CustomType + // platform.BindOption = value.BindOption + // //platform.ServiceFlag = value.ServiceFlag + // platform.UpgradeAccountGiveCoin = value.UpgradeAccountGiveCoin + // platform.NewAccountGiveCoin = value.NewAccountGiveCoin //新账号奖励金币 + // platform.PerBankNoLimitAccount = value.PerBankNoLimitAccount //同一银行卡号绑定用户数量限制 + // platform.ExchangeMin = value.ExchangeMin + // platform.ExchangeLimit = value.ExchangeLimit + // platform.ExchangeTax = value.ExchangeTax + // platform.ExchangeFlow = value.ExchangeFlow + // platform.ExchangeVer = value.ExchangeVer + // platform.ExchangeFlag = value.ExchangeFlag + // platform.ExchangeForceTax = value.ExchangeForceTax + // platform.ExchangeGiveFlow = value.ExchangeGiveFlow + // platform.VipRange = value.VipRange + // //platform.OtherParams = value.OtherParams + // platform.SpreadConfig = value.SpreadConfig + // //platform.RankSwitch = value.Leaderboard + // //platform.ClubConfig = value.ClubConfig + // platform.VerifyCodeType = value.VerifyCodeType + // //platform.RegisterVerifyCodeSwitch = value.RegisterVerifyCodeSwitch + // for _, v := range value.ThirdGameMerchant { + // platform.ThirdGameMerchant[v.Id] = v.Merchant + // } + // //platform.NeedDeviceInfo = value.NeedDeviceInfo + // platform.NeedSameName = value.NeedSameName + // platform.PerBankNoLimitName = value.PerBankNoLimitName + // platform.ExchangeAlipayMax = value.ExchangeAlipayMax + // platform.ExchangeBankMax = value.ExchangeBankMax + // //platform.DgHboConfig = value.DgHboConfig + // platform.IsCanUserBindPromoter = value.IsCanUserBindPromoter + // platform.UserBindPromoterPrize = value.UserBindPromoterPrize + // //platform.SpreadWinLose = value.SpreadWinLose + // platform.ExchangeMultiple = value.ExchangeMultiple + // if platform.ExchangeFlag&ExchangeFlag_Flow == 0 { //修正下 + // platform.ExchangeFlow = 0 + // } + // platform.MerchantKey = value.MerchantKey + // } + // logger.Logger.Trace("Create platform") + // } else { + // logger.Logger.Error("Unmarshal platform data error:", err) + // } + //} else { + // logger.Logger.Error("Get platfrom data error:", err) + //} + } else { + EtcdMgrSington.InitPlatform() + } +} + +func (this *PlatformMgr) LoadPlatformGameFree() { + //不使用etcd的情况下走api获取 + + //获取平台详细信息 game_config_list + if !model.GameParamData.UseEtcd { + //logger.Logger.Trace("API_GetPlatformConfigData") + //buf, err := webapi.API_GetPlatformConfigData(common.GetAppId()) + //if err == nil { + // pcdr := &webapi_proto.ASGameConfig{} + // err = proto.Unmarshal(buf, pcdr) + // if err == nil && pcdr.Tag == webapi_proto.TagCode_SUCCESS { + // platGameCfgs := pcdr.GetConfigs() + // + // //遍历所有平台配置 + // for _, pgc := range platGameCfgs { + // platormId := pgc.GetPlatformId() + // platform := this.GetPlatform(strconv.Itoa(int(platormId))) + // if platform == nil { + // continue + // } + // + // for _, config := range pgc.GetDbGameFrees() { + // dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(config.DbGameFree.Id) + // if dbGameFree == nil { + // logger.Logger.Error("Platform config data error logic id:", config) + // continue + // } + // platform.PltGameCfg.games[config.DbGameFree.Id] = config + // if config.GetDbGameFree() == nil { //数据容错 + // config.DbGameFree = dbGameFree + // } else { + // CopyDBGameFreeField(dbGameFree, config.DbGameFree) + // } + // logger.Logger.Info("PlatformGameConfig data:", config.DbGameFree.Id) + // } + // platform.PltGameCfg.RecreateCache() + // } + // } else { + // logger.Logger.Error("Unmarshal platform config data error:", err, string(buf)) + // } + //} else { + // logger.Logger.Error("Get platfrom config data error:", err) + //} + } else { + EtcdMgrSington.InitPlatformGameConfig() + } +} + +func (this *PlatformMgr) LoadPlatformPackage() { + if !model.GameParamData.UseEtcd { + + } else { + EtcdMgrSington.InitPlatformPackage() + } +} + +func (this *PlatformMgr) LoadCommonNotice() { + if !model.GameParamData.UseEtcd { + + } else { + EtcdMgrSington.InitCommonNotice() + } +} + +func (this *PlatformMgr) LoadGameMatch() { + if !model.GameParamData.UseEtcd { + + } else { + EtcdMgrSington.InitGameMatchDate() + } +} + +// LoadGlobalGameStatus 加载超管平台游戏开关 +func (this *PlatformMgr) LoadGlobalGameStatus() { + //不使用etcd的情况下走api获取 + if !model.GameParamData.UseEtcd { + ////获取全局游戏开关 + //logger.Logger.Trace("API_GetGlobalGameStatus") + //buf, err := webapi.API_GetGlobalGameStatus(common.GetAppId()) + //if err == nil { + // as := webapi_proto.ASGameConfigGlobal{} + // err = proto.Unmarshal(buf, &as) + // if err == nil && as.Tag == webapi_proto.TagCode_SUCCESS { + // if as.GetGameStatus() != nil { + // status := as.GetGameStatus().GetGameStatus() + // for _, v := range status { + // gameId := v.GetGameId() + // status := v.GetStatus() + // PlatformMgrSingleton.GameStatus[gameId] = status + // } + // } + // } else { + // logger.Logger.Error("Unmarshal GlobalGameStatus data error:", err) + // } + //} else { + // logger.Logger.Error("Get GlobalGameStatus data error:", err) + //} + } else { + EtcdMgrSington.InitGameGlobalStatus() + } +} + +// LoadEntrySwitch 拉取界面入口开关 +func (this *PlatformMgr) LoadEntrySwitch() { + //不使用etcd的情况下走api获取 + if model.GameParamData.UseEtcd { + EtcdMgrSington.InitEntrySwitch() + } else { + + } +} + +func (this *PlatformMgr) Update() { + +} + +func (this *PlatformMgr) Shutdown() { + module.UnregisteModule(this) +} + +func (this *PlatformMgr) InterestClockEvent() int { + return 1< this.LastLogoutTime.Unix() { + isFirstLogin = true + } + + // 跨天业务处理 + tNow := time.Now() + tLastLogout := this.PlayerData.LastLogoutTime + this.PlayerData.LastLoginTime = tNow + this.PlayerData.LastLogoutTime = tNow + this.dirty = true + if !common.InSameDay(tNow, tLastLogout) { //跨天 + logger.Logger.Infof("(this *Player) OnLogined(%v) inSameDay LastLogoutTime(%v)", this.SnId, tLastLogout) + isContinueDay := common.IsContinueDay(tNow, tLastLogout) + // 计算跨了多少天 + var t int + t1 := tLastLogout + t2 := tNow + out := time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, t1.Location()) + in := time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, t2.Location()) + t = int(math.Floor(in.Sub(out).Hours() / 24)) + this.OnDayTimer(true, isContinueDay, t) + } + // 跨月 + inSameMoney := common.InSameMonth(tNow, tLastLogout) + if !inSameMoney { + this.OnMonthTimer() + } + // 跨周 + inSameWeek := common.InSameWeek(tNow, tLastLogout) + if !inSameWeek { + this.OnWeekTimer() + } + + this.SetOnline() + + //测试用 + if !this.IsRob { + old := this.VIP + this.VIP = this.GetVIPLevel(this.CoinPayTotal) + if old != this.VIP { + this.dirty = true + } + } else { + this.VIP = rand.Int31n(6) + 1 + //机器人随机vip和头像 + this.RobRandVip() + } + + this.VipExtra = VipMgrSington.GetVipPointsExtra(this.Platform, this.VIP) + + // 头像决定性别 + this.Sex = (this.Head%2 + 1) % 2 + + // 初始化差异数据 + this.BackDiffData() + + this.BindGroupTag([]string{this.Platform}) + + PlatformMgrSingleton.PlayerLogin(this) + + gameStateMgr.PlayerClear(this) + + //玩家登录事件 + FirePlayerLogined(this) + + this.RandRobotExData() + + this.SendJackPotInit() + + this.GetPayGoodsInfo() + + if !this.IsRob { + PlayerOnlineSington.Check = true + + this.LoadMessage(tLastLogout.Unix(), isFirstLogin, !inSameWeek) + + //登录次数 + this.LoginTimes++ + + //登录事件 + this.ReportLoginEvent() + + //抽奖次数兼容老玩家 + if !this.InitLotteryStatus && WelfareMgrSington.GetPhoneLotteryStatus(this.Platform) == WelfareOpen { + this.addLotteryCount(20) + this.InitLotteryStatus = true + this.dirty = true + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(this.SnId, this.Platform, "", 3, 20, 5, 0)) + } + + //登录日志 + logState := LoginStateMgrSington.GetLoginStateBySid(this.sid) + var clog *model.ClientLoginInfo + if logState != nil { + clog = logState.clog + } + LogChannelSingleton.WriteLog(model.NewLoginLog(this.SnId, common.LoginLogTypeLogin, this.Tel, this.Ip, + this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.City, clog, this.GetTotalCoin(), 0, 0, + this.DeviceName, this.PackageName, this.AppVersion, this.BuildVersion, this.AppChannel)) + + this.OnlineLogLogin() + + this.SendToRepSrv(this.PlayerData) + + //七日活动 + ActSignMgrSington.OnPlayerLogin(this) + + //红点检测 + this.CheckShowRed() + + TaskSubjectSingleton.Touch(common.TaskTypeLogin, &TaskData{SnId: this.SnId, Num: 1}) // 登录游戏 + + this.LoadAfter() + } +} + +func (this *Player) OnRehold() { + logger.Logger.Tracef("(this *Player) OnRehold() %v", this.SnId) + this.SetOnline() + + var gameid int + if this.scene != nil && this.scene.gameSess != nil { + //if this.scene.sceneId == SceneMgrSingleton.GetDgSceneId() { + // // DG特殊处理 + // //如果是之前进入的是DG游戏,就退出DG游戏 + // this.DgGameLogout() + //} else { + // 告诉游戏服务玩家重连 + var gateSid int64 + if this.gateSess != nil { + if srvInfo, ok := this.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { + sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) + gateSid = sessionId.Get() + } + } + pack := &server_proto.WGPlayerRehold{ + Id: proto.Int32(this.SnId), + Sid: proto.Int64(this.sid), + SceneId: proto.Int(this.scene.sceneId), + GateSid: proto.Int64(gateSid), + } + proto.SetDefaults(pack) + this.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERREHOLD), pack) + logger.Logger.Tracef("WG PlayerRehold: %v", pack) + //} + gameid = this.scene.gameId + } + + this.BindGroupTag([]string{this.Platform}) + + //登录事件 + this.ReportLoginEvent() + + PlatformMgrSingleton.PlayerLogin(this) + + gameStateMgr.PlayerClear(this) + + //玩家重连事件 + FirePlayerRehold(this) + + FriendMgrSington.ApplyList(this.Platform, this.SnId) + FriendUnreadMgrSington.CheckSendFriendUnreadData(this.Platform, this.SnId) + + //七日活动. + ActSignMgrSington.OnPlayerLogin(this) + + this.CheckShowRed() + + this.SendJackPotInit() + + this.GetPayGoodsInfo() + + RankMgrSingleton.CheckShowRed(this.SnId) + + if !this.IsRob { + PlayerOnlineSington.Check = true + + // 记录重连日志 + logState := LoginStateMgrSington.GetLoginStateBySid(this.sid) + var clog *model.ClientLoginInfo + if logState != nil { + clog = logState.clog + } + LogChannelSingleton.WriteLog(model.NewLoginLog(this.SnId, common.LoginLogTypeRehold, this.Tel, this.Ip, + this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.City, clog, this.GetTotalCoin(), + gameid, 0, this.DeviceName, this.PackageName, this.AppVersion, this.BuildVersion, this.AppChannel)) + + this.OnlineLogRehold() + } +} + +func (this *Player) CheckShowRed() { + this.MessageShowRed() + + //商城红点 + ShopMgrSington.ShopCheckShowRed(this) + + //数据依赖于背包数据 放入加载背包数据之后执行 + //PetMgrSington.CheckShowRed(this) + + //福利红点 + WelfareMgrSington.WelfareShowRed(this) +} + +// 为了worldsrv和gamesrv上机器人信息一致 +func (this *Player) RandRobotExData() { + if !this.IsRob { + return + } + // 角色 + datas := srvdata.PBDB_Game_IntroductionMgr.Datas.GetArr() + if datas != nil { + //随机 + //var roles = []int32{} + //for _, data := range datas { + // if data.Type == 1 { //角色 + // roles = append(roles, data.Id) + // } + //} + //if roles != nil && len(roles) > 0 { + // randId := common.RandInt32Slice(roles) + // this.Roles = new(model.RolePetInfo) + // this.Roles.ModUnlock = make(map[int32]int32) + // this.Roles.ModUnlock[randId] = 1 + // this.Roles.ModId = randId + //} + //女8男2 + rand := common.RandInt(100) + randId := int32(2000001) + if rand < 20 { + randId = 2000002 + } + this.Roles = new(model.RolePetInfo) + this.Roles.ModUnlock = make(map[int32]int32) + this.Roles.ModUnlock[randId] = 1 + this.Roles.ModId = randId + } + // 宠物 + // datas := srvdata.PBDB_Game_IntroductionMgr.Datas.GetArr() + // if datas != nil { + // var pets = []int32{} + // for _, data := range datas { + // if data.Type == 2 { //宠物 + // pets = append(pets, data.Id) + // } + // } + // if pets != nil && len(pets) > 0 { + // randId := common.RandInt32Slice(pets) + // this.Pets = new(model.RolePetInfo) + // this.Pets.ModUnlock = make(map[int32]int32) + // this.Pets.ModUnlock[randId] = 1 + // this.Pets.ModId = randId + // } + // } +} + +// SendGameConfig 玩家断线重连时,获取玩家所有游戏的配置信息 +func (this *Player) SendGameConfig(gameId int32, plf, chl string) { + pack := &hall_proto.SCGetGameConfig{ + GameId: gameId, + } + + gps := PlatformMgrSingleton.GetGameFrees(this.Platform) + for _, v := range gps { + if v.Status && PlatformMgrSingleton.GameStatus[v.DbGameFree.Id] { + if v.DbGameFree.GetGameRule() != 0 && v.DbGameFree.GetGameId() == gameId { + // 场次配置 + lgc := &hall_proto.GameConfig1{ + LogicId: proto.Int32(v.DbGameFree.Id), + LimitCoin: proto.Int64(v.DbGameFree.GetLimitCoin()), + MaxCoinLimit: proto.Int64(v.DbGameFree.GetMaxCoinLimit()), + BaseScore: proto.Int32(v.DbGameFree.GetBaseScore()), + BetScore: proto.Int32(v.DbGameFree.GetBetLimit()), + OtherIntParams: v.DbGameFree.GetOtherIntParams(), + MaxBetCoin: v.DbGameFree.GetMaxBetCoin(), + MatchMode: proto.Int32(v.DbGameFree.GetMatchMode()), + Status: v.Status, + SceneType: v.DbGameFree.GetSceneType(), + ChessGradeLimit: v.DbGameFree.GetChessGradeLimit(), + RankType: v.DbGameFree.GetRankType(), + SceneAdd: v.DbGameFree.GetSceneAdd(), + } + // 排位场参数 + if v.DbGameFree.GetRankType() > 0 { + lgc.RankType = v.DbGameFree.GetRankType() + lgc.SceneAdd = v.DbGameFree.GetSceneAdd() + } + + pack.GameCfg = append(pack.GameCfg, lgc) + } + } + } + + // 游戏配置 + if common.IsChess(int(gameId)) { + cfg := ChessRankMgrSington.GetChessRankConfig(this.GetPlatform().Name, gameId) + for _, v := range cfg.GetDatas() { + pack.ChessRanks = append(pack.ChessRanks, &hall_proto.ChessRankInfo{ + Score: v.Score, + Name: v.Name, + }) + } + } + + this.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_GETGAMECONFIG), pack) + logger.Logger.Tracef("SendGameConfig:%v", pack) +} + +func (this *Player) LoadMessage(lastLogoutTime int64, isFirstLogin, isSkipWeek bool) { + task.New(nil, + task.CallableWrapper(func(o *basic.Object) interface{} { + //msgs, err := model.GetMessageByNotState(this.SnId, model.MSGSTATE_REMOVEED) + //if err == nil { + // return msgs + //} else { + // logger.Logger.Warnf("[%v] LoadMessage err:%v", this.Name, err) + //} + msgs, err := model.GetMessage(this.Platform, this.SnId) // model.GetNotDelMessage(this.Platform, this.SnId) + if err == nil { + return msgs + } else { + logger.Logger.Warnf("[%v] LoadMessage err:%v", this.Name, err) + } + return nil + }), + task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + if msgs, ok := data.([]model.Message); ok { + for i := 0; i < len(msgs); i++ { + this.msgs[msgs[i].Id.Hex()] = &msgs[i] + } + } + //跨周 邮件>0 + if isSkipWeek && len(this.msgs) > 0 { + //过期邮件处理 + for key, msg := range this.msgs { + if msg.AttachState == model.MSGATTACHSTATE_DEFAULT && msg.Ticket > 0 { + this.TicketTotalDel += msg.Ticket + this.DelMessage(key, 1) + } + } + this.dirty = true + } + + var dbMsgs []*model.Message + + //第一次登录需要屏蔽订阅的邮件 + if !isFirstLogin { + msgs := MsgMgrSington.GetSubscribeMsgs(this.Platform, lastLogoutTime) + for _, msg := range msgs { + bHasAddToPlayer := false + for _, pMsg := range this.msgs { + if pMsg.Pid == msg.Id.Hex() { + bHasAddToPlayer = true + break + } + } + if bHasAddToPlayer == false { + newMsg := model.NewMessage(msg.Id.Hex(), msg.SrcId, "", this.SnId, msg.MType, msg.Title, + msg.Content, msg.Coin, msg.Diamond, model.MSGSTATE_UNREAD, msg.CreatTs, msg.AttachState, + msg.GiftId, msg.Params, msg.Platform, msg.ShowId) + dbMsgs = append(dbMsgs, newMsg) + } + } + } + + if len(dbMsgs) != 0 { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertMessage(this.Platform, dbMsgs...) + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data == nil { + for _, msg := range dbMsgs { + bHasAddToPlayer := false + for _, pMsg := range this.msgs { + if pMsg.Pid == msg.Id.Hex() { + bHasAddToPlayer = true + break + } + } + if bHasAddToPlayer == false { + this.AddMessage(msg) + } + } + // 邮件由客户端拉取 + //this.SendMessage() + } + }), "InsertMessage").StartByFixExecutor("logic_message") + } else { + // this.SendMessage() + } + if len(this.msgs) > model.MSG_MAX_COUNT { + maxTs := int64(0) + for _, m := range this.msgs { + if m.CreatTs > maxTs { + maxTs = m.CreatTs + } + } + this.verifyMessage(maxTs) + } + } + }), "GetMessage").StartByFixExecutor("logic_message") +} + +func (this *Player) SendMessage(showId int64) { + pack := &msg_proto.SCMessageList{} + if len(this.msgs) != 0 { + for _, msg := range this.msgs { + + if msg.State != model.MSGSTATE_REMOVEED && (msg.ShowId == model.HallAll || msg.ShowId&showId != 0) { + + giftState := int32(0) + //if len(msg.GiftId) > 0 { + // gift := GiftMgrSington.GetFromRecvGift(this.SnId, msg.GiftId) + // if gift != nil { + // giftState = gift.State + // } else { + // logger.Logger.Error("player: ", this.SnId, " not find gift : ", msg.GiftId) + // } + //} + pack.Msgs = append(pack.Msgs, &msg_proto.MessageData{ + Id: proto.String(msg.Id.Hex()), + Title: proto.String(msg.Title), + Content: proto.String(msg.Content), + MType: proto.Int32(msg.MType), + SrcId: proto.Int32(msg.SrcId), + SrcName: proto.String(msg.SrcName), + Coin: proto.Int64(msg.Coin), + Diamond: proto.Int64(msg.Diamond), + Ticket: proto.Int64(msg.Ticket), + Grade: proto.Int64(msg.Grade), + State: proto.Int32(msg.State), + Ts: proto.Int32(int32(msg.CreatTs)), + Params: msg.Params, + AttachState: proto.Int32(msg.AttachState), + GiftId: proto.String(msg.GiftId), + GiftState: proto.Int32(giftState), + }) + } + } + proto.SetDefaults(pack) + } + //nil的msg需要发给前端便于覆盖前一个玩家的信息 + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGELIST), pack) +} + +func (this *Player) ReadMessage(id string) { + if msg, exist := this.msgs[id]; exist { + if msg.State == model.MSGSTATE_UNREAD { + msg.State = model.MSGSTATE_READED + + task.New(nil, + task.CallableWrapper(func(o *basic.Object) interface{} { + return model.ReadMessage(msg.Id, msg.Platform) + }), + task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data == nil { + pack := &msg_proto.SCMessageRead{ + Id: proto.String(id), + } + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEREAD), pack) + } + }), "ReadMessage").StartByFixExecutor("logic_message") + } + } +} + +func (this *Player) WebDelMessage(id string) { + if msg, exist := this.msgs[id]; exist { + if msg.State != model.MSGSTATE_REMOVEED { // 未删除状态通知客户端 + pack := &msg_proto.SCMessageDel{ + Id: id, + } + + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEDEL), pack) + } + //删除此邮件 + delete(this.msgs, id) + } +} + +func (this *Player) DelMessage(id string, del int32) bool { + if msg, exist := this.msgs[id]; exist { + if msg.State != model.MSGSTATE_REMOVEED { + + task.New(nil, + task.CallableWrapper(func(o *basic.Object) interface{} { + args := &model.DelMsgArgs{ + Platform: msg.Platform, + Id: msg.Id, + Del: del, + } + return model.DelMessage(args) + }), + task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data == nil { + pack := &msg_proto.SCMessageDel{ + Id: id, + } + + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEDEL), pack) + + msg.State = model.MSGSTATE_REMOVEED + //删除此邮件 + delete(this.msgs, id) + } + }), "DelMessage").StartByFixExecutor("logic_message") + + return true + } + } + return false +} +func (this *Player) MessageShowRed() { + msgMap := make(map[int64]int) + for _, msg := range this.msgs { + if msg.State == model.MSGSTATE_UNREAD || + (msg.State == model.MSGSTATE_READED && msg.AttachState == model.MSGATTACHSTATE_DEFAULT && + (msg.Coin > 0 || msg.Diamond > 0 || len(msg.Params) > 0)) { + if msg.ShowId == model.HallAll { + msgMap[model.HallMain] = 1 + msgMap[model.HallTienlen] = 1 + msgMap[model.HallFish] = 1 + } else { + msgMap[msg.ShowId] = 1 + } + } + } + for showId := range msgMap { + this.SendShowRed(hall_proto.ShowRedCode_Mail, int32(showId), 1) + } +} + +/* +func (this *Player) DelAllMessage() bool { + + var keys []string + args := &model.DelAllMsgArgs{} + pack := &msg_proto.SCMessageDel{} + for key, _ := range this.msgs { + if msg, exist := this.msgs[key]; exist { + if msg.State == model.MSGSTATE_REMOVEED { + break + } + msg.State = model.MSGSTATE_REMOVEED + // model.DelMessage(msg.Id, msg.Platform) + keys = append(keys, key) + pack.Ids = append(pack.Ids, msg.Id.Hex()) + args.Ids = append(args.Ids, msg.Id) + } + } + for _, key := range keys { + delete(this.msgs, key) + } + + task.New(nil, + task.CallableWrapper(func(o *basic.Object) interface{} { + return model.DelAllMessage(args) + }), + task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data == nil { + pack := &msg_proto.SCMessageDel{} + for _, id := range keys { + pack.Ids = append(pack.Ids, id) + delete(this.msgs, id) + } + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEDEL), pack) + for _, msg := range this.msgs { + if msg.State == model.MSGSTATE_REMOVEED { + break + } + msg.State = model.MSGSTATE_REMOVEED + } + + //删除此邮件 + + } + }), "DelMessage").StartByFixExecutor("logic_message") + + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEDEL), pack) + + return true + +}*/ + +func (this *Player) AddMessage(msg *model.Message) { + if msg == nil { + return + } + + if _, exist := this.msgs[msg.Id.Hex()]; !exist { + this.msgs[msg.Id.Hex()] = msg + + //giftState := int32(0) + //if len(msg.GiftId) > 0 { + // gift := GiftMgrSington.GetFromRecvGift(this.SnId, msg.GiftId) + // if gift != nil { + // giftState = gift.State + // } else { + // logger.Logger.Error("player: ", this.SnId, " not find gift : ", msg.GiftId) + // } + //} + + //pack := &msg_proto.SCMessageAdd{ + // Msg: &msg_proto.MessageData{ + // Id: proto.String(msg.Id.Hex()), + // Title: proto.String(msg.Title), + // Content: proto.String(msg.Content), + // MType: proto.Int32(msg.MType), + // SrcId: proto.Int32(msg.SrcId), + // SrcName: proto.String(msg.SrcName), + // Coin: proto.Int64(msg.Coin), + // Ticket: proto.Int64(msg.Ticket), + // Grade: proto.Int64(msg.Grade), + // State: proto.Int32(msg.State), + // Ts: proto.Int32(int32(msg.CreatTs)), + // Params: msg.Params, + // AttachState: proto.Int32(msg.AttachState), + // GiftId: proto.String(msg.GiftId), + // GiftState: proto.Int32(giftState), + // }, + //} + // + //proto.SetDefaults(pack) + //this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_MESSAGEADD), pack) + + //if len(this.msgs) > model.MSG_MAX_COUNT { + //如果邮件达到上限,删除最旧的一封邮件,调整一下,如果有附件没有领取,就不删除 + // OldestKeyId := msg.Id.Hex() + this.verifyoneMessage(msg) // 最新的一封时间最大 + + //} + msgMap := make(map[int64]int) + if msg.ShowId == model.HallAll { + msgMap[model.HallMain] = 1 + msgMap[model.HallTienlen] = 1 + msgMap[model.HallFish] = 1 + } else { + msgMap[msg.ShowId] = 1 + } + for showId := range msgMap { + this.SendShowRed(hall_proto.ShowRedCode_Mail, int32(showId), 1) + } + } +} + +func (this *Player) verifyoneMessage(msg *model.Message) { + var len int + for _, m := range this.msgs { + if m.ShowId == model.HallAll || m.ShowId&msg.ShowId != 0 { + len++ + } + } + OldestKeyId := msg.Id.Hex() + OldestCreatTs := msg.CreatTs + + if len > model.MSG_MAX_COUNT { + for id, m := range this.msgs { + if m.ShowId == model.HallAll || m.ShowId&msg.ShowId != 0 { + if m.CreatTs < OldestCreatTs { + OldestCreatTs = m.CreatTs + OldestKeyId = id + } + } + } + this.DelMessage(OldestKeyId, 1) + } +} + +type msgsort struct { + Id []string + Ts []int64 +} + +func (p *msgsort) Len() int { return len(p.Id) } +func (p *msgsort) Less(i, j int) bool { return p.Ts[i] > p.Ts[j] } +func (p *msgsort) Swap(i, j int) { + p.Id[i], p.Id[j] = p.Id[j], p.Id[i] + p.Ts[i], p.Ts[j] = p.Ts[j], p.Ts[i] +} +func (p *msgsort) Sort() { sort.Sort(p) } + +// 初始化 +func (this *Player) verifyMessage1() { + var delId []string + // delmap := make(map[string]bool) + var mainsort, tiensort, fishsort msgsort + + for id, m := range this.msgs { + + if m.ShowId == model.HallAll { // 所有可见 + mainsort.Id = append(mainsort.Id, id) + mainsort.Ts = append(mainsort.Ts, m.CreatTs) + tiensort.Id = append(tiensort.Id, id) + tiensort.Ts = append(tiensort.Ts, m.CreatTs) + fishsort.Id = append(fishsort.Id, id) + fishsort.Ts = append(fishsort.Ts, m.CreatTs) + } else { + if m.ShowId&model.HallMain != 0 { + mainsort.Id = append(mainsort.Id, id) + mainsort.Ts = append(mainsort.Ts, m.CreatTs) + } else if m.ShowId&model.HallTienlen != 0 { + tiensort.Id = append(tiensort.Id, id) + tiensort.Ts = append(tiensort.Ts, m.CreatTs) + } else if m.ShowId&model.HallFish != 0 { + fishsort.Id = append(fishsort.Id, id) + fishsort.Ts = append(fishsort.Ts, m.CreatTs) + } + } + + } + + if mainsort.Len() > model.MSG_MAX_COUNT { + sort.Sort(&mainsort) + delId = append(delId, mainsort.Id[model.MSG_MAX_COUNT:]...) + } + if tiensort.Len() > model.MSG_MAX_COUNT { + sort.Sort(&tiensort) + delId = append(delId, tiensort.Id[model.MSG_MAX_COUNT:]...) + } + if fishsort.Len() > model.MSG_MAX_COUNT { + sort.Sort(&fishsort) + delId = append(delId, fishsort.Id[model.MSG_MAX_COUNT:]...) + } + + for _, id := range delId { + this.DelMessage(id, 1) + } +} + +func (this *Player) verifyMessage(maxCreatTs int64) { + var OldmainKeyId, OldsunKeyId []string + var mainnum, sunnum int + + for id, m := range this.msgs { + + if m.CreatTs < maxCreatTs { + if m.ShowId == model.HallAll || m.ShowId&model.HallMain != 0 { + mainnum++ + } else { + sunnum++ + } + if mainnum > model.MSG_MAX_COUNT { // 主大厅 + OldmainKeyId = append(OldmainKeyId, id) + } else if sunnum > model.MSG_MAX_COUNT { // 子大厅 + OldsunKeyId = append(OldsunKeyId, id) + } + /*if (m.Coin <= 0 && m.Ticket <= 0 && m.Grade <= 0) || m.AttachState == model.MSGATTACHSTATE_GOT { + OldestCreatTs = m.CreatTs + + }*/ + } + } + + for _, id := range OldmainKeyId { + this.DelMessage(id, 1) + } + for _, id := range OldsunKeyId { + this.DelMessage(id, 1) + } +} + +func (this *Player) EditMessage(msg *model.Message) { + if msg == nil { + return + } + + if _, exist := this.msgs[msg.Id.Hex()]; exist { + this.msgs[msg.Id.Hex()] = msg + } +} + +func (this *Player) SendIosInstallStableMail() { + if this.layered[common.ActId_IOSINSTALLSTABLE] { + logger.Logger.Trace("this.layered[common.ActId_IOSINSTALLSTABLE] is true") + return + } + var newMsg *model.Message + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + newMsg = model.NewMessage("", 0, "", this.SnId, model.MSGTYPE_IOSINSTALLSTABLE, "系统通知", fmt.Sprintf("感谢您下载稳定版本,额外奖励%d元,请查收", int(model.GameParamData.IosStableInstallPrize/100)), + int64(model.GameParamData.IosStableInstallPrize), 0, 0, time.Now().Unix(), 0, "", nil, this.Platform, model.HallAll) + return model.InsertMessage(this.Platform, newMsg) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + this.AddMessage(newMsg) + } + }), "SendMessage").Start() +} + +func (this *Player) TestMail() { + + var newMsg *model.Message + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var otherParams []int32 + otherParams = append(otherParams, 10001, 3) + otherParams = append(otherParams, 20001, 3) + otherParams = append(otherParams, 20002, 3) + newMsg = model.NewMessage("", 0, "", this.SnId, model.MSGTYPE_ITEM, "系统通知道具test", "测试", + 100000, 100, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", otherParams, this.Platform, 0) + return model.InsertMessage(this.Platform, newMsg) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + this.AddMessage(newMsg) + + } + }), "TestSendMessage").Start() +} + +func (this *Player) TestSubMail() { + + var newMsg *model.Message + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var otherParams []int32 + otherParams = append(otherParams, 10001, 3) + otherParams = append(otherParams, 20001, 3) + otherParams = append(otherParams, 20002, 3) + newMsg = model.NewMessage("", 0, "", 0, model.MSGTYPE_ITEM, "系统", "测试", + 100000, 100, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", otherParams, this.Platform, model.HallTienlen) + return model.InsertMessage(this.Platform, newMsg) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + MsgMgrSington.AddMsg(newMsg) + } + }), "TestSubSendMessage").Start() +} + +func (this *Player) ClubChangeCoin(gainWay int32, coin int64, remark string) { + this.AddCoin(coin, 0, gainWay, "", remark) +} +func (this *Player) GetMessageAttach(id string) { + if msg, exist := this.msgs[id]; exist { + if msg.AttachState == model.MSGATTACHSTATE_DEFAULT && (msg.Coin > 0 || msg.Ticket > 0 || + msg.Grade > 0 || len(msg.Params) > 0 || msg.Diamond > 0) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + gift, err := model.GetMessageById(msg.Id.Hex(), msg.Platform) + if err != nil || gift == nil { + return nil + } + if gift.AttachState == model.MSGATTACHSTATE_GOT { + return nil + } + err = model.GetMessageAttach(msg.Id, msg.Platform) + if err != nil { + return nil + } + return gift + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + attach_msg, ok := data.(*model.Message) + dirtyCoin := int64(0) + if ok && attach_msg != nil { + msg.AttachState = model.MSGATTACHSTATE_GOT + notifyClient := true + var remark string + var gainWay int32 = common.GainWay_MessageAttach + + // 领取道具 + addItem := func() { + items := make([]*Item, 0) + if num := len(msg.Params); num > 0 && num%2 == 0 { + for i := 0; i < num; i += 2 { + items = append(items, &Item{ + ItemId: msg.Params[i], // 物品id + ItemNum: int64(msg.Params[i+1]), // 数量 + ObtainTime: time.Now().Unix(), + }) + } + if _, code := BagMgrSingleton.AddJybBagInfo(this, items, 0, gainWay, "mail", remark); code != bag.OpResultCode_OPRC_Sucess { // 领取失败 + logger.Logger.Errorf("CSPlayerSettingHandler AddJybBagInfo err", code) + pack := &msg_proto.SCGetMessageAttach{ + Id: proto.String(""), + } + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_GETMESSAGEATTACH), pack) + } else { + var itemIds []int32 + for _, v := range items { + itemIds = append(itemIds, v.ItemId) + itemData := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(this.Platform, this.SnId, ItemObtain, v.ItemId, itemData.Name, v.ItemNum, "邮件领取") + } + } + PetMgrSington.CheckShowRed(this) + } + this.dirty = true + } + } + + switch msg.MType { + case model.MSGTYPE_ITEM: + remark = "领取道具" + gainWay = common.GainWay_MAIL_MTEM + dirtyCoin = msg.Coin + addItem() + case model.MSGTYPE_IOSINSTALLSTABLE: + remark = "IOS下载稳定版本" + gainWay = common.GainWay_IOSINSTALLSTABLE + dirtyCoin = msg.Coin + case model.MSGTYPE_GIFT: + remark = "礼物" + case model.MSGTYPE_GOLDCOMERANK: + remark = "财神降临奖励" + gainWay = common.GainWay_GoldCome + notifyClient = false + dirtyCoin = msg.Coin + case model.MSGTYPE_RANDCOIN: + remark = "红包雨" + gainWay = common.GainWay_OnlineRandCoin + notifyClient = false + dirtyCoin = msg.Coin + case model.MSGTYPE_REBATE: + remark = "流水返利" + gainWay = common.GainWay_RebateTask + notifyClient = false + dirtyCoin = msg.Coin + //邮件领取 添加日志 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertRebateLog(this.Platform, &model.Rebate{ + SnId: this.SnId, + RebateCoin: msg.Coin, + ReceiveType: 1, + CodeCoin: 0, + }) + }), nil, "InsertRebateLog").StartByFixExecutor("ReceiveCodeCoin") + case model.MSGTYPE_ClubGet: + //if len(msg.Params) != 0 { + // //如果俱乐部解散 就存msg.Params[0] + // remark = fmt.Sprintf("%v", msg.Params[0]) + //} + //gainWay = common.GainWay_ClubGetCoin + //dirtyCoin = msg.Coin + case model.MSGTYPE_ClubPump: + //if len(msg.Params) != 0 { + // remark = fmt.Sprintf("%v", msg.Params[0]) + //} + //gainWay = common.GainWay_ClubPumpCoin + //notifyClient = false + //dirtyCoin = msg.Coin + case model.MSGTYPE_MATCH_SIGNUPFEE: + gainWay = common.GainWay_MatchBreakBack + notifyClient = false + case model.MSGTYPE_MATCH_TICKETREWARD: + gainWay = common.GainWay_MatchSystemSupply + notifyClient = false + this.TicketTotal += msg.Ticket + case model.MSGTYPE_MATCH_SHOPEXCHANGE: + remark = "积分商城兑换" + gainWay = common.GainWay_Exchange + case model.MSGTYPE_MATCH_SHOPERETURN: + remark = "撤单返还" + gainWay = common.GainWay_GradeShopReturn + case model.MSGTYPE_RANK_REWARD: + remark = "排位赛段位奖励" + gainWay = common.GainWay_RankMatch + addItem() + } + if msg.Coin > 0 { + this.AddCoin(msg.Coin, 0, gainWay, msg.Id.Hex(), remark) + //增加泥码 + this.AddDirtyCoin(0, dirtyCoin) + //俱乐部获取不算系统赠送 + if msg.MType != model.MSGTYPE_ClubGet { + this.ReportSystemGiveEvent(int32(msg.Coin), gainWay, notifyClient) //邮件附件算是系统赠送 + } else { //俱乐部获取算充值 + this.AddCoinGiveLog(msg.Coin, 0, 0, gainWay, model.COINGIVETYPE_PAY, "club", "club") + } + this.AddPayCoinLog(msg.Coin, model.PayCoinLogType_Coin, "mail") + if msg.Oper == 0 { //系统赠送 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(this.SnId, this.Name, this.Platform, model.SystemFreeGive_GiveType_MailSystemGive, + model.SystemFreeGive_CoinType_Coin, int64(msg.Coin))) + } + } + if msg.Ticket > 0 { + //增加报名券 + this.AddTicket(msg.Ticket, gainWay, msg.Id.Hex(), remark) + } + if msg.Grade > 0 { + //增加积分 + this.AddGrade(msg.Grade, gainWay, msg.Id.Hex(), remark) + } + if msg.Diamond > 0 { + this.AddDiamond(msg.Diamond, 0, gainWay, msg.Id.Hex(), remark) + if msg.Oper == 0 { //系统赠送 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(this.SnId, this.Name, this.Platform, model.SystemFreeGive_GiveType_MailSystemGive, + model.SystemFreeGive_CoinType_Diamond, int64(msg.Diamond))) + } + } + pack := &msg_proto.SCGetMessageAttach{ + Id: proto.String(id), + } + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_GETMESSAGEATTACH), pack) + } + }), "GetMessageAttach").StartByFixExecutor("logic_message") + } + } +} + +// 一键领取 +func (this *Player) GetMessageAttachs(ids []string) { + var msgs []*model.Message + var Ids []string // 可以领取的邮件 + var platform string + for _, id := range ids { + if msg, exist := this.msgs[id]; exist { + if msg.AttachState == model.MSGATTACHSTATE_DEFAULT && (msg.Coin > 0 || msg.Ticket > 0 || + msg.Grade > 0 || len(msg.Params) > 0 || msg.Diamond > 0) { + Ids = append(Ids, id) + platform = msg.Platform + } + } + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + + magids, err := model.GetMessageAttachs(Ids, platform) + if err != nil { + logger.Logger.Trace("GetMessageAttachs err ", err) + } + return magids + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + magids, ok := data.(*[]string) + + if ok && magids != nil { + for _, id := range *magids { + if msg, exist := this.msgs[id]; exist { + if msg.AttachState == model.MSGATTACHSTATE_DEFAULT && (msg.Coin > 0 || msg.Ticket > 0 || + msg.Grade > 0 || len(msg.Params) > 0 || msg.Diamond > 0) { + msgs = append(msgs, msg) + platform = msg.Platform + } + } + } + + pack := &msg_proto.SCGetMessageAttach{ + // Id: proto.String(id), + } + + for _, msg := range msgs { + pack.Ids = append(pack.Ids, msg.Id.Hex()) + dirtyCoin := int64(0) + msg.AttachState = model.MSGATTACHSTATE_GOT + notifyClient := true + var remark string + var gainWay int32 = common.GainWay_MessageAttach + switch msg.MType { + case model.MSGTYPE_ITEM: + remark = "领取道具" + gainWay = common.GainWay_MAIL_MTEM + dirtyCoin = msg.Coin + items := make([]*Item, 0) + if num := len(msg.Params); num > 0 && num%2 == 0 { + for i := 0; i < num; i += 2 { + items = append(items, &Item{ + ItemId: msg.Params[i], // 物品id + ItemNum: int64(msg.Params[i+1]), // 数量 + ObtainTime: time.Now().Unix(), + }) + } + if _, code := BagMgrSingleton.AddJybBagInfo(this, items, 0, gainWay, "mail", remark); code != bag.OpResultCode_OPRC_Sucess { // 领取失败 + logger.Logger.Errorf("CSPlayerSettingHandler AddJybBagInfo err", code) + /* + pack := &msg_proto.SCGetMessageAttach{ + Id: proto.String(""), + } + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_GETMESSAGEATTACH), pack) + */ + } else { + var itemIds []int32 + for _, v := range items { + itemIds = append(itemIds, v.ItemId) + itemData := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(this.Platform, this.SnId, ItemObtain, v.ItemId, itemData.Name, v.ItemNum, "邮件领取") + } + } + PetMgrSington.CheckShowRed(this) + } + this.dirty = true + } + case model.MSGTYPE_IOSINSTALLSTABLE: + remark = "IOS下载稳定版本" + gainWay = common.GainWay_IOSINSTALLSTABLE + dirtyCoin = msg.Coin + case model.MSGTYPE_GIFT: + remark = "礼物" + case model.MSGTYPE_GOLDCOMERANK: + remark = "财神降临奖励" + gainWay = common.GainWay_GoldCome + notifyClient = false + dirtyCoin = msg.Coin + case model.MSGTYPE_RANDCOIN: + remark = "红包雨" + gainWay = common.GainWay_OnlineRandCoin + notifyClient = false + dirtyCoin = msg.Coin + case model.MSGTYPE_REBATE: + remark = "流水返利" + gainWay = common.GainWay_RebateTask + notifyClient = false + dirtyCoin = msg.Coin + //邮件领取 添加日志 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertRebateLog(this.Platform, &model.Rebate{ + SnId: this.SnId, + RebateCoin: msg.Coin, + ReceiveType: 1, + CodeCoin: 0, + }) + }), nil, "InsertRebateLog").StartByFixExecutor("ReceiveCodeCoin") + case model.MSGTYPE_ClubGet: + //if len(msg.Params) != 0 { + // //如果俱乐部解散 就存msg.Params[0] + // remark = fmt.Sprintf("%v", msg.Params[0]) + //} + //gainWay = common.GainWay_ClubGetCoin + //dirtyCoin = msg.Coin + case model.MSGTYPE_ClubPump: + //if len(msg.Params) != 0 { + // remark = fmt.Sprintf("%v", msg.Params[0]) + //} + //gainWay = common.GainWay_ClubPumpCoin + //notifyClient = false + //dirtyCoin = msg.Coin + case model.MSGTYPE_MATCH_SIGNUPFEE: + gainWay = common.GainWay_MatchBreakBack + notifyClient = false + case model.MSGTYPE_MATCH_TICKETREWARD: + gainWay = common.GainWay_MatchSystemSupply + notifyClient = false + this.TicketTotal += msg.Ticket + case model.MSGTYPE_MATCH_SHOPEXCHANGE: + remark = "积分商城兑换" + gainWay = common.GainWay_Exchange + case model.MSGTYPE_MATCH_SHOPERETURN: + remark = "撤单返还" + gainWay = common.GainWay_GradeShopReturn + } + if msg.Coin > 0 { + this.AddCoin(msg.Coin, 0, gainWay, msg.Id.Hex(), remark) + //增加泥码 + this.AddDirtyCoin(0, dirtyCoin) + //俱乐部获取不算系统赠送 + if msg.MType != model.MSGTYPE_ClubGet { + this.ReportSystemGiveEvent(int32(msg.Coin), gainWay, notifyClient) //邮件附件算是系统赠送 + } else { //俱乐部获取算充值 + this.AddCoinGiveLog(msg.Coin, 0, 0, gainWay, model.COINGIVETYPE_PAY, "club", "club") + } + this.AddPayCoinLog(msg.Coin, model.PayCoinLogType_Coin, "mail") + if msg.Oper == 0 { //系统赠送 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(this.SnId, this.Name, this.Platform, model.SystemFreeGive_GiveType_MailSystemGive, + model.SystemFreeGive_CoinType_Coin, int64(msg.Coin))) + } + } + if msg.Ticket > 0 { + //增加报名券 + this.AddTicket(msg.Ticket, gainWay, msg.Id.Hex(), remark) + } + if msg.Grade > 0 { + //增加积分 + this.AddGrade(msg.Grade, gainWay, msg.Id.Hex(), remark) + } + if msg.Diamond > 0 { + this.AddDiamond(msg.Diamond, 0, gainWay, msg.Id.Hex(), remark) + if msg.Oper == 0 { //系统赠送 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(this.SnId, this.Name, this.Platform, model.SystemFreeGive_GiveType_MailSystemGive, + model.SystemFreeGive_CoinType_Diamond, int64(msg.Diamond))) + } + } + + } + + proto.SetDefaults(pack) + this.SendToClient(int(msg_proto.MSGPacketID_PACKET_SC_GETMESSAGEATTACH), pack) + } + }), "GetMessageAttach").StartByFixExecutor("logic_message") + +} + +func (this *Player) GetMessageByGiftId(id string) *model.Message { + for _, msg := range this.msgs { + if msg.GiftId == id && msg.State != model.MSGSTATE_REMOVEED { + return msg + } + } + return nil +} + +// 踢掉线 +func (this *Player) Kickout(reason int32) { + if this.IsOnLine() { + logger.Logger.Trace("(this *Player) Kickout()", this.SnId) + scDisconnect := &login_proto.SSDisconnect{ + SessionId: proto.Int64(this.sid), + Type: proto.Int32(reason), + } + proto.SetDefaults(scDisconnect) + this.SendToClient(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), scDisconnect) + + LoginStateMgrSington.LogoutBySid(this.sid) + this.DropLine() + this.DgGameLogout() + } +} + +// DropLine 掉线 +// 1.玩家登出 +// 2.玩家网络断开 +// 3.被踢掉线 +func (this *Player) DropLine() { + logger.Logger.Tracef("(this *Player) DropLine() %v", this.SnId) + if !this.IsRob { + TournamentMgr.Quit(this.Platform, this.SnId) + + // 记录掉线日志 + logState := LoginStateMgrSington.GetLoginStateBySid(this.sid) + var clog *model.ClientLoginInfo + if logState != nil { + clog = logState.clog + } + var gameid int + if this.scene != nil && this.scene.gameSess != nil { + gameid = this.scene.gameId + } + LogChannelSingleton.WriteLog(model.NewLoginLog(this.SnId, common.LoginLogTypeDrop, this.Tel, this.Ip, + this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.City, clog, this.GetTotalCoin(), + gameid, this.LastGameId, this.DeviceName, this.PackageName, this.AppVersion, this.BuildVersion, this.AppChannel)) + + this.SendPlayerCoin() + this.OnlineLogDrop() + + PlayerOnlineSington.Check = true + } + + this.SetOffline() + this.PlayerData.LastLogoutTime = time.Now().Local() + FriendMgrSington.UpdateLogoutTime(this.Platform, this.SnId) + if this.scene != nil && this.scene.gameSess != nil { + pack := &server_proto.WGPlayerDropLine{ + Id: proto.Int32(this.SnId), + SceneId: proto.Int(this.scene.sceneId), + } + proto.SetDefaults(pack) + this.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERDROPLINE), pack) + } + PlayerMgrSington.DroplinePlayer(this) + PlatformMgrSingleton.PlayerLogout(this) + this.sid = 0 + this.gateSess = nil + //统计在线时长日志 + //this.StatisticsOllen(this.PlayerData.LastLogoutTime) + + gameStateMgr.PlayerClear(this) + + //玩家掉线事件 + FirePlayerDropLine(this) +} + +// 退出 +func (this *Player) Logout() { + logger.Logger.Tracef("(this *Player) Logout() %v", this.SnId) + //退出比赛 + //this.QuitMatch(false) + // 在线奖励:累计在线时长 + //this.OnlineRewardAddUpOnlineDuration() + + scLogout := &login_proto.SCLogout{ + OpRetCode: login_proto.OpResultCode_OPRC_Sucess, + } + proto.SetDefaults(scLogout) + this.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_LOGOUT), scLogout) + this.SetOffline() + this.LastLogoutTime = time.Now().Local() + FriendMgrSington.UpdateLogoutTime(this.Platform, this.SnId) + //clubManager.DropLinePlayer(this.SnId) + PlayerMgrSington.DroplinePlayer(this) + PlatformMgrSingleton.PlayerLogout(this) + if !this.IsRobot() { + PlayerOnlineSington.Check = true + } + this.sid = 0 + this.gateSess = nil + this.DgGameLogout() + gameStateMgr.PlayerClear(this) + this.OnlineLogLogout() +} + +func (this *Player) DgGameLogout() { + //if this.scene != nil { + // if this.scene.sceneId == SceneMgrSingleton.GetDgSceneId() { + // var agentName, agentKey, thirdPlf string + // if len(this.BakDgHboName) > 0 { + // if strings.Contains(this.BakDgHboName, "dg") { + // agentName, agentKey, thirdPlf = model.OnlyGetDgConfigByPlatform(this.Platform) + // } else { + // agentName, agentKey, thirdPlf = model.OnlyGetHboConfigByPlatform(this.Platform) + // } + // + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // webapi.API_DgLogout(thirdPlf, common.GetAppId(), this.DgGame, this.DgPass, agentName, agentKey) + // return nil + // }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { + // this.scene = nil + // }), "DgGameLogout").Start() + // } + // + // } + //} +} + +func (this *Player) ThirdGameLogout() { + _LeaveTransferThird2SystemTask(this) +} + +func (this *Player) IsOnLine() bool { + return this.state != PlayerStateOffline +} + +func (this *Player) SetOnline() { + this.state = PlayerStateOnline +} + +func (this *Player) IsOffline() bool { + return this.state == PlayerStateOffline +} + +func (this *Player) SetOffline() { + this.state = PlayerStateOffline +} + +// OnLogouted 玩家登出 +func (this *Player) OnLogouted() { + logger.Logger.Tracef("(this *Player) OnLogouted() %v", this.SnId) + + //在线时长日志 + //this.WriteOllenLog() + + if !this.IsRob { + FriendUnreadMgrSington.SaveFriendUnreadData(this.Platform, this.SnId) + MatchSeasonMgrSington.SaveMatchSeasonData(this.SnId, true) + } + //平台数据 + PlatformMgrSingleton.PlayerLogout(this) + //PlayerSingleAdjustMgr.DelPlayerData(this.Platform, this.SnId) + + //离线玩家清空俱乐部信息 + //delete(clubManager.theInClubId, this.SnId) + + //玩家登出事件 + FirePlayerLogouted(this) + + //登录日志 + logState := LoginStateMgrSington.GetLoginStateBySid(this.sid) + var clog *model.ClientLoginInfo + if logState != nil { + clog = logState.clog + } + //排除掉机器人 + if !this.IsRob { + LogChannelSingleton.WriteLog(model.NewLoginLog(this.SnId, common.LoginLogTypeLogout, this.Tel, this.Ip, + this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.City, clog, this.GetTotalCoin(), + 0, 0, this.DeviceName, this.PackageName, this.AppVersion, this.BuildVersion, this.AppChannel)) + this.OnlineLogLogout() + } + + //退出通知 + //ActMonitorMgrSington.SendActMonitorEvent(ActState_Login, this.SnId, this.Name, this.Platform, 0, 0, "", 1) + + // 更新数据库 + logger.Logger.Tracef("###%v unmount from DBSaver[DelPlayer]", this.Name) + DbSaver_Inst.UnregisteDbSaveTask(this) + this.Save(true) +} + +func (this *Player) MarshalData(gameid int) (d []byte, e error) { + d, e = netlib.Gob.Marshal(this.PlayerData) + return +} + +func (this *Player) MarshalSingleAdjustData(gamefreeid int32) (d []byte, e error) { + if this.IsRob { + return + } + sa := PlayerSingleAdjustMgr.GetSingleAdjust(this.Platform, this.SnId, gamefreeid) + if sa != nil { + d, e = netlib.Gob.Marshal(sa) + } + return +} + +// UnmarshalData 更新玩家数据 +// 例如游戏服数据同步 +func (this *Player) UnmarshalData(data []byte, scene *Scene) { + pd := &model.PlayerData{} + if err := netlib.Gob.Unmarshal(data, pd); err != nil { + logger.Logger.Warn("Player.SyncData err:", err) + return + } + + // GDatas 同步 + for _, v := range []string{ + strconv.Itoa(int(scene.dbGameFree.GetId())), + strconv.Itoa(int(scene.dbGameFree.GetGameId())), + common.GetKeyNoviceGameId(int(scene.dbGameFree.GetGameId())), + common.GetKeyGameType(int(scene.dbGameFree.GetGameType())), + } { + if d, ok := pd.GDatas[v]; ok { + this.GDatas[v] = d + } + } + + this.LastRechargeWinCoin = pd.LastRechargeWinCoin + oldRecharge := int64(0) + oldExchange := int64(0) + if this.PlayerData.TodayGameData != nil { + oldRecharge = this.PlayerData.TodayGameData.RechargeCoin + oldExchange = this.PlayerData.TodayGameData.ExchangeCoin + } + this.PlayerData.TodayGameData = pd.TodayGameData + if this.PlayerData.TodayGameData != nil { + this.PlayerData.TodayGameData.RechargeCoin = oldRecharge + this.PlayerData.TodayGameData.ExchangeCoin = oldExchange + } + this.PlayerData.YesterdayGameData = pd.YesterdayGameData + this.PlayerData.IsFoolPlayer = pd.IsFoolPlayer + this.PlayerData.TotalGameData = pd.TotalGameData + this.PlayerData.WinTimes = pd.WinTimes + this.PlayerData.FailTimes = pd.FailTimes + this.PlayerData.DrawTimes = pd.DrawTimes + this.PlayerData.WinCoin = pd.WinCoin + this.PlayerData.FailCoin = pd.FailCoin + this.PlayerData.TotalIn = pd.TotalIn + this.PlayerData.TotalOut = pd.TotalOut + this.PlayerData.ChessGrade = pd.ChessGrade + this.PlayerData.MoneyPond = pd.MoneyPond + this.PlayerData.FishLevel = pd.FishLevel + this.PlayerData.FishExp = pd.FishExp + this.PlayerData.UnMaxPower = pd.UnMaxPower + this.PlayerData.PowerList = pd.PowerList + this.PlayerData.PlayerTax = pd.PlayerTax + this.PlayerData.TotalFlow = pd.TotalFlow + this.dirty = true +} + +func (this *Player) MarshalIParam() []*server_proto.PlayerIParam { + var params []*server_proto.PlayerIParam + for i, v := range this.Iparams { + params = append(params, &server_proto.PlayerIParam{ + ParamId: proto.Int(i), + IntVal: proto.Int64(v), + }) + } + return params +} + +func (this *Player) UnmarshalIParam(params []*server_proto.PlayerIParam) { + for _, p := range params { + this.Iparams[int(p.GetParamId())] = p.GetIntVal() + } +} + +func (this *Player) MarshalSParam() []*server_proto.PlayerSParam { + var params []*server_proto.PlayerSParam + for i, v := range this.sparams { + params = append(params, &server_proto.PlayerSParam{ + ParamId: proto.Int(i), + StrVal: proto.String(v), + }) + } + return params +} + +func (this *Player) UnmarshalSParam(params []*server_proto.PlayerSParam) { + for _, p := range params { + this.sparams[int(p.GetParamId())] = p.GetStrVal() + } +} + +func (this *Player) MarshalCParam() []*server_proto.PlayerCParam { + var params []*server_proto.PlayerCParam + for k, v := range this.cparams { + params = append(params, &server_proto.PlayerCParam{ + StrKey: proto.String(k), + StrVal: proto.String(v), + }) + } + return params +} + +func (this *Player) SendToRepSrv(pd *model.PlayerData) { + //replaySess := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), ReplayServerType, ReplayServerId) + //if replaySess != nil { + // var buf bytes.Buffer + // enc := gob.NewEncoder(&buf) + // err := enc.Encode(pd) + // if err != nil { + // logger.Logger.Info("(this *Player) SendToRepSrv json.Marshal error", err) + // } else { + // pack := &server_proto.WRPlayerData{ + // PlayerData: buf.Bytes(), + // } + // proto.SetDefaults(pack) + // replaySess.Send(int(server_proto.SSPacketID_PACKET_WR_PlayerData), pack) + // } + //} +} + +func (this *Player) CanDelete() bool { + if this.isDelete { + return true + } + return !this.IsOnLine() && + !this.dirty && + time.Now().Sub(this.lastSaved) > time.Minute*5 && + this.scene == nil +} + +func (this *Player) Time2Save() { + this.Save(false) + if this != nil && this.CanDelete() { + PlayerMgrSington.DelPlayer(this.SnId) + } +} + +func (this *Player) Save(force bool) { + if this.isDelete { + return + } + // 用户缓存数据清除前同步到player_logicleveldata + if !this.IsRob { + pi := model.ClonePlayerData(this.PlayerData) + if pi != nil { + this.SendToRepSrv(pi) + } + this.SendPlayerCoin() + } + + if !this.dirty && !force { + return + } + + this.dirty = false + if !this.IsRob { //机器人数据不再保存 + logger.Logger.Infof("(this *Player) Time2Save() %v", this.SnId) + pi := model.ClonePlayerData(this.PlayerData) + if pi != nil { + this.SendToRepSrv(pi) + // 跨天任务依赖LastLogoutTime的准确性,跨天任务是定时器ClockMgrSington触发的,所以这里要用定时器的触发时间 + pi.LastLogoutTime = ClockMgrSington.LastTickTime + t := task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if !model.SavePlayerData(pi) { + //save 失败先写到json里面 + model.BackupPlayerData(pi) + return false + } + for _, v := range internal.GetPlayerLoads() { + v.Save(pi.Platform, pi.SnId, true, force) + } + return true + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if saved, ok := i.(bool); ok && saved { + bak := fmt.Sprintf("%v.json", pi.AccountId) + if exist, _ := common.PathExists(bak); exist { + os.Remove(bak) + } + } + }), "SavePlayerTask") + if b := t.StartByExecutor(strconv.Itoa(int(this.SnId))); b { + this.lastSaved = time.Now() + } + } + } +} + +func (this *Player) GetCoin() int64 { + return this.Coin +} + +//func (this *Player) TotalData(num int64, gainWay int32) { +// if this.IsRob { +// return +// } +// //num = int64(math.Abs(float64(num))) +// //sort := common.GetSortByGainWay(gainWay) +// //switch sort { +// //case common.GainWaySort_Act: +// // //活动金额累加 +// // this.ActivityCoin += int32(num) +// //case common.GainWaySort_Club: +// // switch gainWay { +// // case common.GainWay_ClubGiveCoin: //出账 +// // //俱乐部出账 +// // this.ClubOutCoin += num +// // case common.GainWay_ClubGetCoin: +// // //俱乐部入账 +// // this.ClubInCoin += num +// // } +// //case common.GainWaySort_Rebate: +// // //返利获取 也叫 手动洗码 +// // this.TotalRebateCoin += num +// //} +//} + +// AddDiamond 添加钻石 +// num 总数 +// add num总数中有多少是加成获得 +func (this *Player) AddDiamond(num, add int64, gainWay int32, oper, remark string) { + if num == 0 { + return + } + logger.Logger.Tracef("snid(%v) AddDiamond(%v)", this.SnId, num) + //async := false + //if num > 0 && this.scene != nil && !this.scene.IsTestScene() && this.scene.sceneMode != common.SceneMode_Thr { //游戏场中加币,需要同步到gamesrv上 + // if StartAsyncAddCoinTransact(this, num, gainWay, oper, remark, true, 0, true) { + // async = true + // } + //} + + if num != 0 /*&& !async*/ { + this.dirty = true + if num > 0 { + this.Diamond += num + } else { + if -num > this.Diamond { + logger.Logger.Errorf("Player.AddCoin exception!!! num(%v) oper(%v)", num, oper) + num = -this.Diamond + this.Diamond = 0 + } else { + this.Diamond += num + } + } + + this.SendDiffData() + if !this.IsRob { + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: this.Platform, + SnID: this.SnId, + ChangeType: common.BillTypeDiamond, + ChangeNum: num, + RemainNum: this.Diamond, + Add: add, + LogType: gainWay, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } +} + +// AddCoin 添加钻石 +// num 总数 +// add num总数中有多少是加成获得 +func (this *Player) AddCoin(num, add int64, gainWay int32, oper, remark string) { + if num == 0 { + return + } + logger.Logger.Tracef("snid(%v) AddCoin(%v)", this.SnId, num) + + //this.TotalData(num, gainWay) + + async := false + if num > 0 && this.scene != nil && !this.scene.IsTestScene() && this.scene.sceneMode != common.SceneMode_Thr { //游戏场中加币,需要同步到gamesrv上 + if StartAsyncAddCoinTransact(this, num, gainWay, oper, remark, true, 0, true) { + async = true + } + } + + if num != 0 && !async { + this.dirty = true + if num > 0 { + this.Coin += num + } else { + if -num > this.Coin { + logger.Logger.Errorf("Player.AddCoin exception!!! num(%v) oper(%v)", num, oper) + num = -this.Coin + this.Coin = 0 + } else { + this.Coin += num + } + } + + this.SendDiffData() + if !this.IsRob { + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: this.Platform, + SnID: this.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: num, + RemainNum: this.Coin, + Add: add, + LogType: gainWay, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } +} + +func (this *Player) AddCoinAsync(num, add int64, gainWay int32, oper, remark string, broadcast bool, retryCnt int, writeLog bool) bool { + if num == 0 { + return false + } + + //if retryCnt == 0 { + // this.TotalData(num, gainWay) + //} + + //玩家可能正在换房间 + async := false + if num > 0 && retryCnt < 3 && this.scene != nil && !this.scene.IsTestScene() && this.scene.sceneMode != common.SceneMode_Thr { //游戏场中加币,需要同步到gamesrv上 + if StartAsyncAddCoinTransact(this, num, gainWay, oper, remark, broadcast, retryCnt, writeLog) { + async = true + } + } + + if num != 0 && !async { + this.dirty = true + if num > 0 { + this.Coin += num + } else { + if -num > this.Coin { + logger.Logger.Errorf("Player.AddCoin exception!!! num(%v) oper(%v)", num, oper) + num = -this.Coin + this.Coin = 0 + } else { + this.Coin += num + } + } + + this.SendDiffData() + if !this.IsRob && writeLog { + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: this.Platform, + SnID: this.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: num, + RemainNum: this.Coin, + Add: add, + LogType: gainWay, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + } + + return async +} + +//func (this *Player) AddClubCoin(num int64, gainWay int32, oper, remark string) { +// if num == 0 { +// return +// } +// this.TotalData(num, gainWay) +// this.ClubCoin += num +// +// if num != 0 { +// this.dirty = true +// this.SendDiffData() +// restCnt := this.ClubCoin +// log := model.NewCoinLogEx(this.SnId, num, restCnt, this.SafeBoxCoin, this.Ver, gainWay, 0, +// oper, remark, this.Platform, this.Channel, this.BeUnderAgentCode, 0, this.PackageID, 0) +// if log != nil { +// CoinLogChannelSington.Write(log) +// } +// } +//} + +// 增加泥码 +func (this *Player) AddDirtyCoin(paycoin, givecoin int64) { + if this.IsRob { + return + } + + //if cfg, ok := ProfitControlMgrSington.GetCfg(this.Platform); ok && cfg != nil && paycoin >= 0 { + // //洗码折算率=(玩家剩余泥码*洗码折算率+期望营收)/(充值额+赠送额+泥码余额) + // this.RecalcuWashingCoinConvRate(cfg.Rate, paycoin, givecoin) + //} + // + //this.DirtyCoin += paycoin + givecoin + //if this.DirtyCoin < 0 { + // this.DirtyCoin = 0 + //} + this.dirty = true +} + +// 洗码 +func (this *Player) WashingCoin(coin int64) int64 { + if this.IsRob { + return 0 + } + if coin <= 0 { + return 0 + } + + //if this.DirtyCoin > coin { + // this.DirtyCoin -= coin + // this.dirty = true + // return coin + //} + // + ////剩余多少泥码,清洗多少 + //coin = this.DirtyCoin + //this.DirtyCoin = 0 + return coin +} + +func (this *Player) AddTicket(num int64, gainWay int32, oper, remark string) { + if num == 0 { + return + } + if num > 0 { + this.Ticket += num + } else { + if -num > this.Ticket { + logger.Logger.Errorf("Player.AddTicket exception!!! num(%v) oper(%v)", num, oper) + num = -this.Ticket + this.Ticket = 0 + } else { + this.Ticket += num + } + } + if num != 0 { + this.dirty = true + this.diffData.Ticket = -1 + this.SendDiffData() + //restCnt := this.Ticket + //log := model.NewTicketLogEx(this.SnId, num, restCnt, this.Ver, gainWay, oper, remark, this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.InviterId) + //if log != nil { + // TicketLogChannelSington.Write(log) + //} + } +} + +func (this *Player) AddGrade(num int64, gainWay int32, oper, remark string) { + if num == 0 { + return + } + if num > 0 { + this.Grade += num + } else { + if -num > this.Grade { + logger.Logger.Errorf("Player.AddGrade exception!!! num(%v) oper(%v)", num, oper) + num = -this.Grade + this.Grade = 0 + } else { + this.Grade += num + } + } + if num != 0 { + this.dirty = true + this.diffData.Grade = -1 + this.SendDiffData() + //restCnt := this.Grade + //log := model.NewGradeLogEx(this.SnId, num, restCnt, this.Ver, gainWay, oper, remark, this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.InviterId) + //if log != nil { + // GradeLogChannelSington.Write(log) + //} + } +} + +func (this *Player) AddChessScore(num int64) { + if num == 0 { + return + } + if num > 0 { + this.ChessGrade += num + } else { + if -num > this.ChessGrade { + logger.Logger.Errorf("Player.AddChessGrade exception!!! num(%v) oper(%v)", num) + num = -this.ChessGrade + this.ChessGrade = 0 + } else { + this.ChessGrade += num + } + } + if num != 0 { + this.dirty = true + this.diffData.ChessGrade = -1 + this.SendDiffData() + //restCnt := this.Grade + //log := model.NewGradeLogEx(this.SnId, num, restCnt, this.Ver, gainWay, oper, remark, this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, this.InviterId) + //if log != nil { + // GradeLogChannelSington.Write(log) + //} + } +} + +func (this *Player) OnSecTimer() { + FirePlayerSecTimer(this) +} + +func (this *Player) OnMiniTimer() { + FirePlayerMiniTimer(this) +} + +func (this *Player) OnHourTimer() { + FirePlayerHourTimer(this) +} + +func (this *Player) OnDayTimer(login, continuous bool, t int) { + // 校验,跨天操作一天之内只执行一次 + if common.InSameDayNoZero(time.Now().Local(), this.lastOnDayChange) { + return + } + this.lastOnDayChange = time.Now().Local() + logger.Logger.Infof("(this *Player) (%v) OnDayTimer(%v,%v) ", this.SnId, login, continuous) + FirePlayerDayTimer(this, login, continuous) + + this.dirty = true + if login || this.scene == nil { + //跨天登录 数据给昨天,今天置为空 + this.YesterdayGameData = this.TodayGameData + this.TodayGameData = model.NewPlayerGameCtrlData() + /* + for k, v := range this.YesterdayGameData.CtrlData { + t := &model.PlayerGameStatics{} + t.AvgBetCoin = v.AvgBetCoin + this.TodayGameData.CtrlData[k] = t + } + */ + } + + this.OnTimeDayTotal(continuous, t) + + //商城数据更新 + this.ShopTotal = make(map[int32]*model.ShopTotal) + this.ShopLastLookTime = make(map[int32]int64) + // 福利活动更新 + WelfareMgrSington.OnDayChanged(this) + //七日活动 + ActSignMgrSington.OnDayChanged(this) + this.VipMatchTimes = 0 + //VIP商城数据更新 + this.UpdateVipShopData() + // 重置每日任务 + if this.WelfData != nil { + if this.WelfData.Task != nil { + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.GetActivityType() == common.TaskActivityTypeEveryDay { + this.WelfData.Task[v.GetId()] = &model.TaskData{} + } + } + this.WelfData.Task[common.TaskIDInvitePlayGame] = &model.TaskData{} + this.WelfData.Task[common.TaskIDInviteFirstLogin] = &model.TaskData{} + } + } + TaskSubjectSingleton.Touch(common.TaskTypeFirstLogin, &TaskData{SnId: this.SnId, Num: 1}) // 首次登录游戏 + TaskSubjectSingleton.Touch(common.TaskTypeLogin, &TaskData{SnId: this.SnId, Num: 1}) // 登录游戏 +} + +func (this *Player) OnTimeDayTotal(continuous bool, t int) { + for k, tgd := range this.TotalGameData { + for i := 0; i < t; i++ { + tgd = append(tgd, new(model.PlayerGameTotal)) + } + if len(tgd) > 30 { + tgd = tgd[len(tgd)-30:] + } + this.TotalGameData[k] = tgd + } +} + +func (this *Player) OnMonthTimer() { + //判断是否一天即可过滤0点多次切换 + if common.InSameDayNoZero(time.Now().Local(), this.lastOnMonthChange) { + return + } + this.lastOnMonthChange = time.Now().Local() + FirePlayerMonthTimer(this) +} + +func (this *Player) OnWeekTimer() { + //判断是否一天即可过滤0点多次切换 + if common.InSameDayNoZero(time.Now().Local(), this.lastOnWeekChange) { + return + } + this.lastOnWeekChange = time.Now().Local() + FirePlayerWeekTimer(this) + + //清理比赛券 + ticket := this.Ticket + if ticket > 0 { + this.AddTicket(-ticket, common.GainWay_Expire, "system", "过期清理") + this.TicketTotalDel += ticket + this.dirty = true + } + + if len(this.msgs) > 0 { + //自然过渡执行 + //过期邮件处理 + var keysId []string + for key, msg := range this.msgs { + if msg.AttachState == model.MSGATTACHSTATE_DEFAULT && msg.Ticket > 0 { + this.TicketTotalDel += msg.Ticket + keysId = append(keysId, key) + } + } + this.dirty = true + for _, v := range keysId { + this.DelMessage(v, 1) + } + } + + // 重置周任务 + if this.WelfData != nil { + if this.WelfData.Task != nil { + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.GetActivityType() == common.TaskActivityTypeWeek { + this.WelfData.Task[v.GetId()] = &model.TaskData{} + } + } + } + } + // 重置周任务 +} + +func (this *Player) GetName() string { + return this.Name +} + +func (this *Player) setName(newName string) string { + this.Name = newName + return this.Name +} + +func (this *Player) GetIP() string { + return this.Ip +} + +func (this *Player) CreateScene(sceneId, gameId, gameMode, sceneMode int, numOfGames int32, params []int32, dbGameFree *server_proto.DB_GameFree) (*Scene, hall_proto.OpResultCode_Game) { + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Warnf("(this *Player) EnterScene %v, %v GameSessMgrSington.GetMinLoadSess() = nil ", this.SnId, gameId) + return nil, hall_proto.OpResultCode_Game_OPRC_SceneServerMaintain_Game + } + + s := SceneMgrSingleton.CreateScene(0, this.SnId, sceneId, gameId, gameMode, sceneMode, 1, numOfGames, params, gs, this.GetPlatform(), 0, dbGameFree, dbGameFree.GetId()) + if s == nil { + logger.Logger.Tracef("(this *Player) EnterScene %v, SceneMgrSingleton.CreateScene() = nil ", this.SnId) + return nil, hall_proto.OpResultCode_Game_OPRC_Error_Game + } + return s, hall_proto.OpResultCode_Game_OPRC_Sucess_Game +} + +func (this *Player) CreateLocalGameScene(sceneId, gameId, gameSite, sceneMode, playerNum int, params []int32, + dbGameFree *server_proto.DB_GameFree, baseScore, groupId int32) (*Scene, hall_proto.OpResultCode_Game) { + gs := GameSessMgrSington.GetMinLoadSess(gameId) + if gs == nil { + logger.Logger.Warnf("(this *Player) CreateLocalGameScene %v, %v GameSessMgrSington.GetMinLoadSess() = nil ", this.SnId, gameId) + return nil, hall_proto.OpResultCode_Game_OPRC_SceneServerMaintain_Game + } + + s := SceneMgrSingleton.CreateLocalGameScene(this.SnId, sceneId, gameId, gameSite, sceneMode, 1, params, gs, + this.GetPlatform(), playerNum, dbGameFree, baseScore, groupId, dbGameFree.GetId()) + if s == nil { + logger.Logger.Tracef("(this *Player) EnterScene %v, SceneMgrSingleton.CreateScene() = nil ", this.SnId) + return nil, hall_proto.OpResultCode_Game_OPRC_Error_Game + } + return s, hall_proto.OpResultCode_Game_OPRC_Sucess_Game +} + +func (this *Player) EnterScene(s *Scene, ischangeroom bool, pos int) bool { + if s == nil { + logger.Logger.Tracef("(this *Player) EnterScene, s == nil %v", this.SnId) + return false + } + + if s != nil { + this.applyPos = -1 + if s.PlayerEnter(this, pos, ischangeroom) { + FirePlayerEnterScene(this, s) + return true + } + } + + return false +} + +func (this *Player) ReturnScene(isLoaded bool) *Scene { + logger.Logger.Tracef("(this *Player) ReturnScene %v", this.SnId) + if this.scene == nil { + logger.Logger.Warnf("(this *Player) ReturnScene this.scene == nil snid:%d", this.SnId) + return nil + } + if !this.scene.HasPlayer(this) && !this.scene.HasAudience(this) { + logger.Logger.Warnf("(this *Player) ReturnScene !this.scene.HasPlayer(this) && !this.scene.HasAudience(this) snid:%d", this.SnId) + return nil + } + + pack := &server_proto.WGPlayerReturn{ + PlayerId: proto.Int32(this.SnId), + IsLoaded: proto.Bool(isLoaded), + RoomId: proto.Int(this.scene.sceneId), + } + ctx := this.scene.GetPlayerGameCtx(this.SnId) + if ctx != nil { + pack.EnterTs = proto.Int64(ctx.enterTs) + } + proto.SetDefaults(pack) + if this.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERRETURN), pack) { + //比赛场返场检查 + //MatchMgrSington.OnPlayerReturnScene(this.scene, this) + return this.scene + } + //不应该这这里处理,因为 miniGame中小游戏玩家 player.Scene是不存在的。 + //FirePlayerReturnScene(this) + + return nil +} + +func (this *Player) BackDiffData() { + this.diffData.Coin = this.Coin + this.diffData.SafeBoxCoin = this.SafeBoxCoin +} + +func (this *Player) UpdateVip() { + if this.IsRob { + return + } + this.VIP = this.GetVIPLevel(this.CoinPayTotal) + //clubManager.UpdateVip(this) +} + +func (this *Player) AddMoneyPayTotal(amount int64) { + if amount > 0 { + this.MoneyPayTotal += amount + this.SendDiffData() //更新vip + } +} + +func (this *Player) SendDiffData() { + this.UpdateVip() + var dirty bool + pack := &player_proto.SCPlayerDataUpdate{} + pack.UpdateField = 0 + //金币 + if this.diffData.Coin != this.Coin { + dirty = true + pack.Coin = proto.Int64(this.Coin) + this.diffData.Coin = this.Coin + pack.UpdateField += UpdateField_Coin + } + //钻石 + if this.diffData.Diamond != this.Diamond { + dirty = true + pack.Diamond = proto.Int64(this.Diamond) + this.diffData.Diamond = this.Diamond + pack.UpdateField += UpdateField_Diamond + } + //保险箱金币 + if this.diffData.SafeBoxCoin != this.SafeBoxCoin { + dirty = true + pack.SafeBoxCoin = proto.Int64(this.SafeBoxCoin) + this.diffData.SafeBoxCoin = this.SafeBoxCoin + pack.UpdateField += UpdateField_SafeBoxCoin + } + //VIP等级 + if this.diffData.VIP != this.VIP { + dirty = true + pack.Vip = proto.Int32(this.VIP) + this.diffData.VIP = this.VIP + pack.UpdateField += UpdateField_VIP + } + //手机积分 + if this.diffData.PhoneScore != this.PhoneScore { + dirty = true + pack.PhoneScore = this.PhoneScore + this.diffData.PhoneScore = this.PhoneScore + pack.UpdateField += UpdateField_PhoneScore + } + // 邀请积分 + if this.diffData.InviteScore != this.InviteScore { + dirty = true + pack.InviteScore = this.InviteScore + this.diffData.InviteScore = this.InviteScore + pack.UpdateField += UpdateField_InviteScore + } + //总充值金额 + if this.diffData.CoinPayTotal != this.CoinPayTotal { + dirty = true + pack.CoinPayTotal = proto.Int64(this.CoinPayTotal) + this.diffData.CoinPayTotal = this.CoinPayTotal + pack.UpdateField += UpdateField_CoinPayTotal + } + //流水差异 + if this.diffData.TotalConvertibleFlow != this.TotalConvertibleFlow { + dirty = true + pack.TotalConvertibleFlow = proto.Int64(this.TotalConvertibleFlow) + this.diffData.TotalConvertibleFlow = this.TotalConvertibleFlow + pack.UpdateField += UpdateField_TotalConvertibleFlow + } + //比赛报名券 + if this.diffData.Ticket != this.Ticket { + dirty = true + pack.Ticket = proto.Int64(this.Ticket) + this.diffData.Ticket = this.Ticket + pack.UpdateField += UpdateField_Ticket + } + //积分 + if this.diffData.Grade != this.Grade { + dirty = true + pack.Grade = proto.Int64(this.Grade) + this.diffData.Grade = this.Grade + pack.UpdateField += UpdateField_Grade + } + //象棋积分 + if this.diffData.ChessGrade != this.ChessGrade { + dirty = true + pack.ChessGrade = proto.Int64(this.ChessGrade) + this.diffData.ChessGrade = this.ChessGrade + pack.UpdateField += UpdateField_ChessGrade + } + // 排位积分 + if this.diffData.RankScore == nil { + this.diffData.RankScore = make(map[int32]int64) + } + if p := RankMgrSingleton.GetPlayerSeason(this.SnId); p != nil { + for k := range p.RankType { + if this.diffData.RankScore[k] != p.RankType[k].Score { + dirty = true + if pack.RankScore == nil { + pack.RankScore = make(map[int32]int64) + } + pack.RankScore[k] = proto.Int64(p.RankType[k].Score) + this.diffData.RankScore[k] = p.RankType[k].Score + pack.UpdateField += UpdateField_RankScore + } + } + } + if len(pack.RankScore) == 0 { + pack.RankScore = nil + } + + if dirty { + FriendMgrSington.UpdateInfo(this.Platform, this.SnId) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATAUPDATE), pack) + logger.Logger.Trace("(this *Player) SendDiffData() ", pack) + } +} + +func GetExchangeFlow(pd *model.PlayerData) int32 { + if pd == nil { + return 0 + } + + platform := PlatformMgrSingleton.GetPlatform(pd.Platform) + if platform != nil { + if (platform.ExchangeFlag & ExchangeFlag_Flow) != 0 { + key, err := GetPromoterKey(pd.PromoterTree, pd.BeUnderAgentCode, pd.Channel) + if err == nil { + cfg := PromoterMgrSington.GetConfig(key) + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_Flow) != 0 { + return cfg.ExchangeFlow + } + } + } + return platform.ExchangeFlow + } + } + return 0 +} + +func GetExchangeGiveFlow(pd *model.PlayerData) int32 { + if pd == nil { + return 0 + } + + platform := PlatformMgrSingleton.GetPlatform(pd.Platform) + if platform != nil { + if (platform.ExchangeFlag & ExchangeFlag_Flow) != 0 { + key, err := GetPromoterKey(pd.PromoterTree, pd.BeUnderAgentCode, pd.Channel) + if err == nil { + cfg := PromoterMgrSington.GetConfig(key) + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_Flow) != 0 { + return cfg.ExchangeGiveFlow + } + } + } + return platform.ExchangeGiveFlow + } + } + return 0 +} + +func GetExchangeForceTax(pd *model.PlayerData) int32 { + if pd == nil { + return 0 + } + + platform := PlatformMgrSingleton.GetPlatform(pd.Platform) + if platform != nil { + if (platform.ExchangeFlag & ExchangeFlag_Force) != 0 { + key, err := GetPromoterKey(pd.PromoterTree, pd.BeUnderAgentCode, pd.Channel) + if err == nil { + cfg := PromoterMgrSington.GetConfig(key) + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_Force) != 0 { + return cfg.ExchangeForceTax + } + } + } + return platform.ExchangeForceTax + } + } + return 0 +} + +func GetExchangeTax(pd *model.PlayerData) int32 { + if pd == nil { + return 0 + } + + platform := PlatformMgrSingleton.GetPlatform(pd.Platform) + if platform != nil { + if (platform.ExchangeFlag & ExchangeFlag_Tax) != 0 { + key, err := GetPromoterKey(pd.PromoterTree, pd.BeUnderAgentCode, pd.Channel) + if err == nil { + cfg := PromoterMgrSington.GetConfig(key) + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_Tax) != 0 { + return cfg.ExchangeTax + } + } + } + return platform.ExchangeTax + } + } + return 0 +} + +func GetExchangeFlag(pd *model.PlayerData) int32 { + if pd == nil { + return 0 + } + + platform := PlatformMgrSingleton.GetPlatform(pd.Platform) + if platform != nil { + if (platform.ExchangeFlag & ExchangeFlag_Flow) != 0 { + key, err := GetPromoterKey(pd.PromoterTree, pd.BeUnderAgentCode, pd.Channel) + if err == nil { + cfg := PromoterMgrSington.GetConfig(key) + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_Flow) != 0 { + return 2 + } + } + } + return 1 + } + } + return 0 +} + +func (this *Player) GetRegisterPrize() int32 { + platform := this.GetPlatform() + key, err := this.GetPromoterKey() + var cfg *PromoterConfig + if err == nil { + cfg = PromoterMgrSington.GetConfig(key) + } + + return GGetRegisterPrize(platform, cfg) +} + +func GGetRegisterPrize(platform *Platform, cfg *PromoterConfig) int32 { + if platform != nil { + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_UpAcc) != 0 { + return cfg.NewAccountGiveCoin + } + } + return platform.NewAccountGiveCoin + } + return 0 +} + +func (this *Player) GetUpdateAccPrize() int32 { + platform := this.GetPlatform() + key, err := this.GetPromoterKey() + var cfg *PromoterConfig + if err == nil { + cfg = PromoterMgrSington.GetConfig(key) + } + + return GGetUpdateAccPrize(platform, cfg) +} + +func GGetUpdateAccPrize(platform *Platform, cfg *PromoterConfig) int32 { + if platform != nil { + if cfg != nil { + if (cfg.ExchangeFlag & ExchangeFlag_UpAcc) != 0 { + return cfg.UpgradeAccountGiveCoin + } + } + return platform.UpgradeAccountGiveCoin + } + return 0 +} + +func (this *Player) GetPromoterKey() (string, error) { + return GetPromoterKey(this.PromoterTree, this.BeUnderAgentCode, this.Channel) +} + +//计算流水可以兑换的值 返回 还需要多少流水 赠送扣除 强制费用 合计流水 +//func GetExchangeFlowTotal(pd *model.PlayerData, playerTotalFlow int64, givesInfo []*model.CoinGiveLog) (int64, +// int64, int64, int64) { +// //可兑换的流水 +// var flow int64 +// +// var giveLostCoin int64 //赠送扣除 +// var forceTax int64 //强制费用 +// var needTotalFlow int64 //需要的流水 +// var lockCoin int64 //多少金额无法兑换,如果兑换需要强制扣除行政费用 +// +// retIds := []string{} +// retAllIds := []string{} +// +// exchangeFlow := GetExchangeFlow(pd) +// exchangeGiveFlow := GetExchangeGiveFlow(pd) +// exchangeForceTax := GetExchangeForceTax(pd) +// +// if GetExchangeFlag(pd) > 0 { +// +// //逐笔计算兑换流水金额,从上到下 +// curTotalFlow := playerTotalFlow +// +// //按照时间排序 +// sort.Slice(givesInfo, func(i, j int) bool { return givesInfo[i].Ts > givesInfo[j].Ts }) +// +// for i := 0; i < len(givesInfo); i++ { +// info := givesInfo[i] +// if info.Ts > pd.LastExchangeTime { +// retAllIds = append(retAllIds, info.LogId.Hex()) +// //计算是否通过稽核 +// needFlow := int64(0) +// curTotalFlow += info.FLow +// //如果是系统赠送的,需要全部扣除 +// if info.RecType == model.COINGIVETYPE_SYSTEM { +// exchangeGiveFlowS := exchangeGiveFlow +// t := ActMgrSington.GetExchangeFlow(pd.Platform, info.LogType) +// if t != 0 { +// exchangeGiveFlowS = t +// } +// +// if info.NeedGiveFlowRate > 0 { +// exchangeGiveFlowS = info.NeedGiveFlowRate +// } +// +// needFlow = int64(math.Floor(float64(info.GiveCoin)*float64(exchangeGiveFlowS)/100)) * 100 +// +// if curTotalFlow < needFlow { +// //需要扣除 +// giveLostCoin += info.GiveCoin +// lockCoin += info.GiveCoin +// flow += needFlow - curTotalFlow +// curTotalFlow = 0 +// } else { +// curTotalFlow -= needFlow +// retIds = append(retIds, info.LogId.Hex()) +// } +// } else { +// exchangeGiveFlowS := exchangeGiveFlow +// t := ActMgrSington.GetExchangeFlow(pd.Platform, info.LogType) +// if t != 0 { +// exchangeGiveFlowS = t +// } +// if info.NeedGiveFlowRate > 0 { +// exchangeGiveFlowS = info.NeedGiveFlowRate +// } +// +// exchangePayFlowS := exchangeFlow +// if info.NeedFlowRate > 0 { +// exchangePayFlowS = info.NeedFlowRate +// } +// +// //分两部分扣除 +// needFlow = int64(math.Floor(float64(info.GiveCoin)*float64(exchangeGiveFlowS)/100)) * 100 +// needFlow += int64(math.Floor(float64(info.PayCoin)*float64(exchangePayFlowS)/100)) * 100 +// if curTotalFlow < needFlow { +// //需要扣除 +// giveLostCoin += info.GiveCoin +// //强制费用 +// forceTax += info.PayCoin * int64(exchangeForceTax) / 10000 +// lockCoin += info.GiveCoin +// lockCoin += info.PayCoin +// flow += needFlow - curTotalFlow +// curTotalFlow = 0 +// } else { +// curTotalFlow -= needFlow +// retIds = append(retIds, info.LogId.Hex()) +// } +// } +// needTotalFlow += needFlow +// } +// } +// } +// +// return flow, giveLostCoin, forceTax, needTotalFlow +//} +// +////pageNo 1开始 +//func GetExchangeFlowTotalPacket(playerTotalFlow int64, givesInfo []*model.CoinGiveLog, pd *model.PlayerData, +// pageNo, pageNum int32, isCheck bool) *shop_proto.SCGetPlayerPayFlowList { +// pack := &shop_proto.SCGetPlayerPayFlowList{} +// var giveLostCoin int64 //赠送扣除 +// var forceTax int64 //强制费用 +// var needTotalFlow int64 //需要的流水 +// startIndex := (pageNo - 1) * pageNum +// endIndex := pageNo * pageNum +// //platform := this.GetPlatform() +// exchangeFlow := GetExchangeFlow(pd) +// exchangeGiveFlow := GetExchangeGiveFlow(pd) +// exchangeForceTax := GetExchangeForceTax(pd) +// +// //按照时间排序 +// sort.Slice(givesInfo, func(i, j int) bool { return givesInfo[i].Ts > givesInfo[j].Ts }) +// //逐笔计算兑换流水金额,从上到下p +// curTotalFlow := playerTotalFlow +// +// index := int32(0) +// +// if GetExchangeFlag(pd) > 0 { +// for i := 0; i < len(givesInfo); i++ { +// info := givesInfo[i] +// if isCheck || info.Ts > pd.LastExchangeTime { +// tInfo := &shop_proto.PlayerPayFlowLog{} +// tInfo.Ts = proto.Int64(info.Ts) +// tInfo.PayType = proto.Int32(info.RecType) +// tInfo.PayCoin = proto.Int64(info.PayCoin) +// tInfo.GiveCoin = proto.Int64(info.GiveCoin) +// tInfo.FinishFlow = proto.Int64(info.FLow) +// tInfo.OrderID = proto.String(info.LogId.Hex()) +// //计算是否通过稽核 +// needFlow := int64(0) +// isPass := int32(0) +// curTotalFlow += info.FLow +// //如果是系统赠送的,需要全部扣除 +// if info.RecType == model.COINGIVETYPE_SYSTEM { +// exchangeGiveFlowS := exchangeGiveFlow +// t := ActMgrSington.GetExchangeFlow(pd.Platform, info.LogType) +// if t != 0 { +// exchangeGiveFlowS = t +// } +// if info.NeedGiveFlowRate > 0 { +// exchangeGiveFlowS = info.NeedGiveFlowRate +// } +// needFlow = int64(math.Floor(float64(info.GiveCoin)*float64(exchangeGiveFlowS)/100)) * 100 +// tInfo.GiveNeedFlow = proto.Int64(needFlow) +// if curTotalFlow < needFlow { +// //需要扣除 +// giveLostCoin += info.GiveCoin +// tInfo.ForceGiveCoin = proto.Int64(info.GiveCoin) +// +// curTotalFlow = 0 +// } else { +// curTotalFlow -= needFlow +// isPass = 1 +// } +// } else { +// exchangeGiveFlowS := exchangeGiveFlow +// t := ActMgrSington.GetExchangeFlow(pd.Platform, info.LogType) +// if t != 0 { +// exchangeGiveFlowS = t +// } +// +// if info.NeedGiveFlowRate > 0 { +// exchangeGiveFlowS = info.NeedGiveFlowRate +// } +// exchangePayFlowS := exchangeFlow +// if info.NeedFlowRate > 0 { +// exchangePayFlowS = info.NeedFlowRate +// } +// //分两部分扣除 +// needFlow = int64(math.Floor(float64(info.GiveCoin)*float64(exchangeGiveFlowS)/100)) * 100 +// tInfo.GiveNeedFlow = proto.Int64(needFlow) +// +// payNeedFlow := int64(math.Floor(float64(info.PayCoin)*float64(exchangePayFlowS)/100)) * 100 +// tInfo.PayNeedFlow = proto.Int64(payNeedFlow) +// needFlow += payNeedFlow +// +// if curTotalFlow < needFlow { +// //需要扣除 +// giveLostCoin += info.GiveCoin +// tInfo.ForceGiveCoin = proto.Int64(info.GiveCoin) +// +// //强制费用 +// forceTax += info.PayCoin * int64(exchangeForceTax) / 10000 +// tInfo.ForceTax = proto.Int64(info.PayCoin * int64(exchangeForceTax) / 10000) +// curTotalFlow = 0 +// } else { +// curTotalFlow -= needFlow +// isPass = 1 +// } +// } +// +// tInfo.IsPass = proto.Int32(isPass) +// needTotalFlow += needFlow +// +// if index >= startIndex && index < endIndex { +// pack.Data = append(pack.Data, tInfo) +// } +// +// index += 1 +// } +// } +// } +// pack.PageNo = proto.Int32(int32(pageNo)) +// pack.PageSum = proto.Int32(int32(math.Ceil(float64(index) / float64(pageNum)))) +// pack.PageSize = proto.Int32(pageNum) +// pack.TotalNum = proto.Int32(index) +// proto.SetDefaults(pack) +// return pack +//} + +func (this *Player) SendPlayerInfo() { + this.UpdateVip() + scPlayerData := &player_proto.SCPlayerData{ + OpRetCode: player_proto.OpResultCode_OPRC_Sucess, + Data: &player_proto.PlayerData{ + AccId: proto.String(this.AccountId), //账号ID + Platform: proto.String(this.Platform), //平台 + Channel: proto.String(this.Channel), //渠道 + Promoter: proto.String(this.BeUnderAgentCode), //推广员 + Name: proto.String(this.Name), //名字 + SnId: proto.Int32(this.SnId), //数字账号 + Head: proto.Int32(this.Head), //头像 + Sex: proto.Int32(this.Sex), //性别 + GMLevel: proto.Int32(this.GMLevel), //GM等级 + Coin: proto.Int64(this.Coin), //金币 + SpecailFlag: proto.Int32(int32(this.Flags)), //特殊标记 + Tel: proto.String(this.Tel), //手机号码 + InviterId: proto.Int32(this.InviterId), //邀请人ID + SafeBoxCoin: proto.Int64(this.SafeBoxCoin), //保险箱金币 + VIP: proto.Int32(this.VIP), //VIP帐号 + AlipayAccount: proto.String(this.AlipayAccount), //支付宝账号 + AlipayAccName: proto.String(this.AlipayAccName), //支付宝实名 + Bank: proto.String(this.Bank), //银行 + BankAccount: proto.String(this.BankAccount), //银行帐号 + BankAccName: proto.String(this.BankAccName), //银行开户名 + HeadOutLine: proto.Int32(this.HeadOutLine), //头像框 + CoinPayTotal: proto.Int64(this.CoinPayTotal), //总充值金额 + CreateTs: proto.Int64(this.CreateTime.Unix()), //角色创建时间 + //ClubCoin: proto.Int64(this.ClubCoin), //俱乐部金币 + Ticket: proto.Int64(this.Ticket), //比赛入场券 + Grade: proto.Int64(this.Grade), //积分 + Diamond: proto.Int64(this.Diamond), //钻石 + HeadUrl: proto.String(this.HeadUrl), + //VipExp: proto.Int64(this.GetCurrentVIPExp()), + ChessGrade: proto.Int64(this.ChessGrade), + RankScore: make(map[int32]int64), + UnMaxPower: proto.Int64(this.UnMaxPower), + PowerList: this.PowerList, + FishLevel: proto.Int64(this.FishLevel), + FishExp: proto.Int64(this.FishExp), + VipShopRefreshCount: proto.Int32(this.VipShopRefreshCount), + Signature: this.Signature, + Age: this.Age, + }, + } + if this.Roles != nil { + scPlayerData.Data.UseRoleId = this.Roles.ModId + } + if this.Pets != nil { + scPlayerData.Data.UsePetId = this.Pets.ModId + } + if this.WelfData != nil { + scPlayerData.Data.ReliefFundTimes = this.WelfData.ReliefFundTimes + } + if item := BagMgrSingleton.GetItem(this.SnId, VCard); item != nil { + scPlayerData.Data.VCoin = int64(item.ItemNum) //V卡 + + } + + // 排位积分 + scPlayerData.Data.RankScore = RankMgrSingleton.GetPlayerRankScore(this.SnId) + + raw := fmt.Sprintf("%v%v", model.DEFAULT_PLAYER_SAFEBOX_PWD, common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + if this.SafeBoxPassword != pwd { + scPlayerData.Data.SafeBoxIsExist = proto.Int32(1) + } else { + scPlayerData.Data.SafeBoxIsExist = proto.Int32(0) + } + + if this.scene != nil { + scPlayerData.RoomId = proto.Int(this.scene.sceneId) + scPlayerData.GameId = proto.Int(this.scene.gameId) + //增加gameFreeId + scPlayerData.LogicId = this.scene.dbGameFree.Id + } + platform := PlatformMgrSingleton.GetPlatform(this.Platform) + if platform != nil { + scPlayerData.BindOption = proto.Int32(platform.BindOption) + } else { + scPlayerData.BindOption = proto.Int32(7) + } + + proto.SetDefaults(scPlayerData) + logger.Logger.Tracef("Send SCPlayerData %v", scPlayerData) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PLAYERDATA), scPlayerData) + + if !this.IsRob { + this.SyncPlayerDataToGateSrv(this.PlayerData) + } + + // 判断玩家是否在游戏内 + if this.scene != nil && this.thrscene == 0 { + this.SendGameConfig(int32(this.scene.gameId), this.Platform, this.Channel) + } + + PetMgrSington.CheckShowRed(this) + + //this.SendJackpotInfo() +} + +//func (this *Player) SendJackpotInfo() { +// //通知所有的gamesrv向玩家发送奖池信息 +// if this.gateSess != nil { +// var gateSid int64 +// if srvInfo, ok := this.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { +// sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) +// gateSid = sessionId.Get() +// } +// +// //查找当前平台下所以开放的游戏id +// info := make([]*server_proto.GameInfo, 0) +// gps := PlatformMgrSingleton.GetGameFrees(this.Platform) +// for _, v := range gps { +// if v.Status { +// if v.DbGameFree.GetGameRule() != 0 { +// //lgi := &server_proto.GameInfo{ +// // GameId: proto.Int32(v.DbGameFree.GetGameId()), +// // GameFreeId: proto.Int32(v.DbGameFree.GetId()), +// // GameType: proto.Int32(v.DbGameFree.GetGameType()), +// //} +// info = append(info, &server_proto.GameInfo{ +// GameId: proto.Int32(v.DbGameFree.GetGameId()), +// GameFreeId: proto.Int32(v.DbGameFree.GetId()), +// GameType: proto.Int32(v.DbGameFree.GetGameType()), +// }) +// } +// } +// } +// +// servers := GameSessMgrSington.GetAllGameSess() +// for _, v := range servers { +// pack := &server_proto.WGGameJackpot{ +// Sid: this.sid, +// GateSid: gateSid, +// Platform: this.Platform, +// Info: info, +// } +// v.Send(int(server_proto.SSPacketID_PACKET_WG_GAMEJACKPOT), pack) +// } +// } +//} + +func (this *Player) IsGM() bool { + if this.GMLevel > 0 { + return true + } + return false +} + +func (this *Player) IsAgentor() bool { + return false +} + +func (this *Player) IsPlayer() bool { + return true +} + +func (this *Player) HasAuthority(role int) bool { + switch role { + case common.ClientRole_Agentor: + return this.IsAgentor() + case common.ClientRole_GM: + return this.IsGM() + case common.ClientRole_Player: + return true + } + return false +} + +func (this *Player) RobRandVip() { + if this.IsRob { + dbvip := srvdata.PBDB_VIPMgr.GetData(this.VIP) + if dbvip != nil { + outlines := dbvip.GetRewardOutlineID() + n := len(outlines) + this.HeadOutLine = outlines[rand.Intn(n)] + logger.Logger.Tracef("(this *Player) RobRandVip() %d HeadOutLine=%d", this.SnId, this.HeadOutLine) + this.dirty = true + } + this.Head = rand.Int31n(common.HeadRange) + //0:男 1:女 + this.Sex = (this.Head%2 + 1) % 2 + } +} + +func (this *Player) ReportLoginEvent() { + //用户登录 + if !this.IsRob { + isBindPhone := int32(0) + if this.Tel != "" { + isBindPhone = 1 + if this.UpgradeTime.IsZero() { + this.UpgradeTime = this.CreateTime + } + } + LogChannelSingleton.WriteMQData(model.GenerateLogin(model.CreatePlayerLoginEvent(this.SnId, + this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS, this.Ip, + this.CreateTime, this.UpgradeTime, isBindPhone, this.TelephonePromoter, this.DeviceId))) + //登录通知 + //ActMonitorMgrSington.SendActMonitorEvent(ActState_Login, this.SnId, this.Name, this.Platform, + // 0, 0, "", 0) + } +} + +func (this *Player) ReportBindPhoneEvent() { + //升级账号事件 + if !this.IsRob { + //LogChannelSingleton.WriteMQData(model.GenerateBindEvent(model.CreatePlayerBindPhoneEvent( + // this.SnId, this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS, + // this.CreateTime, this.TelephonePromoter))) + } +} + +func (this *Player) ReportBindAlipayEvent() { + //绑定支付宝事件 + //if !this.IsRob { + // d, e := model.MarshalPlayerBindAlipayEvent(2, this.SnId, this.Channel, this.BeUnderAgentCode, + // this.Platform, this.City, this.DeviceOS, this.TelephonePromoter) + // if e == nil { + // rmd := model.NewInfluxDBData("hj.player_bind_alipay", d) + // if rmd != nil { + // InfluxDBDataChannelSington.Write(rmd) + // } + // } + //} +} + +func (this *Player) AddCoinGiveLog(payCoin, giveCoin int64, coinType, logType, recType int32, remark, oper string) { + plt := PlatformMgrSingleton.GetPlatform(this.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + + log := model.NewCoinGiveLogEx(this.SnId, this.Name, payCoin, giveCoin, coinType, logType, this.PromoterTree, + recType, curVer, this.Platform, this.Channel, this.BeUnderAgentCode, remark, oper, this.PackageID, 0, 0) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertGiveCoinLog(log) + if err == nil { + if this.LastExchangeOrder != "" && this.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(log.Platform, this.LastExchangeOrder, this.TotalConvertibleFlow) + } + } + return err + }), task.CompleteNotifyWrapper(func(ud interface{}, t task.Task) { + if ud == nil { + //清空流水,更新id + this.TotalConvertibleFlow = 0 + this.LastExchangeOrder = log.LogId.Hex() + } + }), "UpdateGiveCoinLastFlow").StartByGroupFixExecutor(log.Platform, "UpdateGiveCoinLastFlow") + } +} + +func ReportSystemGiveEvent(pd *model.PlayerData, amount, tag int32, notifyClient bool) { + //系统赠送 + if !pd.IsRob { + //插入本地表 + if amount > 0 { + if ActMgrSington.GetIsNeedGive(pd.Platform, tag) { + plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + + log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, 0, int64(amount), 0, tag, pd.PromoterTree, + model.COINGIVETYPE_SYSTEM, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, "", + "system", pd.PackageID, 0, 0) + if log != nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertGiveCoinLog(log) + if err == nil { + if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(log.Platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + } + } + return err + }), task.CompleteNotifyWrapper(func(ud interface{}, t task.Task) { + if ud == nil { + //清空流水,更新id + pd.TotalConvertibleFlow = 0 + pd.LastExchangeOrder = log.LogId.Hex() + player := PlayerMgrSington.GetPlayerBySnId(pd.SnId) + if player == nil { + //需要回写数据库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdatePlayerExchageFlowAndOrder(pd.Platform, pd.SnId, 0, pd.LastExchangeOrder) + return nil + }), nil, "UpdatePlayerExchageFlowAndOrder").StartByGroupFixExecutor(log.Platform, pd.AccountId) + } + } + }), "UpdateGiveCoinLastFlow").StartByGroupFixExecutor(log.Platform, "UpdateGiveCoinLastFlow") + } + } + + if notifyClient { + player := PlayerMgrSington.GetPlayerBySnId(pd.SnId) + if player != nil { + //通知客户端 + sendPack := &shop_proto.SCNotifyGiveCoinInfo{ + GiveCoin: proto.Int64(int64(amount)), + GiveTag: proto.Int32(tag), + } + + proto.SetDefaults(sendPack) + player.SendToClient(int(shop_proto.SPacketID_SHOP_SC_GIVECOIN_INFO), sendPack) + } + } + } + } +} + +func (this *Player) ReportSystemGiveEvent(amount, tag int32, notifyClient bool) { + ReportSystemGiveEvent(this.PlayerData, amount, tag, notifyClient) +} + +// 破产事件 +func (this *Player) ReportBankRuptcy(gameid, gamemode, gamefreeid int32) { + //if !this.IsRob { + // d, e := model.MarshalBankruptcyEvent(2, this.SnId, this.TelephonePromoter, this.Channel, this.BeUnderAgentCode, + // this.Platform, this.City, this.CreateTime, gameid, gamemode, gamefreeid) + // if e == nil { + // rmd := model.NewInfluxDBData("hj.player_bankruptcy", d) + // if rmd != nil { + // InfluxDBDataChannelSington.Write(rmd) + // } + // } + //} +} + +func (this *Player) CheckType(gameid, gamefreeId int32) *server_proto.DB_PlayerType { + types := srvdata.PlayerTypeMgrSington.GetPlayerType(gamefreeId) + cnt := len(types) + if cnt > 0 { + var pgs model.PlayerGameStatics + if this.GDatas != nil { + if d, exist := this.GDatas[strconv.Itoa(int(gameid))]; exist { + pgs = d.Statics + } + } + + //赔率 产出/投入 万分比 + odds := int64(float64(float64(pgs.TotalOut+1)/float64(pgs.TotalIn+1)) * 10000) + if odds > 10000000 { + odds = 10000000 + } + for i := 0; i < cnt; i++ { + t := types[i] + logger.Logger.Warn("Player CheckType 0 ", this.CoinPayTotal, t.GetPayLowerLimit(), t.GetPayUpperLimit(), pgs.GameTimes, + t.GetGameTimeLowerLimit(), t.GetGameTimeUpperLimit(), pgs.TotalIn, t.GetTotalInLowerLimit(), t.GetTotalInUpperLimit(), + odds, t.GetOddsLowerLimit(), t.GetOddsUpperLimit()) + if t != nil { + if this.CoinPayTotal >= int64(t.GetPayLowerLimit()) && this.CoinPayTotal <= int64(t.GetPayUpperLimit()) && + pgs.GameTimes >= int64(t.GetGameTimeLowerLimit()) && pgs.GameTimes <= int64(t.GetGameTimeUpperLimit()) && + pgs.TotalIn >= int64(t.GetTotalInLowerLimit()) && pgs.TotalIn <= int64(t.GetTotalInUpperLimit()) && + odds >= int64(t.GetOddsLowerLimit()) && odds <= int64(t.GetOddsUpperLimit()) { + return t + } + } + } + } + return nil +} + +// 线程不安全,避免异步任务调用 +func (this *Player) GetPlatform() *Platform { + platform := PlatformMgrSingleton.GetPlatform(this.Platform) + if platform != nil && platform.Isolated { + return platform + } + return PlatformMgrSingleton.GetPlatform(DefaultPlatform) +} + +func (this *Player) RobotRandName() { + if this.IsRob { + //if rand.Int31n(100) < 60 { + pool := srvdata.PBDB_NameMgr.Datas.GetArr() + cnt := int32(len(pool)) + if cnt > 0 { + this.Name = pool[rand.Int31n(cnt)].GetName() + } + //} else { + // this.Name = "Guest" + //} + } + return +} + +func (this *Player) PlayerMacAbnormal() bool { + return false //s.HasSameIp(p.Ip) || s.HasSameMac() || s.HasSameTel() || s.HasSamePostion() +} + +// 这个冲账记录不可随便写,需要加该日志时,请找lyk确认,暂定依据是金币直接加到身上,不依赖其他数据状态的可以写该日志 +// 业务准则:先更新标记,再写冲账记录 +func (this *Player) AddPayCoinLog(coin int64, coinType int32, oper string) { + ts := time.Now().UnixNano() + billNo := ts + log := model.NewPayCoinLog(billNo, this.SnId, coin, 0, oper, coinType, 0) + if log != nil { + ts = log.TimeStamp + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertPayCoinLog(this.Platform, log) + if err != nil { + logger.Logger.Errorf("InsertPayCoinLog err:%v log:%v", err, log) + return err + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + switch coinType { + case model.PayCoinLogType_Coin: + if this.CoinPayTs < ts { + this.CoinPayTs = ts + this.dirty = true + } + case model.PayCoinLogType_SafeBoxCoin: + if this.SafeBoxCoinTs < ts { + this.SafeBoxCoinTs = ts + this.dirty = true + } + } + }), "InsertPayCoinLog").StartByFixExecutor("InsertPayCoinLog") + } +} + +// 充值回调 +func (this *Player) SendPlayerRechargeAnswer(coin int64) { + if this.Tel == "" { + pack := &player_proto.SCPlayerRechargeAnswer{ + OpParam: proto.Int64(1), + AddCoin: proto.Int64(coin), + Coin: proto.Int64(this.Coin), + SafeBoxCoin: proto.Int64(this.SafeBoxCoin), + } + proto.SetDefaults(pack) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PLAYERRECHARGEANSWER), pack) + } +} + +// +//// 在线奖励: 重置, 清零在线时长及奖励领取信息 +//func (this *Player) OnlineRewardReset() { +// this.PlayerData.OnlineRewardData.OnlineDuration = 0 +// this.PlayerData.OnlineRewardData.RewardReceived = 0 +// this.PlayerData.OnlineRewardData.Ts = time.Now().Unix() +// this.dirty = true +//} +// +//// 在线奖励: (Logout时)累计在线时长 +//func (this *Player) OnlineRewardAddUpOnlineDuration() { +// if this.state != PlayerState_Online { +// return +// } +// +// tNow := time.Now() +// inSameDay := common.InSameDay(tNow, time.Unix(this.PlayerData.OnlineRewardData.Ts, 0)) +// if inSameDay { +// this.PlayerData.OnlineRewardData.OnlineDuration += uint32(tNow.Unix() - this.PlayerData.OnlineRewardData.Ts) +// } else { +// this.PlayerData.OnlineRewardData.OnlineDuration = uint32(tNow.Unix() - now.New(tNow).BeginningOfDay().Unix()) +// } +// +// this.PlayerData.OnlineRewardData.Ts = tNow.Unix() +// this.dirty = true +//} +// +//// 在线奖励: (实时)获取在线时长 +//func (this *Player) OnlineRewardGetOnlineDuration() uint32 { +// this.OnlineRewardAddUpOnlineDuration() +// return this.PlayerData.OnlineRewardData.OnlineDuration +//} + +// 幸运转盘 +//func (this *Player) LuckyTurntableSwitchScore(continuous bool) { +// if continuous { +// this.PlayerData.LuckyTurnTableData.Score = this.PlayerData.LuckyTurnTableData.TomorrowScore + +// int64(this.PlayerData.LuckyTurnTableData.TomorrowFloatScore/100) +// } else { +// this.PlayerData.LuckyTurnTableData.Score = 0 +// } +// +// this.PlayerData.LuckyTurnTableData.TomorrowScore = 0 +// this.PlayerData.LuckyTurnTableData.TomorrowFloatScore = 0 +// this.dirty = true +//} + +func (this *Player) SyncSafeBoxCoinToGame() { + pack := &server_proto.WGSyncPlayerSafeBoxCoin{ + SnId: proto.Int32(this.SnId), + SafeBoxCoin: proto.Int64(this.SafeBoxCoin), + } + proto.SetDefaults(pack) + this.SendToGame(int(server_proto.SSPacketID_PACKET_WG_SyncPlayerSafeBoxCoin), pack) +} + +//func (this *Player) GetDgHboPlayerName(plt *Platform) (string, string) { +// if plt == nil { +// return "", "" +// } +// if plt.DgHboConfig == 0 { +// return this.DgGame, this.DgPass +// } else if plt.DgHboConfig == 1 { +// return this.StoreDgGame, this.StoreDgPass +// } else if plt.DgHboConfig == 2 { +// return this.StoreHboGame, this.StoreHboPass +// } +// return "", "" +//} +// +//func (this *Player) SetDgHboPlayerName(plt *Platform, name, pass string) { +// if plt == nil { +// return +// } +// +// if plt.DgHboConfig == 0 { +// this.DgGame = name +// this.DgPass = pass +// if strings.Contains(name, "dg") { +// this.StoreDgGame = name +// this.StoreDgPass = pass +// } else { +// this.StoreHboGame = name +// this.StoreHboPass = pass +// } +// } else if plt.DgHboConfig == 1 { +// this.StoreDgGame = name +// this.StoreDgPass = pass +// } else if plt.DgHboConfig == 2 { +// this.StoreHboGame = name +// this.StoreHboPass = pass +// } +// +//} + +func (this *Player) AddCoinPayTotal(coin int64) { + this.CoinPayTotal += coin +} + +// 当用户充值 +func OnPlayerPay(pd *model.PlayerData, coin int64) { + if pd == nil { + return + } + + buf, err := pd.GetPlayerDataEncoder() + if err == nil { + pack := &server_proto.WTPlayerPay{ + AddCoin: proto.Int64(coin), + PlayerData: buf.Bytes(), + } + proto.SetDefaults(pack) + common.SendToActThrSrv(int(server_proto.SSPacketID_PACKET_WT_PLAYERPAY), pack) + } + + //ActFPayMgrSington.OnPlayerPay(pd.SnId, pd.Platform, coin) +} + +func (this *Player) SendPlatformCanUsePromoterBind() { + state := int32(0) + plt := PlatformMgrSingleton.GetPlatform(this.Platform) + if plt != nil { + if plt.IsCanUserBindPromoter { + state = 1 + if this.BeUnderAgentCode != "" && this.BeUnderAgentCode != "0" { + state = 2 + } + } + } + + pack := &player_proto.SCBindPromoterState{ + BindState: proto.Int32(state), + } + + proto.SetDefaults(pack) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_BINDPROMOTERSTATE), pack) +} + +func (this *Player) RedirectByGame(packetid int, rawpack interface{}) bool { + if this.scene == nil || this.scene.gameSess == nil || this.scene.gameSess.Session == nil { + logger.Logger.Tracef("[%v] sess == nil ", this.Name) + return false + } + if rawpack == nil { + logger.Logger.Trace(" rawpack == nil ") + return false + } + + data, err := netlib.MarshalPacket(packetid, rawpack) + if err == nil { + pack := &server_proto.SSRedirectToPlayer{ + SnId: proto.Int32(this.SnId), + PacketId: proto.Int(packetid), + Data: data, + } + proto.SetDefaults(pack) + return this.SendToGame(int(server_proto.SSPacketID_PACKET_SS_REDIRECTTOPLAYER), pack) + } + return false +} + +// func (this *Player) GetMatchAr(matchId int32) *model.MatchAchievement { +// if this.MatchAchievement == nil { +// this.MatchAchievement = make(map[int32]*model.MatchAchievement) +// } +// if ar, exist := this.MatchAchievement[matchId]; exist { +// return ar +// } +// ar := &model.MatchAchievement{MatchId: matchId, CreateTs: int32(time.Now().Unix())} +// this.MatchAchievement[matchId] = ar +// return ar +// } +//func (this *Player) InitLayered() { +// logger.Logger.Trace("(this *Player) InitLayered") +// if this.IsRob { +// return +// } +// this.layered = make(map[int]bool) +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// return LogicLevelMgrSington.SendPostBySnIds(this.Platform, []int32{this.SnId}) +// }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { +// if data != nil { +// newData := data.([]NewMsg) +// for _, d := range newData { +// cnf := LogicLevelMgrSington.GetConfig(d.Platform) +// if cnf == nil { +// continue +// } +// this.layerlevels = d.Levels +// for _, v := range d.Levels { +// if td, ok := cnf.LogicLevelInfo[int32(v)]; ok { +// if td.StartAct == 1 { +// for _, id := range td.CheckActIds { +// this.layered[int(id)] = true +// } +// } +// } +// } +// } +// } +// //this.ActStateSend2Client() +// }), "SendPostBySnIds").Start() +// //// +// for k, v := range this.layered { +// logger.Logger.Tracef("InitLayered....id=%v %v", k, v) +// } +// +//} + +func (this *Player) ActStateSend2Client() { + logger.Logger.Trace("(this *Player) ActStateSend2Client") + actSwitchCfg := make([]int32, common.ActId_Max) + for i := 0; i < common.ActId_Max; i++ { + isOpen := true + switch i { + //case common.ActId_OnlineReward: + // cfg, ok := ActOnlineRewardMgrSington.Configs[this.Platform] + // if !ok || cfg.StartAct <= 0 { + // isOpen = false + // } + //case common.ActId_GoldTask: + // plateFormConfig := ActGoldTaskMgrSington.GetPlateFormConfig(this.Platform) + // if plateFormConfig == nil || plateFormConfig.StartAct == 0 { + // isOpen = false + // } + //case common.ActId_GoldCome: + // plateFormConfig := ActGoldComeMgrSington.GetPlateFormConfig(this.Platform) + // if plateFormConfig == nil || plateFormConfig.StartAct == 0 { + // isOpen = false + // } + //case common.ActId_LuckyTurntable: + // cfg, ok := ActLuckyTurntableMgrSington.Configs[this.Platform] + // if !ok || cfg == nil || cfg.StartAct <= 0 { + // isOpen = false + // } + //case common.ActId_Yeb: + // cfg, ok := ActYebMgrSington.Configs[this.Platform] + // if !ok || cfg == nil || cfg.StartAct <= 0 { + // isOpen = false + // } + //case common.ActId_Card: + // cfg, ok := ActCardMgrSington.Configs[this.Platform] + // if !ok || cfg == nil || cfg.StartAct <= 0 { + // isOpen = false + // } + case common.ActId_RebateTask: + rebateTask := RebateInfoMgrSington.rebateTask[this.Platform] + if rebateTask != nil && !rebateTask.RebateSwitch { + isOpen = false + } + //case common.ActId_VipLevelBonus: + // config := ActVipMgrSington.GetConfig(this.Platform) + // if config == nil || config.StartAct == 0 { + // isOpen = false + // } + //case common.ActId_Sign: + // config := ActSignMgrSington.GetConfig(this.Platform) + // if config == nil || config.StartAct == 0 { + // isOpen = false + // } + //case common.ActId_Task: + // config := ActTaskMgrSington.GetPlatformConfig(this.Platform) + // if config == nil || config.StartAct == 0 { + // isOpen = false + // } + case common.ExchangeId_Bank: + platform := PlatformMgrSingleton.GetPlatform(this.Platform) + if platform == nil || !platform.IsMarkFlag(int32(BindBackCard)) { + isOpen = false + } + case common.ExchangeId_Alipay: + platform := PlatformMgrSingleton.GetPlatform(this.Platform) + if platform == nil || !platform.IsMarkFlag(int32(BindAlipay)) { + isOpen = false + } + case common.ExchangeId_Wechat: + platform := PlatformMgrSingleton.GetPlatform(this.Platform) + if platform == nil || !platform.IsMarkFlag(int32(BindWeiXin)) { + isOpen = false + } + } + + if !isOpen { + actSwitchCfg[i] = 1 + continue + } + + open, ok := this.layered[i] + if ok && open { + actSwitchCfg[i] = 1 + } + } + pack := &login_proto.SCActSwitchCfg{ + ActSwitchCfg: actSwitchCfg, // 0开 1关 默认开 + } + proto.SetDefaults(pack) + logger.Logger.Trace("ActStateSend2Client: ", this.SnId, pack) + this.SendToClient(int(login_proto.LoginPacketID_PACKET_SC_ACTSWITCHCFG), pack) +} + +func (this *Player) ModifyActSwitch() { + logger.Logger.Trace("====================ModifyActSwitch==================") + + //this.ActStateSend2Client() +} + +// SyncPlayerDataToGateSrv 玩家信息同步到网关 +func (this *Player) SyncPlayerDataToGateSrv(pd *model.PlayerData) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(pd) + if err != nil { + logger.Logger.Info("(this *Player) UpdateToGateSrv gob.Marshal error", err) + } else { + pack := &server_proto.WRPlayerData{ + Sid: this.sid, + PlayerData: buf.Bytes(), + } + proto.SetDefaults(pack) + this.gateSess.Send(int(server_proto.SSPacketID_PACKET_WR_PlayerData), pack) + } +} + +func (this *Player) BindGroupTag(tags []string) { + if this.gateSess == nil { + return + } + pack := &server_proto.SGBindGroupTag{ + Sid: proto.Int64(this.sid), + Code: server_proto.SGBindGroupTag_OpCode_Add, + Tags: tags, + } + proto.SetDefaults(pack) + this.gateSess.Send(int(server_proto.SSPacketID_PACKET_SG_BINDGROUPTAG), pack) +} + +func (this *Player) UnBindGroupTag(tags []string) { + if this.gateSess == nil { + return + } + pack := &server_proto.SGBindGroupTag{ + Sid: proto.Int64(this.sid), + Code: server_proto.SGBindGroupTag_OpCode_Del, + Tags: tags, + } + proto.SetDefaults(pack) + this.gateSess.Send(int(server_proto.SSPacketID_PACKET_SG_BINDGROUPTAG), pack) +} + +func (this *Player) TryRetrieveLostGameCoin(sceneid int) { + //发送一个探针,等待ack后同步金币 + logProbe := model.NewCoinLog() + logProbe.SnId = this.SnId + logProbe.Count = 0 //必须是0,探针标识 + logProbe.RestCount = 0 //this.Coin + logProbe.SeqNo = this.GameCoinTs + logProbe.RoomId = int32(sceneid) + LogChannelSingleton.WriteLog(logProbe) + //先把玩家身上的钱清掉 + //this.Coin = 0 + this.SendDiffData() +} + +func (this *Player) SyncGameCoin(sceneid int, enterts int64) { + //游服金币冲账 + endts := time.Now().UnixNano() + var err error + var gamecoinlogs []model.CoinWAL + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + gamecoinlogs, err = model.GetCoinWALBySnidAndInGameAndGreaterTs(this.Platform, this.SnId, int32(sceneid), enterts) + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err == nil && len(gamecoinlogs) != 0 { + oldCoin := this.Coin + var cnt int64 + for i := 0; i < len(gamecoinlogs); i++ { + ts := gamecoinlogs[i].Ts + if ts >= enterts && ts <= endts { + cnt = gamecoinlogs[i].Count + this.Coin += cnt + if ts > this.GameCoinTs { + this.GameCoinTs = ts + } + } + } + newCoin := this.Coin + newTs := this.GameCoinTs + logger.Logger.Warnf("PlayerData(%v) SyncGameCoin before:enterts=%v before:Coin=%v after:GameCoinTs=%v after:Coin=%v", this.SnId, enterts, oldCoin, newTs, newCoin) + } + this.SendDiffData() + }), "GetCoinWALBySnidAndInGameAndGreaterTs").Start() +} +func (this *Player) SendShowRed(showType hall_proto.ShowRedCode, showChild, isShow int32) { + pack := &hall_proto.SCShowRed{ + ShowRed: &hall_proto.ShowRed{ + ShowType: showType, + ShowChild: proto.Int32(showChild), + IsShow: proto.Int32(isShow), + }, + } + proto.SetDefaults(pack) + //logger.Logger.Trace("SCShowRed:", pack) + this.SendToClient(int(hall_proto.HallPacketID_PACKET_SC_SHOWRED), pack) +} + +func (this *Player) SCVIPBuy(buy int64) { + //buy *= 10000 + //this.AddMoneyPayTotal(buy) + //this.GetVIPLevel(0) // 更新下vip等级 + pack := &player_proto.SCVIPBuy{ + OpRetCode: player_proto.OpResultCode_OPRC_Sucess, + } + pack.TolVipExp, pack.Money = this.GetCurrentVIPExp() // 获取经验会更新vip等级 + pack.Vip = this.VIP + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_VIPBUY), pack) +} + +func (this *Player) SCVIPInfo() { + if this.IsRob { + return + } + + pack := &player_proto.SCVIPInfo{ + OpRetCode: player_proto.OpResultCode_OPRC_Error, + } + + vips := VipMgrSington.GetVIPcfg(this.Platform) + if vips != nil { + pack.MoneyRatio = int32(vips.MoneyRatio) + for _, cfg := range vips.List { + data := &player_proto.VIPcfg{ + VipId: cfg.VipId, + VipEx: cfg.VipEx, + Price: cfg.Price, + Privilege1: cfg.Privilege1, + Privilege2: cfg.Privilege2, + Privilege3: cfg.Privilege3, + Privilege4: cfg.Privilege4, + Privilege5: cfg.Privilege5, + Privilege6: cfg.Privilege6, + Privilege7Price: cfg.Privilege7Price, + Privilege8: cfg.Privilege8, + LineId: cfg.RewardOutlineID, + ShopId2: cfg.ShopId2, + ShopId7: cfg.ShopId7, + } + for itemId, itemNum := range cfg.Award { + data.Item = append(data.Item, &player_proto.ItemInfo{ + ItemId: int32(itemId), + ItemNum: itemNum, + }) + } + for itemId, itemNum := range cfg.Privilege7 { + data.Privilege7 = append(data.Privilege7, &player_proto.ItemInfo{ + ItemId: int32(itemId), + ItemNum: itemNum, + }) + } + data.BagStatus = make([]int32, 3) + data.BagStatus[0] = 0 //每日礼包 0可领取 1不可领取 只能领取当前VIP等级的 + data.BagStatus[1] = 0 //每日金币礼包 0可领取 1不可领取 只能领取当前VIP等级的 + data.BagStatus[2] = this.GetPlayerVipBagStatus(cfg.ShopId7, cfg.VipId) //固定VIP礼包 0可领取 1不可领取 可以领取所有VIP等级的奖励 + if cfg.VipId != this.VIP { + data.BagStatus[0] = 1 //每日礼包 0可领取 1不可领取 只能领取当前VIP等级的 + data.BagStatus[1] = 1 //每日金币礼包 0可领取 1不可领取 只能领取当前VIP等级的 + } else { + data.BagStatus[0] = this.GetPlayerVipBagStatus(0, cfg.VipId) + data.BagStatus[1] = this.GetPlayerVipBagStatus(cfg.ShopId2, cfg.VipId) + } + pack.List = append(pack.List, data) + } + + pack.TolVipExp, pack.Money = this.GetCurrentVIPExp(vips) + pack.Vip = this.VIP + pack.OpRetCode = player_proto.OpResultCode_OPRC_Sucess + //WelfareMgrSington.MonitorWelfData(this) + //pack.VipId = append(pack.VipId, this.WelfData.VIPGift...) + } + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_VIPINFO), pack) +} +func (this *Player) GetVIPExpByPay(payTotal int32) int32 { + vips := VipMgrSington.GetVIPcfg(this.Platform) + return int32(math.Floor(float64(payTotal) * vips.MoneyRatio / 100)) +} +func (this *Player) VIPDraw(id int32) { + //WelfareMgrSington.MonitorWelfData(this) + pack := &player_proto.SCVIPDraw{ + Id: id, + OpRetCode: player_proto.OpResultCode_OPRC_Error, + } + if id != 0 { + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_DRAWVIPGIFT), pack) + return + } + if this.WelfData.VIPBag == nil { + this.WelfData.VIPBag = make(map[int32]map[int32]int32) + } + if innerMap, ok := this.WelfData.VIPBag[this.VIP]; ok { + if _, ok := innerMap[0]; ok { + logger.Logger.Trace("VIPDraw VIP is repeat id%v ", id) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_DRAWVIPGIFT), pack) + return + } + } else { + this.WelfData.VIPBag[this.VIP] = make(map[int32]int32) + } + + vips := VipMgrSington.GetVIPcfg(this.Platform) + if vips != nil { + for _, data := range vips.List { + if data.VipId == this.VIP { + pack.OpRetCode = player_proto.OpResultCode_OPRC_Sucess + this.WelfData.VIPBag[this.VIP][0] = 1 + //金币数量 + money := data.Privilege1[0] + //vip经验 + addVipExp := int64(float64(data.Privilege1[1]) / vips.MoneyRatio) + this.AddCoin(int64(money), 0, common.GainWay_VIPGift, "sys", "VIP每日礼包") + this.AddMoneyPayTotal(addVipExp) + pack.Vip = this.VIP + logger.Logger.Tracef("玩家领取VIP每日礼包成功!snid = %v,Vip = %v,金币数量 = %v,addVipEx = %v", this.SnId, this.VIP, money, addVipExp) + //VIP礼包统计数据 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var item []model.ItemInfo + item = append(item, model.ItemInfo{ItemId: 1, ItemNum: int64(money)}) + log := model.NewDbVip(this.Platform, this.SnId, this.VIP, 0, 0, 0, item, 0, "Vip每日礼包") + return model.InsertDbVipLog(log) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + logger.Logger.Errorf("err:", data.(error)) + } + }), "VIPDraw").Start() + break + } + } + } + + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_DRAWVIPGIFT), pack) + +} + +func (this *Player) GetCurrentVIPExp(vipcfg ...*webapi_proto.VIPcfgDataList) (exp int64, money int64) { + var vips *webapi_proto.VIPcfgDataList + if len(vipcfg) == 0 { + vips = VipMgrSington.GetVIPcfg(this.Platform) + } else { + vips = vipcfg[0] + } + exp = int64(float64(this.MoneyPayTotal) * vips.MoneyRatio) + tolexp := int32(0) + oldVipLevel := this.VIP + if vips != nil && this.MoneyPayTotal != 0 { + allExp := int64(float64(this.MoneyPayTotal) * vips.MoneyRatio) + for _, v := range vips.List { + tolexp = v.VipEx + if allExp >= int64(v.VipEx) { + this.VIP = v.VipId + this.VipExtra = v.Privilege4 + } else { + break + } + } + } + money = int64(tolexp) - exp + if money < 0 { + money = 0 + } + if oldVipLevel != this.VIP { + //玩家VIP升级 + this.SCVIPInfo() + logger.Logger.Trace("VIP升级!") + } + return // 默认 +} + +func (this *Player) GetVIPLevel(CoinPayTotal int64) int32 { + if this.IsRob { + return 0 + } + vips := VipMgrSington.GetVIPcfg(this.Platform) + vip := int32(0) + if vips != nil && this.MoneyPayTotal != 0 { + allExp := int64(float64(this.MoneyPayTotal) * vips.MoneyRatio) + for _, v := range vips.List { + if allExp >= int64(v.VipEx) { + vip = v.VipId + this.VipExtra = v.Privilege4 + } else { + break + } + } + } + if vip != this.VIP { + //玩家VIP升级 + this.SCVIPInfo() + logger.Logger.Trace("VIP升级!") + } + this.VIP = vip + return this.VIP +} + +func (this *Player) GetMaxVIPLevel() int32 { + + vips := VipMgrSington.GetVIPcfg(this.Platform) + if vips != nil && len(vips.List) > 0 { + vn := len(vips.List) - 1 + + return vips.List[vn].VipId + } + return 0 +} + +// 特权1 VIP比赛场免费次数 策划说暂时没了 +func (this *Player) GetVIPPrivilege1() int32 { + /* cfg := VipMgrSington.GetVIPLevelCfg(this.Platform, this.VIP) + if cfg != nil { + return cfg.Privilege1 + }*/ + return 0 +} + +// 玩家登录 检查充值状态 +func (this *Player) GetPayGoodsInfo() { + if this.IsRob { + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + info := model.GetDbShopLogsByState(this.Platform, this.SnId) + if info != nil { + for _, shop := range info { + err := model.UpdateDbShopState(shop.Platform, shop.LogId.Hex(), 1) + if err != nil { + logger.Logger.Error("GetPayGoodsInfo.UpdateDbShopState err:", err) + return nil + } + } + } + return info + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + infos := data.([]*model.DbShop) + for _, info := range infos { + var itemInfo []*player_proto.PayItem + var items []*Item + if len(info.Amount) > 0 { + this.AddCoin(int64(info.Amount[0]), 0, info.GainWay, "Callback_login", info.Remark) + this.AddDiamond(int64(info.Amount[1]), 0, info.GainWay, "Callback_login", info.Remark) + } + this.AddMoneyPayTotal(int64(info.ConsumeNum)) + if info.ItemInfo != nil { + for _, v := range info.ItemInfo { + items = append(items, &Item{ItemId: v.ItemId, ItemNum: v.ItemNum}) + itemInfo = append(itemInfo, &player_proto.PayItem{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + }) + } + } + switch info.Remark { + case "BlindBox": + if len(info.OtherParams) > 0 { + this.WelfData.BlindBoxId = info.OtherParams[0] + } else { + logger.Logger.Errorf("GetPayGoodsInfo BlindBox OtherParams is nil") + } + case "FirstRecharge": + if len(info.OtherParams) > 0 { + + this.WelfData.FirstPayDay = info.OtherParams[0] + this.WelfData.FirstPayTickets = info.Ts + } else { + logger.Logger.Errorf("GetPayGoodsInfo FirstRecharge OtherParams is nil") + } + case "ContinuousPay": + if len(info.OtherParams) > 0 { + + this.WelfData.ContinuousPayDay = info.OtherParams[0] + this.WelfData.ContinuousPayTickets = info.Ts + } else { + logger.Logger.Errorf("GetPayGoodsInfo ContinuousPay OtherParams is nil") + } + } + this.UpdatePlayerVipBag(info.ShopId) + this.UpdateShopID(info.ShopId) + + this.dirty = true + this.SendDiffData() + + info.Amount[2] = this.GetVIPExpByPay(info.ConsumeNum) + + BagMgrSingleton.AddJybBagInfo(this, items, 0, info.GainWay, info.Operator, info.Remark) + + PayGoodsInfo := &player_proto.SCPayGoodsInfo{ + Gold: info.Amount, + Item: itemInfo, + } + proto.SetDefaults(PayGoodsInfo) + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PAYGOODSINFO), PayGoodsInfo) + TaskSubjectSingleton.Touch(common.TaskTypePay, &TaskData{ + SnId: this.SnId, + Num: int64(info.ConsumeNum), + }) + } + } + + })).StartByFixExecutor("GetPayGoodsLogs") +} + +func (this *Player) SendJackPotInit() { + var pack = &hall_proto.SCHundredSceneGetGameJackpot{} + gameFreeIds := []int32{ + 3010001, 3010002, 3010003, 3010004, + 3020001, 3020002, 3020003, 3020004, + 3030001, 3030002, 3030003, 3030004, + 3040001, 3040002, 3040003, + 3050001, 3050002, 3050003} + for _, id := range gameFreeIds { + data := srvdata.PBDB_GameFreeMgr.GetData(id) + if data != nil && len(data.Jackpot) > 0 { + jpfi := &hall_proto.GameJackpotFundInfo{ + GameFreeId: proto.Int32(data.Id), + JackPotFund: proto.Int64(int64(data.BaseScore * data.Jackpot[0])), + } + pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi) + } + } + if len(pack.GameJackpotFund) > 0 { + this.SendToClient(int(hall_proto.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack) + } +} + +func (this *Player) OnlineLogLogin() { + if this.IsRob { + return + } + if this.onlineLog == nil { + this.onlineLog = model.NewOnlineLog(bson.NewObjectId(), this.SnId, common.LoginLogTypeLogin, this.Platform) + LogChannelSingleton.WriteLog(*this.onlineLog) + } +} + +func (this *Player) OnlineLogRehold() { + if this.IsRob { + return + } + if this.onlineLog == nil { + this.onlineLog = model.NewOnlineLog(bson.NewObjectId(), this.SnId, common.LoginLogTypeRehold, this.Platform) + LogChannelSingleton.WriteLog(*this.onlineLog) + } +} + +func (this *Player) OnlineLogDrop() { + if this.IsRob { + return + } + if this.onlineLog != nil && this.onlineLog.OfflineTs == 0 { + this.onlineLog.OfflineType = common.LoginLogTypeDrop + this.onlineLog.OfflineTs = time.Now().Unix() + LogChannelSingleton.WriteLog(*this.onlineLog) + this.onlineLog = nil + } +} + +func (this *Player) OnlineLogLogout() { + if this.IsRob { + return + } + if this.onlineLog != nil && this.onlineLog.OfflineTs == 0 { + this.onlineLog.OfflineType = common.LoginLogTypeLogout + this.onlineLog.OfflineTs = time.Now().Unix() + LogChannelSingleton.WriteLog(*this.onlineLog) + this.onlineLog = nil + } +} + +func (this *Player) AddDiamondToCoin(n int64) { + if n > 0 { + this.dirty = true + this.DiamondToCoin += n + } +} + +// SendRankSeason 通知客户端赛季信息 +func (this *Player) SendRankSeason() { + pack := &rankmatch.SCRMSeasonInfo{} + cfg := RankMgrSingleton.GetSeasonConfig(this.Platform) + if cfg == nil { + this.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMSeasonInfo), pack) + return + } + + pack = &rankmatch.SCRMSeasonInfo{ + Id: cfg.SeasonId, + TimeStamp: []int64{cfg.StartTs, cfg.EndTs}, + } + + rank := RankMgrSingleton.GetPlayerSeason(this.SnId) + if rank != nil && rank.PlayerRankSeason != nil && rank.RankType != nil { + for k, v := range rank.RankType { + if v == nil { + continue + } + info := &rankmatch.SeasonInfo{Id: k} + info.Lv, info.Score = RankMgrSingleton.GetPlayerRankLV(k, this.SnId) + info.LastLv, info.LastScore = RankMgrSingleton.GetPlayerLastRankLV(k, this.SnId) + pack.Seasons = append(pack.Seasons, info) + } + } + this.SendToClient(int(rankmatch.RankMatch_PACKET_RM_SCRMSeasonInfo), pack) + logger.Logger.Trace("SCTMSeasonInfo:", pack) +} + +func (this *Player) SendPlayerCoin() { + LogChannelSingleton.WriteLog(&model.RankPlayerCoin{ + Platform: this.Platform, + SnId: this.SnId, + Name: this.Name, + Sex: this.Sex, + HeadUrl: this.HeadUrl, + Coin: this.GetTotalCoin(), + ModId: this.GetRoleId(), + }) +} + +// 解锁炮倍 +func (this *Player) UnPlayerPowerEx(power int64) { + logger.Logger.Tracef("解锁炮倍 当前最大解锁炮倍:%v,要解锁的炮倍:%v", this.UnMaxPower, power) + if this.UnPlayerPower(power) { + pack := &player_proto.SCPlayerUnPower{ + UnMaxpower: power, + } + this.UnMaxPower = power + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PlayerUnPower), pack) + logger.Logger.Tracef("通知客户端解锁最大炮倍,snid = %v,power = %v", this.SnId, power) + } +} + +// 道具解锁炮台 +func (this *Player) ItemUnPlayerPowerListEx(itemId int32) { + if itemId == 0 { + return + } + //判断炮台存不存在 + var powerId int32 = 0 + for _, data := range srvdata.PBDB_ArtillerySkinMgr.Datas.GetArr() { + if data.CannonId == itemId { + powerId = data.Id + break + } + } + if powerId == 0 { + return + } + + if this.UnPlayerPowerList(powerId) { + //通知客户端解锁炮台 + pack := &player_proto.SCPlayerUnPowerList{ + UnPowerList: powerId, + } + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PlayerUnPowerList), pack) + logger.Logger.Trace("通知客户端解锁炮台 snid = %v,解锁的炮台:%v,当前已有的炮台 = %v", this.SnId, powerId, this.PowerList) + } +} + +// powerId 解锁炮台 +func (this *Player) UnPlayerPowerListEx(powerId int32) { + data := srvdata.PBDB_ArtillerySkinMgr.GetData(powerId) + if data == nil { + return + } + if this.UnPlayerPowerList(powerId) { + //通知客户端解锁炮台 + pack := &player_proto.SCPlayerUnPowerList{ + UnPowerList: powerId, + } + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PlayerUnPowerList), pack) + logger.Logger.Trace("通知客户端解锁炮台 snid = %v,解锁的炮台:%v,当前已有的炮台 = %v", this.SnId, powerId, this.PowerList) + } +} +func (this *Player) UpdatePlayerVipBag(shopId int32) { + //判断是否是vip商品 更新数据 + shopInfo := ShopMgrSington.GetShopInfo(shopId, this) + if shopInfo != nil { + if shopInfo.Page == ShopPagePrivilege { + if this.WelfData.VIPBag == nil { + this.WelfData.VIPBag = make(map[int32]map[int32]int32) + } + if _, ok := this.WelfData.VIPBag[shopInfo.VipLevel]; !ok { + this.WelfData.VIPBag[shopInfo.VipLevel] = make(map[int32]int32) + } + this.WelfData.VIPBag[shopInfo.VipLevel][shopInfo.Id] = shopInfo.Type + logger.Logger.Tracef("玩家购买VIP商品成功,更新购买状态!snid = %v shopId = %v,VIP = %v", this.SnId, shopInfo.Id, shopInfo.VipLevel) + pack := &player_proto.SCVIPDraw{ + Id: shopInfo.Type, + Vip: shopInfo.VipLevel, + OpRetCode: player_proto.OpResultCode_OPRC_Sucess, + } + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_DRAWVIPGIFT), pack) + //VIP金币礼包&VIP固定礼包统计数据 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var item []model.ItemInfo + item = append(item, model.ItemInfo{ItemId: shopInfo.Type, ItemNum: shopInfo.Amount}) + log := model.NewDbVip(this.Platform, this.SnId, shopInfo.VipLevel, shopInfo.Type, shopInfo.ConstType, int64(shopInfo.CostArea[0]), item, shopInfo.Id, shopInfo.Name) + return model.InsertDbVipLog(log) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + logger.Logger.Errorf("err:", data.(error)) + } + }), "UpdatePlayerVipBag").Start() + } + } +} + +// 判断vip特权某个礼包是否领取过 返回值:0可领取 1 不可领取 +func (this *Player) GetPlayerVipBagStatus(shopId, vipLevel int32) int32 { + if vipLevel > this.VIP { + return 1 + } + if this.WelfData == nil || this.WelfData.VIPBag == nil { + return 0 + } + if _, ok := this.WelfData.VIPBag[vipLevel]; !ok { + return 0 + } + if _, ok := this.WelfData.VIPBag[vipLevel][shopId]; !ok { + return 0 + } + return 1 +} + +func (this *Player) UpdateVipShopData() { + this.VipShopData = nil + this.VipShopRefreshCount = 0 + //每日礼包 和每日金币礼包需要初始化 + if this.WelfData != nil && this.WelfData.VIPBag != nil { + delKeyList := []int32{} + for _, data := range this.WelfData.VIPBag { + for shopId, shopType := range data { + if shopId == 0 { + //初始化每日礼包 + delKeyList = append(delKeyList, shopId) + logger.Logger.Trace("初始化VIP每日礼包!!!!!!!") + } else { + if shopType == 1 { + delKeyList = append(delKeyList, shopId) + logger.Logger.Trace("初始化VIP每日固定礼包!!!!!!!!!!") + } + } + } + } + for _, delKey := range delKeyList { + for _, data := range this.WelfData.VIPBag { + delete(data, delKey) + } + } + this.SCVIPInfo() + } +} + +func (this *Player) RandRobotVip(scene *Scene) { + if !this.IsRobot() || scene == nil || scene.dbGameFree == nil { + return + } + // vip + config := srvdata.PBDB_PotOddMgr.GetData(scene.dbGameFree.GetId()) + if config == nil || len(config.VipOdd) == 0 { + return + } + var all int32 + for _, v := range config.VipOdd { + all += v + } + n := rand.Int31n(all) + vip := 0 + var sum int32 + for k, v := range config.VipOdd { + sum += v + if n < sum { + vip = k + break + } + } + this.VIP = int32(vip) +} + +// BindTelReward 发送绑定手机号奖励 +func (this *Player) BindTelReward() { + // todo 奖励限额;ip日期最多赠送多少奖励 + // 发送奖励 + plt := PlatformMgrSingleton.GetPlatform(this.Platform) + if plt != nil { + var items []*Item + for k, v := range plt.BindTelReward { + switch k { + case 1: + items = append(items, &Item{ + ItemId: common.ItemIDCoin, + ItemNum: v, + }) + case 2: + items = append(items, &Item{ + ItemId: common.ItemIDDiamond, + ItemNum: v, + }) + default: + items = append(items, &Item{ + ItemId: k, + ItemNum: v, + }) + } + + } + BagMgrSingleton.AddJybBagInfo(this, items, 0, common.GainWay_BindTel, "system", "绑定手机号") + } +} + +func (this *Player) UpdateShopID(shopId int32) { + var shopInfo *ShopInfo + if shops, ok := ShopMgrSington.ShopInfos[this.Platform]; ok { + if shopInf, ok := shops[shopId]; ok { + shopInfo = shopInf + } + } + if shopInfo != nil && shopInfo.FirstSwitch { + if !slices.Contains(this.ShopID, int(shopInfo.Id)) { + this.ShopID = append(this.ShopID, int(shopInfo.Id)) + } + } +} + +// 增加抽奖次数 +func (this *Player) addLotteryCount(count int32) { + if WelfareMgrSington.GetPhoneLotteryStatus(this.Platform) == WelfareClose { + return + } + this.LotteryCount += count + if this.LotteryCount < 0 { + this.LotteryCount = 0 + } + if this.LotteryCount >= math.MaxInt32 { + this.LotteryCount = math.MaxInt32 + } + //通知客户端 + pack := &player_proto.SCPhoneLotteryCount{ + Count: this.LotteryCount, + } + this.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PhoneLotteryCount), pack) + +} + +// 增加手机积分 +func (this *Player) AddPhoneScore(num, add int64, gainWay int32, oper, remark string) { + if num == 0 { + return + } + if WelfareMgrSington.GetPhoneLotteryStatus(this.Platform) == WelfareClose { + return + } + logger.Logger.Tracef("snid(%v) AddPhoneScore(%v)", this.SnId, num) + if num != 0 /*&& !async*/ { + this.dirty = true + if num > 0 { + this.PhoneScore += num + } else { + if -num > this.PhoneScore { + logger.Logger.Errorf("Player.AddPhoneScore exception!!! num(%v) oper(%v)", num, oper) + num = -this.PhoneScore + this.PhoneScore = 0 + } else { + this.PhoneScore += num + } + } + + this.SendDiffData() + } +} + +// 抽奖任务 +func (this *Player) PhoneLotteryTask(taskId int32, num int64) { + if WelfareMgrSington.GetPhoneLotteryStatus(this.Platform) == WelfareClose { + if this.WelfData != nil && this.WelfData.PhoneLotteryTask != nil { + this.WelfData.PhoneLotteryTask = make(map[int32]*model.TaskData) + } + return + } + if this.WelfData == nil { + this.WelfData = model.NewWelfareData() + } + if this.WelfData.PhoneLotteryTask == nil { + this.WelfData.PhoneLotteryTask = make(map[int32]*model.TaskData) + } + if this.WelfData.PhoneLotteryTask[taskId] == nil { + this.WelfData.PhoneLotteryTask[taskId] = &model.TaskData{N: 0} // 初始化任务数据 + } + switch taskId { + case common.TaskTypePay: + this.WelfData.PhoneLotteryTask[taskId].N += num + if this.WelfData.PhoneLotteryTask[taskId].N/20 > 0 { + count := this.WelfData.PhoneLotteryTask[taskId].N / 20 + this.addLotteryCount(int32(count)) + this.WelfData.PhoneLotteryTask[taskId].N = this.WelfData.PhoneLotteryTask[taskId].N % 20 + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(this.SnId, this.Platform, "", 3, int(count), 4, 0)) + } + + case common.TaskTypeWinOrLose: + this.WelfData.PhoneLotteryTask[taskId].N += num + if this.WelfData.PhoneLotteryTask[taskId].N/200000000 > 0 { + count := this.WelfData.PhoneLotteryTask[taskId].N / 200000000 + this.addLotteryCount(int32(count)) + this.WelfData.PhoneLotteryTask[taskId].N = this.WelfData.PhoneLotteryTask[taskId].N % 200000000 + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(this.SnId, this.Platform, "", 3, int(count), 3, 0)) + } + + case common.TaskTypeTienlenCount: + this.WelfData.PhoneLotteryTask[taskId].N += num + if this.WelfData.PhoneLotteryTask[taskId].N/20 > 0 { + count := this.WelfData.PhoneLotteryTask[taskId].N / 20 + this.addLotteryCount(int32(count)) + this.WelfData.PhoneLotteryTask[taskId].N = this.WelfData.PhoneLotteryTask[taskId].N % 20 + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(this.SnId, this.Platform, "", 3, int(count), 2, 0)) + } + case common.TaskTypeFirstLogin: + this.addLotteryCount(5) + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(this.SnId, this.Platform, "", 3, 5, 1, 0)) + default: + logger.Logger.Errorf("手机抽奖任务未找到对应的任务ID: %d", taskId) + return + } +} + +func (this *Player) InviteTask(scoreType int32, gameId int32, n int64) { + if this.InviteSnId == 0 { + return + } + + if this.WelfData == nil { + this.WelfData = model.NewWelfareData() + } + if this.WelfData.Task == nil { + this.WelfData.Task = make(map[int32]*model.TaskData) + } + + switch scoreType { + case common.InviteScoreTypeBind: + SaveInviteScore(&model.InviteScore{ + Platform: this.Platform, + SnId: this.SnId, + InviteSnId: this.InviteSnId, + Tp: scoreType, + Score: 5000, + Ts: time.Now().UnixNano(), + }) + + case common.InviteScoreTypeLogin: + if this.WelfData.Task[common.TaskIDInviteFirstLogin] == nil { + this.WelfData.Task[common.TaskIDInviteFirstLogin] = &model.TaskData{} // 初始化任务数据 + } + if this.WelfData.Task[common.TaskIDInviteFirstLogin].Ts == 0 { + this.WelfData.Task[common.TaskIDInviteFirstLogin].Ts = time.Now().Unix() + SaveInviteScore(&model.InviteScore{ + Platform: this.Platform, + SnId: this.SnId, + InviteSnId: this.InviteSnId, + Tp: scoreType, + Score: 1000, + Ts: time.Now().UnixNano(), + }) + } + + case common.InviteScoreTypePlayTimes: + if this.WelfData.Task[common.TaskIDInvitePlayGame] == nil { + this.WelfData.Task[common.TaskIDInvitePlayGame] = &model.TaskData{} // 初始化任务数据 + } + if this.WelfData.Task[common.TaskIDInvitePlayGame].Ts == 0 { + this.WelfData.Task[common.TaskIDInvitePlayGame].Ts = time.Now().Unix() + SaveInviteScore(&model.InviteScore{ + Platform: this.Platform, + SnId: this.SnId, + InviteSnId: this.InviteSnId, + Tp: scoreType, + Score: 5000, + Ts: time.Now().UnixNano(), + }) + } + + case common.InviteScoreTypeRecharge: + if this.WelfData.Task[common.TaskIDInviteRecharge] == nil { + this.WelfData.Task[common.TaskIDInviteRecharge] = &model.TaskData{} // 初始化任务数据 + } + this.WelfData.Task[common.TaskIDInviteRecharge].N += n + a := this.WelfData.Task[common.TaskIDInviteRecharge].N / 100 + this.WelfData.Task[common.TaskIDInviteRecharge].N %= 100 + SaveInviteScore(&model.InviteScore{ + Platform: this.Platform, + SnId: this.SnId, + InviteSnId: this.InviteSnId, + Tp: scoreType, + Score: a * 10000, + Ts: time.Now().UnixNano(), + }) + + case common.InviteScoreTypeGameTimes: + score := 0 + switch { + case common.IsTienLen(int(gameId)): + score = 500 + case common.IsThirteen(int(gameId)): + score = 300 + case common.IsSmallRocket(int(gameId)): + score = 30 + case common.IsChess(int(gameId)): + score = 100 + } + SaveInviteScore(&model.InviteScore{ + Platform: this.Platform, + SnId: this.SnId, + InviteSnId: this.InviteSnId, + Tp: scoreType, + Score: int64(score), + Ts: time.Now().UnixNano(), + }) + } +} + +func (this *Player) ResetTaskN(tp int32) { + if this.WelfData == nil || this.WelfData.Task == nil { + return + } + + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.GetTaskType() != tp { + continue + } + data := this.WelfData.Task[v.GetId()] + if data != nil { + data.N = 0 + } + } +} + +func (this *Player) ResetTask(tp int32) { + if this.WelfData == nil || this.WelfData.Task == nil { + return + } + + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + if v.GetTaskType() != tp { + continue + } + this.WelfData.Task[v.GetId()] = &model.TaskData{} + } +} diff --git a/worldsrv/playercache.go b/worldsrv/playercache.go new file mode 100644 index 0000000..94b7334 --- /dev/null +++ b/worldsrv/playercache.go @@ -0,0 +1,219 @@ +package main + +import ( + "strconv" + "time" + + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/model" + "mongo.games.com/game/worldsrv/internal" +) + +const ( + // InvalidPlayerCacheSec 数据不存在,60秒内不再查询 + InvalidPlayerCacheSec int64 = 60 + + InvalidPlayerCacheMax int = 100000 + ListNumber int32 = 300 +) + +func init() { + module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0) + //GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGIN, PlayerCacheMgrSingleton) + //GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGOUT, PlayerCacheMgrSingleton) +} + +var PlayerCacheMgrSingleton = &PlayerCacheMgr{ + playerMap: make(map[int32]*PlayerCacheItem), + playerCbs: make(map[int32][]func(*PlayerCacheItem, bool, bool)), + playerInvalidIds: make(map[int32]int64), + playerWaitClr: make([]*PlayerCacheItem, 0, 128), + DbSaver: &DbSaver{ + Tick: ListNumber, + index: 0, + init: false, + list: make([]*SaverArray, ListNumber), + queue: make([]*BalanceQueue, 10), + pool: make(map[SaveTaskHandler]*SaverArray), + }, +} + +type PlayerCacheItem struct { + *model.PlayerData +} + +func (p *PlayerCacheItem) CanDel() bool { + //return !p.isOnline && time.Now().Unix()-p.lastTs > int64(ListNumber) + return true +} + +func (p *PlayerCacheItem) Time2Save() { + // 并没有对缓存数据做什么操作,只是释放缓存 + if p.CanDel() { + PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p) + } +} + +type PlayerCacheMgr struct { + *DbSaver + playerMap map[int32]*PlayerCacheItem // snid; 玩家信息缓存 + playerCbs map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法 + playerInvalidIds map[int32]int64 // snid; 防止频繁访问数据库 + playerWaitClr []*PlayerCacheItem // 根据DbSaver缓冲失效策略释放玩家数据 +} + +func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(*PlayerCacheItem, bool, bool), createIfNotExist bool) { + // 1.玩家缓冲数据存在 + if p, exist := c.playerMap[snid]; exist { + cb(p, false, false) + return + } + + // 2.查询频率限制(限制数据库没有数据的情况) + // 玩家不存在,避免打到db上 + // 数据库查询失败或没有玩家信息就不要重复查了 + if lastTs, exist := c.playerInvalidIds[snid]; exist { + if time.Now().Unix()-lastTs < InvalidPlayerCacheSec { + cb(nil, false, false) + return + } + delete(c.playerInvalidIds, snid) + } + + // 3.有正在查询中,等待查询结果 + // 查询队列,如果已经有在查数据库了,就等待数据库查询结果 + if cbs, exist := c.playerCbs[snid]; exist { + cbs = append(cbs, cb) + c.playerCbs[snid] = cbs + return + } + c.playerCbs[snid] = []func(*PlayerCacheItem, bool, bool){cb} + + var isnew bool + var replays []*internal.PlayerLoadReplay + task.New(core.CoreObject(), task.CallableWrapper(func(o *basic.Object) interface{} { + pi, flag := model.GetPlayerDataBySnId(plt, snid, true, createIfNotExist) + isnew = flag + for _, v := range internal.GetPlayerLoads() { + replays = append(replays, v.Load(plt, snid, pi)) + } + return pi + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + su := true + pi, ok := data.(*model.PlayerData) + if !ok || pi == nil { + su = false + } + + if su { + for _, v := range replays { + if v == nil { + continue + } + if v.Err != nil { + su = false + break + } + } + } + + if !su { + c.cacheInvalidPlayerId(snid) + delete(c.playerCbs, snid) + for _, cb := range c.playerCbs[snid] { + cb(nil, true, false) + } + return + } + + if len(replays) == len(internal.GetPlayerLoads()) { + for k, v := range internal.GetPlayerLoads() { + if v == nil || replays[k] == nil { + continue + } + v.Callback(pi, replays[k]) + } + } + + // 查询成功,缓存数据,执行cb方法 + item, ok := c.playerMap[snid] + if !ok { + item = &PlayerCacheItem{PlayerData: pi} + c.playerMap[snid] = item + c.RegisterDbSaverTask(item) + } + if cbs, exist := c.playerCbs[snid]; exist { + delete(c.playerCbs, snid) + for _, cb := range cbs { + cb(item, true, isnew) + } + } + + }), "PlayerCacheMgr.Get").StartByExecutor(strconv.Itoa(int(snid))) +} + +func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) { + isAsyn := false + count := len(snid) + result := make([]*PlayerCacheItem, 0, count) + innerCb := func(item *PlayerCacheItem, asyn, isnew bool) { + if item != nil { + result = append(result, item) + } + if asyn { + isAsyn = true + } + count-- + if count == 0 { + cb(result, isAsyn) + } + } + for _, id := range snid { + c.Get(plt, id, innerCb, false) + } +} + +func (c *PlayerCacheMgr) cacheInvalidPlayerId(snid int32) { + if len(c.playerInvalidIds) >= InvalidPlayerCacheMax { + for id := range c.playerInvalidIds { + delete(c.playerInvalidIds, id) + if len(c.playerInvalidIds) < InvalidPlayerCacheMax { + break + } + } + } + c.playerInvalidIds[snid] = time.Now().Unix() +} + +// UnCacheInvalidPlayerId 解除一次玩家数据查询频率限制 +// 玩家数据不存在,限制查询频率,避免查询过于频繁 +// 但是当玩家数据确认存在后,可以解除限制,提前允许查询到玩家数据 +func (c *PlayerCacheMgr) UnCacheInvalidPlayerId(snid int32) { + delete(c.playerInvalidIds, snid) +} + +func (c *PlayerCacheMgr) ModuleName() string { + return "PlayerCacheMgr" +} + +func (c *PlayerCacheMgr) Init() { + c.DbSaver.Init() +} + +func (c *PlayerCacheMgr) Update() { + // 执行Time2Save之后清除缓存 + c.DbSaver.Update() + for _, p := range c.playerWaitClr { + delete(c.playerMap, p.SnId) + c.UnregisteDbSaveTask(p) + } + c.playerWaitClr = c.playerWaitClr[0:0] +} + +func (c *PlayerCacheMgr) Shutdown() { + module.UnregisteModule(c) +} diff --git a/worldsrv/playerlistener.go b/worldsrv/playerlistener.go new file mode 100644 index 0000000..35e223e --- /dev/null +++ b/worldsrv/playerlistener.go @@ -0,0 +1,152 @@ +package main + +var _playerListeners []PlayerListener + +func RegistePlayerListener(l PlayerListener) { + for _, ll := range _playerListeners { + if ll == l { + return + } + } + _playerListeners = append(_playerListeners, l) +} + +type PlayerListener interface { + //登录登出相关 + OnPlayerLogined(p *Player) + OnPlayerLogouted(p *Player) + OnPlayerDropLine(p *Player) + OnPlayerRehold(p *Player) + OnPlayerReturnScene(p *Player) //玩家返回房间 + //时间相关 + OnPlayerSecTimer(p *Player) + OnPlayerMiniTimer(p *Player) + OnPlayerHourTimer(p *Player) + OnPlayerDayTimer(p *Player, login, continuous bool) + OnPlayerWeekTimer(p *Player) + OnPlayerMonthTimer(p *Player) + //业务相关 + OnPlayerEnterScene(p *Player, s *Scene) + OnPlayerLeaveScene(p *Player, s *Scene) +} + +type BasePlayerListener struct { +} + +func (l *BasePlayerListener) OnPlayerLogined(p *Player) {} +func (l *BasePlayerListener) OnPlayerLogouted(p *Player) {} +func (l *BasePlayerListener) OnPlayerDropLine(p *Player) {} +func (l *BasePlayerListener) OnPlayerRehold(p *Player) {} +func (l *BasePlayerListener) OnPlayerReturnScene(p *Player) {} +func (l *BasePlayerListener) OnPlayerSecTimer(p *Player) {} +func (l *BasePlayerListener) OnPlayerMiniTimer(p *Player) {} +func (l *BasePlayerListener) OnPlayerHourTimer(p *Player) {} +func (l *BasePlayerListener) OnPlayerDayTimer(p *Player, login, continuous bool) {} +func (l *BasePlayerListener) OnPlayerWeekTimer(p *Player) {} +func (l *BasePlayerListener) OnPlayerMonthTimer(p *Player) {} +func (l *BasePlayerListener) OnPlayerEnterScene(p *Player, s *Scene) {} +func (l *BasePlayerListener) OnPlayerLeaveScene(p *Player, s *Scene) {} + +func FirePlayerLogined(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerLogined(p) + } + } +} + +func FirePlayerLogouted(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerLogouted(p) + } + } +} + +func FirePlayerDropLine(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerDropLine(p) + } + } +} + +func FirePlayerRehold(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerRehold(p) + } + } +} + +func FirePlayerReturnScene(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerReturnScene(p) + } + } +} + +func FirePlayerSecTimer(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerSecTimer(p) + } + } +} + +func FirePlayerMiniTimer(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerMiniTimer(p) + } + } +} + +func FirePlayerHourTimer(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerHourTimer(p) + } + } +} + +func FirePlayerDayTimer(p *Player, login, continuous bool) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerDayTimer(p, login, continuous) + } + } +} + +func FirePlayerWeekTimer(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerWeekTimer(p) + } + } +} + +func FirePlayerMonthTimer(p *Player) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerMonthTimer(p) + } + } +} + +func FirePlayerEnterScene(p *Player, s *Scene) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerEnterScene(p, s) + } + } +} + +func FirePlayerLeaveScene(p *Player, s *Scene) { + for _, l := range _playerListeners { + if l != nil { + l.OnPlayerLeaveScene(p, s) + } + } +} diff --git a/worldsrv/playermgr.go b/worldsrv/playermgr.go new file mode 100644 index 0000000..adfbc57 --- /dev/null +++ b/worldsrv/playermgr.go @@ -0,0 +1,1145 @@ +package main + +import ( + "math/rand" + "mongo.games.com/game/worldsrv/internal" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/utils" + "mongo.games.com/goserver/srvlib" + srvproto "mongo.games.com/goserver/srvlib/protocol" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + server_proto "mongo.games.com/game/protocol/server" +) + +var PlayerMgrSington = &PlayerMgr{ + sidMap: make(map[int64]*Player), + snidMap: make(map[int32]*Player), + accountMap: make(map[string]*Player), + tokenMap: make(map[string]*Player), + players: make([]*Player, 0, 1024), + playerOfPlatform: make(map[string]map[int32]*Player), + loading: make(map[string]*PlayerPendingData), +} + +type PlayerPendingData struct { + sid int64 // 连接标识 + ts int64 // 加载数据的开始时间 +} + +type PlayerMgr struct { + BaseClockSinker + sidMap map[int64]*Player // sid + snidMap map[int32]*Player // snid + accountMap map[string]*Player // accountid + tokenMap map[string]*Player // 客服token + players []*Player // 只有真实玩家 + playerOfPlatform map[string]map[int32]*Player // platform:snid;只有真实玩家 + + loading map[string]*PlayerPendingData // accountid,控制访问频率 +} + +// PlayerStatics 在线统计 +type PlayerStatics struct { + PlayerCnt int // 真人数量 + RobotCnt int // 机器人数量 + PlayerGamingCnt int // 游戏中的真人数量 + RobotGamingCnt int // 游戏中的机器人数量 + GamingCnt map[int]int // gameid:玩家人数(包含机器人) +} + +// GetOnlineCount 在线人数统计 +func (this *PlayerMgr) GetOnlineCount() *PlayerStatics { + ps := &PlayerStatics{ + GamingCnt: make(map[int]int), + } + for _, player := range this.sidMap { + if player != nil && player.IsOnLine() { + if !player.IsRob { + ps.PlayerCnt++ + if player.scene != nil { + ps.PlayerGamingCnt++ + } + } else { + ps.RobotCnt++ + if player.scene != nil { + ps.RobotGamingCnt++ + } + } + if player.scene != nil { + ps.GamingCnt[player.scene.gameId] = ps.GamingCnt[player.scene.gameId] + 1 + } + } + } + return ps +} + +// IsOnline 判断玩家是否在线 +func (this *PlayerMgr) IsOnline(snId int32) bool { + player, ok := this.snidMap[snId] + if ok { + return player.IsOnLine() + } else { + return false + } +} + +// AddPlayer 缓存玩家数据,并添加到持久化管理器中 +func (this *PlayerMgr) AddPlayer(sid int64, playerInfo *model.PlayerData, s *netlib.Session) bool { + player := NewPlayer(sid, playerInfo, s) + if player == nil { + return false + } + + if sid == 0 { + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer player sid == 0:") + return false + } + + logger.Logger.Trace("(this *PlayerMgr) AddPlayer Set player ip:", player.Ip) + this.sidMap[sid] = player + var oldp *Player + if p, exist := this.snidMap[player.SnId]; exist { + oldp = p + } + this.snidMap[player.SnId] = player + this.accountMap[player.AccountId] = player + if player.customerToken != "" { + this.tokenMap[player.customerToken] = player + } + if !player.IsRob { + var found bool + for i, p := range this.players { + if p.SnId == player.SnId { + found = true + logger.Logger.Warnf("(this *PlayerMgr) AddPlayer [this.players] found player exist snid=%v", player.SnId) + this.players[i] = player + break + } + } + if !found { + this.players = append(this.players, player) + } + if player.HeadUrl == "" { + player.HeadUrl = niceIdMgr.GetRobHeadUrl(player.Head) + } + + //平台玩家管理器 + if pp, exist := this.playerOfPlatform[player.Platform]; exist { + pp[player.SnId] = player + } else { + pp = make(map[int32]*Player) + pp[player.SnId] = player + this.playerOfPlatform[player.Platform] = pp + } + + logger.Logger.Tracef("###%v mount to DBSaver[AddPlayer]", player.Name) + if oldp != nil { //删除旧的玩家 + DbSaver_Inst.UnregisteDbSaveTask(oldp) + } + DbSaver_Inst.RegisterDbSaverTask(player) + niceIdMgr.NiceIdCheck(player.SnId) + } else { + player.NiceId = niceIdMgr.PopNiceId(player.SnId) + player.HeadUrl = niceIdMgr.GetRobHeadUrlIdx() + } + return true +} + +// DelPlayer 清除玩家缓存数据 +// 一般真人是数据持久化后删除或数据没有修改,机器人不用持久化(机器人数据没有主动删除) +func (this *PlayerMgr) DelPlayer(snid int32) bool { + player, ok := this.snidMap[snid] + if !ok || player == nil { + return false + } + + if player.sid != 0 { + delete(this.sidMap, player.sid) + } + delete(this.snidMap, player.SnId) + delete(this.accountMap, player.AccountId) + if player.customerToken != "" { + delete(this.tokenMap, player.customerToken) + } + if player != nil && !player.IsRob { + index := -1 + for i, p := range this.players { + if p.SnId == snid { + index = i + break + } + } + if index != -1 { + this.players = append(this.players[:index], this.players[index+1:]...) + } + + //平台玩家管理器 + if pp, exist := this.playerOfPlatform[player.Platform]; exist { + delete(pp, player.SnId) + } + + niceIdMgr.PushNiceId(player.NiceId) + } + + for _, v := range internal.GetPlayerLoads() { + v.Release(player.Platform, player.SnId) + } + + player.OnLogouted() + + return true +} + +// DroplinePlayer 玩家掉线或登出 +// 1.玩家登出 +// 2.玩家网络断开 +// 3.被踢掉线 +func (this *PlayerMgr) DroplinePlayer(p *Player) { + delete(this.sidMap, p.sid) +} + +// ReholdPlayer 玩家重连 +// 1.登录获取玩家数据 +func (this *PlayerMgr) ReholdPlayer(p *Player, newSid int64, newSess *netlib.Session) { + if p.sid != 0 { + delete(this.sidMap, p.sid) + } + + if newSid == 0 { + logger.Logger.Errorf("(this *PlayerMgr) ReholdPlayer(snid=%v, new=%v)", p.SnId, newSid) + } + + p.sid = newSid + p.gateSess = newSess + p.SetOnline() + this.sidMap[newSid] = p +} + +// GetPlayer 获取玩家数据(玩家在线) +func (this *PlayerMgr) GetPlayer(id int64) *Player { + if pi, ok := this.sidMap[id]; ok { + return pi + } + return nil +} + +func (this *PlayerMgr) GetPlayerBySnId(id int32) *Player { + if p, ok := this.snidMap[id]; ok { + return p + } + return nil +} + +func (this *PlayerMgr) GetPlatformPlayerBySnId(platform string, id int32) *Player { + if players, exit := this.playerOfPlatform[platform]; exit { + if p, ok := players[id]; ok { + return p + } + } + return nil +} + +// GetPlayersBySnIds 批量取出玩家信息 +func (this *PlayerMgr) GetPlayersBySnIds(ids []int32) []*Player { + var retPlayers []*Player + for _, v := range ids { + if p, ok := this.snidMap[v]; ok { + retPlayers = append(retPlayers, p) + } + } + return retPlayers +} + +func (this *PlayerMgr) GetPlayerByAccount(acc string) *Player { + if p, ok := this.accountMap[acc]; ok { + return p + } + return nil +} + +func (this *PlayerMgr) GetPlayerByToken(token string) *Player { + if p, ok := this.tokenMap[token]; ok { + return p + } + return nil +} + +func (this *PlayerMgr) UpdatePlayerToken(p *Player, newToken string) { + oldToken := p.customerToken + if oldToken != newToken { + if oldToken != "" { + if _, ok := this.tokenMap[oldToken]; ok { + delete(this.tokenMap, oldToken) + } + } + if newToken != "" { + this.tokenMap[newToken] = p + p.customerToken = newToken + } + } +} + +// BroadcastMessage 给所有玩家发消息 +func (this *PlayerMgr) BroadcastMessage(packetid int, rawpack interface{}) bool { + sc := &srvproto.BCSessionUnion{ + Bccs: &srvproto.BCClientSession{}, + } + pack, err := BroadcastMaker.CreateBroadcastPacket(sc, packetid, rawpack) + if err == nil && pack != nil { + srvlib.ServerSessionMgrSington.Broadcast(int(srvproto.SrvlibPacketID_PACKET_SS_BROADCAST), pack, common.GetSelfAreaId(), srvlib.GateServerType) + return true + } + return false +} + +// BroadcastMessageToPlatform 给某个平台所有玩家发消息 +func (this *PlayerMgr) BroadcastMessageToPlatform(platform string, packetid int, rawpack interface{}) { + if platform == "" { + this.BroadcastMessage(packetid, rawpack) + } else { + players := this.playerOfPlatform[platform] + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + for _, p := range players { + if p != nil && p.gateSess != nil && p.IsOnLine() /*&& p.Platform == platform*/ { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } + } +} + +func (this *PlayerMgr) BroadcastMessageToPlatformByFunc(platform string, packetid int, rawpack interface{}, f func(p *Player) bool) { + if platform == "" { + this.BroadcastMessage(packetid, rawpack) + } else { + players := this.playerOfPlatform[platform] + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + for _, p := range players { + if p != nil && p.gateSess != nil && p.IsOnLine() && f(p) { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } + } +} + +// BroadcastMessageToPlatformWithHall 给某个平台所有在大厅中的玩家发消息 +func (this *PlayerMgr) BroadcastMessageToPlatformWithHall(platform string, snid int32, packetid int, rawpack interface{}) { + if platform == "" { + this.BroadcastMessage(packetid, rawpack) + } else { + player := this.GetPlayerBySnId(snid) + if player != nil { + players := this.playerOfPlatform[platform] + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + for _, p := range players { + if p != nil && p.gateSess != nil && p.IsOnLine() && p.scene == nil { + if FriendMgrSington.IsShield(p.Platform, p.SnId, snid) { + continue + } + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } + } + } +} + +// BroadcastMessageToGroup 发送群组消息 +func (this *PlayerMgr) BroadcastMessageToGroup(packetid int, rawpack interface{}, tags []string) bool { + pack := &server_proto.SSCustomTagMulticast{ + Tags: tags, + } + if byteData, ok := rawpack.([]byte); ok { + pack.RawData = byteData + } else { + byteData, err := netlib.MarshalPacket(packetid, rawpack) + if err == nil { + pack.RawData = byteData + } else { + logger.Logger.Info("PlayerMgr.BroadcastMessageToGroup err:", err) + return false + } + } + srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_SS_CUSTOMTAG_MULTICAST), pack, common.GetSelfAreaId(), srvlib.GateServerType) + return true +} + +// BroadcastMessageToTarget 给某些玩家发消息 +func (this *PlayerMgr) BroadcastMessageToTarget(platform string, target []int32, packetid int, rawpack interface{}) { + players := this.playerOfPlatform[platform] + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + for _, p := range players { + if p != nil && p.gateSess != nil && p.IsOnLine() /*&& p.Platform == platform*/ { + if common.InSliceInt32(target, p.SnId) { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} + +// 感兴趣所有clock event +func (this *PlayerMgr) InterestClockEvent() int { + return (1 << CLOCK_EVENT_MAX) - 1 +} + +func (this *PlayerMgr) OnSecTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnSecTimer() + }) + } +} + +func (this *PlayerMgr) OnMiniTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnMiniTimer() + }) + } +} + +func (this *PlayerMgr) OnHourTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnHourTimer() + }) + } +} + +func (this *PlayerMgr) OnDayTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnDayTimer(false, true, 1) + }) + } +} + +func (this *PlayerMgr) OnMonthTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnMonthTimer() + }) + } +} + +func (this *PlayerMgr) OnWeekTimer() { + for _, player := range this.players { + utils.CatchPanic(func() { + player.OnWeekTimer() + }) + } +} + +func (this *PlayerMgr) OnShutdown() { + this.SaveAll() +} + +// SaveAll 保存所有数据,dirty=true +func (this *PlayerMgr) SaveAll() { + count := len(this.players) + start := time.Now() + saveCnt := 0 + failCnt := 0 + nochangeCnt := 0 + logger.Logger.Info("===@PlayerMgr.SaveAll BEG@=== TotalCount:", count) + for i, p := range this.players { + idx := i + 1 + if p.dirty { + if model.SavePlayerData(p.PlayerData) { + logger.Logger.Infof("===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v save [ok] @=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs) + saveCnt++ + } else { + logger.Logger.Warnf("===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v save [error]@=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs) + failCnt++ + } + } else { + logger.Logger.Infof("nochange===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v nochange [ok]@=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs) + nochangeCnt++ + } + for _, v := range internal.GetPlayerLoads() { + v.Save(p.Platform, p.SnId, true, true) + } + } + logger.Logger.Infof("===@PlayerMgr.SaveAll END@===, total:%v saveCnt:%v failCnt:%v nochangeCnt:%v take:%v", count, saveCnt, failCnt, nochangeCnt, time.Now().Sub(start)) +} + +// 黑名单事件 +//func (this *PlayerMgr) OnAddBlackInfo(blackinfo *BlackInfo) { +// if blackinfo.Snid > 0 { +// if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil { +// p.PlayerData.BlacklistType = int32(blackinfo.BlackType) +// p.dirty = true +// p.Time2Save() +// } else { +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(blackinfo.BlackType)) +// return nil +// }), nil, "PlayerMgrOnAddBlackInfo").Start() +// } +// } +//} + +//func (this *PlayerMgr) OnEditBlackInfo(blackinfo *BlackInfo) { +// //nothing +// //if blackinfo.Snid > 0 { +// // if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil { +// // p.PlayerData.BlacklistType = int32(blackinfo.BlackType) +// // p.dirty = true +// // p.Time2Save() +// // } else { +// // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// // model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(blackinfo.BlackType)) +// // return nil +// // }), nil, "PlayerMgrOnEditBlackInfo").Start() +// // } +// //} +//} + +//func (this *PlayerMgr) OnRemoveBlackInfo(blackinfo *BlackInfo) { +// //nothing +// //if blackinfo.Snid > 0 { +// // if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil { +// // p.PlayerData.BlacklistType = 0 +// // p.dirty = true +// // p.Time2Save() +// // } else { +// // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// // model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(0)) +// // return nil +// // }), nil, "PlayerMgrOnRemoveBlackInfo").Start() +// // } +// //} +//} + +func (this *PlayerMgr) KickoutByPlatform(name string) { + for _, p := range this.players { + if name == "" || p.Platform == name { + p.Kickout(common.KickReason_Disconnection) + } + } +} + +func (this *PlayerMgr) UpdateAllPlayerPackageTag(packageTag, platform, channel, promoter string, promoterTree, tagkey int32) int { + var cnt int + for _, p := range this.players { + if p != nil && !p.IsRob { + if p.PackageID == packageTag { + p.Platform = platform + p.Channel = channel + p.BeUnderAgentCode = promoter + p.PromoterTree = promoterTree + //p.TagKey = tagkey + p.dirty = true + cnt++ + } + } + } + return cnt +} + +// LoadRobots 预加载机器人数据 +func (this *PlayerMgr) LoadRobots() { + if model.GameParamData.PreLoadRobotCount > 0 { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + tsBeg := time.Now() + robots := model.GetRobotPlayers(model.GameParamData.PreLoadRobotCount) + tsEnd := time.Now() + logger.Logger.Tracef("GetRobotPlayers take:%v total:%v", tsEnd.Sub(tsBeg), len(robots)) + return robots + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if robots, ok := data.([]*model.PlayerData); ok { + if robots != nil { + for i := 0; i < len(robots); i++ { + if this.GetPlayerBySnId(robots[i].SnId) == nil { + player := NewPlayer(0, robots[i], nil) + if player != nil { + this.snidMap[player.SnId] = player + this.accountMap[player.AccountId] = player + if player.customerToken != "" { + this.tokenMap[player.customerToken] = player + } + } + } + } + } + } + }), "GetRobotPlayers").Start() + } +} + +func (this *PlayerMgr) StartLoading(accid string, sid int64) bool { + ts := time.Now().Unix() + if d, exist := this.loading[accid]; exist { + d.sid = sid + if ts-d.ts > 300 { + d.ts = ts + return false + } + return true + } + this.loading[accid] = &PlayerPendingData{sid: sid, ts: ts} + return false +} + +func (this *PlayerMgr) EndPlayerLoading(accid string) int64 { + if d, exist := this.loading[accid]; exist { + delete(this.loading, accid) + return d.sid + } + return 0 +} + +//func PlayerRankGe(p1, p2 *Player, n int) bool { +// switch n { +// case 0: +// if p1.TotalCoin == p2.TotalCoin { +// return p1.SnId < p2.SnId +// } else { +// return p1.TotalCoin > p2.TotalCoin +// } +// case 1: +// if p1.CoinPayTotal == p2.CoinPayTotal { +// return p1.SnId < p2.SnId +// } else { +// return p1.CoinPayTotal > p2.CoinPayTotal +// } +// case 2: +// if p1.CoinExchangeTotal == p2.CoinExchangeTotal { +// return p1.SnId < p2.SnId +// } else { +// return p1.CoinExchangeTotal > p2.CoinExchangeTotal +// } +// case 3: +// a := p1.Coin + p1.SafeBoxCoin - p1.CoinPayTotal +// b := p2.Coin + p2.SafeBoxCoin - p2.CoinPayTotal +// if a == b { +// return p1.SnId < p2.SnId +// } else { +// return a > b +// } +// +// } +// return false +//} + +// func (this *PlayerMgr) GetRank() map[string][]*model.Rank { +// ret := make(map[string][]*model.Rank) +// ls := make(map[string]*list.List) +// +// platforms := PlatformMgrSingleton.Platforms +// for p := range platforms { +// ret[p] = make([]*model.Rank, 0) +// ls[p] = list.New() +// } +// +// for _, player := range this.players { +// if player.IsRob { +// continue +// } +// +// p := player.PlayerData.Platform +// if _, ok := platforms[p]; !ok { +// continue +// } +// +// l := ls[p] +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// if PlayerRankGe(player, np) { +// l.InsertBefore(player, n) +// goto CHECK +// } +// } +// //else { +// // logger.Logger.Warnf("PlayerMgr.GetRank n.Value.(*Player) fail") +// // continue +// //} +// } +// +// l.PushBack(player) +// CHECK: +// if l.Len() > model.MAX_RANK_COUNT { +// l.Remove(l.Back()) +// } +// } +// +// for p := range platforms { +// l := ls[p] +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// ret[p] = append(ret[p], &model.Rank{ +// SnId: np.PlayerData.SnId, +// Name: np.PlayerData.Name, +// Head: np.PlayerData.Head, +// VIP: np.PlayerData.VIP, +// TotalCoin: np.PlayerData.TotalCoin, +// }) +// } +// } +// } +// +// return ret +// } + +//func (this *PlayerMgr) GetAssetRank(platform string) []*model.Rank { +// ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT) +// l := list.New() +// +// for _, player := range this.players { +// if player.IsRob { +// continue +// } +// +// if player.PlayerData.Platform != platform { +// continue +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// if PlayerRankGe(player, np, 0) { +// l.InsertBefore(player, n) +// goto CHECK +// } +// } +// } +// +// l.PushBack(player) +// CHECK: +// if l.Len() > model.MAX_RANK_COUNT { +// l.Remove(l.Back()) +// } +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// ret = append(ret, &model.Rank{ +// SnId: np.PlayerData.SnId, +// Name: np.PlayerData.Name, +// Head: np.PlayerData.Head, +// VIP: np.PlayerData.VIP, +// TotalCoin: np.PlayerData.TotalCoin, +// }) +// } +// } +// +// return ret +//} + +//func (this *PlayerMgr) GetRechargeLists(platform string) []*model.Rank { +// ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT) +// l := list.New() +// +// for _, player := range this.players { +// if player.IsRob { +// continue +// } +// +// if player.PlayerData.Platform != platform { +// continue +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// if PlayerRankGe(player, np, 1) { +// l.InsertBefore(player, n) +// goto CHECK +// } +// } +// } +// +// l.PushBack(player) +// CHECK: +// if l.Len() > model.MAX_RANK_COUNT { +// l.Remove(l.Back()) +// } +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// ret = append(ret, &model.Rank{ +// SnId: np.PlayerData.SnId, +// Name: np.PlayerData.Name, +// Head: np.PlayerData.Head, +// VIP: np.PlayerData.VIP, +// TotalCoin: np.PlayerData.CoinPayTotal, +// }) +// } +// } +// +// return ret +//} + +//func (this *PlayerMgr) GetExchangeLists(platform string) []*model.Rank { +// ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT) +// l := list.New() +// +// for _, player := range this.players { +// if player.IsRob { +// continue +// } +// +// if player.PlayerData.Platform != platform { +// continue +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// if PlayerRankGe(player, np, 2) { +// l.InsertBefore(player, n) +// goto CHECK +// } +// } +// } +// +// l.PushBack(player) +// CHECK: +// if l.Len() > model.MAX_RANK_COUNT { +// l.Remove(l.Back()) +// } +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// ret = append(ret, &model.Rank{ +// SnId: np.PlayerData.SnId, +// Name: np.PlayerData.Name, +// Head: np.PlayerData.Head, +// VIP: np.PlayerData.VIP, +// TotalCoin: np.PlayerData.CoinExchangeTotal, +// }) +// } +// } +// +// return ret +//} + +//func (this *PlayerMgr) GetProfitLists(platform string) []*model.Rank { +// ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT) +// l := list.New() +// +// for _, player := range this.players { +// if player.IsRob { +// continue +// } +// +// if player.PlayerData.Platform != platform { +// continue +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// if PlayerRankGe(player, np, 3) { +// l.InsertBefore(player, n) +// goto CHECK +// } +// } +// } +// +// l.PushBack(player) +// CHECK: +// if l.Len() > model.MAX_RANK_COUNT { +// l.Remove(l.Back()) +// } +// } +// +// for n := l.Front(); n != nil; n = n.Next() { +// if np, ok := n.Value.(*Player); ok { +// ret = append(ret, &model.Rank{ +// SnId: np.PlayerData.SnId, +// Name: np.PlayerData.Name, +// Head: np.PlayerData.Head, +// VIP: np.PlayerData.VIP, +// //TotalCoin: np.PlayerData.ProfitCoin, +// }) +// } +// } +// +// return ret +//} + +//func (this *PlayerMgr) DeletePlayerByPlatform(platform string) { +// var dels []*Player +// for _, p := range this.players { +// if p != nil && p.Platform == platform { +// p.Kickout(common.KickReason_Disconnection) +// dels = append(dels, p) +// } +// } +// +// for _, p := range dels { +// if p != nil { +// p.isDelete = true +// if p.scene == nil { +// this.DelPlayer(p.SnId) +// } +// } +// } +//} + +func (this *PlayerMgr) StatsOnline() model.PlayerOLStats { + stats := model.PlayerOLStats{ + PlatformStats: make(map[string]*model.PlayerStats), + RobotStats: model.PlayerStats{ + InGameCnt: make(map[int32]map[int32]int32), + }, + } + + for _, p := range this.sidMap { + if p != nil { + if p.IsRob { + pps := &stats.RobotStats + if pps != nil { + if p.scene == nil { + pps.InHallCnt++ + } else { + if g, exist := pps.InGameCnt[int32(p.scene.gameId)]; exist { + g[p.scene.dbGameFree.GetId()]++ + } else { + g := make(map[int32]int32) + pps.InGameCnt[int32(p.scene.gameId)] = g + g[p.scene.dbGameFree.GetId()]++ + } + } + } + } else { + var pps *model.PlayerStats + var exist bool + if pps, exist = stats.PlatformStats[p.Platform]; !exist { + pps = &model.PlayerStats{InGameCnt: make(map[int32]map[int32]int32)} + stats.PlatformStats[p.Platform] = pps + } + + if pps != nil { + if p.scene == nil { + pps.InHallCnt++ + } else { + if g, exist := pps.InGameCnt[int32(p.scene.gameId)]; exist { + g[p.scene.dbGameFree.GetId()]++ + } else { + g := make(map[int32]int32) + pps.InGameCnt[int32(p.scene.gameId)] = g + g[p.scene.dbGameFree.GetId()]++ + } + } + } + } + } + } + return stats +} + +func (p *PlayerMgr) UpdateName(snId int32, name string) { + player := p.GetPlayerBySnId(snId) + if player == nil { + return + } + player.setName(name) + player.dirty = true +} + +func (p *PlayerMgr) UpdateHead(snId, head int32) { + player := p.GetPlayerBySnId(snId) + if player == nil { + return + } + player.Head = head + //0:男 1:女 + player.Sex = (player.Head%2 + 1) % 2 + player.dirty = true + player.changeIconTime = time.Now() +} + +func (p *PlayerMgr) UpdateHeadOutline(snId, outline int32) { + player := p.GetPlayerBySnId(snId) + if player == nil { + return + } + player.HeadOutLine = outline + player.dirty = true +} + +func (p *PlayerMgr) UpdateHeadUrl(snId int32, url string) { + player := p.GetPlayerBySnId(snId) + if player == nil { + return + } + if player.HeadUrl != url { + player.HeadUrl = url + player.dirty = true + } +} + +//func (p *PlayerMgr) ModifyActSwitchToPlayer(platform string, modify bool) { +// if modify { //活动开关修改了才去更新活动开关 +// if players, ok := p.playerOfPlatform[platform]; ok { +// for _, p := range players { +// if p != nil && !p.IsRob { +// p.ModifyActSwitch() +// } +// } +// } +// } +//} + +/* +推荐好友规则 +1.优先判断在线玩家人数N + + (1)N≥20;每次刷新,从在线玩家中随机6个 + (2)N<20;则填充机器人,保证N=20,每次填充的机器人头像和昵称随机;然后从N中随机6个 + +2.刷新有CD(暂定20s),刷新过后进入cd +*/ + +type RecommendFriend struct { + Snid int32 + Name string + Head int32 + HeadUrl string + RoleId int32 +} + +// RecommendFriendRule 推荐好友 +func (this *PlayerMgr) RecommendFriendRule(platform string, snid int32) []RecommendFriend { + if platform == "" { + return nil + } else { + rets := []RecommendFriend{} + players := this.playerOfPlatform[platform] + for _, player := range players { //优先真人 + if player.SnId != snid && !FriendMgrSington.IsFriend(platform, snid, player.SnId) { + roleId := common.DefaultRoleId + if player.Roles != nil { + roleId = int(player.Roles.ModId) + } + ret := RecommendFriend{ + Snid: player.SnId, + Name: player.Name, + Head: player.Head, + HeadUrl: player.HeadUrl, + RoleId: int32(roleId), + } + rets = append(rets, ret) + if len(rets) >= 20 { + break + } + } + } + if len(rets) < 20 { + for _, player := range this.snidMap { //其次机器人 + if player.IsRob { + roleId := common.DefaultRoleId + if player.Roles != nil { + roleId = int(player.Roles.ModId) + } + ret := RecommendFriend{ + Snid: player.SnId, + Name: player.Name, + Head: player.Head, + HeadUrl: player.HeadUrl, + RoleId: int32(roleId), + } + rets = append(rets, ret) + if len(rets) >= 20 { + break + } + } + } + } + needIdxs := []int{} + if rets != nil { + if len(rets) >= 6 { + for { + if len(needIdxs) >= 6 { + break + } + randIdx := rand.Intn(len(rets)) + if !common.InSliceInt(needIdxs, randIdx) { + needIdxs = append(needIdxs, randIdx) + } + } + } else { + for i := 0; i < len(rets); i++ { + needIdxs = append(needIdxs, i) + } + } + } + ret := []RecommendFriend{} + for _, idx := range needIdxs { + ret = append(ret, rets[idx]) + } + return ret + } +} + +func init() { + //BlackListMgrSington.RegisterObserver(PlayerMgrSington) + PlayerSubjectSign.AttachName(PlayerMgrSington) + PlayerSubjectSign.AttachHead(PlayerMgrSington) + PlayerSubjectSign.AttachHeadOutline(PlayerMgrSington) + PlayerSubjectSign.AttachHeadUrl(PlayerMgrSington) + + PlayerSubjectSign.AttachName(FriendMgrSington) + PlayerSubjectSign.AttachHead(FriendMgrSington) + + // 定时器 + ClockMgrSington.RegisteSinker(PlayerMgrSington) +} diff --git a/worldsrv/playeronline.go b/worldsrv/playeronline.go new file mode 100644 index 0000000..61e1ad3 --- /dev/null +++ b/worldsrv/playeronline.go @@ -0,0 +1,58 @@ +package main + +import ( + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/module" + "time" +) + +var PlayerOnlineSington = &PlayerOnlineEvent{ + Online: make(map[string]int), +} + +type PlayerOnlineEvent struct { + Online map[string]int + Check bool +} + +func (p *PlayerOnlineEvent) ModuleName() string { + return "PlayerOnlineEvent" +} + +func (p *PlayerOnlineEvent) Init() { +} + +// 每五秒钟统计一次在线数据 +// 没有登录,登出,掉线情况直接不统计 +func (p *PlayerOnlineEvent) Update() { + if !p.Check { + return + } + p.Check = false + m := map[string]int{} + for _, player := range PlayerMgrSington.sidMap { + if player != nil && !player.IsRob && player.IsOnLine() { + m[player.Platform] = m[player.Platform] + 1 + } + } + if len(m) == len(p.Online) { + for k, v := range m { + if p.Online[k] != v { + goto here + } + } + return + } + +here: + p.Online = m + LogChannelSingleton.WriteMQData(model.GenerateOnline(p.Online)) +} + +func (p *PlayerOnlineEvent) Shutdown() { + module.UnregisteModule(p) +} + +func init() { + module.RegisteModule(PlayerOnlineSington, 5*time.Second, 0) +} diff --git a/worldsrv/playersingleadjust.go b/worldsrv/playersingleadjust.go new file mode 100644 index 0000000..e59a3ad --- /dev/null +++ b/worldsrv/playersingleadjust.go @@ -0,0 +1,273 @@ +package main + +import ( + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/model" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "time" +) + +type PlayerSingleAdjustManager struct { + AdjustData map[uint64]*model.PlayerSingleAdjust + dirtyList map[uint64]bool + cacheDirtyList map[uint64]bool //缓存待删除数据 +} + +var PlayerSingleAdjustMgr = &PlayerSingleAdjustManager{ + AdjustData: make(map[uint64]*model.PlayerSingleAdjust), + dirtyList: make(map[uint64]bool), + cacheDirtyList: make(map[uint64]bool), +} + +func (this *PlayerSingleAdjustManager) WebData(msg *webapi.ASSinglePlayerAdjust, p *Player) (sa *webapi.PlayerSingleAdjust) { + psa := model.WebSingleAdjustToModel(msg.PlayerSingleAdjust) + switch msg.Opration { + case 1: + this.AddNewSingleAdjust(psa) + case 2: + this.EditSingleAdjust(psa) + case 3: + this.DeleteSingleAdjust(psa.Platform, psa.SnId, psa.GameFreeId) + case 4: + sa = this.WebGetSingleAdjust(psa.Platform, psa.SnId, psa.GameFreeId) + return + } + //同步到游服 + if p != nil { + if p.scene != nil && p.scene.dbGameFree.Id == psa.GameFreeId { + gss := GameSessMgrSington.GetGameServerSess(int(psa.GameId)) + pack := &server.WGSingleAdjust{ + SceneId: int32(p.scene.sceneId), + Option: msg.Opration, + PlayerSingleAdjust: model.MarshalSingleAdjust(psa), + } + for _, gs := range gss { + gs.Send(int(server.SSPacketID_PACKET_WG_SINGLEADJUST), pack) + } + } + if p.miniScene != nil { + for _, game := range p.miniScene { + if game.dbGameFree.Id == psa.GameFreeId { + gss := GameSessMgrSington.GetGameServerSess(int(psa.GameId)) + pack := &server.WGSingleAdjust{ + SceneId: int32(game.sceneId), + Option: msg.Opration, + PlayerSingleAdjust: model.MarshalSingleAdjust(psa), + } + for _, gs := range gss { + gs.Send(int(server.SSPacketID_PACKET_WG_SINGLEADJUST), pack) + } + break + } + } + } + } + return +} + +func (this *PlayerSingleAdjustManager) IsSingleAdjustPlayer(snid int32, gameFreeId int32) (*model.PlayerSingleAdjust, bool) { + key := uint64(snid)<<32 + uint64(gameFreeId) + if data, ok := this.AdjustData[key]; ok { + if data.CurTime < data.TotalTime { + return data, true + } + } + return nil, false +} +func (this *PlayerSingleAdjustManager) AddAdjustCount(snid int32, gameFreeId int32) { + key := uint64(snid)<<32 + uint64(gameFreeId) + if ad, ok := this.AdjustData[key]; ok { + ad.CurTime++ + this.dirtyList[key] = true + } +} +func (this *PlayerSingleAdjustManager) GetSingleAdjust(platform string, snid, gameFreeId int32) *model.PlayerSingleAdjust { + key := uint64(snid)<<32 + uint64(gameFreeId) + if psa, ok := this.AdjustData[key]; ok { + return psa + } + return nil +} +func (this *PlayerSingleAdjustManager) WebGetSingleAdjust(platform string, snid, gameFreeId int32) *webapi.PlayerSingleAdjust { + key := uint64(snid)<<32 + uint64(gameFreeId) + if psa, ok := this.AdjustData[key]; ok { + return &webapi.PlayerSingleAdjust{ + Id: psa.Id.Hex(), + Platform: psa.Platform, + GameFreeId: psa.GameFreeId, + SnId: psa.SnId, + Mode: psa.Mode, + TotalTime: psa.TotalTime, + CurTime: psa.CurTime, + BetMin: psa.BetMin, + BetMax: psa.BetMax, + BankerLoseMin: psa.BankerLoseMin, + BankerWinMin: psa.BankerWinMin, + CardMin: psa.CardMin, + CardMax: psa.CardMax, + Priority: psa.Priority, + WinRate: psa.WinRate, + GameId: psa.GameId, + GameMode: psa.GameMode, + Operator: psa.Operator, + CreateTime: psa.CreateTime, + UpdateTime: psa.UpdateTime, + } + } + return nil +} +func (this *PlayerSingleAdjustManager) AddNewSingleAdjust(psa *model.PlayerSingleAdjust) *model.PlayerSingleAdjust { + if psa != nil { + key := uint64(psa.SnId)<<32 + uint64(psa.GameFreeId) + psa.Id = bson.NewObjectId() + psa.CreateTime = time.Now().Unix() + psa.UpdateTime = time.Now().Unix() + + this.AdjustData[key] = psa + logger.Logger.Trace("SinglePlayerAdjust new:", psa) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.AddNewSingleAdjust(psa) + }), nil, "AddNewSingleAdjust").StartByFixExecutor("AddNewSingleAdjust") + } + return psa +} +func (this *PlayerSingleAdjustManager) EditSingleAdjust(psa *model.PlayerSingleAdjust) { + if psa != nil { + var inGame bool + psa.UpdateTime = time.Now().Unix() + for key, value := range this.AdjustData { + if value.Id == psa.Id { + var tempKey = key + if psa.GameFreeId != value.GameFreeId { + delete(this.AdjustData, key) + delete(this.dirtyList, key) + tempKey = uint64(psa.SnId)<<32 + uint64(psa.GameFreeId) + } + this.AdjustData[tempKey] = psa + this.dirtyList[tempKey] = true + inGame = true + break + } + } + logger.Logger.Trace("SinglePlayerAdjust edit:", *psa) + if !inGame { + //不在游戏 直接更新库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.EditSingleAdjust(psa) + }), nil, "EditSingleAdjust").StartByFixExecutor("EditSingleAdjust") + } + } +} +func (this *PlayerSingleAdjustManager) DeleteSingleAdjust(platform string, snid, gameFreeId int32) { + key := uint64(snid)<<32 + uint64(gameFreeId) + if _, ok := this.AdjustData[key]; ok { + delete(this.AdjustData, key) + delete(this.dirtyList, key) + } + logger.Logger.Trace("SinglePlayerAdjust delete:", snid, gameFreeId) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.DeleteSingleAdjust(&model.PlayerSingleAdjust{SnId: snid, GameFreeId: gameFreeId, Platform: platform}) + }), nil, "DeleteSingleAdjust").Start() +} + +func (this *PlayerSingleAdjustManager) ModuleName() string { + return "PlayerSingleAdjustManager" +} +func (this *PlayerSingleAdjustManager) Init() { + //data, err := model.QueryAllSingleAdjust("1") + //if err != nil { + // logger.Logger.Warn("QueryAllSingleAdjust is err:", err) + // return + //} + //if len(data) > 0 { + // for _, psa := range data { + // _, gameType := srvdata.DataMgr.GetGameFreeIds(psa.GameId, psa.GameMode) + // if gameType != common.GameType_Mini { + // key := uint64(psa.SnId)<<32 + uint64(psa.GameFreeId) + // this.AdjustData[key] = psa + // } + // } + //} +} + +// 登录加载 +func (this *PlayerSingleAdjustManager) LoadSingleAdjustData(platform string, snid int32) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryAllSingleAdjustByKey(platform, snid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if data != nil { + ret := data.([]*model.PlayerSingleAdjust) + for _, psa := range ret { + key := uint64(psa.SnId)<<32 + uint64(psa.GameFreeId) + this.AdjustData[key] = psa + } + } + })).StartByFixExecutor("LoadPlayerSingleAdjust") +} + +// 掉线删除 +func (this *PlayerSingleAdjustManager) DelPlayerData(platform string, snid int32) { + for _, psa := range this.AdjustData { + if psa.Platform == platform && psa.SnId == snid { + key := uint64(psa.SnId)<<32 + uint64(psa.GameFreeId) + if this.dirtyList[key] { + this.cacheDirtyList[key] = true + } else { + delete(this.AdjustData, key) + } + } + } +} +func (this *PlayerSingleAdjustManager) Update() { + if len(this.dirtyList) == 0 { + return + } + var syncArr []*model.PlayerSingleAdjust + for key, _ := range this.dirtyList { + syncArr = append(syncArr, this.AdjustData[key]) + delete(this.dirtyList, key) + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var saveArr [2][]uint64 + for _, value := range syncArr { + err := model.EditSingleAdjust(value) + if err != nil { + logger.Logger.Error("PlayerSingleAdjustManager edit ", err) + saveArr[0] = append(saveArr[0], uint64(value.SnId)<<32+uint64(value.GameFreeId)) + } else { + saveArr[1] = append(saveArr[1], uint64(value.SnId)<<32+uint64(value.GameFreeId)) + } + } + return saveArr + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + if saveArr, ok := data.([2][]uint64); ok { + //失败处理 + for _, key := range saveArr[0] { + this.dirtyList[key] = true + } + //成功处理 + for _, key := range saveArr[1] { + if this.cacheDirtyList[key] { + delete(this.cacheDirtyList, key) + delete(this.AdjustData, key) + } + } + } + return + })).StartByFixExecutor("PlayerSingleAdjustManager") +} +func (this *PlayerSingleAdjustManager) Shutdown() { + module.UnregisteModule(this) +} +func init() { + module.RegisteModule(PlayerSingleAdjustMgr, time.Minute*5, 0) +} diff --git a/worldsrv/playersubject.go b/worldsrv/playersubject.go new file mode 100644 index 0000000..fc0f31d --- /dev/null +++ b/worldsrv/playersubject.go @@ -0,0 +1,107 @@ +package main + +import "container/list" + +// IHead 更新头像 +type IHead interface { + UpdateHead(snId, head int32) +} + +// IName 更新昵称 +type IName interface { + UpdateName(snId int32, name string) +} + +// IHeadOutline 更新头像框 +type IHeadOutline interface { + UpdateHeadOutline(snId, outline int32) +} + +// IHeadUrl 更新头像url +type IHeadUrl interface { + UpdateHeadUrl(snId int32, url string) +} + +// PlayerSubject 观察者 +type PlayerSubject struct { + headList *list.List + nameList *list.List + outlineList *list.List + headUrl *list.List +} + +func (u *PlayerSubject) UpdateHead(snId int32, head int32) { + for e := u.headList.Front(); e != nil; e = e.Next() { + if o, ok := e.Value.(IHead); ok { + o.UpdateHead(snId, head) + } + } +} + +func (u *PlayerSubject) AttachHead(obj IHead) { + for e := u.headList.Front(); e != nil; e = e.Next() { + if e.Value == obj { + return + } + } + u.headList.PushBack(obj) +} + +func (u *PlayerSubject) UpdateName(snId int32, name string) { + for e := u.nameList.Front(); e != nil; e = e.Next() { + if o, ok := e.Value.(IName); ok { + o.UpdateName(snId, name) + } + } +} + +func (u *PlayerSubject) AttachName(obj IName) { + for e := u.nameList.Front(); e != nil; e = e.Next() { + if e.Value == obj { + return + } + } + u.nameList.PushBack(obj) +} + +func (u *PlayerSubject) UpdateHeadOutline(snId, outline int32) { + for e := u.outlineList.Front(); e != nil; e = e.Next() { + if o, ok := e.Value.(IHeadOutline); ok { + o.UpdateHeadOutline(snId, outline) + } + } +} + +func (u *PlayerSubject) AttachHeadOutline(obj IHeadOutline) { + for e := u.outlineList.Front(); e != nil; e = e.Next() { + if e.Value == obj { + return + } + } + u.outlineList.PushBack(obj) +} + +func (u *PlayerSubject) UpdateHeadUrl(snId int32, url string) { + for e := u.headUrl.Front(); e != nil; e = e.Next() { + if o, ok := e.Value.(IHeadUrl); ok { + o.UpdateHeadUrl(snId, url) + } + } +} + +func (u *PlayerSubject) AttachHeadUrl(obj IHeadUrl) { + for e := u.headUrl.Front(); e != nil; e = e.Next() { + if e.Value == obj { + return + } + } + u.headUrl.PushBack(obj) +} + +// 单例 +var PlayerSubjectSign = &PlayerSubject{ + headList: list.New(), + nameList: list.New(), + outlineList: list.New(), + headUrl: list.New(), +} diff --git a/worldsrv/playgamenum.go b/worldsrv/playgamenum.go new file mode 100644 index 0000000..7e26726 --- /dev/null +++ b/worldsrv/playgamenum.go @@ -0,0 +1,47 @@ +package main + +import ( + "mongo.games.com/game/common" + //"mongo.games.com/game/gamerule/blackjack" + //"mongo.games.com/game/gamerule/dezhoupoker" + //"mongo.games.com/game/gamerule/fivecardstud" + //"mongo.games.com/game/gamerule/omahapoker" +) + +var minPlayGameNum = map[int]int{ + common.GameId_TenHalf: 2, + common.GameId_DezhouPoker: 2, + common.GameId_FiveCardStud: 2, + common.GameId_BlackJack: 1, + //common.GameId_OmahaPoker: omahapoker.MinNumOfPlayer, +} + +var maxPlayGameNum = map[int]int{ + //common.GameId_DezhouPoker: int(dezhoupoker.MaxNumOfPlayer), + //common.GameId_FiveCardStud: int(fivecardstud.MaxNumOfPlayer), + //common.GameId_BlackJack: blackjack.MaxPlayer, + //common.GameId_OmahaPoker: omahapoker.MaxNumOfPlayer, +} + +func GetGameStartMinNum(gameid int) int { + return minPlayGameNum[gameid] +} +func GetGameSuiableNum(gameid int, flag int32) int { + minNum, maxNum := minPlayGameNum[gameid], maxPlayGameNum[gameid] + if flag == MatchTrueManForbid { + if minNum == maxNum { + return minNum + } else { + return maxNum - 1 + } + } else { + if minNum == maxNum { + return minNum + } else { + return maxNum - 2 + } + } +} +func IsRegularNum(gameid int) bool { + return minPlayGameNum[gameid] == maxPlayGameNum[gameid] +} diff --git a/worldsrv/privatescene.go b/worldsrv/privatescene.go new file mode 100644 index 0000000..d4d62c7 --- /dev/null +++ b/worldsrv/privatescene.go @@ -0,0 +1,287 @@ +package main + +import ( + "container/list" + "fmt" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + "strconv" + "time" +) + +const ( + PrivateSceneState_Deleting = iota //删除中 + PrivateSceneState_Deleted //已删除 +) + +var PrivateSceneMgrSington = &PrivateSceneMgr{ + pps: make(map[int32]*PlayerPrivateScene), +} + +type PlayerPrivateScene struct { + snid int32 // 玩家id + creatorName string //创建人昵称 + platform string // 平台名称 + channel string // 渠道名称 + promoter string // 推广员 + packageTag string // 推广包标识 + scenes map[int]*Scene + logsByDay map[int]*list.List + dupLog map[string]struct{} + loaded bool +} + +func (pps *PlayerPrivateScene) AddScene(s *Scene) { + pps.scenes[s.sceneId] = s +} + +func (pps *PlayerPrivateScene) GetScene(sceneId int) *Scene { + if s, exist := pps.scenes[sceneId]; exist { + return s + } + return nil +} + +func (pps *PlayerPrivateScene) GetCount() int { + return len(pps.scenes) +} + +func (pps *PlayerPrivateScene) CanDelete() bool { + return !pps.loaded && len(pps.scenes) == 0 +} + +func (pps *PlayerPrivateScene) OnPlayerLogin(p *Player) { + +} + +func (pps *PlayerPrivateScene) OnPlayerLogout(p *Player) { + pps.logsByDay = nil + pps.loaded = false +} + +func (pps *PlayerPrivateScene) OnCreateScene(p *Player, s *Scene) { + pps.scenes[s.sceneId] = s +} + +func (pps *PlayerPrivateScene) LoadLogs(p *Player, yyyymmdd int32) { + if !pps.loaded { + var logs []*model.PrivateSceneLog + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + logs, err = model.GetPrivateSceneLogBySnId(p.Platform, p.SnId, model.GameParamData.PrivateSceneLogLimit) + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if err == nil { + pps.loaded = true + pps.TidyLog(logs) + pps.SendLogs(p, yyyymmdd) + } + }), "GetPrivateSceneLogBySnId").Start() + } else { + pps.SendLogs(p, yyyymmdd) + } +} + +func (pps *PlayerPrivateScene) TidyLog(logs []*model.PrivateSceneLog) { + if pps.logsByDay == nil { + pps.logsByDay = make(map[int]*list.List) + } + + for _, log := range logs { + if _, exist := pps.dupLog[log.LogId.Hex()]; exist { + continue + } + y, m, d := log.CreateTime.Date() + day := y*10000 + int(m)*100 + d + if lst, exist := pps.logsByDay[day]; exist { + lst.PushBack(log) + } else { + lst = list.New() + pps.logsByDay[day] = lst + lst.PushBack(log) + } + } + pps.dupLog = nil +} + +func (pps *PlayerPrivateScene) SendLogs(p *Player, yyyymmdd int32) { + pack := &hall_proto.SCGetPrivateRoomHistory{ + QueryTime: proto.Int32(yyyymmdd), + } + if logs, exist := pps.logsByDay[int(yyyymmdd)]; exist { + for e := logs.Front(); e != nil; e = e.Next() { + if log, ok := e.Value.(*model.PrivateSceneLog); ok { + data := &hall_proto.PrivateRoomHistory{ + GameFreeId: proto.Int32(log.GameFreeId), + RoomId: proto.Int32(log.SceneId), + CreateTime: proto.Int32(int32(log.CreateTime.Unix())), + DestroyTime: proto.Int32(int32(log.DestroyTime.Unix())), + CreateFee: proto.Int32(log.CreateFee), + } + pack.Datas = append(pack.Datas, data) + } + } + } + proto.SetDefaults(pack) + p.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_GETPRIVATEROOMHISTORY), pack) +} + +func (pps *PlayerPrivateScene) PushLog(log *model.PrivateSceneLog) { + if log == nil { + return + } + + y, m, d := log.CreateTime.Date() + day := y*10000 + int(m)*100 + d + if lst, exist := pps.logsByDay[day]; exist { + lst.PushFront(log) + } else { + lst = list.New() + pps.logsByDay[day] = lst + lst.PushFront(log) + } + if !pps.loaded { + pps.dupLog[log.LogId.Hex()] = struct{}{} + } +} + +func (pps *PlayerPrivateScene) SendPrivateScenes(p *Player) { + pack := &hall_proto.SCGetPrivateRoomList{} + for sceneid, s := range pps.scenes { + data := &hall_proto.PrivateRoomInfo{ + GameFreeId: proto.Int32(s.dbGameFree.GetId()), + RoomId: proto.Int(sceneid), + CurrRound: proto.Int32(s.currRound), + MaxRound: proto.Int32(s.totalRound), + CurrNum: proto.Int(len(s.players)), + MaxPlayer: proto.Int(s.playerNum), + CreateTs: proto.Int32(int32(s.createTime.Unix())), + } + pack.Datas = append(pack.Datas, data) + } + proto.SetDefaults(pack) + p.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_GETPRIVATEROOMLIST), pack) +} + +type PrivateSceneMgr struct { + pps map[int32]*PlayerPrivateScene +} + +func (psm *PrivateSceneMgr) GetOrCreatePlayerPrivateScene(p *Player) *PlayerPrivateScene { + snid := p.SnId + if pps, exist := psm.pps[snid]; exist { + return pps + } + + pps := &PlayerPrivateScene{ + snid: snid, + creatorName: p.Name, + platform: p.Platform, + channel: p.Channel, + promoter: strconv.Itoa(int(p.PromoterTree)), + packageTag: p.PackageID, + scenes: make(map[int]*Scene), + logsByDay: make(map[int]*list.List), + dupLog: make(map[string]struct{}), + } + + psm.pps[snid] = pps + return pps +} + +func (psm *PrivateSceneMgr) GetPlayerPrivateScene(snid int32) *PlayerPrivateScene { + if pps, exist := psm.pps[snid]; exist { + return pps + } + return nil +} + +func (psm *PrivateSceneMgr) OnDestroyScene(scene *Scene) { + if scene == nil { + return + } + + if !scene.IsPrivateScene() { + return + } + + pps := psm.GetPlayerPrivateScene(scene.creator) + if pps != nil { + if pps.GetScene(scene.sceneId) == scene { + delete(pps.scenes, scene.sceneId) + var tax int32 + var returnCoin int32 + p := PlayerMgrSington.GetPlayerBySnId(scene.creator) + + if scene.currRound == 0 && !scene.starting && scene.createFee > 0 { //未开始 + if scene.manualDelete && time.Now().Sub(scene.createTime) < time.Second*time.Duration(model.GameParamData.PrivateSceneFreeDistroySec) { //低于指定时间,要扣除部分费用 + tax = scene.createFee * int32(model.GameParamData.PrivateSceneDestroyTax) / 100 + returnCoin = scene.createFee - tax + } else { + returnCoin = scene.createFee + } + if returnCoin > 0 { + if p != nil { + var remark string + if tax > 0 { + remark = fmt.Sprintf("提前解散扣除费用%.02f", float32(tax)/100.0) + } + p.AddCoin(int64(returnCoin), 0, common.GainWay_PrivateSceneReturn, "", remark) + } else { + //TODO 发送邮件 + //sendClubMail_ClubCreateRoomRefund(scene.creator, scene.limitPlatform.Name, int32(scene.sceneId), int64(tax), int64(returnCoin)) + } + } + //if p != nil { + // //统计创建房间数量 + // key := scene.dbGameFree.GetGameDif() + // if gd, ok := p.GameData[key]; ok { + // gd.CreateRoomTimes-- + // } else { + // p.GameData[key] = &model.PlayerGameStatics{ + // CreateRoomTimes: 0, + // } + // } + //} + } + + if p != nil { + pack := &hall_proto.SCDestroyPrivateRoom{ + OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game, + RoomId: proto.Int(scene.sceneId), + State: proto.Int(PrivateSceneState_Deleted), + } + proto.SetDefaults(pack) + p.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_DESTROYPRIVATEROOM), pack) + } + + //写log + log := model.NewPrivateSceneLog() + if log != nil { + log.SnId = pps.snid + log.Platform = pps.platform + log.Channel = pps.channel + log.Promoter = pps.promoter + log.GameFreeId = scene.dbGameFree.GetId() + log.SceneId = int32(scene.sceneId) + log.CreateTime = scene.createTime + log.DestroyTime = time.Now() + if returnCoin > 0 { + log.CreateFee = tax + } else { + log.CreateFee = scene.createFee + } + //PrivateSceneLogChannelSington.Write(log) + pps.PushLog(log) + } + + if pps.CanDelete() && p == nil { + delete(psm.pps, scene.creator) + } + } + } +} diff --git a/worldsrv/promotermgr.go b/worldsrv/promotermgr.go new file mode 100644 index 0000000..15d5ae6 --- /dev/null +++ b/worldsrv/promotermgr.go @@ -0,0 +1,148 @@ +package main + +import ( + "encoding/json" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "strconv" + "time" +) + +// 0 全民 1无限代理 2 渠道 3 推广员 +const ( + PROMOTER_TYPE_ALL = 0 //全民 + PROMOTER_TYPE_INFINITY = 1 //无限代理 + PROMOTER_TYPE_CHANNAL = 2 //渠道 + PROMOTER_TYPE_PROMOTE = 3 //推广员 +) + +var PromoterMgrSington = &PromoterMgr{ + PromoterConfigMap: make(map[string]*PromoterConfig), + LastTicket: 0, +} + +func GetPromoterKeyByInput(key1 string, pType int32) string { + return key1 + "_" + strconv.Itoa(int(pType)) +} + +func GetPromoterKey(promoterTree int32, promoter string, channel string) (string, error) { + var key string + if promoterTree != 0 { + key = GetPromoterKeyByInput(strconv.Itoa(int(promoterTree)), PROMOTER_TYPE_INFINITY) + return key, nil + } else if promoter != "" { + key = GetPromoterKeyByInput(promoter, PROMOTER_TYPE_PROMOTE) + return key, nil + + } else if channel != "" { + key = GetPromoterKeyByInput(channel, PROMOTER_TYPE_CHANNAL) + return key, nil + + } + + return key, &ErrorString{code: "no find key"} +} + +type PromoterMgr struct { + PromoterConfigMap map[string]*PromoterConfig + LastTicket int64 +} + +func (this *PromoterMgr) AddConfig(promoter *PromoterConfig) { + //todo 可能牵涉修改事件之类的在此处理 + this.PromoterConfigMap[promoter.GetKey()] = promoter +} + +func (this *PromoterMgr) RemoveConfig(promoter *PromoterConfig) { + delete(this.PromoterConfigMap, promoter.GetKey()) +} + +func (this *PromoterMgr) RemoveConfigByKey(promoter string) { + delete(this.PromoterConfigMap, promoter) +} + +func (this *PromoterMgr) GetConfig(promoter string) *PromoterConfig { + pc, ok := this.PromoterConfigMap[promoter] + if ok { + return pc + } + return nil +} + +func (this *PromoterConfig) GetKey() string { + key := GetPromoterKeyByInput(this.PromoterID, this.PromoterType) + return key +} + +// ////////////////////////////////////////////////////////////////// +// / Module Implement [beg] +// ////////////////////////////////////////////////////////////////// +func (this *PromoterMgr) ModuleName() string { + return "PromoterMgr" +} + +type PromoterConfig struct { + PromoterID string //代理ID + Platform string //平台 + PromoterType int32 //代理类型 0 全民 1无限代理 2 渠道 3 推广员 + UpgradeAccountGiveCoin int32 //升级账号奖励金币 + NewAccountGiveCoin int32 //新账号奖励金币 + ExchangeTax int32 //兑换税收(万分比) + ExchangeForceTax int32 //强制兑换税收 + ExchangeFlow int32 //兑换流水比例 + ExchangeGiveFlow int32 //赠送兑换流水比例 + ExchangeFlag int32 //兑换标记 + IsInviteRoot int32 //是否绑定全民用户 + +} + +func (this *PromoterMgr) Init() { + this.LastTicket = time.Now().Unix() + + if this.PromoterConfigMap == nil { + this.PromoterConfigMap = make(map[string]*PromoterConfig) + } + + type ApiResult struct { + Tag int + Msg []PromoterConfig + } + + //不使用etcd的情况下走api获取 + if !model.GameParamData.UseEtcd { + buff, err := webapi.API_GetPromoterConfig(common.GetAppId()) + if err == nil { + ar := ApiResult{} + err = json.Unmarshal(buff, &ar) + if err == nil && ar.Tag == 0 { + logger.Logger.Trace("API_GetPromoterConfig response:", string(buff)) + for _, promoterConfig := range ar.Msg { + temp := promoterConfig + this.AddConfig(&temp) + } + } else { + logger.Logger.Error("Unmarshal PromoterMgr data error:", err, " buff:", string(buff)) + } + } else { + logger.Logger.Error("Init PromoterMgr list failed.") + } + } else { + EtcdMgrSington.InitPromoterConfig() + } + +} + +func (this *PromoterMgr) Update() { + +} + +func (this *PromoterMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(PromoterMgrSington, time.Minute, 0) +} diff --git a/worldsrv/qmflowmgr.go b/worldsrv/qmflowmgr.go new file mode 100644 index 0000000..4933aed --- /dev/null +++ b/worldsrv/qmflowmgr.go @@ -0,0 +1,462 @@ +package main + +import ( + "mongo.games.com/game/model" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" + "time" + + "mongo.games.com/goserver/core/module" +) + +type QMFlowManager struct { + BaseClockSinker + playerStatement map[int32]map[int32]*webapi.PlayerStatementSrc + offPlayerStatement map[int32]map[int32]*webapi.PlayerStatementSrc +} + +var QMFlowMgr = &QMFlowManager{} + +func (this *QMFlowManager) ModuleName() string { + return "QMFlowManager" +} + +func (this *QMFlowManager) Init() { + +} + +func (this *QMFlowManager) Update() { + this.Save() +} + +func (this *QMFlowManager) Shutdown() { + this.ForceSave() + + module.UnregisteModule(this) +} + +// 感兴趣所有clock event +func (this *QMFlowManager) InterestClockEvent() int { + return 1 << CLOCK_EVENT_HOUR +} + +func (this *QMFlowManager) OnHourTimer() { + this.AnsySave() +} + +// 增加玩家流水统计 +// 数据用途: 流水返利,全民代理佣金结算,确保参数计算无误 +// totalWin: 总赢取钱(>=0) +// totalLost: 总输的钱(>=0) +func (this *QMFlowManager) AddPlayerStatement(snid, isBind, totalWin, totalLost, gamefreeID int32, platform, + packageId string, dbGameFree *server_proto.DB_GameFree) { + if model.GameParamData.CloseQMThr { + return + } + + if dbGameFree == nil { + return + } + + if totalWin == 0 && totalLost == 0 { + return + } + /* + if dbGameFree.GetPlayerWaterRate() == 0 { + return + } + */ + if this.playerStatement == nil { + this.playerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.playerStatement[gamefreeID]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[snid]; exist { + ps.TotalWin += float64(totalWin*dbGameFree.GetPlayerWaterRate()) / 100 + ps.TotalLose += float64(totalLost*dbGameFree.GetPlayerWaterRate()) / 100 + ps.TotaSrclWin += float64(totalWin) + ps.TotaSrclLose += float64(totalLost) + } else { + ps := &webapi.PlayerStatementSrc{ + Platform: platform, + PackageId: packageId, + SnId: snid, + IsBind: isBind, + TotalWin: float64(totalWin*dbGameFree.GetPlayerWaterRate()) / 100, + TotalLose: float64(totalLost*dbGameFree.GetPlayerWaterRate()) / 100, + TotaSrclWin: float64(totalWin), + TotaSrclLose: float64(totalLost), + } + gpmap[snid] = ps + } + + this.playerStatement[gamefreeID] = gpmap +} + +// 增加玩家流水统计 +// 数据用途: 流水返利,全民代理佣金结算,确保参数计算无误 +// totalWin: 总赢取钱(>=0) +// totalLost: 总输的钱(>=0) +func (this *QMFlowManager) AddOffPlayerStatement(snid, totalWin, totalLost, gamefreeID int32, dbGameFree *server_proto.DB_GameFree) { + if model.GameParamData.CloseQMThr { + return + } + if dbGameFree == nil { + return + } + if totalWin == 0 && totalLost == 0 { + return + } + /* + if dbGameFree.GetPlayerWaterRate() == 0 { + return + } + */ + if this.offPlayerStatement == nil { + this.offPlayerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.offPlayerStatement[gamefreeID]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[snid]; exist { + ps.TotalWin += float64(totalWin*dbGameFree.GetPlayerWaterRate()) / 100 + ps.TotalLose += float64(totalLost*dbGameFree.GetPlayerWaterRate()) / 100 + ps.TotaSrclWin += float64(totalWin) + ps.TotaSrclLose += float64(totalLost) + } else { + ps := &webapi.PlayerStatementSrc{ + SnId: snid, + TotalWin: float64(totalWin*dbGameFree.GetPlayerWaterRate()) / 100, + TotalLose: float64(totalLost*dbGameFree.GetPlayerWaterRate()) / 100, + TotaSrclWin: float64(totalWin), + TotaSrclLose: float64(totalLost), + } + gpmap[snid] = ps + } + + this.offPlayerStatement[gamefreeID] = gpmap +} + +// 异步保存,用户每个小时的定时处理 +func (this *QMFlowManager) AnsySave() { + //先查找一下不在线的玩家,看是否在线,先保存一次 + tempSave := this.offPlayerStatement + this.offPlayerStatement = nil + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + for _, v := range tempSave { + for m, n := range v { + baseInfo := model.GetPlayerBaseInfo(n.Platform, m) + if baseInfo != nil { + n.Platform = baseInfo.Platform + n.PackageId = baseInfo.PackageID + isBind := int32(0) + if baseInfo.Tel != "" { + isBind = 1 + } + n.IsBind = isBind + } + } + } + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + for k, v := range tempSave { + for m, n := range v { + if n.Platform != "" { + isQuMin := false + //pt := PlatformMgrSingleton.GetPackageTag(n.PackageId) + //if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + //} + if !isQuMin && model.GameParamData.QMOptimization { + //不是全民包,不发送对应的数据 + delete(v, m) + continue + } + if this.playerStatement == nil { + this.playerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.playerStatement[k]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[m]; exist { + ps.TotalWin += n.TotalWin + ps.TotalLose += n.TotalLose + ps.TotaSrclWin += n.TotaSrclWin + ps.TotaSrclLose += n.TotaSrclLose + } else { + ps := &webapi.PlayerStatementSrc{ + SnId: n.SnId, + TotalWin: n.TotalWin, + TotalLose: n.TotalLose, + TotaSrclWin: n.TotaSrclWin, + TotaSrclLose: n.TotaSrclLose, + PackageId: n.PackageId, + Platform: n.Platform, + IsBind: n.IsBind, + } + gpmap[m] = ps + } + + this.playerStatement[k] = gpmap + delete(v, m) + } else { + //没有找到对应的用户信息,可能是数据库出现问题,重新插入数据 + logger.Logger.Errorf("save player qm err:%v,%v,%v,%v,%v,%v", n.SnId, n.TotaSrclLose, n.TotaSrclWin, + n.TotalLose, n.TotalWin, m) + if this.offPlayerStatement == nil { + this.offPlayerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.offPlayerStatement[k]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[m]; exist { + ps.TotalWin += n.TotalWin + ps.TotalLose += n.TotalLose + ps.TotaSrclWin += n.TotaSrclWin + ps.TotaSrclLose += n.TotaSrclLose + } else { + ps := &webapi.PlayerStatementSrc{ + SnId: n.SnId, + TotalWin: n.TotalWin, + TotalLose: n.TotalLose, + TotaSrclWin: n.TotaSrclWin, + TotaSrclLose: n.TotaSrclLose, + PackageId: n.PackageId, + Platform: n.Platform, + IsBind: n.IsBind, + } + gpmap[m] = ps + } + + this.offPlayerStatement[k] = gpmap + } + } + } + }), "QMFlowManagerAnsySave").Start() +} + +// 强制保存,在关闭时调用 +func (this *QMFlowManager) ForceSave() { + //先查找一下不在线的玩家,看是否在线,先保存一次 + tempSave := this.offPlayerStatement + this.offPlayerStatement = nil + for _, v := range tempSave { + for m, n := range v { + baseInfo := model.GetPlayerBaseInfo(n.Platform, m) + if baseInfo != nil { + n.Platform = baseInfo.Platform + n.PackageId = baseInfo.PackageID + isBind := int32(0) + if baseInfo.Tel != "" { + isBind = 1 + } + n.IsBind = isBind + } + } + } + + for k, v := range tempSave { + for m, n := range v { + if n.Platform != "" { + isQuMin := false + //pt := PlatformMgrSingleton.GetPackageTag(n.PackageId) + //if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + //} + if !isQuMin && model.GameParamData.QMOptimization { + //不是全民包,不发送对应的数据 + delete(v, m) + continue + } + if this.playerStatement == nil { + this.playerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.playerStatement[k]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[m]; exist { + ps.TotalWin += n.TotalWin + ps.TotalLose += n.TotalLose + ps.TotaSrclWin += n.TotaSrclWin + ps.TotaSrclLose += n.TotaSrclLose + } else { + ps := &webapi.PlayerStatementSrc{ + SnId: n.SnId, + TotalWin: n.TotalWin, + TotalLose: n.TotalLose, + TotaSrclWin: n.TotaSrclWin, + TotaSrclLose: n.TotaSrclLose, + PackageId: n.PackageId, + Platform: n.Platform, + IsBind: n.IsBind, + } + gpmap[m] = ps + } + + this.playerStatement[k] = gpmap + delete(v, m) + } else { + //没有找到对应的用户信息,可能是数据库出现问题,重新插入数据 + logger.Logger.Errorf("save player qm err:%v,%v,%v,%v,%v,%v", n.SnId, n.TotaSrclLose, n.TotaSrclWin, + n.TotalLose, n.TotalWin, m) + } + } + } + + logger.Logger.Tracef("ready save flow record:%v", len(this.playerStatement)) + //for gamefreeId, info := range this.playerStatement { + // var datas []*webapi.PlayerStatement + // + // for _, ps := range info { + // + // tps := &webapi.PlayerStatement{ + // Platform: ps.Platform, + // IsBind: ps.IsBind, + // PackageId: ps.PackageId, + // TotalWin: int32(ps.TotalWin), + // TotalLose: int32(ps.TotalLose), + // TotaSrclLose: int32(ps.TotaSrclLose), + // TotaSrclWin: int32(ps.TotaSrclWin), + // SnId: ps.SnId, + // } + // datas = append(datas, tps) + // } + // + // QPT := int(model.GameParamData.SpreadAccountQPT) + // //每X条一次,避免GET请求头超长 + // for len(datas) >= QPT { + // d := datas[:QPT] + // LogChannelSingleton.WriteMQData(model.GenerateSpreadAccount(common.GetAppId(), gamefreeId, d)) + // } + // + // if len(datas) != 0 { + // d := datas[:] + // LogChannelSingleton.WriteMQData(model.GenerateSpreadAccount(common.GetAppId(), gamefreeId, d)) + // } + //} + logger.Logger.Tracef("save all qmflow ok") + this.playerStatement = nil +} + +func (this *QMFlowManager) Save() { + //先查找一下不在线的玩家,看是否在线,先保存一次 + for k, v := range this.offPlayerStatement { + for m, n := range v { + player := PlayerMgrSington.GetPlayerBySnId(m) + if player != nil { + isQuMin := false + //pt := PlatformMgrSingleton.GetPackageTag(player.PackageID) + //if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + //} + if !isQuMin && model.GameParamData.QMOptimization { + //不是全民包,不发送对应的数据 + delete(v, m) + continue + } + + isBind := int32(0) + if player.Tel != "" { + isBind = 1 + } + if this.playerStatement == nil { + this.playerStatement = make(map[int32]map[int32]*webapi.PlayerStatementSrc) + } + var gpmap map[int32]*webapi.PlayerStatementSrc + if gp, exist := this.playerStatement[k]; exist { + gpmap = gp + } else { + gpmap = make(map[int32]*webapi.PlayerStatementSrc) + } + + if ps, exist := gpmap[m]; exist { + ps.TotalWin += n.TotalWin + ps.TotalLose += n.TotalLose + ps.TotaSrclWin += n.TotaSrclWin + ps.TotaSrclLose += n.TotaSrclLose + } else { + ps := &webapi.PlayerStatementSrc{ + SnId: n.SnId, + TotalWin: n.TotalWin, + TotalLose: n.TotalLose, + TotaSrclWin: n.TotaSrclWin, + TotaSrclLose: n.TotaSrclLose, + PackageId: player.PackageID, + Platform: player.Platform, + IsBind: isBind, + } + gpmap[m] = ps + } + + this.playerStatement[k] = gpmap + delete(v, m) + } + } + } + + //for gamefreeId, info := range this.playerStatement { + // var datas []*webapi.PlayerStatement + // + // for _, ps := range info { + // + // tps := &webapi.PlayerStatement{ + // Platform: ps.Platform, + // IsBind: ps.IsBind, + // PackageId: ps.PackageId, + // TotalWin: int32(ps.TotalWin), + // TotalLose: int32(ps.TotalLose), + // TotaSrclLose: int32(ps.TotaSrclLose), + // TotaSrclWin: int32(ps.TotaSrclWin), + // SnId: ps.SnId, + // } + // datas = append(datas, tps) + // + // } + // + // QPT := int(model.GameParamData.SpreadAccountQPT) + // //每X条一次,避免GET请求头超长 + // for len(datas) >= QPT { + // d := datas[:QPT] + // LogChannelSingleton.WriteMQData(model.GenerateSpreadAccount(common.GetAppId(), gamefreeId, d)) + // datas = datas[QPT:] + // } + // + // //把剩下的记录在推一遍 + // if len(datas) != 0 { + // d := datas[:] + // LogChannelSingleton.WriteMQData(model.GenerateSpreadAccount(common.GetAppId(), gamefreeId, d)) + // } + //} + + this.playerStatement = nil +} + +func init() { + module.RegisteModule(QMFlowMgr, time.Minute*3, 0) + ClockMgrSington.RegisteSinker(QMFlowMgr) +} diff --git a/worldsrv/rankmatch.go b/worldsrv/rankmatch.go new file mode 100644 index 0000000..137adcc --- /dev/null +++ b/worldsrv/rankmatch.go @@ -0,0 +1,749 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "sort" + "strings" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/model" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/rankmatch" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/worldsrv/internal" +) + +var RankMgrSingleton = &RankMatchMgr{ + seasons: make(map[string]*RankSeasonId), + playerSeasons: make(map[int32]*PlayerRankSeason), +} + +// RankSeasonId 赛季配置 +type RankSeasonId struct { + Dirty bool + *model.RankSeasonId +} + +// PlayerRankSeason 玩家排位信息 +type PlayerRankSeason struct { + Dirty bool + *model.PlayerRankSeason +} + +func (this *PlayerRankSeason) sendEmailAward(rankType int32) { + lv, _ := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId) + + for _, item := range RankMgrSingleton.GetRankAwardList(rankType) { + if lv >= item.Lv { + var has bool + for _, v := range this.RankType[rankType].Awards { + if v.Id == item.Id { + has = true + break + } + } + if !has { + this.RankType[rankType].Awards = append(this.RankType[rankType].Awards, &model.RankAward{ + Id: item.Id, + Ts: time.Now().Unix(), + }) + + var coin int64 + var diamond int64 + var otherParams []int32 + for _, v := range item.Awards { + switch v.Id { + case 1: // 金币 + coin = int64(v.Num) + case 2: // 钻石 + diamond = int64(v.Num) + default: + // 道具 + otherParams = []int32{v.Id, v.Num} + } + } + + // 邮件发送奖励 + var newMsg *model.Message + index := item.Id + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var rankName []string + for _, v := range []string{"zh", "vi", "en", "kh"} { + rankName = append(rankName, i18n.Tr(v, fmt.Sprintf("RankReward_%d_%d", rankType, index))) + } + title := i18n.Tr("languages", "RankAwardTitle", rankName) + content := i18n.Tr("languages", "RankAward") + + newMsg = model.NewMessage("", 0, "", this.SnId, model.MSGTYPE_RANK_REWARD, + title, content, coin, diamond, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", otherParams, this.Platform, model.HallTienlen) + + return model.InsertMessage(this.Platform, newMsg) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + p := PlayerMgrSington.GetPlayerBySnId(this.SnId) + if p != nil { + p.AddMessage(newMsg) + } + } + }), "AddMailByItem").StartByFixExecutor("SendMail") + } + } + } +} + +// CheckNewSeason 检查赛季继承 +func (this *PlayerRankSeason) CheckNewSeason() { + var n int + var f func() + f = func() { + cfg := RankMgrSingleton.GetSeasonConfig(this.Platform) + if cfg.SeasonId > this.SeasonId { + this.Dirty = true + this.SeasonId++ + // 领取的奖励发邮件 + for k := range this.RankType { + this.sendEmailAward(k) + } + + old, err := json.Marshal(this.RankType) + if err != nil { + logger.Logger.Errorf("json.Marshal(this.RankType) err: %v", err) + return + } + this.LastRankType = make(map[int32]*model.PlayerRankInfo) + if err = json.Unmarshal(old, &this.LastRankType); err != nil { + logger.Logger.Errorf("json.Unmarshal(old,&this.LastRankType) err: %v", err) + return + } + for k, v := range this.RankType { + this.RankType[k].Score = RankMgrSingleton.NewSeasonScore(v.Score) + this.RankType[k].Awards = nil + } + + f() + n++ + if n > 1000 { // 防止死循环 + return + } + } + } + f() + + // 通知积分变更 + if this.Dirty { + if p := PlayerMgrSington.GetPlayerBySnId(this.SnId); p != nil { + p.SendRankSeason() + } + for k := range this.RankType { + this.rankLog(k) + this.CheckShowRed(k) + } + } +} + +func (this *PlayerRankSeason) Update(rankScore map[int32]int64) { + this.CheckNewSeason() + + for k, v := range this.RankType { + if v != nil && v.Score != rankScore[k] { + v.Score = rankScore[k] + this.Dirty = true + this.CheckShowRed(k) + this.rankLog(k) + } + } + + for k, v := range rankScore { + info := this.RankType[k] + if info == nil { + info = &model.PlayerRankInfo{ + Score: v, + } + this.RankType[k] = info + this.Dirty = true + this.CheckShowRed(k) + this.rankLog(k) + } + } + + if this.Dirty { + if p := PlayerMgrSington.GetPlayerBySnId(this.SnId); p != nil { + p.SendRankSeason() + } + } +} + +// CheckShowRed 检查是否显示红点 +func (this *PlayerRankSeason) CheckShowRed(rankType int32) { + lv, _ := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId) + info := this.RankType[rankType] + var has bool // 有未领取的 +here: + for _, item := range RankMgrSingleton.GetRankAwardList(rankType) { + if lv >= item.Lv { // 可领取 + if info == nil { + has = true + break + } + if len(info.Awards) == 0 { + has = true + break + } + for _, v := range info.Awards { + if v != nil && v.Id == item.Id { // 领过了 + continue here + } + } + has = true + break + } + } + if has { + p := PlayerMgrSington.GetPlayerBySnId(this.SnId) + if p != nil { + p.SendShowRed(hall_proto.ShowRedCode_RankReward, rankType, 1) + } + } +} + +func (this *PlayerRankSeason) rankLog(rankType int32) { + p := PlayerMgrSington.GetPlayerBySnId(this.SnId) + if p == nil { + return + } + lv, score := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId) + // 排行榜 + log := &model.PlayerRankScore{ + Platform: this.Platform, + SnId: this.SnId, + RankType: rankType, + SeasonId: this.SeasonId, + Lv: lv, + Score: score, + Name: p.Name, + Sex: p.Sex, + HeadUrl: p.HeadUrl, + Coin: p.Coin, + UpdateTs: time.Now().Unix(), + ModId: p.PlayerData.GetRoleId(), + } + LogChannelSingleton.WriteLog(log) +} + +type RankMatchMgr struct { + BaseClockSinker + failSeason []string + seasons map[string]*RankSeasonId // 当前赛季 key平台id + playerSeasons map[int32]*PlayerRankSeason // 玩家排位信息 key玩家id +} + +// 从数据库读取赛季配置 +func (r *RankMatchMgr) load(platform string) { + var ret *model.RankSeasonId + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err = model.FindOneRankSeasonId(platform) + if err != nil { + logger.Logger.Errorf("RankMatchMgr.Load err: %v", err) + return err + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err == nil { + if ret.SeasonId > 0 { + r.SetSeasonConfig(&RankSeasonId{ + Dirty: false, + RankSeasonId: ret, + }) + } else { + season := r.fromFile(platform) + if season != nil { + season.Dirty = true + r.SetSeasonConfig(season) + } + } + r.saveRankSeason(platform, true, true) + } else { + r.failSeason = append(r.failSeason, platform) + } + })).StartByFixExecutor(fmt.Sprintf("platform%s", platform)) +} + +var ErrTimeStr = errors.New("time str err") + +func (r *RankMatchMgr) strToTime(s string) (time.Time, error) { + t := time.Time{} + ls := strings.Split(s, "/") + if len(ls) != 3 { + return t, ErrTimeStr + } + + s = ls[0] + fmt.Sprintf("/%02s", ls[1]) + fmt.Sprintf("/%02s", ls[2]) + if len(s) != 10 { + return t, ErrTimeStr + } + + return time.ParseInLocation("2006/01/02", s, time.Local) +} + +// fromFile 从文件中读取赛季配置 +func (r *RankMatchMgr) fromFile(platform string) *RankSeasonId { + now := time.Now().Unix() + for _, v := range srvdata.PBDB_RankCycleMgr.Datas.Arr { + st, err := r.strToTime(v.Start) + if err != nil { + logger.Logger.Errorf("RankMatchMgr.fromFile start time err:%v", err) + continue + } + et, err := r.strToTime(v.End) + if err != nil { + logger.Logger.Errorf("RankMatchMgr.fromFile end time err:%v", err) + continue + } + if now >= st.Unix() && now < et.AddDate(0, 0, 1).Unix() { + return &RankSeasonId{ + Dirty: false, + RankSeasonId: &model.RankSeasonId{ + Platform: platform, + SeasonId: v.Id, + StartTs: st.Unix(), + EndTs: et.Unix(), + UpdateTs: now, + }, + } + } + } + return nil +} + +// saveRankSeason 保存赛季配置 +func (r *RankMatchMgr) saveRankSeason(platform string, isSync bool, force bool) { + ret, ok := r.seasons[platform] + if !ok || ret == nil || (!ret.Dirty && !force) { + return + } + + //todo 需要拷贝一份数据再保存吗 + + var res bool + f := func() { + res = model.UpsertRankSeasonId(ret.RankSeasonId) + } + cf := func() { + if res { + ret.Dirty = false + } + } + + if isSync { + f() + cf() + return + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + f() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + cf() + })).StartByFixExecutor(fmt.Sprintf("platform%s", platform)) +} + +// checkNewSeason 是否新赛季 +func (r *RankMatchMgr) checkNewSeason(platform string) bool { + newSeason := r.fromFile(platform) + oldSeason := r.GetSeasonConfig(platform) + var oldId, newId int32 + if oldSeason != nil { + oldId = oldSeason.SeasonId + } + if newSeason != nil { + newId = newSeason.SeasonId + } + + if newId > 0 && oldId != newId { + // 新赛季 + newSeason.Dirty = true + r.SetSeasonConfig(newSeason) + + // 玩家段位继承 + for _, v := range r.playerSeasons { + v.CheckNewSeason() + } + + return true + } + return false +} + +// GetSeasonConfig 获取当前赛季配置 +func (r *RankMatchMgr) GetSeasonConfig(platform string) *RankSeasonId { + return r.seasons[platform] +} + +// SetSeasonConfig 设置当前赛季配置 +func (r *RankMatchMgr) SetSeasonConfig(config *RankSeasonId) { + if config == nil { + return + } + r.seasons[config.Platform] = config + + r.checkNewSeason(config.Platform) +} + +func (r *RankMatchMgr) GetPlayerSeason(snid int32) *PlayerRankSeason { + ret := r.playerSeasons[snid] + if ret == nil { + return nil + } + return ret +} + +func (r *RankMatchMgr) GetPlayerRankScore(snid int32) map[int32]int64 { + ret := make(map[int32]int64) + if rank := r.GetPlayerSeason(snid); rank != nil && rank.PlayerRankSeason != nil { + for k, v := range rank.RankType { + if v != nil { + ret[k] = v.Score + } + } + } + return ret +} + +func (r *RankMatchMgr) GetPlayerRankScoreInt64(snid int32) map[int64]int64 { + ret := make(map[int64]int64) + if rank := r.GetPlayerSeason(snid); rank != nil && rank.PlayerRankSeason != nil { + for k, v := range rank.RankType { + if v != nil { + ret[int64(k)] = v.Score + } + } + } + return ret +} + +func (r *RankMatchMgr) CheckShowRed(snid int32) { + p := r.GetPlayerSeason(snid) + if p == nil { + return + } + for k := range p.RankType { + p.CheckShowRed(k) + } +} + +func (r *RankMatchMgr) UpdatePlayerSeason(snid int32, rankScore map[int32]int64) { + season := r.GetPlayerSeason(snid) + if season != nil { + season.Update(rankScore) + } +} + +func (r *RankMatchMgr) UpdateRobotSeason(platform string, snid int32, rankType int32, score int64, + name string, sex int32, headUrl string, coin int64, roleId int32) { + + log := &model.PlayerRankScore{ + Platform: platform, + SnId: snid, + RankType: rankType, + SeasonId: r.GetSeasonConfig(platform).SeasonId, + Lv: srvdata.RankLevelMgr.GetRankLevel(rankType, score), + Score: score, + Name: name, + Sex: sex, + HeadUrl: headUrl, + Coin: coin, + IsRobot: true, + UpdateTs: time.Now().Unix(), + ModId: roleId, + } + LogChannelSingleton.WriteLog(log) +} + +func (r *RankMatchMgr) SetPlayerSeason(config *PlayerRankSeason) { + if config == nil { + return + } + + r.playerSeasons[config.SnId] = config + // 段位继承 + config.CheckNewSeason() +} + +func (r *RankMatchMgr) ModuleName() string { + return "RankMatchMgr" +} + +func (r *RankMatchMgr) Init() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + r.load(platform.IdStr) + } +} + +func (r *RankMatchMgr) Update() { + // 数据库查询失败重新查询 + if len(r.failSeason) > 0 { + ls := r.failSeason + r.failSeason = make([]string, 0) + for _, v := range ls { + r.load(v) + } + } +} + +func (r *RankMatchMgr) Shutdown() { + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + r.saveRankSeason(platform.IdStr, true, true) + } + + module.UnregisteModule(r) +} + +// NewSeasonScore 赛季继承积分 +func (r *RankMatchMgr) NewSeasonScore(old int64) int64 { + return srvdata.RankLevelMgr.GetNewScore(old) +} + +func (r *RankMatchMgr) GetPlayerRankLV(rankType, snid int32) (int32, int64) { + if rankType <= 0 { + return 0, 0 + } + + rank := r.GetPlayerSeason(snid) + if rank == nil || rank.RankType == nil || rank.RankType[rankType] == nil { + return 0, 0 + } + + score := rank.RankType[rankType].Score + + return srvdata.RankLevelMgr.GetRankLevel(rankType, score), score +} + +func (r *RankMatchMgr) GetPlayerLastRankLV(rankType, snid int32) (int32, int64) { + if rankType <= 0 { + return 0, 0 + } + + rank := r.GetPlayerSeason(snid) + if rank == nil || rank.LastRankType == nil || rank.LastRankType[rankType] == nil { + return 0, 0 + } + + score := rank.LastRankType[rankType].Score + + return srvdata.RankLevelMgr.GetRankLevel(rankType, score), score +} + +func (r *RankMatchMgr) GetRankList(rankType int32) []*rankmatch.RankItem { + var list []*rankmatch.RankItem + + // todo 优化 + for _, v := range srvdata.PBDB_RankLevelMgr.Datas.Arr { + if rankType < v.GetRankType() { + continue + } + if rankType > v.GetRankType() { + break + } + + list = append(list, &rankmatch.RankItem{ + Id: v.Id, + Lv: v.Level, + }) + } + sort.Slice(list, func(i, j int) bool { + return list[i].Lv > list[j].Lv + }) + return list +} + +func (r *RankMatchMgr) getRankScore(rankType int32, lv int32) int64 { + for _, v := range srvdata.PBDB_RankLevelMgr.Datas.Arr { + if rankType < v.GetRankType() { + continue + } + if rankType > v.GetRankType() { + break + } + if v.Level == lv { + return v.Score + } + } + return 0 +} + +func (r *RankMatchMgr) GetRankAwardList(rankType int32) []*rankmatch.AwardItem { + var list []*rankmatch.AwardItem + + for _, v := range srvdata.PBDB_RankRewardMgr.Datas.Arr { + if rankType < v.GetRankType() { + continue + } + if rankType > v.GetRankType() { + break + } + + item := &rankmatch.AwardItem{ + Id: v.Id, + Lv: v.Level, + Score: r.getRankScore(rankType, v.Level), + } + item.Awards = []*rankmatch.Award{} + if v.Award1Num > 0 { + item.Awards = append(item.Awards, &rankmatch.Award{ + Id: v.Award1Id, + Num: v.Award1Num, + }) + } + if v.Award2Num > 0 { + item.Awards = append(item.Awards, &rankmatch.Award{ + Id: v.Award2Id, + Num: v.Award2Num, + }) + } + if v.Award3Num > 0 { + item.Awards = append(item.Awards, &rankmatch.Award{ + Id: v.Award3Id, + Num: v.Award3Num, + }) + } + list = append(list, item) + } + sort.Slice(list, func(i, j int) bool { + return list[i].Lv > list[j].Lv + }) + return list +} + +//========================implement ClockSinker ============================== + +func (r *RankMatchMgr) InterestClockEvent() int { + return 1 << CLOCK_EVENT_DAY +} + +func (r *RankMatchMgr) OnDayTimer() { + logger.Logger.Info("(this *RankMatchMgr) OnDayTimer") + for _, platform := range PlatformMgrSingleton.GetPlatforms() { + if platform.IdStr == DefaultPlatform { + continue + } + // 检查新赛季 + r.checkNewSeason(platform.IdStr) + // 保存赛季配置 + r.saveRankSeason(platform.IdStr, false, false) + } +} + +//========================implement IPlayerLoad ============================== + +func (r *RankMatchMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay { + args := &model.FindPlayerRankSeasonArgs{ + Platform: platform, + Id: []int32{snid}, + } + ret, err := model.FindPlayerRankSeason(args) + return &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + Err: err, + Data: ret.List, + } +} + +func (r *RankMatchMgr) Callback(player any, ret *internal.PlayerLoadReplay) { + if ret == nil { + return + } + if ret.Err != nil || ret.Data == nil { + logger.Logger.Errorf("RankMatchMgr load player rankseason err:%v", ret.Err) + return + } + + ls := ret.Data.([]*model.PlayerRankSeason) + if len(ls) == 0 { + // 初始化 + s := r.GetSeasonConfig(ret.Platform) + if s == nil { + return + } + r.SetPlayerSeason(&PlayerRankSeason{ + Dirty: true, + PlayerRankSeason: model.NewPlayerRankSeason(&model.NewPlayerRankSeasonArgs{ + Platform: ret.Platform, + SnId: ret.Snid, + SeasonId: s.SeasonId, + }), + }) + } else { + r.SetPlayerSeason(&PlayerRankSeason{ + Dirty: false, + PlayerRankSeason: ls[0], + }) + } +} + +func (r *RankMatchMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay { + return nil +} + +func (r *RankMatchMgr) CallbackAfter(ret *internal.PlayerLoadReplay) { +} + +func (r *RankMatchMgr) Save(platform string, snid int32, isSync, force bool) { + ret, ok := r.playerSeasons[snid] + if !ok || ret == nil || (!ret.Dirty && !force) { + return + } + + var res bool + f := func() { + if ret.Id == "" { + ret.Id = bson.NewObjectId() + } + res = model.UpsertPlayerRankSeason(ret.PlayerRankSeason) + } + cf := func() { + if res { + ret.Dirty = false + } + } + + if isSync { + f() + cf() + return + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + f() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + cf() + })).StartByFixExecutor(fmt.Sprintf("platform%s", ret.Platform)) +} + +func (r *RankMatchMgr) Release(platform string, snid int32) { + delete(r.playerSeasons, snid) +} + +func init() { + module.RegisteModule(RankMgrSingleton, time.Minute, 0) + ClockMgrSington.RegisteSinker(RankMgrSingleton) + internal.RegisterPlayerLoad(RankMgrSingleton) +} diff --git a/worldsrv/rebatetask.go b/worldsrv/rebatetask.go new file mode 100644 index 0000000..91bdaf0 --- /dev/null +++ b/worldsrv/rebatetask.go @@ -0,0 +1,275 @@ +package main + +import ( + "encoding/json" + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core/logger" + "strconv" +) + +//返利任务 + +type RebateTask struct { + Platform string //平台名称 + RebateSwitch bool //返利开关 + RebateManState int //返利是开启个人返利 0 关闭 1 开启 + ReceiveMode int //领取方式 0实时领取 1次日领取 + NotGiveOverdue int //0不过期 1过期不给 2过期邮件给 + RebateGameCfg map[string]*RebateGameCfg //key为GameDif + RebateGameThirdCfg map[string]*RebateGameThirdCfg //第三方key + Version int //活动版本 后台控制 +} +type RebateGameCfg struct { + BaseCoin [3]int32 //返利基准 + RebateRate [3]int32 //返利比率 万分比 + GameId int32 //游戏id + GameMode int32 //游戏类型 + MaxRebateCoin int64 //最高返利 +} +type RebateGameThirdCfg struct { + BaseCoin [3]int32 //返利基准 + RebateRate [3]int32 //返利比率 + ThirdName string //第三方key + ThirdShowName string //前端显示name + MaxRebateCoin int64 //最高返利 + ThirdId string //三方游戏id +} +type RebateInfo struct { + rebateTask map[string]*RebateTask +} + +var RebateInfoMgrSington = &RebateInfo{ + rebateTask: make(map[string]*RebateTask), +} + +func (this *RebateInfo) Init() { +} +func (this *RebateInfo) Update() { +} +func (this *RebateInfo) Shutdown() { + +} +func (this *RebateInfo) ModuleName() string { + return "RebateTask" +} +func (this *RebateInfo) UpdateRebateDataByApi(platform string, RebateInfo RebateInfos, isThirdName bool) { + rebateTask := this.rebateTask[platform] + if rebateTask == nil { + this.rebateTask[platform] = &RebateTask{} + rebateTask = this.rebateTask[platform] + } + rebateTask.Platform = RebateInfo.Platform + rebateTask.RebateSwitch = RebateInfo.RebateSwitch + rebateTask.Version = RebateInfo.Version + rebateTask.NotGiveOverdue = RebateInfo.NotGiveOverdue + rebateTask.ReceiveMode = RebateInfo.ReceiveMode + rebateTask.RebateManState = RebateInfo.RebateManState + if !isThirdName { + rebateTask.RebateGameCfg = make(map[string]*RebateGameCfg) + for _, v := range RebateInfo.RebateGameCfg { + for _, dfm := range srvdata.PBDB_GameFreeMgr.Datas.Arr { + if dfm.GetGameId() == v.GameId && dfm.GetGameMode() == v.GameMode { + rebateTask.RebateGameCfg[dfm.GetGameDif()] = v + break + } + } + } + } else { + rebateTask.RebateGameThirdCfg = make(map[string]*RebateGameThirdCfg) + for _, v := range RebateInfo.RebateGameThirdCfg { + thirdId := strconv.Itoa(int(ThirdPltGameMappingConfig.FindThirdIdByThird(v.ThirdName))) + rebateTask.RebateGameThirdCfg[thirdId] = v + } + } +} + +// 返利数据初始化 +func (this *RebateInfo) LoadRebateData() { + //构建默认的平台数据 + //this.CreateDefaultPlatform() + //获取平台返利数据 getGameRebateConfig + type ApiResult struct { + Tag int + Msg []RebateInfos + } + if !model.GameParamData.UseEtcd { + rebateBuff, err := webapi.API_GetGameRebateConfig(common.GetAppId()) + if err == nil { + logger.Logger.Trace("API_GetGameRebateConfig:", string(rebateBuff)) + ar := ApiResult{} + err = json.Unmarshal(rebateBuff, &ar) + if err == nil && ar.Tag == 0 { + for _, v := range ar.Msg { + rebateTask := this.rebateTask[v.Platform] + if rebateTask == nil { + this.rebateTask[v.Platform] = &RebateTask{ + Platform: v.Platform, + RebateSwitch: v.RebateSwitch, + ReceiveMode: v.ReceiveMode, + RebateManState: v.RebateManState, + NotGiveOverdue: v.NotGiveOverdue, + Version: v.Version, + } + rebateTask = this.rebateTask[v.Platform] + } + rebateTask.RebateGameCfg = make(map[string]*RebateGameCfg) + rebateTask.RebateGameThirdCfg = make(map[string]*RebateGameThirdCfg) + for _, v := range v.RebateGameCfg { + for _, dfm := range srvdata.PBDB_GameFreeMgr.Datas.Arr { + if dfm.GetGameId() == v.GameId && dfm.GetGameMode() == v.GameMode { + rebateTask.RebateGameCfg[dfm.GetGameDif()] = v + break + } + } + } + for _, v := range v.RebateGameThirdCfg { + thirdId := strconv.Itoa(int(ThirdPltGameMappingConfig.FindThirdIdByThird(v.ThirdName))) + v.ThirdId = thirdId + rebateTask.RebateGameThirdCfg[thirdId] = v + } + } + } else { + logger.Logger.Error("Unmarshal RebateTask data error:", err) + } + } else { + logger.Logger.Error("Get RebateTask data error:", err) + } + } else { + EtcdMgrSington.InitRebateConfig() + } +} + +// /////////////////////////////////////////////////////////////////////// +// 对应客户端 +const ( + //棋牌类 + KaiYuanChessGame = 6 //开元棋牌 + SelfChessGame = 7 //博乐棋牌 + FGChessGame = 11 //FG棋牌 + WWGChessGame = 12 //WWG棋牌 + //电子类 + FGElectronicGame = 4 //FG电子 + WWGElectronicGame = 5 //WWG电子 +) + +// ////////////////////////////////// +// /个人信息 +type TotalInfo struct { + GameTotalNum int32 //游戏总局数 + GameMostPartake string //参与最多游戏名字 + GameMostProfit string //单局最多盈利游戏名字 + CreateRoomNum int32 //创建房间总数 + CreateRoomMost string //创建房间最多游戏名字 + CreateClubNum int32 //创建俱乐部数量 + CreateClubRoomMost string //创建包间最多游戏名字 + TeamNum int32 //团队人数 + AchievementTotal int32 //推广总业绩 + RewardTotal int32 //推广总奖励 +} + +// /手动洗码 +type CodeCoinRecord struct { + GameName string //游戏名称 + GameBetCoin int64 //游戏洗码量 + Rate int32 //比例 + Coin int32 //洗码金额 +} +type CodeCoinTotal struct { + TotalCoin int64 //总计游戏投注 + CodeCoinRecord []*CodeCoinRecord + PageNo int //当前页 + PageSize int //每页数量 + PageNum int //总页数 +} + +// /投注记录 +type BetCoinRecord struct { + Ts int64 //时间戳 + GameName string //游戏名称 + RecordId string //注单号 + BetCoin int64 //投注金额 + ReceivedCoin int64 //已派奖 +} +type BetCoinTotal struct { + BetCoinRecord []*BetCoinRecord + PageNo int //当前页 + PageSize int //每页数量 + PageNum int //总页数 +} + +// /账户明细 +type CoinDetailed struct { + Ts int64 //时间戳 + CoinType int64 //交易类型 + Income int64 //收入 + Disburse int64 //支出 + Coin int64 //金额 +} +type CoinDetailedTotal struct { + RechargeCoin int64 //充值 + ExchangeCoin int64 //兑换 + ClubAddCoin int64 //俱乐部加币 + RebateCoin int64 //返利 + Activity int64 //活动获取 + CoinDetailed []*CoinDetailed + PageNo int //当前页 + PageSize int //每页数量 + PageNum int //总页数 +} + +// /个人报表 +type ReportForm struct { + ShowType int //标签 棋牌游戏等等 + ProfitCoin int64 //盈利总额 + BetCoin int64 //有效投注总额 + FlowCoin int64 //流水总额 +} + +// ////////////////////////////////// +type HallGameType struct { + GameId int32 + GameMode int32 +} +type GameConfig struct { + Platform string + IsKnowType bool + BigGameType map[int32]map[int32][]*HallGameType +} + +//func (this *PersonInfo) FormatConfig(platform string, btl []*protocol.BigTagList) { +// if this.PlatformType[platform] == nil || (this.PlatformType[platform] != nil && !this.PlatformType[platform].IsKnowType) { +// gcf := &GameConfig{ +// Platform: platform, +// IsKnowType: true, +// } +// for _, bigTag := range btl { +// gcf.BigGameType[bigTag.GetBigTagId()] = make(map[int32][]*HallGameType) +// for _, smallTag := range bigTag.SmallTagList { +// gcf.BigGameType[bigTag.GetBigTagId()][smallTag.GetSmallTagId()] = []*HallGameType{} +// bs := gcf.BigGameType[bigTag.GetBigTagId()][smallTag.GetSmallTagId()] +// for _, v := range smallTag.GameType { +// bs = append(bs, &HallGameType{GameId: v.GetGameId(), GameMode: v.GetGameMode()}) +// } +// } +// } +// this.PlatformType[platform] = gcf +// } +//} + +type PersonInfo struct { + PlatformType map[string]*GameConfig +} + +var PersonInfoMgrSingTon = &PersonInfo{ + PlatformType: make(map[string]*GameConfig), +} + +func init() { + //RegisterParallelLoadFunc("平台返利数据", func() error { + // RebateInfoMgrSington.LoadRebateData() + // return nil + //}) +} diff --git a/worldsrv/scene.go b/worldsrv/scene.go new file mode 100644 index 0000000..fad8889 --- /dev/null +++ b/worldsrv/scene.go @@ -0,0 +1,1295 @@ +package main + +import ( + "math" + "math/rand" + "time" + + rawproto "google.golang.org/protobuf/proto" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/srvlib" + srvlibproto "mongo.games.com/goserver/srvlib/protocol" + + "mongo.games.com/game/common" + "mongo.games.com/game/gamerule/tienlen" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + hallproto "mongo.games.com/game/protocol/gamehall" + playerproto "mongo.games.com/game/protocol/player" + serverproto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +const ( + MatchSceneState_Waiting = iota //等待状态 + MatchSceneState_Running //进行状态 + MatchSceneState_Billed //结算状态 +) + +const ( + // PlayerHistoryModel . + PlayerHistoryModel = iota + 1 + + // BIGWIN_HISTORY_MODEL . + BIGWIN_HISTORY_MODEL + + // GameHistoryModel . + GameHistoryModel +) + +type PlayerGameCtx struct { + takeCoin int64 //进房时携带的金币量 + enterTs int64 //进入时间 + totalConvertibleFlow int64 //进房时玩家身上的总流水 +} + +// Scene 场景(房间) +type Scene struct { + sceneId int //场景id + gameId int //游戏id + gameMode int //游戏模式 + sceneMode int //房间模式,参考common.SceneMode_XXX + params []int32 //场景参数 + paramsEx []int32 //其他扩展参数 + playerNum int //人数 + robotNum int //机器人数量 + robotLimit int //最大限制机器人数量 + preInviteRobNum int //准备邀请机器人的数量 + creator int32 //创建者账号id + agentor int32 //代理者id + replayCode string //回放码 + currRound int32 //当前第几轮 + totalRound int32 //总共几轮 + clycleTimes int32 //循环次数 + deleting bool //正在删除 + starting bool //正在开始 + closed bool //房间已关闭 + force bool //强制删除 + hadCost bool //是否已经扣过房卡 + inTeahourse bool //是否在棋牌馆 + players map[int32]*Player //玩家 + audiences map[int32]*Player //观众 + seats [9]*Player //座位 + gameSess *GameSession //所在gameserver + sp ScenePolicy //场景上的一些业务策略 + createTime time.Time //创建时间 + lastTime time.Time //最后活跃时间 + startTime time.Time //开始时间 + dirty bool //脏标记 + applyTimes map[int32]int32 //申请坐下次数 + limitPlatform *Platform //限制平台 + groupId int32 //组id + hallId int32 //厅id + state int32 //场景当前状态 + fishing int32 //渔场的鱼潮状态 + gameCtx map[int32]*PlayerGameCtx //进入房间的环境 + dbGameFree *serverproto.DB_GameFree // + ClubId int32 + clubRoomID string //俱乐部包间ID + clubRoomPos int32 // + clubRoomTax int32 // + createFee int32 //创建房间的费用 + manualDelete bool //是否手动解散 + GameLog []int32 //游戏服务器同步的录单 + JackPotFund int64 //游戏服务器同步的奖池 + State int32 //当前游戏状态,后期放到ScenePolicy里去处理 + StateTs int64 //切换到当前状态的时间 + StateSec int32 //押注状态的秒数 + BankerListNum int32 //庄家列表数量 + matchParams []int32 //比赛参数 + matchState int //比赛状态 + quitMatchSnids []int32 //退赛玩家id + gameSite int //tienlen游戏场次区分 1.初级 2.中级 3.高级场 + BaseScore int32 //tienlen游戏底分 + matchId int32 //比赛场id + + csp *CoinScenePool // 所在场景池 +} + +// NewScene 创建房间 +func NewScene(agentor, creator int32, id, gameId, gameMode, sceneMode int, clycleTimes, numOfGames int32, params []int32, + gs *GameSession, limitPlatform *Platform, groupId int32, dbGameFree *serverproto.DB_GameFree, paramsEx ...int32) *Scene { + sp := GetScenePolicy(gameId, gameMode) + if sp == nil { + logger.Logger.Errorf("NewScene sp == nil, gameId=%v gameMode=%v", gameId, gameMode) + return nil + } + s := &Scene{ + sceneId: id, + hallId: dbGameFree.Id, + playerNum: 0, + creator: creator, + agentor: agentor, + gameId: gameId, + gameMode: gameMode, + sceneMode: sceneMode, + params: params, + paramsEx: paramsEx, + clycleTimes: clycleTimes, + players: make(map[int32]*Player), + audiences: make(map[int32]*Player), + gameSess: gs, + sp: sp, + createTime: time.Now(), + limitPlatform: limitPlatform, + groupId: groupId, + gameCtx: make(map[int32]*PlayerGameCtx), //进入房间的环境 + dbGameFree: dbGameFree, + currRound: 0, + totalRound: numOfGames, + } + // 从房间配置参数获取,最大房间人数 + s.playerNum = int(sp.GetPlayerNum(s)) + s.lastTime = s.createTime + + if s.IsHallScene() || s.IsCoinScene() { + code := SceneMgrSingleton.AllocReplayCode() + s.replayCode = code + } + if s.dbGameFree.GetMatchMode() == 0 { + s.RandRobotCnt() + } + if s.IsMatchScene() { + s.BaseScore = 10 + } + s.sp.OnStart(s) + return s +} + +func NewLocalGameScene(creator int32, sceneId, gameId, gameSite, sceneMode int, clycleTimes int32, params []int32, + gs *GameSession, limitPlatform *Platform, playerNum int, dbGameFree *serverproto.DB_GameFree, baseScore, groupId int32, paramsEx ...int32) *Scene { + sp := GetScenePolicy(gameId, 0) + if sp == nil { + logger.Logger.Errorf("NewLocalGameScene sp == nil, gameId=%v ", gameId) + return nil + } + s := &Scene{ + sceneId: sceneId, + hallId: dbGameFree.Id, + playerNum: playerNum, + creator: creator, + gameId: gameId, + sceneMode: sceneMode, + params: params, + paramsEx: paramsEx, + clycleTimes: clycleTimes, + players: make(map[int32]*Player), + audiences: make(map[int32]*Player), + gameSess: gs, + sp: sp, + createTime: time.Now(), + limitPlatform: limitPlatform, + groupId: groupId, + gameCtx: make(map[int32]*PlayerGameCtx), //进入房间的环境 + dbGameFree: dbGameFree, + gameSite: gameSite, + BaseScore: baseScore, + } + if s.playerNum <= 0 { + s.playerNum = int(sp.GetPlayerNum(s)) + } + if s.BaseScore <= 0 { + s.BaseScore = int32(sp.GetBaseCoin(s)) + } + s.lastTime = s.createTime + + code := SceneMgrSingleton.AllocReplayCode() + s.replayCode = code + s.sp.OnStart(s) + return s +} + +func (this *Scene) RebindPlayerSnId(oldSnId, newSnId int32) { + if this.creator == oldSnId { + this.creator = newSnId + } + if this.agentor == oldSnId { + this.agentor = newSnId + } + if p, exist := this.players[oldSnId]; exist { + delete(this.players, oldSnId) + this.players[newSnId] = p + } + if p, exist := this.audiences[oldSnId]; exist { + delete(this.audiences, oldSnId) + this.audiences[newSnId] = p + } +} + +func (this *Scene) RobotIsLimit() bool { + if this.robotLimit != 0 { + if this.robotNum >= this.robotLimit { + return true + } + } + return false +} + +func (this *Scene) PlayerEnter(p *Player, pos int, ischangeroom bool) bool { + logger.Logger.Infof("(this *Scene:%v) PlayerEnter(%v, %v) ", this.sceneId, p.SnId, pos) + + if p.IsRob { + if this.robotLimit != 0 { + if !model.GameParamData.IsRobFightTest { + //增加所有机器人对战场的 + if this.robotNum+1 > this.robotLimit { + logger.Logger.Warnf("(this *Scene:%v) PlayerEnter(%v) robot num limit(%v)", this.sceneId, p.SnId, this.robotLimit) + return false + } + } + } + } + + // 非百人,设置座位 + if !this.IsHundredScene() { + if pos != -1 { + if this.seats[pos] != nil { + for i := 0; i < this.playerNum; i++ { + if this.seats[i] == nil { + p.pos = i + this.seats[i] = p + break + } + } + } else { + p.pos = pos + this.seats[pos] = p + } + } else { + for i := 0; i < this.playerNum; i++ { + if this.seats[i] == nil { + p.pos = i + this.seats[i] = p + break + } + } + } + } + + p.scene = this + this.players[p.SnId] = p + this.gameSess.AddPlayer(p) + + switch { + case this.IsCoinScene(): + + case this.IsHundredScene(): + // todo 删除这个标记 + HundredSceneMgrSington.OnPlayerEnter(p, this.paramsEx[0]) + case this.IsMatchScene(): + + } + + // 如果正在等待比赛,退赛 + if !this.IsMatchScene() { + isWaiting, tmid := TournamentMgr.IsMatchWaiting(p.Platform, p.SnId) + if isWaiting { + TournamentMgr.CancelSignUp(p.Platform, tmid, p.SnId) + } + } + + takeCoin := p.Coin + leaveCoin := int64(0) + gameTimes := rand.Int31n(100) + matchParams := []int32{} //排名、段位、假snid、假角色 + + if this.IsMatchScene() && p.matchCtx != nil { + takeCoin = int64(p.matchCtx.grade) + matchParams = append(matchParams, p.matchCtx.rank) //排名 + ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId) + if ms != nil { + matchParams = append(matchParams, ms.Lv) //段位 + } else { + if p.IsRob { + //robotRandLv := MatchSeasonRankMgrSington.CreateRobotLv() + matchParams = append(matchParams, p.matchCtx.copyLv) //机器人随机段位 + } else { + matchParams = append(matchParams, 1) //段位默认值 + } + } + matchParams = append(matchParams, p.matchCtx.copySnid) //假snid + matchParams = append(matchParams, p.matchCtx.copyRoleId) //假snid + } else { + if p.IsRob { + if len(this.paramsEx) > 0 { //机器人携带金币动态调整 + gps := PlatformMgrSingleton.GetGameFree(this.limitPlatform.IdStr, this.paramsEx[0]) + if gps != nil { + dbGameFree := gps.DbGameFree + if gps.GroupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + dbGameFree = pgg.DbGameFree + } + } + + flag := false + if common.IsLocalGame(this.gameId) { + baseScore := this.BaseScore + arrs := srvdata.PBDB_CreateroomMgr.Datas.Arr + tmpIds := []int32{} + for i := 0; i < len(arrs); i++ { + arr := arrs[i] + if int(arr.GameId) == this.gameId && int(arr.GameSite) == this.gameSite { + betRange := arr.GetBetRange() + if len(betRange) == 0 { + continue + } + for j := 0; j < len(betRange); j++ { + if betRange[j] == baseScore && len(arr.GetGoldRange()) > 0 && arr.GetGoldRange()[0] != 0 { + tmpIds = append(tmpIds, arr.GetId()) + break + } + } + } + } + if len(tmpIds) > 0 { + randId := common.RandInt32Slice(tmpIds) + crData := srvdata.PBDB_CreateroomMgr.GetData(randId) + if crData != nil { + goldRange := crData.GetGoldRange() + if len(goldRange) == 2 { + takeCoin = common.RandFromRangeInt64(int64(goldRange[0]), int64(goldRange[1])) + flag = true + } else if len(goldRange) == 1 { + takeCoin = common.RandFromRangeInt64(int64(goldRange[0]), 2*int64(goldRange[0])) + flag = true + } + leaveCoin = int64(goldRange[0]) + for _, id := range tmpIds { + tmp := srvdata.PBDB_CreateroomMgr.GetData(id).GetGoldRange() + if int64(tmp[0]) < leaveCoin && tmp[0] != 0 { + leaveCoin = int64(tmp[0]) + } + } + } + } else { + logger.Logger.Warn("gameId: ", this.gameId, " gameSite: ", this.gameSite, " baseScore: ", baseScore) + } + if leaveCoin > takeCoin { + logger.Logger.Warn("robotSnId: ", p.SnId, " baseScore: ", baseScore, " takeCoin: ", takeCoin, " leaveCoin: ", leaveCoin) + } + if takeCoin > p.Coin { + p.Coin = takeCoin + } + } + //if !flag && this.IsCoinScene() && !this.IsTestScene() { + // if expectEnterCoin, expectLeaveCoin, ExpectGameTime, ok := RobotCarryMgrEx.RandOneCarry(dbGameFree.GetId()); ok && expectEnterCoin > dbGameFree.GetLimitCoin() && expectEnterCoin < dbGameFree.GetMaxCoinLimit() { + // takeCoin = int64(expectEnterCoin) + // leaveCoin = int64(expectLeaveCoin) + // //如果带入金币和离开金币比较接近,就调整离开金币值 + // var delta = takeCoin - leaveCoin + // if math.Abs(float64(delta)) < float64(takeCoin/50) { + // if leaveCoin = takeCoin + delta*(10+rand.Int63n(50)); leaveCoin < 0 { + // leaveCoin = 0 + // } + // } + // gameTimes = ExpectGameTime * 2 + // flag = true + // } + //} + + if !flag { + takerng := dbGameFree.GetRobotTakeCoin() + if len(takerng) >= 2 && takerng[1] > takerng[0] { + if takerng[0] < dbGameFree.GetLimitCoin() { + takerng[0] = dbGameFree.GetLimitCoin() + } + takeCoin = int64(common.RandInt(int(takerng[0]), int(takerng[1]))) + } else { + maxlimit := int64(dbGameFree.GetMaxCoinLimit()) + if maxlimit != 0 && p.Coin > maxlimit { + logger.Logger.Trace("Player coin:", p.Coin) + //在下限和上限之间随机,并对其的100的整数倍 + takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit))) + logger.Logger.Trace("Take coin:", takeCoin) + } + if maxlimit == 0 && this.IsCoinScene() { + maxlimit = int64(common.RandInt(10, 50)) * int64(dbGameFree.GetLimitCoin()) + takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit))) + logger.Logger.Trace("Take coin:", takeCoin) + } + } + takeCoin = takeCoin / 100 * 100 + //离场金币 + leaverng := dbGameFree.GetRobotLimitCoin() + if len(leaverng) >= 2 { + leaveCoin = int64(leaverng[0] + rand.Int63n(leaverng[1]-leaverng[0])) + } + } + + // 象棋积分 + chessScore := dbGameFree.GetChessScoreParams() + if len(chessScore) == 2 { + p.ChessGrade = int64(common.RandInt(int(chessScore[0]), int(chessScore[1]))) + } + + bankerLimit := this.dbGameFree.GetBanker() + if bankerLimit != 0 { + //上庄AI携带 + if /*this.gameId == common.GameId_HundredBull ||*/ + this.gameId == common.GameId_RollCoin || + this.gameId == common.GameId_RollAnimals || + this.gameId == common.GameId_DragonVsTiger || + this.gameId == common.GameId_Baccarat { + if this.BankerListNum < 3 { + if rand.Intn(100) < 5 { + randCoin := int64(math.Floor(float64(bankerLimit) * 1 / float64(this.dbGameFree.GetSceneType()))) + takeCoin = rand.Int63n(randCoin) + int64(bankerLimit) + if takeCoin > p.Coin { + //加钱速度慢 暂时不用 + //ExePMCmd(p.gateSess, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, takeCoin)) + } + } + } + } + } + if takeCoin > p.Coin { + p.Coin = takeCoin + } + } + } + } + } + + if p.IsRob { + this.robotNum++ + p.RobotRandName() + p.RandRobotVip(this) + + name := this.GetSceneName() + logger.Logger.Tracef("(this *Scene) PlayerEnter(%v) robot(%v) robotlimit(%v)", name, this.robotNum, this.robotLimit) + } + + //todo:send add msg to gamesrv + data, err := p.MarshalData(this.gameId) + if err == nil { + var gateSid int64 + if p.gateSess != nil { + if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { + sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) + gateSid = sessionId.Get() + } + } + isQuMin := false + //if !p.IsRob { + // pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) + // if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + // } + //} + msg := &serverproto.WGPlayerEnter{ + Sid: proto.Int64(p.sid), + SnId: proto.Int32(p.SnId), + GateSid: proto.Int64(gateSid), + SceneId: proto.Int(this.sceneId), + PlayerData: data, + IsLoaded: proto.Bool(ischangeroom), + IsQM: proto.Bool(isQuMin), + IParams: p.MarshalIParam(), + SParams: p.MarshalSParam(), + CParams: p.MarshalCParam(), + } + //sa, err2 := p.MarshalSingleAdjustData(this.dbGameFree.Id) + //if err2 == nil && sa != nil { + // msg.SingleAdjust = sa + //} + if this.ClubId != 0 { + + } + p.takeCoin = takeCoin + p.sceneCoin = takeCoin + p.enterts = time.Now() + + if !p.IsRob { //保存下进入时的环境 + this.gameCtx[p.SnId] = &PlayerGameCtx{ + takeCoin: p.takeCoin, + enterTs: p.enterts.Unix(), + totalConvertibleFlow: p.TotalConvertibleFlow, + } + } + msg.TakeCoin = proto.Int64(takeCoin) + msg.ExpectLeaveCoin = proto.Int64(leaveCoin) + msg.ExpectGameTimes = proto.Int32(gameTimes) + msg.Pos = proto.Int(p.pos) + if matchParams != nil { + for _, param := range matchParams { + msg.MatchParams = append(msg.MatchParams, param) + } + } + + // 道具 + dbItemArr := srvdata.PBDB_GameItemMgr.Datas.Arr + if dbItemArr != nil { + msg.Items = make(map[int32]int32) + for _, dbItem := range dbItemArr { + msg.Items[dbItem.Id] = proto.Int32(0) + itemInfo := BagMgrSingleton.GetItem(p.SnId, dbItem.Id) + if itemInfo != nil { + msg.Items[dbItem.Id] = proto.Int32(int32(itemInfo.ItemNum)) + } + } + } + + // 排位积分 + ret := RankMgrSingleton.GetPlayerSeason(p.SnId) + if ret != nil && ret.PlayerRankSeason != nil { + msg.RankScore = make(map[int32]int64) + for k, v := range ret.RankType { + if v != nil { + msg.RankScore[k] = v.Score + } + } + } + + if p.IsRobot() { + msg.RankScore = make(map[int32]int64) + rankScore := this.dbGameFree.GetRankScoreParams() + if len(rankScore) == 2 { + //todo 根据游戏id获取排位类型不太方便 + switch { + case common.IsTienLen(int(this.dbGameFree.GetGameId())): + msg.RankScore[tienlen.RankType] = int64(common.RandInt(int(rankScore[0]), int(rankScore[1]))) + } + } + } + + proto.SetDefaults(msg) + this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_PLAYERENTER), msg) + logger.Logger.Tracef("SSPacketID_PACKET_WG_PLAYERENTER Scene:%v ;PlayerEnter(%v, %v)", this.sceneId, p.SnId, pos) + this.lastTime = time.Now() + return true + } else { + logger.Logger.Warnf("(this *Scene:%v) PlayerEnter(%v, %v) Marshal player data error %v", this.sceneId, p.SnId, pos, err) + this.DelPlayer(p) + return false + } +} + +func ExePMCmd(s *netlib.Session, cmd string) { + CSPMCmd := &playerproto.CSPMCmd{ + Cmd: proto.String(cmd), + } + proto.SetDefaults(CSPMCmd) + logger.Logger.Trace("CSPMCmd:", CSPMCmd) + s.Send(int(playerproto.PlayerPacketID_PACKET_CS_PMCMD), CSPMCmd) +} + +func (this *Scene) GetPlayerGameCtx(snid int32) *PlayerGameCtx { + if ctx, exist := this.gameCtx[snid]; exist { + return ctx + } + return nil +} + +func (this *Scene) PlayerLeave(p *Player, reason int) { + logger.Logger.Infof("(this *Scene:%v) PlayerLeave(%v, %v) ", this.sceneId, p.SnId, reason) + //if !this.IsMatchScene() { + //pack := &hall_proto.SCLeaveRoom{ + // Reason: proto.Int(reason), + // OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game, + // Mode: proto.Int(0), + // RoomId: proto.Int(this.sceneId), + //} + //proto.SetDefaults(pack) + //p.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + pack := &hallproto.SCQuitGame{ + Id: int32(this.dbGameFree.Id), + Reason: proto.Int(reason), + } + pack.OpCode = hallproto.OpResultCode_Game_OPRC_Sucess_Game + proto.SetDefaults(pack) + p.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_QUITGAME), pack) + //} + + //其他人直接从房间退出来 + this.DelPlayer(p) + + // 玩家最后所在游戏 + p.LastGameId = int(this.dbGameFree.GetGameId()) + + this.lastTime = time.Now() +} + +func (this *Scene) DelPlayer(p *Player) bool { + if p.scene != this { + inroomid := 0 + if p.scene != nil { + inroomid = p.scene.sceneId + } + logger.Logger.Warnf("(this *Scene) DelPlayer found player:%v in room:%v but room:%v", p.SnId, inroomid, this.sceneId) + } + if this.gameSess != nil { + this.gameSess.DelPlayer(p) + } + delete(this.players, p.SnId) + if !p.IsRob { + delete(this.gameCtx, p.SnId) + } + + p.scene = nil + SceneMgrSingleton.OnPlayerLeaveScene(this, p) + + switch { + case this.IsHundredScene(): + HundredSceneMgrSington.OnPlayerLeave(p) + //case this.IsHallScene(): + // PlatformMgrSingleton.OnPlayerLeaveScene(this, p) + // for i := 0; i < this.playerNum; i++ { + // if this.seats[i] == p { + // p.pos = -1 + // this.seats[i] = nil + // break + // } + // } + case this.IsCoinScene() || this.IsMatchScene(): + for i := 0; i < this.playerNum; i++ { + if this.seats[i] == p { + p.pos = -1 + this.seats[i] = nil + break + } + } + } + + if p.IsRob { + this.robotNum-- + name := this.GetSceneName() + logger.Logger.Tracef("(this *Scene) PlayerLeave(%v) robot(%v) robotlimit(%v)", name, this.robotNum, this.robotLimit) + } + //from gameserver, so don't need send msg + return true +} + +func (this *Scene) AudienceEnter(p *Player, ischangeroom bool) bool { + logger.Logger.Infof("(this *Scene:%v) AudienceEnter(%v) ", this.sceneId, p.SnId) + p.scene = this + p.applyPos = -1 + this.audiences[p.SnId] = p + this.gameSess.AddPlayer(p) + if this.IsHundredScene() { + HundredSceneMgrSington.OnPlayerEnter(p, this.paramsEx[0]) + } + //todo:send add msg to gamesrv + data, err := p.MarshalData(this.gameId) + if err == nil { + var gateSid int64 + if p.gateSess != nil { + if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { + sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) + gateSid = sessionId.Get() + } + } + isQuMin := false + //if !p.IsRob { + // pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) + // if pt != nil && pt.SpreadTag == 1 { + // isQuMin = true + // } + //} + msg := &serverproto.WGPlayerEnter{ + Sid: proto.Int64(p.sid), + SnId: proto.Int32(p.SnId), + GateSid: proto.Int64(gateSid), + SceneId: proto.Int(this.sceneId), + PlayerData: data, + IsLoaded: proto.Bool(ischangeroom), + IsQM: proto.Bool(isQuMin), + IParams: p.MarshalIParam(), + SParams: p.MarshalSParam(), + CParams: p.MarshalCParam(), + } + + if !p.IsRob { //保存下进入时的环境 + this.gameCtx[p.SnId] = &PlayerGameCtx{ + takeCoin: p.takeCoin, + enterTs: p.enterts.Unix(), + totalConvertibleFlow: p.TotalConvertibleFlow, + } + } + + takeCoin := p.Coin + p.takeCoin = takeCoin + msg.TakeCoin = proto.Int64(takeCoin) + proto.SetDefaults(msg) + this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_AUDIENCEENTER), msg) + p.enterts = time.Now() + this.lastTime = time.Now() + return true + } + + return false +} + +func (this *Scene) AudienceLeave(p *Player, reason int) { + logger.Logger.Infof("(this *Scene:%v) AudienceLeave(%v, %v) ", this.sceneId, p.SnId, reason) + pack := &hallproto.SCLeaveRoom{ + Reason: proto.Int(reason), + OpRetCode: hallproto.OpResultCode_Game_OPRC_Sucess_Game, + Mode: proto.Int(0), + RoomId: proto.Int(this.sceneId), + } + proto.SetDefaults(pack) + p.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + //观众直接从房间退出来 + this.DelAudience(p) + this.lastTime = time.Now() +} + +func (this *Scene) DelAudience(p *Player) bool { + logger.Logger.Infof("(this *Scene:%v) DelAudience(%v) ", this.sceneId, p.SnId) + if p.scene != this { + return false + } + if this.gameSess != nil { + this.gameSess.DelPlayer(p) + } + delete(this.audiences, p.SnId) + if !p.IsRob { + delete(this.gameCtx, p.SnId) + } + p.scene = nil + p.applyPos = -1 + SceneMgrSingleton.OnPlayerLeaveScene(this, p) + if this.IsHundredScene() { + HundredSceneMgrSington.OnPlayerLeave(p) + } + //from gameserver, so don't need send msg + return true +} + +//观众坐下 +//func (this *Scene) AudienceSit(p *Player, pos int) bool { +// logger.Logger.Infof("(this *Scene:%v) AudienceSit(%v, %v, %v) ", this.sceneId, p.SnId, pos) +// if _, exist := this.audiences[p.SnId]; exist { +// if pos == -1 && !this.IsHundredScene() { //自动匹配;百人场没座位概念 +// for i := 0; i < this.playerNum; i++ { +// if this.seats[i] == nil { +// pos = i +// break +// } +// } +// } +// if pos != -1 || this.IsHundredScene() { +// if !this.IsHundredScene() { +// if this.seats[pos] != nil { +// return false +// } +// p.pos = pos +// p.applyPos = -1 +// this.seats[pos] = p +// } +// delete(this.audiences, p.SnId) +// } +// +// p.scene = this +// this.players[p.SnId] = p +// +// NpcServerAgentSington.OnPlayerEnterScene(this, p) +// if this.IsCoinScene() { +// CoinSceneMgrSingleton.OnPlayerEnter(p, int32(this.sceneId)) +// } else if this.IsHallScene() { +// PlatformMgrSingleton.OnPlayerEnterScene(this, p) +// } +// +// msg := &protocol.WGAudienceSit{ +// SnId: proto.Int32(p.SnId), +// SceneId: proto.Int(this.sceneId), +// Pos: proto.Int(pos), +// } +// p.takeCoin = p.Coin +// msg.TakeCoin = proto.Int64(p.Coin) +// proto.SetDefaults(msg) +// this.SendToGame(int(protocol.MmoPacketID_PACKET_WG_AUDIENCESIT), msg) +// this.lastTime = time.Now() +// return true +// } +// return false +//} + +func (this *Scene) AudienceSit(p *Player, pos int) bool { + logger.Logger.Infof("(this *Scene:%v) AudienceSit(%v, %v, %v) ", this.sceneId, p.SnId, pos, this.dbGameFree.GetId()) + if _, exist := this.audiences[p.SnId]; exist { + delete(this.audiences, p.SnId) + p.scene = this + this.players[p.SnId] = p + + //NpcServerAgentSington.OnPlayerEnterScene(this, p) + if this.IsCoinScene() { + //CoinSceneMgrSingleton.OnPlayerEnter(p, this.dbGameFree.GetId()) + } else if this.IsHallScene() { + + } + msg := &serverproto.WGAudienceSit{ + SnId: proto.Int32(p.SnId), + SceneId: proto.Int(this.sceneId), + Pos: proto.Int(pos), + } + p.takeCoin = p.Coin + msg.TakeCoin = proto.Int64(p.Coin) + proto.SetDefaults(msg) + this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_AUDIENCESIT), msg) + this.lastTime = time.Now() + return true + } + return false +} + +func (this *Scene) HasPlayer(p *Player) bool { + if _, exist := this.players[p.SnId]; exist { + return true + } + return false +} + +func (this *Scene) HasAudience(p *Player) bool { + if _, exist := this.audiences[p.SnId]; exist { + return true + } + return false +} + +func (this *Scene) GetPlayer(id int32) *Player { + if p, exist := this.players[id]; exist { + return p + } + return nil +} + +func (this *Scene) GetAudience(id int32) *Player { + if p, exist := this.audiences[id]; exist { + return p + } + return nil +} + +func (this *Scene) GetPlayerPos(snId int32) int { + for index, value := range this.seats { + if value == nil { + continue + } + if value.SnId == snId { + return index + } + } + return -1 +} + +func (this *Scene) GetPlayerCnt() int { + return len(this.players) +} + +func (this *Scene) GetAudienceCnt() int { + return len(this.audiences) +} + +func (this *Scene) IsFull() bool { + return this.GetPlayerCnt() >= this.playerNum +} + +func (this *Scene) IsEmpty() bool { + return this.GetPlayerCnt() == 0 +} + +func (this *Scene) AllIsRobot() bool { + return len(this.players) == this.robotNum +} + +func (this *Scene) OnClose() { + scDestroyRoom := &hallproto.SCDestroyRoom{ + RoomId: proto.Int(this.sceneId), + OpRetCode: hallproto.OpResultCode_Game_OPRC_Sucess_Game, + IsForce: proto.Int(1), + } + proto.SetDefaults(scDestroyRoom) + this.Broadcast(int(hallproto.GameHallPacketID_PACKET_SC_DESTROYROOM), scDestroyRoom, 0) + + this.closed = true + PlatformMgrSingleton.OnChangeSceneState(this, this.closed) + this.sp.OnStop(this) + //NpcServerAgentSington.OnSceneClose(this) + for _, p := range this.players { + this.DelPlayer(p) + } + for _, p := range this.audiences { + this.DelAudience(p) + } + this.players = nil + this.audiences = nil + this.gameSess = nil +} + +func (this *Scene) SendToGame(packetId int, pack interface{}) bool { + if this.gameSess != nil { + this.gameSess.Send(packetId, pack) + return true + } + return false +} +func (this *Scene) SendToClient(packetid int, rawpack interface{}, excludeId int32) { + for snid, value := range this.players { + if snid == excludeId { + continue + } + value.SendToClient(packetid, rawpack) + } +} +func (this *Scene) BilledRoomCard(snid []int32) { + if this.sp != nil { + this.sp.BilledRoomCard(this, snid) + } +} + +func (this *Scene) IsLongTimeInactive() bool { + tNow := time.Now() + //删除超过指定不活跃时间的房间 + if len(this.players) == 0 && tNow.Sub(this.lastTime) > time.Second*time.Duration(model.GameParamData.SceneMaxIdle) { + return true + } + return false +} + +func (this *Scene) ForceDelete(isManual bool) { + this.manualDelete = isManual + this.deleting = true + this.force = true + PlatformMgrSingleton.OnChangeSceneState(this, this.force) + pack := &serverproto.WGDestroyScene{ + SceneId: proto.Int(this.sceneId), + } + proto.SetDefaults(pack) + this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_DESTROYSCENE), pack) + logger.Logger.Warnf("(this *Scene) ForceDelete() sceneid=%v", this.sceneId) + + //if this.sceneId == SceneMgrSingleton.GetDgSceneId() { + // for _, value := range PlayerMgrSington.sidMap { + // if value.scene == nil { + // continue + // } + // if value.scene.sceneId == SceneMgrSingleton.GetDgSceneId() { + // value.DgGameLogout() + // pack := &hallproto.SCLeaveDgGame{} + // pack.OpRetCode = hallproto.OpResultCode_Game_OPRC_Sucess_Game + // value.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_LEAVEDGGAME), pack) + // } + // } + //} + if this.IsPreCreateScene() { + CoinSceneMgrSingleton.delayCache = append(CoinSceneMgrSingleton.delayCache, CreateRoomCache{gameFreeId: this.dbGameFree.GetId(), platformName: this.limitPlatform.IdStr}) + } +} + +func (this *Scene) Shutdown() { + if this.hadCost && this.sp != nil { + this.sp.OnShutdown(this) + } +} + +// 小游戏场 +func (this *Scene) IsMiniGameScene() bool { + return this.sceneId >= common.MiniGameSceneStartId && this.sceneId < common.MiniGameSceneMaxId +} + +// 比赛场 +func (this *Scene) IsMatchScene() bool { + return this.sceneId >= common.MatchSceneStartId && this.sceneId < common.MatchSceneMaxId +} + +// 大厅场 +func (this *Scene) IsHallScene() bool { + return this.sceneId >= common.HallSceneStartId && this.sceneId < common.HallSceneMaxId +} + +// 金币场 +func (this *Scene) IsCoinScene() bool { + return this.sceneId >= common.CoinSceneStartId && this.sceneId < common.CoinSceneMaxId +} + +// 百人场 +func (this *Scene) IsHundredScene() bool { + return this.sceneId >= common.HundredSceneStartId && this.sceneId < common.HundredSceneMaxId +} + +// 私人房间 +func (this *Scene) IsPrivateScene() bool { + return this.sceneId >= common.PrivateSceneStartId && this.sceneId < common.PrivateSceneMaxId || this.sceneMode == common.SceneMode_Private +} + +func (this *Scene) IsRankMatch() bool { + if this.dbGameFree == nil { + return false + } + return this.dbGameFree.RankType > 0 +} + +func (this *Scene) IsTestScene() bool { + if this.dbGameFree != nil { + return this.dbGameFree.GetSceneType() == -1 + } + if len(this.paramsEx) > 0 { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(this.paramsEx[0]) + if dbGameFree != nil { + return dbGameFree.GetSceneType() == -1 + } + } + return false +} + +func (this *Scene) GetSceneName() string { + if len(this.paramsEx) > 0 { + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(this.paramsEx[0]) + if dbGameFree != nil { + return dbGameFree.GetName() + dbGameFree.GetTitle() + } + } + return "[unknow scene name]" +} +func (this *Scene) RandRobotCnt() { + if len(this.paramsEx) > 0 { + gps := PlatformMgrSingleton.GetGameFree(this.limitPlatform.IdStr, this.paramsEx[0]) + if gps != nil { + dbGameFree := gps.DbGameFree + if gps.GroupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId) + if pgg != nil { + dbGameFree = pgg.DbGameFree + } + } + numrng := dbGameFree.GetRobotNumRng() + if len(numrng) >= 2 { + if numrng[1] == numrng[0] { + this.robotLimit = int(numrng[0]) + } else { + if numrng[1] < numrng[0] { + numrng[1], numrng[0] = numrng[0], numrng[1] + } + this.robotLimit = int(numrng[1]) //int(numrng[0] + rand.Int31n(numrng[1]-numrng[0]) + 1) + } + } + } + } +} + +func (this *Scene) isPlatform(platform string) bool { + if platform == "0" || platform == this.limitPlatform.IdStr { + return true + } + return false +} + +func (this *Scene) GetWhitePlayerCnt() int { + var cnt int + for _, p := range this.players { + if p.WBLevel > 0 { + cnt++ + } + } + return cnt +} + +func (this *Scene) GetBlackPlayerCnt() int { + var cnt int + for _, p := range this.players { + if p.WBLevel < 0 { + cnt++ + } + } + return cnt +} + +func (this *Scene) GetLostPlayerCnt() int { + var cnt int + for _, p := range this.players { + if p.GDatas != nil { + if d, exist := p.GDatas[this.dbGameFree.GetGameDif()]; exist { + if d.Statics.TotalIn > d.Statics.TotalOut { + cnt++ + } + } + } + } + return cnt +} + +func (this *Scene) GetWinPlayerCnt() int { + var cnt int + for _, p := range this.players { + if p.GDatas != nil { + if d, exist := p.GDatas[this.dbGameFree.GetGameDif()]; exist { + if d.Statics.TotalIn < d.Statics.TotalOut { + cnt++ + } + } + } + } + return cnt +} + +func (this *Scene) GetTruePlayerCnt() int { + return len(this.players) - this.robotNum +} + +// 炸金花房间有几个新手 +func (this *Scene) GetFoolPlayerCnt() int { + var cnt int + for _, p := range this.players { + if p.IsFoolPlayer != nil && p.IsFoolPlayer[this.dbGameFree.GetGameDif()] { + cnt++ + } + } + return cnt +} + +func (this *Scene) GetPlayerType(gameid, gamefreeid int32) (types []int32) { + for _, p := range this.players { + t := int32(0) + if p.IsRob { + t = common.PlayerType_Rob + } else if p.WBLevel < 0 { + t = common.PlayerType_Black + } else if p.WBLevel > 0 { + t = common.PlayerType_White + } else { + pt := p.CheckType(gameid, gamefreeid) + if pt != nil { + t = pt.GetId() + } else { + t = common.PlayerType_Undefine + } + } + if !common.InSliceInt32(types, t) { + types = append(types, t) + } + } + return +} + +func (this *Scene) Broadcast(packetid int, msg rawproto.Message, excludeSid int64) { + mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion) + for _, p := range this.players { + if p != nil { + if p.sid != excludeSid { + if p.gateSess != nil && p.IsOnLine() { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: p.sid, + }, + }) + } + } + } + } + for _, p := range this.audiences { + if p != nil && p.sid != excludeSid { + if p.gateSess != nil && p.IsOnLine() { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{ + Mccs: &srvlibproto.MCClientSession{ + SId: p.sid, + }, + }) + } + } + } + + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetid, msg, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} + +func (this *Scene) HasSameIp(ip string) bool { + for _, p := range this.players { + if !p.IsRob { + if p.GMLevel == 0 && p.Ip == ip { + return true + } + } + } + + return false +} + +func (this *Scene) IsPreCreateScene() bool { + return this.dbGameFree.GetCreateRoomNum() > 0 +} + +func (this *Scene) PlayerTryChange() { + var member []*Player + var player *Player + for _, value := range this.players { + if !value.IsRob { + member = append(member, value) + player = value + } + } + if len(member) <= 1 { + return + } + gameFreeId := this.dbGameFree.GetId() + gameConfig := PlatformMgrSingleton.GetGameFree(player.Platform, gameFreeId) + if gameConfig != nil && gameConfig.DbGameFree.GetMatchMode() == 1 { + return + } + for i := 0; i < len(member)-1; i++ { + p := member[i] + other := member[i+1:] + if this.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlaceBySnid(other, p, + this.dbGameFree.GetGameId(), this.dbGameFree.GetSamePlaceLimit()) { + if p.scene.IsPrivateScene() { + //if ClubSceneMgrSington.PlayerInChanging(p) { + // continue + //} + //ClubSceneMgrSington.PlayerTryChange(p, gameFreeId, []int32{int32(this.sceneId)}, false) + } else { + if CoinSceneMgrSingleton.PlayerInChanging(p) { + continue + } + excludeSceneIds := p.lastSceneId[gameFreeId] + CoinSceneMgrSingleton.PlayerTryChange(p, gameFreeId, excludeSceneIds, false) + } + } + + } +} + +func (this *Scene) GetParamEx(idx int) int32 { + if idx < 0 || idx > len(this.paramsEx) { + return -1 + } + + return this.paramsEx[idx] +} + +func (this *Scene) SetParamEx(idx int, val int32) { + cnt := len(this.paramsEx) + if idx >= 0 && idx < cnt { + this.paramsEx[idx] = val + } +} + +func (this *Scene) TryForceDelectMatchInfo() { + if !this.IsMatchScene() { + return + } + if len(this.players) == 0 { + return + } + if this.matchId == 0 { + return + } + if players, exist := TournamentMgr.players[this.matchId]; exist { + for _, player := range this.players { + if player != nil && !player.IsRob { + if TournamentMgr.IsMatching(player.SnId) { + delete(players, player.SnId) + } + } + } + } +} diff --git a/worldsrv/scene_minigame.go b/worldsrv/scene_minigame.go new file mode 100644 index 0000000..43052d2 --- /dev/null +++ b/worldsrv/scene_minigame.go @@ -0,0 +1,125 @@ +package main + +// +//import ( +// "math/rand" +// +// "mongo.games.com/game/common" +// "mongo.games.com/game/proto" +// "mongo.games.com/game/protocol/mngame" +// server_proto "mongo.games.com/game/protocol/server" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/srvlib" +// srvlibproto "mongo.games.com/goserver/srvlib/protocol" +//) +// +//func (s *Scene) PlayerEnterMiniGame(p *Player) bool { +// s.players[p.SnId] = p +// s.gameSess.AddPlayer(p) +// takeCoin := p.Coin +// leaveCoin := int64(0) +// gameTimes := rand.Int31n(100) +// if p.IsRob { +// takerng := s.dbGameFree.GetRobotTakeCoin() +// if len(takerng) >= 2 && takerng[1] > takerng[0] { +// if takerng[0] < s.dbGameFree.GetLimitCoin() { +// takerng[0] = s.dbGameFree.GetLimitCoin() +// } +// takeCoin = int64(common.RandInt(int(takerng[0]), int(takerng[1]))) +// } else { +// maxlimit := int64(s.dbGameFree.GetMaxCoinLimit()) +// if maxlimit != 0 && p.Coin > maxlimit { +// logger.Logger.Trace("Player coin:", p.Coin) +// //在下限和上限之间随机,并对其的100的整数倍 +// takeCoin = int64(common.RandInt(int(s.dbGameFree.GetLimitCoin()), int(maxlimit))) +// logger.Logger.Trace("Take coin:", takeCoin) +// } +// } +// takeCoin = takeCoin / 100 * 100 +// //离场金币 +// leaverng := s.dbGameFree.GetRobotLimitCoin() +// if len(leaverng) >= 2 { +// leaveCoin = int64(leaverng[0] + rand.Int31n(leaverng[1]-leaverng[0])) +// } +// +// if takeCoin > p.Coin { +// takeCoin = p.Coin +// } +// } +// +// if p.IsRob { +// s.robotNum++ +// p.RobotRandName() +// p.RobRandVipWhenEnterRoom(takeCoin) +// name := s.GetSceneName() +// logger.Logger.Tracef("(this *Scene) PlayerEnter(%v) robot(%v) robotlimit(%v)", name, s.robotNum, s.robotLimit) +// } +// +// data, err := p.MarshalData(s.gameId) +// if err == nil { +// var gateSid int64 +// if p.gateSess != nil { +// if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { +// sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) +// gateSid = sessionId.Get() +// } +// } +// isQuMin := false +// //if !p.IsRob { +// // pt := PlatformMgrSingleton.GetPackageTag(p.PackageID) +// // if pt != nil && pt.SpreadTag == 1 { +// // isQuMin = true +// // } +// //} +// p.miniScene[s.dbGameFree.Id] = s +// msg := &server_proto.WGPlayerEnterMiniGame{ +// Sid: proto.Int64(p.sid), +// SnId: proto.Int32(p.SnId), +// GateSid: proto.Int64(gateSid), +// SceneId: proto.Int(s.sceneId), +// PlayerData: data, +// IsQM: proto.Bool(isQuMin), +// } +// sa, err2 := p.MarshalSingleAdjustData(s.dbGameFree.Id) +// if err2 == nil && sa != nil { +// msg.SingleAdjust = sa +// } +// msg.TakeCoin = proto.Int64(takeCoin) +// msg.ExpectLeaveCoin = proto.Int64(leaveCoin) +// msg.ExpectGameTimes = proto.Int32(gameTimes) +// proto.SetDefaults(msg) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERENTER_MINIGAME), msg) +// } +// return true +//} +// +//func (s *Scene) PlayerLeaveMiniGame(p *Player) bool { +// var gateSid int64 +// if p.gateSess != nil { +// if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil { +// sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0) +// gateSid = sessionId.Get() +// } +// } +// +// delete(p.miniScene, s.dbGameFree.Id) +// +// msg := &server_proto.WGPlayerLeaveMiniGame{ +// Sid: proto.Int64(p.sid), +// SnId: proto.Int32(p.SnId), +// GateSid: proto.Int64(gateSid), +// SceneId: proto.Int(s.sceneId), +// } +// proto.SetDefaults(msg) +// s.SendToGame(int(server_proto.SSPacketID_PACKET_WG_PLAYERLEAVE_MINIGAME), msg) +// +// delete(s.players, p.SnId) +// return true +//} +// +//func (s *Scene) RedirectMiniGameMsg(p *Player, msg *mngame.CSMNGameDispatcher) bool { +// msg.Id = int32(s.sceneId) //换成真实房间id +// msg.SnId = p.SnId +// s.SendToGame(int(mngame.MNGamePacketID_PACKET_CS_MNGAME_DISPATCHER), msg) +// return true +//} diff --git a/worldsrv/scenelimit.go b/worldsrv/scenelimit.go new file mode 100644 index 0000000..59f48d4 --- /dev/null +++ b/worldsrv/scenelimit.go @@ -0,0 +1,163 @@ +package main + +import ( + "fmt" + + "mongo.games.com/game/common" +) + +var sceneLimitMgr = SceneLimitManager{ + SameRoomData: make(map[string][][]int32), +} + +type SceneLimitManager struct { + SameRoomData map[string][][]int32 //用户的同房数据,保存了用户一局游戏中,房间人员的SNID +} + +func (m *SceneLimitManager) Key(gameid, snid int32) string { + return fmt.Sprintf("%v-%v", snid, gameid) +} + +// LimitSamePlace 连续同局检测 +func (m *SceneLimitManager) LimitSamePlace(player *Player, s *Scene) bool { + if player.IsRob { + return false + } + key := m.Key(int32(s.gameId), player.SnId) + var logArr = m.SameRoomData[key] + var samePlaceLimit = int(s.dbGameFree.GetSamePlaceLimit()) + if len(logArr) > samePlaceLimit { + logArr = logArr[len(logArr)-samePlaceLimit:] + m.SameRoomData[key] = logArr + } + //新用户的防伙牌数据中,有没有场景中用户的检查 + for _, value := range s.players { + if value.IsRob { + continue + } + limitCount := 0 + for _, log := range logArr { + if common.InSliceInt32(log, value.SnId) { + limitCount++ + } + } + if limitCount >= samePlaceLimit { + return true + } + } + //场景中用户的防伙牌数据中,有没有新用户的检查 + for _, value := range s.players { + if value.IsRob { + continue + } + key := m.Key(int32(s.gameId), value.SnId) + var logArr = m.SameRoomData[key] + limitCount := 0 + for _, log := range logArr { + if common.InSliceInt32(log, player.SnId) { + limitCount++ + } + } + if limitCount >= samePlaceLimit { + return true + } + } + return false +} + +// LimitSamePlaceBySnid 连续同局检测 +// limit 最大同局数 +func (m *SceneLimitManager) LimitSamePlaceBySnid(member []*Player, player *Player, gameId, limit int32) bool { + if player.IsRob { + return false + } + key := m.Key(gameId, player.SnId) + var logArr = m.SameRoomData[key] + var samePlaceLimit = int(limit) + if len(logArr) > samePlaceLimit { + logArr = logArr[len(logArr)-samePlaceLimit:] + m.SameRoomData[key] = logArr + } + //新用户的防伙牌数据中,有没有场景中用户的检查 + for _, value := range member { + if value.IsRob { + continue + } + limitCount := 0 + for _, log := range logArr { + if common.InSliceInt32(log, value.SnId) { + limitCount++ + } + } + if limitCount >= samePlaceLimit { + return true + } + } + //场景中用户的防伙牌数据中,有没有新用户的检查 + for _, value := range member { + if value.IsRob { + continue + } + key := m.Key(gameId, value.SnId) + var logArr = m.SameRoomData[key] + limitCount := 0 + for _, log := range logArr { + if common.InSliceInt32(log, player.SnId) { + limitCount++ + } + } + if limitCount >= samePlaceLimit { + return true + } + } + return false +} + +// ReceiveData 同局记录 +func (m *SceneLimitManager) ReceiveData(gameid int32, data []int32) { + for _, value := range data { + key := m.Key(gameid, value) + m.SameRoomData[key] = append(m.SameRoomData[key], data) + } +} + +// LimitAvgPlayer 人数的平均分配问题 +func (m *SceneLimitManager) LimitAvgPlayer(s *Scene, totlePlayer int) bool { + scenePlayerCount := s.GetTruePlayerCnt() + switch { + case totlePlayer > 1 && totlePlayer < 15: + //4、如果游戏场的同时在线人数2-14人时,系统每2个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + // 如果有机器人,系统加入机器人,并且保证同桌机器人数量1-3个 + if scenePlayerCount > 2 { + return true + } + case totlePlayer >= 15 && totlePlayer < 22: + //5、如果游戏场的同时在线人数15-21人,系统每3个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + // 如果有机器人,系统加入机器人,并且保证同桌机器人数量1-3个; + if scenePlayerCount > 3 { + return true + } + case totlePlayer >= 22 && totlePlayer < 29: + //6、如果游戏场的同时在线人数22-28人,系统每4个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + // 如果有机器人,系统加入机器人,并且保证同桌机器人数量1-3个; + if scenePlayerCount > 4 { + return true + } + case totlePlayer >= 29 && totlePlayer < 35: + //7、如果游戏场的同时在线人数29-35人,系统每5个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + if scenePlayerCount > 5 { + return true + } + case totlePlayer >= 35 && totlePlayer < 42: + //8、如果游戏场的同时在线人数36-42人,系统每6个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + if scenePlayerCount > 6 { + return true + } + case totlePlayer >= 43: + //9、如果游戏场的同时在线人数43人以上时,系统每7个人分一个桌,如果有剩余的没有分桌,随机找一个桌子进行分配; + if scenePlayerCount > 7 { + return true + } + } + return false +} diff --git a/worldsrv/scenemgr.go b/worldsrv/scenemgr.go new file mode 100644 index 0000000..cc9ce6f --- /dev/null +++ b/worldsrv/scenemgr.go @@ -0,0 +1,388 @@ +package main + +import ( + "math" + "sort" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + serverproto "mongo.games.com/game/protocol/server" + webapi2 "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" +) + +func init() { + ClockMgrSington.RegisteSinker(SceneMgrSingleton) +} + +var SceneMgrSingleton = &SceneMgr{ + scenes: make(map[int]*Scene), + autoId: common.MatchSceneStartId, + coinSceneAutoId: common.CoinSceneStartId, + hundredSceneAutoId: common.HundredSceneStartId, +} + +// SceneMgr 房间管理器 +type SceneMgr struct { + BaseClockSinker // 驱动时间事件 + + scenes map[int]*Scene // sceneid:Scene + autoId int // 比赛场房间号 + coinSceneAutoId int // 金币场房间号 + hundredSceneAutoId int // 百人场房间号 +} + +// AllocReplayCode 获取回访码 +func (m *SceneMgr) AllocReplayCode() string { + code, _ := model.GetOneReplayId() + return code +} + +// GenOneCoinSceneId 生产一个金币场房间id +func (m *SceneMgr) GenOneCoinSceneId() int { + m.coinSceneAutoId++ + if m.coinSceneAutoId > common.CoinSceneMaxId { + m.coinSceneAutoId = common.CoinSceneStartId + } + return m.coinSceneAutoId +} + +// GenOneHundredSceneId 生产一个百人场房间id +func (m *SceneMgr) GenOneHundredSceneId() int { + m.hundredSceneAutoId++ + if m.hundredSceneAutoId > common.HundredSceneMaxId { + m.hundredSceneAutoId = common.HundredSceneStartId + } + return m.hundredSceneAutoId +} + +// GenOneMatchSceneId 生产一个比赛场房间id +func (m *SceneMgr) GenOneMatchSceneId() int { + m.autoId++ + if m.autoId > common.MatchSceneMaxId { + m.autoId = common.MatchSceneStartId + } + return m.autoId +} + +// GetScene 获取房间对象 +func (m *SceneMgr) GetScene(sceneId int) *Scene { + if s, exist := m.scenes[sceneId]; exist && !s.deleting { + return s + } + return nil +} + +// GetScenesByGame 根据游戏id查询房间 +func (m *SceneMgr) GetScenesByGame(gameId int) []*Scene { + var scenes []*Scene + for _, value := range m.scenes { + if value.gameId == gameId { + scenes = append(scenes, value) + } + } + return scenes +} + +// GetScenesByGameFreeId 根据场次id查询房间 +func (m *SceneMgr) GetScenesByGameFreeId(gameFreeId int32) []*Scene { + var scenes []*Scene + for _, value := range m.scenes { + if value.dbGameFree.GetId() == gameFreeId { + scenes = append(scenes, value) + } + } + return scenes +} + +// MarshalAllRoom 获取房间列表 +func (m *SceneMgr) MarshalAllRoom(platform string, groupId, gameId int, gameMode, clubId, sceneMode, sceneId int, + gameFreeId, snId int32, start, end, pageSize int32) ([]*webapi2.RoomInfo, int32, int32) { + roomInfo := make([]*webapi2.RoomInfo, 0, len(m.scenes)) + var isNeedFindAll = false + if model.GameParamData.IsFindRoomByGroup && platform != "" && snId != 0 && gameId == 0 && + gameMode == 0 && sceneId == -1 && groupId == 0 && clubId == 0 && sceneMode == 0 { + p := PlayerMgrSington.GetPlayerBySnId(snId) + if p != nil && p.Platform == platform { + isNeedFindAll = true + } + } + for _, s := range m.scenes { + if (((s.limitPlatform != nil && s.limitPlatform.IdStr == platform) || platform == "") && + ((s.gameId == gameId && s.gameMode == gameMode) || gameId == 0) && + (s.sceneId == sceneId || sceneId == 0) && (s.groupId == int32(groupId) || groupId == 0) && + (s.ClubId == int32(clubId) || clubId == 0) && (s.dbGameFree.GetId() == gameFreeId || gameFreeId == 0) && + (s.sceneMode == sceneMode || sceneMode == -1)) || isNeedFindAll { + var platformName string + if s.limitPlatform != nil { + platformName = s.limitPlatform.IdStr + } + + si := &webapi2.RoomInfo{ + Platform: platformName, + SceneId: int32(s.sceneId), + GameId: int32(s.gameId), + GameMode: int32(s.gameMode), + SceneMode: int32(s.sceneMode), + GroupId: s.groupId, + Creator: s.creator, + Agentor: s.agentor, + ReplayCode: s.replayCode, + Params: s.params, + PlayerCnt: int32(len(s.players) - s.robotNum), + RobotCnt: int32(s.robotNum), + CreateTime: s.createTime.Unix(), + BaseScore: s.dbGameFree.BaseScore, + } + if common.IsLocalGame(s.gameId) { + si.BaseScore = s.BaseScore + } + if s.paramsEx != nil && len(s.paramsEx) > 0 { + si.GameFreeId = s.paramsEx[0] + } + if s.starting { + si.Start = 1 + } else { + si.Start = 0 + } + if s.IsHundredScene() { + si.Start = 1 + } + if s.gameSess != nil { + si.SrvId = s.gameSess.GetSrvId() + } + cnt := 0 + total := len(s.players) + robots := []int32{} + + isContinue := false + if snId != 0 { + for _, p := range s.players { + if p.SnId == int32(snId) { + isContinue = true + break + } + } + } else { + isContinue = true + } + if !isContinue { + continue + } + + //优先显示玩家 + for id, p := range s.players { + if !p.IsRob || total < 10 { + si.PlayerIds = append(si.PlayerIds, id) + cnt++ + } else { + robots = append(robots, id) + } + if cnt > 10 { + break + } + } + //不够再显示机器人 + if total > cnt && cnt < 10 && len(robots) != 0 { + for i := 0; cnt < 10 && i < len(robots); i++ { + si.PlayerIds = append(si.PlayerIds, robots[i]) + cnt++ + if cnt > 10 { + break + } + } + } + roomInfo = append(roomInfo, si) + } + } + + sort.Slice(roomInfo, func(i, j int) bool { + if roomInfo[i].CreateTime < roomInfo[j].CreateTime || + (roomInfo[i].CreateTime == roomInfo[j].CreateTime && roomInfo[i].SceneId < roomInfo[j].SceneId) { + return true + } + return false + }) + + //分页处理 + roomSum := float64(len(roomInfo)) //房间总数 + pageCount := int32(math.Ceil(roomSum / float64(pageSize))) //总页数 + if roomSum <= float64(start) { + start = 0 + } + if roomSum < float64(end) { + end = int32(roomSum) + } + needList := roomInfo[start:end] //需要的房间列表 + if len(needList) > 0 { + return needList, pageCount, int32(roomSum) + } + return nil, 0, int32(roomSum) +} + +// CreateScene 创建房间 +func (m *SceneMgr) CreateScene(agentor, creator int32, sceneId, gameId, gameMode, sceneMode int, clycleTimes int32, + numOfGames int32, params []int32, gs *GameSession, limitPlatform *Platform, groupId int32, dbGameFree *serverproto.DB_GameFree, + paramsEx ...int32) *Scene { + logger.Logger.Trace("(this *SceneMgr) CreateScene ") + s := NewScene(agentor, creator, sceneId, gameId, gameMode, sceneMode, clycleTimes, numOfGames, params, gs, limitPlatform, groupId, + dbGameFree, paramsEx...) + if s == nil { + return nil + } + + m.scenes[sceneId] = s + if !s.IsMatchScene() && dbGameFree != nil && limitPlatform != nil { + //平台水池设置 + gs.DetectCoinPoolSetting(limitPlatform.IdStr, dbGameFree.GetId(), s.groupId) + } + + gs.AddScene(s) + var platformName string + if limitPlatform != nil { + platformName = limitPlatform.IdStr + } + logger.Logger.Infof("(this *SceneMgr) CreateScene (gameId=%v, mode=%v), SceneId=%v groupid=%v platform=%v", + gameId, gameMode, sceneId, groupId, platformName) + return s +} + +// CreateLocalGameScene 创建本地游戏房间 +func (m *SceneMgr) CreateLocalGameScene(creator int32, sceneId, gameId, gameSite, sceneMode int, clycleTimes int32, params []int32, + gs *GameSession, limitPlatform *Platform, playerNum int, dbGameFree *serverproto.DB_GameFree, baseScore, groupId int32, + paramsEx ...int32) *Scene { + logger.Logger.Trace("(this *SceneMgr) CreateLocalGameScene gameSite: ", gameSite, " sceneMode: ", sceneMode) + s := NewLocalGameScene(creator, sceneId, gameId, gameSite, sceneMode, clycleTimes, params, gs, limitPlatform, playerNum, dbGameFree, + baseScore, groupId, paramsEx...) + if s == nil { + return nil + } + + m.scenes[sceneId] = s + gs.AddScene(s) + + var platformName string + if limitPlatform != nil { + platformName = limitPlatform.IdStr + } + logger.Logger.Infof("(this *SceneMgr) CreateScene (gameId=%v), SceneId=%v platform=%v", gameId, sceneId, platformName) + return s +} + +// DestroyScene 解散房间 +// 房间销毁,游戏服务断开 +func (m *SceneMgr) DestroyScene(sceneId int, isCompleted bool) { + logger.Logger.Trace("(this *SceneMgr) DestroyScene ") + s, ok := m.scenes[sceneId] + if !ok || s == nil { + return + } + + switch { + case s.IsCoinScene(): + CoinSceneMgrSingleton.OnDestroyScene(s.sceneId) + + case s.IsHundredScene(): + HundredSceneMgrSington.OnDestroyScene(s.sceneId) + + case s.IsMatchScene(): + MatchSceneMgrSingleton.OnDestroyScene(s.sceneId) + } + + s.gameSess.DelScene(s) + s.OnClose() + delete(m.scenes, s.sceneId) + logger.Logger.Infof("(this *SceneMgr) DestroyScene, SceneId=%v", sceneId) +} + +func (m *SceneMgr) OnPlayerLeaveScene(s *Scene, p *Player) { + logger.Logger.Trace("(this *SceneMgr) OnPlayerLeaveScene", p.SnId) + + // 记录玩家在每个游戏场次最后进入的房间号 + // 只记录金币场 + if s.IsCoinScene() { + const MINHOLD = 10 + const MAXHOLD = 20 + holdCnt := MINHOLD + if s.csp != nil { + holdCnt = s.csp.GetHasTruePlayerSceneCnt() + 2 + if holdCnt < MINHOLD { + holdCnt = MINHOLD + } + if holdCnt > MAXHOLD { + holdCnt = MAXHOLD + } + } + if p.lastSceneId == nil { + p.lastSceneId = make(map[int32][]int32) + } + id := s.dbGameFree.GetId() + if sceneIds, exist := p.lastSceneId[id]; exist { + if !common.InSliceInt32(sceneIds, int32(s.sceneId)) { + sceneIds = append(sceneIds, int32(s.sceneId)) + cnt := len(sceneIds) + if cnt > holdCnt { + sceneIds = sceneIds[cnt-holdCnt:] + } + p.lastSceneId[id] = sceneIds + } + } else { + p.lastSceneId[id] = []int32{int32(s.sceneId)} + } + } +} + +// GetThirdScene 获取三方游戏房间 +func (m *SceneMgr) GetThirdScene(i webapi.IThirdPlatform) *Scene { + if i == nil { + return nil + } + sceneId := i.GetPlatformBase().SceneId + scene := m.scenes[sceneId] + if scene != nil { + return scene + } + + gs := GameSessMgrSington.GetMinLoadSess(i.GetPlatformBase().BaseGameID) + if gs != nil { + limitPlatform := PlatformMgrSingleton.GetPlatform(DefaultPlatform) + var gameMode = common.SceneMode_Thr + dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(i.GetPlatformBase().VultGameID) + scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, i.GetPlatformBase().BaseGameID, gameMode, int(common.SceneMode_Thr), 1, -1, + []int32{}, gs, limitPlatform, 0, dbGameFree, i.GetPlatformBase().VultGameID) + return scene + } else { + logger.Logger.Errorf("Get %v game min session failed.", i.GetPlatformBase().BaseGameID) + return nil + } +} + +//=========================ClockSinker=============================== + +// InterestClockEvent 接收所有时间事件 +func (m *SceneMgr) InterestClockEvent() int { + return 1 << CLOCK_EVENT_MINUTE +} + +func (m *SceneMgr) OnMiniTimer() { + // 解散空闲房间 + for _, s := range m.scenes { + if webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(s.gameId) != nil { + continue + } + if s.IsCoinScene() { + if s.IsLongTimeInactive() && s.dbGameFree.GetCreateRoomNum() == 0 { //预创建的房间,暂不过期销毁,判定依据:CreateRoomNum>0 + logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive CoinScene ForceDelete scene:%v IsLongTimeInactive", s.sceneId) + s.ForceDelete(false) + } + } else if s.IsPrivateScene() { + if s.IsLongTimeInactive() { + logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive PrivateScene ForceDelete scene:%v IsLongTimeInactive", s.sceneId) + s.ForceDelete(false) + } + } + } +} diff --git a/worldsrv/scenepolicy.go b/worldsrv/scenepolicy.go new file mode 100644 index 0000000..b475651 --- /dev/null +++ b/worldsrv/scenepolicy.go @@ -0,0 +1,86 @@ +package main + +import "mongo.games.com/goserver/core/logger" + +// 根据不同的房间模式,选择不同的房间业务策略 +var ScenePolicyPool map[int]map[int]ScenePolicy = make(map[int]map[int]ScenePolicy) + +type ScenePolicy interface { + //场景开启事件 + OnStart(s *Scene) + //场景关闭事件 + OnStop(s *Scene) + //场景心跳事件 + OnTick(s *Scene) + //玩家进入事件 + OnPlayerEnter(s *Scene, p *Player) + //玩家离开事件 + OnPlayerLeave(s *Scene, p *Player) + //系统维护关闭事件 + OnShutdown(s *Scene) + //获得场景的匹配因子(值越大越优先选择) + GetFitFactor(s *Scene, p *Player) int + //能否创建 + CanCreate(s *Scene, p *Player, mode, sceneType int, params []int32, isAgent bool) (bool, []int32) + //能否进入 + CanEnter(s *Scene, p *Player) int + //房间座位是否已满 + IsFull(s *Scene, p *Player, num int32) bool + //是否可以强制开始 + IsCanForceStart(s *Scene) bool + //结算房卡 + BilledRoomCard(s *Scene, snid []int32) + //需要几张房卡 + GetNeedRoomCardCnt(s *Scene) int32 + //房费模式 + GetRoomFeeMode(s *Scene) int32 + //游戏人数 + GetPlayerNum(s *Scene) int32 + //游戏底分 + GetBaseCoin(s *Scene) int + //游戏总局数 + GetTotalOfGames(s *Scene) int32 + // + GetNeedRoomCardCntDependentPlayerCnt(s *Scene) int32 + // + GetBetState() int32 + GetViewLogLen() int32 +} + +func GetScenePolicy(gameId, mode int) ScenePolicy { + if g, exist := ScenePolicyPool[gameId]; exist { + if p, exist := g[mode]; exist { + return p + } + } + return nil +} + +func RegisteScenePolicy(gameId, mode int, sp ScenePolicy) { + pool := ScenePolicyPool[gameId] + if pool == nil { + pool = make(map[int]ScenePolicy) + ScenePolicyPool[gameId] = pool + } + if pool != nil { + pool[mode] = sp + } +} + +func CheckGameConfigVer(ver int32, gameid int32, modeid int32) bool { + v := GetGameConfigVer(gameid, modeid) + logger.Logger.Info("Old game config ver:", v) + return ver == v +} + +func GetGameConfigVer(gameid int32, modeid int32) int32 { + sp := GetScenePolicy(int(gameid), int(modeid)) + if sp == nil { + return 0 + } + spd, ok := sp.(*ScenePolicyData) + if !ok { + return 0 + } + return spd.ConfigVer +} diff --git a/worldsrv/scenepolicydata.go b/worldsrv/scenepolicydata.go new file mode 100644 index 0000000..0cdc3cb --- /dev/null +++ b/worldsrv/scenepolicydata.go @@ -0,0 +1,344 @@ +package main + +import ( + "time" + + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/goserver/core/logger" +) + +const ( + SPDPCustomIndex_GamesOfCard int = iota //局数选项 + SPDPCustomIndex_PlayerNum //人数选项 + SPDPCustomIndex_RoomFeeMode //房费模式选项 + SPDPCustomIndex_LimitOption //新手限制选项 + SPDPCustomIndex_DoorOption //中途不可进选项 + SPDPCustomIndex_SameIPForbid //同IP不可进 + SPDPCustomIndex_BaseCoin //房间底分 +) + +// 创建房间选项 +const ( + CreateRoomParam_NumOfGames int = iota //局数选项 + CreateRoomParam_DoorOption //中途允许加入选项 + CreateRoomParam_SameIPForbid //同IP不可进 + CreateRoomParam_Max +) + +type ScenePolicyDataParam struct { + Name string + AliasName string + Desc string + Min int32 + Max int32 + Default int32 + Value []int32 + Value2 []int32 + CustomIndex int32 + index int +} + +type ScenePolicyData struct { + GameName string + GameId int32 + GameMode []int32 + CanForceStart bool + MinPlayerCnt int32 + DefaultPlayerCnt int32 + MaxIndex int32 + TimeFreeStart int32 + TimeFreeEnd int32 + DependentPlayerCnt bool + EnterAfterStart bool + ConfigVer int32 + PerGameTakeCard int32 + ViewLogCnt int32 + BetState int32 + Params []ScenePolicyDataParam + nameMap map[string]*ScenePolicyDataParam + aliasNameMap map[string]*ScenePolicyDataParam + customIndexParams []*ScenePolicyDataParam +} + +func alignto(val, align int32) int32 { + return (val + align - 1) / align +} + +func (spd *ScenePolicyData) Init() bool { + spd.nameMap = make(map[string]*ScenePolicyDataParam) + spd.aliasNameMap = make(map[string]*ScenePolicyDataParam) + if spd.MaxIndex > 0 { + spd.customIndexParams = make([]*ScenePolicyDataParam, spd.MaxIndex) + } + for i := 0; i < len(spd.Params); i++ { + spd.nameMap[spd.Params[i].Name] = &spd.Params[i] + spd.Params[i].index = i + spd.aliasNameMap[spd.Params[i].AliasName] = &spd.Params[i] + if spd.Params[i].CustomIndex >= 0 && spd.MaxIndex > 0 { + spd.customIndexParams[spd.Params[i].CustomIndex] = &spd.Params[i] + } + } + return true +} + +func (spd *ScenePolicyData) GetParam(idx int) *ScenePolicyDataParam { + if idx >= 0 && idx < len(spd.Params) { + return &spd.Params[idx] + } + return nil +} + +func (spd *ScenePolicyData) GetParamByIndex(idx int) *ScenePolicyDataParam { + if idx >= 0 && idx < len(spd.customIndexParams) { + return spd.customIndexParams[idx] + } + return nil +} + +func (spd *ScenePolicyData) GetParamByName(name string) *ScenePolicyDataParam { + if spdp, exist := spd.nameMap[name]; exist { + return spdp + } + return nil +} + +func (spd *ScenePolicyData) GetParamByAliasName(name string) *ScenePolicyDataParam { + if spdp, exist := spd.aliasNameMap[name]; exist { + return spdp + } + return nil +} + +func (spd *ScenePolicyData) IsTimeFree(mode int) bool { + //是否限时免费 + ts := int32(time.Now().Unix()) + if ts >= spd.TimeFreeStart && ts < spd.TimeFreeEnd { + return true + } + return false +} + +func (spd *ScenePolicyData) IsEnoughRoomCardCnt(s *Scene, p *Player, roomFeeParam, needCardCnt, playerNum int32, isAgent bool) bool { + return true +} + +func (spd *ScenePolicyData) CostRoomCard(s *Scene, roomFeeParam, needCardCnt, playerNum int32, snid []int32) { +} + +func (spd *ScenePolicyData) UpRoomCard(s *Scene, roomFeeParam, needCardCnt, playerNum int32) { +} + +//ScenePolicy interface + +// 能否进入 +func (spd *ScenePolicyData) CanCreate(s *Scene, p *Player, mode, sceneType int, params []int32, isAgent bool) (bool, []int32) { + //参数容错处理 + if len(params) < len(spd.Params) { + logger.Logger.Infof("ScenePolicyData.CanCreate param count not enough, need:%v get:%v", len(spd.Params), len(params)) + for i := len(params); i < len(spd.Params); i++ { + params = append(params, spd.Params[i].Default) + } + } + + //确保参数正确 + for i := 0; i < len(params); i++ { + if params[i] < spd.Params[i].Min || params[i] > spd.Params[i].Max { + params[i] = spd.Params[i].Default + } + } + + return true, params +} + +func (spd *ScenePolicyData) OnStart(s *Scene) { + +} + +func (spd *ScenePolicyData) ReturnRoomCard(s *Scene, roomFeeParam, needCardCnt, playerNum int32) { +} + +// 场景关闭事件 +func (spd *ScenePolicyData) OnStop(s *Scene) { +} + +// 场景心跳事件 +func (spd *ScenePolicyData) OnTick(s *Scene) { + +} + +// 玩家进入事件 +func (spd *ScenePolicyData) OnPlayerEnter(s *Scene, p *Player) { + +} + +// 玩家离开事件 +func (spd *ScenePolicyData) OnPlayerLeave(s *Scene, p *Player) { + +} + +// 系统维护关闭事件 +func (spd *ScenePolicyData) OnShutdown(s *Scene) { + if s.IsPrivateScene() { + PrivateSceneMgrSington.OnDestroyScene(s) + } +} + +// 获得场景的匹配因子(值越大越优先选择) +func (spd *ScenePolicyData) GetFitFactor(s *Scene, p *Player) int { + return len(s.players) +} + +// 是否满座了 +func (spd *ScenePolicyData) IsFull(s *Scene, p *Player, num int32) bool { + if s.HasPlayer(p) { + return false + } + return s.GetPlayerCnt() >= int(num) +} + +// 是否可以强制开始 +func (spd *ScenePolicyData) IsCanForceStart(s *Scene) bool { + return spd.CanForceStart && s.GetPlayerCnt() >= int(spd.MinPlayerCnt) +} + +// 结算房卡 +func (spd *ScenePolicyData) BilledRoomCard(s *Scene, snid []int32) { + //spd.CostRoomCard(s, spd.GetRoomFeeMode(s), spd.GetNeedRoomCardCnt(s), int32(len(s.players)), snid) +} + +func (spd *ScenePolicyData) getNeedRoomCardCnt(params []int32) int32 { + return 0 +} + +// 需求房卡数量 +func (spd *ScenePolicyData) GetNeedRoomCardCnt(s *Scene) int32 { + return 0 +} + +//func (spd *ScenePolicyData) getRoomFeeMode(params []int32) int32 { +// //if len(params) > 0 { +// // param := spd.GetParamByIndex(SPDPCustomIndex_RoomFeeMode) +// // if param != nil && param.index >= 0 && param.index < len(params) { +// // return params[param.index] +// // } +// //} +// return common.RoomFee_Owner +//} + +// 收费方式(AA|房主付费) +func (spd *ScenePolicyData) GetRoomFeeMode(s *Scene) int32 { + //if s != nil { + // return spd.getRoomFeeMode(s.params) + //} + return 0 +} + +func (spd *ScenePolicyData) GetNeedRoomCardCntDependentPlayerCnt(s *Scene) int32 { + return 0 +} + +// 能否进入 +func (spd *ScenePolicyData) CanEnter(s *Scene, p *Player) int { + param := spd.GetParamByIndex(SPDPCustomIndex_SameIPForbid) + if param != nil && len(s.params) <= param.index { + logger.Logger.Errorf("game param len too long %v", s.gameId) + } + + if param != nil && len(s.params) > param.index && s.params[param.index] != 0 { + ip := p.GetIP() + for i := 0; i < s.playerNum; i++ { + pp := s.seats[i] + if pp != nil && pp.GetIP() == ip { + return int(hall_proto.OpResultCode_Game_OPRC_SameIpForbid_Game) + } + } + } + + if !spd.EnterAfterStart { + if s.starting { + return int(hall_proto.OpResultCode_Game_OPRC_GameStarting_Game) + } + } + + if spd.EnterAfterStart { + if s.starting { + param := spd.GetParamByIndex(SPDPCustomIndex_DoorOption) + if param != nil && s.params[param.index] != 0 { + return int(hall_proto.OpResultCode_Game_OPRC_GameStarting_Game) + //} else { + // return int(hall_proto.OpResultCode_OPRC_SceneEnterForWatcher) + } + } + } + return 0 +} + +// 人数 +func (spd *ScenePolicyData) getPlayerNum(params []int32) int32 { + if len(params) > 0 { + param := spd.GetParamByIndex(SPDPCustomIndex_PlayerNum) + if param != nil { + idx := int(params[param.index]) + if idx >= 0 && idx < len(param.Value) { + val := param.Value[idx] + return val + } + } + } + return spd.DefaultPlayerCnt +} + +func (spd *ScenePolicyData) GetPlayerNum(s *Scene) int32 { + if s != nil { + return spd.getPlayerNum(s.params) + } + return spd.DefaultPlayerCnt +} + +func (spd *ScenePolicyData) getBaseCoin(params []int32) int { + if len(params) > 0 { + param := spd.GetParamByIndex(SPDPCustomIndex_BaseCoin) + if param != nil { + idx := int(params[param.index]) + if idx >= 0 && idx < len(param.Value) { + val := param.Value[idx] + return int(val) + } + } + } + return 0 +} + +func (spd *ScenePolicyData) GetBaseCoin(s *Scene) int { + if s != nil { + return spd.getBaseCoin(s.params) + } + return 0 +} + +// 局数 +func (spd *ScenePolicyData) GetTotalOfGames(s *Scene) int32 { + //if len(s.params) > 0 { + // param := spd.GetParamByIndex(SPDPCustomIndex_GamesOfCard) + // if param != nil { + // cardCostIdx := int(s.params[param.index]) + // if cardCostIdx >= 0 && cardCostIdx < len(param.Value) { + // costCardNum := param.Value[cardCostIdx] + // return costCardNum + // } else if int32(cardCostIdx) >= common.CUSTOM_PER_GAME_INDEX_BEG { //自定义局数 + // totalOfGames := int32(cardCostIdx) - common.CUSTOM_PER_GAME_INDEX_BEG + 1 + // return totalOfGames + // } + // } + //} + //return 4 + return s.totalRound +} + +func (spd *ScenePolicyData) GetBetState() int32 { + return spd.BetState +} + +func (spd *ScenePolicyData) GetViewLogLen() int32 { + return spd.ViewLogCnt +} diff --git a/worldsrv/schedule.go b/worldsrv/schedule.go new file mode 100644 index 0000000..69d753e --- /dev/null +++ b/worldsrv/schedule.go @@ -0,0 +1,99 @@ +package main + +//转移到schedulesrv中专门处理 +//import ( +// "time" + +// "mongo.games.com/game/model" + +// "mongo.games.com/goserver/core" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/schedule" +//) + +//func init() { +// core.RegisteHook(core.HOOK_BEFORE_START, func() error { +// //每日凌晨4点执行清理任务 +// //0 0 4 * * * +// task := schedule.NewTask("定期清理cardlog", "0 0 4 * * *", func() error { +// logger.Logger.Info("@@@执行定期清理任务@@@") +// tNow := time.Now() +// m := model.GameParamData.LogHoldDuration +// if m <= 0 { +// m = 1 +// } +// tStart := tNow.AddDate(0, int(-m), 0) +// changeInfo, err := model.RemoveCoinLog(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理coinlog失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理coinlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } +// //1个月前的回放记录 +// changeInfo, err = model.RemoveGameRecs(tStart) +// if err != nil { +// logger.Logger.Warnf("定期清理gamerec失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理gamerec成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } +// //APIlog +// changeInfo, err = model.RemoveAPILog(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理APIlog失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理APIlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } + +// //10天前的数据 +// tStart = tNow.AddDate(0, 0, -10) +// changeInfo, err = model.RemoveAgentGameRecs(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理user_agentgamerec失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理user_agentgamerec成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } + +// //3天前的数据 +// tStart = tNow.AddDate(0, 0, -3) +// changeInfo, err = model.RemoveSceneCoinLog(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理scenecoinlog失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理scenecoinlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } + +// //7天前的数据 +// tStart = tNow.AddDate(0, 0, -7) +// changeInfo, err = model.RemoveGameCoinLog(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理gamecoinlog失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理gamecoinlog成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } +// //游戏记录 +// changeInfo, err = model.RemoveGameLog(tStart.Unix()) +// if err != nil { +// logger.Logger.Warnf("定期清理游戏记录失败: %v", err) +// } else { +// logger.Logger.Warnf("定期清理游戏记录成功: updated:%v removed:%v", changeInfo.Updated, changeInfo.Removed) +// } +// return nil +// }) +// if task != nil { +// logger.Logger.Info("@@@执行定期清理任务@@@加入调度") +// schedule.AddTask(task.Taskname, task) +// } + +// //每日凌晨执行汇总任务 +// //0 0 0 * * * +// taskDay := schedule.NewTask("定期汇总玩家金币总额", "0 0 0 * * *", func() error { +// logger.Logger.Info("@@@执行定期汇总任务@@@") +// return model.AggregatePlayerCoin() +// }) +// if taskDay != nil { +// logger.Logger.Info("@@@执行定期汇总任务@@@加入调度") +// schedule.AddTask(taskDay.Taskname, taskDay) +// } +// return nil +// }) +//} diff --git a/worldsrv/serverctrl.go b/worldsrv/serverctrl.go new file mode 100644 index 0000000..43c180b --- /dev/null +++ b/worldsrv/serverctrl.go @@ -0,0 +1,19 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/mongo" +) + +var SrvIsMaintaining = true + +func init() { + common.RegisteServerCtrlCallback(func(code int32) { + switch code { + case common.SrvCtrlStateSwitchCode: + SrvIsMaintaining = !SrvIsMaintaining + case common.SrvCtrlResetMgoSession: + mongo.ResetAllSession() + } + }) +} diff --git a/worldsrv/shopmgr.go b/worldsrv/shopmgr.go new file mode 100644 index 0000000..8d8f376 --- /dev/null +++ b/worldsrv/shopmgr.go @@ -0,0 +1,1333 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "math/rand" + "slices" + "strconv" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/shop" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" +) + +const ( + BlindBox int32 = iota + 101 //盲盒 + FirstPay //首冲 + ContinuousPay //连充 + Exchange // 兑换商品 + ShopTypeStart int32 = 900000 //商品 +) + +// 消费类型 +const ( + ShopConsumeCoin = iota + 1 // 金币 + ShopConsumeDiamond // 钻石 + ShopConsumeMoney // 现金 + ExchangeConsumeCash //兑换消耗现金 + ShopConsumePhoneScore = 11 //手机积分 +) + +// page类型 +const ( + ShopPageCoin = iota + 1 //金币页面 + ShopPageDiamond //钻石页面 + ShopPageItem //道具页面 + ShopPageVip //VIP页面 + ShopPagePrivilege //VIP特权礼包 + ShopPagePhoneScore = 61 //手机积分商城 + ShopPagePhoneScore_google = 62 +) + +// 商品类型 +const ( + ShopTypeCoin = iota + 1 // 金币 + ShopTypeDiamond // 钻石 + ShopTypeItem // 道具 +) + +// 兑换商品状态 +const ( + Shop_Status_Keep = iota //0为待审核 + Shop_Status_Pass // 1为审核通过 + Shop_Status_Send // 2为已发货 + Shop_Status_NotSend // 3为审核不通过 + Shop_Status_Revoke // 4为撤单 +) + +const ( + VCard int32 = 30001 + JCard int32 = 30002 //金券 +) + +/* +1.兑换成功:兑换成功,请等待审核 +2.今日兑换已达上限,请明日再来 +3.该物品已被兑换完 +*/ +const ( + Err_ExShopNEnough string = "ExShopNotEnough" // 该物品已被兑换完 + Err_ExShopLimit string = "ExShopIsLimit" // 今日兑换已达上限,请明日再来 + Err_ExShopData string = "ExShopDataErr" // 收件人地址有误 +) + +var ShopMgrSington = &ShopMgr{ + ShopInfos: make(map[string]map[int32]*ShopInfo), + ExShops: make(map[string]*webapi_proto.ExchangeShopList), +} + +type ShopMgr struct { + ShopInfos map[string]map[int32]*ShopInfo // Platform:商品id:商品信息 + ExShops map[string]*webapi_proto.ExchangeShopList //兑换商品 消耗v卡 +} + +type ShopInfo struct { + Id int32 // 商品ID + Page int32 // 页面 1,金币页面 2,钻石页面 3,道具页面 + Order int32 // 排序 页面内商品的位置排序 + Location []int32 // 显示位置 第1位,竖版大厅 第2位,Tienlen1级选场 第3位,捕鱼1级选场 + Picture string // 图片id + Name string // 名称 + Label []int32 // 标签 + Ad int32 // 是否观看广告 0不看 + AdTime int32 // 观看几次广告 + RepeatTimes int32 // 领取次数 + CoolingTime []int32 // 观看冷却时间 + Type int32 // 获得类型 1,金币 2,钻石 3,道具类型 + Amount int64 // 获得数量 + AddArea []int32 // 加送百分比(比如加送10%,就配置110) + ItemId int32 // 获得道具ID + AddItemInfo []*shop.ItemInfo // 获得道具 + ConstType int32 // 购买消耗类型 1,金币 2,钻石 3,美金 4,柬埔寨币 + CostArea []int32 // 消耗数量区间 + VipLevel int32 //Vip等级限制 + Ratio int32 //权重 + EndTime int32 //新手礼包结束时间间隔 + //缓存数据 + AdLookedNum int32 //已经观看的次数 + AdReceiveNum int32 //已经领取的次数 + RemainingTime int32 + LastLookTime int32 + RoleAdded int32 + PetAdded int32 + RoleAddedId int32 //加成人物ID + PetAddedId int32 //加成宠物ID + FirstSwitch bool // 首冲翻倍 + AmountFinal int64 // 实际获得数量 +} + +type ExchangeShopInfo struct { + Id int32 //商品ID + Picture string // 图片 + Type int32 // 类型 1,话费2,实物 + Name string // 名称 + Rule string //规则说明 + ExType []*shop.ExchangeType // 获得道具 + OrderId string //兑换的订单Id + ExchangeTypeId int32 //兑换类型的id + ExchangeNum int32 //兑换数量 + +} + +func (this *ShopMgr) ModuleName() string { + return "ShopMgr" +} + +func (this *ShopMgr) Init() { +} + +// UpdateItemShop 更新商品信息 +func (this *ShopMgr) UpdateItemShop(cfgs *webapi_proto.ItemShopList) { + if cfgs == nil { + return + } + shopInfos := make(map[int32]*ShopInfo) + for _, itemShop := range cfgs.List { + var itemInfo []*shop.ItemInfo + for key, value := range itemShop.Award { + itemInfo = append(itemInfo, &shop.ItemInfo{ + ItemId: int32(key), + ItemNum: value, + }) + } + + shopInfos[itemShop.Id] = &ShopInfo{ + Id: itemShop.Id, + ItemId: itemShop.ItemId, + Page: itemShop.Page, + Order: itemShop.Order, + Type: itemShop.Type, + Location: itemShop.Location, + Picture: itemShop.Picture, + Name: itemShop.Name, + Ad: itemShop.Ad, + AdTime: itemShop.AdTime, + RepeatTimes: itemShop.RepeatTimes, + CoolingTime: itemShop.CoolingTime, + Label: itemShop.Label, + AddArea: itemShop.AddArea, + Amount: int64(itemShop.Amount), + ConstType: itemShop.ConstType, + CostArea: itemShop.CostArea, + AddItemInfo: itemInfo, + VipLevel: itemShop.VipLevel, + Ratio: itemShop.Ratio, + EndTime: itemShop.EndTime, + FirstSwitch: itemShop.FirstSwitch, + } + } + this.ShopInfos[cfgs.Platform] = shopInfos +} + +// UpExShop 更新兑换商品信息 +func (this *ShopMgr) UpExShop(cfgs *webapi_proto.ExchangeShopList) { + this.ExShops[cfgs.GetPlatform()] = cfgs +} + +func (this *ShopMgr) GetShopInfoProto(si *ShopInfo, p *Player, vipShopId int32) (shopInfo *shop.ShopInfo) { + if si == nil { + return nil + } + var itemInfo []*shop.ItemInfo + for _, i2 := range si.AddItemInfo { + itemInfo = append(itemInfo, &shop.ItemInfo{ + ItemId: i2.ItemId, + ItemNum: i2.ItemNum, + }) + } + added := int32(rand.Intn(int(si.AddArea[1])-int(si.AddArea[0])+1) + int(si.AddArea[0])) + consumptionAmount := int32(rand.Intn(int(si.CostArea[1])-int(si.CostArea[0])+1) + int(si.CostArea[0])) + amount := int64(si.Amount) + isBuy := false + if si.Page == ShopPageVip { + shopData := p.GetVipShopData(si.Id, vipShopId) + if shopData == nil { + logger.Logger.Errorf("Vip商城 没有当前物品 snid = %v,shopId = %v", p.SnId, si.Id) + return nil + } + added = shopData.AddArea + consumptionAmount = shopData.CostArea + amount = si.Amount * int64(consumptionAmount) + isBuy = shopData.IsBuy + } + shopInfo = &shop.ShopInfo{ + Id: si.Id, + AdLookedNum: si.AdLookedNum, + AdReceiveNum: si.AdReceiveNum, + LastLookTime: si.LastLookTime, + RoleAdded: si.RoleAdded, + PetAdded: si.PetAdded, + ItemId: si.ItemId, + Order: si.Order, + Page: si.Page, + Type: si.Type, + Location: si.Location, + Picture: si.Picture, + Name: si.Name, + Ad: si.Ad, + AdTime: si.AdTime, + RepeatTimes: si.RepeatTimes, + CoolingTime: si.CoolingTime, + Label: si.Label, + Added: added, + Amount: amount, + Consume: si.ConstType, + ConsumptionAmount: consumptionAmount, + AddItemInfo: itemInfo, + VipLevel: si.VipLevel, + EndTime: si.EndTime, + IsBuy: isBuy, + RoleAddedId: si.RoleAddedId, + AmountFinal: this.GetAmountFinal(p, si.Id, vipShopId), + } + return +} + +// GetShopInfo 获取玩家某个商品信息(例如玩家看广告领取) +func (this *ShopMgr) GetShopInfo(shopId int32, p *Player) *ShopInfo { + if shopId == 0 { + return nil + } + var shopInfo *ShopInfo + if shops, ok := this.ShopInfos[p.Platform]; ok { + if shopInf, ok1 := shops[shopId]; ok1 { + shopInfo = shopInf + } + } + if shopInfo != nil { + // AdLookedNum 已看次数 + // AdReceiveNum 已领次数 + // remainingTime 商品设置的冷却时长 + var AdLookedNum, AdReceiveNum, remainingTime int32 + var lastLookTime int64 + if shopInfo.Ad > 0 { + shopTotal := p.ShopTotal[shopInfo.Id] + if shopTotal != nil { + AdLookedNum = shopTotal.AdLookedNum + AdReceiveNum = shopTotal.AdReceiveNum + } + lastLookTime = p.ShopLastLookTime[shopInfo.Id] + if AdLookedNum < int32(len(shopInfo.CoolingTime)) { + remainingTime = shopInfo.CoolingTime[AdLookedNum] + } + if lastLookTime >= 0 { + dif := int32(time.Now().Unix() - lastLookTime) + if dif >= remainingTime { + remainingTime = 0 + } else { + remainingTime = remainingTime - dif + } + } + } + award, roleId := PetMgrSington.GetShopAward(shopInfo, p) + firstBuy := false + if shopInfo.FirstSwitch { + firstBuy = !slices.Contains(p.ShopID, int(shopInfo.Id)) + } + + return &ShopInfo{ + Id: shopInfo.Id, + Ad: shopInfo.Ad, + AdTime: shopInfo.AdTime, + RepeatTimes: shopInfo.RepeatTimes, + CoolingTime: shopInfo.CoolingTime, + AdLookedNum: AdLookedNum, + AdReceiveNum: AdReceiveNum, + RemainingTime: remainingTime, + LastLookTime: int32(lastLookTime), + RoleAdded: award, + ItemId: shopInfo.ItemId, + Order: shopInfo.Order, + Page: shopInfo.Page, + Type: shopInfo.Type, + Location: shopInfo.Location, + Picture: shopInfo.Picture, + Name: shopInfo.Name, + Label: shopInfo.Label, + AddArea: shopInfo.AddArea, + Amount: shopInfo.Amount, + ConstType: shopInfo.ConstType, + CostArea: shopInfo.CostArea, + RoleAddedId: roleId, + AddItemInfo: shopInfo.AddItemInfo, + VipLevel: shopInfo.VipLevel, + EndTime: shopInfo.EndTime, + Ratio: shopInfo.Ratio, + FirstSwitch: firstBuy, + AmountFinal: this.GetAmountFinal(p, shopId, 0), + } + } + return nil +} + +// GetShopInfos 获取玩家商品信息给客户端展示 +func (this *ShopMgr) GetShopInfos(p *Player, nowLocation int) []*shop.ShopInfo { + if p.ShopTotal == nil { + p.ShopTotal = make(map[int32]*model.ShopTotal) + } + if p.ShopLastLookTime == nil { + p.ShopLastLookTime = make(map[int32]int64) + } + var newShops = make([]*shop.ShopInfo, 0) + if p.VipShopData == nil || len(p.VipShopData) == 0 { + this.UpdateVipShopData(p) + } + + for _, shopInfo := range this.ShopInfos[p.Platform] { + if nowLocation == -1 || (nowLocation < len(shopInfo.Location) && shopInfo.Location[nowLocation] == 1) { + if shopInfo.Page == ShopPageVip { + continue + } + var AdLookedNum, AdReceiveNum, remainingTime int32 + var lastLookTime int64 + if shopInfo.Ad > 0 { + shopTotal := p.ShopTotal[shopInfo.Id] + if shopTotal != nil { + AdLookedNum = shopTotal.AdLookedNum + AdReceiveNum = shopTotal.AdReceiveNum + } + lastLookTime = p.ShopLastLookTime[shopInfo.Id] + if AdLookedNum < int32(len(shopInfo.CoolingTime)) { + remainingTime = shopInfo.CoolingTime[AdLookedNum] + } + dif := int32(time.Now().Unix() - lastLookTime) + if dif >= remainingTime { + remainingTime = 0 + } else { + remainingTime = remainingTime - dif + } + } + added := int32(rand.Intn(int(shopInfo.AddArea[1])-int(shopInfo.AddArea[0])+1) + int(shopInfo.AddArea[0])) + consumptionAmount := int32(rand.Intn(int(shopInfo.CostArea[1])-int(shopInfo.CostArea[0])+1) + int(shopInfo.CostArea[0])) + var itemInfo []*shop.ItemInfo + for _, i2 := range shopInfo.AddItemInfo { + itemInfo = append(itemInfo, &shop.ItemInfo{ + ItemId: i2.ItemId, + ItemNum: i2.ItemNum, + }) + } + award, roleId := PetMgrSington.GetShopAward(shopInfo, p) + firstBuy := false + if shopInfo.FirstSwitch { + firstBuy = !slices.Contains(p.ShopID, int(shopInfo.Id)) + } + newShops = append(newShops, &shop.ShopInfo{ + Id: shopInfo.Id, + AdLookedNum: AdLookedNum, + AdReceiveNum: AdReceiveNum, + LastLookTime: int32(lastLookTime), + RoleAdded: award, + RoleAddedId: roleId, + ItemId: shopInfo.ItemId, + Order: shopInfo.Order, + Page: shopInfo.Page, + Type: shopInfo.Type, + Location: shopInfo.Location, + Picture: shopInfo.Picture, + Name: shopInfo.Name, + Ad: shopInfo.Ad, + AdTime: shopInfo.AdTime, + RepeatTimes: shopInfo.RepeatTimes, + CoolingTime: shopInfo.CoolingTime, + Label: shopInfo.Label, + Added: added, + Amount: int64(shopInfo.Amount), + Consume: shopInfo.ConstType, + ConsumptionAmount: consumptionAmount, + AddItemInfo: itemInfo, + VipLevel: shopInfo.VipLevel, + EndTime: shopInfo.EndTime, + IsBuy: false, + FirstBuy: firstBuy, + AmountFinal: this.GetAmountFinal(p, shopInfo.Id, 0), + }) + } + } + //VIP商城数据 + for i, data := range p.VipShopData { + shopInfo := this.ShopInfos[p.Platform][data.Id] + var AdLookedNum, AdReceiveNum, remainingTime int32 + var lastLookTime int64 + if shopInfo.Ad > 0 { + shopTotal := p.ShopTotal[shopInfo.Id] + if shopTotal != nil { + AdLookedNum = shopTotal.AdLookedNum + AdReceiveNum = shopTotal.AdReceiveNum + } + lastLookTime = p.ShopLastLookTime[shopInfo.Id] + if AdLookedNum < int32(len(shopInfo.CoolingTime)) { + remainingTime = shopInfo.CoolingTime[AdLookedNum] + } + dif := int32(time.Now().Unix() - lastLookTime) + if dif >= remainingTime { + remainingTime = 0 + } else { + remainingTime = remainingTime - dif + } + } + var itemInfo []*shop.ItemInfo + for _, i2 := range shopInfo.AddItemInfo { + itemInfo = append(itemInfo, &shop.ItemInfo{ + ItemId: i2.ItemId, + ItemNum: i2.ItemNum, + }) + } + award, roleId := PetMgrSington.GetShopAward(shopInfo, p) + firstBuy := false + if shopInfo.FirstSwitch { + firstBuy = !slices.Contains(p.ShopID, int(shopInfo.Id)) + } + newShops = append(newShops, &shop.ShopInfo{ + Id: shopInfo.Id, + AdLookedNum: AdLookedNum, + AdReceiveNum: AdReceiveNum, + LastLookTime: int32(lastLookTime), + RoleAdded: award, + RoleAddedId: roleId, + ItemId: shopInfo.ItemId, + Order: shopInfo.Order, + Page: shopInfo.Page, + Type: shopInfo.Type, + Location: shopInfo.Location, + Picture: shopInfo.Picture, + Name: shopInfo.Name, + Ad: shopInfo.Ad, + AdTime: shopInfo.AdTime, + RepeatTimes: shopInfo.RepeatTimes, + CoolingTime: shopInfo.CoolingTime, + Label: shopInfo.Label, + Added: data.AddArea, + Amount: shopInfo.Amount * int64(data.CostArea), + Consume: shopInfo.ConstType, + ConsumptionAmount: data.CostArea, + AddItemInfo: itemInfo, + VipLevel: shopInfo.VipLevel, + EndTime: shopInfo.EndTime, + IsBuy: data.IsBuy, + VipShopId: i, + FirstBuy: firstBuy, + AmountFinal: this.GetAmountFinal(p, shopInfo.Id, i), + }) + } + + return newShops +} + +// 更新玩家vipShop数据 +func (this *ShopMgr) UpdateVipShopData(p *Player) { + if p.VIP == 0 { + return + } + if p.VipShopData == nil { + p.VipShopData = make(map[int32]*model.ShopData) + } else { + //购买过的不刷新 + keysToDelete := []int32{} + for id, data := range p.VipShopData { + if !data.IsBuy { + keysToDelete = append(keysToDelete, id) + } + } + + // 删除要删除的键 + for _, id := range keysToDelete { + delete(p.VipShopData, id) + } + } + + //获取vip商品刷新个数 + vipConfig := VipMgrSington.GetVIPLevelCfg(p.Platform, p.VIP) + if vipConfig == nil { + return + } + count := int(vipConfig.Privilege3[1]) + count -= len(p.VipShopData) + logger.Logger.Trace("VIP商城刷新 刷新商品个数为:", count) + if count <= 0 { + logger.Logger.Error("VIP商城 暂时无法刷新!Snid = ", p.SnId) + return + } + //获取对应VIP等级物品列表 + vipShopInfoMap := make(map[int32]*ShopInfo) + var sumRatio = 0 //权重 + for _, shopInfo := range this.ShopInfos[p.Platform] { + if shopInfo.Page == ShopPageVip && shopInfo.VipLevel == p.VIP { + vipShopInfoMap[shopInfo.Id] = shopInfo + sumRatio += int(shopInfo.Ratio) + } + } + if len(vipShopInfoMap) == 0 { + return + } + for i := 1; i <= int(vipConfig.Privilege3[1]); i++ { + if p.VipShopData[int32(i)] != nil { + continue + } + //随机vip物品 + random := rand.Intn(sumRatio + 1) + logger.Logger.Tracef("Vip商城 随机到的值:%v,总权重:%v", random, sumRatio) + ratio := 0 + for id, shopInfo := range vipShopInfoMap { + ratio += int(shopInfo.Ratio) + if random <= ratio { + //判断商品位置 + p.VipShopData[int32(i)] = &model.ShopData{ + Id: id, + AddArea: int32(rand.Intn(int(shopInfo.AddArea[1]-shopInfo.AddArea[0]+1)) + int(shopInfo.AddArea[0])), + CostArea: int32(rand.Intn(int(shopInfo.CostArea[1]-shopInfo.CostArea[0]+1)) + int(shopInfo.CostArea[0])), + IsBuy: false, + } + logger.Logger.Tracef("Vip商城 随机到的物品id = %v,shopInfo = %v,random = %v,num = %v,商品位置:%v", id, shopInfo, random, ratio, i) + break + } + } + } +} + +// LookAdReceive 看广告领取商品,校验领取次数 +func (this *ShopMgr) LookAdReceive(shopId int32, p *Player, position int32) shop.OpResultCode { + var shopInfo *ShopInfo + if shops, ok := this.ShopInfos[p.Platform]; ok { + if shopInf, ok1 := shops[shopId]; ok1 { + shopInfo = shopInf + } + } + if shopInfo == nil { + logger.Logger.Errorf("this shop == nil shopId[%v] ", shopId) + return shop.OpResultCode_OPRC_Error + } + //需要观看广告的 + if shopInfo.Ad > 0 { + if _, ok1 := p.ShopTotal[shopId]; !ok1 { + p.ShopTotal[shopId] = &model.ShopTotal{} + } + shopTotal := p.ShopTotal[shopId] + if shopTotal.AdReceiveNum < shopInfo.RepeatTimes { + shopTotal.AdReceiveNum++ + return this.ReceiveVCPayShop(shopInfo, p, 0, position) + } + } + return shop.OpResultCode_OPRC_Error +} + +// ReceiveVCPayShop 领取VC看广告商品 +func (this *ShopMgr) ReceiveVCPayShop(shopInfo *ShopInfo, p *Player, vipShopId, position int32) shop.OpResultCode { + if shopInfo == nil { + logger.Logger.Errorf("this shop == nil") + return shop.OpResultCode_OPRC_Error + } + //产生订单 + this.PayAway(shopInfo, p, vipShopId, position) + return shop.OpResultCode_OPRC_Sucess +} + +// PayAway 商城 领取商品 +func (this *ShopMgr) PayAway(shopInfo *ShopInfo, p *Player, vipShopId, position int32) { + shopName := shopInfo.Name + "|" + strconv.Itoa(int(shopInfo.Id)) + costNum := rand.Int31n(shopInfo.CostArea[1]-shopInfo.CostArea[0]+1) + shopInfo.CostArea[0] + if shopInfo.Page == ShopPageVip { + shopData := p.GetVipShopData(shopInfo.Id, vipShopId) + if shopData != nil { + costNum = shopData.CostArea + } + } + if shopInfo.Ad <= 0 { //消耗 + logger.Logger.Tracef("AddOrder Consume[%v],shopName[%v]", shopInfo.ConstType, shopName, costNum) + switch shopInfo.ConstType { + case ShopConsumeCoin: + p.AddCoin(int64(-costNum), 0, common.GainWay_Shop_Buy, "sys", shopName) + case ShopConsumeDiamond: + p.AddDiamond(int64(-costNum), 0, common.GainWay_Shop_Buy, "sys", shopName) + case ShopConsumePhoneScore: + p.AddPhoneScore(int64(-costNum), 0, common.GainWay_Shop_Buy, "sys", shopName) + case ShopConsumeMoney: + //task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // return webapi.API_CreateOrder(common.GetAppId(),"", p.SnId, shopInfo.Id, p.Platform, p.PackageID, p.DeviceOS, + // p.DeviceId, shopInfo.Name, shopInfo.Amount, shopInfo.ConsumptionAmount) + //}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // if data != nil { + // //失败 + // } + //}), "API_CreateOrder").Start() + //p.AddMoneyPayTotal(int64(shopInfo.ConsumptionAmount)) + return + } + } else { + TaskSubjectSingleton.Touch(common.TaskTypeAdv, &TaskData{ + SnId: p.SnId, + Num: 1, + Position: position, + }) + } + amount := [3]int32{} // 获得 + if shopInfo.Page == ShopPageVip { + if p.VipShopData[vipShopId] == nil { + return + } + p.VipShopData[vipShopId].IsBuy = true + logger.Logger.Trace("玩家购买VIP商城物品成功,商品Id = ", shopInfo.Id) + } + + addTotal := this.GetAmountFinal(p, shopInfo.Id, vipShopId) + logger.Logger.Trace("addTotal ", addTotal, shopInfo.Amount) + switch shopInfo.Type { + case ShopTypeCoin: + amount[shopInfo.Type-1] = int32(addTotal) + p.AddCoin(addTotal, 0, common.GainWay_Shop_Buy, "system", shopName) + if shopInfo.Ad > 0 { //观看广告 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ShopAd, model.SystemFreeGive_CoinType_Coin, addTotal)) + } + // 记录钻石兑换金币的金币数量,个人水池调控使用 + if shopInfo.ConstType == ShopConsumeDiamond && shopInfo.Ad <= 0 && costNum > 0 { + p.AddDiamondToCoin(addTotal) + } + if shopInfo.Ad <= 0 && costNum != 0 { + TaskSubjectSingleton.Touch(common.TaskTypeBuyCoin, &TaskData{SnId: p.SnId, Num: 1, Position: position}) + } + + case ShopTypeDiamond: + //增加钻石 + amount[shopInfo.Type-1] = int32(addTotal) + p.AddDiamond(addTotal, 0, common.GainWay_Shop_Buy, "system", shopName) + if shopInfo.Ad > 0 { //观看广告 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ShopAd, model.SystemFreeGive_CoinType_Diamond, addTotal)) + } + case ShopTypeItem: + //增加道具 + item := &Item{ItemId: shopInfo.ItemId, ItemNum: addTotal, ObtainTime: time.Now().Unix()} + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_Shop_Buy, "system", shopName) + data := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if data != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, shopInfo.ItemId, data.Name, addTotal, "商城购买") + } + PetMgrSington.CheckShowRed(p) + } + this.ShopAddItem(shopInfo, p) + + this.CreateVCOrder(shopInfo, p.SnId, costNum, p.Platform, amount) + + //抽奖兑换统计 + if shopInfo.ConstType == ShopConsumePhoneScore { + items := make([]*Item, 0) + itemId := int32(0) + if shopInfo.Type == ShopTypeCoin { + itemId = common.ItemIDCoin + } else if shopInfo.Type == ShopTypeDiamond { + itemId = common.ItemIDDiamond + } else { + //道具 + itemId = shopInfo.ItemId + } + items = append(items, &Item{ + ItemId: itemId, // 物品id + ItemNum: addTotal, // 数量 + }) + jsonData, err := json.Marshal(items) + if err == nil { + LogChannelSingleton.WriteMQData(model.GeneratePhoneLottery(p.SnId, p.Platform, string(jsonData), 2, 0, 0, int(costNum))) + } + } +} + +// GetAmountFinal 计算商品实际获得数量 +func (this *ShopMgr) GetAmountFinal(p *Player, shopId, vipShopId int32) int64 { + if p == nil { + return 0 + } + + var shopInfo *ShopInfo + if shops, ok := this.ShopInfos[p.Platform]; ok { + if shopInf, ok := shops[shopId]; ok { + shopInfo = shopInf + } + } + if shopInfo == nil { + return 0 + } + + //默认加成 + var addTotal = shopInfo.Amount + added := rand.Int31n(shopInfo.AddArea[1]-shopInfo.AddArea[0]+1) + shopInfo.AddArea[0] + if shopInfo.Page == ShopPageVip { + if p.VipShopData[vipShopId] == nil { + return 0 + } + addTotal = shopInfo.Amount * int64(p.VipShopData[vipShopId].CostArea) + added = p.VipShopData[vipShopId].AddArea + } + var addNormal int64 + if added > 0 { + addNormal = int64(float64(addTotal) * float64(added) / 100.0) + } + + vipAdded := int64(0) + if shopInfo.Page == ShopPageDiamond { + //vip加成 + vipAdded = int64(VipMgrSington.GetVipDiamondExtra(p.Platform, p.VIP)) + logger.Logger.Tracef("商城钻石购买,vip加成 vipAdded = %v", vipAdded) + vipAdded = int64(float64(addTotal) * float64(vipAdded) / 100.0) + } + + switch shopInfo.Type { + case ShopTypeCoin: + var addCoin int64 + award, _ := PetMgrSington.GetShopAward(shopInfo, p) + if award > 0 { + addCoin = int64(float64(addTotal) * float64(award) / 100.0) + } + //增加金币 + addTotal += addNormal + addCoin + case ShopTypeDiamond: + addTotal += addNormal + vipAdded + + // 首充翻倍 + if shopInfo.FirstSwitch { + if !slices.Contains(p.ShopID, int(shopInfo.Id)) { + addTotal *= 2 + } + } + } + return addTotal +} + +// 商城购买 额外增加道具 +func (this *ShopMgr) ShopAddItem(shopInfo *ShopInfo, p *Player) { + if shopInfo.AddItemInfo != nil { + for _, info := range shopInfo.AddItemInfo { + item := &Item{ItemId: info.ItemId, ItemNum: info.ItemNum, ObtainTime: time.Now().Unix()} + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, common.GainWay_Shop_Buy, "system", shopInfo.Name) + data := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if data != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, shopInfo.ItemId, data.Name, shopInfo.Amount, "商城购买") + } + PetMgrSington.CheckShowRed(p) + } + } +} + +// CreateVCOrder 产生VC订单,记录看广告获取商品的订单 +func (this *ShopMgr) CreateVCOrder(shopInfo *ShopInfo, snid, costNum int32, platform string, amount [3]int32) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + //获得类型 + if shopInfo.Type < ShopTypeCoin && shopInfo.Type > ShopTypeItem { + return errors.New("error type") + } + //道具 + var item []model.ItemInfo + if shopInfo.Type == ShopTypeItem { + item = append(item, model.ItemInfo{ItemId: shopInfo.ItemId, ItemNum: shopInfo.Amount}) + if shopInfo.AddItemInfo != nil { + for _, info := range shopInfo.AddItemInfo { + item = append(item, model.ItemInfo{ItemId: info.ItemId, ItemNum: int64(info.ItemNum)}) + } + } + } + dbShop := model.NewDbShop(platform, shopInfo.Page, amount[:], "sys", 0, shopInfo.ConstType, costNum, + common.GainWay_ShopBuy, item, shopInfo.Id, shopInfo.Name, snid, 1, "shop_goods", []int32{}) + return model.InsertDbShopLog(dbShop) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + logger.Logger.Errorf("err:", data.(error)) + } + }), "CreateVCOrder").Start() +} + +func (this *ShopMgr) GetExchangeData(platform string, id int32) *ExchangeShopInfo { + if exShops := this.ExShops[platform]; exShops != nil { + for _, data := range exShops.List { + if data.Id == id { + var exType []*shop.ExchangeType + for _, info := range data.ExType { + exType = append(exType, &shop.ExchangeType{ + Price: info.Price, + JPrice: info.JPrice, + Cash: info.Cash, + Id: info.Id, + }) + } + return &ExchangeShopInfo{ + Id: data.Id, + Picture: data.Picture, + Type: data.Type, + Name: data.Name, + Rule: data.Content, + ExType: exType, + } + } + } + } + return nil +} + +// 兑换操作 +// Exchange 生成兑换订单(v卡) id 兑换方式 0~2 +func (this *ShopMgr) Exchange(p *Player, goodsId int32, username, mobile, comment string, id int32, num int32) (ret bool) { + ret = true + cdata := this.GetExchangeData(p.Platform, goodsId) + pack := &shop.SCShopExchange{ + RetCode: shop.OpResultCode_OPRC_VCoinNotEnough, + } + if cdata == nil { + pack.RetCode = shop.OpResultCode_OPRC_ExchangeSoldOut + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGE), pack) + return false + } + + var info *shop.ExchangeType + for _, value := range cdata.ExType { + if value.Id == id { + info = value + break + } + } + + if info == nil || (info.Price == 0 && info.JPrice == 0 && info.Cash == 0) { + return false + } + + var itemInfo []model.ItemInfo + // TODO 服务器处理 减劵 成功后调后台生成订单 + // 判断p.VCoin是否足够 不足返回错误 足够扣掉 另外需从后台操作回执成功生成扣除V卡的订单 回执失败 + //扣除V卡 + if info.Price > 0 { + if isF := BagMgrSingleton.SaleItem(p, VCard, int64(info.Price*num)); !isF { // 扣掉V卡 + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGE), pack) + return false + } + itemInfo = append(itemInfo, model.ItemInfo{ + ItemId: VCard, + ItemNum: int64(info.Price * num), + }) + } + //扣除金券 + if info.JPrice > 0 { + if isF := BagMgrSingleton.SaleItem(p, JCard, int64(info.JPrice*num)); !isF { // 扣掉金券 + pack.RetCode = shop.OpResultCode_OPRC_JCoinNotEnough + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGE), pack) + return false + } + itemInfo = append(itemInfo, model.ItemInfo{ + ItemId: JCard, + ItemNum: int64(info.JPrice * num), + }) + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pack := &webapi_proto.ASCreateExchangeOrder{ + Snid: p.SnId, + Platform: p.Platform, + Type: cdata.Type, + GoodsId: cdata.Id, + VCard: info.Price * num, + GoodsName: cdata.Name, + UserName: username, + Mobile: mobile, + Comment: comment, + JPrice: info.JPrice * num, + Cash: info.Cash * num, + Amount: num, + ExchangeType: id, + } + buff, err := webapi.API_CreateExchange(common.GetAppId(), pack) + if err != nil { + logger.Logger.Error("API_CreateExchange error:", err) + } + + as := &webapi_proto.SACreateExchangeOrder{} + if err := proto.Unmarshal(buff, as); err != nil { + logger.Logger.Errorf("API_CreateExchange err: %v %v", err, as.Tag) + } + var amount [ShopTypeItem]int32 + //保存db + dbShop := this.NewDbShop(p, 0, amount[:], ExchangeConsumeCash, info.Cash*num, + common.GainWay_ShopBuy, itemInfo, cdata.Id, cdata.Name, 0, "", []int32{}) + err = model.InsertDbShopLog(dbShop) + if err != nil { + logger.Logger.Errorf("model.InsertDbShopLog err:", err) + return nil + } + return as + + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + pack := &shop.SCShopExchange{ + RetCode: shop.OpResultCode_OPRC_Error, + } + + as := data.(*webapi_proto.SACreateExchangeOrder) // 必不为空 + //dbShop + if as.Tag == webapi_proto.TagCode_SUCCESS { + pack.RetCode = shop.OpResultCode_OPRC_Sucess + //p.SendVCoinDiffData() // 强推 + if info.Price > 0 { + name := "V卡" + if item := BagMgrSingleton.GetItem(p.SnId, VCard); item != nil && item.Name != "" { + name = item.Name + } else { + logger.Logger.Errorf("ExchangeList name err snid %v item %v ", p.SnId, VCard) + } + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, VCard, name, int64(info.Price*num), fmt.Sprintf("兑换订单 %v-%v", cdata.Id, cdata.Name)) + } + + if info.JPrice > 0 { + name := "金券" + if item := BagMgrSingleton.GetItem(p.SnId, JCard); item != nil && item.Name != "" { + name = item.Name + } else { + logger.Logger.Errorf("ExchangeList name err snid %v item %v ", p.SnId, JCard) + } + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, JCard, name, int64(info.JPrice*num), fmt.Sprintf("兑换订单 %v-%v", cdata.Id, cdata.Name)) + } + } else { + if as.GetReturnCPO() != nil { + switch as.ReturnCPO.Err { + case Err_ExShopNEnough: + pack.RetCode = shop.OpResultCode_OPRC_ExchangeNotEnough + case Err_ExShopLimit: + pack.RetCode = shop.OpResultCode_OPRC_ExchangeLimit + case Err_ExShopData: + pack.RetCode = shop.OpResultCode_OPRC_ExchangeDataRtt + default: + pack.RetCode = shop.OpResultCode_OPRC_ExchangeSoldOut + } + } + logger.Logger.Trace("API_CreateExchange: ", as.Tag, as.GetReturnCPO()) + var items = []*Item{} + //返回V卡 + if info.Price > 0 { + item1 := &Item{ + ItemId: VCard, // 物品id + ItemNum: int64(info.Price * num), // 数量 + } + items = append(items, item1) + } + //返回金券 + if info.JPrice > 0 { + item := &Item{ + ItemId: JCard, + ItemNum: int64(info.JPrice * num), + } + items = append(items, item) + } + + if len(items) > 0 { + BagMgrSingleton.AddJybBagInfo(p, items, 0, common.GainWay_Exchange, "system", "返还物品") // 后台订单创建失败 返回物品 + } + } + + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGE), pack) + }), "CreateExchange").Start() + + return +} + +// 兑换列表 +func (this *ShopMgr) ExchangeList(p *Player) (ret bool) { + ret = true + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pack := &webapi_proto.ASGetExchangeShop{ + Snid: p.SnId, + Platform: p.Platform, + } + buff, err := webapi.API_ExchangeList(common.GetAppId(), pack) + + // spack := &shop.SCShopExchangeRecord{} + as := &webapi_proto.SAGetExchangeShop{} + logger.Logger.Trace("SCShopOrder:", pack) + if err != nil { + logger.Logger.Error("API_ExchangeList error:", err) + return nil + } else if err := proto.Unmarshal(buff, as); err != nil { // 有订单 + + logger.Logger.Errorf("ExchangeRecord: %v", err) + return nil + + } + + return as + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + + pack := &shop.SCShopExchangeList{ + RetCode: shop.OpResultCode_OPRC_Sucess, + } + + if data != nil { + if as := data.(*webapi_proto.SAGetExchangeShop); as != nil { + for _, v := range as.List { + var exChangeType []*shop.ExchangeType + for _, info := range v.ExType { + exChangeType = append(exChangeType, &shop.ExchangeType{ + Price: info.Price, + JPrice: info.JPrice, + Id: info.Id, + Cash: info.Cash, + }) + } + pack.Infos = append(pack.Infos, &shop.ShopExchangeInfo{ + Type: v.Type, + Picture: v.Picture, + Name: v.Name, + Rule: v.Content, + GoodsId: v.Id, + ShopLimit: v.ShopLimit, + DayMaxLimit: v.DayMaxLimit, + DayPlayLimit: v.DayPlayLimit, + ExType: exChangeType, + TelCharge: v.TelCharge, + }) + } + } + } else { + pack.RetCode = shop.OpResultCode_OPRC_Error + } + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGELIST), pack) + }), "ExchangeList").Start() + + return +} + +// 获取兑换记录 +func (this *ShopMgr) GetExchangeRecord(p *Player, pageNo int32) { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pack := &webapi_proto.ASGetExchangeOrder{ + Snid: p.SnId, + Platform: p.Platform, + Page: pageNo, + } + buff, err := webapi.API_ExchangeRecord(common.GetAppId(), pack) + + // spack := &shop.SCShopExchangeRecord{} + as := &webapi_proto.SAGetExchangeOrder{} + logger.Logger.Trace("SCShopOrder:", pack) + if err != nil { + logger.Logger.Error("API_ExchangeRecord error:", err) + return nil + } else if err := proto.Unmarshal(buff, as); err != nil { // 有订单 + + logger.Logger.Errorf("ExchangeRecord: %v", err) + return nil + + } + + return as + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + pack := &shop.SCShopExchangeRecord{} + if data != nil { + if as := data.(*webapi_proto.SAGetExchangeOrder); as != nil { + + pack.PageNo = as.CurPage + pack.PageSize = as.PageLimit + pack.PageSum = as.PageTotal + for _, v := range as.OrderList { + + record := &shop.ShopExchangeRecord{ + CreateTs: v.CreateTime, + OrderId: fmt.Sprintf("%v", v.Id), + State: v.Status, + Name: v.Name, + Remark: v.Remark, + PayState: v.PayStatus, + ExchangeNum: v.ExchangeNum, + EXchangeType: v.EXchangeType, + GoodsId: v.GoodsId, + } + // remark + pack.Infos = append(pack.Infos, record) + } + + } + } + p.SendToClient(int(shop.SPacketID_PACKET_SC_SHOP_EXCHANGERECORD), pack) + }), "ExchangeRecord").Start() + +} + +// ShopCheckShowRed 商城红点通知 +func (this *ShopMgr) ShopCheckShowRed(p *Player) { + if p == nil { + return + } + for i := 0; i < 3; i++ { + var isShow bool + for _, shopInfo := range this.ShopInfos[p.Platform] { + var AdLookedNum, remainingTime int32 + var lastLookTime int64 + if shopInfo.Ad > 0 && (i == -1 || (i < len(shopInfo.Location) && shopInfo.Location[i] == 1)) { + shopTotal := p.ShopTotal[shopInfo.Id] + if shopTotal != nil { + AdLookedNum = shopTotal.AdLookedNum + } + lastLookTime = p.ShopLastLookTime[shopInfo.Id] + if AdLookedNum < int32(len(shopInfo.CoolingTime)) { + remainingTime = shopInfo.CoolingTime[AdLookedNum] + } else { + continue + } + dif := int32(time.Now().Unix() - lastLookTime) + if dif >= remainingTime { + remainingTime = 0 + if i+1 <= len(shopInfo.Location) { + isShow = true + p.SendShowRed(hall_proto.ShowRedCode_Shop, int32(i+1), 1) + } + } + } + } + if !isShow { + p.SendShowRed(hall_proto.ShowRedCode_Shop, int32(i+1), 0) + } + } +} + +// 现金订单 +func (this *ShopMgr) NewDbShop(p *Player, pageId int32, amount []int32, consume, consumeNum, gainWay int32, + itemInfo []model.ItemInfo, shopId int32, shopName string, state int32, remark string, other []int32) (dbShop *model.DbShop) { + ct, cr := PlatformMgrSingleton.GetCurrencyByPackageTag(p.PackageID) + dbShop = model.NewDbShop(p.Platform, pageId, amount[:], ct, consumeNum*cr, consume, consumeNum, + gainWay, itemInfo, shopId, shopName, p.SnId, state, remark, other) + return +} + +// SendAPICreateOrder 创建订单 +func (this *ShopMgr) SendAPICreateOrder(p *Player, ConfigPayId int32, data any, remark string) { + //三方购买 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var amount [ShopTypeItem]int32 + var dbShop *model.DbShop + if shopInfo, ok := data.(*ShopInfo); ok { + // 目前现金只能买钻石 + var addTotal = int64(shopInfo.Amount) + added := rand.Int31n(shopInfo.AddArea[1]-shopInfo.AddArea[0]+1) + shopInfo.AddArea[0] + costNum := rand.Int31n(shopInfo.CostArea[1]-shopInfo.CostArea[0]+1) + shopInfo.CostArea[0] + /* if shopInfo.Page == ShopPageVip { + //暂时这样修改 VIP礼包没有现金支付 + shopData := p.GetVipShopData(shopInfo.Id, 0) + if shopData != nil { + added = shopData.AddArea + costNum = shopData.CostArea + } + }*/ + vipAdded := int32(0) + if shopInfo.Page == ShopPageDiamond { + //vip加成 + vipAdded = VipMgrSington.GetVipDiamondExtra(p.Platform, p.VIP) + logger.Logger.Tracef("商城钻石购买,vip加成 vipAdded = %v", vipAdded) + } + + if added > 0 || vipAdded > 0 { + addTotal = shopInfo.Amount + int64((float64(shopInfo.Amount)*float64(added+vipAdded))/100.0) + } + + // 首充翻倍 + if shopInfo.FirstSwitch { + if !slices.Contains(p.ShopID, int(shopInfo.Id)) { + addTotal *= 2 + } + } + + amount[ShopTypeDiamond-1] = int32(addTotal) + var itemInfo []model.ItemInfo + var webItemInfo []*webapi_proto.ItemInfo + if shopInfo.AddItemInfo != nil { + for _, info := range shopInfo.AddItemInfo { + itemInfo = append(itemInfo, model.ItemInfo{ + ItemId: info.ItemId, + ItemNum: int64(info.ItemNum), + }) + webItemInfo = append(webItemInfo, &webapi_proto.ItemInfo{ + ItemId: info.ItemId, + ItemNum: info.ItemNum, + }) + } + } + + dbShop = this.NewDbShop(p, shopInfo.Page, amount[:], ShopConsumeMoney, costNum, + common.GainWay_ShopBuy, itemInfo, shopInfo.Id, shopInfo.Name, 0, remark, []int32{}) + err := model.InsertDbShopLog(dbShop) + if err != nil { + logger.Logger.Errorf("model.InsertDbShopLog err:", err) + return nil + } + return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, shopInfo.Id, p.Platform, p.PackageID, p.DeviceOS, + p.DeviceId, shopInfo.Name, [ShopTypeItem]int32{0, int32(addTotal), 0}, costNum, webItemInfo, "") + + } else if cdata, ok := data.(*ExchangeShopInfo); ok { + var info *shop.ExchangeType + for _, value := range cdata.ExType { + if value.Id == cdata.ExchangeTypeId { + info = value + break + } + } + if info.Cash <= 0 { + return nil + } + //保存到db + dbShop = this.NewDbShop(p, 0, amount[:], ExchangeConsumeCash, info.Cash*cdata.ExchangeNum, + common.GainWay_ShopBuy, nil, cdata.Id, cdata.Name, 0, remark, []int32{}) + err := model.InsertDbShopLog(dbShop) + if err != nil { + logger.Logger.Errorf("model.InsertDbShopLog err:", err) + return nil + } + orderId := cdata.OrderId + //兑换 充值订单 + logger.Logger.Infof("客户端请求兑换 创建支付订单!AppId = %v,SnId = %v,Id = %v,dbShop.LogId.Hex() = %v,cash = %v", common.GetAppId(), p.SnId, cdata.Id, dbShop.LogId.Hex(), info.Cash*cdata.ExchangeNum) + return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, cdata.Id, p.Platform, p.PackageID, p.DeviceOS, + p.DeviceId, cdata.Name, [ShopTypeItem]int32{0, 0, 0}, info.Cash*cdata.ExchangeNum, nil, orderId) + } else if bbd, ok := data.(*webapi_proto.BlindBoxData); ok { + if bbd.Type == 1 { + //金币 + amount[0] = bbd.Grade + } else if bbd.Type == 2 { + //钻石 + amount[1] = bbd.Grade + } + dbShop = this.NewDbShop(p, 0, amount[:], ShopConsumeMoney, int32(bbd.Price2), common.GainWay_ActBlindBox, nil, 0, "", 0, remark, []int32{bbd.Id}) + err := model.InsertDbShopLog(dbShop) + if err != nil { + logger.Logger.Errorf("model.InsertDbShopLog err:", err) + return nil + } + return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, 0, p.Platform, p.PackageID, p.DeviceOS, + p.DeviceId, bbd.Name, amount, int32(bbd.Price2), nil, "") + } else if wfs, ok := data.(*webapi_proto.WelfareSpree); ok { + for _, it := range wfs.Item { + if it.Type == 1 { + amount[0] = it.Grade + } else if it.Type == 2 { + amount[1] = it.Grade + } + } + var gainWay int32 = common.GainWay_ActFirstPay + if remark == "ContinuousPay" { + gainWay = common.GainWay_ActContinuousPay + } + dbShop = this.NewDbShop(p, 0, amount[:], ShopConsumeMoney, int32(wfs.Price2), gainWay, nil, 0, "", 0, remark, []int32{wfs.Day}) + err := model.InsertDbShopLog(dbShop) + if err != nil { + logger.Logger.Errorf("model.InsertDbShopLog err:", err) + return nil + } + return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, 0, p.Platform, p.PackageID, p.DeviceOS, + p.DeviceId, "FirstRecharge", amount, int32(wfs.Price2), nil, "") + } + return nil + }), task.CompleteNotifyWrapper(func(retdata interface{}, t task.Task) { + logger.Logger.Trace("API_CreateOrder:", retdata) + pack := &shop.SCPayInfo{ + RetCode: shop.OpResultCode_OPRC_Error, + } + if retdata == nil { + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + info := retdata.(*webapi_proto.ASCreateOrder) + if info == nil || info.Tag != 0 { + logger.Logger.Errorf("SCPayInfo:Tag[%v] Msg[%v] Url[%v]", info.Tag, info.Msg, info.Url) + //失败 + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + } else { + pack.RetCode = shop.OpResultCode_OPRC_Sucess + pack.Url = info.Url + logger.Logger.Trace("SCPayInfo:", pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + } + }), "API_CreateOrder").Start() +} + +func (this *ShopMgr) StartTimer(SnId int32, afterTs int32) { + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + p := PlayerMgrSington.GetPlayerBySnId(SnId) + if p == nil || !p.IsOnLine() { + return true + } + this.ShopCheckShowRed(p) + return true + }), nil, time.Duration(afterTs)*time.Second, 1) +} + +func (this *ShopMgr) Update() { + +} + +func (this *ShopMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(ShopMgrSington, time.Second, 0) +} diff --git a/worldsrv/srvdatamanager.go b/worldsrv/srvdatamanager.go new file mode 100644 index 0000000..a61f3dc --- /dev/null +++ b/worldsrv/srvdatamanager.go @@ -0,0 +1,121 @@ +package main + +import ( + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +var GameFreeMgrEx = &PBDB_GameFreeMgrEx{ + DbGameFreeId: make(map[string]int32), + DbGameDif: make(map[int32]map[int32]string), + DbGameDifByGFI: make(map[int32]string), + DbGameFreeMgr: make(map[string]*server.DB_GameFree), +} + +type PBDB_GameFreeMgrEx struct { + DbGameFreeId map[string]int32 //key为GameDif + DbGameDif map[int32]map[int32]string //key为 GameId GameMode + DbGameDifByGFI map[int32]string //key 为 DBGameFreeId + DbGameFreeMgr map[string]*server.DB_GameFree //key为GameDif +} + +func (this *PBDB_GameFreeMgrEx) Load(fileFullPath string) error { + for _, gfm := range srvdata.PBDB_GameFreeMgr.Datas.Arr { + if _, ok := this.DbGameFreeId[gfm.GetGameDif()]; !ok { + this.DbGameFreeId[gfm.GetGameDif()] = gfm.GetId() + } + if _, ok := this.DbGameDif[gfm.GetGameId()]; !ok { + this.DbGameDif[gfm.GetGameId()] = make(map[int32]string) + } + this.DbGameDif[gfm.GetGameId()][gfm.GetGameMode()] = gfm.GetGameDif() + this.DbGameDifByGFI[gfm.GetId()] = gfm.GetGameDif() + this.DbGameFreeMgr[gfm.GetGameDif()] = gfm + } + return nil +} + +func (this *PBDB_GameFreeMgrEx) Reload(fileFullPath string) error { + return this.Load(fileFullPath) +} + +// 查询当前游戏的DBGameFreeMgr +func (this *PBDB_GameFreeMgrEx) GetDBGameFreeMgrByGameDif(gameDif string) *server.DB_GameFree { + return this.DbGameFreeMgr[gameDif] +} + +// 查询当前游戏的GameDif +func (this *PBDB_GameFreeMgrEx) GetGameDifByGameFreeId(dbGameFreeId int32) string { + if str, ok := this.DbGameDifByGFI[dbGameFreeId]; ok { + return str + } + return "" +} + +// 查询当前游戏的GameDif +func (this *PBDB_GameFreeMgrEx) GetGameDifByGameIdAndMode(gameId, gameMode int32) string { + if data, ok := this.DbGameDif[gameId]; ok { + if str, ok2 := data[gameMode]; ok2 { + return str + } + } + return "" +} + +// 查询当前游戏的GameFreeId +func (this *PBDB_GameFreeMgrEx) GetGameFreeIdByGameDif(gameDif string) int32 { + if d, ok := this.DbGameFreeId[gameDif]; ok { + return d + } + return 0 +} + +// +//var RobotCarryMgrEx = &PBDB_RobotGameMgrEx{ +// pool: make(map[int32][]*server.DB_RobotGame), +//} +// +//type PBDB_RobotGameMgrEx struct { +// pool map[int32][]*server.DB_RobotGame +//} +// +//func (this *PBDB_RobotGameMgrEx) Reload() { +// for _, item := range srvdata.PBDB_RobotGameMgr.Datas.Arr { +// if pp, exist := this.pool[item.GetId()]; exist { +// pp = append(pp, item) +// this.pool[item.GetId()] = pp +// } else { +// this.pool[item.GetId()] = []*server.DB_RobotGame{item} +// } +// } +//} +// +//func (this *PBDB_RobotGameMgrEx) RandOneCarry(gamefreeId int32) (int32, int32, int32, bool) { +// if pp, exist := this.pool[gamefreeId]; exist { +// if len(pp) > 0 { +// item := pp[rand.Intn(len(pp))] +// enterCoin := item.GetEnterCoin() +// //尽量避免有重复的 +// if enterCoin > 100000000 { //100W以上 +// enterCoin = rand.Int31n(enterCoin%100000000+1) + enterCoin/100000000*100000000 +// } else if enterCoin > 10000000 { //10W以上 +// enterCoin = rand.Int31n(enterCoin%10000000+1) + enterCoin/10000000*10000000 +// } else if enterCoin > 1000000 { //1W以上 +// enterCoin = rand.Int31n(enterCoin%1000000+1) + enterCoin/1000000*1000000 +// } else if enterCoin > 100000 { //1k以上 +// enterCoin = rand.Int31n(enterCoin%100000+1) + enterCoin/100000*100000 +// } else if enterCoin > 10000 { //1百以上 +// enterCoin = rand.Int31n(enterCoin%10000+1) + enterCoin/10000*10000 +// } else if enterCoin > 1000 { //十以上 +// enterCoin = rand.Int31n(enterCoin%1000+1) + enterCoin/1000*1000 +// } else if enterCoin > 100 { //1以上 +// enterCoin = rand.Int31n(enterCoin%100+1) + enterCoin/100*100 +// } +// return enterCoin, item.GetLeaveCoin(), item.GetGameTimes(), true +// } +// } +// return 0, 0, 0, false +//} + +func init() { + srvdata.DataMgrAfter.RegisterLoader("DB_GameFree.dat", GameFreeMgrEx) +} diff --git a/worldsrv/task_login.go b/worldsrv/task_login.go new file mode 100644 index 0000000..67d4d48 --- /dev/null +++ b/worldsrv/task_login.go @@ -0,0 +1,346 @@ +package main + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "strconv" + "strings" + "time" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + login_proto "mongo.games.com/game/protocol/login" + server_proto "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" +) + +type TaskLogin struct { + *login_proto.CSLogin + *netlib.Session + Sid int64 + BackupPromoter string + flag login_proto.OpResultCode + tagkey int32 + codeValid bool +} + +// in task.Worker goroutine +func (t *TaskLogin) Call(o *basic.Object) interface{} { + var playerData *model.PlayerData + acc, retCode := model.AccountIsExist(t.GetUsername(), t.GetUsername(), t.GetPassword(), t.GetPlatform(), + t.GetTimeStamp(), t.GetLoginType(), t.tagkey, false, t.codeValid) + switch retCode { + case common.LoginOk: + t.flag = login_proto.OpResultCode_OPRC_Sucess + + case common.LoginPasswordError, common.LoginTypeNoExist, common.LoginError: + t.flag = login_proto.OpResultCode_OPRC_LoginPassError + + case common.LoginTelCodeExpire: + t.flag = login_proto.OpResultCode_OPRC_TelCodeExpire + + case common.LoginFreeze: + t.flag = login_proto.OpResultCode_OPRC_AccountBeFreeze + + case common.LoginNew: + t.flag = login_proto.OpResultCode_OPRC_Login_CreateAccError + switch t.GetLoginType() { + case common.LoginTypeGuest: // 游客 + raw := fmt.Sprintf("%v%v", t.GetUsername(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + acc, retCode = model.InsertAccount(t.GetUsername(), pwd, t.GetPlatform(), t.GetChannel(), t.GetPromoter(), t.GetParams(), + t.GetDeviceOs(), t.GetInviterId(), t.GetPromoterTree(), t.GetPlatformTag(), t.GetPackage(), t.GetDeviceInfo(), t.BackupPromoter, t.tagkey) + if retCode != common.InsertAccountOk { + return nil + } + + var tf bool + playerData, tf = model.CreatePlayerDataOnRegister(acc.Platform, acc.AccountId.Hex(), 0, "", niceIdMgr.GetRobHeadUrlIdx()) + if playerData == nil || !tf { + return nil + } + + t.flag = login_proto.OpResultCode_OPRC_Sucess + + case common.LoginTypeGoogle: // google,facebook + raw := fmt.Sprintf("%v%v", t.GetUsername(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + acc, retCode = model.InsertTelAccount(t.Username, pwd, t.GetPlatform(), t.GetChannel(), t.GetPromoter(), t.GetParams(), + t.GetInviterId(), t.GetPromoterTree(), "", pwd, t.GetPlatformTag(), t.GetPackage(), t.DeviceInfo, t.tagkey, t.AccountType, t.GetDeviceOs()) + if retCode != common.InsertAccountOk { + return nil + } + + var tf bool + playerData, tf = model.CreatePlayerDataByThird(acc.Platform, acc.AccountId.Hex(), t.Name, t.HeadUrl) + if playerData == nil || !tf { + return nil + } + + t.flag = login_proto.OpResultCode_OPRC_Sucess + + case common.LoginTypeTelCode: + // 需要验证码正确 + if !t.codeValid { + t.flag = login_proto.OpResultCode_OPRC_TelCodeError + } else { + // 随机密码 + raw := fmt.Sprintf("%v%v", bson.NewObjectId().Hex(), common.GetAppId()) + h := md5.New() + io.WriteString(h, raw) + pwd := hex.EncodeToString(h.Sum(nil)) + + // Username 就是手机号 + acc, retCode = model.InsertTelAccount(t.Username, pwd, t.GetPlatform(), t.GetChannel(), t.GetPromoter(), t.GetParams(), + t.GetInviterId(), t.GetPromoterTree(), t.Username, pwd, t.GetPlatformTag(), t.GetPackage(), t.DeviceInfo, t.tagkey, t.AccountType, t.GetDeviceOs()) + if retCode != common.InsertAccountOk { + return nil + } + + var tf bool + name := fmt.Sprintf("mango%v", acc.SnId) + playerData, tf = model.CreatePlayerDataByThird(acc.Platform, acc.AccountId.Hex(), name, t.HeadUrl) + if playerData == nil || !tf { + return nil + } + + t.flag = login_proto.OpResultCode_OPRC_Sucess + } + } + + if t.flag == login_proto.OpResultCode_OPRC_Sucess && playerData != nil { // playerData != nil 表示新用户 + // 新账号奖励 + err := model.UpdatePlayerCoin(playerData.Platform, playerData.SnId, playerData.Coin+int64(model.GameParamData.NewPlayerCoin), 0, 0, time.Now().Unix(), time.Now().Unix(), 0, playerData.ShopID) + if err == nil { + // 新号赠送日志 + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(playerData.SnId, playerData.Name, playerData.Platform, model.SystemFreeGive_GiveType_NewPlayer, model.SystemFreeGive_CoinType_Coin, int64(model.GameParamData.NewPlayerCoin))) + // 帐变记录 + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: playerData.Platform, + SnID: playerData.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: int64(model.GameParamData.NewPlayerCoin), + RemainNum: playerData.Coin + int64(model.GameParamData.NewPlayerCoin), + Add: 0, + LogType: common.GainWay_NewPlayer, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "", + Remark: "", + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + + // 发送绑定手机号奖励 + if t.GetAccountType() == common.AccountTypeTel { + freeFunc := func(coin int64, diamond int64, item map[int32]int64) { + err := model.UpdatePlayerCoin(playerData.Platform, playerData.SnId, playerData.Coin+coin, playerData.Diamond+diamond, 0, time.Now().Unix(), time.Now().Unix(), 0, playerData.ShopID) + if err == nil { + // 金币 + if coin > 0 { + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(playerData.SnId, playerData.Name, playerData.Platform, model.SystemFreeGive_GiveType_BindTel, model.SystemFreeGive_CoinType_Coin, coin)) + // 帐变记录 + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: playerData.Platform, + SnID: playerData.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: coin, + RemainNum: playerData.Coin + coin, + Add: 0, + LogType: common.GainWay_BindTel, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "", + Remark: "", + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + // 钻石 + if diamond > 0 { + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(playerData.SnId, playerData.Name, playerData.Platform, model.SystemFreeGive_GiveType_BindTel, model.SystemFreeGive_CoinType_Diamond, diamond)) + // 帐变记录 + log := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: playerData.Platform, + SnID: playerData.SnId, + ChangeType: common.BillTypeDiamond, + ChangeNum: diamond, + RemainNum: playerData.Diamond + diamond, + Add: 0, + LogType: common.GainWay_BindTel, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "", + Remark: "", + }) + if log != nil { + LogChannelSingleton.WriteLog(log) + } + } + //todo道具 + + } + } + + var coin, diamond int64 + items := map[int32]int64{} + for k, v := range PlatformMgrSingleton.GetPlatform(acc.Platform).BindTelReward { + switch k { + case 1: // 金币 + coin += v + case 2: // 钻石 + diamond += v + default: + // 道具 + items[k] = items[k] + v + } + } + freeFunc(coin, diamond, items) + } + } + } + + return acc +} + +// in laucher goroutine +func (t *TaskLogin) Done(i interface{}, tt task.Task) { + acc, _ := i.(*model.Account) + SCLogin(t.Session, t.Sid, t.CSLogin, acc, t.flag) + + // 账号冻结 + if t.flag == login_proto.OpResultCode_OPRC_AccountBeFreeze { + ssDis := &login_proto.SSDisconnect{ + SessionId: proto.Int64(t.Sid), + Type: proto.Int32(common.KickReason_Freeze), + } + proto.SetDefaults(ssDis) + t.Send(int(login_proto.GatePacketID_PACKET_SS_DICONNECT), ssDis) + } + + // 登录成功 + if t.flag == login_proto.OpResultCode_OPRC_Sucess { + // 标记当前玩家登录成功,断开其它连接 + lss := LoginStateMgrSington.LoginFinish(t.GetUsername(), t.GetPlatform(), t.Sid, acc, t.tagkey) + if len(lss) != 0 { + for k, ls := range lss { + ssDis := &login_proto.SSDisconnect{ + SessionId: proto.Int64(k), + Type: proto.Int32(common.KickReason_OtherLogin), + } + proto.SetDefaults(ssDis) + t.Send(int(server_proto.SSPacketID_PACKET_SS_DICONNECT), ssDis) + logger.Logger.Warnf("==========顶号 oldsid:%v newsid:%v", k, t.Sid) + LoginStateMgrSington.Logout(ls) + //todo:game->rehold + } + } + } else { + //清理登录状态 + LoginStateMgrSington.LogoutBySid(t.Sid) + } +} + +func SCLogin(s *netlib.Session, sid int64, csLogin *login_proto.CSLogin, acc *model.Account, code login_proto.OpResultCode) { + if s == nil || csLogin == nil { + return + } + sclogin := &login_proto.SCLogin{ + OpRetCode: code, + ClientParam: string(model.ClinetBuf), + } + if acc != nil { + now := time.Now() + sclogin.AccId = proto.String(acc.AccountId.Hex()) + sclogin.SrvTs = proto.Int64(now.Unix()) + if code == login_proto.OpResultCode_OPRC_Sucess { + acc.LastLoginTime = now + acc.LoginTimes++ + //获取平台名称 + if acc.Platform != common.Platform_Rob { + if acc.AccountType == common.AccountTypeTel { + sclogin.Token = acc.TelPassWord // 密码和appid + } + // 版本号 + deviceOs := csLogin.GetDeviceOs() + packVers := srvdata.GetPackVers(csLogin.GetPlatformTag()) + if deviceOs != "" && packVers != nil { + if cvers, ok := packVers[deviceOs]; ok { + sclogin.MinApkVer = proto.Int32(cvers.MinResVer) + sclogin.LatestApkVer = proto.Int32(cvers.LatestApkVer) + sclogin.MinApkVer = proto.Int32(cvers.MinApkVer) + sclogin.LatestResVer = proto.Int32(cvers.LatestResVer) + } + } + gameVers := srvdata.GetPackVers(csLogin.GetPlatformTag()) + if gameVers != nil { + for k, v := range gameVers { + token := strings.Split(k, ",") + if len(token) == 2 && token[1] == deviceOs { + if gameId, err := strconv.Atoi(token[0]); err == nil { + sclogin.SubGameVer = append(sclogin.SubGameVer, &login_proto.GameVer{ + GameId: proto.Int(gameId), + MinApkVer: proto.Int32(v.MinApkVer), + LatestApkVer: proto.Int32(v.LatestApkVer), + MinResVer: proto.Int32(v.MinResVer), + LatestResVer: proto.Int32(v.LatestResVer), + }) + } + } + } + } + // 游戏配置 + gps := PlatformMgrSingleton.GetGameFrees(acc.Platform) + for _, v := range gps { + if v.Status { + if v.DbGameFree.GetGameRule() != 0 { + // 平台游戏 + lgi := &login_proto.LoginGameInfo{ + GameId: proto.Int32(v.DbGameFree.GetGameId()), + LogicId: proto.Int32(v.DbGameFree.GetId()), + } + sclogin.GameInfo = append(sclogin.GameInfo, lgi) + } else { + // 三方游戏 + sclogin.ThrGameId = append(sclogin.ThrGameId, v.DbGameFree.Id) + } + } + } + // 开关 + cfg := PlatformMgrSingleton.GetEntrySwitch(acc.Platform) + if cfg != nil { + for _, vv := range cfg.GetDatas() { + sclogin.EntrySwitch = append(sclogin.EntrySwitch, vv) + } + } + // 比例 + ct, cr := PlatformMgrSingleton.GetCurrencyByPackageTag(csLogin.PlatformTag) + sclogin.CurrencyType = ct + sclogin.CurrencyRatio = cr + } + } + } + proto.SetDefaults(sclogin) + common.SendToGate(sid, int(login_proto.LoginPacketID_PACKET_SC_LOGIN), sclogin, s) +} diff --git a/worldsrv/taskmgr.go b/worldsrv/taskmgr.go new file mode 100644 index 0000000..97543de --- /dev/null +++ b/worldsrv/taskmgr.go @@ -0,0 +1,246 @@ +package main + +import ( + "container/list" + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + taskproto "mongo.games.com/game/protocol/task" + "mongo.games.com/game/srvdata" +) + +/* + 任务观察者 +*/ + +var TaskSubjectSingleton = &TaskSubject{ + list: make(map[int]*list.List), +} + +type ITask interface { + // TaskUpdate 更新任务进度 + // id 任务类型 + // data 数据 + TaskUpdate(id int, data any) +} + +// TaskSubject 观察者 +type TaskSubject struct { + list map[int]*list.List +} + +// Touch 任务统计事件 +// id 任务类型 +// data 数据 +func (t *TaskSubject) Touch(id int, data any) { + l, ok := t.list[id] + if !ok { + return + } + for e := l.Front(); e != nil; e = e.Next() { + if o, ok := e.Value.(ITask); ok { + o.TaskUpdate(id, data) + } + } +} + +func (t *TaskSubject) Attach(id int, obj ITask) { + l, ok := t.list[id] + if !ok { + l = new(list.List) + t.list[id] = l + } + for e := l.Front(); e != nil; e = e.Next() { + if e.Value == obj { + return + } + } + l.PushBack(obj) +} + +type TaskData struct { + SnId int32 + GameID int + GameFreeID int32 + Num int64 + Position int32 + Debug bool +} + +type TaskHandle struct { +} + +func (t *TaskHandle) TaskUpdate(id int, data any) { + info, ok := data.(*TaskData) + if !ok { + return + } + p := PlayerMgrSington.GetPlayerBySnId(info.SnId) + if p == nil { + return + } + + t.AllTask(id, data) + + num := info.Num + logger.Logger.Tracef("玩家任务事件 Snid = %d,GameId = %d,GameFreeId = %d,任务 id = %d,num = %d", info.SnId, info.GameID, info.GameFreeID, id, num) + switch id { + case common.TaskTypeAdv: // 看广告 + + case common.TaskTypeBuyCoin: // 买金币 + + case common.TaskTypeLogin: // 每日登录 + + case common.TaskTypeWinTimes: // 赢游戏次数 + + case common.TaskTypePlayTimes: // 玩游戏次数 + p.InviteTask(common.InviteScoreTypePlayTimes, int32(info.GameID), num) + p.InviteTask(common.InviteScoreTypeGameTimes, int32(info.GameID), num) + + case common.TaskTypeRankMatchTimes: // 排位赛次数 + + case common.TaskTypePay: // 玩家充值 + p.PhoneLotteryTask(common.TaskTypePay, num) + p.InviteTask(common.InviteScoreTypeRecharge, int32(info.GameID), num) + + case common.TaskTypeWinOrLose: // 游戏输赢金币数量 + p.PhoneLotteryTask(common.TaskTypeWinOrLose, num) + case common.TaskTypeTienlenCount: //tienlen游戏场次 + p.PhoneLotteryTask(common.TaskTypeTienlenCount, 1) + case common.TaskTypeBindInviter: // 绑定邀请人数量 + p.InviteTask(common.InviteScoreTypeBind, 0, num) + + case common.TaskTypeWinCoin: // 赢取金币数量 + + case common.TaskTypeTienlenWinTimes: // tienlen游戏赢取次数 + + case common.TaskTypeActivityScore: // 活跃积分数量 + + case common.TaskTypeInviteScore: // 邀请积分数量 + + case common.TaskTypeFirstLogin: + //抽奖次数增加 + p.PhoneLotteryTask(common.TaskTypeFirstLogin, 0) + p.InviteTask(common.InviteScoreTypeLogin, int32(info.GameID), num) + + case common.TaskTypeInviteNum: + + default: + return + } + return +} + +// AllTask 任务活动 +func (t *TaskHandle) AllTask(id int, data any) { + info, ok := data.(*TaskData) + if !ok { + return + } + p := PlayerMgrSington.GetPlayerBySnId(info.SnId) + if p == nil { + return + } + + if p.WelfData == nil { + p.WelfData = model.NewWelfareData() + } + if p.WelfData.Task == nil { + p.WelfData.Task = make(map[int32]*model.TaskData) + } + + logger.Logger.Tracef("AllTask taskID:%v %v", id, p.WelfData.Task[int32(id)]) + + change := map[int32][]*taskproto.TaskData{} + for _, v := range srvdata.PBDB_TaskMgr.Datas.GetArr() { + // 任务类型 + if int(v.TaskType) != id { + continue + } + if !info.Debug { + // 游戏类型 + switch v.GetGameType() { + case common.TaskGameTypeTienlen: + if !common.IsTienLen(info.GameID) { + continue + } + case common.TaskGameTypeThirteen: + if !common.IsThirteen(info.GameID) { + continue + } + case common.TaskGameTypeChess: + if !common.IsChess(info.GameID) { + continue + } + case common.TaskGameTypeSmallRocket: + if !common.IsSmallRocket(info.GameID) { + continue + } + default: + } + // 大厅类型 + if len(v.Position) > 0 && info.Position > 0 { + if len(v.Position) > int(info.Position) { + if v.Position[info.Position-1] != 1 { + continue + } + } + } + } + + if p.WelfData.Task[v.Id] == nil { + p.WelfData.Task[v.Id] = &model.TaskData{N: 0} // 初始化任务数据 + } + p.WelfData.Task[v.Id].N += info.Num + // 通知变更 + if !IsTaskReward(p, v.Id) { + item := &taskproto.TaskData{ + Id: v.Id, + N: p.WelfData.Task[v.Id].N, + TargetN: v.TargetTimes, + Status: 0, + Reward: v.Award, + } + if item.N > item.TargetN { + item.N = item.TargetN + } + if IsTaskFinish(p, v.Id) { + item.Status = TaskStateFinish + } + change[v.GetActivityType()] = append(change[v.GetActivityType()], item) + } + } + // 通知变更 + for k, v := range change { + if len(v) == 0 { + continue + } + pack := &taskproto.SCTaskChange{ + Tp: k, + List: v, + } + p.SendToClient(int(taskproto.TaskPacketID_PACKET_SCTaskChange), pack) + logger.Logger.Tracef("SCTaskChange %v", pack) + } +} + +func init() { + taskHandle := new(TaskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeAdv, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeBuyCoin, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeLogin, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeWinTimes, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypePlayTimes, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeRankMatchTimes, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypePay, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeWinOrLose, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeTienlenCount, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeBindInviter, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeWinCoin, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeTienlenWinTimes, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeInviteScore, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeActivityScore, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeFirstLogin, taskHandle) + TaskSubjectSingleton.Attach(common.TaskTypeInviteNum, taskHandle) +} diff --git a/worldsrv/thirdPlatformGameMapping.go b/worldsrv/thirdPlatformGameMapping.go new file mode 100644 index 0000000..ac2907a --- /dev/null +++ b/worldsrv/thirdPlatformGameMapping.go @@ -0,0 +1,129 @@ +package main + +import ( + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" +) + +var ( + ThirdPltGameMappingConfig = &ThirdPlatformGameMappingConfiguration{ + DB_ThirdPlatformGameMappingMgr: srvdata.PBDB_ThirdPlatformGameMappingMgr, + GamefreeIdMappingMap: make(map[int32]*server.DB_ThirdPlatformGameMapping), + } +) + +type ThirdPlatformGameMappingConfiguration struct { + *srvdata.DB_ThirdPlatformGameMappingMgr + GamefreeIdMappingMap map[int32]*server.DB_ThirdPlatformGameMapping +} + +func (this *ThirdPlatformGameMappingConfiguration) Load(fileFullPath string) error { + // this.Test() + var rawMappingInfo = make(map[int32]*webapi.WebAPI_ThirdPlatformGameMapping) + for _, v := range this.Datas.Arr { + this.GamefreeIdMappingMap[v.GetSystemGameID()] = v + rawMappingInfo[v.GetSystemGameID()] = &webapi.WebAPI_ThirdPlatformGameMapping{ + GameFreeID: v.GetSystemGameID(), + ThirdPlatformName: v.GetThirdPlatformName(), + ThirdGameID: v.GetThirdGameID(), + Desc: v.GetDesc(), + ScreenOrientationType: v.GetScreenOrientationType(), + ThirdID: v.GetThirdID(), + } + } + webapi.ThridPlatformMgrSington.ThridPlatformMap.Range(func(key, value interface{}) bool { + value.(webapi.IThirdPlatform).InitMappingRelation(rawMappingInfo) + return true + }) + return nil +} + +func (this *ThirdPlatformGameMappingConfiguration) Reload(fileFullPath string) error { + //todo 缓存数据加快查找 + //logger.Logger.Info("=== 缓存三方平台游戏id映射关系数据加快查找===") + this.GamefreeIdMappingMap = make(map[int32]*server.DB_ThirdPlatformGameMapping) + var rawMappingInfo = make(map[int32]*webapi.WebAPI_ThirdPlatformGameMapping) + for _, v := range this.Datas.Arr { + this.GamefreeIdMappingMap[v.GetSystemGameID()] = v + rawMappingInfo[v.GetSystemGameID()] = &webapi.WebAPI_ThirdPlatformGameMapping{ + GameFreeID: v.GetSystemGameID(), + ThirdPlatformName: v.GetThirdPlatformName(), + ThirdGameID: v.GetThirdGameID(), + Desc: v.GetDesc(), + ScreenOrientationType: v.GetScreenOrientationType(), + ThirdID: v.GetThirdID(), + } + } + webapi.ThridPlatformMgrSington.ThridPlatformMap.Range(func(key, value interface{}) bool { + value.(webapi.IThirdPlatform).InitMappingRelation(rawMappingInfo) + return true + }) + return nil +} + +func (this *ThirdPlatformGameMappingConfiguration) Test() { + var rawMappingInfo = make(map[int32]*webapi.WebAPI_ThirdPlatformGameMapping) + v := &server.DB_ThirdPlatformGameMapping{ + Id: 1, + SystemGameID: 9010001, + ThirdPlatformName: "测试平台", + ThirdGameID: "901", + Desc: "", + ScreenOrientationType: 0, + ThirdID: 901, + } + this.GamefreeIdMappingMap[v.GetSystemGameID()] = v + rawMappingInfo[v.GetSystemGameID()] = &webapi.WebAPI_ThirdPlatformGameMapping{ + GameFreeID: v.GetSystemGameID(), + ThirdPlatformName: v.GetThirdPlatformName(), + ThirdGameID: v.GetThirdGameID(), + Desc: v.GetDesc(), + ScreenOrientationType: v.GetScreenOrientationType(), + ThirdID: v.GetThirdID(), + } + + webapi.ThridPlatformMgrSington.ThridPlatformMap.Range(func(key, value interface{}) bool { + value.(webapi.IThirdPlatform).InitMappingRelation(rawMappingInfo) + return true + }) +} + +func (this *ThirdPlatformGameMappingConfiguration) FindByGameID(gamefreeId int32) *server.DB_ThirdPlatformGameMapping { + return this.GamefreeIdMappingMap[gamefreeId] +} + +// 包含dg的查询 +func (this *ThirdPlatformGameMappingConfiguration) FindSystemGamefreeidByThirdGameInfo(thirdPlt string, inThirdGameId, inThirdGameName string) (gamefreeid int32) { + if v, exist := webapi.ThridPlatformMgrSington.ThridPlatformMap.Load(thirdPlt); exist { + return v.(webapi.IThirdPlatform).ThirdGameInfo2GamefreeId(&webapi.WebAPI_ThirdPlatformGameMapping{ + ThirdPlatformName: thirdPlt, + ThirdGameID: inThirdGameId, + Desc: inThirdGameName, + }) + } + return 0 +} +func (this *ThirdPlatformGameMappingConfiguration) FindThirdIdByThird(thirdName string) (thirdId int32) { + if v, exist := webapi.ThridPlatformMgrSington.ThridPlatformMap.Load(thirdName); exist { + if plt, ok := v.(webapi.IThirdPlatform); ok { + return int32(plt.GetPlatformBase().BaseGameID) + } + } + return 0 +} +func (this *ThirdPlatformGameMappingConfiguration) FindThirdInfoBySystemGameId(systemGameId int32) (*server.DB_ThirdPlatformGameMapping, webapi.IThirdPlatform) { + info := this.FindByGameID(systemGameId) + if info != nil { + if v, exist := webapi.ThridPlatformMgrSington.ThridPlatformMap.Load(info.ThirdPlatformName); exist { + if plt, ok := v.(webapi.IThirdPlatform); ok { + return info, plt + } + } + } + return nil, nil +} + +func init() { + srvdata.DataMgrAfter.RegisterLoader("DB_ThirdPlatformGameMapping.dat", ThirdPltGameMappingConfig) +} diff --git a/worldsrv/thirdplatformmgr.go b/worldsrv/thirdplatformmgr.go new file mode 100644 index 0000000..59ca605 --- /dev/null +++ b/worldsrv/thirdplatformmgr.go @@ -0,0 +1,252 @@ +package main + +import ( + "encoding/json" + "mongo.games.com/game/model" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "strings" +) + +var ThirdPlatformMgrSington = &ThirdPlatformMgr{ + Platforms: make(map[string]*PlatformOfThirdPlatform), +} + +type PlatformOfThirdPlatform struct { + *model.PlatformOfThirdPlatform + dirty bool +} + +func (this *PlatformOfThirdPlatform) AddCoin(platform string, coin int64) { + if this.ThdPlatform == nil { + this.ThdPlatform = make(map[string]*model.ThirdPlatform) + } + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + tpp.Coin += coin + + this.dirty = true + } else { + this.ThdPlatform[strings.ToLower(platform)] = &model.ThirdPlatform{ + Coin: coin, + } + this.dirty = true + } + } +} + +func (this *PlatformOfThirdPlatform) AddNextCoin(platform string, coin int64) { + if this.ThdPlatform == nil { + this.ThdPlatform = make(map[string]*model.ThirdPlatform) + } + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + tpp.NextCoin += coin + + this.dirty = true + } else { + this.ThdPlatform[strings.ToLower(platform)] = &model.ThirdPlatform{ + NextCoin: coin, + } + this.dirty = true + } + } +} + +func (this *PlatformOfThirdPlatform) SetCoin(platform string, coin int64) { + if this.ThdPlatform == nil { + this.ThdPlatform = make(map[string]*model.ThirdPlatform) + } + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + tpp.Coin = coin + this.dirty = true + } else { + this.ThdPlatform[strings.ToLower(platform)] = &model.ThirdPlatform{ + Coin: coin, + } + this.dirty = true + } + } +} + +func (this *PlatformOfThirdPlatform) SetNextCoin(platform string, coin int64) { + if this.ThdPlatform == nil { + this.ThdPlatform = make(map[string]*model.ThirdPlatform) + } + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + tpp.NextCoin = coin + this.dirty = true + } else { + this.ThdPlatform[strings.ToLower(platform)] = &model.ThirdPlatform{ + NextCoin: coin, + } + this.dirty = true + } + } +} + +func (this *PlatformOfThirdPlatform) GetCoin(platform string) int64 { + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + return tpp.Coin + } + } + return 0 +} + +func (this *PlatformOfThirdPlatform) GetNextCoin(platform string) int64 { + if this.ThdPlatform != nil { + if tpp, exist := this.ThdPlatform[strings.ToLower(platform)]; exist { + return tpp.NextCoin + } + } + return 0 +} + +func (this *PlatformOfThirdPlatform) Clone() *PlatformOfThirdPlatform { + var ptp PlatformOfThirdPlatform + data, err := json.Marshal(this) + if err == nil { + err = json.Unmarshal(data, &ptp) + if err == nil { + return &ptp + } + } + return nil +} + +type ThirdPlatformMgr struct { + BaseClockSinker + Platforms map[string]*PlatformOfThirdPlatform +} + +func (this *ThirdPlatformMgr) InitData() { + platformList, err := model.GetAllThirdPlatform() + if err != nil { + logger.Logger.Error("InitData count failed:", err) + } + + for i := 0; i < len(platformList); i++ { + p := &platformList[i] + if p != nil { + this.Platforms[p.Platform] = &PlatformOfThirdPlatform{PlatformOfThirdPlatform: p} + } + } +} + +func (this *ThirdPlatformMgr) AddPlatform(platform string) *PlatformOfThirdPlatform { + ptp := &PlatformOfThirdPlatform{ + PlatformOfThirdPlatform: model.NewThirdPlatform(platform), + } + this.Platforms[platform] = ptp + return ptp +} + +func (this *ThirdPlatformMgr) InsertPlatform(platform *PlatformOfThirdPlatform) { + if platform != nil { + pCopy := platform.Clone() + if pCopy != nil { + platform.dirty = false + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.InsertThirdPlatform(pCopy.PlatformOfThirdPlatform) + }), nil, "UpdateThirdPlatform").StartByFixExecutor("ThirdPlatform") + } + } +} + +func (this *ThirdPlatformMgr) GetThirdPlatform(platform string) *PlatformOfThirdPlatform { + if p, exist := this.Platforms[platform]; exist && p != nil { + return p + } + return nil +} + +func (this *ThirdPlatformMgr) GetThirdPlatformCoin(platform, thirdPlatform string) int64 { + p := this.GetThirdPlatform(platform) + if p != nil { + return p.GetCoin(thirdPlatform) + } + return 0 +} + +func (this *ThirdPlatformMgr) AddThirdPlatformCoin(platform, thirdPlatform string, coin int64) bool { + p := this.GetThirdPlatform(platform) + if p != nil { + p.AddCoin(thirdPlatform, coin) + return true + } + return false +} + +func (this *ThirdPlatformMgr) ModuleName() string { + return "ThirdPlatformMgr" +} + +func (this *ThirdPlatformMgr) Init() { + this.InitData() +} + +func (this *ThirdPlatformMgr) Update() { + this.SaveAll(false) +} + +func (this *ThirdPlatformMgr) Shutdown() { + this.SaveAll(true) + module.UnregisteModule(this) +} + +// 感兴趣所有clock event +func (this *ThirdPlatformMgr) InterestClockEvent() int { + return 1 << CLOCK_EVENT_MONTH +} + +func (this *ThirdPlatformMgr) OnMonthTimer() { + for _, p := range this.Platforms { + if p != nil { + p.dirty = true + for _, thr := range p.ThdPlatform { + if thr != nil { + if thr.Coin > thr.NextCoin { + thr.Coin = thr.NextCoin + } else { + thr.NextCoin = thr.Coin + } + } + } + } + } + + this.SaveAll(false) +} + +func (this *ThirdPlatformMgr) SaveAll(bImm bool) { + for _, p := range this.Platforms { + if p != nil && p.dirty { + pCopy := p.Clone() + if pCopy != nil { + if bImm { + err := model.UpdateThirdPlatform(pCopy.PlatformOfThirdPlatform) + if err != nil { + logger.Logger.Warnf("UpdateThirdPlatform err:%v", err) + } else { + p.dirty = false + } + } else { + p.dirty = false + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.UpdateThirdPlatform(pCopy.PlatformOfThirdPlatform) + }), nil, "UpdateThirdPlatform").StartByFixExecutor("ThirdPlatform") + } + } + } + } +} + +func init() { + //module.RegisteModule(ThirdPlatformMgrSington, time.Minute*5, 0) + //ClockMgrSington.RegisteSinker(ThirdPlatformMgrSington) +} diff --git a/worldsrv/tmmatch.go b/worldsrv/tmmatch.go new file mode 100644 index 0000000..4402a9b --- /dev/null +++ b/worldsrv/tmmatch.go @@ -0,0 +1,296 @@ +package main + +import ( + "math" + "math/rand" + "sort" + "time" + + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/timer" + srvproto "mongo.games.com/goserver/srvlib/protocol" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/server" + "mongo.games.com/game/protocol/tournament" + webapi_proto "mongo.games.com/game/protocol/webapi" +) + +type TmPlayer struct { + SnId int32 + seq int // 报名序号(第几个报名的) +} +type TmMatch struct { + SortId int32 // 比赛开始时间戳,纳秒 + TMId int32 // 比赛配置Id + TmPlayer map[int32]*TmPlayer // 比赛玩家 + Platform string // 平台 + gmd *webapi_proto.GameMatchDate // 比赛配置 + gml *server.DB_GameMatchLevel // 难度配置 + dbGameFree *server.DB_GameFree // 游戏配置 + robotGrades map[int][]*TmGradeInfo // 第几轮:玩家积分 + copyRobotGrades []*TmGradeInfo // 最近一轮的机器人积分备份 + tmpUseRobot int32 // 是否使用机器人 +} + +type TmGradeInfo struct { + grade int32 + copySnid int32 + copyLv int32 + copyRoleId int32 +} + +func (tm *TmMatch) Start() { + logger.Logger.Trace("(this *TmMatch) Start()") + //通知客户端比赛开始 + pack := &tournament.SCTMStart{ + MatchId: proto.Int32(tm.TMId), + } + proto.SetDefaults(pack) + logger.Logger.Trace("SCTMStart:", pack) + tm.BroadcastMessage(int(tournament.TOURNAMENTID_PACKET_TM_SCTMStart), pack) + + //创建房间 + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + MatchSceneMgrSingleton.MatchStart(tm) + return true + }), nil, time.Millisecond*3500, 1) +} + +func (tm *TmMatch) Stop() { + //销毁房间 + MatchSceneMgrSingleton.MatchStop(tm) + logger.Logger.Trace("(this *TmMatch) Stop()") +} + +func (tm *TmMatch) BroadcastMessage(packetId int, rawPack interface{}) { + mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion) + for _, tmp := range tm.TmPlayer { + p := PlayerMgrSington.GetPlayerBySnId(tmp.SnId) + if p != nil && p.gateSess != nil && p.IsOnLine() && p.scene == nil { + mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{ + Mccs: &srvproto.MCClientSession{ + SId: proto.Int64(p.sid), + }, + }) + } + } + for gateSess, v := range mgs { + if gateSess != nil && len(v) != 0 { + pack, err := MulticastMaker.CreateMulticastPacket(packetId, rawPack, v...) + if err == nil { + proto.SetDefaults(pack) + gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack) + } + } + } +} + +func (tm *TmMatch) CopyMap(b map[int32]*TmPlayer) { + tm.TmPlayer = make(map[int32]*TmPlayer) + for _, v := range b { + var tmp TmPlayer + tmp = *v + tm.TmPlayer[v.SnId] = &tmp + } +} + +// CreateRobotGrades 生成假积分 +func (tm *TmMatch) CreateRobotGrades(round int) { + logger.Logger.Tracef("======创建假积分======当前第 %v 轮============", round) + if tm.robotGrades == nil { + tm.robotGrades = make(map[int][]*TmGradeInfo) + } + if tm.robotGrades[round] == nil { //生成假数据 + gmd := tm.gmd + lastPromotionNum := gmd.MatchPromotion[round-1] - 1 // 当前轮机器人数量 + logger.Logger.Trace("机器人数量: ", lastPromotionNum) + + // 第初始轮数据 + if tm.robotGrades == nil || tm.robotGrades[round-1] == nil { //初始化数据 + tm.robotGrades[round-1] = []*TmGradeInfo{} + snids := []int32{} + lvs := []int32{} + roleIds := []int32{} + for _, player := range PlayerMgrSington.snidMap { + if len(snids) > int(lastPromotionNum) { + break + } + if player != nil && player.IsRob { + snids = append(snids, player.SnId) + ms := MatchSeasonMgrSington.GetMatchSeason(player.SnId) + lv := MatchSeasonRankMgrSington.CreateRobotLv() + if ms != nil { + lv = ms.Lv + } + lvs = append(lvs, lv) + roleId := int32(2000001) + if player.Roles != nil && player.Roles.ModId != 0 { + roleId = player.Roles.ModId + } + roleIds = append(roleIds, roleId) + } + } + if len(snids) <= int(lastPromotionNum) { + // 机器人不够了 + logger.Logger.Warn("robot num not enough to match, must fixed robot num") + max := int32(99999999) + min := int32(10000000) + old := len(snids) + for i := 0; i < int(lastPromotionNum)-old; i++ { + tmpSnid := rand.Int31n(max-min) + min + if common.InSliceInt32(snids, tmpSnid) { + tmpSnid = rand.Int31n(max-min) + min + } + snids = append(snids, tmpSnid) + lv := MatchSeasonRankMgrSington.CreateRobotLv() + lvs = append(lvs, lv) + roleIds = append(roleIds, int32(2000001)) + } + } + for i := 0; i < int(lastPromotionNum); i++ { + gradeInfo := &TmGradeInfo{ + grade: 1000, // 初始都是1000积分 + copySnid: snids[i], + copyLv: lvs[i], + copyRoleId: roleIds[i], + } + tm.robotGrades[round-1] = append(tm.robotGrades[round-1], gradeInfo) + } + } + sort.Slice(tm.robotGrades[round-1], func(i, j int) bool { + return tm.robotGrades[round-1][i].grade > tm.robotGrades[round-1][j].grade + }) + + // 当前轮数据 + tm.robotGrades[round] = []*TmGradeInfo{} + upNum := int32(math.Floor(float64(lastPromotionNum*tm.gml.RobotUpRatio) / 100.0)) + indexs := []int32{} + for i := 0; i < int(lastPromotionNum); i++ { + indexs = append(indexs, int32(i)) + } + upIndexs := []int32{} // 积分上升的机器人 + for i := 0; i < int(upNum); i++ { + randIndex := rand.Intn(len(indexs)) + upIndexs = append(upIndexs, indexs[randIndex]) + indexs = append(indexs[:randIndex], indexs[randIndex+1:]...) + } + downNum := lastPromotionNum - upNum + downIndexs := []int32{} // 积分下降的机器人 + for i := 0; i < int(downNum); i++ { + randIndex := rand.Intn(len(indexs)) + downIndexs = append(downIndexs, indexs[randIndex]) + indexs = append(indexs[:randIndex], indexs[randIndex+1:]...) + } + // 上升 + var n int32 + for _, v := range tm.gml.UpGradeOdds { + n += v + } + for _, index := range upIndexs { + grade := tm.robotGrades[round-1][index].grade + r := rand.Int31n(n) + t := int32(0) + findI := 0 + for i, odd := range tm.gml.UpGradeOdds { + t += odd + if r < t { + findI = i + break + } + } + grade += tm.gml.UpGrade[findI] + gradeInfo := &TmGradeInfo{ + grade: grade, + copySnid: tm.robotGrades[round-1][index].copySnid, + copyLv: tm.robotGrades[round-1][index].copyLv, + copyRoleId: tm.robotGrades[round-1][index].copyRoleId, + } + tm.robotGrades[round] = append(tm.robotGrades[round], gradeInfo) + } + // 下降 + n = 0 + for _, v := range tm.gml.DownGradeOdds { + n += v + } + for _, index := range downIndexs { + grade := tm.robotGrades[round-1][index].grade + r := rand.Int31n(n) //0-999 + t := int32(0) + findI := 0 + for i, odd := range tm.gml.DownGradeOdds { + t += odd + if r < t { + findI = i + break + } + } + grade -= tm.gml.DownGrade[findI] + gradeInfo := &TmGradeInfo{ + grade: grade, + copySnid: tm.robotGrades[round-1][index].copySnid, + copyLv: tm.robotGrades[round-1][index].copyLv, + copyRoleId: tm.robotGrades[round-1][index].copyRoleId, + } + tm.robotGrades[round] = append(tm.robotGrades[round], gradeInfo) + } + } + sort.Slice(tm.robotGrades[round], func(i, j int) bool { + return tm.robotGrades[round][i].grade > tm.robotGrades[round][j].grade + }) + + // 清除上上一轮数据 + if tm.robotGrades[round-2] != nil { + delete(tm.robotGrades, round-2) + } + for i, infos := range tm.robotGrades { + logger.Logger.Tracef(">>>积分历史>>> 第 %v 轮", i) + for _, info := range infos { + logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) + } + } +} + +// RobotGradesDecline 假积分衰减 +func (tm *TmMatch) RobotGradesDecline(round int) { + //衰减上一轮的积分 + lastRound := round - 1 + tm.copyRobotGrades = []*TmGradeInfo{} + if tm.robotGrades == nil { + tm.robotGrades = make(map[int][]*TmGradeInfo) + } + if round == 1 { + tm.CreateRobotGrades(1) + } + if tm.robotGrades[lastRound] == nil { //生成假数据 + tm.CreateRobotGrades(lastRound) + } + if tm.robotGrades[lastRound] != nil { + for i, info := range tm.robotGrades[lastRound] { + declineGrade := info.grade + if round != 1 { + declineGrade = info.grade * 75 / 100 + } + gradeInfo := &TmGradeInfo{ + grade: declineGrade, + copySnid: info.copySnid, + copyLv: info.copyLv, + copyRoleId: info.copyRoleId, + } + tm.robotGrades[lastRound][i] = gradeInfo + if info.copySnid != 0 { + // 备份机器人积分,机器人入场时从备份数据中获取一个 + tm.copyRobotGrades = append(tm.copyRobotGrades, gradeInfo) + } + } + } + logger.Logger.Tracef("======积分衰减======当前第 %v 轮============", round) + for i, infos := range tm.robotGrades { + logger.Logger.Tracef(">>>积分历史>>> 第 %v 轮", i) + for _, info := range infos { + logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) + } + } +} diff --git a/worldsrv/tournament.go b/worldsrv/tournament.go new file mode 100644 index 0000000..1e4fd3d --- /dev/null +++ b/worldsrv/tournament.go @@ -0,0 +1,1426 @@ +package main + +import ( + "fmt" + "math/rand" + "sort" + "strconv" + "sync" + "time" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/i18n" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" + "mongo.games.com/goserver/core/task" + "mongo.games.com/goserver/core/timer" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + "mongo.games.com/game/protocol/tournament" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/game/webapi" +) + +const ( + MatchTypeNormal = 1 // 锦标赛 + MatchTypeChampion = 2 // 冠军赛 + MatchTypeVIP = 3 // vip比赛 +) + +const ( + MatchSwitchStart = 1 // 开启 + MatchSwitchClose = 2 // 关闭 +) + +const ( + MatchUseRobot = 0 // 使用机器人 + MatchNotUserRobot = 1 // 关闭机器人 +) + +// tournament + +var TournamentMgr = &Tournament{ + GameMatchDateList: make(map[string]map[int32]*webapi_proto.GameMatchDate), //比赛配置 + matches: make(map[int32]map[int32]*TmMatch), + signUpPlayers: make(map[string]map[int32]*SignInfo), + players: make(map[int32]map[int32]*PlayerMatchContext), + copyPlayers: make(map[int32]map[int32][]*PlayerMatchContext), + rankPlayers: make(map[int32]map[int32][]*PlayerMatchContext), + roundOverPlayerNum: make(map[int32]map[int32]int32), + finalPerRank: make(map[int32][]*PerRankInfo), + playerSignUpTime: make(map[int32]int64), + playerWaitStart: make(map[int32]int64), + singleSignupPlayers: make(map[int32]*SignupInfo), + singleFinalGrades: make(map[int32]map[int32]int32), +} + +type Tournament struct { + GameMatchDateList map[string]map[int32]*webapi_proto.GameMatchDate // 比赛配置,platform:比赛场配置id:比赛配置 + matches map[int32]map[int32]*TmMatch // 开始比赛的数据,比赛配置Id:比赛顺序序号:一场开始的比赛数据 + signUpPlayers map[string]map[int32]*SignInfo // 报名的玩家 platform:比赛配置id:报名人 + players map[int32]map[int32]*PlayerMatchContext // 比赛中玩家 比赛顺序序号:snid:玩家信息 + copyPlayers map[int32]map[int32][]*PlayerMatchContext // 比赛玩家数据备份 比赛顺序序号:round:玩家信息 + rankPlayers map[int32]map[int32][]*PlayerMatchContext //比赛玩家为了每轮积分排名用 比赛顺序序号,round + roundOverPlayerNum map[int32]map[int32]int32 // 完成比赛人数 比赛序号:轮次:人数 + finalPerRank map[int32][]*PerRankInfo //本场比赛最后排名,每淘汰一位记录一位,最后记录决赛玩家 + playerWaitStart map[int32]int64 // 等待时间 玩家Id:等待时长秒 + singleFinalGrades map[int32]map[int32]int32 //使用机器人模式最后一轮分数 + + // UseRobot + playerSignUpTime map[int32]int64 //玩家报名时间 玩家Id:报名时间戳 + singleSignupPlayers map[int32]*SignupInfo //报名的玩家,玩家Id:报名信息 + // UseRobot +} + +type PerRankInfo struct { + Name string + SnId int32 + RankId int32 + Grade int32 +} + +type SignInfo struct { + signup map[int32]*TmPlayer //玩家Id + Platform string + MaxCnt int // 最大报名人数 +} + +type SignupInfo struct { + Platform string + Tmid int32 + Snid int32 +} + +// 检查下数据是否合法(主要检查报名人数和晋级方式) n n|...|4|1 +func (this *Tournament) checkData(cfg *webapi_proto.GameMatchDate) bool { + if cfg == nil { + return false + } + l := len(cfg.MatchPromotion) + if l < 2 { + return false + } + if cfg.MatchPromotion[0] != cfg.MatchNumebr { //第一位必须和报名人数相同 + return false + } + if cfg.MatchPromotion[l-1] != 1 { //最后一位必须是1 + return false + } + if cfg.MatchPromotion[l-2] != 4 { //倒数第二位必须是4 + return false + } + for i, num := range cfg.MatchPromotion { + if i < l-1 { //除了最后一位 + if num%4 != 0 { //必须是4的整倍数 + return false + } + if num < cfg.MatchPromotion[i+1] { //必须递减 + return false + } + } + } + + return true +} + +// UpdateData 更新配置 +// init: 通知客户端配置变更 +func (this *Tournament) UpdateData(init bool, cfgs *webapi_proto.GameMatchDateList) { + //logger.Logger.Trace("(this *Tournament) UpdateData:", cfgs) + if cfgs.Platform == "0" { + return + } + gmds := make(map[int32]*webapi_proto.GameMatchDate) + for _, v := range cfgs.List { + if this.checkData(v) { + gmds[v.Id] = v + } else { + logger.Logger.Error("GameMatchDate error: ", v) + } + } + oldgmds := this.GameMatchDateList[cfgs.Platform] + //旧配置关闭踢人 --旧配置关闭不影响已经开始的只取消报名未开始的 + for _, v := range oldgmds { + gmd := gmds[v.Id] + if (gmd != nil && v.MatchSwitch == MatchSwitchStart && gmd.MatchSwitch != v.MatchSwitch) || //配置关闭 + (gmd != nil && !this.IsTimeRange(gmd)) || //或者不在时间段内 + (gmd != nil && v.UseRobot != gmd.UseRobot) || //或者匹配模式有修改 + gmd == nil { //或者删除 + signInfo := this.signUpPlayers[cfgs.Platform][v.Id] + if signInfo != nil && len(signInfo.signup) > 0 { + // 取消报名 + this.CancelSignUpAll(cfgs.Platform, v.Id) + } + } + } + //新配置增加更新 + for _, v := range gmds { + oldgmd := oldgmds[v.Id] + if oldgmd == nil || v.MatchSwitch == MatchSwitchStart { + if this.signUpPlayers[cfgs.Platform] == nil { + this.signUpPlayers[cfgs.Platform] = make(map[int32]*SignInfo) + } + this.signUpPlayers[cfgs.Platform][v.Id] = &SignInfo{ + signup: make(map[int32]*TmPlayer), + Platform: cfgs.Platform, + MaxCnt: int(v.MatchNumebr), + } + } + if v.MatchSwitch == MatchSwitchClose || !this.IsTimeRange(v) { + this.CancelSignUpAll(cfgs.Platform, v.Id) + if v.MatchSwitch == MatchSwitchClose { + delete(this.signUpPlayers[cfgs.Platform], v.Id) + } + } + } + this.GameMatchDateList[cfgs.Platform] = gmds + + if !init { + //todo 优化 + for _, v := range PlayerMgrSington.playerOfPlatform[cfgs.Platform] { + //通知平台玩家数据更新 + pack := TournamentMgr.GetSCTMInfosPack(cfgs.Platform, v.AppChannel) + proto.SetDefaults(pack) + logger.Logger.Trace("SCTMInfos++++++++++++:", pack) + v.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack) + } + } +} + +// GetMatchInfo 比赛配置 +func (this *Tournament) GetMatchInfo(platform string, tmId int32) *webapi_proto.GameMatchDate { + if list, ok := this.GameMatchDateList[platform]; ok { + if gmd, ok1 := list[tmId]; ok1 { + return gmd + } + } + return nil +} + +// MatchSwitch 比赛开关 +func (this *Tournament) MatchSwitch(platform string, tmId int32) bool { + info := this.GetMatchInfo(platform, tmId) + return info != nil && info.MatchSwitch == MatchSwitchStart +} + +// IsUseRobot 是否用机器人 +func (this *Tournament) IsUseRobot(platform string, tmId int32) bool { + info := this.GetMatchInfo(platform, tmId) + return info != nil && info.UseRobot == MatchUseRobot +} + +// 周几 +func getWeekNum(t time.Time) int { + strWeek := []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} + week := t.Weekday() + for i, s := range strWeek { + if week.String() == s { + return i + } + } + return 0 +} + +// IsTimeRange 判断是否在比赛时间段内 +// 在时间段内才能报名 +func (this *Tournament) IsTimeRange(gmd *webapi_proto.GameMatchDate) bool { + if gmd != nil { + if gmd.MatchType == 2 { //冠军赛 + tNow := time.Now() + switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段 + case 0: + return true + case 1: + nowWeek := getWeekNum(tNow) + hms := int32(tNow.Hour()*10000 + tNow.Minute()*100 + tNow.Second()) + if gmd.MatchTimeWeek != nil && len(gmd.MatchTimeWeek) > 0 { + for _, week := range gmd.MatchTimeWeek { + if nowWeek == int(week) { + if hms >= gmd.MatchTimeStartHMS && hms <= gmd.MatchTimeEndHMS { + return true + } + } + } + } + case 2: + if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 { + startStamp := gmd.MatchTimeStamp[0] + endStamp := gmd.MatchTimeStamp[1] + if tNow.Unix() >= startStamp && tNow.Unix() <= endStamp { + return true + } + } + } + } else { //锦标赛没有时间限制 + return true + } + } + return false +} + +// IsOutTime 是否过期 +func (this *Tournament) IsOutTime(gmd *webapi_proto.GameMatchDate) bool { + if gmd == nil || gmd.MatchSwitch != MatchSwitchStart { + return true + } + if gmd.MatchType == MatchTypeChampion { //冠军赛的一次性时间段才有过期可能 + switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段 + case 2: + tNow := time.Now() + if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 { + endStamp := gmd.MatchTimeStamp[1] + if tNow.Unix() >= endStamp { //当前时间大于截止时间 + return true + } + } + } + } + return false +} + +// GetAllMatchInfo 比赛配置数据 +func (this *Tournament) GetAllMatchInfo(platform string) map[int32]*webapi_proto.GameMatchDate { + if list, ok := this.GameMatchDateList[platform]; ok { + return list + } + return nil +} + +// IsMatching 判断是否在比赛中 +func (this *Tournament) IsMatching(snid int32) bool { + if this.players != nil { + for _, v := range this.players { + if v != nil { + for k := range v { + if k == snid { + return true + } + } + } + } + } + return false +} + +// IsMatchWaiting 判断是否在匹配中 +func (this *Tournament) IsMatchWaiting(platform string, snid int32) (bool, int32) { + // 未使用机器人 + if this.signUpPlayers != nil && this.signUpPlayers[platform] != nil { + for tmid, info := range this.signUpPlayers[platform] { + if info.signup != nil { + for sid, _ := range info.signup { + if sid == snid { + return true, tmid + } + } + } + } + } + // 使用机器人了 + if v, ok := this.singleSignupPlayers[snid]; ok { + return true, v.Tmid + } + return false, 0 +} + +// Quit 如果正在等待比赛,退赛 +func (this *Tournament) Quit(platform string, snid int32) { + isWaiting, tmid := this.IsMatchWaiting(platform, snid) + if isWaiting { + this.CancelSignUp(platform, tmid, snid) + } +} + +// signUpCost 报名 +// tmId 比赛配置id +// cost true报名、false取消报名 +// 报名费用 0成功 3道具不足 5金币不足 6钻石不足 7免费次数不足 +func (this *Tournament) signUpCost(tmId int32, p *Player, cost bool) (bool, int32) { + logger.Logger.Trace("报名费用 signUpCost: ", tmId, " snid: ", p.SnId, " cost: ", cost) + if p == nil { + return false, int32(tournament.SignRaceCode_OPRC_NoItem) + } + gmd := this.GetMatchInfo(p.Platform, tmId) + if gmd != nil && !p.IsRob { //真人费用检测 + if gmd.MatchType == MatchTypeVIP { //VIP比赛场 + freeTimes := p.GetVIPPrivilege1() // VIP比赛场免费次数 + logger.Logger.Trace("报名费用免费次数: freeTimes: ", freeTimes, " p.VipMatchTimes: ", p.VipMatchTimes) + if freeTimes > 0 { + if cost { + if p.VipMatchTimes < freeTimes { //参与次数<免费次数 + p.VipMatchTimes++ + } else { + logger.Logger.Trace("免费次数不足1") + return false, int32(tournament.SignRaceCode_OPRC_Free) + } + } else { + if p.VipMatchTimes > 0 { + p.VipMatchTimes-- + } else { + p.VipMatchTimes = 0 + } + } + } else { + logger.Logger.Trace("免费次数不足2") + return false, int32(tournament.SignRaceCode_OPRC_Free) + } + } + if gmd.SignupCostCoin > 0 { + logger.Logger.Trace("比赛场报名消耗金币", cost, gmd.SignupCostCoin) + if cost { + if p.Coin < gmd.SignupCostCoin { //金币不足 + logger.Logger.Trace("金币不足") + return false, int32(tournament.SignRaceCode_OPRC_Coin) + } else { + p.AddCoin(-gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗") + } + } else { + p.AddCoin(gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还") + } + } + if gmd.SignupCostDiamond > 0 { + logger.Logger.Trace("比赛场报名消耗钻石", cost, gmd.SignupCostDiamond) + if cost { + if p.Diamond < gmd.SignupCostDiamond { //钻石不足 + logger.Logger.Trace("钻石不足") + return false, int32(tournament.SignRaceCode_OPRC_Diamond) + } else { + p.AddDiamond(-gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗") + } + } else { + p.AddDiamond(gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还") + } + } + if gmd.SignupCostItem != nil && gmd.SignupCostItem.ItemNum > 0 { + //背包数据处理 + logger.Logger.Trace("比赛场报名消耗道具", cost, gmd.SignupCostItem.ItemNum) + item := BagMgrSingleton.GetItem(p.SnId, gmd.SignupCostItem.ItemId) + if item != nil { + if cost { + if item.ItemNum < gmd.SignupCostItem.ItemNum { + logger.Logger.Trace("道具不足") + return false, int32(tournament.SignRaceCode_OPRC_NoItem) + } else { + BagMgrSingleton.SalePlayerItem(p, item, gmd.SignupCostItem.ItemNum) + //item.ItemNum -= gmd.SignupCostItem.ItemNum + //p.dirty = true + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名消耗") + //BagMgrSingleton.SyncBagData(p, item.ItemId) + } + } else { + BagMgrSingleton.SalePlayerItem(p, item, -gmd.SignupCostItem.ItemNum) + //item.ItemNum += gmd.SignupCostItem.ItemNum + //p.dirty = true + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名退还") + //BagMgrSingleton.SyncBagData(p, item.ItemId) + } + } else { + logger.Logger.Trace("道具不足") + return false, int32(tournament.SignRaceCode_OPRC_NoItem) + } + } + } + return true, 0 +} + +// SignUp 报名 +// 0成功 1重复报名 2比赛没有开启 3道具不足 4不在报名时间段 5金币不足 6钻石不足 7免费次数不足 +func (this *Tournament) SignUp(tmId int32, p *Player) (bool, int32) { + logger.Logger.Trace("(this *Tournament) SignUp:", tmId, p.SnId) + // 开启 + info := this.GetMatchInfo(p.Platform, tmId) + if info == nil || info.GetMatchSwitch() == MatchSwitchClose { + return false, int32(tournament.SignRaceCode_OPRC_Close) + } + // 报名时间 + if !TournamentMgr.IsTimeRange(info) { + return false, int32(tournament.SignRaceCode_OPRC_Time) + } + // 已报名 + isWaiting, _ := TournamentMgr.IsMatchWaiting(p.Platform, p.SnId) + if TournamentMgr.IsMatching(p.SnId) || isWaiting { + return false, int32(tournament.SignRaceCode_OPRC_Repeat) + } + // 扣报名费 + ok, code := this.signUpCost(tmId, p, true) + if !ok { + return false, code + } + + var signInfo *SignInfo + gmd := this.GetMatchInfo(p.Platform, tmId) + if gmd == nil { + logger.Logger.Errorf("GetMatchInfo is nil. id=%v", tmId) + return false, int32(tournament.SignRaceCode_OPRC_Repeat) + } + + if this.IsUseRobot(p.Platform, tmId) { + signupInfo := &SignupInfo{ + Platform: p.Platform, + Tmid: tmId, + Snid: p.SnId, + } + this.singleSignupPlayers[p.SnId] = signupInfo + this.playerSignUpTime[p.SnId] = time.Now().Unix() + } else { + signInfo = &SignInfo{ + signup: make(map[int32]*TmPlayer), + Platform: p.Platform, + MaxCnt: int(gmd.MatchNumebr), + } + var platform = p.Platform + gps := PlatformMgrSingleton.GetGameFree(p.Platform, gmd.GameFreeId) + if gps != nil && gps.GroupId != 0 { + for plt := range this.signUpPlayers { + gps2 := PlatformMgrSingleton.GetGameFree(plt, gmd.GameFreeId) + if gps.GroupId == gps2.GroupId { + platform = plt + break + } + } + } + if _, ok := this.signUpPlayers[platform][tmId]; !ok { + this.signUpPlayers[platform][tmId] = signInfo + } else { + signInfo = this.signUpPlayers[platform][tmId] + } + + if _, ok := signInfo.signup[p.SnId]; !ok { + n := len(signInfo.signup) + 1 + signInfo.signup[p.SnId] = &TmPlayer{SnId: p.SnId, seq: n} + logger.Logger.Trace("没使用机器人,真人 报名.............", n, " p.SnId: ", p.SnId) + } else { + return false, int32(tournament.SignRaceCode_OPRC_Repeat) + } + } + //(报名人数/20,报名人数/10) + minTime := gmd.MatchNumebr / 20 + maxTime := gmd.MatchNumebr / 10 + if gmd.MatchNumebr > 20 { + this.playerWaitStart[p.SnId] = int64(minTime + rand.Int31n(maxTime-minTime)) + } else { + this.playerWaitStart[p.SnId] = 1 + } + return true, 0 +} + +// CancelSignUp 取消玩家报名 +func (this *Tournament) CancelSignUp(platform string, tmid, snid int32) { + delete(this.playerWaitStart, snid) + if this.signUpPlayers[platform] != nil && this.signUpPlayers[platform][tmid] != nil { + signInfo := this.signUpPlayers[platform][tmid] + if _, ok := signInfo.signup[snid]; ok { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil && p.scene == nil { + //退费 + this.signUpCost(tmid, p, false) + delete(this.signUpPlayers[platform][tmid].signup, snid) + //通知取消报名 + pack := &tournament.SCSignRace{ + OpCode: 1, + RetCode: 0, + } + proto.SetDefaults(pack) + logger.Logger.Trace("signUpPlayers: ", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) + return + } + } + } + if signupinfo, ok := this.singleSignupPlayers[snid]; ok { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil && p.scene == nil { + //退费 + this.signUpCost(signupinfo.Tmid, p, false) + delete(this.singleSignupPlayers, snid) + delete(this.playerSignUpTime, snid) + //通知取消报名 + pack := &tournament.SCSignRace{ + OpCode: 1, + RetCode: 0, + } + proto.SetDefaults(pack) + logger.Logger.Trace("singleSignupPlayers: ", pack) + p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) + return + } + } +} + +// CancelSignUpAll 取消所有报名 +func (this *Tournament) CancelSignUpAll(platform string, tmId int32) { + if this.signUpPlayers[platform] != nil && this.signUpPlayers[platform][tmId] != nil { + for _, tmp := range this.signUpPlayers[platform][tmId].signup { + this.CancelSignUp(platform, tmId, tmp.SnId) + } + } + if this.singleSignupPlayers != nil { + for _, signupInfo := range this.singleSignupPlayers { + if signupInfo.Tmid == tmId { + this.CancelSignUp(signupInfo.Platform, tmId, signupInfo.Snid) + } + } + } +} + +// CanStart 是否开赛 +// 没有开机器人走这个判断 +func (this *Tournament) CanStart(platform string, tmId int32) bool { + if this.signUpPlayers != nil && this.signUpPlayers[platform] != nil { + if signInfo, ok := this.signUpPlayers[platform][tmId]; ok { + matchInfo := this.GetMatchInfo(signInfo.Platform, tmId) + if matchInfo != nil { + n := 0 + for _, v := range signInfo.signup { + p := PlayerMgrSington.GetPlayerBySnId(v.SnId) + if p == nil || p.scene != nil { //人不在或者在游戏内 + this.CancelSignUp(platform, tmId, v.SnId) + break + } + n++ + } + logger.Logger.Trace("TournamentMgr.CanStart: n: ", n, " matchInfo: ", matchInfo) + needNum := matchInfo.MatchNumebr / 4 * 4 //对4向下取整防止后台配置数据不是4的倍数 + //所有人都准备好了 + if needNum == int32(n) { + //人满 开始比赛 + return true + } + } + } + } + return false +} + +// Start 开赛 +func (this *Tournament) Start(platform string, tmId int32) { + if this.signUpPlayers[platform] == nil { + this.signUpPlayers[platform] = make(map[int32]*SignInfo) + } + matchInfo := this.GetMatchInfo(platform, tmId) + signInfo := this.signUpPlayers[platform][tmId] + sortId := int32(time.Now().Nanosecond()) + tm := &TmMatch{ + TMId: tmId, + SortId: sortId, + gmd: matchInfo, + Platform: signInfo.Platform, + tmpUseRobot: matchInfo.UseRobot, + } + tm.gml = srvdata.MatchLevelMgr.Get(matchInfo.GameFreeId, matchInfo.MatchLevel) + tm.dbGameFree = srvdata.PBDB_GameFreeMgr.GetData(matchInfo.GameFreeId) + tm.CopyMap(signInfo.signup) + if this.matches[tmId] == nil { + this.matches[tmId] = make(map[int32]*TmMatch) + } + logger.Logger.Tracef("开赛:%+v, 难度:%v", tm, tm.gml) + // 开始比赛,清除报名数据 + this.matches[tmId][sortId] = tm + tm.Start() + this.signUpPlayers[platform][tmId].signup = make(map[int32]*TmPlayer) +} + +// NextRoundStartSingle 下一轮是否开始 +func (this *Tournament) NextRoundStartSingle(sortId int32, mtp *PlayerMatchContext, matchRobotGrades map[int32]int32) { + logger.Logger.Tracef("================NextRoundStart_Single==========当前第 %v 轮============", mtp.round) + if _, ok := this.copyPlayers[sortId]; ok { + if _, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 { + gmd := mtp.tm.gmd + //需要晋级的人数 + promotionNum := gmd.MatchPromotion[mtp.round] // 晋级人数 + if promotionNum != 1 { // 非决赛 + if mtp.tm.robotGrades == nil || mtp.tm.robotGrades[int(mtp.round)] == nil { + mtp.tm.CreateRobotGrades(int(mtp.round)) + } + if mtp.tm.robotGrades != nil && mtp.tm.robotGrades[int(mtp.round)] != nil { + robotGrades := mtp.tm.robotGrades[int(mtp.round)] + // 修改陪真人玩的机器人积分 + if matchRobotGrades != nil { + logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades) + for _, gradeInfo := range robotGrades { + if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok { + gradeInfo.grade = grade + } + } + } + //非决赛淘汰后开始配桌 + logger.Logger.Trace("需要晋级的人数 promotionNum: ", promotionNum) + logger.Logger.Trace("非决赛开始淘汰晋级 grade: ", mtp.grade) + // 记录真人积分 + findMe := false + for _, gradeinfo := range robotGrades { + if gradeinfo.copySnid == 0 { + gradeinfo.grade = mtp.grade + findMe = true + break + } + } + if !findMe { + gradeInfo := &TmGradeInfo{ + grade: mtp.grade, + copySnid: 0, + } + robotGrades = append(robotGrades, gradeInfo) // 添加了真人数据 + } + sort.Slice(robotGrades, func(i, j int) bool { + return robotGrades[i].grade > robotGrades[j].grade + }) + for _, info := range robotGrades { + logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) + } + // 获取排名 + index := int32(0) //晋级淘汰 + for i, gradeinfo := range robotGrades { + if mtp.grade >= gradeinfo.grade { + index = int32(i) + 1 + break + } + } + robotGrades = append(robotGrades[:promotionNum]) + mtp.tm.robotGrades[int(mtp.round)] = robotGrades + + if index == 0 { // 是最后一名 + index = gmd.MatchPromotion[mtp.round-1] + } + if index <= promotionNum { + mtp.rank = index + this.sendPromotionInfo(mtp, sortId, JinJi, false, false) //晋级 + + mct := []*PlayerMatchContext{mtp} + finals := false + if mtp.round == int32(len(gmd.MatchPromotion)-2) { + finals = true + } + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1) + return true + }), nil, time.Second*7, 1) + } else { + mtp.rank = index + pri := &PerRankInfo{ + Name: mtp.p.Name, + SnId: mtp.p.SnId, + RankId: mtp.rank, + Grade: mtp.grade, + } + this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) + this.sendPromotionInfo(mtp, sortId, TaoTai, true, false) //淘汰 + } + } + } else { + // 比赛结束 + // 修改陪真人玩的机器人积分 + robotGrades := mtp.tm.robotGrades[int(mtp.round-1)] + if matchRobotGrades != nil { + logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades) + for snid, grade := range matchRobotGrades { + logger.Logger.Trace("Snid: ", snid, " grade: ", grade) + } + for _, gradeInfo := range robotGrades { + if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok { + gradeInfo.grade = grade + } + } + } + // 记录真人积分 + for _, gradeinfo := range robotGrades { + if gradeinfo.copySnid == 0 { + gradeinfo.grade = mtp.grade + break + } + } + sort.Slice(robotGrades, func(i, j int) bool { + return robotGrades[i].grade > robotGrades[j].grade + }) + logger.Logger.Trace("robotGrades: ") + for _, info := range robotGrades { + logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) + } + // 获取真人名次 + index := int32(0) //晋级淘汰 + for i, gradeinfo := range robotGrades { + if mtp.grade >= gradeinfo.grade { + index = int32(i) + 1 + break + } + } + if index == 0 { + index = gmd.MatchPromotion[mtp.round-1] //最后一名 + } + mtp.rank = index + if this.finalPerRank[sortId] == nil { + this.finalPerRank[sortId] = []*PerRankInfo{} + } + pri := &PerRankInfo{ + Name: mtp.p.Name, + SnId: mtp.p.SnId, + RankId: mtp.rank, + Grade: mtp.grade, + } + this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜 + outCode := TaoTai + if mtp.rank == 1 { + outCode = JinJi + } + this.sendPromotionInfo(mtp, sortId, outCode, true, true) //晋级 + logger.Logger.Trace("比赛结束!!! ") + // 比赛结束 + this.StopMatch(mtp.tm.TMId, sortId) + } + } + } +} + +// 下一轮是否开始 +func (this *Tournament) NextRoundStart(sortId int32, mtp *PlayerMatchContext) { + logger.Logger.Tracef("================NextRoundStart==========当前第 %v 轮============", mtp.round) + if _, ok := this.copyPlayers[sortId]; ok { + if mcs, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 { + gmd := mtp.tm.gmd + //需要晋级的人数 + promotionNum1 := gmd.MatchPromotion[mtp.round-1] + promotionNum := gmd.MatchPromotion[mtp.round] + //通知晋级信息:0.晋级等待匹配 1.失败退出 2.等待判断是否晋级 + pack := &tournament.SCPromotionInfo{} + if promotionNum != 1 { + n := int32(len(mcs)) + //非决赛淘汰后开始配桌 + logger.Logger.Trace("非决赛开始淘汰晋级") + outNum := promotionNum1 - promotionNum + //已经晋级的人数减去一桌之后 剩余人数还能够满足本轮淘汰 + logger.Logger.Trace("n: ", n, " outNum: ", outNum) + if n-4 >= outNum { + //提前晋级的开始凑桌 + MatchContextSlice(mcs).Sort(false) + //挑选出晋级的玩家 + meIn := false //自己晋级 + for k, v := range mcs { + if mtp.p.SnId == v.p.SnId { + meIn = true + } + logger.Logger.Trace("排序之后=========== ", k, v.p.SnId, v.round, v.seq, v.grade) + } + mct := []*PlayerMatchContext{} + finals := false + for i := 0; i < len(mcs)-int(outNum); i++ { + var mc PlayerMatchContext + mc = *mcs[i] + this.sendPromotionInfo(mcs[i], sortId, JinJi, false, false) //晋级 + mc.rank = mcs[i].rank + mct = append(mct, &mc) + logger.Logger.Trace("======凑桌==========mc=================", mc) + if !finals && mc.round == int32(len(gmd.MatchPromotion)-2) { + finals = true + } + } + mcs = mcs[len(mct):] + this.copyPlayers[sortId][mtp.round] = this.copyPlayers[sortId][mtp.round][len(mct):] + willOut := false + if promotionNum1 == this.roundOverPlayerNum[sortId][mtp.round-1] { + //最后一个人打完了,确定要淘汰的人 + willOut = true + } else { + if !meIn { //自己暂时没晋级 + this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定 + } + } + if this.finalPerRank[sortId] == nil { + this.finalPerRank[sortId] = []*PerRankInfo{} + } + isOver := false + for k, v := range this.copyPlayers[sortId][mtp.round] { + logger.Logger.Trace("凑桌之后剩余===2======== ", k, v.p.SnId, v.round, v.seq, v.grade) + if willOut { + this.sendPromotionInfo(v, sortId, TaoTai, true, false) //淘汰 + //把淘汰的玩家记录在排行榜 + pri := &PerRankInfo{ + Name: v.p.Name, + SnId: v.p.SnId, + RankId: pack.RankId, + Grade: v.grade, + } + this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) + //真人被淘汰,如果剩下的都是机器人,比赛解散 + if !v.p.IsRob { + if this.players[sortId] != nil { + hasReal := false + for snid, context := range this.players[sortId] { + if !context.p.IsRob && !this.isOut(sortId, snid) { // 有真人没有淘汰 + hasReal = true + break + } + } + //没有真人比赛解散 + if !hasReal { + isOver = true + logger.Logger.Trace("没有真人比赛解散") + this.StopMatch(mtp.tm.TMId, sortId) + } + } + } + } + } + if !isOver { + timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { + MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1) + return true + }), nil, time.Second*7, 1) + } + } else { + this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定 + } + } else { + if len(mcs) == 4 { + MatchContextSlice(mcs).Sort(true) + if this.finalPerRank[sortId] == nil { + this.finalPerRank[sortId] = []*PerRankInfo{} + } + for _, mc := range mcs { + pri := &PerRankInfo{ + Name: mc.p.Name, + SnId: mc.p.SnId, + RankId: mc.rank, + Grade: mc.grade, + } + this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜 + outCode := TaoTai + if mc.rank == 1 { + outCode = JinJi + } + this.sendPromotionInfo(mc, sortId, outCode, true, true) //晋级 + } + logger.Logger.Trace("比赛结束!!! ") + // 比赛结束 + this.StopMatch(mtp.tm.TMId, sortId) + } + } + } + } +} + +const ( + JinJi = 0 // 晋级 + TaoTai = 1 // 淘汰 + DaiDing = 2 // 待定 +) + +// 发送晋级信息 +// outCode 0晋级 1淘汰 2待定 +// isOver 玩家比赛结束 +// isFinals 比赛结束 +func (this *Tournament) sendPromotionInfo(mc *PlayerMatchContext, sortId int32, outCode int, isOver, isFinals bool) { + if mc == nil { + return + } + rankId := mc.rank + if mc.tm.tmpUseRobot == MatchNotUserRobot { // 不使用机器人 + rankId = this.getRank(sortId, mc.round, mc.p.SnId, isFinals) + mc.rank = rankId + } + //通知晋级信息:0.晋级等待匹配 1.失败退出 2.等待判断是否晋级 + pack := &tournament.SCPromotionInfo{} + pack.RetCode = int32(outCode) + pack.Round = mc.round + pack.RankId = rankId + pack.RoundCoin = 100 //暂时用不到先写死 + pack.Record = mc.record + if mc.tm != nil { + pack.MatchId = mc.tm.TMId + } + if mc.tm != nil && mc.tm.gmd != nil { + pack.MatchPromotion = mc.tm.gmd.GetMatchPromotion() + pack.MatchName = mc.tm.gmd.GetMatchName() + if !mc.p.IsRob && isOver { //真人发奖 + if mc.tm.gmd.Award != nil { + for _, award := range mc.tm.gmd.Award { + if rankId >= award.UpLimit && rankId <= award.DownLimit { //上下限是反的,我也是醉了 + rankAward := &tournament.RankAward{ + Coin: proto.Int64(award.Coin), + Diamond: proto.Int64(award.Diamond), + } + if award.ItemId != nil { + for _, info := range award.ItemId { + item := &tournament.ItemInfo{ + ItemId: proto.Int32(info.ItemId), + ItemNum: proto.Int32(int32(info.ItemNum)), + Name: proto.String(info.Name), + } + rankAward.ItemInfo = append(rankAward.ItemInfo, item) + } + } + // test + //rankAward.ItemInfo = append(rankAward.ItemInfo, &tournament.ItemInfo{ + // ItemId: 30003, + // ItemNum: 1, + // Name: "1元话费兑换码", + //}) + // test + pack.RankAward = rankAward + } + } + } + } + } + + sendFunc := func() { + outStr := "晋级" + switch outCode { + case 1: + outStr = "淘汰" + case 2: + outStr = "待定" + } + proto.SetDefaults(pack) + logger.Logger.Trace("sendPromotionInfo: ", outStr, " snid: ", mc.p.SnId, " pack: ", pack) + ok := mc.p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCPromotionInfo), pack) + if ok && !mc.p.IsRob && isOver { + if pack.RankAward != nil { + if pack.RankAward.Coin != 0 { //金币 + mc.p.AddCoin(pack.RankAward.Coin, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") + } + if pack.RankAward.Diamond != 0 { //钻石 + mc.p.AddDiamond(pack.RankAward.Diamond, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") + } + if pack.RankAward.ItemInfo != nil { + for _, info := range pack.RankAward.ItemInfo { + if info.ItemNum <= 0 { + continue + } + item := &Item{ + ItemId: info.ItemId, + ItemNum: int64(info.ItemNum), + } + BagMgrSingleton.AddJybBagInfo(mc.p, []*Item{item}, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") + data := srvdata.PBDB_GameItemMgr.GetData(info.ItemId) + if data != nil { + // 背包变更记录 + BagMgrSingleton.RecordItemLog(mc.p.Platform, mc.p.SnId, ItemObtain, item.ItemId, data.Name, item.ItemNum, mc.tm.gmd.MatchName+"排名奖励") + } + } + } + } + } + if isOver && mc.tm != nil { // 自己比赛结束 + if !mc.p.IsRob { + this.CheckAddMatchSeasonLv(mc) + } + delete(this.players[sortId], mc.p.SnId) + } + } + + // 获取兑换码奖品 + var has bool + wg := new(sync.WaitGroup) + if pack.GetRankAward() != nil { + for _, v := range pack.GetRankAward().GetItemInfo() { + data := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) + if data != nil && data.GetType() == common.ItemTypePhoneCode { + has = true + item := v // 并发需要复制 + wg.Add(1) + // 兑换码奖品 + var err error + var newMsg *model.Message + res := &webapi_proto.SAGetMatchAwardCode{} + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // 获取兑换码 + pack := &webapi_proto.ASGetMatchAwardCode{ + Platform: mc.p.Platform, + Snid: mc.p.SnId, + ItemID: data.GetId(), + Money: data.GetNum(), + Tel: mc.p.Tel, + } + logger.Logger.Trace("GetMatchAwardCode ", pack) + var buff []byte + buff, err = webapi.APIGetMatchAwardCode(common.GetAppId(), pack) + if err != nil { + return err + } + if err = proto.Unmarshal(buff, res); err != nil { + return err + } + + if res.GetCode() == "" || res.GetTag() != webapi_proto.TagCode_SUCCESS { + return nil + } + + var params []string + for range []string{"zh", "vi", "en", "kh"} { + params = append(params, res.GetMoney(), res.GetCode()) // 金额,兑换码 + } + // 发送邮件 + title := i18n.Tr("languages", "MatchAwardTitle") + content := i18n.Tr("languages", "MatchAward", params) + newMsg = model.NewMessage("", 0, "", mc.p.SnId, model.MSGTYPE_RANK_REWARD, + title, content, 0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", nil, mc.p.Platform, model.HallTienlen) + err := model.InsertMessage(mc.p.Platform, newMsg) + if err != nil { + logger.Logger.Errorf("发送邮件失败 snid:%v itemID:%v err:%v", mc.p.SnId, data.Id, err) + return err + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + defer wg.Done() + if err != nil || res.GetCode() == "" || res.GetTag() != webapi_proto.TagCode_SUCCESS { + logger.Logger.Errorf("获取兑换码失败 snid:%v itemID:%v res:%v err:%v", mc.p.SnId, data.Id, res, err) + return + } + item.Code = res.GetCode() + item.Msg = res.GetMoney() + p := PlayerMgrSington.GetPlayerBySnId(mc.p.SnId) + if p != nil { + p.AddMessage(newMsg) + } + }), fmt.Sprintf("RankAward%d", mc.p.SnId)).Start() + } + } + } + + if !has { + sendFunc() + return + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + wg.Wait() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + sendFunc() + })).Start() +} + +// 更新段位 +func (this *Tournament) CheckAddMatchSeasonLv(mc *PlayerMatchContext) { + if mc == nil { + return + } + platform := mc.p.Platform + if platform == DefaultPlatform { + return + } + rank := mc.rank + maxPlayerNum := mc.tm.gmd.MatchNumebr + upLine := maxPlayerNum * 33 / 100 + downLine := maxPlayerNum * 67 / 100 + snid := mc.p.SnId + ms := MatchSeasonMgrSington.GetMatchSeason(mc.p.SnId) + msid := MatchSeasonMgrSington.GetMatchSeasonId(platform) + if msid == nil { + MatchSeasonMgrSington.UpdateMatchSeasonId(platform) + } + if ms == nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, err := model.QueryMatchSeasonBySnid(platform, snid) + if err != nil { + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + var ret *model.MatchSeason + dirty := false + seasonId := int32(1) + if msid != nil { + seasonId = msid.SeasonId + } + if data == nil || data.(*model.MatchSeason) == nil { + player := PlayerMgrSington.GetPlayerBySnId(snid) + if player != nil { + ret = model.NewMatchSeason(player.Platform, snid, player.Name, seasonId, 1) + dirty = true + } else { + logger.Logger.Error("CSTMSeasonInfoHandler error: player == nil or msi == nil", snid) + } + } else { + ret = data.(*model.MatchSeason) + if ret.SeasonId < seasonId { //不同赛季段位继承 + num := seasonId - ret.SeasonId + finalLv := ret.Lv + for i := 0; i < int(num); i++ { //继承几次 + if i == int(num)-1 { //上个赛季 + ret.LastLv = finalLv + } + finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) + } + ret.Lv = finalLv + ret.SeasonId = seasonId + ret.IsAward = false + dirty = true + } + } + ms = MatchSeasonMgrSington.exchangeModel2Cache(ret) + ms.dirty = dirty + MatchSeasonMgrSington.SetMatchSeason(ms) + if rank <= upLine { //加分 + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty) + } else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分 + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty) + } else { + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty) + } + })).StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) + } else { + dirty := false + if ms.SeasonId < msid.SeasonId { //不同赛季段位继承 + num := msid.SeasonId - ms.SeasonId + finalLv := ms.Lv + for i := 0; i < int(num); i++ { //继承几次 + if i == int(num)-1 { //上个赛季 + ms.LastLv = finalLv + } + finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) + } + ms.Lv = finalLv + ms.SeasonId = msid.SeasonId + ms.IsAward = false + ms.UpdateTs = time.Now().Unix() + ms.dirty = true + dirty = true + MatchSeasonMgrSington.SetMatchSeason(ms) + } + if rank <= upLine { //加分 + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty) + } else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分 + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty) + } else { + MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty) + } + } +} + +// 比赛结束 +func (this *Tournament) StopMatch(tmId, sortId int32) { + //房间清理 + if this.matches[tmId] != nil && this.matches[tmId][sortId] != nil { + this.matches[tmId][sortId].Stop() + delete(this.matches[tmId], sortId) + } + //数据清理 + delete(this.players, sortId) + delete(this.copyPlayers, sortId) + delete(this.rankPlayers, sortId) + delete(this.roundOverPlayerNum, sortId) + delete(this.finalPerRank, sortId) +} + +func (this *Tournament) getRank(sortId, round, snid int32, isFinals bool) int32 { + if _, ok := this.rankPlayers[sortId]; ok { + if rps, ok1 := this.rankPlayers[sortId][round]; ok1 { + MatchContextSlice(rps).Sort(isFinals) + for _, rp := range rps { + if rp.p.SnId == snid { + return rp.rank + } + } + } + } + return 0 +} + +func (this *Tournament) isOut(sortId, snid int32) bool { + out := false + if this.finalPerRank[sortId] != nil { + for _, info := range this.finalPerRank[sortId] { + if info.SnId == snid { + out = true + } + } + } + return out +} + +func (this *Tournament) GetTm(sortId int32) *TmMatch { + if this.matches != nil { + for _, sortTms := range this.matches { + for id, match := range sortTms { + if id == sortId { + return match + } + } + } + } + return nil +} + +// 玩家比赛结束 更新积分 +func (this *Tournament) UpdateMatchInfo(p *Player, sortId, grade, isWin int32, matchRobotGrades map[int32]int32) { + logger.Logger.Trace("=========== UpdateMatchInfo ==============") + logger.Logger.Tracef("UpdateMatchInfo: sortId:%v, grade:%v, isWin: %v, matchRobotGrades:%v", sortId, grade, isWin, matchRobotGrades) + if _, ok := this.players[sortId]; !ok { + this.players[sortId] = make(map[int32]*PlayerMatchContext) + } + if mtp, ok := this.players[sortId][p.SnId]; ok { + mtp.grade = grade + if this.roundOverPlayerNum[sortId] == nil { + this.roundOverPlayerNum[sortId] = make(map[int32]int32) + } + this.roundOverPlayerNum[sortId][mtp.round]++ + if mtp.record == nil { + mtp.record = make(map[int32]int32) + } + mtp.record[isWin]++ + //轮数增加 + mtp.round++ + mtp.gaming = false + if this.copyPlayers[sortId] == nil { + this.copyPlayers[sortId] = make(map[int32][]*PlayerMatchContext) + } + var mc PlayerMatchContext + mc = *mtp + this.copyPlayers[sortId][mtp.round] = append(this.copyPlayers[sortId][mtp.round], &mc) + if this.rankPlayers[sortId] == nil { + this.rankPlayers[sortId] = make(map[int32][]*PlayerMatchContext) + } + this.rankPlayers[sortId][mtp.round] = append(this.rankPlayers[sortId][mtp.round], &mc) + logger.Logger.Tracef("========snid(%v) grade(%v) ============", p.SnId, grade) + if mtp.tm.tmpUseRobot == MatchUseRobot { // 使用机器人 + this.NextRoundStartSingle(sortId, mtp, matchRobotGrades) + } else { + this.NextRoundStart(sortId, mtp) + } + } +} + +func (this *Tournament) CreatePlayerMatchContext(p *Player, m *TmMatch, seq int) *PlayerMatchContext { + ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId) + var lv int32 + if ms != nil { + lv = ms.Lv + } + roleId := int32(2000001) + if p.Roles != nil { + roleId = p.Roles.ModId + } + + mc := NewMatchContext(p, m, 1000, p.SnId, lv, roleId, seq) + if mc != nil { + if this.players[m.SortId] == nil { + this.players[m.SortId] = make(map[int32]*PlayerMatchContext) + } + this.players[m.SortId][p.SnId] = mc + p.matchCtx = mc + return mc + } + return nil +} + +// GetSCTMInfosPack 发送比赛配置数据 +func (this *Tournament) GetSCTMInfosPack(platform, channelName string) *tournament.SCTMInfos { + pack := &tournament.SCTMInfos{} + matchInfo := this.GetAllMatchInfo(platform) + if matchInfo != nil { + for id, info := range matchInfo { + if info.MatchSwitch == 1 && !this.IsOutTime(info) && common.InMatchChannel(info.OnChannelName, channelName) { + tMInfo := &tournament.TMInfo{ + Id: proto.Int32(id), + GameFreeId: info.GameFreeId, + MatchType: info.MatchType, + MatchName: info.MatchName, + MatchNumebr: info.MatchNumebr, + MatchSwitch: info.MatchSwitch, + SignupCostCoin: info.SignupCostCoin, + SignupCostDiamond: info.SignupCostDiamond, + MatchTimeType: info.MatchTimeType, + MatchTimeStartHMS: info.MatchTimeStartHMS, + MatchTimeEndHMS: info.MatchTimeEndHMS, + TitleURL: info.TitleURL, + AwardShow: info.AwardShow, + Rule: info.Rule, + SortId: info.SortId, + OnChannelName: info.OnChannelName, + } + if info.MatchTimeWeek != nil && len(info.MatchTimeWeek) > 0 { + for _, week := range info.MatchTimeWeek { + tMInfo.MatchTimeWeek = append(tMInfo.MatchTimeWeek, week) + } + } + if info.MatchTimeStamp != nil && len(info.MatchTimeStamp) > 0 { + for _, stamp := range info.MatchTimeStamp { + tMInfo.MatchTimeStamp = append(tMInfo.MatchTimeStamp, stamp) + } + } + if info.MatchPromotion != nil { + for _, mp := range info.MatchPromotion { + tMInfo.MatchPromotion = append(tMInfo.MatchPromotion, mp) + } + } + if info.Award != nil { + for _, award := range info.Award { + miAward := &tournament.MatchInfoAward{ + Coin: award.Coin, + Diamond: award.Diamond, + UpLimit: award.UpLimit, + DownLimit: award.DownLimit, + } + if award.ItemId != nil { + for _, itemInfo := range award.ItemId { + a := &tournament.ItemInfo{ + ItemId: itemInfo.ItemId, + ItemNum: int32(itemInfo.ItemNum), + Name: itemInfo.Name, + } + miAward.ItemInfo = append(miAward.ItemInfo, a) + } + } + tMInfo.Award = append(tMInfo.Award, miAward) + } + } + if info.SignupCostItem != nil && info.SignupCostItem.ItemNum > 0 { + signupCost := &tournament.ItemInfo{ + ItemId: info.SignupCostItem.ItemId, + ItemNum: int32(info.SignupCostItem.ItemNum), + Name: info.SignupCostItem.Name, + } + tMInfo.SignupCostItem = signupCost + } + pack.TMInfo = append(pack.TMInfo, tMInfo) + } + } + } + return pack +} + +func (this *Tournament) ModuleName() string { + return "Tournament" +} + +// 初始化 +func (this *Tournament) Init() { + logger.Logger.Trace("(this *Tournament) Init()") +} + +func (this *Tournament) Update() { + // 使用机器人的情况 + for snid, info := range this.singleSignupPlayers { + if this.playerSignUpTime[snid] != 0 { + matchInfo := this.GetMatchInfo(info.Platform, info.Tmid) + if matchInfo.MatchSwitch == MatchSwitchStart && !this.IsOutTime(matchInfo) && this.IsUseRobot(info.Platform, info.Tmid) { + needTime := this.playerWaitStart[snid] + if time.Now().Unix()-this.playerSignUpTime[snid] > needTime { + delete(this.singleSignupPlayers, snid) + delete(this.playerSignUpTime, snid) + signInfo := &SignInfo{ + signup: make(map[int32]*TmPlayer), + Platform: info.Platform, + MaxCnt: int(matchInfo.MatchNumebr), + } + seq := rand.Intn(int(matchInfo.MatchNumebr)) + 1 + signInfo.signup[snid] = &TmPlayer{SnId: snid, seq: seq} + this.signUpPlayers[info.Platform][info.Tmid] = signInfo + //倒计时结束 开始比赛 + this.Start(info.Platform, info.Tmid) + } + } else { + this.CancelSignUpAll(info.Platform, info.Tmid) + } + } + } +} + +func (this *Tournament) Shutdown() { +} +func init() { + module.RegisteModule(TournamentMgr, time.Second, 0) +} diff --git a/worldsrv/transact_addcoin.go b/worldsrv/transact_addcoin.go new file mode 100644 index 0000000..2509420 --- /dev/null +++ b/worldsrv/transact_addcoin.go @@ -0,0 +1,122 @@ +package main + +import ( + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +var TransAddCoinTimeOut = time.Second * 30 + +const ( + TRANSACT_ADDCOIN_CTX = iota +) + +type AsyncAddCoinTransactContext struct { + p *Player + coin int64 + gainWay int32 + oper string + remark string + broadcast bool + writeLog bool + retryCnt int +} +type AddCoinTransactHandler struct { +} + +func (this *AddCoinTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnExcute ") + if ctx, ok := ud.(*AsyncAddCoinTransactContext); ok { + if ctx.p != nil && ctx.p.scene != nil { + pack := &common.WGAddCoin{ + SnId: ctx.p.SnId, + Coin: ctx.coin, + GainWay: ctx.gainWay, + Oper: ctx.oper, + Remark: ctx.remark, + Broadcast: ctx.broadcast, + WriteLog: ctx.writeLog, + } + tnp := &transact.TransNodeParam{ + Tt: common.TransType_AddCoin, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: int(ctx.p.scene.gameSess.GetSrvId()), + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + + tNode.TransEnv.SetField(TRANSACT_ADDCOIN_CTX, ud) + tNode.StartChildTrans(tnp, pack, TransAddCoinTimeOut) + } + } + + return transact.TransExeResult_Success +} + +func (this *AddCoinTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnCommit ") + ud := tNode.TransEnv.GetField(TRANSACT_ADDCOIN_CTX) + if ctx, ok := ud.(*AsyncAddCoinTransactContext); ok { + p := PlayerMgrSington.GetPlayerBySnId(ctx.p.SnId) //重新获得p + if p != nil { + p.Coin += ctx.coin + p.dirty = true + } + } + return transact.TransExeResult_Success +} + +func (this *AddCoinTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnRollBack ") + ud := tNode.TransEnv.GetField(TRANSACT_ADDCOIN_CTX) + if ctx, ok := ud.(*AsyncAddCoinTransactContext); ok { + p := PlayerMgrSington.GetPlayerBySnId(ctx.p.SnId) //重新获得p + if p != nil { + p.AddCoinAsync(ctx.coin, 0, ctx.gainWay, ctx.oper, ctx.remark, ctx.broadcast, ctx.retryCnt+1, ctx.writeLog) + } else { + logger.Logger.Errorf("AddCoinTransactHandler.OnRollBack SnId=%v,Coin=%v,GainWay=%v,Oper=%v,Remark=%v,RetryCnt=%v", ctx.p.SnId, ctx.coin, ctx.gainWay, ctx.oper, ctx.remark, ctx.retryCnt) + } + } + return transact.TransExeResult_Success +} + +func (this *AddCoinTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("AddCoinTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func StartAsyncAddCoinTransact(p *Player, num int64, gainWay int32, oper, remark string, broadcast bool, retryCnt int, writeLog bool) bool { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_AddCoin, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + ctx := &AsyncAddCoinTransactContext{ + p: p, + coin: num, + gainWay: gainWay, + oper: oper, + remark: remark, + broadcast: broadcast, + writeLog: writeLog, + retryCnt: retryCnt, + } + tNode := transact.DTCModule.StartTrans(tnp, ctx, TransAddCoinTimeOut) + if tNode != nil { + tNode.Go(core.CoreObject()) + return true + } + + return false +} + +func init() { + transact.RegisteHandler(common.TransType_AddCoin, &AddCoinTransactHandler{}) +} diff --git a/worldsrv/transact_coinscenechange.go b/worldsrv/transact_coinscenechange.go new file mode 100644 index 0000000..f0b09af --- /dev/null +++ b/worldsrv/transact_coinscenechange.go @@ -0,0 +1,168 @@ +package main + +import ( + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/proto" + hall_proto "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +var CoinSceneChangeTimeOut = time.Second * 10 +var CoinSceneChangeTransactParam int + +type CoinSceneChangeCtx struct { + id int32 + snid int32 + sceneId int32 + exclude []int32 + isAudience bool + roomId string + isClub bool + clubId int32 +} + +type CoinSceneChangeTransactHandler struct { +} + +func (this *CoinSceneChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnExcute ") + if ctx, ok := ud.(*CoinSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil && player.scene != nil { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_CoinSceneChange, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: int(player.scene.gameSess.GetSrvId()), + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_SelfDecide, + } + pack := &common.WGCoinSceneChange{ + SnId: ctx.snid, + SceneId: ctx.sceneId, + } + tNode.TransEnv.SetField(CoinSceneChangeTransactParam, ud) + tNode.StartChildTrans(tnp, pack, CoinSceneChangeTimeOut) + } + } + + return transact.TransExeResult_Success +} + +func (this *CoinSceneChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnCommit ") + ud := tNode.TransEnv.GetField(CoinSceneChangeTransactParam) + if ctx, ok := ud.(*CoinSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil { + if ctx.isClub { + //ClubSceneMgrSington.ClearPlayerChanging(player) + } else { + CoinSceneMgrSingleton.ClearPlayerChanging(player) + } + op := common.CoinSceneOp_Change + if ctx.isAudience { + op = common.CoinSceneOp_AudienceChange + } + pack := &hall_proto.SCCoinSceneOp{ + Id: proto.Int32(ctx.id), + OpType: proto.Int32(op), + OpParams: ctx.exclude, + } + + //此处需要判定是否在线,异步后,可能玩家已经离线,无法再执行此操作了 + if player.IsOnLine() == false { + pack := &hall_proto.SCLeaveRoom{ + OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game, + RoomId: proto.Int32(ctx.sceneId), + } + proto.SetDefaults(pack) + player.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + return transact.TransExeResult_Success + } + + var ret hall_proto.OpResultCode + if ctx.isAudience { + noother := false + ret = CoinSceneMgrSingleton.AudienceEnter(player, ctx.id, 0, ctx.exclude, true) + if ret != hall_proto.OpResultCode_OPRC_Sucess { //失败,还进当前桌 + ret = CoinSceneMgrSingleton.AudienceEnter(player, ctx.id, 0, nil, true) + if ret == hall_proto.OpResultCode_OPRC_Sucess { + noother = true + } + } + if ret != hall_proto.OpResultCode_OPRC_Sucess { //失败,直接离场 + pack := &hall_proto.SCLeaveRoom{ + OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game, + RoomId: proto.Int32(ctx.sceneId), + } + proto.SetDefaults(pack) + player.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + } + if ret == hall_proto.OpResultCode_OPRC_Sucess && noother { + ret = hall_proto.OpResultCode_OPRC_NoOtherDownTiceRoom + } + } else { + if ctx.isClub { + //ret = ClubSceneMgrSington.ClubPlayerEnter(player, ctx.clubId, ctx.roomId, int(-1), "", true, + // int(ctx.sceneId)) + } else { + ret = CoinSceneMgrSingleton.PlayerEnter(player, ctx.id, 0, ctx.exclude, true) + } + if !(ret == hall_proto.OpResultCode_OPRC_Sucess || ret == hall_proto.OpResultCode_OPRC_CoinSceneEnterQueueSucc) { //失败,直接离场 + pack := &hall_proto.SCLeaveRoom{ + OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game, + RoomId: proto.Int32(ctx.sceneId), + } + proto.SetDefaults(pack) + player.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack) + } + } + pack.OpCode = ret + proto.SetDefaults(pack) + player.SendToClient(int(hall_proto.CoinSceneGamePacketID_PACKET_SC_COINSCENE_OP), pack) + } + } + return transact.TransExeResult_Success +} + +func (this *CoinSceneChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnRollBack ") + ud := tNode.TransEnv.GetField(CoinSceneChangeTransactParam) + if ctx, ok := ud.(*CoinSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil { + if ctx.isClub { + //ClubSceneMgrSington.ClearPlayerChanging(player) + } else { + CoinSceneMgrSingleton.ClearPlayerChanging(player) + } + op := common.CoinSceneOp_Change + if ctx.isAudience { + op = common.CoinSceneOp_AudienceChange + } + pack := &hall_proto.SCCoinSceneOp{ + Id: proto.Int32(ctx.id), + OpType: proto.Int32(op), + OpParams: ctx.exclude, + OpCode: hall_proto.OpResultCode_OPRC_CoinSceneYouAreGaming, + } + proto.SetDefaults(pack) + player.SendToClient(int(hall_proto.CoinSceneGamePacketID_PACKET_SC_COINSCENE_OP), pack) + } + } + return transact.TransExeResult_Success +} + +func (this *CoinSceneChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("CoinSceneChangeTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_CoinSceneChange, &CoinSceneChangeTransactHandler{}) +} diff --git a/worldsrv/transact_daytimechange.go b/worldsrv/transact_daytimechange.go new file mode 100644 index 0000000..8500ca0 --- /dev/null +++ b/worldsrv/transact_daytimechange.go @@ -0,0 +1,75 @@ +package main + +import ( + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +var wgDayTimeChangePack = &common.WGDayTimeChange{} +var DayTimeChangeTimeOut = time.Second * 10 + +type DayTimeChangeTransactHandler struct { +} + +func (this *DayTimeChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnExcute ") + ClockMgrSington.Notifying = true + for sid, _ := range GameSessMgrSington.servers { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_DayTimeChange, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: sid, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_SelfDecide, + } + //logger.Logger.Tracef("TransNode=%v", *tnp) + _, wgDayTimeChangePack.LastMin, wgDayTimeChangePack.LastHour, wgDayTimeChangePack.LastDay, wgDayTimeChangePack.LastWeek, wgDayTimeChangePack.LastMonth = ClockMgrSington.GetLast() + tNode.StartChildTrans(tnp, wgDayTimeChangePack, DayTimeChangeTimeOut) + } + + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnCommit ") + ClockMgrSington.Notifying = false + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnRollBack ") + ClockMgrSington.Notifying = false + return transact.TransExeResult_Success +} + +func (this *DayTimeChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("DayTimeChangeTransactHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +type DayTimeChangeTransactSinker struct { + BaseClockSinker +} + +func (this *DayTimeChangeTransactSinker) OnMiniTimer() { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_DayTimeChange, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + tNode := transact.DTCModule.StartTrans(tnp, nil, DayTimeChangeTimeOut) + if tNode != nil { + tNode.Go(core.CoreObject()) + } +} + +func init() { + ClockMgrSington.RegisteSinker(&DayTimeChangeTransactSinker{}) + transact.RegisteHandler(common.TransType_DayTimeChange, &DayTimeChangeTransactHandler{}) +} diff --git a/worldsrv/transact_matchscenechange.go b/worldsrv/transact_matchscenechange.go new file mode 100644 index 0000000..e44d07e --- /dev/null +++ b/worldsrv/transact_matchscenechange.go @@ -0,0 +1,91 @@ +package main + +import ( + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +var MatchSceneChangeTimeOut = time.Second * 10 +var MatchSceneChangeTransactParam int + +type MatchSceneChangeCtx struct { + snid int32 + sceneId int32 + matchId int32 + processIdx int +} + +type MatchSceneChangeTransactHandler struct { +} + +func (this *MatchSceneChangeTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnExcute") + if ctx, ok := ud.(*MatchSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil && player.scene != nil { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_MatchSceneChange, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: int(player.scene.gameSess.GetSrvId()), + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_SelfDecide, + } + pack := &common.WGCoinSceneChange{ + SnId: ctx.snid, + SceneId: ctx.sceneId, + } + tNode.TransEnv.SetField(MatchSceneChangeTransactParam, ud) + tNode.StartChildTrans(tnp, pack, MatchSceneChangeTimeOut) + } + } + + return transact.TransExeResult_Success +} + +func (this *MatchSceneChangeTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnCommit") + ud := tNode.TransEnv.GetField(MatchSceneChangeTransactParam) + if ctx, ok := ud.(*MatchSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil { + //m := MatchMgrSington.GetCopyMatch(ctx.matchId) + //if m != nil { + // if m.process != nil && m.process.idx == ctx.processIdx { + // m.process.TryEnterMatch(player.matchCtx) + // } + //} + } + } + return transact.TransExeResult_Success +} + +func (this *MatchSceneChangeTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnRollBack") + ud := tNode.TransEnv.GetField(MatchSceneChangeTransactParam) + if ctx, ok := ud.(*MatchSceneChangeCtx); ok { + player := PlayerMgrSington.GetPlayerBySnId(ctx.snid) + if player != nil { + //m := MatchMgrSington.GetCopyMatch(ctx.matchId) + //if m != nil { + // if m.process != nil && m.process.idx == ctx.processIdx { + // m.process.waitPlayer = append(m.process.waitPlayer, player.matchCtx) + // } + //} + } + } + return transact.TransExeResult_Success +} + +func (this *MatchSceneChangeTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("MatchSceneChangeTransactHandler.OnChildTransRep") + return transact.TransExeResult_Success +} + +func init() { + transact.RegisteHandler(common.TransType_MatchSceneChange, &MatchSceneChangeTransactHandler{}) +} diff --git a/worldsrv/transact_minigameaddcoin.go b/worldsrv/transact_minigameaddcoin.go new file mode 100644 index 0000000..7f8b1a3 --- /dev/null +++ b/worldsrv/transact_minigameaddcoin.go @@ -0,0 +1,102 @@ +package main + +// +//import ( +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/netlib" +// "mongo.games.com/goserver/core/transact" +// "mongo.games.com/goserver/srvlib" +// "time" +//) +// +//const ( +// TRANSACT_MINIGAMEADDCOIN_CTX = iota +//) +// +//type MiniGameAddCoinTransactHandler struct { +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnExcute ") +// ctx := &common.WGAddCoin{} +// err := netlib.UnmarshalPacketNoPackId(ud.([]byte), ctx) +// if err != nil { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnExcute failed:", err) +// return transact.TransExeResult_Failed +// } +// p := PlayerMgrSington.GetPlayerBySnId(ctx.SnId) +// if p != nil { +// //玩家可能正在换房间 +// if ctx.Coin != 0 && p.scene != nil && !p.scene.IsTestScene() && p.scene.sceneMode != common.SceneMode_Thr { //游戏场中加币,需要同步到gamesrv上 +// tnp := &transact.TransNodeParam{ +// Tt: common.TransType_MiniGameAddCoin, +// Ot: transact.TransOwnerType(srvlib.GameServerType), +// Oid: int(p.scene.gameSess.GetSrvId()), +// AreaID: common.GetSelfAreaId(), +// Tct: transact.TransactCommitPolicy_SelfDecide, +// } +// tNode.StartChildTrans(tnp, ctx, time.Duration(tNode.MyTnp.ExpiresTs-time.Now().UnixNano())) +// tNode.TransEnv.SetField(TRANSACT_MINIGAMEADDCOIN_CTX, ctx) +// return transact.TransExeResult_Success +// } +// +// //钱不够扣 +// if ctx.Coin < 0 && -ctx.Coin > p.Coin { +// return transact.TransExeResult_Failed +// } +// +// logger.Logger.Tracef("MiniGameAddCoinTransactHandler.OnExcute 当前金币:%v 金币变化:%v 变化后金币:%v", p.Coin, ctx.Coin, p.Coin+ctx.Coin) +// p.Coin += ctx.Coin +// p.dirty = true +// p.SendDiffData() +// if !p.IsRob && ctx.WriteLog { +// log := model.NewCoinLogEx(p.SnId, ctx.Coin, p.Coin, p.SafeBoxCoin, p.Ver, ctx.GainWay, 0, +// ctx.Oper, ctx.Remark, p.Platform, p.Channel, p.BeUnderAgentCode, 0, p.PackageID, 0) +// if log != nil { +// LogChannelSingleton.WriteLog(log) +// } +// } +// tNode.TransRep.RetFiels = p.Coin +// return transact.TransExeResult_Success +// } +// +// return transact.TransExeResult_Failed +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnCommit ") +// ud := tNode.TransEnv.GetField(TRANSACT_MINIGAMEADDCOIN_CTX) +// if ctx, ok := ud.(*common.WGAddCoin); ok { +// p := PlayerMgrSington.GetPlayerBySnId(ctx.SnId) //重新获得p +// if p != nil { +// p.Coin += ctx.Coin +// p.dirty = true +// } +// } +// return transact.TransExeResult_Success +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnRollBack ") +// return transact.TransExeResult_Success +//} +// +//func (this *MiniGameAddCoinTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, +// retCode int, ud interface{}) transact.TransExeResult { +// logger.Logger.Trace("MiniGameAddCoinTransactHandler.OnChildTransRep ") +// if retCode == transact.TransResult_Success { +// if data, ok := ud.([]byte); ok { +// var coin int64 +// if netlib.UnmarshalPacketNoPackId(data, &coin) == nil { +// tNode.TransRep.RetFiels = coin +// } +// } +// } +// return transact.TransExeResult_Success +//} +// +//func init() { +// transact.RegisteHandler(common.TransType_MiniGameAddCoin, &MiniGameAddCoinTransactHandler{}) +//} diff --git a/worldsrv/transact_queryallcoinpoolstates.go b/worldsrv/transact_queryallcoinpoolstates.go new file mode 100644 index 0000000..5c37477 --- /dev/null +++ b/worldsrv/transact_queryallcoinpoolstates.go @@ -0,0 +1,192 @@ +package main + +import ( + "mongo.games.com/game/common" + "mongo.games.com/game/webapi" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" + "sort" + "strconv" + "time" +) + +var QueryAllCoinPoolTimeOut = time.Second * 30 + +const ( + QueryAllCoinPoolTransactParam_ParentNode int = iota + QueryAllCoinPoolTransactParam_Data + QueryAllCoinPoolTransactParam_Total +) + +type QueryAllCoinPoolTransactHandler struct { +} + +func (this *QueryAllCoinPoolTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnExcute ") + for sid, gs := range GameSessMgrSington.servers { + if gs.srvType == srvlib.GameServerType { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_QueryAllCoinPool, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: sid, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_SelfDecide, + } + tNode.StartChildTrans(tnp, ud, QueryAllCoinPoolTimeOut) + } + } + + return transact.TransExeResult_Success +} + +func (this *QueryAllCoinPoolTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnCommit ") + field := tNode.TransEnv.GetField(QueryAllCoinPoolTransactParam_Data) + parent := tNode.TransEnv.GetField(QueryAllCoinPoolTransactParam_ParentNode) + Total := tNode.TransEnv.GetField(QueryAllCoinPoolTransactParam_Total) + if tParent, ok := parent.(*transact.TransNode); ok { + resp := webapi.NewResponseBody() + resp[webapi.RESPONSE_STATE] = webapi.STATE_OK + if settings, ok := field.(map[string]*common.PlatformStates); ok { + //根据平台id排序 + //map排序 + keys := []int{} + for k := range settings { + id, err := strconv.Atoi(k) + if err == nil { + keys = append(keys, id) + } + } + sort.Ints(keys) + info := []*common.PlatformStates{} + for _, id := range keys { + info = append(info, settings[strconv.Itoa(id)]) + } + resp[webapi.RESPONSE_DATA] = info + } + resp[webapi.RESPONSE_TOTAL] = Total + dataResp := &common.M2GWebApiResponse{} + dataResp.Body, _ = resp.Marshal() + tParent.TransRep.RetFiels = dataResp + tParent.Resume() + } + return transact.TransExeResult_Success +} + +func (this *QueryAllCoinPoolTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} +func (this *QueryAllCoinPoolTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryAllCoinPoolTransactHandler.OnChildTransRep ") + if ud != nil { + settings := make(map[string]*common.PlatformStates) + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), &settings) + if err == nil { + field := tNode.TransEnv.GetField(QueryAllCoinPoolTransactParam_Data) + if field == nil { + tNode.TransEnv.SetField(QueryAllCoinPoolTransactParam_Data, settings) + } else { + if arr, ok := field.(map[string]*common.PlatformStates); ok { + //arr是第一个game返回的值 + for k, newpf := range settings { + if oldpf, ok := arr[k]; ok { + for m, newg := range newpf.GamesVal { + if oldg, gok := oldpf.GamesVal[m]; gok { + if newg.States > oldg.States { + oldg.States = newg.States + oldg.CoinValue = newg.CoinValue + oldg.LowerLimit = newg.LowerLimit + oldg.UpperLimit = newg.UpperLimit + + } + } else { + //找不到游戏 + arr[k].GamesVal[m] = newg + } + } + } else { + //找不到pf + arr[k] = newpf + } + } + tNode.TransEnv.SetField(QueryAllCoinPoolTransactParam_Data, arr) + } + } + } + } + + return transact.TransExeResult_Success +} + +func StartQueryCoinPoolStatesTransact(tParent *transact.TransNode, pageNo, pageSize int32) { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_QueryAllCoinPool, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + + Platforms := PlatformMgrSingleton.GetPlatforms() + + //map排序 + var keys []int + for _, v := range Platforms { + //平台是否开启 + if !v.Disable { + id, err := strconv.Atoi(v.IdStr) + if err == nil { + keys = append(keys, id) + } + } + } + sort.Ints(keys) + + if pageNo <= 0 { + pageNo = 1 + } + end := pageNo * pageSize + start := end - pageSize + n := int32(len(keys)) + if start > n || end > n { + start = 0 + end = 100 + } + if end > n { + end = n + } + //当前页所有的平台id + NeedPlatforms := keys[start:end] + games := make(map[string][]*common.GamesIndex) + for _, platform := range NeedPlatforms { + //获取当前所有平台下的gameid + pf := strconv.Itoa(platform) + //加载配置 + gps := PlatformMgrSingleton.GetGameFrees(pf) + for _, v := range gps { + //获取所有开启的游戏 + if v.Status && v.DbGameFree.Id%10 != 4 { + g := &common.GamesIndex{} + g.GameFreeId = v.DbGameFree.Id + g.GroupId = v.GroupId + games[pf] = append(games[pf], g) + } + } + } + ud := &common.QueryGames{ + Index: games, + } + tNode := transact.DTCModule.StartTrans(tnp, ud, QueryAllCoinPoolTimeOut) + if tNode != nil { + tNode.TransEnv.SetField(QueryAllCoinPoolTransactParam_ParentNode, tParent) + tNode.TransEnv.SetField(QueryAllCoinPoolTransactParam_Total, n) + tNode.Go(core.CoreObject()) + } +} + +func init() { + transact.RegisteHandler(common.TransType_QueryAllCoinPool, &QueryAllCoinPoolTransactHandler{}) +} diff --git a/worldsrv/transact_querycoinpool.go b/worldsrv/transact_querycoinpool.go new file mode 100644 index 0000000..42d8199 --- /dev/null +++ b/worldsrv/transact_querycoinpool.go @@ -0,0 +1,126 @@ +package main + +import ( + webapi2 "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/netlib" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" + "mongo.games.com/goserver/srvlib" +) + +var QueryCoinPoolTimeOut = time.Second * 30 + +const ( + QueryCoinPoolTransactParam_ParentNode int = iota + QueryCoinPoolTransactParam_Data +) + +type QueryCoinPoolTransactHandler struct { +} + +func (this *QueryCoinPoolTransactHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryCoinPoolTransactHandler.OnExcute ") + if data, ok := ud.(*common.W2GQueryCoinPool); ok { + var ids []int32 + //var gameType int32 + if data.GroupId != 0 { + pgg := PlatformGameGroupMgrSington.GetGameGroup(data.GroupId) + if pgg != nil { + ids = append(ids, pgg.DbGameFree.Id) + //gameType = pgg.DbGameFree.GetGameType() + } + } else { + ids, _ = srvdata.DataMgr.GetGameFreeIds(data.GameId, data.GameMode) + } + for sid, gs := range GameSessMgrSington.servers { + if common.InSliceInt32(gs.gameIds, data.GameId) || len(gs.gameIds) == 0 { + //其他类型 gameIds通配 + for _, id := range ids { + gs.DetectCoinPoolSetting(data.Platform, id, data.GroupId) + } + tnp := &transact.TransNodeParam{ + Tt: common.TransType_QueryCoinPool, + Ot: transact.TransOwnerType(srvlib.GameServerType), + Oid: sid, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_SelfDecide, + } + tNode.StartChildTrans(tnp, ud, QueryCoinPoolTimeOut) + } + } + } + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryCoinPoolTransactHandler.OnCommit ") + field := tNode.TransEnv.GetField(QueryCoinPoolTransactParam_Data) + parent := tNode.TransEnv.GetField(QueryCoinPoolTransactParam_ParentNode) + if tParent, ok := parent.(*transact.TransNode); ok { + queryGamePool := &webapi2.SAQueryGamePoolByGameId{ + Tag: webapi2.TagCode_SUCCESS, + CoinPoolSetting: field.([]*webapi2.CoinPoolSetting), + } + tParent.TransRep.RetFiels = queryGamePool + tParent.Resume() + } + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + //logger.Logger.Trace("QueryCoinPoolTransactHandler.OnRollBack ") + return transact.TransExeResult_Success +} + +func (this *QueryCoinPoolTransactHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, + retCode int, ud interface{}) transact.TransExeResult { + //logger.Logger.Trace("QueryCoinPoolTransactHandler.OnChildTransRep ") + if ud != nil { + var userData []*webapi2.CoinPoolSetting + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), &userData) + if err == nil { + field := tNode.TransEnv.GetField(QueryCoinPoolTransactParam_Data) + if field == nil { + tNode.TransEnv.SetField(QueryCoinPoolTransactParam_Data, userData) + } else { + if arr, ok := field.([]*webapi2.CoinPoolSetting); ok { + arr = append(arr, userData...) + tNode.TransEnv.SetField(QueryCoinPoolTransactParam_Data, arr) + } + } + } else { + logger.Logger.Trace("trascate.OnChildRespWrapper err:", err) + } + } + + return transact.TransExeResult_Success +} + +func StartQueryCoinPoolTransact(tParent *transact.TransNode, gameid, gamemode int32, platform string, groupId int32) { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_QueryCoinPool, + Ot: transact.TransOwnerType(common.GetSelfSrvType()), + Oid: common.GetSelfSrvId(), + AreaID: common.GetSelfAreaId(), + } + ud := &common.W2GQueryCoinPool{ + GameId: gameid, + GameMode: gamemode, + Platform: platform, + GroupId: groupId, + } + tNode := transact.DTCModule.StartTrans(tnp, ud, QueryCoinPoolTimeOut) + if tNode != nil { + tNode.TransEnv.SetField(QueryCoinPoolTransactParam_ParentNode, tParent) + tNode.Go(core.CoreObject()) + } +} + +func init() { + transact.RegisteHandler(common.TransType_QueryCoinPool, &QueryCoinPoolTransactHandler{}) +} diff --git a/worldsrv/trascate_cache.go b/worldsrv/trascate_cache.go new file mode 100644 index 0000000..cf7a951 --- /dev/null +++ b/worldsrv/trascate_cache.go @@ -0,0 +1,62 @@ +package main + +// +//import ( +// "fmt" +// "mongo.games.com/game/common" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core/transact" +//) +// +//func init() { +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/Get", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// key, _ := params_data.GetStr("Key") //必填 +// resp_data := make(map[string]interface{}) +// resp_data["Key"] = key +// if CacheMemory.IsExist(key) { +// val := CacheMemory.Get(key) +// resp_data["Val"] = val +// resp_data["IsExist"] = true +// } else { +// resp_data["IsExist"] = false +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = resp_data +// return common.ResponseTag_Ok, resp +// })) +// +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/Put", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// key, _ := params_data.GetStr("Key") //必填 +// val, _ := params_data.GetStr("Val") +// timeout, _ := params_data.GetInt64("Timeout") +// CacheMemory.Put(key, val, timeout) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = "ok" +// return common.ResponseTag_Ok, resp +// })) +// +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/Del", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// key, _ := params_data.GetStr("Key") //必填 +// if CacheMemory.IsExist(key) { +// val := CacheMemory.Get(key) +// resp_data := make(map[string]interface{}) +// resp_data["Key"] = key +// resp_data["Val"] = val +// resp[webapi.RESPONSE_DATA] = resp_data +// CacheMemory.Delete(key) +// } else { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = fmt.Sprintf("key:%v not exist", key) +// } +// return common.ResponseTag_Ok, resp +// })) +//} diff --git a/worldsrv/trascate_stop.go b/worldsrv/trascate_stop.go new file mode 100644 index 0000000..17b5769 --- /dev/null +++ b/worldsrv/trascate_stop.go @@ -0,0 +1,52 @@ +package main + +import ( + "time" + + "mongo.games.com/game/common" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/transact" +) + +func init() { + transact.RegisteHandler(common.TransType_StopServer, &transact.TransHanderWrapper{ + OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnExecuteWrapper %x", tNode.MyTnp.TId) + for _, s := range GameSessMgrSington.gates { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_StopServer, + Ot: transact.TransOwnerType(s.srvType), + Oid: s.srvId, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + tNode.StartChildTrans(tnp, nil, time.Minute*5) + logger.Logger.Infof("StopApi start TransType_StopServer StartChildTrans srvid:%v srvtype:%v", s.srvId, s.srvType) + } + for _, s := range GameSessMgrSington.servers { + tnp := &transact.TransNodeParam{ + Tt: common.TransType_StopServer, + Ot: transact.TransOwnerType(s.srvType), + Oid: s.srvId, + AreaID: common.GetSelfAreaId(), + Tct: transact.TransactCommitPolicy_TwoPhase, + } + tNode.StartChildTrans(tnp, nil, time.Minute*5) + logger.Logger.Infof("StopApi start TransType_StopServer StartChildTrans srvid:%v srvtype:%v", s.srvId, s.srvType) + } + return transact.TransExeResult_Success + }), + OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnCommitWrapper ") + return transact.TransExeResult_Success + }), + OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Info("StopApi start TransType_StopServer OnRollBackWrapper") + return transact.TransExeResult_Success + }), + OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult { + logger.Logger.Infof("StopApi start TransType_StopServer OnChildRespWrapper ret:%v childid:%x", retCode, hChild) + return transact.TransExeResult(retCode) + }), + }) +} diff --git a/worldsrv/trascate_webapi.go b/worldsrv/trascate_webapi.go new file mode 100644 index 0000000..4811acc --- /dev/null +++ b/worldsrv/trascate_webapi.go @@ -0,0 +1,4584 @@ +package main + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "io" + "slices" + "sort" + + player_proto "mongo.games.com/game/protocol/player" + + "github.com/globalsign/mgo/bson" + "mongo.games.com/game/gamerule/crash" + "mongo.games.com/game/protocol/bag" + "mongo.games.com/game/protocol/gamehall" + "mongo.games.com/game/protocol/qpapi" + "mongo.games.com/game/protocol/server" + webapi "mongo.games.com/game/protocol/telegramapi" + "mongo.games.com/game/srvdata" + win88webapi "mongo.games.com/game/webapi" + + //"encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "sync" + "time" + + login_proto "mongo.games.com/game/protocol/login" + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/proto" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/netlib" + "mongo.games.com/goserver/core/transact" +) + +const ( + WebAPITransactParam_Path = iota + WebAPITransactParam_CreateTime +) + +func init() { + transact.RegisteHandler(common.TransType_WebApi, &WebAPITranscateHandler{}) +} + +var WebAPIHandlerMgrSingleton = &WebAPIHandlerMgr{wshMap: make(map[string]WebAPIHandler)} + +type WebAPITranscateHandler struct { +} + +var WebAPIStats = make(map[string]*model.APITransactStats) + +// 返利配置 +type RebateInfos struct { + Platform string //平台名称 + RebateSwitch bool //返利开关 + RebateManState int //返利是开启个人返利 0 关闭 1 开启 + ReceiveMode int //领取方式 0实时领取 1次日领取 + NotGiveOverdue int //0不过期 1过期不给 2过期邮件给 + RebateGameCfg []*RebateGameCfg //key为"gameid"+"gamemode" + RebateGameThirdCfg []*RebateGameThirdCfg //第三方key + Version int //活动版本 后台控制 +} + +func (this *WebAPITranscateHandler) OnExcute(tNode *transact.TransNode, ud interface{}) transact.TransExeResult { + logger.Logger.Trace("WebAPITranscateHandler.OnExcute ") + req := &common.M2GWebApiRequest{} + err := netlib.UnmarshalPacketNoPackId(ud.([]byte), req) + if err == nil { + wsh := WebAPIHandlerMgrSingleton.GetWebAPIHandler(req.Path) + if wsh == nil { + logger.Logger.Trace("WebAPITranscateHandler no registe WebAPIHandler ", req.Path) + return transact.TransExeResult_Failed + } + + tNode.TransEnv.SetField(WebAPITransactParam_Path, req.Path) + tNode.TransEnv.SetField(WebAPITransactParam_CreateTime, time.Now()) + + tag, msg := wsh.Handler(tNode, req.Body) + tNode.TransRep.RetFiels = msg + switch tag { + case common.ResponseTag_Ok: + return transact.TransExeResult_Success + case common.ResponseTag_TransactYield: + return transact.TransExeResult_Yield + } + } + logger.Logger.Trace("WebAPITranscateHandler.OnExcute err:", err) + return transact.TransExeResult_Failed +} + +func (this *WebAPITranscateHandler) OnCommit(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("WebAPITranscateHandler.OnCommit ") + paramPath := tNode.TransEnv.GetField(WebAPITransactParam_Path) + paramCreateTime := tNode.TransEnv.GetField(WebAPITransactParam_CreateTime) + if path, ok := paramPath.(string); ok { + var stats *model.APITransactStats + var exist bool + if stats, exist = WebAPIStats[path]; !exist { + stats = &model.APITransactStats{} + WebAPIStats[path] = stats + } + + if stats != nil { + stats.RunTimes++ + runingTime := int64(time.Now().Sub(paramCreateTime.(time.Time)) / time.Millisecond) + if runingTime > stats.MaxRuningTime { + stats.MaxRuningTime = runingTime + } + stats.TotalRuningTime += runingTime + stats.SuccessTimes++ + } + } + return transact.TransExeResult_Success +} + +func (this *WebAPITranscateHandler) OnRollBack(tNode *transact.TransNode) transact.TransExeResult { + logger.Logger.Trace("WebAPITranscateHandler.OnRollBack ") + paramPath := tNode.TransEnv.GetField(WebAPITransactParam_Path) + paramCreateTime := tNode.TransEnv.GetField(WebAPITransactParam_CreateTime) + if path, ok := paramPath.(string); ok { + var stats *model.APITransactStats + var exist bool + if stats, exist = WebAPIStats[path]; !exist { + stats = &model.APITransactStats{} + WebAPIStats[path] = stats + } + + if stats != nil { + stats.RunTimes++ + runingTime := int64(time.Now().Sub(paramCreateTime.(time.Time)) / time.Millisecond) + if runingTime > stats.MaxRuningTime { + stats.MaxRuningTime = runingTime + } + stats.TotalRuningTime += runingTime + stats.FailedTimes++ + } + } + return transact.TransExeResult_Success +} + +func (this *WebAPITranscateHandler) OnChildTransRep(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, + ud interface{}) transact.TransExeResult { + logger.Logger.Trace("WebAPITranscateHandler.OnChildTransRep ") + return transact.TransExeResult_Success +} + +type WebAPIHandler interface { + Handler(*transact.TransNode, []byte) (int, proto.Message) +} + +type WebAPIHandlerWrapper func(*transact.TransNode, []byte) (int, proto.Message) + +func (wshw WebAPIHandlerWrapper) Handler(tNode *transact.TransNode, params []byte) (int, proto.Message) { + return wshw(tNode, params) +} + +type WebAPIHandlerMgr struct { + wshMap map[string]WebAPIHandler + DataWaitList sync.Map +} + +func (this *WebAPIHandlerMgr) RegisteWebAPIHandler(name string, wsh WebAPIHandler) { + this.wshMap[name] = wsh +} + +func (this *WebAPIHandlerMgr) GetWebAPIHandler(name string) WebAPIHandler { + if wsh, exist := this.wshMap[name]; exist { + return wsh + } + return nil +} + +func init() { + //API用户登录 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Member/APIMemberRegisterOrLogin", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // logger.Logger.Trace("WebAPIHandler:/api/Member/APIMemberRegisterOrLogin", params) + // pack := &webapi.SALogin{} + // msg := &webapi.ASLogin{} + // err1 := proto.Unmarshal(params, msg) + // if err1 != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err1.Error() + // return common.ResponseTag_ParamError, pack + // } + // + // platformID, channel, _, _, tagkey := PlatformMgrSingleton.GetPlatformByPackageTag(msg.GetPlatformTag()) + // platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(platformID))) + // if platform == nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "没有对应的包标识" + // return common.ResponseTag_ParamError, pack + // } + // merchantkey := platform.MerchantKey + // + // sign := msg.GetSign() + // + // raw := fmt.Sprintf("%v%v%v%v%v", msg.GetTelegramId(), msg.GetPlatformTag(), msg.GetUsername(), merchantkey, msg.GetTs()) + // h := md5.New() + // io.WriteString(h, raw) + // newsign := hex.EncodeToString(h.Sum(nil)) + // + // if newsign != sign { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "商户验签失败" + // return common.ResponseTag_ParamError, pack + // } + // + // var acc *model.Account + // var errcode int + // + // rawpwd := fmt.Sprintf("%v%v%v", msg.GetSign(), common.GetAppId(), time.Now().Unix()) + // hpwd := md5.New() + // io.WriteString(hpwd, rawpwd) + // pwd := hex.EncodeToString(h.Sum(nil)) + // + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // acc, errcode = model.AccountIsExist(msg.GetTelegramId(), msg.GetTelegramId(), "", platform.IdStr, time.Now().Unix(), 5, tagkey, false) + // if errcode == 2 { + // var err int + // acc, err = model.InsertAccount(msg.GetTelegramId(), pwd, platform.IdStr, strconv.Itoa(int(channel)), "", "", + // "telegramapi", 0, 0, msg.GetPlatformTag(), "", "", "", tagkey) + // if acc != nil { //需要预先创建玩家数据 + // _, _ = model.GetPlayerData(acc.PackegeTag,acc.AccountId.Hex()) + // } + // if err == 5 { + // return 1 + // } else { + // //t.flag = login_proto.OpResultCode_OPRC_Login_CreateAccError + // } + // } + // return errcode + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // switch data.(int) { + // case 1: + // { + // if err := model.UpdatePlayerTokenPassword(acc.Platform, acc.AccountId.Hex(), pwd); err != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = err.Error() + // } else { + // tokenuserdata := &common.TokenUserData{ + // TelegramId: msg.GetTelegramId(), + // Password: pwd, + // Packagetag: msg.GetPlatformTag(), + // Expired: time.Now().Add(time.Minute * 15).Unix(), + // } + // + // token, err := common.CreateTokenAes(tokenuserdata) + // if err != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = err.Error() + // } else { + // pack.Tag = webapi.TagCode_SUCCESS + // pack.Msg = "" + // pack.Snid = proto.Int32(acc.SnId) + // pack.Token = fmt.Sprintf("%v?token=%v&snid=%v", model.GameParamData.CSURL, token, acc.SnId) + // } + // } + // } + // case 2: + // { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "创建帐号失败" + // } + // case 3: + // { + // //t.flag = login_proto.OpResultCode_OPRC_LoginPassError + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "账号密码错误" + // } + // case 4: + // { + // //t.flag = login_proto.OpResultCode_OPRC_AccountBeFreeze + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "帐号冻结" + // } + // } + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // }), "APIMemberRegisterOrLogin").Start() + // return common.ResponseTag_TransactYield, pack + // })) + + //API用户加减币 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/APIAddSubCoinById", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi.SAAddCoinById{} + // msg := &webapi.ASAddCoinById{} + // err1 := proto.Unmarshal(params, msg) + // if err1 != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err1.Error() + // return common.ResponseTag_ParamError, pack + // } + // + // member_snid := msg.GetID() + // coin := msg.GetGold() + // coinEx := msg.GetGoldEx() + // oper := msg.GetOper() + // gold_desc := msg.GetDesc() + // billNo := int(msg.GetBillNo()) + // platform := msg.GetPlatform() + // //logType := msg.GetLogType() + // isAccTodayRecharge := msg.GetIsAccTodayRecharge() + // needFlowRate := msg.GetNeedFlowRate() + // needGiveFlowRate := msg.GetNeedGiveFlowRate() + // + // if CacheDataMgr.CacheBillCheck(billNo, platform) { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "Bill number repeated!" + // return common.ResponseTag_ParamError, pack + // } + // CacheDataMgr.CacheBillNumber(billNo, platform) //防止手抖点两下 + // + // var err error + // var pd *model.PlayerData + // oldGold := int64(0) + // oldSafeBoxGold := int64(0) + // var timeStamp = time.Now().UnixNano() + // player := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) + // if player != nil { //在线玩家处理 + // if player.scene != nil { + // CacheDataMgr.ClearCacheBill(billNo, platform) + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "Unsupported!!! because player in scene!" + // return common.ResponseTag_ParamError, pack + // } + // pd = player.PlayerData + // if len(platform) > 0 && player.Platform != platform { + // CacheDataMgr.ClearCacheBill(billNo, platform) + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "player platform forbit!" + // return common.ResponseTag_ParamError, pack + // } + // + // opcode := int32(common.GainWay_Api_In) + // + // if coin < 0 { + // opcode = int32(common.GainWay_Api_Out) + // if player.Coin+coin < 0 { + // CacheDataMgr.ClearCacheBill(billNo, platform) + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "coin not enough!" + // return common.ResponseTag_ParamError, pack + // } + // } + // + // //if logType != 0 { + // // opcode = logType + // //} + // + // oldGold = player.Coin + // oldSafeBoxGold = player.SafeBoxCoin + // coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + // gold_desc, model.PayCoinLogType_Coin, coinEx) + // timeStamp = coinLog.TimeStamp + // //增加帐变记录 + // coinlogex := model.NewCoinLogEx(int32(member_snid), coin+coinEx, oldGold+coin+coinEx, + // oldSafeBoxGold, 0, opcode, 0, oper, gold_desc, pd.Platform, pd.Channel, + // pd.BeUnderAgentCode, 0, pd.PackageID, 0) + // + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // err = model.InsertPayCoinLogs(platform, coinLog) + // if err != nil { + // logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + // return err + // } + // err = model.InsertCoinLog(coinlogex) + // if err != nil { + // //回滚到对账日志 + // model.RemovePayCoinLog(platform, coinLog.LogId) + // logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + // return err + // } + // return err + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // CacheDataMgr.ClearCacheBill(billNo, platform) + // if data != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = data.(error).Error() + // } else { + // //player.Coin += coin + coinEx + // player.AddCoinAsync(coin+coinEx, common.GainWay_Api_In, oper, gold_desc, true, 0, false) + // //增加相应的泥码量 + // player.AddDirtyCoin(coin, coinEx) + // player.SetPayTs(timeStamp) + // + // if player.TodayGameData == nil { + // player.TodayGameData = model.NewPlayerGameCtrlData() + // } + // //actRandCoinMgr.OnPlayerRecharge(player, coin) + // if isAccTodayRecharge { + // + // player.AddCoinPayTotal(coin) + // player.TodayGameData.RechargeCoin += coin //累加当天充值金额 + // if coin >= 0 && coinEx >= 0 { + // plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + // curVer := int32(0) + // if plt != nil { + // curVer = plt.ExchangeVer + // } + // log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, coinEx, 0, opcode, pd.PromoterTree, + // model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + // "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + // if log != nil { + // err := model.InsertGiveCoinLog(log) + // if err == nil { + // if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + // err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + // } + // } + // //清空流水,更新id + // pd.TotalConvertibleFlow = 0 + // pd.LastExchangeOrder = log.LogId.Hex() + // if player == nil { + // //需要回写数据库 + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // model.UpdatePlayerExchageFlowAndOrder(platform, member_snid, 0, pd.LastExchangeOrder) + // return nil + // }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + // } + // } + // } + // } + // + // player.dirty = true + // player.Time2Save() + // if player.scene == nil { //如果在大厅,那么同步下金币 + // player.SendDiffData() + // } + // player.SendPlayerRechargeAnswer(coin) + // pack.Tag = webapi.TagCode_SUCCESS + // pack.Msg = "" + // } + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // if err != nil { + // logger.Logger.Error("AddSubCoinById task marshal data error:", err) + // } + // }), "APIAddCoinById").Start() + // return common.ResponseTag_TransactYield, pack + // } else { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // pd, _ = model.GetPlayerDataBySnId(platform, int32(member_snid), false, true) + // if pd == nil { + // return errors.New("Player not find.") + // } + // if len(platform) > 0 && pd.Platform != platform { + // return errors.New("player platform forbit.") + // } + // oldGold = pd.Coin + // oldSafeBoxGold = pd.SafeBoxCoin + // + // opcode := int32(common.GainWay_Api_In) + // if coin < 0 { + // opcode = int32(common.GainWay_Api_Out) + // if pd.Coin+coin < 0 { + // return errors.New("coin not enough!") + // } + // } + // + // //if logType != 0 { + // // opcode = logType + // //} + // coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + // gold_desc, model.PayCoinLogType_Coin, coinEx) + // timeStamp = coinLog.TimeStamp + // err = model.InsertPayCoinLogs(platform, coinLog) + // if err != nil { + // logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + // return err + // } + // //增加帐变记录 + // coinlogex := model.NewCoinLogEx(int32(member_snid), coin+coinEx, oldGold+coin+coinEx, + // oldSafeBoxGold, 0, opcode, 0, oper, gold_desc, pd.Platform, + // pd.Channel, pd.BeUnderAgentCode, 0, pd.PackageID, 0) + // err = model.InsertCoinLog(coinlogex) + // if err != nil { + // //回滚到对账日志 + // model.RemovePayCoinLog(platform, coinLog.LogId) + // logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + // return err + // } + // return err + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // CacheDataMgr.ClearCacheBill(billNo, platform) + // if data != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = data.(error).Error() + // } else { + // pack.Tag = webapi.TagCode_SUCCESS + // pack.Msg = "" + // if isAccTodayRecharge && coin >= 0 { + // OnPlayerPay(pd, coin) + // } + // if isAccTodayRecharge && coin >= 0 && coinEx >= 0 { + // + // plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + // curVer := int32(0) + // if plt != nil { + // curVer = plt.ExchangeVer + // } + // log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, coinEx, 0, common.GainWay_Api_In, pd.PromoterTree, + // model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + // "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + // if log != nil { + // err := model.InsertGiveCoinLog(log) + // if err == nil { + // if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + // err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + // } + // } + // //清空流水,更新id + // pd.TotalConvertibleFlow = 0 + // pd.LastExchangeOrder = log.LogId.Hex() + // if player == nil { + // //需要回写数据库 + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // model.UpdatePlayerExchageFlowAndOrder(platform, int32(member_snid), 0, pd.LastExchangeOrder) + // return nil + // }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + // } + // } + // + // } + // } + // + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // if err != nil { + // logger.Logger.Error("AddSubCoinById task marshal data error:", err) + // } + // }), "APIAddSubCoinById").Start() + // return common.ResponseTag_TransactYield, pack + // } + // })) + + //获取用户金币数量 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Member/GetMemberGoldById", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi.SAMemberGold{} + // msg := &webapi.ASMemberGold{} + // + // err1 := proto.Unmarshal(params, msg) + // if err1 != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err1.Error() + // return common.ResponseTag_ParamError, pack + // } + // + // platform := PlatformMgrSingleton.GetPlatform(msg.GetPlatform()) + // if platform == nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "没有对应的包标识" + // return common.ResponseTag_ParamError, pack + // } + // platform_param := msg.GetPlatform() + // member_snid := msg.GetSnid() + // var err error + // gold := int64(0) + // bank := int64(0) + // p := PlayerMgrSington.GetPlayerBySnId(member_snid) + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // if p != nil { + // if len(platform_param) > 0 && p.Platform != platform_param { + // return errors.New("Platform error.") + // } + // bank = p.SafeBoxCoin + // gold = p.GetCoin() + // return nil + // } else { + // pbi, _ := model.GetPlayerDataBySnId(platform_param, member_snid, true, true) + // if pbi == nil { + // return errors.New("snid error") + // } + // if len(platform_param) > 0 && pbi.Platform != platform_param { + // return errors.New("Platform error.") + // } + // bank = pbi.SafeBoxCoin + // gold = pbi.Coin + // return nil + // } + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // if data != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = data.(error).Error() + // } else { + // + // pd := &webapi.PlayerCoinData{ + // Id: member_snid, + // Gold: gold, + // Bank: bank, + // } + // pack.Tag = webapi.TagCode_SUCCESS + // //pack.Msg = data.(error).Error() + // pack.Data = pd + // } + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // if err != nil { + // logger.Logger.Error("AddSubCoinById task marshal data error:", err) + // } + // }), "GetMemberGoldById").Start() + // + // return common.ResponseTag_TransactYield, pack + // })) + + //获取用户注单记录游戏记录 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Member/GetGameHistory", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi.SAPlayerHistory{} + // msg := &webapi.ASPlayerHistory{} + // + // err1 := proto.Unmarshal(params, msg) + // if err1 != nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err1.Error() + // return common.ResponseTag_ParamError, pack + // } + // + // platform := PlatformMgrSingleton.GetPlatform(msg.GetPlatform()) + // if platform == nil { + // pack.Tag = webapi.TagCode_FAILED + // pack.Msg = "没有对应的包标识" + // return common.ResponseTag_ParamError, pack + // } + // platform_param := msg.GetPlatform() + // member_snid := msg.GetSnid() + // + // gameid := int(msg.GetGameId()) + // historyModel := msg.GetGameHistoryModel() + // p := PlayerMgrSington.GetPlayerBySnId(member_snid) + // if p == nil{ + // pi,_:=model.GetPlayerDataBySnId(platform_param, member_snid, true, true) + // p = &Player{PlayerData:pi} + // } + // + // if p != nil { + // switch historyModel { + // case PLAYER_HISTORY_MODEL: // 历史记录 + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // var genPlayerHistoryInfo = func(spinID string, isFree bool, createdTime, totalBetValue, totalPriceValue, totalBonusValue, multiple int64, player *gamehall.PlayerHistoryInfo) { + // player.SpinID = proto.String(spinID) + // player.CreatedTime = proto.Int64(createdTime) + // player.TotalBetValue = proto.Int64(totalBetValue) + // player.TotalPriceValue = proto.Int64(totalPriceValue) + // player.IsFree = proto.Bool(isFree) + // player.TotalBonusValue = proto.Int64(totalBonusValue) + // player.Multiple = proto.Int64(multiple) + // } + // + // var genPlayerHistoryInfoMsg = func(spinid string, v *model.NeedGameRecord, gdl *model.GameDetailedLog, player *gamehall.PlayerHistoryInfo) { + // switch gameid { + // case common.GameId_Crash: + // data, err := model.UnMarshalGameNoteByHUNDRED(gdl.GameDetailedNote) + // if err != nil { + // logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) + // } + // jsonString, _ := json.Marshal(data) + // + // // convert json to struct + // gnd := model.CrashType{} + // json.Unmarshal(jsonString, &gnd) + // + // //gnd := data.(*model.CrashType) + // for _, curplayer := range gnd.PlayerData { + // if curplayer.UserId == p.SnId { + // genPlayerHistoryInfo(spinid, false, int64(v.Ts), int64(curplayer.UserBetTotal), curplayer.ChangeCoin, 0, int64(curplayer.UserMultiple), player) + // break + // } + // } + // case common.GameId_Avengers: + // data, err := model.UnMarshalAvengersGameNote(gdl.GameDetailedNote) + // if err != nil { + // logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) + // } + // gnd := data.(*model.GameResultLog) + // genPlayerHistoryInfo(spinid, gnd.BaseResult.IsFree, int64(v.Ts), int64(gnd.BaseResult.TotalBet), gnd.BaseResult.WinTotal, gnd.BaseResult.WinSmallGame, 0, player) + // //case common.GameId_EasterIsland: + // // data, err := model.UnMarshalEasterIslandGameNote(gdl.GameDetailedNote) + // // if err != nil { + // // logger.Logger.Errorf("World UnMarshalEasterIslandGameNote error:%v", err) + // // } + // // gnd := data.(*model.EasterIslandType) + // // genPlayerHistoryInfo(spinid, gnd.IsFree, int64(v.Ts), int64(gnd.Score), gnd.TotalPriceValue, gnd.TotalBonusValue, player) + // default: + // logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive gameid(%v) error", gameid) + // } + // } + // + // gameclass := int32(2) + // spinid := strconv.FormatInt(int64(p.SnId), 10) + // dbGameFrees := srvdata.PBDB_GameFreeMgr.Datas.Arr //.GetData(data.DbGameFree.Id) + // roomtype := int32(0) + // for _, v := range dbGameFrees { + // if int32(gameid) == v.GetGameId() { + // gameclass = v.GetGameClass() + // roomtype = v.GetSceneType() + // break + // } + // } + // + // gpl := model.GetPlayerListByHallEx(p.SnId, p.Platform, 0, 50, 0, 0, roomtype, gameclass, gameid) + // pack := &gamehall.SCPlayerHistory{} + // for _, v := range gpl.Data { + // if v.GameDetailedLogId == "" { + // logger.Logger.Error("World PlayerHistory GameDetailedLogId is nil") + // break + // } + // gdl := model.GetPlayerHistory(p.Platform, v.GameDetailedLogId) + // player := &gamehall.PlayerHistoryInfo{} + // genPlayerHistoryInfoMsg(spinid, v, gdl, player) + // pack.PlayerHistory = append(pack.PlayerHistory, player) + // } + // proto.SetDefaults(pack) + // logger.Logger.Infof("World gameid:%v PlayerHistory:%v ", gameid, pack) + // return pack + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // if data == nil { + // tNode.TransRep.RetFiels = data + // tNode.Resume() + // } + // }), "CSGetPlayerHistoryHandlerWorld").Start() + // case BIGWIN_HISTORY_MODEL: // 爆奖记录 + // jackpotList := JackpotListMgrSington.GetJackpotList(gameid) + // //if len(jackpotList) < 1 { + // // JackpotListMgrSington.GenJackpot(gameid) // 初始化爆奖记录 + // // JackpotListMgrSington.after(gameid) // 开启定时器 + // // jackpotList = JackpotListMgrSington.GetJackpotList(gameid) + // //} + // pack := JackpotListMgrSington.GetStoCMsg(jackpotList) + // pack.GameId = msg.GetGameId() + // logger.Logger.Infof("World BigWinHistory: %v %v", gameid, pack) + // p.SendToClient(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEBIGWINHISTORY), pack) + // case GAME_HISTORY_MODEL: + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // var genGameHistoryInfo = func(gameNumber string, createdTime, multiple int64, hash string, gamehistory *gamehall.GameHistoryInfo) { + // gamehistory.GameNumber = proto.String(gameNumber) + // gamehistory.CreatedTime = proto.Int64(createdTime) + // gamehistory.Hash = proto.String(hash) + // gamehistory.Multiple = proto.Int64(multiple) + // } + // + // gls := model.GetAllGameDetailedLogsByGameIdAndTs(p.Platform, gameid, 20) + // + // pack := &gamehall.SCPlayerHistory{} + // for _, v := range gls { + // + // gamehistory := &gamehall.GameHistoryInfo{} + // + // data, err := model.UnMarshalGameNoteByHUNDRED(v.GameDetailedNote) + // if err != nil { + // logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) + // } + // jsonString, _ := json.Marshal(data) + // + // // convert json to struct + // gnd := model.CrashType{} + // json.Unmarshal(jsonString, &gnd) + // + // genGameHistoryInfo(v.LogId, int64(v.Ts), int64(gnd.Rate), gnd.Hash, gamehistory) + // pack.GameHistory = append(pack.GameHistory, gamehistory) + // } + // proto.SetDefaults(pack) + // logger.Logger.Infof("World gameid:%v History:%v ", gameid, pack) + // return pack + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // if data == nil { + // tNode.TransRep.RetFiels = data + // tNode.Resume() + // } + // }), "CSGetGameHistoryHandlerWorld").Start() + // default: + // logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive historyModel(%v) error", historyModel) + // } + // } + // + // return common.ResponseTag_TransactYield, pack + // })) + + //API用户加减币 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/QPAPIAddSubCoinById", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &qpapi.SAAddCoinById{} + msg := &qpapi.ASAddCoinById{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + + username := msg.GetUsername() + coin := msg.GetGold() + billNo := int(msg.GetBillNo()) + platform := msg.GetMerchantTag() + curplatform := PlatformMgrSingleton.GetPlatform(platform) + + if curplatform == nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "没有对应的平台" + return common.ResponseTag_ParamError, pack + } + merchantkey := curplatform.MerchantKey + + sign := msg.GetSign() + + raw := fmt.Sprintf("%v%v%v%v%v%v", username, coin, billNo, platform, merchantkey, msg.GetTs()) + h := md5.New() + io.WriteString(h, raw) + newsign := hex.EncodeToString(h.Sum(nil)) + + if newsign != sign { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "商户验签失败" + return common.ResponseTag_ParamError, pack + } + + if CacheDataMgr.CacheBillCheck(billNo, platform) { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "Bill number repeated!" + return common.ResponseTag_ParamError, pack + } + CacheDataMgr.CacheBillNumber(billNo, platform) //防止手抖点两下 + + var err error + var pd *model.PlayerData + oldGold := int64(0) + var timeStamp = time.Now().UnixNano() + acc, accerr := model.GetAccountByName(platform, username) + if accerr != nil { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = accerr.Error() + return common.ResponseTag_ParamError, pack + } + member_snid := acc.SnId + player := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) + if player != nil { //在线玩家处理 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if player.scene != nil && coin < 0 { + //player.Kickout(common.KickReason_CheckCodeErr) + + leavemsg := &server.WGPlayerLeave{ + SnId: proto.Int32(player.SnId), + } + proto.SetDefaults(leavemsg) + player.SendToGame(int(server.SSPacketID_PACKET_WG_PlayerLEAVE), leavemsg) + + select { + case <-player.leavechan: + case <-time.After(time.Second * 1): + } + + } + //player = PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) + if player.scene != nil { + CacheDataMgr.ClearCacheBill(billNo, platform) + //pack.Tag = qpapi.TagCode_FAILED + //pack.Msg = "Unsupported!!! because player in scene!" + //return common.ResponseTag_ParamError, pack + return errors.New("Unsupported!!! because player in scene!") + } + pd = player.PlayerData + if len(platform) > 0 && player.Platform != platform { + CacheDataMgr.ClearCacheBill(billNo, platform) + //pack.Tag = qpapi.TagCode_FAILED + //pack.Msg = "player platform forbit!" + //return common.ResponseTag_ParamError, pack + return errors.New("player platform forbit!") + } + + opcode := int32(common.GainWay_Api_In) + + if coin < 0 { + opcode = int32(common.GainWay_Api_Out) + if player.Coin+coin < 0 { + CacheDataMgr.ClearCacheBill(billNo, platform) + //pack.Tag = qpapi.TagCode_FAILED + //pack.Msg = "coin not enough!" + //return common.ResponseTag_ParamError, pack + return errors.New("coin not enough!") + } + } + + //if logType != 0 { + // opcode = logType + //} + + oldGold = player.Coin + coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + "qpsystem", model.PayCoinLogType_Coin, 0) + timeStamp = coinLog.TimeStamp + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: pd.Platform, + SnID: member_snid, + ChangeType: common.BillTypeCoin, + ChangeNum: coin, + RemainNum: oldGold + coin, + Add: 0, + LogType: opcode, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "oper", + Remark: "online", + }) + + err = model.InsertPayCoinLogs(platform, coinLog) + if err != nil { + logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + return err + } + err = model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemovePayCoinLog(platform, coinLog.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + CacheDataMgr.ClearCacheBill(billNo, platform) + if data != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + //player.Coin += coin + coinEx + player.AddCoinAsync(coin, 0, common.GainWay_Api_In, "oper", "Async", true, 0, false) + //增加相应的泥码量 + player.AddDirtyCoin(coin, 0) + player.SetPayTs(timeStamp) + + if player.TodayGameData == nil { + player.TodayGameData = model.NewPlayerGameCtrlData() + } + //actRandCoinMgr.OnPlayerRecharge(player, coin) + /* + if isAccTodayRecharge { + + player.AddCoinPayTotal(coin) + player.TodayGameData.RechargeCoin += coin //累加当天充值金额 + if coin >= 0 { + plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, 0, 0, opcode, pd.PromoterTree, + model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + if log != nil { + err := model.InsertGiveCoinLog(log) + if err == nil { + if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + } + } + //清空流水,更新id + pd.TotalConvertibleFlow = 0 + pd.LastExchangeOrder = log.LogId.Hex() + if player == nil { + //需要回写数据库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdatePlayerExchageFlowAndOrder(platform, member_snid, 0, pd.LastExchangeOrder) + return nil + }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + } + } + } + } + */ + player.dirty = true + player.Time2Save() + if player.scene == nil { //如果在大厅,那么同步下金币 + player.SendDiffData() + } + player.SendPlayerRechargeAnswer(coin) + pack.Tag = qpapi.TagCode_SUCCESS + pack.Msg = "" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("AddSubCoinById task marshal data error:", err) + } + }), "APIAddSubCoinById").Start() + return common.ResponseTag_TransactYield, pack + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pd, _ = model.GetPlayerDataBySnId(platform, int32(member_snid), false, true) + if pd == nil { + return errors.New("Player not find.") + } + if len(platform) > 0 && pd.Platform != platform { + return errors.New("player platform forbit.") + } + oldGold = pd.Coin + opcode := int32(common.GainWay_Api_In) + if coin < 0 { + opcode = int32(common.GainWay_Api_Out) + if pd.Coin+coin < 0 { + return errors.New("coin not enough!") + } + } + + //if logType != 0 { + // opcode = logType + //} + coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + "not online", model.PayCoinLogType_Coin, 0) + timeStamp = coinLog.TimeStamp + err = model.InsertPayCoinLogs(platform, coinLog) + if err != nil { + logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + return err + } + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: pd.Platform, + SnID: member_snid, + ChangeType: common.BillTypeCoin, + ChangeNum: coin, + RemainNum: oldGold + coin, + Add: 0, + LogType: opcode, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: "oper", + Remark: "not online", + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemovePayCoinLog(platform, coinLog.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + CacheDataMgr.ClearCacheBill(billNo, platform) + if data != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + pack.Tag = qpapi.TagCode_SUCCESS + pack.Msg = "" + /* + if isAccTodayRecharge && coin >= 0 { + OnPlayerPay(pd, coin) + } + if isAccTodayRecharge && coin >= 0 && coinEx >= 0 { + + plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, coinEx, 0, common.GainWay_Api_In, pd.PromoterTree, + model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + if log != nil { + err := model.InsertGiveCoinLog(log) + if err == nil { + if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + } + } + //清空流水,更新id + pd.TotalConvertibleFlow = 0 + pd.LastExchangeOrder = log.LogId.Hex() + if player == nil { + //需要回写数据库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdatePlayerExchageFlowAndOrder(platform, int32(member_snid), 0, pd.LastExchangeOrder) + return nil + }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + } + } + + } + */ + } + + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("AddSubCoinById task marshal data error:", err) + } + }), "APIAddSubCoinById").Start() + return common.ResponseTag_TransactYield, pack + } + })) + + //获取用户金币数量 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Member/QPGetMemberGoldById", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &qpapi.SAMemberGold{} + msg := &qpapi.ASMemberGold{} + + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + username := msg.GetUsername() + platform := msg.GetMerchantTag() + curplatform := PlatformMgrSingleton.GetPlatform(platform) + //platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(platformID))) + if curplatform == nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "没有对应的平台" + return common.ResponseTag_ParamError, pack + } + merchantkey := curplatform.MerchantKey + + sign := msg.GetSign() + + raw := fmt.Sprintf("%v%v%v%v", username, platform, merchantkey, msg.GetTs()) + h := md5.New() + io.WriteString(h, raw) + newsign := hex.EncodeToString(h.Sum(nil)) + + if newsign != sign { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "商户验签失败" + return common.ResponseTag_ParamError, pack + } + + //platform := PlatformMgrSingleton.GetPlatform(msg.GetPlatform()) + //if platform == nil { + // pack.Tag = qpapi.TagCode_FAILED + // pack.Msg = "没有对应的包标识" + // return common.ResponseTag_ParamError, pack + //} + //platform_param := msg.GetPlatform() + //member_snid := msg.GetSnid() + + var err error + gold := int64(0) + bank := int64(0) + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + acc, accerr := model.GetAccountByName(platform, msg.GetUsername()) + if accerr != nil { + //pack.Tag = qpapi.TagCode_FAILED + //pack.Msg = accerr.Error() + //return common.ResponseTag_ParamError, pack + return accerr + } + member_snid := acc.SnId + p := PlayerMgrSington.GetPlayerBySnId(member_snid) + if p != nil { + if len(platform) > 0 && p.Platform != platform { + return errors.New("Platform error.") + } + bank = p.SafeBoxCoin + gold = p.GetCoin() + return nil + } else { + pbi, _ := model.GetPlayerDataBySnId(platform, member_snid, true, true) + if pbi == nil { + return errors.New("snid error") + } + if len(platform) > 0 && pbi.Platform != platform { + return errors.New("Platform error.") + } + bank = pbi.SafeBoxCoin + gold = pbi.Coin + return nil + } + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + + pd := &qpapi.PlayerCoinData{ + Username: msg.GetUsername(), + Gold: gold, + Bank: bank, + } + pack.Tag = qpapi.TagCode_SUCCESS + //pack.Msg = data.(error).Error() + pack.Data = pd + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("AddSubCoinById task marshal data error:", err) + } + }), "GetMemberGoldById").Start() + + return common.ResponseTag_TransactYield, pack + })) + + //获取用户注单记录游戏记录 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Member/QPGetGameHistory", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &qpapi.SAPlayerHistory{} + msg := &qpapi.ASPlayerHistory{} + + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + + historyModel := msg.GetGameHistoryModel() + username := msg.GetUsername() + platform := msg.GetMerchantTag() + curplatform := PlatformMgrSingleton.GetPlatform(platform) + starttime := msg.GetStartTime() + endtime := msg.GetEndTime() + pageno := msg.GetPageNo() + pagesize := msg.GetPageSize() + if pagesize == 0 { + pagesize = 50 + } + //platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(int(platformID))) + if curplatform == nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "没有对应的平台" + return common.ResponseTag_ParamError, pack + } + merchantkey := curplatform.MerchantKey + + sign := msg.GetSign() + + raw := fmt.Sprintf("%v%v%v%v%v%v%v%v%v", username, platform, historyModel, starttime, endtime, pageno, pagesize, merchantkey, msg.GetTs()) + h := md5.New() + io.WriteString(h, raw) + newsign := hex.EncodeToString(h.Sum(nil)) + + if newsign != sign { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "商户验签失败" + return common.ResponseTag_ParamError, pack + } + + searchsnid := int32(0) + if username != "" { + acc, accerr := model.GetAccountByName(platform, username) + if accerr != nil { + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = accerr.Error() + return common.ResponseTag_ParamError, pack + } + member_snid := acc.SnId + + p := PlayerMgrSington.GetPlayerBySnId(member_snid) + if p == nil { + pi, _ := model.GetPlayerDataBySnId(platform, member_snid, true, true) + p = &Player{PlayerData: pi} + } + searchsnid = p.SnId + } + + //if p != nil { + + //gameid := 112 + switch int(historyModel) { + case PlayerHistoryModel: // 历史记录 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var genPlayerHistoryInfo = func(logid string, gameid int32, spinID, username string, isFree bool, createdTime, totalBetValue, totalPriceValue, totalBonusValue, multiple int64, player *qpapi.PlayerHistoryInfo) { + player.SpinID = proto.String(spinID) + player.CreatedTime = proto.Int64(createdTime) + player.TotalBetValue = proto.Int64(totalBetValue) + player.TotalPriceValue = proto.Int64(totalPriceValue) + player.IsFree = proto.Bool(isFree) + player.TotalBonusValue = proto.Int64(totalBonusValue) + player.Multiple = proto.Int64(multiple) + player.Gameid = proto.Int32(gameid) + player.Logid = proto.String(logid) + player.UserName = proto.String(username) + } + + var genPlayerHistoryInfoMsg = func(v *model.NeedGameRecord, gdl *model.GameDetailedLog, player *qpapi.PlayerHistoryInfo) { + switch gdl.GameId { + case common.GameId_Crash: + data, err := model.UnMarshalGameNoteByHUNDRED(gdl.GameDetailedNote) + if err != nil { + logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) + } + jsonString, _ := json.Marshal(data) + + // convert json to struct + gnd := model.CrashType{} + json.Unmarshal(jsonString, &gnd) + + //gnd := data.(*model.CrashType) + for _, curplayer := range gnd.PlayerData { + if curplayer.UserId == v.SnId { + genPlayerHistoryInfo(gdl.LogId, gdl.GameId, strconv.FormatInt(int64(curplayer.UserId), 10), v.Username, false, int64(v.Ts), int64(curplayer.UserBetTotal), curplayer.ChangeCoin, 0, int64(curplayer.UserMultiple), player) + break + } + } + default: + logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive gameid(%v) error", gdl.GameId) + } + } + + gpl := model.GetPlayerListByHallExAPI(searchsnid, curplatform.IdStr, starttime, endtime, int(pageno), int(pagesize)) + //pack := &gamehall.SCPlayerHistory{} + for _, v := range gpl.Data { + if v.GameDetailedLogId == "" { + logger.Logger.Error("World PlayerHistory GameDetailedLogId is nil") + break + } + gdl := model.GetPlayerHistory(curplatform.IdStr, v.GameDetailedLogId) + player := &qpapi.PlayerHistoryInfo{} + genPlayerHistoryInfoMsg(v, gdl, player) + pack.PlayerHistory = append(pack.PlayerHistory, player) + } + pack.PageNo = proto.Int32(int32(gpl.PageNo)) + pack.PageSize = proto.Int32(int32(gpl.PageSize)) + pack.PageSum = proto.Int32(int32(gpl.PageSum)) + proto.SetDefaults(pack) + //logger.Logger.Infof("World gameid:%v PlayerHistory:%v ", gdl.GameId, pack) + return pack + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = qpapi.TagCode_SUCCESS + tNode.TransRep.RetFiels = data + tNode.Resume() + } + }), "CSGetPlayerHistoryHandlerWorld").Start() + return common.ResponseTag_TransactYield, pack + case GameHistoryModel: + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var genGameHistoryInfo = func(gameNumber string, createdTime, multiple int64, hash string, gamehistory *qpapi.GameHistoryInfo) { + gamehistory.GameNumber = proto.String(gameNumber) + gamehistory.CreatedTime = proto.Int64(createdTime) + gamehistory.Hash = proto.String(hash) + gamehistory.Multiple = proto.Int64(multiple) + } + + gls := model.GetPlayerHistoryAPI(searchsnid, curplatform.IdStr, starttime, endtime, int(pageno), int(pagesize)) + + //pack := &gamehall.SCPlayerHistory{} + for _, v := range gls.Data { + + gamehistory := &qpapi.GameHistoryInfo{} + + data, err := model.UnMarshalGameNoteByHUNDRED(v.GameDetailedNote) + if err != nil { + logger.Logger.Errorf("World UnMarshalAvengersGameNote error:%v", err) + } + jsonString, _ := json.Marshal(data) + + // convert json to struct + gnd := model.CrashType{} + json.Unmarshal(jsonString, &gnd) + + genGameHistoryInfo(v.LogId, int64(v.Ts), int64(gnd.Rate), gnd.Hash, gamehistory) + pack.GameHistory = append(pack.GameHistory, gamehistory) + } + proto.SetDefaults(pack) + //logger.Logger.Infof("World gameid:%v History:%v ", gameid, pack) + return pack + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = qpapi.TagCode_SUCCESS + tNode.TransRep.RetFiels = data + tNode.Resume() + } + }), "CSGetGameHistoryHandlerWorld").Start() + return common.ResponseTag_TransactYield, pack + + default: + pack.Tag = qpapi.TagCode_FAILED + pack.Msg = "GameHistoryModel 有误" + return common.ResponseTag_ParamError, pack + //logger.Logger.Errorf("World CSHundredSceneGetGameHistoryInfoHandler receive historyModel(%v) error", historyModel) + } + //} + })) + + //Cresh游戏Hash校验 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/CrashVerifier", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + logger.Logger.Trace("WebAPIHandler:/api/mongo.games.com/game/CrashVerifier", params) + resp := &webapi.SACrachHash{} + msg := &webapi.ASCrachHash{} + err := proto.Unmarshal(params, msg) + if err != nil { + resp.Tag = webapi.TagCode_FAILED + resp.Msg = "数据序列化失败" + return common.ResponseTag_ParamError, resp + } + + hash := msg.GetHash() + wheel := msg.GetWheel() + if hash != "" { + result := crash.HashToMultiple(hash, int(wheel)) + resp.Tag = webapi.TagCode_SUCCESS + resp.Msg = "" + resp.Multiple = result + return common.ResponseTag_Ok, resp + } else { + resp.Tag = webapi.TagCode_FAILED + resp.Msg = "Hash错误" + return common.ResponseTag_ParamError, resp + } + })) + + //----------------------------------传输数据改成protobuf结构--------------------------------------------------- + //后台修改平台数据后推送给游服 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/game_srv/platform_config", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + resp := &webapi_proto.SAUpdatePlatform{} + msg := &webapi_proto.ASUpdatePlatform{} + err := proto.Unmarshal(params, msg) + if err != nil { + resp.Tag = webapi_proto.TagCode_FAILED + resp.Msg = "数据序列化失败" + return common.ResponseTag_ParamError, resp + } + + platforms := msg.GetPlatforms() + if platforms == nil { + logger.Logger.Error("Get platform data error") + resp.Tag = webapi_proto.TagCode_FAILED + resp.Msg = "Get platform data error!" + return common.ResponseTag_ParamError, resp + } else { + for _, ptf := range platforms { + platformName := ptf.GetPlatformName() + isolated := ptf.GetIsolated() + disable := ptf.GetDisabled() + id := ptf.GetId() + url := ptf.GetCustomService() + bindOption := ptf.GetBindOption() + serviceFlag := ptf.GetServiceFlag() + upgradeAccountGiveCoin := ptf.GetUpgradeAccountGiveCoin() + newAccountGiveCoin := ptf.GetNewAccountGiveCoin() + perBankNoLimitAccount := ptf.GetPerBankNoLimitAccount() + exchangeMin := ptf.GetExchangeMin() + exchangeLimit := ptf.GetExchangeLimit() + exchangeTax := ptf.GetExchangeTax() + exchangeFlow := ptf.GetExchangeFlow() + exchangeFlag := ptf.GetExchangeFlag() + spreadConfig := ptf.GetSpreadConfig() + vipRange := ptf.GetVipRange() + otherParams := "" //未传递 + ccf := &ClubConfig{} //未传递 + verifyCodeType := ptf.GetVerifyCodeType() + thirdGameMerchant := ptf.GetThirdGameMerchant() + ths := make(map[int32]int32) + for _, v := range thirdGameMerchant { + ths[v.Id] = v.Merchant + } + customType := ptf.GetCustomType() + needDeviceInfo := false //未传递 + needSameName := ptf.GetNeedSameName() + exchangeForceTax := ptf.GetExchangeForceTax() + exchangeGiveFlow := ptf.GetExchangeGiveFlow() + exchangeVer := ptf.GetExchangeVer() + exchangeBankMax := ptf.GetExchangeBankMax() + exchangeAlipayMax := ptf.GetExchangeAlipayMax() + dbHboCfg := 0 //未传递 + PerBankNoLimitName := ptf.GetPerBankNoLimitName() + IsCanUserBindPromoter := ptf.GetIsCanUserBindPromoter() + UserBindPromoterPrize := ptf.GetUserBindPromoterPrize() + SpreadWinLose := false //未传递 + exchangeMultiple := ptf.GetExchangeMultiple() + registerVerifyCodeSwitch := false //未传递 + merchantKey := ptf.GetMerchantKey() + bindTelReward := ptf.GetBindTelReward() + + platform := PlatformMgrSingleton.UpsertPlatform(platformName, isolated, disable, id, url, + int32(bindOption), serviceFlag, int32(upgradeAccountGiveCoin), int32(newAccountGiveCoin), + int32(perBankNoLimitAccount), int32(exchangeMin), int32(exchangeLimit), int32(exchangeTax), + int32(exchangeFlow), int32(exchangeFlag), int32(spreadConfig), vipRange, otherParams, ccf, + int32(verifyCodeType), ths, int32(customType), needDeviceInfo, needSameName, int32(exchangeForceTax), + int32(exchangeGiveFlow), int32(exchangeVer), int32(exchangeBankMax), int32(exchangeAlipayMax), + int32(dbHboCfg), int32(PerBankNoLimitName), IsCanUserBindPromoter, int32(UserBindPromoterPrize), + SpreadWinLose, int32(exchangeMultiple), registerVerifyCodeSwitch, merchantKey, bindTelReward) + + if platform != nil { + //通知客户端 + scPlatForm := &login_proto.SCPlatFormConfig{ + Platform: platform.IdStr, + OpRetCode: login_proto.OpResultCode_OPRC_Sucess, + UpgradeAccountGiveCoin: upgradeAccountGiveCoin, + ExchangeMin: exchangeMin, + ExchangeLimit: exchangeLimit, + VipRange: vipRange, + OtherParams: otherParams, + SpreadConfig: spreadConfig, + ExchangeTax: platform.ExchangeTax, + ExchangeFlow: platform.ExchangeFlow, + ExchangeBankMax: platform.ExchangeBankMax, + ExchangeAlipayMax: platform.ExchangeAlipayMax, + ExchangeMultiple: platform.ExchangeMultiple, + } + + proto.SetDefaults(scPlatForm) + PlayerMgrSington.BroadcastMessageToPlatform(platform.IdStr, int(login_proto.LoginPacketID_PACKET_SC_PLATFORMCFG), scPlatForm) + } + } + + resp.Tag = webapi_proto.TagCode_SUCCESS + resp.Msg = "" + return common.ResponseTag_Ok, resp + } + //return common.ResponseTag_ParamError, resp + })) + //======= 全局游戏开关 ======= + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/game_srv/update_global_game_status", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateGameConfigGlobal{} + msg := &webapi_proto.ASUpdateGameConfigGlobal{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("err:%v", err.Error()) + return common.ResponseTag_ParamError, pack + } + + gcGlobal := msg.GetGameStatus() + gameStatus := gcGlobal.GetGameStatus() + if len(gameStatus) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Get game status data error" + return common.ResponseTag_ParamError, pack + } else { + //更改所有游戏平台下,游戏的状态 + for _, s := range gameStatus { + gameId := s.GetGameId() + status := s.GetStatus() + PlatformMgrSingleton.GameStatus[gameId] = status + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + return common.ResponseTag_Ok, pack + } + //pack.Tag = webapi_proto.TagCode_FAILED + //pack.Msg = "error" + //return common.ResponseTag_ParamError, pack + })) + //======= 更新游戏配置 ======= + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/game_srv/update_game_configs", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateGameConfig{} + msg := &webapi_proto.ASUpdateGameConfig{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("err:%v", err.Error()) + return common.ResponseTag_ParamError, pack + } + + platformConfig := msg.GetConfig() + platformId := int(platformConfig.GetPlatformId()) + platform := PlatformMgrSingleton.GetPlatform(strconv.Itoa(platformId)) + dbGameFrees := platformConfig.GetDbGameFrees() + if len(dbGameFrees) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Not have DbGameFrees" + return common.ResponseTag_ParamError, pack + } else { + //更改所有游戏平台下,游戏的状态 + for _, v := range dbGameFrees { + dbGameFree := v.GetDbGameFree() + logicId := dbGameFree.GetId() + platform.PltGameCfg.games[logicId] = v + } + platform.PltGameCfg.RecreateCache() + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + return common.ResponseTag_Ok, pack + } + //pack.Tag = webapi_proto.TagCode_FAILED + //pack.Msg = "error" + //return common.ResponseTag_ParamError, pack + })) + //======= 更新游戏分组配置 ======= + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/game_srv/game_config_group", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateGameConfigGroup{} + msg := &webapi_proto.ASUpdateGameConfigGroup{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("err:%v", err.Error()) + return common.ResponseTag_ParamError, pack + } + + value := msg.GetGameConfigGroup() + PlatformGameGroupMgrSington.UpsertGameGroup(value) + logger.Logger.Trace("PlatformGameGroup data:", value) + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + return common.ResponseTag_Ok, pack + })) + + //---------------------------------------------------------------------------------------------------------- + //加币加钻石 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/AddCoinByIdAndPT", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAAddCoinByIdAndPT{} + msg := &webapi_proto.ASAddCoinByIdAndPT{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + member_snid := msg.GetID() + billNo := int(msg.GetBillNo()) + platform := msg.GetPlatform() + + if CacheDataMgr.CacheBillCheck(billNo, platform) { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Bill number repeated!" + return common.ResponseTag_ParamError, pack + } + CacheDataMgr.CacheBillNumber(billNo, platform) //防止手抖点两下 + player := PlayerMgrSington.GetPlayerBySnId(member_snid) + + var remainNum = player.Coin + var addcoin, diamond int64 = msg.GetGold(), 0 + var logtype = int32(common.GainWay_API_AddCoin) + if msg.GetLogType() == 1 { + addcoin = 0 + diamond = msg.GetGold() + remainNum = player.Diamond + } + money := msg.Money + //玩家在线 + if player != nil { + //玩家在游戏内 + if player.scene != nil { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Unsupported!!! because player in scene!" + return common.ResponseTag_ParamError, pack + } + if len(platform) > 0 && player.Platform != platform { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "player platform forbit!" + return common.ResponseTag_ParamError, pack + } + if player.Coin+addcoin < 0 || player.Diamond+diamond < 0 { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "coin not enough!" + return common.ResponseTag_ParamError, pack + } + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: player.Platform, + SnID: player.SnId, + ChangeType: msg.GetLogType(), + ChangeNum: msg.GetGold(), + RemainNum: remainNum + addcoin, + Add: 0, + LogType: logtype, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: msg.GetOper(), + Remark: msg.GetDesc(), + }) + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err := model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemoveCoinLogOne(platform, coinlogex.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "success." + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + player.Coin += addcoin + player.Diamond += diamond + if addcoin+diamond > 0 { + player.UpdateShopID(msg.GetShopId()) + } + if money > 0 { + player.MoneyPayTotal += money + player.SCVIPInfo() + TaskSubjectSingleton.Touch(common.TaskTypePay, &TaskData{ + SnId: player.SnId, + Num: money, + }) + } + player.SendDiffData() + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "AddCoinByIdAndPT").Start() + } else { + //玩家不在线 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + findPlayer, _ := model.GetPlayerDataBySnId(platform, int32(member_snid), false, true) + if findPlayer != nil { + if len(platform) > 0 && findPlayer.Platform != platform { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Msg = "player platform forbit!" + return nil + } + if findPlayer.Coin+addcoin < 0 || findPlayer.Diamond+diamond < 0 { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Msg = "coin not enough!" + return nil + } + + // 首次购买现金商品,获得翻倍;标记商品已经购买过 + if addcoin+diamond > 0 { + if m, ok := ShopMgrSington.ShopInfos[platform]; ok && m != nil { + if shopInfo, ok := m[msg.GetShopId()]; ok && shopInfo != nil { + if shopInfo.FirstSwitch { + if !slices.Contains(findPlayer.ShopID, int(msg.GetShopId())) { + findPlayer.ShopID = append(findPlayer.ShopID, int(msg.GetShopId())) + } + } + } + } + } + + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: findPlayer.Platform, + SnID: findPlayer.SnId, + ChangeType: msg.GetLogType(), + ChangeNum: msg.GetGold(), + RemainNum: remainNum + addcoin, + Add: 0, + LogType: logtype, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: msg.GetOper(), + Remark: msg.GetDesc(), + }) + + err := model.UpdatePlayerCoin(findPlayer.Platform, findPlayer.SnId, findPlayer.Coin+addcoin, + findPlayer.Diamond+diamond, findPlayer.SafeBoxCoin, findPlayer.CoinPayTs, findPlayer.SafeBoxCoinTs, findPlayer.MoneyPayTotal+money, findPlayer.ShopID) + if err != nil { + logger.Logger.Errorf("model.UpdatePlayerCoin err:%v.", err) + return nil + } + //账变记录 + err = model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemoveCoinLogOne(platform, coinlogex.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + } + return findPlayer + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Tag = webapi_proto.TagCode_FAILED + if pack.Msg == "" { + pack.Msg = "Player not find." + } + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "success." + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "AddCoinByIdAndPT").Start() + + } + + return common.ResponseTag_TransactYield, pack + })) + + // //------------------------------------------------------------------------------------------------------- + // //钱包操作接口 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/AddCoinById", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAAddCoinById{} + msg := &webapi_proto.ASAddCoinById{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + + member_snid := msg.GetID() + coin := msg.GetGold() + coinEx := msg.GetGoldEx() + oper := msg.GetOper() + gold_desc := msg.GetDesc() + billNo := int(msg.GetBillNo()) + platform := msg.GetPlatform() + logType := msg.GetLogType() + isAccTodayRecharge := msg.GetIsAccTodayRecharge() + needFlowRate := msg.GetNeedFlowRate() + needGiveFlowRate := msg.GetNeedGiveFlowRate() + + if CacheDataMgr.CacheBillCheck(billNo, platform) { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Bill number repeated!" + return common.ResponseTag_ParamError, pack + } + CacheDataMgr.CacheBillNumber(billNo, platform) //防止手抖点两下 + + var err error + var pd *model.PlayerData + oldGold := int64(0) + var timeStamp = time.Now().UnixNano() + player := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) + if player != nil { //在线玩家处理 + if player.scene != nil { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Unsupported!!! because player in scene!" + return common.ResponseTag_ParamError, pack + } + pd = player.PlayerData + if len(platform) > 0 && player.Platform != platform { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "player platform forbit!" + return common.ResponseTag_ParamError, pack + } + + if coin < 0 { + if player.Coin+coin < 0 { + CacheDataMgr.ClearCacheBill(billNo, platform) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "coin not enough!" + return common.ResponseTag_ParamError, pack + } + } + + opcode := int32(common.GainWay_API_AddCoin) + if logType != 0 { + opcode = logType + } + + oldGold = player.Coin + coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + gold_desc, model.PayCoinLogType_Coin, coinEx) + timeStamp = coinLog.TimeStamp + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: pd.Platform, + SnID: member_snid, + ChangeType: common.BillTypeCoin, + ChangeNum: coin + coinEx, + RemainNum: oldGold + coin + coinEx, + Add: 0, + LogType: opcode, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: gold_desc, + }) + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + err = model.InsertPayCoinLogs(platform, coinLog) + if err != nil { + logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + return err + } + err = model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemovePayCoinLog(platform, coinLog.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + CacheDataMgr.ClearCacheBill(billNo, platform) + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + //player.Coin += coin + coinEx + player.AddCoinAsync(coin+coinEx, 0, common.GainWay_API_AddCoin, oper, gold_desc, true, 0, false) + //增加相应的泥码量 + player.AddDirtyCoin(coin, coinEx) + player.SetPayTs(timeStamp) + + if player.TodayGameData == nil { + player.TodayGameData = model.NewPlayerGameCtrlData() + } + //actRandCoinMgr.OnPlayerRecharge(player, coin) + if isAccTodayRecharge { + + player.AddCoinPayTotal(coin) + player.TodayGameData.RechargeCoin += coin //累加当天充值金额 + if coin >= 0 && coinEx >= 0 { + plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, coinEx, 0, opcode, pd.PromoterTree, + model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + if log != nil { + err := model.InsertGiveCoinLog(log) + if err == nil { + if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + } + } + //清空流水,更新id + pd.TotalConvertibleFlow = 0 + pd.LastExchangeOrder = log.LogId.Hex() + if player == nil { + //需要回写数据库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdatePlayerExchageFlowAndOrder(platform, member_snid, 0, pd.LastExchangeOrder) + return nil + }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + } + } + } + } + + player.dirty = true + player.Time2Save() + if player.scene == nil { //如果在大厅,那么同步下金币 + player.SendDiffData() + } + player.SendPlayerRechargeAnswer(coin) + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("AddCoinById task marshal data error:", err) + } + }), "AddCoinById").Start() + return common.ResponseTag_TransactYield, pack + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pd, _ = model.GetPlayerDataBySnId(platform, int32(member_snid), false, true) + if pd == nil { + return errors.New("Player not find.") + } + if len(platform) > 0 && pd.Platform != platform { + return errors.New("player platform forbit.") + } + oldGold = pd.Coin + if coin < 0 { + if pd.Coin+coin < 0 { + return errors.New("coin not enough!") + } + } + + opcode := int32(common.GainWay_API_AddCoin) + if logType != 0 { + opcode = logType + } + coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, opcode, + gold_desc, model.PayCoinLogType_Coin, coinEx) + timeStamp = coinLog.TimeStamp + err = model.InsertPayCoinLogs(platform, coinLog) + if err != nil { + logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, coinLog) + return err + } + //增加帐变记录 + coinlogex := model.NewCoinLogEx(&model.CoinLogParam{ + Platform: pd.Platform, + SnID: member_snid, + ChangeType: common.BillTypeCoin, + ChangeNum: coin + coinEx, + RemainNum: oldGold + coin + coinEx, + Add: 0, + LogType: opcode, + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: gold_desc, + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + //回滚到对账日志 + model.RemovePayCoinLog(platform, coinLog.LogId) + logger.Logger.Errorf("model.InsertCoinLogs err:%v log:%v", err, coinlogex) + return err + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + CacheDataMgr.ClearCacheBill(billNo, platform) + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = data.(error).Error() + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + if isAccTodayRecharge && coin >= 0 { + OnPlayerPay(pd, coin) + } + if isAccTodayRecharge && coin >= 0 && coinEx >= 0 { + + plt := PlatformMgrSingleton.GetPlatform(pd.Platform) + curVer := int32(0) + if plt != nil { + curVer = plt.ExchangeVer + } + log := model.NewCoinGiveLogEx(pd.SnId, pd.Name, coin, coinEx, 0, common.GainWay_API_AddCoin, pd.PromoterTree, + model.COINGIVETYPE_PAY, curVer, pd.Platform, pd.Channel, pd.BeUnderAgentCode, + "", "system", pd.PackageID, int32(needFlowRate), int32(needGiveFlowRate)) + if log != nil { + err := model.InsertGiveCoinLog(log) + if err == nil { + if pd.LastExchangeOrder != "" && pd.TotalConvertibleFlow > 0 { + err = model.UpdateGiveCoinLastFlow(platform, pd.LastExchangeOrder, pd.TotalConvertibleFlow) + } + } + //清空流水,更新id + pd.TotalConvertibleFlow = 0 + pd.LastExchangeOrder = log.LogId.Hex() + if player == nil { + //需要回写数据库 + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.UpdatePlayerExchageFlowAndOrder(platform, int32(member_snid), 0, pd.LastExchangeOrder) + return nil + }), nil, "UpdateGiveCoinLogs").StartByExecutor(pd.AccountId) + } + } + + } + } + + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("AddCoinById task marshal data error:", err) + } + }), "AddCoinById").Start() + return common.ResponseTag_TransactYield, pack + } + })) + //重置水池 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/ResetGamePool", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAResetGamePool{} + msg := &webapi_proto.ASResetGamePool{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + if msg.GameFreeId == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "GameFreeId id zero" + return common.ResponseTag_ParamError, pack + } + gs := GameSessMgrSington.GetGameSess(int(msg.ServerId)) + if gs != nil { + msg := &server.WGResetCoinPool{ + Platform: proto.String(msg.GetPlatform()), + GameFreeId: proto.Int32(msg.GameFreeId), + ServerId: proto.Int32(msg.ServerId), + GroupId: proto.Int32(msg.GroupId), + PoolType: proto.Int32(msg.PoolType), + Value: proto.Int64(msg.Value), + } + proto.SetDefaults(msg) + gs.Send(int(server.SSPacketID_PACKET_WG_RESETCOINPOOL), msg) + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "no find srvId" + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "reset game pool success" + return common.ResponseTag_Ok, pack + })) + + // 水池开关 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/GamePoolSwitch", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi_proto.SAGamePoolSwitch{} + // msg := &webapi_proto.ASGamePoolSwitch{} + // err := proto.Unmarshal(params, msg) + // if err != nil { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err.Error() + // return common.ResponseTag_ParamError, pack + // } + // if msg.GameFreeId == 0 { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "GameFreeId id zero" + // return common.ResponseTag_ParamError, pack + // } + // gs := GameSessMgrSington.GetGameSess(int(msg.ServerId)) + // if gs != nil { + // msg := &server.WGGamePoolSwitch{ + // Platform: msg.GetPlatform(), + // GameFreeId: msg.GetGameFreeId(), + // ServerId: msg.GetServerId(), + // GroupId: msg.GetGroupId(), + // Switch: msg.GetSwitch(), + // } + // gs.Send(int(server.SSPacketID_PACKET_WG_GAMEPOOLSWITCH), msg) + // } else { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "no find srvId" + // return common.ResponseTag_ParamError, pack + // } + // pack.Tag = webapi_proto.TagCode_SUCCESS + // pack.Msg = "switch game pool success" + // return common.ResponseTag_Ok, pack + // })) + + //更新水池 + // 修改本地缓存,修改数据库,同步到gamesrv + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/UpdateGamePool", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateGamePool{} + msg := &webapi_proto.ASUpdateGamePool{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + coinPoolSetting := msg.GetCoinPoolSetting() + if coinPoolSetting == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "GetCoinPoolSetting is nil" + return common.ResponseTag_ParamError, pack + } + var old *model.CoinPoolSetting + cps := model.GetCoinPoolSetting(msg.GetGameFreeId(), msg.GetServerId(), msg.GetGroupId(), msg.GetPlatform()) + if cps == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "CoinPoolSettingDatas is nil" + return common.ResponseTag_NoData, pack + } + temp := *cps + old = &temp + // 修改worldsrv中的水池配置 + cps.InitValue = coinPoolSetting.GetInitValue() + cps.LowerLimit = coinPoolSetting.GetLowerLimit() + cps.UpperLimit = coinPoolSetting.GetUpperLimit() + cps.QuDu = coinPoolSetting.GetQuDu() + cps.UpperOdds = coinPoolSetting.GetUpperOdds() + cps.UpperOddsMax = coinPoolSetting.GetUpperOddsMax() + cps.LowerOdds = coinPoolSetting.GetLowerOdds() + cps.LowerOddsMax = coinPoolSetting.GetLowerOddsMax() + cps.ProfitRate = coinPoolSetting.GetProfitRate() + cps.ResetTime = coinPoolSetting.GetResetTime() + cps.Switch = coinPoolSetting.GetSwitch() + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // 新配置更新到user_coinpoolsetting,旧配置保存到log_coinpoolsetting + return model.UpsertCoinPoolSetting(cps, old) + }), task.CompleteNotifyWrapper(func(data interface{}, task task.Task) { + if data != nil { + logger.Logger.Errorf("model.UpsertCoinPoolSetting cps:%v err:%v", cps, data) + } + }), "UpsertCoinPoolSetting").Start() + // 水池配置同步到gamesrv + gs := GameSessMgrSington.GetGameSess(int(cps.ServerId)) + if gs != nil { + var msg = &webapi_proto.CoinPoolSetting{ + Platform: cps.Platform, + GameFreeId: cps.GameFreeId, + ServerId: cps.ServerId, + GroupId: cps.GroupId, + InitValue: cps.InitValue, + LowerLimit: cps.LowerLimit, + UpperLimit: cps.UpperLimit, + QuDu: cps.QuDu, + UpperOdds: cps.UpperOdds, + UpperOddsMax: cps.UpperOddsMax, + LowerOdds: cps.LowerOdds, + LowerOddsMax: cps.LowerOddsMax, + ProfitRate: cps.ProfitRate, + ResetTime: cps.ResetTime, + Switch: cps.Switch, + } + proto.SetDefaults(msg) + gs.Send(int(server.SSPacketID_PACKET_WG_COINPOOLSETTING), msg) + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "update game pool success" + return common.ResponseTag_Ok, pack + })) + + //查询水池gameid + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/QueryGamePoolByGameId", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAQueryGamePoolByGameId{} + msg := &webapi_proto.ASQueryGamePoolByGameId{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + StartQueryCoinPoolTransact(tNode, msg.GetGameId(), msg.GetGameMode(), msg.GetPlatform(), msg.GetGroupId()) + return common.ResponseTag_TransactYield, nil + })) + + //查询水池 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/QueryAllGamePool", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi_proto.SAQueryAllGamePool{} + // msg := &webapi_proto.ASQueryAllGamePool{} + // err := proto.Unmarshal(params, msg) + // if err != nil { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "数据序列化失败" + err.Error() + // return common.ResponseTag_ParamError, pack + // } + // pageNo := msg.GetPageNo() + // pageSize := msg.GetPageSize() + // StartQueryCoinPoolStatesTransact(tNode, int32(pageNo), int32(pageSize)) + // return common.ResponseTag_TransactYield, nil + // })) + //////////////////////////房间////////////////////////// + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/GetRoom", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAGetRoom{} + msg := &webapi_proto.ASGetRoom{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + roomId := int(msg.GetSceneId()) + snid := msg.GetSnId() + pack.Tag = webapi_proto.TagCode_SUCCESS + if roomId == 0 { + if snid != 0 { + p := PlayerMgrSington.GetPlayerBySnId(snid) + if p != nil && p.scene != nil { + roomId = p.scene.sceneId + } + } + } + s := SceneMgrSingleton.GetScene(roomId) + if s == nil { + pack.Msg = "no found" + return common.ResponseTag_NoData, pack + } else { + si := &webapi_proto.RoomInfo{ + Platform: s.limitPlatform.Name, + SceneId: int32(s.sceneId), + GameId: int32(s.gameId), + GameMode: int32(s.gameMode), + SceneMode: int32(s.sceneMode), + GroupId: s.groupId, + GameFreeId: s.paramsEx[0], + Creator: s.creator, + Agentor: s.agentor, + ReplayCode: s.replayCode, + Params: s.params, + PlayerCnt: int32(len(s.players) - s.robotNum), + RobotCnt: int32(s.robotNum), + CreateTime: s.createTime.Unix(), + BaseScore: s.dbGameFree.BaseScore, + } + if common.IsLocalGame(s.gameId) { + si.BaseScore = s.BaseScore + } + if s.starting { + si.Start = 1 + } else { + si.Start = 0 + } + if s.gameSess != nil { + si.SrvId = s.gameSess.GetSrvId() + } + cnt := 0 + total := len(s.players) + robots := []int32{} + //优先显示玩家 + for id, p := range s.players { + if !p.IsRob || total < 10 { + si.PlayerIds = append(si.PlayerIds, id) + cnt++ + } else { + robots = append(robots, id) + } + if cnt > 10 { + break + } + } + //不够再显示机器人 + if total > cnt && cnt < 10 && len(robots) != 0 { + for i := 0; cnt < 10 && i < len(robots); i++ { + si.PlayerIds = append(si.PlayerIds, robots[i]) + cnt++ + if cnt > 10 { + break + } + } + } + pack.RoomInfo = si + } + return common.ResponseTag_Ok, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/ListRoom", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAListRoom{} + msg := &webapi_proto.ASListRoom{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + + pageNo := msg.GetPageNo() + pageSize := msg.GetPageSize() + //数据校验 + if pageNo == 0 { + pageNo = 1 + } + if pageSize == 0 { + pageSize = 20 + } + pack.Tag = webapi_proto.TagCode_SUCCESS + start := (pageNo - 1) * pageSize + end := pageNo * pageSize + roomList, count, roomSum := SceneMgrSingleton.MarshalAllRoom(msg.GetPlatform(), int(msg.GetGroupId()), int(msg.GetGameId()), + int(msg.GetGameMode()), int(msg.GetClubId()), int(msg.GetRoomType()), int(msg.GetSceneId()), msg.GamefreeId, msg.GetSnId(), start, end, pageSize) + if count < pageNo { + pageNo = 1 + } + pack.RoomInfo = roomList + pack.PageCount = count + pack.PageNo = pageNo + pack.TotalList = roomSum + return common.ResponseTag_Ok, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/DestroyRoom", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SADestroyRoom{} + msg := &webapi_proto.ASDestroyRoom{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + platform := msg.GetPlatform() + if len(platform) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "the platform is nil" + return common.ResponseTag_ParamError, pack + } + switch msg.DestroyType { + case 1: //删除所有空房间 + for _, s := range SceneMgrSingleton.scenes { + if !s.isPlatform(platform) { + continue + } + if s != nil && !s.deleting && len(s.players) == 0 { + logger.Logger.Warnf("WebService SpecailEmptySceneId destroyroom scene:%v", s.sceneId) + s.TryForceDelectMatchInfo() + s.ForceDelete(false) + } + } + case 2: //删除所有未开始的房间 + for _, s := range SceneMgrSingleton.scenes { + if !s.isPlatform(platform) { + continue + } + if s != nil && !s.deleting && !s.starting && !s.IsHundredScene() { + logger.Logger.Warnf("WebService SpecailUnstartSceneId destroyroom scene:%v", s.sceneId) + s.TryForceDelectMatchInfo() + s.ForceDelete(false) + } + } + default: //删除指定房间 + if len(msg.SceneIds) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "the sceneid is nil" + return common.ResponseTag_NoFindRoom, pack + } + for _, sceneId := range msg.GetSceneIds() { + s := SceneMgrSingleton.GetScene(int(sceneId)) + if s == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "the sceneid is nil" + return common.ResponseTag_NoFindRoom, pack + } + if !s.isPlatform(platform) { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "the sceneid is not ower platform" + return common.ResponseTag_NoFindRoom, pack + } + logger.Logger.Warnf("WebService destroyroom scene:%v", s.sceneId) + s.TryForceDelectMatchInfo() + s.ForceDelete(false) + } + } + return common.ResponseTag_Ok, pack + })) + ///////////////////////////玩家信息/////////////////////////// + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/PlayerData", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAGetPlayerData{} + msg := &webapi_proto.ASGetPlayerData{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + id := msg.GetID() + platform := msg.GetPlatform() + player := PlayerMgrSington.GetPlayerBySnId(id) + var playerRankScore *model.PlayerRankSeason + if player == nil { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + data, _ := model.GetPlayerDataBySnId(platform, id, true, false) + if data == nil || data.SnId == 0 { + data = model.GetDelBackupPlayerData(platform, id) + } + ret, _ := model.FindPlayerRankSeason(&model.FindPlayerRankSeasonArgs{ + Platform: platform, + Id: []int32{id}, + }) + if len(ret.List) > 0 { + playerRankScore = ret.List[0] + } + return data + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + playerData, ok := data.(*model.PlayerData) + if !ok || playerData == nil { + pack.Msg = "no find player" + pack.Tag = webapi_proto.TagCode_FAILED + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pdfw := model.ConvertPlayerDataToWebData(getPlayerDataParam(playerData, playerRankScore)) + + pdfw.Online = false + pack.PlayerData = pdfw + } + logger.Logger.Trace("PlayerData==>", pack) + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "PlayerData").Start() + return common.ResponseTag_TransactYield, pack + } else { + if len(platform) > 0 && player.Platform == platform { + pack.Tag = webapi_proto.TagCode_SUCCESS + pwdf := model.ConvertPlayerDataToWebData(getPlayerDataParam(player.PlayerData, nil)) + pwdf.Online = player.IsOnLine() + if pwdf != nil { + if player.scene != nil && player.scene.sceneId != common.DgSceneId && !player.scene.IsTestScene() { + pwdf.Coin = player.sceneCoin + } + } + pack.PlayerData = pwdf + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "no find player" + } + logger.Logger.Trace("PlayerData==>", pack) + return common.ResponseTag_Ok, pack + } + })) + //多个玩家数据 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/MorePlayerData", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAMorePlayerData{} + msg := &webapi_proto.ASMorePlayerData{} + err := proto.Unmarshal(params, msg) + pack.Tag = webapi_proto.TagCode_FAILED + if err != nil { + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + if len(msg.GetSnIds()) == 0 { + pack.Msg = "IDs value error!" + return common.ResponseTag_ParamError, pack + } + if len(msg.GetSnIds()) > model.GameParamData.MorePlayerLimit { + pack.Msg = "IDs too more error!" + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + var leavePlayer []int32 + var playerRankScore = map[int32]*model.PlayerRankSeason{} + for _, snid := range msg.GetSnIds() { + player := PlayerMgrSington.GetPlayerBySnId(snid) + if player != nil { + pwdf := model.ConvertPlayerDataToWebData(getPlayerDataParam(player.PlayerData, nil)) + if pwdf != nil { + pwdf.Online = true + if player.scene != nil && player.scene.sceneId != common.DgSceneId { + pwdf.Coin = player.sceneCoin + } + pack.PlayerData = append(pack.PlayerData, pwdf) + } + } else { + leavePlayer = append(leavePlayer, snid) + } + } + if len(leavePlayer) == 0 { + return common.ResponseTag_Ok, pack + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, _ := model.FindPlayerRankSeason(&model.FindPlayerRankSeasonArgs{ + Platform: msg.GetPlatform(), + Id: leavePlayer, + }) + for _, v := range ret.List { + playerRankScore[v.SnId] = v + } + return model.GetPlayerDatasBySnIds(msg.Platform, leavePlayer, false) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + playerDatas, _ := data.([]*model.PlayerData) + for _, v := range playerDatas { + pdfw := model.ConvertPlayerDataToWebData(getPlayerDataParam(v, playerRankScore[v.SnId])) + if pdfw != nil { + pdfw.Online = false + pack.PlayerData = append(pack.PlayerData, pdfw) + } + } + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "PlayerData").Start() + return common.ResponseTag_TransactYield, pack + })) + //更新玩家数据 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Cache/UpdatePlayerElement", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdatePlayerElement{} + msg := &webapi_proto.ASUpdatePlayerElement{} + uerr := proto.Unmarshal(params, msg) + pack.Tag = webapi_proto.TagCode_FAILED + if uerr != nil { + pack.Msg = "数据序列化失败" + uerr.Error() + return common.ResponseTag_ParamError, pack + } + //简单校验接收到的数据 + if msg.SnId == 0 || len(msg.GetPlayerEleArgs()) == 0 { + pack.Msg = "error: ID or ElementList is nil" + return common.ResponseTag_Ok, pack + } + + playerMap := make(map[string]interface{}) + for _, v := range msg.GetPlayerEleArgs() { + playerMap[v.Key] = v.Val + } + listBan := [...]string{"Id", "SnId", "AccountId"} + for i := 0; i < len(listBan); i++ { + if _, exist := playerMap[listBan[i]]; exist { + delete(playerMap, listBan[i]) + } + } + if len(playerMap) == 0 { + pack.Msg = "no any data" + return common.ResponseTag_Ok, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + player := PlayerMgrSington.GetPlayerBySnId(msg.SnId) + if player != nil { + if len(msg.Platform) > 0 && player.Platform != msg.Platform { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Platform error." + return common.ResponseTag_NoFindUser, pack + } else { + //玩家在线 + //获取玩家数据 + var pd *model.PlayerData + pd = player.PlayerData + SetInfo := func(u *model.PlayerData, o interface{}, playerMap map[string]interface{}) *model.PlayerData { + t := reflect.TypeOf(o) + v := reflect.ValueOf(o) + s := reflect.ValueOf(u).Elem() + for k, n := range playerMap { + if f, ok := t.FieldByName(k); ok { + if v.FieldByName(k).CanInterface() { + switch f.Type.Kind() { + case reflect.Int64, reflect.Int32: + a, _ := strconv.ParseInt((fmt.Sprintf("%v", n)), 10, 64) + s.FieldByName(k).SetInt(a) + case reflect.Bool: + s.FieldByName(k).SetBool(n.(bool)) + case reflect.String: + s.FieldByName(k).SetString(n.(string)) + default: + logger.Logger.Warn("type not in the player !!") + } + } + } + } + return u + } + pd = SetInfo(pd, *pd, playerMap) + player.dirty = true + player.SendDiffData() + var alipayAcc, alipayAccName, bankAccount, bankAccName string + if val, ok := playerMap["AlipayAccount"]; ok { + if str, ok := val.(string); ok { + alipayAcc = str + } + } + if val, ok := playerMap["AlipayAccName"]; ok { + if str, ok := val.(string); ok { + alipayAccName = str + } + } + if val, ok := playerMap["BankAccount"]; ok { + if str, ok := val.(string); ok { + bankAccount = str + } + } + if val, ok := playerMap["BankAccName"]; ok { + if str, ok := val.(string); ok { + bankAccName = str + } + } + if alipayAcc != "" || alipayAccName != "" { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.NewBankBindLog(msg.SnId, msg.Platform, model.BankBindLogType_Ali, + alipayAccName, alipayAcc, 2) + return nil + }), nil, "NewBankBindLog").Start() + } + if bankAccount != "" || bankAccName != "" { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + model.NewBankBindLog(msg.SnId, msg.Platform, model.BankBindLogType_Bank, + bankAccName, bankAccount, 2) + return nil + }), nil, "NewBankBindLog").Start() + } + pack.Msg = "success" + return common.ResponseTag_Ok, pack + } + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + //玩家不在线 + //先检测是否有玩家 + if len(msg.Platform) > 0 { + pbi := model.GetPlayerBaseInfo(msg.Platform, msg.SnId) + if pbi == nil { + return errors.New("no player") + } + } + //直接操作数据库 + err := model.UpdatePlayerElement(msg.Platform, msg.SnId, playerMap) + if err == nil { + var alipayAcc, alipayAccName, bankAccount, bankAccName string + if val, ok := playerMap["AlipayAccount"]; ok { + if str, ok := val.(string); ok { + alipayAcc = str + } + } + if val, ok := playerMap["AlipayAccName"]; ok { + if str, ok := val.(string); ok { + alipayAccName = str + } + } + if val, ok := playerMap["BankAccount"]; ok { + if str, ok := val.(string); ok { + bankAccount = str + } + } + if val, ok := playerMap["BankAccName"]; ok { + if str, ok := val.(string); ok { + bankAccName = str + } + } + if alipayAcc != "" || alipayAccName != "" { + model.NewBankBindLog(msg.SnId, msg.Platform, model.BankBindLogType_Ali, + alipayAccName, alipayAcc, 2) + } + if bankAccount != "" || bankAccName != "" { + model.NewBankBindLog(msg.SnId, msg.Platform, model.BankBindLogType_Bank, + bankAccName, bankAccount, 2) + } + } else if err != nil { + logger.Logger.Error("UpdatePlayerElement task marshal data error:", err) + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Msg = "success" + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("%v", data) + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "UpdatePlayerElement").Start() + return common.ResponseTag_TransactYield, pack + } + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/KickPlayer", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAKickPlayer{} + msg := &webapi_proto.ASKickPlayer{} + err := proto.Unmarshal(params, msg) + pack.Tag = webapi_proto.TagCode_FAILED + if err != nil { + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + if msg.SnId == 0 { + pack.Msg = "snid is zero" + return common.ResponseTag_ParamError, pack + } + player := PlayerMgrSington.GetPlayerBySnId(msg.SnId) + if player != nil { + if msg.Platform == "" || msg.Platform != player.Platform { + pack.Msg = "platform is dif" + return common.ResponseTag_ParamError, pack + } + sid := player.sid + endTime := time.Now().Unix() + int64(time.Minute.Seconds())*int64(msg.Minute) + ls := LoginStateMgrSington.GetLoginStateBySid(sid) + if ls != nil { + if ls.als != nil && ls.als.acc != nil { + ls.als.acc.State = endTime + } + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.FreezeAccount(msg.Platform, player.AccountId, int(msg.Minute)) + }), nil, "FreezeAccount").Start() + player.Kickout(common.KickReason_Freeze) + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "success" + } else { + pack.Msg = "the player not online or no player." + } + return common.ResponseTag_Ok, pack + })) + //获取在线统计 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Report/OnlineReportTotal", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAOnlineReportTotal{} + msg := &webapi_proto.ASOnlineReportTotal{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + //inGameCnt := make(map[int]int32) + pack.OnlineReport = &webapi_proto.OnlineReport{} + tNow := time.Now() + for _, p := range PlayerMgrSington.players { + if !p.IsOnLine() { + continue + } + if len(msg.Platform) != 0 && msg.Platform != "0" && p.Platform != msg.Platform { + continue + } + if len(msg.Channel) != 0 && p.Channel != msg.Channel { + continue + } + if len(msg.Promoter) != 0 && p.BeUnderAgentCode != msg.Promoter { + continue + } + pack.OnlineReport.TotalCnt++ + if p.DeviceOS == common.DeviceOS_Android { + pack.OnlineReport.AndroidOnlineCnt++ + } else if p.DeviceOS == common.DeviceOS_IOS { + pack.OnlineReport.IosOnlineCnt++ + } + if p.scene == nil { + pack.OnlineReport.DatingPlayers++ + } else { + pack.OnlineReport.OnRoomPlayers++ + //inGameCnt[p.scene.gameId]++ + } + if common.InSameDay(p.CreateTime, tNow) { + pack.OnlineReport.TodayRegisterOnline++ + } else { + if common.DiffDay(tNow, p.CreateTime) <= 7 { + pack.OnlineReport.SevenDayRegisterOnline++ + } + } + } + //var onlineGameCnt []*webapi_proto.OnlineGameCnt + //for gameId, cnt := range inGameCnt { + // onlineGameCnt = append(onlineGameCnt, &webapi_proto.OnlineGameCnt{ + // GameId: int32(gameId), + // Cnt: cnt}, + // ) + //} + pack.Tag = webapi_proto.TagCode_SUCCESS + //pack.OnlineReport.GameCount = onlineGameCnt + return common.ResponseTag_Ok, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Report/QueryOnlineReportList", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAQueryOnlineReportList{} + msg := &webapi_proto.ASQueryOnlineReportList{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + platform := msg.Platform + pageNo := msg.PageNo + pageSize := msg.PageSize + orderColumn := msg.OrderColumn + orderType := msg.OrderType + channel := msg.Channel + + start := (pageNo - 1) * pageSize + end := pageNo * pageSize + count := len(PlayerMgrSington.players) + if count < int(start) || count == 0 { + pack.Tag = webapi_proto.TagCode_SUCCESS + return common.ResponseTag_Ok, pack + } + var players []*Player + for _, p := range PlayerMgrSington.players { + if !p.IsOnLine() { + continue + } + if msg.GameFreeId == 0 { + if msg.GameId != 0 && (p.scene == nil || p.scene.gameId != int(msg.GameId)) { + continue + } + } else { + if p.scene == nil || p.scene.dbGameFree.GetId() != msg.GameFreeId { + continue + } + } + if platform != DefaultPlatform && p.Platform != platform { + continue + } + if channel != "" && p.AppChannel != channel { + continue + } + players = append(players, p) + } + // 排序 + var sortFunc func(i, j int) bool + switch orderColumn { + case common.OrderColumnCoinPayTotal: + sortFunc = func(i, j int) bool { + if players[i].CoinPayTotal == players[j].CoinPayTotal { + return false + } + return players[i].CoinPayTotal > players[j].CoinPayTotal + } + case common.OrderColumnCoinExchangeTotal: + sortFunc = func(i, j int) bool { + if players[i].CoinExchangeTotal == players[j].CoinExchangeTotal { + return false + } + return players[i].CoinExchangeTotal > players[j].CoinExchangeTotal + } + case common.OrderColumnTaxTotal: + sortFunc = func(i, j int) bool { + if players[i].GameTax == players[j].GameTax { + return false + } + return players[i].GameTax > players[j].GameTax + } + case common.OrderColumnRegisterTime: + sortFunc = func(i, j int) bool { + if players[i].CreateTime.Equal(players[j].CreateTime) { + return false + } + return players[i].CreateTime.After(players[j].CreateTime) + } + case common.OrderColumnRoomNumber: + sortFunc = func(i, j int) bool { + a, b := players[i], players[j] + if a.scene == nil && b.scene == nil { + return false + } + if a.scene != nil && b.scene == nil { + return true + } + if a.scene == nil && b.scene != nil { + return false + } + if a.scene.sceneId == b.scene.sceneId { + return false + } + return a.scene.sceneId > b.scene.sceneId + } + case common.OrderColumnLose: + sortFunc = func(i, j int) bool { + if players[i].FailTimes == players[j].FailTimes { + return false + } + return players[i].FailTimes > players[j].FailTimes + } + case common.OrderColumnWin: + sortFunc = func(i, j int) bool { + if players[i].WinTimes == players[j].WinTimes { + return false + } + return players[i].WinTimes > players[j].WinTimes + } + case common.OrderColumnDraw: + sortFunc = func(i, j int) bool { + if players[i].DrawTimes == players[j].DrawTimes { + return false + } + return players[i].DrawTimes > players[j].DrawTimes + } + case common.OrderColumnWinCoin: + sortFunc = func(i, j int) bool { + if players[i].WinCoin == players[j].WinCoin { + return false + } + return players[i].WinCoin > players[j].WinCoin + } + case common.OrderColumnLoseCoin: + sortFunc = func(i, j int) bool { + if players[i].FailCoin == players[j].FailCoin { + return false + } + return players[i].FailCoin > players[j].FailCoin + } + default: + } + if sortFunc != nil { + sort.Slice(players, func(i, j int) bool { + if orderType == 0 { + return sortFunc(i, j) + } + return !sortFunc(i, j) + }) + } + count = len(players) + if int(end) > count { + end = int32(count) + } + for i := start; i < end; i++ { + p := players[i] + if p != nil { + pb := model.ConvertPlayerDataToWebData(getPlayerDataParam(p.PlayerData, nil)) + if p.scene != nil { + pb.GameFreeId = p.scene.dbGameFree.Id + pb.SceneId = int32(p.scene.sceneId) + } + pack.PlayerData = append(pack.PlayerData, pb) + } + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.PageCount = int32(count) + pack.PageNo = pageNo + pack.PageSize = pageSize + return common.ResponseTag_Ok, pack + })) + ////黑白名单 + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/WhiteBlackControl", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi_proto.SAWhiteBlackControl{} + // msg := &webapi_proto.ASWhiteBlackControl{} + // err := proto.Unmarshal(params, msg) + // pack.Tag = webapi_proto.TagCode_FAILED + // if err != nil { + // pack.Msg = "数据序列化失败" + err.Error() + // return common.ResponseTag_ParamError, pack + // } + // if msg.SnId == 0 { + // pack.Msg = "snid is zero" + // return common.ResponseTag_ParamError, pack + // } + // idMap := make(map[int32]struct{}) + // if msg.SnId > 0 { + // idMap[msg.SnId] = struct{}{} + // } + // for _, id := range msg.SnIds { + // idMap[id] = struct{}{} + // } + // wbCoinLimit := msg.WBCoinLimit + // maxNum := msg.MaxNum + // resetTotalCoin := msg.ResetTotalCoin + // tNow := time.Now() + // var errMsg interface{} + // wg := new(sync.WaitGroup) + // wg.Add(len(idMap)) + // for id := range idMap { + // snid := id + // player := PlayerMgrSington.GetPlayerBySnId(snid) + // if player != nil { + // if len(msg.Platform) == 0 || player.Platform == msg.Platform { + // player.WBLevel = msg.WBLevel + // player.WBCoinLimit = wbCoinLimit + // player.WBMaxNum = maxNum + // if resetTotalCoin == 1 { + // player.WBCoinTotalIn = 0 + // player.WBCoinTotalOut = 0 + // } + // player.WBTime = tNow + // //出去比赛场 + // if player.scene != nil && !player.scene.IsMatchScene() { + // //同步刷到游服 + // wgpack := &server.WGSetPlayerBlackLevel{ + // SnId: proto.Int(int(snid)), + // WBLevel: proto.Int32(msg.WBLevel), + // WBCoinLimit: proto.Int64(wbCoinLimit), + // MaxNum: proto.Int32(maxNum), + // ResetTotalCoin: proto.Bool(resetTotalCoin != 0), + // } + // player.SendToGame(int(server.SSPacketID_PACKET_WG_SETPLAYERBLACKLEVEL), wgpack) + // } + // } + // } + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // if resetTotalCoin == 1 { + // //后台sql有统计这里屏蔽掉 + // //log := model.NewBlackWhiteCoinLog(snid, msg.WBLevel, 0, int32(resetTotalCoin), msg.Platform) + // //if log != nil { + // // model.InsertBlackWhiteCoinLog(log) + // //} + // return model.SetBlackWhiteLevel(snid, msg.WBLevel, int32(maxNum), 0, 0, wbCoinLimit, msg.Platform, tNow) + // } else { + // //后台sql有统计这里屏蔽掉 + // //log := model.NewBlackWhiteCoinLog(snid, msg.WBLevel, wbCoinLimit, int32(resetTotalCoin), msg.Platform) + // //if log != nil { + // // model.InsertBlackWhiteCoinLog(log) + // //} + // return model.SetBlackWhiteLevelUnReset(snid, msg.WBLevel, int32(maxNum), wbCoinLimit, msg.Platform, tNow) + // } + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // defer wg.Done() + // if data == nil { + // delete(idMap, snid) + // } else { + // errMsg = data + // } + // }), "SetBlackWhiteLevel").Start() + // } + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // wg.Wait() + // if errMsg != nil || len(idMap) > 0 { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = fmt.Sprintf("数据库写入错误:%v , 玩家id:%v", errMsg, idMap) + // } else { + // pack.Tag = webapi_proto.TagCode_SUCCESS + // } + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // return nil + // }), nil).Start() + // return common.ResponseTag_TransactYield, pack + // })) + //黑白名单 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/WhiteBlackControl", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAWhiteBlackControl{} + msg := &webapi_proto.ASWhiteBlackControl{} + err := proto.Unmarshal(params, msg) + pack.Tag = webapi_proto.TagCode_FAILED + if err != nil { + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + //if msg.SnId == 0 { + // pack.Msg = "snid is zero" + // return common.ResponseTag_ParamError, pack + //} + idMap := make(map[int32]struct{}) + if msg.SnId > 0 { + idMap[msg.SnId] = struct{}{} + } + for _, id := range msg.SnIds { + idMap[id] = struct{}{} + } + wbCoinLimit := msg.WBCoinLimit + maxNum := msg.MaxNum + resetTotalCoin := msg.ResetTotalCoin + wbState := msg.State + + tNow := time.Now() + var errMsg interface{} + wg := new(sync.WaitGroup) + wg.Add(len(idMap)) + for id := range idMap { + snid := id + player := PlayerMgrSington.GetPlayerBySnId(snid) + if player != nil { + if len(msg.Platform) == 0 || player.Platform == msg.Platform { + player.WBLevel = msg.WBLevel + player.WBCoinLimit = wbCoinLimit + player.WBMaxNum = maxNum + if resetTotalCoin == 1 { + player.WBCoinTotalIn = 0 + player.WBCoinTotalOut = 0 + } + player.WBTime = tNow + //出去比赛场 + if player.scene != nil && !player.scene.IsMatchScene() { + //if !WBCtrlCfgMgr.GetRealCtrl(msg.Platform) { + // wbState = 0 + //} + //同步刷到游服 + wgpack := &server.WGSetPlayerBlackLevel{ + SnId: proto.Int(int(snid)), + WBLevel: proto.Int32(msg.WBLevel), + WBCoinLimit: proto.Int64(wbCoinLimit), + MaxNum: proto.Int32(maxNum), + ResetTotalCoin: proto.Bool(resetTotalCoin != 0), + State: proto.Int32(wbState), + } + player.SendToGame(int(server.SSPacketID_PACKET_WG_SETPLAYERBLACKLEVEL), wgpack) + } + } + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + if resetTotalCoin == 1 { + //后台sql有统计这里屏蔽掉 + //log := model.NewBlackWhiteCoinLog(snid, msg.WBLevel, 0, int32(resetTotalCoin), msg.Platform) + //if log != nil { + // model.InsertBlackWhiteCoinLog(log) + //} + return model.SetBlackWhiteLevel(snid, msg.WBLevel, int32(maxNum), wbState, 0, 0, wbCoinLimit, msg.Platform, tNow) + } else { + //后台sql有统计这里屏蔽掉 + //log := model.NewBlackWhiteCoinLog(snid, msg.WBLevel, wbCoinLimit, int32(resetTotalCoin), msg.Platform) + //if log != nil { + // model.InsertBlackWhiteCoinLog(log) + //} + return model.SetBlackWhiteLevelUnReset(snid, msg.WBLevel, int32(maxNum), wbState, wbCoinLimit, msg.Platform, tNow) + } + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + defer wg.Done() + if data == nil { + delete(idMap, snid) + } else { + errMsg = data + } + }), "SetBlackWhiteLevel").Start() + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + wg.Wait() + if errMsg != nil || len(idMap) > 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("数据库写入错误:%v , 玩家id:%v", errMsg, idMap) + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + return nil + }), nil).Start() + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Ctrl/ListServerStates", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAListServerStates{} + msg := &webapi_proto.ASListServerStates{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.ServerInfo = GameSessMgrSington.ListServerState(int(msg.SrvId), int(msg.SrvType)) + return common.ResponseTag_Ok, pack + })) + //邮件 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/CreateShortMessage", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SACreateShortMessage{} + msg := &webapi_proto.ASCreateShortMessage{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + + title := msg.GetNoticeTitle() + content := msg.GetNoticeContent() + platform := msg.GetPlatform() + srcSnid := msg.GetSrcSnid() + destSnid := msg.GetDestSnid() + messageType := msg.GetMessageType() + coin := msg.GetCoin() + diamond := msg.GetDiamond() + otherParams := msg.GetParams() + showId := msg.GetShowId() + + if messageType == model.MSGTYPE_ITEM && len(otherParams) != 0 && len(otherParams)%2 != 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "otherParams is err" + return common.ResponseTag_ParamError, pack + } + + //由于多线程问题,次数组只能在此处和task.new 内使用,在其它地方使用会引起多线程崩溃的问题 + var onlinePlayerSnid []int32 + if destSnid == 0 { + for _, p := range PlayerMgrSington.snidMap { + if p.IsRob == true { //排除掉机器人 + continue + } + if platform == "" || p.Platform == platform { + onlinePlayerSnid = append(onlinePlayerSnid, p.SnId) + } + } + } + + var newMsg *model.Message + var dbMsgs []*model.Message + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + code := login_proto.OpResultCode_OPRC_Sucess + //如果不是群发则检查一下该用户snid是不是存在,不存在就返回,在这里不关心玩家帐变是否正确,不做校正 + if destSnid != 0 { + playerData, _ := model.GetPlayerDataBySnId(platform, destSnid, true, false) + if playerData == nil { + code = login_proto.OpResultCode_OPRC_Error + return code + } + } + // var otherParams []int32 + + newMsg = model.NewMessage("", int32(srcSnid), "", int32(destSnid), int32(messageType), title, content, coin, diamond, + model.MSGSTATE_UNREAD, time.Now().Unix(), model.MSGATTACHSTATE_DEFAULT, "", otherParams, platform, showId) + if newMsg != nil { + err := model.InsertMessage(platform, newMsg) + if err != nil { + code = login_proto.OpResultCode_OPRC_Error + logger.Logger.Errorf("/api/mongo.games.com/game/CreateShortMessage,InsertMessage err:%v title:%v content:%v platform:%v srcSnid:%v destSnid:%v messageType:%v ", err, title, content, platform, srcSnid, destSnid, messageType) + return code + } + } + + if destSnid == 0 { + for _, psnid := range onlinePlayerSnid { + newMsg := model.NewMessage(newMsg.Id.Hex(), newMsg.SrcId, "", psnid, newMsg.MType, newMsg.Title, newMsg.Content, newMsg.Coin, newMsg.Diamond, + newMsg.State, newMsg.CreatTs, newMsg.AttachState, newMsg.GiftId, otherParams, platform, newMsg.ShowId) + if newMsg != nil { + dbMsgs = append(dbMsgs, newMsg) + } + } + if len(dbMsgs) > 0 { + err := model.InsertMessage(platform, dbMsgs...) + if err != nil { + logger.Logger.Errorf("/api/mongo.games.com/game/CreateShortMessage,InsertMessage err:%v title:%v content:%v platform:%v srcSnid:%v destSnid:%v messageType:%v ", err, title, content, platform, srcSnid, destSnid, messageType) + } + } + } + return code + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if code, ok := data.(login_proto.OpResultCode); ok { + if code != login_proto.OpResultCode_OPRC_Sucess { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + } else { + pp := PlayerMgrSington.GetPlayerBySnId(int32(destSnid)) + if pp != nil { + //单独消息 + pp.AddMessage(newMsg) + } else { + if destSnid == 0 { //群发消息 + MsgMgrSington.AddMsg(newMsg) + + if len(platform) > 0 { //特定渠道消息 + for _, m := range dbMsgs { + pp := PlayerMgrSington.GetPlayerBySnId(m.SnId) + if pp != nil && pp.Platform == platform { + pp.AddMessage(m) + } + } + } else { //全渠道消息 + for _, m := range dbMsgs { + pp := PlayerMgrSington.GetPlayerBySnId(m.SnId) + if pp != nil { + pp.AddMessage(m) + } + } + } + } + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("Marshal CreateShortMessage response data error:", err) + } + } else { + logger.Logger.Error("CreateShortMessage task result data covert failed.") + } + }), "CreateShortMessage").Start() + + return common.ResponseTag_TransactYield, pack + })) + //查询邮件 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/QueryShortMessageList", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAQueryShortMessageList{} + msg := &webapi_proto.ASQueryShortMessageList{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + startTime := msg.GetStartTime() + endTime := msg.GetEndTime() + pageNo := msg.GetPageNo() + pageSize := msg.GetPageSize() + messageType := msg.GetMessageType() + platform := msg.GetPlatform() + destSnid := msg.GetDestSnid() + if destSnid == 0 { + destSnid = -1 + } + start := (pageNo - 1) * pageSize + end := pageNo * pageSize + + var msgs []model.Message + var count int + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + mas := &model.MsgArgs{ + Platform: platform, + StartTs: startTime, + EndTs: endTime, + ToIndex: int(end), + DestSnId: int(destSnid), + MsgType: int(messageType), + FromIndex: int(start), + } + retMsgs, err := model.GetMessageInRangeTsLimitByRange(mas) + count = retMsgs.Count + msgs = retMsgs.Msg + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "" + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + pack.Count = int32(count) + for i := 0; i < len(msgs); i++ { + giftState := int32(0) + mi := &webapi_proto.MessageInfo{ + Id: msgs[i].Id.Hex(), + MType: msgs[i].MType, + Title: msgs[i].Title, + Content: msgs[i].Content, + State: msgs[i].State, + CreateTime: msgs[i].CreatTs, + SrcSnid: msgs[i].SrcId, + DestSnid: msgs[i].SnId, + Coin: msgs[i].Coin, + GiftId: msgs[i].GiftId, + GiftState: giftState, + Platform: msgs[i].Platform, + } + pack.MessageInfo = append(pack.MessageInfo, mi) + } + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "QueryShortMessageList").Start() + return common.ResponseTag_TransactYield, pack + })) + //删除邮件 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/DeleteShortMessage", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SADeleteShortMessage{} + msg := &webapi_proto.ASDeleteShortMessage{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + msgId := msg.GetId() + platform := msg.GetPlatform() + if len(msgId) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "not find msg id" + return common.ResponseTag_ParamError, pack + } + var delMsg *model.Message + var err error + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + delMsg, err = model.GetMessageById(msgId, platform) + if err == nil { + if len(platform) > 0 && msg.Platform != platform { + return errors.New("Platform error.") + } + args := &model.DelMsgArgs{ + Platform: platform, + Id: delMsg.Id, + } + err = model.DelMessage(args) // 真删除 + } + return err + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil || delMsg == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "not find msg id" + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + } + /*if delMsg.State == model.MSGSTATE_REMOVEED { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "repeate delete" + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + }*/ + pp := PlayerMgrSington.GetPlayerBySnId(delMsg.SnId) + if pp != nil { + pp.WebDelMessage(delMsg.Id.Hex()) + } else { + MsgMgrSington.RemoveMsg(delMsg) + } + + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "DeleteShortMessage").Start() + return common.ResponseTag_TransactYield, pack + })) + //删除玩家已经删除的邮件 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/DeleteAllShortMessage", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SADeleteShortMessage{} + msg := &webapi_proto.ASDeleteShortMessage{} + err1 := proto.Unmarshal(params, msg) + if err1 != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err1.Error() + return common.ResponseTag_ParamError, pack + } + platform := msg.GetPlatform() + if len(platform) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "platform is nil" + return common.ResponseTag_ParamError, pack + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.DelAllMessage(&model.DelAllMsgArgs{Platform: platform}) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "del err:" + data.(error).Error() + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "del success." + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "DeleteShortMessage").Start() + return common.ResponseTag_TransactYield, pack + })) + //黑名单 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Player/BlackBySnId", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SABlackBySnId{} + msg := &webapi_proto.ASBlackBySnId{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + player := PlayerMgrSington.GetPlayerBySnId(msg.SnId) + if player != nil { + if player.Platform != msg.Platform { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Platform is dif" + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + player.BlacklistType = msg.BlacklistType + player.dirty = true + player.Time2Save() + //在线需要踢掉玩家 + if uint(msg.BlacklistType)&BlackState_Login != 0 { + logger.Logger.Infof("found platform:%v player:%d snid in blacklist", msg.Platform, player.SnId) + player.Kickout(int32(login_proto.SSDisconnectTypeCode_SSDTC_BlackList)) + } + return common.ResponseTag_Ok, pack + } else { + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + data, _ := model.GetPlayerDataBySnId(msg.Platform, msg.SnId, true, false) + if data != nil { + model.UpdatePlayerBlacklistType(msg.Platform, msg.SnId, msg.BlacklistType) + } + return data + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + playerData, ok := data.(*model.PlayerData) + if !ok || playerData == nil { + pack.Msg = "no find player" + pack.Tag = webapi_proto.TagCode_FAILED + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "success" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "PlayerData").Start() + return common.ResponseTag_TransactYield, pack + } + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Message/QueryHorseRaceLampList", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAQueryHorseRaceLampList{} + msg := &webapi_proto.ASQueryHorseRaceLampList{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + start := (msg.PageNo - 1) * msg.PageSize + end := msg.PageNo * msg.PageSize + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + ret, erro := model.GetHorseRaceLampInRangeTsLimitByRange(msg.Platform, msg.MsgType, msg.State, start, end) + if erro != nil { + logger.Logger.Error("api GetNoticeInRangeTsLimitByRange is error", erro) + return nil + } + return ret + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "no these msg" + } else { + noticesRet := data.(*model.QueryHorseRaceLampRet) + if noticesRet != nil { + var apiNoticMsgList []*webapi_proto.HorseRaceLamp + for _, notice := range noticesRet.Data { + apiNoticeMsg := &webapi_proto.HorseRaceLamp{ + Id: notice.Id.Hex(), + Platform: notice.Platform, + Title: notice.Title, + Content: notice.Content, + Footer: notice.Footer, + StartTime: notice.StartTime, + Frequency: notice.Interval, + State: notice.State, + CreateTime: notice.CreateTime, + Count: notice.Count, + Priority: notice.Priority, + MsgType: notice.MsgType, + Target: notice.Target, + StandSec: notice.StandSec, + } + apiNoticMsgList = append(apiNoticMsgList, apiNoticeMsg) + } + pack.PageCount = int32(noticesRet.Count) + pack.HorseRaceLamp = apiNoticMsgList + } + pack.Tag = webapi_proto.TagCode_SUCCESS + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("Marshal notice msg list error:", err) + } + }), "QueryHorseRaceLampList").Start() + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Message/CreateHorseRaceLamp", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SACreateHorseRaceLamp{} + msg := &webapi_proto.ASCreateHorseRaceLamp{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + platform := msg.Platform + title := msg.Title + content := msg.Content + footer := msg.Footer + count := msg.Count + state := msg.State + startTime := msg.StartTime + priority := msg.Priority + msgType := msg.MsgType + standSec := msg.StandSec + target := msg.Target + var horseRaceLamp *model.HorseRaceLamp + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + horseRaceLamp = model.NewHorseRaceLamp("", platform, title, content, footer, startTime, standSec, count, + priority, state, msgType, target, standSec) + return model.InsertHorseRaceLamp(platform, horseRaceLamp) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = data.(error).Error() + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + } + HorseRaceLampMgrSington.AddHorseRaceLampMsg(horseRaceLamp.Id.Hex(), "", platform, title, content, footer, startTime, standSec, + count, msgType, state, priority, horseRaceLamp.CreateTime, target, standSec) + pack.Tag = webapi_proto.TagCode_SUCCESS + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "CreateHorseRaceLamp").Start() + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Message/GetHorseRaceLampById", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAGetHorseRaceLampById{} + msg := &webapi_proto.ASGetHorseRaceLampById{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + noticeKey := msg.NoticeId + platform := msg.Platform + horseRaceLamp := HorseRaceLampMgrSington.HorseRaceLampMsgList[noticeKey] + if horseRaceLamp == nil || (len(platform) > 0 && horseRaceLamp.Platform != platform) { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "not find data" + } else { + pack.HorseRaceLamp = &webapi_proto.HorseRaceLamp{ + Id: noticeKey, + Title: horseRaceLamp.Title, + Content: horseRaceLamp.Content, + Footer: horseRaceLamp.Footer, + StartTime: horseRaceLamp.StartTime, + Frequency: horseRaceLamp.Interval, + Count: horseRaceLamp.Count, + State: horseRaceLamp.State, + CreateTime: horseRaceLamp.CreateTime, + Priority: horseRaceLamp.Priority, + MsgType: horseRaceLamp.MsgType, + } + pack.Tag = webapi_proto.TagCode_SUCCESS + } + return common.ResponseTag_Ok, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Message/EditHorseRaceLamp", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAEditHorseRaceLamp{} + msg := &webapi_proto.ASEditHorseRaceLamp{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + noticeKey := msg.HorseRaceLamp.Id + if len(noticeKey) == 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "NoticeMsg id is nil" + return common.ResponseTag_ParamError, pack + } + hrl := msg.HorseRaceLamp + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + notice, err2 := model.GetHorseRaceLamp(hrl.Platform, bson.ObjectIdHex(noticeKey)) + if err2 != nil { + logger.Logger.Error("api GetNotice is error", err2) + return nil + } + model.EditHorseRaceLamp(&model.HorseRaceLamp{ + Id: bson.ObjectIdHex(noticeKey), + Channel: "", + Title: hrl.Title, + Content: hrl.Content, + Footer: hrl.Footer, + StartTime: hrl.StartTime, + Interval: hrl.Frequency, + Count: hrl.Count, + CreateTime: hrl.CreateTime, + Priority: hrl.Priority, + MsgType: hrl.MsgType, + Platform: hrl.Platform, + State: hrl.State, + Target: hrl.Target, + StandSec: hrl.StandSec, + }) + return notice + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "api GetNotice is error" + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("Marshal EditHorseRaceLamp response data error1:", err) + } + return + } else { + cache := HorseRaceLampMgrSington.EditHorseRaceLampMsg(&HorseRaceLamp{ + Key: noticeKey, + Channel: "", + Title: hrl.Title, + Content: hrl.Content, + Footer: hrl.Footer, + StartTime: hrl.StartTime, + Interval: hrl.Frequency, + Count: hrl.Count, + CreateTime: hrl.CreateTime, + Priority: hrl.Priority, + MsgType: hrl.MsgType, + Platform: hrl.Platform, + State: hrl.State, + Target: hrl.Target, + StandSec: hrl.StandSec, + }) + if !cache { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "api EditNoticeMsg is error" + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + } + } + pack.Tag = webapi_proto.TagCode_SUCCESS + tNode.TransRep.RetFiels = pack + tNode.Resume() + if err != nil { + logger.Logger.Error("Marshal EditHorseRaceLamp response data error3:", err) + } + }), "EditHorseRaceLamp").Start() + + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Message/RemoveHorseRaceLampById", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SARemoveHorseRaceLampById{} + msg := &webapi_proto.ASRemoveHorseRaceLampById{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + noticeKey := msg.GetHorseRaceId() + platform := msg.GetPlatform() + notice := HorseRaceLampMgrSington.HorseRaceLampMsgList[noticeKey] + if notice == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "not find data" + return common.ResponseTag_Ok, pack + } + if len(platform) > 0 && notice.Platform != notice.Platform { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "not find data" + return common.ResponseTag_Ok, pack + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.RemoveHorseRaceLamp(platform, noticeKey) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "RemoveNotice is error" + data.(error).Error() + tNode.TransRep.RetFiels = pack + tNode.Resume() + return + } + HorseRaceLampMgrSington.DelHorseRaceLampMsg(noticeKey) + pack.Tag = webapi_proto.TagCode_SUCCESS + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "ResponseTag_TransactYield").Start() + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Ctrl/ResetEtcdData", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAResetEtcdData{} + msg := &webapi_proto.ASResetEtcdData{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + EtcdMgrSington.Reset() + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "Etcd Reset success" + return common.ResponseTag_Ok, nil + })) + //WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/SinglePlayerAdjust", WebAPIHandlerWrapper( + // func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + // pack := &webapi_proto.SASinglePlayerAdjust{} + // msg := &webapi_proto.ASSinglePlayerAdjust{} + // err2 := proto.Unmarshal(params, msg) + // if err2 != nil { + // fmt.Printf("err:%v", err2) + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "数据序列化失败" + // return common.ResponseTag_ParamError, pack + // } + // ////////////////////验证玩家/////////////////// + // snid := msg.PlayerSingleAdjust.SnId + // platform := msg.PlayerSingleAdjust.Platform + // p := PlayerMgrSington.GetPlayerBySnId(snid) + // if p == nil { + // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // pb, _ := model.GetPlayerDataBySnId(platform, snid, false, false) + // return pb + // }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // if data == nil || data.(*model.PlayerData) == nil { + // pack.Tag = webapi_proto.TagCode_FAILED + // pack.Msg = "not find player." + // } else { + // pack.Tag = webapi_proto.TagCode_SUCCESS + // pack.Msg = "success" + // sa := PlayerSingleAdjustMgr.WebData(msg, &Player{PlayerData: data.(*model.PlayerData)}) + // if sa != nil { + // pack.PlayerSingleAdjust = sa + // } + // } + // tNode.TransRep.RetFiels = pack + // tNode.Resume() + // }), "SinglePlayerAdjust").Start() + // return common.ResponseTag_TransactYield, pack + // } else { + // pack.Tag = webapi_proto.TagCode_SUCCESS + // pack.Msg = "success" + // sa := PlayerSingleAdjustMgr.WebData(msg, p) + // if sa != nil { + // pack.PlayerSingleAdjust = sa + // } + // } + // return common.ResponseTag_Ok, pack + // })) + // CreateJYB 创建礼包码 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/CreateJYB", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SACreateJYB{} + msg := &webapi_proto.ASCreateJYB{} + + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + if msg.CodeType == 1 && (msg.Code == "" || len(msg.Code) > 11) { + pack.Tag = webapi_proto.TagCode_JYB_DATA_ERROR + pack.Msg = "CreateJYB Code failed" + return common.ResponseTag_Ok, pack + } + // (plt, name, content string, startTs, endTs int64, max int32, award *JybInfoAward) + award := &model.JybInfoAward{} + if msg.GetAward() != nil { + award.Coin = msg.GetAward().Coin + award.Diamond = msg.GetAward().Diamond + if msg.GetAward().GetItemId() != nil { + for _, item := range msg.GetAward().ItemId { + if v := srvdata.PBDB_GameItemMgr.GetData(item.ItemId); item != nil { + if item.ItemNum == 0 { + pack.Tag = webapi_proto.TagCode_JYB_DATA_ERROR + pack.Msg = "ItemNum failed" + return common.ResponseTag_Ok, pack + } + award.Item = append(award.Item, &model.Item{ + ItemId: v.Id, + ItemNum: item.ItemNum, + ObtainTime: time.Now().Unix(), + }) + } else { + pack.Tag = webapi_proto.TagCode_JYB_DATA_ERROR + pack.Msg = "ItemId failed" + return common.ResponseTag_Ok, pack + } + } + } + } + jyb := model.NewJybInfo(msg.Platform, msg.Name, msg.Content, msg.Code, msg.StartTime, msg.EndTime, msg.Max, msg.CodeType, award) + args := &model.CreateJyb{ + JybInfo: jyb, + Codelen: msg.CodeLen, + Num: uint64(msg.Max), + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.CreateJybInfo(args) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "CreateJYB success" + } else { + logger.Logger.Trace("err: ", data.(error)) + if data.(error) == model.ErrJYBCode { + pack.Tag = webapi_proto.TagCode_JYB_CODE_EXIST + } else { + pack.Tag = webapi_proto.TagCode_FAILED + } + pack.Msg = "CreateJYB failed" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "webCreateJybInfo").Start() + return common.ResponseTag_TransactYield, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/UpdateJYB", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateJYB{} + msg := &webapi_proto.ASUpdateJYB{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + + if msg.Opration == 1 { + args := &model.GetJybInfoArgs{ + Id: msg.JYBID, + Plt: msg.Platform, + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + return model.DelJybInfo(args) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "UpdateJYB success" + + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "UpdateJYB failed" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "webDelJybInfo").Start() + return common.ResponseTag_TransactYield, pack + } + + return common.ResponseTag_Ok, pack + })) + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Customer/UpExchangeStatus", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpExchangeStatus{} + msg := &webapi_proto.ASUpExchangeStatus{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + snid := msg.Snid + platform := msg.Platform + player := PlayerMgrSington.GetPlayerBySnId(snid) + if msg.Status != Shop_Status_Revoke { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Status is Revoke" + return common.ResponseTag_ParamError, pack + } + //cdata := ShopMgrSington.GetExchangeData(msg.GoodsId) + item := srvdata.PBDB_GameItemMgr.GetData(VCard) + if item == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "item is nil" + return common.ResponseTag_ParamError, pack + } + addvcoin := msg.NeedNum + jPrice := msg.JPrice + remark := fmt.Sprintf("兑换撤单 %v-%v", msg.GoodsId, msg.Name) + if player != nil { + // 在线 + var items []*Item + //V卡 + if addvcoin > 0 { + items = append(items, &Item{ItemId: VCard, ItemNum: int64(addvcoin)}) + } + //金券 + if jPrice > 0 { + items = append(items, &Item{ItemId: JCard, ItemNum: int64(jPrice)}) + } + if _, code := BagMgrSingleton.AddJybBagInfo(player, items, 0, common.GainWay_Exchange, "system", remark); code != bag.OpResultCode_OPRC_Sucess { // 领取失败 + logger.Logger.Errorf("UpExchangeStatus AddJybBagInfo err", code) + pack.Msg = "AddJybBagInfo err" + return common.ResponseTag_ParamError, pack + } + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "UpExchange success" + BagMgrSingleton.RecordItemLog(player.Platform, player.SnId, ItemObtain, VCard, item.Name, int64(addvcoin), remark) + return common.ResponseTag_Ok, pack + } else { + var findPlayer *model.PlayerBaseInfo + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + findPlayer = model.GetPlayerBaseInfo(platform, snid) + if findPlayer == nil { + pack.Tag = webapi_proto.TagCode_Play_NotEXIST + pack.Msg = fmt.Sprintf("player is not exist %v", snid) + return errors.New("player is not exist") + } + newBagInfo := &model.BagInfo{ + SnId: findPlayer.SnId, + Platform: findPlayer.Platform, + BagItem: make(map[int32]*model.Item), + } + if addvcoin > 0 { + newBagInfo.BagItem[VCard] = &model.Item{ItemId: VCard, ItemNum: int64(addvcoin)} + } + if jPrice > 0 { + newBagInfo.BagItem[JCard] = &model.Item{ItemId: JCard, ItemNum: int64(jPrice)} + } + + return model.SaveDBBagItem(newBagInfo) + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data == nil && findPlayer != nil { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "UpExchange success" + BagMgrSingleton.RecordItemLog(findPlayer.Platform, findPlayer.SnId, ItemObtain, VCard, item.Name, int64(addvcoin), remark) + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "UpExchange failed:" + data.(error).Error() + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "UpExchange").Start() + return common.ResponseTag_TransactYield, pack + } + })) + //更新用户三方的金币到游戏服务器 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/thd/UpdatePlayerCoin", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + msg := &webapi_proto.ASThdUpdatePlayerCoin{} + pack := &webapi_proto.SAThdUpdatePlayerCoin{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + member_snid := msg.Snid + BaseGameId := int(msg.BaseGameID) + platform := msg.Platform + + plt := win88webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(BaseGameId) + if plt == nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "三方不存在" + return common.ResponseTag_ParamError, pack + } + + p := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) + + if p != nil { + if len(platform) > 0 && p.Platform != platform { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "Platform is err." + return common.ResponseTag_ParamError, pack + } + //请求太快,不做处理,给API减轻一些压力 + if p.thridBalanceRefreshReqing { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "刷新频率太高,稍等" + return common.ResponseTag_ParamError, pack + } + + p.thridBalanceRefreshReqing = true + gainway := common.GainWay_Transfer_Thrid2System + //isSucces := true + timeout := false + timeStamp := time.Now().UnixNano() + noBaseGameId := false + pfConfig := PlatformMgrSingleton.GetPlatform(p.Platform) + if pfConfig != nil { + if pfConfig.ThirdGameMerchant == nil || pfConfig.ThirdGameMerchant[int32(plt.GetPlatformBase().BaseGameID)] == 0 { + // noBaseGameId = true + } + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + var err error + var coinLog *model.PayCoinLog + var coinlogex *model.CoinLog + //var apiHasTransfer = false + remark := "手动刷新" + plt.GetPlatformBase().Name + "转出到系统" + amount := int64(0) + + if pfConfig == nil { + return int64(-2) + } + + if noBaseGameId { + return int64(-3) + } + + oper := plt.GetPlatformBase().Name + "2System" + err, amount = plt.ReqLeaveGame(p.SnId, fmt.Sprintf("%v", BaseGameId), p.Ip, p.Platform, p.Channel) + if err != nil { + goto Rollback + } + if amount <= 0 { + return int64(-4) + } + if plt.GetPlatformBase().TransferInteger { + amount = (amount / 100) * 100 + if amount <= 0 { + return int64(-4) + } + } + + //apiHasTransfer = true + coinLog = model.NewPayCoinLog(time.Now().UnixNano(), int32(p.SnId), amount, int32(gainway), oper, model.PayCoinLogType_Coin, 0) + + // err = model.InsertPayCoinLogs(coinLog) + err = model.InsertPayCoinLogs(p.Platform, coinLog) + if err != nil { + logger.Logger.Tracef("player snid=%v at %v model.InsertPayCoinLogs() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + timeStamp = coinLog.TimeStamp + coinlogex = model.NewCoinLogEx(&model.CoinLogParam{ + Platform: p.Platform, + SnID: p.SnId, + ChangeType: common.BillTypeCoin, + ChangeNum: amount, + RemainNum: p.Coin + amount, + Add: 0, + LogType: int32(gainway), + GameID: 0, + GameFreeID: 0, + BaseCoin: 0, + Operator: oper, + Remark: remark, + }) + err = model.InsertCoinLog(coinlogex) + if err != nil { + logger.Logger.Tracef("player snid=%v at %v model.InsertCoinLogs() err: %v", p.SnId, plt.GetPlatformBase().Name, err) + goto Rollback + } + return amount + Rollback: + if coinLog != nil { + + model.RemovePayCoinLog(p.Platform, coinLog.LogId) + } + if coinlogex != nil { + model.RemoveCoinLogOne(coinlogex.Platform, coinlogex.LogId) + } + if timeout { + logger.Logger.Errorf("web player snid=%v CSThridBalanceRefreshHandler transfer %v to %v timeout!", p.SnId, -amount, plt.GetPlatformBase().Name) + return int64(-1) + } + //if apiHasTransfer { + // err, timeout = plt.ReqTransfer(p.SnId, thirdBalance, strconv.FormatInt(time.Now().UnixNano(), 10), p.Platform, p.Channel) + // if timeout { + // logger.Logger.Errorf("web player snid=%v CSThridBalanceRefreshHandler transfer rollback %v to %v timeout!", p.SnId, thirdBalance, plt.GetPlatformBase().Name) + // } + //} + return int64(-1) + }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { + amount := data.(int64) + statePack := &gamehall.SCThridGameBalanceUpdateState{} + if amount < 0 { + pack.Tag = webapi_proto.TagCode_FAILED + // return common.ResponseTag_ParamError, pack + pack.Msg = "刷新金币失败" + if amount == -3 { + pack.Msg = "三方关闭中" + } + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Error_Game + p.thridBalanceReqIsSucces = false + //isSucces = false + logger.Logger.Tracef("player snid=%v at platform=%v CSThridBalanceRefreshHandler third->system transfer fail", p.SnId, plt.GetPlatformBase().Name) + } else if amount > 0 { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "刷新金币成功" + statePack.OpRetCode = gamehall.OpResultCode_Game_OPRC_Sucess_Game + p.thridBalanceReqIsSucces = true + + p.Coin += amount + p.SetPayTs(timeStamp) + ThirdPlatformMgrSington.AddThirdPlatformCoin(p.Platform, plt.GetPlatformBase().Tag, amount) + p.dirty = true + logger.Logger.Tracef("player snid=%v at platform=%v CSThridBalanceRefreshHandler third->system transfer succes", p.SnId, plt.GetPlatformBase().Name) + //if !model.GameParamData.CloseOftenSavePlayerData { + // p.Time2Save() + //} + } + + p.diffData.Coin = -1 + p.SendDiffData() + //statePack := &protocol.SCThridGameBalanceUpdateState{} + //pack := &protocol.SCThridGameBalanceUpdate{} + //if isSucces { + // pack.OpRetCode = protocol.OpResultCode_OPRC_Sucess.Enum() + // p.thridBalanceReqIsSucces = true + // statePack.OpRetCode = protocol.OpResultCode_OPRC_Sucess.Enum() + //} + + p.SendToClient(int(gamehall.GameHallPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATESTATE), statePack) + p.dirty = true + + //pack.Coin = proto.Int64(p.Coin) + //p.SendToClient(int(protocol.MmoPacketID_PACKET_SC_THRIDGAMEBALANCEUPDATE), pack) + if p.thrscene != 0 { + p.thrscene = 0 + p.scene = nil + } + p.thridBalanceRefreshReqing = false + logger.Logger.Tracef("SendToClient() player snid=%v at CSThridBalanceRefreshHandler() pack:%v", p.SnId, pack.String()) + + //dataResp := &common.M2GWebApiResponse{} + //dataResp.Body, _ = resp.Marshal() + tNode.TransRep.RetFiels = pack + tNode.Resume() + + }), "ThrUpdatePlayerCoin").Start() + + } else { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "暂不支持用户不在线刷新" + return common.ResponseTag_ParamError, pack + } + return common.ResponseTag_TransactYield, pack + })) + //支付回调 + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/pay/CallbackPayment", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + msg := &webapi_proto.ASCallbackPayment{} + pack := &webapi_proto.SACallbackPayment{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "数据序列化失败" + err.Error() + return common.ResponseTag_ParamError, pack + } + if msg.OrderId == "" || msg.Platform == "" { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "OrderId == nil || msg.Platform == nil" + return common.ResponseTag_ParamError, pack + } + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + info := model.GetDbShopLog(msg.Platform, msg.OrderId) + if info == nil { + return errors.New("info is nil") + } + if info.State != 0 { + return errors.New("the order state not 0 " + info.LogId.Hex()) + } + var state = msg.State + player := PlayerMgrSington.GetPlayerBySnId(info.SnId) + if player != nil && player.IsOnLine() { + if len(info.Amount) > 0 { + player.AddCoin(int64(info.Amount[0]), 0, info.GainWay, "Callback", info.Remark) + player.AddDiamond(int64(info.Amount[1]), 0, info.GainWay, "Callback", info.Remark) + } + player.AddMoneyPayTotal(int64(info.ConsumeNum)) + player.dirty = true + player.SendDiffData() + info.Amount[2] = player.GetVIPExpByPay(info.ConsumeNum) + + var itemInfo []*player_proto.PayItem + var items []*Item + if info.ItemInfo != nil { + for _, v := range info.ItemInfo { + items = append(items, &Item{ItemId: v.ItemId, ItemNum: v.ItemNum}) + itemInfo = append(itemInfo, &player_proto.PayItem{ + ItemId: v.ItemId, + ItemNum: v.ItemNum, + }) + } + } + BagMgrSingleton.AddJybBagInfo(player, items, 0, info.GainWay, "Callback", info.Remark) + switch info.Remark { + case "BlindBox": + if len(info.OtherParams) > 0 { + player.WelfData.BlindBoxId = info.OtherParams[0] + } else { + logger.Logger.Errorf("CallbackPayment BlindBox OtherParams is nil") + } + case "FirstRecharge": + if len(info.OtherParams) > 0 { + + player.WelfData.FirstPayDay = info.OtherParams[0] + player.WelfData.FirstPayTickets = info.Ts + } else { + logger.Logger.Errorf("CallbackPayment FirstRecharge OtherParams is nil") + } + case "ContinuousPay": + if len(info.OtherParams) > 0 { + + player.WelfData.ContinuousPayDay = info.OtherParams[0] + player.WelfData.ContinuousPayTickets = info.Ts + } else { + logger.Logger.Errorf("CallbackPayment ContinuousPay OtherParams is nil") + } + } + player.UpdatePlayerVipBag(info.ShopId) + player.UpdateShopID(info.ShopId) + + PayGoodsInfo := &player_proto.SCPayGoodsInfo{ + Gold: info.Amount, + Item: itemInfo, + } + proto.SetDefaults(PayGoodsInfo) + player.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_PAYGOODSINFO), PayGoodsInfo) + + TaskSubjectSingleton.Touch(common.TaskTypePay, &TaskData{ + SnId: player.SnId, + Num: int64(info.ConsumeNum), + }) + } else { + if state == 1 { + state = 3 + } + } + err := model.UpdateDbShopState(msg.Platform, msg.OrderId, state) + if err != nil { + logger.Logger.Error("UpdateDbShopState.err:", err) + return err + } + return nil + }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + if data != nil && data.(error) != nil { + info := data.(error) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("%v", info) + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "success" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + }), "CallbackPayment").Start() + return common.ResponseTag_TransactYield, pack + })) + + //======= 资源更新通知 ======= + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/mongo.games.com/game/resource", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAResource{} + msg := &webapi_proto.ASResource{} + err := proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = fmt.Sprintf("err:%v", err.Error()) + return common.ResponseTag_ParamError, pack + } + + // 通知所有玩家 + playerMsg := &player_proto.SCResource{ + Msg: msg.GetMsg(), + } + PlayerMgrSington.BroadcastMessage(int(player_proto.PlayerPacketID_PACKET_SC_RESOURCE), playerMsg) + + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "" + return common.ResponseTag_Ok, pack + })) + + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/player/update_tel", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAUpdateTel{} + msg := &webapi_proto.ASUpdateTel{} + + var err error + err = proto.Unmarshal(params, msg) + if err != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "参数错误" + return common.ResponseTag_ParamError, pack + } + logger.Logger.Tracef( + "WebAPIHandlerMgrSingleton.RegisteWebAPIHandler /api/player/update_tel snid:%v, tel:%v ,platform:%v", msg.GetSnid(), msg.GetTel(), msg.GetPlatform()) + + p := PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) + if p != nil { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "玩家在线,只能离线修改" + return common.ResponseTag_ParamError, pack + } + + var snid int32 + var a *model.Account + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // 手机号是否已经被绑定 + snid, _ = model.GetPlayerTel(msg.GetTel(), msg.GetPlatform(), 0) // tagKey没用了,给0就行 + if snid != 0 { + return nil + } + + a, err = model.GetAccountBySnid(msg.GetPlatform(), msg.GetSnid()) + if err != nil || a == nil { + return nil + } + if a.Tel == "" { + return nil + } + + // 绑定手机号 + if err = model.BindTelAccount(a.AccountId.Hex(), msg.GetTel(), msg.GetPlatform()); err != nil { + logger.Logger.Errorf("BindTelAccount err: %v", err) + return nil + } + + if err = model.UpdatePlayerTel(msg.GetPlatform(), msg.GetSnid(), msg.GetTel()); err != nil { + logger.Logger.Errorf("UpdatePlayerTel err: %v", err) + return nil + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + // 修改错误 + logger.Logger.Errorf("update tel %v err: %v", msg.GetSnid(), err) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "修改失败" + } else if snid != 0 { + // 手机号已经绑定 + pack.Tag = webapi_proto.TagCode_TelExist + pack.Msg = "手机号已使用" + } else if a == nil { + // 手机号已经绑定 + pack.Tag = webapi_proto.TagCode_AccountNotFound + pack.Msg = "账号没找到" + } else if a.Tel == "" { + // 没有绑定手机号 + pack.Tag = webapi_proto.TagCode_TelNotBind + pack.Msg = "原账号没有绑定手机号,请自行绑定" + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "修改成功" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + logger.Logger.Tracef("update tel %v ret: %v", msg.GetSnid(), pack) + })).StartByFixExecutor(fmt.Sprintf("Platform%v", msg.GetPlatform())) + return common.ResponseTag_TransactYield, pack + })) + + WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/player/delete", WebAPIHandlerWrapper( + func(tNode *transact.TransNode, params []byte) (int, proto.Message) { + pack := &webapi_proto.SAPlayerDelete{} + msg := &webapi_proto.ASPlayerDelete{} + + var err error + err = proto.Unmarshal(params, msg) + if err != nil || msg.Snid <= 0 { + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "参数错误" + return common.ResponseTag_ParamError, pack + } + logger.Logger.Tracef("/api/player/delete %v", msg) + + // 踢掉玩家 + p := PlayerMgrSington.GetPlayerBySnId(msg.GetSnid()) + if p != nil { + ls := LoginStateMgrSington.GetLoginStateBySid(p.sid) + if ls != nil { + if ls.als != nil && ls.als.acc != nil { + ls.als.acc.State = time.Now().AddDate(0, 0, 1).Unix() + } + } + + p.Kickout(common.KickReason_Freeze) + PlayerMgrSington.DelPlayer(p.SnId) + LoginStateMgrSington.DelAccountByAccid(p.AccountId) + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + pd, _ := model.GetPlayerDataBySnId(msg.Platform, msg.Snid, false, false) + if pd == nil || pd.SnId == 0 { + err = errors.New("player not found") + return nil + } + // 冻结 + err = model.FreezeAccount(pd.Platform, pd.AccountId, 24*60) + if err != nil { + return nil + } + // 备份账号 + var account *model.Account + account, err = model.GetAccount(pd.Platform, pd.AccountId) + if err != nil { + return nil + } + err = model.SaveToDelBackupAccount(account) + if err != nil { + return nil + } + //if !model.SaveDelBackupPlayerData(pd) { + // err = errors.New("SaveDelBackupPlayerData failed") + // return nil + //} + // 删除账号 + err = model.RemoveAccount(pd.Platform, pd.AccountId) + if err != nil { + return nil + } + err = model.ResetPlayer(pd.Platform, pd.SnId) + if err != nil { + return nil + } + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + if err != nil { + logger.Logger.Errorf("player delete %v err: %v", msg, err) + pack.Tag = webapi_proto.TagCode_FAILED + pack.Msg = "删除失败" + } else { + pack.Tag = webapi_proto.TagCode_SUCCESS + pack.Msg = "删除成功" + } + tNode.TransRep.RetFiels = pack + tNode.Resume() + logger.Logger.Tracef("player delete %v ret: %v", msg.GetSnid(), pack) + })).StartByExecutor(fmt.Sprint(msg.GetSnid())) + return common.ResponseTag_TransactYield, pack + })) +} + +func getPlayerDataParam(p *model.PlayerData, season *model.PlayerRankSeason) *model.WebPlayerDataParam { + if p == nil { + return nil + } + + data := PlatformMgrSingleton.Get(p.Platform).PlayerPool + + rankScore := make(map[int64]int64) + if season == nil { + rankScore = RankMgrSingleton.GetPlayerRankScoreInt64(p.SnId) + } else { + for k, v := range season.RankType { + if v != nil { + rankScore[int64(k)] = v.Score + } + } + } + //人物加成 + award := int32(0) + level := p.Roles.ModUnlock[2000002] + if level > 0 { + roleInfo := srvdata.RolePetMgrSington.GetRoleByRoleIdAndLevel(2000002, level) + if roleInfo != nil { + award = roleInfo.Award + } + } + vipAdded := VipMgrSington.GetVipDiamondExtra(p.Platform, p.VIP) + var vipExp int64 + vipConfig := VipMgrSington.GetVIPcfg(p.Platform) + if vipConfig != nil { + vipExp = int64(float64(p.MoneyPayTotal) * vipConfig.MoneyRatio) + } + ret := &model.WebPlayerDataParam{ + PlayerData: p, + RankScore: rankScore, + PlayerPoolUpper: p.GetPoolUpper(data), + PlayerPoolLower: p.GetPoolLower(data), + PlayerPoolCurrent: p.GetPoolCurrent(), + PlayerPoolOdds: p.GetPoolOdds(data), + RoleAdded: int64(award), + VipAdded: int64(vipAdded), + VipExp: vipExp, + } + + return ret +} diff --git a/worldsrv/trascate_webapi_club.go b/worldsrv/trascate_webapi_club.go new file mode 100644 index 0000000..dec7b06 --- /dev/null +++ b/worldsrv/trascate_webapi_club.go @@ -0,0 +1,1198 @@ +package main + +// +//import ( +// "mongo.games.com/game/proto" +// "encoding/json" +// "errors" +// "fmt" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/protocol" +// "mongo.games.com/game/srvdata" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core/basic" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/task" +// "mongo.games.com/goserver/core/transact" +// "sort" +// "strconv" +// "strings" +// "time" +//) +// +////俱乐部相关提供给后台的API +//func init() { +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部创建者的基本信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubCreatorBaseInfo", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// snid_int, _ := params_data.GetInt("Snid") +// snid := int32(snid_int) +// //判断参数是否合法 +// if snid <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Snid value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// //检测平台参数 +// if len(plt_id) != 0 { +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// } +// +// creatorClubMaxLevel := int32(0) +// platformId := "" +// for _, v := range clubManager.clubList { +// if v.Owner == snid { +// if v.Level > creatorClubMaxLevel { +// platformId = v.Platform +// creatorClubMaxLevel = v.Level +// } +// } +// } +// if creatorClubMaxLevel == 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = "Can't find player." +// resp[webapi.RESPONSE_DATA] = "" +// } else { +// resp_data := make(map[string]interface{}) +// pf := PlatformMgrSingleton.GetPlatform(platformId) +// if pf != nil { +// p := PlayerMgrSington.GetPlayerBySnId(snid) +// if p != nil { +// resp_data["ClubCoin"] = p.ClubCoin +// resp_data["ClubMaxLevel"] = creatorClubMaxLevel +// resp_data["GiveRate"] = pf.ClubConfig.GiveCoinRate[creatorClubMaxLevel-1] +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = resp_data +// return common.ResponseTag_Ok, resp +// } else { +// async := true +// OfflinePlayerMgrSington.GetOfflinePlayer(snid, func(op *OfflinePlayer, bAsync bool) { +// async = bAsync +// resp_data["ClubCoin"] = op.ClubCoin +// resp_data["ClubMaxLevel"] = creatorClubMaxLevel +// resp_data["GiveRate"] = pf.ClubConfig.GiveCoinRate[creatorClubMaxLevel-1] +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = resp_data +// dataResp := &common.M2GWebApiResponse{} +// var err error +// dataResp.Body, err = resp.Marshal() +// if err == nil { +// if bAsync { +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// } +// } +// }, false) +// if !async { +// return common.ResponseTag_Ok, resp +// } else { +// return common.ResponseTag_TransactYield, resp +// } +// } +// } +// } +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部大纲信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubOutline", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") +// club_id, _ := params_data.GetInt("ClubID") +// +// //检测平台参数 +// if len(plt_id) != 0 { +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// } +// +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// data := model.ClubOutline{} +// if club == nil { +// //查一个平台下的数据 +// //通过俱乐部的平台找到这个平台下的所有场景 +// club_scenes := ClubSceneMgrSington.GetPlatformClub(plt_id) +// if club_scenes == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltID have no scenes info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// for _, v := range club_scenes { +// data.PlayerPlayingNum += int32(len(v.players)) +// data.RoomPlayingNum += int32(len(v.sceneList)) +// } +// } else { +// //看是否属于该平台 +// if club.Platform != plt_id { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltID no have this ClubID error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// //通过俱乐部的平台找到这个平台下的所有场景 +// club_scenes := ClubSceneMgrSington.GetPlatformClub(club.Platform) +// if club_scenes == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID have no plt info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //根据俱乐部ID找到该俱乐部下的ClubScenePool信息 +// csp := club_scenes[club.Id] +// if csp == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID have no ClubScenePool info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// if len(plt_id) == 0 && club_id == 0 { +// data.PlayerPlayingNum = int32(len(csp.players)) +// data.RoomPlayingNum = int32(len(csp.sceneList)) +// } +// } +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = data +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取多个俱乐部信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubSet", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") +// club_id, _ := params_data.GetInt("ClubID") +// page_size, _ := params_data.GetInt("PageSize") +// page_num, _ := params_data.GetInt("PageNum") +// +// //判断参数 +// if page_size == 0 || page_num == 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PageSize or PageNum arg must hava a value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// //检测平台参数 +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// clubArr := make([]model.ClubItem, 0) +// var club *ClubInst +// +// //如果有俱乐部ID参数的就取一个 +// if club_id != 0 { +// club = clubManager.clubList[int32(club_id)] +// if club != nil && club.Platform == plt_id { +// club_item := model.ClubItem{ +// ClubId: club.Id, +// PltID: club.Platform, +// ClubName: club.Name, +// Level: club.Level, +// CreatorSnid: club.Owner, +// MemberNum: club.GetMemberSum(), +// MaxMemberNum: plt.ClubConfig.ClubInitPlayerNum, +// OtherPumpRate: club.Setting.Taxes, +// CreateTime: club.CreateTs, +// } +// clubArr = append(clubArr, club_item) +// } +// } else { +// //如果没有俱乐部ID参数,就取该平台下的全部俱乐部信息 +// for _, v := range clubManager.clubList { +// if v.Platform == plt_id { +// club_item := model.ClubItem{ +// ClubId: v.Id, +// PltID: v.Platform, +// ClubName: v.Name, +// Level: v.Level, +// CreatorSnid: v.Owner, +// MemberNum: v.GetMemberSum(), +// MaxMemberNum: plt.ClubConfig.ClubInitPlayerNum, +// OtherPumpRate: v.Setting.Taxes, +// CreateTime: v.CreateTs, +// } +// clubArr = append(clubArr, club_item) +// } +// } +// } +// +// //开始做分页 +// start := (page_num - 1) * page_size +// end := page_num * page_size +// count := len(clubArr) +// if count < start { +// return common.ResponseTag_Ok, resp +// } +// if end > count { +// end = count +// } +// sort.Slice(clubArr, func(i, j int) bool { +// return clubArr[i].ClubId < clubArr[j].ClubId +// }) +// r := clubArr[start:end] +// resp["PageCount"] = count +// resp["PageNo"] = page_num +// resp["PageSize"] = page_size +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = r +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //刷新待审核的俱乐部信息 +// //接受后台俱乐部审核信息的刷新 +// //用途:游服给后台发送审核失败后,后台查不到该条俱乐部的待审核记录,这时后台可以通过该接口刷新一下。 +// //该接口会触发游服将未审核的俱乐部再次重新发送到后台。 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/RefreshWaitCheckClubInfo", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// +// waitCreateCheckClubArr := make([]*model.Club, 0) +// waitNoticeCheckClubArr := make([]*model.Club, 0) +// for _, v := range clubManager.clubList { +// if !v.CreateCheckPosted { +// waitCreateCheckClubArr = append(waitCreateCheckClubArr, v.Club) +// } +// if !v.NoticeCheckPosted { +// waitNoticeCheckClubArr = append(waitCreateCheckClubArr, v.Club) +// } +// } +// needPostCount := len(waitCreateCheckClubArr) + len(waitNoticeCheckClubArr) +// retdata := make(map[string]interface{}) +// retdata["PushCount"] = needPostCount +// if needPostCount != 0 { +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// for _, v := range waitCreateCheckClubArr { +// if err := webapi.API_ClubCreateWaitCheck(common.GetAppId(), v.Id, v.Owner, v.Platform, v.Name, v.Billboard); err == nil { +// v.CreateCheckPosted = true +// } +// } +// for _, v := range waitNoticeCheckClubArr { +// if err := webapi.API_ClubNoticeWaitCheck(common.GetAppId(), v.Id, v.Owner, v.Platform, v.Name, v.Billboard); err == nil { +// v.NoticeCheckPosted = true +// } +// } +// return nil +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = retdata +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// }), "RefreshWaitCheckClubInfo").Start() +// return common.ResponseTag_TransactYield, resp +// } else { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = retdata +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// } +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部下面的基础信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubBaseInfo", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// club_id, _ := params_data.GetInt("ClubID") +// //判断参数是否合法 +// if club_id <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID is not exist error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //检查一下俱乐部ID和平台是否对应 +// if club.Platform != plt_id { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltId ClubID is not match error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //通过俱乐部的平台找到这个平台下的所有场景 +// club_scenes := ClubSceneMgrSington.GetPlatformClub(club.Platform) +// data := &model.ClubBaseInfo{ +// ClubId: club.Id, +// ClubName: club.Name, +// Level: club.Level, +// CreatorSnid: club.Owner, +// CreateTs: club.CreateTs, +// MemberNum: club.GetMemberSum(), +// PumpRate: club.Setting.Taxes, +// PlayingRoomsNum: 0, +// JoinPlayerNum: 0, +// Billboard: club.Billboard, +// } +// +// //根据俱乐部ID找到该俱乐部下的ClubScenePool信息 +// if club_scenes == nil { +// data.PlayingRoomsNum = 0 +// data.JoinPlayerNum = 0 +// } else { +// data.PlayingRoomsNum = 0 +// data.JoinPlayerNum = 0 +// for _, v := range club_scenes { +// if v.clubId == int32(club_id) { +// data.PlayingRoomsNum = int32(len(v.players)) +// data.JoinPlayerNum = int32(len(v.sceneList)) +// break +// } +// } +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = data +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部下面的成员信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubMemberSet", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// club_id, _ := params_data.GetInt("ClubID") +// page_size, _ := params_data.GetInt("PageSize") +// page_num, _ := params_data.GetInt("PageNum") +// +// //判断参数 +// if page_size == 0 || page_num == 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PageSize or PageNum arg must hava a value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断参数是否合法 +// if club_id <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID is not exist error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //检查一下俱乐部ID和平台是否对应 +// if club.Platform != plt_id { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltId and ClubID is not match error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// clubMemberArr := make([]model.ClubMemberItem, 0) +// for _, v := range club.memberList { +// item := model.ClubMemberItem{ +// Snid: v.SnId, +// Position: int32(v.Position), +// IsBlack: v.IsBlack, +// JoinClubTimeTs: v.LastTime, +// } +// clubMemberArr = append(clubMemberArr, item) +// } +// //开始做分页 +// start := (page_num - 1) * page_size +// end := page_num * page_size +// count := len(clubMemberArr) +// if count < start { +// return common.ResponseTag_Ok, resp +// } +// if end > count { +// end = count +// } +// //根据加入俱乐部的时间倒序 +// sort.Slice(clubMemberArr, func(i, j int) bool { +// return clubMemberArr[i].JoinClubTimeTs > clubMemberArr[j].JoinClubTimeTs +// }) +// r := clubMemberArr[start:end] +// resp["PageCount"] = count +// resp["PageNo"] = page_num +// resp["PageSize"] = page_size +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = r +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取玩家“名下的俱乐部信息”和“玩家加入的俱乐部信息”包含已经解散的俱乐部(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/PlayerClubRelation", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// snid, _ := params_data.GetInt("Snid") +// +// //判断参数是否合法 +// if snid <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Snid value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //检测平台参数 +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// type PlayerClub struct { +// Snid int32 +// ClubId int32 +// State int32 //state=-1为解散,state=1为正常 +// } +// ownerClubs := make([]PlayerClub, 0) +// joinClubs := make([]PlayerClub, 0) +// +// //获取内存中的数据 +// for _, v := range clubManager.clubList { +// if v.Platform == plt_id { +// if v.Owner == int32(snid) { +// ownerClubs = append(ownerClubs, PlayerClub{ +// Snid: v.Owner, +// ClubId: v.Id, +// State: 1, +// }) +// } +// for _, m := range v.memberList { +// if m.SnId == int32(snid) { +// joinClubs = append(joinClubs, PlayerClub{ +// Snid: m.SnId, +// ClubId: m.ClubId, +// State: 1, +// }) +// break +// } +// } +// } +// } +// +// //获取日志中的数据 +// var clublog []*model.ClubLog +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// clublog = model.GetAllClubLogDataBySnid(int32(snid)) +// return nil +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// for _, v := range clublog { +// if v.PltId == plt_id { +// if v.LogType == model.ClubLogType_Dismiss { +// ownerClubs = append(ownerClubs, PlayerClub{ +// Snid: v.SnId, +// ClubId: v.ClubId, +// State: -1, +// }) +// } +// if v.LogType == model.ClubLogType_PassiveDismiss { +// ownerClubs = append(ownerClubs, PlayerClub{ +// Snid: v.SnId, +// ClubId: v.ClubId, +// State: -1, +// }) +// } +// } +// } +// r := make(map[string]interface{}) +// r["ownerClubs"] = ownerClubs +// r["joinClubs"] = joinClubs +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = r +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// }), "PlayerClubRelation").Start() +// return common.ResponseTag_TransactYield, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部下面的游戏包间信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubAreaSet", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// club_id, _ := params_data.GetInt("ClubID") +// page_size, _ := params_data.GetInt("PageSize") +// page_num, _ := params_data.GetInt("PageNum") +// +// //判断参数是否合法 +// if club_id <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID is not exist error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //检查一下俱乐部ID和平台是否对应 +// if club.Platform != plt_id { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltId and ClubID is not match error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// clubAreaArr := make([]*model.ClubAreaItem, 0) +// for _, r := range club.RoomGroup { +// item := &model.ClubAreaItem{ +// AreaID: r.ClubRoomId, +// GameName: srvdata.PBDB_GameFreeMgr.GetData(int32(r.GameFreeId)).GetName(), +// BaseScore: r.BaseScore, +// PlayingRoomsNum: 0, +// CurrentRound: 0, +// JoinPlayerNum: 0, +// Ts: r.Ts, +// } +// clubAreaArr = append(clubAreaArr, item) +// } +// +// club_scene := ClubSceneMgrSington.GetPlatformClub(club.Platform) +// var csp *ClubScenePool +// if club_scene == nil { +// goto Next +// } +// for _, value := range club_scene { +// if value.clubId == club.Id { +// csp = value +// break +// } +// } +// if csp == nil { +// goto Next +// } +// for _, v := range clubAreaArr { +// for _, s := range csp.sceneList { +// if s.clubRoomID == v.AreaID { +// v.JoinPlayerNum += int32(len(s.players)) +// v.PlayingRoomsNum++ +// v.CurrentRound = s.currRound +// } +// } +// } +// Next: +// //开始做分页 +// start := (page_num - 1) * page_size +// end := page_num * page_size +// count := len(clubAreaArr) +// if count < start { +// return common.ResponseTag_Ok, resp +// } +// if end > count { +// end = count +// } +// sort.Slice(clubAreaArr, func(i, j int) bool { +// return clubAreaArr[i].Ts > clubAreaArr[j].Ts +// }) +// r := clubAreaArr[start:end] +// resp["PageCount"] = count +// resp["PageNo"] = page_num +// resp["PageSize"] = page_size +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = r +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取俱乐部下面的游戏房间信息(正式) +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubRoomSet", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") //必填 +// club_id, _ := params_data.GetInt("ClubID") //必填 +// page_size, _ := params_data.GetInt("PageSize") //必填 +// page_num, _ := params_data.GetInt("PageNum") //必填 +// +// //判断参数是否合法 +// if club_id <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ClubID is not exist error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //检查一下俱乐部ID和平台是否对应 +// if club.Platform != plt_id { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "PltId ClubID is not match error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// clubRoomDetailArr := make([]model.ClubRoomDetailItem, 0) +// var csp *ClubScenePool +// //通过俱乐部的平台找到这个平台下的所有场景 +// club_scenes := ClubSceneMgrSington.GetPlatformClub(club.Platform) +// if len(club_scenes) == 0 { +// goto NEXT +// } +// +// //根据俱乐部ID找到该俱乐部下的ClubScenePool信息 +// for _, value := range club_scenes { +// if value.clubId == club.Id { +// csp = value +// break +// } +// } +// if csp == nil { +// goto NEXT +// } +// +// //待优化数据结构 +// for _, s := range csp.sceneList { +// players := make([]int32, 0) +// for _, p := range s.players { +// players = append(players, p.SnId) +// } +// +// item := model.ClubRoomDetailItem{ +// DeskID: int32(s.sceneId), +// GameName: srvdata.PBDB_GameFreeMgr.GetData(int32(s.gameId)).GetName(), +// BaseBet: 0, +// AreaID: s.clubRoomID, +// PlayerSnids: players, +// } +// for _, r := range club.RoomGroup { +// if r.ClubRoomId == s.clubRoomID { +// item.BaseBet = r.BaseScore +// } +// } +// clubRoomDetailArr = append(clubRoomDetailArr, item) +// } +// NEXT: +// //开始做分页 +// start := (page_num - 1) * page_size +// end := page_num * page_size +// count := len(clubRoomDetailArr) +// if count < start { +// return common.ResponseTag_Ok, resp +// } +// if end > count { +// end = count +// } +// sort.Slice(clubRoomDetailArr, func(i, j int) bool { +// return clubRoomDetailArr[i].DeskID < clubRoomDetailArr[j].DeskID +// }) +// r := clubRoomDetailArr[start:end] +// resp["PageCount"] = count +// resp["PageNo"] = page_num +// resp["PageSize"] = page_size +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = r +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //后台发送俱乐部创建的审核结果,同意或者拒绝,并自动发送邮件给该俱乐部会长 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubCreateCheck", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") +// club_id_str, _ := params_data.GetStr("ClubID") +// isAgree, _ := params_data.GetBool("IsAgree") +// desc, _ := params_data.GetStr("Desc") //如果isAgree=true则desc可以为空 +// +// //检测平台参数 +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断逻辑是否合理 +// if !isAgree && len(desc) == 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "if IsAgree==false then Desc must be have value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// club_id_str_arr := strings.Split(club_id_str, ",") +// var club_id_int_arr = make([]int32, 0) +// for _, v := range club_id_str_arr { +// i, err := strconv.Atoi(v) +// if err == nil { +// if i <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = fmt.Sprintf("ClubID=%v value error!", i) +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// club_id_int_arr = append(club_id_int_arr, int32(i)) +// } else { +// logger.Logger.Errorf("/api/Club/ClubCreateCheck arg err:%v", err) +// } +// } +// +// //判断参数是否合法 +// for _, club_id := range club_id_int_arr { +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// logger.Logger.Errorf("ClubID=%v is not exist error!", club_id) +// continue +// } +// //检查一下避免后台连点 +// if club.Activity { +// continue +// } +// if isAgree { +// club.Activity = true +// //日志信息 +// club.SaveClubOpLog(&model.ClubLog{ +// SnId: club.Owner, +// Name: club.OwnerName, +// LogType: model.ClubLogType_Create, +// }) +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// model.SaveClub(club.Club) +// return nil +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// }), "SaveClub").StartByFixExecutor(club.Platform) +// //邮件 +// sendClubMail_ClubCreateSucces(club.Owner, club.Id, club.OwnerName, club.Name, plt_id) +// //给会长发送更新审核的俱乐部 +// if p, ok := club.memberList[club.Owner]; ok { +// pack := &protocol.SCClubSyncList{ +// ClubData: clubManager.GetClubBaseInfo(club.Id), +// ClubId: proto.Int32(club.Id), +// } +// logger.Logger.Info("SCClubSyncList: ", pack) +// p.SendToClient(protocol.ClubPacketID_PACKET_SC_CLUBSYNCLIST, pack) +// pp := PlayerMgrSington.GetPlayerBySnId(p.SnId) +// if pp != nil { +// pp.CreateClubNum++ +// } else { +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// return model.UpdateCreateCreateClubNum(p.SnId) +// }), nil, "UpdateCreateCreateClubNum").StartByFixExecutor("UpdateCreateCreateClubNum") +// } +// } +// ClubSceneMgrSington.OnClubCreate(club.Id) +// } else { +// clubManager.ClubDestory(int32(club_id)) +// sendClubMail_ClubCreateFail(club.Owner, club.Id, club.OwnerName, club.Name, plt_id, desc) +// } +// } +// +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //后台发送俱乐部公告修改的审核结果,同意或者拒绝,并自动发送邮件 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/ClubNoticeCheck", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// plt_id, _ := params_data.GetStr("PltID") +// op_snid, _ := params_data.GetInt("OpSnid") +// club_id_str, _ := params_data.GetStr("ClubID") +// isAgree, _ := params_data.GetBool("IsAgree") +// desc, _ := params_data.GetStr("Desc") //如果isAgree=true则desc可以为空 +// +// //检测平台参数 +// plt := PlatformMgrSingleton.GetPlatform(plt_id) +// if plt == nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Can't find PltID info error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// //判断逻辑是否合理 +// if !isAgree && len(desc) == 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "if IsAgree==false then Desc must be has value error!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// club_id_str_arr := strings.Split(club_id_str, ",") +// var club_id_int_arr = make([]int32, 0) +// for _, v := range club_id_str_arr { +// i, err := strconv.Atoi(v) +// if err == nil { +// if i <= 0 { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = fmt.Sprintf("ClubID=%v value error!", i) +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// club_id_int_arr = append(club_id_int_arr, int32(i)) +// } else { +// logger.Logger.Errorf("/api/Club/ClubNoticeCheck arg err:%v", err) +// } +// } +// +// for _, club_id := range club_id_int_arr { +// //判断俱乐部号码是否存在 +// club := clubManager.clubList[int32(club_id)] +// if club == nil { +// logger.Logger.Warnf("ClubID=%v is not exist error!", club_id) +// continue +// } +// +// //检查一下避免后台连点 +// if len(club.BillboardNew) != 0 { +// if isAgree { +// opp := club.memberList[int32(op_snid)] +// if opp != nil { +// club.SaveClubOpLog(&model.ClubLog{ +// SnId: int32(op_snid), +// Name: opp.Name, +// LogType: model.ClubLogType_NewBillboard, +// }) +// } +// club.Billboard = club.BillboardNew +// club.BillboardNew = "" +// club.BillboardTs = time.Now().Unix() +// sendClubMail_ClubEditNoticeSucces(club.Owner, club.Id, club.OwnerName, club.Name, plt_id) +// } else { +// club.BillboardNew = "" +// sendClubMail_ClubEditNoticeFail(club.Owner, club.Id, club.OwnerName, club.Name, plt_id, desc) +// } +// //修改完通知客户端 +// pack := &protocol.SCClubBillboard{ +// Billboard: proto.String(club.Billboard), +// BillboardState: protocol.SCClubBillboard_ClubNoticeUnLock, +// } +// proto.SetDefaults(pack) +// club.Broadcast(protocol.ClubPacketID_PACKET_SC_CLUBBILLBOARD, pack, 0) +// } +// } +// +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //俱乐部充值 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Club/AddClubGoldById", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// +// member_snid, _ := params_data.GetInt("ID") +// coin, _ := params_data.GetInt64("Gold") +// coinExt, _ := params_data.GetInt64("GoldExt") +// gold_desc, _ := params_data.GetStr("Desc") +// billNo, _ := params_data.GetInt("BillNo") +// platform, _ := params_data.GetStr("Platform") +// if CacheDataMgr.CacheBillCheck(billNo) { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Bill number repeated!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// CacheDataMgr.CacheBillNumber(billNo) //防止手抖点两下 +// +// var existBillNo bool +// var err error +// var pd *model.PlayerData +// oldGold := int64(0) +// oldSafeBoxGold := int64(0) +// var timeStamp = time.Now().UnixNano() +// type PlayerCoinData struct { +// ID int32 +// ClubCoin int64 +// } +// player := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// if player == nil { +// pd = model.GetPlayerDataBySnId(int32(member_snid), true) +// if pd == nil { +// return errors.New("Player data not find.") +// } +// } else { +// pd = player.PlayerData +// } +// if len(platform) > 0 && pd.Platform != platform { +// return errors.New("Platform error.") +// } +// +// log, err := model.GetPayCoinLogByBillNo(int32(member_snid), int64(billNo)) +// if err == nil && log != nil && log.BillNo == int64(billNo) && log.SnId == int32(member_snid) { +// existBillNo = true +// return fmt.Errorf("paycoin billno(%v) exist!", billNo) +// } +// oldGold = pd.ClubCoin +// oldSafeBoxGold = pd.SafeBoxCoin +// coinLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), coin, common.GainWay_ClubPay, +// fmt.Sprintf("RechargeById-%v", gold_desc), model.PayCoinLogType_Club, coinExt) +// //修正时间 +// timeStamp = coinLog.TimeStamp +// err = model.InsertPayCoinLogs(coinLog) +// if err != nil { +// return err +// } +// //增加帐变记录 +// coinlogex := model.NewCoinLogEx(int32(member_snid), coin+coinExt, oldGold+coin+coinExt, +// oldSafeBoxGold, 0, common.GainWay_ClubPay, 0, "超管加币", +// gold_desc, pd.Platform, pd.Channel, pd.BeUnderAgentCode, 2, pd.PackageID, 0) +// err = model.InsertCoinLogs(coinlogex) +// if err != nil { +// //回滚到对账日志 +// model.RemovePayCoinLog(coinLog.LogId) +// return err +// } +// return err +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// CacheDataMgr.ClearCacheBill(billNo) +// if data != nil { +// if existBillNo { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = data.(error).Error() +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// } else { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = data.(error).Error() +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// } +// } else { +// if player != nil { +// pd = player.PlayerData +// +// player.ClubCoin += (coin + coinExt) +// player.SetClubPayTs(timeStamp) +// player.dirty = true +// player.ClubCoinPayTotal += coin +// if !model.GameParamData.CloseOftenSavePlayerData { +// player.Time2Save() +// } +// player.SendDiffData() +// pcd := &PlayerCoinData{ +// ID: int32(member_snid), +// ClubCoin: player.ClubCoin, +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_DATA] = pcd +// } else { +// if pd != nil { +// pcd := &PlayerCoinData{ +// ID: int32(member_snid), +// ClubCoin: pd.ClubCoin + (coin + coinExt), +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_DATA] = pcd +// } else { +// logger.Logger.Errorf("%v Recharge %v coin failed on %v,bill no %v", member_snid, coin, time.Now(), billNo) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Recharge error" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// } +// player = PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) +// if player != nil { +// if timeStamp > player.ClubCoinPayTs { //金币冲账 +// logger.Logger.Warnf("/api/Member/AddClubGoldById(%v) found ClubCoinPayTs not fit,try auto op(%v) curr(%v)", +// player.SnId, timeStamp, player.ClubCoinPayTs) +// coinlogs, err := model.GetAllPayClubCoinLog(player.SnId, player.ClubCoinPayTs) +// if err == nil { +// var dirty bool +// var cnt int64 +// var cntExt int64 +// for i := 0; i < len(coinlogs); i++ { +// cnt = coinlogs[i].Coin +// cntExt = coinlogs[i].CoinEx +// +// player.ClubCoin += cnt + cntExt +// player.ClubCoinPayTotal += cnt +// player.SetClubPayTs(coinlogs[i].TimeStamp) +// dirty = true +// } +// if dirty { +// player.dirty = true +// if !model.GameParamData.CloseOftenSavePlayerData { +// player.Time2Save() +// } +// player.SendDiffData() +// } +// } +// } +// } else { +// //更新线下缓存数据 +// op := OfflinePlayerMgrSington.GetPlayer(int32(member_snid)) +// if op != nil { +// op.ClubCoin += (coin + coinExt) +// op.SetClubPayTs(timeStamp) +// op.ClubCoinPayTotal += coin +// } +// } +// } +// } +// +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, err = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// if err != nil { +// logger.Logger.Error("/api/Member/AddClubGoldById task marshal data error:", err) +// } +// }), "/api/Member/AddClubGoldById").Start() +// return common.ResponseTag_TransactYield, resp +// })) +//} +// +////请求俱乐部的包间流水信息 +////注意是阻塞协程 +////DateTs参数为请求某一天的时间戳,只要该时间戳在这一天之内即可 +//func reqClubTurnover(clubID int32, DateTs int64) []model.AreaTurnoverItem { +// if clubID <= 0 { +// return nil +// } +// buff, err := webapi.ReqClubTurnover(common.GetAppId(), clubID, DateTs) +// type ApiResult struct { +// Msg []model.AreaTurnoverItem `json:"Msg"` +// Tag int `json:"Tag"` +// } +// result := ApiResult{} +// err = json.Unmarshal(buff, &result) +// if err != nil { +// logger.Logger.Error("ReqClubTurnover: ", string(buff)) +// logger.Logger.Errorf(fmt.Sprintf("reqClubTurnover json.Unmarshal failed._%v", err)) +// return nil +// } +// return result.Msg +//} +// +////请求俱乐部的包间流水信息 +////注意是阻塞协程 +////DateTs参数为请求某一天的时间戳,只要该时间戳在这一天之内即可 +////PageNum为要请求那一页 +//func reqClubRoomTurnover(clubID int32, clubRoomID string, PageSize, PageNum int32, DateTs int64) *model.RoundTurnoverDetail { +// buff, err := webapi.ReqClubRoomPumpDetail(common.GetAppId(), clubID, clubRoomID, PageSize, PageNum, DateTs) +// type ApiResult struct { +// Msg *model.RoundTurnoverDetail `json:"Msg"` +// Tag int `json:"Tag"` +// } +// result := ApiResult{} +// err = json.Unmarshal(buff, &result) +// if err != nil { +// logger.Logger.Errorf(fmt.Sprintf("reqClubRoomTurnover json.Unmarshal failed._%v", err)) +// return nil +// } +// //fmt.Println(string(buff)) +// if result.Tag != 0 { +// logger.Logger.Errorf(fmt.Sprintf("reqClubRoomTurnover result code failed._%v", result.Msg)) +// return nil +// } else { +// return result.Msg +// } +//} diff --git a/worldsrv/trascate_webapi_match.go b/worldsrv/trascate_webapi_match.go new file mode 100644 index 0000000..24ca5d6 --- /dev/null +++ b/worldsrv/trascate_webapi_match.go @@ -0,0 +1,410 @@ +package main + +// +//import ( +// "encoding/json" +// "errors" +// "mongo.games.com/game/common" +// "mongo.games.com/game/model" +// "mongo.games.com/game/webapi" +// "mongo.games.com/goserver/core/basic" +// "mongo.games.com/goserver/core/i18n" +// "mongo.games.com/goserver/core/logger" +// "mongo.games.com/goserver/core/task" +// "mongo.games.com/goserver/core/transact" +// "time" +//) +// +////比赛相关提供给后台的API +//func init() { +// //------------------------------------------------------------------------------------------------------- +// //更新比赛配置 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/UpdateMatchConfig", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// if data, ok := params_data.GetRequestBody("Data"); ok { +// buf, err := json.Marshal(data) +// if err != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "params error!" +// return common.ResponseTag_ParamError, resp +// } +// +// var cfg MatchConfig +// err = json.Unmarshal(buf, &cfg) +// if err != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "params error!" +// return common.ResponseTag_ParamError, resp +// } +// +// MatchMgrSington.UpdateConfig(&cfg, false) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// return common.ResponseTag_Ok, resp +// } +// +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "need Data!" +// return common.ResponseTag_ParamError, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //删除比赛配置 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/DeleteMatchConfig", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// platform, _ := params_data.GetStr("Platform") +// matchId, _ := params_data.GetInt("MatchId") +// +// m := MatchMgrSington.GetMatch(int32(matchId)) +// if m != nil { +// if platform != "" && m.dbMatch.Platform != platform { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "limits of authority!" +// return common.ResponseTag_OpFailed, resp +// } +// } +// +// MatchMgrSington.DeleteMatch(int32(matchId)) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取当前未开始的比赛 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/QueryNotStartedMatch", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// platform, _ := params_data.GetStr("Platform") +// data := MatchMgrSington.MarshalAllNotStartedMatch(platform) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = data +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //强制解散正在进行中的比赛 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/CancelSignupNotStartedMatch", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// platform, _ := params_data.GetStr("Platform") +// matchId, _ := params_data.GetInt("MatchId") +// +// m := MatchMgrSington.GetMatch(int32(matchId)) +// if m != nil { +// if platform != "" && m.dbMatch.Platform != platform { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "limits of authority!" +// return common.ResponseTag_OpFailed, resp +// } +// +// m.ForceCancelSignupAll() +// } +// +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //获取当前正在进行中的比赛 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/QueryRunningMatch", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// platform, _ := params_data.GetStr("Platform") +// data := MatchMgrSington.MarshalAllRunningMatch(platform) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_DATA] = data +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //强制解散正在进行中的比赛 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/DestroyRunningMatch", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// platform, _ := params_data.GetStr("Platform") +// matchId, _ := params_data.GetInt("MatchCopyId") +// +// m := MatchMgrSington.GetCopyMatch(int32(matchId)) +// if m != nil { +// if platform != "" && m.dbMatch.Platform != platform { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "limits of authority!" +// return common.ResponseTag_OpFailed, resp +// } +// +// m.ForceDestroy() +// } +// +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// return common.ResponseTag_Ok, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //更新比赛券配置 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/act/UpdateTicketConfig", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// if data, ok := params_data.GetRequestBody("Data"); ok { +// buf, err := json.Marshal(data) +// if err != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "params error!" +// return common.ResponseTag_ParamError, resp +// } +// +// var cfg ActTicketConfig +// err = json.Unmarshal(buf, &cfg) +// if err != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "params error!" +// return common.ResponseTag_ParamError, resp +// } +// +// ActTicketMgrSington.UpdateConfig(&cfg, false) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// return common.ResponseTag_Ok, resp +// } +// +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "need Data!" +// return common.ResponseTag_ParamError, resp +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //钱包操作接口 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/Match/AddTicket", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// params_data, _ := params.GetRequestBody("Param") +// resp := webapi.NewResponseBody() +// +// member_snid, _ := params_data.GetInt("ID") +// count, _ := params_data.GetInt64("Ticket") +// oper, _ := params_data.GetStr("Oper") +// desc, _ := params_data.GetStr("Desc") +// billNo, _ := params_data.GetInt("BillNo") +// platform, _ := params_data.GetStr("Platform") +// +// if CacheDataMgr.CacheBillCheck(billNo) { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "Bill number repeated!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// CacheDataMgr.CacheBillNumber(billNo) //防止手抖点两下 +// +// var err error +// var pd *model.PlayerData +// oldTicket := int64(0) +// var timeStamp = time.Now().UnixNano() +// type PlayerTicketData struct { +// ID int32 +// Ticket int64 +// } +// player := PlayerMgrSington.GetPlayerBySnId(int32(member_snid)) +// if player != nil { //在线玩家处理 +// pd = player.PlayerData +// if len(platform) > 0 && player.Platform != platform { +// CacheDataMgr.ClearCacheBill(billNo) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "player platform forbit!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// +// if count < 0 { +// if player.Ticket+count < 0 { +// CacheDataMgr.ClearCacheBill(billNo) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ticket not enough!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// } +// +// gainWay := int32(common.GainWay_API_AddTicket) +// +// oldTicket = player.Ticket +// ticketLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), count, gainWay, +// desc, model.PayCoinLogType_Ticket, 0) +// timeStamp = ticketLog.TimeStamp +// //增加日志记录 +// log := model.NewTicketLogEx(int32(member_snid), count, oldTicket+count, player.Ver, gainWay, oper, desc, player.Platform, player.Channel, player.BeUnderAgentCode, player.PackageID, player.InviterId) +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// err = model.InsertPayCoinLogs(ticketLog) +// if err != nil { +// logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, ticketLog) +// return err +// } +// err = model.InsertTicketLogs(log) +// if err != nil { +// //回滚到对账日志 +// model.RemovePayCoinLog(ticketLog.LogId) +// logger.Logger.Errorf("model.InsertTicketLogs err:%v log:%v", err, ticketLog) +// return err +// } +// return err +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// CacheDataMgr.ClearCacheBill(billNo) +// if data != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = data.(error).Error() +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// } else { +// player.Ticket += count +// player.TicketTotal += count +// player.SetTicketPayTs(timeStamp) +// player.dirty = true +// if player.scene == nil { //如果在大厅,那么同步下数据 +// player.SendDiffData() +// } +// pcd := &PlayerTicketData{ +// ID: int32(member_snid), +// Ticket: player.Ticket, +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_DATA] = pcd +// } +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, err = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// if err != nil { +// logger.Logger.Error("AddTicket task marshal data error:", err) +// } +// }), "AddTicket").Start() +// return common.ResponseTag_TransactYield, resp +// } else { +// op := OfflinePlayerMgrSington.GetPlayer(int32(member_snid)) +// if op != nil { +// if count < 0 { +// if op.Ticket+count < 0 { +// CacheDataMgr.ClearCacheBill(billNo) +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = "ticket not enough!" +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// return common.ResponseTag_ParamError, resp +// } +// } +// } +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// pd = model.GetPlayerDataBySnId(int32(member_snid), true) +// if pd == nil { +// return errors.New("Player not find.") +// } +// if len(platform) > 0 && pd.Platform != platform { +// return errors.New("player platform forbit.") +// } +// oldTicket = pd.Ticket +// if count < 0 { +// if oldTicket+count < 0 { +// return errors.New("ticket not enough!") +// } +// } +// +// gainWay := int32(common.GainWay_API_AddTicket) +// ticketLog := model.NewPayCoinLog(int64(billNo), int32(member_snid), count, gainWay, +// desc, model.PayCoinLogType_Ticket, 0) +// timeStamp = ticketLog.TimeStamp +// err = model.InsertPayCoinLogs(ticketLog) +// if err != nil { +// logger.Logger.Errorf("model.InsertPayCoinLogs err:%v log:%v", err, ticketLog) +// return err +// } +// //增加帐变记录 +// //增加日志记录 +// log := model.NewTicketLogEx(int32(member_snid), count, oldTicket+count, pd.Ver, gainWay, oper, desc, pd.Platform, pd.Channel, pd.BeUnderAgentCode, pd.PackageID, pd.InviterId) +// err = model.InsertTicketLogs(log) +// if err != nil { +// //回滚到对账日志 +// model.RemovePayCoinLog(ticketLog.LogId) +// logger.Logger.Errorf("model.InsertTicketLogs err:%v log:%v", err, log) +// return err +// } +// return err +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// CacheDataMgr.ClearCacheBill(billNo) +// if data != nil { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// resp[webapi.RESPONSE_ERRMSG] = data.(error).Error() +// resp[webapi.RESPONSE_DATA] = make(map[string]interface{}) +// } else { +// pcd := &PlayerTicketData{ +// ID: int32(member_snid), +// Ticket: pd.Ticket + count, +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// resp[webapi.RESPONSE_ERRMSG] = "" +// resp[webapi.RESPONSE_DATA] = pcd +// //更新线下缓存数据 +// op := OfflinePlayerMgrSington.GetPlayer(int32(member_snid)) +// if op != nil { +// op.Ticket += count +// op.TicketTotal += count +// op.SetTicketPayTs(timeStamp) +// } +// } +// +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, err = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// if err != nil { +// logger.Logger.Error("AddTicket task marshal data error:", err) +// } +// }), "AddTicket").Start() +// return common.ResponseTag_TransactYield, resp +// } +// })) +// +// //------------------------------------------------------------------------------------------------------- +// //更新比赛券配置 +// WebAPIHandlerMgrSingleton.RegisteWebAPIHandler("/api/act/ManualDistTicket", WebAPIHandlerWrapper( +// func(tNode *transact.TransNode, params webapi.RequestBody) (int, webapi.ResponseBody) { +// resp := webapi.NewResponseBody() +// params_data, _ := params.GetRequestBody("Param") +// orderId, _ := params_data.GetStr("OrderId") +// platform, _ := params_data.GetStr("Platform") +// task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { +// order, err := model.DistAwaitIssuedTicketOrder(orderId, platform) +// if err != nil { +// return err +// } +// title := i18n.Tr("cn", "ActTicketTitle") +// content := i18n.Tr("cn", "ActTicketContent", order.SnId, order.MatchName, order.Rank, order.Count) +// msg := model.NewMessage("", int32(0), order.InviterId, model.MSGTYPE_MATCH_TICKETREWARD, title, content, 0, +// model.MSGSTATE_UNREAD, time.Now().Unix(), model.MSGATTACHSTATE_DEFAULT, "", nil, order.Platform) +// msg.Ticket = order.Count +// err = model.InsertMessage(msg) +// if err != nil { +// return err +// } +// return msg +// }), task.CompleteNotifyWrapper(func(data interface{}, t *task.Task) { +// if data != nil { +// if msg, ok := data.(*model.Message); ok && msg != nil { +// p := PlayerMgrSington.GetPlayerBySnId(msg.SnId) +// if p != nil { +// p.AddMessage(msg) +// } +// } +// resp[webapi.RESPONSE_STATE] = webapi.STATE_OK +// } else { +// resp[webapi.RESPONSE_STATE] = webapi.STATE_ERR +// } +// dataResp := &common.M2GWebApiResponse{} +// dataResp.Body, _ = resp.Marshal() +// tNode.TransRep.RetFiels = dataResp +// tNode.Resume() +// }), "DistAwaitIssuedTicketOrder").Start() +// return common.ResponseTag_TransactYield, resp +// })) +//} diff --git a/worldsrv/viplevel.go b/worldsrv/viplevel.go new file mode 100644 index 0000000..5bbb84d --- /dev/null +++ b/worldsrv/viplevel.go @@ -0,0 +1,117 @@ +package main + +import ( + "sort" + "time" + + "mongo.games.com/goserver/core/logger" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/goserver/core/module" +) + +const ( + MoneyRatio = 100 // 默认 +) + +var VipMgrSington = &VipMgr{ + VIPcfg: make(map[string]*webapi_proto.VIPcfgDataList), +} + +type VipMgr struct { + VIPcfg map[string]*webapi_proto.VIPcfgDataList +} + +func (this *VipMgr) ModuleName() string { + return "VipMgr" +} + +func (this *VipMgr) Init() { + if !model.GameParamData.UseEtcd { + // 后台说现在没有不走ETCD情况~ + } else { + + EtcdMgrSington.InitUpdateVIPcfg() + } +} + +func (this *VipMgr) UpdateVIPcfg(cfg *webapi_proto.VIPcfgDataList) { + this.VIPcfg[cfg.Platform] = cfg +} + +func (this *VipMgr) GetVIPcfg(platform string) *webapi_proto.VIPcfgDataList { + if platform == common.Platform_Rob { + return nil + } + + if this.VIPcfg[platform] == nil { + logger.Logger.Error("玩家平台错误 platform = ", platform) + return nil + } + sort.Slice(this.VIPcfg[platform].List, func(i, j int) bool { + return this.VIPcfg[platform].List[i].VipId < this.VIPcfg[platform].List[j].VipId + }) + return this.VIPcfg[platform] +} + +func (this *VipMgr) GetVipCfg(platform string, vipLevel int32) *webapi_proto.VIPcfg { + if this.VIPcfg[platform] != nil { + for _, cfg := range this.VIPcfg[platform].List { + if cfg.VipId == vipLevel { + return cfg + } + } + } + return nil +} + +// 获取VIP赛季积分加成 +func (this *VipMgr) GetVipPointsExtra(platform string, vipLevel int32) int32 { + if this.VIPcfg[platform] == nil { + return 0 + } + for _, cfg := range this.VIPcfg[platform].List { + if cfg.VipId == vipLevel { + return cfg.Privilege4 + } + } + return 0 +} + +// 获取VIP钻石加成 +func (this *VipMgr) GetVipDiamondExtra(platform string, vipLevel int32) int32 { + if this.VIPcfg[platform] == nil { + return 0 + } + for _, cfg := range this.VIPcfg[platform].List { + if cfg.VipId == vipLevel { + return cfg.Privilege6 + } + } + return 0 +} +func (this *VipMgr) GetVIPLevelCfg(platform string, vip int32) *webapi_proto.VIPcfg { + vips := this.GetVIPcfg(platform) + if vip > 0 && vips != nil && len(vips.List) > 0 { + for _, v := range vips.List { + if v.VipId == vip { + return v + } + } + } + return nil +} + +func (this *VipMgr) Update() { + +} + +func (this *VipMgr) Shutdown() { + module.UnregisteModule(this) +} + +func init() { + module.RegisteModule(VipMgrSington, time.Second, 0) +} diff --git a/worldsrv/welfmgr.go b/worldsrv/welfmgr.go new file mode 100644 index 0000000..f8e17bf --- /dev/null +++ b/worldsrv/welfmgr.go @@ -0,0 +1,1466 @@ +package main + +import ( + "fmt" + "mongo.games.com/game/protocol/shop" + "time" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + hall_proto "mongo.games.com/game/protocol/gamehall" + player_proto "mongo.games.com/game/protocol/player" + webapi_proto "mongo.games.com/game/protocol/webapi" + "mongo.games.com/game/protocol/welfare" + "mongo.games.com/game/srvdata" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/module" +) + +const ( + OpAll = 0 + OpTurnplate = 1 + OpBlindBox = 2 + OpFirstPay = 3 + OpContinuousPay = 4 + OpPhoneLottery = 5 +) + +const ( + WelfareNil = 0 // 不处理 + WelfareOpen = 1 // 开启 + WelfareClose = 2 // 关闭 +) + +const ( + GameSubsidyid = 1 //救济金id +) + +var WelfareMgrSington = &WelfareMgr{ + Sign7: make(map[string]*webapi_proto.Welfare7SignDateList), + Turnplate: make(map[string]*webapi_proto.WelfareTurnplateDateList), + BlindBox: make(map[string]*webapi_proto.WelfareBlindBoxDataList), + BlindBoxCycle: make(map[string]int32), + FirstPay: make(map[string]*webapi_proto.WelfareFirstPayDataList), + FirstPayCycle: make(map[string]int32), + ContinuousPay: make(map[string]*webapi_proto.WelfareContinuousPayDataList), + ContinuousPayCycle: make(map[string]int32), + PhoneLotteryStatus: make(map[string]*webapi_proto.WelfarePhoneLotteryStatus), +} + +type WelfareMgr struct { + BaseClockSinker + Sign7 map[string]*webapi_proto.Welfare7SignDateList + Turnplate map[string]*webapi_proto.WelfareTurnplateDateList + BlindBox map[string]*webapi_proto.WelfareBlindBoxDataList + BlindBoxCycle map[string]int32 + FirstPay map[string]*webapi_proto.WelfareFirstPayDataList + FirstPayCycle map[string]int32 + ContinuousPay map[string]*webapi_proto.WelfareContinuousPayDataList + ContinuousPayCycle map[string]int32 + PhoneLotteryStatus map[string]*webapi_proto.WelfarePhoneLotteryStatus +} + +func (this *WelfareMgr) ModuleName() string { + return "WelfareMgr" +} + +// 设置 rnums 救济金领取次数 +func (this *WelfareMgr) SetWelfData(p *Player, rnums int32) { + // this.MonitorWelfData(p) + if rnums > 0 { + p.WelfData.ReliefFundTimes += rnums + } + p.dirty = true +} + +func (this *WelfareMgr) GetReliefFund(p *Player, isVideo bool) { + sdata := srvdata.PBDB_GameSubsidyMgr.GetData(GameSubsidyid) + // this.MonitorWelfData(p) + pack := &welfare.SCGetReliefFund{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + if sdata != nil { + + if p.WelfData.ReliefFundTimes >= sdata.Times { + pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETRELIEFFUND), pack) + + } else if p.Coin >= int64(sdata.LimitNum) { + pack.OpRetCode = welfare.OpResultCode_OPRC_CoinTooMore + } else { + var rate int = 1 + var gainWay int32 = common.GainWay_ReliefFund + var add int64 + coin := int64(sdata.Get) // 增加金币 + p.WelfData.ReliefFundTimes += 1 + award := PetMgrSington.GetAwardPetByWelf(p) + if award > 0 { + if isVideo { // 双倍领取,蝶女视频加成 + gainWay = common.GainWay_ReliefFund2 + rate = 2 + add = int64(float64(coin*award*2) / 100.0) + roleGirl := PetMgrSington.GetRoleInfo(p, 2000001) + if roleGirl != nil && roleGirl.Level > 0 { + //蝶女加成 + add += int64(float64(coin*int64(roleGirl.Award)*2) / 100.0) + } + coin = coin*2 + add + TaskSubjectSingleton.Touch(common.TaskTypeAdv, &TaskData{ + SnId: p.SnId, + Num: 1, + }) + } else { + add = int64(float64(coin*award) / 100.0) + coin = coin + add + } + } + p.AddCoin(coin, add, gainWay, "ReliefFund", + fmt.Sprintf("领取救济金-%v-%v倍", coin, rate)) + + //LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ReliefFund, model.SystemFreeGive_CoinType_Coin, int64(coin))) + + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + pack.Coin = coin + pack.Times = p.WelfData.ReliefFundTimes + + logger.Logger.Tracef("NewReliefFundLogEx snid: %v Coin:%v", p.SnId, pack.Coin) + getType := model.SystemFreeGive_GiveType_ReliefFund + if isVideo { + getType = model.SystemFreeGive_GiveType_ShopAd + } + + log := model.NewReliefFundLogEx(p.SnId, getType, model.SystemFreeGive_CoinType_Coin, coin, p.CreateTime.Unix(), p.Platform) + if log != nil { + LogChannelSingleton.WriteLog(log) + logger.Logger.Tracef("NewReliefFundLogEx WriteLog snid: %v Coin:%v", p.SnId, pack.Coin) + } + } + } + logger.Logger.Tracef("GetReliefFund snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETRELIEFFUND), pack) +} + +func (this *WelfareMgr) Init() { + if !model.GameParamData.UseEtcd { + // 后台说现在没有不走ETCD情况~ + } else { + + EtcdMgrSington.InitSign7() + + EtcdMgrSington.InitTurnplate() + + EtcdMgrSington.InitBlindBox() + + EtcdMgrSington.InitFirstPay() + + EtcdMgrSington.InitContinuousPay() + + EtcdMgrSington.InitPhoneLottery() + + } +} + +func (this *WelfareMgr) UpdateSign7(cfg *webapi_proto.Welfare7SignDateList) { + this.Sign7[cfg.Platform] = cfg +} +func (this *WelfareMgr) UpdateTurnplate(cfg *webapi_proto.WelfareTurnplateDateList) { + s := int32(0) + if turnplate := this.Turnplate[cfg.Platform]; turnplate != nil { + s = this.Turnplate[cfg.Platform].Switch + } + this.Turnplate[cfg.Platform] = cfg + if s != 0 && s != cfg.Switch { // 打开关闭要广播给客户端 + this.WelfareClues(nil, cfg.Platform, OpTurnplate) + } +} + +func (this *WelfareMgr) UpdateBlindBox(cfg *webapi_proto.WelfareBlindBoxDataList) { + s := int32(0) + if blindBox := this.BlindBox[cfg.Platform]; blindBox != nil { + s = this.BlindBox[cfg.Platform].Switch + } + this.BlindBox[cfg.Platform] = cfg + if cfg.Cycle == WelfareOpen { + this.BlindBoxCycle[cfg.Platform] = 1 + } + if s != 0 && s != cfg.Switch { // 打开关闭要广播给客户端 + this.WelfareClues(nil, cfg.Platform, OpBlindBox) + } +} + +func (this *WelfareMgr) UpdateFirstPay(cfg *webapi_proto.WelfareFirstPayDataList) { + s := int32(0) + if firstPay := this.FirstPay[cfg.Platform]; firstPay != nil { + s = this.FirstPay[cfg.Platform].Switch + } + this.FirstPay[cfg.Platform] = cfg + if cfg.Cycle == WelfareOpen { + this.FirstPayCycle[cfg.Platform] = 1 + } + if s != 0 && s != cfg.Switch { // 打开关闭要广播给客户端 + this.WelfareClues(nil, cfg.Platform, OpFirstPay) + } +} + +func (this *WelfareMgr) UpdateContinuousPay(cfg *webapi_proto.WelfareContinuousPayDataList) { + s := int32(0) + if continuousPay := this.ContinuousPay[cfg.Platform]; continuousPay != nil { + s = this.ContinuousPay[cfg.Platform].Switch + } + this.ContinuousPay[cfg.Platform] = cfg + if cfg.Cycle == WelfareOpen { + this.ContinuousPayCycle[cfg.Platform] = 1 + } + if s != 0 && s != cfg.Switch { // 打开关闭要广播给客户端 + this.WelfareClues(nil, cfg.Platform, OpContinuousPay) + } +} + +func (this *WelfareMgr) UpdatePhoneLotteryStatus(cfg *webapi_proto.WelfarePhoneLotteryStatus) { + this.PhoneLotteryStatus[cfg.Platform] = cfg + s := int32(0) + if phoneLotteryStatus := this.PhoneLotteryStatus[cfg.Platform]; phoneLotteryStatus != nil { + s = this.PhoneLotteryStatus[cfg.Platform].Switch + } + this.PhoneLotteryStatus[cfg.Platform] = cfg + if s != 0 && s != cfg.Switch { // 打开关闭要广播给客户端 + this.WelfareClues(nil, cfg.Platform, OpPhoneLottery) + } + +} +func (this *WelfareMgr) GetPhoneLotteryStatus(platform string) int32 { + if this.PhoneLotteryStatus[platform] != nil { + return this.PhoneLotteryStatus[platform].GetSwitch() + } + //测试代码 + return WelfareClose +} +func (this *WelfareMgr) OnDayChanged(player *Player) error { + if player.WelfData != nil { + player.WelfData.ReliefFundTimes = 0 + blindBox := this.BlindBox[player.Platform] + //continuousPay := this.ContinuousPay[player.Platform] + if blindBox != nil { // 关闭循环重置为-1 + if blindBox.Cycle == WelfareClose && player.WelfData.BlindBoxId != 0 { + player.WelfData.BlindBoxId = -1 + } else { // 循环 + player.WelfData.BlindBoxId = 0 + } + } + //this.WelfareShowRed(player) // + if !this.Welfareturnplate(player, 0) { + player.SendShowRed(hall_proto.ShowRedCode_Welfare, 0, 1) // 0 轮盘红点 + } + + // 同步救济金次数 + pack := &welfare.SCGetReliefFund{ + OpRetCode: welfare.OpResultCode_OPRC_Sucess, + Times: player.WelfData.ReliefFundTimes, + Coin: -1, // 更新剩余次数 + } + player.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETRELIEFFUND), pack) + logger.Logger.Tracef("OnDayChanged SCGetReliefFund snid: %v pack: %v", player.SnId, pack) + } + return nil +} + +func (this *WelfareMgr) MonitorWelfData(player *Player) { + + if player.WelfData == nil { + player.WelfData = &model.WelfareData{ + Sign7: &model.NewSignData{}, + VIPBag: make(map[int32]map[int32]int32), + } + } else if player.WelfData.Sign7 == nil { + player.WelfData.Sign7 = &model.NewSignData{} + } else if player.WelfData.VIPBag == nil { + player.WelfData.VIPBag = make(map[int32]map[int32]int32) + } +} + +func (this *WelfareMgr) GetTurnplate(p *Player) { + // this.MonitorWelfData(p) + pack := &welfare.SCGetTurnplate{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + turnplate := this.Turnplate[p.Platform] + sign7 := this.Sign7[p.Platform] + if turnplate != nil && sign7 != nil { + if turnplate.Switch != WelfareOpen { + logger.Logger.Tracef("GetTurnplate Switch err p.SnId = %v %v", p.SnId, turnplate.Switch) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + if len(turnplate.RateList) != len(sign7.List) || len(turnplate.List) == 0 { + logger.Logger.Tracef("GetTurnplate turnplate.List err p.SnId = %v %v %v", p.SnId, len(turnplate.List), len(turnplate.RateList)) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } else { + for _, v := range turnplate.RateList { + if len(turnplate.List) != len(v.Rate) { // 概率和转盘奖励不等 + logger.Logger.Tracef("GetTurnplate turnplate.RateList err p.SnId = %v %v", p.SnId, len(turnplate.List)) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + } + } + + diff := p.WelfData.Sign7.SignIndex % int32(len(turnplate.RateList)) + + if diff >= int32(len(turnplate.RateList)) { + logger.Logger.Tracef("GetTurnplate turnplate.List err p.SnId = %v %v %v", p.SnId, len(turnplate.List), len(turnplate.RateList)) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + ts := time.Now().Unix() + + // 查看是否已经领取 + dif := common.DiffDaybyTs(ts, p.WelfData.Sign7.SignTickets) + if dif == 0 { // 当天已经领取 + logger.Logger.Tracef("GetTurnplate LastTickets is repeat p.SnId = %v %v %v", p.SnId, p.WelfData.Sign7.SignTickets, diff) + pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + var drawdates []*webapi_proto.WelfareDate + for _, v := range sign7.List { + if v.Day == diff+1 { // 找到对应天数 + drawdates = append(drawdates, v.Date...) // 领取原始奖励 + + rand := turnplate.RateList[diff].Rate // 找到对应天数概率 + idx := common.RandSliceIndexByWight31N(rand) // 获取奖励下标 + datas := turnplate.List[idx] + + drawdates = append(drawdates, datas.Date...) // 领取转盘奖励 + + p.WelfData.Sign7.SignTickets = ts // 签到 + if len(p.WelfData.Sign7.TurnplateIdx) >= len(turnplate.RateList) { + p.WelfData.Sign7.TurnplateIdx = nil + } + p.WelfData.Sign7.TurnplateIdx = append(p.WelfData.Sign7.TurnplateIdx, int32(idx)) // 获取领取转盘下标 + p.WelfData.Sign7.SignIndex += 1 + // 转盘 + gainWay := int32(common.GainWay_ActTurnplate) + oper, remark := "system", "轮盘奖励" + DrawWelfareDate(datas.Date, p, gainWay, oper, remark, false) // 领取奖励 + // 签到 + gainWay = int32(common.GainWay_ActSignNew) + oper, remark = "system", "新七日签到" + DrawWelfareDate(v.Date, p, gainWay, oper, remark, false) // 领取奖励 + + for _, d := range drawdates { + pack.Date = append(pack.Date, &welfare.WelfareDate{ + Grade: d.Grade, + Type: d.Type, + Name: d.Name, + Item_Id: d.Item_Id, + }) + } + pack.Idx = int32(idx) + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + hadSign := p.WelfData.Sign7.SignIndex % int32(len(turnplate.RateList)) + if hadSign == 0 { + hadSign = int32(len(turnplate.RateList)) + } + pack.SignDay = hadSign // 已签到天数 + break + } + } + + } + logger.Logger.Tracef("GetTurnplate snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) +} + +func (this *WelfareMgr) GetTurnplteVideo(p *Player) { + pack := &welfare.SCGetTurnplate{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + IsVideo: true, + } + + send := func() { + logger.Logger.Tracef("GetTurnplate snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + } + + turn := this.Turnplate[p.Platform] // 转盘 + sign7 := this.Sign7[p.Platform] // 签到 + if turn == nil || sign7 == nil { + send() + return + } + + if turn.Switch != WelfareOpen { + logger.Logger.Tracef("GetTurnplate Switch err p.SnId = %v %v", p.SnId, turn.Switch) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + if len(turn.RateList) != len(sign7.List) || len(turn.List) == 0 { + logger.Logger.Tracef("GetTurnplate turn.List err p.SnId = %v %v %v", p.SnId, len(turn.List), len(turn.RateList)) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } else { + for _, v := range turn.RateList { + if len(turn.List) != len(v.Rate) { // 概率和转盘奖励不等 + logger.Logger.Tracef("GetTurnplate turn.RateList err p.SnId = %v %v", p.SnId, len(turn.List)) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + } + } + + // 看视频奖励只能当天领,在签到之后 + ts := time.Now().Unix() + // 查看是否已经领取 + diff := common.DiffDaybyTs(ts, p.WelfData.Sign7.SignTickets) + if diff != 0 { // 当天没签到,需要先签到 + logger.Logger.Tracef("GetTurnplate LastTickets is repeat p.SnId = %v %v %v", p.SnId, p.WelfData.Sign7.SignTickets, diff) + pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + // 判断是否已经领取过 + diff = common.DiffDaybyTs(ts, p.WelfData.Sign7.VideoTicket) + if diff == 0 { // 已经领取过了 + logger.Logger.Tracef("GetTurnplate VideoTicket is repeat p.SnId = %v %v %v", p.SnId, p.WelfData.Sign7.VideoTicket, diff) + pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) + return + } + + // 可以领取 + p.WelfData.Sign7.VideoTicket = ts + index := p.WelfData.Sign7.SignIndex % int32(len(turn.RateList)) + var drawdates []*webapi_proto.WelfareDate + for _, v := range sign7.List { + if v.Day == index { // 找到对应天数 + idx := p.WelfData.Sign7.TurnplateIdx[len(p.WelfData.Sign7.TurnplateIdx)-1] + drawdates = append(drawdates, v.Date...) // 签到奖励 + datas := turn.List[idx] + drawdates = append(drawdates, datas.Date...) // 转盘奖励 + + // 转盘 + gainWay := int32(common.GainWay_ActTurnplate2) + oper, remark := "system", "轮盘奖励" + DrawWelfareDate(datas.Date, p, gainWay, oper, remark, true) // 领取奖励 + // 签到 + gainWay = int32(common.GainWay_ActSignNew2) + oper, remark = "system", "新七日签到" + DrawWelfareDate(v.Date, p, gainWay, oper, remark, true) // 领取奖励 + + for _, d := range drawdates { + coin := d.Grade * 2 + if d.Type == 1 { // 蝶女金币加成 + roleGirl := PetMgrSington.GetRoleInfo(p, 2000001) + if roleGirl != nil && roleGirl.Level > 0 { + coin = int32(float64(coin) * float64(100+roleGirl.Award) / 100.0) + } + } + pack.Date = append(pack.Date, &welfare.WelfareDate{ + Grade: coin, + Type: d.Type, + Name: d.Name, + Item_Id: d.Item_Id, + }) + } + pack.Idx = idx + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + hadSign := p.WelfData.Sign7.SignIndex % int32(len(turn.RateList)) + if hadSign == 0 { + hadSign = int32(len(turn.RateList)) + } + pack.SignDay = hadSign // 已签到天数 + TaskSubjectSingleton.Touch(common.TaskTypeAdv, &TaskData{ + SnId: p.SnId, + Num: 1, + }) + break + } + } + + logger.Logger.Tracef("GetTurnplate snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETTURNPLATE), pack) +} + +// isVideo 看视频了,计算看视频加成 +func DrawWelfareDate(dates []*webapi_proto.WelfareDate, p *Player, gainWay int32, oper, remark string, isVideo bool) { + // 注意dates只能读,不能写 + for _, v := range dates { + switch v.Type { + case 1: //金币 + coin := int64(v.Grade) + add := int64(0) + if isVideo { + roleGirl := PetMgrSington.GetRoleInfo(p, 2000001) + if roleGirl != nil && roleGirl.Level > 0 /*|| !role.IsUsing*/ { + //蝶女加成 + add = int64(float64(coin) * float64(roleGirl.Award) * 2 / 100.0) + } + coin = coin*2 + add + } + + p.AddCoin(coin, add, gainWay, oper, remark) + giveType := int32(-1) + switch gainWay { + case common.GainWay_VIPGift: //vip礼包 + giveType = model.SystemFreeGive_GiveType_VipGift + case common.GainWay_ActTurnplate: //转盘 + giveType = model.SystemFreeGive_GiveType_ActTurnplate + case common.GainWay_ActSignNew: + if remark == "新七日签到" { + giveType = model.SystemFreeGive_GiveType_ActSign + } else { //累签 + giveType = model.SystemFreeGive_GiveType_ActContinuousSign + } + } + if giveType != -1 { + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, giveType, model.SystemFreeGive_CoinType_Coin, int64(coin))) + } + case 2: //钻石 + p.AddDiamond(int64(v.Grade), 0, gainWay, oper, remark) + giveType := int32(-1) + switch gainWay { + case common.GainWay_VIPGift: + giveType = model.SystemFreeGive_GiveType_VipGift + case common.GainWay_ActTurnplate: + giveType = model.SystemFreeGive_GiveType_ActTurnplate + case common.GainWay_ActSignNew: + if remark == "新七日签到" { + giveType = model.SystemFreeGive_GiveType_ActSign + } else { //累签 + giveType = model.SystemFreeGive_GiveType_ActContinuousSign + } + } + if giveType != -1 { + LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, giveType, model.SystemFreeGive_CoinType_Diamond, int64(v.Grade))) + } + case 3: //道具 + if v.Grade > 0 { + item := &Item{ + ItemId: v.Item_Id, + ItemNum: int64(v.Grade), + } + BagMgrSingleton.AddJybBagInfo(p, []*Item{item}, 0, gainWay, oper, remark) + itemData := srvdata.PBDB_GameItemMgr.GetData(item.ItemId) + if itemData != nil { + BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, itemData.Name, item.ItemNum, remark) + } + } + } + } +} + +func (this *WelfareMgr) GetAddupSign(p *Player, addupday int32) { + // this.MonitorWelfData(p) + pack := &welfare.SCGetAddupSign{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + turnplate := this.Turnplate[p.Platform] + sign7 := this.Sign7[p.Platform] + if turnplate != nil && sign7 != nil { + if turnplate.Switch != WelfareOpen { + logger.Logger.Tracef("Get7Sign Switch err p.SnId = %v %v", p.SnId, turnplate.Switch) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETADDUPSIGN), pack) + return + } + signIndex := p.WelfData.Sign7.SignIndex + + if addupday > signIndex || addupday > int32(len(sign7.List)) || addupday < 1 { // 累计签到不够 或者超过签到上限 或者非法输入 + logger.Logger.Tracef("Get7Sign Sign7.SignIndex is repeat p.SnId = %v %v %v", p.SnId, signIndex, addupday) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETADDUPSIGN), pack) + return + } + + for _, v := range p.WelfData.Sign7.AddupIndex { + if v == addupday { // 已经领取 + pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + logger.Logger.Tracef("Get7Sign Sign7.AddupIndex is repeat p.SnId = %v %v", p.SnId, addupday) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETADDUPSIGN), pack) + return + } + } + flag := false + for _, v := range sign7.List { + for _, d := range v.AddUpDate { // 累计奖励 + if d.AddUpDay != addupday { + continue + } + p.WelfData.Sign7.AddupIndex = append(p.WelfData.Sign7.AddupIndex, addupday) // 签到 + ad := &welfare.AddUpWelfareDate{ + AddUpDay: d.AddUpDay, + } + for _, d1 := range d.AddUpDate { + ad.AddUpDate = append(ad.AddUpDate, &welfare.WelfareDate{ + Grade: d1.Grade, + Type: d1.Type, + Name: d1.Name, + Item_Id: d1.Item_Id, + }) + } + pack.Date = append(pack.Date, ad) + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + pack.AddUpSignDay = append(pack.AddUpSignDay, p.WelfData.Sign7.AddupIndex...) + gainWay := int32(common.GainWay_ActSignNew) + oper, remark := "system", fmt.Sprintf("累计%v天签到", addupday) + DrawWelfareDate(d.AddUpDate, p, gainWay, oper, remark, false) // 领取奖励 + flag = true + break + } + if flag { + break + } + } + } + logger.Logger.Tracef("GetAddupSign snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETADDUPSIGN), pack) +} + +func (this *WelfareMgr) WelfaredInfo(p *Player) { + // this.MonitorWelfData(p) + pack := &welfare.SCWelfaredInfo{} + diff := 0 + ts := time.Now().Unix() + turnplate := this.Turnplate[p.Platform] + sign7 := this.Sign7[p.Platform] + if turnplate != nil && sign7 != nil { + pack.Switch = turnplate.Switch + if turnplate.Switch != WelfareClose { + pack.DrawTurnplate = 2 + signIndex := p.WelfData.Sign7.SignIndex % int32(len(turnplate.RateList)) + + diff = common.DiffDaybyTs(ts, p.WelfData.Sign7.SignTickets) + if diff == 0 || signIndex >= int32(len(turnplate.RateList)) { // 已经领取 或者全部领取 + pack.DrawTurnplate = 1 + } + + for _, v := range turnplate.List { + data := &welfare.WelfareTurnplateDate{ + Id: v.Id, + } + for _, d := range v.Date { + data.Date = append(data.Date, &welfare.WelfareDate{ + Grade: d.Grade, + Type: d.Type, + Name: d.Name, + Item_Id: d.Item_Id, + }) + } + pack.Tlist = append(pack.Tlist, data) + } + + for _, v := range sign7.List { + + data := &welfare.Welfare7SignDate{ + Day: v.Day, + } + for _, d := range v.Date { + data.Date = append(data.Date, &welfare.WelfareDate{ + Grade: d.Grade, + Type: d.Type, + Name: d.Name, + Item_Id: d.Item_Id, + }) + } + for _, d := range v.AddUpDate { // 累计奖励 + ad := &welfare.AddUpWelfareDate{ + AddUpDay: d.AddUpDay, + } + for _, d1 := range d.AddUpDate { + ad.AddUpDate = append(ad.AddUpDate, &welfare.WelfareDate{ + Grade: d1.Grade, + Type: d1.Type, + Name: d1.Name, + Item_Id: d1.Item_Id, + }) + } + data.AddUpDate = append(data.AddUpDate, ad) + } + pack.Slist = append(pack.Slist, data) + } + pack.TurnplateIdx = append(pack.TurnplateIdx, p.WelfData.Sign7.TurnplateIdx...) + //需要判断是要显示7 还是显示0 + if signIndex == 0 { + if pack.DrawTurnplate == 1 { + signIndex = int32(len(turnplate.RateList)) + } + } + pack.SignDay = signIndex + pack.AddUpSignDay = append(pack.AddUpSignDay, p.WelfData.Sign7.AddupIndex...) + } + } + logger.Logger.Tracef("WelfaredInfo snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_WELFAREINFO), pack) +} + +func (this *WelfareMgr) WelfareClues(p *Player, platform string, op int) { + + pack := &player_proto.SCEasyWelfaredInfo{} + // 0转盘1盲盒2首冲3连续充值 + turnplate := this.Turnplate[platform] + if turnplate != nil { + if op == OpAll || op == OpTurnplate { + if turnplate.Switch == WelfareOpen && p != nil && this.Welfareturnplate(p, 1) { // 没有关闭且所有奖励已经领取 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) //关闭 + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, turnplate.Switch) + } + } else { // 不更新 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareNil) + } + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) // 配置nil就是关闭 + } + + blindBox := this.BlindBox[platform] + if blindBox != nil { + if op == OpAll || op == OpBlindBox { + if blindBox.Switch == WelfareOpen && blindBox.Cycle == WelfareClose && p != nil && p.WelfData.BlindBoxId != 0 { // 没有关闭且所有奖励已经领取 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) //关闭 + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, blindBox.Switch) + } + } else { // 不更新 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareNil) + } + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) // 配置nil就是关闭 + } + + firstPay := this.FirstPay[platform] + if firstPay != nil { + if op == OpAll || op == OpFirstPay { + ln := len(firstPay.List) + max := firstPay.List[ln-1].Day // 正序 + if firstPay.Switch == WelfareOpen && firstPay.Cycle == WelfareClose && p != nil && p.WelfData.FirstPayDay >= max { // 没有关闭且所有奖励已经领取 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) //关闭 + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, firstPay.Switch) + } + } else { // 不更新 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareNil) + } + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) // 配置nil就是关闭 + } + + continuousPay := this.ContinuousPay[platform] + if continuousPay != nil { + if op == OpAll || op == OpContinuousPay { + ln := len(continuousPay.List) + max := continuousPay.List[ln-1].Day // 正序 + if continuousPay.Switch == WelfareOpen && continuousPay.Cycle == WelfareClose && p != nil && p.WelfData.ContinuousPayDay >= max { // 没有关闭且所有奖励已经领取 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) //关闭 + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, continuousPay.Switch) + } + } else { // 不更新 + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareNil) + } + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) // 配置nil就是关闭 + } + //抽手机活动开关 + if this.PhoneLotteryStatus[platform] != nil { + phoneLotterySwitch := this.PhoneLotteryStatus[platform].Switch + pack.WelfareSwitch = append(pack.WelfareSwitch, phoneLotterySwitch) //抽手机活动开关 + } else { + pack.WelfareSwitch = append(pack.WelfareSwitch, WelfareClose) + } + if p != nil { + logger.Logger.Tracef("WelfareClues snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(player_proto.PlayerPacketID_PACKET_SC_SWELFAREINFO), pack) + } else { + logger.Logger.Tracef("WelfareClues to Platform pack: %v", pack) + PlayerMgrSington.BroadcastMessageToPlatform(platform, int(player_proto.PlayerPacketID_PACKET_SC_SWELFAREINFO), pack) + } +} + +func (this *WelfareMgr) Welfareturnplate(p *Player, op int32) bool { // 0 红点提示 1全部领取 + var isShow bool + turnplate := this.Turnplate[p.Platform] + sign7 := this.Sign7[p.Platform] + if turnplate != nil && sign7 != nil { + if turnplate.Switch == WelfareClose { + return true + } + diff := 0 + ts := time.Now().Unix() + signIndex := p.WelfData.Sign7.SignIndex % int32(len(turnplate.RateList)) + + diff = common.DiffDaybyTs(ts, p.WelfData.Sign7.SignTickets) + if diff == 0 || signIndex >= int32(len(turnplate.RateList)) { // 已经领取 或者全部领取 + isShow = true + if op == 1 && signIndex < int32(len(turnplate.RateList)) { // 没有领完 + isShow = false + } + } + + if isShow { // 当日已经领取 查看累计奖励是否领取 + var addUpDate []int32 + for _, v := range sign7.List { + + for _, d := range v.AddUpDate { // 累计奖励 + addUpDate = append(addUpDate, d.AddUpDay) + } + } + signIndex := p.WelfData.Sign7.SignIndex + for _, addupday := range addUpDate { + if addupday <= signIndex { + falg := false + for _, v := range p.WelfData.Sign7.AddupIndex { + if v == addupday { // 已经领取 + falg = true + break + } + } + if !falg { + isShow = false // 有未领取每日奖励 + break + } + + } + } + } + + } + return isShow +} + +func (this *WelfareMgr) WelfareShowRed(p *Player) { + if p == nil { + return + } + this.MonitorWelfData(p) // 登录检测 + this.WelfareClues(p, p.Platform, OpAll) + + if !this.Welfareturnplate(p, 0) { + p.SendShowRed(hall_proto.ShowRedCode_Welfare, 0, 1) // 0 轮盘红点 + } + +} + +func (this *WelfareMgr) BlindBoxInfo(p *Player, bid int32) { + // this.MonitorWelfData(p) + pack := &welfare.SCBlindBoxInfo{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + blindBox := this.BlindBox[p.Platform] + if blindBox != nil { + if blindBox.Switch != WelfareOpen { + logger.Logger.Tracef("BlindBoxInfo Switch err p.SnId = %v %v", p.SnId, blindBox.Switch) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_BLINBOXINFO), pack) + return + } + ln := len(blindBox.List) + if ln < 2 { // 一个以上 + logger.Logger.Tracef("BlindBoxInfo blindBox.List err p.SnId = %v %v", p.SnId, ln) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_BLINBOXINFO), pack) + return + } + + pack.MinId = blindBox.MinId + pack.Draw = 1 + cyc := this.BlindBoxCycle[p.Platform] + if p.WelfData.BlindBoxId == -1 { + if cyc == 1 || blindBox.Cycle == WelfareOpen { + p.WelfData.BlindBoxId = 0 + } + } // == 1代表当日循环 + if p.WelfData.BlindBoxId == 0 { // 未领取过发随机Date + + idx := bid + num := 0 + for int32(idx) == bid { // 不相等跳出 ln>1 不会死循环 + idx = int32(common.RandInt(ln)) + 1 + if num > 100 { + break + } + num++ + } + data := blindBox.List[idx-1] + pack.Draw = 2 + pack.Date = &welfare.BlindBoxData{ + Id: data.Id, + Type: data.Type, + Name: data.Name, + Grade: data.Grade, + Consume: data.Consume, + Price1: data.Price1, + Price2: data.Price2, + Discount: data.Discount, + //Item_Id: data.Item_Id, 目前未使用 + } + } + + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + pack.Cycle = blindBox.Cycle + } + logger.Logger.Tracef("BlindBoxInfo snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_BLINBOXINFO), pack) +} + +func (this *WelfareMgr) RecordWelfareLog(p *Player, num, takenum, discount int64, day, gainWay, wtype int32, item []*model.WelfareItem, oper, remark string) { + + log := model.NewWelfareLogEx(p.SnId, num, takenum, p.Coin, p.Diamond, discount, day, + p.Ver, gainWay, wtype, item, oper, remark, p.Platform, p.Channel, + p.BeUnderAgentCode, p.PackageID) + + if log != nil { + //logger.Logger.Trace("RecordItemLog 开始记录 道具操作") + LogChannelSingleton.WriteLog(log) + } +} + +func (this *WelfareMgr) BuyBlindBox(p *Player, buyid, ConfigPayId int32) { + // this.MonitorWelfData(p) + //pack := &welfare.SCGetBlindBox{ + // OpRetCode: welfare.OpResultCode_OPRC_Error, + //} + pack := &shop.SCPayInfo{ + RetCode: shop.OpResultCode_OPRC_Error, + } + blindBox := this.BlindBox[p.Platform] + if blindBox == nil { + logger.Logger.Tracef("BuyBlindBox blindBox==nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + if blindBox.Switch != WelfareOpen { + logger.Logger.Tracef("BuyBlindBox Switch err p.SnId = %v %v", p.SnId, blindBox.Switch) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + + if p.WelfData.BlindBoxId != 0 { // 已经领取 + logger.Logger.Tracef("BuyBlindBox BlindBoxId is repeat = %v %v", p.SnId, p.WelfData.BlindBoxId) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + var bbd *webapi_proto.BlindBoxData + for _, v := range blindBox.List { + if v.Id == buyid { + bbd = v + //if v.Consume == ConsumeDiamond { + //if p.Diamond < v.Price2 { + // logger.Logger.Tracef("BuyBlindBox Diamond < Price2 p.SnId = %v %v %v", p.SnId, p.Diamond, v.Price2) + // pack.OpRetCode = welfare.OpResultCode_OPRC_ErrCoin + // p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETBLINBOX), pack) + // return + //} + //p.WelfData.BlindBoxId = v.Id + //gainWay := int32(common.GainWay_ActBlindBox) + //oper, remark := "system", "购买盲盒" + //p.AddDiamond(-v.Price2, gainWay, oper, remark) // 消耗 + //oper, remark = "system", "盲盒奖励" + //switch v.Type { // 增加 + //case 1: //金币 + // p.AddCoin(int64(v.Grade), gainWay, oper, remark) + //case 2: //钻石 + // p.AddDiamond(int64(v.Grade), gainWay, oper, remark) + //} + // + //// 记录 + //item := []*model.WelfareItem{&model.WelfareItem{ + // Num: int64(v.Grade), + // ItemId: v.Item_Id, + // Type: v.Type, + //}} + //this.RecordWelfareLog(p, int64(v.Grade), v.Price2, int64(v.Discount*10000), 0, gainWay, model.WelfareBuyBlindBox, + // item, oper, remark) + //pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + //break + //} + + //if v.Consume != ConsumeDiamond { // 非钻石支付需要接三方 不能走这里判断 + // logger.Logger.Tracef("BuyBlindBox Switch err p.SnId = %v %v", p.SnId, blindBox.Switch) + // p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_GETBLINBOX), pack) + // return + //} + } + } + if bbd == nil { + logger.Logger.Tracef("BuyBlindBox bbd == nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + ShopMgrSington.SendAPICreateOrder(p, ConfigPayId, bbd, "BlindBox") + ////三方购买 + //task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // var amount [3]int32 + // if bbd.Type == 1 { + // //金币 + // amount[0] = bbd.Grade + // } else if bbd.Type == 2 { + // //钻石 + // amount[1] = bbd.Grade + // } + // dbShop := model.NewDbShop(p.Platform, 0, amount[:], Shop_Consume_Money, int32(bbd.Price2), nil, 0, "", + // p.SnId, 0, "BlindBox") + // err := model.InsertDbShopLog(dbShop) + // if err != nil { + // logger.Logger.Errorf("model.InsertDbShopLog err:", err) + // return nil + // } + // return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, 0, p.Platform, p.PackageID, p.DeviceOS, + // p.DeviceId, bbd.Name, amount, int32(bbd.Price2), nil) + //}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // logger.Logger.Trace("API_CreateOrder_BuyBlindBox:", data) + // info := data.(*webapi_proto.ASCreateOrder) + // if info == nil || info.Tag != 0 { + // //失败 + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } else { + // pack.RetCode = shop.OpResultCode_OPRC_Sucess + // pack.Url = info.Url + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } + //}), "API_CreateOrder_BuyBlindBox").Start() +} + +func (this *WelfareMgr) FirstPayInfo(p *Player) { + // this.MonitorWelfData(p) + pack := &welfare.SCWelfareFirstPayData{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + firstPay := this.FirstPay[p.Platform] + if firstPay != nil { + pack.Switch = firstPay.Switch + if firstPay.Switch != WelfareClose { + ln := len(firstPay.List) + if ln == 0 { // 不能为空 + logger.Logger.Tracef("FirstPayInfo firstPay.List err p.SnId = %v %v", p.SnId, ln) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_FIRSTPAYINFO), pack) + return + } + + ts := time.Now().Unix() + day := p.WelfData.FirstPayDay + pack.Draw = 2 + if day != 0 { // 查看是否已经领取 + dif := common.DiffDaybyTs(ts, p.WelfData.FirstPayTickets) + if dif == 0 { // 当天已经领取 + pack.Draw = 1 + } + } + + max := firstPay.List[ln-1].Day // 正序 + cyc := this.FirstPayCycle[p.Platform] + if pack.Draw == 2 { // 当天未领取判断 + if day >= max { + if firstPay.Cycle != WelfareOpen && cyc != 1 { // 全部领取完就发最后一天 + day = max + pack.Draw = 1 // 不循环 且今日无开启循环操作 领完不能再领取了 + } else { + // p.WelfData.FirstPayDay = 0 // 循环 + day = firstPay.List[0].Day // 从第一天开始 + } + } else if day == 0 { + day = firstPay.List[0].Day + } + } + pack.Cycle = firstPay.Cycle + MoneyRatio := float64(0) + if vips := VipMgrSington.GetVIPcfg(p.Platform); vips != nil { + MoneyRatio = vips.MoneyRatio + } + for _, v := range firstPay.List { + if v.Day == day { + vipEx := v.VIPEX + if MoneyRatio > 0.000001 { + vipEx = int32(float64(v.Price2) * MoneyRatio) + } + pack.List = &welfare.WelfareSpree{ + Day: v.Day, + VIPEX: vipEx, + Consume: v.Consume, + Price1: v.Price1, + Price2: v.Price2, + Discount: v.Discount, + } + for _, it := range v.Item { + pack.List.Item = append(pack.List.Item, &welfare.WelfareDate{ + Grade: it.Grade, + Type: it.Type, + Name: it.Name, + Item_Id: it.Item_Id, + }) + } + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + break + } + } + + } + } + logger.Logger.Tracef("FirstPayInfo snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_FIRSTPAYINFO), pack) +} + +// 默认已经成功 +func (this *WelfareMgr) BuyFirstPay(p *Player, ConfigPayId int32) { + // this.MonitorWelfData(p) + //pack := &welfare.SCWelfareFirstPay{ + // OpRetCode: welfare.OpResultCode_OPRC_Error, + //} + pack := &shop.SCPayInfo{ + RetCode: shop.OpResultCode_OPRC_Error, + } + firstPay := this.FirstPay[p.Platform] + if firstPay == nil { + logger.Logger.Errorf("BuyFirstPay firstPay == nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + ts := time.Now().Unix() + + if p.WelfData.FirstPayDay != 0 { // 查看是否已经领取 + dif := common.DiffDaybyTs(ts, p.WelfData.FirstPayTickets) + if dif == 0 { // 当天已经领取 + logger.Logger.Errorf("BuyFirstPay LastTickets is repeat p.SnId = %v %v", p.SnId, p.WelfData.FirstPayTickets) + //pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + } + + ln := len(firstPay.List) + if ln == 0 { // 不能为空 + logger.Logger.Errorf("BuyFirstPay firstPay.List err p.SnId = %v %v", p.SnId, ln) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + max := firstPay.List[ln-1].Day // 正序 + + if p.WelfData.FirstPayDay >= max { + cyc := this.FirstPayCycle[p.Platform] + if firstPay.Cycle != WelfareOpen && cyc != 1 { // 全部领取完 + //pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + logger.Logger.Tracef("BuyFirstPay Cycle p.WelfData.FirstPayDay max p.SnId = %v %v", p.SnId, firstPay.Cycle, p.WelfData.FirstPayDay, max) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + p.WelfData.FirstPayDay = 0 + } + + var wfs *webapi_proto.WelfareSpree + + for i, v := range firstPay.List { + + if v.Day == p.WelfData.FirstPayDay || p.WelfData.FirstPayDay == 0 { + + if p.WelfData.FirstPayDay != 0 { // 为0领取第一个 p.WelfData.FirstPayDay不为0代表已经领取到该礼包 需要领取下一个 + i++ + } + wfs = v + + //data := firstPay.List[i] // 越界条件已判断 + // + //p.WelfData.FirstPayDay = data.Day + //p.WelfData.FirstPayTickets = ts + //// TODO + //p.AddMoneyPayTotal(v.Price2) + //gainWay := int32(common.GainWay_ActFirstPay) + //oper, remark := "system", "首充奖励" + //DrawWelfareDate(data.Item, p, gainWay, oper, remark) // 领取奖励 + //var item []*model.WelfareItem + //for _, v := range data.Item { + // item = append(item, &model.WelfareItem{ + // Num: int64(v.Grade), + // ItemId: v.Item_Id, + // Type: v.Type, + // }) + //} + //MoneyRatio := float64(0) + //if vips := VipMgrSington.GetVIPcfg(p.Platform); vips != nil { + // MoneyRatio = vips.MoneyRatio + //} + //vipEx := int64(data.VIPEX) + //if MoneyRatio > 0.000001 { + // vipEx = int64(float64(v.Price2) * MoneyRatio) + //} + //this.RecordWelfareLog(p, vipEx, v.Price2, int64(v.Discount*10000), p.WelfData.FirstPayDay, gainWay, model.WelfareBuyFirstPay, + // item, oper, remark) + // + //pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + break + } + } + if wfs == nil || wfs.Item == nil { + logger.Logger.Errorf("BuyFirstPay fwfs == nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + ShopMgrSington.SendAPICreateOrder(p, ConfigPayId, wfs, "FirstRecharge") + ////三方购买 + //task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // var amount [3]int32 + // for _, it := range wfs.Item { + // if it.Type == 1 { + // amount[0] = it.Grade + // } else if it.Type == 2 { + // amount[1] = it.Grade + // } + // } + // dbShop := model.NewDbShop(p.Platform, 0, amount[:], Shop_Consume_Money, int32(wfs.Price2), + // nil, 0, "", p.SnId, 0, "FirstRecharge") + // err := model.InsertDbShopLog(dbShop) + // if err != nil { + // logger.Logger.Errorf("model.InsertDbShopLog err:", err) + // return nil + // } + // return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, 0, p.Platform, p.PackageID, p.DeviceOS, + // p.DeviceId, "FirstRecharge", amount, int32(wfs.Price2), nil) + //}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // logger.Logger.Trace("API_CreateOrder_BuyFirstPay:", data) + // info := data.(*webapi_proto.ASCreateOrder) + // if info == nil || info.Tag != 0 { + // //失败 + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } else { + // pack.RetCode = shop.OpResultCode_OPRC_Sucess + // pack.Url = info.Url + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } + //}), "API_CreateOrder_BuyFirstPay").Start() +} + +func (this *WelfareMgr) ContinuousPayInfo(p *Player) { + // this.MonitorWelfData(p) + pack := &welfare.SCWelfareContinuousPayData{ + OpRetCode: welfare.OpResultCode_OPRC_Error, + } + continuousPay := this.ContinuousPay[p.Platform] + if continuousPay != nil { + pack.Switch = continuousPay.Switch + if continuousPay.Switch != WelfareClose { + ln := len(continuousPay.List) + if ln == 0 { // 不能为空 + logger.Logger.Tracef("ContinuousPayInfo continuousPay.List err p.SnId = %v %v", p.SnId, ln) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_FIRSTPAYINFO), pack) + return + } + + ts := time.Now().Unix() + day := p.WelfData.ContinuousPayDay + max := continuousPay.List[ln-1].Day // 正序 + pack.Draw = 2 + if day != 0 { // 查看是否已经领取 + dif := common.DiffDaybyTs(ts, p.WelfData.ContinuousPayTickets) + if dif == 0 { // 当天已经领取 + pack.Draw = 1 + } else if continuousPay.Break == WelfareOpen && dif > 1 && p.WelfData.ContinuousPayDay < max { // 开启中断 + day = 0 //continuousPay.List[0].Day // 从头开始 + } + } + + if pack.Draw == 2 { // 当天未领取判断 + cyc := this.ContinuousPayCycle[p.Platform] + for i, v := range continuousPay.List { + if v.Day == day || day == 0 || day >= max { + if day != 0 { // 为0领取第一个 day不为0代表已经领取到该礼包 需要领取下一个 + i++ + } + if day >= max { + i = ln + } + if i < ln { + day = continuousPay.List[i].Day + } else { + if continuousPay.Cycle != WelfareOpen && cyc != 1 { // 全部领取完就发最后一天 + day = max + pack.Draw = 1 // 不循环 领完不能再领取了 + } else { + day = continuousPay.List[0].Day // 从第一天开始 + } + } + break + } + } + + } + pack.Day = day + pack.Cycle = continuousPay.Cycle + for _, v := range continuousPay.List { + + data := &welfare.WelfareSpree{ + Day: v.Day, + // VIPEX: v.VIPEX, + Consume: v.Consume, + Price1: v.Price1, + Price2: v.Price2, + Discount: v.Discount, + } + for _, it := range v.Item { + data.Item = append(data.Item, &welfare.WelfareDate{ + Grade: it.Grade, + Type: it.Type, + Name: it.Name, + Item_Id: it.Item_Id, + }) + } + pack.List = append(pack.List, data) + } + pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + } + } + logger.Logger.Tracef("ContinuousPayInfo snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(welfare.SPacketID_PACKET_SC_WELF_CONTINPAYINFO), pack) +} + +// 默认已经成功 +func (this *WelfareMgr) BuyContinuousPay(p *Player, ConfigPayId int32) { + // this.MonitorWelfData(p) + //pack := &welfare.SCWelfareContinuousPay{ + // OpRetCode: welfare.OpResultCode_OPRC_Error, + //} + pack := &shop.SCPayInfo{ + RetCode: shop.OpResultCode_OPRC_Error, + } + continuousPay := this.ContinuousPay[p.Platform] + if continuousPay == nil { + logger.Logger.Tracef("BuyContinuousPay continuousPay == nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + ts := time.Now().Unix() + + ln := len(continuousPay.List) + if ln == 0 { // 不能为空 + logger.Logger.Tracef("BuyContinuousPay firstPay.List err p.SnId = %v %v", p.SnId, ln) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + max := continuousPay.List[ln-1].Day // 正序 + + if p.WelfData.ContinuousPayDay != 0 { // 查看是否已经领取 + dif := common.DiffDaybyTs(ts, p.WelfData.ContinuousPayTickets) + if dif == 0 { // 当天已经领取 + logger.Logger.Tracef("BuyContinuousPay LastTickets is repeat p.SnId = %v %v", p.SnId, p.WelfData.ContinuousPayTickets) + //pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } else if continuousPay.Break == WelfareOpen && dif > 1 && p.WelfData.ContinuousPayDay < max { // 开启中断 且没有领取到最后一天 + p.WelfData.ContinuousPayDay = 0 // 从第一天开始 + } + } + + if p.WelfData.ContinuousPayDay >= max { + cyc := this.ContinuousPayCycle[p.Platform] + if continuousPay.Cycle != WelfareOpen && cyc != 1 { // 全部领取完 + //pack.OpRetCode = welfare.OpResultCode_OPRC_NoTimes + logger.Logger.Tracef("BuyContinuousPay Cycle p.WelfData.FirstPayDay max p.SnId = %v %v", p.SnId, continuousPay.Cycle, p.WelfData.ContinuousPayDay, max) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + p.WelfData.ContinuousPayDay = 0 + } + var wfs *webapi_proto.WelfareSpree + for i, v := range continuousPay.List { + + if v.Day == p.WelfData.ContinuousPayDay || p.WelfData.ContinuousPayDay == 0 { + if p.WelfData.ContinuousPayDay != 0 { // 为0领取第一个 p.WelfData.FirstPayDay不为0代表已经领取到该礼包 需要领取下一个 + i++ + } + wfs = v + + //data := continuousPay.List[i] // 越界条件已判断 + // + //p.WelfData.ContinuousPayDay = data.Day + //p.WelfData.ContinuousPayTickets = ts + //// TODO + //p.AddMoneyPayTotal(v.Price2) + //gainWay := int32(common.GainWay_ActContinuousPay) + //oper, remark := "system", "连续充值奖励" + //DrawWelfareDate(data.Item, p, gainWay, oper, remark) // 领取奖励 + //var item []*model.WelfareItem + //for _, v := range data.Item { + // item = append(item, &model.WelfareItem{ + // Num: int64(v.Grade), + // ItemId: v.Item_Id, + // Type: v.Type, + // }) + //} + //MoneyRatio := float64(0) + //if vips := VipMgrSington.GetVIPcfg(p.Platform); vips != nil { + // MoneyRatio = vips.MoneyRatio + //} + //vipEx := int64(data.VIPEX) + //if MoneyRatio > 0.000001 { + // vipEx = int64(float64(v.Price2) * MoneyRatio) + //} + //this.RecordWelfareLog(p, vipEx, v.Price2, int64(v.Discount*10000), p.WelfData.FirstPayDay, gainWay, model.WelfareBuyFirstPay, + // item, oper, remark) + // + //pack.OpRetCode = welfare.OpResultCode_OPRC_Sucess + break + } + } + if wfs == nil || wfs.Item == nil { + logger.Logger.Tracef("BuyContinuousPay wfs == nil snid: %v pack: %v", p.SnId, pack) + p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + return + } + ShopMgrSington.SendAPICreateOrder(p, ConfigPayId, wfs, "ContinuousPay") + //三方购买 + //task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + // var amount [3]int32 + // for _, it := range wfs.Item { + // if it.Type == 1 { + // amount[0] = it.Grade + // } else if it.Type == 2 { + // amount[1] = it.Grade + // } + // } + // dbShop := model.NewDbShop(p.Platform, 0, amount[:], Shop_Consume_Money, int32(wfs.Price2), + // nil, 0, "", p.SnId, 0, "ContinuousPay") + // err := model.InsertDbShopLog(dbShop) + // if err != nil { + // logger.Logger.Errorf("model.InsertDbShopLog err:", err) + // return nil + // } + // return webapi.API_CreateOrder(common.GetAppId(), dbShop.LogId.Hex(), ConfigPayId, p.SnId, 0, p.Platform, p.PackageID, p.DeviceOS, + // p.DeviceId, "ContinuousRecharge", amount, int32(wfs.Price2), nil) + //}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { + // logger.Logger.Trace("API_CreateOrder_BuyContinuousPay:", data) + // info := data.(*webapi_proto.ASCreateOrder) + // if info == nil || info.Tag != 0 { + // //失败 + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } else { + // pack.RetCode = shop.OpResultCode_OPRC_Sucess + // pack.Url = info.Url + // p.SendToClient(int(shop.SPacketID_PACKET_SCPAYINFO), pack) + // } + //}), "API_CreateOrder_BuyContinuousPay").Start() +} + +func (this *WelfareMgr) Update() { + +} + +func (this *WelfareMgr) Shutdown() { + module.UnregisteModule(this) +} + +func (this *WelfareMgr) OnDayTimer() { + //logger.Logger.Tracef("WelfareMgr OnDayTimer") + this.BlindBoxCycle = make(map[string]int32) + this.FirstPayCycle = make(map[string]int32) + this.ContinuousPayCycle = make(map[string]int32) +} + +func (this *WelfareMgr) InterestClockEvent() int { + + return (1 << CLOCK_EVENT_MAX) - 1 +} + +func init() { + module.RegisteModule(WelfareMgrSington, time.Second, 0) + ClockMgrSington.RegisteSinker(WelfareMgrSington) +}