slambb 1 rok temu
rodzic
commit
47d03aab7a

+ 1 - 0
SmartBowGameServer/.gitignore

@@ -0,0 +1 @@
+/*.log

+ 35 - 0
SmartBowGameServer/Linux部署说明.md

@@ -0,0 +1,35 @@
+1. 安装Go环境
+
+去官网下载安装包 https://golang.google.cn/dl/
+
+把下载好的安装包放到Linux服务器并用以下命令解压
+```
+tar -C /usr/local -xzf go1.17.7.linux-amd64.tar.gz
+```
+
+2. 部署项目
+
+把SmartBowGameServer目录放Linux服务器,在该目录下按顺序执行以下命令
+
+- 通过命令行临时设置环境变量(即关闭命令行后会失效)
+```
+export PATH=$PATH:/usr/local/go/bin
+export GO111MODULE=on
+export GOPROXY=https://goproxy.cn
+```
+- 运行项目
+```
+nohup go run main.go
+```
+
+3. 配置Nginx反向代理
+```
+location /SmartBowGameServer {
+    proxy_pass http://127.0.0.1:11811/SmartBowGameServer;
+
+    # 主要websocket代理配置, $http_upgrade是指http协议头部Upgrade的值. 
+    proxy_http_version 1.1;
+    proxy_set_header Upgrade $http_upgrade;
+    proxy_set_header Connection "upgrade";
+}
+```

+ 45 - 0
SmartBowGameServer/game/player.go

@@ -0,0 +1,45 @@
+package game
+
+import (
+	"github.com/gorilla/websocket"
+)
+
+type Player struct {
+	Conn    *websocket.Conn
+	IsValid bool
+	Room    *Room
+}
+
+func (player *Player) OnLoad() {
+	player.IsValid = true
+}
+
+func (player *Player) OnDestroy() {
+	player.IsValid = false
+}
+
+//监听函数-进入房间
+func (player *Player) OnEnterRoom(room *Room) {
+	player.Room = room
+}
+
+//可被客户端调用的接口
+//申请进入房间
+func (player *Player) RequestEnterRoom(enterKey string) {
+	EnterRoom(player, enterKey)
+}
+
+//可被客户端调用的接口
+//上传游戏数据,并广播给房间内的其他人
+func (player *Player) UploadPKGameData(key string, str string) {
+	if player.Room != nil {
+		player.Room.BroadcastToOthers(player, "OnReceivePKGameData", key, str)
+	}
+}
+
+//初始化-安全的远程调用
+func InitSecureRemoteCall() {
+	CheckServiceFuncMapBeforInvoke = true
+	ServiceFuncMap["RequestEnterRoom"] = true
+	ServiceFuncMap["UploadPKGameData"] = true
+}

+ 128 - 0
SmartBowGameServer/game/room.go

@@ -0,0 +1,128 @@
+package game
+
+import (
+	"sync"
+	"time"
+
+	"github.com/wonderivan/logger"
+)
+
+type Room struct {
+	//房间ID,目前没啥用,就是方便打印查看
+	ID int
+	//入房钥匙
+	EnterKey string
+	//玩家列表
+	PlayerList []*Player
+}
+
+var staticRoomMapLocker sync.RWMutex
+var staticRoomMap = make(map[string]*Room)
+
+//进入房间
+func EnterRoom(player *Player, enterKey string) {
+	staticRoomMapLocker.Lock()
+	defer staticRoomMapLocker.Unlock()
+	needCreateRoom := false
+	room := staticRoomMap[enterKey]
+	if room == nil {
+		needCreateRoom = true
+	} else if room != nil {
+		needCreateRoom = room.IsFullMembers()
+	}
+	if needCreateRoom {
+		room = &Room{ID: GetUuidInt("Room"), EnterKey: enterKey, PlayerList: []*Player{player, nil}}
+		go room.AutoCheck()
+		staticRoomMap[enterKey] = room
+		logger.Info("roomID:", room.ID, "Create", "EnterKey:", room.EnterKey)
+		player.OnEnterRoom(room)
+	} else {
+		room.PlayerList[1] = player
+		player.OnEnterRoom(room)
+		room.OnRoomReadyComplete()
+	}
+}
+
+//开启检测-主要为了自动销毁
+func (room *Room) AutoCheck() {
+	defer room.CloseAllPlayers()
+	defer func() {
+		staticRoomMapLocker.Lock()
+		if staticRoomMap[room.EnterKey] == room {
+			delete(staticRoomMap, room.EnterKey)
+		}
+		staticRoomMapLocker.Unlock()
+		logger.Info("roomID:", room.ID, "Destroy")
+	}()
+	loopInterval := 2
+	waitTime := 0
+	for {
+		time.Sleep(time.Duration(loopInterval) * time.Second)
+		if room.IsFullMembers() {
+			if !room.IsAllMembersValid() {
+				break
+			}
+		} else {
+			waitTime += loopInterval
+			if waitTime >= 30 {
+				break
+			}
+		}
+	}
+}
+
+//判断房间的人数是否已满
+func (room *Room) IsFullMembers() bool {
+	for _, p := range room.PlayerList {
+		if p == nil {
+			return false
+		}
+	}
+	return true
+}
+
+//判断房间内是由所有玩家都有效
+func (room *Room) IsAllMembersValid() bool {
+	for _, p := range room.PlayerList {
+		if !p.IsValid {
+			return false
+		}
+	}
+	return true
+}
+
+//关闭所有玩家连接
+func (room *Room) CloseAllPlayers() {
+	for _, p := range room.PlayerList {
+		if p != nil {
+			p.Conn.Close()
+		}
+	}
+}
+
+//广播到其他人
+func (room *Room) BroadcastToOthers(player *Player, func_ string, args ...interface{}) {
+	for _, p := range room.PlayerList {
+		if p != nil && p != player {
+			SendPackFunction(p.Conn, func_, args...)
+		}
+	}
+}
+
+//广播到全部人
+func (room *Room) BroadcastToAll(func_ string, args ...interface{}) {
+	for _, p := range room.PlayerList {
+		if p != nil {
+			SendPackFunction(p.Conn, func_, args...)
+		}
+	}
+}
+
+//向客户端发送的请求
+//房间准备完毕,即房间内的玩家都准备好了,接到该通知就可以开始游戏啦
+func (room *Room) OnRoomReadyComplete() {
+	for playerIndexInRoom, p := range room.PlayerList {
+		SendPackFunction(p.Conn, "OnRoomReadyComplete", playerIndexInRoom)
+	}
+	logger.Info("roomID:", room.ID, "OnRoomReadyComplete")
+}

+ 94 - 0
SmartBowGameServer/game/server.go

@@ -0,0 +1,94 @@
+package game
+
+import (
+	"encoding/json"
+	"net/http"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+	"github.com/gorilla/websocket"
+)
+
+type dataPack struct {
+	Type int32         `json:"type"`
+	Func string        `json:"func"`
+	Args []interface{} `json:"args"`
+}
+
+//CheckOrigin防止跨站点的请求伪造
+var upGrader = websocket.Upgrader{
+	CheckOrigin: func(r *http.Request) bool {
+		return true
+	},
+}
+
+func ping(c *gin.Context) {
+	ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
+	if err != nil {
+		return
+	}
+	se := createEntity(ws)
+	defer invokeFunc(se, "OnDestroy")
+	defer ws.Close()
+	invokeFunc(se, "OnLoad")
+	for {
+		_, message, err := ws.ReadMessage()
+		if err != nil {
+			break
+		}
+		dp := dataPack{}
+		json.Unmarshal(message, &dp)
+		invokeFuncByDataPack(se, &dp)
+	}
+}
+
+type CreateEntity func(conn *websocket.Conn) interface{}
+
+var createEntity CreateEntity
+
+func StartServer(port int, path string, createEntityFunc CreateEntity) {
+	createEntity = createEntityFunc
+	r := gin.Default()
+	r.GET(path, ping)
+	r.Run(":" + strconv.Itoa(port))
+}
+
+func SendPackFunction(ws *websocket.Conn, func_ string, args ...interface{}) {
+	dp := dataPack{Func: func_, Args: args, Type: 1}
+	ws.WriteJSON(&dp)
+}
+
+func invokeFunc(obj interface{}, func_ string, args ...interface{}) {
+	ctx := reflect.ValueOf(obj)
+	if strings.Contains(func_, ".") {
+		func_Split := strings.Split(func_, ".")
+		ctx = ctx.FieldByName(func_Split[0])
+		func_ = func_Split[1]
+	}
+	method := ctx.MethodByName(func_)
+	if !method.IsValid() {
+		return
+	}
+	argsLen := len(args)
+	argsList := make([]reflect.Value, argsLen)
+	for i := 0; i < argsLen; i++ {
+		argsList[i] = reflect.ValueOf(args[i])
+	}
+	method.Call(argsList)
+}
+
+//检测开关-在远程调用前是否进行合法检测
+var CheckServiceFuncMapBeforInvoke = false
+
+//为了防止非法远程调用,只能调用Map中记录的方法
+var ServiceFuncMap = make(map[string]bool)
+
+func invokeFuncByDataPack(obj interface{}, dp *dataPack) {
+	if dp.Type == 1 {
+		if !CheckServiceFuncMapBeforInvoke || ServiceFuncMap[dp.Func] {
+			invokeFunc(obj, dp.Func, dp.Args...)
+		}
+	}
+}

+ 37 - 0
SmartBowGameServer/game/util.go

@@ -0,0 +1,37 @@
+package game
+
+import (
+	"fmt"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+//获取线程ID
+func GoID() int {
+	var buf [64]byte
+	n := runtime.Stack(buf[:], false)
+	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
+	id, err := strconv.Atoi(idField)
+	if err != nil {
+		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
+	}
+	return id
+}
+
+var _uuidIntMap = make(map[string]int)
+var _uuidIntLock sync.Mutex
+
+//获取int型的自增ID
+func GetUuidInt(key string) int {
+	_uuidIntLock.Lock()
+	id := _uuidIntMap[key]
+	nextID := id + 1
+	if nextID > 1000000000 {
+		nextID = 0
+	}
+	_uuidIntMap[key] = nextID
+	_uuidIntLock.Unlock()
+	return id
+}

+ 27 - 0
SmartBowGameServer/go.mod

@@ -0,0 +1,27 @@
+module SmartBowGameServer
+
+go 1.17
+
+require github.com/wonderivan/logger v1.0.0
+
+require (
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/gin-gonic/gin v1.7.7 // indirect
+	github.com/go-playground/locales v0.13.0 // indirect
+	github.com/go-playground/universal-translator v0.17.0 // indirect
+	github.com/go-playground/validator/v10 v10.4.1 // indirect
+	github.com/gofrs/uuid v4.0.0+incompatible // indirect
+	github.com/golang/protobuf v1.3.3 // indirect
+	github.com/gomodule/redigo v1.8.4 // indirect
+	github.com/googollee/go-socket.io v1.6.1 // indirect
+	github.com/gorilla/websocket v1.4.2 // indirect
+	github.com/json-iterator/go v1.1.9 // indirect
+	github.com/leodido/go-urn v1.2.0 // indirect
+	github.com/mattn/go-isatty v0.0.12 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
+	github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
+	github.com/ugorji/go/codec v1.1.7 // indirect
+	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
+	golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
+	gopkg.in/yaml.v2 v2.2.8 // indirect
+)

+ 61 - 0
SmartBowGameServer/go.sum

@@ -0,0 +1,61 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
+github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
+github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/googollee/go-socket.io v1.6.1 h1:qSaB0rqZOIbHLs94BkZ/Uwgd6+x/GzkdNtc2IEt8Psc=
+github.com/googollee/go-socket.io v1.6.1/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/wonderivan/logger v1.0.0 h1:Z6Nz+3SNcizolx3ARH11axdD4DXjFpb2J+ziGUVlv/U=
+github.com/wonderivan/logger v1.0.0/go.mod h1:NObMfQ3WOLKfYEZuGeZQfuQfSPE5+QNgRddVMzsAT/k=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 17 - 0
SmartBowGameServer/logs/log.json

@@ -0,0 +1,17 @@
+{
+    "TimeFormat": "2006-01-02 15:04:05",
+    "Console": {
+        "level": "TRAC",
+        "color": true
+    },
+    "File": {
+        "filename": "app.log",
+        "level": "TRAC",
+        "daily": true,
+        "maxlines": 1000000,
+        "maxsize": 1,
+        "maxdays": -1,
+        "append": true,
+        "permit": "0660"
+    }
+}

+ 17 - 0
SmartBowGameServer/main.go

@@ -0,0 +1,17 @@
+package main
+
+import (
+	"SmartBowGameServer/game"
+
+	"github.com/gorilla/websocket"
+	"github.com/wonderivan/logger"
+)
+
+//主函数入口
+func main() {
+	logger.SetLogger("./logs/log.json")
+	game.InitSecureRemoteCall()
+	game.StartServer(11811, "/SmartBowGameServer", func(conn *websocket.Conn) interface{} {
+		return &game.Player{Conn: conn}
+	})
+}