3
0
Fork 0

CHG: refactor

CHG: use concurrent map instead of regular map with lock
DEL: global session control, moved to per server only
DEL: unused command system
This commit is contained in:
Thomas van Weert 2023-02-16 20:04:17 +01:00
parent 570f501a76
commit 9edcb83fc4
12 changed files with 216 additions and 335 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/.idea
/out

View file

@ -1,58 +0,0 @@
package command
import (
"bufio"
"fmt"
"github.com/ParadoxPixel/ThemePark-Websocket/server"
"os"
"strings"
)
var commands = make(map[string]func([]string) bool)
func ReadConsole() {
loadCommands()
reader := bufio.NewReader(os.Stdin)
for {
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
args := strings.Split(text, " ")
command := strings.ToLower(args[0])
args = args[1:]
b := handleCommand(command, args)
if b {
break
}
}
}
func loadCommands() {
commands["exit"] = func(i []string) bool {
server.Stop()
return true
}
commands["ip"] = func(i []string) bool {
ip, err := GetPublicIp()
if err != nil {
fmt.Println("Unable to get public IP")
} else {
fmt.Println("IP:", ip)
}
return false
}
}
func handleCommand(cmd string, args []string) bool {
f := commands[cmd]
if f == nil {
fmt.Println("Unknown command:", cmd)
return false
}
return f(args)
}

View file

@ -1,22 +0,0 @@
package command
import (
"io/ioutil"
"net/http"
)
func GetPublicIp() (str string, err error) {
url := "https://api.ipify.org?format=text"
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(ip), nil
}

View file

@ -5,15 +5,10 @@ import (
"sync" "sync"
) )
var serverMux sync.RWMutex var servers = new(sync.Map)
var servers = make(map[string]*objects.Server)
func AddServer(server *objects.Server) { func AddServer(server *objects.Server) {
serverMux.Lock() servers.LoadOrStore(server.ID, server)
if _,ok := servers[server.ID]; !ok {
servers[server.ID] = server
}
serverMux.Unlock()
} }
func CanServer(server string) bool { func CanServer(server string) bool {
@ -28,27 +23,12 @@ func CanServer(server string) bool {
} }
} }
func GetServer(server string) *objects.Server {
serverMux.Lock()
val,ok := servers[server]
serverMux.Unlock()
if ok {
return val
} else {
return nil
}
}
func HasServer(publicKey string) (bool, *objects.Server) { func HasServer(publicKey string) (bool, *objects.Server) {
serverMux.Lock() val, ok := servers.Load(publicKey)
val,ok := servers[publicKey] return ok, val.(*objects.Server)
serverMux.Unlock()
return ok, val
} }
func RemoveServer(session string) { func RemoveServer(session string) *objects.Server {
serverMux.Lock() val, _ := servers.LoadAndDelete(session)
delete(servers, session) return val.(*objects.Server)
serverMux.Unlock()
} }

View file

@ -1,45 +0,0 @@
package data
import (
"github.com/ParadoxPixel/ThemePark-Websocket/objects"
"sync"
)
var sessionMux sync.RWMutex
var sessions = make(map[string]*objects.Session)
func AddSession(session *objects.Session) {
sessionMux.Lock()
sessions[session.Token] = session
sessionMux.Unlock()
}
func GetSession(session string) *objects.Session {
sessionMux.Lock()
val := sessions[session]
sessionMux.Unlock()
return val
}
func HasSession(session string) bool {
sessionMux.Lock()
_,ok := sessions[session]
sessionMux.Unlock()
return ok
}
func CanSession(session string) bool {
if session == "" || session == "null" {
return false
}
return !HasSession(session)
}
func RemoveSession(session string) {
sessionMux.Lock()
delete(sessions, session)
sessionMux.Unlock()
}

View file

@ -10,10 +10,11 @@ import (
func Load(namespace socketio.Namespace, debug bool) { func Load(namespace socketio.Namespace, debug bool) {
namespace.OnEvent("data", func(so socketio.Socket, msg string) { namespace.OnEvent("data", func(so socketio.Socket, msg string) {
clientType := so.GetQuery().Get("type") == "server" isServer := so.GetQuery().Get("type") == "server"
var ok bool var ok bool
var server *objects.Server var server *objects.Server
if !clientType { var err error
if !isServer {
ok, server = data.HasServer(so.GetQuery().Get("control_id")) ok, server = data.HasServer(so.GetQuery().Get("control_id"))
if !ok { if !ok {
if debug { if debug {
@ -27,28 +28,39 @@ func Load(namespace socketio.Namespace, debug bool) {
return return
} }
_ = server.Socket.Emit("data", msg) err = server.Socket.Emit("data", msg)
if err != nil && debug {
log.Println("error while sending data to server:", err)
}
return return
} }
ok, server = data.HasServer(so.GetQuery().Get("id")) ok, server = data.HasServer(so.GetQuery().Get("id"))
if !strings.HasPrefix(msg, "{") { if msg[0] == '{' {
args := strings.SplitN(msg, "@", 2) err = server.Socket.EmitError("messages can only be send to clients")
ok, session := server.HasSession(args[0]) if err != nil && debug {
if !ok { log.Println("error while sending data to server:", err)
if debug {
log.Println(
"Unable to send packet from server:",
server.ID,
"to client:",
args[0],
)
}
return
} }
_ = session.Emit("data", args[1])
return return
} }
args := strings.SplitN(msg, "@", 2)
ok, session := server.HasSession(args[0])
if !ok {
if debug {
log.Println(
"Unable to send packet from server:",
server.ID,
"to client:",
args[0],
)
}
return
}
err = session.Emit("data", args[1])
if err != nil && debug {
log.Println("error while sending data to server:", err)
}
}) })
} }

80
handlers/user/client.go Normal file
View file

@ -0,0 +1,80 @@
package user
import (
"github.com/Mindgamesnl/socketio"
"github.com/ParadoxPixel/ThemePark-Websocket/data"
"log"
)
func clientLogin(so socketio.Socket) {
ok, server := data.HasServer(so.GetQuery().Get("control_id"))
if !ok {
if debug {
log.Println(
"No server with id:",
so.GetQuery().Get("control_id"),
"for token:",
so.GetQuery().Get("token"),
)
}
_ = so.Emit("close", "SERVER_ERROR")
_ = so.Close()
return
}
b := server.AddSession(so.GetQuery().Get("token"), so.GetQuery().Get("attraction"), so)
if !b {
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("control_id"),
"already has operator for attraction:",
so.GetQuery().Get("attraction"),
)
}
_ = so.Emit("close", "AUTHENTICATION_ERROR")
_ = so.Close()
return
}
if debug {
log.Println(
"Client connected to id:",
so.GetQuery().Get("control_id"),
"with token:",
so.GetQuery().Get("token"),
"to attraction:",
so.GetQuery().Get("attraction"),
)
}
err := server.Socket.Emit("data", "{\"channel\":\"SERVER_IN_REGISTER_CLIENT\",\"data\":{\"payload\":{\"uuid\":\""+so.GetQuery().Get("uuid")+"\",\"token\":\""+so.GetQuery().Get("token")+"\",\"attraction_id\":\""+so.GetQuery().Get("attraction")+"\"},\"type\":\"ClientConnectPayload\"}}")
if err != nil && debug {
log.Println(err)
}
}
func clientQuit(so socketio.Socket) {
session := so.GetQuery().Get("token")
ok, server := data.HasServer(so.GetQuery().Get("control_id"))
if !ok {
return
}
if debug {
log.Println(
"Client disconnected to id:",
so.GetQuery().Get("control_id"),
"with token:",
so.GetQuery().Get("token"),
"to attraction:",
so.GetQuery().Get("attraction"),
)
}
server.RemoveSession(session, so.GetQuery().Get("attraction"))
err := server.Socket.Emit("data", "{\"channel\":\"SERVER_IN_UNREGISTER_CLIENT\",\"data\":{\"payload\":{\"uuid\":\""+so.GetQuery().Get("uuid")+"\"},\"type\":\"ClientDisconnectPayload\"}}")
if err != nil && debug {
log.Println(err)
}
}

View file

@ -3,8 +3,6 @@ package user
import ( import (
"fmt" "fmt"
"github.com/Mindgamesnl/socketio" "github.com/Mindgamesnl/socketio"
"github.com/ParadoxPixel/ThemePark-Websocket/data"
"github.com/ParadoxPixel/ThemePark-Websocket/objects"
"log" "log"
) )
@ -22,57 +20,18 @@ func Load(namespace socketio.Namespace, b bool) {
return return
} else { } else {
_ = so.Close() _ = so.Close()
if debug {
log.Println("invalid client type:", clientType)
}
} }
}) })
namespace.OnDisconnect(func(so socketio.Socket) { namespace.OnDisconnect(func(so socketio.Socket) {
clientType := so.GetQuery().Get("type") clientType := so.GetQuery().Get("type")
var session string
if clientType == "server" { if clientType == "server" {
session = so.GetQuery().Get("id") serverQuit(so)
obj := data.GetServer(session)
if obj == nil {
return
}
data.RemoveServer(session)
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"disconnected",
)
}
for _, user := range obj.Sessions {
_ = user.Emit("close", "SERVER_ERROR")
_ = user.Close()
}
} else if clientType == "client" { } else if clientType == "client" {
session = so.GetQuery().Get("token") clientQuit(so)
obj := data.GetSession(session)
if obj == nil {
return
}
ok, server := data.HasServer(obj.ID)
if !ok {
return
}
if debug {
log.Println(
"Client disconnected to id:",
so.GetQuery().Get("control_id"),
"with token:",
so.GetQuery().Get("token"),
"to attraction:",
so.GetQuery().Get("attraction"),
)
}
data.RemoveSession(session)
server.RemoveSession(session, so.GetQuery().Get("attraction"))
_ = server.Socket.Emit("data", "{\"channel\":\"SERVER_IN_UNREGISTER_CLIENT\",\"data\":{\"payload\":{\"uuid\":\""+obj.UUID+"\"},\"type\":\"ClientDisconnectPayload\"}}")
} }
}) })
@ -80,93 +39,3 @@ func Load(namespace socketio.Namespace, b bool) {
fmt.Println(err) fmt.Println(err)
}) })
} }
func serverLogin(so socketio.Socket) {
id := so.GetQuery().Get("id")
if !data.CanServer(id) {
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"already connected",
)
}
_ = so.Close()
return
}
data.AddServer(&objects.Server{
ID: id,
Sessions: make(map[string]socketio.Socket),
Attraction: make(map[string]string),
Socket: so,
})
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"connected",
)
}
}
func clientLogin(so socketio.Socket) {
if !data.CanSession(so.GetQuery().Get("token")) {
_ = so.Close()
return
}
ok, server := data.HasServer(so.GetQuery().Get("control_id"))
if !ok {
if debug {
log.Println(
"No server with id:",
so.GetQuery().Get("control_id"),
"for token:",
so.GetQuery().Get("token"),
)
}
_ = so.Emit("close", "SERVER_ERROR")
_ = so.Close()
return
}
ok = server.CanAttraction(so.GetQuery().Get("attraction"))
if !ok {
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("control_id"),
"already has operator for attraction:",
so.GetQuery().Get("attraction"),
)
}
_ = so.Emit("close", "AUTHENTICATION_ERROR")
_ = so.Close()
return
}
data.AddSession(&objects.Session{
Token: so.GetQuery().Get("token"),
UUID: so.GetQuery().Get("uuid"),
ID: so.GetQuery().Get("control_id"),
Attraction: so.GetQuery().Get("attraction"),
})
server.AddSession(so.GetQuery().Get("token"), so.GetQuery().Get("attraction"), so)
if debug {
log.Println(
"Client connected to id:",
so.GetQuery().Get("control_id"),
"with token:",
so.GetQuery().Get("token"),
"to attraction:",
so.GetQuery().Get("attraction"),
)
}
err := server.Socket.Emit("data", "{\"channel\":\"SERVER_IN_REGISTER_CLIENT\",\"data\":{\"payload\":{\"uuid\":\""+so.GetQuery().Get("uuid")+"\",\"token\":\""+so.GetQuery().Get("token")+"\",\"attraction_id\":\""+so.GetQuery().Get("attraction")+"\"},\"type\":\"ClientConnectPayload\"}}")
if err != nil {
log.Println(err)
}
}

62
handlers/user/server.go Normal file
View file

@ -0,0 +1,62 @@
package user
import (
"github.com/Mindgamesnl/socketio"
"github.com/ParadoxPixel/ThemePark-Websocket/data"
"github.com/ParadoxPixel/ThemePark-Websocket/objects"
"log"
"sync"
)
func serverLogin(so socketio.Socket) {
id := so.GetQuery().Get("id")
if !data.CanServer(id) {
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"already connected",
)
}
_ = so.Close()
return
}
data.AddServer(&objects.Server{
ID: id,
Sessions: make(map[string]socketio.Socket),
Attraction: make(map[string]string),
Socket: so,
Mux: new(sync.RWMutex),
})
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"connected",
)
}
}
func serverQuit(so socketio.Socket) {
session := so.GetQuery().Get("id")
obj := data.RemoveServer(session)
if obj == nil {
return
}
if debug {
log.Println(
"Server with id:",
so.GetQuery().Get("id"),
"disconnected",
)
}
// Disconnect users
for _, user := range obj.Sessions {
_ = user.Emit("close", "SERVER_ERROR")
_ = user.Close()
}
}

View file

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"github.com/ParadoxPixel/ThemePark-Websocket/command"
"github.com/ParadoxPixel/ThemePark-Websocket/objects" "github.com/ParadoxPixel/ThemePark-Websocket/objects"
"github.com/ParadoxPixel/ThemePark-Websocket/server" "github.com/ParadoxPixel/ThemePark-Websocket/server"
) )
@ -14,7 +13,5 @@ func main() {
return return
} }
go server.Start("/", config.Address, config.Debug) server.Start("/", config.Address, config.Debug)
command.ReadConsole()
} }

View file

@ -6,32 +6,44 @@ import (
) )
type Server struct { type Server struct {
Socket socketio.Socket Socket socketio.Socket
ID string ID string
Sessions map[string]socketio.Socket Sessions map[string]socketio.Socket
Attraction map[string]string Attraction map[string]string
mux sync.RWMutex Mux *sync.RWMutex
} }
func (serv *Server) CanAttraction(attraction string) bool { func (serv *Server) CanAttraction(attraction string) bool {
serv.mux.RLock() serv.Mux.RLock()
defer serv.mux.RUnlock() defer serv.Mux.RUnlock()
_,b := serv.Attraction[attraction] _, b := serv.Attraction[attraction]
return !b return !b
} }
func (serv *Server) AddSession(session, attraction string, io socketio.Socket) { func (serv *Server) AddSession(session, attraction string, io socketio.Socket) bool {
serv.mux.Lock() serv.Mux.Lock()
defer serv.Mux.Unlock()
// Check if session in use
if _, b := serv.Sessions[session]; b {
return false
}
// Check if attraction is already controlled
if _, b := serv.Attraction[attraction]; b {
return false
}
serv.Sessions[session] = io serv.Sessions[session] = io
serv.Attraction[attraction] = session serv.Attraction[attraction] = session
serv.mux.Unlock() return true
} }
func (serv *Server) HasSession(session string) (bool, socketio.Socket) { func (serv *Server) HasSession(session string) (bool, socketio.Socket) {
serv.mux.RLock() serv.Mux.RLock()
defer serv.mux.RUnlock() defer serv.Mux.RUnlock()
io, b := serv.Sessions[session] io, b := serv.Sessions[session]
@ -39,8 +51,8 @@ func (serv *Server) HasSession(session string) (bool, socketio.Socket) {
} }
func (serv *Server) RemoveSession(session, attraction string) { func (serv *Server) RemoveSession(session, attraction string) {
serv.mux.Lock() serv.Mux.Lock()
delete(serv.Sessions, session) delete(serv.Sessions, session)
delete(serv.Attraction, attraction) delete(serv.Attraction, attraction)
serv.mux.Unlock() serv.Mux.Unlock()
} }

View file

@ -1,8 +0,0 @@
package objects
type Session struct {
Token string `json:"token"`
UUID string `json:"uuid"`
ID string `json:"id"`
Attraction string `json:"attraction"`
}