├── .circleci
└── config.yml
├── .gitattributes
├── .gitignore
├── Dockerfile
├── Golang Game ServerArchitecture.png
├── Gopkg.lock
├── Gopkg.toml
├── LICENSE
├── README.md
├── agent
├── ClientToAgent.go
└── session
│ ├── kubermanager.go
│ ├── room.go
│ └── session.go
├── cluster
├── accountbind.yaml
├── agent.yaml
├── deploy.yaml
└── pod.yaml
├── data.proto
├── demo.gif
├── game
├── agentToGame.go
├── clientToGame.go
├── data
│ ├── PhysicObj.json
│ └── data.go
├── entity
│ ├── attack.go
│ ├── enemy.go
│ ├── entity.go
│ ├── entity_test.go
│ ├── gameManager.go
│ ├── player.go
│ ├── room.go
│ ├── shell.go
│ └── treasure.go
├── hmap
│ └── hmap.go
├── physic
│ ├── physic.go
│ └── physic_test.go
└── session
│ ├── room.go
│ └── session.go
├── gameServer.code-workspace
├── gdb_sandbox
├── main
├── main.go
├── msg
├── Message.cs
├── MessageGrpc.cs
├── any.proto
├── github.com
│ └── golang
│ │ └── protobuf
│ │ └── ptypes
│ │ └── any
│ │ └── any.pb.go
├── message.pb.go
├── message.proto
├── message.zip
└── update.sh
├── rpctest
├── client
│ └── main.go
├── invokeTest
│ └── main.go
└── server
│ └── main.go
├── service
├── AgentToGame.go
├── ClientToAgent.go
└── ClientToGame.go
├── setupEnv.sh
├── storage
├── storage.go
└── storage_test.go
├── timeCalibrate
└── timeCalibration.go
├── user
└── user.go
├── util
└── util.go
└── uuid
└── uuid.go
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/golang:1.9
6 | working_directory: /go/src/github.com/daniel840829/gameServer
7 | steps:
8 | - checkout
9 | - run:
10 | name: dep install
11 | command: go get github.com/golang/dep/cmd/dep && dep ensure -v
12 | - run:
13 | name: build
14 | command: go build main.go
15 | - setup_remote_docker:
16 | docker_layer_caching: true
17 | - run:
18 | name: Build and Push Image daniel840829/gameplayserver
19 | command: |
20 | TAG=circleci-latest
21 | docker build -t daniel840829/gameplayserver:$TAG .
22 | docker login -u $DOCKER_USER -p $DOCKER_PASS
23 | docker push daniel840829/gameplayserver:$TAG
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | msg/* linguist-vendored
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/*
2 | *.log
3 | *.vim
4 | .undodir
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:latest as builder
2 | MAINTAINER daniel840829 "s102033114@gapp.nthu.edu.tw"
3 | COPY . $GOPATH/src/github.com/daniel840829/gameServer
4 | WORKDIR $GOPATH/src/github.com/daniel840829/gameServer
5 | RUN set -x && go get github.com/golang/dep/cmd/dep && dep ensure -v
6 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /gameServer
7 | CMD ["/gameServer"]
8 | EXPOSE 3000 8080 50051
9 |
10 | FROM alpine
11 | COPY --from=builder /gameServer .
12 | COPY ./cluster ./cluster
13 | EXPOSE 3000 8080 50051
14 | # ENTRYPOINT [ "/bin/bash" ]
15 | CMD ["./gameServer"]
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Golang Game ServerArchitecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danie1Lin/Distributed-Golang-Game-Server/620f6c91b6e21db9f8eca88916b1130223ff8b81/Golang Game ServerArchitecture.png
--------------------------------------------------------------------------------
/Gopkg.lock:
--------------------------------------------------------------------------------
1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
2 |
3 |
4 | [[projects]]
5 | branch = "master"
6 | digest = "1:c7b7c951907bb9f79d231d62878b06e98ce34ba3e90ae91c986315b0137baff7"
7 | name = "github.com/daniel840829/ode"
8 | packages = ["."]
9 | pruneopts = "UT"
10 | revision = "e0dee66213cb59e9957f6a6f6f16d19ee7b241ad"
11 |
12 | [[projects]]
13 | digest = "1:79a12e216d50c72810973304a485eb77c6e43d1493b5c506778ae61908dd99d1"
14 | name = "github.com/gazed/vu"
15 | packages = ["math/lin"]
16 | pruneopts = "UT"
17 | revision = "a2dfbe3ce829aabbb139ff4f8002bbdb78097058"
18 | version = "v0.10.0"
19 |
20 | [[projects]]
21 | digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda"
22 | name = "github.com/ghodss/yaml"
23 | packages = ["."]
24 | pruneopts = "UT"
25 | revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
26 | version = "v1.0.0"
27 |
28 | [[projects]]
29 | branch = "master"
30 | digest = "1:e0361634f0b5e1fd3227eaec90a86bc8595cb2526e71e41bcd72b6e474e198ca"
31 | name = "github.com/globalsign/mgo"
32 | packages = [
33 | ".",
34 | "bson",
35 | "internal/json",
36 | "internal/sasl",
37 | "internal/scram",
38 | ]
39 | pruneopts = "UT"
40 | revision = "1ca0a4f7cbcbe61c005d1bd43fdd8bb8b71df6bc"
41 |
42 | [[projects]]
43 | digest = "1:34e709f36fd4f868fb00dbaf8a6cab4c1ae685832d392874ba9d7c5dec2429d1"
44 | name = "github.com/gogo/protobuf"
45 | packages = [
46 | "proto",
47 | "sortkeys",
48 | ]
49 | pruneopts = "UT"
50 | revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
51 | version = "v1.1.1"
52 |
53 | [[projects]]
54 | branch = "master"
55 | digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467"
56 | name = "github.com/golang/glog"
57 | packages = ["."]
58 | pruneopts = "UT"
59 | revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
60 |
61 | [[projects]]
62 | digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf"
63 | name = "github.com/golang/protobuf"
64 | packages = [
65 | "proto",
66 | "ptypes",
67 | "ptypes/any",
68 | "ptypes/duration",
69 | "ptypes/timestamp",
70 | ]
71 | pruneopts = "UT"
72 | revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
73 | version = "v1.2.0"
74 |
75 | [[projects]]
76 | branch = "master"
77 | digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb"
78 | name = "github.com/google/gofuzz"
79 | packages = ["."]
80 | pruneopts = "UT"
81 | revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
82 |
83 | [[projects]]
84 | digest = "1:65c4414eeb350c47b8de71110150d0ea8a281835b1f386eacaa3ad7325929c21"
85 | name = "github.com/googleapis/gnostic"
86 | packages = [
87 | "OpenAPIv2",
88 | "compiler",
89 | "extensions",
90 | ]
91 | pruneopts = "UT"
92 | revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
93 | version = "v0.2.0"
94 |
95 | [[projects]]
96 | branch = "master"
97 | digest = "1:0778dc7fce1b4669a8bfa7ae506ec1f595b6ab0f8989c1c0d22a8ca1144e9972"
98 | name = "github.com/howeyc/gopass"
99 | packages = ["."]
100 | pruneopts = "UT"
101 | revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
102 |
103 | [[projects]]
104 | digest = "1:8eb1de8112c9924d59bf1d3e5c26f5eaa2bfc2a5fcbb92dc1c2e4546d695f277"
105 | name = "github.com/imdario/mergo"
106 | packages = ["."]
107 | pruneopts = "UT"
108 | revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"
109 | version = "v0.3.6"
110 |
111 | [[projects]]
112 | digest = "1:3e551bbb3a7c0ab2a2bf4660e7fcad16db089fdcfbb44b0199e62838038623ea"
113 | name = "github.com/json-iterator/go"
114 | packages = ["."]
115 | pruneopts = "UT"
116 | revision = "1624edc4454b8682399def8740d46db5e4362ba4"
117 | version = "v1.1.5"
118 |
119 | [[projects]]
120 | digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
121 | name = "github.com/modern-go/concurrent"
122 | packages = ["."]
123 | pruneopts = "UT"
124 | revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
125 | version = "1.0.3"
126 |
127 | [[projects]]
128 | digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855"
129 | name = "github.com/modern-go/reflect2"
130 | packages = ["."]
131 | pruneopts = "UT"
132 | revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
133 | version = "1.0.1"
134 |
135 | [[projects]]
136 | digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc"
137 | name = "github.com/sirupsen/logrus"
138 | packages = ["."]
139 | pruneopts = "UT"
140 | revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
141 | version = "v1.0.6"
142 |
143 | [[projects]]
144 | digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
145 | name = "github.com/spf13/pflag"
146 | packages = ["."]
147 | pruneopts = "UT"
148 | revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
149 | version = "v1.0.3"
150 |
151 | [[projects]]
152 | branch = "master"
153 | digest = "1:f8710e1ecd1653103b5e9d03e8e20c0779d3463d2af48e7c324863e06a630780"
154 | name = "github.com/zheng-ji/goSnowFlake"
155 | packages = ["."]
156 | pruneopts = "UT"
157 | revision = "fc763800eec9e4efe1dc7ca1cd9cb8831bd13227"
158 |
159 | [[projects]]
160 | branch = "master"
161 | digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8"
162 | name = "golang.org/x/crypto"
163 | packages = ["ssh/terminal"]
164 | pruneopts = "UT"
165 | revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
166 |
167 | [[projects]]
168 | branch = "master"
169 | digest = "1:deafe4ab271911fec7de5b693d7faae3f38796d9eb8622e2b9e7df42bb3dfea9"
170 | name = "golang.org/x/net"
171 | packages = [
172 | "context",
173 | "http/httpguts",
174 | "http2",
175 | "http2/hpack",
176 | "idna",
177 | "internal/timeseries",
178 | "trace",
179 | ]
180 | pruneopts = "UT"
181 | revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2"
182 |
183 | [[projects]]
184 | branch = "master"
185 | digest = "1:374fc90fcb026e9a367e3fad29e988e5dd944b68ca3f24a184d77abc5307dda4"
186 | name = "golang.org/x/sys"
187 | packages = [
188 | "unix",
189 | "windows",
190 | ]
191 | pruneopts = "UT"
192 | revision = "d0be0721c37eeb5299f245a996a483160fc36940"
193 |
194 | [[projects]]
195 | digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
196 | name = "golang.org/x/text"
197 | packages = [
198 | "collate",
199 | "collate/build",
200 | "internal/colltab",
201 | "internal/gen",
202 | "internal/tag",
203 | "internal/triegen",
204 | "internal/ucd",
205 | "language",
206 | "secure/bidirule",
207 | "transform",
208 | "unicode/bidi",
209 | "unicode/cldr",
210 | "unicode/norm",
211 | "unicode/rangetable",
212 | ]
213 | pruneopts = "UT"
214 | revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
215 | version = "v0.3.0"
216 |
217 | [[projects]]
218 | branch = "master"
219 | digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4"
220 | name = "golang.org/x/time"
221 | packages = ["rate"]
222 | pruneopts = "UT"
223 | revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
224 |
225 | [[projects]]
226 | branch = "master"
227 | digest = "1:9cc1d3de11a1b6994eb01fe3cf81db064470d0fd39a37475f53a396aa5e8622c"
228 | name = "google.golang.org/genproto"
229 | packages = ["googleapis/rpc/status"]
230 | pruneopts = "UT"
231 | revision = "5a2fd4cab2d6d4a18e70c34937662526cd0c4bd1"
232 |
233 | [[projects]]
234 | digest = "1:ab8e92d746fb5c4c18846b0879842ac8e53b3d352449423d0924a11f1020ae1b"
235 | name = "google.golang.org/grpc"
236 | packages = [
237 | ".",
238 | "balancer",
239 | "balancer/base",
240 | "balancer/roundrobin",
241 | "codes",
242 | "connectivity",
243 | "credentials",
244 | "encoding",
245 | "encoding/proto",
246 | "grpclog",
247 | "internal",
248 | "internal/backoff",
249 | "internal/channelz",
250 | "internal/envconfig",
251 | "internal/grpcrand",
252 | "internal/transport",
253 | "keepalive",
254 | "metadata",
255 | "naming",
256 | "peer",
257 | "resolver",
258 | "resolver/dns",
259 | "resolver/passthrough",
260 | "stats",
261 | "status",
262 | "tap",
263 | ]
264 | pruneopts = "UT"
265 | revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1"
266 | version = "v1.15.0"
267 |
268 | [[projects]]
269 | digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
270 | name = "gopkg.in/inf.v0"
271 | packages = ["."]
272 | pruneopts = "UT"
273 | revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
274 | version = "v0.9.1"
275 |
276 | [[projects]]
277 | digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
278 | name = "gopkg.in/yaml.v2"
279 | packages = ["."]
280 | pruneopts = "UT"
281 | revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
282 | version = "v2.2.1"
283 |
284 | [[projects]]
285 | digest = "1:9e4dd60b5b4fda39e18da7879b26f8f44f47fa7fbc4f465eaeafd929bce54803"
286 | name = "k8s.io/api"
287 | packages = [
288 | "admissionregistration/v1alpha1",
289 | "admissionregistration/v1beta1",
290 | "apps/v1",
291 | "apps/v1beta1",
292 | "apps/v1beta2",
293 | "authentication/v1",
294 | "authentication/v1beta1",
295 | "authorization/v1",
296 | "authorization/v1beta1",
297 | "autoscaling/v1",
298 | "autoscaling/v2beta1",
299 | "batch/v1",
300 | "batch/v1beta1",
301 | "batch/v2alpha1",
302 | "certificates/v1beta1",
303 | "core/v1",
304 | "events/v1beta1",
305 | "extensions/v1beta1",
306 | "networking/v1",
307 | "policy/v1beta1",
308 | "rbac/v1",
309 | "rbac/v1alpha1",
310 | "rbac/v1beta1",
311 | "scheduling/v1alpha1",
312 | "settings/v1alpha1",
313 | "storage/v1",
314 | "storage/v1alpha1",
315 | "storage/v1beta1",
316 | ]
317 | pruneopts = "UT"
318 | revision = "0f11257a8a25954878633ebdc9841c67d8f83bdb"
319 | version = "kubernetes-1.10.7"
320 |
321 | [[projects]]
322 | digest = "1:bc7920d9796bd371e092d379b27b6b9bd3b50d135260cbdc10512dde51aec449"
323 | name = "k8s.io/apimachinery"
324 | packages = [
325 | "pkg/api/errors",
326 | "pkg/api/meta",
327 | "pkg/api/resource",
328 | "pkg/apis/meta/v1",
329 | "pkg/apis/meta/v1/unstructured",
330 | "pkg/apis/meta/v1beta1",
331 | "pkg/conversion",
332 | "pkg/conversion/queryparams",
333 | "pkg/fields",
334 | "pkg/labels",
335 | "pkg/runtime",
336 | "pkg/runtime/schema",
337 | "pkg/runtime/serializer",
338 | "pkg/runtime/serializer/json",
339 | "pkg/runtime/serializer/protobuf",
340 | "pkg/runtime/serializer/recognizer",
341 | "pkg/runtime/serializer/streaming",
342 | "pkg/runtime/serializer/versioning",
343 | "pkg/selection",
344 | "pkg/types",
345 | "pkg/util/clock",
346 | "pkg/util/errors",
347 | "pkg/util/framer",
348 | "pkg/util/intstr",
349 | "pkg/util/json",
350 | "pkg/util/net",
351 | "pkg/util/runtime",
352 | "pkg/util/sets",
353 | "pkg/util/validation",
354 | "pkg/util/validation/field",
355 | "pkg/util/wait",
356 | "pkg/util/yaml",
357 | "pkg/version",
358 | "pkg/watch",
359 | "third_party/forked/golang/reflect",
360 | ]
361 | pruneopts = "UT"
362 | revision = "e386b2658ed20923da8cc9250e552f082899a1ee"
363 | version = "kubernetes-1.10.7"
364 |
365 | [[projects]]
366 | digest = "1:0a4e3d4f41939942aa81b5900cd8332def6f529f2f286e521cc54d6d7874dbb8"
367 | name = "k8s.io/client-go"
368 | packages = [
369 | "discovery",
370 | "kubernetes",
371 | "kubernetes/scheme",
372 | "kubernetes/typed/admissionregistration/v1alpha1",
373 | "kubernetes/typed/admissionregistration/v1beta1",
374 | "kubernetes/typed/apps/v1",
375 | "kubernetes/typed/apps/v1beta1",
376 | "kubernetes/typed/apps/v1beta2",
377 | "kubernetes/typed/authentication/v1",
378 | "kubernetes/typed/authentication/v1beta1",
379 | "kubernetes/typed/authorization/v1",
380 | "kubernetes/typed/authorization/v1beta1",
381 | "kubernetes/typed/autoscaling/v1",
382 | "kubernetes/typed/autoscaling/v2beta1",
383 | "kubernetes/typed/batch/v1",
384 | "kubernetes/typed/batch/v1beta1",
385 | "kubernetes/typed/batch/v2alpha1",
386 | "kubernetes/typed/certificates/v1beta1",
387 | "kubernetes/typed/core/v1",
388 | "kubernetes/typed/events/v1beta1",
389 | "kubernetes/typed/extensions/v1beta1",
390 | "kubernetes/typed/networking/v1",
391 | "kubernetes/typed/policy/v1beta1",
392 | "kubernetes/typed/rbac/v1",
393 | "kubernetes/typed/rbac/v1alpha1",
394 | "kubernetes/typed/rbac/v1beta1",
395 | "kubernetes/typed/scheduling/v1alpha1",
396 | "kubernetes/typed/settings/v1alpha1",
397 | "kubernetes/typed/storage/v1",
398 | "kubernetes/typed/storage/v1alpha1",
399 | "kubernetes/typed/storage/v1beta1",
400 | "pkg/apis/clientauthentication",
401 | "pkg/apis/clientauthentication/v1alpha1",
402 | "pkg/version",
403 | "plugin/pkg/client/auth/exec",
404 | "rest",
405 | "rest/watch",
406 | "tools/auth",
407 | "tools/clientcmd",
408 | "tools/clientcmd/api",
409 | "tools/clientcmd/api/latest",
410 | "tools/clientcmd/api/v1",
411 | "tools/metrics",
412 | "tools/reference",
413 | "transport",
414 | "util/cert",
415 | "util/flowcontrol",
416 | "util/homedir",
417 | "util/integer",
418 | ]
419 | pruneopts = "UT"
420 | revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
421 | version = "v7.0.0"
422 |
423 | [solve-meta]
424 | analyzer-name = "dep"
425 | analyzer-version = 1
426 | input-imports = [
427 | "github.com/daniel840829/ode",
428 | "github.com/gazed/vu/math/lin",
429 | "github.com/globalsign/mgo",
430 | "github.com/globalsign/mgo/bson",
431 | "github.com/golang/protobuf/proto",
432 | "github.com/golang/protobuf/ptypes",
433 | "github.com/golang/protobuf/ptypes/any",
434 | "github.com/sirupsen/logrus",
435 | "github.com/zheng-ji/goSnowFlake",
436 | "golang.org/x/net/context",
437 | "google.golang.org/grpc",
438 | "google.golang.org/grpc/codes",
439 | "google.golang.org/grpc/metadata",
440 | "google.golang.org/grpc/status",
441 | "k8s.io/api/core/v1",
442 | "k8s.io/apimachinery/pkg/api/errors",
443 | "k8s.io/apimachinery/pkg/apis/meta/v1",
444 | "k8s.io/apimachinery/pkg/util/yaml",
445 | "k8s.io/client-go/kubernetes",
446 | "k8s.io/client-go/rest",
447 | "k8s.io/client-go/tools/clientcmd",
448 | ]
449 | solver-name = "gps-cdcl"
450 | solver-version = 1
451 |
--------------------------------------------------------------------------------
/Gopkg.toml:
--------------------------------------------------------------------------------
1 | # Gopkg.toml example
2 | #
3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
4 | # for detailed Gopkg.toml documentation.
5 | #
6 | # required = ["github.com/user/thing/cmd/thing"]
7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
8 | #
9 | # [[constraint]]
10 | # name = "github.com/user/project"
11 | # version = "1.0.0"
12 | #
13 | # [[constraint]]
14 | # name = "github.com/user/project2"
15 | # branch = "dev"
16 | # source = "github.com/myfork/project2"
17 | #
18 | # [[override]]
19 | # name = "github.com/x/y"
20 | # version = "2.4.0"
21 | #
22 | # [prune]
23 | # non-go = false
24 | # go-tests = true
25 | # unused-packages = true
26 |
27 |
28 | [[constraint]]
29 | branch = "master"
30 | name = "github.com/daniel840829/ode"
31 |
32 | [[constraint]]
33 | name = "github.com/gazed/vu"
34 | version = "0.10.0"
35 |
36 | [[constraint]]
37 | branch = "master"
38 | name = "github.com/globalsign/mgo"
39 |
40 | [[constraint]]
41 | name = "github.com/golang/protobuf"
42 | version = "1.2.0"
43 |
44 | [[constraint]]
45 | name = "github.com/sirupsen/logrus"
46 | version = "1.0.6"
47 |
48 | [[constraint]]
49 | branch = "master"
50 | name = "github.com/zheng-ji/goSnowFlake"
51 |
52 | [[constraint]]
53 | branch = "master"
54 | name = "golang.org/x/net"
55 |
56 | [[constraint]]
57 | name = "google.golang.org/grpc"
58 | version = "1.15.0"
59 |
60 |
61 | [prune]
62 | go-tests = true
63 | unused-packages = true
64 |
65 | [[override]]
66 | name = "k8s.io/api"
67 | version = "kubernetes-1.10.7"
68 |
69 | [[override]]
70 | name = "k8s.io/apimachinery"
71 | version = "kubernetes-1.10.7"
72 |
73 | [[override]]
74 | name = "k8s.io/client-go"
75 | version = "7.0.0"
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Lin Chan Wei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Golang Distributed Game Server
2 | [](https://goreportcard.com/report/github.com/daniel840829/Distributed-Golang-Game-Server)
3 | [](https://circleci.com/gh/daniel840829/Distributed-Golang-Game-Server)
4 | [](https://gitter.im/daniel840829/Distributed-Golang-Game-Server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
5 | ## Motivation
6 | At first, I just want to learn Golang.I started to think about which is the best way?
7 | Because the concurrency mechanisms of Golang is very powerful, I choose online game to verify if I can use Golang to make a efficient game server.For me, this is the first time I make this such hard project. I have to learn Unity, Golang, C# At a time. I am glad that I still have full passion to this project and I never give up.
8 | ## Tech/framework used
9 | - golang
10 | - gRPC
11 | - Kubernetes
12 | - Protobuf
13 | ## Features
14 | - CrossPlatform - Message packet use protobuf which is light, fast, crossplatform.
15 | - Autoscaling - controller is written with go-client ,you can wirte the strategy to autoscale dedicated game server by your own.
16 | - Lightweight - The image of dedicated game server is less than 40MB.
17 | ## Architecture
18 | ### Agent server :
19 | - match players to join other player's room or create own room
20 | - control the amount of gameplay server and load balancing. when the amountof a gameplay server's connections exceed maxium connections it should have, agent will create a new pod run gameplay server.
21 | ### Gameplay Server :
22 | - After players are matched successfully ,these players will get the gameplay server's ip and token,and player can start to play.
23 | 
24 | ### Packet Validating
25 | In the branch master, I use ODE to simulate the physics on server.It is the most safe way to keep game fair.However, I found the memory server use is too much for me, Because I don't have money to maintance the server. So I started to design a way to let client validate packet and simulate physics separatly to reduce the heavy load on the server. I just complete the entities can attack each others so far. I will start to design aftewards:
26 | - The validation part preventing form players cheating
27 | - The interface connecting a physics simulator
28 | ## Installation
29 | - Server :
30 | Two Way to run server:
31 | - Run distributed server using Kubernete cluster
32 | 1. Use Kops to install kubernete on AWS
33 | 2. Create cluster
34 | 3. Install Mongodb
35 | 4. edit setupEnv.sh with your setting and bash setupEnv.sh
36 | 4. ```go run main.go --type=agent``` on your local machine (Must on Where you install Kops)
37 | - Run Standalone Server on local machine
38 | 1. Install Mongodb
39 | 3. edit setupEnv.sh( DONT_USE_KUBE = true )with your setting and bash setupEnv.sh
40 | 2. ```go run main.go --type=standalone``` on your local machine
41 | - Client :
42 | - [Download this project](https://github.com/daniel840829/Tank-Online)
43 | - You can run in Unity Editor by open the Prestart.scene as first scene.
44 | - If you want to test with mutliplayer you can try build Andorid apk because it is likely to be builded successfully.
45 |
46 | [](https://www.youtube.com/watch?v=7Q9g6AqXasg "Demo")
47 | ## How to use?
48 | If you want to make your own game by modifying this project, I am pleasured.
49 | You can throught these step to make it work.
50 | ### Modify The msg/message.proto
51 | 1. Change the GameFrame message in proto buff.
52 | 2. Run './update.sh' in 'msg/'
53 | 3. unzip message.zip under 'Asset/gameServer/proto' in the Unity Project.
54 | ### Create Your Game Logic
55 | 1. add your handler to "gameServer/game/session/room.go": func (r *Room) Run()
56 | 2. modify the UpdateFrame fuction to handle packets design by yourself. then,Data Flow to Entity to render the change of entity's properties.
57 | ## The file structure:
58 | - agent
59 | - session
60 | - room.go
61 | - session.go
62 | - kubernetes.go
63 | - game
64 | - room.go
65 | - session.go
66 | - kubernetes.go
67 | - msg Use Protobuf to define package and RPC service interface
68 | - uuid generate different IDs of objects that can be call with reflection
69 | - user
70 | - UserManager
71 | - User
72 | - storage Use mongoDB to storage user infomation
73 | ## Support me
74 |
75 |
--------------------------------------------------------------------------------
/agent/ClientToAgent.go:
--------------------------------------------------------------------------------
1 | package agent
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/daniel840829/gameServer/agent/session"
7 | . "github.com/daniel840829/gameServer/msg"
8 | "github.com/daniel840829/gameServer/user"
9 |
10 | //. "github.com/daniel840829/gameServer/uuid"
11 | //"github.com/globalsign/mgo"
12 | //"fmt"
13 | //"github.com/daniel840829/gameServer/storage"
14 | //p "github.com/golang/protobuf/proto"
15 | //"github.com/golang/protobuf/ptypes"
16 | //any "github.com/golang/protobuf/ptypes/any"
17 | //log "github.com/sirupsen/logrus"
18 |
19 | "time"
20 |
21 | "os"
22 |
23 | log "github.com/sirupsen/logrus"
24 | "golang.org/x/net/context"
25 | "google.golang.org/grpc/codes"
26 | "google.golang.org/grpc/metadata"
27 | "google.golang.org/grpc/status"
28 | /*
29 | "io"
30 | "reflect"
31 | "sync"
32 | */)
33 |
34 | /*
35 | type ClientToAgentServer interface {
36 | AquireSessionKey(context.Context, *Empty) (*SessionKey, error)
37 | AquireOtherAgent(context.Context, *Empty) (*ServerInfo, error)
38 | // Login
39 | Login(context.Context, *LoginInput) (*UserInfo, error)
40 | CreateAccount(context.Context, *RegistInput) (*Error, error)
41 | // UserSetting
42 | SetAccount(context.Context, *AttrSetting) (*Success, error)
43 | SetCharacter(context.Context, *AttrSetting) (*Success, error)
44 | // room
45 | AquireGameServer(context.Context, *Empty) (*ServerInfo, error)
46 | CreateRoom(context.Context, *RoomSetting) (*Success, error)
47 | JoinRoom(context.Context, *ID) (*Success, error)
48 | RoomReady(context.Context, *Empty) (*Success, error)
49 | // View
50 | UpdateHome(*Empty, ClientToAgent_UpdateHomeServer) error
51 | UpdateRoomList(*Empty, ClientToAgent_UpdateRoomListServer) error
52 | UpdateUserList(*Empty, ClientToAgent_UpdateUserListServer) error
53 | // rpc UpdateRoomInfo(SessionKey) returns (stream RoomInfoView) {}
54 | Pipe(ClientToAgent_PipeServer) error
55 | }*/
56 |
57 | type ErrorPipe struct {
58 | toClient chan (*MessageToUser)
59 | }
60 |
61 | func NewAgentRpc() (agent *Agent) {
62 | agent = &Agent{
63 | ErrorPipe: &ErrorPipe{
64 | toClient: make(chan (*MessageToUser), 10),
65 | },
66 | }
67 | return
68 | }
69 |
70 | type Agent struct {
71 | Uuid int64
72 | ErrorPipe *ErrorPipe
73 | GameServer AgentToGameClient
74 | }
75 |
76 | func (a *Agent) Init(ip, agentToGamePort, clientToGamePort string) {
77 | if os.Getenv("DONT_USE_KUBE") == "true" {
78 | session.RoomManager.ConnectGameServer(ip, clientToGamePort, agentToGamePort, "0")
79 | } else {
80 | session.ClusterManager.KubeClientSet()
81 | }
82 | }
83 |
84 | func (a *Agent) AquireSessionKey(c context.Context, e *Empty) (*SessionKey, error) {
85 | id := session.Manager.MakeSession()
86 | return &SessionKey{Value: strconv.FormatInt(id, 10)}, nil
87 | }
88 | func (a *Agent) AquireOtherAgent(c context.Context, e *Empty) (*ServerInfo, error) {
89 | return nil, nil
90 | }
91 |
92 | func (a *Agent) GetSessionCache(c context.Context, e *Empty) (*SessionCache, error) {
93 | s := GetSesionFromContext(c)
94 |
95 | if s == nil {
96 | log.Warn("GetSessionCache Fail")
97 | return &SessionCache{}, status.Errorf(codes.NotFound, "Session Not Found!")
98 | }
99 |
100 | cache := s.GetSessionCache()
101 | return cache, nil
102 | }
103 |
104 | // Login
105 |
106 | func (a *Agent) Login(c context.Context, in *LoginInput) (*UserInfo, error) {
107 | s := GetSesionFromContext(c)
108 | if s == nil {
109 | return &UserInfo{}, status.Errorf(codes.NotFound, "Session Not Found!")
110 | }
111 | s.Lock()
112 | user := s.State.Login(in.UserName, in.Pswd)
113 | if user == nil {
114 | s.Unlock()
115 | return nil, nil
116 | }
117 | s.Unlock()
118 | return user.UserInfo, nil
119 | }
120 | func (a *Agent) CreateAccount(c context.Context, in *RegistInput) (*Error, error) {
121 | errmsg, err := user.Manager.Regist(in)
122 | return errmsg, err
123 | }
124 |
125 | // UserSetting
126 | func (a *Agent) SetAccount(context.Context, *AttrSetting) (*Success, error) {
127 | return nil, nil
128 | }
129 |
130 | func (a *Agent) SetCharacter(c context.Context, setting *CharacterSetting) (*Success, error) {
131 | s := GetSesionFromContext(c)
132 | if s == nil {
133 | return &Success{
134 | Ok: false,
135 | }, status.Errorf(codes.NotFound, "Session Not Found!")
136 | }
137 | ok := s.State.SettingCharacter(setting)
138 | return &Success{
139 | Ok: ok,
140 | }, nil
141 | }
142 |
143 | // allocate room
144 | func (a *Agent) AquireGameServer(c context.Context, e *Empty) (*ServerInfo, error) {
145 | s := GetSesionFromContext(c)
146 | log.Debug("Aquiring game server...")
147 | time.Sleep(5 * time.Second)
148 | if s == nil {
149 | return &ServerInfo{}, status.Errorf(codes.NotFound, "Session Not Found!")
150 | }
151 | msg := s.GetMsgChan("ServerInfo")
152 | if msg != nil {
153 | serverInfo := <-msg.DataCh
154 | return serverInfo.(*ServerInfo), nil
155 | }
156 | return &ServerInfo{}, nil
157 | }
158 |
159 | // View
160 | func (a *Agent) UpdateHome(*Empty, ClientToAgent_UpdateHomeServer) error {
161 | return nil
162 | }
163 |
164 | func (a *Agent) UpdateRoomList(e *Empty, stream ClientToAgent_UpdateRoomListServer) error {
165 | s := GetSesionFromContext(stream.Context())
166 | if s == nil {
167 | return status.Errorf(codes.NotFound, "Session Not Found!")
168 | }
169 | msgChan := s.GetMsgChan("RoomList")
170 | data := msgChan.DataCh
171 | stop := msgChan.StopSignal
172 | for {
173 | select {
174 | case msg := <-data:
175 | stream.Send(msg.(*RoomList))
176 | case <-stop:
177 | break
178 | }
179 | }
180 | return nil
181 | }
182 |
183 | func (a *Agent) UpdateUserList(*Empty, ClientToAgent_UpdateUserListServer) error {
184 | return nil
185 | }
186 |
187 | // rpc UpdateRoomInfo(SessionKey) returns (stream RoomInfoView) {}
188 | func (a *Agent) Pipe(ClientToAgent_PipeServer) error {
189 | return nil
190 | }
191 | func (a *Agent) CreateRoom(c context.Context, roomSetting *RoomSetting) (*Success, error) {
192 | s := GetSesionFromContext(c)
193 | if s == nil {
194 | return &Success{}, status.Errorf(codes.NotFound, "Session Not Found!")
195 | }
196 | ok := s.State.CreateRoom(roomSetting)
197 | return &Success{
198 | Ok: ok,
199 | }, nil
200 | }
201 |
202 | func (a *Agent) JoinRoom(c context.Context, id *ID) (*Success, error) {
203 | s := GetSesionFromContext(c)
204 |
205 | if s == nil {
206 | return &Success{
207 | Ok: false,
208 | }, status.Errorf(codes.NotFound, "Session Not Found!")
209 | }
210 | ok := s.State.EnterRoom(id.Value)
211 | return &Success{
212 | Ok: ok,
213 | }, nil
214 | }
215 |
216 | func (a *Agent) UpdateRoomContent(e *Empty, stream ClientToAgent_UpdateRoomContentServer) error {
217 | s := GetSesionFromContext(stream.Context())
218 | if s == nil {
219 | return status.Errorf(codes.NotFound, "Session Not Found!")
220 | }
221 | msgChan := s.GetMsgChan("RoomContent")
222 | data := msgChan.DataCh
223 | stop := msgChan.StopSignal
224 | for {
225 | select {
226 | case msg := <-data:
227 | stream.Send(msg.(*RoomContent))
228 | case <-stop:
229 | break
230 | }
231 | }
232 | return nil
233 | }
234 |
235 | func (a *Agent) RoomReady(c context.Context, e *Empty) (*Success, error) {
236 | s := GetSesionFromContext(c)
237 | if s == nil {
238 | return &Success{
239 | Ok: false,
240 | }, status.Errorf(codes.NotFound, "Session Not Found!")
241 | }
242 |
243 | if s.IsReady {
244 | if s.State.CancelReady() {
245 | return &Success{
246 | Ok: s.IsReady,
247 | }, nil
248 | }
249 | } else {
250 | if s.State.ReadyRoom() {
251 | return &Success{
252 | Ok: s.IsReady,
253 | }, nil
254 | }
255 | }
256 | return &Success{}, status.Errorf(codes.Internal, "Somethig Wrong")
257 | }
258 |
259 | func GetSesionFromContext(c context.Context) *session.Session {
260 | md, ok := metadata.FromIncomingContext(c)
261 | if !ok {
262 | return nil
263 | }
264 | s := session.Manager.GetSession(md)
265 | return s
266 | }
267 |
--------------------------------------------------------------------------------
/agent/session/kubermanager.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "fmt"
5 | "os/signal"
6 |
7 | "k8s.io/api/core/v1"
8 |
9 | log "github.com/sirupsen/logrus"
10 | "k8s.io/apimachinery/pkg/util/yaml"
11 | "k8s.io/client-go/kubernetes"
12 |
13 | //"k8s.io/client-go/rest"
14 | "os"
15 | "path/filepath"
16 |
17 | "strconv"
18 |
19 | "syscall"
20 |
21 | "k8s.io/apimachinery/pkg/api/errors"
22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23 | "k8s.io/client-go/rest"
24 | "k8s.io/client-go/tools/clientcmd"
25 | )
26 |
27 | const NAME_SPACE string = "default"
28 | const SERVER_AMOUNT int = 1
29 | const MAX_SERVER_AMOUT int = 20
30 | const MAX_PORT int = 31000
31 | const MIN_PORT int = 30000
32 |
33 | //MAX_ROOM_IN_POD maxium rooms in pod
34 | const MAX_ROOM_IN_POD = 1
35 |
36 | var interrupt chan os.Signal
37 | var PodMod *v1.Pod
38 |
39 | //ClusterManager clustermanager's instance
40 | var ClusterManager *clusterManager
41 |
42 | //ClusterManager manage kubernete's cluster and gameplayServer
43 | type clusterManager struct {
44 | client *kubernetes.Clientset
45 | pods map[string]*v1.Pod
46 | PodAmount int
47 | }
48 |
49 | //KubeClientSet create clientset
50 | func (c *clusterManager) KubeClientSet() {
51 | //config, err := rest.InClusterConfig()
52 | config, err := clientcmd.BuildConfigFromFlags("", filepath.Join(os.Getenv("HOME"), ".kube", "config"))
53 |
54 | if err != nil {
55 | log.Warn(err)
56 | config, err = rest.InClusterConfig()
57 | if err != nil {
58 | log.Fatal(err)
59 | }
60 | }
61 | clientset, err := kubernetes.NewForConfig(config)
62 | c.client = clientset
63 | //log.Debug(config, clientset)
64 |
65 | //clientset.CoreV1().Services("NAME_SPACE")
66 | ex, err := os.Executable()
67 | if err != nil {
68 | panic(err)
69 | }
70 | exPath := filepath.Dir(ex)
71 | fmt.Println(exPath)
72 | p, _ := os.Getwd()
73 | f, err := os.Open(filepath.Join(p, "cluster/pod.yaml"))
74 | if err != nil {
75 | log.Fatal(ex, "not found:", err)
76 | return
77 | }
78 |
79 | yamlde := yaml.NewYAMLOrJSONDecoder(f, 300)
80 | podInfo := &v1.Pod{}
81 | if err = yamlde.Decode(podInfo); err != nil {
82 | log.Debug("yaml parse fail", err)
83 | }
84 | //log.Debug("pod info :", podInfo)
85 | PodMod = podInfo.DeepCopy()
86 | // id: ''
87 | // agentToGamePort: ''
88 | // clientToGamePort: ''
89 | // clientToAgentPort: ''
90 | if MIN_PORT+SERVER_AMOUNT*2 > MAX_PORT {
91 | panic("Usable port is not enough.")
92 | }
93 | for i := 0; i < SERVER_AMOUNT; i++ {
94 | c.CreatePod()
95 | }
96 | }
97 |
98 | //CreatePod scale gameplay server
99 | func (c *clusterManager) CreatePod() {
100 | if c.PodAmount == MAX_SERVER_AMOUT {
101 | return
102 | }
103 | i := c.PodAmount
104 | podInfo := PodMod.DeepCopy()
105 | podInfo.Name = "gameplayserver-" + strconv.Itoa(i)
106 | podInfo.Labels["type"] = "game"
107 | podInfo.Labels["id"] = strconv.Itoa(i)
108 | podInfo.Labels["agentToGamePort"] = strconv.Itoa(MIN_PORT + i*2)
109 | podInfo.Labels["clientToGamePort"] = strconv.Itoa(MIN_PORT + i*2 + 1)
110 | _, err := c.client.CoreV1().Pods(NAME_SPACE).Create(podInfo.DeepCopy())
111 | if err != nil {
112 | log.Warn(err)
113 | if errors.IsAlreadyExists(err) {
114 | policy := metav1.DeletePropagationForeground
115 | deleteTime := int64(0)
116 | c.client.CoreV1().Pods(NAME_SPACE).Delete(podInfo.Name, &metav1.DeleteOptions{GracePeriodSeconds: &deleteTime, PropagationPolicy: &policy})
117 |
118 | _, err := c.client.CoreV1().Pods(NAME_SPACE).Create(podInfo.DeepCopy())
119 | if err != nil {
120 | log.Fatal("Create Fail", err)
121 | }
122 | }
123 | }
124 | c.pods[podInfo.Labels["id"]], err = c.client.CoreV1().Pods(NAME_SPACE).Get(podInfo.Name, metav1.GetOptions{})
125 | if err != nil {
126 | log.Warn(err)
127 | }
128 |
129 | nodeName := c.pods[podInfo.Labels["id"]].Spec.NodeName
130 | node, err := c.client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
131 | if err != nil {
132 | log.Fatal(err)
133 | }
134 | extIP := ""
135 | for _, add := range node.Status.Addresses {
136 | if add.Type == v1.NodeExternalIP {
137 | extIP = add.Address
138 | break
139 | }
140 | }
141 |
142 | RoomManager.ConnectGameServer(extIP, podInfo.Labels["clientToGamePort"], podInfo.Labels["agentToGamePort"], podInfo.Labels["id"])
143 | log.Info("Gameplyer Server created: id: ", podInfo.Labels["id"], " IP: ", extIP, " Client Port: ", podInfo.Labels["clientToGamePort"], " Agent Port: ", podInfo.Labels["agentToGamePort"])
144 | c.PodAmount++
145 | }
146 |
147 | func cleanUp() {
148 | <-interrupt
149 | log.Info("Stopping agent")
150 | log.Info("Stopping gameplayer server")
151 | for _, pod := range ClusterManager.pods {
152 | err := ClusterManager.client.CoreV1().Pods(NAME_SPACE).Delete(pod.Name, &metav1.DeleteOptions{})
153 | if err != nil {
154 | log.Warn(err)
155 | }
156 | }
157 | os.Exit(1)
158 | }
159 |
160 | func init() {
161 | if os.Getenv("DONT_USE_KUBE") == "true" {
162 | return
163 | }
164 | ClusterManager = &clusterManager{
165 | pods: make(map[string]*v1.Pod),
166 | }
167 | interrupt = make(chan os.Signal)
168 | signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
169 | go cleanUp()
170 | }
171 |
--------------------------------------------------------------------------------
/agent/session/room.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | . "github.com/daniel840829/gameServer/msg"
8 | . "github.com/daniel840829/gameServer/uuid"
9 | "github.com/golang/protobuf/proto"
10 | log "github.com/sirupsen/logrus"
11 | "golang.org/x/net/context"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | type ChatMessage struct {
16 | SpeakerId int32
17 | SpeakerName string
18 | Content string
19 | }
20 |
21 | type ChatRoom struct {
22 | ReadingBuffer []*ChatMessage
23 | }
24 |
25 | //TODO : Chat function
26 |
27 | var limitReadyTime = 1000 //wait ms to start a room
28 |
29 | var RoomManager *roomManager = &roomManager{
30 | Rooms: make(map[int64]*Room),
31 | UserIdleRoomListChan: make(map[*MsgChannel]struct{}),
32 | GameServers: make(map[*GameServer]struct{}),
33 | }
34 |
35 | type GameServer struct {
36 | ExtIp string
37 | AgentPort string
38 | ClientPort string
39 | Client AgentToGameClient
40 | Rooms map[*Room]struct{}
41 | id string
42 | }
43 |
44 | func newGameServer(ExtIp, clientToGamePort, agentToGamePort, id string) (gameServer *GameServer) {
45 | conn, err := grpc.Dial(ExtIp+":"+agentToGamePort, grpc.WithInsecure())
46 | if err != nil {
47 | log.Warn("Agent can't connect to GameServer", err)
48 | return
49 | }
50 | gameServer = &GameServer{
51 | id: id,
52 | ExtIp: ExtIp,
53 | ClientPort: ":" + clientToGamePort,
54 | AgentPort: ":" + agentToGamePort,
55 | Rooms: make(map[*Room]struct{}, 0),
56 | }
57 | gameServer.Client = NewAgentToGameClient(conn)
58 | log.Debug("client", gameServer.Client)
59 | return
60 | }
61 |
62 | type roomManager struct {
63 | UserIdleRoomListChan map[*MsgChannel]struct{}
64 | Rooms map[int64]*Room
65 | GameServers map[*GameServer]struct{}
66 | sync.RWMutex
67 | RoomList *RoomList
68 | }
69 |
70 | func (rm *roomManager) DeleteRoom(room *Room) {
71 | room.DeleteRoom()
72 | delete(rm.Rooms, room.Uuid)
73 | rm.UpdateRoomList()
74 | }
75 |
76 | func (rm *roomManager) ConnectGameServer(ExtIp, clientToGamePort, agentToGamePort, id string) {
77 | rm.GameServers[newGameServer(ExtIp, clientToGamePort, agentToGamePort, id)] = struct{}{}
78 | log.Debug("Connect Game Server")
79 | }
80 |
81 | func (rm *roomManager) GetGameServer() (game *GameServer) {
82 |
83 | for gs, _ := range rm.GameServers {
84 | if len(gs.Rooms) >= MAX_ROOM_IN_POD {
85 | continue
86 | }
87 | game = gs
88 | break
89 | }
90 | if game == nil {
91 | ClusterManager.CreatePod()
92 | game = rm.GetGameServer()
93 | }
94 | return
95 | }
96 | func (rm *roomManager) UpdateRoomList() {
97 | roomList := &RoomList{
98 | Item: make([]*RoomReview, 0),
99 | }
100 | for _, room := range rm.Rooms {
101 | roomList.Item = append(roomList.Item, room.GetReview())
102 | }
103 | for c, _ := range rm.UserIdleRoomListChan {
104 | c.DataCh <- roomList
105 | }
106 | log.Debug("Update room list", roomList)
107 | }
108 |
109 | func (rm *roomManager) AddIdleUserMsgChan(m *MsgChannel) {
110 | if _, ok := rm.UserIdleRoomListChan[m]; ok {
111 | return
112 | }
113 | rm.UserIdleRoomListChan[m] = struct{}{}
114 | }
115 |
116 | func (rm *roomManager) RemoveIdleUserMsgChan(m *MsgChannel) {
117 | if _, ok := rm.UserIdleRoomListChan[m]; ok {
118 | delete(rm.UserIdleRoomListChan, m)
119 | }
120 | }
121 |
122 | func (rm *roomManager) Run() {
123 |
124 | }
125 |
126 | func (rm *roomManager) LeaveRoom() {
127 |
128 | }
129 |
130 | func (rm *roomManager) CreateRoom(master *Session, setting *RoomSetting) *Room {
131 | id, _ := Uid.NewId(ROOM_ID)
132 | room := NewRoom(master, id, setting)
133 | rm.Rooms[id] = room
134 | rm.UpdateRoomList()
135 | return room
136 | }
137 |
138 | func NewRoom(master *Session, roomId int64, setting *RoomSetting) *Room {
139 | room := &Room{
140 | Master: master,
141 | Client: make(map[*Session]struct{}),
142 | Uuid: roomId,
143 | Name: setting.Name,
144 | GameType: setting.GameType,
145 | MaxPlyer: setting.MaxPlayer,
146 | PlayerInRoom: 1,
147 | Review: &RoomReview{
148 | Uuid: roomId,
149 | },
150 | }
151 | room.UpdateReview()
152 | RoomManager.UpdateRoomList()
153 | room.UpdateRoomContent()
154 | return room
155 | }
156 |
157 | type Room struct {
158 | Name string
159 | GameType string
160 | Master *Session
161 | Client map[*Session]struct{}
162 | Uuid int64
163 | IsFull bool
164 | IsCreatOnGameServer bool
165 | MaxPlyer int32
166 | PlayerInRoom int32
167 | Review *RoomReview
168 | GameServer *GameServer
169 | sync.RWMutex
170 | }
171 |
172 | func (r *Room) GetReview() (Review *RoomReview) {
173 | r.RLock()
174 | Review = proto.Clone(r.Review).(*RoomReview)
175 | r.RUnlock()
176 | return
177 | }
178 |
179 | func (r *Room) UpdateReview() {
180 | r.Lock()
181 | r.Review.Name = r.Name
182 | r.Review.GameType = r.GameType
183 | r.Review.InRoomPlayer = r.PlayerInRoom
184 | r.Review.MaxPlayer = r.MaxPlyer
185 | r.Unlock()
186 | RoomManager.UpdateRoomList()
187 | }
188 |
189 | func (r *Room) EnterRoom(client *Session) bool {
190 | if _, ok := r.Client[client]; ok {
191 | return false
192 | }
193 | if r.IsFull {
194 | return false
195 | }
196 | r.Client[client] = struct{}{}
197 | r.PlayerInRoom += 1
198 | if r.PlayerInRoom == r.MaxPlyer {
199 | r.IsFull = true
200 | }
201 | r.UpdateReview()
202 | r.UpdateRoomContent()
203 | return true
204 | }
205 |
206 | func (r *Room) KickOut(master *Session, client *Session) bool {
207 | if master != r.Master {
208 | return false
209 | }
210 | if _, ok := r.Client[client]; ok {
211 | client.Room = nil
212 | delete(r.Client, client)
213 | r.PlayerInRoom -= 1
214 | r.IsFull = false
215 | r.UpdateReview()
216 | r.UpdateRoomContent()
217 | return true
218 | }
219 | return false
220 | }
221 |
222 | func (r *Room) DeleteRoom() bool {
223 | if r.IsCreatOnGameServer {
224 |
225 | _, err := r.GameServer.Client.DeletGameRoom(context.Background(), &RoomInfo{Uuid: r.Uuid})
226 | if err != nil {
227 | log.Warn(err)
228 | }
229 | }
230 | if r.Master != nil {
231 | r.Master.Room = nil
232 | }
233 | for s, _ := range r.Client {
234 | s.Room = nil
235 | }
236 | return true
237 | }
238 |
239 | func (r *Room) LeaveRoom(s *Session) bool {
240 | if s == r.Master {
241 | s.Room = nil
242 | r.Master = nil
243 | r.PlayerInRoom -= 1
244 | r.IsFull = false
245 | r.UpdateReview()
246 | r.UpdateRoomContent()
247 |
248 | } else {
249 | if _, ok := r.Client[s]; ok {
250 | s.Room = nil
251 | delete(r.Client, s)
252 | r.PlayerInRoom -= 1
253 | r.IsFull = false
254 | r.UpdateReview()
255 | r.UpdateRoomContent()
256 |
257 | } else {
258 | return false
259 | }
260 | }
261 | if len(r.Client) == 0 && r.Master == nil {
262 | RoomManager.DeleteRoom(r)
263 | }
264 | return true
265 | }
266 |
267 | func (r *Room) UpdateRoomContent() {
268 | content := &RoomContent{
269 | Uuid: r.Uuid,
270 | Players: make(map[string]*PlayerInfo),
271 | }
272 | if r.Master != nil {
273 | pInfo := r.Master.GetPlayerInfo()
274 | content.Players[pInfo.UserName] = pInfo
275 | }
276 | for s, _ := range r.Client {
277 | pInfo := s.GetPlayerInfo()
278 | content.Players[pInfo.UserName] = pInfo
279 | }
280 |
281 | if r.Master != nil {
282 | ch := r.Master.GetMsgChan("RoomContent")
283 | ch.DataCh <- content
284 | }
285 | for s, _ := range r.Client {
286 | ch := s.GetMsgChan("RoomContent")
287 | ch.DataCh <- content
288 | }
289 | }
290 |
291 | func (r *Room) CheckReady() bool {
292 | r.UpdateRoomContent()
293 | if !r.Master.IsReady {
294 | return false
295 | }
296 | for s, _ := range r.Client {
297 | if !s.IsReady {
298 | return false
299 | }
300 | }
301 | r.CreateRoomOnGameServer()
302 | return true
303 | //Start Game
304 | }
305 |
306 | func (r *Room) CreateRoomOnGameServer() {
307 | if !r.IsCreatOnGameServer {
308 | gameCreation := &GameCreation{
309 | PlayerSessions: make([]*SessionInfo, 0),
310 | RoomInfo: &RoomInfo{Uuid: r.Uuid},
311 | }
312 | gameCreation.RoomInfo.GameType = r.GameType
313 | r.Master.SetState(int32(SessionInfo_ConnectingGame))
314 | gameCreation.PlayerSessions = append(gameCreation.PlayerSessions, r.Master.GetSessionInfo())
315 | for s, _ := range r.Client {
316 | s.SetState(int32(SessionInfo_ConnectingGame))
317 | gameCreation.PlayerSessions = append(gameCreation.PlayerSessions, s.GetSessionInfo())
318 | }
319 | gs := RoomManager.GetGameServer()
320 | gs.Rooms[r] = struct{}{}
321 | pem := make(chan *PemKey)
322 | var getKey func(sig chan *PemKey)
323 | getKey = func(sig chan *PemKey) {
324 | key, err := gs.Client.AquireGameRoom(context.Background(), gameCreation)
325 | if err != nil {
326 | log.Warn("GameServer has some issue: ", err)
327 | time.Sleep(2 * time.Second)
328 | go getKey(sig)
329 | return
330 | }
331 | sig <- key
332 | }
333 | go getKey(pem)
334 | key := <-pem
335 | c := r.Master.GetMsgChan("ServerInfo")
336 | serverInfo := &ServerInfo{
337 | Addr: gs.ExtIp,
338 | Port: gs.ClientPort,
339 | PublicKey: key.SSL,
340 | }
341 | c.DataCh <- serverInfo
342 | r.Master.ServerInfo = serverInfo
343 | for s, _ := range r.Client {
344 | s.ServerInfo = serverInfo
345 | c = s.GetMsgChan("ServerInfo")
346 | c.DataCh <- serverInfo
347 | }
348 | r.GameServer = gs
349 | r.IsCreatOnGameServer = true
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/agent/session/session.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | //"github.com/daniel840829/gameServer/entity"
5 | . "github.com/daniel840829/gameServer/msg"
6 | "github.com/daniel840829/gameServer/user"
7 | . "github.com/daniel840829/gameServer/uuid"
8 | log "github.com/sirupsen/logrus"
9 | "google.golang.org/grpc/metadata"
10 | "strconv"
11 | "sync"
12 | )
13 |
14 | type MsgChannel struct {
15 | DataCh chan (interface{})
16 | StopSignal chan (struct{})
17 | }
18 |
19 | func (m *MsgChannel) Close() {
20 | select {
21 | case <-m.StopSignal:
22 | return
23 | default:
24 | close(m.StopSignal)
25 | }
26 | }
27 |
28 | func NewMsgChannel(bufferNumber int32) *MsgChannel {
29 | return &MsgChannel{
30 | DataCh: make(chan (interface{}), bufferNumber),
31 | StopSignal: make(chan (struct{}), 1),
32 | }
33 | }
34 |
35 | func NewMsgChannelManager() *MsgChannelManager {
36 | return &MsgChannelManager{
37 | make(map[string]*MsgChannel),
38 | }
39 | }
40 |
41 | type MsgChannelManager struct {
42 | c map[string]*MsgChannel
43 | }
44 |
45 | func (m *MsgChannelManager) AddMsgChan(name string, bufferNumber int32) bool {
46 | if _, ok := m.c[name]; ok {
47 | return false
48 | }
49 | m.c[name] = NewMsgChannel(bufferNumber)
50 | return true
51 | }
52 |
53 | func (m *MsgChannelManager) GetMsgChan(name string) *MsgChannel {
54 | return m.c[name]
55 | }
56 |
57 | func (m *MsgChannelManager) CloseMsgChan(name string) {
58 | if ch, ok := m.c[name]; ok {
59 | ch.Close()
60 | delete(m.c, name)
61 | }
62 | }
63 |
64 | type sessionManager struct {
65 | Sessions map[int64]*Session
66 | UserNameMapSession map[string]int64
67 | sync.RWMutex
68 | }
69 |
70 | func (sm *sessionManager) CleanSession(id int64) {
71 | if s, ok := sm.Sessions[id]; ok {
72 | s.Room.LeaveRoom(s)
73 | }
74 | }
75 |
76 | func (sm *sessionManager) MakeSession() int64 {
77 | s := NewSession()
78 | sm.Lock()
79 | sm.Sessions[s.Info.Uuid] = s
80 | sm.Unlock()
81 | return s.Info.Uuid
82 | }
83 |
84 | func (sm *sessionManager) GetSession(md metadata.MD) *Session {
85 | mdid := md.Get("session-id")
86 | if len(mdid) == 0 {
87 | return nil
88 | }
89 | id, err := strconv.ParseInt(mdid[0], 10, 64)
90 | if err != nil {
91 | return nil
92 | }
93 | s, ok := sm.Sessions[id]
94 | if !ok {
95 | return nil
96 | }
97 | s.RLock()
98 | if s.User != nil {
99 | uname := md.Get("uname")
100 | if len(uname) == 0 {
101 | s.RUnlock()
102 | return nil
103 | } else if s.User.UserInfo.UserName != uname[0] {
104 | s.RUnlock()
105 | return nil
106 | }
107 | } else {
108 | uname := md.Get("uname")
109 | if len(uname) != 0 {
110 | return nil
111 | }
112 | }
113 | s.RUnlock()
114 | return s
115 | }
116 | func NewSession() *Session {
117 | s := &Session{
118 | Info: &SessionInfo{},
119 | MsgChannelManager: NewMsgChannelManager(),
120 | PlayerInfo: &PlayerInfo{},
121 | }
122 | for i := int32(SessionInfo_NoSession); i <= int32(SessionInfo_GameServerWaitReconnect); i++ {
123 | ss := SessionStateFactory.makeSessionState(s, SessionInfo_SessionState(i))
124 | s.States = append(s.States, ss)
125 | }
126 | s.SetState(0)
127 | s.State.CreateSession()
128 | return s
129 | }
130 |
131 | type Session struct {
132 | Info *SessionInfo
133 | State SessionState
134 | SessionKey int64
135 | User *user.User
136 | States []SessionState
137 | Room *Room
138 | *MsgChannelManager
139 | sync.RWMutex
140 | PlayerInfo *PlayerInfo
141 | TeamNo int32
142 | IsReady bool
143 | ServerInfo *ServerInfo
144 | }
145 |
146 | func (s *Session) GetPlayerInfo() *PlayerInfo {
147 | if s.User == nil {
148 | return nil
149 | }
150 | s.PlayerInfo.UserName = s.User.UserInfo.UserName
151 | s.PlayerInfo.UserId = s.User.UserInfo.Uuid
152 | if s.User.UserInfo.UsedCharacter == int64(0) {
153 | for id, c := range s.User.UserInfo.OwnCharacter {
154 | s.User.UserInfo.UsedCharacter = id
155 | s.PlayerInfo.Character = c
156 | break
157 | }
158 | } else {
159 | s.PlayerInfo.Character = s.User.UserInfo.OwnCharacter[s.User.UserInfo.UsedCharacter]
160 | }
161 | s.PlayerInfo.TeamNo = s.TeamNo
162 | s.PlayerInfo.IsReady = s.IsReady
163 | return s.PlayerInfo
164 | }
165 |
166 | func (s *Session) SetState(state_index int32) {
167 | s.State = s.States[state_index]
168 | }
169 |
170 | func (s *Session) GetSessionInfo() *SessionInfo {
171 | info := &UserInfo{}
172 | if s.User != nil {
173 | info = s.User.GetInfo()
174 | }
175 | return &SessionInfo{
176 | Uuid: s.Info.Uuid,
177 | UserInfo: info,
178 | State: s.State.GetStateCode(),
179 | }
180 | }
181 |
182 | func (s *Session) GetSessionCache() *SessionCache {
183 | return &SessionCache{
184 | GameServerInfo: s.ServerInfo,
185 | SessionInfo: s.GetSessionInfo(),
186 | }
187 | }
188 |
189 | type SessionState interface {
190 | SetSession(s *Session) bool
191 | SetStateCode(SessionInfo_SessionState)
192 | GetStateCode() SessionInfo_SessionState
193 | CreateSession() int64
194 | Login(uname string, pswd string) *user.User
195 | Logout() bool
196 | Regist(uname string, pswd string, info ...string) bool
197 | CreateRoom(setting *RoomSetting) bool
198 | EnterRoom(roomId int64) bool
199 | DeleteRoom() bool
200 | ReadyRoom() bool
201 | LeaveRoom() bool
202 | StartRoom() bool
203 | SettingCharacter(*CharacterSetting) bool
204 | SettingRoom() bool
205 | CancelReady() bool
206 | EndRoom() bool
207 | String() string
208 | Lock()
209 | Unlock()
210 | }
211 |
212 | func (sb *SessionStateBase) SetSession(s *Session) bool {
213 | if sb.Session != nil {
214 | return false
215 | }
216 | sb.Session = s
217 | return true
218 | }
219 |
220 | func (sb *SessionStateBase) String() string {
221 | return SessionInfo_SessionState_name[int32(sb.StateCode)]
222 | }
223 |
224 | func (sb *SessionStateBase) SetStateCode(code SessionInfo_SessionState) {
225 | sb.StateCode = code
226 | }
227 | func (sb *SessionStateBase) GetStateCode() SessionInfo_SessionState {
228 | return sb.StateCode
229 | }
230 | func (sb *SessionStateBase) CreateSession() int64 {
231 | return 0
232 | }
233 | func (sb *SessionStateBase) Login(uname string, pswd string) *user.User {
234 | return nil
235 | }
236 | func (sb *SessionStateBase) Logout() bool {
237 | return false
238 | }
239 | func (sb *SessionStateBase) Regist(uname string, pswd string, info ...string) bool {
240 | return false
241 | }
242 | func (sb *SessionStateBase) CreateRoom(setting *RoomSetting) bool {
243 | return false
244 | }
245 | func (sb *SessionStateBase) EnterRoom(roomId int64) bool {
246 | return false
247 | }
248 | func (sb *SessionStateBase) DeleteRoom() bool {
249 | return false
250 | }
251 | func (sb *SessionStateBase) ReadyRoom() bool {
252 | return false
253 | }
254 | func (sb *SessionStateBase) LeaveRoom() bool {
255 | return false
256 | }
257 | func (sb *SessionStateBase) StartRoom() bool {
258 | return false
259 | }
260 | func (sb *SessionStateBase) SettingCharacter(*CharacterSetting) bool {
261 | return false
262 | }
263 | func (sb *SessionStateBase) SettingRoom() bool {
264 | return false
265 | }
266 | func (sb *SessionStateBase) EndRoom() bool {
267 | return false
268 | }
269 |
270 | func (sb *SessionStateBase) CancelReady() bool {
271 | return false
272 | }
273 |
274 | type SessionStateBase struct {
275 | StateCode SessionInfo_SessionState
276 | Session *Session
277 | sync.RWMutex
278 | }
279 |
280 | type NoSessionState struct {
281 | SessionStateBase
282 | }
283 |
284 | func (ss *NoSessionState) CreateSession() int64 {
285 | //TODO
286 | s := ss.Session
287 | s.Lock()
288 | s.Info.Uuid, _ = Uid.NewId(SESSION_ID)
289 | uuid := s.Info.Uuid
290 | ss.Session.SetState(int32(ss.StateCode) + 1)
291 | s.Unlock()
292 | return uuid
293 | }
294 |
295 | type GuestSessionState struct {
296 | SessionStateBase
297 | }
298 |
299 | func (ss *GuestSessionState) Regist(uname string, pswd string, info ...string) bool {
300 | //TODO
301 | in := &RegistInput{UserName: uname, Pswd: pswd}
302 | _, err := user.Manager.Regist(in)
303 | if err != nil {
304 | return false
305 | }
306 | return true
307 | }
308 |
309 | func (ss *GuestSessionState) Login(uname string, pswd string) *user.User {
310 | //TODO
311 |
312 | in := &LoginInput{UserName: uname, Pswd: pswd}
313 | userInfo, err := user.Manager.Login(in)
314 | if err != nil {
315 | log.Warn(err)
316 | }
317 | if userInfo == nil {
318 | return nil
319 | }
320 | user := user.Manager.UserOnline[userInfo.Uuid]
321 | ss.Session.User = user
322 | ss.Session.SetState(int32(ss.StateCode) + 1)
323 | log.Info("user state:", ss.Session.State)
324 | ss.Session.AddMsgChan("RoomList", 10)
325 | RoomManager.AddIdleUserMsgChan(ss.Session.GetMsgChan("RoomList"))
326 | RoomManager.UpdateRoomList()
327 | if id, ok := Manager.UserNameMapSession[uname]; ok {
328 | Manager.CleanSession(id)
329 | }
330 | Manager.UserNameMapSession[uname] = ss.Session.Info.Uuid
331 | return user
332 | }
333 |
334 | type UserIdleSessionState struct {
335 | SessionStateBase
336 | }
337 |
338 | func (ss *UserIdleSessionState) CreateRoom(roomSetting *RoomSetting) bool {
339 | //TODO
340 | ss.Session.Info.Capacity = SessionInfo_RoomMaster
341 | ss.Session.AddMsgChan("RoomContent", 10)
342 | room := RoomManager.CreateRoom(ss.Session, roomSetting)
343 | ss.Session.Room = room
344 | ss.Session.SetState(int32(ss.StateCode) + 1)
345 | RoomManager.RemoveIdleUserMsgChan(ss.Session.GetMsgChan("RoomList"))
346 | ss.Session.CloseMsgChan("RoomList")
347 | ss.Session.AddMsgChan("ServerInfo", 1)
348 | return true
349 | }
350 |
351 | func (ss *UserIdleSessionState) EnterRoom(roomId int64) bool {
352 | room := RoomManager.Rooms[roomId]
353 | if room != nil {
354 | ss.Session.AddMsgChan("RoomContent", 2)
355 | if room.EnterRoom(ss.Session) {
356 | ss.Session.Room = room
357 | ss.Session.SetState(int32(ss.StateCode) + 1)
358 | RoomManager.RemoveIdleUserMsgChan(ss.Session.GetMsgChan("RoomList"))
359 | ss.Session.CloseMsgChan("RoomList")
360 | ss.Session.AddMsgChan("ServerInfo", 1)
361 | return true
362 | } else {
363 | ss.Session.CloseMsgChan("RoomContent")
364 | }
365 | }
366 | return false
367 | }
368 |
369 | type UserInRoomSessionState struct {
370 | SessionStateBase
371 | }
372 |
373 | func (ss *UserInRoomSessionState) DeleteRoom() bool {
374 | return false
375 | }
376 |
377 | func (ss *UserInRoomSessionState) ReadyRoom() bool {
378 | ss.Session.IsReady = true
379 | ss.Session.Room.CheckReady()
380 | return true
381 | }
382 |
383 | func (ss *UserInRoomSessionState) LeaveRoom() bool {
384 | ss.Session.IsReady = false
385 | ss.Session.Room = nil
386 | ss.Session.SetState(int32(ss.StateCode) - 1)
387 | return false
388 | }
389 |
390 | func (ss *UserInRoomSessionState) SettingCharacter(setting *CharacterSetting) bool {
391 | if ss.Session.User.SetCharacter(setting) {
392 | ss.Session.Room.UpdateRoomContent()
393 | return true
394 | }
395 | return false
396 |
397 | }
398 | func (ss *UserInRoomSessionState) CancelReady() bool {
399 | ss.Session.IsReady = false
400 | ss.Session.Room.CheckReady()
401 | return true
402 | }
403 |
404 | type ConnectingGameSessionState struct {
405 | SessionStateBase
406 | }
407 |
408 | func (ss *ConnectingGameSessionState) EndRoom() bool {
409 | return false
410 | }
411 |
412 | type sessionStateFactory struct {
413 | }
414 |
415 | func (sf *sessionStateFactory) makeSessionState(session *Session, state_code SessionInfo_SessionState) SessionState {
416 | var s SessionState
417 | switch state_code {
418 | case SessionInfo_NoSession:
419 | s = &NoSessionState{}
420 | case SessionInfo_Guest:
421 | s = &GuestSessionState{}
422 | case SessionInfo_UserIdle:
423 | s = &UserIdleSessionState{}
424 | case SessionInfo_UserInRoom:
425 | s = &UserInRoomSessionState{}
426 | case SessionInfo_ConnectingGame:
427 | s = &ConnectingGameSessionState{}
428 | default:
429 | s = &SessionStateBase{}
430 | }
431 | s.Lock()
432 | s.SetSession(session)
433 | s.SetStateCode(state_code)
434 | s.Unlock()
435 | return s
436 | }
437 |
438 | var Manager *sessionManager
439 |
440 | var SessionStateFactory *sessionStateFactory
441 |
442 | func init() {
443 | Manager = &sessionManager{
444 | Sessions: make(map[int64]*Session),
445 | UserNameMapSession: make(map[string]int64),
446 | }
447 | SessionStateFactory = &sessionStateFactory{}
448 | }
449 |
--------------------------------------------------------------------------------
/cluster/accountbind.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1beta1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: fabric8-rbac
5 | subjects:
6 | - kind: ServiceAccount
7 | # Reference to upper's `metadata.name`
8 | name: default
9 | # Reference to upper's `metadata.namespace`
10 | namespace: default
11 | roleRef:
12 | kind: ClusterRole
13 | name: cluster-admin
14 | apiGroup: rbac.authorization.k8s.io
15 |
--------------------------------------------------------------------------------
/cluster/agent.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: agent
5 | labels:
6 | app: gameserver
7 | type: agent
8 | id: ''
9 | agentToGamePort: ''
10 | clientToGamePort: '8080'
11 | clientToAgentPort: '50051'
12 | spec:
13 | hostNetwork: true
14 | restartPolicy: Never
15 | containers:
16 | - name: game-server
17 | image: daniel840829/gameplayserver:circleci-latest
18 | imagePullPolicy: Always
19 | env:
20 | - name: CLIENT_TO_GAME_PORT
21 | valueFrom:
22 | fieldRef:
23 | fieldPath: metadata.labels['clientToGamePort']
24 | - name: AGENT_TO_GAME_PORT
25 | valueFrom:
26 | fieldRef:
27 | fieldPath: metadata.labels['agentToGamePort']
28 | - name: SERVER_TYPE
29 | valueFrom:
30 | fieldRef:
31 | fieldPath: metadata.labels['type']
32 | - name: ID
33 | valueFrom:
34 | fieldRef:
35 | fieldPath: metadata.labels['id']
36 | - name: CLIENT_TO_AGENT_PORT
37 | valueFrom:
38 | fieldRef:
39 | fieldPath: metadata.labels['clientToAgentPort']
40 | - name: MGO_PASS
41 | valueFrom:
42 | secretKeyRef:
43 | name: agent-secret
44 | key: mgoPass
45 | - name: MGO_USER
46 | valueFrom:
47 | secretKeyRef:
48 | name: agent-secret
49 | key: mgoUser
50 | - name: MGO_ADDR
51 | valueFrom:
52 | secretKeyRef:
53 | name: agent-secret
54 | key: mgoAddr
55 |
--------------------------------------------------------------------------------
/cluster/deploy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1beta2
2 | kind: Deployment
3 | metadata:
4 | name: hello-deployment
5 | spec:
6 | replicas: 3
7 | selector:
8 | matchLabels:
9 | app: gameserver
10 | type: game
11 | template:
12 | metadata:
13 | labels:
14 | app: gameserver
15 | type: game
16 | id: 0
17 | spec:
18 | hostNetwork: true
19 | containers:
20 | - name: gameplayserver
21 | image: daniel840829/gameplayserver:v3
22 | ports:
23 | - name: client-port
24 | containerPort: 50051
25 | - name: agent-port
26 | containerPort: 3000
27 |
--------------------------------------------------------------------------------
/cluster/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: gameplayserver-
5 | labels:
6 | app: gameserver
7 | type: game
8 | id: ''
9 | agentToGamePort: ''
10 | clientToGamePort: ''
11 | clientToAgentPort: ''
12 | spec:
13 | hostNetwork: true
14 | imagePullPolicy: Always
15 | containers:
16 | - name: game-server
17 | image: daniel840829/gameplayserver:circleci-latest
18 | env:
19 | - name: CLIENT_TO_GAME_PORT
20 | valueFrom:
21 | fieldRef:
22 | fieldPath: metadata.labels['clientToGamePort']
23 | - name: AGENT_TO_GAME_PORT
24 | valueFrom:
25 | fieldRef:
26 | fieldPath: metadata.labels['agentToGamePort']
27 | - name: SERVER_TYPE
28 | valueFrom:
29 | fieldRef:
30 | fieldPath: metadata.labels['type']
31 | - name: ID
32 | valueFrom:
33 | fieldRef:
34 | fieldPath: metadata.labels['id']
35 | - name: CLIENT_TO_AGENT_PORT
36 | valueFrom:
37 | fieldRef:
38 | fieldPath: metadata.labels['clientToAgentPort']
39 |
--------------------------------------------------------------------------------
/data.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package remoteProxy
3 |
4 |
5 |
6 | message Invoker {
7 | string Func = 1;
8 | int64 id = 2;
9 | string objName = 3;
10 | repeated Data Arg = 4;
11 | }
12 |
13 | message Data {
14 | oneof data{
15 | string String = 1;
16 | int32 Int32 = 2;
17 | int64 Int64 = 3;
18 | double Double = 4;
19 | float Float =5;
20 | }
21 | }
22 |
23 | message CallableObj {
24 | string Name = 1;
25 | }
26 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danie1Lin/Distributed-Golang-Game-Server/620f6c91b6e21db9f8eca88916b1130223ff8b81/demo.gif
--------------------------------------------------------------------------------
/game/agentToGame.go:
--------------------------------------------------------------------------------
1 | package game
2 |
3 | import (
4 | "github.com/daniel840829/gameServer/game/session"
5 | . "github.com/daniel840829/gameServer/msg"
6 | log "github.com/sirupsen/logrus"
7 | "golang.org/x/net/context"
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/metadata"
10 | "google.golang.org/grpc/status"
11 | // io"
12 | "time"
13 | )
14 |
15 | /*
16 | type AgentToGameServer interface {
17 | // SessionManager
18 | AquireGameRoom(context.Context, *GameCreation) (*PemKey, error)
19 | }
20 | */
21 |
22 | type ATGServer struct {
23 | }
24 |
25 | type CTGServer struct {
26 | }
27 |
28 | func (g *CTGServer) TimeCalibrate(c context.Context, e *Empty) (*TimeStamp, error) {
29 | log.Debug("Calibration")
30 | return &TimeStamp{Value: int64(time.Now().UnixNano() / 1000000)}, nil
31 | }
32 |
33 | func (g *ATGServer) AquireGameRoom(c context.Context, gc *GameCreation) (*PemKey, error) {
34 | err := session.RoomManager.CreateGame(gc)
35 | return &PemKey{SSL: "HI"}, err
36 | }
37 |
38 | func (g *ATGServer) DeletGameRoom(c context.Context, info *RoomInfo) (*Success, error) {
39 | err := session.RoomManager.DeleteRoom(info)
40 | if err != nil {
41 | return &Success{}, err
42 | }
43 | return &Success{Ok: true}, nil
44 | }
45 |
46 | func (g *CTGServer) PlayerInput(stream ClientToGame_PlayerInputServer) error {
47 | log.Debug("input")
48 | session := GetSesionFromContext(stream.Context())
49 | if session == nil {
50 | return status.Errorf(codes.NotFound, "Session Not Found!")
51 | }
52 | LOOP2:
53 | for {
54 | select {
55 | default:
56 | input, err := stream.Recv()
57 | if HandleRPCError(session, err) {
58 | break LOOP2
59 | }
60 | session.State.HandleInput(input)
61 | }
62 | }
63 |
64 | return nil
65 | }
66 | func (g *CTGServer) UpdateGameFrame(e *Empty, stream ClientToGame_UpdateGameFrameServer) error {
67 |
68 | log.Debug("Frame")
69 | session := GetSesionFromContext(stream.Context())
70 | if session == nil {
71 | return status.Errorf(codes.NotFound, "Session Not Found!")
72 | }
73 | msgch := session.GetMsgChan("GameFrame")
74 | if msgch == nil {
75 | return status.Errorf(codes.Internal, "GameFrame MsgChan Not Found!")
76 | }
77 | session.State.StartGame()
78 | LOOP:
79 | for {
80 | select {
81 | case <-msgch.StopSignal:
82 | break LOOP
83 | case msg := <-msgch.DataCh:
84 | err := stream.Send(msg.(*GameFrame))
85 | if HandleRPCError(session, err) {
86 | break LOOP
87 | }
88 | }
89 | }
90 |
91 | return nil
92 | }
93 | func (g *CTGServer) Pipe(ClientToGame_PipeServer) error {
94 | return nil
95 | }
96 |
97 | func GetSesionFromContext(c context.Context) *session.Session {
98 | md, ok := metadata.FromIncomingContext(c)
99 | if !ok {
100 | return nil
101 | }
102 | s := session.Manager.GetSession(md)
103 | return s
104 | }
105 |
106 | func HandleRPCError(s *session.Session, e error) (IfEndStream bool) {
107 | if e == nil {
108 | return false
109 | }
110 | st, _ := status.FromError(e)
111 | log.Warn(st.Message())
112 | switch st.Code() {
113 | case codes.Canceled:
114 | IfEndStream = ReconnectError(s)
115 | case codes.Unavailable:
116 | IfEndStream = ReconnectError(s)
117 | default:
118 | IfEndStream = RecordError(s, e)
119 | }
120 | return
121 | }
122 |
123 | func IgnoreError(s *session.Session) (IfEndStream bool) {
124 | return true
125 | }
126 |
127 | func RecordError(s *session.Session, e error) (IfEndStream bool) {
128 | log.Warn("RPCError:", e)
129 | EndConnection(s)
130 | return true
131 | }
132 | func ReconnectError(s *session.Session) (IfEndStream bool) {
133 | log.Warn("Wait to reconnect")
134 | s.State.WaitReconnect()
135 | return true
136 | }
137 |
138 | func EndConnection(s *session.Session) (IfEndStream bool) {
139 | s.State.EndConnection()
140 | return true
141 | }
142 |
--------------------------------------------------------------------------------
/game/clientToGame.go:
--------------------------------------------------------------------------------
1 | package game
2 |
3 | /*
4 | type ClientToGameServer interface {
5 | // roomManager
6 | EnterRoom(context.Context, *Empty) (*Success, error)
7 | LeaveRoom(context.Context, *Empty) (*Success, error)
8 | // entityManager
9 | PlayerInput(ClientToGame_PlayerInputServer) error
10 | // View
11 | UpdateGameFrame(*Empty, ClientToGame_UpdateGameFrameServer) error
12 | Pipe(ClientToGame_PipeServer) error
13 | }
14 | */
15 |
--------------------------------------------------------------------------------
/game/data/PhysicObj.json:
--------------------------------------------------------------------------------
1 | {
2 | "Tank":{
3 | "Name":"Tank",
4 | "Type":"Player",
5 | "Shape":"Box",
6 | "Lens":[1.8, 2, 1.8],
7 | "Mass":1.0
8 | },
9 | "Shell":{
10 | "Name":"Shell",
11 | "Type":"Skill",
12 | "Shape":"Capsule",
13 | "Lens":[0.15,0.55],
14 | "Mass":0.30
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/game/data/data.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "encoding/json"
5 | log "github.com/sirupsen/logrus"
6 | "io/ioutil"
7 | "os"
8 | )
9 |
10 | type Obj struct {
11 | Name string
12 | Type string
13 | Shape string
14 | Lens []float64
15 | Mass float64
16 | }
17 |
18 | type Objs map[string]Obj
19 |
20 | func ReadObjData() Objs {
21 | dir := os.Getenv("GOPATH")
22 | raw, err := ioutil.ReadFile(dir + "/src/github.com/daniel840829/gameServer/data/PhysicObj.json")
23 | if err != nil {
24 | log.Fatal(err.Error())
25 | os.Exit(1)
26 | }
27 |
28 | var c Objs
29 | json.Unmarshal(raw, &c)
30 | return c
31 | }
32 |
33 | var ObjData Objs
34 |
35 | func init() {
36 | log.SetFormatter(&log.TextFormatter{})
37 | log.SetOutput(os.Stdout)
38 | log.SetLevel(log.DebugLevel)
39 | log.Info("{data}[init] Loading Data...")
40 | ObjData = ReadObjData()
41 | log.Info("{data}[init] ObjData: ", ObjData)
42 | }
43 |
--------------------------------------------------------------------------------
/game/entity/attack.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | )
6 |
7 | type AttackBehavier interface {
8 | Attack(FromPos Position, Value int64)
9 | AutoAttack(targetId int64)
10 | }
11 |
12 | type AttackBase struct {
13 | AutoAttackable bool
14 | CoolDownLeftTime int32
15 | MaxValue float64
16 | MaxCombo int32
17 | }
18 |
19 | func (atk *AttackBase) Attack(FromPos Position, Value float64) {
20 |
21 | }
22 |
23 | func (atk *AttackBase) AutoAttack(targetId int64) {
24 |
25 | }
26 |
27 | func NewShootBehavier(autoAttackable bool, coolDownLeftTime int32, maxValue float64, maxCombo int32) *ShootBehavier {
28 | return &ShootBehavier{
29 | AttackBase: AttackBase{
30 | AutoAttackable: autoAttackable,
31 | CoolDownLeftTime: coolDownLeftTime,
32 | MaxValue: maxValue,
33 | MaxCombo: maxCombo,
34 | },
35 | }
36 | }
37 |
38 | type ShootBehavier struct {
39 | AttackBase
40 | }
41 |
42 | func (atk *ShootBehavier) Attack(FromPos Position, Value float64) {
43 |
44 | }
45 |
46 | func (atk *ShootBehavier) AutoAttack(targetId int64) {
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/game/entity/enemy.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/physic"
6 | "github.com/daniel840829/ode"
7 | "github.com/gazed/vu/math/lin"
8 | log "github.com/sirupsen/logrus"
9 | )
10 |
11 | type Enemy struct {
12 | Player
13 | CD int64
14 | }
15 |
16 | func (e *Enemy) PhysicUpdate() {
17 | e.Player.PhysicUpdate()
18 | }
19 |
20 | func (e *Enemy) Tick() {
21 | e.FindTargetAndAttack(30.0)
22 | }
23 |
24 | func (e *Enemy) FindTargetAndAttack(searchRadius float64) {
25 | e.CD += 1
26 | //var targetId int64
27 | var targetPos ode.Vector3
28 | var targetDis float64 = searchRadius
29 | var isFindTarget bool = false
30 | var isReadyToAttack bool = false
31 | //Loop obj in AOE
32 | for id, pos := range e.Obj.AOEObjs {
33 | //Check if it is real player
34 | if _, ok := e.Room.EntityOfUser[id]; ok {
35 | dis := physic.V3_OdeToLin(e.Obj.CBody.PosRelPoint(pos)).Len()
36 | if targetDis > dis && dis > 1 {
37 | targetDis = dis
38 | //targetId = id
39 | targetPos = pos
40 | isFindTarget = true
41 | if targetDis < 20 && e.CD > 100 {
42 | isReadyToAttack = true
43 | }
44 | }
45 | }
46 | }
47 | if isFindTarget {
48 | directionV3 := lin.NewV3().Sub(physic.V3_OdeToLin(targetPos), physic.V3_OdeToLin(e.Obj.CBody.Position()))
49 | targetQ := physic.Q_OdeToLin(physic.DirectionV3ToQuaternion(directionV3))
50 | NowQ := physic.Q_OdeToLin(e.Obj.CBody.Quaternion())
51 | if isReadyToAttack {
52 | e.Obj.CBody.SetQuaternion(physic.Q_LinToOde(targetQ))
53 | e.Shoot(&CallFuncInfo{Value: 15})
54 | e.CD = 0
55 | } else {
56 | MoveToQ := lin.NewQ().Nlerp(NowQ, targetQ, 0.3)
57 | log.Debugf("[Enemy]{Tick} m%v,targetQ:%v,NowQ:%v,MoveToQ:%v", targetQ, NowQ, MoveToQ)
58 | e.Obj.CBody.SetQuaternion(physic.Q_LinToOde(MoveToQ))
59 | input := &Input{
60 | V_Movement: float32(targetDis / 25.0),
61 | }
62 | e.Move(input)
63 |
64 | }
65 | } else {
66 | input := &Input{}
67 | e.Move(input)
68 | }
69 | e.Obj.ClearAOE()
70 | }
71 |
--------------------------------------------------------------------------------
/game/entity/entity.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "fmt"
5 | . "github.com/daniel840829/gameServer/msg"
6 | "github.com/daniel840829/gameServer/physic"
7 | "github.com/golang/protobuf/proto"
8 | log "github.com/sirupsen/logrus"
9 | "os"
10 | "sync"
11 | )
12 |
13 | func init() {
14 | // Log as JSON instead of the default ASCII formatter.
15 | log.SetFormatter(&log.TextFormatter{})
16 |
17 | // Output to stdout instead of the default stderr
18 | // Can be any io.Writer, see below for File example
19 | log.SetOutput(os.Stdout)
20 |
21 | // Only log the warning severity or above.
22 | log.SetLevel(log.DebugLevel)
23 | }
24 |
25 | type Entity struct {
26 | sync.RWMutex
27 | EntityInfo *Character
28 | TypeName string
29 | Health float32
30 | Alive bool
31 | I IEntity
32 | GM *GameManager
33 | Room *Room
34 | World *physic.World
35 | Obj *physic.Obj
36 | Skill map[string]AttackBehavier
37 | }
38 | type IEntity interface {
39 | IGameBehavier
40 | Hit(int32)
41 | GetInfo() *Character
42 | Init(*GameManager, *Room, *Character)
43 | Move(in *Input)
44 | GetTransform() *TransForm
45 | Harm(blood float32)
46 | }
47 |
48 | func (e *Entity) GetInfo() *Character {
49 | e.RLock()
50 | entityInfo := proto.Clone(e.EntityInfo).(*Character)
51 | e.RUnlock()
52 | return entityInfo
53 | }
54 | func (e *Entity) Hit(damage int32) {
55 | fmt.Println("-", damage)
56 | }
57 |
58 | func (e *Entity) Harm(blood float32) {
59 | e.Lock()
60 | e.Health -= blood
61 | if e.Health <= 0 {
62 | //Dead
63 | e.Alive = false
64 | e.Unlock()
65 | e.Destroy()
66 | return
67 | }
68 | f := &CallFuncInfo{
69 | Func: "Health",
70 | Value: e.Health,
71 | TargetId: e.EntityInfo.Uuid,
72 | }
73 | e.Room.SendFuncToAll(f)
74 | e.Unlock()
75 | }
76 |
77 | func (e *Entity) Init(gm *GameManager, room *Room, entityInfo *Character) {
78 | e.GM = gm
79 | e.EntityInfo = entityInfo
80 | e.Room = room
81 | e.World = room.World
82 | var ok bool
83 | e.Obj, ok = room.World.Objs.Get(entityInfo.Uuid)
84 | if !ok {
85 | log.Fatal("[entity]{init} Get obj ", entityInfo.Uuid, " is not found. ")
86 | }
87 | //call All client create enitity at some point
88 | e.costumeInit()
89 | }
90 |
91 | func (e *Entity) costumeInit() {
92 | log.Warn("Please define your costumeInit")
93 | }
94 | func (e *Entity) Tick() {
95 | }
96 | func (e *Entity) Destroy() {
97 | e.Lock()
98 | e.GM.DestroyEntity(e.EntityInfo.Uuid)
99 | e.Room.DestroyEntity(e.EntityInfo.Uuid)
100 | e.World.DeleteObj(e.EntityInfo.Uuid)
101 | e.Obj.Destroy()
102 | e.Obj = nil
103 | e.Unlock()
104 | e = nil
105 | }
106 |
107 | func (e *Entity) Run() {
108 | }
109 | func (e *Entity) PhysicUpdate() {
110 | }
111 |
112 | func (e *Entity) GetTransform() *TransForm {
113 | return &TransForm{}
114 | }
115 | func (e *Entity) Move(in *Input) {
116 | turnSpeed := e.EntityInfo.Ability.TSPD
117 | moveSpeed := e.EntityInfo.Ability.SPD
118 | moveValue := in.V_Movement
119 | turnValue := in.H_Movement
120 | e.Room.World.Move(e.EntityInfo.Uuid, float64(moveValue*moveSpeed), float64(turnValue*turnSpeed))
121 | }
122 |
123 | /*
124 | type EntityInfo struct {
125 | //mathod's name map to Mathod's info
126 | MethodMap map[string]EntityMathod
127 | Type reflect.Type
128 | }
129 |
130 | type EntityMathod struct {
131 | Func reflect.Value
132 | Type reflect.Type
133 | Args int
134 | }
135 | type EntityManager struct {
136 | EntityTypeMap map[string]EntityInfo
137 | EntityIdMap map[uuid.UUID]reflect.Value
138 | }
139 |
140 | var eManager *EntityManager = &EntityManager{
141 | EntityTypeMap: make(map[string]EntityInfo),
142 | EntityIdMap: make(map[uuid.UUID]reflect.Value),
143 | }
144 |
145 | func (em *EntityManager) Call(entityTypeName string, id uuid.UUID, fName string, args ...reflect.Value) {
146 | e, ok := em.EntityIdMap[id]
147 | eInfo, ok := em.EntityTypeMap[entityTypeName]
148 | if !ok {
149 | panic("Id not found")
150 | }
151 |
152 | f := eInfo.MethodMap[fName]
153 | fmt.Println("f:", f)
154 | in := make([]reflect.Value, f.Args)
155 | in[0] = e
156 | for i := 1; i < f.Args; i++ {
157 | in[i] = args[i-1]
158 | }
159 | f.Func.Call(in)
160 | }
161 |
162 | func (em *EntityManager) CreateEnitity(entityTypeName string, isClient bool) (id uuid.UUID) {
163 | entityInfo, ok := em.EntityTypeMap[entityTypeName]
164 | if !ok {
165 | fmt.Println(entityTypeName, "is not regist.")
166 | }
167 | vEntityPtr := reflect.New(entityInfo.Type)
168 | //check uuid repeat
169 | err := error(nil)
170 | id, err = uuid.NewV4()
171 | fmt.Println(id, err)
172 | for _, ok := em.EntityIdMap[id]; ok; {
173 | id, _ = uuid.NewV4()
174 | fmt.Println(id, err)
175 | }
176 | em.EntityIdMap[id] = vEntityPtr
177 | vEntityPtr.Elem().FieldByName("Id").Set(reflect.ValueOf(id))
178 | vEntityPtr.Elem().FieldByName("TypeName").Set(reflect.ValueOf(vEntityPtr.Type().Elem().Name()))
179 | em.Call(entityTypeName, id, "Init")
180 | return
181 | }
182 |
183 | func RegisterEnitity(iEntity IEntity) {
184 | rEntity := reflect.ValueOf(iEntity)
185 | tEntity := rEntity.Type()
186 | entityName := tEntity.Elem().Name()
187 | rEntity.Elem().FieldByName("TypeName").Set(reflect.ValueOf(entityName))
188 | fmt.Println("t:", tEntity, "v:", rEntity, "m:", rEntity.NumMethod())
189 | entityInfo := &EntityInfo{MethodMap: make(map[string]EntityMathod)}
190 | entityInfo.Type = tEntity.Elem()
191 | for i := 0; i < rEntity.NumMethod(); i++ {
192 | m := tEntity.Method(i)
193 | em := EntityMathod{m.Func, m.Type, m.Type.NumIn()}
194 | entityInfo.MethodMap[tEntity.Method(i).Name] = em
195 | }
196 | fmt.Println(entityInfo)
197 | eManager.EntityTypeMap[entityName] = *entityInfo
198 | fmt.Println(eManager)
199 | }
200 |
201 | */
202 |
--------------------------------------------------------------------------------
/game/entity/entity_test.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestRegisterAndCall(*testing.T) {
9 | RegisterEnitity(&Player{})
10 | id := eManager.CreateEnitity("Player", true)
11 | eManager.Call("Player", id, "Say", reflect.ValueOf("Yo"))
12 | id = eManager.CreateEnitity("Player", true)
13 | eManager.Call("Player", id, "Say", reflect.ValueOf("HI"))
14 | id = eManager.CreateEnitity("Player", true)
15 | eManager.Call("Player", id, "Say", reflect.ValueOf("HI"))
16 | }
17 |
--------------------------------------------------------------------------------
/game/entity/gameManager.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/service"
6 | . "github.com/daniel840829/gameServer/uuid"
7 | "github.com/golang/protobuf/ptypes"
8 | any "github.com/golang/protobuf/ptypes/any"
9 | log "github.com/sirupsen/logrus"
10 | "reflect"
11 | "sync"
12 | "time"
13 | )
14 |
15 | type GameManager struct {
16 | ////basic information
17 | Uuid int64
18 | ////Reflection
19 | thisType reflect.Type
20 | ReflectFunc sync.Map
21 | ////child component
22 | //rpcCallableObject map[int64]reflect.Value
23 | //time calibration
24 |
25 | //room
26 | rl sync.RWMutex
27 | TypeMapRoom map[string]reflect.Type
28 | IdMapRoom map[int64]IRoom
29 | //entity
30 | el sync.RWMutex
31 | TypeMapEntity map[string]reflect.Type
32 | IdMapEntity map[int64]IEntity
33 | UserIdMapEntityId map[int64]int64
34 | UserIdMapRoomId sync.Map
35 | //Buffer
36 |
37 | ////rpc channel
38 | SendFuncToClient map[int64](chan *CallFuncInfo)
39 | RecvFuncFromClient chan *CallFuncInfo
40 | PosToClient map[int64](chan *Position)
41 | InputFromClient chan *Input
42 | ErrToClient map[int64](chan *Error)
43 | ErrFromClient chan *Error
44 | }
45 |
46 | func (gm *GameManager) Init(rpc *service.Rpc) {
47 | gm.Uuid, _ = Uid.NewId(GM_ID)
48 | gm.IdMapRoom = make(map[int64]IRoom)
49 | gm.TypeMapRoom = make(map[string]reflect.Type)
50 | gm.TypeMapEntity = make(map[string]reflect.Type)
51 | gm.IdMapEntity = make(map[int64]IEntity)
52 | gm.UserIdMapEntityId = make(map[int64]int64)
53 | gm.UserIdMapRoomId = sync.Map{}
54 |
55 | //gm.rpcCallableObject = make(map[int64]reflect.Value)
56 | gm.SendFuncToClient = rpc.SendFuncToClient
57 | gm.RecvFuncFromClient = rpc.RecvFuncFromClient
58 | gm.PosToClient = rpc.PosToClient
59 | gm.InputFromClient = rpc.InputFromClient
60 | gm.ErrFromClient = rpc.ErrFromClient
61 | gm.ErrToClient = rpc.ErrToClient
62 | gm.thisType = reflect.TypeOf(gm)
63 | gm.ReflectFunc = sync.Map{}
64 | for i := 0; i < gm.thisType.NumMethod(); i++ {
65 | f := gm.thisType.Method(i)
66 | gm.ReflectFunc.Store(f.Name, f)
67 | }
68 | }
69 |
70 | func (gm *GameManager) Run() {
71 | for {
72 | select {
73 | case f := <-gm.RecvFuncFromClient:
74 | log.Debug("[GM Call]", f)
75 | go gm.Call(f)
76 | case err := <-gm.ErrFromClient:
77 | gm.ErrorHandle(err)
78 | case input := <-gm.InputFromClient:
79 | gm.SyncPos(input)
80 | }
81 | }
82 | }
83 |
84 | func (gm *GameManager) Calibrate(f *CallFuncInfo) {
85 | f_send := &CallFuncInfo{}
86 | //client time
87 | f_send.TargetId = f.TimeStamp
88 | //recieve time
89 | f_send.FromId = int64(time.Now().UnixNano() / 1000000)
90 | f_send.Func = "Calibrate"
91 | gm.SendFuncToClient[f.FromId] <- f_send
92 | }
93 |
94 | func (gm *GameManager) RegistRoom(roomTypeName string, iRoom IRoom) {
95 | if _, ok := gm.TypeMapRoom[roomTypeName]; ok {
96 | log.Fatal(roomTypeName, "is already registed.")
97 | }
98 | vRoom := reflect.ValueOf(iRoom)
99 | gm.TypeMapRoom[roomTypeName] = vRoom.Type().Elem()
100 | //TODO : Record method info to speed up reflection invoke.
101 | }
102 |
103 | func (gm *GameManager) DestroyEntity(entityId int64) {
104 | delete(gm.IdMapEntity, entityId)
105 | }
106 |
107 | func (gm *GameManager) Call(f *CallFuncInfo) {
108 | log.Debug("Function INFO :", f)
109 | /**
110 | m, ok := gm.ReflectFunc.Load(f.Func)
111 | method := m.(reflect.Method)
112 | **/
113 | method, ok := gm.thisType.MethodByName(f.Func)
114 | if !ok {
115 | log.Debug("[GM]{Call}gm does not have ", f.Func, " method ")
116 | return
117 | }
118 | param := make([]reflect.Value, 0)
119 | param = append(param, reflect.ValueOf(gm))
120 | param = append(param, reflect.ValueOf(f))
121 | method.Func.Call(param)
122 | /*
123 | targetType, _ := Uid.ParseId(f.TargetId)
124 | switch targetType {
125 | case ENTITY_ID:
126 | case EQUIP_ID:
127 | case GM_ID:
128 | case ROOM_ID:
129 | default:
130 | if targetType == "" {
131 | log.Warn(f.TargetId, " is not existed !")
132 | return
133 | }
134 | log.Warn(targetType, " is not callable!")
135 | }
136 | */
137 | }
138 |
139 | func (gm *GameManager) UserDisconnect(f *CallFuncInfo) {
140 | userId := f.TargetId
141 | log.Debug("[GM]{UserDisconnect}", userId)
142 | v, ok := gm.UserIdMapRoomId.Load(userId)
143 | if !ok {
144 | return
145 | }
146 | roomId := v.(int64)
147 | room := gm.IdMapRoom[roomId]
148 | room.UserDisconnect(userId)
149 | }
150 |
151 | func (gm *GameManager) Entity(f *CallFuncInfo) {
152 | F := &CallFuncInfo{}
153 | ptypes.UnmarshalAny(f.Param[0], F)
154 | e, ok := gm.IdMapEntity[f.TargetId]
155 | if !ok {
156 | log.Warn("No Such Entity id", f.TargetId)
157 | return
158 | }
159 | entity := reflect.ValueOf(e)
160 | method, ok := entity.Type().MethodByName(F.Func)
161 | if !ok {
162 | log.Warn("entity does not have ", F.Func, " method ")
163 | return
164 | }
165 | param := make([]reflect.Value, 0)
166 | param = append(param, entity)
167 | param = append(param, reflect.ValueOf(F))
168 | method.Func.Call(param)
169 | log.Debug("[gm]{Entity}call function", f)
170 | }
171 |
172 | func (gm *GameManager) ErrorHandle(err *Error) {
173 | log.Warn("Something Wrong", err)
174 | }
175 |
176 | func (gm *GameManager) SyncPos(input *Input) {
177 | //Deal with value
178 | entity, ok := gm.IdMapEntity[gm.UserIdMapEntityId[input.UserId]]
179 | if !ok {
180 | log.Warn("No Such Entity id", input.UserId)
181 | return
182 | }
183 | //TODO
184 | //if timestamp is too far from
185 |
186 | entity.Move(input)
187 | }
188 |
189 | func (gm *GameManager) CreateNewRoom(f *CallFuncInfo) {
190 | roomInfo := &RoomInfo{}
191 | err := ptypes.UnmarshalAny(f.Param[0], roomInfo)
192 | if err != nil {
193 | log.Warn("[*any Unmarshal Error]", f.Param[0])
194 | return
195 | }
196 | tRoom, ok := gm.TypeMapRoom[roomInfo.GameType]
197 | if !ok {
198 | log.Warn(roomInfo.GameType, " is not registed yet. ")
199 | return
200 | }
201 | room, ok := reflect.New(tRoom).Interface().(IRoom)
202 | if !ok {
203 | log.Warn("Something Wrong with RegisterRoom")
204 | return
205 | }
206 | id, err := Uid.NewId(ROOM_ID)
207 | if err != nil {
208 | log.Fatal("Id generator error:", err)
209 | return
210 | }
211 | roomInfo.Uuid = id
212 | room.Init(gm, roomInfo)
213 | gm.rl.Lock()
214 | gm.IdMapRoom[id] = room
215 | gm.rl.Unlock()
216 | gm.getAllRoomInfo(f.FromId)
217 | }
218 |
219 | func (gm *GameManager) RegistEnitity(EntityTypeName string, iEntity IEntity) {
220 | if _, ok := gm.TypeMapEntity[EntityTypeName]; ok {
221 | log.Fatal(EntityTypeName, "is already registed.")
222 | }
223 | vEntity := reflect.ValueOf(iEntity)
224 | gm.TypeMapEntity[EntityTypeName] = vEntity.Type().Elem()
225 |
226 | }
227 |
228 | func (gm *GameManager) CreatePlayer(room *Room, entityType string, userInfo *UserInfo) IEntity {
229 | tEntity, ok := gm.TypeMapEntity[entityType]
230 | if !ok {
231 | log.Warn(entityType, " is not registed yet. ")
232 | return nil
233 | }
234 | entity, ok := reflect.New(tEntity).Interface().(IEntity)
235 | if !ok {
236 | log.Warn("Something Wrong with RegistEnitity")
237 | return nil
238 | }
239 | entityInfo := userInfo.OwnCharacter[userInfo.UsedCharacter]
240 | entity.Init(gm, room, entityInfo)
241 |
242 | gm.rl.Lock()
243 | gm.IdMapEntity[entityInfo.Uuid] = entity
244 | gm.UserIdMapEntityId[userInfo.Uuid] = entityInfo.Uuid
245 | gm.rl.Unlock()
246 | return entity
247 | }
248 |
249 | func (gm *GameManager) CreateEntity(room *Room, entityInfo *Character, entityType string) IEntity {
250 | tEntity, ok := gm.TypeMapEntity[entityType]
251 | if !ok {
252 | log.Warn(entityType, " is not registed yet. ")
253 | return nil
254 | }
255 | entity, ok := reflect.New(tEntity).Interface().(IEntity)
256 | if !ok {
257 | log.Warn("Something Wrong with RegistEnitity")
258 | return nil
259 | }
260 | entity.Init(gm, room, entityInfo)
261 | gm.rl.Lock()
262 | gm.IdMapEntity[entityInfo.Uuid] = entity
263 | gm.rl.Unlock()
264 | return entity
265 |
266 | }
267 |
268 | //Not Done Yet
269 | func (gm *GameManager) GetRoomStatus(f *CallFuncInfo) {
270 | //leftSecond := gm.IdMapRoom[f.TargetId].GetStatus()
271 | gm.SendFuncToClient[f.FromId] <- &CallFuncInfo{}
272 | }
273 |
274 | func (gm *GameManager) EnterRoom(f *CallFuncInfo) {
275 | log.Debug("{GM}[EnterRoom]Excute")
276 | if ok := gm.IdMapRoom[f.TargetId].EnterRoom(f.FromId); ok {
277 | gm.enterRoom(f.FromId, gm.IdMapRoom[f.TargetId].GetInfo())
278 | gm.UserIdMapRoomId.Store(f.FromId, f.TargetId)
279 | }
280 | }
281 |
282 | func (gm *GameManager) GetAllRoomInfo(f *CallFuncInfo) {
283 | gm.getAllRoomInfo(f.TargetId)
284 | }
285 |
286 | func (gm *GameManager) LeaveRoom(f *CallFuncInfo) {
287 | id := gm.IdMapRoom[f.TargetId].LeaveRoom(f.FromId)
288 | f_send := &CallFuncInfo{
289 | Func: "LeaveRoom",
290 | TargetId: id,
291 | }
292 | gm.SendFuncToClient[f.FromId] <- f_send
293 | }
294 |
295 | func (gm *GameManager) GetLoginData(f *CallFuncInfo) {
296 | gm.getLoginData(f.FromId)
297 | }
298 |
299 | func (gm *GameManager) ReadyRoom(f *CallFuncInfo) {
300 | log.Info("{GM}[ReadyRoom]User [", f.FromId, "] is ready in [", f.TargetId, "] Room")
301 | gm.IdMapRoom[f.TargetId].Ready(f.FromId)
302 | }
303 |
304 | ////Send Rpc command to client function
305 | func (gm *GameManager) getLoginData(userId int64) {
306 | //
307 | //b := &BasicType{}
308 |
309 | //param, _ := ptypes.MarshalAny(roomInfo)
310 |
311 | }
312 |
313 | func (gm *GameManager) enterRoom(userId int64, roomInfo *RoomInfo) {
314 | params := make([]*any.Any, 0)
315 | param, _ := ptypes.MarshalAny(roomInfo)
316 | params = append(params, param)
317 | f := &CallFuncInfo{}
318 | f.Func = "EnterRoom"
319 | f.Param = params
320 | gm.SendFuncToClient[userId] <- f
321 | }
322 | func (gm *GameManager) getAllRoomInfo(userId int64) {
323 | params := make([]*any.Any, 0)
324 | for _, room := range gm.IdMapRoom {
325 | roomInfo := room.GetInfo()
326 | param, _ := ptypes.MarshalAny(roomInfo)
327 | params = append(params, param)
328 | }
329 | gm.SendFuncToClient[userId] <- &CallFuncInfo{
330 | Func: "GetAllRoomInfo",
331 | Param: params,
332 | }
333 | }
334 |
335 | func (gm *GameManager) getMyRoom(userId int64, roomInfo *RoomInfo) {
336 | param, _ := ptypes.MarshalAny(roomInfo)
337 | gm.SendFuncToClient[userId] <- &CallFuncInfo{
338 | Func: "GetMyRoom",
339 | Param: append(make([]*any.Any, 0), param),
340 | }
341 | }
342 |
343 | func init() {
344 |
345 | }
346 |
--------------------------------------------------------------------------------
/game/entity/player.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/physic"
6 | . "github.com/daniel840829/gameServer/uuid"
7 | "github.com/gazed/vu/math/lin"
8 | log "github.com/sirupsen/logrus"
9 | "math"
10 | )
11 |
12 | type Player struct {
13 | Entity
14 | }
15 |
16 | func (e *Player) Init(gm *GameManager, room *Room, entityInfo *Character) {
17 | e.Entity.Init(gm, room, entityInfo)
18 | e.Health = e.EntityInfo.MaxHealth
19 | log.Info("player's costumeInit")
20 | }
21 |
22 | func (e *Player) Shoot(f *CallFuncInfo) {
23 | log.Debug("[entity]{Shoot}", f)
24 | //create shell
25 | p, q := e.World.GetTransform(e.EntityInfo.Uuid)
26 | muzzle := lin.NewV3().MultQ(lin.NewV3S(0, 1.1, 1.8), q)
27 | p.Add(p, muzzle)
28 | id, _ := Uid.NewId(ENTITY_ID)
29 | e.World.CreateEntity("Shell", id, *p, *q)
30 | entityInfo := &Character{}
31 | entityInfo.Uuid = id
32 | entityInfo.CharacterType = "Shell"
33 | log.Debug("{Player}[Shoot]", entityInfo)
34 | e.World.Move(id, float64(f.Value/10), 0.0)
35 | entity := e.GM.CreateEntity(e.Room, entityInfo, "Shell")
36 | e.Room.CreateShell(entity, entityInfo, physic.V3_LinToMsg(p), physic.Q_LinToMsg(q))
37 | //set Velocity
38 | }
39 |
40 | func (e *Player) PhysicUpdate() {
41 | q := e.Obj.CBody.Quaternion()
42 | rot := e.Obj.CBody.AngularVel()
43 | q[1] = 0.0
44 | q[2] = 0.0
45 | len := math.Sqrt(q[0]*q[0] + q[3]*q[3])
46 | q[0] /= len
47 | q[3] /= len
48 | rot[0] = 0.0
49 | rot[1] = 0.0
50 | e.Obj.CBody.SetQuaternion(q)
51 | e.Obj.CBody.SetAngularVelocity(rot)
52 | e.Obj.SyncAOEPos()
53 | }
54 |
--------------------------------------------------------------------------------
/game/entity/room.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/physic"
6 | "github.com/daniel840829/gameServer/user"
7 | . "github.com/daniel840829/gameServer/uuid"
8 | "github.com/gazed/vu/math/lin"
9 | "github.com/golang/protobuf/proto"
10 | "github.com/golang/protobuf/ptypes"
11 | any "github.com/golang/protobuf/ptypes/any"
12 | log "github.com/sirupsen/logrus"
13 | "math/rand"
14 | "sync"
15 | "time"
16 | )
17 |
18 | const (
19 | FRAME_INTERVAL = 60 * time.Millisecond
20 | PHYSIC_UPDATE_INTERVAL = 10 * time.Millisecond
21 | )
22 |
23 | type IGameBehavier interface {
24 | Tick()
25 | PhysicUpdate()
26 | Destroy()
27 | Run()
28 | }
29 |
30 | type IRoom interface {
31 | IGameBehavier
32 | Init(*GameManager, *RoomInfo)
33 | //CreateEnitity()
34 | UserDisconnect(userId int64)
35 | UserReconnect(userId int64)
36 | GetInfo() *RoomInfo
37 | EnterRoom(int64) bool
38 | LeaveRoom(int64) int64
39 | Ready(int64) bool
40 | GetUserInRoom() []int64
41 | }
42 |
43 | type Room struct {
44 | RoomInfo *RoomInfo
45 | sync.RWMutex
46 | EntityInRoom map[int64]IEntity
47 | GM *GameManager
48 | EntityOfUser map[int64]int64 //map[id]id
49 | World *physic.World
50 | PosChans map[int64](chan *Position)
51 | FuncChans map[int64](chan *CallFuncInfo)
52 | roomEndChan chan (struct{})
53 | }
54 |
55 | func (r *Room) UserDisconnect(userId int64) {
56 | entity, ok := r.EntityInRoom[r.GM.UserIdMapEntityId[userId]]
57 | if !ok {
58 | log.Info("Entity of User[", userId, "] is not created yet.")
59 |
60 | } else {
61 |
62 | entity.Destroy()
63 | }
64 | r.LeaveRoom(userId)
65 | if len(r.GetInfo().UserInRoom) == 0 {
66 | r.Destroy()
67 | }
68 | }
69 |
70 | func (r *Room) UserReconnect(userId int64) {
71 | //TODO
72 | }
73 |
74 | func (r *Room) Init(gm *GameManager, roomInfo *RoomInfo) {
75 | r.World = &physic.World{}
76 | r.roomEndChan = make(chan (struct{}))
77 | r.GM = gm
78 | r.EntityInRoom = make(map[int64]IEntity)
79 | r.EntityOfUser = make(map[int64]int64)
80 | r.PosChans = make(map[int64](chan *Position), 0)
81 | r.FuncChans = make(map[int64](chan *CallFuncInfo), 0)
82 | r.Lock()
83 | r.RoomInfo = roomInfo
84 | r.World.Init(r.RoomInfo.Uuid)
85 | r.RoomInfo.ReadyUser = make(map[int64]bool)
86 | r.RoomInfo.UserInRoom = make(map[int64]*UserInfo)
87 | r.Unlock()
88 | log.Info("[", roomInfo.Uuid, "]Room is Create: ", roomInfo)
89 | go r.Run()
90 | }
91 |
92 | func (r *Room) Tick() {
93 | //Syncpostion
94 | //callfuncinfo
95 | //Game Logic
96 | r.GetAllTransform()
97 | for _, entity := range r.EntityInRoom {
98 | entity.Tick()
99 | }
100 | }
101 | func (r *Room) Destroy() {
102 | log.Debug("[Room]{Destroy}")
103 | r.Lock()
104 | r.roomEndChan <- struct{}{}
105 | r.World.Destroy()
106 | r.Unlock()
107 | r = nil
108 | }
109 |
110 | func (r *Room) DestroyEntity(id int64) {
111 | r.Lock()
112 | delete(r.EntityInRoom, id)
113 | if _, ok := r.EntityOfUser[id]; ok {
114 | delete(r.EntityOfUser, id)
115 | }
116 | f := &CallFuncInfo{}
117 | f.Func = "DestroyEntity"
118 | f.TargetId = id
119 | r.SendFuncToAll(f)
120 | r.Unlock()
121 | }
122 | func (r *Room) GetInfo() *RoomInfo {
123 | r.RLock()
124 | roomInfo, _ := proto.Clone(r.RoomInfo).(*RoomInfo)
125 | r.RUnlock()
126 | return roomInfo
127 | }
128 |
129 | func (r *Room) Ready(userId int64) bool {
130 | log.Debug("{Room}[Ready]:", userId, " is ready ")
131 | r.Lock()
132 | r.RoomInfo.ReadyUser[userId] = true
133 | r.Unlock()
134 | return true
135 | }
136 | func (r *Room) EnterRoom(userId int64) bool {
137 | r.Lock()
138 | if _, ok := r.RoomInfo.UserInRoom[userId]; ok {
139 | r.Unlock()
140 | log.Debug("User", userId, " is already in room")
141 | return false
142 | }
143 | r.PosChans[userId] = r.GM.PosToClient[userId]
144 | r.FuncChans[userId] = r.GM.SendFuncToClient[userId]
145 | r.RoomInfo.UserInRoom[userId] = user.Manager.GetUserInfo(userId)
146 | r.RoomInfo.ReadyUser[userId] = false
147 | log.Debug("{Room}[EnterRoom]", r.RoomInfo.UserInRoom)
148 | r.Unlock()
149 | return true
150 | }
151 |
152 | func (r *Room) LeaveRoom(userId int64) int64 {
153 | r.Lock()
154 | log.Debug("[room]{LeaveRoom}")
155 | delete(r.RoomInfo.UserInRoom, userId)
156 | delete(r.RoomInfo.ReadyUser, userId)
157 | delete(r.FuncChans, userId)
158 | delete(r.PosChans, userId)
159 | r.Unlock()
160 | return r.RoomInfo.Uuid
161 | }
162 | func (r *Room) Run() {
163 | //Read Info
164 | allReady := false
165 | for !allReady {
166 | r.RLock()
167 | for _, ready := range r.RoomInfo.ReadyUser {
168 | if !ready {
169 | allReady = false
170 | break
171 | } else {
172 | allReady = true
173 | }
174 | }
175 | r.RUnlock()
176 | <-time.After(time.Millisecond)
177 | }
178 | log.Debug("{room}[Run]:start")
179 | r.createPlayers()
180 | r.CreateEnemies()
181 | r.start()
182 | physicUpdate := time.NewTicker(PHYSIC_UPDATE_INTERVAL)
183 | frameUpdate := time.NewTicker(FRAME_INTERVAL)
184 | for {
185 | select {
186 | case <-frameUpdate.C:
187 | r.Tick()
188 | case <-physicUpdate.C:
189 | r.PhysicUpdate()
190 | case <-r.roomEndChan:
191 | return
192 | }
193 | }
194 | }
195 |
196 | func (r *Room) GetUserInRoom() (ids []int64) {
197 | r.RLock()
198 | for id, _ := range r.RoomInfo.UserInRoom {
199 | ids = append(ids, id)
200 | }
201 | r.RUnlock()
202 | return
203 | }
204 |
205 | func (r *Room) GetAllTransform() {
206 | //t1 := time.Now().UnixNano()
207 | pos := r.World.GetAllTransform()
208 | //log.Debug("[room]{GetAllTransform}GetAllTransform time:", (time.Now().UnixNano() - t1))
209 | pos.TimeStamp = int64(time.Now().UnixNano() / 1000000)
210 | go func() {
211 | for _, posChan := range r.PosChans {
212 | posChan <- pos
213 | }
214 | }()
215 | }
216 |
217 | func (r *Room) SendFuncToAll(f *CallFuncInfo) {
218 |
219 | go func() {
220 | for _, funcChan := range r.FuncChans {
221 | funcChan <- f
222 | }
223 | }()
224 | }
225 |
226 | func (r *Room) CreateEnemies() {
227 | log.Debug("[Room]{CreateEnemies}")
228 | for i := 0; i < 1; i++ {
229 | r.CreateEnemy()
230 | }
231 | }
232 |
233 | func (r *Room) CreateEnemy() {
234 | r.Lock()
235 | log.Debug("[Room]{CreateEnemy}")
236 | entityInfo := &Character{
237 | MaxHealth: 100.0,
238 | CharacterType: "Enemy",
239 | Color: &Color{
240 | B: 255.0,
241 | R: 0.0,
242 | G: 0.0,
243 | },
244 | Ability: &Ability{
245 | SPD: 0.5,
246 | TSPD: 1.0,
247 | },
248 | }
249 | entityInfo.Uuid, _ = Uid.NewId(ENTITY_ID)
250 | q := physic.EulerToQuaternion(0.0, 0.0, 0.0)
251 | p := lin.NewV3S((rand.Float64()*64 - 32), (rand.Float64()*64 - 32), 1.0)
252 | r.World.CreateEntity("Tank", entityInfo.Uuid, *p, *q)
253 | entity := r.GM.CreateEntity(r, entityInfo, "Enemy")
254 | if entity == nil {
255 | return
256 | }
257 | r.createEntity(entity, p, q)
258 | r.Unlock()
259 | }
260 | func (r *Room) createPlayers() {
261 | r.RLock()
262 | for id, userInfo := range r.RoomInfo.UserInRoom {
263 | log.Debug("[RoomInfo.UserInRoom]", id, userInfo)
264 | if userInfo.UsedCharacter == int64(0) {
265 | for id, _ := range userInfo.OwnCharacter {
266 | userInfo.UsedCharacter = id
267 | break
268 | }
269 | }
270 | entityInfo := userInfo.OwnCharacter[userInfo.UsedCharacter]
271 | q := physic.EulerToQuaternion(0.0, 0.0, 0.0)
272 | p := lin.NewV3S((rand.Float64()*64 - 32), (rand.Float64()*64 - 32), 1.0)
273 | r.World.CreateEntity("Tank", entityInfo.Uuid, *p, *q)
274 | entity := r.GM.CreatePlayer(r, "Player", userInfo)
275 | if entity == nil {
276 | return
277 | }
278 | r.EntityOfUser[entityInfo.Uuid] = userInfo.Uuid
279 | r.createEntity(entity, p, q)
280 | }
281 | r.RUnlock()
282 | }
283 |
284 | func (r *Room) createEntity(iEntity IEntity, position *lin.V3, quaternion *lin.Q) {
285 | entityInfo := iEntity.GetInfo()
286 | //r.Lock()
287 | r.EntityInRoom[entityInfo.Uuid] = iEntity
288 |
289 | f := &CallFuncInfo{}
290 | f.Func = "CreateEntity"
291 | f.FromPos = &TransForm{Position: physic.V3_LinToMsg(position), Rotation: physic.Q_LinToMsg(quaternion)}
292 | params := make([]*any.Any, 0)
293 | param, _ := ptypes.MarshalAny(entityInfo)
294 | params = append(params, param)
295 | f.Param = params
296 | r.SendFuncToAll(f) //r.Unlock()
297 | }
298 |
299 | //TODO : These two function should be combined
300 | func (r *Room) CreateShell(iEntity IEntity, entityInfo *Character, p *Vector3, q *Quaternion) {
301 | //send msg to all
302 | r.EntityInRoom[entityInfo.Uuid] = iEntity
303 |
304 | f := &CallFuncInfo{}
305 | f.Func = "CreateShell"
306 | f.FromPos = &TransForm{Position: p, Rotation: q}
307 | param, _ := ptypes.MarshalAny(entityInfo)
308 | params := make([]*any.Any, 0)
309 | params = append(params, param)
310 | f.Param = params
311 | r.SendFuncToAll(f)
312 | }
313 |
314 | func (r *Room) start() {
315 | f := &CallFuncInfo{}
316 | f.Func = "StartRoom"
317 | r.RLock()
318 | for id, _ := range r.RoomInfo.UserInRoom {
319 | r.GM.SendFuncToClient[id] <- f
320 | }
321 | r.RUnlock()
322 | }
323 | func (r *Room) PhysicUpdate() {
324 | //r.Lock()
325 | r.World.PhysicUpdate()
326 | for _, entity := range r.EntityInRoom {
327 | entity.PhysicUpdate()
328 | }
329 | //r.Lock()
330 | }
331 |
--------------------------------------------------------------------------------
/game/entity/shell.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "github.com/daniel840829/gameServer/physic"
5 | "github.com/daniel840829/ode"
6 | log "github.com/sirupsen/logrus"
7 | )
8 |
9 | const (
10 | ExplosionRadius = 5.0
11 | )
12 |
13 | type Shell struct {
14 | Entity
15 | State int
16 | //0 : not explosion
17 | //1 : explode
18 | }
19 |
20 | func (s *Shell) PhysicUpdate() {
21 | objData := s.Obj.GetData()
22 | //log.Debug("[Shell]CollideTimes", objData.CollideTimes)
23 | switch s.State {
24 | case 0:
25 | if objData.CollideTimes > 0 {
26 | s.Obj.ResetCollide()
27 | //Send explosion
28 | shellGeom := s.Obj.CGeom
29 | q := shellGeom.Quaternion()
30 | p := shellGeom.Position()
31 | shellGeom.Destroy()
32 | s.Obj.CBody.Destroy()
33 | CGeom := s.World.Space.NewSphere(ExplosionRadius)
34 | CGeom.SetData(s.Obj)
35 | CGeom.SetCategoryBits(0)
36 | CGeom.SetCollideBits(physic.SetBitExcept(physic.SetAllBits(), physic.Skill_Bit, physic.Terrain_Bit))
37 | s.Obj.CGeom = CGeom
38 | body := s.World.World.NewBody()
39 | body.SetKinematic(true)
40 | body.SetPosition(p)
41 | body.SetQuaternion(q)
42 | body.SetLinearVelocity(ode.NewVector3(0.0, 0.0, 0.0))
43 | body.SetAngularVelocity(ode.NewVector3(0.0, 0.0, 0.0))
44 | body.SetGravityEnabled(false)
45 | s.Obj.CBody = body
46 | s.Obj.CGeom.SetBody(body)
47 | s.State = 1
48 | }
49 | case 1:
50 | log.Debug("[Shell]{explode}")
51 | f := func(obj *physic.Obj, times int64) bool {
52 | log.Debug(obj.GetData(), " is damage ", times)
53 | //id := obj.GetData().Uuid
54 | id := obj.GetData().Uuid
55 | entity, ok := s.Room.EntityInRoom[id]
56 | if !ok {
57 | log.Debug("Something Unkown harm")
58 | return true
59 | }
60 | entity.Harm(10)
61 | return true
62 | }
63 | s.Obj.LoopCollideObj(f)
64 | s.Obj.ResetCollide()
65 | s.State = 2
66 | default:
67 | s.Destroy()
68 | break
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/game/entity/treasure.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type Treasure struct {
4 | Entity
5 | lifeTime int64
6 | }
7 |
8 | func (t *Treasure) Tick() {
9 | t.Disapear()
10 | }
11 |
12 | func (t *Treasure) Disapear() {
13 | t.lifeTime++
14 | if t.lifeTime > 1000 {
15 | t.Destroy()
16 | }
17 | }
18 |
19 | func (t *Treasure) BeCollected() {
20 |
21 | }
22 |
23 | func (t *Treasure) BeAttracted() {
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/game/hmap/hmap.go:
--------------------------------------------------------------------------------
1 | // Command go-heightmap provides a basic Square Diamond heightmap / terrain generator.
2 | package hmap
3 |
4 | import (
5 | "flag"
6 | "image"
7 | "image/color"
8 | "image/png"
9 | "math/rand"
10 | "os"
11 | "time"
12 | )
13 |
14 | func main() {
15 | optSize := flag.Int("size", 1024, "Image size, must be power of two.")
16 | optSamples := flag.Int("samples", 128, "Number of samples. Lower is more rough terrain. Must be power of two.")
17 | optBlur := flag.Int("blur", 2, "Blur/Smooth size. Somewhere between 1 and 5.")
18 | optScale := flag.Float64("scale", 1.0, "Scale. Most lileky 1.0")
19 | optOut := flag.String("o", "", "Output png filename.")
20 | flag.Parse()
21 |
22 | if *optOut == "" {
23 | flag.Usage()
24 | os.Exit(1)
25 | }
26 |
27 | h := NewHeightmap(*optSize)
28 | h.generate(*optSamples, *optScale)
29 | h.normalize()
30 | h.blur(*optBlur)
31 | h.png(*optOut)
32 | }
33 |
34 | // Heightmap generates a heightmap based on the Square Diamond algorithm.
35 | type Heightmap struct {
36 | random *rand.Rand
37 | points []float64
38 | width, height int
39 | }
40 |
41 | type generator interface {
42 | Generate()
43 | }
44 |
45 | // NewHeightmap initializes a new Heightmap using the specified size.
46 | func NewHeightmap(size int) *Heightmap {
47 | h := &Heightmap{}
48 | h.random = rand.New(rand.NewSource(time.Now().Unix()))
49 | h.points = make([]float64, size*size)
50 | h.width = size
51 | h.height = size
52 |
53 | // init/randomize
54 | for x := 0; x < h.width; x++ {
55 | for y := 0; y < h.height; y++ {
56 | h.set(x, y, h.frand())
57 | }
58 | }
59 | return h
60 | }
61 |
62 | func (h *Heightmap) png(fname string) {
63 | rect := image.Rect(0, 0, h.width, h.height)
64 | img := image.NewGray16(rect)
65 |
66 | for x := 0; x < h.width; x++ {
67 | for y := 0; y < h.height; y++ {
68 | val := h.get(x, y)
69 | col := color.Gray16{uint16(val * 0xffff)}
70 | img.Set(x, y, col)
71 | }
72 | }
73 |
74 | f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY, 0644)
75 | if err != nil {
76 | panic(err)
77 | }
78 | defer f.Close()
79 |
80 | err = png.Encode(f, img)
81 | if err != nil {
82 | panic(err)
83 | }
84 | }
85 |
86 | func (h *Heightmap) normalize() {
87 | var min = 1.0
88 | var max = 0.0
89 |
90 | for i := 0; i < h.width*h.height; i++ {
91 | if h.points[i] < min {
92 | min = h.points[i]
93 | }
94 | if h.points[i] > max {
95 | max = h.points[i]
96 | }
97 | }
98 | rat := max - min
99 | for i := 0; i < h.width*h.height; i++ {
100 | h.points[i] = (h.points[i] - min) / rat
101 | }
102 | }
103 |
104 | func (h *Heightmap) blur(size int) {
105 | for x := 0; x < h.width; x++ {
106 | for y := 0; y < h.height; y++ {
107 | count := 0
108 | total := 0.0
109 |
110 | for x0 := x - size; x0 <= x+size; x0++ {
111 | for y0 := y - size; y0 <= y+size; y0++ {
112 | total += h.get(x0, y0)
113 | count++
114 | }
115 | }
116 | if count > 0 {
117 | h.set(x, y, total/float64(count))
118 | }
119 | }
120 | }
121 | }
122 |
123 | func (h *Heightmap) frand() float64 {
124 | return (h.random.Float64() * 2.0) - 1.0
125 | }
126 |
127 | func (h *Heightmap) get(x, y int) float64 {
128 | return h.points[(x&(h.width-1))+((y&(h.height-1))*h.width)]
129 | }
130 |
131 | func (h *Heightmap) set(x, y int, val float64) {
132 | h.points[(x&(h.width-1))+((y&(h.height-1))*h.width)] = val
133 | }
134 |
135 | func (h *Heightmap) generate(samples int, scale float64) {
136 | for samples > 0 {
137 | h.squarediamond(samples, scale)
138 | samples /= 2
139 | scale /= 2.0
140 | }
141 | }
142 |
143 | func (h *Heightmap) squarediamond(step int, scale float64) {
144 | half := step / 2
145 | for y := half; y < h.height+half; y += step {
146 | for x := half; x < h.width+half; x += step {
147 | h.square(x, y, step, h.frand()*scale)
148 | }
149 | }
150 | for y := 0; y < h.height; y += step {
151 | for x := 0; x < h.width; x += step {
152 | h.diamond(x+half, y, step, h.frand()*scale)
153 | h.diamond(x, y+half, step, h.frand()*scale)
154 | }
155 | }
156 | }
157 |
158 | func (h *Heightmap) square(x, y, size int, val float64) {
159 | half := size / 2
160 | a := h.get(x-half, y-half)
161 | b := h.get(x+half, y-half)
162 | c := h.get(x-half, y+half)
163 | d := h.get(x+half, y+half)
164 | h.set(x, y, ((a+b+c+d)/4.0)+val)
165 | }
166 |
167 | func (h *Heightmap) diamond(x, y, size int, val float64) {
168 | half := size / 2
169 | a := h.get(x-half, y)
170 | b := h.get(x+half, y)
171 | c := h.get(x, y-half)
172 | d := h.get(x, y+half)
173 | h.set(x, y, ((a+b+c+d)/4.0)+val)
174 | }
175 |
--------------------------------------------------------------------------------
/game/physic/physic.go:
--------------------------------------------------------------------------------
1 | package physic
2 |
3 | // #include
4 | import "C"
5 |
6 | import (
7 | "github.com/daniel840829/gameServer/data"
8 | . "github.com/daniel840829/gameServer/msg"
9 | "github.com/daniel840829/ode"
10 | "github.com/gazed/vu/math/lin"
11 | "github.com/golang/protobuf/proto"
12 | log "github.com/sirupsen/logrus"
13 | "math"
14 | "strconv"
15 | "sync"
16 | )
17 |
18 | // ObjCategory
19 | const (
20 | Player_Bit = iota
21 | Enemy_Bit
22 | Skill_Bit
23 | Terrain_Bit
24 | Specified_Bit
25 | AOE_Bit
26 | TREASURE_Bit
27 | TeamA_Bit
28 | TeamB_Bit
29 | TeamC_Bit
30 | TeamD_Bit
31 | )
32 |
33 | const (
34 | No_Team_Mode = iota
35 | Team_Mode
36 | )
37 |
38 | func SetBits(b int, bits ...uint) int {
39 | for _, bit := range bits {
40 | b |= 1 << bit
41 | }
42 | return b
43 | }
44 |
45 | func SetBitExcept(b int, bits ...uint) int {
46 | for _, bit := range bits {
47 | b &= ^(1 << bit)
48 | }
49 | return b
50 | }
51 |
52 | func SetAllBits() int {
53 | return 65535
54 | }
55 |
56 | type World struct {
57 | sync.RWMutex
58 | World ode.World
59 | Space ode.Space
60 | Floor ode.Plane
61 | CtGrp ode.JointGroup
62 | Objs Objs
63 | Cb func(data interface{}, obj1, obj2 ode.Geom)
64 | }
65 |
66 | type WorldData struct {
67 | Cb func(data interface{}, obj1, obj2 ode.Geom)
68 | CtGrp ode.JointGroup
69 | }
70 |
71 | func (w *World) Init(roomId int64) {
72 | w.World = ode.NewWorld()
73 | ctGrp := ode.NewJointGroup(100000)
74 | var cb CollideCallback
75 | cb = GetCollideHandler(w)
76 | w.Cb = cb
77 | w.World.SetData(&WorldData{Cb: GetCollideHandler(w), CtGrp: ctGrp})
78 | w.Space = ode.NilSpace().NewHashSpace()
79 | w.CreateTerrain()
80 | w.Space.SetSublevel(0)
81 | w.World.SetGravity(ode.V3(0, 0, -9.8))
82 | w.CtGrp = ctGrp
83 | }
84 |
85 | func (w *World) Destroy() {
86 | w.Lock()
87 | w.World.Destroy()
88 | w.CtGrp.Destroy()
89 | w.Cb = nil
90 | w.Unlock()
91 | w = nil
92 | }
93 |
94 | func (w *World) CreateTerrain() {
95 | plane := w.Space.NewPlane(ode.V4(0, 0, 1, 0))
96 | plane.SetCategoryBits(SetBits(0, Terrain_Bit))
97 | plane.SetCollideBits(SetBitExcept(SetAllBits(), AOE_Bit))
98 | obj := &Obj{
99 | CGeom: plane,
100 | Data: &ObjData{Name: "Floor", Type: "Terrain"},
101 | CollideObjs: make(map[*Obj]int64),
102 | }
103 | obj.AddGeom(plane)
104 | wallVector := [][]float64{
105 | {1, 0, 0, -40},
106 | {-1, 0, 0, -40},
107 | {0, 1, 0, -40},
108 | {0, -1, 0, -40},
109 | }
110 | for i := 0; i < 4; i++ {
111 | v4 := wallVector[i]
112 | wall := w.Space.NewPlane(ode.V4(v4...))
113 | wall.SetCategoryBits(SetBits(0, Terrain_Bit))
114 | wall.SetCollideBits(SetBitExcept(SetAllBits(), AOE_Bit))
115 | obj := &Obj{
116 | CGeom: wall,
117 | Data: &ObjData{Name: "Wall" + strconv.Itoa(i), Type: "Terrain"},
118 | CollideObjs: make(map[*Obj]int64),
119 | }
120 | obj.AddGeom(wall)
121 | }
122 |
123 | }
124 |
125 | type CollideCallback func(data interface{}, obj1, obj2 ode.Geom)
126 |
127 | func GetCollideHandler(w *World) func(data interface{}, obj1, obj2 ode.Geom) {
128 | var cb func(data interface{}, obj1, obj2 ode.Geom)
129 | cb = func(data interface{}, obj1, obj2 ode.Geom) {
130 | body1, body2 := obj1.Body(), obj2.Body()
131 | var world ode.World
132 | //log.Debug("GetCollideHandler", body1, " and ", body2)
133 | if body1 == 0 && body2 == 0 {
134 | return
135 | } else if body1 == 0 {
136 | world = body2.World()
137 | } else {
138 | world = body1.World()
139 | }
140 | if (obj1.IsSpace()) || (obj2.IsSpace()) {
141 | spaceCallback := world.Data().(*WorldData).Cb
142 | obj1.Collide2(obj2, data, spaceCallback)
143 | // if need to traverses through all spaces and sub-spaces
144 | if obj1.IsSpace() {
145 | obj1.ToSpace().Collide(data, spaceCallback)
146 | }
147 | if obj2.IsSpace() {
148 | obj2.ToSpace().Collide(data, spaceCallback)
149 | }
150 | } else {
151 | cat1, cat2 := obj1.CategoryBits(), obj2.CategoryBits()
152 | col1, col2 := obj1.CollideBits(), obj2.CollideBits()
153 | if ((cat1 & col2) | (cat2 & col1)) != 0 {
154 | if cat1 == SetBits(0, AOE_Bit) || cat2 == SetBits(0, AOE_Bit) {
155 | cts := obj1.Collide(obj2, 1, 0)
156 | if len(cts) > 0 {
157 | d1, d2 := obj1.Data().(*Obj), obj2.Data().(*Obj)
158 | if d1 == d2 {
159 | return
160 | } else {
161 | var p ode.Vector3
162 | if cat1&SetBits(0, AOE_Bit) != 0 {
163 | p = body2.Position()
164 | d1.InAOE(d2.GetData().Uuid, p)
165 | } else {
166 | p = body1.Position()
167 | d2.InAOE(d1.GetData().Uuid, p)
168 | }
169 | }
170 | }
171 | } else {
172 | ctGrp := world.Data().(*WorldData).CtGrp
173 | //log.Debug("Body1:", body1.Data(), "\n\rBody2:", body2.Data())
174 | contact := ode.NewContact()
175 | contact.Surface.Mode = 0
176 | contact.Surface.Mu = 0.1
177 | contact.Surface.Mu2 = 0
178 | cts := obj1.Collide(obj2, 1, 0)
179 | if len(cts) > 0 {
180 | d1, d2 := obj1.Data().(*Obj), obj2.Data().(*Obj)
181 | if d1 == d2 {
182 | log.Info("CollideHandle right")
183 | } else {
184 | d1.Collide(d2)
185 | d2.Collide(d1)
186 | contact.Geom = cts[0]
187 | ct := world.NewContactJoint(ctGrp, contact)
188 | ct.Attach(body1, body2)
189 |
190 | }
191 | }
192 | }
193 | }
194 | }
195 | }
196 | return cb
197 | }
198 | func (w *World) PhysicUpdate() {
199 | w.Lock()
200 | w.Space.Collide(0, w.Cb)
201 | w.World.Step(0.01)
202 | w.CtGrp.Empty()
203 | w.Unlock()
204 | }
205 |
206 | func (w *World) GetAllTransform() (pos *Position) {
207 | pos = &Position{}
208 | pos.PosMap = make(map[int64]*TransForm)
209 | f := func(key interface{}, value interface{}) bool {
210 | k := key.(int64)
211 | v := value.(*Obj).CBody
212 | p := V3_OdeToMsg(v.Position())
213 | q := Q_OdeToMsg(v.Quaternion())
214 | t := &TransForm{proto.Clone(p).(*Vector3), proto.Clone(q).(*Quaternion)}
215 | pos.PosMap[k] = t
216 | return true
217 | }
218 | w.Lock()
219 | w.Objs.Range(f)
220 | w.Unlock()
221 | return
222 | }
223 |
224 | func (w *World) GetTransform(id int64) (p *lin.V3, q *lin.Q) {
225 | w.Lock()
226 | obj, ok := w.Objs.Get(id)
227 | body := obj.CBody
228 | if !ok {
229 | w.Unlock()
230 | log.Warn("[physic]{GetTransform}id is missed", id)
231 | return
232 | }
233 | p = V3_OdeToLin(body.Position())
234 | q = Q_OdeToLin(body.Quaternion())
235 | w.Unlock()
236 | return
237 | }
238 |
239 | func (w *World) AddObj(id int64, obj *Obj) {
240 | log.Debug("{physic}[Addbody] Id:", id)
241 | w.Objs.Store(id, obj)
242 | }
243 |
244 | func (w *World) DeleteObj(id int64) {
245 | w.Objs.Delete(id)
246 | }
247 | func (w *World) CreateEntity(objName string, id int64, pos lin.V3, rot lin.Q) {
248 |
249 | obj := data.ObjData[objName]
250 | body := w.World.NewBody()
251 | mass := ode.NewMass()
252 | switch obj.Shape {
253 | case "Box":
254 | mass.SetBoxTotal(obj.Mass, ode.NewVector3(obj.Lens[0], obj.Lens[1], obj.Lens[2]))
255 | box := w.Space.NewBox(ode.NewVector3(obj.Lens[0], obj.Lens[1], obj.Lens[2]))
256 | box.SetCategoryBits(SetBits(0, Player_Bit))
257 | box.SetCollideBits(SetBitExcept(SetAllBits(), AOE_Bit))
258 | box.SetBody(body)
259 | object := &Obj{
260 | CBody: body,
261 | CGeom: box,
262 | Data: &ObjData{Uuid: id, Name: objName, Type: obj.Type},
263 | CollideObjs: make(map[*Obj]int64),
264 | }
265 | object.CreateAOE(w.Space, 100.0)
266 | w.Lock()
267 | object.AddGeom(box)
268 | object.AddBody(body)
269 | w.AddObj(id, object)
270 | //joint := w.World.NewPlane2DJoint(ode.JointGroup(0))
271 | //joint.Attach(body, ode.Body(0))
272 | case "Capsule":
273 | mass.SetCapsuleTotal(obj.Mass, 1, obj.Lens[0], obj.Lens[1])
274 | capsule := w.Space.NewCapsule(obj.Lens[0], obj.Lens[1])
275 | capsule.SetCategoryBits(SetBits(0, Skill_Bit))
276 | capsule.SetCollideBits(SetBits(0, Terrain_Bit, Player_Bit, Enemy_Bit))
277 | capsule.SetBody(body)
278 | object := &Obj{
279 | CBody: body,
280 | CGeom: capsule,
281 | Data: &ObjData{Uuid: id, Name: objName, Type: obj.Type},
282 | CollideObjs: make(map[*Obj]int64),
283 | }
284 | w.Lock()
285 | object.AddGeom(capsule)
286 | object.AddBody(body)
287 | w.AddObj(id, object)
288 | default:
289 | log.Debug("[World]{CreateEntity} No ", objName)
290 | return
291 | }
292 | body.SetPosition(V3_LinToOde(&pos))
293 | body.SetQuaternion(Q_LinToOde(&rot))
294 | w.Unlock()
295 | }
296 |
297 | func (w *World) SetTranform(id int64, t *TransForm) {
298 | q := t.Rotation
299 | v3 := t.Position
300 | obj, ok := w.Objs.Get(id)
301 | body := obj.CBody
302 | if !ok {
303 | log.Warn("{physic}[SetTranform]Not Found:", id)
304 | }
305 | body.SetPosition(V3_MsgToOde(v3))
306 | body.SetQuaternion(Q_MsgToOde(q))
307 | }
308 |
309 | func (w *World) Move(id int64, v float64, omega float64) {
310 | w.Lock()
311 | obj, ok := w.Objs.Get(id)
312 | if !ok {
313 | log.Warn("{physic}[Move] No such body ", id)
314 | }
315 | body := obj.CBody
316 | body.SetAngularVelocity(ode.NewVector3(0, 0, -3*omega))
317 | body.SetLinearVelocity(body.VectorToWorld(ode.NewVector3(0, 10*v, body.LinearVelocity()[2])))
318 | //log.Debug("{physic}[Move] vel:", body.LinearVelocity(), "ang vel:", body.AngularVel())
319 | w.Unlock()
320 | //log.Debug("{physic}[Move] position: ", body.Position(), " Rotation: ", body.Quaternion())
321 | }
322 |
323 | type Objs struct {
324 | sync.Map
325 | }
326 |
327 | func (g *Objs) Get(id int64) (*Obj, bool) {
328 | v, ok := g.Load(id)
329 | obj := v.(*Obj)
330 | return obj, ok
331 | }
332 |
333 | type ObjData struct {
334 | Uuid int64
335 | Type string
336 | Name string
337 | CollideTimes int64
338 | }
339 |
340 | type Obj struct {
341 | sync.Mutex
342 | CBody ode.Body
343 | CGeom ode.Geom
344 | Space ode.Space
345 | AOE ode.Geom
346 | OtherBodys []ode.Body
347 | OtherGeoms []ode.Geom
348 | Data *ObjData
349 | CollideObjs map[*Obj]int64
350 | AOEObjs map[int64]ode.Vector3
351 | }
352 |
353 | func (obj *Obj) CreateAOE(space ode.Space, radius float64) {
354 | obj.AOEObjs = make(map[int64]ode.Vector3)
355 | s := space.NewSphere(radius)
356 | s.SetCategoryBits(SetBits(0, AOE_Bit))
357 | s.SetCollideBits(SetBits(0, Player_Bit))
358 | s.SetData(obj)
359 | obj.AOE = s
360 | }
361 |
362 | func (obj *Obj) SyncAOEPos() {
363 | obj.AOE.SetPosition(obj.CBody.Position())
364 | obj.AOE.SetQuaternion(obj.CBody.Quaternion())
365 | }
366 |
367 | func (obj *Obj) InAOE(entityId int64, p ode.Vector3) {
368 | if _, ok := obj.AOEObjs[entityId]; ok {
369 | return
370 | }
371 | obj.AOEObjs[entityId] = p
372 | }
373 |
374 | func (obj *Obj) ClearAOE() {
375 | obj.AOEObjs = make(map[int64]ode.Vector3)
376 | }
377 | func (obj *Obj) AddGeom(geom ode.Geom) {
378 | obj.OtherGeoms = append(obj.OtherGeoms, geom)
379 | geom.SetData(obj)
380 | }
381 |
382 | func (obj *Obj) AddBody(body ode.Body) {
383 | obj.OtherBodys = append(obj.OtherBodys, body)
384 | }
385 |
386 | func (obj *Obj) Collide(obj2 *Obj) {
387 | obj.Data.CollideTimes += 1
388 | obj.CollideObjs[obj2] += 1
389 | }
390 |
391 | func (obj *Obj) ResetCollide() {
392 | obj.Data.CollideTimes = 0
393 | obj.CollideObjs = make(map[*Obj]int64)
394 | }
395 |
396 | func (obj *Obj) LoopCollideObj(f func(obj *Obj, times int64) bool) {
397 | for obj, times := range obj.CollideObjs {
398 | if !f(obj, times) {
399 | break
400 | }
401 | }
402 | }
403 | func (obj *Obj) GetData() *ObjData {
404 | return obj.Data
405 | }
406 |
407 | func (obj *Obj) SetData(objData ObjData) {
408 | obj.Data = &objData
409 | }
410 |
411 | func (obj *Obj) Destroy() {
412 | obj.Lock()
413 | log.Debug("[obj]{Destroy} Space:", obj.Space)
414 | if obj.Space != nil {
415 | obj.Space.Destroy()
416 | } else {
417 | obj.CGeom.Destroy()
418 | obj.CBody.Destroy()
419 | }
420 | obj.Unlock()
421 | }
422 |
423 | var (
424 | roomIdMapCtGrp map[int64]ode.JointGroup
425 | tempCtGrp ode.JointGroup
426 | )
427 |
428 | func init() {
429 | log.Debug("ode Init")
430 | ode.Init(0, ode.AllAFlag)
431 | }
432 |
433 | func EulerToQuaternion(yaw, pitch, roll float64) *lin.Q {
434 | p := pitch * math.Pi / 180 / 2.0
435 | ya := yaw * math.Pi / 180 / 2.0
436 | r := roll * math.Pi / 180 / 2.0
437 |
438 | sinp := math.Sin(p)
439 | siny := math.Sin(ya)
440 | sinr := math.Sin(r)
441 | cosp := math.Cos(p)
442 | cosy := math.Cos(ya)
443 | cosr := math.Cos(r)
444 |
445 | x := sinr*cosp*cosy - cosr*sinp*siny
446 | y := cosr*sinp*cosy + sinr*cosp*siny
447 | z := cosr*cosp*siny - sinr*sinp*cosy
448 | w := cosr*cosp*cosy + sinr*sinp*siny
449 | q := lin.NewQ()
450 | q.SetS(x, y, z, w)
451 |
452 | return q
453 | }
454 |
455 | func Q_LinToOde(q *lin.Q) ode.Quaternion {
456 | x, y, z, w := q.GetS()
457 | return ode.NewQuaternion(w, x, y, z)
458 | }
459 |
460 | func V3_LinToOde(v3 *lin.V3) ode.Vector3 {
461 | x, y, z := v3.GetS()
462 | return ode.NewVector3(x, y, z)
463 | }
464 |
465 | func Q_OdeToLin(q ode.Quaternion) *lin.Q {
466 | x := q[1]
467 | y := q[2]
468 | z := q[3]
469 | w := q[0]
470 | result := lin.NewQ()
471 | result.SetS(x, y, z, w)
472 | return result
473 | }
474 | func V3_OdeToLin(q ode.Vector3) *lin.V3 {
475 | x := q[0]
476 | y := q[1]
477 | z := q[2]
478 | result := lin.NewV3()
479 | result.SetS(x, y, z)
480 | return result
481 | }
482 |
483 | func V3_OdeToMsg(v3 ode.Vector3) (msg *Vector3) {
484 | msg = &Vector3{v3[0], v3[1], v3[2]}
485 | return
486 | }
487 |
488 | func Q_OdeToMsg(q ode.Quaternion) (msg *Quaternion) {
489 | msg = &Quaternion{q[1], q[2], q[3], q[0]}
490 | return
491 | }
492 |
493 | func V3_MsgToOde(msg *Vector3) (v3 ode.Vector3) {
494 | v3 = ode.NewVector3(msg.X, msg.Y, msg.Z)
495 | return
496 | }
497 | func Q_MsgToOde(msg *Quaternion) (q ode.Quaternion) {
498 | q = ode.NewQuaternion(msg.W, msg.X, msg.Y, msg.Z)
499 | return
500 | }
501 |
502 | func Q_LinToMsg(q *lin.Q) (msg *Quaternion) {
503 | msg = &Quaternion{}
504 | msg.X, msg.Y, msg.Z, msg.W = q.GetS()
505 | return
506 | }
507 |
508 | func V3_LinToMsg(p *lin.V3) (msg *Vector3) {
509 | msg = &Vector3{}
510 | msg.X, msg.Y, msg.Z = p.GetS()
511 | return
512 | }
513 |
514 | func DirectionV3ToQuaternion(v3 *lin.V3) ode.Quaternion {
515 | angle := -1 * math.Atan2(v3.X, v3.Y)
516 | targetQ := lin.NewQ().SetS(0, 0, math.Sin(angle/2), math.Cos(angle/2))
517 | return Q_LinToOde(targetQ)
518 | }
519 |
--------------------------------------------------------------------------------
/game/physic/physic_test.go:
--------------------------------------------------------------------------------
1 | package physic
2 |
3 | import (
4 | //"github.com/gazed/vu/math/lin"
5 | "fmt"
6 | "testing"
7 | )
8 |
9 | func TestWorld(t *testing.T) {
10 | fmt.Printf("Bits:%b", SetBitExcept(SetAllBits(), Skill_Bit, Terrain_Bit))
11 | //
12 | }
13 |
--------------------------------------------------------------------------------
/game/session/room.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "errors"
5 | . "github.com/daniel840829/gameServer/msg"
6 | . "github.com/daniel840829/gameServer/uuid"
7 | "github.com/golang/protobuf/proto"
8 | log "github.com/sirupsen/logrus"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type ChatMessage struct {
14 | SpeakerId int32
15 | SpeakerName string
16 | Content string
17 | }
18 |
19 | type ChatRoom struct {
20 | ReadingBuffer []*ChatMessage
21 | }
22 |
23 | //TODO : Chat function
24 |
25 | var limitReadyTime = 1000 //wait ms to start a room
26 |
27 | var RoomManager *roomManager = &roomManager{
28 | Rooms: make(map[int64]*Room),
29 | UserIdleRoomListChan: make(map[*MsgChannel]struct{}),
30 | }
31 |
32 | type roomManager struct {
33 | UserIdleRoomListChan map[*MsgChannel]struct{}
34 | Rooms map[int64]*Room
35 | sync.RWMutex
36 | RoomList *RoomList
37 | }
38 |
39 | func (rm *roomManager) CreateGame(gameCreation *GameCreation) error {
40 | switch gameCreation.RoomInfo.GameType {
41 | default:
42 | if _, ok := rm.Rooms[gameCreation.RoomInfo.Uuid]; ok {
43 | return errors.New("Room already exist")
44 | }
45 | room := NewRoom(gameCreation.RoomInfo.Uuid)
46 | for _, sessionInfo := range gameCreation.PlayerSessions {
47 | //sessionInfo.Uuid
48 | session := Manager.CreateSessionFromAgent(sessionInfo)
49 | log.Debug("[CreateGame]", session)
50 | session.InputPool = room.GetMsgChan("Input")
51 | room.Client[session] = struct{}{}
52 | session.Room = room
53 |
54 | }
55 | rm.Rooms[gameCreation.RoomInfo.Uuid] = room
56 | go room.Run()
57 | return nil
58 | }
59 | }
60 |
61 | func (rm *roomManager) DeleteRoom(info *RoomInfo) error {
62 | room, ok := rm.Rooms[info.Uuid]
63 | if !ok {
64 | log.Debug(rm.Rooms)
65 | return errors.New("no this room")
66 | }
67 | room.end <- struct{}{}
68 | delete(rm.Rooms, info.Uuid)
69 | log.Debug("Game Rooms After delete", rm.Rooms)
70 | return nil
71 | }
72 |
73 | func (rm *roomManager) LeaveRoom() {
74 |
75 | }
76 |
77 | func (rm *roomManager) CreateRoom(master *Session, setting *RoomSetting) *Room {
78 | id, _ := Uid.NewId(ROOM_ID)
79 | room := NewRoom(id)
80 | rm.Rooms[id] = room
81 | return room
82 | }
83 |
84 | func NewRoom(roomId int64) *Room {
85 | room := &Room{
86 | Client: make(map[*Session]struct{}),
87 | Uuid: roomId,
88 | GameStart: make(chan (struct{}), 1),
89 | MsgChannelManager: NewMsgChannelManager(),
90 | end: make(chan struct{}, 10),
91 | }
92 | room.AddMsgChan("Input", 200)
93 | return room
94 | }
95 |
96 | type Room struct {
97 | Name string
98 | GameType string
99 | Master *Session
100 | Client map[*Session]struct{}
101 | Uuid int64
102 | GameFrame *GameFrame
103 | *MsgChannelManager
104 | sync.RWMutex
105 | GameStart chan (struct{})
106 | end chan (struct{})
107 | }
108 |
109 | func (r *Room) GenerateStartFrame() {
110 | r.GameFrame.EntityStates = make(map[int64]*EntityState)
111 | r.GameFrame.Interaction = make([]*Interaction, 0)
112 | r.GameFrame.Characters = make(map[int64]*Character)
113 | for s, _ := range r.Client {
114 |
115 | c := s.Info.UserInfo.OwnCharacter[s.Info.UserInfo.UsedCharacter]
116 | r.GameFrame.EntityStates[s.Info.UserInfo.UsedCharacter] = NewEntityState(s.Info.UserInfo.UsedCharacter, "Tank", c)
117 | r.GameFrame.Characters[s.Info.UserInfo.UsedCharacter] = c
118 | log.Debug(c, s.Info.UserInfo.UsedCharacter)
119 | }
120 | r.SyncGameFrame()
121 | //r.GameFrame.Characters = make(map[int64]*Character)
122 | }
123 |
124 | func NewEntityState(id int64, prefabName string, c *Character) *EntityState {
125 | es := &EntityState{
126 | Health: c.MaxHealth,
127 | Uuid: id,
128 | PrefabName: prefabName,
129 | Transform: &Transform{
130 | Position: &Vector3{0, 0, 0},
131 | Rotation: &Quaternion{0, 0, 0, 1},
132 | },
133 | Animation: &Animation{},
134 | Speed: &Vector3{},
135 | }
136 | return es
137 | }
138 |
139 | func (r *Room) Run() {
140 | //Syncpos
141 | inputPool := r.GetMsgChan("Input")
142 | for _, _ = range r.Client {
143 | <-r.GameStart
144 | }
145 | r.GameFrame = &GameFrame{}
146 | r.GameFrame.RunnigNo = 0
147 | r.GameFrame.TimeStamp = GetTimeStamp()
148 | r.GenerateStartFrame()
149 | r.GameFrame.RunnigNo += 1
150 | update := time.NewTicker(time.Millisecond * 30)
151 | END:
152 | for {
153 | select {
154 | case <-update.C:
155 | //log.Debug("GameFrame: ", r.GameFrame)
156 | r.UpdateFrame()
157 | case msg := <-inputPool.DataCh:
158 | input := msg.(*Input)
159 | r.HandleEntityState(input)
160 | r.HandleNewEntity(input)
161 | r.HandleInteraction(input)
162 | r.HandleEntityDestory(input)
163 | case <-r.end:
164 | break END
165 | }
166 | }
167 |
168 | log.Debug("End Gameing")
169 | for s, _ := range r.Client {
170 | s.Room = nil
171 | }
172 | r.Master.Room = nil
173 | }
174 |
175 | func (r *Room) UpdateFrame() {
176 | r.SyncGameFrame()
177 | r.GameFrame.RunnigNo += 1
178 | r.GameFrame.TimeStamp = GetTimeStamp()
179 | r.GameFrame.Interaction = make([]*Interaction, 0)
180 | }
181 |
182 | func (r *Room) HandleEntityDestory(input *Input) {
183 | if len(input.DestroyEntity) > 0 {
184 | //r.UpdateFrame()
185 | for _, id := range input.DestroyEntity {
186 | //log.Debug("[Destroy Entity]", r.GameFrame.EntityStates)
187 | delete(r.GameFrame.Characters, id)
188 | delete(r.GameFrame.EntityStates, id)
189 | }
190 | }
191 | }
192 |
193 | func (r *Room) HandleInteraction(input *Input) {
194 | /*
195 | if r.GameFrame.TimeStamp > input.TimeStamp {
196 | return
197 | }
198 | */
199 | for _, in := range input.Interaction {
200 | r.GameFrame.Interaction = append(r.GameFrame.Interaction, in)
201 | }
202 | }
203 |
204 | func (r *Room) HandleEntityState(input *Input) {
205 | for _, in := range input.EntityStates {
206 | r.GameFrame.EntityStates[in.Uuid] = in
207 | }
208 | }
209 |
210 | func (r *Room) HandleNewEntity(input *Input) {
211 | for id, in := range input.NewEntityCharacters {
212 | r.GameFrame.Characters[id] = in
213 | }
214 | }
215 |
216 | func (r *Room) SyncGameFrame() {
217 | gf := proto.Clone(r.GameFrame).(*GameFrame)
218 | for s, _ := range r.Client {
219 | msg := s.GetMsgChan("GameFrame")
220 | msg.DataCh <- gf
221 | }
222 | }
223 |
224 | func (r *Room) WaitPlayerReconnect(s *Session) {
225 | //Do nothing
226 | }
227 |
228 | func (r *Room) PlayerLeave(s *Session) {
229 | //TODO: Check player number
230 | delete(r.Client, s)
231 | //if there is no player
232 | if len(r.Client) == 0 {
233 | r.end <- struct{}{}
234 | }
235 | }
236 |
237 | func GetTimeStamp() int64 {
238 | return int64(time.Now().UnixNano() / 1000000)
239 | }
240 |
--------------------------------------------------------------------------------
/game/session/session.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | //"github.com/daniel840829/gameServer2/entity"
5 | . "github.com/daniel840829/gameServer/msg"
6 | "github.com/daniel840829/gameServer/user"
7 | //. "github.com/daniel840829/gameServer/uuid"
8 | log "github.com/sirupsen/logrus"
9 | "google.golang.org/grpc/metadata"
10 | "strconv"
11 | "sync"
12 | "time"
13 | )
14 |
15 | type MsgChannel struct {
16 | DataCh chan (interface{})
17 | StopSignal chan (struct{})
18 | }
19 |
20 | func (m *MsgChannel) Close() {
21 | select {
22 | case <-m.StopSignal:
23 | return
24 | default:
25 | close(m.StopSignal)
26 | }
27 | }
28 |
29 | func NewMsgChannel(bufferNumber int32) *MsgChannel {
30 | return &MsgChannel{
31 | DataCh: make(chan (interface{}), bufferNumber),
32 | StopSignal: make(chan (struct{}), 1),
33 | }
34 | }
35 |
36 | func NewMsgChannelManager() *MsgChannelManager {
37 | return &MsgChannelManager{
38 | make(map[string]*MsgChannel),
39 | }
40 | }
41 |
42 | type MsgChannelManager struct {
43 | c map[string]*MsgChannel
44 | }
45 |
46 | func (m *MsgChannelManager) AddMsgChan(name string, bufferNumber int32) bool {
47 | if _, ok := m.c[name]; ok {
48 | return false
49 | }
50 | m.c[name] = NewMsgChannel(bufferNumber)
51 | return true
52 | }
53 |
54 | func (m *MsgChannelManager) GetMsgChan(name string) *MsgChannel {
55 | return m.c[name]
56 | }
57 |
58 | func (m *MsgChannelManager) CloseMsgChan(name string) {
59 | if ch, ok := m.c[name]; ok {
60 | ch.Close()
61 | delete(m.c, name)
62 | }
63 | }
64 |
65 | type sessionManager struct {
66 | Sessions map[int64]*Session
67 | sync.RWMutex
68 | }
69 |
70 | func (sm *sessionManager) MakeSession(info *SessionInfo) int64 {
71 | s := NewSession(info)
72 | sm.Lock()
73 | sm.Sessions[s.Info.Uuid] = s
74 | sm.Unlock()
75 | return s.Info.Uuid
76 | }
77 |
78 | func (sm *sessionManager) CreateSessionFromAgent(sessionInfo *SessionInfo) *Session {
79 | characterInfo := sessionInfo.UserInfo.OwnCharacter[sessionInfo.UserInfo.UsedCharacter]
80 | log.Debug(characterInfo)
81 | s := sm.Sessions[sm.MakeSession(sessionInfo)]
82 | return s
83 | }
84 |
85 | func (sm *sessionManager) GetSession(md metadata.MD) *Session {
86 | mdid := md.Get("session-id")
87 | if len(mdid) == 0 {
88 | return nil
89 | }
90 | id, err := strconv.ParseInt(mdid[0], 10, 64)
91 | if err != nil {
92 | return nil
93 | }
94 | s, ok := sm.Sessions[id]
95 | if !ok {
96 | return s
97 | }
98 | s.RLock()
99 | if s.User != nil {
100 | uname := md.Get("uname")
101 | if len(uname) == 0 {
102 | s.RUnlock()
103 | return nil
104 | } else if s.User.UserInfo.UserName != uname[0] {
105 | s.RUnlock()
106 | return nil
107 | }
108 | }
109 | s.RUnlock()
110 | return s
111 | }
112 |
113 | func NewSession(info *SessionInfo) *Session {
114 | s := &Session{
115 | Info: info,
116 | MsgChannelManager: NewMsgChannelManager(),
117 | PlayerInfo: &PlayerInfo{},
118 | }
119 | for i := int32(SessionInfo_NoSession); i <= int32(SessionInfo_GameServerWaitReconnect); i++ {
120 | ss := SessionStateFactory.makeSessionState(s, SessionInfo_SessionState(i))
121 | s.States = append(s.States, ss)
122 | }
123 | s.SetState(int32(SessionInfo_OnStart))
124 | s.State.CreateSession()
125 | s.AddMsgChan("GameFrame", 5)
126 | return s
127 | }
128 |
129 | type Session struct {
130 | Info *SessionInfo
131 | State SessionState
132 | SessionKey int64
133 | User *user.User
134 | States []SessionState
135 | Room *Room
136 | sync.RWMutex
137 | PlayerInfo *PlayerInfo
138 | TeamNo int32
139 | InputPool *MsgChannel
140 | InputStamp int64
141 | *MsgChannelManager
142 | }
143 |
144 | func (s *Session) GetPlayerInfo() *PlayerInfo {
145 | if s.User == nil {
146 | return nil
147 | }
148 | s.PlayerInfo.UserName = s.User.UserInfo.UserName
149 | s.PlayerInfo.UserId = s.User.UserInfo.Uuid
150 | if s.User.UserInfo.UsedCharacter == int64(0) {
151 | for id, c := range s.User.UserInfo.OwnCharacter {
152 | s.User.UserInfo.UsedCharacter = id
153 | s.PlayerInfo.Character = c
154 | break
155 | }
156 | } else {
157 | s.PlayerInfo.Character = s.User.UserInfo.OwnCharacter[s.User.UserInfo.UsedCharacter]
158 | }
159 | s.PlayerInfo.TeamNo = s.TeamNo
160 | return s.PlayerInfo
161 | }
162 |
163 | func (s *Session) SetState(state_index int32) {
164 | s.State = s.States[state_index]
165 | }
166 |
167 | type SessionState interface {
168 | SetSession(s *Session) bool
169 | SetStateCode(SessionInfo_SessionState)
170 | GetStateCode() SessionInfo_SessionState
171 | CreateSession() int64
172 | Login(uname string, pswd string) *user.User
173 | Logout() bool
174 | Regist(uname string, pswd string, info ...string) bool
175 | CreateRoom(setting *RoomSetting) bool
176 | EnterRoom(roomId int64) bool
177 | DeleteRoom() bool
178 | ReadyRoom() bool
179 | LeaveRoom() bool
180 | StartRoom() bool
181 | SettingCharacter(*CharacterSetting) bool
182 | SettingRoom() bool
183 | CancelReady() bool
184 | EndRoom() bool
185 | String() string
186 | Lock()
187 | HandleInput(input *Input)
188 | Unlock()
189 | StartGame()
190 | Reconnect()
191 | EndConnection()
192 | WaitReconnect()
193 | End()
194 | }
195 |
196 | func (sb *SessionStateBase) SetSession(s *Session) bool {
197 | if sb.Session != nil {
198 | return false
199 | }
200 | sb.Session = s
201 | return true
202 | }
203 |
204 | func (sb *SessionStateBase) End() {
205 | log.Debug("origin")
206 | }
207 |
208 | func (sb *SessionStateBase) String() string {
209 | return SessionInfo_SessionState_name[int32(sb.StateCode)]
210 | }
211 |
212 | func (sb *SessionStateBase) StartGame() {
213 | //TODO
214 | }
215 |
216 | func (sb *SessionStateBase) Reconnect() {
217 |
218 | }
219 |
220 | func (sb *SessionStateBase) WaitReconnect() {
221 |
222 | }
223 | func (sb *SessionStateBase) EndConnection() {
224 |
225 | }
226 |
227 | func (sb *SessionStateBase) SetStateCode(code SessionInfo_SessionState) {
228 | sb.StateCode = code
229 | }
230 | func (sb *SessionStateBase) GetStateCode() SessionInfo_SessionState {
231 | return sb.StateCode
232 | }
233 | func (sb *SessionStateBase) CreateSession() int64 {
234 | return 0
235 | }
236 |
237 | func (sb *SessionStateBase) HandleInput(input *Input) {
238 | }
239 |
240 | func (sb *SessionStateBase) Login(uname string, pswd string) *user.User {
241 | return nil
242 | }
243 | func (sb *SessionStateBase) Logout() bool {
244 | return false
245 | }
246 | func (sb *SessionStateBase) Regist(uname string, pswd string, info ...string) bool {
247 | return false
248 | }
249 | func (sb *SessionStateBase) CreateRoom(setting *RoomSetting) bool {
250 | return false
251 | }
252 | func (sb *SessionStateBase) EnterRoom(roomId int64) bool {
253 | return false
254 | }
255 | func (sb *SessionStateBase) DeleteRoom() bool {
256 | return false
257 | }
258 | func (sb *SessionStateBase) ReadyRoom() bool {
259 | return false
260 | }
261 | func (sb *SessionStateBase) LeaveRoom() bool {
262 | return false
263 | }
264 | func (sb *SessionStateBase) StartRoom() bool {
265 | return false
266 | }
267 | func (sb *SessionStateBase) SettingCharacter(*CharacterSetting) bool {
268 | return false
269 | }
270 | func (sb *SessionStateBase) SettingRoom() bool {
271 | return false
272 | }
273 | func (sb *SessionStateBase) EndRoom() bool {
274 | return false
275 | }
276 |
277 | func (sb *SessionStateBase) CancelReady() bool {
278 | return false
279 | }
280 |
281 | type SessionStateBase struct {
282 | StateCode SessionInfo_SessionState
283 | Session *Session
284 | sync.RWMutex
285 | }
286 |
287 | type SessionStateGameOnStart struct {
288 | SessionStateBase
289 | }
290 |
291 | func (ssgos *SessionStateGameOnStart) StartGame() {
292 | //firsttimeGetGameframe
293 | ssgos.Session.Room.GameStart <- struct{}{}
294 | ssgos.Session.SetState(int32(SessionInfo_Playing))
295 | log.Debug("state code :", int32(ssgos.Session.State.GetStateCode()))
296 | }
297 |
298 | type SessionStatePlaying struct {
299 | SessionStateBase
300 | }
301 |
302 | func (sb *SessionStatePlaying) HandleInput(input *Input) {
303 | if sb.Session.InputStamp > input.TimeStamp {
304 | log.Debug("input sequence is disorder")
305 | }
306 | sb.Session.InputPool.DataCh <- input
307 | }
308 |
309 | func (sb *SessionStatePlaying) WaitReconnect() {
310 | sb.Session.SetState(int32(SessionInfo_GameServerWaitReconnect))
311 | }
312 |
313 | func (sb *SessionStatePlaying) EndConnection() {
314 | sb.Session.SetState(int32(SessionInfo_GameOver))
315 | log.Debug("state code :", int32(sb.Session.State.GetStateCode()))
316 | sb.Session.State.End()
317 | }
318 |
319 | type SessionStateWaitingReconnection struct {
320 | SessionStateBase
321 | }
322 |
323 | func (sswr *SessionStateWaitingReconnection) Waiting() {
324 | go func() {
325 | c := time.After(10 * time.Second)
326 | END:
327 | for {
328 | select {
329 | case <-c:
330 | break END
331 | }
332 | }
333 |
334 | log.Warn("Exceed waiting time, end connection!")
335 | sswr.EndConnection()
336 | }()
337 | }
338 |
339 | func (sswr *SessionStateWaitingReconnection) EndConnection() {
340 | sswr.Session.SetState(int32(SessionInfo_GameOver))
341 | log.Debug("state code :", int32(sswr.Session.State.GetStateCode()))
342 | sswr.Session.State.End()
343 | }
344 |
345 | func (sswr *SessionStateWaitingReconnection) StartGame() {
346 | sswr.Reconnect()
347 | log.Debug("state code :", int32(sswr.Session.State.GetStateCode()))
348 | }
349 |
350 | func (sswr *SessionStateWaitingReconnection) Reconnect() {
351 | sswr.Session.SetState(int32(SessionInfo_Playing))
352 | log.Debug("state code :", int32(sswr.Session.State.GetStateCode()))
353 | sswr.Session.Room.Client[sswr.Session] = struct{}{}
354 | }
355 |
356 | type SessionStateGameOver struct {
357 | SessionStateBase
358 | }
359 |
360 | func (ssgo *SessionStateGameOver) End() {
361 | ssgo.Session.Lock()
362 | if ssgo.Session.Room == nil {
363 | ssgo.Session.Unlock()
364 | return
365 | }
366 | ssgo.Session.Room.PlayerLeave(ssgo.Session)
367 | log.Debug("ssgo.Session.Room", ssgo.Session.Room)
368 | ssgo.Session.Room = nil
369 | ssgo.Session.Unlock()
370 | }
371 |
372 | type sessionStateFactory struct {
373 | }
374 |
375 | func (sf *sessionStateFactory) makeSessionState(session *Session, state_code SessionInfo_SessionState) SessionState {
376 | var s SessionState
377 | switch state_code {
378 | case SessionInfo_OnStart:
379 | s = &SessionStateGameOnStart{}
380 | case SessionInfo_Playing:
381 | s = &SessionStatePlaying{}
382 | case SessionInfo_GameOver:
383 | s = &SessionStateGameOver{}
384 | case SessionInfo_GameServerWaitReconnect:
385 | s = &SessionStateWaitingReconnection{}
386 | default:
387 | s = &SessionStateBase{}
388 | }
389 | s.Lock()
390 | s.SetSession(session)
391 | s.SetStateCode(state_code)
392 | s.Unlock()
393 | return s
394 | }
395 |
396 | var Manager *sessionManager
397 |
398 | var SessionStateFactory *sessionStateFactory
399 |
400 | func init() {
401 | Manager = &sessionManager{
402 | Sessions: make(map[int64]*Session),
403 | }
404 | SessionStateFactory = &sessionStateFactory{}
405 | }
406 |
--------------------------------------------------------------------------------
/gameServer.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ]
7 | }
--------------------------------------------------------------------------------
/gdb_sandbox:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danie1Lin/Distributed-Golang-Game-Server/620f6c91b6e21db9f8eca88916b1130223ff8b81/gdb_sandbox
--------------------------------------------------------------------------------
/main:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danie1Lin/Distributed-Golang-Game-Server/620f6c91b6e21db9f8eca88916b1130223ff8b81/main
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 |
6 | //"github.com/daniel840829/gameServer/entity"
7 | "runtime/pprof"
8 |
9 | "github.com/daniel840829/gameServer/agent"
10 | "github.com/daniel840829/gameServer/game"
11 | "github.com/daniel840829/gameServer/msg"
12 | "google.golang.org/grpc"
13 |
14 | //"google.golang.org/grpc/grpclog"
15 | "flag"
16 | "fmt"
17 | "os"
18 |
19 | log "github.com/sirupsen/logrus"
20 | )
21 |
22 | func init() {
23 | log.SetFormatter(&log.TextFormatter{})
24 | log.SetOutput(os.Stdout)
25 | log.SetLevel(log.DebugLevel)
26 |
27 | }
28 |
29 | var (
30 | serverType *string
31 | configFile *string
32 | AgentPort *string
33 | AgentToGamePort *string
34 | ClientToGamePort *string
35 | cpuprofile *string
36 | )
37 |
38 | func main() {
39 |
40 | serverType = flag.String("type", "game", "choose server Type")
41 | configFile = flag.String("config", "", "config file's path")
42 | AgentPort = flag.String("agentPort", "50051", "ClientToAgent Port")
43 | AgentToGamePort = flag.String("agentToGamePort", "3000", "AgentToGame Port")
44 | ClientToGamePort = flag.String("clientToGamePort", "8080", "ClientToGame Port")
45 | cpuprofile = flag.String("cpuprofile", "./cpu.prof", "write cpu profile to file,set blank to close profile function")
46 | log.Debug("config :", "type :", serverType)
47 | flag.Parse()
48 | //SERVER_TYPE ID CLIENT_TO_AGENT_PORT CLIENT_TO_GAME_PORT AGENT_TO_GAME_PORT
49 | ReadEnv(serverType, "SERVER_TYPE")
50 | ReadEnv(AgentToGamePort, "AGENT_TO_GAME_PORT")
51 | ReadEnv(ClientToGamePort, "CLIENT_TO_GAME_PORT")
52 | ReadEnv(AgentPort, "CLIENT_TO_AGENT_PORT")
53 |
54 | if *cpuprofile != "" {
55 | f, err := os.Create(*cpuprofile)
56 | if err != nil {
57 | log.Fatal(err)
58 | }
59 | pprof.StartCPUProfile(f)
60 | defer pprof.StopCPUProfile()
61 | }
62 | if *serverType == "agent" {
63 | RunAgent()
64 | } else if *serverType == "game" {
65 | go RunGame()
66 | RunAgentToGame()
67 | } else {
68 | go RunGame()
69 | go RunAgentToGame()
70 | RunAgent()
71 | }
72 |
73 | /*
74 | //初始化gameManager
75 | rpc := service.NewRpc()
76 | gm := &entity.GameManager{}
77 | gm.Init(rpc)
78 | gm.RegistRoom("room", &entity.Room{})
79 | gm.RegistEnitity("Player", &entity.Player{})
80 | gm.RegistEnitity("Shell", &entity.Shell{})
81 | gm.RegistEnitity("Enemy", &entity.Enemy{})
82 | go gm.Run()
83 | // 注册HelloService
84 | */
85 | }
86 |
87 | //ReadEnv if use kubernete Read para from env
88 | func ReadEnv(para *string, envName string) {
89 | v := os.Getenv(envName)
90 | if v != "" {
91 | log.Info(envName, " change from ", *para, " to ", v)
92 | *para = v
93 | }
94 | }
95 |
96 | func RunAgent() {
97 | listen, err := net.Listen("tcp", ":"+*AgentPort)
98 | if err != nil {
99 | fmt.Println("AgentServer failed to listen: %v", err)
100 | }
101 | agentRpc := agent.NewAgentRpc()
102 | s := grpc.NewServer()
103 | msg.RegisterClientToAgentServer(s, agentRpc)
104 | fmt.Println("AgentServer Listen on " + *AgentPort)
105 | agentRpc.Init("127.0.0.1", *AgentToGamePort, *ClientToGamePort)
106 | s.Serve(listen)
107 | }
108 |
109 | func RunGame() {
110 | listen, err := net.Listen("tcp", ":"+*ClientToGamePort)
111 | if err != nil {
112 | fmt.Println("GameServer failed to listen: %v", err)
113 | }
114 | s := grpc.NewServer()
115 | msg.RegisterClientToGameServer(s, &game.CTGServer{})
116 | fmt.Println("GameServer Listen on " + *ClientToGamePort)
117 | s.Serve(listen)
118 | }
119 |
120 | func RunAgentToGame() {
121 | listen, err := net.Listen("tcp", ":"+*AgentToGamePort)
122 | if err != nil {
123 | fmt.Println("AgentToGameServer failed to listen: %v", err)
124 | }
125 | s := grpc.NewServer()
126 | msg.RegisterAgentToGameServer(s, &game.ATGServer{})
127 | fmt.Println("AgentToGameServer Listen on " + *AgentToGamePort)
128 | s.Serve(listen)
129 | }
130 |
--------------------------------------------------------------------------------
/msg/any.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "github.com/golang/protobuf/ptypes/any";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "AnyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 |
42 | // `Any` contains an arbitrary serialized protocol buffer message along with a
43 | // URL that describes the type of the serialized message.
44 | //
45 | // Protobuf library provides support to pack/unpack Any values in the form
46 | // of utility functions or additional generated methods of the Any type.
47 | //
48 | // Example 1: Pack and unpack a message in C++.
49 | //
50 | // Foo foo = ...;
51 | // Any any;
52 | // any.PackFrom(foo);
53 | // ...
54 | // if (any.UnpackTo(&foo)) {
55 | // ...
56 | // }
57 | //
58 | // Example 2: Pack and unpack a message in Java.
59 | //
60 | // Foo foo = ...;
61 | // Any any = Any.pack(foo);
62 | // ...
63 | // if (any.is(Foo.class)) {
64 | // foo = any.unpack(Foo.class);
65 | // }
66 | //
67 | // Example 3: Pack and unpack a message in Python.
68 | //
69 | // foo = Foo(...)
70 | // any = Any()
71 | // any.Pack(foo)
72 | // ...
73 | // if any.Is(Foo.DESCRIPTOR):
74 | // any.Unpack(foo)
75 | // ...
76 | //
77 | // Example 4: Pack and unpack a message in Go
78 | //
79 | // foo := &pb.Foo{...}
80 | // any, err := ptypes.MarshalAny(foo)
81 | // ...
82 | // foo := &pb.Foo{}
83 | // if err := ptypes.UnmarshalAny(any, foo); err != nil {
84 | // ...
85 | // }
86 | //
87 | // The pack methods provided by protobuf library will by default use
88 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack
89 | // methods only use the fully qualified type name after the last '/'
90 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type
91 | // name "y.z".
92 | //
93 | //
94 | // JSON
95 | //
96 | // The JSON representation of an `Any` value uses the regular
97 | // representation of the deserialized, embedded message, with an
98 | // additional field `@type` which contains the type URL. Example:
99 | //
100 | // package google.profile;
101 | // message Person {
102 | // string first_name = 1;
103 | // string last_name = 2;
104 | // }
105 | //
106 | // {
107 | // "@type": "type.googleapis.com/google.profile.Person",
108 | // "firstName": ,
109 | // "lastName":
110 | // }
111 | //
112 | // If the embedded message type is well-known and has a custom JSON
113 | // representation, that representation will be embedded adding a field
114 | // `value` which holds the custom JSON in addition to the `@type`
115 | // field. Example (for message [google.protobuf.Duration][]):
116 | //
117 | // {
118 | // "@type": "type.googleapis.com/google.protobuf.Duration",
119 | // "value": "1.212s"
120 | // }
121 | //
122 | message Any {
123 | // A URL/resource name whose content describes the type of the
124 | // serialized protocol buffer message.
125 | //
126 | // For URLs which use the scheme `http`, `https`, or no scheme, the
127 | // following restrictions and interpretations apply:
128 | //
129 | // * If no scheme is provided, `https` is assumed.
130 | // * The last segment of the URL's path must represent the fully
131 | // qualified name of the type (as in `path/google.protobuf.Duration`).
132 | // The name should be in a canonical form (e.g., leading "." is
133 | // not accepted).
134 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
135 | // value in binary format, or produce an error.
136 | // * Applications are allowed to cache lookup results based on the
137 | // URL, or have them precompiled into a binary to avoid any
138 | // lookup. Therefore, binary compatibility needs to be preserved
139 | // on changes to types. (Use versioned type names to manage
140 | // breaking changes.)
141 | //
142 | // Schemes other than `http`, `https` (or the empty scheme) might be
143 | // used with implementation specific semantics.
144 | //
145 | string type_url = 1;
146 |
147 | // Must be a valid serialized protocol buffer of the above specified type.
148 | bytes value = 2;
149 | }
150 |
--------------------------------------------------------------------------------
/msg/github.com/golang/protobuf/ptypes/any/any.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: any.proto
3 |
4 | /*
5 | Package any is a generated protocol buffer package.
6 |
7 | It is generated from these files:
8 | any.proto
9 | message.proto
10 |
11 | It has these top-level messages:
12 | Any
13 | Vector3
14 | Rotation
15 | Callin
16 | Reply
17 | */
18 | package any
19 |
20 | import proto "github.com/golang/protobuf/proto"
21 | import fmt "fmt"
22 | import math "math"
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ = proto.Marshal
26 | var _ = fmt.Errorf
27 | var _ = math.Inf
28 |
29 | // This is a compile-time assertion to ensure that this generated file
30 | // is compatible with the proto package it is being compiled against.
31 | // A compilation error at this line likely means your copy of the
32 | // proto package needs to be updated.
33 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
34 |
35 | // `Any` contains an arbitrary serialized protocol buffer message along with a
36 | // URL that describes the type of the serialized message.
37 | //
38 | // Protobuf library provides support to pack/unpack Any values in the form
39 | // of utility functions or additional generated methods of the Any type.
40 | //
41 | // Example 1: Pack and unpack a message in C++.
42 | //
43 | // Foo foo = ...;
44 | // Any any;
45 | // any.PackFrom(foo);
46 | // ...
47 | // if (any.UnpackTo(&foo)) {
48 | // ...
49 | // }
50 | //
51 | // Example 2: Pack and unpack a message in Java.
52 | //
53 | // Foo foo = ...;
54 | // Any any = Any.pack(foo);
55 | // ...
56 | // if (any.is(Foo.class)) {
57 | // foo = any.unpack(Foo.class);
58 | // }
59 | //
60 | // Example 3: Pack and unpack a message in Python.
61 | //
62 | // foo = Foo(...)
63 | // any = Any()
64 | // any.Pack(foo)
65 | // ...
66 | // if any.Is(Foo.DESCRIPTOR):
67 | // any.Unpack(foo)
68 | // ...
69 | //
70 | // Example 4: Pack and unpack a message in Go
71 | //
72 | // foo := &pb.Foo{...}
73 | // any, err := ptypes.MarshalAny(foo)
74 | // ...
75 | // foo := &pb.Foo{}
76 | // if err := ptypes.UnmarshalAny(any, foo); err != nil {
77 | // ...
78 | // }
79 | //
80 | // The pack methods provided by protobuf library will by default use
81 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack
82 | // methods only use the fully qualified type name after the last '/'
83 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type
84 | // name "y.z".
85 | //
86 | //
87 | // JSON
88 | //
89 | // The JSON representation of an `Any` value uses the regular
90 | // representation of the deserialized, embedded message, with an
91 | // additional field `@type` which contains the type URL. Example:
92 | //
93 | // package google.profile;
94 | // message Person {
95 | // string first_name = 1;
96 | // string last_name = 2;
97 | // }
98 | //
99 | // {
100 | // "@type": "type.googleapis.com/google.profile.Person",
101 | // "firstName": ,
102 | // "lastName":
103 | // }
104 | //
105 | // If the embedded message type is well-known and has a custom JSON
106 | // representation, that representation will be embedded adding a field
107 | // `value` which holds the custom JSON in addition to the `@type`
108 | // field. Example (for message [google.protobuf.Duration][]):
109 | //
110 | // {
111 | // "@type": "type.googleapis.com/google.protobuf.Duration",
112 | // "value": "1.212s"
113 | // }
114 | //
115 | type Any struct {
116 | // A URL/resource name whose content describes the type of the
117 | // serialized protocol buffer message.
118 | //
119 | // For URLs which use the scheme `http`, `https`, or no scheme, the
120 | // following restrictions and interpretations apply:
121 | //
122 | // * If no scheme is provided, `https` is assumed.
123 | // * The last segment of the URL's path must represent the fully
124 | // qualified name of the type (as in `path/google.protobuf.Duration`).
125 | // The name should be in a canonical form (e.g., leading "." is
126 | // not accepted).
127 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
128 | // value in binary format, or produce an error.
129 | // * Applications are allowed to cache lookup results based on the
130 | // URL, or have them precompiled into a binary to avoid any
131 | // lookup. Therefore, binary compatibility needs to be preserved
132 | // on changes to types. (Use versioned type names to manage
133 | // breaking changes.)
134 | //
135 | // Schemes other than `http`, `https` (or the empty scheme) might be
136 | // used with implementation specific semantics.
137 | //
138 | TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
139 | // Must be a valid serialized protocol buffer of the above specified type.
140 | Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
141 | }
142 |
143 | func (m *Any) Reset() { *m = Any{} }
144 | func (m *Any) String() string { return proto.CompactTextString(m) }
145 | func (*Any) ProtoMessage() {}
146 | func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
147 | func (*Any) XXX_WellKnownType() string { return "Any" }
148 |
149 | func (m *Any) GetTypeUrl() string {
150 | if m != nil {
151 | return m.TypeUrl
152 | }
153 | return ""
154 | }
155 |
156 | func (m *Any) GetValue() []byte {
157 | if m != nil {
158 | return m.Value
159 | }
160 | return nil
161 | }
162 |
163 | func init() {
164 | proto.RegisterType((*Any)(nil), "google.protobuf.Any")
165 | }
166 |
167 | func init() { proto.RegisterFile("any.proto", fileDescriptor0) }
168 |
169 | var fileDescriptor0 = []byte{
170 | // 182 bytes of a gzipped FileDescriptorProto
171 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0xcc, 0xab, 0xd4,
172 | 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0x85, 0xf0, 0x92,
173 | 0x4a, 0xd3, 0x94, 0xcc, 0xb8, 0x98, 0x1d, 0xf3, 0x2a, 0x85, 0x24, 0xb9, 0x38, 0x4a, 0x2a, 0x0b,
174 | 0x52, 0xe3, 0x4b, 0x8b, 0x72, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xd8, 0x41, 0xfc, 0xd0,
175 | 0xa2, 0x1c, 0x21, 0x11, 0x2e, 0xd6, 0xb2, 0xc4, 0x9c, 0xd2, 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d,
176 | 0x9e, 0x20, 0x08, 0xc7, 0x29, 0x9f, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x38, 0x27, 0x0e,
177 | 0xc7, 0xbc, 0xca, 0x00, 0x10, 0x27, 0x80, 0x31, 0x4a, 0x35, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49,
178 | 0x2f, 0x39, 0x3f, 0x57, 0x3f, 0x3d, 0x3f, 0x27, 0x31, 0x2f, 0x5d, 0x1f, 0xa6, 0x4e, 0xbf, 0x00,
179 | 0x64, 0x7a, 0xb1, 0x7e, 0x62, 0x5e, 0xe5, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72,
180 | 0xee, 0x10, 0xa3, 0x02, 0xa0, 0x4a, 0xf4, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3,
181 | 0x42, 0x40, 0x4a, 0x93, 0xd8, 0xc0, 0x7a, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x3a,
182 | 0x7a, 0xed, 0xcd, 0x00, 0x00, 0x00,
183 | }
184 |
--------------------------------------------------------------------------------
/msg/message.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package msg;
4 |
5 | //import "any.proto";
6 |
7 | message Input{
8 | map EntityStates = 1;
9 | repeated Interaction Interaction = 2;
10 | map NewEntityCharacters = 3;
11 | repeated int64 DestroyEntity = 4;
12 | int64 TimeStamp = 6;
13 | }
14 |
15 |
16 |
17 | message Interaction{
18 | int64 FromEntityId = 6;
19 | int64 ToEntityId = 1;
20 | string Type = 2;//Force Harm Torque Use
21 | Vector3 Direction = 3;//if is scalar then only X has value
22 | Vector3 ApplyPoint = 4;
23 | bool IsRebouncable = 5;
24 | }
25 |
26 | message EntityState {
27 | int64 Uuid =1;
28 | Transform Transform = 2;
29 | Vector3 Speed = 3;
30 | Animation Animation = 8;
31 | string PrefabName = 5;//Obj Player Obstacle
32 | float Health = 6;
33 | }
34 |
35 | message GameFrame {
36 | map EntityStates = 1;
37 | repeated Interaction Interaction = 2;
38 | map Characters = 3;
39 | repeated int64 DestroyEntity = 6;
40 | int64 TimeStamp = 4;
41 | int64 RunnigNo = 5;
42 | }
43 |
44 | message Animation{
45 | string Name = 1;
46 | float Value = 2;
47 | }
48 |
49 | message Transform {
50 | Vector3 Position= 1;
51 | Quaternion Rotation = 2;
52 | }
53 |
54 | message SessionCache {
55 | ServerInfo GameServerInfo = 1;
56 | SessionInfo SessionInfo = 2;
57 |
58 | }
59 |
60 | message Vector3{
61 | float X = 1;
62 | float Y = 2;
63 | float Z = 3;
64 | }
65 |
66 | message Quaternion{
67 | float X = 1;
68 | float Y= 2;
69 | float Z = 3;
70 | float W = 4;
71 | }
72 |
73 | message Position{
74 | map PosMap = 1;
75 | int64 TimeStamp = 2;
76 | }
77 |
78 | message Error{
79 | string ErrType = 1;
80 | string ErrMsg = 2;
81 | string RunnigNo = 3;
82 | int64 FromId = 4;
83 | }
84 |
85 | message TimeStamp {
86 | int64 Value = 1;
87 | }
88 |
89 | service ClientToGame{
90 | //roomManager
91 | //entityManager
92 | rpc PlayerInput(stream Input) returns (Empty){}
93 | //View
94 | rpc UpdateGameFrame(Empty) returns (stream GameFrame) {}
95 | rpc Pipe(stream LogMessage) returns (stream MessageToUser) {}
96 | rpc TimeCalibrate(Empty) returns (TimeStamp) {}
97 | }
98 |
99 | service AgentToGame{
100 | //SessionManager
101 | rpc AquireGameRoom(GameCreation) returns (PemKey) {}
102 | rpc DeletGameRoom(RoomInfo) returns (Success){}
103 | }
104 |
105 | service ClientToAgent{
106 | rpc AquireSessionKey(Empty) returns (SessionKey) {}
107 | rpc AquireOtherAgent(Empty) returns (ServerInfo) {}
108 | rpc GetSessionCache(Empty) returns (SessionCache) {}
109 | //Login
110 | rpc Login(LoginInput) returns(UserInfo){}
111 | rpc CreateAccount(RegistInput) returns(Error){}
112 | //UserSetting
113 | rpc SetAccount(AttrSetting) returns (Success) {}
114 | rpc SetCharacter(CharacterSetting) returns (Success) {}
115 | //room
116 | rpc AquireGameServer(Empty) returns (ServerInfo){}
117 | rpc CreateRoom(RoomSetting) returns (Success){}
118 | rpc JoinRoom(ID) returns (Success){}
119 | rpc RoomReady(Empty) returns (Success) {}
120 |
121 | //Team
122 | /*
123 | rpc InviteTeam() returns (){}
124 | rpc ReplyInvite() returns (){}
125 | rpc AddFriend(FriendRequest) returns (Success) {}
126 | rpc AcceptFriend(FriendRequest)returns(Success) {}
127 | rpc SearchUser(SearchKeyWord) returns (SearchResult) {}
128 | */
129 | //View
130 | rpc UpdateRoomContent(Empty) returns (stream RoomContent) {}
131 | rpc UpdateHome(Empty) returns (stream HomeView) {}
132 | rpc UpdateRoomList(Empty) returns (stream RoomList) {}
133 | rpc UpdateUserList(Empty) returns (stream UserList) {}
134 | //rpc UpdateRoomInfo(SessionKey) returns (stream RoomInfoView) {}
135 | rpc Pipe(stream LogMessage) returns (stream MessageToUser) {}
136 | }
137 |
138 | message ID {
139 | int64 Value = 1;
140 | }
141 |
142 | message MessageToUser {
143 | enum Type {
144 | ToView = 0;
145 | ToDebugLog = 2;
146 | }
147 | Type MsgType = 1;
148 | string Context = 2;
149 | }
150 |
151 | message LogMessage {
152 | enum Level {
153 | Debug = 0;
154 | Info = 1;
155 | Warn = 2;
156 | Fatal = 3;
157 | }
158 | Level LogLevel = 1;
159 | string Context = 2;
160 | }
161 |
162 | message FriendRequest{
163 | int64 UserId = 1;
164 | string UserName = 2;
165 | }
166 |
167 | message SearchKeyWord {
168 | string Value = 1;
169 | }
170 |
171 | message SearchResult {
172 | repeated UserInfo List = 1;
173 | }
174 |
175 | message UserList {
176 | repeated UserInfo userInfos = 1;
177 | }
178 |
179 | message GameCreation {
180 | RoomInfo RoomInfo = 1;
181 | repeated SessionInfo PlayerSessions = 2;
182 | int64 MasterSessionId = 3;
183 | }
184 |
185 |
186 | message PemKey{
187 | string TLS = 1;
188 | string SSL = 2;
189 | }
190 |
191 | message Empty{}
192 |
193 |
194 | message RoomPrepareView{
195 |
196 | }
197 |
198 |
199 |
200 |
201 |
202 | message EntityInfo{
203 | int64 Uuid = 1;
204 | int32 TeamNo = 4;
205 | Transform Transform = 2;
206 | int64 CharacterId = 3;
207 | Skill ActiveSkill = 5;
208 | string Motion = 6;
209 | }
210 |
211 | message Skill {
212 | bool Active = 1;
213 | string Name = 2;
214 | float Value = 3;
215 | }
216 |
217 | message HomeView {
218 |
219 | }
220 |
221 | message RoomSetting {
222 | int32 MaxPlayer = 1;
223 | string GameType = 2;
224 | string Name = 3;
225 | }
226 |
227 |
228 | message RoomList {
229 | repeated RoomReview item = 1;
230 | }
231 |
232 | message RoomInfo{
233 | int64 Uuid = 1;
234 | string Name = 2;
235 | string GameType = 3;
236 | int64 OwnerUuid = 4;
237 | map UserInRoom = 5;
238 | map ReadyUser = 6;
239 | enum RoomStatus {
240 | Preparing = 0;
241 | OnPlaying = 1;
242 | Ending = 2;
243 | }
244 | RoomStatus Status = 7;
245 | int64 LeftMilliSecond = 8;
246 | }
247 |
248 | message RoomReview {
249 | int64 Uuid = 1;
250 | string Name = 2;
251 | string GameType = 3;
252 | int32 MaxPlayer = 4;
253 | int32 InRoomPlayer = 5;
254 | }
255 |
256 | message RoomContent {
257 | int64 Uuid = 1;
258 | map Players = 2;
259 | }
260 |
261 | message PlayerInfo {
262 | int64 CharacterCode = 1;
263 | int32 TeamNo = 2;
264 | Character Character = 4;
265 | int64 UserId = 5;
266 | string UserName = 6;
267 | bool IsReady = 7;
268 | }
269 |
270 | //character 即是沒有實體之腳色
271 | //entity 則藉由character來初始化
272 | message Character {
273 | int64 Uuid = 1;
274 | string CharacterType = 2;
275 | string Name = 3;
276 | Color Color =4;
277 | int32 Level = 5;
278 | int32 Exp = 6;
279 | float MaxHealth = 10;
280 | //基本能力值
281 | Ability Ability = 7;
282 | repeated Equipment Equipments = 8;
283 | //戰鬥時添加的狀態
284 | map Attr = 9;
285 | }
286 |
287 | message SessionInfo{
288 | enum SessionState {
289 | NoSession = 0;
290 | Guest = 1;
291 | UserIdle = 2;
292 | UserInRoom = 3;
293 | ConnectingGame = 4;
294 | AgentServerWaitReconnect = 5;
295 | //in game server
296 | OnStart = 6;
297 | Playing = 7;
298 | GameOver = 8;
299 | GameServerWaitReconnect = 9;
300 | }
301 | enum SessionCapacity {
302 | GM = 0;
303 | RoomMaster = 1;
304 | RoomClient = 2;
305 | None = 3;
306 | }
307 | SessionState State = 1;
308 | SessionCapacity Capacity = 2;
309 | int64 Uuid = 5;
310 | SessionKey Key = 3;
311 | UserInfo UserInfo = 4;
312 | }
313 |
314 | message Success {
315 | bool ok = 1;
316 | }
317 |
318 | message AttrSetting {
319 | string Method = 1;
320 | string Key = 2;
321 | string Value = 3;
322 | }
323 |
324 | message SessionKey {
325 | string Value = 1;
326 | }
327 |
328 | message ServerInfo {
329 | enum Type {
330 | GameServer = 0;
331 | AgentServer = 1;
332 | }
333 | Type ServerType = 1;
334 | string PublicKey = 2;
335 | string Port = 3;
336 | string Addr = 4;
337 | SessionKey SessionKey = 5;
338 | int64 MaxConn = 6;
339 | int64 NowConn = 7;
340 | string GameTerrianName = 8;
341 | }
342 |
343 | message LoginInput {
344 | string UserName = 1;
345 | string Pswd = 2;
346 | }
347 |
348 | message RegistInput {
349 | string UserName = 1;
350 | string Pswd = 2;
351 | string Email = 3;
352 | }
353 |
354 | message UserInfo{
355 | string UserName = 1;
356 | int64 Uuid = 2;
357 | map OwnCharacter = 3;
358 | int64 UsedCharacter = 4;
359 | }
360 |
361 | message UserState{
362 | enum UserStatus {
363 | OnCreating = 0;
364 | Login = 1;
365 | Offline = 2;
366 | OnPlaying = 3;
367 | }
368 | UserStatus State = 1;
369 | }
370 |
371 | message CharacterSetting{
372 | int64 Uuid = 1;
373 | Color Color = 2;
374 | repeated Equipment Equipments = 8;
375 | }
376 |
377 |
378 |
379 |
380 | message Color {
381 | int32 R = 1;
382 | int32 G = 2;
383 | int32 B = 3;
384 | }
385 |
386 | message Equipment{
387 | string Name = 1;
388 | string Type =2 ;
389 | int64 Uuid = 3 ;
390 | repeated Color Colors = 4;
391 | Ability Ability =5;
392 | int32 CD =6;//Cool down time tick
393 | int32 Usable =7;
394 | int32 Inventory =8;
395 | }
396 |
397 | message Ability{
398 | int32 ATK = 1;
399 | int32 DEF = 2;
400 | float SPD = 3;
401 | float TSPD = 6;
402 | int32 MP = 4;
403 | int32 MAKT = 5;
404 | }
405 |
--------------------------------------------------------------------------------
/msg/message.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danie1Lin/Distributed-Golang-Game-Server/620f6c91b6e21db9f8eca88916b1130223ff8b81/msg/message.zip
--------------------------------------------------------------------------------
/msg/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | protoc -I. --csharp_out . --grpc_out . message.proto --plugin=protoc-gen-grpc=/usr/sbin/grpc_csharp_plugin
3 | protoc --go_out=plugins=grpc:./ message.proto
4 | zip -r message.zip Message* message.proto
5 |
6 |
--------------------------------------------------------------------------------
/rpctest/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/daniel840829/gameServer/proto" // 引入proto包
5 |
6 | "fmt"
7 | log "github.com/sirupsen/logrus"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | "os"
11 | )
12 |
13 | func init() {
14 | // Log as JSON instead of the default ASCII formatter.
15 | log.SetFormatter(&log.TextFormatter{})
16 |
17 | // Output to stdout instead of the default stderr
18 | // Can be any io.Writer, see below for File example
19 | log.SetOutput(os.Stdout)
20 |
21 | // Only log the warning severity or above.
22 | log.SetLevel(log.DebugLevel)
23 | }
24 |
25 | const (
26 | // Address gRPC服务地址
27 | Address = "35.185.75.79:8080"
28 | )
29 |
30 | func main() {
31 | // 连接
32 | conn, err := grpc.Dial(Address, grpc.WithInsecure())
33 |
34 | if err != nil {
35 | fmt.Println(err)
36 | }
37 |
38 | defer conn.Close()
39 |
40 | // 初始化客户端
41 | c := pb.NewPacketClient(conn)
42 |
43 | // 调用方法
44 | reqBody := &pb.Pos{Id: "test", Vector3: &pb.Vector3{1.2, 1.3, 1.4}, Rotation: &pb.Rotation{}}
45 | r, err := c.SyncPostion(context.Background(), reqBody)
46 | if err != nil {
47 | fmt.Println(err)
48 | }
49 | log.Debug(r)
50 | }
51 |
--------------------------------------------------------------------------------
/rpctest/invokeTest/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | type MyStruct struct {
9 | name string
10 | }
11 |
12 | type A interface {
13 | HI()
14 | }
15 |
16 | type B struct {
17 | Name string
18 | }
19 |
20 | func (b B) HI() {
21 |
22 | }
23 |
24 | func (s *MyStruct) Test(b B) string {
25 | return b.Name
26 | }
27 |
28 | func (this *MyStruct) GetName(str string) string {
29 | this.name = str
30 | return this.name
31 | }
32 |
33 | func main() {
34 |
35 | // 备注: reflect.Indirect -> 如果是指针则返回 Elem()
36 | // 首先,reflect包有两个数据类型我们必须知道,一个是Type,一个是Value。
37 | // Type就是定义的类型的一个数据类型,Value是值的类型
38 |
39 | // 对象
40 | s := "this is string"
41 |
42 | // 获取对象类型 (string)
43 | fmt.Println(reflect.TypeOf(s))
44 |
45 | // 获取对象值 (this is string)
46 | fmt.Println(reflect.ValueOf(s))
47 |
48 | // 对象
49 | var x float64 = 3.4
50 |
51 | // 获取对象值 ()
52 | fmt.Println(reflect.ValueOf(x))
53 |
54 | // 对象
55 | a := &MyStruct{name: "nljb"}
56 |
57 | // 返回对象的方法的数量 (1)
58 | fmt.Println(reflect.TypeOf(a).NumMethod())
59 |
60 | // 遍历对象中的方法
61 | for m := 0; m < reflect.TypeOf(a).NumMethod(); m++ {
62 | method := reflect.TypeOf(a).Method(m)
63 | fmt.Println(method.Type) // func(*main.MyStruct) string
64 | fmt.Println(method.Name) // GetName
65 | fmt.Println(method.Type.NumIn()) // 参数个数
66 | fmt.Println(method.Type.In(1)) // 参数类型
67 | }
68 |
69 | // 获取对象值 (<*main.MyStruct Value>)
70 | fmt.Println(reflect.ValueOf(a))
71 |
72 | // 获取对象名称
73 | fmt.Println(reflect.Indirect(reflect.ValueOf(a)).Type().Name())
74 |
75 | // 参数
76 | i := "Hello"
77 | v := make([]reflect.Value, 0)
78 | v = append(v, reflect.ValueOf(i))
79 |
80 | // 通过对象值中的方法名称调用方法 ([nljb]) (返回数组因为Go支持多值返回)
81 | fmt.Println("invoke: ", reflect.ValueOf(a).MethodByName("GetName").Call(v))
82 | var c A
83 | c = B{"Daniel"}
84 | fmt.Println(c)
85 | v = make([]reflect.Value, 0)
86 | v = append(v, reflect.ValueOf(i))
87 | fmt.Println("invoke2: ", reflect.ValueOf(a).MethodByName("Test").Call(v))
88 |
89 | // 通过对值中的子对象名称获取值 (nljb)
90 | fmt.Println(reflect.Indirect(reflect.ValueOf(a)).FieldByName("name"))
91 |
92 | // 是否可以改变这个值 (false)
93 | fmt.Println(reflect.Indirect(reflect.ValueOf(a)).FieldByName("name").CanSet())
94 |
95 | // 是否可以改变这个值 (true)
96 | fmt.Println(reflect.Indirect(reflect.ValueOf(&(a.name))).CanSet())
97 |
98 | // 不可改变 (false)
99 | fmt.Println(reflect.Indirect(reflect.ValueOf(s)).CanSet())
100 |
101 | // 可以改变
102 | // reflect.Indirect(reflect.ValueOf(&s)).SetString("jbnl")
103 | fmt.Println(reflect.Indirect(reflect.ValueOf(&s)).CanSet())
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/rpctest/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 |
6 | pb "github.com/daniel840829/gameServer/proto" // 引入编译生成的包
7 |
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | //"google.golang.org/grpc/grpclog"
11 | "fmt"
12 |
13 | log "github.com/sirupsen/logrus"
14 | "os"
15 | )
16 |
17 | func init() {
18 | // Log as JSON instead of the default ASCII formatter.
19 | log.SetFormatter(&log.TextFormatter{})
20 |
21 | // Output to stdout instead of the default stderr
22 | // Can be any io.Writer, see below for File example
23 | log.SetOutput(os.Stdout)
24 |
25 | // Only log the warning severity or above.
26 | log.SetLevel(log.DebugLevel)
27 | }
28 |
29 | const (
30 | // Address gRPC服务地址
31 | Address = ":8080"
32 | )
33 |
34 | type rpcService struct{}
35 |
36 | var rpc *rpcService = &rpcService{}
37 |
38 | func (r *rpcService) SyncPostion(ctx context.Context, in *pb.Pos) (*pb.PosReply, error) {
39 | log.Debug(ctx)
40 | log.Debug(in)
41 | return new(pb.PosReply), nil
42 | }
43 |
44 | func (r *rpcService) CallServer(ctx context.Context, in *pb.Callin) (*pb.Reply, error) {
45 | log.Debug(in)
46 | return new(pb.Reply), nil
47 | }
48 | func (r *rpcService) CallClient(in *pb.ClientStart, stream pb.Packet_CallClientServer) error {
49 | log.Debug(in)
50 | return nil
51 | }
52 |
53 | func main() {
54 | listen, err := net.Listen("tcp", Address)
55 | if err != nil {
56 | fmt.Println("failed to listen: %v", err)
57 | }
58 |
59 | // 实例化grpc Server
60 | s := grpc.NewServer()
61 |
62 | // 注册HelloService
63 | pb.RegisterPacketServer(s, &rpcService{})
64 |
65 | fmt.Println("Listen on " + Address)
66 |
67 | s.Serve(listen)
68 | }
69 |
--------------------------------------------------------------------------------
/service/AgentToGame.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | /* Server
4 | type AgentToGameServer interface {
5 | // SessionManager
6 | CreateSession(context.Context, *SessionInfo) (*Success, error)
7 | GetGameServerInfo(context.Context, *SessionKey) (*ServerInfo, error)
8 | GetRoomList(context.Context, *SessionKey) (*RoomList, error)
9 | }
10 | */
11 |
12 | type ATGServer struct {
13 | }
14 |
15 | func (s *ATGServer) CreateSession(context.Context, *SessionInfo) (*Success, error) {
16 | return nil, nil
17 | }
18 | func (s *ATGServer) GetGameServerInfo(context.Context, *SessionKey) (*ServerInfo, error) {
19 | return nil, nil
20 | }
21 | func (s *ATGServer) GetRoomList(context.Context, *SessionKey) (*RoomList, error) {
22 | return nil, nil
23 | }
24 |
25 | /*Client
26 | type AgentToGameClient interface {
27 | // SessionManager
28 | CreateSession(ctx context.Context, in *SessionInfo, opts ...grpc.CallOption) (*Success, error)
29 | GetGameServerInfo(ctx context.Context, in *SessionKey, opts ...grpc.CallOption) (*ServerInfo, error)
30 | GetRoomList(ctx context.Context, in *SessionKey, opts ...grpc.CallOption) (*RoomList, error)
31 | }
32 | */
33 |
34 | type ATGClient struct {
35 | }
36 |
37 | func (c *ATGClient) CreateSession(ctx context.Context, in *SessionInfo, opts ...grpc.CallOption) (*Success, error) {
38 | return nil, nil
39 | }
40 | func (c *ATGClient) GetGameServerInfo(ctx context.Context, in *SessionKey, opts ...grpc.CallOption) (*ServerInfo, error) {
41 | return nil, nil
42 | }
43 | func (c *ATGClient) GetRoomList(ctx context.Context, in *SessionKey, opts ...grpc.CallOption) (*RoomList, error) {
44 | return nil, nil
45 | }
46 |
--------------------------------------------------------------------------------
/service/ClientToAgent.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/session"
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc/metadata"
8 | "strconv"
9 | )
10 |
11 | /*
12 | type ClientToAgentServer interface {
13 | AquireSessionKey(context.Context, *Empty) (*SessionKey, error)
14 | AquireOtherAgent(context.Context, *Empty) (*ServerInfo, error)
15 | // Login
16 | Login(context.Context, *LoginInput) (*UserInfo, error)
17 | CreateAccount(context.Context, *RegistInput) (*Error, error)
18 | // UserSetting
19 | SetAccount(context.Context, *AttrSetting) (*Success, error)
20 | SetCharacter(context.Context, *AttrSetting) (*Success, error)
21 | // room
22 | AquireGameServer(context.Context, *Empty) (*ServerInfo, error)
23 | CreateRoom(context.Context, *RoomSetting) (*Success, error)
24 | JoinRoom(context.Context, *ID) (*Success, error)
25 | RoomReady(context.Context, *Empty) (*Success, error)
26 | // View
27 | UpdateHome(*Empty, ClientToAgent_UpdateHomeServer) error
28 | UpdateRoomList(*Empty, ClientToAgent_UpdateRoomListServer) error
29 | UpdateUserList(*Empty, ClientToAgent_UpdateUserListServer) error
30 | // rpc UpdateRoomInfo(SessionKey) returns (stream RoomInfoView) {}
31 | Pipe(ClientToAgent_PipeServer) error
32 | }*/
33 |
34 | func NewAgentRpc() (agent *Agent) {
35 | agent = &Agent{}
36 | return
37 | }
38 |
39 | type Agent struct {
40 | Uuid int64
41 | }
42 |
43 | func (a *Agent) Init() {
44 |
45 | }
46 |
47 | func (a *Agent) AquireSessionKey(c context.Context, e *Empty) (*SessionKey, error) {
48 | id := session.Manager.MakeSession()
49 | return &SessionKey{Value: strconv.FormatInt(id, 10)}, nil
50 | }
51 | func (a *Agent) AquireOtherAgent(c context.Context, e *Empty) (*ServerInfo, error) {
52 | return nil, nil
53 | }
54 |
55 | // Login
56 |
57 | func (a *Agent) Login(c context.Context, in *LoginInput) (*UserInfo, error) {
58 | s := GetSesionFromContext(c)
59 | s.Lock()
60 | user := s.State.Login(in.UserName, in.Pswd)
61 | s.Unlock()
62 | return user.UserInfo, nil
63 | }
64 | func (a *Agent) CreateAccount(context.Context, *RegistInput) (*Error, error) {
65 | return nil, nil
66 | }
67 |
68 | // UserSetting
69 | func (a *Agent) SetAccount(context.Context, *AttrSetting) (*Success, error) {
70 | return nil, nil
71 | }
72 | func (a *Agent) SetCharacter(context.Context, *AttrSetting) (*Success, error) {
73 | return nil, nil
74 | }
75 |
76 | // allocate room
77 | func (a *Agent) AquireGameServer(context.Context, *Empty) (*ServerInfo, error) {
78 | return nil, nil
79 | }
80 |
81 | // View
82 | func (a *Agent) UpdateHome(*Empty, ClientToAgent_UpdateHomeServer) error {
83 | return nil
84 | }
85 | func (a *Agent) UpdateRoomList(*Empty, ClientToAgent_UpdateRoomListServer) error {
86 | return nil
87 | }
88 | func (a *Agent) UpdateUserList(*Empty, ClientToAgent_UpdateUserListServer) error {
89 | return nil
90 | }
91 |
92 | // rpc UpdateRoomInfo(SessionKey) returns (stream RoomInfoView) {}
93 | func (a *Agent) Pipe(ClientToAgent_PipeServer) error {
94 | return nil
95 | }
96 | func (a *Agent) CreateRoom(context.Context, *RoomSetting) (*Success, error) {
97 | return nil, nil
98 | }
99 | func (a *Agent) JoinRoom(context.Context, *ID) (*Success, error) {
100 | return nil, nil
101 | }
102 | func (a *Agent) RoomReady(context.Context, *Empty) (*Success, error) {
103 | return nil, nil
104 | }
105 |
106 | func GetSesionFromContext(c context.Context) *Session {
107 | md, ok := metadata.FromIncomingContext(c)
108 | if !ok {
109 | return nil
110 | }
111 | s := session.Manager.GetSession(md)
112 |
113 | return s
114 | }
115 |
--------------------------------------------------------------------------------
/service/ClientToGame.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | /*
4 | type ClientToGameServer interface {
5 | // roomManager
6 | EnterRoom(context.Context, *ServerInfo) (*Success, error)
7 | LeaveRoom(context.Context, *Empty) (*Success, error)
8 | // entityManager
9 | PlayerInput(ClientToGame_PlayerInputServer) error
10 | // View
11 | UpdateRoomPrepareView(*Empty, ClientToGame_UpdateRoomPrepareViewServer) error
12 | UpdateGameFrame(*Empty, ClientToGame_UpdateGameFrameServer) error
13 | }
14 | */
15 |
16 | type CTGServer struct {
17 | }
18 |
19 | func (s *CTGServer) EnterRoom(context.Context, *ServerInfo) (*Success, error) {
20 | return nil, nil
21 | }
22 |
23 | func (s *CTGServer) LeaveRoom(context.Context, *Empty) (*Success, error) {
24 | return nil, nil
25 | }
26 |
27 | // entityManager
28 | func (s *CTGServer) PlayerInput(ClientToGame_PlayerInputServer) error {
29 | return nil
30 | }
31 |
32 | // View
33 | func (s *CTGServer) UpdateRoomPrepareView(*Empty, ClientToGame_UpdateRoomPrepareViewServer) error {
34 | return nil
35 | }
36 | func (s *CTGServer) UpdateGameFrame(*Empty, ClientToGame_UpdateGameFrameServer) error {
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/setupEnv.sh:
--------------------------------------------------------------------------------
1 | # Set up these env var to connect mongodb
2 | export MOG_ADDR=0.0.0.0:27071
3 | export MGO_USER=username
4 | export MGO_PASS=1234
5 | export DONT_USE_KUBE=false
--------------------------------------------------------------------------------
/storage/storage.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "os"
7 |
8 | "github.com/globalsign/mgo"
9 | //"github.com/globalsign/mgo/bson"
10 | //"os"
11 | //"regexp"
12 | log "github.com/sirupsen/logrus"
13 | )
14 |
15 | const (
16 | MGO_DB_NAME = "gameServer"
17 | UserInfo_COLLECTION = "UserInfo"
18 | RegistInput_COLLECTION = "RegistInput"
19 | )
20 |
21 | var (
22 | _MGO_ADDR string = ""
23 | _MGO_USER string = ""
24 | _MGO_PASS string = ""
25 | )
26 |
27 | var MgoDb *MongoDb = &MongoDb{}
28 |
29 | func init() {
30 | MgoDb.Init(MGO_DB_NAME, UserInfo_COLLECTION, RegistInput_COLLECTION)
31 | }
32 |
33 | type Db interface {
34 | Init(string, ...string)
35 | Save(string, interface{}) bool
36 | Update(string, interface{}, interface{}) bool
37 | Delete(string, interface{}) bool
38 | Find(string, interface{}) *mgo.Iter
39 | }
40 |
41 | type MongoDb struct {
42 | Session *mgo.Session
43 | Collections map[string]*mgo.Collection
44 | }
45 |
46 | type FileStore struct {
47 | FileName string
48 | Path string
49 | }
50 |
51 | type MgoError struct {
52 | Op string
53 | Err error
54 | }
55 |
56 | func (e *MgoError) Error() string {
57 | return e.Op + ":" + e.Err.Error()
58 | }
59 |
60 | func ReadMgoSettingFromEnv() {
61 | _MGO_PASS = os.Getenv("MGO_PASS")
62 | _MGO_USER = os.Getenv("MGO_USER")
63 | _MGO_ADDR = os.Getenv("MGO_ADDR")
64 | }
65 |
66 | func (m *MongoDb) Init(dbName string, collectionNames ...string) {
67 | ReadMgoSettingFromEnv()
68 | err := error(nil)
69 | defer func() {
70 | if r := recover(); r != nil {
71 | err := fmt.Errorf("%v", r)
72 | fmt.Printf("%T \n\r", err)
73 | a := &MgoError{"mgo:", errors.New("MgoInit")}
74 | log.Warn("", a.Error())
75 | //panic("Please open mangodb server")
76 | }
77 | }()
78 | m.Collections = make(map[string]*mgo.Collection)
79 |
80 | m.Session, err = mgo.Dial(_MGO_ADDR)
81 | if err != nil {
82 | fmt.Println("mongodb connecting error :", err)
83 | }
84 | if err := m.Session.DB(dbName).Login(_MGO_USER, _MGO_PASS); err != nil {
85 | log.Fatal("Mgo connect error:", err, "/n Do you set env MGO_USER and MGO_PASS? or Do you create a User?")
86 | }
87 | for _, collectionName := range collectionNames {
88 | m.Collections[collectionName] = m.Session.DB(dbName).C(collectionName)
89 | fmt.Println(collectionName, " is register.")
90 | }
91 | }
92 |
93 | func (m *MongoDb) Save(collectionName string, data interface{}) bool {
94 | if _, ok := m.Collections[collectionName]; !ok {
95 | return false
96 | }
97 | m.Collections[collectionName].Insert(data)
98 | return true
99 | }
100 |
101 | func (m *MongoDb) Find(collectionName string, query interface{}) *mgo.Iter {
102 | if _, ok := m.Collections[collectionName]; !ok {
103 | log.Warn("No such Collection", collectionName)
104 | return nil
105 | }
106 | iter := m.Collections[collectionName].Find(query).Iter()
107 | return iter
108 | }
109 |
110 | func (m *MongoDb) Update(collectionName string, query interface{}, data interface{}) bool {
111 | if _, ok := m.Collections[collectionName]; !ok {
112 | return false
113 | }
114 | err := m.Collections[collectionName].Update(query, data)
115 | if err != nil {
116 | fmt.Println("db update error:", err)
117 | return false
118 | }
119 | return true
120 |
121 | }
122 |
123 | func (m *MongoDb) Delete(collectionName string, query interface{}) bool {
124 | if _, ok := m.Collections[collectionName]; !ok {
125 | return false
126 | }
127 | changelog, err := m.Collections[collectionName].RemoveAll(query)
128 | fmt.Println(changelog, err)
129 | if err != nil {
130 | return false
131 | }
132 | return true
133 | }
134 |
--------------------------------------------------------------------------------
/storage/storage_test.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "fmt"
5 | "github.com/daniel840829/gameServer/service"
6 | //"github.com/globalsign/mgo"
7 | "github.com/globalsign/mgo/bson"
8 | "testing"
9 | )
10 |
11 | func TestMgo(t *testing.T) {
12 | m := &MongoDb{}
13 | m.Init("Testing_OnlineGame", "UserInfo", "Character")
14 | m.Save("UserInfo", &service.UserInfo{Uuid: "123", UserName: "Daniel"})
15 | iter := m.Find("UserInfo", bson.M{"uuid": "123"})
16 | r := &service.UserInfo{}
17 | for iter.Next(r) {
18 | fmt.Println(r)
19 | }
20 | if err := iter.Close(); err != nil {
21 | return
22 | }
23 | m.Update("UserInfo", bson.M{"uuid": "123"}, bson.M{"$set": bson.M{"uuid": "456"}})
24 | m.Delete("UserInfo", bson.M{"uuid": "456"})
25 | }
26 |
--------------------------------------------------------------------------------
/timeCalibrate/timeCalibration.go:
--------------------------------------------------------------------------------
1 | package timeCalibration
2 |
3 | import (
4 | log "github.com/sirupsen/logrus"
5 | "runtime"
6 | "sync"
7 | "time"
8 | )
9 |
10 | type TimeCalibration struct {
11 | RunningNoMapUserId sync.Map
12 | Proccess sync.Map
13 | }
14 |
15 | func (tc *TimeCalibration) FromClientTimeDelay(userId int64, t int64) {
16 |
17 | }
18 |
19 | func (tc *TimeCalibration) ToClientTimeDelay(userId int64, t int64) {
20 |
21 | }
22 |
23 | type Proccess struct {
24 | RunningNo int64
25 | RpcFuncName string
26 | StageTime []*StageInfo
27 | }
28 |
29 | type StageInfo struct {
30 | Fn string
31 | }
32 |
33 | func (p *Proccess) String() {
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | . "github.com/daniel840829/gameServer/msg"
5 | "github.com/daniel840829/gameServer/storage"
6 | . "github.com/daniel840829/gameServer/uuid"
7 | "github.com/globalsign/mgo/bson"
8 | "github.com/golang/protobuf/proto"
9 | log "github.com/sirupsen/logrus"
10 | "math/rand"
11 | "sync"
12 | )
13 |
14 | func NewUserManager(db storage.Db) *UserManager {
15 | return &UserManager{
16 | UserOnline: make(map[int64]*User),
17 | Db: db,
18 | }
19 | }
20 |
21 | type UserManager struct {
22 | sync.RWMutex
23 | UserOnline map[int64]*User
24 | Db storage.Db
25 | }
26 |
27 | func (um *UserManager) Login(in *LoginInput) (*UserInfo, error) {
28 | iter := um.Db.Find(storage.RegistInput_COLLECTION, bson.M{"username": in.UserName})
29 | userPassword := &RegistInput{}
30 | userInfo := &UserInfo{}
31 | if iter.Next(userPassword) {
32 | if userPassword.Pswd == in.Pswd {
33 | UserInfoIter := um.Db.Find(storage.UserInfo_COLLECTION, bson.M{"username": in.UserName})
34 | if UserInfoIter.Next(userInfo) {
35 | user := &User{}
36 | user.Login(userInfo)
37 | um.Lock()
38 | um.UserOnline[userInfo.Uuid] = user
39 | um.Unlock()
40 | log.Debug("[User][Login]", userInfo)
41 | return userInfo, nil
42 | }
43 | }
44 | }
45 | return nil, nil
46 | }
47 |
48 | func (um *UserManager) Logout(userId int64) {
49 | um.Lock()
50 | user := um.UserOnline[userId]
51 | _ = user
52 | delete(um.UserOnline, userId)
53 | user = nil
54 | um.Unlock()
55 | }
56 |
57 | func (um *UserManager) Regist(in *RegistInput) (*Error, error) {
58 | if iter := um.Db.Find(storage.RegistInput_COLLECTION, bson.M{"username": in.UserName}); iter.Next(&RegistInput{}) {
59 | return &Error{ErrMsg: "Username exists"}, nil
60 | }
61 | userInfo := NewUserInfo(in.UserName)
62 | um.Db.Save(storage.UserInfo_COLLECTION, userInfo)
63 | um.Db.Save(storage.RegistInput_COLLECTION, in)
64 | log.Debug(userInfo)
65 | return &Error{}, nil
66 | }
67 |
68 | func (um *UserManager) GetUserInfo(id int64) *UserInfo {
69 | um.RLock()
70 | userInfo := um.UserOnline[id].GetInfo()
71 | um.RUnlock()
72 | return userInfo
73 | }
74 |
75 | type User struct {
76 | UserInfo *UserInfo
77 | sync.RWMutex
78 | }
79 |
80 | func (u *User) Login(userInfo *UserInfo) {
81 | u.Lock()
82 | u.UserInfo = userInfo
83 | u.Unlock()
84 | }
85 |
86 | func (u *User) GetInfo() *UserInfo {
87 | u.RLock()
88 | userInfo, _ := proto.Clone(u.UserInfo).(*UserInfo)
89 | u.RUnlock()
90 | return userInfo
91 | }
92 |
93 | func (u *User) SetCharacter(setting *CharacterSetting) bool {
94 | if c, ok := u.UserInfo.OwnCharacter[setting.Uuid]; ok {
95 | c.Equipments = setting.Equipments
96 | c.Color = setting.Color
97 | u.UserInfo.UsedCharacter = setting.Uuid
98 | log.Debug(u.UserInfo)
99 | Manager.Db.Update(storage.UserInfo_COLLECTION, bson.M{"username": u.UserInfo.UserName}, u.UserInfo)
100 | var userInfo *UserInfo
101 | iter := Manager.Db.Find(storage.UserInfo_COLLECTION, bson.M{"username": u.UserInfo.UserName})
102 | if iter.Next(userInfo) {
103 | log.Debug("[Userinfo Update]", userInfo)
104 | }
105 | return true
106 | } else {
107 | return false
108 | }
109 | }
110 | func NewCharacter() (c *Character) {
111 | c = &Character{}
112 | uuid, _ := Uid.NewId(CHA_ID)
113 | c.Uuid = uuid
114 | c.Color = &Color{int32(rand.Intn(256)), int32(rand.Intn(256)), int32(rand.Intn(256))}
115 | c.CharacterType = "Player"
116 | c.MaxHealth = 100.0
117 | c.Ability = &Ability{}
118 | c.Ability.SPD = 1.0
119 | c.Ability.TSPD = 1.0
120 | return
121 | }
122 |
123 | func NewUserInfo(userName string) (u *UserInfo) {
124 | uuid, _ := Uid.NewId(USER_ID)
125 | u = &UserInfo{OwnCharacter: make(map[int64]*Character)}
126 | u.Uuid = uuid
127 | u.UserName = userName
128 | c := NewCharacter()
129 | u.OwnCharacter[c.Uuid] = c
130 | u.UsedCharacter = c.Uuid
131 | return
132 | }
133 |
134 | var Manager *UserManager
135 |
136 | func init() {
137 | Manager = NewUserManager(storage.MgoDb)
138 | }
139 |
--------------------------------------------------------------------------------
/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | )
6 |
--------------------------------------------------------------------------------
/uuid/uuid.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | log "github.com/sirupsen/logrus"
5 | "github.com/zheng-ji/goSnowFlake"
6 | )
7 |
8 | const (
9 | GM_ID = "GameManager"
10 | ROOM_ID = "Room"
11 | USER_ID = "User"
12 | ENTITY_ID = "Entity"
13 | EQUIP_ID = "Equipment"
14 | CHA_ID = "Character"
15 | SESSION_ID = "Session"
16 | )
17 |
18 | type UID struct {
19 | Gen map[string]*goSnowFlake.IdWorker
20 | WorkersName map[int64]string
21 | }
22 |
23 | func (u *UID) RegisterWorker(workers ...string) {
24 | u.Gen = make(map[string]*goSnowFlake.IdWorker)
25 | u.WorkersName = make(map[int64]string)
26 | err := error(nil)
27 | for idx, worker := range workers {
28 | u.Gen[worker], err = goSnowFlake.NewIdWorker(int64(idx + 1))
29 | u.WorkersName[int64(idx+1)] = worker
30 | if err != nil {
31 | log.Fatal(err)
32 | break
33 | }
34 | }
35 | }
36 |
37 | func (u *UID) NewId(worker string) (id int64, err error) {
38 | id, err = u.Gen[worker].NextId()
39 | return
40 | }
41 |
42 | func (u *UID) ParseId(id int64) (worker string, ts int64) {
43 | _, ts, workerId, _ := goSnowFlake.ParseId(id)
44 | worker, ok := u.WorkersName[workerId]
45 | if !ok {
46 | worker = ""
47 | }
48 | return
49 | }
50 |
51 | var Uid *UID = &UID{}
52 |
53 | func init() {
54 | Uid.RegisterWorker(GM_ID, ROOM_ID, USER_ID, CHA_ID, ENTITY_ID, EQUIP_ID, SESSION_ID)
55 | }
56 |
--------------------------------------------------------------------------------