├── bin ├── log │ └── .gitignore ├── gamedata │ └── .gitignore ├── server └── conf │ └── server.json ├── doc ├── gameServerLogin.png ├── gameServerStruct.jpg ├── gameServerGameInit.png ├── gameServerArchitecture.png ├── README_ZH.md └── 网络同步.md ├── src ├── client │ ├── simpleClient │ ├── useMemDB.go │ ├── useMongodb.go │ ├── otherMongodb.go │ └── simpleClient.go ├── server │ ├── gate │ │ ├── external.go │ │ ├── internal │ │ │ └── module.go │ │ └── router.go │ ├── game │ │ ├── external.go │ │ └── internal │ │ │ ├── module.go │ │ │ ├── chanrpc.go │ │ │ └── handler.go │ ├── login │ │ ├── external.go │ │ └── internal │ │ │ ├── module.go │ │ │ └── handler.go │ ├── .idea │ │ ├── modules.xml │ │ ├── server.iml │ │ ├── misc.xml │ │ ├── libraries │ │ │ └── GOPATH__server_.xml │ │ └── workspace.xml │ ├── msg │ │ ├── UserData.go │ │ ├── car.go │ │ └── msg.go │ ├── gamedata │ │ └── reader.go │ ├── base │ │ └── skeleton.go │ ├── conf │ │ ├── conf.go │ │ └── json.go │ └── main.go └── test │ └── mongodb_test.go ├── pkg └── darwin_amd64 │ └── server │ ├── base.a │ ├── conf.a │ ├── game.a │ ├── gate.a │ ├── msg.a │ ├── login.a │ ├── game │ └── internal.a │ ├── gate │ └── internal.a │ └── login │ └── internal.a ├── .idea ├── vcs.xml ├── modules.xml ├── leafserver.iml ├── misc.xml ├── libraries │ └── GOPATH__leafserver_.xml └── workspace.xml ├── README.md ├── README_CN.md └── LICENSE /bin/log/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bin/gamedata/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bin/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/bin/server -------------------------------------------------------------------------------- /doc/gameServerLogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/doc/gameServerLogin.png -------------------------------------------------------------------------------- /src/client/simpleClient: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/src/client/simpleClient -------------------------------------------------------------------------------- /doc/gameServerStruct.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/doc/gameServerStruct.jpg -------------------------------------------------------------------------------- /doc/gameServerGameInit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/doc/gameServerGameInit.png -------------------------------------------------------------------------------- /doc/gameServerArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/doc/gameServerArchitecture.png -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/base.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/base.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/conf.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/conf.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/game.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/game.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/gate.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/gate.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/msg.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/msg.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/login.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/login.a -------------------------------------------------------------------------------- /bin/conf/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "LogLevel": "debug", 3 | "LogPath": "", 4 | "TCPAddr": "127.0.0.1:3389", 5 | "MaxConnNum": 20000 6 | } 7 | -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/game/internal.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/game/internal.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/gate/internal.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/gate/internal.a -------------------------------------------------------------------------------- /pkg/darwin_amd64/server/login/internal.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamgoing/gameServer/HEAD/pkg/darwin_amd64/server/login/internal.a -------------------------------------------------------------------------------- /src/server/gate/external.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "server/gate/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/server/game/external.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import ( 4 | "server/game/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/server/login/external.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "server/login/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | 10 | UserDB = internal.Userdb 11 | ChanRPC = internal.ChanRPC 12 | UserAgent = internal.UserAgent 13 | 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /src/client/useMemDB.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var userDB map[string]string 6 | func main() { 7 | userDB=make(map[string]string) 8 | fmt.Println(userDB["wang"]) 9 | if userDB["wang"] ==""{ 10 | userDB["wang"]="123" 11 | } 12 | fmt.Println(userDB) 13 | } 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/server/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/server/msg/UserData.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | "image" 5 | "time" 6 | ) 7 | 8 | ///保存和同步用户 9 | 10 | type UserData struct { 11 | Img image.Image `json:"img"`///图片数据 12 | OnlineTime time.Duration `json:"online_time"`///用户在线的时间 13 | Friends map[string]string ///用户的好友 14 | 15 | ///拥有的在线的车辆 16 | } 17 | -------------------------------------------------------------------------------- /src/test/mongodb_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import "testing" 4 | import "gopkg.in/mgo.v2" 5 | 6 | 7 | func TestConnDB(t *testing.T) { 8 | 9 | session,err:=mgo.Dial("localhost") 10 | if err != nil { 11 | 12 | ///出现了错误 13 | return 14 | } 15 | anotherSession:=session.Copy() 16 | defer anotherSession.Close() 17 | session.DB("game").C("col") 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.idea/leafserver.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/server/.idea/server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/server/game/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/name5566/leaf/module" 5 | "server/base" 6 | 7 | ) 8 | 9 | var ( 10 | skeleton = base.NewSkeleton() 11 | ChanRPC = skeleton.ChanRPCServer 12 | ) 13 | 14 | type Module struct { 15 | *module.Skeleton 16 | } 17 | 18 | func (m *Module) OnInit() { 19 | m.Skeleton = skeleton 20 | } 21 | 22 | func (m *Module) OnDestroy() { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/server/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /src/server/gamedata/reader.go: -------------------------------------------------------------------------------- 1 | package gamedata 2 | 3 | import ( 4 | "github.com/name5566/leaf/log" 5 | "github.com/name5566/leaf/recordfile" 6 | "reflect" 7 | ) 8 | 9 | func readRf(st interface{}) *recordfile.RecordFile { 10 | rf, err := recordfile.New(st) 11 | if err != nil { 12 | log.Fatal("%v", err) 13 | } 14 | fn := reflect.TypeOf(st).Name() + ".txt" 15 | err = rf.Read("gamedata/" + fn) 16 | if err != nil { 17 | log.Fatal("%v: %v", fn, err) 18 | } 19 | 20 | return rf 21 | } 22 | -------------------------------------------------------------------------------- /src/server/login/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/name5566/leaf/module" 5 | "server/base" 6 | "github.com/name5566/leaf/gate" 7 | ) 8 | 9 | var ( 10 | skeleton = base.NewSkeleton() 11 | ChanRPC = skeleton.ChanRPCServer 12 | ) 13 | 14 | type Module struct { 15 | *module.Skeleton 16 | } 17 | 18 | func (m *Module) OnInit() { 19 | m.Skeleton = skeleton 20 | UserAgent=make(map[string]gate.Agent) 21 | } 22 | 23 | func (m *Module) OnDestroy() { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/server/base/skeleton.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "github.com/name5566/leaf/chanrpc" 5 | "github.com/name5566/leaf/module" 6 | "server/conf" 7 | 8 | ) 9 | 10 | func NewSkeleton() *module.Skeleton { 11 | skeleton := &module.Skeleton{ 12 | GoLen: conf.GoLen, 13 | TimerDispatcherLen: conf.TimerDispatcherLen, 14 | AsynCallLen: conf.AsynCallLen, 15 | ChanRPCServer: chanrpc.NewServer(conf.ChanRPCLen), 16 | } 17 | skeleton.Init() 18 | return skeleton 19 | } 20 | -------------------------------------------------------------------------------- /src/server/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | var ( 9 | // log conf 10 | LogFlag = log.LstdFlags 11 | 12 | // gate conf 13 | PendingWriteNum = 2000 14 | MaxMsgLen uint32 = 4096 15 | HTTPTimeout = 10 * time.Second 16 | LenMsgLen = 2 17 | LittleEndian = false 18 | 19 | // skeleton conf 20 | GoLen = 10000 21 | TimerDispatcherLen = 10000 22 | AsynCallLen = 10000 23 | ChanRPCLen = 10000 24 | ) 25 | -------------------------------------------------------------------------------- /doc/README_ZH.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | ## leaf服务器架构 4 | 5 | 通常游戏服务器开发采用分布式架构,即服务器整体被划分成不同的模块, 6 | 各个模块承担不同的功能,模块之间采用TCP进行交互,单点故障不会导致 7 | 服务器整体奔溃,leaf采用一体式的游戏服务器架构,模块之间采用chanRPC 8 | 进行交互,是一种简易高效的服务器. 9 | 10 | 11 | 与一些Web服务器不同,Leaf运行的大部分数据,在内存中,保证了实时的交互性, 12 | 采用mongodb是为了做一些持久化的数据,比如说登录认证等. 13 | 14 | 15 | 16 | ![](./gameServerStruct.jpg) 17 | 以上是比较常见的结构,客户端登录的时候,连接GateServer,然后由GateServer去连接LoginServer进行登录。 18 | 登录后通过CenterServer转发到GameServer(GameServer即是服务器大区)。而其中的DCServer,主要的功能是 19 | 缓存玩家角色数据,保证角色数据能快速的读取和保存。LogServer便是保存日志的了。 20 | 21 | -------------------------------------------------------------------------------- /src/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/name5566/leaf" 5 | lconf "github.com/name5566/leaf/conf" 6 | "server/conf" 7 | "server/game" 8 | "server/gate" 9 | "server/login" 10 | ) 11 | 12 | func main() { 13 | lconf.LogLevel = conf.Server.LogLevel 14 | lconf.LogPath = conf.Server.LogPath 15 | lconf.LogFlag = conf.LogFlag 16 | lconf.ConsolePort = conf.Server.ConsolePort 17 | lconf.ProfilePath = conf.Server.ProfilePath 18 | 19 | leaf.Run( 20 | game.Module, 21 | gate.Module, 22 | login.Module, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/server/msg/car.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | 4 | ///用户的数据,carw 5 | type Car struct { 6 | CarID int `json:"car_id"` 7 | X float32 `json:"x"` 8 | Y float32 `json:"y"` 9 | A float32 `json:"a"` 10 | V float32 `json:"v"` 11 | } 12 | 13 | /***@brief 14 | 目前设定汽车的简单运动,汽车的初始位置位于(0,0) 按照如下的坐标系,进行运动 15 | y 16 | ^ 17 | | 18 | | 19 | | 20 | | 21 | | ---------->x 22 | */ 23 | 24 | ///汽车的运动的函数 25 | func (s *Car)Up() { 26 | s.Y++ 27 | } 28 | 29 | func (s *Car) Left() { 30 | s.X-- 31 | } 32 | 33 | func (s *Car) Right() { 34 | s.X++ 35 | } 36 | 37 | func (s *Car) Down() { 38 | s.Y-- 39 | } -------------------------------------------------------------------------------- /src/server/.idea/libraries/GOPATH__server_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/server/conf/json.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/name5566/leaf/log" 6 | "io/ioutil" 7 | ) 8 | 9 | var Server struct { 10 | LogLevel string 11 | LogPath string 12 | WSAddr string 13 | CertFile string 14 | KeyFile string 15 | TCPAddr string 16 | MaxConnNum int 17 | ConsolePort int 18 | ProfilePath string 19 | } 20 | 21 | func init() { 22 | data, err := ioutil.ReadFile("conf/server.json") 23 | if err != nil { 24 | log.Fatal("%v", err) 25 | } 26 | err = json.Unmarshal(data, &Server) 27 | if err != nil { 28 | log.Fatal("%v", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/server/game/internal/chanrpc.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/name5566/leaf/gate" 5 | ) 6 | 7 | ///设置gate.Agent到空struct的映射 8 | var agents=make(map[gate.Agent]struct{}) 9 | var users=make(map[string]gate.Agent) 10 | 11 | 12 | func init() { 13 | skeleton.RegisterChanRPC("NewAgent", rpcNewAgent) 14 | skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent) 15 | } 16 | 17 | func rpcNewAgent(args []interface{}) { 18 | a := args[0].(gate.Agent) 19 | agents[a]= struct{}{} 20 | 21 | } 22 | 23 | func rpcCloseAgent(args []interface{}) { 24 | a := args[0].(gate.Agent) 25 | ///从agents里删除 26 | delete(agents,a) 27 | } 28 | -------------------------------------------------------------------------------- /.idea/libraries/GOPATH__leafserver_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/server/gate/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/name5566/leaf/gate" 5 | "server/conf" 6 | "server/game" 7 | "server/msg" 8 | ) 9 | 10 | type Module struct { 11 | *gate.Gate 12 | } 13 | 14 | func (m *Module) OnInit() { 15 | m.Gate = &gate.Gate{ 16 | MaxConnNum: conf.Server.MaxConnNum, 17 | PendingWriteNum: conf.PendingWriteNum, 18 | MaxMsgLen: conf.MaxMsgLen, 19 | WSAddr: conf.Server.WSAddr, 20 | HTTPTimeout: conf.HTTPTimeout, 21 | CertFile: conf.Server.CertFile, 22 | KeyFile: conf.Server.KeyFile, 23 | TCPAddr: conf.Server.TCPAddr, 24 | LenMsgLen: conf.LenMsgLen, 25 | LittleEndian: conf.LittleEndian, 26 | Processor: msg.Processor, 27 | AgentChanRPC: game.ChanRPC, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GameServer 2 | =========== 3 | A game server based on [Leaf framework](https://github.com/name5566/leaf). 4 | 5 | ## Brief 6 | This gameServer is a server for a car racing game,which client is implemented by Unity3D & C#,gameServer is implemented 7 | by golang,and MongoDB. 8 | 9 | ## Document 10 | 11 | [中文文档](./README_CN.md) 12 | 13 | ### Game Synchronization 14 | 15 | An efficient synchronization mechanism 16 | 17 | ## Reference 18 | 19 | [中文文档](./README_CN.md) 20 | 21 | [leaf game server](https://github.com/name5566/leaf) 22 | 23 | [中文简介](./doc/README_ZH.md) 24 | 25 | [网络同步](./doc/网络同步.md) 26 | 27 | Licensing 28 | -------- 29 | 30 | Leaf server is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/name5566/leafserver/blob/master/LICENSE) for the full license text. 31 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # 赛车游戏服务器(GameServer) 2 | > 赛车游戏服务器端,golang语言实现 3 | 4 | ## 简介 5 | 6 | 多人赛车游戏服务器,基于开源[Leaf](https://github.com/name5566/leaf)游戏服务器框架的开发.该项目采用golang语言进行开发,利用了golang优秀并发性. 7 | 8 | 该游戏客户端采用Unity3D +C#开发,服务器端最终部署在阿里云上面,是一个个人的学习项目. 9 | 10 | ## 项目架构 11 | ![architecture](./doc/gameServerArchitecture.png) 12 | 13 | ## 项目技术 14 | 15 | + leaf开源框架,其中主要内核实现为chanRPC,基于golang channel的RPC模块间通信机制 16 | + 客户端与服务器交互采用json消息格式,易于扩展. 17 | + 游戏服务器使用了模块机制,不同的模块运行在不同的goroutine中,模块之间的通信采用chanRPC 18 | + 游戏服务器端中,用户实时的数据保存在内存中,保证交互的实时性,其他用户的基本资料保存使用mongoDB进行数据的持久化 19 | + 轻量级日志功能,可以记录服务器中的相关信息 20 | 21 | ## 支持功能 22 | + 用户登录注册认证 23 | + 游戏中添加好友功能,与好友聊天功能 24 | + 多人在线匹配,多人实时赛车竞赛 25 | + 个人资料信息的数据持久化 26 | 27 | ## 实现方案 28 | ### 登录认证模块(登录,注册,退出) 29 | + 客户端将相应的认证信息发送到登录认证模块 30 | + 登录认证模块通过与MongoDB数据库的交互进行认证 31 | 32 | + @TODO MongoDB存储密码时,进行SHA或者MD5的单项哈希存储密码,增加系统的安全性 33 | 34 | ### 游戏主逻辑模块 35 | + 游戏核心多人赛车竞赛,采用 __帧同步方案__ 来实现游戏数据的同步 36 | 37 | ### 游戏聊天模块 38 | + 服务器主要负责转发客户端到客户端之间的信息 39 | + @TODO 转发信息时,加密信息,或者过滤敏感信息 40 | 41 | ## 参考 42 | 43 | [leaf服务器文档](https://github.com/name5566/leaf/blob/master/TUTORIAL_ZH.md) 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/client/useMongodb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopkg.in/mgo.v2" 5 | "fmt" 6 | "gopkg.in/mgo.v2/bson" 7 | ) 8 | 9 | type Book struct { 10 | Title string 11 | Detail string 12 | } 13 | //type Person struct { 14 | // id int `json:"id"` 15 | // name string `json:"name"` 16 | // age uint `json:"age"` 17 | //} 18 | 19 | const URL = "localhost" 20 | func main() { 21 | session, err := mgo.Dial(URL) //连接服务器 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c := session.DB("mydb").C("account") //选择ChatRoom库的account表 27 | 28 | c.Insert(map[string]interface{}{"id": 7, "name": "tongjh", "age": 25}) //增 29 | 30 | //objid := bson.ObjectIdHex("55b97a2e16bc6197ad9cad59") 31 | 32 | //c.RemoveId(objid) //删除 33 | 34 | //c.UpdateId(objid, map[string]interface{}{"id": 8, "name": "aaaaa", "age": 30}) //改 35 | 36 | //var one map[string]interface{} 37 | //c.FindId(objid).One(&one) //查询符合条件的一行数据 38 | //fmt.Println(one) 39 | 40 | var result []map[string]interface{} 41 | c.Find(nil).All(&result) //查询全部 42 | var man Person 43 | c.Find(bson.M{"age":25}).One(&man) 44 | fmt.Println(result) 45 | fmt.Println(man) 46 | } -------------------------------------------------------------------------------- /src/server/gate/router.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "server/msg" 5 | "server/game" 6 | 7 | "server/login" 8 | ) 9 | 10 | ///gate模块决定了,某一个消息具体交给内部某一个模块来处理 11 | func init() { 12 | 13 | ///指定消息到game模块,模块间使用ChanRPC通信 14 | msg.Processor.SetRouter(&msg.Ok{},game.ChanRPC) 15 | 16 | msg.Processor.SetRouter(&msg.SignUp{},login.ChanRPC) 17 | 18 | msg.Processor.SetRouter(&msg.SignIn{},login.ChanRPC) 19 | 20 | msg.Processor.SetRouter(&msg.Up{},game.ChanRPC) 21 | 22 | msg.Processor.SetRouter(&msg.Left{},game.ChanRPC) 23 | 24 | msg.Processor.SetRouter(&msg.Right{},game.ChanRPC) 25 | 26 | ///同步用户个人信息在login的个人模块中 27 | msg.Processor.SetRouter(&msg.UpLoad{},login.ChanRPC) 28 | 29 | ///联网模式匹配多人消息处理在game模块中 30 | msg.Processor.SetRouter(&msg.Match{},game.ChanRPC) 31 | 32 | ///管理员模式,暂时在游戏主逻辑,game.ChanRPC中处理 33 | msg.Processor.SetRouter(&msg.Admin{},game.ChanRPC) 34 | 35 | ///用户向其他用户发送窗口消息 36 | msg.Processor.SetRouter(&msg.UserMsg{},game.ChanRPC) 37 | 38 | ///用户向服务器发送确认参加匹配模式 39 | msg.Processor.SetRouter(&msg.MatchMode{},game.ChanRPC) 40 | 41 | msg.Processor.SetRouter(&msg.Order{},game.ChanRPC) 42 | 43 | msg.Processor.SetRouter(&msg.Finished{},game.ChanRPC) 44 | } 45 | -------------------------------------------------------------------------------- /doc/网络同步.md: -------------------------------------------------------------------------------- 1 | # 网络同步 2 | 3 | ## 网络时延 4 | 由于网络时延的存在,无法完成完全的同步 5 | 6 | ## 同步的关键点 7 | 8 | ### 同步的对象: 9 | 10 | 1. 单个用户:创建用户消息,私聊消息 11 | 2. 同屏:发送给同一个区域的所有用户,常见的主城 12 | 3. 同服:发给整个游戏世界中的所有用户消息,游戏中的喇叭 13 | 14 | ### 同步的数据 15 | 1. 命令同步,即时发送.client或者server都可以发起,比如一个用户开始行走,可以发送一个掉行走的命令给server,server校验合法后将该条命令转发给其他client中的用户 16 | 2. 状态同步:按照一定的频率,可以区分细节度发送 17 | 18 | ### 同步的周期 19 | 1. 即时 20 | 2. 按照一定的频率 21 | 22 | ## 同步问题 23 | ### 网络延迟 24 | 网络传输过程中一定会产生延迟,和当发生延迟后如何矫正客户端和服务器端 25 | ### 网络带宽 26 | 1. 对象的第一次状态同步与平时的状态同步需要区别对待,第一次状态同步需要创建相应的客户端对象,需要完整的客户端信息,以后同步只需要传递少量的,经常改变的信息 27 | 2. 每个协议内容的优化,尽可能减少传输的内容 28 | 3. 某些复杂的数据内容,可以通过传送基本数据,服务器端进行相同算法计算得到相同内容 29 | 30 | ### 反外挂 31 | 1. 关键游戏逻辑只在服务器端完成,但也要尽量保证客户端的流程性 32 | 2. 协议要加密,秘钥频繁改变 33 | 3. 服务器端收到不合理的数据,立即矫正 34 | 35 | ## 常见的同步策略 36 | 37 | ### 时间轴同步 38 | >p2p 39 | 40 | 1. A产生命令,发给B的同时,为了增加本地的流畅度,自己预执行命令,B收到消息后,将使用同样的算法执行命令. 41 | 2. 为了克服延迟,当A发送命令之后等待一点时间,到B接收命令是,A和B同时执行命令. 42 | 43 | ### 帧同步 44 | > p2p 45 | 46 | 帧同步技术是早期RTS游戏常用的一种同步技术。与状态同步不同的是,帧同步只同步操作,其大部分游戏逻辑都在客户端上实现,服务器主要负责广播和验证操作,有着逻辑直观易实现、数据量少、可重播等优点。 47 | 48 | 客户端逻辑帧保持一致.比如A,B均从第0逻辑帧开始,在B端,如果更新逻辑到第m帧时,需要A的数据,那么此时进行行锁帧,不再继续执行逻辑,直到接受到从A发送过来的第m帧数据,处理后才继续执行第m+1帧. 49 | 50 | 优点:保证了逻辑帧的"完全同步" 51 | 缺点:锁帧等待,当某个客户端网络较差则需要等待 52 | 53 | ### 服务器同步 54 | 服务器参与,server可以用来中转消息,可以作为仲裁者.即:client产生命令,并发送给server,server校验通过后,再将命令下发给相关的client. 55 | 56 | 缺点:发起命令的client可能会存在一定的等待时机,不能及时表现,但是可以发送之后进行执行,来提高客户端的流程度,但是服务器校验不通过则刚才执行无效. 57 | 58 | 优点:可以对client的命令进行处理或者校验,然后再下发给client这样所有的client行为基本保持一致,也不会出现作弊.伤害计算,升级等关键数据应该通过这种同步策略 59 | 60 | ### 状态同步 61 | 62 | 主要同步关键的信息,如血量,等等. 63 | ### 帧同步改进版 64 | > 王者荣耀 65 | 66 | + 修改客户端,input部分和logcial部分 67 | + input部分向服务器直接发送,服务器收到之后,发给客户端逻辑层消息 68 | + 服务器切帧,服务器控制时间轴驱动 69 | 70 | ##Reference 71 | [同步算法](http://www.skywind.me/blog/archives/131) 72 | 73 | [腾讯帧同步](http://gad.qq.com/article/detail/7195472) 74 | 75 | [腾讯游戏同步课](http://gad.qq.com/content/coursedetail/7193958) -------------------------------------------------------------------------------- /src/client/otherMongodb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import ( 5 | 6 | "fmt" 7 | "gopkg.in/mgo.v2" 8 | "gopkg.in/mgo.v2/bson" 9 | ) 10 | 11 | type Person struct { 12 | NAME string 13 | PHONE string 14 | } 15 | type Child struct { 16 | Name string 17 | Phone string 18 | Age string 19 | } 20 | type Men struct { 21 | Persons []Person 22 | } 23 | 24 | func main() { 25 | 26 | session, err := mgo.Dial("localhost") //连接数据库 27 | if err != nil { 28 | panic(err) 29 | } 30 | defer session.Close() 31 | session.SetMode(mgo.Monotonic, true) 32 | 33 | db := session.DB("mydb") //数据库名称 34 | collection := db.C("person") //如果该集合已经存在的话,则直接返回 35 | 36 | 37 | //*****集合中元素数目******** 38 | countNum, err := collection.Count() 39 | if err != nil { 40 | panic(err) 41 | } 42 | fmt.Println("Things objects count: ", countNum) 43 | 44 | //*******插入元素******* 45 | temp := &Person{ 46 | PHONE: "18811577546", 47 | NAME: "zhangzheHero", 48 | } 49 | //一次可以插入多个对象 插入两个Person对象 50 | err = collection.Insert(&Person{"Ale", "+55 53 8116 9639"}, temp) 51 | err = collection.Insert(&Child{"wang","+1234 ","12"}) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | //*****查询单条数据******* 57 | result := Person{} 58 | err = collection.Find(bson.M{"phone": "456"}).One(&result) 59 | err = collection.Find(bson.M{"name":"Ale"}).One(&result) 60 | fmt.Println("Phone:", result.NAME, result.PHONE) 61 | 62 | rechild := Child{} 63 | err = collection.Find(bson.M{"name":"wang"}).One(&rechild) 64 | fmt.Println(rechild) 65 | 66 | //*****查询多条数据******* 67 | var personAll Men //存放结果 68 | iter := collection.Find(nil).Iter() 69 | for iter.Next(&result) { 70 | fmt.Printf("Result: %v\n", result.NAME) 71 | personAll.Persons = append(personAll.Persons, result) 72 | } 73 | 74 | //*******更新数据********** 75 | err = collection.Update(bson.M{"name": "ccc"}, bson.M{"$set": bson.M{"name": "ddd"}}) 76 | err = collection.Update(bson.M{"name": "ddd"}, bson.M{"$set": bson.M{"phone": "12345678"}}) 77 | err = collection.Update(bson.M{"name": "aaa"}, bson.M{"phone": "1245", "name": "bbb"}) 78 | 79 | //******删除数据************ 80 | //_, err = collection.RemoveAll(bson.M{"name": "Ale"}) 81 | _, err = collection.RemoveAll(bson.M{"name":"wang"}) 82 | _, err = collection.RemoveAll(nil) 83 | } -------------------------------------------------------------------------------- /src/server/msg/msg.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import ( 4 | //"github.com/name5566/leaf/network" 5 | "github.com/name5566/leaf/network/json" 6 | ) 7 | 8 | //var Processor network.Processor 9 | var Processor = json.NewProcessor() 10 | 11 | //var SignUp_processor = json.NewProcessor() 12 | 13 | func init() { 14 | 15 | ///注册json消息 16 | Processor.Register(&Ok{}) 17 | Processor.Register(&SignUp{}) 18 | Processor.Register(&SignIn{}) 19 | Processor.Register(&State{}) 20 | Processor.Register(&Up{}) 21 | Processor.Register(&Right{}) 22 | Processor.Register(&Left{}) 23 | Processor.Register(&Down{}) 24 | Processor.Register(&Command{}) 25 | Processor.Register(&UpLoad{}) 26 | Processor.Register(&Match{}) 27 | Processor.Register(&Admin{}) 28 | Processor.Register(&UserMsg{}) 29 | Processor.Register(&MatchMode{}) 30 | Processor.Register(&Order{}) 31 | Processor.Register(&Finished{}) 32 | } 33 | 34 | ///结构体定义了一个JSON消息格式 35 | 36 | ///测试消息结构 37 | type Ok struct { 38 | Name string 39 | } 40 | 41 | type Admin struct { 42 | Name string `json:"name"` 43 | } 44 | 45 | ///注册消息结构 46 | type SignUp struct { 47 | 48 | Name string `json:"name"` 49 | Password string `json:"password"` 50 | } 51 | 52 | ///登录消息结构 53 | type SignIn struct { 54 | Name string `json:"name"` 55 | Password string `json:"password"` 56 | } 57 | 58 | ///用来同步用户的个人资料 59 | type UpLoad struct { 60 | ID int `json:"id"` 61 | Data UserData `json:"data"` 62 | } 63 | 64 | type MatchMode struct { 65 | Name string `json:"name"` ///参加匹配的玩家名 66 | } 67 | 68 | 69 | 70 | ///状态消息(向客户端发送)的状态信息 71 | const ( 72 | Login_success = iota 73 | Login_mismatch 74 | Login_noexist 75 | Login_duplicate 76 | 77 | SignUp_success 78 | SignUp_duplicate 79 | ) 80 | type State struct { 81 | Kind int `json:"kind"` 82 | } 83 | 84 | 85 | ///匹配消息 86 | type Match struct { 87 | ///匹配名 88 | Name string `json:"name"` 89 | Car int `json:"car"` 90 | } 91 | 92 | type Order struct { 93 | Name string `json:"name"` 94 | Val int `json:"val"` 95 | } 96 | 97 | ///测试的向前开车消息 98 | type Up struct { 99 | Direction float32 100 | } 101 | 102 | ///向左转 103 | type Left struct { 104 | Direction float32 105 | } 106 | 107 | ///向右转 108 | type Right struct { 109 | Direction float32 110 | } 111 | 112 | type Down struct { 113 | Direction float32 114 | } 115 | 116 | type UserMsg struct { 117 | Src string `json:"src"` 118 | Dst string `json:"dst"` 119 | Context string `json:"context"` 120 | } 121 | 122 | type Finished struct { 123 | Name string `json:"name"` 124 | Time int `json:"time"` 125 | } 126 | ///定义了具体的命令 127 | const ( 128 | UpCom = iota 129 | DownCom 130 | LeftCom 131 | RightCom 132 | ) 133 | 134 | type Command struct { 135 | CarID int `json:"car_id"` 136 | ID int `json:"id"` 137 | Cmd int `json:"cmd"` 138 | Val float32 `json:"val"` 139 | } -------------------------------------------------------------------------------- /src/server/login/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | 4 | 5 | import ( 6 | "reflect" 7 | //"github.com/name5566/leaf/db/mongodb" 8 | 9 | "server/msg" 10 | "github.com/name5566/leaf/gate" 11 | "github.com/name5566/leaf/log" 12 | 13 | "gopkg.in/mgo.v2" 14 | 15 | "gopkg.in/mgo.v2/bson" 16 | ) 17 | 18 | 19 | ///@todo 好友功能,聊天功能 20 | ///@todo 登出时,修改相关的变量,在内存数据库中,可以考虑使用redis缓存 21 | ///@todo 思考登录登出游戏流程 22 | 23 | 24 | ///表示当前是否使用,内存作为数据库 25 | var useMemoryDB bool = true 26 | ///全局的UserID,表示当前在线的用户 27 | var UserID int 28 | 29 | ///内存数据库,简单的map实现 30 | var Userdb map[string]string 31 | 32 | ///内存用户数据存储,简单的map实现 33 | var UserInfo map[string]msg.UserData 34 | 35 | var UserAgent map[string]gate.Agent 36 | 37 | const URL = "localhost" 38 | 39 | func handleMsg(m interface{}, h interface{}) { 40 | skeleton.RegisterChanRPC(reflect.TypeOf(m), h) 41 | } 42 | 43 | func init() { 44 | if !useMemoryDB { 45 | handleMsg(&msg.SignUp{},handleSignUpDB) 46 | handleMsg(&msg.SignIn{},handleSignInDB) 47 | }else { 48 | handleMsg(&msg.SignIn{},handleSignInMem) 49 | handleMsg(&msg.SignUp{},handleSignUpMem) 50 | } 51 | 52 | ///保持用户的数据 53 | handleMsg(&msg.UpLoad{},handleUploadDB) 54 | 55 | } 56 | 57 | func handleSignInMem(args []interface{}) { 58 | m:=args[0].(*msg.SignIn) 59 | a:=args[1].(gate.Agent) 60 | 61 | if Userdb[m.Name] == "" { 62 | ///不存在用户 63 | a.WriteMsg(&msg.State{msg.Login_noexist}) 64 | }else { 65 | if Userdb[m.Name]!=m.Password{ 66 | ///用户名密码错误 67 | a.WriteMsg(&msg.State{msg.Login_mismatch}) 68 | }else { 69 | 70 | ///登录成功后,使用户名和用户数据相关联. 71 | a.SetUserData(&msg.Car{CarID:UserID}) 72 | 73 | 74 | ///@bug nil map 75 | ///建立用户名与agent的映射 76 | UserAgent[m.Name] = a 77 | UserID++ 78 | a.WriteMsg(&msg.State{msg.Login_success}) 79 | } 80 | } 81 | 82 | } 83 | 84 | func handleSignUpMem(args []interface{}) { 85 | 86 | 87 | 88 | ///Comma-ok断言 89 | 90 | m:=args[0].(*msg.SignUp) 91 | 92 | a:=args[1].(gate.Agent) 93 | 94 | 95 | log.Debug("%s %v",a.RemoteAddr(),m) 96 | 97 | if Userdb==nil { 98 | Userdb=make(map[string]string) 99 | } 100 | if Userdb[m.Name]==""{ 101 | Userdb[m.Name]=m.Password 102 | //a.WriteMsg(&msg.SignUp{"ok","123"}) 103 | a.WriteMsg(&msg.State{msg.SignUp_success}) 104 | }else { 105 | a.WriteMsg(&msg.State{msg.SignUp_duplicate}) 106 | } 107 | } 108 | 109 | ///@todo 考虑,具体的登录流程, 110 | 111 | 112 | func handleSignUpDB(args []interface{}) { 113 | m:=args[0].(*msg.SignUp) 114 | 115 | 116 | 117 | ///获取消息的发送者 118 | a:=args[1].(gate.Agent) 119 | 120 | log.Debug("login module: sign up%v %v",m.Name,a) 121 | 122 | session,err:=mgo.Dial("localhost") 123 | defer session.Close() 124 | if err!=nil{ 125 | panic(err) 126 | } 127 | defer session.Close() 128 | 129 | c:=session.DB("mydb").C("try") 130 | 131 | 132 | //err =c.Find(bson.M{}) 133 | 134 | ///@todo 注册新用户时,可以添加加密模块和查看是否重复注册模块 135 | err = c.Insert(m) 136 | 137 | if err!=nil{ 138 | log.Error("insert mydb fail") 139 | //a.WriteMsg("signUp error") 140 | } 141 | 142 | 143 | ///注册成功,向客户端返回一个消息 144 | a.WriteMsg(&msg.SignUp{ 145 | Name:"client", 146 | }) 147 | 148 | 149 | } 150 | 151 | func handleSignInDB(args []interface{}) { 152 | m:=args[0].(*msg.SignIn) 153 | 154 | ///客户端地址 155 | a:=args[1].(gate.Agent) 156 | 157 | log.Debug("login module: sign in%v %v",m.Name,a) 158 | 159 | session,err:=mgo.Dial(URL) 160 | defer session.Close() 161 | if err!=nil{ 162 | panic(err) 163 | } 164 | defer session.Close() 165 | c:=session.DB("mydb").C("try") 166 | 167 | var tmpUsr map[string]interface{} 168 | 169 | 170 | err=c.Find(bson.M{"name":m.Name}).One(&tmpUsr) 171 | 172 | log.Debug("Sign in %v %v",tmpUsr["name"],tmpUsr["password"]) 173 | log.Debug("%v %v",a.RemoteAddr().String(),a.LocalAddr().String()) 174 | 175 | if err!=nil { 176 | log.Error("sign in module find fail") 177 | panic(err) 178 | } 179 | 180 | if tmpUsr["name"]==nil { 181 | ///空值没找到,向客户端发送一个该用户不存在的消息 182 | log.Debug("sign in module user no found") 183 | 184 | }else{ 185 | //log.Debug("%v %v",tmpUsr[m.Name],m.Password) 186 | log.Debug("%s %v",tmpUsr["password"].(string),tmpUsr["password"].(string)==m.Password) 187 | if tmpUsr["password"].(string)!=m.Password{ 188 | ///密码错误 189 | log.Debug("sign in module user name and password mismatch") 190 | }else { 191 | 192 | ///向客户端发送一个成功连接的token 193 | ///并在内存中创建一个当前用户 194 | 195 | if a.UserData()!=nil{ 196 | a.WriteMsg(&msg.State{msg.Login_duplicate}) 197 | log.Debug("sign in module duplicate signIn") 198 | } 199 | 200 | 201 | ///@todo 初始化车的时候,设置相应的车的编号 202 | a.SetUserData(&msg.Car{CarID:UserID}) 203 | UserID++ 204 | 205 | 206 | 207 | //log.Debug("%v",reflect.TypeOf(a.UserData())) 208 | 209 | a.WriteMsg(&msg.State{msg.Login_duplicate}) 210 | 211 | } 212 | 213 | } 214 | 215 | ///hello 216 | } 217 | 218 | 219 | ///用来同步更新用户的数据 220 | func handleUploadDB(args []interface{}) { 221 | 222 | } 223 | 224 | -------------------------------------------------------------------------------- /src/server/game/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "reflect" 5 | "server/msg" 6 | "github.com/name5566/leaf/gate" 7 | "github.com/name5566/leaf/log" 8 | 9 | "server/login" 10 | ) 11 | 12 | ///@todo 考虑如何简化,服务器端的游戏逻辑设计 13 | 14 | ///@todo 客户端和服务器端,设置计时器,和超时等功能 15 | 16 | ///@todo 考虑如何实现真正的房间匹配并发操作 17 | 18 | ///@todo 需要实现严格的服务器状态流程校验 19 | 20 | ///当前广播,适用于弱实时性项目, 21 | 22 | ///完成匹配记时 23 | 24 | /// 25 | 26 | type matchInfo struct { 27 | MatchQueue []msg.Match `json:"matchInfo"` 28 | } 29 | 30 | var matchMsg matchInfo 31 | 32 | 33 | func init() { 34 | //向当前模块(game 模块)注册Ok 消息的消息处理函数 handleOk 35 | handler(&msg.Ok{},handleOk) 36 | handler(&msg.Up{},handleUp) 37 | handler(&msg.Left{},handleLeft) 38 | handler(&msg.Right{},handleRight) 39 | handler(&msg.Match{},handleMatch) 40 | handler(&msg.Admin{},handleAdmin) 41 | handler(&msg.Order{},handleOrder) 42 | handler(&msg.Finished{},handleFinish) 43 | } 44 | 45 | func handler(m interface{},h interface{}) { 46 | 47 | skeleton.RegisterChanRPC(reflect.TypeOf(m),h) 48 | } 49 | 50 | ///Ok 的消息处理函数 51 | func handleOk(args []interface{}) { 52 | ///收到的 Ok 消息 53 | m := args[0].(*msg.Ok) 54 | 55 | //log.Debug("%v",m) 56 | ///消息的发送者 57 | a := args[1].(gate.Agent) 58 | 59 | log.Debug("Ok %v", m.Name) 60 | 61 | ///给发送者回应一个Ok消息 62 | a.WriteMsg(&msg.Ok{ 63 | Name: "client", 64 | }) 65 | } 66 | 67 | func handleUp(args []interface{}) { 68 | ///收到的 up消息 69 | m:=args[0].(*msg.Up) 70 | 71 | //获取网关,即客户端地址,消息的发送着 72 | 73 | a:=args[1].(gate.Agent) 74 | 75 | 76 | ///此处可以处理相应得逻辑 77 | 78 | ///将命令消息广播转发到其他的分组,设置相应的速度 79 | log.Debug("%v %v",m.Direction,a) 80 | 81 | ///向客户端发送,确认接收的消息,注意此处的Car为指针类型 82 | tmp:=a.UserData().(*msg.Car) 83 | 84 | tmp.Up() 85 | 86 | log.Debug("%v %v %v",a.RemoteAddr(),tmp.X,tmp.Y) 87 | 88 | 89 | 90 | ///广播车的命令 91 | handleBroadcast(&msg.Command{CarID:tmp.CarID,ID:tmp.CarID,Cmd:msg.UpCom,Val:m.Direction}) 92 | 93 | } 94 | 95 | func handleLeft(args []interface{}) { 96 | m:=args[0].(*msg.Left) 97 | 98 | a:=args[1].(gate.Agent) 99 | 100 | tmp:=a.UserData().(*msg.Car) 101 | tmp.Left() 102 | 103 | log.Debug("%v %v",m.Direction,a) 104 | log.Debug("%v %v %v",a.RemoteAddr(),tmp.X,tmp.Y) 105 | 106 | 107 | ///广播向左转的命令 108 | handleBroadcast(&msg.Command{tmp.CarID,tmp.CarID,msg.LeftCom,m.Direction}) 109 | 110 | } 111 | func handleRight(args []interface{}) { 112 | m:=args[0].(*msg.Right) 113 | 114 | a:=args[1].(gate.Agent) 115 | 116 | tmp:=a.UserData().(*msg.Car) 117 | tmp.Right() 118 | 119 | log.Debug("%v %v",m.Direction,a) 120 | log.Debug("%v %v %v",a.RemoteAddr(),tmp.X,tmp.Y) 121 | 122 | 123 | ///广播向左转的命令 124 | handleBroadcast(&msg.Command{tmp.CarID,tmp.CarID,msg.RightCom,m.Direction}) 125 | 126 | } 127 | 128 | func handleMatch(args []interface{}) { 129 | ///处理当前匹配多人模式的消息 130 | 131 | m,ok:=args[0].(*msg.Match) 132 | log.Debug("ok") 133 | if ok { 134 | log.Debug("multi-player match game %v",m) 135 | } 136 | 137 | ///添加到匹配的队列中去 138 | log.Debug("match %v\n",m) 139 | 140 | ///存放匹配信息 141 | tmp:=&msg.Match{Name:m.Name,Car:m.Car} 142 | //matchMsg.MatchQueue = append(matchMsg.MatchQueue,tmp) 143 | // 144 | /////如果当前在匹配队列的人数大于等于2 145 | //if len(matchMsg.MatchQueue)>=2 { 146 | // ///向其他的用户发送广播 147 | // for _,x:=range matchMsg.MatchQueue{ 148 | // tmp:=login.UserAgent[x.Name] 149 | // ///向客户端发送匹配数组信息 150 | // tmp.WriteMsg(&matchMsg) 151 | // 152 | // } 153 | //} 154 | handleBroadcast(tmp) 155 | 156 | } 157 | 158 | func handleOrder(args []interface{}) { 159 | m,ok:=args[0].(*msg.Order) 160 | if ok { 161 | log.Debug("Order %v",m) 162 | } 163 | 164 | ///广播消息 165 | handleBroadcast(m) 166 | } 167 | func handleFinish(args []interface{}) { 168 | m,ok:=args[0].(*msg.Finished) 169 | if ok { 170 | log.Debug("Finish %v",m) 171 | } 172 | 173 | ///广播消息 174 | handleBroadcast(m) 175 | } 176 | 177 | ///广播 178 | func handleBroadcast(cmd interface{}){ 179 | log.Debug("%v",len(agents)) 180 | for a:=range agents{ 181 | log.Debug("%v\n",a.RemoteAddr().String()) 182 | a.WriteMsg(cmd) 183 | } 184 | } 185 | 186 | 187 | func handleAdmin(args []interface{}) { 188 | if val,ok:=args[0].(*msg.Admin);ok{ 189 | log.Debug("Admin login: %s",val.Name) 190 | } 191 | if a,ok:=args[1].(gate.Agent);ok{ 192 | log.Debug("Admin ip: %v\n",a.RemoteAddr().String()) 193 | for it:=range agents { 194 | log.Debug("%v\n",it.RemoteAddr().String()) 195 | } 196 | } 197 | 198 | } 199 | 200 | 201 | ///用来处理不同用户之间的聊天功能 202 | func handleUserMsg(args []interface{}) { 203 | 204 | m,ok:=args[0].(*msg.UserMsg) 205 | if ok{ 206 | log.Debug("user message %v",m) 207 | } 208 | 209 | 210 | a, ok := args[1].(gate.Agent) 211 | if ok{ 212 | log.Debug("send from: %v",a.RemoteAddr().String()) 213 | } 214 | 215 | ///a发送消息到b 216 | bname:=m.Dst 217 | 218 | ///@todo 对于这里的判断要进行用户当前是否在线处理 219 | bgate:=login.UserAgent[bname] 220 | 221 | if bgate==nil { 222 | ///没找到对应用户的gate,进行错误处理 223 | 224 | } 225 | 226 | ///转发给用户b 227 | bgate.WriteMsg(&msg.UserMsg{m.Src,m.Dst,m.Context}) 228 | 229 | } -------------------------------------------------------------------------------- /src/client/simpleClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | "fmt" 7 | "encoding/json" 8 | ) 9 | 10 | ///@todo 考虑如何设置返回值为err 11 | type User struct { 12 | Name string `json:"name"` 13 | Password string `json:"password"` 14 | } 15 | 16 | type SignInMsg struct { 17 | User `json:"SignIn"` 18 | } 19 | 20 | type SignUpMsg struct { 21 | User `json:"SignUp"` 22 | } 23 | 24 | ///一个用户向另一个用户转发的消息 25 | type UserMsg struct { 26 | MsgPack InterMsg `json:"UserMsg"` 27 | } 28 | 29 | type InterMsg struct { 30 | Src string `json:"src"` 31 | Dst string `json:"dst"` 32 | Context string `json:"context"` 33 | } 34 | 35 | type MatchMsg struct { 36 | Match `json:"Match"` 37 | } 38 | type Match struct { 39 | Name string `json:"name"` 40 | Car int `json:"car"` 41 | } 42 | 43 | type OrderMsg struct { 44 | Order `json:"Order"` 45 | } 46 | type Order struct { 47 | Name string `json:"name"` 48 | Val int `json:"val"` 49 | } 50 | /** 51 | 发送数据包以这种数据包发送 52 | -------------- 53 | | len | data | 54 | -------------- 55 | */ 56 | func sendPackage(conn net.Conn, jsonData []byte) bool { 57 | 58 | m:=make([]byte,len(jsonData)+2) 59 | 60 | binary.BigEndian.PutUint16(m,uint16(len(jsonData))) 61 | 62 | copy(m[2:],jsonData) 63 | 64 | conn.Write(m) 65 | 66 | return true 67 | 68 | } 69 | 70 | func sendAdmin(conn net.Conn) { 71 | adm:=[]byte(`{ 72 | "Admin":{ 73 | "Name":"Wang" 74 | } 75 | }`) 76 | m:=make([]byte,2+len(adm)) 77 | binary.BigEndian.PutUint16(m,uint16(len(adm))) 78 | copy(m[2:],adm) 79 | conn.Write(m) 80 | } 81 | 82 | 83 | func sendUp(conn net.Conn) { 84 | up:=[]byte(`{ 85 | "Up": { 86 | "Direction": 0 87 | } 88 | }`) 89 | m:=make([]byte,2+len(up)) 90 | binary.BigEndian.PutUint16(m,uint16(len(up))) 91 | copy(m[2:],up) 92 | conn.Write(m) 93 | } 94 | func sendLeft(conn net.Conn) { 95 | left:=[]byte(`{ 96 | "Left":{ 97 | "Direction": 0 98 | } 99 | }`) 100 | m:=make([]byte,2+len(left)) 101 | binary.BigEndian.PutUint16(m,uint16(len(left))) 102 | copy(m[2:],left) 103 | conn.Write(m) 104 | } 105 | 106 | func sendRight(conn net.Conn) { 107 | right:=[]byte(`{ 108 | "Right":{ 109 | "Direction": 0 110 | } 111 | }`) 112 | m:=make([]byte,2+len(right)) 113 | binary.BigEndian.PutUint16(m,uint16(len(right))) 114 | copy(m[2:],right) 115 | conn.Write(m) 116 | 117 | } 118 | func connect(network string,address string) net.Conn { 119 | conn,err:=net.Dial(network,address) 120 | if err!=nil { 121 | panic(err) 122 | } 123 | return conn 124 | 125 | } 126 | func login(conn net.Conn,name, password string) bool { 127 | 128 | user:=&SignInMsg{User{Name:name,Password:password}} 129 | 130 | userdata,err:=json.Marshal(user) 131 | if err!=nil { 132 | panic(err) 133 | } 134 | 135 | //fmt.Println(string(userdata)) 136 | 137 | 138 | return sendPackage(conn,userdata) 139 | 140 | } 141 | 142 | func match(conn net.Conn,name string,car int) bool { 143 | 144 | userMatch:=&MatchMsg{Match{name,car}} 145 | 146 | 147 | data,err:=json.Marshal(userMatch) 148 | 149 | //fmt.Println(data) 150 | if err!=nil { 151 | panic(err) 152 | } 153 | return sendPackage(conn,data) 154 | 155 | } 156 | 157 | func order(conn net.Conn, name string, val int) bool { 158 | userOrder:=&OrderMsg{Order{name,val}} 159 | 160 | data,err:=json.Marshal(userOrder) 161 | 162 | //fmt.Println(data) 163 | if err != nil { 164 | panic(err) 165 | } 166 | return sendPackage(conn,data) 167 | } 168 | 169 | ///向一个特定的用户发送,私信暂未支持加密功能 170 | func sendMsg(conn net.Conn,src string,dst string,context string) bool { 171 | tmpMsg:=&UserMsg{InterMsg{src,dst,context}} 172 | 173 | tmpdata,err:=json.Marshal(tmpMsg) 174 | if err!=nil { 175 | panic(err) 176 | } 177 | //fmt.Println(string(tmpdata)) 178 | 179 | return sendPackage(conn,tmpdata) 180 | } 181 | 182 | func signUp(conn net.Conn,name,password string)bool { 183 | user:=&SignUpMsg{User{Name:name,Password:password}} 184 | 185 | userdata,err:=json.Marshal(user) 186 | if err!=nil { 187 | panic(err) 188 | } 189 | //fmt.Println(string(userdata)) 190 | 191 | return sendPackage(conn,userdata) 192 | } 193 | 194 | 195 | func simulation() { 196 | conn:=connect("tcp","47.93.17.101:3389") 197 | 198 | defer conn.Close() 199 | 200 | // Hello 消息(JSON 格式) 201 | // 对应游戏服务器 Hello 消息结构体 202 | 203 | 204 | ret:=make([]byte,80) 205 | 206 | c1:=make(chan []byte) 207 | c2:=make(chan string) 208 | 209 | ///结束之后关闭channel 210 | defer func() { 211 | close(c1) 212 | close(c2) 213 | }() 214 | go func() { 215 | for true{ 216 | conn.Read(ret) 217 | c1<-ret 218 | } 219 | }() 220 | 221 | go func() { 222 | for true{ 223 | var in string 224 | fmt.Scanf("%s",&in) 225 | c2<-in 226 | } 227 | }() 228 | var name string 229 | var password string 230 | 231 | fmt.Println("welcom client! press 'h' to get help") 232 | for true{ 233 | select { 234 | case data:=<-c1: 235 | fmt.Printf("Read from Server: %v \n",string(data)) 236 | case op:=<-c2: 237 | if op==string("w"){ 238 | sendUp(conn) 239 | }else if op==string("a") { 240 | sendLeft(conn) 241 | }else if op==string("d") { 242 | sendRight(conn) 243 | }else if op==string("q"){ 244 | ///退出 245 | fmt.Println("client quit") 246 | return 247 | }else if op==string("j"){ 248 | fmt.Println("Sign Up:") 249 | fmt.Print("name: ") 250 | fmt.Scanf("%s",&name) 251 | fmt.Print("password: ") 252 | fmt.Scanf("%s",&password) 253 | signUp(conn,name,password) 254 | }else if op==string("l"){ 255 | fmt.Println("Sign In:") 256 | fmt.Print("name: ") 257 | fmt.Scanf("%s",&name) 258 | fmt.Print("password: ") 259 | fmt.Scanf("%s",&password) 260 | login(conn,name,password) 261 | }else if op==string("h"){ 262 | fmt.Println("command:") 263 | fmt.Println("w a s d is direction command") 264 | fmt.Println("j is sign up command") 265 | fmt.Println("l is login command") 266 | fmt.Println("o is adminUser login") 267 | fmt.Println("q is quit command") 268 | fmt.Println("m is show all online user") 269 | fmt.Println("b is match mode") 270 | fmt.Println("v is order") 271 | }else if op == string("o") { 272 | 273 | signUp(conn,"wang","123") 274 | login(conn,"wang","123") 275 | 276 | }else if op==string("m"){ 277 | sendAdmin(conn) 278 | }else if op==string("n"){ 279 | fmt.Println("send msg to an existed user") 280 | }else if op == string("b") { 281 | 282 | match(conn,"wang",1) 283 | }else if op==string("v"){ 284 | order(conn,"wang",12) 285 | } 286 | } 287 | } 288 | 289 | } 290 | func main() { 291 | simulation() 292 | //go simulation() 293 | 294 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2015-2017 Name5566. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /src/server/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | true 61 | DEFINITION_ORDER 62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 89 | 90 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 119 | 120 | 121 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 148 | 149 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 138 | 139 | 140 | 141 | Hello 142 | agents 143 | 144 | 145 | Ok 146 | Agents 147 | 148 | 149 | 150 | 152 | 153 | 182 | 183 | 184 | 185 | 186 | true 187 | DEFINITION_ORDER 188 | 189 | 190 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 215 | 216 | 219 | 220 | 221 | 222 | 225 | 226 | 229 | 230 | 233 | 234 | 235 | 236 | 239 | 240 | 243 | 244 | 247 | 248 | 251 | 252 | 253 | 254 | 257 | 258 | 261 | 262 | 265 | 266 | 269 | 270 | 273 | 274 | 275 | 276 | 279 | 280 | 283 | 284 | 287 | 288 | 291 | 292 | 295 | 296 | 297 | 298 | 301 | 302 | 305 | 306 | 309 | 310 | 313 | 314 | 317 | 318 | 321 | 322 | 323 | 324 | 327 | 328 | 331 | 332 | 335 | 336 | 339 | 340 | 343 | 344 | 345 | 346 | 349 | 350 | 353 | 354 | 357 | 358 | 361 | 362 | 365 | 366 | 369 | 370 | 371 | 372 | 375 | 376 | 379 | 380 | 383 | 384 | 387 | 388 | 391 | 392 | 393 | 394 | 397 | 398 | 401 | 402 | 405 | 406 | 409 | 410 | 413 | 414 | 415 | 416 | 419 | 420 | 423 | 424 | 427 | 428 | 431 | 432 | 435 | 436 | 439 | 440 | 441 | 442 | 445 | 446 | 449 | 450 | 453 | 454 | 457 | 458 | 461 | 462 | 463 | 464 | 467 | 468 | 471 | 472 | 475 | 476 | 479 | 480 | 483 | 484 | 485 | 486 | 489 | 490 | 493 | 494 | 497 | 498 | 501 | 502 | 503 | 504 | 507 | 508 | 511 | 512 | 515 | 516 | 517 | 518 | 521 | 522 | 525 | 526 | 529 | 530 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | project 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 725 | 726 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 739 | 740 | 741 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | --------------------------------------------------------------------------------