├── 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 |
6 |
7 |
8 |
9 |
10 |
11 |
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 | 
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 | 
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | true
61 | DEFINITION_ORDER
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 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
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 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
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 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | Hello
142 | agents
143 |
144 |
145 | Ok
146 | Agents
147 |
148 |
149 |
150 |
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 | true
187 | DEFINITION_ORDER
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
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 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
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 |
681 |
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 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
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 |
--------------------------------------------------------------------------------