├── log └── .gitkeep ├── bin ├── release │ └── .gitkeep └── debug │ ├── 4-web.bat │ ├── 8-gps.bat │ ├── 1-gate.bat │ ├── 2-login.bat │ ├── 3-database.bat │ ├── 5-world.bat │ ├── 6-world.bat │ ├── 7-world.bat │ ├── 9-balcony.bat │ ├── 0-center.bat │ ├── 1-gate.command │ ├── 4-web.command │ ├── 8-gps.command │ ├── 2-login.command │ ├── 3-database.command │ ├── 5-world.command │ ├── 6-world.command │ ├── 7-world.command │ ├── 9-balcony.command │ ├── 0-center.command │ ├── fstopall.sh │ ├── stopall.sh │ ├── runall.bat │ ├── runall.command │ └── runall.sh ├── etc ├── basic.yaml ├── database.yaml ├── zones.yaml └── servers.yaml ├── src ├── services │ ├── world │ │ ├── util.go │ │ ├── messageHandler.go │ │ └── zoneworld.go │ ├── ai │ │ └── ai.go │ ├── robot │ │ └── robot.go │ ├── chat │ │ └── chat.go │ ├── innet │ │ ├── util.go │ │ ├── retry.go │ │ ├── sender.go │ │ ├── address.go │ │ └── receiver.go │ ├── gate │ │ ├── services.go │ │ └── session.go │ ├── cluster │ │ ├── cluster.go │ │ └── running.go │ ├── balcony │ │ └── balcony.go │ ├── node │ │ └── node.go │ ├── web │ │ └── web.go │ ├── gps │ │ └── gps.go │ └── center │ │ └── center.go ├── common │ ├── global │ │ ├── interfaces.go │ │ ├── global.go │ │ └── const.go │ ├── protect │ │ └── protect.go │ ├── dbobj │ │ └── dbobj.go │ ├── profiler │ │ └── profiler.go │ ├── util │ │ └── util.go │ ├── logger │ │ └── logger.go │ └── uuid │ │ └── uuid.go ├── README.md ├── unittest │ ├── golang_test.go │ ├── uuid_test.go │ └── udp_test.go ├── gameplay │ ├── ecs │ │ ├── system │ │ │ ├── system.go │ │ │ ├── physics.go │ │ │ ├── reborn.go │ │ │ └── move.go │ │ ├── controller.go │ │ └── entity.go │ ├── matrix │ │ └── matrix.go │ └── gamemap │ │ ├── scene.go │ │ └── gamemap.go ├── engine │ ├── extensions │ │ ├── rect │ │ │ └── rect.go │ │ └── vector3 │ │ │ └── vector3.go │ └── grid │ │ └── grid.go ├── proto │ ├── config │ │ ├── map.pb.json.go │ │ └── scene.pb.json.go │ ├── data │ │ ├── data.map.pb.json.go │ │ ├── data.player.pb.json.go │ │ ├── data.entity.pb.json.go │ │ ├── data.gate.pb.json.go │ │ ├── data.role.pb.json.go │ │ └── data.component.pb.json.go │ ├── etc │ │ ├── etc.basic.pb.json.go │ │ ├── etc.database.pb.json.go │ │ ├── etc.zones.pb.json.go │ │ └── etc.servers.pb.json.go │ └── engine │ │ └── math.pb.json.go ├── cli │ └── cli.go ├── lifetime │ ├── finalize │ │ └── finalize.go │ └── startup │ │ └── startup.go └── dao │ ├── dao.player.go │ ├── dao.role.go │ └── dao.map.go ├── tools ├── build.sh ├── kibana.bat ├── filebeat.setup.bat ├── metricbeat.setup.bat ├── elasticsearch.bat ├── filebeat.bat ├── metricbeat.bat ├── genclient.sh └── genproto.sh ├── doc ├── 场景类消息流.jpg ├── 主要服务器关服流程.jpg ├── 主要服务器启动流程.jpg ├── 玩家注册登录流程.jpg └── README.md ├── .gitmodules ├── .vscode ├── settings.json └── launch.json ├── proto ├── etc │ ├── etc.basic.proto │ ├── etc.zones.proto │ ├── etc.database.proto │ └── etc.servers.proto ├── msg │ ├── login-gate.proto │ ├── world.proto │ ├── gate-world.proto │ ├── gate.proto │ ├── world-database.proto │ ├── balcony.proto │ ├── client-chat.proto │ ├── message.proto │ ├── database.proto │ ├── client-gate.proto │ ├── client-world.proto │ ├── center.proto │ ├── gps.proto │ ├── gate-database.proto │ ├── client-login.proto │ └── command.proto ├── config │ ├── map.proto │ └── scene.proto ├── data │ ├── data.player.proto │ ├── data.map.proto │ ├── data.gate.proto │ ├── data.entity.proto │ ├── data.role.proto │ └── data.component.proto ├── db │ ├── db.role.proto │ ├── db.player.proto │ └── db.map.proto ├── engine │ └── math.proto ├── gogoproto │ ├── gogo.pb.golden │ ├── Makefile │ └── gogo.proto └── protobuf │ ├── source_context.proto │ ├── empty.proto │ ├── struct.proto │ ├── wrappers.proto │ ├── duration.proto │ ├── any.proto │ ├── timestamp.proto │ └── type.proto ├── .travis.yml ├── go.mod ├── web └── web.html ├── .gitignore ├── sql ├── dynamicmaps.sql ├── players.sql ├── roles.sql ├── staticmaps.sql └── accounts.sql ├── MIND.md ├── main.go ├── LICENSE ├── README.md └── go.sum /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/release/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/basic.yaml: -------------------------------------------------------------------------------- 1 | gamename: 突突突 2 | -------------------------------------------------------------------------------- /src/services/world/util.go: -------------------------------------------------------------------------------- 1 | package world 2 | -------------------------------------------------------------------------------- /bin/debug/4-web.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 4 -------------------------------------------------------------------------------- /bin/debug/8-gps.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 8 -------------------------------------------------------------------------------- /bin/debug/1-gate.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 1 -------------------------------------------------------------------------------- /bin/debug/2-login.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 2 -------------------------------------------------------------------------------- /bin/debug/3-database.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 3 -------------------------------------------------------------------------------- /bin/debug/5-world.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 5 -------------------------------------------------------------------------------- /bin/debug/6-world.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 6 -------------------------------------------------------------------------------- /bin/debug/7-world.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 7 -------------------------------------------------------------------------------- /bin/debug/9-balcony.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 9 -------------------------------------------------------------------------------- /bin/debug/0-center.bat: -------------------------------------------------------------------------------- 1 | cd ../../ 2 | go run main.go -id 0 3 | -------------------------------------------------------------------------------- /bin/debug/1-gate.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 1 -------------------------------------------------------------------------------- /bin/debug/4-web.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 4 -------------------------------------------------------------------------------- /bin/debug/8-gps.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 8 -------------------------------------------------------------------------------- /bin/debug/2-login.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 2 -------------------------------------------------------------------------------- /bin/debug/3-database.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 3 -------------------------------------------------------------------------------- /bin/debug/5-world.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 5 -------------------------------------------------------------------------------- /bin/debug/6-world.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 6 -------------------------------------------------------------------------------- /bin/debug/7-world.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 7 -------------------------------------------------------------------------------- /bin/debug/9-balcony.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 9 -------------------------------------------------------------------------------- /tools/build.sh: -------------------------------------------------------------------------------- 1 | cd .. 2 | go build -o bin/release/server.exe 3 | echo "finish" 4 | -------------------------------------------------------------------------------- /bin/debug/0-center.command: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd ../../ 3 | go run main.go -id 0 4 | -------------------------------------------------------------------------------- /doc/场景类消息流.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iNeverSleeeeep/INServer/HEAD/doc/场景类消息流.jpg -------------------------------------------------------------------------------- /bin/debug/fstopall.sh: -------------------------------------------------------------------------------- 1 | ps ef|grep "@in-"|grep -v "grep"|awk '{print $1}'|xargs kill -9 2 | -------------------------------------------------------------------------------- /bin/debug/stopall.sh: -------------------------------------------------------------------------------- 1 | ps aux|grep "@in-"|grep -v "grep"|awk '{print $2}'|xargs kill -2 2 | -------------------------------------------------------------------------------- /doc/主要服务器关服流程.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iNeverSleeeeep/INServer/HEAD/doc/主要服务器关服流程.jpg -------------------------------------------------------------------------------- /doc/主要服务器启动流程.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iNeverSleeeeep/INServer/HEAD/doc/主要服务器启动流程.jpg -------------------------------------------------------------------------------- /doc/玩家注册登录流程.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iNeverSleeeeep/INServer/HEAD/doc/玩家注册登录流程.jpg -------------------------------------------------------------------------------- /etc/database.yaml: -------------------------------------------------------------------------------- 1 | ip: 127.0.0.1 2 | username: root 3 | password: 123456 4 | connmaxlifetime: 5 -------------------------------------------------------------------------------- /tools/kibana.bat: -------------------------------------------------------------------------------- 1 | title kibana 2 | ../ELK/kibana-7.5.1-windows-x86_64/bin/kibana.bat 3 | pause -------------------------------------------------------------------------------- /tools/filebeat.setup.bat: -------------------------------------------------------------------------------- 1 | cd ../ELK/filebeat-7.5.1-windows-x86_64/ 2 | filebeat.exe setup -e 3 | pause -------------------------------------------------------------------------------- /tools/metricbeat.setup.bat: -------------------------------------------------------------------------------- 1 | cd ../ELK/metricbeat-7.5.1-windows-x86_64/ 2 | metricbeat setup -e 3 | pause -------------------------------------------------------------------------------- /tools/elasticsearch.bat: -------------------------------------------------------------------------------- 1 | title elasticsearch 2 | ../ELK/elasticsearch-7.5.1/bin/elasticsearch.bat 3 | pause -------------------------------------------------------------------------------- /tools/filebeat.bat: -------------------------------------------------------------------------------- 1 | title filebeat 2 | cd ../ELK/filebeat-7.5.1-windows-x86_64/ 3 | filebeat -c filebeat.yml -e 4 | pause -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ELK"] 2 | path = ELK 3 | url = https://gitee.com/iNeverSleeeeep/ELK.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /tools/metricbeat.bat: -------------------------------------------------------------------------------- 1 | title metricbeat 2 | cd ../ELK/metricbeat-7.5.1-windows-x86_64/ 3 | metricbeat -c metricbeat.yml -e 4 | pause -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true, 3 | "go.lintFlags": [ 4 | "-min_confidence=.8" 5 | ], 6 | } -------------------------------------------------------------------------------- /bin/debug/runall.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | for /f "tokens=1 delims=." %%i in ('dir /a-d /b ^| findstr \-') do ( 3 | START "%%i" %~dp0%%i.bat 4 | ) -------------------------------------------------------------------------------- /bin/debug/runall.command: -------------------------------------------------------------------------------- 1 | cd "$(dirname "$0")" 2 | for file in `ls|grep command` 3 | do 4 | if [[ $file != "runall.command" ]] 5 | then 6 | open "$file" 7 | fi 8 | done -------------------------------------------------------------------------------- /proto/etc/etc.basic.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/etc"; 4 | 5 | // 一些基本配置 6 | 7 | message BasicConfig { 8 | string GameName = 1; // 游戏名称 9 | } -------------------------------------------------------------------------------- /src/services/world/messageHandler.go: -------------------------------------------------------------------------------- 1 | package world 2 | 3 | var messageHandler = new(handler) 4 | 5 | type ( 6 | handler struct { 7 | } 8 | ) 9 | 10 | func Init() { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /etc/zones.yaml: -------------------------------------------------------------------------------- 1 | zones: 2 | - {zoneid: 0, name: 沙漠之鹰, state: open} 3 | - {zoneid: 1, name: 加特林, state: open} 4 | - {zoneid: 2, name: AK47, state: open} 5 | - {zoneid: 3, name: M4A1, state: open} -------------------------------------------------------------------------------- /proto/msg/login-gate.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "client-gate.proto"; 6 | 7 | message LoginToGate { 8 | SessionCert Cert = 1; 9 | } -------------------------------------------------------------------------------- /proto/config/map.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/config"; 4 | 5 | message Map { 6 | int32 MapID = 1; 7 | repeated int32 Scenes = 2; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/common/global/interfaces.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | // 这里类里面是避免一些循环引用问题,所以使用接口来调用方法 4 | 5 | // IRoleGateGetter 取得角色所在的门服务器 6 | type IRoleGateGetter interface { 7 | GetRoleGate(uuid string) int32 8 | } 9 | -------------------------------------------------------------------------------- /proto/config/scene.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/config"; 4 | 5 | import "math.proto"; 6 | 7 | message Scene { 8 | int32 SceneID = 1; 9 | Rect Rect = 2; 10 | } 11 | -------------------------------------------------------------------------------- /proto/msg/world.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "data.role.proto"; 6 | 7 | message RoleEnterNTF { 8 | int32 Gate = 1; 9 | Role Role = 2; 10 | } -------------------------------------------------------------------------------- /proto/msg/gate-world.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | message GetMapIDReq { 6 | string MapUUID = 1; 7 | } 8 | 9 | message GetMapIDResp { 10 | int32 MapID = 1; 11 | } -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # 路径说明 2 | 3 | - cli 交互式命令行 4 | - common 通用函数 5 | - dao 数据库操作 6 | - engine 游戏引擎模块 7 | - gameplay 游戏内容 8 | - lifetime 服务器生命周期控制 9 | - proto proto生成文件 10 | - services 服务 目前一个服务器进程只启动一个服务 11 | - unittest 单元测试 12 | -------------------------------------------------------------------------------- /proto/data/data.player.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "data.role.proto"; 6 | 7 | message Player { 8 | repeated RoleSummaryData RoleList = 1; 9 | string UUID = 2; 10 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.13 4 | script: 5 | - go build main.go 6 | notifications: 7 | email: 8 | recipients: 9 | - 362761534@qq.com 10 | on_success: change 11 | on_failure: always 12 | git: 13 | submodules: false 14 | env: 15 | - GO111MODULE=on -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module INServer 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.5.0 7 | github.com/gogo/protobuf v1.3.1 8 | github.com/golang/protobuf v1.3.2 9 | github.com/gorilla/websocket v1.4.1 10 | gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 11 | ) 12 | -------------------------------------------------------------------------------- /src/common/protect/protect.go: -------------------------------------------------------------------------------- 1 | package protect 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | ) 6 | 7 | // CatchPanic 捕获异常 8 | func CatchPanic() { 9 | if logger.IsDebug { 10 | return 11 | } 12 | if err := recover(); err != nil { 13 | logger.Fatal(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /proto/data/data.map.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "data.entity.proto"; 6 | 7 | message MapData { 8 | int32 MapID = 1; 9 | string MapUUID = 2; 10 | int64 LastTickTime = 3; 11 | repeated EntityData Entities = 4; 12 | } -------------------------------------------------------------------------------- /proto/etc/etc.zones.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/etc"; 4 | 5 | // 游戏区的各种配置 6 | 7 | message Zone { 8 | int32 ZoneID = 1; 9 | string Name = 2; 10 | string State = 3; 11 | } 12 | 13 | message ZoneList { 14 | repeated Zone Zones = 1; 15 | } -------------------------------------------------------------------------------- /src/services/ai/ai.go: -------------------------------------------------------------------------------- 1 | package ai 2 | 3 | // Instance AI单例 4 | var Instance *AI 5 | 6 | // AI 负责游戏内怪物/NPC等的行为 7 | type AI struct { 8 | } 9 | 10 | // New 构造AI服务 11 | func New() *AI { 12 | a := new(AI) 13 | return a 14 | } 15 | 16 | // Start 启动AI服务 17 | func (a *AI) Start() { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /proto/msg/gate.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | message ForwardPlayerMessage { 6 | string UUID = 1; 7 | bytes Buffer = 2; 8 | } 9 | 10 | message RoleLeaveReq { 11 | repeated string Roles = 1; 12 | } 13 | 14 | message RoleLeaveResp { 15 | } -------------------------------------------------------------------------------- /proto/msg/world-database.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "data.map.proto"; 6 | 7 | message LoadStaticMapReq { 8 | int32 ZoneID = 1; 9 | int32 StaticMapID = 2; 10 | } 11 | 12 | message LoadStaticMapResp { 13 | MapData Map = 1; 14 | } -------------------------------------------------------------------------------- /src/services/robot/robot.go: -------------------------------------------------------------------------------- 1 | package robot 2 | 3 | // Instance 机器人服务单例 4 | var Instance *Robot 5 | 6 | // Robot 机器人 用于压测 7 | type Robot struct { 8 | } 9 | 10 | // New 构造Robot服务 11 | func New() *Robot { 12 | r := new(Robot) 13 | return r 14 | } 15 | 16 | // Start 启动Robot服务 17 | func (r *Robot) Start() { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/services/world/zoneworld.go: -------------------------------------------------------------------------------- 1 | package world 2 | 3 | import "INServer/src/gameplay/gamemap" 4 | 5 | type ( 6 | ZoneWorld struct { 7 | gameMaps map[int32]*gamemap.Map 8 | } 9 | ) 10 | 11 | func NewZoneWorld() *ZoneWorld { 12 | zw := new(ZoneWorld) 13 | zw.gameMaps = make(map[int32]*gamemap.Map) 14 | return zw 15 | } -------------------------------------------------------------------------------- /web/web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Hello Web

11 | 12 | -------------------------------------------------------------------------------- /proto/msg/balcony.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | 4 | option go_package = "INServer/src/proto/msg"; 5 | 6 | import "data.role.proto"; 7 | 8 | message RoleEnterReq { 9 | string RoleUUID = 1; 10 | } 11 | 12 | message RoleEnterResp { 13 | bool Success = 1; 14 | int32 MapID = 2; 15 | Role Role = 3; 16 | int32 WorldID = 4; 17 | } -------------------------------------------------------------------------------- /src/unittest/golang_test.go: -------------------------------------------------------------------------------- 1 | package unittest 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | "strconv" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestTime(t *testing.T) { 11 | t1 := strconv.FormatInt(time.Now().UnixNano(), 10) 12 | t2 := strconv.FormatInt(time.Now().UnixNano(), 10) 13 | logger.Debug(t1) 14 | logger.Debug(t2) 15 | } 16 | -------------------------------------------------------------------------------- /proto/db/db.role.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/db"; 4 | 5 | import "gogo.proto"; 6 | 7 | message DBRole { 8 | string UUID = 1 [(gogoproto.moretags) = "db:\"UUID\""]; 9 | bytes SummaryData = 2 [(gogoproto.moretags) = "db:\"SummaryData\""]; 10 | bytes OnlineData = 3 [(gogoproto.moretags) = "db:\"OnlineData\""]; 11 | } -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | - [主要服务器启动流程图](#主要服务器启动流程图) 3 | - [主要服务器关服流程图](#主要服务器关服流程图) 4 | - [玩家注册登录流程图](#玩家注册登录流程图) 5 | - [场景类消息流](#场景类消息流) 6 | 7 | ### 主要服务器启动流程图 8 | ![主要服务器启动流程图](/doc/主要服务器启动流程.jpg) 9 | 10 | ### 主要服务器关服流程图 11 | ![主要服务器关服流程图](/doc/主要服务器关服流程.jpg) 12 | 13 | ### 玩家注册登录流程图 14 | ![玩家注册登录流程图](/doc/玩家注册登录流程.jpg) 15 | 16 | ### 场景类消息流 17 | ![场景类消息流](/doc/场景类消息流.jpg) 18 | -------------------------------------------------------------------------------- /proto/etc/etc.database.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/etc"; 4 | 5 | message Database { 6 | string IP = 1; // 数据库IP地址 7 | string UserName = 2; // 用户名 8 | string Password = 3; // 密码 9 | int64 ConnMaxLifetime = 4; // 最大连接周期,超过时间的连接就close 单位秒 10 | int32 MaxOpenConns = 5; // 最大连接数 11 | int32 MaxIdleConns = 6; // 闲置连接数 12 | } -------------------------------------------------------------------------------- /src/unittest/uuid_test.go: -------------------------------------------------------------------------------- 1 | package unittest 2 | 3 | import ( 4 | "INServer/src/common/uuid" 5 | "testing" 6 | ) 7 | 8 | func TestUUID(t *testing.T) { 9 | uuids := make(map[string]bool) 10 | // 测试一千万次循环uuid是否会重复 11 | for i := 0; i < 10000000; i++ { 12 | uid := uuid.New() 13 | if _, ok := uuids[uid]; ok { 14 | t.Fail() 15 | return 16 | } else { 17 | uuids[uid] = true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/gameplay/ecs/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import "INServer/src/gameplay/ecs" 4 | 5 | var ( 6 | MoveSystem = &move{} 7 | PhysicsSystem = &physics{} 8 | ) 9 | 10 | type ISystem interface { 11 | Tick(dt float64, entities map[string]*ecs.Entity) 12 | } 13 | 14 | func Tick(dt float64, entities map[string]*ecs.Entity) { 15 | MoveSystem.Tick(dt, entities) 16 | PhysicsSystem.Tick(dt, entities) 17 | } 18 | -------------------------------------------------------------------------------- /bin/debug/runall.sh: -------------------------------------------------------------------------------- 1 | cd "$(dirname "$0")" 2 | go build -o ../../inserver-temp.exe ../../main.go 3 | cd ../.. 4 | ./inserver-temp.exe -id 0 & 5 | ./inserver-temp.exe -id 1 & 6 | ./inserver-temp.exe -id 2 & 7 | ./inserver-temp.exe -id 3 & 8 | ./inserver-temp.exe -id 4 & 9 | ./inserver-temp.exe -id 5 & 10 | ./inserver-temp.exe -id 6 & 11 | ./inserver-temp.exe -id 7 & 12 | ./inserver-temp.exe -id 8 & 13 | ./inserver-temp.exe -id 9 & 14 | -------------------------------------------------------------------------------- /src/services/chat/chat.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "INServer/src/proto/msg" 5 | "INServer/src/services/node" 6 | ) 7 | 8 | type ( 9 | Chat struct { 10 | } 11 | ) 12 | 13 | func New() *Chat { 14 | c := new(Chat) 15 | 16 | return c 17 | } 18 | 19 | func (c *Chat) Start() { 20 | node.Net.Listen(msg.CMD_CCHAT_CHAT, c.onClientChatMessage) 21 | } 22 | 23 | func (c *Chat) onClientChatMessage(eader *msg.MessageHeader, buffer []byte) { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /proto/msg/client-chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | enum ChatType { 6 | LOCAL = 0; 7 | WORLD = 1; 8 | PRIVATE = 3; 9 | } 10 | 11 | message ClientToChat { 12 | ChatType ChatType = 1; 13 | string Message = 2; 14 | string ReceiverUUID = 3; // 私聊的时候需要发接受者ID 15 | } 16 | 17 | message ChatToClient { 18 | ChatType ChatType = 1; 19 | string Message = 2; 20 | string SenderUUID = 3; 21 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | __debug_bin 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | *.log 16 | 17 | # Mac 18 | *.DS_Store 19 | 20 | clientproto 21 | 22 | # 本地配置 23 | etc/local* 24 | 25 | # go build 产生的文件 26 | INServer 27 | main 28 | 29 | # https 30 | *.crt 31 | *.key 32 | 33 | *.zip -------------------------------------------------------------------------------- /proto/engine/math.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/engine"; 4 | 5 | message Vector2 { 6 | double X = 1; 7 | double Z = 2; 8 | } 9 | 10 | message Vector3 { 11 | double X = 1; 12 | double Y = 2; 13 | double Z = 3; 14 | } 15 | 16 | message Rect { 17 | double X = 1; 18 | double Z = 2; 19 | double Width = 3; 20 | double Height = 4; 21 | } 22 | 23 | message Quaternion { 24 | double X = 1; 25 | double Y = 2; 26 | double Z = 3; 27 | double W = 4; 28 | } -------------------------------------------------------------------------------- /proto/db/db.player.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/db"; 4 | 5 | import "gogo.proto"; 6 | 7 | message DBAccount { 8 | string Name = 1 [(gogoproto.moretags) = "db:\"Name\""]; 9 | string PasswordHash = 2 [(gogoproto.moretags) = "db:\"PasswordHash\""]; 10 | string PlayerUUID = 3 [(gogoproto.moretags) = "db:\"PlayerUUID\""]; 11 | } 12 | 13 | message DBPlayer { 14 | string UUID = 1 [(gogoproto.moretags) = "db:\"UUID\""]; 15 | bytes SerializedData = 2 [(gogoproto.moretags) = "db:\"SerializedData\""]; 16 | } -------------------------------------------------------------------------------- /sql/dynamicmaps.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | -- ---------------------------- 5 | -- Table structure for dynamicmaps 6 | -- ---------------------------- 7 | DROP TABLE IF EXISTS `dynamicmaps`; 8 | CREATE TABLE `dynamicmaps` ( 9 | `UUID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 10 | `SerializedData` longblob NULL, 11 | PRIMARY KEY (`UUID`) USING BTREE 12 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 13 | 14 | SET FOREIGN_KEY_CHECKS = 1; 15 | -------------------------------------------------------------------------------- /proto/data/data.gate.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "data.role.proto"; 6 | 7 | enum SessionState { 8 | Offline = 0; // 无连接 默认状态 可以认为无连接的用户应该被Gate删除 9 | Connected = 1; // 连接状态,但是没有经过验证 10 | Online = 2; // 验证通过状态 11 | OutOfContact = 3; // 短暂失联状态,可以断线重连 12 | } 13 | 14 | message RoleSessionInfo { 15 | string RoleUUID = 1; 16 | RoleAddress Address = 2; 17 | SessionState State = 3; 18 | } 19 | 20 | message SessionCertData { 21 | string Key = 1; 22 | int64 OutOfDateTime = 2; 23 | } -------------------------------------------------------------------------------- /proto/msg/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "command.proto"; 6 | 7 | message MessageHeader { 8 | CMD Command = 1; 9 | uint64 Sequence = 2; 10 | int32 From = 3; 11 | string RoleUUID = 4; 12 | } 13 | 14 | message Message { 15 | MessageHeader Header = 1; 16 | bytes Buffer = 2; 17 | } 18 | 19 | message Package { 20 | uint64 UniqueID = 1; 21 | int32 From = 2; 22 | int32 Index = 3; 23 | int32 Total = 4; 24 | bytes Buffer = 5; 25 | } 26 | 27 | message KeepAlive { 28 | int32 ServerID = 1; 29 | } 30 | -------------------------------------------------------------------------------- /sql/players.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | -- ---------------------------- 5 | -- Table structure for players 6 | -- ---------------------------- 7 | DROP TABLE IF EXISTS `players`; 8 | CREATE TABLE `players` ( 9 | `UUID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 10 | `SerializedData` blob NULL, 11 | PRIMARY KEY (`UUID`) USING BTREE, 12 | UNIQUE INDEX `UUID_UNIQUE`(`UUID`) USING BTREE 13 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 14 | 15 | SET FOREIGN_KEY_CHECKS = 1; 16 | -------------------------------------------------------------------------------- /src/common/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "INServer/src/proto/etc" 5 | ) 6 | 7 | var ( 8 | // CurrentServerID 当前服务器ID 9 | CurrentServerID int32 = 0 10 | // CurrentServerType 当前服务器类型 11 | CurrentServerType string = InvalidServer 12 | // CurrentServerConfig 当前服务器配置 13 | CurrentServerConfig *etc.ServerConfig = nil 14 | 15 | // CenterIP 中心服默认IP 16 | CenterIP string = "127.0.0.1" 17 | 18 | // PendingExit 等待进程终止状态 19 | PendingExit bool 20 | // Exit 由命令行或消息引起的进程退出使用这个chan 21 | Exit chan bool 22 | 23 | // RoleGateGetter 根据角色UUID取得所在门服务器 24 | RoleGateGetter IRoleGateGetter 25 | ) 26 | -------------------------------------------------------------------------------- /proto/db/db.map.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/db"; 4 | 5 | import "gogo.proto"; 6 | 7 | message DBDynamicMap { 8 | string UUID = 1 [(gogoproto.moretags) = "db:\"UUID\""]; 9 | bytes SerializedData = 2 [(gogoproto.moretags) = "db:\"SerializedData\""]; 10 | } 11 | 12 | message DBStaticMap { 13 | int32 ZoneID = 1 [(gogoproto.moretags) = "db:\"ZoneID\""]; 14 | int32 MapID = 2 [(gogoproto.moretags) = "db:\"MapID\""]; 15 | string UUID = 3 [(gogoproto.moretags) = "db:\"UUID\""]; 16 | bytes SerializedData = 4 [(gogoproto.moretags) = "db:\"SerializedData\""]; 17 | } -------------------------------------------------------------------------------- /proto/data/data.entity.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "data.component.proto"; 6 | 7 | enum EntityType { 8 | OAAEntity = 0; // one above all 9 | RoleEntity = 1; 10 | MonsterEntity = 2; 11 | BulletEntity = 3; 12 | NPCEntity = 4; 13 | TriggerEntity = 5; 14 | } 15 | 16 | message EntityRealtimeData { 17 | string LastStaticMapUUID = 1; 18 | string CurrentMapUUID = 2; 19 | } 20 | 21 | message EntityData { 22 | string EntityUUID = 1; 23 | EntityRealtimeData RealTimeData = 2; 24 | repeated Component Components = 3; 25 | } -------------------------------------------------------------------------------- /proto/msg/database.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "data.map.proto"; 6 | import "data.role.proto"; 7 | 8 | message SaveStaticMapReq { 9 | repeated MapData StaticMaps = 1; 10 | } 11 | 12 | message SaveStaticMapResp { 13 | bool Success = 1; 14 | } 15 | 16 | message SaveRoleReq { 17 | repeated Role Roles = 1; 18 | } 19 | 20 | message SaveRoleResp { 21 | bool Success = 1; 22 | } 23 | 24 | message SaveDynamicMapReq { 25 | repeated MapData DynamicMaps = 1; 26 | } 27 | 28 | message SaveDynamicMapResp { 29 | bool Success = 1; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /sql/roles.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | -- ---------------------------- 5 | -- Table structure for roles 6 | -- ---------------------------- 7 | DROP TABLE IF EXISTS `roles`; 8 | CREATE TABLE `roles` ( 9 | `UUID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 10 | `SummaryData` longblob NULL, 11 | `OnlineData` longblob NULL, 12 | PRIMARY KEY (`UUID`) USING BTREE, 13 | UNIQUE INDEX `UUID_UNIQUE`(`UUID`) USING BTREE 14 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 15 | 16 | SET FOREIGN_KEY_CHECKS = 1; 17 | -------------------------------------------------------------------------------- /src/engine/extensions/rect/rect.go: -------------------------------------------------------------------------------- 1 | package rect 2 | 3 | import "INServer/src/proto/engine" 4 | 5 | func Quadrants(r *engine.Rect) (ul, ur, ll, lr *engine.Rect) { 6 | w := r.Width / 2.0 7 | h := r.Height / 2.0 8 | ll = &engine.Rect{X: r.X, Z: r.Z, Width: w, Height: h} 9 | ul = &engine.Rect{X: r.X, Z: r.Z + h, Width: w, Height: h} 10 | ur = &engine.Rect{X: r.X + w, Z: r.Z + h, Width: w, Height: h} 11 | lr = &engine.Rect{X: r.X + w, Z: r.Z, Width: w, Height: h} 12 | return ul, ur, ll, lr 13 | } 14 | 15 | func Contains(r *engine.Rect, p *engine.Vector2) bool { 16 | return p.X >= r.X && p.Z >= r.Z && p.X < r.X+r.Width && p.Z < r.Z+r.Height 17 | } 18 | -------------------------------------------------------------------------------- /src/proto/config/map.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: map.proto 3 | 4 | package config 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Map) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Map) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /sql/staticmaps.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | -- ---------------------------- 5 | -- Table structure for staticmaps 6 | -- ---------------------------- 7 | DROP TABLE IF EXISTS `staticmaps`; 8 | CREATE TABLE `staticmaps` ( 9 | `UUID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 10 | `SerializedData` longblob NULL, 11 | `ZoneID` int(11) NULL DEFAULT NULL, 12 | `MapID` int(11) NULL DEFAULT NULL, 13 | PRIMARY KEY (`UUID`) USING BTREE, 14 | UNIQUE INDEX `UUID_UNIQUE`(`UUID`) USING BTREE 15 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 16 | 17 | SET FOREIGN_KEY_CHECKS = 1; 18 | -------------------------------------------------------------------------------- /src/proto/config/scene.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: scene.proto 3 | 4 | package config 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Scene) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Scene) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /src/proto/data/data.map.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.map.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *MapData) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *MapData) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /proto/msg/client-gate.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "command.proto"; 6 | import "data.role.proto"; 7 | 8 | message SessionCert { 9 | string UUID = 1; 10 | string Key = 2; 11 | } 12 | 13 | message ConnectGateReq { 14 | SessionCert SessionCert = 1; 15 | } 16 | 17 | message ConnectGateResp { 18 | bool Success = 1; 19 | int32 MapID = 2; 20 | Role Role = 3; 21 | } 22 | 23 | message ClientToGate { 24 | CMD Command = 1; 25 | uint64 Sequence = 2; 26 | bytes Request = 3; 27 | bytes Notify = 4; 28 | } 29 | 30 | message GateToClient { 31 | CMD Command = 1; 32 | uint64 Sequence = 2; 33 | bytes Buffer = 3; 34 | } -------------------------------------------------------------------------------- /src/proto/data/data.player.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.player.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Player) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Player) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /src/proto/etc/etc.basic.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: etc.basic.proto 3 | 4 | package etc 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *BasicConfig) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *BasicConfig) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /src/proto/etc/etc.database.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: etc.database.proto 3 | 4 | package etc 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Database) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Database) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | -------------------------------------------------------------------------------- /proto/msg/client-world.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "math.proto"; 6 | import "data.entity.proto"; 7 | 8 | message MoveINF { 9 | Vector3 Position = 1; 10 | Vector3 To = 2; 11 | } 12 | 13 | message StopMoveINF { 14 | Vector3 Position = 1; 15 | } 16 | 17 | message MoveNTF { 18 | string EntityUUID = 1; 19 | Vector3 To = 2; 20 | } 21 | 22 | message NearEntity { 23 | string EntityUUID = 1; 24 | Vector3 Position = 2; 25 | } 26 | 27 | message NearEntitiesNTF { 28 | repeated NearEntity Entities = 1; 29 | } 30 | 31 | message EntityDataReq { 32 | repeated string EntityUUIDs = 1; 33 | } 34 | 35 | message EntityDataRes { 36 | repeated EntityData Entities = 1; 37 | } -------------------------------------------------------------------------------- /sql/accounts.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8mb4; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | 4 | -- ---------------------------- 5 | -- Table structure for accounts 6 | -- ---------------------------- 7 | DROP TABLE IF EXISTS `accounts`; 8 | CREATE TABLE `accounts` ( 9 | `Name` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 10 | `PasswordHash` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 11 | `PlayerUUID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 12 | PRIMARY KEY (`Name`) USING BTREE, 13 | UNIQUE INDEX `Name_UNIQUE`(`Name`) USING BTREE 14 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 15 | 16 | SET FOREIGN_KEY_CHECKS = 1; 17 | -------------------------------------------------------------------------------- /proto/data/data.role.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "data.entity.proto"; 6 | 7 | // 离线数据 8 | message RoleSummaryData { 9 | string Name = 1; // 角色名 10 | int32 Zone = 2; // 游戏区 11 | string RoleUUID = 3; // RoleUUID 12 | string PlayerUUID = 4; // PlayerUUID 13 | string MapUUID = 5; // 所在地图 14 | string MailUUID = 6; // 邮件地址 15 | } 16 | 17 | message RoleAddress { 18 | int32 Gate = 1; 19 | int32 World = 2; 20 | } 21 | 22 | // 在线数据 23 | message RoleOnlineData { 24 | EntityData EntityData = 1; 25 | } 26 | 27 | // 实时数据 与场景相关的数据 28 | message RoleRealtimeData { 29 | string LastStaticMapUUID = 1; 30 | } 31 | 32 | message Role { 33 | RoleSummaryData SummaryData = 1; 34 | RoleOnlineData OnlineData = 2; 35 | } -------------------------------------------------------------------------------- /proto/msg/center.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "etc.servers.proto"; 4 | import "etc.zones.proto"; 5 | import "etc.basic.proto"; 6 | import "etc.database.proto"; 7 | 8 | option go_package = "INServer/src/proto/msg"; 9 | 10 | enum NodeState { 11 | Unset = 0; 12 | Ready = 1; 13 | Running = 2; 14 | Offline = 3; 15 | } 16 | 17 | message ETCSyncNTF { 18 | BasicConfig BasicConfig = 1; 19 | Database Database = 2; 20 | ServerList ServerList = 3; 21 | ZoneList ZoneList = 4; 22 | } 23 | 24 | message NodeStartNTF { 25 | bytes Address = 1; 26 | } 27 | 28 | message Node { 29 | NodeState NodeState = 1; 30 | bytes NodeAddress = 2; 31 | } 32 | 33 | message NodesInfoNTF { 34 | repeated Node Nodes = 1; 35 | } 36 | 37 | message ResetConnectionNTF { 38 | int32 ServerID = 1; 39 | } -------------------------------------------------------------------------------- /tools/genclient.sh: -------------------------------------------------------------------------------- 1 | cd ../proto 2 | 3 | # data 4 | echo "--------- data ---------" 5 | for file in `ls data` 6 | do 7 | echo $file 8 | protoc --csharp_out=../clientproto --proto_path=./data --proto_path=./engine $file 9 | done 10 | 11 | # msg 12 | echo "--------- msg ---------" 13 | for file in `ls msg` 14 | do 15 | echo $file 16 | protoc --csharp_out=../clientproto --proto_path=./msg --proto_path=./data --proto_path=./etc --proto_path=./engine $file 17 | done 18 | 19 | # msg 20 | echo "--------- engine ---------" 21 | for file in `ls engine` 22 | do 23 | echo $file 24 | protoc --csharp_out=../clientproto --proto_path=./engine $file 25 | done 26 | 27 | # msg 28 | echo "--------- etc ---------" 29 | for file in `ls etc` 30 | do 31 | echo $file 32 | protoc --csharp_out=../clientproto --proto_path=./etc $file 33 | done 34 | -------------------------------------------------------------------------------- /src/gameplay/ecs/system/physics.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "INServer/src/gameplay/ecs" 5 | "INServer/src/proto/data" 6 | ) 7 | 8 | type physics struct { 9 | } 10 | 11 | func (m *physics) Tick(dt float64, entities map[string]*ecs.Entity) { 12 | for _, entity := range entities { 13 | physics := entity.GetComponent(data.ComponentType_Physics).Physics 14 | if physics != nil { 15 | slowdown(dt, physics) 16 | } 17 | } 18 | } 19 | 20 | func max(a, b float64) float64 { 21 | if a > b { 22 | return a 23 | } 24 | return b 25 | } 26 | 27 | func slowdown(dt float64, physics *data.PhysicsComponent) { 28 | a := 1 / physics.Mass 29 | physics.PassiveSpeed.X -= max(a*dt, physics.PassiveSpeed.X) 30 | physics.PassiveSpeed.Y -= max(a*dt, physics.PassiveSpeed.Y) 31 | physics.PassiveSpeed.Z -= max(a*dt, physics.PassiveSpeed.Z) 32 | } 33 | -------------------------------------------------------------------------------- /src/services/innet/util.go: -------------------------------------------------------------------------------- 1 | package innet 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | "errors" 6 | "net" 7 | ) 8 | 9 | const ( 10 | timeout int64 = 10 * 1E6 // 纳秒 11 | sliceSize int = 1000 // MTU 1472 12 | 13 | sendport = 11000 14 | recvport = 12000 15 | expvarport = 13000 16 | ) 17 | 18 | func getIP() []byte { 19 | netInterfaces, err := net.Interfaces() 20 | if err != nil { 21 | logger.Fatal(err) 22 | } 23 | 24 | for i := 0; i < len(netInterfaces); i++ { 25 | if (netInterfaces[i].Flags & net.FlagUp) != 0 { 26 | addrs, _ := netInterfaces[i].Addrs() 27 | 28 | for _, address := range addrs { 29 | if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 30 | if ipnet.IP.To4() != nil { 31 | return ipnet.IP.To4() 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | logger.Fatal(errors.New("No Valid IPV4!")) 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /MIND.md: -------------------------------------------------------------------------------- 1 | ### 一些想法 2 | * 所有的数据都定义在proto/data里面,数据和逻辑完全分离,玩家的数据存储在一个proto中,这样既方便序列化,又方便使用json进行快速验证,又方便客户端做热加载(客户端逻辑与数据分离) 3 | * 所有服务器通过与中心服务器通信来确定自己的类型,所有配置档(包括服务器配置和游戏配置)都由中心服发出 4 | * 最终实现的目标是所有玩家只在逻辑上分区,实际不分区服的在服务器集群中 5 | * 使用ECS结构编写逻辑 6 | * 只有有限的数据作为存储单元 这个得想一下 比如角色、地图、帮会等。 而怪物应该是跟随地图走,Buff跟随角色走 7 | * 是否使用机器服务器来启动当前机器上的各个服务器 8 | * 后台功能 9 | - (done) ELK 收集日志 10 | - 现在服务器都是单线程的 如果后期有必要的话 可以把一些压力大的服务器改为多线程 11 | 12 | ### 服务器类型包括如下: 13 | * 中心服务器 (广播服务器状态,发送服务器配置,游戏配置) 14 | * 登录服务器 15 | * 门服务器 (只做消息转发,不做逻辑处理) 16 | * 世界服务器 (静态场景,分线) 17 | * 副本服务器 (动态场景,可以随时创建销毁,不保存副本配置,这样可以由客户端直接发来运行时配置用于快速调试) 18 | * 聊天服务器 (世界,当前,帮派,组队,好友) 19 | * 邮件服务器 20 | * AI服务器 21 | * 玩家消息中转服务器 (用于向不确定Gate的玩家发送消息) 22 | * 数据库服务器 23 | * 日志服务器 24 | * 负载服务器 (负责统计当前机器的负载情况) 25 | * 匹配、帧同步、排行榜、帮派等专项功能服务器 26 | * SDK服务器 (登录、支付等) 27 | 28 | ### 接下来要做的 29 | - 消息分类与消息流整理 30 | - 服务器存储数据类型整理 31 | - 角色加载与释放流程整理 32 | 33 | ### 可以用到的包 34 | * gopsutil 搜集机器负载,CPU使用率等 35 | -------------------------------------------------------------------------------- /src/unittest/udp_test.go: -------------------------------------------------------------------------------- 1 | package unittest 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | "net" 6 | "testing" 7 | ) 8 | 9 | func newConn() *net.UDPConn { 10 | raddr, err := net.ResolveUDPAddr("udp4", ":10000") 11 | if err != nil { 12 | logger.Debug(err) 13 | return nil 14 | } 15 | //laddr, err := net.ResolveUDPAddr("udp4", ":10001") 16 | //if err != nil { 17 | // logger.Debug(err) 18 | // return nil 19 | //} 20 | conn, err := net.DialUDP("udp4", nil, raddr) 21 | if err != nil { 22 | logger.Debug(err) 23 | return nil 24 | } 25 | return conn 26 | } 27 | 28 | func TestDialUDP(t *testing.T) { 29 | conn := newConn() 30 | if conn == nil { 31 | t.FailNow() 32 | } 33 | conn = newConn() 34 | if conn == nil { 35 | t.FailNow() 36 | } 37 | conn = newConn() 38 | if conn == nil { 39 | t.FailNow() 40 | } 41 | conn = newConn() 42 | if conn == nil { 43 | t.FailNow() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /proto/msg/gps.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "data.role.proto"; 6 | 7 | message UpdateRoleAddressNTF { 8 | string RoleUUID = 1; 9 | RoleAddress Address = 2; 10 | } 11 | 12 | message RemoveRoleAddressNTF { 13 | string RoleUUID = 1; 14 | } 15 | 16 | message UpdateMapAddressNTF { 17 | string MapUUID = 1; 18 | int32 ServerID = 2; 19 | } 20 | 21 | message RemoveMapAddressNTF { 22 | string MapUUID = 1; 23 | } 24 | 25 | message GetMapAddressReq { 26 | string MapUUID = 1; 27 | } 28 | 29 | message GetMapAddressResp { 30 | int32 ServerID = 1; 31 | } 32 | 33 | message UpdateStaticMapUUIDNTF { 34 | int32 ZoneID = 1; 35 | int32 StaticMapID = 2; 36 | string StaticMapUUID = 3; 37 | } 38 | 39 | message GetStaticMapUUIDReq { 40 | int32 ZoneID = 1; 41 | int32 StaticMapID = 2; 42 | } 43 | 44 | message GetStaticMapUUIDResp { 45 | string StaticMapUUID = 1; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/gameplay/ecs/system/reborn.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "INServer/src/gameplay/ecs" 5 | "INServer/src/proto/data" 6 | "time" 7 | ) 8 | 9 | type reborn struct { 10 | } 11 | 12 | func (r *reborn) Tick(dt float64, entities map[string]*ecs.Entity) { 13 | now := time.Now().UnixNano() 14 | for _, entity := range entities { 15 | reborn := entity.GetComponent(data.ComponentType_Reborn).Reborn 16 | if reborn != nil && reborn.RebornType == data.RebornType_Auto && reborn.RebornTime > 0 { 17 | if reborn.RebornTime < now { 18 | reset(entity, reborn) 19 | } 20 | } 21 | } 22 | } 23 | 24 | func reset(entity *ecs.Entity, reborn *data.RebornComponent) { 25 | attribute := entity.GetComponent(data.ComponentType_Attribute).Attribute 26 | if attribute != nil { 27 | attribute.HP = attribute.MaxHP 28 | } 29 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 30 | if transform != nil { 31 | transform.Position = reborn.Position 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/services/gate/services.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "INServer/src/proto/data" 5 | "INServer/src/proto/msg" 6 | "INServer/src/services/node" 7 | 8 | "github.com/golang/protobuf/proto" 9 | ) 10 | 11 | type ( 12 | services struct { 13 | } 14 | ) 15 | 16 | func newServices() *services { 17 | s := new(services) 18 | return s 19 | } 20 | 21 | func (s *services) start() { 22 | node.Net.Listen(msg.CMD_SESSION_CERT_NTF, s.onSessionCert) 23 | } 24 | 25 | func (s *services) onSessionCert(header *msg.MessageHeader, buffer []byte) { 26 | message := &msg.LoginToGate{} 27 | err := proto.Unmarshal(buffer, message) 28 | if err != nil { 29 | return 30 | } 31 | UUID := message.Cert.UUID 32 | role, ok := Instance.roles[UUID] 33 | if ok == false { 34 | role = newSession(nil, UUID) 35 | role.info.State = data.SessionState_Offline 36 | Instance.roles[UUID] = role 37 | } 38 | role.cert.Key = message.Cert.Key 39 | role.cert.OutOfDateTime = generateCertOutOfDateTime() 40 | } 41 | -------------------------------------------------------------------------------- /proto/msg/gate-database.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "data.player.proto"; 6 | import "data.role.proto"; 7 | 8 | message CreatePlayerReq { 9 | string PlayerUUID = 1; 10 | } 11 | 12 | message CreatePlayerResp { 13 | bool Success = 1; 14 | } 15 | 16 | message LoadPlayerReq { 17 | string PlayerUUID = 1; 18 | } 19 | 20 | message LoadPlayerResp { 21 | bool Success = 1; 22 | Player Player = 2; 23 | } 24 | 25 | message ReleasePlayerNtf { 26 | string PlayerUUID = 1; 27 | } 28 | 29 | message LoadRoleReq { 30 | string RoleUUID = 1; 31 | } 32 | 33 | message LoadRoleResp { 34 | bool Success = 1; 35 | int32 WorldID = 2; 36 | string MapUUID = 3; 37 | Role Role = 4; 38 | } 39 | 40 | message CreateRoleReq { 41 | string PlayerUUID = 1; 42 | string RoleName = 2; 43 | int32 Zone = 3; 44 | } 45 | 46 | message CreateRoleResp { 47 | bool Success = 1; 48 | RoleSummaryData Role = 2; 49 | } 50 | -------------------------------------------------------------------------------- /proto/msg/client-login.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | import "client-gate.proto"; 6 | import "data.player.proto"; 7 | 8 | message CLLogon { 9 | string Name = 1; 10 | string PasswordHash = 2; 11 | } 12 | 13 | message CLLogin { 14 | string Name = 1; 15 | string PasswordHash = 2; 16 | } 17 | 18 | message CLChangePassword { 19 | string Name = 1; 20 | string OldPasswordHash = 2; 21 | string NewPasswordHash = 3; 22 | } 23 | 24 | message CLRoleEnterGame { 25 | string RoleUUID = 1; 26 | } 27 | 28 | message CLCreateRole { 29 | string RoleName = 1; 30 | int32 Zone = 2; 31 | } 32 | 33 | message ClientToLogin { 34 | CLLogon Logon = 1; 35 | CLLogin Login = 2; 36 | CLChangePassword ChangePassword = 3; 37 | CLCreateRole CreateRole = 4; 38 | CLRoleEnterGame EnterGame = 5; 39 | } 40 | 41 | message LoginToClient { 42 | bool Success = 1; 43 | SessionCert SessionCert = 2; 44 | string GateIP = 3; 45 | int32 GatePort = 4; 46 | int32 GateWebPort = 5; 47 | Player Player = 6; 48 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "INServer/src/cli" 5 | "INServer/src/common/global" 6 | "INServer/src/common/logger" 7 | "INServer/src/common/profiler" 8 | "INServer/src/lifetime/finalize" 9 | "INServer/src/lifetime/startup" 10 | _ "expvar" 11 | "flag" 12 | "fmt" 13 | "log" 14 | "runtime" 15 | ) 16 | 17 | var serverID = flag.Int("id", -1, "本服务器ID(范围0~65535)") 18 | var centerIP = flag.String("center", "127.0.0.1", "中心服务器IP") 19 | var interactive = flag.Bool("i", false, "开启交互命令行") 20 | 21 | func main() { 22 | runtime.GOMAXPROCS(1) 23 | flag.Parse() 24 | global.CurrentServerID = int32(*serverID) 25 | global.CenterIP = *centerIP 26 | 27 | if global.CurrentServerID == -1 { 28 | log.Fatalln("必须使用参数(-id ?)指定本服务器ID") 29 | } else if global.CurrentServerID > global.SERVER_ID_MAX || global.CurrentServerID < 0 { 30 | log.Fatalln("服务器ID范围0~999") 31 | } 32 | 33 | logger.Setup() 34 | 35 | profiler.Start() 36 | 37 | startup.Run() 38 | 39 | if *interactive { 40 | go cli.Run() 41 | } 42 | 43 | finalize.Wait() 44 | 45 | logger.Info(fmt.Sprintf("%d-%s Shut Down!", global.CurrentServerID, global.CurrentServerType)) 46 | } 47 | -------------------------------------------------------------------------------- /src/common/dbobj/dbobj.go: -------------------------------------------------------------------------------- 1 | package dbobj 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | "INServer/src/proto/etc" 6 | "database/sql" 7 | "fmt" 8 | "time" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | ) 12 | 13 | type ( 14 | DBObject struct { 15 | config *etc.Database 16 | database *sql.DB 17 | } 18 | ) 19 | 20 | func New() *DBObject { 21 | d := new(DBObject) 22 | return d 23 | } 24 | 25 | func (d *DBObject) Open(config *etc.Database, dbname string) { 26 | dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", config.UserName, config.Password, "tcp", config.IP, 3306, dbname) 27 | database, err := sql.Open("mysql", dsn) 28 | if err != nil { 29 | logger.Fatal(err) 30 | } 31 | database.SetConnMaxLifetime(time.Duration(config.ConnMaxLifetime) * time.Second) 32 | database.SetMaxOpenConns(int(config.MaxOpenConns)) 33 | database.SetMaxIdleConns(int(config.MaxIdleConns)) 34 | d.config = config 35 | d.database = database 36 | } 37 | 38 | func (d *DBObject) Close() { 39 | err := d.database.Close() 40 | if err != nil { 41 | logger.Debug(err) 42 | } 43 | } 44 | 45 | func (d *DBObject) DB() *sql.DB { 46 | return d.database 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 iNeverSleeeeep 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/proto/etc/etc.zones.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: etc.zones.proto 3 | 4 | package etc 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Zone) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Zone) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *ZoneList) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *ZoneList) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/iNeverSleeeeep/INServer.svg?branch=master)](https://travis-ci.org/iNeverSleeeeep/INServer) 2 | 3 | # INServer 4 | 5 | #### 介绍 6 | 基于Golang的分布式MMO游戏服务器,目标是只在逻辑上区分游戏区,单游戏区没有承载上限。 7 | 8 | ### 初始化 9 | protobuf环境初始化 10 | ``` 11 | go get github.com/gogo/protobuf/protoc-gen-gofast 12 | go get github.com/gogo/protobuf/proto 13 | go get github.com/gogo/protobuf/jsonpb 14 | go get github.com/gogo/protobuf/protoc-gen-gogo 15 | go get github.com/gogo/protobuf/gogoproto 16 | go get github.com/mitchellh/protoc-gen-go-json 17 | go get github.com/divan/expvarmon 18 | ``` 19 | 20 | [一些原始想法](/MIND.md) 21 | [一些流程图](/doc/README.md) 22 | 23 | 目前版本存在几个核心问题没有解决,这些问题直接影响了服务器的可用性和易用性。 24 | 1. 服务器如何支持乱序启动,如何支持服务器宕机重开后恢复各个服务器的状态,服务器的关机流程。 25 | 2. **服务器中有几种大的类型的消息,他们的流向应该是如何,消息如何从一个起点到达终点。这个问题不解决,目前实现玩家移动的时候,整体的逻辑是混乱的,就算做出来了,以后也会越来越乱。** 26 | 3. 每个服务器应该保存玩家/角色的哪些数据,数据发生变化时的同步如何进行。 27 | 28 | 根据以上的问题,接下来顺序完成的功能 29 | 1. 完成整个服务器群的生命周期整理,清晰的开服关服流程,服务器重启流程。(done) 30 | 2. 玩家数据分布整理,玩家的哪些数据需要存储在哪些服务器。 31 | 3. 增加月台服务器 承接玩家进入游戏但是没有分到逻辑服务器的状态。(done) 32 | 4. 整理清楚玩家和角色的概念,登录流程走完之后,一切都是角色,不应该有玩家了。完成一个玩家的生命周期整理,进入,退出游戏流程与关服时保存流程。 33 | 5. 消息类别整理,不同类别的消息的在服务器之间如何流动的流程整理。 34 | 6. 完成玩家的移动和切地图逻辑。 35 | -------------------------------------------------------------------------------- /src/cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "bufio" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | // Run 启动交互命令行 13 | func Run() { 14 | reader := bufio.NewReader(os.Stdin) 15 | 16 | http.HandleFunc("/cli", handleHTTPCli) 17 | go http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", 8000+global.CurrentServerID), nil) 18 | for { 19 | fmt.Print("$ ") 20 | cmdString, err := reader.ReadString('\n') 21 | if err != nil { 22 | fmt.Fprintln(os.Stderr, err) 23 | } 24 | err = runCommand(cmdString) 25 | if err != nil { 26 | fmt.Fprintln(os.Stderr, err) 27 | } 28 | } 29 | } 30 | 31 | func handleHTTPCli(w http.ResponseWriter, r *http.Request) { 32 | err := runCommand(r.FormValue("cmd")) 33 | if err != nil { 34 | w.Write([]byte(fmt.Sprintf("%s", err))) 35 | } else { 36 | w.Write([]byte("Success")) 37 | } 38 | } 39 | 40 | func runCommand(commandStr string) error { 41 | commandStr = strings.TrimSuffix(commandStr, "\n") 42 | arrCommandStr := strings.Fields(commandStr) 43 | switch arrCommandStr[0] { 44 | case "exit": 45 | global.Exit <- true 46 | break 47 | case "ping": 48 | fmt.Println("pong") 49 | break 50 | } 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /src/proto/data/data.entity.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.entity.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *EntityRealtimeData) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *EntityRealtimeData) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *EntityData) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *EntityData) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | -------------------------------------------------------------------------------- /src/proto/data/data.gate.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.gate.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *RoleSessionInfo) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *RoleSessionInfo) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *SessionCertData) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *SessionCertData) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | -------------------------------------------------------------------------------- /src/engine/extensions/vector3/vector3.go: -------------------------------------------------------------------------------- 1 | package vector3 2 | 3 | import ( 4 | "INServer/src/proto/engine" 5 | "math" 6 | ) 7 | 8 | // Equal 判断相等 9 | func Equal(a *engine.Vector3, b *engine.Vector3) bool { 10 | return a.X == b.X && a.Z == b.Z 11 | } 12 | 13 | // Normalize 归一化 14 | func Normalize(a *engine.Vector3) *engine.Vector3 { 15 | length := math.Sqrt(a.X*a.X + a.Y*a.Y + a.Z*a.Z) 16 | return &engine.Vector3{ 17 | X: a.X / length, 18 | Y: a.Y / length, 19 | Z: a.Z / length, 20 | } 21 | } 22 | 23 | // Multiply 乘法 24 | func Multiply(a *engine.Vector3, scale float64) *engine.Vector3 { 25 | return &engine.Vector3{ 26 | X: a.X * scale, 27 | Y: a.Y * scale, 28 | Z: a.Z * scale, 29 | } 30 | } 31 | 32 | // Minus 减法 33 | func Minus(a *engine.Vector3, b *engine.Vector3) *engine.Vector3 { 34 | return &engine.Vector3{ 35 | X: a.X - b.X, 36 | Y: a.Y - b.Y, 37 | Z: a.Z - b.Z, 38 | } 39 | } 40 | 41 | // Add 加法 42 | func Add(a *engine.Vector3, b *engine.Vector3) *engine.Vector3 { 43 | return &engine.Vector3{ 44 | X: a.X + b.X, 45 | Y: a.Y + b.Y, 46 | Z: a.Z + b.Z, 47 | } 48 | } 49 | 50 | // Dot 点积 51 | func Dot(a *engine.Vector3, b *engine.Vector3) float64 { 52 | return a.X*b.X + a.Y*b.Y + a.Z*b.Z 53 | } 54 | -------------------------------------------------------------------------------- /src/common/profiler/profiler.go: -------------------------------------------------------------------------------- 1 | package profiler 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "expvar" 6 | "net/http" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | var ( 12 | messages map[uint64]int64 13 | expvarport = 13000 14 | enabled bool = false 15 | messageRT int64 = 0 16 | ) 17 | 18 | func BeginSampleMessage(id uint64) { 19 | if enabled { 20 | messages[id] = time.Now().UnixNano() 21 | } 22 | } 23 | 24 | func EndSampleMessage(id uint64) { 25 | if enabled { 26 | messageRT += time.Now().UnixNano() - messages[id] 27 | delete(messages, id) 28 | } 29 | } 30 | 31 | func getRT() interface{} { 32 | rt := messageRT 33 | messageRT = 0 34 | return rt 35 | } 36 | 37 | func getServerID() interface{} { 38 | return global.CurrentServerID 39 | } 40 | 41 | func getServerType() interface{} { 42 | return global.CurrentServerType 43 | } 44 | 45 | func Start() { 46 | expvar.Publish("Message RT", expvar.Func(getRT)) 47 | expvar.Publish("ServerID", expvar.Func(getServerID)) 48 | expvar.Publish("ServerType", expvar.Func(getServerType)) 49 | go http.ListenAndServe(":"+strconv.Itoa(expvarport+int(global.CurrentServerID)), nil) 50 | enabled = true 51 | } 52 | 53 | func init() { 54 | messages = make(map[uint64]int64) 55 | } 56 | -------------------------------------------------------------------------------- /src/common/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "INServer/src/common/logger" 5 | "math/rand" 6 | "os" 7 | "reflect" 8 | "time" 9 | "unsafe" 10 | ) 11 | 12 | // GetRandomString 取得随机字符串 13 | func GetRandomString(l int) string { 14 | str := "0123456789abcdefghijklmnopqrstuvwxyz" 15 | bytes := []byte(str) 16 | result := []byte{} 17 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 18 | for i := 0; i < l; i++ { 19 | result = append(result, bytes[r.Intn(len(bytes))]) 20 | } 21 | return string(result) 22 | } 23 | 24 | // SetProcessName 设置linux下进程名称 25 | func SetProcessName(name string) error { 26 | argv0str := (*reflect.StringHeader)(unsafe.Pointer(&os.Args[0])) 27 | argv0 := (*[1 << 30]byte)(unsafe.Pointer(argv0str.Data))[:argv0str.Len] 28 | 29 | n := copy(argv0, name) 30 | if n < len(argv0) { 31 | argv0[n] = 0 32 | } 33 | 34 | return nil 35 | } 36 | 37 | // Wait 等待函数返回true 38 | func Wait(f func() bool, message string, d time.Duration) { 39 | for { 40 | if f() { 41 | break 42 | } 43 | logger.Info(message) 44 | time.Sleep(d) 45 | } 46 | } 47 | 48 | // PathExists 文件或目录是否存在 49 | func PathExists(path string) bool { 50 | _, err := os.Stat(path) 51 | if err == nil { 52 | return true 53 | } 54 | return false 55 | } 56 | -------------------------------------------------------------------------------- /src/common/global/const.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | const ( 4 | // CenterID 中心服务器ID 5 | CenterID int32 = 0 6 | 7 | // InvalidServerID 无效服务器ID 8 | InvalidServerID int32 = -1 9 | // InvalidServer 无效服务器类型 10 | InvalidServer string = "InvalidServer" 11 | 12 | // CenterServer 中心服务器 13 | CenterServer string = "Center" 14 | // GateServer 门服务器 15 | GateServer string = "Gate" 16 | // LoginServer 登录服务器 17 | LoginServer string = "Login" 18 | // ChatServer 聊天服务器 19 | ChatServer string = "Chat" 20 | // GPSServer 定位服务器 21 | GPSServer string = "GPS" 22 | // WorldServer 世界服务器 23 | WorldServer string = "World" 24 | // DatabaseServer 数据库服务器 25 | DatabaseServer string = "Database" 26 | // WebServer Web服务器 27 | WebServer string = "Web" 28 | // BalconyServer 月台服务器 29 | BalconyServer string = "Balcony" 30 | // AIServer AI服务器 31 | AIServer string = "AI" 32 | // RobotServer 机器人服务器(压测) 33 | RobotServer string = "Robot" 34 | 35 | // ZoneStateOpen 游戏区开启 36 | ZoneStateOpen string = "Open" 37 | // ZoneStateClosed 游戏区关闭 38 | ZoneStateClosed string = "Closed" 39 | 40 | // DatabaseSchema 数据库名 41 | DatabaseSchema string = "indb" 42 | 43 | // SERVER_ID_MAX 服务器ID最大值 44 | SERVER_ID_MAX int32 = 999 45 | // CERT_KEY_LEN 客户端登录秘钥长度 46 | CERT_KEY_LEN int = 10 47 | 48 | NANO_PER_SECONE int64 = 1E9 49 | ) 50 | -------------------------------------------------------------------------------- /src/services/gate/session.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/util" 6 | "INServer/src/proto/data" 7 | "net" 8 | "time" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | const INT_MAX int = int(^uint(0) >> 1) 14 | 15 | type ( 16 | session struct { 17 | conn *net.TCPConn 18 | webconn *websocket.Conn 19 | info *data.RoleSessionInfo 20 | cert *data.SessionCertData 21 | } 22 | ) 23 | 24 | func newSession(conn *net.TCPConn, uuid string) *session { 25 | s := &session{ 26 | conn: conn, 27 | info: &data.RoleSessionInfo{ 28 | RoleUUID: uuid, 29 | Address: &data.RoleAddress{ 30 | Gate: global.CurrentServerID, 31 | World: global.InvalidServerID, 32 | }, 33 | State: data.SessionState_Connected, 34 | }, 35 | cert: &data.SessionCertData{ 36 | Key: util.GetRandomString(global.CERT_KEY_LEN), 37 | OutOfDateTime: generateCertOutOfDateTime(), 38 | }, 39 | } 40 | return s 41 | } 42 | 43 | func (s *session) generateNewCertKey() { 44 | s.cert = &data.SessionCertData{ 45 | Key: util.GetRandomString(global.CERT_KEY_LEN), 46 | OutOfDateTime: generateCertOutOfDateTime(), 47 | } 48 | // TODO 发送到客户端 49 | } 50 | 51 | func generateCertOutOfDateTime() int64 { 52 | return time.Now().Unix() + global.CurrentServerConfig.GateConfig.OutOfDateTimeout 53 | } 54 | -------------------------------------------------------------------------------- /tools/genproto.sh: -------------------------------------------------------------------------------- 1 | cd ../proto 2 | 3 | # engine 4 | echo "--------- engine ---------" 5 | for file in `ls engine` 6 | do 7 | echo $file 8 | protoc --gofast_out=../../ --go-json_out=../src/proto/engine --proto_path=./engine $file 9 | done 10 | 11 | # config 12 | echo "--------- config ---------" 13 | for file in `ls config` 14 | do 15 | echo $file 16 | protoc --gofast_out=../../ --go-json_out=../src/proto/config --proto_path=./config --proto_path=./engine $file 17 | done 18 | 19 | # data 20 | echo "--------- data ---------" 21 | for file in `ls data` 22 | do 23 | echo $file 24 | protoc --gofast_out=../../ --go-json_out=../src/proto/data --proto_path=./data --proto_path=./engine $file 25 | done 26 | 27 | # msg 28 | echo "--------- msg ---------" 29 | for file in `ls msg` 30 | do 31 | echo $file 32 | protoc --gofast_out=../../ --proto_path=./msg --proto_path=./data --proto_path=./etc --proto_path=./engine --proto_path=./gogoproto --proto_path=./protobuf $file 33 | done 34 | 35 | # etc 36 | echo "--------- etc ---------" 37 | for file in `ls etc` 38 | do 39 | echo $file 40 | protoc --gofast_out=../../ --go-json_out=../src/proto/etc --proto_path=./etc --proto_path=./gogoproto --proto_path=./protobuf $file 41 | done 42 | 43 | # db 44 | echo "--------- db ---------" 45 | for file in `ls db` 46 | do 47 | echo $file 48 | protoc --gofast_out=../../ --proto_path=./db --proto_path=./gogoproto --proto_path=./protobuf $file 49 | done 50 | -------------------------------------------------------------------------------- /proto/gogoproto/gogo.pb.golden: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: gogo.proto 3 | // DO NOT EDIT! 4 | 5 | package gogoproto 6 | 7 | import proto "github.com/gogo/protobuf/proto" 8 | import json "encoding/json" 9 | import math "math" 10 | import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" 11 | 12 | // Reference proto, json, and math imports to suppress error if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = &json.SyntaxError{} 15 | var _ = math.Inf 16 | 17 | var E_Nullable = &proto.ExtensionDesc{ 18 | ExtendedType: (*google_protobuf.FieldOptions)(nil), 19 | ExtensionType: (*bool)(nil), 20 | Field: 51235, 21 | Name: "gogoproto.nullable", 22 | Tag: "varint,51235,opt,name=nullable", 23 | } 24 | 25 | var E_Embed = &proto.ExtensionDesc{ 26 | ExtendedType: (*google_protobuf.FieldOptions)(nil), 27 | ExtensionType: (*bool)(nil), 28 | Field: 51236, 29 | Name: "gogoproto.embed", 30 | Tag: "varint,51236,opt,name=embed", 31 | } 32 | 33 | var E_Customtype = &proto.ExtensionDesc{ 34 | ExtendedType: (*google_protobuf.FieldOptions)(nil), 35 | ExtensionType: (*string)(nil), 36 | Field: 51237, 37 | Name: "gogoproto.customtype", 38 | Tag: "bytes,51237,opt,name=customtype", 39 | } 40 | 41 | func init() { 42 | proto.RegisterExtension(E_Nullable) 43 | proto.RegisterExtension(E_Embed) 44 | proto.RegisterExtension(E_Customtype) 45 | } 46 | -------------------------------------------------------------------------------- /proto/data/data.component.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/data"; 4 | 5 | import "math.proto"; 6 | 7 | enum ComponentType { 8 | Invalid = 0; 9 | Transofrm = 1; 10 | Physics = 2; 11 | Attribute = 3; 12 | Move = 4; 13 | Controller = 5; 14 | Reborn = 6; 15 | } 16 | 17 | enum ControllerType { 18 | PlayerController = 0; 19 | AIController = 1; 20 | } 21 | 22 | enum RebornType { 23 | None = 0; 24 | Auto = 1; 25 | Manual = 2; 26 | } 27 | 28 | message TransformComponent { 29 | Vector3 Position = 1; 30 | Quaternion rotation = 2; 31 | } 32 | 33 | message PhysicsComponent { 34 | double Mass = 1; 35 | Vector3 RawSpeed = 2; // 玩家操作的移动速度 36 | Vector3 PassiveSpeed = 3; // 其他外力影响的速度 37 | } 38 | 39 | message AttributeComponent { 40 | float Speed = 1; 41 | float HP = 2; 42 | float MaxHP = 3; 43 | } 44 | 45 | message MoveComponent { 46 | Vector3 Destination = 1; 47 | } 48 | 49 | message ControllerComponent { 50 | ControllerType ControllerType = 1; 51 | } 52 | 53 | message RebornComponent { 54 | int64 RebornTime = 1; 55 | RebornType RebornType = 2; 56 | Vector3 Position = 3; 57 | } 58 | 59 | message Component { 60 | ComponentType Type = 1; 61 | TransformComponent Transform = 2; 62 | PhysicsComponent Physics = 3; 63 | AttributeComponent Attribute = 4; 64 | MoveComponent Move = 5; 65 | ControllerComponent Controller = 6; 66 | RebornComponent Reborn = 7; 67 | } -------------------------------------------------------------------------------- /src/gameplay/ecs/system/move.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "INServer/src/engine/extensions/vector3" 5 | "INServer/src/gameplay/ecs" 6 | "INServer/src/proto/data" 7 | "INServer/src/proto/engine" 8 | ) 9 | 10 | type move struct { 11 | } 12 | 13 | func (m *move) Tick(dt float64, entities map[string]*ecs.Entity) { 14 | for _, entity := range entities { 15 | physics := entity.GetComponent(data.ComponentType_Physics).Physics 16 | if physics != nil { 17 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 18 | if transform != nil { 19 | stop(dt, entity, physics, transform) 20 | step(dt, transform.Position, vector3.Add(physics.RawSpeed, physics.PassiveSpeed)) 21 | } 22 | } 23 | } 24 | } 25 | 26 | func stop(dt float64, entity *ecs.Entity, physics *data.PhysicsComponent, transform *data.TransformComponent) { 27 | move := entity.GetComponent(data.ComponentType_Move).Move 28 | if move != nil { 29 | dir := vector3.Minus(move.Destination, transform.Position) 30 | // 如果前进方向和目标方向相反 则停止移动 31 | if vector3.Dot(physics.RawSpeed, dir) < 0 { 32 | physics.RawSpeed = &engine.Vector3{} 33 | transform.Position = move.Destination 34 | } 35 | } else { 36 | physics.RawSpeed = &engine.Vector3{} 37 | } 38 | } 39 | 40 | func step(dt float64, pos *engine.Vector3, speed *engine.Vector3) bool { 41 | X, Y, Z := pos.X, pos.Y, pos.Z 42 | pos.X += dt * speed.X 43 | pos.Y += dt * speed.Y 44 | pos.Z += dt * speed.Z 45 | return X != pos.X || Y != pos.Y || Z != pos.Z 46 | } 47 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 2 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 3 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 4 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 5 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 6 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 7 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 8 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 9 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 10 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 11 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563 h1:NIou6eNFigscvKJmsbyez16S2cIS6idossORlFtSt2E= 12 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo= 16 | gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /etc/servers.yaml: -------------------------------------------------------------------------------- 1 | servers: 2 | - 3 | serverid: 0 4 | servertype: Center 5 | serverconfig: 6 | - 7 | serverid: 1 8 | servertype: Gate 9 | serverconfig: 10 | gateconfig: 11 | address: 127.0.0.1 12 | port: 10999 13 | outofdatatimeout: 10 14 | webport: 10998 15 | - 16 | serverid: 2 17 | servertype: Login 18 | serverconfig: 19 | loginconfig: 20 | port: 10800 21 | webport: 10801 22 | - 23 | serverid: 3 24 | servertype: Database 25 | serverconfig: 26 | databaseconfig: 27 | database: 28 | - 29 | serverid: 4 30 | servertype: Web 31 | serverconfig: 32 | webconfig: 33 | port: 10700 34 | account: ins 35 | password: 123456 36 | - 37 | serverid: 5 38 | servertype: World 39 | serverconfig: 40 | worldconfig: 41 | fps: 100 42 | zones: 43 | - {zoneid: 0, staticmaps: [1]} 44 | - {zoneid: 1, staticmaps: [1]} 45 | - 46 | serverid: 6 47 | servertype: World 48 | serverconfig: 49 | worldconfig: 50 | fps: 100 51 | zones: 52 | - {zoneid: 1, staticmaps: [2]} 53 | - {zoneid: 2, staticmaps: [1,2]} 54 | - 55 | serverid: 7 56 | servertype: World 57 | serverconfig: 58 | worldconfig: 59 | fps: 100 60 | zones: 61 | - {zoneid: 3, staticmaps: [1,2]} 62 | - {zoneid: 4, staticmaps: [1,2]} 63 | - 64 | serverid: 8 65 | servertype: GPS 66 | serverconfig: 67 | - 68 | serverid: 9 69 | servertype: Balcony 70 | serverconfig: -------------------------------------------------------------------------------- /src/lifetime/finalize/finalize.go: -------------------------------------------------------------------------------- 1 | package finalize 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/util" 6 | "INServer/src/services/cluster" 7 | "INServer/src/services/world" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | // Wait 等待结束 15 | func Wait() { 16 | global.Exit = make(chan bool) 17 | sigs := make(chan os.Signal, 1) 18 | signal.Notify(sigs, syscall.SIGINT) 19 | 20 | for { 21 | stopped := false 22 | select { 23 | case sig := <-sigs: 24 | if sig.String() == "interrupt" && stopped == false { 25 | stopped = true 26 | stopNode() 27 | } 28 | break 29 | case exit := <-global.Exit: 30 | if exit == true && stopped == false { 31 | stopped = true 32 | stopNode() 33 | } 34 | } 35 | if stopped { 36 | break 37 | } 38 | time.Sleep(time.Millisecond * 10) 39 | } 40 | } 41 | 42 | func stopNode() { 43 | global.PendingExit = true 44 | stopServer() 45 | } 46 | 47 | // 关服流程 48 | // step1 登录服务器 门服务器 等 49 | // step2 世界服务器 50 | // step3 数据库服务器 51 | // step4 中心服务器 52 | func stopServer() { 53 | switch global.CurrentServerType { 54 | case global.WorldServer: 55 | util.Wait(func() bool { 56 | return len(cluster.RunningGates()) == 0 57 | }, "等待门服务器关服完成...", time.Second) 58 | world.Instance.Stop() 59 | break 60 | case global.DatabaseServer: 61 | util.Wait(func() bool { 62 | return cluster.RunningCount() == 2 63 | }, "等待其他服务器关服完成...", time.Second) 64 | break 65 | case global.CenterServer: 66 | util.Wait(func() bool { 67 | return cluster.RunningCount() == 1 68 | }, "等待其他服务器关服完成...", time.Second) 69 | break 70 | } 71 | } 72 | 73 | func init() { 74 | global.PendingExit = false 75 | } 76 | -------------------------------------------------------------------------------- /proto/etc/etc.servers.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/etc"; 4 | 5 | import "etc.database.proto"; 6 | 7 | // 服务器的各种配置 8 | 9 | message GateConfig { 10 | int32 Port = 1; // 监听客户端连接的端口 11 | int64 OutOfDateTimeout = 2; // 断开连接后Session删除的Timeout 12 | string Address = 3; // IP地址 因为阿里云没有办法取得公网IP所以只能配置 13 | int32 WebPort = 4; // 客户端websocket端口 14 | } 15 | 16 | message LoginConfig { 17 | int32 Port = 1; // 监听客户端连接的端口 18 | Database Database = 2; // 账号数据库 19 | int32 WebPort = 3; // 客户端websocket端口 20 | } 21 | 22 | message ChatConfig { 23 | 24 | } 25 | 26 | message DatabaseConfig { 27 | Database Database = 1; 28 | } 29 | 30 | message WebConfig { 31 | int32 Port = 1; // 监听Http请求的端口 32 | string Account = 2; // 账号 33 | string Password = 3; // 密码 34 | } 35 | 36 | message ZoneWorld { 37 | int32 ZoneID = 1; // 游戏区ID 38 | repeated int32 StaticMaps = 2; // 世界地图列表 39 | } 40 | 41 | message WorldConfig { 42 | repeated ZoneWorld Zones = 1; // 游戏区配置 43 | int32 FPS = 2; // 帧率 44 | } 45 | 46 | message AIConfig { 47 | 48 | } 49 | 50 | message RobotConfig { 51 | 52 | } 53 | 54 | message ServerConfig { 55 | GateConfig GateConfig = 1; // 门服务器配置 56 | LoginConfig LoginConfig = 2; // 登录服务器 57 | ChatConfig ChatConfig = 3; // 聊天服务器 58 | DatabaseConfig DatabaseConfig = 4; // 数据库服务器 59 | WebConfig WebConfig = 5; // Http服务器 60 | WorldConfig WorldConfig = 6; // 世界服务器 61 | AIConfig AIConfig = 7; // AI服务器 62 | RobotConfig RobotConfig = 8; // 机器人服务器 63 | } 64 | 65 | message Server { 66 | int32 ServerID = 1; 67 | string ServerType = 2; 68 | ServerConfig ServerConfig = 3; 69 | string Describe = 4; 70 | } 71 | 72 | message ServerList { 73 | repeated Server Servers = 1; 74 | } -------------------------------------------------------------------------------- /proto/gogoproto/Makefile: -------------------------------------------------------------------------------- 1 | # Protocol Buffers for Go with Gadgets 2 | # 3 | # Copyright (c) 2013, The GoGo Authors. All rights reserved. 4 | # http://github.com/gogo/protobuf 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | regenerate: 30 | go install github.com/gogo/protobuf/protoc-gen-gogo 31 | protoc --gogo_out=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor:../../../../ --proto_path=../../../../:../protobuf/:. *.proto 32 | 33 | restore: 34 | cp gogo.pb.golden gogo.pb.go 35 | 36 | preserve: 37 | cp gogo.pb.go gogo.pb.golden 38 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "Launch Center", 10 | "type": "go", 11 | "request": "launch", 12 | "mode": "debug", 13 | "program": "${workspaceFolder}", 14 | "env": {}, 15 | "args": ["-id", "0"] 16 | }, 17 | { 18 | "name": "Launch Gate", 19 | "type": "go", 20 | "request": "launch", 21 | "mode": "debug", 22 | "program": "${workspaceFolder}", 23 | "env": {}, 24 | "args": ["-id", "1"] 25 | }, 26 | { 27 | "name": "Launch Login", 28 | "type": "go", 29 | "request": "launch", 30 | "mode": "debug", 31 | "program": "${workspaceFolder}", 32 | "env": {}, 33 | "args": ["-id", "2"] 34 | }, 35 | { 36 | "name": "Launch Database", 37 | "type": "go", 38 | "request": "launch", 39 | "mode": "debug", 40 | "program": "${workspaceFolder}", 41 | "env": {}, 42 | "args": ["-id", "3"] 43 | }, 44 | { 45 | "name": "Launch Web", 46 | "type": "go", 47 | "request": "launch", 48 | "mode": "debug", 49 | "program": "${workspaceFolder}", 50 | "env": {}, 51 | "args": ["-id", "4"] 52 | }, 53 | { 54 | "name": "Launch World5", 55 | "type": "go", 56 | "request": "launch", 57 | "mode": "debug", 58 | "program": "${workspaceFolder}", 59 | "env": {}, 60 | "args": ["-id", "5", "-i", "true"] 61 | }, 62 | ] 63 | } -------------------------------------------------------------------------------- /src/gameplay/matrix/matrix.go: -------------------------------------------------------------------------------- 1 | package matrix 2 | 3 | import ( 4 | "INServer/src/common/uuid" 5 | "INServer/src/gameplay/ecs" 6 | "INServer/src/gameplay/gamemap" 7 | "INServer/src/proto/data" 8 | "INServer/src/proto/engine" 9 | ) 10 | 11 | // Matrix 控制地图内实体的产生消亡 12 | type Matrix struct { 13 | } 14 | 15 | // New 构造Matrix 16 | func New() *Matrix { 17 | m := new(Matrix) 18 | return m 19 | } 20 | 21 | // OnGamemapCreate 地图创建时的初始化 22 | func (m *Matrix) OnGamemapCreate(gameMap *gamemap.Map) { 23 | if len(gameMap.MapData().Entities) == 0 { 24 | for _, scene := range gameMap.Scenes() { 25 | for x := scene.Width / -2; x < scene.Width/2; x = x + 10 { 26 | for z := scene.Height / -2; z < scene.Height/2; z = z + 10 { 27 | entityUUID := uuid.New() 28 | components := ecs.InitComponents(data.EntityType_MonsterEntity) 29 | components[data.ComponentType_Transofrm].Transform.Position = &engine.Vector3{ 30 | X: float64(x), 31 | Y: 0, 32 | Z: float64(z), 33 | } 34 | entityData := &data.EntityData{ 35 | EntityUUID: entityUUID, 36 | Components: components, 37 | RealTimeData: &data.EntityRealtimeData{ 38 | LastStaticMapUUID: gameMap.MapData().MapUUID, 39 | CurrentMapUUID: gameMap.MapData().MapUUID, 40 | }, 41 | } 42 | entity := ecs.NewEntity(entityData, data.EntityType_MonsterEntity) 43 | if entity != nil { 44 | gameMap.EntityEnter(entity.UUID(), entity) 45 | } 46 | } 47 | } 48 | } 49 | } else { 50 | for _, entityData := range gameMap.MapData().Entities { 51 | entity := ecs.NewEntity(entityData, data.EntityType_MonsterEntity) 52 | if entity != nil { 53 | gameMap.EntityEnter(entity.UUID(), entity) 54 | } 55 | } 56 | } 57 | } 58 | 59 | // OnGamemapDestroy 地图销毁时的回收 60 | func (m *Matrix) OnGamemapDestroy(gameMap *gamemap.Map) { 61 | 62 | } 63 | 64 | // TickGamemap Tick 65 | func (m *Matrix) TickGamemap(gameMap *gamemap.Map) { 66 | 67 | } 68 | -------------------------------------------------------------------------------- /proto/msg/command.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "INServer/src/proto/msg"; 4 | 5 | enum CMD { 6 | KEEP_ALIVE = 0; // any -> center 7 | RESP = 1; 8 | GATE = 2; // client -> gate -> any 9 | CONNECT_GATE_REQ = 6; // client -> gate 10 | SESSION_CERT_NTF = 7; // gate -> client 11 | CCHAT_CHAT = 8; // client -> chat 12 | DISPATCH = 9; // any -> dispatcher 13 | LD_CREATE_PLAYER_REQ = 10; // login -> database 14 | GD_LOAD_PLAYER_REQ = 11; // gate -> database 15 | GD_RELEASE_PLAYER_NTF = 12; // gate -> database 16 | GD_CREATE_ROLE_REQ = 13; // gate -> database 17 | GD_LOAD_ROLE_REQ = 14; // gate -> database 18 | RELOAD_ETC_REQ = 15; // web -> center 19 | MOVE_NTF = 17; // world -> client 20 | NEAR_ENTITIES_NTF = 18; // world -> client 21 | ENTITY_DATA_REQ = 19; // client -> world 22 | ROLE_ENTER = 20; // client -> gate; any -> world 23 | UPDATE_ROLE_ADDRESS_NTF = 21; // any -> gps 24 | REMOVE_ROLE_ADDRESS_NTF = 22; // gate -> gps 25 | UPDATE_MAP_ADDRESS_NTF = 23; // world -> gps 26 | REMOVE_MAP_ADDRESS_NTF = 24; // world -> gps 27 | GET_MAP_ADDRESS_REQ = 25; // database -> gps 28 | LOAD_STATIC_MAP_REQ = 26; // world -> database 29 | LOAD_DYNAMIC_MAP_REQ = 27; // world -> database 30 | UPDATE_STATIC_MAP_UUID_NTF = 28; // world -> gps 31 | GET_STATIC_MAP_UUID_REQ = 29; // any -> gps 32 | SAVE_STATIC_MAP_REQ = 30; // any -> database 33 | SAVE_DYNAMIC_MAP_REQ = 31; // any -> database 34 | SAVE_ROLE_REQ = 32; // any -> database 35 | GET_MAP_ID = 33; // gate -> world 36 | MOVE_INF = 34; // client -> world 37 | FORWARD_CLIENT_MESSAGE = 35; // any -> gate -> client 38 | STOP_MOVE_INF = 36; // client -> world 39 | 40 | NODE_START_NTF = 1000; // any -> center 41 | ETC_SYNC_NTF = 1001; // center -> any 42 | NODES_INFO_NTF = 1002; // any <-> center 43 | RESET_CONNECTION_NTF = 1003; // center -> any 44 | ROLE_LEAVE_REQ = 1004; // gate -> any 45 | } -------------------------------------------------------------------------------- /src/services/innet/retry.go: -------------------------------------------------------------------------------- 1 | package innet 2 | 3 | import ( 4 | "INServer/src/proto/msg" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type ( 10 | packageCache struct { 11 | addr *net.UDPAddr 12 | packages []*msg.Package 13 | retryTime int64 14 | toServerID int32 15 | } 16 | 17 | retry struct { 18 | innet *INNet 19 | cachedPackages map[uint64]*packageCache 20 | stoped bool 21 | } 22 | ) 23 | 24 | func newRetry(innet *INNet) *retry { 25 | r := new(retry) 26 | r.innet = innet 27 | r.cachedPackages = make(map[uint64]*packageCache) 28 | r.startTickRetry() 29 | return r 30 | } 31 | 32 | func (r *retry) addPackagesCache(packageID uint64, cache *packageCache) { 33 | r.cachedPackages[packageID] = cache 34 | } 35 | 36 | func (r *retry) resetServer(serverID int32) { 37 | packageIDList := make([]uint64, 0) 38 | for packageID, cache := range r.cachedPackages { 39 | if cache.toServerID == serverID { 40 | packageIDList = append(packageIDList, packageID) 41 | } 42 | } 43 | for _, packageID := range packageIDList { 44 | delete(r.cachedPackages, packageID) 45 | } 46 | } 47 | 48 | func (r *retry) handlePackageReceive(receivedPkg *msg.Package) { 49 | if cache, ok := r.cachedPackages[receivedPkg.UniqueID]; ok { 50 | for index, pkg := range cache.packages { 51 | if pkg.UniqueID == receivedPkg.UniqueID { 52 | cache.packages = append(cache.packages[:index], cache.packages[index+1:]...) 53 | if len(cache.packages) == 0 { 54 | delete(r.cachedPackages, receivedPkg.UniqueID) 55 | } 56 | break 57 | } 58 | } 59 | } 60 | } 61 | 62 | func (r *retry) startTickRetry() { 63 | r.stoped = false 64 | go func() { 65 | for r.stoped == false { 66 | time.Sleep(time.Millisecond * 30) 67 | current := time.Now().UnixNano() 68 | for _, cache := range r.cachedPackages { 69 | retryTime := cache.retryTime 70 | if retryTime < current { 71 | r.innet.sender.sendPackages(cache.addr, cache.packages) 72 | cache.retryTime = current + timeout 73 | } 74 | } 75 | } 76 | }() 77 | } 78 | -------------------------------------------------------------------------------- /src/services/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "INServer/src/proto/msg" 5 | "INServer/src/services/etcmgr" 6 | ) 7 | 8 | var ( 9 | nodes []*msg.Node 10 | ) 11 | 12 | // SetNodes 设置集群信息 13 | func SetNodes(nodesArray []*msg.Node) { 14 | for serverID, node := range nodesArray { 15 | SetNode(int32(serverID), node) 16 | } 17 | RefreshRunning() 18 | RefreshRunningZones() 19 | } 20 | 21 | // SetNode 设置单个节点信息 22 | func SetNode(serverID int32, node *msg.Node) { 23 | for len(nodes) < int(serverID+1) { 24 | nodes = append(nodes, &msg.Node{}) 25 | } 26 | if len(node.NodeAddress) > 0 { 27 | nodes[serverID].NodeAddress = node.NodeAddress 28 | } 29 | if node.NodeState == msg.NodeState_Unset { 30 | return 31 | } 32 | if node.NodeState == msg.NodeState_Ready && nodes[serverID].NodeState == msg.NodeState_Running { 33 | // 因为消息乱序处理的问题,有可能先处理runining后处理ready 这个时候状态不要切换错了 34 | return 35 | } 36 | nodes[serverID].NodeState = node.NodeState 37 | } 38 | 39 | // GetNode 取得单个节点信息 40 | func GetNode(serverID int32) *msg.Node { 41 | if len(nodes) < int(serverID+1) { 42 | return &msg.Node{} 43 | } 44 | if nodes[serverID] != nil { 45 | return nodes[serverID] 46 | } 47 | return &msg.Node{} 48 | } 49 | 50 | // GetNodeState 取得节点状态 51 | func GetNodeState(serverID int32) msg.NodeState { 52 | if len(nodes) < int(serverID+1) { 53 | return msg.NodeState_Unset 54 | } 55 | if nodes[serverID] == nil { 56 | return msg.NodeState_Unset 57 | } 58 | return nodes[serverID].NodeState 59 | } 60 | 61 | // GetNodes 取得集群信息 62 | func GetNodes() []*msg.Node { 63 | return nodes 64 | } 65 | 66 | // GetGatePublicAddress 网关公网地址 67 | func GetGatePublicAddress(serverID int32) (string, int, int) { 68 | servers := etcmgr.Instance.Servers() 69 | ip := servers[int(serverID)].ServerConfig.GateConfig.Address 70 | port := int(servers[int(serverID)].ServerConfig.GateConfig.Port) 71 | webport := int(servers[int(serverID)].ServerConfig.GateConfig.WebPort) 72 | return ip, port, webport 73 | } 74 | 75 | func init() { 76 | nodes = make([]*msg.Node, 0) 77 | } 78 | -------------------------------------------------------------------------------- /src/dao/dao.player.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "INServer/src/common/dbobj" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/db" 7 | ) 8 | 9 | func AccountQuery(DB *dbobj.DBObject, username string) (*db.DBAccount, error) { 10 | account := new(db.DBAccount) 11 | row := DB.DB().QueryRow("select * from accounts where Name=?", username) 12 | if err := row.Scan(&account.Name, &account.PasswordHash, &account.PlayerUUID); err != nil { 13 | logger.Debug(err) 14 | return nil, err 15 | } 16 | return account, nil 17 | } 18 | 19 | func AccountInsert(DB *dbobj.DBObject, account *db.DBAccount) error { 20 | _, err := DB.DB().Exec("insert INTO accounts(Name,PasswordHash,PlayerUUID) values(?,?,?)", account.Name, account.PasswordHash, account.PlayerUUID) 21 | if err != nil { 22 | logger.Debug(err) 23 | return err 24 | } 25 | return nil 26 | } 27 | 28 | func AccountUpdate(DB *dbobj.DBObject, account *db.DBAccount) error { 29 | _, err := DB.DB().Exec("UPDATE accounts set PasswordHash=? PlayerUUID=? where Name=?", account.PasswordHash, account.PlayerUUID, account.Name) 30 | if err != nil { 31 | logger.Debug(err) 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func PlayerQuery(DB *dbobj.DBObject, uuid string) (*db.DBPlayer, error) { 38 | player := new(db.DBPlayer) 39 | row := DB.DB().QueryRow("select * from players where UUID=?", uuid) 40 | if err := row.Scan(&player.UUID, &player.SerializedData); err != nil { 41 | logger.Debug(err) 42 | return nil, err 43 | } 44 | return player, nil 45 | } 46 | 47 | func PlayerInsert(DB *dbobj.DBObject, player *db.DBPlayer) error { 48 | _, err := DB.DB().Exec("insert INTO players(UUID,SerializedData) values(?,?)", player.UUID, player.SerializedData) 49 | if err != nil { 50 | logger.Debug(err) 51 | return err 52 | } 53 | return nil 54 | } 55 | 56 | func PlayerUpdate(DB *dbobj.DBObject, player *db.DBPlayer) error { 57 | _, err := DB.DB().Exec("UPDATE players set SerializedData=? where UUID=?", player.SerializedData, player.UUID) 58 | if err != nil { 59 | logger.Debug(err) 60 | return err 61 | } 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /src/proto/engine/math.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: math.proto 3 | 4 | package engine 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *Vector2) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *Vector2) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *Vector3) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *Vector3) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | 44 | // MarshalJSON implements json.Marshaler 45 | func (msg *Rect) MarshalJSON() ([]byte, error) { 46 | var buf bytes.Buffer 47 | err := (&jsonpb.Marshaler{ 48 | EnumsAsInts: false, 49 | EmitDefaults: false, 50 | OrigName: false, 51 | }).Marshal(&buf, msg) 52 | return buf.Bytes(), err 53 | } 54 | 55 | // UnmarshalJSON implements json.Unmarshaler 56 | func (msg *Rect) UnmarshalJSON(b []byte) error { 57 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 58 | } 59 | 60 | // MarshalJSON implements json.Marshaler 61 | func (msg *Quaternion) MarshalJSON() ([]byte, error) { 62 | var buf bytes.Buffer 63 | err := (&jsonpb.Marshaler{ 64 | EnumsAsInts: false, 65 | EmitDefaults: false, 66 | OrigName: false, 67 | }).Marshal(&buf, msg) 68 | return buf.Bytes(), err 69 | } 70 | 71 | // UnmarshalJSON implements json.Unmarshaler 72 | func (msg *Quaternion) UnmarshalJSON(b []byte) error { 73 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 74 | } 75 | -------------------------------------------------------------------------------- /src/gameplay/ecs/controller.go: -------------------------------------------------------------------------------- 1 | package ecs 2 | 3 | import ( 4 | "INServer/src/proto/data" 5 | "INServer/src/proto/msg" 6 | "INServer/src/services/node" 7 | 8 | "github.com/gogo/protobuf/proto" 9 | ) 10 | 11 | type ( 12 | // Controller 全部Controller的接口 13 | Controller interface { 14 | OnOtherMove(entity *Entity, ntf *msg.MoveNTF) 15 | OnNearEntities([]*msg.NearEntity) 16 | } 17 | 18 | // DummyController 占位 什么事情也不做 19 | DummyController struct { 20 | entity *Entity 21 | } 22 | // AIController 服务器控制 23 | AIController struct { 24 | entity *Entity 25 | } 26 | // PlayerController 玩家控制 27 | PlayerController struct { 28 | entity *Entity 29 | } 30 | ) 31 | 32 | func initController(entity *Entity) { 33 | switch entity.entityType { 34 | case data.EntityType_MonsterEntity, data.EntityType_NPCEntity: 35 | entity.controller = &AIController{entity} 36 | case data.EntityType_RoleEntity: 37 | entity.controller = &PlayerController{entity} 38 | } 39 | if entity.controller == nil { 40 | entity.controller = &DummyController{entity} 41 | } 42 | } 43 | 44 | func (c *DummyController) OnOtherMove(entity *Entity, ntf *msg.MoveNTF) { 45 | 46 | } 47 | func (c *DummyController) OnNearEntities([]*msg.NearEntity) { 48 | 49 | } 50 | func (c *AIController) OnOtherMove(entity *Entity, ntf *msg.MoveNTF) { 51 | 52 | } 53 | func (c *AIController) OnNearEntities([]*msg.NearEntity) { 54 | 55 | } 56 | func (c *PlayerController) OnOtherMove(entity *Entity, ntf *msg.MoveNTF) { 57 | c.sendMessage(msg.CMD_MOVE_NTF, ntf) 58 | } 59 | func (c *PlayerController) OnNearEntities(items []*msg.NearEntity) { 60 | if len(items) > 1 { 61 | nearEntitiesNTF := &msg.NearEntitiesNTF{ 62 | Entities: items, 63 | } 64 | node.Net.NotifyClient(msg.CMD_NEAR_ENTITIES_NTF, nearEntitiesNTF, c.entity.UUID()) 65 | } 66 | } 67 | 68 | func (c *PlayerController) sendMessage(command msg.CMD, message proto.Message) { 69 | //gate := world.Instance.RoleGate(c.entity.UUID()) 70 | //if gate != global.InvalidServerID { 71 | // if buffer, err := proto.Marshal(message); err == nil { 72 | // forward := &msg.ForwardPlayerMessage{} 73 | // } 74 | // node.Net.NotifyServer(msg.CMD_MOVE_NTF, message, gate) 75 | //} 76 | } 77 | -------------------------------------------------------------------------------- /src/dao/dao.role.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "INServer/src/common/dbobj" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/db" 7 | ) 8 | 9 | func AllRoleSummaryDataQuery(DB *dbobj.DBObject) []*db.DBRole { 10 | rows, err := DB.DB().Query("select UUID, SummaryData from roles") 11 | if err != nil { 12 | logger.Fatal(err) 13 | } 14 | roles := []*db.DBRole{} 15 | for rows.Next() { 16 | role := &db.DBRole{} 17 | rows.Scan(&role.UUID, &role.SummaryData) 18 | roles = append(roles, role) 19 | } 20 | return roles 21 | } 22 | 23 | func RoleOnlineDataQuery(DB *dbobj.DBObject, uuid string) ([]byte, error) { 24 | onlineData := make([]byte, 0) 25 | row := DB.DB().QueryRow("select OnlineData from roles where UUID=?", uuid) 26 | if err := row.Scan(&onlineData); err != nil { 27 | logger.Debug(err) 28 | return nil, err 29 | } 30 | return onlineData, nil 31 | } 32 | 33 | func RoleInsert(DB *dbobj.DBObject, role *db.DBRole) error { 34 | _, err := DB.DB().Exec("insert INTO roles(UUID,SummaryData,OnlineData) values(?,?,?)", role.UUID, role.SummaryData, role.OnlineData) 35 | if err != nil { 36 | logger.Debug(err) 37 | return err 38 | } 39 | return nil 40 | } 41 | 42 | func RoleUpdate(DB *dbobj.DBObject, role *db.DBRole) error { 43 | _, err := DB.DB().Exec("UPDATE roles set SummaryData=? OnlineData=? where UUID=?", role.SummaryData, role.OnlineData, role.UUID) 44 | if err != nil { 45 | logger.Debug(err) 46 | return err 47 | } 48 | return nil 49 | } 50 | 51 | func BulkRoleUpdate(DB *dbobj.DBObject, roles []*db.DBRole) error { 52 | tx, err := DB.DB().Begin() 53 | if err != nil { 54 | logger.Error(err) 55 | return err 56 | } 57 | //stmt, err := tx.Prepare(`UPDATE roles set SummaryData=? OnlineData=? where UUID=?`) 58 | stmt, err := tx.Prepare(`REPLACE into roles (UUID, SummaryData, OnlineData) values(?,?,?)`) 59 | if err != nil { 60 | logger.Error(err) 61 | return err 62 | } 63 | for _, role := range roles { 64 | _, err := stmt.Exec(role.UUID, role.SummaryData, role.OnlineData) 65 | if err != nil { 66 | logger.Error(err) 67 | return err 68 | } 69 | } 70 | err = tx.Commit() 71 | if err != nil { 72 | logger.Error(err) 73 | } 74 | return err 75 | } 76 | -------------------------------------------------------------------------------- /src/common/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "fmt" 6 | "log" 7 | "os" 8 | "runtime/debug" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | const ( 14 | timeFormat string = "2006-01-02T15:04:05.999999+08:00" 15 | ) 16 | 17 | var ( 18 | day = time.Now().Day() 19 | logHandler *log.Logger 20 | id string 21 | 22 | skipstrings = []string{"/stack.go", "debug.Stack", "/protect", "panic", "logger"} 23 | 24 | // IsDebug 是否是调试模式运行的 25 | IsDebug = strings.Contains(os.Args[0], "__debug_bin") 26 | ) 27 | 28 | // Setup 日志初始化 29 | func Setup() { 30 | wd, err := os.Getwd() 31 | if err != nil { 32 | log.Fatalln(err) 33 | } 34 | now := time.Now() 35 | file := wd + "/log/" + fmt.Sprintf("%d-%d%02d%02d.log", int(global.CurrentServerID), now.Year(), now.Month(), now.Day()) 36 | 37 | logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766) 38 | if err != nil { 39 | log.Fatalln(err) 40 | } 41 | logHandler = log.New(logFile, "", 0) 42 | } 43 | 44 | func format(level string, v ...interface{}) string { 45 | now := time.Now().Format(timeFormat) 46 | return fmt.Sprintf("{\"level\":\"%s\", \"@timestamp\":\"%s\", \"message\":\"%s\", \"server\": {\"type\":\"%s\", \"id\":\"%d\"}}", level, now, fmt.Sprint(v...), global.CurrentServerType, global.CurrentServerID) 47 | } 48 | 49 | // Debug 调试日志 50 | func Debug(v ...interface{}) { 51 | log.Println(v...) 52 | logHandler.Println(format("debug", v...)) 53 | PrintStack() 54 | } 55 | 56 | // Info 普通信息日志 57 | func Info(v ...interface{}) { 58 | log.Println(v...) 59 | logHandler.Println(format("info", v...)) 60 | } 61 | 62 | // Error 错误信息日志 63 | func Error(v ...interface{}) { 64 | log.Println(v...) 65 | logHandler.Println(format("error", v...)) 66 | PrintStack() 67 | } 68 | 69 | // Fatal 严重错误日志 70 | func Fatal(v ...interface{}) { 71 | log.Println(v...) 72 | logHandler.Println(format("fatal", v...)) 73 | PrintStack() 74 | } 75 | 76 | // PrintStack 输出调用栈 77 | func PrintStack() { 78 | stack := strings.Split(string(debug.Stack()), "\n") 79 | logstr := "" 80 | for _, str := range stack { 81 | skip := false 82 | for _, skipstr := range skipstrings { 83 | if strings.Contains(str, skipstr) { 84 | skip = true 85 | break 86 | } 87 | } 88 | if skip == false { 89 | logstr = logstr + str 90 | } 91 | } 92 | log.Println(logstr) 93 | logHandler.Println(format("stack", logstr)) 94 | } 95 | -------------------------------------------------------------------------------- /proto/protobuf/source_context.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option java_package = "com.google.protobuf"; 37 | option java_outer_classname = "SourceContextProto"; 38 | option java_multiple_files = true; 39 | option objc_class_prefix = "GPB"; 40 | option go_package = "types"; 41 | 42 | // `SourceContext` represents information about the source of a 43 | // protobuf element, like the file in which it is defined. 44 | message SourceContext { 45 | // The path-qualified name of the .proto file that contained the associated 46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`. 47 | string file_name = 1; 48 | } 49 | -------------------------------------------------------------------------------- /src/services/balcony/balcony.go: -------------------------------------------------------------------------------- 1 | package balcony 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/data" 7 | "INServer/src/proto/msg" 8 | "INServer/src/services/node" 9 | "fmt" 10 | 11 | "github.com/gogo/protobuf/proto" 12 | ) 13 | 14 | // Instance 月台单例 15 | var Instance *Balcony 16 | 17 | // Balcony 月台 负责角色进入游戏世界之前的一些逻辑 18 | type Balcony struct { 19 | } 20 | 21 | // New 构造月台实例 22 | func New() *Balcony { 23 | b := new(Balcony) 24 | b.initMessageHandler() 25 | return b 26 | } 27 | 28 | // Start 服务启动 29 | func (b *Balcony) Start() { 30 | 31 | } 32 | 33 | // Stop 服务停止 34 | func (b *Balcony) Stop() { 35 | 36 | } 37 | 38 | func (b *Balcony) initMessageHandler() { 39 | node.Net.Listen(msg.CMD_ROLE_ENTER, b.HANDLE_ROLE_ENTER) 40 | } 41 | 42 | func (b *Balcony) HANDLE_ROLE_ENTER(header *msg.MessageHeader, buffer []byte) { 43 | roleEnterResp := &msg.RoleEnterResp{} 44 | defer node.Net.Responce(header, roleEnterResp) 45 | roleEnterReq := &msg.RoleEnterReq{} 46 | err := proto.Unmarshal(buffer, roleEnterReq) 47 | if err != nil { 48 | logger.Info(err) 49 | return 50 | } 51 | 52 | loadRoleReq := &msg.LoadRoleReq{ 53 | RoleUUID: roleEnterReq.RoleUUID, 54 | } 55 | loadRoleResp := &msg.LoadRoleResp{} 56 | if err := node.Net.Request(msg.CMD_GD_LOAD_ROLE_REQ, loadRoleReq, loadRoleResp); err != nil { 57 | logger.Info(err) 58 | return 59 | } 60 | logger.Info(fmt.Sprintf("加载角色 %s", roleEnterReq.RoleUUID)) 61 | roleEnterResp.Success = loadRoleResp.Success 62 | roleEnterResp.Role = loadRoleResp.Role 63 | if loadRoleResp.Success { 64 | getMapIDReq := &msg.GetMapIDReq{ 65 | MapUUID: loadRoleResp.MapUUID, 66 | } 67 | getMapIDResp := &msg.GetMapIDResp{} 68 | err := node.Net.RequestServer(msg.CMD_GET_MAP_ID, getMapIDReq, getMapIDResp, loadRoleResp.WorldID) 69 | if err != nil { 70 | logger.Info(err) 71 | return 72 | } 73 | roleEnterResp.MapID = getMapIDResp.MapID 74 | roleEnterResp.WorldID = loadRoleResp.WorldID 75 | 76 | updateRoleAddressNTF := &msg.UpdateRoleAddressNTF{ 77 | RoleUUID: roleEnterReq.RoleUUID, 78 | Address: &data.RoleAddress{ 79 | Gate: global.InvalidServerID, 80 | World: loadRoleResp.WorldID, 81 | }, 82 | } 83 | node.Net.Notify(msg.CMD_UPDATE_ROLE_ADDRESS_NTF, updateRoleAddressNTF) 84 | 85 | roleEnterNTF := &msg.RoleEnterNTF{ 86 | Gate: header.From, 87 | Role: loadRoleResp.Role, 88 | } 89 | node.Net.NotifyServer(msg.CMD_ROLE_ENTER, roleEnterNTF, loadRoleResp.WorldID) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/services/cluster/running.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/proto/etc" 6 | "INServer/src/proto/msg" 7 | "INServer/src/services/etcmgr" 8 | ) 9 | 10 | var ( 11 | running []int32 12 | gates []int32 13 | database int32 14 | gps int32 15 | balcony int32 16 | zones []*etc.Zone 17 | ) 18 | 19 | // RunningGates 处于Running状态下的门服务器 20 | func RunningGates() []int32 { 21 | return gates 22 | } 23 | 24 | // RunningDatabase 处于Running状态下的数据库服务器 25 | func RunningDatabase() int32 { 26 | return database 27 | } 28 | 29 | // RunningGPS 处于Running状态下的定位服务器 30 | func RunningGPS() int32 { 31 | return gps 32 | } 33 | 34 | // RunningBalcony 处于Runing状态下的月台服务器 35 | func RunningBalcony() int32 { 36 | return balcony 37 | } 38 | 39 | // RefreshRunning 刷新活跃中的服务器 40 | func RefreshRunning() { 41 | running = make([]int32, 0) 42 | gates = make([]int32, 0) 43 | database = global.InvalidServerID 44 | gps = global.InvalidServerID 45 | balcony = global.InvalidServerID 46 | for serverID, info := range nodes { 47 | serverType := etcmgr.Instance.GetServerType(int32(serverID)) 48 | if info.NodeState == msg.NodeState_Running { 49 | running = append(running, int32(serverID)) 50 | if serverType == global.GateServer { 51 | gates = append(gates, int32(serverID)) 52 | } else if serverType == global.DatabaseServer { 53 | database = int32(serverID) 54 | } else if serverType == global.GPSServer { 55 | gps = int32(serverID) 56 | } else if serverType == global.BalconyServer { 57 | balcony = int32(serverID) 58 | } 59 | } 60 | } 61 | } 62 | 63 | // RefreshRunningZones 刷新游戏区状态 64 | func RefreshRunningZones() { 65 | zones = make([]*etc.Zone, 0) 66 | for _, zone := range etcmgr.Instance.Zones() { 67 | realZone := &etc.Zone{} 68 | realZone.ZoneID = zone.ZoneID 69 | realZone.Name = zone.Name 70 | realZone.State = zone.State 71 | if zone.State != global.ZoneStateClosed { 72 | servers := etcmgr.Instance.GetZoneLocation(zone.ZoneID) 73 | for _, serverID := range servers { 74 | if GetNodeState(serverID) != msg.NodeState_Running { 75 | realZone.State = global.ZoneStateClosed 76 | break 77 | } 78 | } 79 | } 80 | zones = append(zones, realZone) 81 | } 82 | } 83 | 84 | // RunningCount 运行中服务器数量 85 | func RunningCount() int { 86 | return len(running) 87 | } 88 | 89 | func init() { 90 | running = make([]int32, 0) 91 | gates = make([]int32, 0) 92 | database = global.InvalidServerID 93 | gps = global.InvalidServerID 94 | balcony = global.InvalidServerID 95 | zones = make([]*etc.Zone, 0) 96 | } 97 | -------------------------------------------------------------------------------- /proto/protobuf/empty.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "types"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "EmptyProto"; 39 | option java_multiple_files = true; 40 | option objc_class_prefix = "GPB"; 41 | option cc_enable_arenas = true; 42 | 43 | // A generic empty message that you can re-use to avoid defining duplicated 44 | // empty messages in your APIs. A typical example is to use it as the request 45 | // or the response type of an API method. For instance: 46 | // 47 | // service Foo { 48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 49 | // } 50 | // 51 | // The JSON representation for `Empty` is empty JSON object `{}`. 52 | message Empty {} 53 | -------------------------------------------------------------------------------- /src/common/uuid/uuid.go: -------------------------------------------------------------------------------- 1 | // 参考了github.com/satori/go.uuid v1的代码,进行了一些修改减少了位数,可以在服务器群中生成一个唯一的UUID 2 | package uuid 3 | 4 | import ( 5 | "INServer/src/common/global" 6 | "crypto/rand" 7 | "encoding/binary" 8 | "errors" 9 | "io" 10 | "net" 11 | "strconv" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | const epochStart = 122192928000000000 17 | 18 | type epochFunc func() time.Time 19 | type hwAddrFunc func() (net.HardwareAddr, error) 20 | 21 | type ( 22 | rfc4122Generator struct { 23 | clockSequenceOnce sync.Once 24 | hardwareAddrOnce sync.Once 25 | storageMutex sync.Mutex 26 | 27 | rand io.Reader 28 | 29 | epochFunc epochFunc 30 | hwAddrFunc hwAddrFunc 31 | lastTime uint64 32 | clockSequence uint16 33 | hardwareAddr [6]byte 34 | } 35 | ) 36 | 37 | var generator = newRFC4122Generator() 38 | 39 | func newRFC4122Generator() *rfc4122Generator { 40 | return &rfc4122Generator{ 41 | epochFunc: time.Now, 42 | hwAddrFunc: defaultHWAddrFunc, 43 | rand: rand.Reader, 44 | } 45 | } 46 | 47 | func New() string { 48 | u := make([]byte, 8) 49 | timeNow, clockSeq, _ := generator.getClockSequence() 50 | binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) 51 | binary.BigEndian.PutUint16(u[4:], clockSeq) 52 | binary.BigEndian.PutUint16(u[6:], uint16(global.CurrentServerID)) 53 | 54 | return strconv.FormatUint(binary.BigEndian.Uint64(u), 36) 55 | } 56 | 57 | func (g *rfc4122Generator) getClockSequence() (uint64, uint16, error) { 58 | var err error 59 | g.clockSequenceOnce.Do(func() { 60 | buf := make([]byte, 2) 61 | if _, err = io.ReadFull(g.rand, buf); err != nil { 62 | return 63 | } 64 | g.clockSequence = binary.BigEndian.Uint16(buf) 65 | }) 66 | if err != nil { 67 | return 0, 0, err 68 | } 69 | 70 | g.storageMutex.Lock() 71 | defer g.storageMutex.Unlock() 72 | 73 | timeNow := g.getEpoch() 74 | // Clock didn't change since last UUID generation. 75 | // Should increase clock sequence. 76 | if timeNow <= g.lastTime { 77 | g.clockSequence++ 78 | } 79 | g.lastTime = timeNow 80 | 81 | return timeNow, g.clockSequence, nil 82 | } 83 | 84 | func (g *rfc4122Generator) getEpoch() uint64 { 85 | return epochStart + uint64(g.epochFunc().UnixNano()/100) 86 | } 87 | 88 | func defaultHWAddrFunc() (net.HardwareAddr, error) { 89 | ifaces, err := net.Interfaces() 90 | if err != nil { 91 | return []byte{}, err 92 | } 93 | for _, iface := range ifaces { 94 | if len(iface.HardwareAddr) >= 6 { 95 | return iface.HardwareAddr, nil 96 | } 97 | } 98 | return []byte{}, errors.New("uuid: no HW address found") 99 | } 100 | -------------------------------------------------------------------------------- /src/proto/data/data.role.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.role.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *RoleSummaryData) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *RoleSummaryData) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *RoleAddress) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *RoleAddress) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | 44 | // MarshalJSON implements json.Marshaler 45 | func (msg *RoleOnlineData) MarshalJSON() ([]byte, error) { 46 | var buf bytes.Buffer 47 | err := (&jsonpb.Marshaler{ 48 | EnumsAsInts: false, 49 | EmitDefaults: false, 50 | OrigName: false, 51 | }).Marshal(&buf, msg) 52 | return buf.Bytes(), err 53 | } 54 | 55 | // UnmarshalJSON implements json.Unmarshaler 56 | func (msg *RoleOnlineData) UnmarshalJSON(b []byte) error { 57 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 58 | } 59 | 60 | // MarshalJSON implements json.Marshaler 61 | func (msg *RoleRealtimeData) MarshalJSON() ([]byte, error) { 62 | var buf bytes.Buffer 63 | err := (&jsonpb.Marshaler{ 64 | EnumsAsInts: false, 65 | EmitDefaults: false, 66 | OrigName: false, 67 | }).Marshal(&buf, msg) 68 | return buf.Bytes(), err 69 | } 70 | 71 | // UnmarshalJSON implements json.Unmarshaler 72 | func (msg *RoleRealtimeData) UnmarshalJSON(b []byte) error { 73 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 74 | } 75 | 76 | // MarshalJSON implements json.Marshaler 77 | func (msg *Role) MarshalJSON() ([]byte, error) { 78 | var buf bytes.Buffer 79 | err := (&jsonpb.Marshaler{ 80 | EnumsAsInts: false, 81 | EmitDefaults: false, 82 | OrigName: false, 83 | }).Marshal(&buf, msg) 84 | return buf.Bytes(), err 85 | } 86 | 87 | // UnmarshalJSON implements json.Unmarshaler 88 | func (msg *Role) UnmarshalJSON(b []byte) error { 89 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 90 | } 91 | -------------------------------------------------------------------------------- /src/engine/grid/grid.go: -------------------------------------------------------------------------------- 1 | package grid 2 | 3 | import ( 4 | "INServer/src/proto/engine" 5 | "INServer/src/proto/msg" 6 | ) 7 | 8 | type ( 9 | Grid struct { 10 | grids [][]*msg.NearEntity 11 | gridSize float64 12 | width int32 13 | height int32 14 | } 15 | ) 16 | 17 | func New(gridSize float64, width int32, height int32) *Grid { 18 | g := new(Grid) 19 | g.grids = make([][]*msg.NearEntity, width*height) 20 | g.gridSize = gridSize 21 | g.width = width 22 | g.height = height 23 | return g 24 | } 25 | 26 | func (g *Grid) Add(uuid string, position *engine.Vector3) { 27 | x := int32(position.X / g.gridSize) 28 | z := int32(position.Z / g.gridSize) 29 | index := g.getGirdIndex(x, z) 30 | if g.grids[index] == nil { 31 | g.grids[index] = make([]*msg.NearEntity, 0) 32 | } 33 | g.grids[index] = append(g.grids[index], &msg.NearEntity{ 34 | EntityUUID: uuid, 35 | Position: position, 36 | }) 37 | } 38 | 39 | func (g *Grid) Remove(uuid string, position *engine.Vector3) { 40 | x := int32(position.X / g.gridSize) 41 | z := int32(position.Z / g.gridSize) 42 | index := g.getGirdIndex(x, z) 43 | if g.grids[index] == nil { 44 | g.grids[index] = make([]*msg.NearEntity, 0) 45 | } 46 | for i, item := range g.grids[index] { 47 | if item.EntityUUID == uuid { 48 | g.grids[index] = append(g.grids[index][:i], g.grids[index][i+1:]...) 49 | break 50 | } 51 | } 52 | } 53 | 54 | func (g *Grid) Move(uuid string, from *engine.Vector3, to *engine.Vector3) { 55 | fromX := int32(from.X / g.gridSize) 56 | fromZ := int32(to.Z / g.gridSize) 57 | toX := int32(to.X / g.gridSize) 58 | toZ := int32(to.Z / g.gridSize) 59 | if fromX == toX && fromZ == toZ { 60 | return 61 | } 62 | g.Remove(uuid, from) 63 | g.Add(uuid, to) 64 | } 65 | 66 | func (g *Grid) GetNearItems(center *engine.Vector3) []*msg.NearEntity { 67 | x := int32(center.X / g.gridSize) 68 | z := int32(center.Z / g.gridSize) 69 | items := make([]*msg.NearEntity, 0) 70 | items = append(items, g.getGridItems(x, z)...) 71 | items = append(items, g.getGridItems(x-1, z-1)...) 72 | items = append(items, g.getGridItems(x-1, z)...) 73 | items = append(items, g.getGridItems(x-1, z+1)...) 74 | items = append(items, g.getGridItems(x, z-1)...) 75 | items = append(items, g.getGridItems(x, z+1)...) 76 | items = append(items, g.getGridItems(x+1, z-1)...) 77 | items = append(items, g.getGridItems(x+1, z)...) 78 | items = append(items, g.getGridItems(x+1, z+1)...) 79 | return items 80 | } 81 | 82 | func (g *Grid) getGirdIndex(x int32, z int32) int32 { 83 | return (z+g.height/2)*g.width + (x + g.width/2) 84 | } 85 | 86 | func (g *Grid) getGridItems(x int32, z int32) []*msg.NearEntity { 87 | if x >= 0 && z >= 0 { 88 | index := g.getGirdIndex(x, z) 89 | if index < int32(len(g.grids)) { 90 | return g.grids[index] 91 | } 92 | } 93 | return make([]*msg.NearEntity, 0) 94 | } 95 | -------------------------------------------------------------------------------- /src/services/node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/msg" 7 | "INServer/src/services/cluster" 8 | "INServer/src/services/etcmgr" 9 | "INServer/src/services/innet" 10 | "time" 11 | 12 | "github.com/golang/protobuf/proto" 13 | ) 14 | 15 | var ( 16 | Instance *Node 17 | Net *innet.INNet 18 | ) 19 | 20 | type ( 21 | Node struct { 22 | } 23 | ) 24 | 25 | func New() *Node { 26 | n := new(Node) 27 | Net = innet.New() 28 | n.registerListeners() 29 | Net.Start() 30 | return n 31 | } 32 | 33 | // Prepare 节点进入Ready状态 这个状态之后可以收到集群的状态信息了 34 | func (n *Node) Prepare() { 35 | ntf := &msg.NodesInfoNTF{} 36 | ntf.Nodes = make([]*msg.Node, 0) 37 | for index := 0; index < len(etcmgr.Instance.Servers()); index++ { 38 | ntf.Nodes = append(ntf.Nodes, &msg.Node{ 39 | NodeState: msg.NodeState_Unset, 40 | NodeAddress: nil, 41 | }) 42 | } 43 | ntf.Nodes[global.CurrentServerID] = &msg.Node{ 44 | NodeState: msg.NodeState_Ready, 45 | NodeAddress: Net.IP, 46 | } 47 | Net.NotifyServer(msg.CMD_NODES_INFO_NTF, ntf, global.CenterID) 48 | } 49 | 50 | // Start 节点进入Running状态 工作状态 51 | func (n *Node) Start() { 52 | ntf := &msg.NodesInfoNTF{} 53 | ntf.Nodes = make([]*msg.Node, 0) 54 | for index := 0; index < len(etcmgr.Instance.Servers()); index++ { 55 | ntf.Nodes = append(ntf.Nodes, &msg.Node{ 56 | NodeState: msg.NodeState_Unset, 57 | NodeAddress: nil, 58 | }) 59 | } 60 | ntf.Nodes[global.CurrentServerID] = &msg.Node{ 61 | NodeState: msg.NodeState_Running, 62 | NodeAddress: Net.IP, 63 | } 64 | Net.NotifyServer(msg.CMD_NODES_INFO_NTF, ntf, global.CenterID) 65 | n.keepAlive() 66 | } 67 | 68 | func (n *Node) registerListeners() { 69 | Net.Listen(msg.CMD_NODES_INFO_NTF, n.HANDLE_NODES_INFO_NTF) 70 | Net.Listen(msg.CMD_ETC_SYNC_NTF, etcmgr.Instance.HANDLE_ETC_SYNC_NTF) 71 | Net.Listen(msg.CMD_RESET_CONNECTION_NTF, n.HANDLE_RESET_CONNECTION_NTF) 72 | } 73 | 74 | func (n *Node) HANDLE_NODES_INFO_NTF(header *msg.MessageHeader, buffer []byte) { 75 | ntf := &msg.NodesInfoNTF{} 76 | err := proto.Unmarshal(buffer, ntf) 77 | if err != nil { 78 | logger.Error(err) 79 | return 80 | } 81 | cluster.SetNodes(ntf.Nodes) 82 | Net.RefreshNodesAddress() 83 | } 84 | 85 | func (n *Node) HANDLE_RESET_CONNECTION_NTF(header *msg.MessageHeader, buffer []byte) { 86 | ntf := &msg.ResetConnectionNTF{} 87 | err := proto.Unmarshal(buffer, ntf) 88 | if err != nil { 89 | logger.Error(err) 90 | return 91 | } 92 | Net.ResetServer(ntf.ServerID) 93 | } 94 | 95 | func (n *Node) keepAlive() { 96 | go func() { 97 | for { 98 | info := &msg.KeepAlive{ 99 | ServerID: global.CurrentServerID, 100 | } 101 | Net.NotifyServer(msg.CMD_KEEP_ALIVE, info, 0) 102 | time.Sleep(time.Second) 103 | } 104 | }() 105 | } 106 | -------------------------------------------------------------------------------- /src/dao/dao.map.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "INServer/src/common/dbobj" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/db" 7 | ) 8 | 9 | func AllStaticMapQuery(DB *dbobj.DBObject) []*db.DBStaticMap { 10 | rows, err := DB.DB().Query("select ZoneID, MapID, UUID, SerializedData from staticmaps") 11 | if err != nil { 12 | logger.Fatal(err) 13 | } 14 | staticMaps := []*db.DBStaticMap{} 15 | for rows.Next() { 16 | staticMap := &db.DBStaticMap{} 17 | rows.Scan(&staticMap.ZoneID, &staticMap.MapID, &staticMap.UUID, &staticMap.SerializedData) 18 | staticMaps = append(staticMaps, staticMap) 19 | } 20 | return staticMaps 21 | } 22 | 23 | func StaticMapInsert(DB *dbobj.DBObject, staticMap *db.DBStaticMap) error { 24 | _, err := DB.DB().Exec("insert INTO staticmaps(ZoneID,MapID,UUID,SerializedData) values(?,?,?,?)", staticMap.ZoneID, staticMap.MapID, staticMap.UUID, staticMap.SerializedData) 25 | if err != nil { 26 | logger.Error(err) 27 | return err 28 | } 29 | return nil 30 | } 31 | 32 | func StaticMapUpdate(DB *dbobj.DBObject, staticMap *db.DBStaticMap) error { 33 | _, err := DB.DB().Exec("UPDATE staticmaps set SerializedData=? where UUID=?", staticMap.SerializedData, staticMap.UUID) 34 | if err != nil { 35 | logger.Error(err) 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func BulkStaticMapUpdate(DB *dbobj.DBObject, staticMaps []*db.DBStaticMap) error { 42 | tx, err := DB.DB().Begin() 43 | if err != nil { 44 | logger.Error(err) 45 | return err 46 | } 47 | stmt, err := tx.Prepare(`UPDATE staticmaps set SerializedData=? where UUID=?`) 48 | if err != nil { 49 | logger.Error(err) 50 | return err 51 | } 52 | for _, staticMap := range staticMaps { 53 | _, err := stmt.Exec(staticMap.SerializedData, staticMap.UUID) 54 | if err != nil { 55 | logger.Error(err) 56 | return err 57 | } 58 | } 59 | err = tx.Commit() 60 | if err != nil { 61 | logger.Error(err) 62 | } 63 | return err 64 | } 65 | 66 | func DynamicMapQuery(DB *dbobj.DBObject, uuid string) (*db.DBDynamicMap, error) { 67 | dynamicMap := new(db.DBDynamicMap) 68 | row := DB.DB().QueryRow("select * from dynamicmaps where UUID=?", uuid) 69 | if err := row.Scan(&dynamicMap.UUID, &dynamicMap.SerializedData); err != nil { 70 | logger.Debug(err) 71 | return nil, err 72 | } 73 | return dynamicMap, nil 74 | } 75 | 76 | func DynamicMapInsert(DB *dbobj.DBObject, dynamicMap *db.DBDynamicMap) error { 77 | _, err := DB.DB().Exec("insert INTO dynamicmaps(UUID,SerializedData) values(?,?)", dynamicMap.UUID, dynamicMap.SerializedData) 78 | if err != nil { 79 | logger.Debug(err) 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | func DynamicMapUpdate(DB *dbobj.DBObject, dynamicMap *db.DBDynamicMap) error { 86 | _, err := DB.DB().Exec("UPDATE dynamicmaps set SerializedData=? where UUID=?", dynamicMap.SerializedData, dynamicMap.UUID) 87 | if err != nil { 88 | logger.Debug(err) 89 | return err 90 | } 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /src/services/innet/sender.go: -------------------------------------------------------------------------------- 1 | package innet 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/msg" 7 | "encoding/binary" 8 | "net" 9 | "time" 10 | 11 | "github.com/golang/protobuf/proto" 12 | "github.com/gorilla/websocket" 13 | ) 14 | 15 | type ( 16 | sender struct { 17 | innet *INNet 18 | } 19 | ) 20 | 21 | func newSender(innet *INNet) *sender { 22 | s := new(sender) 23 | s.innet = innet 24 | return s 25 | } 26 | 27 | func SendUDPBytesHelper(addr *net.UDPAddr, bytes []byte) error { 28 | sizebuf := make([]byte, 2) 29 | binary.BigEndian.PutUint16(sizebuf, uint16(len(bytes))) 30 | _, err := udpconn.WriteToUDP(append(sizebuf, bytes...), addr) 31 | if err != nil { 32 | logger.Debug(err) 33 | } 34 | return err 35 | } 36 | 37 | func SendBytesHelper(conn net.Conn, bytes []byte) error { 38 | sizebuf := make([]byte, 2) 39 | binary.BigEndian.PutUint16(sizebuf, uint16(len(bytes))) 40 | _, err := conn.Write(append(sizebuf, bytes...)) 41 | if err != nil { 42 | logger.Debug(err) 43 | } 44 | return err 45 | } 46 | 47 | func SendWebBytesHelper(conn *websocket.Conn, bytes []byte) error { 48 | sizebuf := make([]byte, 2) 49 | binary.BigEndian.PutUint16(sizebuf, uint16(len(bytes))) 50 | err := conn.WriteMessage(websocket.BinaryMessage, append(sizebuf, bytes...)) 51 | if err != nil { 52 | logger.Debug(err) 53 | } 54 | return err 55 | } 56 | 57 | func (s *sender) send(svr *server, buffer []byte) error { 58 | svr.packageID++ 59 | slices := cutslices(buffer) 60 | var packages []*msg.Package 61 | total := int32(len(slices)) 62 | for index, slice := range slices { 63 | packages = append(packages, &msg.Package{ 64 | UniqueID: svr.packageID, 65 | From: int32(global.CurrentServerID), 66 | Index: int32(index), 67 | Total: total, 68 | Buffer: slice, 69 | }) 70 | } 71 | s.sendPackages(svr.addr, packages) 72 | 73 | s.innet.retry.addPackagesCache(svr.packageID, &packageCache{ 74 | addr: svr.addr, 75 | packages: packages, 76 | retryTime: time.Now().UnixNano() + timeout, 77 | toServerID: svr.id, 78 | }) 79 | return nil 80 | } 81 | 82 | func (s *sender) start(svr *server, buffer []byte) { 83 | var packages []*msg.Package 84 | packages = append(packages, &msg.Package{ 85 | UniqueID: 0, 86 | From: int32(global.CurrentServerID), 87 | Index: 0, 88 | Total: 1, 89 | Buffer: buffer, 90 | }) 91 | s.sendPackages(svr.addr, packages) 92 | } 93 | 94 | func (s *sender) ack(addr *net.UDPAddr, pkg *msg.Package) { 95 | buffer, _ := proto.Marshal(&msg.Package{ 96 | UniqueID: pkg.UniqueID, 97 | From: int32(global.CurrentServerID), 98 | Index: pkg.Index, 99 | Total: pkg.Total, 100 | }) 101 | SendUDPBytesHelper(addr, buffer) 102 | } 103 | 104 | func (s *sender) sendPackages(addr *net.UDPAddr, packages []*msg.Package) { 105 | for _, pkg := range packages { 106 | buf, _ := proto.Marshal(pkg) 107 | SendUDPBytesHelper(addr, buf) 108 | } 109 | } 110 | 111 | func cutslices(buffer []byte) [][]byte { 112 | var slices [][]byte 113 | from := 0 114 | for len(buffer[from:]) > sliceSize { 115 | to := from + sliceSize 116 | slices = append(slices, buffer[from:to]) 117 | from = to 118 | } 119 | slices = append(slices, buffer[from:]) 120 | return slices 121 | } 122 | -------------------------------------------------------------------------------- /src/lifetime/startup/startup.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/common/util" 7 | "INServer/src/services/ai" 8 | "INServer/src/services/balcony" 9 | "INServer/src/services/center" 10 | "INServer/src/services/cluster" 11 | "INServer/src/services/database" 12 | "INServer/src/services/etcmgr" 13 | "INServer/src/services/gate" 14 | "INServer/src/services/gps" 15 | "INServer/src/services/login" 16 | "INServer/src/services/node" 17 | "INServer/src/services/robot" 18 | "INServer/src/services/web" 19 | "INServer/src/services/world" 20 | "fmt" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | // Run 服务器启动流程 26 | func Run() { 27 | if global.CurrentServerID == global.CenterID { 28 | go startCenter() 29 | } else { 30 | go startNode() 31 | } 32 | } 33 | 34 | func startCenter() { 35 | global.CurrentServerType = global.CenterServer 36 | util.SetProcessName(fmt.Sprintf("%s@in-%d ", strings.ToLower(global.CurrentServerType), global.CurrentServerID)) 37 | etcmgr.Instance = etcmgr.New() 38 | global.CurrentServerConfig = etcmgr.Instance.GetServerConfig(global.CurrentServerID) 39 | center.Instance = center.New() 40 | center.Instance.Start() 41 | } 42 | 43 | func startNode() { 44 | etcmgr.Instance = etcmgr.New() 45 | node.Instance = node.New() 46 | for { 47 | if etcmgr.Instance.OK() { 48 | break 49 | } else { 50 | node.Net.SendNodeStartNTF() 51 | logger.Info("等待中心服启动完成...") 52 | time.Sleep(time.Second) 53 | } 54 | } 55 | 56 | global.CurrentServerType = etcmgr.Instance.GetServerType(global.CurrentServerID) 57 | global.CurrentServerConfig = etcmgr.Instance.GetServerConfig(global.CurrentServerID) 58 | util.SetProcessName(fmt.Sprintf("%s@in-%d ", strings.ToLower(global.CurrentServerType), global.CurrentServerID)) 59 | node.Instance.Prepare() 60 | startServer() 61 | node.Instance.Start() 62 | logger.Info(fmt.Sprintf("服务器启动完成 ID:%d Type:%s", global.CurrentServerID, global.CurrentServerType)) 63 | } 64 | 65 | func startServer() { 66 | switch global.CurrentServerType { 67 | case global.GateServer: 68 | gate.Instance = gate.New() 69 | gate.Instance.Start() 70 | break 71 | case global.LoginServer: 72 | login.Instance = login.New() 73 | login.Instance.Start() 74 | case global.DatabaseServer: 75 | database.Instance = database.New() 76 | database.Instance.Start() 77 | case global.WebServer: 78 | web.Instance = web.New() 79 | web.Instance.Start() 80 | case global.WorldServer: 81 | util.Wait(func() bool { 82 | return cluster.RunningDatabase() != global.InvalidServerID 83 | }, "等待数据库服务器启动完成...", time.Second) 84 | util.Wait(func() bool { 85 | return cluster.RunningGPS() != global.InvalidServerID 86 | }, "等待定位服务器启动完成...", time.Second) 87 | world.Instance = world.New() 88 | world.Instance.Start() 89 | case global.GPSServer: 90 | gps.Instance = gps.New() 91 | gps.Instance.Start() 92 | case global.BalconyServer: 93 | balcony.Instance = balcony.New() 94 | balcony.Instance.Start() 95 | case global.AIServer: 96 | ai.Instance = ai.New() 97 | ai.Instance.Start() 98 | case global.RobotServer: 99 | robot.Instance = robot.New() 100 | robot.Instance.Start() 101 | default: 102 | logger.Fatal("不支持的服务器类型:" + global.CurrentServerType) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/services/innet/address.go: -------------------------------------------------------------------------------- 1 | package innet 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/msg" 7 | "INServer/src/services/cluster" 8 | "fmt" 9 | "net" 10 | "strconv" 11 | ) 12 | 13 | type ( 14 | server struct { 15 | addr *net.UDPAddr 16 | packageID uint64 17 | id int32 18 | } 19 | 20 | address struct { 21 | innet *INNet 22 | servers map[int32]*server 23 | center *server 24 | } 25 | ) 26 | 27 | func newAddress(innet *INNet) *address { 28 | a := new(address) 29 | a.innet = innet 30 | a.servers = make(map[int32]*server) 31 | 32 | addr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:"+strconv.Itoa(int(global.CenterID)+recvport)) 33 | if err != nil { 34 | logger.Fatal(err) 35 | } 36 | a.center = &server{ 37 | addr: addr, 38 | packageID: 0, 39 | id: global.CenterID, 40 | } 41 | return a 42 | } 43 | 44 | func (a *address) refresh() { 45 | servers := cluster.GetNodes() 46 | for serverID, info := range servers { 47 | if info.NodeAddress != nil && len(info.NodeAddress) > 0 { 48 | var svr *server 49 | var ok = false 50 | if svr, ok = a.servers[int32(serverID)]; ok == false { 51 | svr = &server{ 52 | packageID: 0, 53 | id: int32(serverID), 54 | } 55 | a.servers[svr.id] = svr 56 | } 57 | ip := &net.IPAddr{IP: info.NodeAddress} 58 | addr := &net.UDPAddr{IP: ip.IP, Port: serverID + recvport, Zone: ip.Zone} 59 | svr.addr = addr 60 | } 61 | } 62 | } 63 | 64 | func (a *address) resetServer(serverID int32) { 65 | if svr, ok := a.servers[serverID]; ok { 66 | svr.packageID = 0 67 | } 68 | } 69 | 70 | func (a *address) getByCommand(command msg.CMD) *server { 71 | switch command { 72 | case msg.CMD_KEEP_ALIVE, msg.CMD_RELOAD_ETC_REQ: 73 | return a.center 74 | case msg.CMD_LD_CREATE_PLAYER_REQ, 75 | msg.CMD_GD_LOAD_PLAYER_REQ, 76 | msg.CMD_GD_RELEASE_PLAYER_NTF, 77 | msg.CMD_GD_CREATE_ROLE_REQ, 78 | msg.CMD_GD_LOAD_ROLE_REQ, 79 | msg.CMD_SAVE_ROLE_REQ, 80 | msg.CMD_LOAD_STATIC_MAP_REQ, 81 | msg.CMD_SAVE_STATIC_MAP_REQ: 82 | serverID := cluster.RunningDatabase() 83 | if serverID != global.InvalidServerID { 84 | if svr, ok := a.servers[serverID]; ok { 85 | return svr 86 | } 87 | } 88 | logger.Error(fmt.Sprintf("没有找到Database服务器 %d", serverID)) 89 | break 90 | case msg.CMD_GET_MAP_ADDRESS_REQ, 91 | msg.CMD_GET_STATIC_MAP_UUID_REQ, 92 | msg.CMD_UPDATE_MAP_ADDRESS_NTF, 93 | msg.CMD_UPDATE_ROLE_ADDRESS_NTF, 94 | msg.CMD_REMOVE_ROLE_ADDRESS_NTF, 95 | msg.CMD_UPDATE_STATIC_MAP_UUID_NTF: 96 | serverID := cluster.RunningGPS() 97 | if serverID != global.InvalidServerID { 98 | if svr, ok := a.servers[serverID]; ok { 99 | return svr 100 | } 101 | } 102 | logger.Error(fmt.Sprintf("没有找到GPS服务器 %d", serverID)) 103 | break 104 | case msg.CMD_ROLE_ENTER: 105 | serverID := cluster.RunningBalcony() 106 | if serverID != global.InvalidServerID { 107 | if svr, ok := a.servers[serverID]; ok { 108 | return svr 109 | } 110 | } 111 | logger.Error(fmt.Sprintf("没有找到月台服务器 %d", serverID)) 112 | break 113 | } 114 | return nil 115 | } 116 | 117 | func (a *address) getByServerID(serverID int32) *server { 118 | if serverID == global.CenterID { 119 | return a.center 120 | } else if info, ok := a.servers[serverID]; ok { 121 | return info 122 | } 123 | 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /src/services/web/web.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/common/util" 7 | "INServer/src/proto/msg" 8 | "INServer/src/services/etcmgr" 9 | "INServer/src/services/node" 10 | "encoding/base64" 11 | "encoding/json" 12 | "fmt" 13 | "html/template" 14 | "io" 15 | "net/http" 16 | "os" 17 | "strconv" 18 | "strings" 19 | ) 20 | 21 | //定义全局的模板变量 22 | var webhtml *template.Template 23 | var Instance *Web 24 | 25 | type ( 26 | Web struct { 27 | } 28 | ) 29 | 30 | func New() *Web { 31 | w := new(Web) 32 | return w 33 | } 34 | 35 | func (w *Web) Start() { 36 | http.HandleFunc("/", w.root) 37 | http.HandleFunc("/zones", w.zones) 38 | http.HandleFunc("/reloadetc", w.reloadetc) 39 | dir, err := os.Getwd() 40 | if err != nil { 41 | logger.Fatal(err) 42 | } 43 | certFile := fmt.Sprintf("%s/web/server.crt", dir) 44 | keyFile := fmt.Sprintf("%s/web/server.key", dir) 45 | if util.PathExists(certFile) && util.PathExists(keyFile) { 46 | go http.ListenAndServeTLS(":"+strconv.Itoa(int(global.CurrentServerConfig.WebConfig.Port)), certFile, keyFile, nil) 47 | } 48 | go http.ListenAndServe(":"+strconv.Itoa(int(global.CurrentServerConfig.WebConfig.Port)), nil) 49 | } 50 | 51 | func (w *Web) checkauth(writer http.ResponseWriter, req *http.Request) bool { 52 | auth := req.Header.Get("Authorization") 53 | if auth == "" { 54 | writer.Header().Set("WWW-Authenticate", `Basic realm="Dotcoo User Login"`) 55 | writer.WriteHeader(http.StatusUnauthorized) 56 | return false 57 | } 58 | auths := strings.SplitN(auth, " ", 2) 59 | if len(auths) != 2 { 60 | fmt.Println("error") 61 | return false 62 | } 63 | authMethod := auths[0] 64 | authB64 := auths[1] 65 | switch authMethod { 66 | case "Basic": 67 | authstr, err := base64.StdEncoding.DecodeString(authB64) 68 | if err != nil { 69 | logger.Error(err) 70 | io.WriteString(writer, "Unauthorized!\n") 71 | return false 72 | } 73 | user := strings.SplitN(string(authstr), ":", 2) 74 | if len(user) != 2 { 75 | logger.Error(user) 76 | return false 77 | } 78 | account := user[0] 79 | password := user[1] 80 | config := global.CurrentServerConfig.WebConfig 81 | if account != config.Account || password != config.Password { 82 | io.WriteString(writer, "账号或密码错误!\n") 83 | return false 84 | } 85 | default: 86 | logger.Error(authMethod) 87 | return false 88 | } 89 | return true 90 | } 91 | 92 | func (w *Web) root(writer http.ResponseWriter, req *http.Request) { 93 | logger.Info("HTTP请求", req.RemoteAddr) 94 | if w.checkauth(writer, req) == false { 95 | return 96 | } 97 | err := webhtml.Execute(writer, nil) 98 | if err != nil { 99 | logger.Error(err) 100 | } 101 | } 102 | 103 | func (w *Web) zones(writer http.ResponseWriter, req *http.Request) { 104 | bytes, err := json.Marshal(etcmgr.Instance.Zones()) 105 | if err != nil { 106 | logger.Debug(err) 107 | } else { 108 | io.WriteString(writer, string(bytes)) 109 | } 110 | } 111 | 112 | func (w *Web) reloadetc(writer http.ResponseWriter, req *http.Request) { 113 | _, err := node.Net.RequestBytes(msg.CMD_RELOAD_ETC_REQ, make([]byte, 1)) 114 | if err != nil { 115 | io.WriteString(writer, err.Error()) 116 | } else { 117 | io.WriteString(writer, "success") 118 | } 119 | } 120 | 121 | func init() { 122 | dir, err := os.Getwd() 123 | if err != nil { 124 | logger.Fatal(err) 125 | } 126 | webhtml, err = template.ParseFiles(fmt.Sprintf("%s/web/web.html", dir)) 127 | if err != nil { 128 | logger.Fatal(err) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/proto/data/data.component.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: data.component.proto 3 | 4 | package data 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *TransformComponent) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *TransformComponent) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *PhysicsComponent) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *PhysicsComponent) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | 44 | // MarshalJSON implements json.Marshaler 45 | func (msg *AttributeComponent) MarshalJSON() ([]byte, error) { 46 | var buf bytes.Buffer 47 | err := (&jsonpb.Marshaler{ 48 | EnumsAsInts: false, 49 | EmitDefaults: false, 50 | OrigName: false, 51 | }).Marshal(&buf, msg) 52 | return buf.Bytes(), err 53 | } 54 | 55 | // UnmarshalJSON implements json.Unmarshaler 56 | func (msg *AttributeComponent) UnmarshalJSON(b []byte) error { 57 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 58 | } 59 | 60 | // MarshalJSON implements json.Marshaler 61 | func (msg *MoveComponent) MarshalJSON() ([]byte, error) { 62 | var buf bytes.Buffer 63 | err := (&jsonpb.Marshaler{ 64 | EnumsAsInts: false, 65 | EmitDefaults: false, 66 | OrigName: false, 67 | }).Marshal(&buf, msg) 68 | return buf.Bytes(), err 69 | } 70 | 71 | // UnmarshalJSON implements json.Unmarshaler 72 | func (msg *MoveComponent) UnmarshalJSON(b []byte) error { 73 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 74 | } 75 | 76 | // MarshalJSON implements json.Marshaler 77 | func (msg *ControllerComponent) MarshalJSON() ([]byte, error) { 78 | var buf bytes.Buffer 79 | err := (&jsonpb.Marshaler{ 80 | EnumsAsInts: false, 81 | EmitDefaults: false, 82 | OrigName: false, 83 | }).Marshal(&buf, msg) 84 | return buf.Bytes(), err 85 | } 86 | 87 | // UnmarshalJSON implements json.Unmarshaler 88 | func (msg *ControllerComponent) UnmarshalJSON(b []byte) error { 89 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 90 | } 91 | 92 | // MarshalJSON implements json.Marshaler 93 | func (msg *RebornComponent) MarshalJSON() ([]byte, error) { 94 | var buf bytes.Buffer 95 | err := (&jsonpb.Marshaler{ 96 | EnumsAsInts: false, 97 | EmitDefaults: false, 98 | OrigName: false, 99 | }).Marshal(&buf, msg) 100 | return buf.Bytes(), err 101 | } 102 | 103 | // UnmarshalJSON implements json.Unmarshaler 104 | func (msg *RebornComponent) UnmarshalJSON(b []byte) error { 105 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 106 | } 107 | 108 | // MarshalJSON implements json.Marshaler 109 | func (msg *Component) MarshalJSON() ([]byte, error) { 110 | var buf bytes.Buffer 111 | err := (&jsonpb.Marshaler{ 112 | EnumsAsInts: false, 113 | EmitDefaults: false, 114 | OrigName: false, 115 | }).Marshal(&buf, msg) 116 | return buf.Bytes(), err 117 | } 118 | 119 | // UnmarshalJSON implements json.Unmarshaler 120 | func (msg *Component) UnmarshalJSON(b []byte) error { 121 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 122 | } 123 | -------------------------------------------------------------------------------- /src/gameplay/gamemap/scene.go: -------------------------------------------------------------------------------- 1 | package gamemap 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/engine/extensions/vector3" 7 | "INServer/src/engine/grid" 8 | "INServer/src/gameplay/ecs" 9 | "INServer/src/proto/config" 10 | "INServer/src/proto/data" 11 | "INServer/src/proto/engine" 12 | "INServer/src/proto/msg" 13 | "time" 14 | ) 15 | 16 | type ( 17 | Scene struct { 18 | masterMap *Map 19 | search *grid.Grid 20 | Width int32 21 | Height int32 22 | entities map[string]*ecs.Entity 23 | 24 | syncTime int64 25 | } 26 | ) 27 | 28 | func NewScene(masterMap *Map, sceneConfig *config.Scene) *Scene { 29 | s := new(Scene) 30 | s.masterMap = masterMap 31 | s.Width = 1000 32 | s.Height = 1000 33 | s.search = grid.New(10, s.Width, s.Height) 34 | s.entities = make(map[string]*ecs.Entity) 35 | return s 36 | } 37 | 38 | func (s *Scene) Tick() { 39 | now := time.Now().UnixNano() 40 | if s.syncTime+global.NANO_PER_SECONE < now { 41 | s.syncEntitiesNTF() 42 | } 43 | } 44 | 45 | func (s *Scene) EntityEnter(uuid string, entity *ecs.Entity) { 46 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 47 | if transform != nil { 48 | s.search.Add(uuid, transform.Position) 49 | } 50 | s.entities[uuid] = entity 51 | } 52 | 53 | func (s *Scene) EntityLeave(uuid string, entity *ecs.Entity) { 54 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 55 | if transform != nil { 56 | s.search.Remove(uuid, transform.Position) 57 | } 58 | delete(s.entities, uuid) 59 | } 60 | 61 | func (s *Scene) SyncEntityPosition(uuid string, entity *ecs.Entity, from *engine.Vector3) { 62 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 63 | if transform != nil { 64 | s.search.Move(uuid, from, transform.Position) 65 | } 66 | } 67 | 68 | func (s *Scene) onEntityMoveINF(entity *ecs.Entity, inf *msg.MoveINF) { 69 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 70 | if transform != nil { 71 | s.search.Move(entity.UUID(), transform.Position, inf.Position) 72 | transform.Position = inf.Position 73 | physics := entity.GetComponent(data.ComponentType_Physics).Physics 74 | attribute := entity.GetComponent(data.ComponentType_Attribute).Attribute 75 | move := entity.GetComponent(data.ComponentType_Move).Move 76 | if physics != nil && attribute != nil && move != nil { 77 | move.Destination = inf.To 78 | physics.RawSpeed = vector3.Multiply(vector3.Normalize(vector3.Minus(inf.To, inf.Position)), float64(attribute.Speed)) 79 | ntf := &msg.MoveNTF{} 80 | ntf.EntityUUID = entity.UUID() 81 | ntf.To = inf.To 82 | items := s.search.GetNearItems(inf.Position) 83 | for _, item := range items { 84 | logger.Info(item.EntityUUID) 85 | //nearEntity := s.masterMap.GetEntity(item.UUID()) 86 | } 87 | } 88 | } 89 | } 90 | 91 | func (s *Scene) onEntityStopMoveINF(entity *ecs.Entity, inf *msg.StopMoveINF) { 92 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 93 | if transform != nil { 94 | s.search.Move(entity.UUID(), transform.Position, inf.Position) 95 | transform.Position = inf.Position 96 | physics := entity.GetComponent(data.ComponentType_Physics).Physics 97 | if physics != nil { 98 | physics.RawSpeed = &engine.Vector3{} 99 | } 100 | move := entity.GetComponent(data.ComponentType_Move).Move 101 | if move != nil { 102 | move.Destination = inf.Position 103 | } 104 | } 105 | } 106 | 107 | func (s *Scene) syncEntitiesNTF() { 108 | for _, entity := range s.entities { 109 | controller := entity.GetComponent(data.ComponentType_Controller).Controller 110 | if controller.ControllerType != data.ControllerType_PlayerController { 111 | continue 112 | } 113 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 114 | if transform != nil { 115 | items := s.search.GetNearItems(transform.Position) 116 | entity.Controller().OnNearEntities(items) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /proto/protobuf/struct.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "types"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "StructProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // `Struct` represents a structured data value, consisting of fields 44 | // which map to dynamically typed values. In some languages, `Struct` 45 | // might be supported by a native representation. For example, in 46 | // scripting languages like JS a struct is represented as an 47 | // object. The details of that representation are described together 48 | // with the proto support for the language. 49 | // 50 | // The JSON representation for `Struct` is JSON object. 51 | message Struct { 52 | // Unordered map of dynamically typed values. 53 | map fields = 1; 54 | } 55 | 56 | // `Value` represents a dynamically typed value which can be either 57 | // null, a number, a string, a boolean, a recursive struct value, or a 58 | // list of values. A producer of value is expected to set one of that 59 | // variants, absence of any variant indicates an error. 60 | // 61 | // The JSON representation for `Value` is JSON value. 62 | message Value { 63 | // The kind of value. 64 | oneof kind { 65 | // Represents a null value. 66 | NullValue null_value = 1; 67 | // Represents a double value. 68 | double number_value = 2; 69 | // Represents a string value. 70 | string string_value = 3; 71 | // Represents a boolean value. 72 | bool bool_value = 4; 73 | // Represents a structured value. 74 | Struct struct_value = 5; 75 | // Represents a repeated `Value`. 76 | ListValue list_value = 6; 77 | } 78 | } 79 | 80 | // `NullValue` is a singleton enumeration to represent the null value for the 81 | // `Value` type union. 82 | // 83 | // The JSON representation for `NullValue` is JSON `null`. 84 | enum NullValue { 85 | // Null value. 86 | NULL_VALUE = 0; 87 | } 88 | 89 | // `ListValue` is a wrapper around a repeated field of values. 90 | // 91 | // The JSON representation for `ListValue` is JSON array. 92 | message ListValue { 93 | // Repeated field of dynamically typed values. 94 | repeated Value values = 1; 95 | } 96 | -------------------------------------------------------------------------------- /src/gameplay/ecs/entity.go: -------------------------------------------------------------------------------- 1 | package ecs 2 | 3 | import ( 4 | "INServer/src/proto/data" 5 | "INServer/src/proto/engine" 6 | ) 7 | 8 | type ( 9 | // Entity 游戏实体 10 | Entity struct { 11 | entityData *data.EntityData 12 | entityType data.EntityType 13 | controller Controller 14 | } 15 | ) 16 | 17 | // NewEntity 构造实体 18 | func NewEntity(entityData *data.EntityData, entityType data.EntityType) *Entity { 19 | e := new(Entity) 20 | e.entityData = entityData 21 | e.entityType = entityType 22 | initController(e) 23 | return e 24 | } 25 | 26 | // AddComponent 添加组件 27 | func (e *Entity) AddComponent(component *data.Component) { 28 | e.entityData.Components[component.Type] = component 29 | } 30 | 31 | // RemoveComponent 移除组件 32 | func (e *Entity) RemoveComponent(componentType data.ComponentType) { 33 | e.entityData.Components[componentType] = nil 34 | } 35 | 36 | // GetComponent 取得组件 37 | func (e *Entity) GetComponent(componentType data.ComponentType) *data.Component { 38 | if e.entityData.Components[componentType] == nil { 39 | return &data.Component{} 40 | } 41 | return e.entityData.Components[componentType] 42 | } 43 | 44 | // RealTimeData 实时数据 45 | func (e *Entity) RealTimeData() *data.EntityRealtimeData { 46 | return e.entityData.RealTimeData 47 | } 48 | 49 | // EntityData 实体数据 50 | func (e *Entity) EntityData() *data.EntityData { 51 | return e.entityData 52 | } 53 | 54 | // UUID 返回UUID 55 | func (e *Entity) UUID() string { 56 | return e.entityData.EntityUUID 57 | } 58 | 59 | // Controller 返回Controller 60 | func (e *Entity) Controller() Controller { 61 | return e.controller 62 | } 63 | 64 | // InitComponents 根据实体类型初始化组件 65 | func InitComponents(entityType data.EntityType) []*data.Component { 66 | components := make([]*data.Component, len(data.ComponentType_name)) 67 | for index := 0; index < len(data.ComponentType_name); index++ { 68 | components[index] = &data.Component{ 69 | Type: data.ComponentType(index), 70 | } 71 | } 72 | switch entityType { 73 | case data.EntityType_MonsterEntity: 74 | components[data.ComponentType_Transofrm].Transform = &data.TransformComponent{ 75 | Position: &engine.Vector3{}, 76 | Rotation: &engine.Quaternion{}, 77 | } 78 | components[data.ComponentType_Physics].Physics = &data.PhysicsComponent{ 79 | Mass: 100, 80 | RawSpeed: &engine.Vector3{}, 81 | PassiveSpeed: &engine.Vector3{}, 82 | } 83 | components[data.ComponentType_Attribute].Attribute = &data.AttributeComponent{ 84 | Speed: 10, 85 | HP: 100, 86 | MaxHP: 100, 87 | } 88 | components[data.ComponentType_Move].Move = &data.MoveComponent{ 89 | Destination: &engine.Vector3{}, 90 | } 91 | components[data.ComponentType_Controller].Controller = &data.ControllerComponent{ 92 | ControllerType: data.ControllerType_PlayerController, 93 | } 94 | components[data.ComponentType_Reborn].Reborn = &data.RebornComponent{ 95 | RebornTime: 0, 96 | RebornType: data.RebornType_Auto, 97 | } 98 | break 99 | case data.EntityType_RoleEntity: 100 | components[data.ComponentType_Transofrm].Transform = &data.TransformComponent{ 101 | Position: &engine.Vector3{}, 102 | Rotation: &engine.Quaternion{}, 103 | } 104 | components[data.ComponentType_Physics].Physics = &data.PhysicsComponent{ 105 | Mass: 100, 106 | RawSpeed: &engine.Vector3{}, 107 | PassiveSpeed: &engine.Vector3{}, 108 | } 109 | components[data.ComponentType_Attribute].Attribute = &data.AttributeComponent{ 110 | Speed: 10, 111 | HP: 100, 112 | MaxHP: 100, 113 | } 114 | components[data.ComponentType_Move].Move = &data.MoveComponent{ 115 | Destination: &engine.Vector3{}, 116 | } 117 | components[data.ComponentType_Controller].Controller = &data.ControllerComponent{ 118 | ControllerType: data.ControllerType_PlayerController, 119 | } 120 | components[data.ComponentType_Reborn].Reborn = &data.RebornComponent{ 121 | RebornTime: 0, 122 | RebornType: data.RebornType_Auto, 123 | } 124 | break 125 | default: 126 | break 127 | } 128 | return components 129 | } 130 | -------------------------------------------------------------------------------- /src/services/innet/receiver.go: -------------------------------------------------------------------------------- 1 | package innet 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/msg" 7 | "encoding/binary" 8 | "net" 9 | 10 | "github.com/gogo/protobuf/proto" 11 | ) 12 | 13 | type ( 14 | serverPackageCache struct { 15 | currentUniqueID uint64 16 | packages map[uint64]map[int32]*msg.Package 17 | } 18 | receiver struct { 19 | innet *INNet 20 | packageCache map[int32]*serverPackageCache 21 | } 22 | ) 23 | 24 | func newReceiver(innet *INNet) *receiver { 25 | r := new(receiver) 26 | r.innet = innet 27 | r.packageCache = make(map[int32]*serverPackageCache) 28 | return r 29 | } 30 | 31 | func (r *receiver) start() { 32 | go r.receiveLoop() 33 | } 34 | 35 | func (r *receiver) receiveLoop() { 36 | conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: int(global.CurrentServerID) + recvport}) 37 | if err != nil { 38 | logger.Fatal(err) 39 | } 40 | var buf = make([]byte, 65536) 41 | current := 0 42 | var udpaddr *net.UDPAddr 43 | for { 44 | // 等待读取数据长度 45 | for current < 2 { 46 | n, addr, _ := conn.ReadFromUDP(buf[current:]) 47 | udpaddr = addr 48 | current = current + n 49 | } 50 | 51 | // 等待读取数据 52 | size := binary.BigEndian.Uint16(buf[:2]) 53 | for (current - 2) < int(size) { 54 | n, _ := conn.Read(buf[current:]) 55 | current = current + n 56 | } 57 | 58 | pkg := &msg.Package{} 59 | proto.Unmarshal(buf[2:size+2], pkg) 60 | r.innet.retry.handlePackageReceive(pkg) 61 | if pkg.Buffer != nil { 62 | port := udpaddr.Port + recvport - sendport 63 | addr := &net.UDPAddr{IP: udpaddr.IP, Port: port} 64 | r.innet.sender.ack(addr, pkg) 65 | r.handlePackage(pkg) 66 | } 67 | 68 | copy(buf[0:], buf[size+2:current]) 69 | current = current - int(size) - 2 70 | } 71 | } 72 | 73 | func (r *receiver) handlePackage(pkg *msg.Package) { 74 | if _, ok := r.packageCache[pkg.From]; ok == false { 75 | r.packageCache[pkg.From] = new(serverPackageCache) 76 | r.packageCache[pkg.From].packages = make(map[uint64]map[int32]*msg.Package) 77 | r.packageCache[pkg.From].currentUniqueID = 1 78 | } 79 | packages := r.packageCache[pkg.From] 80 | 81 | // 如果收到的UniqueID为0直接处理,因为这是服务器启动后的第一个包 82 | if pkg.UniqueID == 0 { 83 | msg := &msg.Message{} 84 | proto.Unmarshal(pkg.Buffer, msg) 85 | r.innet.pushToMessageChan(msg) 86 | } else if pkg.UniqueID >= packages.currentUniqueID { 87 | if _, ok := packages.packages[pkg.UniqueID]; ok == false { 88 | packages.packages[pkg.UniqueID] = make(map[int32]*msg.Package) 89 | } 90 | sequencePackages := packages.packages[pkg.UniqueID] 91 | if _, ok := sequencePackages[pkg.Index]; ok == false { 92 | sequencePackages[pkg.Index] = pkg 93 | if len(sequencePackages) == int(pkg.Total) && pkg.UniqueID == packages.currentUniqueID { 94 | r.onPackagesReceiveFinished(sequencePackages) 95 | delete(packages.packages, packages.currentUniqueID) 96 | packages.currentUniqueID++ 97 | r.testCurrentPackages(packages) 98 | } 99 | } 100 | } 101 | } 102 | 103 | func (r *receiver) resetServer(serverID int32) { 104 | if _, ok := r.packageCache[serverID]; ok { 105 | delete(r.packageCache, serverID) 106 | } 107 | } 108 | 109 | func (r *receiver) testCurrentPackages(serverPackages *serverPackageCache) { 110 | if packages, ok := serverPackages.packages[serverPackages.currentUniqueID]; ok { 111 | for _, packageCache := range packages { 112 | if packageCache.Total == int32(len(packages)) { 113 | r.onPackagesReceiveFinished(packages) 114 | delete(serverPackages.packages, serverPackages.currentUniqueID) 115 | serverPackages.currentUniqueID++ 116 | r.testCurrentPackages(serverPackages) 117 | } 118 | break 119 | } 120 | } 121 | } 122 | 123 | func (r *receiver) onPackagesReceiveFinished(packages map[int32]*msg.Package) { 124 | var buffer []byte 125 | for index := int32(0); index < int32(len(packages)); index++ { 126 | buffer = append(buffer, packages[index].Buffer...) 127 | } 128 | msg := &msg.Message{} 129 | proto.Unmarshal(buffer, msg) 130 | r.innet.pushToMessageChan(msg) 131 | } 132 | -------------------------------------------------------------------------------- /src/gameplay/gamemap/gamemap.go: -------------------------------------------------------------------------------- 1 | package gamemap 2 | 3 | import ( 4 | "INServer/src/gameplay/ecs" 5 | "INServer/src/gameplay/ecs/system" 6 | "INServer/src/proto/config" 7 | "INServer/src/proto/data" 8 | "INServer/src/proto/engine" 9 | "INServer/src/proto/msg" 10 | "time" 11 | ) 12 | 13 | type ( 14 | ComponentValueCache struct { 15 | position engine.Vector3 16 | } 17 | 18 | Map struct { 19 | mapData *data.MapData 20 | mapConfig *config.Map 21 | scenes []*Scene 22 | 23 | firstScene *Scene 24 | entitiesMap map[string]*ecs.Entity 25 | running bool 26 | } 27 | ) 28 | 29 | func NewMap(mapConfig *config.Map, mapData *data.MapData) *Map { 30 | m := new(Map) 31 | m.scenes = make([]*Scene, 0) 32 | m.firstScene = NewScene(m, nil) 33 | m.scenes = append(m.scenes, m.firstScene) 34 | m.mapData = mapData 35 | m.mapConfig = mapConfig 36 | m.entitiesMap = make(map[string]*ecs.Entity) 37 | if m.mapData == nil { 38 | m.mapData = &data.MapData{ 39 | MapID: mapConfig.MapID, 40 | MapUUID: mapData.MapUUID, 41 | Entities: make([]*data.EntityData, 0), 42 | } 43 | } 44 | return m 45 | } 46 | 47 | func (m *Map) MapData() *data.MapData { 48 | return m.mapData 49 | } 50 | 51 | func (m *Map) MapConfig() *config.Map { 52 | return m.mapConfig 53 | } 54 | 55 | func (m *Map) Scenes() []*Scene { 56 | return m.scenes 57 | } 58 | 59 | func (m *Map) GetEntity(uuid string) *ecs.Entity { 60 | if entity, ok := m.entitiesMap[uuid]; ok { 61 | return entity 62 | } 63 | return nil 64 | } 65 | 66 | func (m *Map) Start() { 67 | m.running = true 68 | if m.mapData.LastTickTime == 0 { 69 | m.mapData.LastTickTime = time.Now().UnixNano() 70 | } 71 | go func() { 72 | for m.running { 73 | lasttime := m.mapData.LastTickTime 74 | now := time.Now().UnixNano() 75 | dt := float64(now-lasttime) / float64(1E9) 76 | 77 | m.tickSystems(dt) 78 | 79 | time.Sleep(time.Millisecond * 33) 80 | } 81 | }() 82 | } 83 | 84 | func (m *Map) EntityEnter(uuid string, entity *ecs.Entity) { 85 | m.mapData.Entities = append(m.mapData.Entities, entity.EntityData()) 86 | m.entitiesMap[uuid] = entity 87 | m.firstScene.EntityEnter(uuid, entity) 88 | } 89 | 90 | func (m *Map) EntityLeave(uuid string) { 91 | if entity, ok := m.entitiesMap[uuid]; ok { 92 | m.firstScene.EntityLeave(uuid, entity) 93 | delete(m.entitiesMap, uuid) 94 | for index, entityData := range m.mapData.Entities { 95 | if entityData.EntityUUID == uuid { 96 | m.mapData.Entities = append(m.mapData.Entities[:index], m.mapData.Entities[index+1:]...) 97 | break 98 | } 99 | } 100 | } 101 | } 102 | 103 | func (m *Map) Tick() { 104 | for _, scene := range m.scenes { 105 | scene.Tick() 106 | } 107 | } 108 | 109 | func (m *Map) tickSystems(dt float64) { 110 | cachedValues := make(map[string]*ComponentValueCache) 111 | for uuid, entity := range m.entitiesMap { 112 | cache := &ComponentValueCache{} 113 | cachedValues[uuid] = cache 114 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 115 | if transform != nil { 116 | cache.position = *transform.Position 117 | } 118 | } 119 | system.Tick(dt, m.entitiesMap) 120 | for uuid, entity := range m.entitiesMap { 121 | transform := entity.GetComponent(data.ComponentType_Transofrm).Transform 122 | if transform != nil { 123 | m.firstScene.SyncEntityPosition(uuid, entity, &cachedValues[uuid].position) 124 | } 125 | } 126 | } 127 | 128 | // OnRoleMoveINF 响应角色移动 129 | func (m *Map) OnRoleMoveINF(role *data.Role, inf *msg.MoveINF) { 130 | entity := m.GetEntity(role.SummaryData.RoleUUID) 131 | if entity != nil { 132 | m.firstScene.onEntityMoveINF(entity, inf) 133 | } 134 | } 135 | 136 | // OnRoleStopMoveINF 响应角色停止移动 137 | func (m *Map) OnRoleStopMoveINF(role *data.Role, inf *msg.StopMoveINF) { 138 | entity := m.GetEntity(role.SummaryData.RoleUUID) 139 | if entity != nil { 140 | m.firstScene.onEntityStopMoveINF(entity, inf) 141 | } 142 | } 143 | 144 | // FillEntityData 填充实体信息 145 | func (m *Map) FillEntityData(uuids []string, resp *msg.EntityDataRes) { 146 | for _, uuid := range uuids { 147 | if entity, ok := m.entitiesMap[uuid]; ok { 148 | resp.Entities = append(resp.Entities, entity.EntityData()) 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /proto/protobuf/wrappers.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | // Wrappers for primitive (non-message) types. These types are useful 32 | // for embedding primitives in the `google.protobuf.Any` type and for places 33 | // where we need to distinguish between the absence of a primitive 34 | // typed field and its default value. 35 | // 36 | // These wrappers have no meaningful use within repeated fields as they lack 37 | // the ability to detect presence on individual elements. 38 | // These wrappers have no meaningful use within a map or a oneof since 39 | // individual entries of a map or fields of a oneof can already detect presence. 40 | 41 | syntax = "proto3"; 42 | 43 | package google.protobuf; 44 | 45 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 46 | option cc_enable_arenas = true; 47 | option go_package = "types"; 48 | option java_package = "com.google.protobuf"; 49 | option java_outer_classname = "WrappersProto"; 50 | option java_multiple_files = true; 51 | option objc_class_prefix = "GPB"; 52 | 53 | // Wrapper message for `double`. 54 | // 55 | // The JSON representation for `DoubleValue` is JSON number. 56 | message DoubleValue { 57 | // The double value. 58 | double value = 1; 59 | } 60 | 61 | // Wrapper message for `float`. 62 | // 63 | // The JSON representation for `FloatValue` is JSON number. 64 | message FloatValue { 65 | // The float value. 66 | float value = 1; 67 | } 68 | 69 | // Wrapper message for `int64`. 70 | // 71 | // The JSON representation for `Int64Value` is JSON string. 72 | message Int64Value { 73 | // The int64 value. 74 | int64 value = 1; 75 | } 76 | 77 | // Wrapper message for `uint64`. 78 | // 79 | // The JSON representation for `UInt64Value` is JSON string. 80 | message UInt64Value { 81 | // The uint64 value. 82 | uint64 value = 1; 83 | } 84 | 85 | // Wrapper message for `int32`. 86 | // 87 | // The JSON representation for `Int32Value` is JSON number. 88 | message Int32Value { 89 | // The int32 value. 90 | int32 value = 1; 91 | } 92 | 93 | // Wrapper message for `uint32`. 94 | // 95 | // The JSON representation for `UInt32Value` is JSON number. 96 | message UInt32Value { 97 | // The uint32 value. 98 | uint32 value = 1; 99 | } 100 | 101 | // Wrapper message for `bool`. 102 | // 103 | // The JSON representation for `BoolValue` is JSON `true` and `false`. 104 | message BoolValue { 105 | // The bool value. 106 | bool value = 1; 107 | } 108 | 109 | // Wrapper message for `string`. 110 | // 111 | // The JSON representation for `StringValue` is JSON string. 112 | message StringValue { 113 | // The string value. 114 | string value = 1; 115 | } 116 | 117 | // Wrapper message for `bytes`. 118 | // 119 | // The JSON representation for `BytesValue` is JSON string. 120 | message BytesValue { 121 | // The bytes value. 122 | bytes value = 1; 123 | } 124 | -------------------------------------------------------------------------------- /src/services/gps/gps.go: -------------------------------------------------------------------------------- 1 | package gps 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/data" 7 | "INServer/src/proto/msg" 8 | "INServer/src/services/node" 9 | "fmt" 10 | 11 | "github.com/gogo/protobuf/proto" 12 | ) 13 | 14 | var Instance *GPS 15 | 16 | type ( 17 | role struct { 18 | address *data.RoleAddress 19 | uuid string 20 | } 21 | 22 | // GPS 定位服务器 可以查询每个地图和每个角色的位置 23 | GPS struct { 24 | maps map[string]int32 25 | roles map[string]*role 26 | staticmaps map[int32]map[int32]string 27 | } 28 | ) 29 | 30 | // New 创建定位服务器 31 | func New() *GPS { 32 | g := new(GPS) 33 | g.maps = make(map[string]int32) 34 | g.roles = make(map[string]*role) 35 | g.staticmaps = make(map[int32]map[int32]string) 36 | return g 37 | } 38 | 39 | // Start 启动定位服务器 40 | func (g *GPS) Start() { 41 | g.initMessageHandler() 42 | } 43 | 44 | func (g *GPS) initMessageHandler() { 45 | node.Net.Listen(msg.CMD_UPDATE_ROLE_ADDRESS_NTF, g.HANDLE_UPDATE_ROLE_ADDRESS_NTF) 46 | node.Net.Listen(msg.CMD_REMOVE_ROLE_ADDRESS_NTF, g.HANDLE_REMOVE_ROLE_ADDRESS_NTF) 47 | node.Net.Listen(msg.CMD_UPDATE_MAP_ADDRESS_NTF, g.onUpdateMapAddressNTF) 48 | node.Net.Listen(msg.CMD_REMOVE_MAP_ADDRESS_NTF, g.onRemoveMapAddressNTF) 49 | node.Net.Listen(msg.CMD_GET_MAP_ADDRESS_REQ, g.onGetMapLocationReq) 50 | node.Net.Listen(msg.CMD_UPDATE_STATIC_MAP_UUID_NTF, g.onUpdateStaticMapUUIDNTF) 51 | node.Net.Listen(msg.CMD_GET_STATIC_MAP_UUID_REQ, g.onGetStaticMapUUIDReq) 52 | } 53 | 54 | func (g *GPS) HANDLE_UPDATE_ROLE_ADDRESS_NTF(header *msg.MessageHeader, buffer []byte) { 55 | ntf := &msg.UpdateRoleAddressNTF{} 56 | err := proto.Unmarshal(buffer, ntf) 57 | if err != nil { 58 | logger.Error(err) 59 | return 60 | } 61 | if len(ntf.RoleUUID) > 0 { 62 | if _, ok := g.roles[ntf.RoleUUID]; ok == false { 63 | address := &data.RoleAddress{ 64 | Gate: global.InvalidServerID, 65 | World: global.InvalidServerID, 66 | } 67 | g.roles[ntf.RoleUUID] = &role{ 68 | address: address, 69 | } 70 | 71 | } 72 | if ntf.Address.Gate != global.InvalidServerID { 73 | g.roles[ntf.RoleUUID].address.Gate = ntf.Address.Gate 74 | } 75 | if ntf.Address.World != global.InvalidServerID { 76 | g.roles[ntf.RoleUUID].address.World = ntf.Address.World 77 | } 78 | } else { 79 | logger.Error("Empty RoleUUID") 80 | } 81 | node.Net.NotifyServer(msg.CMD_UPDATE_ROLE_ADDRESS_NTF, ntf, g.roles[ntf.RoleUUID].address.Gate) 82 | } 83 | 84 | func (g *GPS) HANDLE_REMOVE_ROLE_ADDRESS_NTF(header *msg.MessageHeader, buffer []byte) { 85 | ntf := &msg.RemoveRoleAddressNTF{} 86 | proto.Unmarshal(buffer, ntf) 87 | if r, ok := g.roles[ntf.RoleUUID]; ok { 88 | if _, ok := g.roles[r.uuid]; ok { 89 | delete(g.roles, r.uuid) 90 | } 91 | } 92 | } 93 | 94 | func (g *GPS) onUpdateMapAddressNTF(header *msg.MessageHeader, buffer []byte) { 95 | ntf := &msg.UpdateMapAddressNTF{} 96 | err := proto.Unmarshal(buffer, ntf) 97 | if err != nil { 98 | logger.Error(err) 99 | } else { 100 | g.maps[ntf.MapUUID] = ntf.ServerID 101 | logger.Info(fmt.Sprintf("MapAddress UUID:%s ServerID:%d", ntf.MapUUID, ntf.ServerID)) 102 | } 103 | } 104 | 105 | func (g *GPS) onUpdateStaticMapUUIDNTF(header *msg.MessageHeader, buffer []byte) { 106 | ntf := &msg.UpdateStaticMapUUIDNTF{} 107 | err := proto.Unmarshal(buffer, ntf) 108 | if err != nil { 109 | logger.Error(err) 110 | } else { 111 | if _, ok := g.staticmaps[ntf.ZoneID]; ok == false { 112 | g.staticmaps[ntf.ZoneID] = make(map[int32]string) 113 | } 114 | g.staticmaps[ntf.ZoneID][ntf.StaticMapID] = ntf.StaticMapUUID 115 | logger.Info(fmt.Sprintf("StaticMap ZoneID:%d StaticMapID:%d UUID:%s", ntf.ZoneID, ntf.StaticMapID, ntf.StaticMapUUID)) 116 | } 117 | } 118 | 119 | func (g *GPS) onGetStaticMapUUIDReq(header *msg.MessageHeader, buffer []byte) { 120 | resp := &msg.GetStaticMapUUIDResp{} 121 | defer node.Net.Responce(header, resp) 122 | req := &msg.GetStaticMapUUIDReq{} 123 | err := proto.Unmarshal(buffer, req) 124 | if err != nil { 125 | logger.Error(err) 126 | } else { 127 | if maps, ok := g.staticmaps[req.ZoneID]; ok { 128 | if uuid, ok := maps[req.StaticMapID]; ok { 129 | resp.StaticMapUUID = uuid 130 | } 131 | } 132 | } 133 | } 134 | 135 | func (g *GPS) onRemoveMapAddressNTF(header *msg.MessageHeader, buffer []byte) { 136 | ntf := &msg.RemoveMapAddressNTF{} 137 | proto.Unmarshal(buffer, ntf) 138 | delete(g.maps, ntf.MapUUID) 139 | } 140 | 141 | func (g *GPS) onGetMapLocationReq(header *msg.MessageHeader, buffer []byte) { 142 | resp := &msg.GetMapAddressResp{ 143 | ServerID: global.InvalidServerID, 144 | } 145 | defer node.Net.Responce(header, resp) 146 | req := &msg.GetMapAddressReq{} 147 | err := proto.Unmarshal(buffer, req) 148 | if err != nil { 149 | logger.Error(err) 150 | return 151 | } 152 | if serverID, ok := g.maps[req.MapUUID]; ok { 153 | resp.ServerID = serverID 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /proto/protobuf/duration.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "types"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "DurationProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Duration represents a signed, fixed-length span of time represented 44 | // as a count of seconds and fractions of seconds at nanosecond 45 | // resolution. It is independent of any calendar and concepts like "day" 46 | // or "month". It is related to Timestamp in that the difference between 47 | // two Timestamp values is a Duration and it can be added or subtracted 48 | // from a Timestamp. Range is approximately +-10,000 years. 49 | // 50 | // # Examples 51 | // 52 | // Example 1: Compute Duration from two Timestamps in pseudo code. 53 | // 54 | // Timestamp start = ...; 55 | // Timestamp end = ...; 56 | // Duration duration = ...; 57 | // 58 | // duration.seconds = end.seconds - start.seconds; 59 | // duration.nanos = end.nanos - start.nanos; 60 | // 61 | // if (duration.seconds < 0 && duration.nanos > 0) { 62 | // duration.seconds += 1; 63 | // duration.nanos -= 1000000000; 64 | // } else if (durations.seconds > 0 && duration.nanos < 0) { 65 | // duration.seconds -= 1; 66 | // duration.nanos += 1000000000; 67 | // } 68 | // 69 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 70 | // 71 | // Timestamp start = ...; 72 | // Duration duration = ...; 73 | // Timestamp end = ...; 74 | // 75 | // end.seconds = start.seconds + duration.seconds; 76 | // end.nanos = start.nanos + duration.nanos; 77 | // 78 | // if (end.nanos < 0) { 79 | // end.seconds -= 1; 80 | // end.nanos += 1000000000; 81 | // } else if (end.nanos >= 1000000000) { 82 | // end.seconds += 1; 83 | // end.nanos -= 1000000000; 84 | // } 85 | // 86 | // Example 3: Compute Duration from datetime.timedelta in Python. 87 | // 88 | // td = datetime.timedelta(days=3, minutes=10) 89 | // duration = Duration() 90 | // duration.FromTimedelta(td) 91 | // 92 | // # JSON Mapping 93 | // 94 | // In JSON format, the Duration type is encoded as a string rather than an 95 | // object, where the string ends in the suffix "s" (indicating seconds) and 96 | // is preceded by the number of seconds, with nanoseconds expressed as 97 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be 98 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 99 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1 100 | // microsecond should be expressed in JSON format as "3.000001s". 101 | // 102 | // 103 | message Duration { 104 | // Signed seconds of the span of time. Must be from -315,576,000,000 105 | // to +315,576,000,000 inclusive. Note: these bounds are computed from: 106 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 107 | int64 seconds = 1; 108 | 109 | // Signed fractions of a second at nanosecond resolution of the span 110 | // of time. Durations less than one second are represented with a 0 111 | // `seconds` field and a positive or negative `nanos` field. For durations 112 | // of one second or more, a non-zero value for the `nanos` field must be 113 | // of the same sign as the `seconds` field. Must be from -999,999,999 114 | // to +999,999,999 inclusive. 115 | int32 nanos = 2; 116 | } 117 | -------------------------------------------------------------------------------- /proto/gogoproto/gogo.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers for Go with Gadgets 2 | // 3 | // Copyright (c) 2013, The GoGo Authors. All rights reserved. 4 | // http://github.com/gogo/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | syntax = "proto2"; 30 | package gogoproto; 31 | 32 | import "descriptor.proto"; 33 | 34 | option java_package = "com.google.protobuf"; 35 | option java_outer_classname = "GoGoProtos"; 36 | option go_package = "github.com/gogo/protobuf/gogoproto"; 37 | 38 | extend google.protobuf.EnumOptions { 39 | optional bool goproto_enum_prefix = 62001; 40 | optional bool goproto_enum_stringer = 62021; 41 | optional bool enum_stringer = 62022; 42 | optional string enum_customname = 62023; 43 | optional bool enumdecl = 62024; 44 | } 45 | 46 | extend google.protobuf.EnumValueOptions { 47 | optional string enumvalue_customname = 66001; 48 | } 49 | 50 | extend google.protobuf.FileOptions { 51 | optional bool goproto_getters_all = 63001; 52 | optional bool goproto_enum_prefix_all = 63002; 53 | optional bool goproto_stringer_all = 63003; 54 | optional bool verbose_equal_all = 63004; 55 | optional bool face_all = 63005; 56 | optional bool gostring_all = 63006; 57 | optional bool populate_all = 63007; 58 | optional bool stringer_all = 63008; 59 | optional bool onlyone_all = 63009; 60 | 61 | optional bool equal_all = 63013; 62 | optional bool description_all = 63014; 63 | optional bool testgen_all = 63015; 64 | optional bool benchgen_all = 63016; 65 | optional bool marshaler_all = 63017; 66 | optional bool unmarshaler_all = 63018; 67 | optional bool stable_marshaler_all = 63019; 68 | 69 | optional bool sizer_all = 63020; 70 | 71 | optional bool goproto_enum_stringer_all = 63021; 72 | optional bool enum_stringer_all = 63022; 73 | 74 | optional bool unsafe_marshaler_all = 63023; 75 | optional bool unsafe_unmarshaler_all = 63024; 76 | 77 | optional bool goproto_extensions_map_all = 63025; 78 | optional bool goproto_unrecognized_all = 63026; 79 | optional bool gogoproto_import = 63027; 80 | optional bool protosizer_all = 63028; 81 | optional bool compare_all = 63029; 82 | optional bool typedecl_all = 63030; 83 | optional bool enumdecl_all = 63031; 84 | 85 | optional bool goproto_registration = 63032; 86 | optional bool messagename_all = 63033; 87 | 88 | optional bool goproto_sizecache_all = 63034; 89 | optional bool goproto_unkeyed_all = 63035; 90 | } 91 | 92 | extend google.protobuf.MessageOptions { 93 | optional bool goproto_getters = 64001; 94 | optional bool goproto_stringer = 64003; 95 | optional bool verbose_equal = 64004; 96 | optional bool face = 64005; 97 | optional bool gostring = 64006; 98 | optional bool populate = 64007; 99 | optional bool stringer = 67008; 100 | optional bool onlyone = 64009; 101 | 102 | optional bool equal = 64013; 103 | optional bool description = 64014; 104 | optional bool testgen = 64015; 105 | optional bool benchgen = 64016; 106 | optional bool marshaler = 64017; 107 | optional bool unmarshaler = 64018; 108 | optional bool stable_marshaler = 64019; 109 | 110 | optional bool sizer = 64020; 111 | 112 | optional bool unsafe_marshaler = 64023; 113 | optional bool unsafe_unmarshaler = 64024; 114 | 115 | optional bool goproto_extensions_map = 64025; 116 | optional bool goproto_unrecognized = 64026; 117 | 118 | optional bool protosizer = 64028; 119 | optional bool compare = 64029; 120 | 121 | optional bool typedecl = 64030; 122 | 123 | optional bool messagename = 64033; 124 | 125 | optional bool goproto_sizecache = 64034; 126 | optional bool goproto_unkeyed = 64035; 127 | } 128 | 129 | extend google.protobuf.FieldOptions { 130 | optional bool nullable = 65001; 131 | optional bool embed = 65002; 132 | optional string customtype = 65003; 133 | optional string customname = 65004; 134 | optional string jsontag = 65005; 135 | optional string moretags = 65006; 136 | optional string casttype = 65007; 137 | optional string castkey = 65008; 138 | optional string castvalue = 65009; 139 | 140 | optional bool stdtime = 65010; 141 | optional bool stdduration = 65011; 142 | optional bool wktpointer = 65012; 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/proto/etc/etc.servers.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: etc.servers.proto 3 | 4 | package etc 5 | 6 | import ( 7 | "bytes" 8 | 9 | "github.com/golang/protobuf/jsonpb" 10 | ) 11 | 12 | // MarshalJSON implements json.Marshaler 13 | func (msg *GateConfig) MarshalJSON() ([]byte, error) { 14 | var buf bytes.Buffer 15 | err := (&jsonpb.Marshaler{ 16 | EnumsAsInts: false, 17 | EmitDefaults: false, 18 | OrigName: false, 19 | }).Marshal(&buf, msg) 20 | return buf.Bytes(), err 21 | } 22 | 23 | // UnmarshalJSON implements json.Unmarshaler 24 | func (msg *GateConfig) UnmarshalJSON(b []byte) error { 25 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 26 | } 27 | 28 | // MarshalJSON implements json.Marshaler 29 | func (msg *LoginConfig) MarshalJSON() ([]byte, error) { 30 | var buf bytes.Buffer 31 | err := (&jsonpb.Marshaler{ 32 | EnumsAsInts: false, 33 | EmitDefaults: false, 34 | OrigName: false, 35 | }).Marshal(&buf, msg) 36 | return buf.Bytes(), err 37 | } 38 | 39 | // UnmarshalJSON implements json.Unmarshaler 40 | func (msg *LoginConfig) UnmarshalJSON(b []byte) error { 41 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 42 | } 43 | 44 | // MarshalJSON implements json.Marshaler 45 | func (msg *ChatConfig) MarshalJSON() ([]byte, error) { 46 | var buf bytes.Buffer 47 | err := (&jsonpb.Marshaler{ 48 | EnumsAsInts: false, 49 | EmitDefaults: false, 50 | OrigName: false, 51 | }).Marshal(&buf, msg) 52 | return buf.Bytes(), err 53 | } 54 | 55 | // UnmarshalJSON implements json.Unmarshaler 56 | func (msg *ChatConfig) UnmarshalJSON(b []byte) error { 57 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 58 | } 59 | 60 | // MarshalJSON implements json.Marshaler 61 | func (msg *DatabaseConfig) MarshalJSON() ([]byte, error) { 62 | var buf bytes.Buffer 63 | err := (&jsonpb.Marshaler{ 64 | EnumsAsInts: false, 65 | EmitDefaults: false, 66 | OrigName: false, 67 | }).Marshal(&buf, msg) 68 | return buf.Bytes(), err 69 | } 70 | 71 | // UnmarshalJSON implements json.Unmarshaler 72 | func (msg *DatabaseConfig) UnmarshalJSON(b []byte) error { 73 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 74 | } 75 | 76 | // MarshalJSON implements json.Marshaler 77 | func (msg *WebConfig) MarshalJSON() ([]byte, error) { 78 | var buf bytes.Buffer 79 | err := (&jsonpb.Marshaler{ 80 | EnumsAsInts: false, 81 | EmitDefaults: false, 82 | OrigName: false, 83 | }).Marshal(&buf, msg) 84 | return buf.Bytes(), err 85 | } 86 | 87 | // UnmarshalJSON implements json.Unmarshaler 88 | func (msg *WebConfig) UnmarshalJSON(b []byte) error { 89 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 90 | } 91 | 92 | // MarshalJSON implements json.Marshaler 93 | func (msg *ZoneWorld) MarshalJSON() ([]byte, error) { 94 | var buf bytes.Buffer 95 | err := (&jsonpb.Marshaler{ 96 | EnumsAsInts: false, 97 | EmitDefaults: false, 98 | OrigName: false, 99 | }).Marshal(&buf, msg) 100 | return buf.Bytes(), err 101 | } 102 | 103 | // UnmarshalJSON implements json.Unmarshaler 104 | func (msg *ZoneWorld) UnmarshalJSON(b []byte) error { 105 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 106 | } 107 | 108 | // MarshalJSON implements json.Marshaler 109 | func (msg *WorldConfig) MarshalJSON() ([]byte, error) { 110 | var buf bytes.Buffer 111 | err := (&jsonpb.Marshaler{ 112 | EnumsAsInts: false, 113 | EmitDefaults: false, 114 | OrigName: false, 115 | }).Marshal(&buf, msg) 116 | return buf.Bytes(), err 117 | } 118 | 119 | // UnmarshalJSON implements json.Unmarshaler 120 | func (msg *WorldConfig) UnmarshalJSON(b []byte) error { 121 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 122 | } 123 | 124 | // MarshalJSON implements json.Marshaler 125 | func (msg *AIConfig) MarshalJSON() ([]byte, error) { 126 | var buf bytes.Buffer 127 | err := (&jsonpb.Marshaler{ 128 | EnumsAsInts: false, 129 | EmitDefaults: false, 130 | OrigName: false, 131 | }).Marshal(&buf, msg) 132 | return buf.Bytes(), err 133 | } 134 | 135 | // UnmarshalJSON implements json.Unmarshaler 136 | func (msg *AIConfig) UnmarshalJSON(b []byte) error { 137 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 138 | } 139 | 140 | // MarshalJSON implements json.Marshaler 141 | func (msg *RobotConfig) MarshalJSON() ([]byte, error) { 142 | var buf bytes.Buffer 143 | err := (&jsonpb.Marshaler{ 144 | EnumsAsInts: false, 145 | EmitDefaults: false, 146 | OrigName: false, 147 | }).Marshal(&buf, msg) 148 | return buf.Bytes(), err 149 | } 150 | 151 | // UnmarshalJSON implements json.Unmarshaler 152 | func (msg *RobotConfig) UnmarshalJSON(b []byte) error { 153 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 154 | } 155 | 156 | // MarshalJSON implements json.Marshaler 157 | func (msg *ServerConfig) MarshalJSON() ([]byte, error) { 158 | var buf bytes.Buffer 159 | err := (&jsonpb.Marshaler{ 160 | EnumsAsInts: false, 161 | EmitDefaults: false, 162 | OrigName: false, 163 | }).Marshal(&buf, msg) 164 | return buf.Bytes(), err 165 | } 166 | 167 | // UnmarshalJSON implements json.Unmarshaler 168 | func (msg *ServerConfig) UnmarshalJSON(b []byte) error { 169 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 170 | } 171 | 172 | // MarshalJSON implements json.Marshaler 173 | func (msg *Server) MarshalJSON() ([]byte, error) { 174 | var buf bytes.Buffer 175 | err := (&jsonpb.Marshaler{ 176 | EnumsAsInts: false, 177 | EmitDefaults: false, 178 | OrigName: false, 179 | }).Marshal(&buf, msg) 180 | return buf.Bytes(), err 181 | } 182 | 183 | // UnmarshalJSON implements json.Unmarshaler 184 | func (msg *Server) UnmarshalJSON(b []byte) error { 185 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 186 | } 187 | 188 | // MarshalJSON implements json.Marshaler 189 | func (msg *ServerList) MarshalJSON() ([]byte, error) { 190 | var buf bytes.Buffer 191 | err := (&jsonpb.Marshaler{ 192 | EnumsAsInts: false, 193 | EmitDefaults: false, 194 | OrigName: false, 195 | }).Marshal(&buf, msg) 196 | return buf.Bytes(), err 197 | } 198 | 199 | // UnmarshalJSON implements json.Unmarshaler 200 | func (msg *ServerList) UnmarshalJSON(b []byte) error { 201 | return jsonpb.Unmarshal(bytes.NewReader(b), msg) 202 | } 203 | -------------------------------------------------------------------------------- /proto/protobuf/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "types"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "AnyProto"; 39 | option java_multiple_files = true; 40 | option objc_class_prefix = "GPB"; 41 | 42 | // `Any` contains an arbitrary serialized protocol buffer message along with a 43 | // URL that describes the type of the serialized message. 44 | // 45 | // Protobuf library provides support to pack/unpack Any values in the form 46 | // of utility functions or additional generated methods of the Any type. 47 | // 48 | // Example 1: Pack and unpack a message in C++. 49 | // 50 | // Foo foo = ...; 51 | // Any any; 52 | // any.PackFrom(foo); 53 | // ... 54 | // if (any.UnpackTo(&foo)) { 55 | // ... 56 | // } 57 | // 58 | // Example 2: Pack and unpack a message in Java. 59 | // 60 | // Foo foo = ...; 61 | // Any any = Any.pack(foo); 62 | // ... 63 | // if (any.is(Foo.class)) { 64 | // foo = any.unpack(Foo.class); 65 | // } 66 | // 67 | // Example 3: Pack and unpack a message in Python. 68 | // 69 | // foo = Foo(...) 70 | // any = Any() 71 | // any.Pack(foo) 72 | // ... 73 | // if any.Is(Foo.DESCRIPTOR): 74 | // any.Unpack(foo) 75 | // ... 76 | // 77 | // Example 4: Pack and unpack a message in Go 78 | // 79 | // foo := &pb.Foo{...} 80 | // any, err := ptypes.MarshalAny(foo) 81 | // ... 82 | // foo := &pb.Foo{} 83 | // if err := ptypes.UnmarshalAny(any, foo); err != nil { 84 | // ... 85 | // } 86 | // 87 | // The pack methods provided by protobuf library will by default use 88 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 89 | // methods only use the fully qualified type name after the last '/' 90 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 91 | // name "y.z". 92 | // 93 | // 94 | // JSON 95 | // ==== 96 | // The JSON representation of an `Any` value uses the regular 97 | // representation of the deserialized, embedded message, with an 98 | // additional field `@type` which contains the type URL. Example: 99 | // 100 | // package google.profile; 101 | // message Person { 102 | // string first_name = 1; 103 | // string last_name = 2; 104 | // } 105 | // 106 | // { 107 | // "@type": "type.googleapis.com/google.profile.Person", 108 | // "firstName": , 109 | // "lastName": 110 | // } 111 | // 112 | // If the embedded message type is well-known and has a custom JSON 113 | // representation, that representation will be embedded adding a field 114 | // `value` which holds the custom JSON in addition to the `@type` 115 | // field. Example (for message [google.protobuf.Duration][]): 116 | // 117 | // { 118 | // "@type": "type.googleapis.com/google.protobuf.Duration", 119 | // "value": "1.212s" 120 | // } 121 | // 122 | message Any { 123 | // A URL/resource name that uniquely identifies the type of the serialized 124 | // protocol buffer message. This string must contain at least 125 | // one "/" character. The last segment of the URL's path must represent 126 | // the fully qualified name of the type (as in 127 | // `path/google.protobuf.Duration`). The name should be in a canonical form 128 | // (e.g., leading "." is not accepted). 129 | // 130 | // In practice, teams usually precompile into the binary all types that they 131 | // expect it to use in the context of Any. However, for URLs which use the 132 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 133 | // server that maps type URLs to message definitions as follows: 134 | // 135 | // * If no scheme is provided, `https` is assumed. 136 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 137 | // value in binary format, or produce an error. 138 | // * Applications are allowed to cache lookup results based on the 139 | // URL, or have them precompiled into a binary to avoid any 140 | // lookup. Therefore, binary compatibility needs to be preserved 141 | // on changes to types. (Use versioned type names to manage 142 | // breaking changes.) 143 | // 144 | // Note: this functionality is not currently available in the official 145 | // protobuf release, and it is not used for type URLs beginning with 146 | // type.googleapis.com. 147 | // 148 | // Schemes other than `http`, `https` (or the empty scheme) might be 149 | // used with implementation specific semantics. 150 | // 151 | string type_url = 1; 152 | 153 | // Must be a valid serialized protocol buffer of the above specified type. 154 | bytes value = 2; 155 | } 156 | -------------------------------------------------------------------------------- /src/services/center/center.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | import ( 4 | "INServer/src/common/global" 5 | "INServer/src/common/logger" 6 | "INServer/src/proto/msg" 7 | "INServer/src/services/cluster" 8 | "INServer/src/services/etcmgr" 9 | "INServer/src/services/innet" 10 | "fmt" 11 | "os" 12 | "time" 13 | 14 | "github.com/golang/protobuf/proto" 15 | ) 16 | 17 | // Instance 中心服务器的单例 18 | var Instance *Center 19 | 20 | const ( 21 | timeout = 2000 * 1E6 // 纳秒 22 | ) 23 | 24 | type ( 25 | // Center 中心服务器 26 | Center struct { 27 | Net *innet.INNet 28 | keepAliveTime []int64 29 | } 30 | ) 31 | 32 | // New 创建中心服务器 33 | func New() *Center { 34 | c := new(Center) 35 | c.Net = innet.New() 36 | c.keepAliveTime = make([]int64, len(etcmgr.Instance.Servers())) 37 | c.tickServerState() 38 | return c 39 | } 40 | 41 | // Start 启动中心服务器 42 | func (c *Center) Start() { 43 | c.Net.Start() 44 | cluster.SetNode(global.CurrentServerID, &msg.Node{ 45 | NodeState: msg.NodeState_Running, 46 | NodeAddress: c.Net.IP, 47 | }) 48 | c.registerListeners() 49 | logger.Info(fmt.Sprintf("Server Start Type:%s ID:%d", global.CurrentServerType, global.CurrentServerID)) 50 | } 51 | 52 | func (c *Center) registerListeners() { 53 | c.Net.Listen(msg.CMD_NODE_START_NTF, c.HANDLE_NODE_START_NTF) 54 | c.Net.Listen(msg.CMD_KEEP_ALIVE, c.HANDLE_KEEP_ALIVE) 55 | c.Net.Listen(msg.CMD_RELOAD_ETC_REQ, c.HANDLE_RELOAD_ETC_REQ) 56 | c.Net.Listen(msg.CMD_NODES_INFO_NTF, c.HANDLE_NODES_INFO_NTF) 57 | } 58 | 59 | // HANDLE_NODE_START_NTF 节点启动 60 | func (c *Center) HANDLE_NODE_START_NTF(header *msg.MessageHeader, buffer []byte) { 61 | if global.PendingExit { 62 | return 63 | } 64 | ntf := &msg.NodeStartNTF{} 65 | err := proto.Unmarshal(buffer, ntf) 66 | if err != nil { 67 | return 68 | } 69 | cluster.SetNode(header.From, &msg.Node{ 70 | NodeState: msg.NodeState_Unset, 71 | NodeAddress: ntf.Address, 72 | }) 73 | cluster.RefreshRunning() 74 | cluster.RefreshRunningZones() 75 | c.Net.RefreshNodesAddress() 76 | c.Net.ResetServer(header.From) 77 | c.broadcastResetConnectionNTF(header.From) 78 | c.sendETCSyncNTF(header.From) 79 | } 80 | 81 | // HANDLE_NODES_INFO_NTF 每个节点会同步自己的状态过来 82 | func (c *Center) HANDLE_NODES_INFO_NTF(header *msg.MessageHeader, buffer []byte) { 83 | ntf := &msg.NodesInfoNTF{} 84 | err := proto.Unmarshal(buffer, ntf) 85 | if err != nil { 86 | return 87 | } 88 | cluster.SetNodes(ntf.Nodes) 89 | c.Net.RefreshNodesAddress() 90 | c.pushNodesInfo() 91 | } 92 | 93 | func (c *Center) pushNodesInfo() { 94 | nodes := cluster.GetNodes() 95 | ntf := &msg.NodesInfoNTF{Nodes: nodes} 96 | for serverID, node := range nodes { 97 | if serverID != int(global.CenterID) { 98 | if node.NodeState == msg.NodeState_Ready || node.NodeState == msg.NodeState_Running { 99 | c.Net.NotifyServer(msg.CMD_NODES_INFO_NTF, ntf, int32(serverID)) 100 | } 101 | } 102 | } 103 | c.printServerState() 104 | } 105 | 106 | // HANDLE_KEEP_ALIVE 心跳消息 107 | func (c *Center) HANDLE_KEEP_ALIVE(header *msg.MessageHeader, buffer []byte) { 108 | message := &msg.KeepAlive{} 109 | err := proto.Unmarshal(buffer, message) 110 | if err != nil { 111 | return 112 | } 113 | serverID := message.ServerID 114 | c.keepAliveTime[serverID] = time.Now().UnixNano() + timeout 115 | info := cluster.GetNode(serverID) 116 | if info.NodeState == msg.NodeState_Offline { 117 | info.NodeState = msg.NodeState_Running 118 | cluster.RefreshRunning() 119 | cluster.RefreshRunningZones() 120 | c.pushNodesInfo() 121 | } 122 | } 123 | 124 | // HANDLE_RELOAD_ETC_REQ 重载配置 125 | func (c *Center) HANDLE_RELOAD_ETC_REQ(header *msg.MessageHeader, buffer []byte) { 126 | defer c.Net.ResponceBytes(header, make([]byte, 1)) 127 | dir, _ := os.Getwd() 128 | etcmgr.Instance.Load(dir + "/etc") 129 | c.broadcastETCSyncNTF() 130 | } 131 | 132 | func (c *Center) tickServerState() { 133 | go func() { 134 | for { 135 | time.Sleep(time.Millisecond * 10) 136 | now := time.Now().UnixNano() 137 | stateDirty := false 138 | for serverID, info := range cluster.GetNodes() { 139 | if int32(serverID) == global.CenterID { 140 | continue 141 | } 142 | if info.NodeState == msg.NodeState_Running { 143 | if c.keepAliveTime[serverID] == 0 { 144 | c.keepAliveTime[serverID] = now 145 | } 146 | if c.keepAliveTime[serverID] < now { 147 | info.NodeState = msg.NodeState_Offline 148 | serverType := etcmgr.Instance.GetServerType(int32(serverID)) 149 | logger.Info(fmt.Sprintf("Node Offline ID:%d Type:%s", serverID, serverType)) 150 | stateDirty = true 151 | } 152 | } 153 | } 154 | if stateDirty { 155 | cluster.RefreshRunning() 156 | cluster.RefreshRunningZones() 157 | c.pushNodesInfo() 158 | } 159 | } 160 | }() 161 | } 162 | 163 | func (c *Center) broadcastETCSyncNTF() { 164 | ntf := etcmgr.Instance.GenerateETCSyncNTF() 165 | for serverID, info := range cluster.GetNodes() { 166 | if int32(serverID) != global.CenterID { 167 | if info.NodeState == msg.NodeState_Ready || info.NodeState == msg.NodeState_Running { 168 | c.Net.NotifyServer(msg.CMD_ETC_SYNC_NTF, ntf, int32(serverID)) 169 | } 170 | } 171 | } 172 | } 173 | 174 | func (c *Center) sendETCSyncNTF(serverID int32) { 175 | ntf := etcmgr.Instance.GenerateETCSyncNTF() 176 | c.Net.NotifyServer(msg.CMD_ETC_SYNC_NTF, ntf, int32(serverID)) 177 | } 178 | 179 | func (c *Center) broadcastResetConnectionNTF(server int32) { 180 | ntf := &msg.ResetConnectionNTF{ 181 | ServerID: server, 182 | } 183 | for serverID, info := range cluster.GetNodes() { 184 | if int32(serverID) != global.CenterID { 185 | if info.NodeState != msg.NodeState_Unset { 186 | c.Net.NotifyServer(msg.CMD_RESET_CONNECTION_NTF, ntf, int32(serverID)) 187 | } 188 | } 189 | } 190 | } 191 | 192 | func (c *Center) printServerState() { 193 | states := "[ServerState] " 194 | for _, svr := range etcmgr.Instance.Servers() { 195 | if svr.ServerID == 0 { 196 | continue 197 | } 198 | info := cluster.GetNode(svr.ServerID) 199 | running := info.NodeState == msg.NodeState_Running 200 | ready := info.NodeState == msg.NodeState_Ready 201 | offline := info.NodeState == msg.NodeState_Offline 202 | if running { 203 | states = states + fmt.Sprintf("%d%s ", svr.ServerID, "O") 204 | } else if ready { 205 | states = states + fmt.Sprintf("%d%s ", svr.ServerID, "R") 206 | } else if offline { 207 | states = states + fmt.Sprintf("%d%s ", svr.ServerID, "?") 208 | } else { 209 | states = states + fmt.Sprintf("%d%s ", svr.ServerID, "X") 210 | } 211 | } 212 | logger.Info(states) 213 | } 214 | -------------------------------------------------------------------------------- /proto/protobuf/timestamp.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "types"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "TimestampProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Timestamp represents a point in time independent of any time zone or local 44 | // calendar, encoded as a count of seconds and fractions of seconds at 45 | // nanosecond resolution. The count is relative to an epoch at UTC midnight on 46 | // January 1, 1970, in the proleptic Gregorian calendar which extends the 47 | // Gregorian calendar backwards to year one. 48 | // 49 | // All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap 50 | // second table is needed for interpretation, using a [24-hour linear 51 | // smear](https://developers.google.com/time/smear). 52 | // 53 | // The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By 54 | // restricting to that range, we ensure that we can convert to and from [RFC 55 | // 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. 56 | // 57 | // # Examples 58 | // 59 | // Example 1: Compute Timestamp from POSIX `time()`. 60 | // 61 | // Timestamp timestamp; 62 | // timestamp.set_seconds(time(NULL)); 63 | // timestamp.set_nanos(0); 64 | // 65 | // Example 2: Compute Timestamp from POSIX `gettimeofday()`. 66 | // 67 | // struct timeval tv; 68 | // gettimeofday(&tv, NULL); 69 | // 70 | // Timestamp timestamp; 71 | // timestamp.set_seconds(tv.tv_sec); 72 | // timestamp.set_nanos(tv.tv_usec * 1000); 73 | // 74 | // Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. 75 | // 76 | // FILETIME ft; 77 | // GetSystemTimeAsFileTime(&ft); 78 | // UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 79 | // 80 | // // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z 81 | // // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. 82 | // Timestamp timestamp; 83 | // timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); 84 | // timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); 85 | // 86 | // Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. 87 | // 88 | // long millis = System.currentTimeMillis(); 89 | // 90 | // Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) 91 | // .setNanos((int) ((millis % 1000) * 1000000)).build(); 92 | // 93 | // 94 | // Example 5: Compute Timestamp from current time in Python. 95 | // 96 | // timestamp = Timestamp() 97 | // timestamp.GetCurrentTime() 98 | // 99 | // # JSON Mapping 100 | // 101 | // In JSON format, the Timestamp type is encoded as a string in the 102 | // [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the 103 | // format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" 104 | // where {year} is always expressed using four digits while {month}, {day}, 105 | // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional 106 | // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), 107 | // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone 108 | // is required. A proto3 JSON serializer should always use UTC (as indicated by 109 | // "Z") when printing the Timestamp type and a proto3 JSON parser should be 110 | // able to accept both UTC and other timezones (as indicated by an offset). 111 | // 112 | // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 113 | // 01:30 UTC on January 15, 2017. 114 | // 115 | // In JavaScript, one can convert a Date object to this format using the 116 | // standard 117 | // [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) 118 | // method. In Python, a standard `datetime.datetime` object can be converted 119 | // to this format using 120 | // [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with 121 | // the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use 122 | // the Joda Time's [`ISODateTimeFormat.dateTime()`]( 123 | // http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D 124 | // ) to obtain a formatter capable of generating timestamps in this format. 125 | // 126 | // 127 | message Timestamp { 128 | // Represents seconds of UTC time since Unix epoch 129 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 130 | // 9999-12-31T23:59:59Z inclusive. 131 | int64 seconds = 1; 132 | 133 | // Non-negative fractions of a second at nanosecond resolution. Negative 134 | // second values with fractions must still have non-negative nanos values 135 | // that count forward in time. Must be from 0 to 999,999,999 136 | // inclusive. 137 | int32 nanos = 2; 138 | } 139 | -------------------------------------------------------------------------------- /proto/protobuf/type.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | import "google/protobuf/any.proto"; 36 | import "google/protobuf/source_context.proto"; 37 | 38 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 39 | option cc_enable_arenas = true; 40 | option java_package = "com.google.protobuf"; 41 | option java_outer_classname = "TypeProto"; 42 | option java_multiple_files = true; 43 | option objc_class_prefix = "GPB"; 44 | option go_package = "types"; 45 | 46 | // A protocol buffer message type. 47 | message Type { 48 | // The fully qualified message name. 49 | string name = 1; 50 | // The list of fields. 51 | repeated Field fields = 2; 52 | // The list of types appearing in `oneof` definitions in this type. 53 | repeated string oneofs = 3; 54 | // The protocol buffer options. 55 | repeated Option options = 4; 56 | // The source context. 57 | SourceContext source_context = 5; 58 | // The source syntax. 59 | Syntax syntax = 6; 60 | } 61 | 62 | // A single field of a message type. 63 | message Field { 64 | // Basic field types. 65 | enum Kind { 66 | // Field type unknown. 67 | TYPE_UNKNOWN = 0; 68 | // Field type double. 69 | TYPE_DOUBLE = 1; 70 | // Field type float. 71 | TYPE_FLOAT = 2; 72 | // Field type int64. 73 | TYPE_INT64 = 3; 74 | // Field type uint64. 75 | TYPE_UINT64 = 4; 76 | // Field type int32. 77 | TYPE_INT32 = 5; 78 | // Field type fixed64. 79 | TYPE_FIXED64 = 6; 80 | // Field type fixed32. 81 | TYPE_FIXED32 = 7; 82 | // Field type bool. 83 | TYPE_BOOL = 8; 84 | // Field type string. 85 | TYPE_STRING = 9; 86 | // Field type group. Proto2 syntax only, and deprecated. 87 | TYPE_GROUP = 10; 88 | // Field type message. 89 | TYPE_MESSAGE = 11; 90 | // Field type bytes. 91 | TYPE_BYTES = 12; 92 | // Field type uint32. 93 | TYPE_UINT32 = 13; 94 | // Field type enum. 95 | TYPE_ENUM = 14; 96 | // Field type sfixed32. 97 | TYPE_SFIXED32 = 15; 98 | // Field type sfixed64. 99 | TYPE_SFIXED64 = 16; 100 | // Field type sint32. 101 | TYPE_SINT32 = 17; 102 | // Field type sint64. 103 | TYPE_SINT64 = 18; 104 | } 105 | 106 | // Whether a field is optional, required, or repeated. 107 | enum Cardinality { 108 | // For fields with unknown cardinality. 109 | CARDINALITY_UNKNOWN = 0; 110 | // For optional fields. 111 | CARDINALITY_OPTIONAL = 1; 112 | // For required fields. Proto2 syntax only. 113 | CARDINALITY_REQUIRED = 2; 114 | // For repeated fields. 115 | CARDINALITY_REPEATED = 3; 116 | }; 117 | 118 | // The field type. 119 | Kind kind = 1; 120 | // The field cardinality. 121 | Cardinality cardinality = 2; 122 | // The field number. 123 | int32 number = 3; 124 | // The field name. 125 | string name = 4; 126 | // The field type URL, without the scheme, for message or enumeration 127 | // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. 128 | string type_url = 6; 129 | // The index of the field type in `Type.oneofs`, for message or enumeration 130 | // types. The first type has index 1; zero means the type is not in the list. 131 | int32 oneof_index = 7; 132 | // Whether to use alternative packed wire representation. 133 | bool packed = 8; 134 | // The protocol buffer options. 135 | repeated Option options = 9; 136 | // The field JSON name. 137 | string json_name = 10; 138 | // The string value of the default value of this field. Proto2 syntax only. 139 | string default_value = 11; 140 | } 141 | 142 | // Enum type definition. 143 | message Enum { 144 | // Enum type name. 145 | string name = 1; 146 | // Enum value definitions. 147 | repeated EnumValue enumvalue = 2; 148 | // Protocol buffer options. 149 | repeated Option options = 3; 150 | // The source context. 151 | SourceContext source_context = 4; 152 | // The source syntax. 153 | Syntax syntax = 5; 154 | } 155 | 156 | // Enum value definition. 157 | message EnumValue { 158 | // Enum value name. 159 | string name = 1; 160 | // Enum value number. 161 | int32 number = 2; 162 | // Protocol buffer options. 163 | repeated Option options = 3; 164 | } 165 | 166 | // A protocol buffer option, which can be attached to a message, field, 167 | // enumeration, etc. 168 | message Option { 169 | // The option's name. For protobuf built-in options (options defined in 170 | // descriptor.proto), this is the short name. For example, `"map_entry"`. 171 | // For custom options, it should be the fully-qualified name. For example, 172 | // `"google.api.http"`. 173 | string name = 1; 174 | // The option's value packed in an Any message. If the value is a primitive, 175 | // the corresponding wrapper type defined in google/protobuf/wrappers.proto 176 | // should be used. If the value is an enum, it should be stored as an int32 177 | // value using the google.protobuf.Int32Value type. 178 | Any value = 2; 179 | } 180 | 181 | // The syntax in which a protocol buffer element is defined. 182 | enum Syntax { 183 | // Syntax `proto2`. 184 | SYNTAX_PROTO2 = 0; 185 | // Syntax `proto3`. 186 | SYNTAX_PROTO3 = 1; 187 | } 188 | --------------------------------------------------------------------------------