├── Excel ├── BossWorld.xlsm ├── Buffs.xlsm ├── Effect.xlsm ├── Goods.xlsm ├── Maps.xlsm ├── Monster.xlsm ├── MonsterBehavior.xlsm ├── MonsterProp.xlsm ├── Skill.xlsm ├── SkillEffect.xlsm ├── _打包单个配置.bat ├── ~$Buffs.xlsm ├── ~$Skill.xlsm └── ~$SkillEffect.xlsm ├── LICENSE ├── Makefile ├── README.md ├── boot └── boot.go ├── config ├── def.go ├── func.go ├── json │ ├── BossWorld.json │ ├── Buffs.json │ ├── Effect.json │ ├── Goods.json │ ├── Maps.json │ ├── Monster.json │ ├── MonsterBehavior.json │ ├── MonsterProp.json │ ├── Skill.json │ ├── SkillEffect.json │ └── system │ │ └── Server.json └── maps │ └── 101 ├── global ├── build_debug.go ├── build_release.go ├── const.go ├── db_table.go ├── function.go ├── game.go ├── gw.go ├── lang │ ├── cn.go │ ├── const.go │ ├── en.go │ └── tw.go ├── map_mod.go ├── node.go ├── node_proto.go ├── pb_def.go └── player_mod.go ├── go.mod ├── go.sum ├── gw ├── gw_cache.go ├── gw_client.go ├── gw_lib.go └── gw_login.go ├── hotfix └── 20210119 │ └── 20210119.go ├── lib ├── db.go ├── degree.go ├── goods.go ├── lib.go ├── p_msg_encode.go ├── props.go └── type.go ├── main.exe ├── main.go ├── maps ├── maps.go ├── maps_actor.go ├── maps_agent.go ├── maps_aoi.go ├── maps_aoi_const.go ├── maps_app.go ├── maps_astar.go ├── maps_broadcast.go ├── maps_buff.go ├── maps_config.go ├── maps_misc.go ├── maps_monster.go ├── maps_monster_behavior.go ├── maps_move.go ├── maps_node_proto.go ├── maps_prop.go ├── maps_role.go ├── maps_skill.go ├── maps_skill_callback.go ├── maps_skill_effect.go ├── maps_svr.go ├── mod │ └── mod_common.go └── type.go ├── player ├── attr.go ├── bag.go ├── const.go ├── hook.go ├── login.go ├── map.go ├── plalyer.go ├── props.go ├── role.go ├── skill.go ├── transaction.go └── type.go ├── proto ├── pb_auto.go ├── proto.go └── router │ └── router.go ├── service └── sys_account_server.go ├── start.sh ├── test_node.go └── tool ├── exporter ├── build.bat ├── excel │ ├── Def.go │ ├── excel.go │ └── type.go ├── main.go └── toGo │ └── to-go.go └── pbBuild ├── build.bat ├── main.go ├── parser ├── field.go ├── parser.go └── type.go ├── to_lua_client.go └── to_server.go /Excel/BossWorld.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/BossWorld.xlsm -------------------------------------------------------------------------------- /Excel/Buffs.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Buffs.xlsm -------------------------------------------------------------------------------- /Excel/Effect.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Effect.xlsm -------------------------------------------------------------------------------- /Excel/Goods.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Goods.xlsm -------------------------------------------------------------------------------- /Excel/Maps.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Maps.xlsm -------------------------------------------------------------------------------- /Excel/Monster.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Monster.xlsm -------------------------------------------------------------------------------- /Excel/MonsterBehavior.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/MonsterBehavior.xlsm -------------------------------------------------------------------------------- /Excel/MonsterProp.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/MonsterProp.xlsm -------------------------------------------------------------------------------- /Excel/Skill.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/Skill.xlsm -------------------------------------------------------------------------------- /Excel/SkillEffect.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/SkillEffect.xlsm -------------------------------------------------------------------------------- /Excel/_打包单个配置.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/Excel/_打包单个配置.bat -------------------------------------------------------------------------------- /Excel/~$Buffs.xlsm: -------------------------------------------------------------------------------- 1 | 4399 4399 -------------------------------------------------------------------------------- /Excel/~$Skill.xlsm: -------------------------------------------------------------------------------- 1 | 4399 4399 -------------------------------------------------------------------------------- /Excel/~$SkillEffect.xlsm: -------------------------------------------------------------------------------- 1 | 4399 4399 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2021 liangmanlin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | # print-% : ;@echo $* = $($*) 3 | .PHONY: all release dialyzer clean 4 | MAKEFLAGS+= --no-print-director 5 | SERVER_ROOT := . 6 | ifndef OUT_DIR 7 | OUT_DIR := $(SERVER_ROOT)/bin 8 | endif 9 | 10 | ifndef DEBUG 11 | DEBUG := debug 12 | endif 13 | 14 | ifndef LG 15 | LG := cn 16 | endif 17 | 18 | NOTMain := $(wildcard tool/*/*.go) 19 | 20 | HotFix := $(sort $(wildcard hotfix/*)) 21 | HotFix := $(word $(words $(HotFix)),$(HotFix)) 22 | HotFix := $(HotFix:%=%/*.go) 23 | HotFix := $(wildcard $(HotFix)) 24 | 25 | SOURCES := $(wildcard *.go */*.go */*/*.go) 26 | 27 | HotFixOut := $(HotFix:%.go=$(OUT_DIR)/%.so) 28 | 29 | SOURCES := $(filter-out $(NOTMain) $(HotFix),$(SOURCES)) 30 | 31 | PROTO_TOOL := $(SERVER_ROOT)/tool/bin/pbBuild 32 | PROTO_SRC := $(wildcard $(SERVER_ROOT)/tool/pbBuild/*.go $(SERVER_ROOT)/tool/pbBuild/*/*.go) 33 | 34 | all: mk_dir $(PROTO_TOOL) $(SERVER_ROOT)/proto/pb_auto.go $(OUT_DIR)/main $(HotFixOut) 35 | 36 | $(PROTO_TOOL): $(PROTO_SRC) 37 | go build -o $@ $(SERVER_ROOT)/tool/pbBuild/*.go 38 | 39 | $(SERVER_ROOT)/proto/pb_auto.go: $(SERVER_ROOT)/global/pb_def.go $(PROTO_TOOL) 40 | @($(PROTO_TOOL) -f $< -o $(SERVER_ROOT)/proto) 41 | 42 | $(OUT_DIR)/main: $(SOURCES) 43 | go build -tags "$(DEBUG) $(LG)" -o $@ main.go 44 | 45 | $(OUT_DIR)/%.so: %.go 46 | go build -tags "$(DEBUG) $(LG)" -buildmode=plugin -o $@ $< 47 | 48 | release: clean 49 | $(MAKE) DEBUG=release LG=$(LG) 50 | 51 | mk_dir: 52 | @(mkdir -p $(OUT_DIR)) 53 | @(mkdir -p $(OUT_DIR)/hotfix) 54 | 55 | clean: 56 | @(rm -rf $(OUT_DIR)/main) 57 | @(rm -rf $(OUT_DIR)/hotfix/*) 58 | @(echo ****clean****) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 使用golang开发的框架 2 | 3 | > **** 4 | ## How? 5 | 6 | - 参考 [main.go](main.go) 7 | 8 | ## Features 9 | 10 | ### `kernel`使用 channel 和 goroutine 模拟的`Actor`模式 11 | * 使用`channel`模拟的消息队列 12 | * `kernel.Context`内部实现了一个`ringbuf`,用于发起call的时候,由于自身channel满,导致双向阻塞 13 | 14 | 15 | ### 乞丐版otp框架,核心package:`kernel` 16 | * 提供了简略版的`supervisor` 17 | * 提供类型`gen_server`的进程回调 18 | * 内置`logger`模块 19 | * 支持local名字注册 20 | * 支持进程`link` 21 | * 内置全局定时器`timer` 22 | * 提交交互式命令行工具,你可以自己开发自己的`debug`命令 23 | 24 | 25 | ### 框架提供一种热更新方案 26 | * 详情请参考`hotfix`相关代码 27 | * how to use? 28 | * 参考`hotfix/20210119/20210119.go`编写更新代码,编译通过后 29 | ```bash 30 | ./gshell -node game@127.0.0.1 -cookie 6d27544c07937e4a7fab8123291cc4df -cmd "reload 20210119" 31 | ``` 32 | * 你也可以通过启动交互命令行环境进行更新 33 | 34 | * 其中`reload`的逻辑由`main.go`中的`func reload(echo func(s string), commands []string) string`提供 35 | 36 | 37 | ### 内置`mysql`支持 38 | * 引用如下代码开始使用 39 | ```golang 40 | import ( 41 | "github.com/liangmanlin/gootp/db" 42 | ) 43 | 44 | tabSlice := []*db.TabDef{ 45 | {Name: "account", DataStruct: &global.Account{}, Pkey: []string{"Account"}, Keys: []string{"AgentID"}}, 46 | } 47 | db.Env.DBConfig = db.Config{Host: "127.0.0.1", Port: 3306, User: "root", PWD: "123456"} 48 | 49 | db.Start(tabSlice, "dbName", "logDbName") 50 | ``` 51 | 52 | ### 内置一种定长协议 53 | 54 | * 定义文件:`global/pb_def.go` 55 | ```golang 56 | type LoginTosLogin struct { // router:LoginLogin 57 | Account string 58 | } 59 | 60 | type LoginTocLogin struct { 61 | ID int32 62 | Name string 63 | F float32 64 | M map[int32]int32 65 | S []string 66 | } 67 | ``` 68 | 69 | * 核心实现package:`github.com/liangmanlin/gootp/gate/pb` 70 | 71 | * 辅助工具:`tool/pbBuild` 72 | 73 | * `router`是工具生成的,仅仅提供路由到`player`包的函数 74 | 75 | * 目前可以到处lua代码直接使用(仅支持lua5.3及以上版本) 76 | * 使用如下脚本到处lua代码 77 | ```bat 78 | %% 假设在game目录 79 | tool\bin\pbBuild.exe -f ./global/pb_def.go -c client 80 | ``` 81 | 82 | * 项目内 **require** 所有导出文件;接着可以如下: 83 | ```lua 84 | local function test() 85 | local tos = LoginTosLogin() 86 | local sendBuf = tos.encode() 87 | -- 然后你可以同网络发送这个sendBuf到服务器 88 | 89 | -- 假设接收到服务器发来的数据 90 | local recBuff 91 | ---@type LoginTocLogin 92 | local proto,protoID = ProtoDecode(recBuff) 93 | -- proto即为解析到的对象 94 | local a = proto.F 95 | end 96 | ``` 97 | 98 | 99 | ### 框架内置一个网关:`gate` 100 | 101 | * 通过如下方式启动 102 | ```golang 103 | import "github.com/liangmanlin/gootp/gate" 104 | 105 | gate.Start(flag string, handler *kernel.Actor, port int, opt ...interface{}) 106 | 107 | ``` 108 | 109 | * 建议结合内置的定长协议使用 110 | 111 | 112 | ### 2021.1.28 添加分布式多节点支持 113 | * 目前不支持在**windows**中运行多节点 114 | * `Pid`支持跨节点传输 115 | * 你可以往另外一个节点发送一个本地`pid`,然后在那个节点上调用`call`和`cast` 116 | * 向一个远程节点发送数据需要先定义协议,例如: 117 | ```golang 118 | import "game/node" 119 | // 目前尚未找到一种可以直接发送对象的办法,所以这个是一种妥协 120 | // 即使使用grpc,也是要定义一个proto,所以依然选择使用内置协议,减少一层转换 121 | nodeProto := []interface{}{&global.TestCall{},&global.StrArgs{},&global.RpcStrResult{}} 122 | Cookie := "6d27544c07937e4a7fab8123291cc4df" 123 | node.Start(global.Root, "game@127.0.0.1", Cookie, nodeProto) 124 | 125 | // 更推荐使用命令行方式启动 126 | node.StartFromCommandLine(nodeProto) 127 | ``` 128 | * 支持`RpcCall` 129 | * 目前是单线程执行的,如果执行的函数会阻塞,尽量不要使用 130 | * 需要自行调用`node.RpcRegister(fun string,function RpcFunc)`注册 131 | 132 | ### 2021.2.23 添加excel配置文件支持 133 | 134 | * 具体使用参考 Excel/Effect.xlsm 配置文件 135 | * 如需导出配置,请双击 `Excel/_打包单个配置.bat` 136 | 137 | * 支持热更新配置 138 | ```bash 139 | ## 以热更 Skill 配置为例 140 | ./gshell -node game@127.0.0.1 -cookie 6d27544c07937e4a7fab8123291cc4df -cmd "reload_config Skill" 141 | ``` 142 | 143 | ### 2021.4.1 抽离大部分公共代码到一个代码仓库,命名为 `gootp` 144 | 145 | - 同时移除原来使用一个函数对象结构体的做法,直接使用包的全局变量代替导出函数, 146 | 这样也可以达到热更新的目的,并且代码编写会轻松一点 147 | 148 | ## Problem 149 | * 为了热更新,框架使用起来比较繁琐,期待有人提出更好的优化方案 150 | * 为了热更新,框架使用大量全局变量、函数指针,会降低函数的执行效率 151 | * `gate` 的实现是直接使用net包实现的,为了异步接收数据,开启了一个goroutine,后续是优化方向 152 | 153 | 154 | ## Future 155 | * `db`添加缓存 -------------------------------------------------------------------------------- /boot/boot.go: -------------------------------------------------------------------------------- 1 | package boot 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "log" 6 | ) 7 | 8 | type Boot struct { 9 | Svr *kernel.Actor 10 | Name string 11 | } 12 | 13 | func StartService(sup *kernel.Pid, bootList []Boot) { 14 | for _, s := range bootList { 15 | child := &kernel.SupChild{ChildType: kernel.SupChildTypeWorker, Name: s.Name, Svr: s.Svr, ReStart: true} 16 | err, _ := kernel.SupStartChild(sup, child) 17 | if err != nil { 18 | log.Panic(err) 19 | }else{ 20 | kernel.ErrorLog("%s started",s.Name) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/func.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "path/filepath" 8 | "strings" 9 | "sync/atomic" 10 | "unsafe" 11 | ) 12 | 13 | func sliceToString(keys []interface{}) string { 14 | if len(keys) == 1 { 15 | switch v2 := keys[0].(type) { 16 | case string: 17 | return v2 18 | case int8, int16, int, int32, int64, uint8, uint16, uint32, uint64: 19 | return fmt.Sprintf("%d", v2) 20 | } 21 | return "" 22 | } 23 | var l []string 24 | for _, v := range keys { 25 | switch v2 := v.(type) { 26 | case string: 27 | l = append(l, v2) 28 | case int8, int16, int, int32, int64, uint8, uint16, uint32, uint64: 29 | l = append(l, fmt.Sprintf("%d", v2)) 30 | } 31 | } 32 | return fmt.Sprintf("{%s}", strings.Join(l, ",")) 33 | } 34 | 35 | // 加载所有配置 36 | func LoadAll(path string) { 37 | fs, err := ioutil.ReadDir(path) 38 | if err != nil { 39 | panic(err) 40 | } 41 | for _, f := range fs { 42 | if !f.IsDir() { 43 | ext := filepath.Ext(f.Name()) 44 | if ext == ".json" { 45 | baseName := strings.TrimSuffix(f.Name(), ext) 46 | if p, ok := PtrMap[baseName]; ok { 47 | p.load(path) 48 | } 49 | } 50 | } else { 51 | LoadAll(path + "/" + f.Name()) 52 | } 53 | } 54 | } 55 | 56 | func Load(name, path string) bool { 57 | if f, ok := PtrMap[name]; ok { 58 | f.load(path) 59 | return true 60 | } 61 | return false 62 | } 63 | 64 | func readFile(name, path string) []byte { 65 | file := fmt.Sprintf("%s/%s.json", path, name) 66 | buf, err := ioutil.ReadFile(file) 67 | if err != nil { 68 | panic(err) 69 | } 70 | return buf 71 | } 72 | 73 | var PtrMap = make(map[string]configFunc) 74 | 75 | type configFunc interface { 76 | load(path string) 77 | } 78 | 79 | type serverMapType map[string]interface{} 80 | 81 | var Server serverMapType 82 | 83 | func (S serverMapType) Get(key string) interface{} { 84 | return S[key] 85 | } 86 | func (S serverMapType) GetListStr(key string, idx int) string { 87 | if c, ok := S[key]; ok { 88 | return c.([]interface{})[idx].(string) 89 | } 90 | return "" 91 | } 92 | func (S serverMapType) GetListInt(key string, idx int) int { 93 | if c, ok := S[key]; ok { 94 | return int(c.([]interface{})[idx].(float64)) 95 | } 96 | return 0 97 | } 98 | func (S serverMapType) GetStr(key string) string { 99 | if c, ok := S[key]; ok { 100 | return c.(string) 101 | } 102 | return "" 103 | } 104 | func (S serverMapType) GetInt(key string) int { 105 | if c, ok := S[key]; ok { 106 | return int(c.(float64)) 107 | } 108 | return 0 109 | } 110 | func (S serverMapType) load(path string) { 111 | s := make(map[string]interface{}) 112 | if err := json.Unmarshal(readFile("Server", path), &s); err != nil { 113 | panic(err) 114 | } 115 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&Server)), *(*unsafe.Pointer)(unsafe.Pointer(&s))) 116 | } 117 | -------------------------------------------------------------------------------- /config/json/BossWorld.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Type_id":301001,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":118,"Y":120,"Tire":0,"Sp_drop":[]}, 3 | {"Type_id":301002,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":119,"Y":183,"Tire":0,"Sp_drop":[]}, 4 | {"Type_id":301003,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":120,"Y":58,"Tire":0,"Sp_drop":[]}, 5 | {"Type_id":301004,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":57,"Y":121,"Tire":0,"Sp_drop":[]}, 6 | {"Type_id":301005,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":242,"Y":120,"Tire":0,"Sp_drop":[]}, 7 | {"Type_id":301006,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":240,"Y":184,"Tire":0,"Sp_drop":[]}, 8 | {"Type_id":301007,"Map_id":301,"Dec_lv":60,"Refresh_time":30,"X":240,"Y":60,"Tire":0,"Sp_drop":[]}, 9 | {"Type_id":302001,"Map_id":302,"Dec_lv":60,"Refresh_time":600,"X":118,"Y":120,"Tire":1,"Sp_drop":[]}, 10 | {"Type_id":302002,"Map_id":302,"Dec_lv":60,"Refresh_time":600,"X":119,"Y":183,"Tire":1,"Sp_drop":[]}, 11 | {"Type_id":302003,"Map_id":302,"Dec_lv":60,"Refresh_time":600,"X":120,"Y":58,"Tire":1,"Sp_drop":[]}, 12 | {"Type_id":302004,"Map_id":302,"Dec_lv":60,"Refresh_time":600,"X":57,"Y":121,"Tire":1,"Sp_drop":[]}, 13 | {"Type_id":302005,"Map_id":302,"Dec_lv":60,"Refresh_time":600,"X":242,"Y":120,"Tire":1,"Sp_drop":[]}, 14 | {"Type_id":302006,"Map_id":302,"Dec_lv":60,"Refresh_time":900,"X":240,"Y":184,"Tire":1,"Sp_drop":[]}, 15 | {"Type_id":302007,"Map_id":302,"Dec_lv":60,"Refresh_time":900,"X":240,"Y":60,"Tire":1,"Sp_drop":[]}, 16 | {"Type_id":302008,"Map_id":302,"Dec_lv":60,"Refresh_time":900,"X":302,"Y":118,"Tire":1,"Sp_drop":[]}, 17 | {"Type_id":303001,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":118,"Y":120,"Tire":1,"Sp_drop":[]}, 18 | {"Type_id":303002,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":119,"Y":183,"Tire":1,"Sp_drop":[]}, 19 | {"Type_id":303003,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":120,"Y":58,"Tire":1,"Sp_drop":[]}, 20 | {"Type_id":303004,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":57,"Y":121,"Tire":1,"Sp_drop":[]}, 21 | {"Type_id":303005,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":242,"Y":120,"Tire":1,"Sp_drop":[]}, 22 | {"Type_id":303006,"Map_id":303,"Dec_lv":60,"Refresh_time":900,"X":240,"Y":184,"Tire":1,"Sp_drop":[]}, 23 | {"Type_id":303007,"Map_id":303,"Dec_lv":60,"Refresh_time":1200,"X":240,"Y":60,"Tire":1,"Sp_drop":[]}, 24 | {"Type_id":303008,"Map_id":303,"Dec_lv":60,"Refresh_time":1200,"X":302,"Y":118,"Tire":1,"Sp_drop":[]}, 25 | {"Type_id":304001,"Map_id":304,"Dec_lv":60,"Refresh_time":1200,"X":118,"Y":120,"Tire":1,"Sp_drop":[]}, 26 | {"Type_id":304002,"Map_id":304,"Dec_lv":60,"Refresh_time":1200,"X":119,"Y":183,"Tire":1,"Sp_drop":[]}, 27 | {"Type_id":304003,"Map_id":304,"Dec_lv":60,"Refresh_time":1200,"X":120,"Y":58,"Tire":1,"Sp_drop":[]}, 28 | {"Type_id":304004,"Map_id":304,"Dec_lv":60,"Refresh_time":1200,"X":57,"Y":121,"Tire":1,"Sp_drop":[]}, 29 | {"Type_id":304005,"Map_id":304,"Dec_lv":60,"Refresh_time":1200,"X":242,"Y":120,"Tire":1,"Sp_drop":[]}, 30 | {"Type_id":304006,"Map_id":304,"Dec_lv":0,"Refresh_time":60,"X":240,"Y":184,"Tire":1,"Sp_drop":[]}, 31 | {"Type_id":304007,"Map_id":304,"Dec_lv":0,"Refresh_time":60,"X":240,"Y":60,"Tire":1,"Sp_drop":[]}, 32 | {"Type_id":304008,"Map_id":304,"Dec_lv":0,"Refresh_time":60,"X":302,"Y":118,"Tire":1,"Sp_drop":[]}, 33 | {"Type_id":305001,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":118,"Y":120,"Tire":1,"Sp_drop":[]}, 34 | {"Type_id":305002,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":119,"Y":183,"Tire":1,"Sp_drop":[]}, 35 | {"Type_id":305003,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":120,"Y":58,"Tire":1,"Sp_drop":[]}, 36 | {"Type_id":305004,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":57,"Y":121,"Tire":1,"Sp_drop":[]}, 37 | {"Type_id":305005,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":242,"Y":120,"Tire":1,"Sp_drop":[]}, 38 | {"Type_id":305006,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":240,"Y":184,"Tire":1,"Sp_drop":[]}, 39 | {"Type_id":305007,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":240,"Y":60,"Tire":1,"Sp_drop":[]}, 40 | {"Type_id":305008,"Map_id":305,"Dec_lv":0,"Refresh_time":60,"X":302,"Y":118,"Tire":1,"Sp_drop":[]} 41 | ] -------------------------------------------------------------------------------- /config/json/Buffs.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":1,"BuffType":1,"BuffLV":1,"BuffValue":3000,"LastTime":500,"EffectType":0,"EffectFun":"","SumType":3,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":0,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 3 | {"Id":2,"BuffType":2,"BuffLV":1,"BuffValue":3000,"LastTime":5000,"EffectType":0,"EffectFun":"","SumType":0,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":1,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 4 | {"Id":3,"BuffType":3,"BuffLV":1,"BuffValue":100,"LastTime":3600000,"EffectType":1,"EffectFun":"add_attack","SumType":1,"InvType":1,"Inv":0,"OpFun":"","IsFight":0,"IsDebuff":0,"NoticeType":0,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 5 | {"Id":4,"BuffType":4,"BuffLV":1,"BuffValue":-4000,"LastTime":2000,"EffectType":1,"EffectFun":"move_speed_rate","SumType":4,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":1,"NoticeType":1,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 6 | {"Id":5,"BuffType":5,"BuffLV":1,"BuffValue":5000,"LastTime":10000,"EffectType":1,"EffectFun":"move_speed_rate","SumType":4,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":0,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 7 | {"Id":6,"BuffType":6,"BuffLV":1,"BuffValue":0,"LastTime":1000,"EffectType":0,"EffectFun":"","SumType":5,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":1,"NoticeType":1,"ChangeMapDel":0,"AvoidList":[8],"CleanList":[],"NoMmove":1,"NoSkill":1}, 8 | {"Id":7,"BuffType":7,"BuffLV":1,"BuffValue":1000,"LastTime":5000,"EffectType":1,"EffectFun":"add_attack_rate","SumType":4,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":1,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 9 | {"Id":8,"BuffType":7,"BuffLV":1,"BuffValue":1000,"LastTime":7000,"EffectType":1,"EffectFun":"add_attack_rate","SumType":4,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":1,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0}, 10 | {"Id":9,"BuffType":8,"BuffLV":1,"BuffValue":0,"LastTime":1000,"EffectType":0,"EffectFun":"","SumType":3,"InvType":1,"Inv":0,"OpFun":"","IsFight":1,"IsDebuff":0,"NoticeType":0,"ChangeMapDel":0,"AvoidList":[],"CleanList":[],"NoMmove":0,"NoSkill":0} 11 | ] -------------------------------------------------------------------------------- /config/json/Goods.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Type_id":10000001,"Name":"test","Color":1,"Pile_num":99}, 3 | {"Type_id":10000002,"Name":"test","Color":1,"Pile_num":99} 4 | ] -------------------------------------------------------------------------------- /config/json/Maps.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":101,"Type":1,"Level":1,"BlockPath":101,"PkModel":[1],"BornX":156,"BornY":48} 3 | ] -------------------------------------------------------------------------------- /config/json/Monster.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":101001,"Name":"野猴子","Type":1,"Level":2,"Behavior":20001,"NotReduceHP":0,"AttackSpeed":2000,"PropID":101001,"GuardRadius":15,"AttackRadius":3,"ActivityRadius":15,"Skills":[],"RefreshTime":1000,"Drops":[],"Exp":0,"Gold":0,"Radius":1,"EventList":[],"FightPower":0}, 3 | {"Id":101002,"Name":"野蛮人","Type":1,"Level":5,"Behavior":20001,"NotReduceHP":0,"AttackSpeed":2000,"PropID":101002,"GuardRadius":15,"AttackRadius":3,"ActivityRadius":15,"Skills":[],"RefreshTime":1000,"Drops":[],"Exp":0,"Gold":0,"Radius":1,"EventList":[],"FightPower":0}, 4 | {"Id":101003,"Name":"秃鹫","Type":1,"Level":14,"Behavior":20001,"NotReduceHP":0,"AttackSpeed":2000,"PropID":101003,"GuardRadius":15,"AttackRadius":3,"ActivityRadius":15,"Skills":[],"RefreshTime":1000,"Drops":[],"Exp":0,"Gold":0,"Radius":1,"EventList":[],"FightPower":0}, 5 | {"Id":101004,"Name":"老虎","Type":1,"Level":1,"Behavior":20001,"NotReduceHP":0,"AttackSpeed":2000,"PropID":101004,"GuardRadius":5,"AttackRadius":3,"ActivityRadius":15,"Skills":[],"RefreshTime":1000,"Drops":[],"Exp":0,"Gold":0,"Radius":1,"EventList":[],"FightPower":0} 6 | ] -------------------------------------------------------------------------------- /config/json/MonsterBehavior.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":10001,"Type":1,"Func":"fight","Arg":[],"Child":[]}, 3 | {"Id":10002,"Type":2,"Func":"searchEnemyNeighbor","Arg":[1,0],"Child":[10001]}, 4 | {"Id":10003,"Type":4,"Func":"","Arg":[],"Child":[10002,20002]}, 5 | {"Id":10004,"Type":1,"Func":"fight","Arg":[],"Child":[]}, 6 | {"Id":10005,"Type":2,"Func":"searchEnemyInmap","Arg":[1,0],"Child":[10001]}, 7 | {"Id":20001,"Type":3,"Func":"patrol","Arg":[10000],"Child":[]}, 8 | {"Id":20002,"Type":3,"Func":"patrol2","Arg":[10000],"Child":[]}, 9 | {"Id":20003,"Type":3,"Func":"patrol","Arg":[10000],"Child":[10004]}, 10 | {"Id":20004,"Type":1,"Func":"none","Arg":[],"Child":[]}, 11 | {"Id":20005,"Type":3,"Func":"none","Arg":[],"Child":[20004]}, 12 | {"Id":20006,"Type":1,"Func":"random_move","Arg":[],"Child":[]}, 13 | {"Id":20007,"Type":3,"Func":"patrol","Arg":[5000],"Child":[20006]} 14 | ] -------------------------------------------------------------------------------- /config/json/MonsterProp.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"ID":101001,"MaxHP":30,"PhyAttack":5,"ArmorBreak":0,"PhyDefence":4,"Hit":204,"Miss":0,"Crit":0,"Tenacity":0,"MoveSpeed":300,"HpRecover":0,"MaxHpRate":0,"AttackRate":0,"ArmorBreakRate":0,"DefenceRate":0,"HitAddRate":0,"MissAddRate":0,"CritAddRate":0,"TenacityAddRate":0,"DamageDeepen":0,"DamageDef":0,"HitRate":0,"MissRate":0,"CritRate":0,"CritDef":0,"CritValue":0,"CritValueDef":0,"ParryRate":0,"ParryOver":0,"HuixinRate":0,"HuixinDef":0,"PvpDamageDeepen":0,"PvpDamageDef":0,"PveDamageDeepen":0,"PveDamageDef":0,"TotalDef":0}, 3 | {"ID":101002,"MaxHP":90,"PhyAttack":8,"ArmorBreak":0,"PhyDefence":10,"Hit":216,"Miss":0,"Crit":0,"Tenacity":0,"MoveSpeed":300,"HpRecover":0,"MaxHpRate":0,"AttackRate":0,"ArmorBreakRate":0,"DefenceRate":0,"HitAddRate":0,"MissAddRate":0,"CritAddRate":0,"TenacityAddRate":0,"DamageDeepen":0,"DamageDef":0,"HitRate":0,"MissRate":0,"CritRate":0,"CritDef":0,"CritValue":0,"CritValueDef":0,"ParryRate":0,"ParryOver":0,"HuixinRate":0,"HuixinDef":0,"PvpDamageDeepen":0,"PvpDamageDef":0,"PveDamageDeepen":0,"PveDamageDef":0,"TotalDef":0}, 4 | {"ID":101003,"MaxHP":220,"PhyAttack":30,"ArmorBreak":0,"PhyDefence":28,"Hit":252,"Miss":0,"Crit":0,"Tenacity":0,"MoveSpeed":300,"HpRecover":0,"MaxHpRate":0,"AttackRate":0,"ArmorBreakRate":0,"DefenceRate":0,"HitAddRate":0,"MissAddRate":0,"CritAddRate":0,"TenacityAddRate":0,"DamageDeepen":0,"DamageDef":0,"HitRate":0,"MissRate":0,"CritRate":0,"CritDef":0,"CritValue":0,"CritValueDef":0,"ParryRate":0,"ParryOver":0,"HuixinRate":0,"HuixinDef":0,"PvpDamageDeepen":0,"PvpDamageDef":0,"PveDamageDeepen":0,"PveDamageDef":0,"TotalDef":0}, 5 | {"ID":101004,"MaxHP":1600,"PhyAttack":10,"ArmorBreak":0,"PhyDefence":40,"Hit":276,"Miss":1,"Crit":0,"Tenacity":0,"MoveSpeed":300,"HpRecover":0,"MaxHpRate":0,"AttackRate":0,"ArmorBreakRate":0,"DefenceRate":0,"HitAddRate":0,"MissAddRate":0,"CritAddRate":0,"TenacityAddRate":0,"DamageDeepen":0,"DamageDef":0,"HitRate":0,"MissRate":0,"CritRate":0,"CritDef":0,"CritValue":0,"CritValueDef":0,"ParryRate":0,"ParryOver":0,"HuixinRate":0,"HuixinDef":0,"PvpDamageDeepen":0,"PvpDamageDef":0,"PveDamageDeepen":0,"PveDamageDef":0,"TotalDef":0}, 6 | {"ID":101005,"MaxHP":370,"PhyAttack":4,"ArmorBreak":0,"PhyDefence":6,"Hit":208,"Miss":10,"Crit":0,"Tenacity":0,"MoveSpeed":300,"HpRecover":0,"MaxHpRate":0,"AttackRate":0,"ArmorBreakRate":0,"DefenceRate":0,"HitAddRate":0,"MissAddRate":0,"CritAddRate":0,"TenacityAddRate":0,"DamageDeepen":0,"DamageDef":0,"HitRate":0,"MissRate":0,"CritRate":0,"CritDef":0,"CritValue":0,"CritValueDef":0,"ParryRate":0,"ParryOver":0,"HuixinRate":0,"HuixinDef":0,"PvpDamageDeepen":0,"PvpDamageDef":0,"PveDamageDeepen":0,"PveDamageDef":0,"TotalDef":0} 7 | ] -------------------------------------------------------------------------------- /config/json/Skill.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":1,"SkillIndex":0,"PosType":3,"Dist":10,"SkillType":2,"BreakType":0,"Monster":0,"Role":0,"NASectionIndex":0,"SkillCD":1000,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[450],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":2,"A":3}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":800,"Effect":[[1,2],[1,2]],"AfterEffect":[],"PhaseCB":[[],[1,2]],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":1}, 3 | {"Id":100160,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":3,"BreakType":2,"Monster":4,"Role":2,"NASectionIndex":0,"SkillCD":6000,"MoveChangePos":0,"SkillMove":[{"StartTime":0,"Dist":800,"EndTime":300}],"SkillPhase":[],"SkillFlyFrames":[],"SkillShape":[],"SkillSpread":[],"SkillSpreadSpeed":[],"ContinueTime":350,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":3}, 4 | {"Id":100100,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":2,"BreakType":1,"Monster":6,"Role":4,"NASectionIndex":1,"SkillCD":400,"MoveChangePos":0,"SkillMove":[{"StartTime":133,"Dist":60,"EndTime":100}],"SkillPhase":[166],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":1,"B":7,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":400,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4}, 5 | {"Id":100101,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":2,"BreakType":1,"Monster":6,"Role":4,"NASectionIndex":2,"SkillCD":300,"MoveChangePos":0,"SkillMove":[{"StartTime":0,"Dist":100,"EndTime":166}],"SkillPhase":[100],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":1,"B":7,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":300,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4}, 6 | {"Id":100102,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":2,"BreakType":1,"Monster":6,"Role":4,"NASectionIndex":3,"SkillCD":366,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[100],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":1,"B":8,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":366,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4}, 7 | {"Id":100103,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":2,"BreakType":1,"Monster":6,"Role":4,"NASectionIndex":4,"SkillCD":800,"MoveChangePos":0,"SkillMove":[{"StartTime":0,"Dist":60,"EndTime":300}],"SkillPhase":[300],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":1,"B":7,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":800,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4}, 8 | {"Id":100109,"SkillIndex":0,"PosType":1,"Dist":0,"SkillType":1,"BreakType":2,"Monster":6,"Role":4,"NASectionIndex":0,"SkillCD":8000,"MoveChangePos":1,"SkillMove":[{"StartTime":0,"Dist":500,"EndTime":220}],"SkillPhase":[180],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":2,"B":7,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":400,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":7.5}, 9 | {"Id":100110,"SkillIndex":1,"PosType":1,"Dist":0,"SkillType":3,"BreakType":1,"Monster":6,"Role":4,"NASectionIndex":0,"SkillCD":8000,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[100,200,166,266,300],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":1,"A":2,"B":9,"C":5},{"ShapeType":1,"A":2,"B":9,"C":5},{"ShapeType":1,"A":2,"B":9,"C":5},{"ShapeType":1,"A":2,"B":9,"C":5},{"ShapeType":1,"A":2,"B":9,"C":5}],"SkillSpread":[2,2,2,2,2],"SkillSpreadSpeed":[],"ContinueTime":1200,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4.5}, 10 | {"Id":100120,"SkillIndex":2,"PosType":1,"Dist":0,"SkillType":3,"BreakType":1,"Monster":8,"Role":4,"NASectionIndex":0,"SkillCD":6000,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[200,100],"SkillFlyFrames":[3,3],"SkillShape":[{"ShapeType":1,"A":0,"B":3.5,"C":4},{"ShapeType":1,"A":1.31,"B":3.5,"C":4}],"SkillSpread":[1,1],"SkillSpreadSpeed":[{"Key":35,"Value":0},{"Key":35,"Value":0}],"ContinueTime":800,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":7}, 11 | {"Id":100130,"SkillIndex":3,"PosType":1,"Dist":0,"SkillType":3,"BreakType":1,"Monster":8,"Role":4,"NASectionIndex":0,"SkillCD":11000,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[667,533,400,400,400,400],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":2,"A":5},{"ShapeType":1,"A":3,"B":8,"C":5},{"ShapeType":1,"A":3,"B":8,"C":5},{"ShapeType":1,"A":3,"B":8,"C":5},{"ShapeType":1,"A":3,"B":8,"C":5},{"ShapeType":1,"A":3,"B":8,"C":5}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":1430,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":4.5}, 12 | {"Id":100140,"SkillIndex":4,"PosType":2,"Dist":10,"SkillType":3,"BreakType":2,"Monster":6,"Role":4,"NASectionIndex":0,"SkillCD":13000,"MoveChangePos":0,"SkillMove":[{"StartTime":100,"Dist":900,"EndTime":500}],"SkillPhase":[610],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":2,"A":4}],"SkillSpread":[2],"SkillSpreadSpeed":[],"ContinueTime":700,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":7}, 13 | {"Id":100150,"SkillIndex":5,"PosType":1,"Dist":0,"SkillType":3,"BreakType":1,"Monster":10,"Role":5,"NASectionIndex":0,"SkillCD":17000,"MoveChangePos":0,"SkillMove":[],"SkillPhase":[60,170,20,170,530],"SkillFlyFrames":[],"SkillShape":[{"ShapeType":2,"A":7}],"SkillSpread":[2,2,2,2,2],"SkillSpreadSpeed":[],"ContinueTime":1333,"Effect":[],"AfterEffect":[],"PhaseCB":[],"FlyEndCB":[],"ReleaseEffect":[],"AIRange":6} 14 | ] -------------------------------------------------------------------------------- /config/json/SkillEffect.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"Id":1001,"Type":1,"Func":"add_self_buff","Arg":[1,2000,1]}, 3 | {"Id":1002,"Type":1,"Func":"add_target_buff","Arg":[1,2000,1]}, 4 | {"Id":2001,"Type":2,"Func":"common_attack","Arg":[100,100,0]} 5 | ] -------------------------------------------------------------------------------- /config/json/system/Server.json: -------------------------------------------------------------------------------- 1 | { 2 | "c": "// 服务器id列表", 3 | "server_id": [1000], 4 | "c": "//[数据库域名,端口,User,PWD]", 5 | "db": [ 6 | "192.168.24.128", 7 | 3306, 8 | "tttt", 9 | "tttt" 10 | ], 11 | "game_db":"pkfr2", 12 | "log_db": "pkfr2_log", 13 | "c": "//游戏端口", 14 | "game_port": 4000 15 | } -------------------------------------------------------------------------------- /config/maps/101: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/config/maps/101 -------------------------------------------------------------------------------- /global/build_debug.go: -------------------------------------------------------------------------------- 1 | //+build debug 2 | 3 | package global 4 | 5 | const DEBUG = true 6 | -------------------------------------------------------------------------------- /global/build_release.go: -------------------------------------------------------------------------------- 1 | //+build !debug 2 | 3 | package global 4 | 5 | const DEBUG = false 6 | -------------------------------------------------------------------------------- /global/const.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | const ( 4 | DB_OP_ADD int32 = iota + 1 5 | DB_OP_UPDATE 6 | DB_OP_DELETE 7 | ) 8 | 9 | const GOODS_TYPE_DIV int32 = 10000000 10 | 11 | const ( 12 | GOODS_TYPE_ITEM int32 = iota + 1 // 普通道具 13 | ) 14 | 15 | const ( 16 | BACKUP_BAG_ID int32 = iota 17 | BACKUP_BAG_INSERT 18 | BACKUP_BAG_UPDATE_NUM 19 | BACKUP_BAG_DELETE 20 | BACKUP_ATTR 21 | ) 22 | 23 | const ROLEID_AGENT int64 = 1000000000000 24 | const ROLEID_SERVER int64 = 1000000 25 | 26 | const ( 27 | SYS_ACCOUNT_SERVER = "sys_account_server" 28 | ) 29 | 30 | const ( 31 | ACTOR_ROLE int8 = iota + 1 32 | ACTOR_MONSTER 33 | C_ACTOR_SIZE // 这个一动要在最后 34 | ) 35 | 36 | const CHECK_NODE_PROTO = true 37 | -------------------------------------------------------------------------------- /global/db_table.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import "github.com/liangmanlin/gootp/db" 4 | 5 | const ( 6 | TABLE_ROLE_ID_INDEX = "role_id_index" 7 | TABLE_ACCOUNT = "account" 8 | TABLE_BAG = "bag" 9 | TABLE_ROLE_ATTR = "role_attr" 10 | TABLE_ROLE_BASE = "role_base" 11 | TABLE_ROLE_PROP = "role_prop" 12 | TABLE_ROLE_MAP = "role_map" 13 | ) 14 | 15 | // 数据表需要在这里添加 16 | var DBTables = []*db.TabDef{ 17 | {Name: TABLE_ROLE_ID_INDEX, Pkey: []string{"AgentID", "ServerID"}, DataStruct: &RoleIDIndex{}}, 18 | {Name: TABLE_ACCOUNT, Pkey: []string{"Account", "AgentID", "ServerID"}, DataStruct: &Account{}}, 19 | {Name: TABLE_BAG, Pkey: []string{"RoleID", "ID"}, DataStruct: &Bag{}}, 20 | {Name: TABLE_ROLE_ATTR, Pkey: []string{"RoleID"}, DataStruct: &PRoleAttr{}}, 21 | {Name: TABLE_ROLE_BASE, Pkey: []string{"RoleID"}, DataStruct: &PRoleBase{}}, 22 | {Name: TABLE_ROLE_PROP, Pkey: []string{"RoleID"}, DataStruct: &RoleProp{}}, 23 | {Name: TABLE_ROLE_MAP, Pkey: []string{"RoleID"}, DataStruct: &RoleMap{}}, 24 | } 25 | 26 | type RoleIDIndex struct { 27 | AgentID int32 28 | ServerID int32 29 | Index int32 30 | } 31 | 32 | type Account struct { 33 | Account string 34 | AgentID int32 35 | ServerID int32 36 | LastRole int64 37 | FcmOffline int32 38 | LastOffline int32 39 | BanTime int32 40 | } 41 | 42 | // 需要与Goods保持一致 43 | type Bag struct { 44 | RoleID int64 45 | ID int32 46 | Type int32 47 | TypeID int32 48 | Num int32 49 | Bind bool 50 | StartTime int32 51 | EndTime int32 52 | CreateTime int32 53 | } 54 | 55 | type RoleProp struct { 56 | RoleID int64 57 | Prop *PProp 58 | CeilFP []PCeilFP 59 | } 60 | 61 | type RoleMap struct { 62 | RoleID int64 63 | MapID int32 64 | MapName string 65 | Node string 66 | X int32 67 | Y int32 68 | } 69 | -------------------------------------------------------------------------------- /global/function.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | func (p *PMapRole) ID() int64 { 4 | return p.RoleID 5 | } 6 | 7 | func (p *PMapRole) Type() int8 { 8 | return ACTOR_ROLE 9 | } 10 | 11 | func (p *PMapRole) GetPos() *PPos { 12 | return p.Pos 13 | } 14 | func (p *PMapRole) GetMove() (int32, *PMovePath, *PPos) { 15 | return p.MoveSpeed, p.MovePath, p.Pos 16 | } 17 | 18 | func (p *PMapRole) SetMovePath(path *PMovePath) { 19 | p.MovePath = path 20 | } 21 | 22 | func (p *PMapRole) IsAlive() bool { 23 | return p.State != 1 24 | } 25 | func (p *PMapRole) GetRadius() float64 { 26 | return 0.5 27 | } 28 | 29 | func (p *PMapRole)GetCamp() int8 { 30 | return p.Camp 31 | } 32 | 33 | func (p *PMapRole)GetBuffs() []*PBuff { 34 | return p.Buffs 35 | } 36 | 37 | func (p *PMapRole)SetBuffs(buff []*PBuff){ 38 | p.Buffs = buff 39 | } 40 | 41 | //-------------------------------------monster--------------------------------- 42 | func (p *PMapMonster) ID() int64 { 43 | return p.MonsterID 44 | } 45 | 46 | func (p *PMapMonster) Type() int8 { 47 | return ACTOR_MONSTER 48 | } 49 | 50 | func (p *PMapMonster) GetPos() *PPos { 51 | return p.Pos 52 | } 53 | 54 | func (p *PMapMonster) GetMove() (int32, *PMovePath, *PPos) { 55 | return p.MoveSpeed, p.MovePath, p.Pos 56 | } 57 | func (p *PMapMonster) SetMovePath(path *PMovePath) { 58 | p.MovePath = path 59 | } 60 | func (p *PMapMonster) IsAlive() bool { 61 | return p.HP > 0 62 | } 63 | 64 | func (p *PMapMonster) GetRadius() float64 { 65 | return float64(p.Radius) 66 | } 67 | 68 | func (p *PMapMonster)GetCamp() int8 { 69 | return p.Camp 70 | } 71 | 72 | func (p *PMapMonster)GetBuffs() []*PBuff { 73 | return p.Buffs 74 | } 75 | 76 | func (p *PMapMonster)SetBuffs(buff []*PBuff){ 77 | p.Buffs = buff 78 | } 79 | //-------------------------------------------------------------------------------- 80 | -------------------------------------------------------------------------------- /global/game.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/gate" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "github.com/liangmanlin/gootp/rand" 7 | "github.com/liangmanlin/gootp/timer" 8 | "unsafe" 9 | ) 10 | 11 | type Player struct { 12 | *kernel.Context 13 | Transaction 14 | RoleID int64 15 | Conn gate.Conn 16 | GWPid *kernel.Pid 17 | MapPid *kernel.Pid 18 | DirtyMod map[string]bool 19 | BagMaxID int32 20 | Bag *BagData 21 | Attr *PRoleAttr 22 | Base *PRoleBase 23 | Timer *timer.Timer 24 | PersistentTime int64 25 | Rand *rand.Rand 26 | PropData unsafe.Pointer // 规避循环引用 27 | Prop *PProp 28 | Map *RoleMap 29 | HeartTime int64 30 | } 31 | 32 | type Transaction struct { 33 | IsTransaction bool 34 | BackupMap map[BackupKey]int 35 | Backup []BackData 36 | DBQueue []DBQueue 37 | } 38 | 39 | type BagData struct { 40 | Goods map[int32]*PGoods 41 | MaxSize int32 42 | Dirty map[int32]GoodsDirty 43 | TypeIDMap map[int32][]int32 44 | } 45 | 46 | type GoodsDirty struct { 47 | Type int32 48 | Goods *PGoods 49 | } 50 | 51 | type DBQueue struct { 52 | OP int32 53 | Fun func(player *Player, op int32, arg interface{}) 54 | Arg interface{} 55 | } 56 | 57 | type BackData struct { 58 | Key interface{} 59 | Value interface{} 60 | } 61 | 62 | type BackupKey struct { 63 | BackupID int32 64 | ID int32 65 | } 66 | 67 | type HandleFunc = func(player *Player, proto interface{}) 68 | 69 | type KV struct { 70 | K int32 71 | V int32 72 | } 73 | 74 | type CreateRole struct { 75 | AgentID int32 76 | ServerID int32 77 | Account string 78 | Name string 79 | HeroType int32 80 | Sex int32 81 | } 82 | 83 | type CreateRoleResult struct { 84 | RoleID int64 85 | Roles []int64 86 | } 87 | 88 | type TcpReConnect struct { 89 | } 90 | 91 | type TcpReConnectGW struct { 92 | Conn gate.Conn 93 | RecID int64 94 | Token string 95 | } 96 | 97 | type Kick struct { 98 | } 99 | 100 | type OK struct { 101 | } 102 | 103 | type RoleDeadArg struct { 104 | SrcID int64 105 | SrcType int8 106 | } 107 | 108 | type RoleUpdateProps struct { 109 | RoleID int64 110 | FightPower int64 111 | UP []*PKV 112 | } 113 | 114 | type RoleExitMap struct { 115 | RoleID int64 116 | } 117 | 118 | type MapRoleExit struct { 119 | ExitReturn bool 120 | X float32 121 | Y float32 122 | } 123 | 124 | type MapRoleEnter struct { 125 | Pid *kernel.Pid 126 | MapID int32 127 | MapName string 128 | X float32 129 | Y float32 130 | } 131 | 132 | // PMsgParam start--------------------------------------------------------- 133 | 134 | const ( 135 | param_num byte = iota + 1 136 | param_string 137 | param_map_id 138 | param_role 139 | ) 140 | 141 | type ParamNum int32 142 | 143 | func (p ParamNum) ToBinary() []byte { 144 | return []byte{param_num, uint8(p >> 24), uint8(p >> 16), uint8(p >> 8), uint8(p)} 145 | } 146 | 147 | type ParamString string 148 | 149 | func (p ParamString) ToBinary() []byte { 150 | size := len(p) 151 | buf := make([]byte, 0, 3+size) 152 | buf = append(buf, param_string, uint8(size>>8), uint8(size)) 153 | buf = append(buf, p...) 154 | return buf 155 | } 156 | 157 | type ParamMapID int32 158 | 159 | func (p ParamMapID) ToBinary() []byte { 160 | return []byte{param_map_id, uint8(p >> 24), uint8(p >> 16), uint8(p >> 8), uint8(p)} 161 | } 162 | 163 | type ParamRole struct { 164 | RoleID int64 165 | RoleName string 166 | } 167 | 168 | func (p ParamRole) ToBinary() []byte { 169 | size := len(p.RoleName) 170 | buf := make([]byte, 0, 3+8+size) 171 | roleID := p.RoleID 172 | buf = append(buf, param_role, uint8(roleID>>56), uint8(roleID>>48), uint8(roleID>>40), uint8(roleID>>32), 173 | uint8(roleID>>24), uint8(roleID>>16), uint8(roleID>>8), uint8(roleID), uint8(size>>8), uint8(size)) 174 | buf = append(buf, p.RoleName...) 175 | return buf 176 | } 177 | 178 | // PMsgParam end------------------------------------------------------ 179 | -------------------------------------------------------------------------------- /global/gw.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/gate" 5 | "github.com/liangmanlin/gootp/gate/pb" 6 | "github.com/liangmanlin/gootp/kernel" 7 | ) 8 | 9 | const CacheSize = 300 10 | 11 | type TcpClientState struct { 12 | Account string 13 | AgentID int32 14 | ServerID int32 15 | Conn gate.Conn 16 | Coder *pb.Coder 17 | Player *kernel.Pid 18 | RoleID int64 19 | SendID int64 20 | Roles []*PRole 21 | Cache [CacheSize][]byte 22 | } 23 | -------------------------------------------------------------------------------- /global/lang/cn.go: -------------------------------------------------------------------------------- 1 | //+build cn 2 | 3 | package lang 4 | 5 | const LANG_TYPE = LANG_CN 6 | -------------------------------------------------------------------------------- /global/lang/const.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | const ( 4 | LANG_CN int32 = iota +1 5 | LANG_EN 6 | LANG_TW 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /global/lang/en.go: -------------------------------------------------------------------------------- 1 | //+build en 2 | 3 | package lang 4 | 5 | const LANG_TYPE = LANG_EN 6 | -------------------------------------------------------------------------------- /global/lang/tw.go: -------------------------------------------------------------------------------- 1 | //+build tw 2 | 3 | package lang 4 | 5 | const LANG_TYPE = LANG_TW 6 | -------------------------------------------------------------------------------- /global/map_mod.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | const ( 4 | MAP_MOD_ROLE int32 = iota + 1 5 | MAP_MOD_MAX // 放在最后 6 | ) 7 | -------------------------------------------------------------------------------- /global/node.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | type TestCall struct { 4 | ID int32 5 | } 6 | 7 | type StrArgs struct { 8 | Str string 9 | } 10 | 11 | type RpcStrResult struct { 12 | Str string 13 | } 14 | -------------------------------------------------------------------------------- /global/node_proto.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | var NodeProto = []interface{}{ 4 | &TestCall{}, 5 | &StrArgs{}, 6 | &RpcStrResult{}, 7 | &RoleUpdateProps{}, 8 | &RoleDeadArg{}, 9 | &RoleExitMap{}, 10 | &MapRoleExit{}, 11 | } 12 | -------------------------------------------------------------------------------- /global/pb_def.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | //--------------------------Login------------------------------------------------------------ 4 | 5 | // 连接 6 | type LoginTosConnect struct { 7 | IsReconnect bool 8 | Token string 9 | RecID int64 10 | } 11 | type LoginTocConnect struct { 12 | Succ bool 13 | IsReconnect bool 14 | Token string 15 | } 16 | 17 | // 登录协议 18 | type LoginTosLogin struct { 19 | Account string 20 | AgentID int32 21 | ServerID int32 22 | } 23 | type LoginTocLogin struct { 24 | Succ bool 25 | Reason *PMsg 26 | Account string 27 | LastRoleID int64 28 | Roles []*PRole 29 | } 30 | 31 | type LoginTosCreateRole struct { 32 | Name string 33 | HeroType int32 34 | Sex int32 35 | } 36 | type LoginTocCreateRole struct { 37 | Succ bool 38 | Reason *PMsg 39 | RoleID int64 40 | } 41 | 42 | type LoginTosSelect struct { 43 | RoleID int64 44 | } 45 | type LoginTocSelect struct { 46 | Succ bool 47 | Reason *PMsg 48 | RoleID int64 49 | } 50 | 51 | //--------------------------Game------------------------------------------------------------ 52 | 53 | type GameTosUP struct { // router:LoginLogin 54 | 55 | } 56 | 57 | type GameTocUP struct { 58 | } 59 | 60 | //-----------------------map--------------------------------------------------------------- 61 | type MapTocActorLeaveArea struct { 62 | ActorID int64 63 | ActorType int8 64 | } 65 | 66 | type MapTocEnterArea struct { 67 | RolesShow []*PMapRole 68 | MonstersShow []*PMapMonster 69 | RolesDel []int64 70 | MonstersDel []int64 71 | } 72 | 73 | type MapTocRoleEnterArea struct { 74 | MapInfo *PMapRole 75 | } 76 | 77 | type MapTocMonsterEnterArea struct { 78 | MapInfo *PMapMonster 79 | } 80 | 81 | type MapTocStop struct { 82 | ActorID int64 83 | ActorType int8 84 | Dir int16 85 | X float32 86 | Y float32 87 | } 88 | 89 | type MapTosMove struct { 90 | } 91 | type MapTocMove struct { 92 | ActorID int64 93 | ActorType int8 94 | StartX float32 95 | StartY float32 96 | MovePath *PMovePath 97 | } 98 | 99 | type MapTosMoveStop struct { 100 | X float32 101 | Y float32 102 | Dir int16 103 | } 104 | type MapTocMoveStop struct { 105 | ActorID int64 106 | ActorType int8 107 | Dir int16 108 | X float32 109 | Y float32 110 | } 111 | 112 | type MapTocUpdateMonsterInfo struct { 113 | ID int64 114 | UL []*PKV 115 | } 116 | 117 | type MapTocUpdateRoleInfo struct { 118 | RoleID int64 119 | UL []*PKV 120 | } 121 | 122 | type MapTocActorDead struct { 123 | ActorID int64 124 | ActorType int8 125 | } 126 | 127 | type MapTocDelBuff struct { 128 | ActorID int64 129 | ActorType int8 130 | BuffID int16 131 | } 132 | 133 | //------------------------------------map end-------------------------------------------- 134 | //------------------------------------fight---------------------------------------------- 135 | 136 | type FightTocUseSkill struct { 137 | ActorID int64 138 | ActorType int8 139 | SkillID int32 140 | X float32 141 | Y float32 142 | } 143 | 144 | type FightTocSkillEffect struct { 145 | SkillID int32 146 | SkillDir int16 147 | SrcType int8 148 | SrcID int64 149 | EffectList []*PSkillEffect 150 | } 151 | 152 | //------------------------------------fight end------------------------------------------ 153 | 154 | //-------------------------------------role---------------------------------------------- 155 | type RoleTocUPProps struct { 156 | UP []*PKV 157 | } 158 | 159 | //-------------------------------------role end------------------------------------------- 160 | 161 | type PMsg struct { 162 | MsgID int32 163 | Bin []byte // 客户端需要根据商定的规则解开一个字符参数 164 | } 165 | 166 | type PRole struct { 167 | RoleID int64 168 | RoleName string 169 | HeroType int32 170 | Level int32 171 | Skin *PSkin 172 | } 173 | 174 | type PSkin struct { 175 | } 176 | 177 | type PGoods struct { 178 | RoleID int64 179 | ID int32 180 | Type int32 181 | TypeID int32 182 | Num int32 183 | Bind bool 184 | StartTime int32 185 | EndTime int32 186 | CreateTime int32 187 | } 188 | 189 | type PRoleAttr struct { 190 | RoleID int64 191 | Diamond int64 192 | Gold int64 193 | } 194 | 195 | 196 | type PRoleBase struct { 197 | RoleID int64 198 | Name string 199 | Account string 200 | AgentID int32 201 | ServerID int32 202 | HeroType int32 203 | Level int32 204 | Skin *PSkin 205 | } 206 | 207 | type PPos struct { 208 | X float32 209 | Y float32 210 | Dir int16 211 | } 212 | 213 | type PMapRole struct { 214 | RoleID int64 215 | Name string 216 | HeroType int32 217 | ServerID int32 218 | Level int16 219 | State int8 220 | Camp int8 221 | Skin *PSkin 222 | Pos *PPos 223 | MovePath *PMovePath 224 | HP int32 225 | MaxHP int32 226 | MoveSpeed int32 227 | Buffs []*PBuff 228 | } 229 | 230 | type PMapMonster struct { 231 | MonsterID int64 232 | TypeID int32 233 | Level int16 234 | Radius int8 235 | Camp int8 236 | Pos *PPos 237 | MovePath *PMovePath 238 | HP int32 239 | MaxHP int32 240 | MoveSpeed int32 241 | Buffs []*PBuff 242 | } 243 | 244 | type PBuff struct { 245 | ID int16 246 | Type int16 247 | SrcType int8 248 | SrcID int64 249 | EndTime int64 250 | Value int32 251 | } 252 | 253 | type PMovePath struct { 254 | EndX float32 255 | EndY float32 256 | GridPath []*PGrid 257 | } 258 | 259 | type PGrid struct { 260 | GridX int16 261 | GridY int16 262 | } 263 | 264 | type PProp struct { 265 | MaxHP int32 266 | PhyAttack int32 267 | ArmorBreak int32 268 | PhyDefence int32 269 | Hit int32 270 | Miss int32 271 | Crit int32 272 | Tenacity int32 273 | MoveSpeed int32 274 | HpRecover int32 275 | MaxHpRate int32 276 | AttackRate int32 277 | ArmorBreakRate int32 278 | DefenceRate int32 279 | HitAddRate int32 280 | MissAddRate int32 281 | CritAddRate int32 282 | TenacityAddRate int32 283 | DamageDeepen int32 284 | DamageDef int32 285 | HitRate int32 286 | MissRate int32 287 | CritRate int32 288 | CritDef int32 289 | CritValue int32 290 | CritValueDef int32 291 | ParryRate int32 292 | ParryOver int32 293 | HuixinRate int32 294 | HuixinDef int32 295 | PvpDamageDeepen int32 296 | PvpDamageDef int32 297 | PveDamageDeepen int32 298 | PveDamageDef int32 299 | TotalDef int32 300 | } 301 | 302 | type PKV struct { 303 | Key int32 304 | Value int32 305 | } 306 | 307 | type PActor struct { 308 | ActorID int64 309 | ActorType int8 310 | } 311 | 312 | type PSkillEffect struct { 313 | ActorID int64 314 | ActorType int8 315 | EffectType int8 316 | Value int32 317 | } 318 | 319 | type PCeilFP struct { 320 | ID int32 321 | SubID int32 322 | FightPower int64 323 | } 324 | -------------------------------------------------------------------------------- /global/player_mod.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | const ( 4 | PLAYER_MOD_ROLE int32 = iota + 1 5 | PLAYER_MOD_MAP 6 | PLAYER_MOD_MAX // 放在最后 7 | ) 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module game 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3 // indirect 7 | github.com/liangmanlin/gootp v0.0.0-20210526082724-cd567e8bd51e 8 | github.com/xuri/excelize/v2 v2.4.1 9 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect 10 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect 11 | golang.org/x/text v0.3.7 // indirect 12 | ) 13 | 14 | replace github.com/liangmanlin/gootp => ../gootp 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 2 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 3 | github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= 4 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 8 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 9 | github.com/lesismal/llib v1.1.4/go.mod h1:3vmCrIMrpkaoA3bDu/sI+J7EyEUMPbOvmAxb7PlzilM= 10 | github.com/lesismal/nbio v1.2.10/go.mod h1:X2ILVmTUGwZ10337Vat4MLs7FuSZ93gWg7S9sPpN8RY= 11 | github.com/lesismal/nbio v1.2.12-0.20220225113352-a1a08d04dc84 h1:Y3HBmh5EO4anXQkylQ8MoYJrj2Kr8UQwCDCtmES/6mo= 12 | github.com/lesismal/nbio v1.2.12-0.20220225113352-a1a08d04dc84/go.mod h1:X2ILVmTUGwZ10337Vat4MLs7FuSZ93gWg7S9sPpN8RY= 13 | github.com/lesismal/nbio v1.2.13-0.20220228132352-36b68811404b h1:/n7s8nmF7L2qQuVfit3Gyi2Qw9Ph33om+koR45L9eeo= 14 | github.com/lesismal/nbio v1.2.13-0.20220228132352-36b68811404b/go.mod h1:X2ILVmTUGwZ10337Vat4MLs7FuSZ93gWg7S9sPpN8RY= 15 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3 h1:670ZAWj/DNCclpVRZSYZUXImKlSLLj87MpENDT7C4ZA= 16 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3/go.mod h1:X2ILVmTUGwZ10337Vat4MLs7FuSZ93gWg7S9sPpN8RY= 17 | github.com/liangmanlin/readline v0.0.0-20220106124050-231e336f3d3d/go.mod h1:reZL5xuyygWwVtSJLetMCTzfFCeS6fA3RRijnf1Q3W4= 18 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= 19 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 22 | github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI= 23 | github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= 24 | github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= 25 | github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= 26 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 29 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o= 31 | github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= 32 | github.com/xuri/excelize/v2 v2.4.1 h1:veeeFLAJwsNEBPBlDepzPIYS1eLyBVcXNZUW79exZ1E= 33 | github.com/xuri/excelize/v2 v2.4.1/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A= 34 | golang.org/x/crypto v0.0.0-20210513122933-cd7d49e622d5/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 35 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 36 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= 37 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 38 | golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= 39 | golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 40 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 41 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 42 | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 43 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 44 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= 45 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 46 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 48 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 50 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 51 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 52 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 53 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 54 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 55 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 56 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 57 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 58 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 59 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 60 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 61 | -------------------------------------------------------------------------------- /gw/gw_cache.go: -------------------------------------------------------------------------------- 1 | package gw 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "sync" 6 | ) 7 | 8 | var tokenMap sync.Map 9 | 10 | func InsertToken(token string, pid *kernel.Pid) { 11 | tokenMap.Store(token, pid) 12 | } 13 | 14 | func TokenToPid(token string) *kernel.Pid { 15 | if pid, ok := tokenMap.Load(token); ok { 16 | return pid.(*kernel.Pid) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /gw/gw_client.go: -------------------------------------------------------------------------------- 1 | package gw 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/gate" 6 | "github.com/liangmanlin/gootp/gate/pb" 7 | "github.com/liangmanlin/gootp/kernel" 8 | ) 9 | 10 | var TcpClientActor = &kernel.Actor{ 11 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 12 | t := global.TcpClientState{} 13 | t.Coder = args[0].(*pb.Coder) 14 | t.Conn = args[1].(gate.Conn) 15 | kernel.ErrorLog("connect") 16 | return &t 17 | }, 18 | HandleCast: func(context *kernel.Context, msg interface{}) { 19 | t := context.State.(*global.TcpClientState) 20 | switch m := msg.(type) { 21 | case bool: 22 | doLogin(t, context) 23 | case []byte: 24 | sendBuf(t, m) 25 | case *gate.TcpError: 26 | context.Exit("normal") 27 | case int: 28 | context.Exit("normal") 29 | case *global.TcpReConnectGW: 30 | doReConnect(t, context, m) 31 | case gate.Pack: 32 | switch proto := m.Proto.(type) { 33 | case *global.LoginTosLogin: 34 | doLoginLogin(t, context, proto) 35 | case *global.LoginTosSelect: 36 | doLoginSelect(t, context, proto) 37 | case *global.LoginTosCreateRole: 38 | doLoginCreate(t, context, proto) 39 | default: 40 | // 只有极少数情况会走到这里 41 | if t.Player != nil { 42 | context.Cast(t.Player, m) 43 | } 44 | } 45 | default: 46 | kernel.ErrorLog("un handle msg: %#v", m) 47 | } 48 | }, 49 | HandleCall: func(context *kernel.Context, request interface{}) interface{} { 50 | return nil 51 | }, 52 | Terminate: func(context *kernel.Context, reason *kernel.Terminate) { 53 | t := context.State.(*global.TcpClientState) 54 | if t.Conn != nil { 55 | t.Conn.Close() 56 | } 57 | kernel.ErrorLog("exit reason:%s", reason.Reason) 58 | }, 59 | ErrorHandler: func(context *kernel.Context, err interface{}) bool { 60 | return false 61 | }, 62 | } 63 | 64 | func doLogin(t *global.TcpClientState, context *kernel.Context) { 65 | t.Conn.SetHead(2) 66 | buf, err := t.Conn.Recv(0, 50000) 67 | if err != nil { 68 | kernel.ErrorLog("handshake error %s", err.Error()) 69 | context.Exit(kernel.ExitReasonNormal) 70 | return 71 | } 72 | _, proto := pb.GetCoder(1).Decode(buf) 73 | pt := proto.(*global.LoginTosConnect) 74 | if !pt.IsReconnect { 75 | token := RandomToken() // 生成一个唯一key,作为重连的标志 76 | InsertToken(token, context.Self()) 77 | rs := &global.LoginTocConnect{Succ: true, IsReconnect: false, Token: token} 78 | if t.Conn.SendBufHead(t.Coder.Encode(rs, 2)) != nil { 79 | context.Exit(kernel.ExitReasonNormal) 80 | return 81 | } 82 | // 异步接收 83 | t.Conn.StartReader(context.Self()) 84 | } else { 85 | pid := TokenToPid(pt.Token) 86 | if pid != nil { 87 | // 重连,转移到目标进程 88 | context.Cast(pid, &global.TcpReConnectGW{Conn: t.Conn, RecID: pt.RecID, Token: pt.Token}) 89 | rs := &global.LoginTocConnect{Succ: true, IsReconnect: true, Token: pt.Token} 90 | _ = t.Conn.SendBufHead(t.Coder.Encode(rs, 2)) 91 | t.Conn = nil 92 | } else { 93 | rs := &global.LoginTocConnect{Succ: false, IsReconnect: true, Token: ""} 94 | _ = t.Conn.SendBufHead(t.Coder.Encode(rs, 2)) 95 | } 96 | context.Exit(kernel.ExitReasonNormal) 97 | } 98 | } 99 | 100 | func SendPack(state *global.TcpClientState, pack interface{}) { 101 | buf := state.Coder.Encode(pack, 2) 102 | sendBuf(state, buf) 103 | } 104 | 105 | func sendBuf(state *global.TcpClientState, buf []byte) { 106 | state.SendID++ 107 | state.Cache[state.SendID%global.CacheSize] = buf 108 | if state.Conn != nil { 109 | if state.Conn.SendBufHead(buf) != nil { 110 | state.Conn.Close() 111 | state.Conn = nil 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /gw/gw_lib.go: -------------------------------------------------------------------------------- 1 | package gw 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "github.com/liangmanlin/gootp/gate/pb" 7 | "github.com/liangmanlin/gootp/rand" 8 | "strconv" 9 | "sync/atomic" 10 | ) 11 | 12 | var idx int32 13 | 14 | func init() { 15 | idx = rand.New().Random(1,2000) 16 | } 17 | 18 | func RandomToken() string { 19 | id := atomic.AddInt32(&idx,1) 20 | buf := md5.Sum(strconv.AppendInt(nil,int64(id+1024),10)) 21 | pb.WriteIn32(buf[:],id,0) 22 | token := hex.EncodeToString(buf[:]) 23 | if _,ok := tokenMap.Load(token);ok{ 24 | return RandomToken() 25 | } 26 | return token 27 | } 28 | -------------------------------------------------------------------------------- /gw/gw_login.go: -------------------------------------------------------------------------------- 1 | package gw 2 | 3 | import ( 4 | "fmt" 5 | "game/global" 6 | "game/lib" 7 | "game/player" 8 | "github.com/liangmanlin/gootp/db" 9 | "github.com/liangmanlin/gootp/kernel" 10 | ) 11 | 12 | func doReConnect(state *global.TcpClientState, ctx *kernel.Context, m *global.TcpReConnectGW) { 13 | if state.SendID-m.RecID > global.CacheSize { 14 | rs := &global.LoginTocConnect{Succ: false, IsReconnect: true} 15 | _ = state.Conn.SendBufHead(state.Coder.Encode(rs, 2)) 16 | return 17 | } 18 | rs := &global.LoginTocConnect{Succ: true, IsReconnect: true, Token: m.Token} 19 | _ = state.Conn.SendBufHead(state.Coder.Encode(rs, 2)) 20 | // 补包 21 | syncPack(state, m.RecID) 22 | // 通知player 23 | if state.Player != nil { 24 | ctx.Cast(state.Player, global.TcpReConnect{}) 25 | m.Conn.StartReader(state.Player) 26 | } 27 | } 28 | 29 | func doLoginLogin(state *global.TcpClientState, ctx *kernel.Context, m *global.LoginTosLogin) { 30 | t := lib.GameDB.ModSelectRow(global.TABLE_ACCOUNT, m.Account, m.AgentID, m.ServerID) 31 | var account *global.Account 32 | if t == nil { 33 | account = &global.Account{Account: m.Account, AgentID: m.AgentID, ServerID: m.ServerID} 34 | lib.GameDB.ModInsert(global.TABLE_ACCOUNT, account) 35 | } else { 36 | account = t.(*global.Account) 37 | } 38 | baseList := lib.GameDB.ModSelectAllWhere(global.TABLE_ROLE_BASE, 39 | fmt.Sprintf("Account = %s and AgentID = %d and ServerID = %d", db.Encode(m.Account), m.AgentID, m.ServerID)) 40 | var roles []*global.PRole 41 | for i := range baseList { 42 | b := baseList[i].(*global.PRoleBase) 43 | role := &global.PRole{RoleID: b.RoleID, RoleName: b.Name, HeroType: b.HeroType, Level: b.Level, Skin: b.Skin} 44 | roles = append(roles, role) 45 | } 46 | state.Roles = roles 47 | r := &global.LoginTocLogin{Succ: true, Account: m.Account, LastRoleID: account.LastRole, Roles: roles} 48 | SendPack(state, r) 49 | state.Account = m.Account 50 | state.AgentID = m.AgentID 51 | state.ServerID = m.ServerID 52 | } 53 | 54 | func doLoginSelect(state *global.TcpClientState, ctx *kernel.Context, m *global.LoginTosSelect) { 55 | for i := range state.Roles { 56 | if state.Roles[i].RoleID == m.RoleID { 57 | state.RoleID = m.RoleID 58 | break 59 | } 60 | } 61 | if state.RoleID == 0 { 62 | // 错误 63 | r := &global.LoginTocSelect{Succ: false, Reason: &global.PMsg{MsgID: 1}} 64 | SendPack(state, r) 65 | return 66 | } 67 | var rl []int64 68 | for i := range state.Roles { 69 | rl = append(rl, state.Roles[i].RoleID) 70 | } 71 | kickRoles(ctx, rl, state.RoleID) 72 | playerPid := player.Start(ctx.Self(), state.Conn, state.RoleID) 73 | if playerPid != nil { 74 | state.Roles = nil // gc 75 | state.Player = playerPid 76 | r := &global.LoginTocSelect{Succ: true, RoleID: state.RoleID} 77 | SendPack(state, r) 78 | } else { 79 | r := &global.LoginTocSelect{Succ: false, Reason: &global.PMsg{MsgID: 1}} 80 | SendPack(state, r) 81 | } 82 | } 83 | 84 | func doLoginCreate(state *global.TcpClientState, ctx *kernel.Context, m *global.LoginTosCreateRole) { 85 | if state.RoleID > 0 || state.Account == "" { 86 | r := &global.LoginTocCreateRole{Succ: false, Reason: &global.PMsg{MsgID: 32}} 87 | SendPack(state, r) 88 | return 89 | } 90 | ok, rs := ctx.CallName(global.SYS_ACCOUNT_SERVER, 91 | &global.CreateRole{ 92 | AgentID: state.AgentID, 93 | Account: state.Account, 94 | ServerID: state.ServerID, 95 | Name: m.Name, 96 | HeroType: m.HeroType, 97 | Sex: m.Sex, 98 | }) 99 | if !ok { 100 | r := &global.LoginTocCreateRole{Succ: false, Reason: lib.ErrToPMsg(rs)} 101 | SendPack(state, r) 102 | return 103 | } 104 | switch msg := rs.(type) { 105 | case *global.PMsg: 106 | r := &global.LoginTocCreateRole{Succ: false, Reason: msg} 107 | SendPack(state, r) 108 | case *global.CreateRoleResult: 109 | state.RoleID = msg.RoleID 110 | kickRoles(ctx, msg.Roles, msg.RoleID) 111 | playerPid := player.Start(ctx.Self(), state.Conn, state.RoleID) 112 | if playerPid != nil { 113 | state.Roles = nil // gc 114 | state.Player = playerPid 115 | r := &global.LoginTocCreateRole{Succ: true, RoleID: state.RoleID} 116 | SendPack(state, r) 117 | } else { 118 | r := &global.LoginTocCreateRole{Succ: false, Reason: &global.PMsg{MsgID: 1}} 119 | SendPack(state, r) 120 | } 121 | } 122 | } 123 | 124 | func syncPack(state *global.TcpClientState, id int64) { 125 | id++ 126 | c := state.Conn 127 | for id <= state.SendID { 128 | pack := state.Cache[id%global.CacheSize] 129 | _ = c.SendBufHead(pack) 130 | id++ 131 | } 132 | } 133 | 134 | func kickRoles(ctx *kernel.Context, roles []int64, roleID int64) { 135 | for _, rid := range roles { 136 | if rid != roleID { 137 | if pid := lib.GetRolePid(rid); pid != nil { 138 | ctx.Call(pid, global.Kick{}) 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /hotfix/20210119/20210119.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "game/global" 5 | "game/player" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "sync/atomic" 8 | "unsafe" 9 | ) 10 | 11 | func HotFix(){ 12 | // 这里赋予新的函数 13 | f := func(player *global.Player, proto interface{}) { 14 | kernel.ErrorLog("this is new api") 15 | } 16 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&player.LoginLogin)),*(*unsafe.Pointer)(unsafe.Pointer(&f))) 17 | } -------------------------------------------------------------------------------- /lib/db.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import "github.com/liangmanlin/gootp/db" 4 | 5 | var GameDB *db.Group 6 | -------------------------------------------------------------------------------- /lib/degree.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import "math" 4 | 5 | func Sin(dir int16) float64 { 6 | r := float64(dir) / 180 * math.Pi 7 | return math.Sin(r) 8 | } 9 | 10 | func Cos(dir int16) float64 { 11 | r := float64(dir) / 180 * math.Pi 12 | return math.Cos(r) 13 | } 14 | -------------------------------------------------------------------------------- /lib/goods.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "github.com/liangmanlin/gootp/kernel" 7 | ) 8 | 9 | var CreateGoods = func(typeID, num int32, bind bool) []*global.PGoods { 10 | Type := typeID / global.GOODS_TYPE_DIV 11 | switch Type { 12 | case global.GOODS_TYPE_ITEM: 13 | return CreateItem(typeID, num, bind) 14 | } 15 | kernel.ErrorLog("unknow typeid:%d", typeID) 16 | return nil 17 | } 18 | 19 | var CreateItem = func(typeID, num int32, bind bool) []*global.PGoods { 20 | def := config.Goods.Get(typeID) 21 | less := num % def.Pile_num 22 | var goodsList []*global.PGoods 23 | now := int32(kernel.Now()) 24 | if less > 0 { 25 | goodsList = append(goodsList, &global.PGoods{ 26 | Type: global.GOODS_TYPE_ITEM, 27 | TypeID: typeID, 28 | Num: less, 29 | Bind: bind, 30 | CreateTime: now, 31 | }) 32 | } 33 | div := num / def.Pile_num 34 | for i := int32(0); i < div; i++ { 35 | goodsList = append(goodsList, &global.PGoods{ 36 | Type: global.GOODS_TYPE_ITEM, 37 | TypeID: typeID, 38 | Num: def.Pile_num, 39 | Bind: bind, 40 | CreateTime: now, 41 | }) 42 | } 43 | return goodsList 44 | } 45 | 46 | var GetPileNum = func(typeID int32) int32 { 47 | switch typeID / global.GOODS_TYPE_DIV { 48 | default: 49 | def := config.Goods.Get(typeID) 50 | return def.Pile_num 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/lib.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "game/config" 6 | "game/global" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "github.com/liangmanlin/gootp/node" 9 | "math" 10 | "os" 11 | "path/filepath" 12 | "reflect" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "sync" 17 | ) 18 | 19 | // 公共库,这里的代码不能依赖其他包,仅仅可以依赖 global 包 20 | 21 | var roleIDMap sync.Map 22 | 23 | func IsRoleOnline(roleID int64) bool { 24 | _, ok := roleIDMap.Load(roleID) 25 | return ok 26 | } 27 | 28 | func GetRolePid(roleID int64) *kernel.Pid { 29 | if pid, ok := roleIDMap.Load(roleID); ok { 30 | return pid.(*kernel.Pid) 31 | } 32 | return nil 33 | } 34 | 35 | func SetRolePid(roleID int64, pid *kernel.Pid) { 36 | roleIDMap.Store(roleID, pid) 37 | } 38 | 39 | func DelRolePid(roleID int64) { 40 | roleIDMap.Delete(roleID) 41 | } 42 | 43 | func GetPlayerName(roleID int64) string { 44 | return "player_" + strconv.FormatInt(roleID, 10) 45 | } 46 | 47 | func ErrToPMsg(err interface{}) *global.PMsg { 48 | switch r := err.(type) { 49 | case *global.PMsg: 50 | return r 51 | default: 52 | _, file, line, ok := runtime.Caller(1) 53 | if !ok { 54 | file = "???" 55 | line = 0 56 | } else { 57 | file = filepath.Base(file) 58 | } 59 | kernel.ErrorLog("[%s:%d] error:%#v,", file, line, err) 60 | return &global.PMsg{MsgID: 1} 61 | } 62 | } 63 | 64 | var _serverRoot string 65 | 66 | func GetServerRoot() string { 67 | return _serverRoot 68 | } 69 | 70 | func GetServerID() int32 { 71 | return int32(config.Server.GetListInt("server_id",0)) 72 | } 73 | 74 | func NormalMapName(mapID int32) string { 75 | return "normal_" + strconv.FormatInt(int64(mapID), 10) 76 | } 77 | 78 | var mapNamePid sync.Map 79 | 80 | func RegisterMap(name string, pid *kernel.Pid) { 81 | mapNamePid.Store(name, pid) 82 | } 83 | func UnRegisterMap(name string) { 84 | mapNamePid.Delete(name) 85 | } 86 | 87 | func GetMapPid(name string) *kernel.Pid { 88 | if pid, ok := mapNamePid.Load(name); ok { 89 | return pid.(*kernel.Pid) 90 | } 91 | return nil 92 | } 93 | 94 | func XYLessThan(x, y, x2, y2, dist float32) bool { 95 | x2 = x2 - x 96 | y2 = y2 - y 97 | return x2*x2+y2*y2 <= dist 98 | } 99 | 100 | func PosLessThan(pos1, pos2 *global.PPos, dist float32) bool { 101 | return XYLessThan(pos1.X, pos1.Y, pos2.X, pos2.Y, dist) 102 | } 103 | 104 | func CalcDir(pos1, pos2 *global.PPos) int32 { 105 | r := math.Atan2(float64(pos2.Y-pos1.Y), float64(pos2.X-pos1.X)) 106 | dir := int32(math.Round(r * 180 / math.Pi)) 107 | if dir < 0 { 108 | dir += 360 109 | } 110 | return dir 111 | } 112 | 113 | func Dir(dx, dy float64) int16 { 114 | r := math.Atan2(dy, dx) 115 | dir := int16(math.Round(r * 180 / math.Pi)) 116 | if dir < 0 { 117 | dir += 360 118 | } 119 | return dir 120 | } 121 | 122 | func DirToTan(dir int32) float64 { 123 | return float64(dir) / 180 * math.Pi 124 | } 125 | 126 | // TODO 使用unsafe会更快 127 | func ToProp(from interface{}) *global.PProp { 128 | ft := reflect.ValueOf(from) 129 | if ft.Kind() == reflect.Ptr { 130 | ft = ft.Elem() 131 | } 132 | size := ft.NumField() 133 | rs := global.PProp{} 134 | ptr := reflect.New(reflect.TypeOf(rs)) 135 | rt := ptr.Elem() 136 | for i := 1; i < size; i++ { 137 | v := rt.Field(i - 1) 138 | d := ft.Field(i) 139 | v.Set(d) 140 | } 141 | return ptr.Interface().(*global.PProp) 142 | } 143 | 144 | // 利用反射,获取一个比较好看的数据 145 | // 性能一般,慎用 146 | func ToString(proto interface{}) string { 147 | rf := reflect.ValueOf(proto) 148 | return toString(rf) 149 | } 150 | 151 | func toString(rf reflect.Value) string { 152 | if rf.Kind() == reflect.Ptr { 153 | if rf.IsZero() { 154 | return "nil" 155 | } 156 | rf = rf.Elem() 157 | } 158 | switch rf.Kind() { 159 | case reflect.Struct: 160 | return structToString(rf) 161 | case reflect.Slice, reflect.Array: 162 | return sliceToString(rf) 163 | case reflect.Map: 164 | return mapToString(rf) 165 | case reflect.String: 166 | return rf.String() 167 | default: 168 | return fmt.Sprintf("%v", rf.Interface()) 169 | } 170 | } 171 | 172 | func structToString(rf reflect.Value) string { 173 | size := rf.NumField() 174 | tf := rf.Type() 175 | var sl []string 176 | var vs string 177 | for i := 0; i < size; i++ { 178 | f := rf.Field(i) 179 | vs = toString(f) 180 | sl = append(sl, tf.Field(i).Name+":"+vs) 181 | } 182 | return rf.Type().Name() + "{" + strings.Join(sl, ",") + "}" 183 | } 184 | 185 | func sliceToString(rf reflect.Value) string { 186 | size := rf.Len() 187 | var sl []string 188 | for i := 0; i < size; i++ { 189 | sl = append(sl, toString(rf.Index(i))) 190 | } 191 | return "[" + strings.Join(sl, ",") + "]" 192 | } 193 | 194 | func mapToString(rf reflect.Value) string { 195 | ranges := rf.MapRange() 196 | var sl []string 197 | for ranges.Next() { 198 | k := ranges.Key() 199 | v := ranges.Value() 200 | sl = append(sl, toString(k)+":"+toString(v)) 201 | } 202 | return "{" + strings.Join(sl, ",") + "}" 203 | } 204 | 205 | func CastMap(mapPid *kernel.Pid, mod int32, msg interface{}) { 206 | // 用于检测消息是否注册,正式环境不检查 207 | if global.DEBUG && global.CHECK_NODE_PROTO { 208 | rType := reflect.TypeOf(msg) 209 | if !node.IsProtoDef(rType) { 210 | kernel.ErrorLog("node proto %s not defined", rType.Name()) 211 | return 212 | } 213 | } 214 | if mapPid != nil { 215 | kernel.Cast(mapPid, &kernel.KMsg{ModID: mod, Msg: msg}) 216 | } 217 | } 218 | 219 | // 代码一样的 220 | func CastPlayer(playerPid *kernel.Pid, mod int32, msg interface{}) { 221 | // 用于检测消息是否注册,正式环境不检查 222 | if global.DEBUG && global.CHECK_NODE_PROTO { 223 | rType := reflect.TypeOf(msg) 224 | if !node.IsProtoDef(rType) { 225 | kernel.ErrorLog("node proto %s not defined", rType.Name()) 226 | return 227 | } 228 | } 229 | kernel.Cast(playerPid, &kernel.KMsg{ModID: mod, Msg: msg}) 230 | } 231 | 232 | func init() { 233 | _serverRoot = filepath.Dir(filepath.Dir(os.Args[0])) 234 | } 235 | -------------------------------------------------------------------------------- /lib/p_msg_encode.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/gate/pb" 6 | ) 7 | 8 | func NewPMsg(msgID int32) *global.PMsg { 9 | return &global.PMsg{MsgID: msgID} 10 | } 11 | 12 | func NewPMsgParam(msgID int32, param ...[]byte) *global.PMsg { 13 | return &global.PMsg{MsgID: msgID, Bin: appendPMsgByte(param)} 14 | } 15 | 16 | func appendPMsgByte(param [][]byte) []byte { 17 | if len(param) == 0{ 18 | return nil 19 | } 20 | var size int 21 | for _,v := range param{ 22 | size += len(v) 23 | } 24 | buf := make([]byte,2, size+2) 25 | for _, v := range param { 26 | buf = append(buf, v...) 27 | } 28 | pb.WriteSize(buf, 0, size) 29 | return buf 30 | } 31 | 32 | func M_Num(n int32) []byte { 33 | buf := make([]byte,1,1+4) 34 | buf[0] = 1 35 | pb.WriteIn32(buf,n,1) 36 | return buf 37 | } 38 | 39 | func M_String(str string) []byte { 40 | size := len(str) 41 | buf := make([]byte,1+2+size) 42 | buf[0] = 2 43 | pb.WriteString(buf,str,1) 44 | return buf 45 | } 46 | 47 | func M_Role(roleID int64,roleName string) []byte { 48 | size := len(roleName) 49 | buf := make([]byte,1+8+2+size) 50 | buf[0] = 3 51 | pb.WriteInt64(buf,roleID,1) 52 | pb.WriteString(buf,roleName,9) 53 | return buf 54 | } 55 | -------------------------------------------------------------------------------- /lib/props.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "github.com/liangmanlin/gootp/gutil" 7 | "unsafe" 8 | ) 9 | 10 | const propSize = int32(unsafe.Sizeof(global.PProp{}) / 4) 11 | 12 | var allKeys []int32 13 | 14 | func init() { 15 | allKeys = make([]int32, 0, propSize) 16 | for i := int32(1); i <= propSize; i++ { 17 | allKeys = append(allKeys,i) 18 | } 19 | } 20 | 21 | func AllKeys() []int32 { 22 | return allKeys 23 | } 24 | 25 | /* 26 | 属性计算模块 27 | */ 28 | 29 | func NewPropData() *PropData { 30 | return &PropData{ 31 | Cache: make(map[PropKey][]int32), 32 | PropMap: make(map[int32]*PropSum), 33 | } 34 | } 35 | 36 | // 添加属性,会替换掉原有的属性 37 | func (p *PropData) SetPropKvs(key PropKey, list []config.KV) []int32 { 38 | // 先删除原来已有的属性 39 | propKeys := p.RmPropKvs(key) 40 | pm := p.PropMap 41 | var ps *PropSum 42 | var ok bool 43 | keys := make([]int32, 0, len(list)) 44 | for _, kv := range list { 45 | // 判断合法性 46 | if !checkKey(kv.Key) { 47 | continue 48 | } 49 | ps, ok = pm[kv.Key] 50 | if !ok { 51 | ps.Total = 0 52 | ps.Props = make(map[PropKey]int32) 53 | pm[kv.Key] = ps 54 | } 55 | ps.Total += kv.Value 56 | ps.Props[key] = kv.Value 57 | keys = append(keys, kv.Key) 58 | } 59 | p.Cache[key] = keys 60 | return MergerKeys(propKeys, keys) 61 | } 62 | 63 | func (p *PropData) RmPropKvs(key PropKey) []int32 { 64 | if cache, ok := p.Cache[key]; ok { 65 | delete(p.Cache, key) 66 | pm := p.PropMap 67 | for _, pKey := range cache { 68 | rmPropKey(pm, pKey, key) 69 | } 70 | return cache 71 | } 72 | return nil 73 | } 74 | 75 | // 通常是计算player进程的属性 76 | func (p *PropData) CalcProps(keys []int32) []*global.PKV { 77 | keysMap := GenCalcKeys(keys) 78 | rs := make([]*global.PKV, 0, len(keysMap)) 79 | for key := range keysMap { 80 | rateKey := HasRate(key) 81 | switch rateKey { 82 | case 0: 83 | rs = append(rs, &global.PKV{Key: key, Value: p.PropValue(key)}) 84 | default: 85 | v := p.PropValue(key) 86 | rate := p.PropValue(rateKey) 87 | value := v + gutil.Ceil(float32(v)*float32((rate)/10000)) 88 | rs = append(rs, &global.PKV{Key: key, Value: value}) 89 | } 90 | } 91 | return rs 92 | } 93 | 94 | // 通常是计算map进程的属性 95 | func (p *PropData) CalcMapProps(keys []int32, baseProp *global.PProp) []global.PKV { 96 | keysMap := GenCalcKeys(keys) 97 | rs := make([]global.PKV, 0, len(keysMap)) 98 | for key := range keysMap { 99 | baseValue := GetPropValue(baseProp, key) 100 | rateKey := HasRate(key) 101 | switch rateKey { 102 | case 0: 103 | rs = append(rs, global.PKV{Key: key, Value: p.PropValue(key) + baseValue}) 104 | default: 105 | v := p.PropValue(key) + baseValue 106 | rate := p.PropValue(rateKey) 107 | value := v + gutil.Ceil(float32(v)*float32((rate)/10000)) 108 | rs = append(rs, global.PKV{Key: key, Value: value}) 109 | } 110 | } 111 | return rs 112 | } 113 | 114 | func (p *PropData) PropValue(key int32) int32 { 115 | return p.PropMap[key].Total 116 | } 117 | 118 | func MergerKeys(keys, acc []int32) []int32 { 119 | for _, v := range keys { 120 | if !gutil.SliceInt32Member(v, acc) { 121 | acc = append(acc, v) 122 | } 123 | } 124 | return acc 125 | } 126 | 127 | func MergerKeysVal(keys []config.KV, acc []int32) []int32 { 128 | for _, v := range keys { 129 | if !gutil.SliceInt32Member(v.Key, acc) { 130 | acc = append(acc, v.Key) 131 | } 132 | } 133 | return acc 134 | } 135 | 136 | func MergerProps(props, acc []config.KV) []config.KV { 137 | for _, v := range props { 138 | if i, ok := findIndex(v.Key, acc); ok { 139 | acc[i].Value += v.Value 140 | } else { 141 | acc = append(acc, v) 142 | } 143 | } 144 | return acc 145 | } 146 | 147 | func rmPropKey(pm map[int32]*PropSum, pKey int32, key PropKey) { 148 | if ps, ok := pm[pKey]; ok { 149 | if v, ok := ps.Props[key]; ok { 150 | ps.Total -= v 151 | delete(ps.Props, key) 152 | pm[pKey] = ps 153 | } 154 | } 155 | } 156 | 157 | func checkKey(key int32) bool { 158 | if key >= 1 && key <= propSize { 159 | return true 160 | } 161 | _, ok := sp_prop_map[key] 162 | return ok 163 | } 164 | 165 | func findIndex(key int32, arr []config.KV) (int, bool) { 166 | for i, v := range arr { 167 | if v.Key == key { 168 | return i, true 169 | } 170 | } 171 | return 0, false 172 | } 173 | 174 | // 通过指针位置,返回属性 175 | func GetPropValue(prop *global.PProp, key int32) int32 { 176 | ptr := unsafe.Pointer(prop) 177 | return *(*int32)(unsafe.Pointer(uintptr(ptr) + uintptr(key-1)*4)) 178 | } 179 | 180 | func SetPropValue(prop *global.PProp, key int32, value int32) { 181 | ptr := unsafe.Pointer(prop) 182 | *(*int32)(unsafe.Pointer(uintptr(ptr) + uintptr(key-1)*4)) = value 183 | } 184 | 185 | func GenCalcKeys(keys []int32) map[int32]bool { 186 | rs := make(map[int32]bool, len(keys)) 187 | for _, key := range keys { 188 | if key == PROP_MoveSpeedRate { 189 | rs[PROP_MoveSpeed] = true 190 | } else { 191 | rs[key] = true 192 | if pKey := IsRate(key); pKey > 0 { 193 | rs[pKey] = true 194 | } 195 | } 196 | } 197 | return rs 198 | } 199 | 200 | func HasRate(key int32) int32 { 201 | switch key { 202 | case PROP_MaxHP: 203 | return PROP_MaxHpRate 204 | case PROP_PhyAttack: 205 | return PROP_AttackRate 206 | case PROP_ArmorBreak: 207 | return PROP_ArmorBreakRate 208 | case PROP_PhyDefence: 209 | return PROP_DefenceRate 210 | case PROP_Hit: 211 | return PROP_HitAddRate 212 | case PROP_Miss: 213 | return PROP_MissAddRate 214 | case PROP_Crit: 215 | return PROP_CritAddRate 216 | case PROP_Tenacity: 217 | return PROP_TenacityAddRate 218 | case PROP_MoveSpeed: 219 | return PROP_MoveSpeedRate 220 | } 221 | return 0 222 | } 223 | 224 | func IsRate(key int32) int32 { 225 | switch key { 226 | case PROP_MaxHpRate: 227 | return PROP_MaxHP 228 | case PROP_AttackRate: 229 | return PROP_PhyAttack 230 | case PROP_ArmorBreakRate: 231 | return PROP_ArmorBreak 232 | case PROP_DefenceRate: 233 | return PROP_PhyDefence 234 | case PROP_HitAddRate: 235 | return PROP_Hit 236 | case PROP_MissAddRate: 237 | return PROP_Miss 238 | case PROP_CritAddRate: 239 | return PROP_Crit 240 | case PROP_TenacityAddRate: 241 | return PROP_Tenacity 242 | case PROP_MoveSpeedRate: 243 | return PROP_MoveSpeed 244 | } 245 | return 0 246 | } 247 | -------------------------------------------------------------------------------- /lib/type.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | type PropData struct { 4 | Cache map[PropKey][]int32 5 | PropMap map[int32]*PropSum 6 | } 7 | 8 | type PropKey struct { 9 | ID int32 10 | SubID int32 11 | } 12 | 13 | type PropSum struct { 14 | Total int32 15 | Props map[PropKey]int32 16 | } 17 | 18 | const ( 19 | PROP_MaxHP int32 = iota+1 20 | PROP_PhyAttack 21 | PROP_ArmorBreak 22 | PROP_PhyDefence 23 | PROP_Hit 24 | PROP_Miss 25 | PROP_Crit 26 | PROP_Tenacity 27 | PROP_MoveSpeed 28 | PROP_HpRecover 29 | PROP_MaxHpRate 30 | PROP_AttackRate 31 | PROP_ArmorBreakRate 32 | PROP_DefenceRate 33 | PROP_HitAddRate 34 | PROP_MissAddRate 35 | PROP_CritAddRate 36 | PROP_TenacityAddRate 37 | PROP_DamageDeepen 38 | PROP_DamageDef 39 | PROP_HitRate 40 | PROP_MissRate 41 | PROP_CritRate 42 | PROP_CritDef 43 | PROP_CritValue 44 | PROP_CritValueDef 45 | PROP_ParryRate 46 | PROP_ParryOver 47 | PROP_HuixinRate 48 | PROP_HuixinDef 49 | PROP_PvpDamageDeepen 50 | PROP_PvpDamageDef 51 | PROP_PveDamageDeepen 52 | PROP_PveDamageDef 53 | PROP_TotalDef 54 | ) 55 | 56 | // 添加是需要处理下面的一个map 57 | const ( 58 | PROP_MoveSpeedRate int32 = iota+1000 59 | ) 60 | 61 | var sp_prop_map = map[int32]int32{ 62 | PROP_MoveSpeedRate:PROP_MoveSpeed, 63 | } 64 | 65 | const ( 66 | PROP_KEY_BASE int32 = iota+1 67 | PROP_KEY_LEVEL 68 | PROP_KEY_BUFF 69 | ) 70 | -------------------------------------------------------------------------------- /main.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangmanlin/go-game-server/c8d43a0b2fd3dec603826a542833d6590f0c9116/main.exe -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "game/boot" 6 | "game/config" 7 | "game/global" 8 | "game/gw" 9 | "game/lib" 10 | "game/maps" 11 | _ "game/maps/mod" 12 | "game/player" 13 | "game/proto" 14 | "game/proto/router" 15 | "game/service" 16 | "github.com/liangmanlin/gootp/db" 17 | "github.com/liangmanlin/gootp/gate" 18 | "github.com/liangmanlin/gootp/gate/pb" 19 | "github.com/liangmanlin/gootp/kernel" 20 | "github.com/liangmanlin/gootp/node" 21 | "plugin" 22 | "time" 23 | ) 24 | 25 | func main() { 26 | root := lib.GetServerRoot() 27 | config.LoadAll(root + "/config/json") 28 | //kernel.Env.SetTimerMinTick(100) 29 | // 设置Env需要在启动之前 30 | kernel.KernelStart(initGame, stopGame) 31 | } 32 | 33 | func initGame() { 34 | kernel.ErrorLog("game is debug: %v", global.DEBUG) 35 | node.Env.PingTick = 30 * 1000 // 可以指定心跳间隔,默认60*1000ms 36 | global.NodeProto = append(global.NodeProto, maps.NodeProto()...) 37 | node.StartFromCommandLine(global.NodeProto) 38 | global.NodeProto = nil // gc 39 | kernel.SupStartChild("kernel", &kernel.SupChild{ChildType: kernel.SupChildTypeSup, Name: "game_sup"}) 40 | _, serviceSup := kernel.SupStartChild("game_sup", &kernel.SupChild{ChildType: kernel.SupChildTypeSup, Name: "service_sup"}) 41 | player.ENC = pb.Parse(proto.TOC, 0) 42 | player.DEC = pb.Parse(proto.TOS, 1) 43 | // 删除没有的数据,以免浪费gc的时间 44 | proto.TOC = nil 45 | proto.TOS = nil 46 | player.Router = router.MakeRouter() 47 | dbConfig := config.Server.Get("db").([]interface{}) 48 | port := int(dbConfig[1].(float64)) 49 | DBConfig := db.Config{Host: dbConfig[0].(string), Port: port, User: dbConfig[2].(string), PWD: dbConfig[3].(string)} 50 | lib.GameDB = db.Start(0,DBConfig, global.DBTables, config.Server.GetStr("game_db"),5,db.MODE_NORMAL) 51 | maps.Start(player.ENC, lib.GetServerRoot()+"/config/maps") 52 | // 启动控制台 53 | kernel.StartConsole( 54 | kernel.ConsoleHandler("reload", reload, 55 | kernel.ConsoleArg("dir"), 56 | kernel.ConsoleCommit("热更新so")), 57 | kernel.ConsoleHandler("reload_config", reloadConfig, 58 | kernel.ConsoleArg("configName"), 59 | kernel.ConsoleCommit("热更新配置")), 60 | ) 61 | svrList := []boot.Boot{ 62 | {service.AccountActor, global.SYS_ACCOUNT_SERVER}, 63 | } 64 | boot.StartService(serviceSup, svrList) 65 | // 最后启动网关 66 | gatePort := config.Server.GetInt("game_port") 67 | gate.Start("gw", gw.TcpClientActor, gatePort, gate.WithUseEpoll(),gate.WithClientArgs(player.ENC)) 68 | kernel.ErrorLog("start completed") 69 | } 70 | 71 | func stopGame() { 72 | gate.Stop("gw") 73 | time.Sleep(time.Second) 74 | kernel.ErrorLog("begin to stop map") 75 | kernel.AppStop("MapApp") 76 | } 77 | 78 | func reload(echo func(s string), commands []string) string { 79 | mod := commands[0] 80 | file := kernel.GetMainRoot() + "/hotfix/" + mod + "/" + mod + ".so" 81 | p, e := plugin.Open(file) 82 | if e != nil { 83 | kernel.ErrorLog("%s", e.Error()) 84 | return fmt.Sprintf("so can not load: %s\n", file) 85 | } 86 | f, err := p.Lookup("HotFix") 87 | if err != nil { 88 | kernel.ErrorLog("%s", err.Error()) 89 | return "HotFix func can not load\n" 90 | } 91 | f.(func())() 92 | kernel.ErrorLog("reload: %s", file) 93 | return fmt.Sprintf("reload: %s\n", file) 94 | } 95 | 96 | func reloadConfig(echo func(s string), commands []string) string { 97 | name := commands[0] 98 | var ok bool 99 | if name == "Server" { 100 | ok = config.Load(name, "./config/json/system") 101 | } else { 102 | ok = config.Load(name, "./config/json") 103 | } 104 | if ok { 105 | kernel.ErrorLog("reload config: %s", name) 106 | return fmt.Sprintf("reload config: %s\n", name) 107 | } else { 108 | kernel.ErrorLog("error: cannot reload config: %s", name) 109 | return fmt.Sprintf("error: cannot reload config: %s\n", name) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /maps/maps.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | "github.com/liangmanlin/gootp/astar" 7 | "github.com/liangmanlin/gootp/gate/pb" 8 | "github.com/liangmanlin/gootp/gutil" 9 | "github.com/liangmanlin/gootp/kernel" 10 | "github.com/liangmanlin/gootp/rand" 11 | "github.com/liangmanlin/gootp/timer" 12 | "log" 13 | ) 14 | 15 | var modRouter [global.MAP_MOD_MAX]*MsgHandler 16 | 17 | type MsgHandler func(ctx *kernel.Context, state *MapState, msg interface{}) 18 | 19 | var Encoder *pb.Coder 20 | 21 | var ModMap = make(map[string]*MapMod) 22 | 23 | func Start(encoder *pb.Coder, mapConfigPath string) { 24 | Encoder = encoder 25 | if err := kernel.AppStart(&app{mapConfigPath: mapConfigPath}); err != nil { 26 | log.Panic(err) 27 | } 28 | } 29 | 30 | func NewMapState(mapID int32, name string, mod *MapMod,ctx *kernel.Context) *MapState { 31 | state := MapState{Config: GetMapConfig(mapID), Mod: mod, Name: name, 32 | MapRoles: make(map[int64]*global.PMapRole), 33 | MapMonsters: make(map[int64]*global.PMapMonster), 34 | Roles: make(map[int64]*MapRoleData), 35 | Monsters: make(map[int64]*MState), 36 | Actors: make(map[AKey]*MapActor), 37 | MoveActor: make(map[AKey]*Move), 38 | Rand: rand.New(), 39 | Timer: timer.NewTimer(), 40 | DataMonster: &DataMonster{ 41 | PosMonsterNum: make(map[Area]int32, 50), 42 | }, 43 | DataSkill: &DataSkill{ 44 | EntityList: make(map[int32]*SkillEntity, 8), 45 | Add: make(map[int32]*SkillEntity, 4), 46 | }, 47 | } 48 | for i := 1; i < len(state.AreaMap); i++ { 49 | state.AreaMap[i] = make(AreaMap, 5) 50 | } 51 | state.Context = ctx 52 | return &state 53 | } 54 | 55 | func State(ctx *kernel.Context) *MapState { 56 | return ctx.State.(*MapState) 57 | } 58 | 59 | func (m *MapState) EnterActor(area Area, actorType int8, actorID int64) { 60 | m.AreaMap[actorType].EnterActorID(area, actorID) 61 | } 62 | 63 | func (m *MapState) LeaveActor(area Area, actorType int8, actorID int64) { 64 | m.AreaMap[actorType].LeaveActorID(area, actorID) 65 | } 66 | 67 | func (m *MapState) GetActor(aKey AKey) *MapActor { 68 | return m.Actors[aKey] 69 | } 70 | 71 | func (m *MapState) GetActorByID(actorType int8, actorID int64) *MapActor { 72 | return m.Actors[AKey{ActorID: actorID, ActorType: actorType}] 73 | } 74 | 75 | func (m *MapState) GetMapInfo(actorType int8, actorID int64) MapInfo { 76 | if actorType == global.ACTOR_ROLE { 77 | return m.MapRoles[actorID] 78 | } 79 | return m.MapMonsters[actorID] 80 | } 81 | 82 | func (m *MapState) GetMapInfoAKey(key AKey) MapInfo { 83 | return m.GetMapInfo(key.ActorType, key.ActorID) 84 | } 85 | 86 | func (m *MapState) GetMapInfoRole(roleID int64) *global.PMapRole { 87 | return m.MapRoles[roleID] 88 | } 89 | 90 | func (m *MapState) GetMapInfoMonster(monsterID int64) *global.PMapMonster { 91 | return m.MapMonsters[monsterID] 92 | } 93 | 94 | func (m *MapState) PosWalkable(pos *global.PPos) bool { 95 | return m.Config.PosWalkAble(pos) 96 | } 97 | 98 | func (m *MapState) XYWalkAble(X, Y float32) bool { 99 | return m.Config.XYWalkAble(X, Y) 100 | } 101 | 102 | func (m *MapState) XYI32WalkAble(X, Y int32) bool { 103 | return m.Config.XYI32WalkAble(X, Y) 104 | } 105 | 106 | func (m *MapState) GetGridConfig() astar.GridConfig { 107 | return m.Config 108 | } 109 | 110 | func (m *MapState) GetAStarCache() *astar.AStar { 111 | return m.AStar 112 | } 113 | 114 | func (m *MapState) SetAStarCache(a *astar.AStar) { 115 | m.AStar = a 116 | } 117 | 118 | func (m *MapState) MakeMonsterID() int64 { 119 | dm := m.DataMonster 120 | dm.MonsterID++ 121 | return dm.MonsterID 122 | } 123 | 124 | func (m *MapState) UpdateMonsterInfo(monsterID int64, pos *global.PPos, ul []*global.PKV) { 125 | proto := &global.MapTocUpdateMonsterInfo{ID: monsterID, UL: ul} 126 | m.BroadCastPos(pos, proto) 127 | } 128 | 129 | func (m *MapState) UpdateRoleInfo(roleID int64, pos *global.PPos, ul []*global.PKV) { 130 | proto := &global.MapTocUpdateRoleInfo{RoleID: roleID, UL: ul} 131 | m.BroadCastPos(pos, proto) 132 | } 133 | 134 | func (m *MapState) MonsterActor(monsterID int64) *MapActor { 135 | return m.Actors[AKey{monsterID, global.ACTOR_MONSTER}] 136 | } 137 | 138 | func (m *MapState) RoleActor(roleID int64) *MapActor { 139 | return m.Actors[AKey{roleID, global.ACTOR_ROLE}] 140 | } 141 | 142 | func (m *MapState) PosHaveMonster(pos *global.PPos) bool { 143 | dm := m.DataMonster 144 | k := Area{int16(gutil.Round(pos.X)), int16(gutil.Round(pos.Y))} 145 | if v, ok := dm.PosMonsterNum[k]; ok { 146 | return v > 0 147 | } 148 | return false 149 | } 150 | 151 | func (m *MapState) PosMonsterUP(grid Grid, inc int32) { 152 | mk := m.DataMonster.PosMonsterNum 153 | k := Area{int16(grid.X), int16(grid.Y)} 154 | if v, ok := mk[k]; ok { 155 | mk[k] = v + inc 156 | } else { 157 | mk[k] = gutil.MaxInt32(0, v+inc) 158 | } 159 | } 160 | 161 | func (m *MapState) PosSafe(pos *global.PPos) bool { 162 | return m.Config.PosSafe(pos) 163 | } 164 | 165 | func (m *MapState) AddSkillEntity(skillEntity *SkillEntity) { 166 | data := m.DataSkill 167 | if data.IsLoop { 168 | data.Add[skillEntity.ID] = skillEntity 169 | } else { 170 | data.EntityList[skillEntity.ID] = skillEntity 171 | } 172 | } 173 | 174 | func (m *MapState) DelSkillEntity(ID int32) { 175 | data := m.DataSkill 176 | if data.IsLoop { 177 | data.Del = append(data.Del, ID) 178 | } else { 179 | delete(data.EntityList, ID) 180 | } 181 | } 182 | 183 | func (m *MapState) CastPlayer(roleID int64, mod int32, msg interface{}) { 184 | if player := m.Roles[roleID]; player != nil { 185 | lib.CastPlayer(player.Player, mod, msg) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /maps/maps_actor.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | ) 7 | 8 | func ActorReduceHP(state *MapState, srcActor, destActor *MapActor, target MapInfo, damage int32) { 9 | if !target.IsAlive() { 10 | return 11 | } 12 | if destActor.ActorType == global.ACTOR_ROLE { 13 | RoleReduceHP(state, srcActor, destActor, target.(*global.PMapRole), damage) 14 | } else { 15 | MonsterReduceHP(state, srcActor, destActor, target.(*global.PMapMonster), damage) 16 | } 17 | } 18 | 19 | func NewActor(actorType int8, actorID int64) *MapActor { 20 | buffData := &BuffData{ 21 | Type2ID: make(map[int16]int16), 22 | Data: make(map[int16]*BuffInfo), 23 | EffectCount: make([]int16,2), 24 | } 25 | propData := lib.NewPropData() 26 | return &MapActor{ActorType: actorType, ActorID: actorID, State: ACTOR_STATE_NORMAL,BuffData: buffData,PropData: propData} 27 | } 28 | 29 | func (m *MapActor) IsAlive() bool { 30 | return m.State != ACTOR_STATE_DEAD 31 | } 32 | 33 | func UpdateHPMoveSpeed(state *MapState,actor *MapActor,upList []global.PKV) { 34 | if actor.ActorType == global.ACTOR_ROLE { 35 | RoleUpdateHPMoveSpeed(state,actor,upList) 36 | }else{ 37 | MonsterUpdateHPMoveSpeed(state,actor,upList) 38 | } 39 | } -------------------------------------------------------------------------------- /maps/maps_agent.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | "github.com/liangmanlin/gootp/kernel" 7 | ) 8 | 9 | var agentSvr = kernel.NewActor( 10 | kernel.HandleCastFunc( 11 | func(ctx *kernel.Context, msg interface{}) { 12 | switch m := msg.(type) { 13 | case *AgentChangeMap: 14 | if pid := lib.GetMapPid(m.MapName); pid != nil { 15 | lib.CastMap(pid, global.MAP_MOD_ROLE, m.Change) 16 | } else { 17 | // TODO 回城,这里比较复杂,先简单考虑直接连通 18 | lib.CastPlayer(m.Change.RoleData.Player, global.PLAYER_MOD_MAP, m.Change) 19 | } 20 | } 21 | 22 | }), 23 | ) 24 | -------------------------------------------------------------------------------- /maps/maps_aoi.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/gutil" 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | /* 11 | 使用九宫格aoi 12 | */ 13 | 14 | var _mapInfoPool = sync.Pool{ 15 | New: func() interface{} { 16 | return make([]MapInfo, 0, 5) 17 | }, 18 | } 19 | 20 | var areaCache = make(map[Area][]Area, 100) 21 | 22 | // 根据九宫格特性,对于同一个变编号的slice,他周围的8个slice id是一样的,所以为了节约内存,这里直接cache 23 | func buildAreasCache(maxWidth, maxHeight int32) { 24 | maxWidthID := maxWidth / AREA_WIDTH 25 | maxHeightID := maxHeight / AREA_HEIGHT 26 | for i := int32(0); i <= maxWidthID; i++ { 27 | for j := int32(0); j <= maxHeightID; j++ { 28 | areaID := GetArea(i, j) 29 | areas := getAroundAreas(i, j, maxWidthID, maxHeightID) 30 | areaCache[areaID] = areas 31 | } 32 | } 33 | } 34 | 35 | // 返回一个完整的九宫格 36 | func getAroundAreas(areaX, areaY, maxWidthID, maxHeightID int32) []Area { 37 | xMin := gutil.MaxInt32(areaX-1, 0) 38 | xMax := gutil.MinInt32(areaX+1, maxWidthID) 39 | yMin := gutil.MaxInt32(areaY-1, 0) 40 | yMax := gutil.MinInt32(areaY+1, maxHeightID) 41 | areas := make([]Area, (xMax-xMin+1)*(yMax-yMin+1)) 42 | var idx int 43 | for i := xMin; i <= xMax; i++ { 44 | for j := yMin; j <= yMax; j++ { 45 | areas[idx] = GetArea(i, j) 46 | idx++ 47 | } 48 | } 49 | return areas 50 | } 51 | 52 | func Get9AreaByPos(pos *global.PPos) []Area { 53 | area := GetAreaByPos(pos) 54 | return areaCache[area] 55 | } 56 | 57 | func Get9AreaByArea(area Area) []Area { 58 | return areaCache[area] 59 | } 60 | 61 | func GetAreaByPos(pos *global.PPos) Area { 62 | return GetArea(gutil.Round(pos.X), gutil.Round(pos.Y)) 63 | } 64 | 65 | func GetAreaByXY(posX, posY float32) Area { 66 | return GetArea(gutil.Round(posX), gutil.Round(posY)) 67 | } 68 | 69 | func GetAreaByGrid(grid Grid) Area { 70 | return GetArea(grid.X, grid.Y) 71 | } 72 | 73 | func GetArea(gridX, gridY int32) Area { 74 | return Area{X: int16(gridX / AREA_WIDTH), Y: int16(gridY / AREA_HEIGHT)} 75 | } 76 | 77 | func GetGridByPos(pos *global.PPos) Grid { 78 | return Grid{X: gutil.Round(pos.X), Y: gutil.Round(pos.Y)} 79 | } 80 | 81 | func GetGrid(posX, posY float32) Grid { 82 | return Grid{X: gutil.Round(posX), Y: gutil.Round(posY)} 83 | } 84 | 85 | func EnterArea(state *MapState, area Area, actorType int8, actorID int64) { 86 | state.AreaMap[actorType].EnterActorID(area, actorID) 87 | } 88 | 89 | func LeaveArea(state *MapState, area Area, actorType int8, actorID int64) { 90 | state.AreaMap[actorType].LeaveActorID(area, actorID) 91 | } 92 | 93 | type AreaActors []int64 94 | 95 | func NewAreaActors() *AreaActors { 96 | a := make(AreaActors, 0, 2) 97 | return &a 98 | } 99 | 100 | func (a *AreaActors) Del(actorID int64) { 101 | tmp := *a 102 | size := len(tmp) - 1 103 | for i := 0; i <= size; i++ { 104 | if tmp[i] == actorID { 105 | tmp[i] = tmp[size] 106 | *a = tmp[0:size] // 暂时不考虑gc问题,理论上不会占多少内存 107 | break 108 | } 109 | } 110 | } 111 | func (a *AreaActors) Add(actorID int64) { 112 | *a = append(*a, actorID) 113 | } 114 | 115 | type AreaMap map[Area]*AreaActors 116 | 117 | func (a AreaMap) EnterActorID(area Area, actorID int64) { 118 | if p, ok := a[area]; ok { 119 | p.Add(actorID) 120 | } 121 | p := NewAreaActors() 122 | p.Add(actorID) 123 | a[area] = p 124 | } 125 | 126 | func (a AreaMap) LeaveActorID(area Area, actorID int64) { 127 | if p, ok := a[area]; ok { 128 | p.Del(actorID) 129 | } 130 | } 131 | 132 | func (a AreaMap) AreaListActorIDs(areas []Area) []int64 { 133 | // 这里假设一个基数,大概4个人 134 | rs := make([]int64, 0, 4) 135 | for i := range areas { 136 | if p, ok := a[areas[i]]; ok { 137 | rs = append(rs, *p...) 138 | } 139 | } 140 | return rs 141 | } 142 | 143 | // 只读,上层逻辑需要注意,而且不建议存储,每次使用都读取 144 | func (a AreaMap) AreaActorIDs(area Area) []int64 { 145 | if p, ok := a[area]; ok { 146 | return *p 147 | } 148 | return nil 149 | } 150 | 151 | func AoiUpdatePos(state *MapState, oldX, oldY float32, newPos *global.PPos, mapInfo MapInfo) { 152 | oldGrid := GetGrid(oldX, oldY) 153 | newGrid := GetGridByPos(newPos) 154 | oldArea := GetAreaByGrid(oldGrid) 155 | newArea := GetAreaByGrid(newGrid) 156 | actorType := mapInfo.Type() 157 | actorID := mapInfo.ID() 158 | // 只有改变了区域才需要更新视野目标 159 | if oldArea != newArea { 160 | // 先退出 161 | state.AreaMap[actorType].LeaveActorID(oldArea, actorID) 162 | leaveAreas, enterAreas := GetEnterLeave(oldArea, newArea) // 计算一个进入和离开的区域 163 | LeaveRoles := GetAreasActorIDList(state, leaveAreas, global.ACTOR_ROLE) 164 | // 先通知退出 165 | state.BroadCastRoles(LeaveRoles, &global.MapTocActorLeaveArea{ActorID: actorID, ActorType: actorType}) 166 | //角色需要看见新的视野,但是怪物是不需要的 167 | if actorType == global.ACTOR_ROLE { 168 | roles := GetAreaRoles(state, enterAreas) 169 | monsters := GetAreaMonsters(state, enterAreas) 170 | LeaveMonsters := GetAreasActorIDList(state, leaveAreas, global.ACTOR_MONSTER) 171 | proto := &global.MapTocEnterArea{ 172 | RolesShow: roles, 173 | MonstersShow: monsters, 174 | RolesDel: LeaveRoles, 175 | MonstersDel: LeaveMonsters, 176 | } 177 | state.SendRoleProto(actorID, proto) 178 | proto2 := &global.MapTocRoleEnterArea{MapInfo: mapInfo.(*global.PMapRole)} 179 | state.BroadCastAreas(enterAreas, proto2) 180 | } else { 181 | proto2 := &global.MapTocMonsterEnterArea{MapInfo: mapInfo.(*global.PMapMonster)} 182 | state.BroadCastAreas(enterAreas, proto2) 183 | } 184 | // 最后进入area 185 | state.AreaMap[actorType].EnterActorID(newArea, actorID) 186 | } 187 | if actorType == global.ACTOR_MONSTER && oldGrid != newGrid { 188 | // 切换一下计数器 189 | state.PosMonsterUP(oldGrid, -1) 190 | state.PosMonsterUP(newGrid, 1) 191 | } 192 | } 193 | 194 | // 通过计算得到 195 | func GetEnterLeave(leaveArea, enterArea Area) (leave []Area, enter []Area) { 196 | key := Vector(leaveArea, enterArea) 197 | if p, ok := constLeaveEnterMap[key]; ok { 198 | size := len(p.leave) 199 | leave = make([]Area, 0, size) 200 | s := p.leave 201 | for i := range s { 202 | t := s[i] 203 | x := t.X + leaveArea.X 204 | y := t.Y + leaveArea.Y 205 | if x >= 0 && y >= 0 { 206 | leave = append(leave, Area{x, y}) 207 | } 208 | } 209 | size = len(p.enter) 210 | enter = make([]Area, 0, size) 211 | s = p.enter 212 | for i := range s { 213 | t := s[i] 214 | x := t.X + enterArea.X 215 | y := t.Y + enterArea.Y 216 | if x >= 0 && y >= 0 { 217 | enter = append(enter, Area{x, y}) 218 | } 219 | } 220 | return leave, enter 221 | } 222 | // 到这里说明跨越了九宫格,直接读取预生成的信息 223 | // TODO 看一下后续设计,理论上这里不需要拷贝 224 | copy(leave, areaCache[leaveArea]) 225 | copy(enter, areaCache[enterArea]) 226 | return leave, enter 227 | } 228 | 229 | func Vector(old, new Area) Area { 230 | return Area{new.X - old.X, new.Y - old.Y} 231 | } 232 | 233 | func GetAreasActorIDList(state *MapState, areas []Area, actorType int8) []int64 { 234 | return state.AreaMap[actorType].AreaListActorIDs(areas) 235 | } 236 | 237 | // 通常这些返回的对象都是不能修改的,切记 238 | func GetAreaRoles(state *MapState, areas []Area) []*global.PMapRole { 239 | p := state.AreaMap[global.ACTOR_ROLE] 240 | rs := make([]*global.PMapRole, 0, 2) 241 | mr := state.MapRoles 242 | for i := range areas { 243 | if l, ok := p[areas[i]]; ok { 244 | for _, roleID := range *l { 245 | if mapInfo, ok := mr[roleID]; ok { 246 | rs = append(rs, mapInfo) 247 | } 248 | } 249 | } 250 | } 251 | return rs 252 | } 253 | 254 | func GetAreaMonsters(state *MapState, areas []Area) []*global.PMapMonster { 255 | p := state.AreaMap[global.ACTOR_MONSTER] 256 | rs := make([]*global.PMapMonster, 0, 2) 257 | mr := state.MapMonsters 258 | for i := range areas { 259 | if l, ok := p[areas[i]]; ok { 260 | for _, monsterID := range *l { 261 | if mapInfo, ok := mr[monsterID]; ok { 262 | rs = append(rs, mapInfo) 263 | } 264 | } 265 | } 266 | } 267 | return rs 268 | } 269 | 270 | // 返回值最好不要进行修改,并且使用完归还pool里面 271 | func AreasActorFold(state *MapState, actorType int8, areas []Area, srcInfo MapInfo, f AreaFoldFunc, args ...unsafe.Pointer) []MapInfo { 272 | rs := MakeMapInfoSlice() 273 | pm := state.AreaMap[actorType] 274 | for i := range areas { 275 | if l, ok := pm[areas[i]]; ok { 276 | for _, actorID := range *l { 277 | if mapInfo := state.GetMapInfo(actorType, actorID); mapInfo != nil { 278 | if f(state, srcInfo, mapInfo, args...) { 279 | rs = append(rs, mapInfo) 280 | } 281 | } 282 | } 283 | } 284 | } 285 | return rs 286 | } 287 | 288 | func MakeMapInfoSlice() []MapInfo { 289 | s := _mapInfoPool.Get().([]MapInfo) 290 | return s[0:0] 291 | } 292 | 293 | func ReleaseMapInfoSlice(s []MapInfo) { 294 | for i := 0; i < cap(s); i++ { 295 | s[i] = nil // gc 296 | } 297 | _mapInfoPool.Put(s) 298 | } 299 | -------------------------------------------------------------------------------- /maps/maps_aoi_const.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | type le struct { 4 | leave []Area 5 | enter []Area 6 | } 7 | 8 | // 预先构造一个数据,方便计算出结果,相比取补集来说,这样快2倍以上 9 | var constLeaveEnterMap = map[Area]*le{ 10 | {1,1}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{1,-1}}, 11 | enter:[]Area{{-1,1},{0,1},{1,-1},{1,0},{1,1}}}, 12 | {-2,-2}:{leave:[]Area{{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 13 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0}}}, 14 | {-1,-1}:{leave:[]Area{{-1,1},{0,1},{1,-1},{1,0},{1,1}}, 15 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{1,-1}}}, 16 | {-1,1}:{leave:[]Area{{-1,-1},{0,-1},{1,-1},{1,0},{1,1}}, 17 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}}, 18 | {0,2}:{leave:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{1,-1},{1,0}}, 19 | enter:[]Area{{-1,0},{-1,1},{0,0},{0,1},{1,0},{1,1}}}, 20 | {2,-1}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,1}}, 21 | enter:[]Area{{-1,-1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 22 | {2,0}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1}}, 23 | enter:[]Area{{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 24 | {-2,2}:{leave:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 25 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,0},{1,1}}}, 26 | {-1,-2}:{leave:[]Area{{-1,0},{-1,1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 27 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{1,-1},{1,0}}}, 28 | {-1,2}:{leave:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{1,-1},{1,0},{1,1}}, 29 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,0},{0,1},{1,0},{1,1}}}, 30 | {0,1}:{leave:[]Area{{-1,-1},{0,-1},{1,-1}}, 31 | enter:[]Area{{-1,1},{0,1},{1,1}}}, 32 | {1,2}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{1,-1},{1,0}}, 33 | enter:[]Area{{-1,0},{-1,1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 34 | {-2,-1}:{leave:[]Area{{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 35 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1}}}, 36 | {-2,1}:{leave:[]Area{{-1,-1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 37 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,1}}}, 38 | {-1,0}:{leave:[]Area{{1,-1},{1,0},{1,1}}, 39 | enter:[]Area{{-1,-1},{-1,0},{-1,1}}}, 40 | {1,-1}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}, 41 | enter:[]Area{{-1,-1},{0,-1},{1,-1},{1,0},{1,1}}}, 42 | {1,0}:{leave:[]Area{{-1,-1},{-1,0},{-1,1}}, 43 | enter:[]Area{{1,-1},{1,0},{1,1}}}, 44 | {2,-2}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,0},{1,1}}, 45 | enter:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 46 | {2,1}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1}}, 47 | enter:[]Area{{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 48 | {-2,0}:{leave:[]Area{{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}, 49 | enter:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1}}}, 50 | {0,-2}:{leave:[]Area{{-1,0},{-1,1},{0,0},{0,1},{1,0},{1,1}}, 51 | enter:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{1,-1},{1,0}}}, 52 | {0,-1}:{leave:[]Area{{-1,1},{0,1},{1,1}}, 53 | enter:[]Area{{-1,-1},{0,-1},{1,-1}}}, 54 | {1,-2}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,0},{0,1},{1,0},{1,1}}, 55 | enter:[]Area{{-1,-1},{-1,0},{0,-1},{0,0},{1,-1},{1,0},{1,1}}}, 56 | {2,2}:{leave:[]Area{{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0}}, 57 | enter:[]Area{{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}}}, 58 | 59 | } 60 | -------------------------------------------------------------------------------- /maps/maps_app.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/boot" 5 | "game/lib" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "strconv" 8 | ) 9 | 10 | func (a *app) Name() string { 11 | return "MapApp" 12 | } 13 | 14 | func (a *app) Start(bootType kernel.AppBootType) *kernel.Pid { 15 | pid := kernel.SupStart("map_sup") 16 | boot.StartService(pid,[]boot.Boot{ 17 | {Name: "map_agent",Svr: agentSvr}, 18 | }) 19 | // 加载配置 20 | loadConfig(a.mapConfigPath) 21 | // 启动场景 22 | for mapID := range allMaps { 23 | for i:=0;i<200;i++ { 24 | StartMap(mapID, lib.NormalMapName(mapID)+strconv.Itoa(i), ModMap["mod_common"]) 25 | } 26 | } 27 | kernel.ErrorLog("map started") 28 | return pid 29 | } 30 | 31 | func (a *app) Stop(stopType kernel.AppStopType) { 32 | kernel.ErrorLog("map stopped") 33 | } 34 | 35 | func (a *app) SetEnv(key string, value interface{}) { 36 | 37 | } 38 | 39 | func (a *app) GetEnv(key string) interface{} { 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /maps/maps_astar.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/astar" 6 | ) 7 | 8 | func AStarSearch(state *MapState, x, y, dx, dy float32) (astar.ReturnType, *global.PMovePath) { 9 | rt, gridList := astar.Search(state, x, y, dx, dy) 10 | if rt == astar.ASTAR_FOUNDED { 11 | size := len(gridList) 12 | movePath := &global.PMovePath{EndX: dx, EndY: dy} 13 | if size > 0 { 14 | pGridList := make([]*global.PGrid, 0, size/2) 15 | for i := 0; i < size; i += 2 { 16 | pGrid := &global.PGrid{GridX: gridList[i],GridY: gridList[i+1]} 17 | pGridList = append(pGridList,pGrid) 18 | } 19 | movePath.GridPath = pGridList 20 | } 21 | return rt,movePath 22 | } 23 | return rt, nil 24 | } 25 | -------------------------------------------------------------------------------- /maps/maps_broadcast.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | func (m *MapState)BroadCastPos(pos *global.PPos,proto interface{}) { 9 | m.BroadCastAreas(Get9AreaByPos(pos), proto) 10 | } 11 | 12 | func (m *MapState)BroadCastXY(x,y float32,proto interface{}) { 13 | m.BroadCastAreas(Get9AreaByArea(GetAreaByXY(x,y)), proto) 14 | } 15 | 16 | func (m *MapState)BroadCastAreas(areas []Area, proto interface{}) { 17 | bin := Encoder.Encode(proto, 2) 18 | roleAreaMap := m.AreaMap[global.ACTOR_ROLE] 19 | for i := range areas { 20 | l := roleAreaMap.AreaActorIDs(areas[i]) 21 | for _, roleID := range l { 22 | if r, ok := m.Roles[roleID]; ok { 23 | kernel.Cast(r.TcpPid, bin) 24 | } 25 | } 26 | } 27 | } 28 | 29 | func (m *MapState)BroadCastMapAreas(areas map[Area]bool, proto interface{}) { 30 | bin := Encoder.Encode(proto, 2) 31 | rm := m.AreaMap[global.ACTOR_ROLE] 32 | for area,_ := range areas { 33 | l := rm.AreaActorIDs(area) 34 | for _, roleID := range l { 35 | if r, ok := m.Roles[roleID]; ok { 36 | kernel.Cast(r.TcpPid, bin) 37 | } 38 | } 39 | } 40 | } 41 | 42 | func (m *MapState)BroadCastPosExclude(pos *global.PPos,roleID int64,proto interface{}) { 43 | m.BroadCastAreasExclude(Get9AreaByPos(pos),roleID, proto) 44 | } 45 | 46 | func (m *MapState)BroadCastAreasExclude(areas []Area,roleID int64, proto interface{}) { 47 | bin := Encoder.Encode(proto, 2) 48 | rm := m.AreaMap[global.ACTOR_ROLE] 49 | for i := range areas { 50 | l := rm.AreaActorIDs(areas[i]) 51 | for _, rid := range l { 52 | if rid == roleID { 53 | continue 54 | } 55 | if r, ok := m.Roles[rid]; ok { 56 | kernel.Cast(r.TcpPid, bin) 57 | } 58 | } 59 | } 60 | } 61 | 62 | func (m *MapState)BroadCastRoles(roles []int64, proto interface{}) { 63 | bin := Encoder.Encode(proto, 2) 64 | for _, roleID := range roles { 65 | if r, ok := m.Roles[roleID]; ok { 66 | kernel.Cast(r.TcpPid, bin) 67 | } 68 | } 69 | } 70 | 71 | func (m *MapState) SendRoleProto(roleID int64, proto interface{}) { 72 | if r, ok := m.Roles[roleID]; ok { 73 | bin := Encoder.Encode(proto, 2) 74 | kernel.Cast(r.TcpPid, bin) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /maps/maps_buff.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | ) 7 | 8 | const ( 9 | BUFF_INV_TYPE_NORMAL int32 = iota + 1 10 | BUFF_INV_TYPE_INV 11 | ) 12 | 13 | const ( 14 | BUFF_EFFECT_COUNT_NO_MOVE = iota 15 | BUFF_EFFECT_COUNT_NO_SKILL 16 | ) 17 | 18 | func BuffLoop(state *MapState, now2 int64) { 19 | // 先遍历玩家buff 20 | roles := state.MapRoles 21 | for _, role := range roles { 22 | BuffLoopMapInfo(state, role, now2) 23 | } 24 | //monsters := state.MapMonsters 25 | //for _, monster := range monsters { 26 | // if monster.IsAlive() { 27 | // BuffLoopMapInfo(state, monster, now2) 28 | // } 29 | //} 30 | } 31 | 32 | var BuffLoopMapInfo = func(state *MapState, MI MapInfo, now2 int64) { 33 | buffs := MI.GetBuffs() 34 | if len(buffs) == 0 { 35 | return 36 | } 37 | actor := state.GetActorByID(MI.Type(), MI.ID()) 38 | data := actor.BuffData 39 | var idx int 40 | for _, buff := range buffs { 41 | cfg := config.Buffs.Get(int32(buff.ID)) 42 | if cfg.InvType == BUFF_INV_TYPE_INV { 43 | bd := data.Data[buff.ID] 44 | if now2 >= bd.TickTime { 45 | // TODO 执行效果 46 | bd.TickTime += int64(cfg.Inv) 47 | } 48 | } 49 | if now2 >= buff.EndTime { 50 | // 删除buff,需要移除一些效果 51 | DelBuffEffect(state,actor,MI,buff,cfg) 52 | DelBuffNotice(state, cfg, MI) 53 | } else { 54 | buffs[idx] = buff 55 | idx++ 56 | } 57 | } 58 | if idx > 0 { 59 | MI.SetBuffs(buffs[0:idx]) 60 | } else { 61 | // gc 62 | MI.SetBuffs(nil) 63 | } 64 | } 65 | 66 | var DelBuffEffectCount = func(data *BuffData, cfg *config.DefBuffs) { 67 | if cfg.NoMmove > 0 { 68 | data.EffectCount[BUFF_EFFECT_COUNT_NO_MOVE]-- 69 | } 70 | if cfg.NoSkill > 0 { 71 | data.EffectCount[BUFF_EFFECT_COUNT_NO_SKILL]-- 72 | } 73 | } 74 | 75 | var DelBuffNotice = func(state *MapState, cfg *config.DefBuffs, MI MapInfo) { 76 | switch cfg.NoticeType { 77 | case 1: 78 | // 广播 79 | proto := &global.MapTocDelBuff{ActorType: MI.Type(), ActorID: MI.ID(), BuffID: int16(cfg.Id)} 80 | state.BroadCastPos(MI.GetPos(), proto) 81 | case 2: 82 | // 只通知自己 83 | if MI.Type() == global.ACTOR_ROLE { 84 | proto := &global.MapTocDelBuff{ActorType: MI.Type(), ActorID: MI.ID(), BuffID: int16(cfg.Id)} 85 | state.SendRoleProto(MI.ID(), proto) 86 | } 87 | } 88 | } 89 | 90 | // buff通常都是有一些效果的,例如加属性 91 | var DelBuffEffect = func(state *MapState,actor *MapActor,MI MapInfo,buff *global.PBuff,cfg *config.DefBuffs) { 92 | data := actor.BuffData 93 | // 删除数据 94 | delete(data.Type2ID, buff.Type) 95 | delete(data.Data, buff.ID) 96 | DelBuffEffectCount(data, cfg) 97 | switch cfg.EffectType { 98 | case 1:// 加了属性 99 | PropRMBuffProp(state,actor,buff.ID) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /maps/maps_config.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "fmt" 5 | "game/config" 6 | "game/global" 7 | "github.com/liangmanlin/gootp/gutil" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | const ( 15 | MAP_DATA_GRID = iota + 1 16 | MAP_DATA_JUMP 17 | MAP_DATA_MONSTER 18 | MAP_DATA_DYNAMIC_GRID 19 | MAP_DATA_BORN 20 | MAP_DATA_COLLECT 21 | ) 22 | 23 | var allMaps = make(map[int32]*MapConfig, 100) 24 | 25 | // 需要保证单线程执行 26 | func loadConfig(mapConfigPath string) { 27 | cache := make(map[int32]*MapConfig) 28 | allMap := config.Maps.All() 29 | for i := range allMap { 30 | cfg := &allMap[i] 31 | blockID := cfg.BlockPath 32 | if c ,ok := cache[blockID];ok{ 33 | mc := *c 34 | mc.MapID = cfg.Id 35 | allMaps[cfg.Id] = &mc 36 | continue 37 | } 38 | file := mapConfigPath+"/"+strconv.FormatInt(int64(blockID),10) 39 | fi, err := os.Stat(file) 40 | if err != nil || fi.IsDir() { 41 | log.Panic(fmt.Errorf("map %d block:%d not found",cfg.Id,blockID)) 42 | } 43 | cache[blockID] = load(file,cfg.Id) 44 | } 45 | var maxWidth, maxHeight int32 46 | for _, v := range allMaps { 47 | maxWidth = gutil.MaxInt32(v.Width, maxWidth) 48 | maxHeight = gutil.MaxInt32(v.Height, maxHeight) 49 | } 50 | buildAreasCache(maxWidth, maxHeight) 51 | } 52 | 53 | func load(file string,mapID int32) *MapConfig { 54 | buf, err := ioutil.ReadFile(file) 55 | if err != nil { 56 | log.Panic(err) 57 | } 58 | var idx int 59 | // TODO 暂行先弄一个老项目的测试 60 | idx = 3 // 前三位已经废弃 61 | mapC := &MapConfig{MapID: mapID} 62 | idx, v := readInt16(idx, buf) 63 | mapC.Width = int32(v) 64 | idx, v = readInt16(idx, buf) 65 | mapC.Height = int32(v) 66 | mapC.PosList = make([]MapPos, int(mapC.Width+1)*int(mapC.Height+1)) 67 | size := len(buf) 68 | var t int8 69 | var bSize int32 70 | for idx < size { 71 | idx, t = readInt8(idx, buf) 72 | switch t { 73 | case MAP_DATA_GRID: 74 | idx = readGrid(mapC, idx, buf) 75 | case MAP_DATA_BORN: 76 | idx, mapC.BornPos = readBorn(idx, buf) 77 | case MAP_DATA_MONSTER: 78 | idx = readMonster(mapC, idx, buf) 79 | default: 80 | // 其他暂时不需要 81 | idx, bSize = readInt32(idx, buf) 82 | idx += int(bSize) 83 | } 84 | } 85 | allMaps[mapID] = mapC 86 | return mapC 87 | } 88 | 89 | func GetMapConfig(mapID int32) *MapConfig { 90 | return allMaps[mapID] 91 | } 92 | 93 | func readInt32(idx int, buf []byte) (int, int32) { 94 | v := int32(buf[idx])<<24 + int32(buf[idx+1])<<16 + int32(buf[idx+2])<<8 + int32(buf[idx+3]) 95 | return idx + 4, v 96 | } 97 | 98 | func readInt16(idx int, buf []byte) (int, int16) { 99 | v := int16(buf[idx])<<8 + int16(buf[idx+1]) 100 | return idx + 2, v 101 | } 102 | 103 | func readInt8(idx int, buf []byte) (int, int8) { 104 | return idx + 1, int8(buf[idx]) 105 | } 106 | 107 | func readGrid(config *MapConfig, idx int, buf []byte) int { 108 | idx, v := readInt32(idx, buf) 109 | end := idx + int(v) 110 | var x, y, g int16 111 | var t int8 112 | for idx < end { 113 | idx, x = readInt16(idx, buf) 114 | idx, y = readInt16(idx, buf) 115 | idx, t = readInt8(idx, buf) 116 | g = 1 117 | if t > 10 { // 如果是边缘,增加g值,使得寻路尽量避过边缘 118 | g = 3 119 | } 120 | config.PosList[config.GridIdx(int32(x), int32(y))] = MapPos{X: x, Y: y, Type: int16(t) % 10, G: g} // 大于10的是边缘 121 | config.PosCount++ 122 | } 123 | return idx 124 | } 125 | 126 | func readMonster(config *MapConfig, idx int, buf []byte) int { 127 | idx, v := readInt32(idx, buf) 128 | count := v / 8 129 | config.Monsters = make([]MapConfigMonster, count) 130 | for i := int32(0); i < count; i++ { 131 | m := &config.Monsters[i] 132 | idx, m.X = readInt16(idx, buf) 133 | idx, m.Y = readInt16(idx, buf) 134 | idx, m.TypeID = readInt32(idx, buf) 135 | } 136 | return idx 137 | } 138 | 139 | func readBorn(idx int, buf []byte) (int, Area) { 140 | idx, _ = readInt32(idx, buf) 141 | idx, x := readInt16(idx, buf) 142 | idx, y := readInt16(idx, buf) 143 | return idx, Area{x, y} 144 | } 145 | 146 | func (m *MapConfig) PosWalkAble(pos *global.PPos) bool { 147 | return m.XYI32WalkAble(gutil.Round(pos.X),gutil.Round(pos.Y)) 148 | } 149 | 150 | func (m *MapConfig) XYWalkAble(x, y float32) bool { 151 | return m.XYI32WalkAble(gutil.Round(x), gutil.Round(y)) 152 | } 153 | 154 | func (m *MapConfig) XYI32WalkAble(x, y int32) bool { 155 | if x >= 0 && x <= m.Width && y >= 0 && y <= m.Height { 156 | idx := m.GridIdx(x, y) 157 | return m.PosList[idx].Type > 0 158 | } 159 | return false 160 | } 161 | 162 | func (m *MapConfig) XYI32WalkAbleBorder(x, y int32, c int) int { 163 | if x >= 0 && x <= m.Width && y >= 0 && y <= m.Height { 164 | idx := m.GridIdx(x, y) 165 | grid := m.PosList[idx] 166 | if grid.Type > 0 { 167 | if grid.G > 1 { 168 | c++ 169 | } 170 | return c 171 | } 172 | return 100 173 | } 174 | return 100 175 | } 176 | 177 | func(m *MapConfig) GetWidth() int32 { 178 | return m.Width 179 | } 180 | 181 | func(m *MapConfig) GetHeight() int32 { 182 | return m.Height 183 | } 184 | 185 | func (m *MapConfig)GridType(gridIdx int32) (gridType int16, g int16) { 186 | grid := m.PosList[gridIdx] 187 | return grid.Type,grid.G 188 | } 189 | 190 | func (m *MapConfig) PosSafe(pos *global.PPos)bool { 191 | x := gutil.Round(pos.X) 192 | y := gutil.Round(pos.Y) 193 | if x >= 0 && x <= m.Width && y >= 0 && y <= m.Height { 194 | idx := m.GridIdx(x, y) 195 | return m.PosList[idx].Type == 2 196 | } 197 | return false 198 | } 199 | 200 | func (m *MapConfig) GridIdx(x, y int32) int { 201 | return int(m.Width*y + x) 202 | } 203 | 204 | -------------------------------------------------------------------------------- /maps/maps_misc.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | ) 7 | 8 | func MakeRandomXY(state *MapState, pos *global.PPos, dist int32) (x, y float32, ok bool) { 9 | // 最多随机20次 10 | for i := 0; i < 20; i++ { 11 | rx := state.Rand.Random(-dist, dist) 12 | ry := state.Rand.Random(-dist, dist) 13 | if rx != 0 && ry != 0 { 14 | x = pos.X + float32(rx) 15 | y = pos.Y + float32(ry) 16 | if state.XYWalkAble(x, y) { 17 | ok = true 18 | break 19 | } 20 | } 21 | } 22 | return 23 | } 24 | 25 | func CalcNewWalkAbleXY(state *MapState, pos *global.PPos, dir int16, dist int32) (x, y float32) { 26 | cos := lib.Cos(dir) 27 | sin := lib.Sin(dir) 28 | x = pos.X + float32(cos*float64(dist)) 29 | y = pos.Y + float32(sin*float64(dist)) 30 | // 假如目标点可走,直接返回 31 | if state.XYWalkAble(x, y) { 32 | return 33 | } 34 | for i := int32(1); i < dist; i++ { 35 | x = pos.X + float32(cos*float64(i)) 36 | y = pos.Y + float32(sin*float64(i)) 37 | if state.XYWalkAble(x, y) { 38 | return 39 | } 40 | } 41 | return pos.X, pos.Y 42 | } 43 | -------------------------------------------------------------------------------- /maps/maps_monster.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "game/lib" 7 | "github.com/liangmanlin/gootp/gutil" 8 | "github.com/liangmanlin/gootp/kernel" 9 | "math" 10 | "sync" 11 | ) 12 | 13 | var _argPool = sync.Pool{ 14 | New: func() interface{} { 15 | return &MonsterCreateArg{} 16 | }, 17 | } 18 | func CreateMonsterArg() *MonsterCreateArg { 19 | arg := _argPool.Get().(*MonsterCreateArg) 20 | arg.Dir = -1 21 | arg.Level = 0 22 | arg.DeadArgs = nil 23 | arg.DeadCB = nil 24 | arg.Prop = nil 25 | return arg 26 | } 27 | 28 | // 不太可能热更 29 | func MonsterLoop(state *MapState, now2 int64) { 30 | dm := state.DataMonster 31 | if dm.StopMonster == 0 || dm.StopMonster > now2 { 32 | dm.IsMonsterLoop = true 33 | for _, mState := range state.Monsters { 34 | // 先更新buff 35 | if mapMonster := state.GetMapInfoMonster(mState.ID);mapMonster.IsAlive() { 36 | BuffLoopMapInfo(state, mapMonster, now2) 37 | } 38 | monsterUpdate(state, mState, now2) 39 | } 40 | dm.IsMonsterLoop = false 41 | if len(dm.DelMonster) > 0 { 42 | for _, id := range dm.DelMonster { 43 | MonsterDelete(state, state.Monsters[id]) 44 | } 45 | dm.DelMonster = dm.DelMonster[0:0] 46 | } 47 | } 48 | } 49 | 50 | // 不太可能热更 51 | func MonsterUpdateSecond(state *MapState, now2 int64) { 52 | dm := state.DataMonster 53 | if len(dm.AroundDest) > 0 { 54 | // 处理环绕 55 | for ak, l := range dm.AroundDest { 56 | actor := state.GetActor(ak) 57 | if actor == nil || actor.State == ACTOR_STATE_DEAD { 58 | continue 59 | } 60 | mi := state.GetMapInfoAKey(ak) 61 | for _, monsterID := range l { 62 | monsterInfo := state.GetMapInfoMonster(monsterID) 63 | if monsterInfo == nil || monsterInfo.HP == 0 || !state.PosHaveMonster(monsterInfo.Pos) { 64 | continue 65 | } 66 | mState := state.Monsters[monsterID] 67 | attackRadius := mState.Config.AttackRadius 68 | if x, y, ok := GetFreeAroundPos(state, mi.GetPos(), monsterInfo.Pos, float32(attackRadius)-0.5); ok { 69 | MoveToPos(state, mState, now2, x, y, 0) 70 | } 71 | } 72 | delete(dm.AroundDest, ak) 73 | } 74 | } 75 | roleNum := len(state.Roles) 76 | if roleNum > 0 { 77 | dm.StopMonster = 0 78 | }else if roleNum == 0 && dm.StopMonster == 0 { 79 | dm.StopMonster = now2+60*1000 80 | } 81 | } 82 | 83 | // 计算一个环绕坐标,如果所有目标都有怪物,就采用当前位置 84 | func GetFreeAroundPos(state *MapState, centerPos, pos *global.PPos, dist float32) (x, y float32, ok bool) { 85 | // 计算起始位置的角度 86 | // 假设同一面的怪物是聚集点,所以随机一个300度 87 | for i := 0; i < 20; i++ { 88 | dir := lib.CalcDir(centerPos, pos) 89 | dir = state.Rand.Random(0, 300) + dir + 30 90 | r := lib.DirToTan(dir) 91 | x = centerPos.X + float32(math.Cos(r))*dist 92 | y = centerPos.Y + float32(math.Sin(r))*dist 93 | if state.XYWalkAble(x, y) { 94 | ok = true 95 | break 96 | } 97 | } 98 | return 99 | } 100 | 101 | func CreateMapAllMonster(state *MapState) { 102 | cfg := state.Config 103 | for i := range cfg.Monsters { 104 | m := &cfg.Monsters[i] 105 | arg := CreateMonsterArg() 106 | arg.X = float32(m.X) 107 | arg.Y = float32(m.Y) 108 | arg.TypeID = m.TypeID 109 | CreateMonster(state, arg) 110 | } 111 | } 112 | 113 | var CreateMonster = func(state *MapState, arg *MonsterCreateArg) (monsterID int64) { 114 | // 最后归还 115 | defer _argPool.Put(arg) 116 | cfg := config.Monster.Get(arg.TypeID) 117 | if cfg == nil { 118 | kernel.ErrorLog("monster %d config not found", arg.TypeID) 119 | return 120 | } 121 | monsterID = state.MakeMonsterID() 122 | bornPos := &global.PPos{X: arg.X, Y: arg.Y, Dir: arg.Dir} 123 | mState := &MState{ID: monsterID, TypeID: arg.TypeID, 124 | BornPos: bornPos, DeadCB: arg.DeadCB, DeadArgs: arg.DeadArgs, 125 | Enemies: make(map[AKey]*MonsterEnemy), 126 | } 127 | RebornMonster(state, mState, arg.Level, cfg, arg.Prop) 128 | state.Monsters[monsterID] = mState 129 | return 130 | } 131 | 132 | var RebornMonster = func(state *MapState, mState *MState, level int16, cfg *config.DefMonster, prop *global.PProp) { 133 | mState.Config = cfg 134 | if prop == nil { 135 | prop = lib.ToProp(config.MonsterProp.Get(cfg.PropID)) 136 | } 137 | if level == 0 { 138 | level = int16(cfg.Level) 139 | } 140 | var pos *global.PPos 141 | var mapInfo *global.PMapMonster 142 | if t, ok := state.MapMonsters[mState.ID]; ok { 143 | mapInfo = t 144 | mapInfo.HP = prop.MaxHP 145 | mapInfo.MaxHP = prop.MaxHP 146 | mapInfo.MoveSpeed = prop.MoveSpeed 147 | mapInfo.Level = level 148 | mapInfo.Pos.X = mState.BornPos.X 149 | mapInfo.Pos.Y = mState.BornPos.Y 150 | mapInfo.Pos.Dir = mState.BornPos.Dir 151 | pos = mapInfo.Pos 152 | } else { 153 | p := *mState.BornPos 154 | pos = &p 155 | mapInfo = &global.PMapMonster{MonsterID: mState.ID, TypeID: mState.TypeID, HP: prop.MaxHP, MaxHP: prop.MaxHP, 156 | Level: level, Pos: pos, MoveSpeed: prop.MoveSpeed, 157 | } 158 | } 159 | if pos.Dir < 0 { 160 | pos.Dir = int16(state.Rand.Random(0, 359)) 161 | } 162 | aKey := AKey{ActorID: mState.ID, ActorType: global.ACTOR_MONSTER} 163 | actor := state.GetActor(aKey) 164 | if actor == nil { 165 | actor = NewActor(global.ACTOR_MONSTER, mState.ID) 166 | state.Actors[aKey] = actor 167 | } 168 | actor.State = ACTOR_STATE_NORMAL 169 | mState.State = ACTOR_STATE_NORMAL 170 | state.MapMonsters[mState.ID] = mapInfo 171 | state.Monsters[mState.ID] = mState 172 | if len(mState.Skills) == 0 { 173 | mState.Skills = MakeMonsterSkill(cfg) 174 | } 175 | grid := GetGridByPos(pos) 176 | state.EnterActor(GetAreaByGrid(grid), global.ACTOR_MONSTER, mState.ID) 177 | state.PosMonsterUP(grid, 1) 178 | // 通知视野的人 179 | proto := &global.MapTocMonsterEnterArea{MapInfo: mapInfo} 180 | state.BroadCastPos(pos, proto) 181 | } 182 | 183 | func MakeMonsterSkill(cfg *config.DefMonster) []MonsterSkill { 184 | var rs []MonsterSkill 185 | for _, skillID := range cfg.Skills { 186 | cd := config.Skill.Get(skillID).SkillCD 187 | rs = append(rs, MonsterSkill{SkillID: skillID, CD: cd}) 188 | } 189 | return rs 190 | } 191 | 192 | func monsterUpdate(state *MapState, mState *MState, now2 int64) { 193 | if now2 >= mState.NextTime { 194 | MonsterUpdate(state, mState, now2) 195 | } 196 | } 197 | 198 | func MonsterUpdate(state *MapState, mState *MState, now2 int64) { 199 | if mState.State == ACTOR_STATE_DEAD { 200 | cfg := config.Monster.Get(mState.TypeID) 201 | if cfg.RefreshTime > 0 { 202 | level := state.GetMapInfoMonster(mState.ID).Level 203 | RebornMonster(state, mState, level, cfg, state.Actors[AKey{ActorType: global.ACTOR_MONSTER, ActorID: mState.ID}].BaseProp) 204 | } else { 205 | MonsterDelete(state, mState) 206 | } 207 | return 208 | } 209 | size := len(mState.Doings) 210 | if size == 0 { 211 | BehaviorActTree(state, mState, now2) 212 | } else { 213 | do := mState.Doings[size-1] 214 | (*do.Fun)(state, mState, now2, do.Args) 215 | } 216 | } 217 | 218 | func MonsterDelete(state *MapState, mState *MState) { 219 | dm := state.DataMonster 220 | if dm.IsMonsterLoop { 221 | dm.DelMonster = append(dm.DelMonster, mState.ID) 222 | return 223 | } 224 | aKey := AKey{ActorType: global.ACTOR_MONSTER, ActorID: mState.ID} 225 | if mState.State != ACTOR_STATE_DEAD { 226 | MoveMonsterStop(state, mState.ID, false) 227 | mapInfo := state.GetMapInfoMonster(mState.ID) 228 | grid := GetGridByPos(mapInfo.Pos) 229 | state.LeaveActor(GetAreaByGrid(grid), global.ACTOR_MONSTER, mState.ID) 230 | state.PosMonsterUP(grid, -1) 231 | state.BroadCastPos(mapInfo.Pos, &global.MapTocActorLeaveArea{ActorID: mState.ID, ActorType: global.ACTOR_MONSTER}) 232 | } 233 | delete(state.Monsters, mState.ID) 234 | delete(state.MapMonsters, mState.ID) 235 | delete(state.Actors, aKey) 236 | } 237 | 238 | var MonsterReduceHP = func(state *MapState, srcActor, destActor *MapActor, destMI *global.PMapMonster, damage int32) { 239 | mState := state.Monsters[destMI.MonsterID] 240 | AddEnemy(state, mState, srcActor, damage) 241 | if mState.HPChange != nil { 242 | (*mState.HPChange)(state,mState,srcActor,damage) 243 | } 244 | if destMI.HP > damage { 245 | destMI.HP -= damage 246 | } else { 247 | MonsterDead(state, mState, srcActor, destActor, destMI) 248 | // 执行死亡后在设置血量为0 249 | destMI.HP = 0 250 | } 251 | } 252 | 253 | var MonsterDead = func(state *MapState, mState *MState, srcActor, destActor *MapActor, destMI *global.PMapMonster) { 254 | monsterID := destMI.MonsterID 255 | MoveMonsterStop(state, monsterID, false) 256 | destActor.State = ACTOR_STATE_DEAD 257 | mState.State = ACTOR_STATE_DEAD 258 | grid := GetGridByPos(destMI.Pos) 259 | state.LeaveActor(GetAreaByGrid(grid), global.ACTOR_MONSTER, monsterID) 260 | state.PosMonsterUP(grid, -1) 261 | mState.NextTime = kernel.Now2() + int64(mState.Config.RefreshTime) 262 | proto := &global.MapTocActorDead{ActorID: monsterID, ActorType: global.ACTOR_MONSTER} 263 | state.BroadCastPos(destMI.Pos, proto) 264 | if mState.DeadCB != nil { 265 | (*mState.DeadCB)(state, monsterID, srcActor, mState.DeadArgs) 266 | } 267 | } 268 | 269 | func AddEnemy(state *MapState, mState *MState, srcActor *MapActor, damage int32) { 270 | key := AKey{ActorID: srcActor.ActorID, ActorType: srcActor.ActorType} 271 | if enemy, ok := mState.Enemies[key]; ok { 272 | enemy.LastAttackTime = kernel.Now2() 273 | enemy.TotalHurt += int64(damage) 274 | } else { 275 | enemy = NewEnemy() 276 | enemy.TotalHurt = int64(damage) 277 | enemy.LastAttackTime = kernel.Now2() 278 | enemy.ActorType = srcActor.ActorType 279 | enemy.ActorID = srcActor.ActorID 280 | mState.Enemies[key] = enemy 281 | // 如果第一次被攻击,需要切换到战斗状态 282 | if len(mState.Enemies) == 1 { 283 | TryFight(mState) 284 | } 285 | } 286 | } 287 | 288 | func TryFight(mState *MState) { 289 | 290 | } 291 | 292 | func MonsterUpdateHPMoveSpeed(state *MapState,actor *MapActor,upList []global.PKV) { 293 | MI := state.MapMonsters[actor.ActorID] 294 | ul := make([]*global.PKV,0,len(upList)) 295 | for i := range upList { 296 | v := &upList[i] 297 | switch v.Key { 298 | case lib.PROP_MaxHP: 299 | if MI.HP >= v.Value { 300 | MI.HP = v.Value 301 | }else{ 302 | MI.HP = gutil.Trunc(float32(MI.HP)/float32(MI.MaxHP)*float32(v.Value)) 303 | } 304 | MI.MaxHP = v.Value 305 | v.Key = MAP_MONSTER_MAX_HP 306 | ul = append(ul,v,&global.PKV{Key: MAP_MONSTER_MAX_HP,Value: MI.HP}) 307 | case lib.PROP_MoveSpeed: 308 | if v.Value != MI.MoveSpeed { 309 | MI.MoveSpeed = v.Value 310 | v.Key = MAP_MONSTER_MOVE_SPEED 311 | ul = append(ul,v) 312 | } 313 | } 314 | } 315 | state.UpdateMonsterInfo(MI.MonsterID,MI.Pos,ul) 316 | } 317 | 318 | func (m *MState) DelDoing(funPtr *behaviorFunc) { 319 | size := len(m.Doings) 320 | if size > 0 { 321 | doing := m.Doings[size-1] 322 | if doing.Fun == funPtr { 323 | _doingPool.Put(doing) 324 | m.Doings = m.Doings[0 : size-1] 325 | } 326 | } 327 | } 328 | 329 | func (m *MState) DelDoingName(funName string) { 330 | ptr := GetBehaviorFun(funName) 331 | m.DelDoing(ptr) 332 | } 333 | 334 | func (m *MState) CleanDoing() { 335 | size := len(m.Doings) 336 | if size > 0 { 337 | for i := 0; i < size; i++ { 338 | doing := m.Doings[i] 339 | _doingPool.Put(doing) 340 | } 341 | m.Doings = m.Doings[0:0] 342 | } 343 | } 344 | 345 | func (m *MState) AddDoing(bType int32, fun *behaviorFunc, args interface{}) { 346 | doing := NewDoing() 347 | doing.Type = bType 348 | doing.Fun = fun 349 | doing.Args = args 350 | m.Doings = append(m.Doings, doing) 351 | } 352 | 353 | func (m *MState) AddDoingName(bType int32, funName string, args interface{}) { 354 | m.AddDoing(bType, GetBehaviorFun(funName), args) 355 | } 356 | 357 | func (m *MState) ReturnType() (ReturnType, int64) { 358 | if m.Return != nil { 359 | return m.Return.Type, m.Return.Time 360 | } 361 | return RETURN_TYPE_TRUE, 0 362 | } 363 | 364 | func (m *MState) CleanEnemy() { 365 | size := len(m.Enemies) 366 | if size > 0 { 367 | for k, v := range m.Enemies { 368 | _enemyPool.Put(v) 369 | delete(m.Enemies, k) 370 | } 371 | } 372 | } 373 | 374 | -------------------------------------------------------------------------------- /maps/maps_move.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "math" 7 | "sync" 8 | ) 9 | 10 | const DELTA_ERROR = 64 11 | const DIST_FAIL = 25 12 | 13 | var _movePool = sync.Pool{ 14 | New: func() interface{} { 15 | return &Move{} 16 | }, 17 | } 18 | 19 | func MakeMove() *Move { 20 | return _movePool.Get().(*Move) 21 | } 22 | 23 | // 开始移动 24 | var MoveStart = func(state *MapState, actorType int8, actorID int64, startX, startY float32, movePath *global.PMovePath) { 25 | now2 := kernel.Now2() 26 | aKey := AKey{ActorID: actorID, ActorType: actorType} 27 | actor := state.GetActor(aKey) 28 | if actorType == global.ACTOR_ROLE && actor.IsMove { 29 | move := state.MoveActor[aKey] 30 | if move != nil { 31 | ActorMove(state, actor, move, now2, false) 32 | } 33 | } 34 | mapInfo := state.GetMapInfo(actorType, actorID) 35 | pos := mapInfo.GetPos() 36 | // 先简单判断一下 37 | if actorType == global.ACTOR_MONSTER || 38 | (MoveCheckDist(state, actorType, actorID, pos, startX, startY) && state.XYWalkAble(startX, startY)) { 39 | var dir int16 40 | move := MakeMove() 41 | move.LastUpdateTime = now2 42 | move.StepCount = 0 43 | if len(movePath.GridPath) == 0 { 44 | move.DestX = movePath.EndX 45 | move.DestY = movePath.EndY 46 | } else { 47 | grid := movePath.GridPath[0] 48 | move.DestX = float32(grid.GridX) 49 | move.DestY = float32(grid.GridY) 50 | } 51 | _, _, dir = CalcNewXY(startX, startY, move.DestX, move.DestY, 0) 52 | move.StepTotal = CalcTotalMove(startX, startY, move.DestX, move.DestY) 53 | state.MoveActor[aKey] = move 54 | actor.IsMove = true 55 | oldX := pos.X 56 | oldY := pos.Y 57 | pos.X = startX 58 | pos.Y = startY 59 | pos.Dir = dir 60 | mapInfo.SetMovePath(movePath) 61 | // 可能起始位置有变化,所以这里需要刷新一下视野 62 | if actorType == global.ACTOR_ROLE { 63 | AoiUpdatePos(state, oldX, oldY, pos, mapInfo) 64 | } 65 | proto := &global.MapTocMove{ActorType: actorType, ActorID: actorID, StartX: startX, StartY: startY, MovePath: movePath} 66 | //kernel.ErrorLog("%s",lib.ToString(proto)) 67 | state.BroadCastPos(pos, proto) 68 | return 69 | } 70 | // 说明不合法,需要停止移动 71 | kernel.ErrorLog("un walkable:%d,%d,%#v,%f,%f,%v", actorID, now2, pos, startX, startY, actor.IsMove) 72 | SyncStop(state, actor, true) 73 | } 74 | 75 | // 角色停止移动 76 | var MoveRoleStop = func(state *MapState, roleID int64, stopX, stopY float32, dir int16) { 77 | mapInfo := state.MapRoles[roleID] 78 | if mapInfo == nil { 79 | return 80 | } 81 | aKey := AKey{ActorType: global.ACTOR_ROLE, ActorID: roleID} 82 | actor := state.GetActor(aKey) 83 | if actor.IsMove { 84 | DelMove(state.MoveActor, aKey) 85 | actor.IsMove = false 86 | mapInfo.MovePath = nil 87 | if MoveCheckDist(state, global.ACTOR_ROLE, roleID, mapInfo.Pos, stopX, stopY) && 88 | state.XYWalkAble(stopX, stopY) { 89 | oldX := mapInfo.Pos.X 90 | oldY := mapInfo.Pos.Y 91 | mapInfo.Pos.X = stopX 92 | mapInfo.Pos.Y = stopY 93 | mapInfo.Pos.Dir = dir 94 | AoiUpdatePos(state, oldX, oldY, mapInfo.Pos, mapInfo) 95 | proto := &global.MapTocMoveStop{ActorType: global.ACTOR_ROLE, ActorID: roleID, X: stopX, Y: stopY, Dir: dir} 96 | state.BroadCastPosExclude(mapInfo.Pos, roleID, proto) 97 | } else { 98 | SyncStop(state, actor, false) 99 | } 100 | } else { 101 | // 没有在移动 102 | proto := &global.MapTocStop{ActorType: global.ACTOR_ROLE, ActorID: roleID, X: mapInfo.Pos.X, Y: mapInfo.Pos.Y, Dir: dir} 103 | state.SendRoleProto(roleID, proto) 104 | } 105 | } 106 | 107 | // 怪物停止移动要简单很多 108 | var MoveMonsterStop = func(state *MapState, monsterID int64, notice bool) { 109 | mapInfo := state.MapMonsters[monsterID] 110 | if mapInfo == nil { 111 | return 112 | } 113 | aKey := AKey{ActorType: global.ACTOR_MONSTER, ActorID: monsterID} 114 | actor := state.GetActor(aKey) 115 | if actor.IsMove { 116 | actor.IsMove = false 117 | DelMove(state.MoveActor, aKey) 118 | mapInfo.MovePath = nil 119 | if notice { 120 | proto := &global.MapTocMoveStop{ActorType: global.ACTOR_MONSTER, ActorID: monsterID, 121 | X: mapInfo.Pos.X, Y: mapInfo.Pos.Y, Dir: mapInfo.Pos.Dir} 122 | state.BroadCastPos(mapInfo.Pos, proto) 123 | } 124 | } 125 | } 126 | 127 | // 每帧更新位置 128 | func MoveUpdate(state *MapState, now2 int64) { 129 | for aKey, move := range state.MoveActor { 130 | actor := state.GetActor(aKey) 131 | if !ActorMove(state, actor, move, now2, true) { 132 | DelMove(state.MoveActor, aKey) 133 | } 134 | } 135 | } 136 | 137 | var ActorMove = func(state *MapState, actor *MapActor, move *Move, now2 int64, syncStop bool) (keepMove bool) { 138 | if !actor.IsMove { 139 | return false 140 | } 141 | dcTime := now2 - move.LastUpdateTime 142 | if dcTime < 0 { 143 | return true 144 | } 145 | mapInfo := state.GetMapInfo(actor.ActorType, actor.ActorID) 146 | moveSpeed, movePath, pos := mapInfo.GetMove() 147 | moveDistance := float64(moveSpeed) * float64(dcTime) / (100 * 1000) 148 | newMoveDistance := move.StepCount + moveDistance 149 | var x, y float32 150 | var dir int16 151 | var nexGrid bool 152 | if newMoveDistance >= move.StepTotal { 153 | // 走完当前拐点,判断一下是否有下一个拐点 154 | size := len(movePath.GridPath) 155 | switch size { 156 | case 0: 157 | // 走完了 158 | x = move.DestX 159 | y = move.DestY 160 | dir = pos.Dir 161 | move.LastUpdateTime = 0 162 | movePath = nil 163 | case 1: 164 | nexGrid = true 165 | startGrid := movePath.GridPath[0] 166 | // 最后一个拐点 167 | move.StepCount = newMoveDistance - move.StepTotal 168 | move.DestX = movePath.EndX 169 | move.DestY = movePath.EndY 170 | move.StepTotal = CalcTotalMove(float32(startGrid.GridX), float32(startGrid.GridY), movePath.EndX, movePath.EndY) 171 | x, y, dir = CalcNewXY(float32(startGrid.GridX), float32(startGrid.GridY), movePath.EndX, movePath.EndY, move.StepCount) 172 | default: 173 | nexGrid = true 174 | startGrid := movePath.GridPath[0] 175 | endGrid := movePath.GridPath[1] 176 | move.StepCount = newMoveDistance - move.StepTotal 177 | move.DestX = float32(endGrid.GridX) 178 | move.DestY = float32(endGrid.GridY) 179 | move.StepTotal = CalcTotalMove(float32(startGrid.GridX), float32(startGrid.GridY), move.DestX, move.DestY) 180 | x, y, dir = CalcNewXY(float32(startGrid.GridX), float32(startGrid.GridY), move.DestX, move.DestY, move.StepCount) 181 | } 182 | } else { 183 | move.StepCount = newMoveDistance 184 | x, y, dir = CalcNewXY(pos.X, pos.Y, movePath.EndX, movePath.EndY, moveDistance) 185 | } 186 | // 检查一下新的坐标点是否可走 187 | if !state.XYWalkAble(x, y) { 188 | // 客户端的路径可能会偶尔走过不可走的点,这里做一下容错 189 | if actor.ActorType == global.ACTOR_ROLE && actor.ActorID > 0 && moveDistance >= 300 { 190 | kernel.ErrorLog("un walkable:%d,%f,%f", state.Config.MapID, x, y) 191 | if syncStop { 192 | SyncStop(state, actor, false) 193 | } 194 | return false 195 | } 196 | return true 197 | } 198 | oldX := pos.X 199 | oldY := pos.Y 200 | pos.X = x 201 | pos.Y = y 202 | pos.Dir = dir 203 | if move.LastUpdateTime == 0 { 204 | mapInfo.SetMovePath(nil) 205 | actor.IsMove = false 206 | AoiUpdatePos(state, oldX, oldY, pos, mapInfo) 207 | return false 208 | } 209 | move.LastUpdateTime = now2 210 | if nexGrid { 211 | movePath.GridPath = movePath.GridPath[1:] 212 | } 213 | AoiUpdatePos(state, oldX, oldY, pos, mapInfo) 214 | return true 215 | } 216 | 217 | func CalcTotalMove(x, y, x2, y2 float32) float64 { 218 | x = x2 - x 219 | y = y2 - y 220 | return math.Sqrt(float64(x*x + y*y)) 221 | } 222 | 223 | func CalcNewXY(x, y, x2, y2 float32, dist float64) (float32, float32, int16) { 224 | r := math.Atan2(float64(y2-y), float64(x2-x)) 225 | x2 = float32(math.Cos(r)*dist) + x 226 | y2 = float32(math.Sin(r)*dist) + y 227 | dir := int16(math.Round(r * 180 / math.Pi)) 228 | if dir < 0 { 229 | dir += 360 230 | } 231 | return x2, y2, dir 232 | } 233 | 234 | func SyncStop(state *MapState, actor *MapActor, del bool) { 235 | mapInfo := state.GetMapInfo(actor.ActorType, actor.ActorID) 236 | actor.IsMove = false 237 | if del { 238 | DelMove(state.MoveActor, AKey{ActorID: actor.ActorID, ActorType: actor.ActorType}) 239 | } 240 | mapInfo.SetMovePath(nil) 241 | pos := mapInfo.GetPos() 242 | proto := &global.MapTocStop{ActorType: actor.ActorType, ActorID: actor.ActorID, X: pos.X, Y: pos.Y, Dir: pos.Dir} 243 | state.BroadCastPos(pos, proto) 244 | } 245 | 246 | func MoveCheckDist(state *MapState, actorType int8, actorID int64, pos *global.PPos, startX, startY float32) bool { 247 | startX = startX - pos.X 248 | startY = startY - pos.Y 249 | dist := startX*startX + startY*startY 250 | if dist <= DELTA_ERROR { 251 | if dist > DIST_FAIL && actorType == global.ACTOR_ROLE { 252 | state.Roles[actorID].MoveFail++ 253 | } 254 | return true 255 | } 256 | return false 257 | } 258 | 259 | func DelMove(moveMap map[AKey]*Move, aKey AKey) { 260 | if move, ok := moveMap[aKey]; ok { 261 | _movePool.Put(move) 262 | delete(moveMap, aKey) 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /maps/maps_node_proto.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | func NodeProto() []interface{} { 4 | return []interface{}{ 5 | &AgentChangeMap{}, 6 | &MapChangeData{}, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /maps/maps_prop.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import "game/lib" 4 | 5 | func PropRMBuffProp(state *MapState, actor *MapActor, buffID int16) { 6 | pKey := lib.PropKey{ID: lib.PROP_KEY_BUFF, SubID: int32(buffID)} 7 | PropRmProp(state, actor, pKey) 8 | } 9 | 10 | func PropRmProp(state *MapState, actor *MapActor, pkey lib.PropKey) { 11 | keys := actor.PropData.RmPropKvs(pkey) 12 | PropCalcProp(state, actor, keys) 13 | } 14 | 15 | var PropCalcProp = func(state *MapState, actor *MapActor, keys []int32) { 16 | upList := actor.PropData.CalcMapProps(keys, actor.BaseProp) 17 | totalProp := actor.TotalProp 18 | var idx int 19 | for _,v := range upList{ 20 | if v.Key == lib.PROP_MaxHP || v.Key == lib.PROP_MoveSpeed{ 21 | upList[idx] = v 22 | idx++ 23 | } 24 | lib.SetPropValue(totalProp,v.Key,v.Value) 25 | } 26 | if idx > 0 { 27 | UpdateHPMoveSpeed(state,actor,upList[0:idx]) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /maps/maps_role.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | "github.com/liangmanlin/gootp/gutil" 7 | "github.com/liangmanlin/gootp/kernel" 8 | ) 9 | 10 | const ( 11 | P_MAP_ROLE_HP int32 = 11 12 | P_MAP_ROLE_MAXHP int32 = 12 13 | P_MAP_ROLE_MOVESPEED int32 = 13 14 | ) 15 | 16 | var RoleSecondLoop = func(state *MapState, now2 int64) { 17 | 18 | } 19 | 20 | var RoleReduceHP = func(state *MapState, srcActor, destActor *MapActor, destMI *global.PMapRole, damage int32) { 21 | if destMI.HP > damage { 22 | destMI.HP -= damage 23 | } else { 24 | RoleDead(state, srcActor, destActor, destMI) 25 | destMI.HP = 0 26 | } 27 | } 28 | 29 | var RoleDead = func(state *MapState, srcActor, destActor *MapActor, destMI *global.PMapRole) { 30 | roleID := destMI.RoleID 31 | destActor.State = ACTOR_STATE_DEAD 32 | destMI.State = ACTOR_STATE_DEAD 33 | RoleStopMove(state, roleID,true) 34 | pos := destMI.Pos 35 | // 广播通知死亡 36 | proto := &global.MapTocActorDead{ActorID: roleID, ActorType: global.ACTOR_ROLE} 37 | state.BroadCastPos(pos, proto) 38 | // 通知player 39 | arg := &global.RoleDeadArg{SrcID: srcActor.ActorID, SrcType: srcActor.ActorType} 40 | state.CastPlayer(roleID, global.PLAYER_MOD_ROLE,arg) 41 | } 42 | 43 | func RoleStopMove(state *MapState, roleID int64,broadCast bool) { 44 | aKey := AKey{ActorType: global.ACTOR_ROLE, ActorID: roleID} 45 | actor := state.GetActor(aKey) 46 | if actor.IsMove { 47 | DelMove(state.MoveActor, aKey) 48 | actor.IsMove = false 49 | mapInfo := state.MapRoles[roleID] 50 | mapInfo.MovePath = nil 51 | if broadCast { 52 | pos := mapInfo.Pos 53 | proto := &global.MapTocMoveStop{ActorType: global.ACTOR_ROLE, ActorID: roleID, X: pos.X, Y: pos.Y, Dir: pos.Dir} 54 | state.BroadCastPosExclude(mapInfo.Pos, roleID, proto) 55 | } 56 | } 57 | } 58 | 59 | func RoleUpdateHPMoveSpeed(state *MapState, actor *MapActor, upList []global.PKV) { 60 | MI := state.MapRoles[actor.ActorID] 61 | ul := make([]*global.PKV,0,len(upList)) 62 | for i := range upList { 63 | v := &upList[i] 64 | switch v.Key { 65 | case lib.PROP_MaxHP: 66 | if MI.HP >= v.Value { 67 | MI.HP = v.Value 68 | }else{ 69 | MI.HP = gutil.Trunc(float32(MI.HP)/float32(MI.MaxHP)*float32(v.Value)) 70 | } 71 | MI.MaxHP = v.Value 72 | v.Key = P_MAP_ROLE_MAXHP 73 | ul = append(ul,v,&global.PKV{Key: P_MAP_ROLE_HP,Value: MI.HP}) 74 | case lib.PROP_MoveSpeed: 75 | if v.Value != MI.MoveSpeed { 76 | MI.MoveSpeed = v.Value 77 | v.Key = P_MAP_ROLE_MOVESPEED 78 | ul = append(ul,v) 79 | } 80 | } 81 | } 82 | state.UpdateRoleInfo(MI.RoleID,MI.Pos,ul) 83 | } 84 | 85 | var MapRoleHandle MsgHandler = func(ctx *kernel.Context, state *MapState, msg interface{}) { 86 | switch m := msg.(type) { 87 | case *global.RoleUpdateProps: 88 | MapRoleUpdateProps(state,m) 89 | case *global.RoleExitMap: 90 | // 包装成结构,方便扩展 91 | MapRoleChangeMap(ctx,state,ChangeMapArg{ 92 | RoleID: m.RoleID, 93 | DestMapID: state.Config.MapID, 94 | DestMapName: state.Name, 95 | DestNode: kernel.SelfNode(), 96 | IsExit: true, 97 | }) 98 | default: 99 | kernel.UnHandleMsg(msg) 100 | } 101 | } 102 | 103 | var MapRoleChangeMap = func(ctx *kernel.Context, state *MapState,arg ChangeMapArg) { 104 | roleID := arg.RoleID 105 | // 在真正退出之前执行回调 106 | state.Mod.RoleLeave(state,ctx,roleID,arg.IsExit) 107 | // 退出aoi 108 | mi := state.MapRoles[roleID] 109 | area := GetAreaByPos(mi.Pos) 110 | state.LeaveActor(area,global.ACTOR_ROLE,roleID) 111 | RoleStopMove(state,roleID,false) 112 | proto := &global.MapTocActorLeaveArea{ActorType: global.ACTOR_ROLE,ActorID: roleID} 113 | state.BroadCastAreas(Get9AreaByArea(area),proto) 114 | if arg.IsExit { 115 | // 退出地图,通知player下线 116 | MapRoleExitMap(state,roleID,mi) 117 | // 清理数据 118 | MapRoleCleanData(state,roleID,false) 119 | }else{ 120 | // 构造迁移数据 121 | changeData := MapRoleCleanData(state,roleID,true) 122 | ChangeMap(state,arg.DestNode,arg.DestMapName,changeData) 123 | } 124 | } 125 | 126 | var ChangeMap = func(state *MapState,destNode *kernel.Node,mapName string,changeData *MapChangeData) { 127 | if destNode.Equal(kernel.SelfNode()){ 128 | // 相同节点 129 | if pid := lib.GetMapPid(mapName);pid != nil{ 130 | lib.CastMap(pid,global.MAP_MOD_ROLE,changeData) 131 | }else{ 132 | // 回出生点 133 | lib.CastPlayer(changeData.RoleData.Player,global.PLAYER_MOD_MAP,changeData) 134 | } 135 | }else{ 136 | // 跨节点,目前只支持集群模式 137 | if kernel.IsNodeConnect(destNode.Name()) { 138 | kernel.CastNameNode("map_agent",destNode,&AgentChangeMap{MapName: mapName,Change: changeData}) 139 | }else{ 140 | // 回出生点 141 | lib.CastPlayer(changeData.RoleData.Player,global.PLAYER_MOD_MAP,changeData) 142 | } 143 | } 144 | } 145 | 146 | var MapRoleExitMap = func(state *MapState,roleID int64,mi *global.PMapRole) { 147 | pos := mi.Pos 148 | data := &global.MapRoleExit{X: pos.X,Y:pos.Y} 149 | state.CastPlayer(roleID,global.PLAYER_MOD_MAP,data) 150 | } 151 | 152 | var MapRoleCleanData = func(state *MapState,roleID int64,rt bool)(rs *MapChangeData) { 153 | if rt { 154 | rs = &MapChangeData{ 155 | RoleID: roleID, 156 | MapInfo: state.MapRoles[roleID], 157 | Actor: state.GetActorByID(global.ACTOR_ROLE,roleID), 158 | RoleData: state.Roles[roleID], 159 | } 160 | } 161 | delete(state.MapRoles,roleID) 162 | delete(state.Actors,AKey{roleID,global.ACTOR_ROLE}) 163 | delete(state.Roles,roleID) 164 | return 165 | } 166 | 167 | var MapRoleUpdateProps = func(state *MapState,msg *global.RoleUpdateProps) { 168 | actor := state.GetActorByID(global.ACTOR_ROLE,msg.RoleID) 169 | baseProp := actor.BaseProp 170 | keys := make([]int32,0,len(msg.UP)) 171 | for _,v :=range msg.UP{ 172 | lib.SetPropValue(baseProp,v.Key,v.Value) 173 | keys = append(keys,v.Key) 174 | } 175 | PropCalcProp(state,actor,keys) 176 | } 177 | 178 | func init() { 179 | modRouter[global.MAP_MOD_ROLE] = &MapRoleHandle 180 | } -------------------------------------------------------------------------------- /maps/maps_skill_callback.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | type skillCallBackFunc = func(state *MapState,entity *SkillEntity,targets []MapInfo,args []int32) 4 | 5 | var SkillCallBackMap = make(map[string]*skillCallBackFunc,100) 6 | 7 | var AddSelfBuff skillCallBackFunc = func(state *MapState, entity *SkillEntity, targets []MapInfo,args []int32) { 8 | 9 | } 10 | 11 | func init() { 12 | SkillCallBackMap[`add_self_buff`] = &AddSelfBuff 13 | } -------------------------------------------------------------------------------- /maps/maps_skill_effect.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/gutil" 6 | ) 7 | 8 | const ( 9 | EFFECT_TYPE_DAMAGE int8 = iota + 1 10 | ) 11 | 12 | type skillEffectFunc = func(state *MapState, entity *SkillEntity, srcActor *MapActor, target MapInfo, allTargets []MapInfo, 13 | effectList []*global.PSkillEffect, args []int32) []*global.PSkillEffect 14 | 15 | var SkillEffectMap = make(map[string]*skillEffectFunc, 100) 16 | 17 | var CommonAttack skillEffectFunc = func(state *MapState, entity *SkillEntity, srcActor *MapActor, target MapInfo, allTargets []MapInfo, 18 | effectList []*global.PSkillEffect, args []int32) []*global.PSkillEffect { 19 | destType := target.Type() 20 | destID := target.ID() 21 | destActor := state.GetActorByID(destType, destID) 22 | // 先简单计算,理论上应该计算暴击,闪避等 23 | srcProp := srcActor.TotalProp 24 | destProp := destActor.TotalProp 25 | damage := gutil.MaxInt32(srcProp.PhyAttack-destProp.PhyDefence, 1) 26 | // 扣血 27 | ActorReduceHP(state,srcActor,destActor,target,damage) 28 | effect := NewEffect(destType, destID, EFFECT_TYPE_DAMAGE, damage) 29 | return append(effectList,effect) 30 | } 31 | 32 | func init() { 33 | SkillEffectMap["common_attack"] = &CommonAttack 34 | } 35 | -------------------------------------------------------------------------------- /maps/maps_svr.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/lib" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | func StartMap(mapID int32, mapName string, mod *MapMod,args...interface{}) { 9 | kernel.SupStartChild("map_sup", &kernel.SupChild{ 10 | ChildType: kernel.SupChildTypeWorker, 11 | ReStart: true, 12 | Svr: SvrActor, 13 | InitArgs: append(kernel.MakeArgs(mapID, mapName, mod),args...), 14 | }) 15 | } 16 | 17 | func StartCopy(mapID int32, mapName string, mod *MapMod,args...interface{}) { 18 | kernel.Start(SvrActor,append(kernel.MakeArgs(mapID, mapName, mod),args...)...) 19 | } 20 | 21 | var SvrActor = &kernel.Actor{ 22 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 23 | mapID := args[0].(int32) 24 | mapName := args[1].(string) 25 | mod := args[2].(*MapMod) 26 | state := NewMapState(mapID,mapName,mod,ctx) 27 | if len(args) > 3 { 28 | mod.Init(state,ctx, mapID, args[3:]...) 29 | }else{ 30 | mod.Init(state,ctx, mapID) 31 | } 32 | lib.RegisterMap(mapName,pid) 33 | // 启动定轮训 34 | kernel.SendAfterForever(pid,100,kernel.Loop{}) 35 | return state 36 | }, 37 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 38 | state := State(ctx) 39 | switch msg.(type) { 40 | case kernel.Loop: 41 | mapLoop(state,ctx) 42 | } 43 | }, 44 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 45 | return nil 46 | }, 47 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 48 | lib.UnRegisterMap(State(ctx).Name) 49 | }, 50 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 51 | return true 52 | }, 53 | } 54 | 55 | func mapLoop(state *MapState,ctx *kernel.Context) { 56 | now2 := kernel.Now2() 57 | MoveUpdate(state,now2) 58 | SkillUpdate(state,now2) 59 | if (state.LC & 1) == 0{ // 200ms 一次 60 | BuffLoop(state,now2) 61 | MonsterLoop(state,now2) 62 | state.Timer.Loop(state,now2) // 驱动计时器 63 | }else { 64 | if state.LC == 9 { 65 | // 秒级轮询 66 | state.LC = -1 67 | RoleSecondLoop(state, now2) 68 | MonsterUpdateSecond(state,now2) 69 | } 70 | } 71 | state.LC++ 72 | } -------------------------------------------------------------------------------- /maps/mod/mod_common.go: -------------------------------------------------------------------------------- 1 | package mod 2 | 3 | import ( 4 | "game/maps" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | // 解决循环依赖 9 | func init() { 10 | maps.ModMap["mod_common"] = Common 11 | } 12 | 13 | var Common = &maps.MapMod{ 14 | Init: func(state *maps.MapState, ctx *kernel.Context, mapID int32, args ...interface{}) { 15 | maps.CreateMapAllMonster(state) 16 | }, 17 | RoleEnter: func(state *maps.MapState, ctx *kernel.Context, roleID int64) { 18 | 19 | }, 20 | RoleReconnect: func(state *maps.MapState, ctx *kernel.Context, roleID int64) { 21 | 22 | }, 23 | RoleCanEnter: func(state *maps.MapState, ctx *kernel.Context, roleID int64) bool { 24 | return true 25 | }, 26 | RoleLeave: func(state *maps.MapState, ctx *kernel.Context, roleID int64,isExit bool) { 27 | 28 | }, 29 | RoleDead: func(state *maps.MapState, ctx *kernel.Context, roleID int64) { 30 | 31 | }, 32 | RoleRelive: func(state *maps.MapState, ctx *kernel.Context, roleID int64) { 33 | 34 | }, 35 | Handle: func(state *maps.MapState, ctx *kernel.Context, msg interface{}) { 36 | 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /maps/type.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "game/lib" 7 | "github.com/liangmanlin/gootp/astar" 8 | "github.com/liangmanlin/gootp/kernel" 9 | "github.com/liangmanlin/gootp/rand" 10 | "github.com/liangmanlin/gootp/timer" 11 | "unsafe" 12 | ) 13 | 14 | const ( 15 | AREA_WIDTH int32 = 15 16 | AREA_HEIGHT int32 = 15 17 | ) 18 | 19 | const ( 20 | ACTOR_STATE_NORMAL int8 = iota + 1 21 | ACTOR_STATE_DEAD 22 | ) 23 | 24 | const ( 25 | MAP_MONSTER_HP int32 = iota + 6 26 | MAP_MONSTER_MAX_HP 27 | MAP_MONSTER_MOVE_SPEED 28 | ) 29 | 30 | const ( 31 | RETURN_TYPE_TRUE ReturnType = iota + 1 32 | RETURN_TYPE_FALSE 33 | RETURN_TYPE_TIME 34 | ) 35 | 36 | type app struct { 37 | mapConfigPath string 38 | } 39 | 40 | type MapConfig struct { 41 | MapID int32 42 | Width int32 43 | Height int32 44 | BornPos Area 45 | PosList []MapPos 46 | PosCount int32 47 | Monsters []MapConfigMonster 48 | } 49 | 50 | // 强制内存对齐 51 | type MapPos struct { 52 | X int16 53 | Y int16 54 | Type int16 55 | G int16 56 | } 57 | 58 | type MapState struct { 59 | *kernel.Context 60 | Config *MapConfig 61 | Mod *MapMod 62 | Name string 63 | AreaMap [global.C_ACTOR_SIZE]AreaMap 64 | MapRoles map[int64]*global.PMapRole 65 | MapMonsters map[int64]*global.PMapMonster 66 | Roles map[int64]*MapRoleData 67 | Monsters map[int64]*MState 68 | Rand rand.Rand 69 | Timer *timer.Timer 70 | LC int 71 | Actors map[AKey]*MapActor 72 | MoveActor map[AKey]*Move 73 | AStar *astar.AStar 74 | DataMonster *DataMonster 75 | DataSkill *DataSkill 76 | } 77 | 78 | type MapInfo interface { 79 | ID() int64 80 | Type() int8 81 | GetMove() (int32, *global.PMovePath, *global.PPos) 82 | SetMovePath(path *global.PMovePath) 83 | GetPos() *global.PPos 84 | IsAlive() bool 85 | GetRadius() float64 86 | GetCamp() int8 87 | GetBuffs() []*global.PBuff 88 | SetBuffs([]*global.PBuff) 89 | } 90 | 91 | type DataMonster struct { 92 | MonsterID int64 93 | StopMonster int64 94 | DelMonster []int64 95 | PosMonsterNum map[Area]int32 96 | AroundDest map[AKey][]int64 97 | IsMonsterLoop bool 98 | } 99 | 100 | type DataSkill struct { 101 | Index int32 102 | EntityList map[int32]*SkillEntity 103 | Del []int32 104 | Add map[int32]*SkillEntity 105 | IsLoop bool 106 | } 107 | 108 | type Area struct { 109 | X, Y int16 110 | } 111 | 112 | type Grid struct { 113 | X, Y int32 114 | } 115 | 116 | type MapRoleData struct { 117 | TcpPid *kernel.Pid 118 | Player *kernel.Pid 119 | MoveFail int32 120 | Skills map[int32]*FightSkill 121 | } 122 | 123 | type FightSkill struct { 124 | CoolTime int64 125 | } 126 | 127 | type MState struct { 128 | ID int64 129 | TypeID int32 130 | State int8 131 | NextTime int64 132 | LastAttackTime int64 133 | BornPos *global.PPos 134 | DeadCB *func(state *MapState, monsterID int64, srcActor *MapActor, args interface{}) 135 | DeadArgs interface{} 136 | Doings []*Doing 137 | Enemies map[AKey]*MonsterEnemy 138 | Return *MReturn 139 | Config *config.DefMonster // 这里持有了一个配置,这样做对gc不友好,因为假如热更了配置,这里要等怪物死亡才会更新 140 | Skills []MonsterSkill 141 | HPChange *func(state *MapState, mState *MState, srcActor *MapActor,reduce int32) 142 | } 143 | 144 | type MonsterSkill struct { 145 | SkillID int32 146 | CD int32 147 | UseTime int64 148 | } 149 | 150 | type MReturn struct { 151 | Type ReturnType 152 | Time int64 153 | } 154 | 155 | type Doing struct { 156 | Fun *behaviorFunc 157 | Args interface{} 158 | Type int32 159 | } 160 | 161 | type MonsterEnemy struct { 162 | ActorID int64 163 | ActorType int8 164 | TotalHurt int64 165 | LastAttackTime int64 166 | } 167 | 168 | type MapMod struct { 169 | // 初始化场景回调 170 | Init func(state *MapState, ctx *kernel.Context, mapID int32, args ...interface{}) 171 | // 角色进入地图时回调 172 | RoleEnter func(state *MapState, ctx *kernel.Context, roleID int64) 173 | // 角色重连回调 174 | RoleReconnect func(state *MapState, ctx *kernel.Context, roleID int64) 175 | // 角色离开地图前回调 176 | RoleLeave func(state *MapState, ctx *kernel.Context, roleID int64, isExit bool) 177 | // 判断角色是否可以进入该场景,可以为空 178 | RoleCanEnter func(state *MapState, ctx *kernel.Context, roleID int64) bool 179 | // 角色死亡回调 180 | RoleDead func(state *MapState, ctx *kernel.Context, roleID int64) 181 | // 角色复活回调 182 | RoleRelive func(state *MapState, ctx *kernel.Context, roleID int64) 183 | // 场景消息 handler 184 | Handle func(state *MapState, ctx *kernel.Context, msg interface{}) 185 | } 186 | 187 | // actorKey 188 | type AKey struct { 189 | ActorID int64 190 | ActorType int8 191 | } 192 | 193 | type Move struct { 194 | LastUpdateTime int64 // 毫秒 195 | StepCount float64 196 | StepTotal float64 197 | DestX float32 198 | DestY float32 199 | } 200 | 201 | type MapActor struct { 202 | ActorID int64 203 | ActorType int8 204 | State int8 205 | IsMove bool 206 | BaseProp *global.PProp 207 | TotalProp *global.PProp 208 | Skills []int32 // 考虑到同时释放的技能不会很多,这里使用slice保存 209 | BuffData *BuffData 210 | PropData *lib.PropData 211 | } 212 | 213 | type BuffData struct { 214 | Type2ID map[int16]int16 215 | EffectCount []int16 216 | Data map[int16]*BuffInfo 217 | } 218 | 219 | type BuffInfo struct { 220 | AddTime int64 221 | TickTime int64 222 | } 223 | 224 | type MonsterCreateArg struct { 225 | TypeID int32 226 | X, Y float32 227 | Dir int16 228 | Level int16 229 | DeadCB *func(state *MapState, monsterID int64, srcActor *MapActor, args interface{}) 230 | DeadArgs interface{} 231 | Prop *global.PProp 232 | } 233 | 234 | type MoveArg struct { 235 | DX, DY float32 236 | IdleTime int32 237 | } 238 | 239 | type MoveDestArg struct { 240 | ActorID int64 241 | ActorType int8 242 | Dist int32 243 | DX, DY float32 244 | } 245 | 246 | type MapConfigMonster struct { 247 | X, Y int16 248 | TypeID int32 249 | } 250 | 251 | type ReturnType int8 252 | 253 | type AreaFoldFunc = func(state *MapState, srcInfo, destInfo MapInfo, args ...unsafe.Pointer) bool 254 | 255 | type SkillEntity struct { 256 | ID int32 // 肯定不可能有这么多,本来是一个循环,所以溢出了也没问题 257 | MovePhase int32 258 | EffectPhase int32 259 | MoveTime int64 260 | EffectTime int64 261 | Cfg *config.DefSkill // 暂时先缓存配置 262 | MapInfo MapInfo 263 | SkillPos *global.PPos 264 | FlyInfo *SkillFlyInfo // 飞行子弹特有 265 | InitPos *global.PPos // 技能起始位置 266 | Target *global.PActor // 锁定目标技能特有 267 | } 268 | 269 | type SkillFlyInfo struct { 270 | EffectActors map[AKey]bool // 保存已经造成伤害的目标,规避重复 271 | Frames int32 // 记录已经飞行了多少针 272 | RoleCount int32 273 | MonsterCount int32 274 | } 275 | 276 | type ChangeMapArg struct { 277 | RoleID int64 278 | DestMapID int32 279 | DestMapName string 280 | DestNode *kernel.Node 281 | IsExit bool 282 | } 283 | 284 | type MapChangeData struct { 285 | RoleID int64 286 | MapInfo *global.PMapRole 287 | Actor *MapActor 288 | RoleData *MapRoleData 289 | IsFirstEnter bool 290 | } 291 | 292 | type AgentChangeMap struct { 293 | MapName string 294 | Change *MapChangeData 295 | } 296 | -------------------------------------------------------------------------------- /player/attr.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | ) 7 | 8 | const attrName = "attr" 9 | 10 | const ( 11 | backup_diamond int32 = iota 12 | backup_gold 13 | ) 14 | 15 | var AttrLoad = func(player *global.Player) { 16 | attr := lib.GameDB.SyncSelectRow(player.Context.Call, global.TABLE_ROLE_ATTR, player.RoleID, player.RoleID) 17 | player.Attr = attr.(*global.PRoleAttr) 18 | } 19 | 20 | var AttrPersistent = func(player *global.Player) { 21 | a := *player.Attr // 没有二层数据,可以直接拷贝,比起使用反射,效率极高 22 | lib.GameDB.SyncUpdate(global.TABLE_ROLE_ATTR, player.RoleID, &a) 23 | } 24 | 25 | var AttrReduceDiamond = func(player *global.Player, reduce int32) { 26 | checkTransaction(player) 27 | rd := int64(reduce) 28 | if player.Attr.Diamond < rd { 29 | Abort(&global.PMsg{}) 30 | } 31 | Backup(player, global.BackupKey{BackupID: global.BACKUP_ATTR, ID: backup_diamond}, "Attr.Diamond", player.Attr.Diamond) 32 | player.Attr.Diamond -= rd 33 | AddDBQueue(player,AttrDBOP,global.DB_OP_UPDATE,nil) 34 | } 35 | 36 | var AttrAddDiamond = func(player *global.Player, add int32) { 37 | checkTransaction(player) 38 | Backup(player, global.BackupKey{BackupID: global.BACKUP_ATTR, ID: backup_diamond}, "Attr.Diamond", player.Attr.Diamond) 39 | player.Attr.Diamond += int64(add) 40 | AddDBQueue(player,AttrDBOP,global.DB_OP_UPDATE,nil) 41 | } 42 | 43 | var AttrReduceGold = func(player *global.Player, reduce int32) { 44 | checkTransaction(player) 45 | rd := int64(reduce) 46 | if player.Attr.Gold < rd { 47 | Abort(&global.PMsg{}) 48 | } 49 | Backup(player, global.BackupKey{BackupID: global.BACKUP_ATTR, ID: backup_gold}, "Attr.Gold", player.Attr.Gold) 50 | player.Attr.Gold -= rd 51 | AddDBQueue(player,AttrDBOP,global.DB_OP_UPDATE,nil) 52 | } 53 | 54 | var AttrAddGold = func(player *global.Player, add int32) { 55 | checkTransaction(player) 56 | Backup(player, global.BackupKey{BackupID: global.BACKUP_ATTR, ID: backup_gold}, "Attr.Gold", player.Attr.Gold) 57 | player.Attr.Gold += int64(add) 58 | AddDBQueue(player,AttrDBOP,global.DB_OP_UPDATE,nil) 59 | } 60 | 61 | var AttrDBOP = func(player *global.Player, _ int32, _ interface{}){ 62 | player.DirtyMod[attrName] = true 63 | } -------------------------------------------------------------------------------- /player/bag.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | "github.com/liangmanlin/gootp/gutil" 7 | "github.com/liangmanlin/gootp/kernel" 8 | ) 9 | 10 | const bagName = "bag" 11 | 12 | var BagLoad = func(player *global.Player) { 13 | rl := lib.GameDB.SyncSelect(player.Context.Call, global.TABLE_BAG, player.RoleID, player.RoleID) 14 | m := make(map[int32]*global.PGoods) 15 | var maxID int32 16 | tm := make(map[int32][]int32) 17 | for _, v := range rl { 18 | goods := toPGoods(v.(*global.Bag)) 19 | m[goods.ID] = goods 20 | maxID = gutil.MaxInt32(maxID, goods.ID) 21 | tmp := tm[goods.TypeID] 22 | tm[goods.TypeID] = append(tmp, goods.ID) 23 | } 24 | player.BagMaxID = maxID // todo 这个值可以考虑持久化 25 | player.Bag = &global.BagData{MaxSize: 100, Goods: m, TypeIDMap: tm} 26 | } 27 | 28 | var BagPersistent = func(player *global.Player) { 29 | if len(player.Bag.Dirty) > 0 { 30 | for id, v := range player.Bag.Dirty { 31 | switch v.Type { 32 | case global.DB_OP_ADD: 33 | lib.GameDB.SyncInsert(global.TABLE_BAG, player.RoleID, toBag(player.Bag.Goods[id])) 34 | case global.DB_OP_UPDATE: 35 | lib.GameDB.SyncUpdate(global.TABLE_BAG, player.RoleID, toBag(player.Bag.Goods[id])) 36 | case global.DB_OP_DELETE: 37 | lib.GameDB.SyncDelete(global.TABLE_BAG, player.RoleID, toBag(v.Goods)) 38 | } 39 | } 40 | player.Bag.Dirty = make(map[int32]global.GoodsDirty) 41 | } 42 | } 43 | 44 | var BagGetGoodsByID = func(player *global.Player, id int32) *global.PGoods { 45 | if g, ok := player.Bag.Goods[id]; ok { 46 | return g 47 | } 48 | return nil 49 | } 50 | 51 | var BagGetGoodsByTypeID = func(player *global.Player, typeID int32) (goodsList []*global.PGoods) { 52 | tmp := player.Bag.TypeIDMap[typeID] 53 | for _, id := range tmp { 54 | goodsList = append(goodsList, player.Bag.Goods[id]) 55 | } 56 | return 57 | } 58 | 59 | // 建议使用该函数获取道具数量判断 60 | var BagGetGoodsNum = func(player *global.Player, typeID int32) (notBindNum, bindNum int32) { 61 | tmp := player.Bag.TypeIDMap[typeID] 62 | for _, id := range tmp { 63 | g := player.Bag.Goods[id] 64 | if g.Bind { 65 | bindNum += g.Num 66 | } else { 67 | notBindNum += g.Num 68 | } 69 | } 70 | return 71 | } 72 | 73 | var BagCreateGoods = func(player *global.Player, typeID, num int32, bind bool) { 74 | pileNum := lib.GetPileNum(typeID) 75 | // 先判断是否可以堆叠 76 | if pileNum > 1 { 77 | BagInsertGoodsTypeID(player, typeID, num, bind, pileNum) 78 | } else { 79 | // 不可堆叠,直接创建插入 80 | goodsList := lib.CreateGoods(typeID, num, bind) 81 | BagInsertGoodsNotPile(player, goodsList) 82 | } 83 | } 84 | 85 | var BagInsertGoodsNotPile = func(player *global.Player, goodsList []*global.PGoods) { 86 | checkTransaction(player) 87 | // 预先判断减少回滚消耗 88 | if int(player.Bag.MaxSize)-len(player.Bag.Goods) < len(goodsList) { 89 | Abort(&global.PMsg{}) 90 | } 91 | for _, goods := range goodsList { 92 | goods.RoleID = player.RoleID 93 | goods.ID = bagMakeID(player) 94 | bagAddGoods(player, goods) 95 | } 96 | } 97 | 98 | var BagInsertGoodsTypeID = func(player *global.Player, typeID, num int32, bind bool, pileNum int32) { 99 | checkTransaction(player) 100 | bag := player.Bag 101 | tm := bag.TypeIDMap[typeID] 102 | for i := 0; i < len(tm) && num > 0; i++ { 103 | goods := bag.Goods[tm[i]] 104 | if bind == goods.Bind && goods.Num < pileNum { 105 | add := gutil.MinInt32(pileNum-goods.Num, num) 106 | num -= add 107 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_UPDATE_NUM, ID: goods.ID}, rollbackBagGoodsNum, []int32{goods.ID, goods.Num}) 108 | goods.Num += add 109 | AddDBQueue(player, BagDBOP, global.DB_OP_UPDATE, goods.ID) 110 | } 111 | } 112 | if num > 0 { 113 | goodsList := lib.CreateGoods(typeID, num, bind) 114 | BagInsertGoodsNotPile(player, goodsList) 115 | } 116 | } 117 | 118 | // 上层逻辑不建议直接修改数据,包括数量,除非你清楚自己在做什么 119 | // 上层逻辑如果自己修改了PGoods的数据,需要自行调用,否则不会更新到数据库 120 | // 原则不提倡自行修改,因为事务也会失效,所以只能在事务不会回滚的地方使用 121 | var BagUpdateGoods = func(player *global.Player, id int32) { 122 | AddDBQueue(player, BagDBOP, global.DB_OP_UPDATE, id) 123 | } 124 | 125 | // 优先扣除绑定的道具 126 | var BagDeleteByTypeID = func(player *global.Player, typeID, num int32) { 127 | checkTransaction(player) 128 | bag := player.Bag 129 | tm := bag.TypeIDMap[typeID] 130 | var bind, notBind []*global.PGoods 131 | var total int32 132 | for _, id := range tm { 133 | g := player.Bag.Goods[id] 134 | total += g.Num 135 | if g.Bind { 136 | bind = append(bind, g) 137 | } else { 138 | notBind = append(notBind, g) 139 | } 140 | } 141 | if total < num { 142 | Abort(&global.PMsg{}) 143 | } 144 | // 走到这里肯定是足够的 145 | num = deleteList(player, bind, num) 146 | if num > 0 { 147 | deleteList(player, notBind, num) 148 | } 149 | } 150 | 151 | var BagDeleteByID = func(player *global.Player, id, num int32) { 152 | checkTransaction(player) 153 | bag := player.Bag 154 | goods := bag.Goods[id] 155 | if goods.Num < num { 156 | Abort(&global.PMsg{}) 157 | } 158 | if num == goods.Num { 159 | bagDeleteGoods(player,goods) 160 | }else{ 161 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_UPDATE_NUM, ID: goods.ID}, rollbackBagGoodsNum, []int32{goods.ID, goods.Num}) 162 | AddDBQueue(player, BagDBOP, global.DB_OP_UPDATE, goods.ID) 163 | goods.Num -= num 164 | } 165 | } 166 | 167 | var BagDBOP = func(player *global.Player, op int32, arg interface{}) { 168 | bag := player.Bag 169 | switch op { 170 | case global.DB_OP_ADD: 171 | id := arg.(int32) 172 | bag.Dirty[id] = global.GoodsDirty{Type: op} 173 | case global.DB_OP_UPDATE: 174 | id := arg.(int32) 175 | if _, ok := bag.Dirty[id]; !ok { 176 | bag.Dirty[id] = global.GoodsDirty{Type: op} 177 | } 178 | case global.DB_OP_DELETE: 179 | goods := arg.(*global.PGoods) 180 | if f, ok := bag.Dirty[goods.ID]; ok { 181 | if f.Type == global.DB_OP_ADD { 182 | delete(bag.Dirty, goods.ID) 183 | } else { 184 | bag.Dirty[goods.ID] = global.GoodsDirty{Type: op, Goods: goods} 185 | } 186 | } else { 187 | bag.Dirty[goods.ID] = global.GoodsDirty{Type: op, Goods: goods} 188 | } 189 | } 190 | player.DirtyMod[bagName] = true 191 | } 192 | 193 | func deleteList(player *global.Player, arr []*global.PGoods, num int32) int32 { 194 | for i := 0; i < len(arr) && num > 0; i++ { 195 | g := arr[i] 196 | if num >= g.Num { 197 | bagDeleteGoods(player,g) 198 | num -= g.Num 199 | }else { 200 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_UPDATE_NUM, ID: g.ID}, rollbackBagGoodsNum, []int32{g.ID, g.Num}) 201 | AddDBQueue(player, BagDBOP, global.DB_OP_UPDATE, g.ID) 202 | g.Num -= num 203 | num = 0 204 | } 205 | } 206 | return num 207 | } 208 | 209 | func bagDeleteGoods(player *global.Player,goods *global.PGoods) { 210 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_DELETE, ID: goods.ID}, rollbackBagDelete, goods) 211 | AddDBQueue(player, BagDBOP, global.DB_OP_DELETE, kernel.DeepCopy(goods)) 212 | bag := player.Bag 213 | delete(bag.Goods,goods.ID) 214 | if tl,ok := bag.TypeIDMap[goods.TypeID];ok{ 215 | bag.TypeIDMap[goods.TypeID] = gutil.SliceDelInt32(tl, goods.ID) 216 | } 217 | 218 | } 219 | 220 | // 直接插入 221 | func bagAddGoods(player *global.Player, goods *global.PGoods) { 222 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_INSERT, ID: goods.ID}, rollbackBagInsert, goods.ID) 223 | AddDBQueue(player, BagDBOP, global.DB_OP_ADD, goods.ID) 224 | bag := player.Bag 225 | bag.Goods[goods.ID] = goods 226 | arr := bag.TypeIDMap[goods.TypeID] 227 | bag.TypeIDMap[goods.TypeID] = append(arr, goods.ID) 228 | } 229 | 230 | func rollbackBagInsert(player *global.Player, id int32) { 231 | bag := player.Bag 232 | if goods, ok := bag.Goods[id]; ok { 233 | delete(bag.Goods, id) 234 | if arr, ok := bag.TypeIDMap[goods.TypeID]; ok { 235 | bag.TypeIDMap[goods.TypeID] = gutil.SliceDelInt32(arr, id) 236 | } 237 | } 238 | } 239 | 240 | func rollbackBagDelete(player *global.Player,goods *global.PGoods) { 241 | bag := player.Bag 242 | bag.Goods[goods.ID] = goods 243 | arr := bag.TypeIDMap[goods.TypeID] 244 | bag.TypeIDMap[goods.TypeID] = append(arr, goods.ID) 245 | } 246 | 247 | func rollbackBagGoodsNum(player *global.Player, arg []int32) { 248 | id, num := arg[0], arg[1] 249 | bag := player.Bag 250 | if goods, ok := bag.Goods[id]; ok { 251 | goods.Num = num 252 | } 253 | } 254 | 255 | func bagMakeID(player *global.Player) int32 { 256 | Backup(player, global.BackupKey{BackupID: global.BACKUP_BAG_ID}, "BagMaxID", player.BagMaxID) 257 | player.BagMaxID++ 258 | return player.BagMaxID 259 | } 260 | 261 | func toPGoods(bagGoods *global.Bag) *global.PGoods { 262 | goods := &global.PGoods{ 263 | RoleID: bagGoods.RoleID, 264 | ID: bagGoods.ID, 265 | Type: bagGoods.Type, 266 | TypeID: bagGoods.TypeID, 267 | Num: bagGoods.Num, 268 | Bind: bagGoods.Bind, 269 | StartTime: bagGoods.StartTime, 270 | EndTime: bagGoods.EndTime, 271 | CreateTime: bagGoods.CreateTime, 272 | } 273 | return goods 274 | } 275 | 276 | func toBag(bagGoods *global.PGoods) *global.Bag { 277 | goods := &global.Bag{ 278 | RoleID: bagGoods.RoleID, 279 | ID: bagGoods.ID, 280 | Type: bagGoods.Type, 281 | TypeID: bagGoods.TypeID, 282 | Num: bagGoods.Num, 283 | Bind: bagGoods.Bind, 284 | StartTime: bagGoods.StartTime, 285 | EndTime: bagGoods.EndTime, 286 | CreateTime: bagGoods.CreateTime, 287 | } 288 | return goods 289 | } 290 | -------------------------------------------------------------------------------- /player/const.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | const ( 4 | TIMER_EXIT int32 = iota + 1 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /player/hook.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | ) 6 | 7 | // 角色下线时回调 8 | var HookExit = func(player *global.Player) { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /player/login.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | var LoginLogin = func(player *global.Player, proto interface{}) { 9 | kernel.ErrorLog("%#v", proto) 10 | } 11 | -------------------------------------------------------------------------------- /player/map.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "game/lib" 7 | "game/maps" 8 | "github.com/liangmanlin/gootp/gutil" 9 | "github.com/liangmanlin/gootp/kernel" 10 | ) 11 | 12 | const mapModName = "map" 13 | 14 | func init() { 15 | modRouter[global.PLAYER_MOD_MAP] = &MapHandler 16 | } 17 | 18 | var MapLoad = func(player *global.Player) { 19 | rp := lib.GameDB.SyncSelectRow(player.Context.Call, global.TABLE_ROLE_MAP, player.RoleID, player.RoleID) 20 | player.Map = rp.(*global.RoleMap) 21 | } 22 | 23 | var MapPersistent = func(player *global.Player) { 24 | lib.GameDB.SyncUpdate(global.TABLE_ROLE_MAP, player.RoleID, kernel.DeepCopy(player.Map)) 25 | } 26 | 27 | var MapExit = func(player *global.Player) { 28 | // 无论如何都会在2s后退出 29 | StartTimer(player, TIMER_EXIT, 0, 2*1000, 1, MapExitTimeOut) 30 | MapCastMap(player, global.MAP_MOD_ROLE, &global.RoleExitMap{RoleID: player.RoleID}) 31 | } 32 | 33 | var MapExitTimeOut = func(player *global.Player) { 34 | MapExitFinal(player, 1, nil) 35 | } 36 | 37 | var MapExitFinal = func(player *global.Player, exitType int, exitData *global.MapRoleExit) { 38 | player.Exit(kernel.ExitReasonNormal) 39 | if exitType == 1 { 40 | // 在尝试一次退出场景 41 | MapCastMap(player, global.MAP_MOD_ROLE, &global.RoleExitMap{RoleID: player.RoleID}) 42 | }else{ 43 | player.Map.X = gutil.Round(exitData.X) 44 | player.Map.Y = gutil.Round(exitData.Y) 45 | player.DirtyMod[mapModName] = true 46 | } 47 | } 48 | 49 | // 登录后首次进入地图 50 | var MapFirstEnter = func(player *global.Player) { 51 | // 判断当前地图是否可以进入 52 | info := player.Map 53 | CheckCanEnter(info) 54 | base := player.Base 55 | prop := player.Prop 56 | // 构造PMapRole 57 | mapRole := &global.PMapRole{ 58 | RoleID: player.RoleID, 59 | Name: base.Name, 60 | HeroType: base.HeroType, 61 | ServerID: lib.GetServerID(), 62 | Level: int16(base.Level), 63 | State: maps.ACTOR_STATE_NORMAL, 64 | Camp: 0, 65 | Skin: base.Skin, 66 | Pos: &global.PPos{X: float32(info.X),Y: float32(info.Y),Dir: 270}, 67 | HP: prop.MaxHP, 68 | MaxHP: prop.MaxHP, 69 | MoveSpeed: prop.MoveSpeed, 70 | } 71 | actor := maps.NewActor(global.ACTOR_ROLE,player.RoleID) 72 | baseProp := *prop 73 | actor.BaseProp = &baseProp 74 | enter := &maps.MapChangeData{ 75 | RoleID: player.RoleID, 76 | MapInfo: mapRole, 77 | Actor: actor, 78 | RoleData: &maps.MapRoleData{ 79 | Player:player.Self(), 80 | TcpPid: player.GWPid, 81 | Skills: SkillGetFightSkill(player), 82 | }, 83 | IsFirstEnter: true, 84 | } 85 | mapPid := lib.GetMapPid(info.MapName) 86 | player.MapPid = mapPid 87 | lib.CastMap(mapPid, global.MAP_MOD_ROLE, enter) 88 | } 89 | 90 | func MapCastMap(player *global.Player, mod int32, msg interface{}) { 91 | lib.CastMap(player.MapPid, mod, msg) 92 | } 93 | 94 | func CheckCanEnter(info *global.RoleMap) { 95 | if lib.GetMapPid(info.MapName)== nil { 96 | // 暂时先简单处理回主城 97 | info.MapID = int32(101) 98 | info.MapName = lib.NormalMapName(info.MapID) 99 | cfg := config.Maps.Get(info.MapID) 100 | info.X = cfg.BornX 101 | info.Y = cfg.BornY 102 | info.Node = kernel.SelfNode().Name() 103 | } 104 | } 105 | 106 | var MapHandler MsgHandler = func(player *global.Player, msg interface{}) { 107 | switch m := msg.(type) { 108 | case *global.MapRoleEnter: 109 | MapRoleEnter(player,m) 110 | case *global.MapRoleExit: 111 | MapExitFinal(player, 0,m) 112 | case *maps.MapChangeData: 113 | // 需要回到主城 114 | mainMapID := int32(101) 115 | mapName := lib.NormalMapName(mainMapID) 116 | cfg := config.Maps.Get(mainMapID) 117 | m.MapInfo.Pos.X = float32(cfg.BornX) 118 | m.MapInfo.Pos.Y = float32(cfg.BornY) 119 | pid := lib.GetMapPid(mapName) 120 | player.MapPid = pid 121 | MapCastMap(player,global.MAP_MOD_ROLE,m) 122 | default: 123 | kernel.UnHandleMsg(msg) 124 | } 125 | } 126 | 127 | var MapRoleEnter = func(player *global.Player,m *global.MapRoleEnter){ 128 | player.MapPid = m.Pid 129 | player.Map.X = int32(m.X) 130 | player.Map.Y = int32(m.Y) 131 | player.Map.MapName = m.MapName 132 | player.Map.MapID = m.MapID 133 | player.Map.Node = m.Pid.Node().Name() 134 | } -------------------------------------------------------------------------------- /player/plalyer.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "game/lib" 6 | "game/proto" 7 | "github.com/liangmanlin/gootp/bpool" 8 | "github.com/liangmanlin/gootp/gate" 9 | "github.com/liangmanlin/gootp/gate/pb" 10 | "github.com/liangmanlin/gootp/kernel" 11 | "github.com/liangmanlin/gootp/timer" 12 | "unsafe" 13 | ) 14 | 15 | var modRouter [global.PLAYER_MOD_MAX]*MsgHandler 16 | var DEC *pb.Coder 17 | var ENC *pb.Coder 18 | var Router map[int]*global.HandleFunc 19 | var modList = []mod{ 20 | {bagName, &BagLoad, &BagPersistent}, 21 | {attrName, &AttrLoad, &AttrPersistent}, 22 | {propName, &PropLoad, &PropPersistent}, 23 | {mapModName, &MapLoad, &MapPersistent}, 24 | } 25 | 26 | const PersistentTime int64 = 10 * 60 * 1000 // 毫秒 27 | 28 | var Actor = &kernel.Actor{ 29 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 30 | conn := args[0].(gate.Conn) 31 | gwPid := args[1].(*kernel.Pid) 32 | roleID := args[2].(int64) 33 | player := global.Player{ 34 | Conn: conn, 35 | GWPid: gwPid, 36 | RoleID: roleID, 37 | DirtyMod: make(map[string]bool), 38 | Timer: timer.NewTimer(), 39 | PersistentTime: kernel.Now2() + PersistentTime, 40 | PropData: unsafe.Pointer(lib.NewPropData()), 41 | } 42 | player.Context = ctx 43 | player.BackupMap = make(map[global.BackupKey]int) 44 | // 加载数据 45 | for _, m := range modList { 46 | (*m.load)(&player) 47 | } 48 | // 计算属性 49 | InitProps(&player) 50 | kernel.ErrorLog("Player start: %d", roleID) 51 | // 角色进程接收网络数据,减少一次消息转发 52 | conn.StartReader(pid) 53 | kernel.SendAfterForever(pid, 1000, kernel.Loop{}) 54 | lib.SetRolePid(roleID, pid) 55 | 56 | // 最后进入地图 57 | MapFirstEnter(&player) 58 | return &player 59 | }, 60 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 61 | switch m := msg.(type) { 62 | case gate.Pack: 63 | if !proto.Router(Router, m.ProtoID, m.Proto, Player(ctx)) { 64 | kernel.ErrorLog("un handle id:%d msg: %#v", m.ProtoID, m.Proto) 65 | } 66 | case *kernel.KMsg: 67 | if int(m.ModID) < len(modRouter) { 68 | (*modRouter[m.ModID])(Player(ctx), m.Msg) 69 | } else { 70 | kernel.UnHandleMsg(m) 71 | } 72 | case *gate.TcpError: 73 | kernel.ErrorLog("tcp error:%s", m.Err.Error()) 74 | kernel.Cast(Player(ctx).GWPid, m) 75 | case kernel.Loop: 76 | loop(Player(ctx)) 77 | case global.TcpReConnect: 78 | // TODO 刷新一下心跳 79 | Player(ctx).HeartTime = kernel.Now2() 80 | case *bpool.Buff: 81 | protoID,p := DEC.Decode(m.ToBytes()) 82 | if !proto.Router(Router, protoID, p, Player(ctx)) { 83 | kernel.ErrorLog("un handle id:%d msg: %#v", protoID,p) 84 | } 85 | m.Free() 86 | default: 87 | kernel.UnHandleMsg(msg) 88 | } 89 | }, 90 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 91 | player := Player(ctx) 92 | switch request.(type) { 93 | case global.Kick: 94 | // 先退出场景 95 | MapExit(player) 96 | //可以优先退出网络 97 | kernel.Cast(player.GWPid, 1) 98 | } 99 | return nil 100 | }, 101 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 102 | player := Player(ctx) 103 | // 先执行退出流程 104 | HookExit(player) 105 | // 执行持久化 106 | player.PersistentTime = 0 107 | persistent(player, kernel.Now2()) 108 | lib.DelRolePid(player.RoleID) 109 | kernel.Cast(player.GWPid, 1) 110 | }, 111 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 112 | return true 113 | }, 114 | } 115 | 116 | func loop(player *global.Player) { 117 | now2 := kernel.Now2() 118 | player.Timer.Loop(player, now2) 119 | // 判断是否需要持久化 120 | persistent(player, now2) 121 | } 122 | 123 | func persistent(player *global.Player, now2 int64) { 124 | if now2 >= player.PersistentTime { 125 | player.PersistentTime += PersistentTime 126 | for _, m := range modList { 127 | if _, ok := player.DirtyMod[m.name]; ok { 128 | (*m.persistent)(player) 129 | delete(player.DirtyMod, m.name) 130 | } 131 | } 132 | } 133 | } 134 | 135 | // 非常简单的包装,不可能有热更需求 136 | func StartTimer(player *global.Player, key, id, inv, times int32, f interface{}, arg ...interface{}) { 137 | player.Timer.Add(timer.TimerKey{Key: key, ID: id}, inv, times, f, arg...) 138 | } 139 | 140 | func DelTimer(player *global.Player, key, id int32) { 141 | player.Timer.Del(timer.TimerKey{Key: key, ID: id}) 142 | } 143 | 144 | func Player(ctx *kernel.Context) *global.Player { 145 | return ctx.State.(*global.Player) 146 | } 147 | 148 | func Start(gwPid *kernel.Pid, conn gate.Conn, roleID int64) *kernel.Pid { 149 | err, pid := kernel.SupStartChild("player_sup", 150 | &kernel.SupChild{ 151 | ChildType: kernel.SupChildTypeWorker, 152 | ReStart: false, 153 | Svr: Actor, 154 | Name: lib.GetPlayerName(roleID), 155 | InitArgs: kernel.MakeArgs(conn, gwPid, roleID), 156 | }) 157 | if err != nil { 158 | kernel.ErrorLog("start Player error:%#v", err) 159 | return nil 160 | } 161 | return pid 162 | } 163 | 164 | func SendProto(player *global.Player, proto interface{}) { 165 | bin := ENC.Encode(proto, 2) 166 | kernel.Cast(player.GWPid, bin) 167 | } 168 | -------------------------------------------------------------------------------- /player/props.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/config" 5 | "game/global" 6 | "game/lib" 7 | ) 8 | 9 | const propName = "prop" 10 | 11 | var PropLoad = func(player *global.Player) { 12 | rp := lib.GameDB.SyncSelectRow(player.Context.Call, global.TABLE_ROLE_PROP, player.RoleID, player.RoleID) 13 | player.Prop = rp.(*global.RoleProp).Prop 14 | } 15 | 16 | var PropPersistent = func(player *global.Player) { 17 | p := *player.Prop 18 | lib.GameDB.SyncUpdate(global.TABLE_ROLE_PROP, player.RoleID, &global.RoleProp{RoleID: player.RoleID, Prop: &p}) 19 | } 20 | 21 | // 登录之后,从新计算一次属性 22 | var InitProps = func(player *global.Player) { 23 | pd := propData(player) 24 | pl := pd.CalcProps(lib.AllKeys()) 25 | prop := ToProp(player.Prop,pl) 26 | player.Prop = prop 27 | } 28 | 29 | func ToProp(prop *global.PProp,pl []*global.PKV) *global.PProp { 30 | for _,v := range pl { 31 | lib.SetPropValue(prop,v.Key,v.Value) 32 | } 33 | return prop 34 | } 35 | 36 | // 添加属性 37 | var PropSetKvs = func(player *global.Player, key lib.PropKey, props []config.KV) { 38 | pd := propData(player) 39 | keys := pd.SetPropKvs(key, props) 40 | PropCalcTotal(player, pd, keys) 41 | } 42 | 43 | // 删除属性 44 | var PropRmProp = func(player *global.Player, key lib.PropKey) { 45 | pd := propData(player) 46 | keys := pd.RmPropKvs(key) 47 | PropCalcTotal(player, pd, keys) 48 | } 49 | 50 | // 从新计算属性,以及战力 51 | var PropCalcTotal = func(player *global.Player, pd *lib.PropData, keys []int32) { 52 | kvList := pd.CalcProps(keys) 53 | prop := player.Prop 54 | // 更新属性 55 | for _, v := range kvList { 56 | lib.SetPropValue(prop, v.Key, v.Value) 57 | } 58 | if len(kvList) > 0 { 59 | // 发送到场景 60 | MapCastMap(player, global.MAP_MOD_ROLE, &global.RoleUpdateProps{RoleID: player.RoleID, UP: kvList}) 61 | // 通知客户端 62 | SendProto(player, &global.RoleTocUPProps{UP: kvList}) 63 | } 64 | } 65 | 66 | func propData(player *global.Player) *lib.PropData { 67 | return (*lib.PropData)(player.PropData) 68 | } 69 | -------------------------------------------------------------------------------- /player/role.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | func init() { 9 | modRouter[global.PLAYER_MOD_ROLE] = &RoleHandler 10 | } 11 | 12 | var RoleHandler MsgHandler = func(player *global.Player, msg interface{}) { 13 | switch m := msg.(type) { 14 | case *global.RoleDeadArg: 15 | RoleDead(player,m) 16 | default: 17 | kernel.UnHandleMsg(msg) 18 | } 19 | } 20 | 21 | var RoleDead = func(player *global.Player,arg *global.RoleDeadArg) { 22 | 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /player/skill.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | "game/maps" 6 | ) 7 | 8 | var SkillGetFightSkill = func(player *global.Player)map[int32]*maps.FightSkill { 9 | return map[int32]*maps.FightSkill{ 10 | 100100:{0}, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /player/transaction.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "fmt" 5 | "game/global" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "reflect" 8 | "runtime/debug" 9 | "strings" 10 | ) 11 | 12 | func checkTransaction(player *global.Player) { 13 | if !player.IsTransaction { 14 | panic(&global.PMsg{}) 15 | } 16 | } 17 | 18 | func Transaction(player *global.Player, f func() interface{}) (rs TResult) { 19 | if player.IsTransaction { 20 | panic(fmt.Errorf("Transaction nesting ")) 21 | } 22 | player.IsTransaction = true 23 | defer transactionCatch(player, &rs) 24 | rs.Result = f() 25 | rs.OK = true 26 | commit(player) 27 | return 28 | } 29 | 30 | func Backup(player *global.Player, backupKey global.BackupKey, key, value interface{}) { 31 | if _, ok := player.BackupMap[backupKey]; !ok { 32 | data := global.BackData{Key: key, Value: value} 33 | player.Backup = append(player.Backup, data) 34 | player.BackupMap[backupKey] = len(player.Backup) - 1 35 | } 36 | } 37 | 38 | func Abort(m interface{}) { 39 | panic(m) 40 | } 41 | 42 | func AddDBQueue(player *global.Player, fun func(player *global.Player,op int32,arg interface{}), op int32, arg interface{}) { 43 | player.DBQueue = append(player.DBQueue, global.DBQueue{OP: op, Fun: fun, Arg: arg}) 44 | } 45 | 46 | func transactionCatch(player *global.Player, rs *TResult) { 47 | player.IsTransaction = false 48 | if p := recover(); p != nil { 49 | // 捕捉到错误 50 | rs.OK = false 51 | switch p.(type) { 52 | case *global.PMsg: 53 | rs.Result = p 54 | default: 55 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 56 | rs.Result = &global.PMsg{MsgID: 1} 57 | } 58 | // 回滚 59 | rollback(player) 60 | } 61 | } 62 | 63 | func commit(player *global.Player) { 64 | if len(player.DBQueue) > 0 { 65 | for _,v := range player.DBQueue { 66 | v.Fun(player,v.OP,v.Arg) 67 | } 68 | player.DBQueue = nil 69 | } 70 | if len(player.BackupMap) > 0 { 71 | player.BackupMap = make(map[global.BackupKey]int) 72 | } 73 | if len(player.Backup) > 0{ 74 | player.Backup = nil 75 | } 76 | } 77 | 78 | func rollback(player *global.Player) { 79 | if len(player.DBQueue) > 0 { 80 | player.DBQueue = nil 81 | } 82 | if len(player.BackupMap) > 0 { 83 | player.BackupMap = make(map[global.BackupKey]int) 84 | } 85 | // 先进后出,从尾部开始回滚 86 | if l := len(player.Backup); l > 0 { 87 | for i := l - 1; i >= 0; i-- { 88 | d := player.Backup[i] 89 | switch k := d.Key.(type) { 90 | case string: 91 | // 如果是字符串,是可以直接赋值的类型,用反射处理 92 | rollbackValue(player, k, d.Value) 93 | default: 94 | // 余下的一定是函数 95 | rollbackFun(player, d) 96 | } 97 | } 98 | } 99 | } 100 | 101 | func rollbackValue(player *global.Player, k string, v interface{}) { 102 | arr := strings.Split(k, ".") 103 | vrt := reflect.ValueOf(v) 104 | root := reflect.ValueOf(player).Elem() 105 | l := len(arr) - 1 106 | for i := 0; i < l; i++ { 107 | root = root.FieldByName(arr[i]) 108 | if root.Kind() == reflect.Ptr{ 109 | root = root.Elem() 110 | } 111 | } 112 | vf := root.FieldByName(arr[l]) 113 | vf.Set(vrt) 114 | } 115 | 116 | func rollbackFun(player *global.Player,data global.BackData) { 117 | fun := reflect.ValueOf(data.Key) 118 | fun.Call([]reflect.Value{reflect.ValueOf(player),reflect.ValueOf(data.Value)}) 119 | } 120 | -------------------------------------------------------------------------------- /player/type.go: -------------------------------------------------------------------------------- 1 | package player 2 | 3 | import ( 4 | "game/global" 5 | ) 6 | 7 | type mod struct { 8 | name string 9 | load *func(player *global.Player) 10 | persistent *func(player *global.Player) 11 | } 12 | 13 | type TResult struct { 14 | OK bool 15 | Result interface{} 16 | } 17 | 18 | type MsgHandler func(player *global.Player,msg interface{}) -------------------------------------------------------------------------------- /proto/pb_auto.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import "game/global" 4 | 5 | // TODO 自动生成,请勿手工修改 6 | 7 | var TOS = map[int]interface{}{ 8 | 101:&global.LoginTosConnect{}, 9 | 102:&global.LoginTosLogin{}, 10 | 103:&global.LoginTosCreateRole{}, 11 | 104:&global.LoginTosSelect{}, 12 | 201:&global.GameTosUP{}, 13 | 306:&global.MapTosMove{}, 14 | 307:&global.MapTosMoveStop{}, 15 | } 16 | 17 | var TOC = map[int]interface{}{ 18 | 101:&global.LoginTocConnect{}, 19 | 102:&global.LoginTocLogin{}, 20 | 103:&global.LoginTocCreateRole{}, 21 | 104:&global.LoginTocSelect{}, 22 | 201:&global.GameTocUP{}, 23 | 301:&global.MapTocActorLeaveArea{}, 24 | 302:&global.MapTocEnterArea{}, 25 | 303:&global.MapTocRoleEnterArea{}, 26 | 304:&global.MapTocMonsterEnterArea{}, 27 | 305:&global.MapTocStop{}, 28 | 306:&global.MapTocMove{}, 29 | 307:&global.MapTocMoveStop{}, 30 | 308:&global.MapTocUpdateMonsterInfo{}, 31 | 309:&global.MapTocUpdateRoleInfo{}, 32 | 310:&global.MapTocActorDead{}, 33 | 311:&global.MapTocDelBuff{}, 34 | 401:&global.FightTocUseSkill{}, 35 | 402:&global.FightTocSkillEffect{}, 36 | 501:&global.RoleTocUPProps{}, 37 | } -------------------------------------------------------------------------------- /proto/proto.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import ( 4 | "game/global" 5 | ) 6 | 7 | func Router(router map[int]*global.HandleFunc,protoID int,proto interface{},player *global.Player) bool { 8 | if f,ok := router[protoID];ok{ 9 | (*f)(player,proto) 10 | return true 11 | } 12 | return false 13 | } 14 | -------------------------------------------------------------------------------- /proto/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "game/global" 4 | import "game/player" 5 | 6 | func MakeRouter()map[int]*global.HandleFunc{ 7 | var rs = map[int]*global.HandleFunc{ 8 | 201:&player.LoginLogin, 9 | } 10 | return rs 11 | } -------------------------------------------------------------------------------- /service/sys_account_server.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "game/global" 6 | "game/lib" 7 | "github.com/liangmanlin/gootp/db" 8 | "github.com/liangmanlin/gootp/kernel" 9 | ) 10 | 11 | type as struct { 12 | AgentID int32 13 | ServerID int32 14 | } 15 | 16 | type AccountState struct { 17 | Dirty map[as]bool 18 | IDMap map[as]int32 19 | } 20 | 21 | var AccountActor = &kernel.Actor{ 22 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 23 | // 查询数据库 24 | rs := lib.GameDB.SyncSelect(kernel.Call,global.TABLE_ROLE_ID_INDEX,1) 25 | m := make(map[as]int32,len(rs)) 26 | for _,v := range rs{ 27 | v2 := v.(*global.RoleIDIndex) 28 | m[as{v2.AgentID,v2.ServerID}] = v2.Index 29 | } 30 | state := AccountState{IDMap: m,Dirty: make(map[as]bool)} 31 | kernel.SendAfterForever(pid,60*kernel.Millisecond,kernel.Loop{}) 32 | return &state 33 | }, 34 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 35 | switch msg.(type) { 36 | case kernel.Loop: 37 | dump(ctx.State.(*AccountState)) 38 | } 39 | }, 40 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 41 | switch r := request.(type) { 42 | case *global.CreateRole: // 创建角色 43 | return createRole(ctx.State.(*AccountState),r) 44 | } 45 | return nil 46 | }, 47 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 48 | 49 | }, 50 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 51 | return true 52 | }, 53 | } 54 | 55 | func createRole(state *AccountState,m *global.CreateRole) interface{} { 56 | rs := lib.GameDB.ModSelect(global.TABLE_ROLE_BASE, []string{"RoleID"}, fmt.Sprintf("Account=%s and AgentID=%s and ServerID=%s", 57 | db.Encode(m.Account), db.Encode(m.AgentID), db.Encode(m.ServerID))) 58 | roles := make([]int64, 0, len(rs)) 59 | for _, v := range rs { 60 | roles = append(roles, v[0].(int64)) 61 | } 62 | if len(roles) >= 3 { 63 | return &global.PMsg{MsgID: 32} 64 | } 65 | roleID := makeRoleID(state,m.AgentID,m.ServerID) 66 | Attr := &global.PRoleAttr{RoleID: roleID} 67 | Base := &global.PRoleBase{RoleID: roleID,AgentID: m.AgentID,ServerID: m.ServerID,HeroType: m.HeroType} 68 | // 由于这个变量不会被修改了所以不需要拷贝 69 | _,err := lib.GameDB.ModInsert(global.TABLE_ROLE_ATTR,Attr) 70 | if err != nil { 71 | return lib.NewPMsg(32) 72 | } 73 | _,err = lib.GameDB.ModInsert(global.TABLE_ROLE_BASE,Base) 74 | if err != nil { 75 | return lib.NewPMsg(32) 76 | } 77 | lib.GameDB.ModUpdateFields(global.TABLE_ACCOUNT,[]string{"LastRole"},[]interface{}{roleID}, 78 | fmt.Sprintf("Account=%s and AgengID=%d and ServerID=%d",db.Encode(m.Account),m.AgentID,m.ServerID)) 79 | 80 | return &global.CreateRoleResult{RoleID: roleID,Roles: roles} 81 | } 82 | 83 | func makeRoleID(state *AccountState,agentID,serverID int32) int64 { 84 | key := as{AgentID: agentID,ServerID: serverID} 85 | if i,ok := state.IDMap[key];ok{ 86 | state.Dirty[key] = true 87 | i++ 88 | state.IDMap[key] = i 89 | return global.ROLEID_AGENT*int64(agentID)+global.ROLEID_SERVER*int64(serverID)+int64(i) 90 | } 91 | state.IDMap[key] = 1 92 | lib.GameDB.ModInsert(global.TABLE_ROLE_ID_INDEX, 93 | &global.RoleIDIndex{AgentID: agentID,ServerID: serverID,Index: 1}) 94 | return global.ROLEID_AGENT*int64(agentID)+global.ROLEID_SERVER*int64(serverID)+1 95 | } 96 | 97 | func dump(state *AccountState) { 98 | for k := range state.Dirty { 99 | lib.GameDB.SyncUpdate(global.TABLE_ROLE_ID_INDEX,1, 100 | &global.RoleIDIndex{AgentID: k.AgentID,ServerID: k.ServerID,Index: state.IDMap[k]}) 101 | delete(state.Dirty,k) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "a$1" = "a" ];then 4 | echo "必要的参数:" 5 | echo " sh start.sh -name game@127.0.0.1 -cookie 6d27544c07937e4a7fab8123291cc4df" 6 | fi 7 | 8 | ulimit -c unlimited 9 | export GOTRACEBACK=crash 10 | sh -c "./bin/main -WriteLogStd false $* 2>main.core &" 11 | 12 | -------------------------------------------------------------------------------- /test_node.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "game/global" 5 | _ "game/player" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "github.com/liangmanlin/gootp/node" 8 | ) 9 | 10 | func main() { 11 | kernel.Env.WriteLogStd = true 12 | kernel.Env.LogPath = "./logs" 13 | kernel.Env.SetTimerMinTick(50) 14 | // 设置Env需要在启动之前 15 | kernel.KernelStart(func() { 16 | node.Env.Port = 5000 // 可以指定端口 17 | node.Env.PingTick = 30000 // 指定ping频率 毫秒 18 | node.Start("game_2@127.0.0.1", "6d27544c07937e4a7fab8123291cc4df", 19 | []interface{}{&global.TestCall{},&global.StrArgs{},&global.RpcStrResult{}}) 20 | destNode := "game@127.0.0.1" 21 | if node.ConnectNode(destNode) { 22 | succ,rss := node.RpcCall("game@127.0.0.1","Echo",&global.StrArgs{Str: "ffffffffffffffff"}) 23 | kernel.ErrorLog("%v,%#v",succ,rss) 24 | kernel.CastNameNode("ttt",destNode,&global.TestCall{ID: 1}) 25 | ok,rs := kernel.CallNameNode("ttt",destNode,&global.TestCall{ID: 2}) 26 | kernel.ErrorLog("%v,%#v",ok,rs) 27 | for _,n := range kernel.Nodes(){ 28 | kernel.ErrorLog("%#v",n) 29 | } 30 | } 31 | }, 32 | nil) 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /tool/exporter/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set codeFile=main.go 3 | @echo on 4 | go build -o ../bin/CfgExporter.exe %codeFile% 5 | pause -------------------------------------------------------------------------------- /tool/exporter/excel/Def.go: -------------------------------------------------------------------------------- 1 | package excel 2 | 3 | const ( 4 | RuleNone Rule = iota 5 | RuleCommon 6 | RuleServer 7 | RuleClient 8 | ) 9 | 10 | const ( 11 | FTypeInt FType = iota 12 | FTypeFloat 13 | FTypeString 14 | FTypeKey 15 | FTypeTerm 16 | FTypeSlice 17 | ) 18 | 19 | var ruleMap = map[int]Rule{ 20 | 17: RuleCommon, 21 | 40: RuleClient, 22 | 51: RuleServer, 23 | 55: RuleNone, 24 | } 25 | 26 | const ( 27 | HeadChildTypeStruct ChildType = iota 28 | HeadChildTypeSlice 29 | HeadChildTypeMap 30 | ) 31 | 32 | const ( 33 | KeyTypeInt KeyType = iota 34 | KeyTypeString 35 | ) 36 | 37 | 38 | -------------------------------------------------------------------------------- /tool/exporter/excel/excel.go: -------------------------------------------------------------------------------- 1 | package excel 2 | 3 | import ( 4 | "fmt" 5 | "github.com/xuri/excelize/v2" 6 | "path/filepath" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var reg = regexp.MustCompile(``) 12 | 13 | func LoadFile(fileName string) *Excel { 14 | file, err := excelize.OpenFile(fileName) 15 | if err != nil { 16 | panic(fmt.Errorf("读取文件:%s 失败:%s", fileName, err.Error())) 17 | } 18 | sheets := file.GetSheetList() 19 | sheetName := sheets[0] // 第一个工作表默认 20 | rows, err := file.GetRows(sheetName) 21 | if err != nil { 22 | panic(fmt.Errorf("读取文件:%s 失败:%s", fileName, err.Error())) 23 | } 24 | if len(rows) < 4 { 25 | return nil 26 | } 27 | heads := readHead(file, sheetName, rows) 28 | // 读取注释 29 | commits := readCommit(file,sheetName,rows,heads) 30 | var all [][]string 31 | // 读取数据 32 | for i := 4; i < len(rows); i++ { 33 | row := rows[i] 34 | if len(row) == 0 || row[0] == "" { 35 | continue 36 | } 37 | var dataRow []string 38 | for _, head := range heads { 39 | if len(row) <= head.Index { 40 | dataRow = append(dataRow, "") 41 | continue 42 | } 43 | val := row[head.Index] 44 | switch head.Type { 45 | case FTypeInt,FTypeFloat: 46 | if val == "" {val = "0"} 47 | case FTypeString: 48 | val = CheckValString(val) 49 | } 50 | dataRow = append(dataRow, val) 51 | } 52 | all = append(all, dataRow) 53 | } 54 | fileName = filepath.Base(fileName) 55 | ext := filepath.Ext(fileName) 56 | fileName = strings.TrimSuffix(fileName, ext) 57 | excel := &Excel{Name: fileName, Header: heads,Commits: commits, AllData: all} 58 | return excel 59 | } 60 | 61 | func readHead(file *excelize.File, sheetName string, rows [][]string) []*Head { 62 | var h int = 'A' 63 | // 第二行是head定义 64 | row := rows[1] 65 | var heads []*Head 66 | for i, v := range row { 67 | rule := getRule(h, 2, file, sheetName) 68 | h++ 69 | if isColSkip(rule, v) { 70 | continue 71 | } 72 | TypeName := strings.Trim(rows[2][i], " \t\n") // 类型定义 73 | var commit string 74 | if len(rows[3]) > i { 75 | commit = rows[3][i] // 注释 76 | } 77 | head := &Head{Rule: rule, Name: FirstUP(v), Type: getHType(TypeName), TypeName: TypeName, Commit: commit, Index: i} 78 | heads = append(heads, head) 79 | 80 | } 81 | return heads 82 | } 83 | 84 | func readCommit(file *excelize.File, sheetName string, rows [][]string,heads []*Head) []string { 85 | row := rows[3] 86 | var commits []string 87 | for _,h := range heads{ 88 | commits = append(commits,row[h.Index]) 89 | } 90 | return commits 91 | } 92 | 93 | func isColSkip(rule Rule, val string) bool { 94 | return rule == RuleNone || val == "" 95 | } 96 | 97 | func getRule(h int, index int, file *excelize.File, sheetName string) Rule { 98 | var axis string 99 | if h <= 'Z' { 100 | axis = fmt.Sprintf("%s%d", string(h), index) 101 | } else { 102 | h -= 'A' 103 | i := h%26 + 'A' // 这里假设不可能超过26*26个字段 104 | h = 'A' + h/26 - 1 105 | axis = fmt.Sprintf("%s%s%d", string(h), string(i), index) 106 | } 107 | styleID, _ := file.GetCellStyle(sheetName, axis) 108 | fillID := file.Styles.CellXfs.Xf[styleID].FillID 109 | fgColor := file.Styles.Fills.Fill[*fillID].PatternFill.FgColor 110 | return ruleMap[fgColor.Indexed] 111 | } 112 | 113 | func getHType(TypeName string) FType { 114 | switch TypeName { 115 | case "int": 116 | return FTypeInt 117 | case "float32": 118 | return FTypeFloat 119 | case "string": 120 | return FTypeString 121 | case "key": 122 | return FTypeKey 123 | default: 124 | return FTypeTerm 125 | } 126 | } 127 | 128 | func CheckValString(val string) string { 129 | if len(val) == 0 { 130 | return "\"\"" 131 | } 132 | if val[0] == '"' { 133 | return val 134 | } 135 | return "\"" + val + "\"" 136 | } 137 | 138 | func FirstUP(val string) string { 139 | if val[0] >= 'A' && val[0] <= 'Z' { 140 | return val 141 | } 142 | return string(val[0]-('a'-'A')) + val[1:] 143 | } -------------------------------------------------------------------------------- /tool/exporter/excel/type.go: -------------------------------------------------------------------------------- 1 | package excel 2 | 3 | type Excel struct { 4 | Name string 5 | Header []*Head 6 | Commits []string 7 | AllData [][]string 8 | } 9 | 10 | type Head struct { 11 | Name string 12 | Type FType 13 | TypeName string 14 | Commit string 15 | Rule Rule 16 | Index int 17 | Child *Child // toGo专用,起始不会有值 18 | } 19 | 20 | type Child struct { 21 | Type ChildType 22 | Key KeyType 23 | Value FType 24 | Fields []*Field 25 | } 26 | type Field struct { 27 | Name string 28 | Child *Child 29 | } 30 | 31 | type ChildType int 32 | type KeyType int 33 | 34 | type Rule int 35 | type FType int 36 | -------------------------------------------------------------------------------- /tool/exporter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "game/tool/exporter/excel" 7 | "game/tool/exporter/toGo" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | inFilePtr := flag.String("f", "", "请输入excel文件") 13 | outDirPtr := flag.String("os", "", "可选,请输入后端输出路径") 14 | clientDirPtr := flag.String("oc", "", "可选,请输入client输出路径") 15 | flag.Parse() 16 | if *inFilePtr == "" { 17 | flag.Usage() 18 | os.Exit(1) 19 | } 20 | exc := excel.LoadFile(*inFilePtr) 21 | fmt.Printf("head len :%d\n", len(exc.Header)) 22 | if *outDirPtr != "" { 23 | toGo.Export(exc, *outDirPtr, *outDirPtr+"/def.go") 24 | } 25 | if *clientDirPtr != "" { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tool/pbBuild/build.bat: -------------------------------------------------------------------------------- 1 | @echo on 2 | go build -o ../bin/pbBuild.exe main.go to_server.go to_lua_client.go 3 | pause -------------------------------------------------------------------------------- /tool/pbBuild/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "game/tool/pbBuild/parser" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | inFilePtr := flag.String("f","","请输入pb_def.go文件路径") 13 | outDirPtr := flag.String("o","","请输入proto输出路径") 14 | clientDirPtr := flag.String("c","","请输入client输出路径") 15 | flag.Parse() 16 | inFile := *inFilePtr 17 | outDir := *outDirPtr 18 | clientDir := *clientDirPtr 19 | fmt.Println("开始分析协议文件:", inFile) 20 | f, err := os.Open(inFile) 21 | if err != nil { 22 | panic(err) 23 | } 24 | buf, err := ioutil.ReadAll(f) 25 | if err != nil { 26 | panic(err) 27 | } 28 | all := parser.Scan(buf) 29 | if outDir != "" { 30 | toServer(all,outDir) 31 | } 32 | // TODO 导出客户端代码 33 | if clientDir != "" { 34 | toLuaClient(all,clientDir) 35 | } 36 | } 37 | 38 | 39 | type idString struct { 40 | id int 41 | str string 42 | } 43 | 44 | func WriteFile(file,outStr string) { 45 | ioFile, err := os.Create(file) 46 | if err != nil { 47 | fmt.Println(err.Error()) 48 | panic(err) 49 | } 50 | ioFile.WriteString(outStr) 51 | fmt.Println("生成目标文件:", file) 52 | } 53 | -------------------------------------------------------------------------------- /tool/pbBuild/parser/field.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | // 判断是否基础类型 4 | func (f *FieldType)IsBase() bool { 5 | switch f.Type { 6 | case PTBool,PTInt8,PTInt16,PTInt32,PTInt64,PTUInt16,PTFloat32,PTFloat64,PTString: 7 | return true 8 | } 9 | return false 10 | } 11 | 12 | func (f *FieldType)IsArray() bool { 13 | return f.Type == PTSlice 14 | } 15 | 16 | func (f *FieldType)IsMap() bool { 17 | return f.Type == PTMap 18 | } 19 | -------------------------------------------------------------------------------- /tool/pbBuild/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | "unsafe" 8 | ) 9 | 10 | var exp = regexp.MustCompile(`[\t ]*map[\t ]*\[[\t ]*\w+[\t ]*\][\t ]*\**[\t ]*\w+|[\t ]*\[[\t ]*\][\t ]*\*?[\t ]*\w+|[\t ]*\*?[\t ]*\w+`) 11 | var fieldExp = regexp.MustCompile(`(map)*\[(\w+)\]\**(\w+)|\[\]\*?(\w+)|\*?(\w+)`) 12 | var typeExp = regexp.MustCompile(`(\w+)(Tos|Toc)(\w+)`) 13 | 14 | var tMap = map[string]PType{ 15 | "bool": PTBool, 16 | "int8": PTInt8, 17 | "int16": PTInt16, 18 | "int32": PTInt32, 19 | "int64": PTInt64, 20 | "uint16": PTUInt16, 21 | "float32": PTFloat32, 22 | "float64": PTFloat64, 23 | "string": PTString, 24 | } 25 | 26 | var TypeNameMap = map[PType]string{ 27 | PTBool: "bool", 28 | PTInt8: "int8", 29 | PTInt16: "int16", 30 | PTInt32: "int32", 31 | PTInt64: "int64", 32 | PTUInt16: "uint16", 33 | PTFloat32: "float32", 34 | PTFloat64: "float64", 35 | PTString: "string", 36 | } 37 | 38 | /* 39 | 根据语法规则,逐字分析 40 | */ 41 | 42 | func Scan(buf []byte) []*Proto { 43 | lens := len(buf) 44 | var all []*Proto 45 | modIndex := 0 46 | modMap := make(map[string]int) 47 | subModMap := make(map[string]int) 48 | for i := 0; i < lens; { 49 | i, modIndex, all = readStruct(buf, i, lens, all, modIndex, modMap, subModMap) 50 | } 51 | return all 52 | } 53 | 54 | func readStruct(buf []byte, index int, lens int, all []*Proto, 55 | modIndex int, modMap map[string]int, subModMap map[string]int) (int, int, []*Proto) { 56 | var ok, lineEnd bool 57 | var name, tmp, headC, lineC string 58 | ti := index 59 | index, headC = readHeadC(buf, lens, index) 60 | if index, ok = startWith("type", buf, lens, index); ok { 61 | index, name, _ = readWord(buf, lens, index) 62 | index, tmp, _ = readWord(buf, lens, index) 63 | if tmp != "struct" { 64 | return index, modIndex, all 65 | } 66 | index, tmp, lineEnd = readWord(buf, lens, index) 67 | if tmp == "" { 68 | lineC = "" 69 | index, tmp, lineEnd = readWord(buf, lens, index) 70 | if tmp != "{" { 71 | return index, modIndex, all 72 | } 73 | index = readLine(buf, lens, index) 74 | goto step1 //这里需要跳过读取行注释 75 | } else if tmp != "{" { 76 | return index, modIndex, all 77 | } 78 | if lineEnd { 79 | lineC = "" 80 | } else { 81 | index, lineC = readLineC(buf, lens, index) //行注释,会把换行符读取掉 82 | } 83 | step1: 84 | var fields []*Field 85 | for index < lens { 86 | index, fields, ok = readField(buf, lens, index, fields) 87 | if !ok { 88 | break 89 | } 90 | } 91 | pbType, mod, subMod := getPBType(name) 92 | var protoID int 93 | if pbType == PBTos || pbType == PBToc { 94 | protoID, modIndex = makeProtoID(modIndex, mod, subMod, modMap, subModMap) 95 | } 96 | p := &Proto{Name: name, Type: pbType, Mod: mod, SubMod: subMod, ProtoID: protoID, HeadC: headC, LineC: lineC, Fields: fields} 97 | return index, modIndex, append(all, p) 98 | } 99 | index = readLine(buf, lens, ti) 100 | return index, modIndex, all 101 | } 102 | 103 | func startWith(str string, buf []byte, lens int, index int) (int, bool) { 104 | i := 0 105 | b := *(*[]byte)(unsafe.Pointer(&str)) 106 | strLen := len(b) 107 | for index < lens { 108 | v := buf[index] 109 | index++ 110 | if v == 32 || v == 9 { 111 | continue 112 | } 113 | if v == b[i] { 114 | i++ 115 | if i >= strLen { 116 | return index, true 117 | } 118 | } else { 119 | return index, false 120 | } 121 | } 122 | return index, false 123 | } 124 | 125 | func readWord(buf []byte, lens int, index int) (int, string, bool) { 126 | start := false 127 | startIndex := 0 128 | for index < lens { 129 | v := buf[index] 130 | index++ 131 | if v == 32 || v == 9 { 132 | if !start { 133 | continue 134 | } 135 | return index, string(buf[startIndex : index-1]), false 136 | } else if v == 10 || v == 13 { 137 | if start { 138 | return index, string(buf[startIndex : index-1]), true 139 | } 140 | return index, "", true 141 | } 142 | if !start { 143 | start = true 144 | startIndex = index - 1 145 | } 146 | } 147 | return index, "", true 148 | } 149 | 150 | func readHeadC(buf []byte, lens, index int) (int, string) { 151 | var s []string 152 | var ok bool 153 | step: 154 | tmp := index 155 | if index, ok = startWith("//", buf, lens, index); ok { 156 | start := index 157 | index = readLine(buf, lens, index) 158 | s = append(s, string(buf[start:index-1])) 159 | tmp = index 160 | goto step 161 | } 162 | return tmp, strings.Join(s, "\n") 163 | } 164 | 165 | func readLineC(buf []byte, lens, index int) (int, string) { 166 | for index < lens { 167 | v := buf[index] 168 | index++ 169 | if v == 47 && buf[index] == 47 { 170 | index++ 171 | start := index 172 | index = readLine(buf, lens, index) 173 | return index, string(buf[start : index-1]) 174 | } else if v == 10 || v == 13 { 175 | return index, "" 176 | } 177 | } 178 | return index, "" 179 | } 180 | 181 | func readLine(buf []byte, lens int, index int) int { 182 | for index < lens { 183 | v := buf[index] 184 | index++ 185 | if v == 10 || v == 13 { 186 | break 187 | } 188 | } 189 | return index 190 | } 191 | 192 | func readField(buf []byte, lens, index int, fields []*Field) (int, []*Field, bool) { 193 | var name, lineC string 194 | var fType *FieldType 195 | var lineEnd bool 196 | for index < lens { 197 | index, name, lineEnd = readWord(buf, lens, index) 198 | if name == "}" { 199 | return index, fields, false 200 | } else if name == "" { 201 | continue 202 | } else if _, ok := startWith("//", *(*[]byte)(unsafe.Pointer(&name)), len(name), 0); ok { 203 | index = readLine(buf, lens, index) 204 | continue 205 | } 206 | index, fType, lineEnd = readType(buf, lens, index) 207 | lineC = "" 208 | if !lineEnd { 209 | index, lineC = readLineC(buf, lens, index) 210 | } 211 | f := &Field{Name: name, FType: fType, LineC: lineC} 212 | return index, append(fields, f), true 213 | } 214 | return index, fields, false 215 | } 216 | 217 | func readType(buf []byte, lens, index int) (int, *FieldType, bool) { 218 | var name string 219 | b := exp.Find(buf[index:]) 220 | index += len(b) 221 | i := 0 222 | // 去除空白 223 | for _, v := range b { 224 | if v == 32 || v == 9 { 225 | continue 226 | } 227 | b[i] = v 228 | i++ 229 | } 230 | b = b[0:i] 231 | name = *(*string)(unsafe.Pointer(&b)) 232 | s := fieldExp.FindStringSubmatch(name) 233 | var ft *FieldType 234 | if t := s[5]; t != "" { 235 | ft = makeFieldType(name, t) 236 | } else if s[1] != "" { 237 | ft = makeFieldTypeMap(name, s[2], s[3]) 238 | } else { 239 | ft = makeFieldTypeSlice(name, s[4]) 240 | } 241 | // 分析字段类型 242 | return index, ft, false 243 | } 244 | 245 | func makeFieldType(name, TName string) *FieldType { 246 | if name[0] == 42 { 247 | name = name[1:] 248 | } 249 | t := &FieldType{ 250 | Type: getType(TName), 251 | Name: name, 252 | } 253 | return t 254 | } 255 | 256 | func makeFieldTypeMap(name, key, value string) *FieldType { 257 | t := &FieldType{ 258 | Type: PTMap, 259 | Name: name, 260 | Key: getType(key), 261 | Value: &FieldType{ 262 | Type: getType(value), 263 | Name: value, 264 | }, 265 | } 266 | return t 267 | } 268 | 269 | func makeFieldTypeSlice(name, value string) *FieldType { 270 | t := &FieldType{ 271 | Type: PTSlice, 272 | Name: name, 273 | Value: &FieldType{ 274 | Type: getType(value), 275 | Name: value, 276 | }, 277 | } 278 | return t 279 | } 280 | 281 | func getType(TName string) PType { 282 | if t, ok := tMap[TName]; ok { 283 | return t 284 | } 285 | return PTStruct 286 | } 287 | 288 | func getPBType(name string) (PBType, string, string) { 289 | s := typeExp.FindStringSubmatch(name) 290 | if len(s) == 4 { 291 | mod := s[1] 292 | subMod := s[3] 293 | if s[2] == "Tos" { 294 | return PBTos, mod, subMod 295 | } 296 | return PBToc, mod, subMod 297 | } 298 | return PBP, name, "" 299 | } 300 | 301 | func makeProtoID(modIndex int, mod, subMod string, modMap map[string]int, subModMap map[string]int) (int, int) { 302 | var modID int 303 | modIndex, modID = checkMod(mod, modIndex, modMap) 304 | key := mod + "_" + subMod 305 | subID := checkSubMod(mod, key, subModMap) 306 | protoID := modID*100 + subID 307 | return protoID, modIndex 308 | } 309 | 310 | func checkMod(mod string, modIndex int, modMap map[string]int) (int, int) { 311 | if modID, ok := modMap[mod]; ok { 312 | return modIndex, modID 313 | } 314 | modIndex++ 315 | modMap[mod] = modIndex 316 | return modIndex, modIndex 317 | } 318 | 319 | func checkSubMod(mod string, key string, subModMap map[string]int) int { 320 | if subModID, ok := subModMap[key]; ok { 321 | return subModID 322 | } 323 | if id, ok := subModMap[mod]; ok { 324 | id++ 325 | if id > 99 { 326 | panic(fmt.Errorf("[%s] sub mod lager than 99", mod)) 327 | } 328 | subModMap[key] = id 329 | subModMap[mod] = id 330 | return id 331 | } 332 | id := 1 333 | subModMap[mod] = id 334 | subModMap[key] = id 335 | return id 336 | } 337 | -------------------------------------------------------------------------------- /tool/pbBuild/parser/type.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | const ( 4 | PTBool PType = iota 5 | PTInt8 6 | PTInt16 7 | PTInt32 8 | PTInt64 9 | PTUInt16 10 | PTFloat32 11 | PTFloat64 12 | PTString 13 | PTStruct 14 | PTSlice 15 | PTMap 16 | ) 17 | 18 | const ( 19 | PBTos PBType = iota 20 | PBToc 21 | PBP 22 | ) 23 | 24 | type PType = int 25 | 26 | type PBType = int 27 | 28 | type Proto struct { 29 | Name string 30 | Type PBType 31 | Mod string 32 | SubMod string 33 | ProtoID int 34 | HeadC string 35 | LineC string 36 | Fields []*Field 37 | } 38 | 39 | type Field struct { 40 | Name string 41 | FType *FieldType 42 | LineC string 43 | } 44 | 45 | type FieldType struct { 46 | Type PType 47 | Name string 48 | Key PType //只有map类型才有 49 | Value *FieldType //只有map|slice类型才有 50 | } 51 | -------------------------------------------------------------------------------- /tool/pbBuild/to_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "game/tool/pbBuild/parser" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | func toServer(all []*parser.Proto,outDir string) { 11 | outBuf, rt := makeAuto(all) 12 | WriteFile(outDir+"/pb_auto.go",outBuf) 13 | 14 | outBuf = makeRouter(rt) 15 | WriteFile(outDir+"/router/router.go",outBuf) 16 | } 17 | 18 | func makeAuto(all []*parser.Proto) (string, []*idString) { 19 | routerExp := regexp.MustCompile(`[\t ]*router[\t ]*:[\t ]*([\w.]+)`) 20 | 21 | var tos []*idString 22 | var toc []*idString 23 | var rt []*idString 24 | for _, f := range all { 25 | s := routerExp.FindStringSubmatch(f.LineC) 26 | var router string 27 | if len(s) == 2 { 28 | router = s[1] 29 | } 30 | name := f.Name 31 | 32 | if f.Type == parser.PBTos { 33 | id := f.ProtoID 34 | tos = append(tos, &idString{id: id, str: name}) 35 | if router != "" { 36 | rt = append(rt, &idString{id: id, str: router}) 37 | } 38 | } else if f.Type == parser.PBToc { 39 | id := f.ProtoID 40 | toc = append(toc, &idString{id: id, str: name}) 41 | } 42 | } 43 | tosSlice := []string{ 44 | "var TOS = map[int]interface{}{", 45 | } 46 | for _, v := range tos { 47 | tosSlice = append(tosSlice, fmt.Sprintf("\t%d:&global.%s{},", v.id, v.str)) 48 | } 49 | tosSlice = append(tosSlice, "}") 50 | 51 | tocSlice := []string{ 52 | "var TOC = map[int]interface{}{", 53 | } 54 | for _, v := range toc { 55 | tocSlice = append(tocSlice, fmt.Sprintf("\t%d:&global.%s{},", v.id, v.str)) 56 | } 57 | tocSlice = append(tocSlice, "}") 58 | code := "package proto\n\nimport \"game/global\"\n\n// TODO 自动生成,请勿手工修改\n\n" + strings.Join(tosSlice, "\n") + "\n\n" + strings.Join(tocSlice, "\n") 59 | return code, rt 60 | } 61 | 62 | func makeRouter(rt []*idString) string { 63 | strs := []string{ 64 | "package router\n", 65 | "import \"game/global\"", 66 | "import \"game/player\"\n", 67 | "func MakeRouter()map[int]*global.HandleFunc{", 68 | "\tvar rs = map[int]*global.HandleFunc{", 69 | } 70 | for _, v := range rt { 71 | s := fmt.Sprintf("\t\t%d:&player.%s,", v.id, v.str) 72 | strs = append(strs, s) 73 | } 74 | strs = append(strs, "\t}\n\treturn rs\n}") 75 | return strings.Join(strs, "\n") 76 | } --------------------------------------------------------------------------------