diff --git a/config_template.json b/config_template.json index 174f78a..cc5401e 100644 --- a/config_template.json +++ b/config_template.json @@ -5,19 +5,9 @@ "redis": { "cache": "redis://192.168.8.94:6380/0", "session": "redis://192.168.8.94:6380/1", - "ranking": "redis://192.168.8.94:6380/2", - "wshandler": "redis://192.168.8.94:6380/3", - "tavern": "redis://192.168.8.94:6380/4" - } - }, - "dev": { - "mongo": "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", - "redis": { - "cache": "redis://192.168.8.94:6380/5", - "session": "redis://192.168.8.94:6380/6", - "ranking": "redis://192.168.8.94:6380/7", - "wshandler": "redis://192.168.8.94:6380/8", - "tavern": "redis://192.168.8.94:6380/9" + "tx": "redis://192.168.8.94:6380/2", + "tavern": "redis://192.168.8.94:6380/3", + "wshandler": "redis://192.168.8.94:6380/4" } } }, diff --git a/core/group_chat.go b/core/group_chat.go index ec18c1e..22cb6f8 100644 --- a/core/group_chat.go +++ b/core/group_chat.go @@ -6,8 +6,10 @@ import ( "io" "net/http" "reflect" + "strings" "time" + "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "repositories.action2quare.com/ayo/gocommon" @@ -20,9 +22,16 @@ func init() { } type channelID = string +type channelConfig struct { + Capacity int64 `json:"capacity"` + Size int64 `json:"size"` + Key string `json:"key"` + emptyJson string +} + type chatConfig struct { - DefaultCapacity int64 `json:"default_capacity"` - Channels map[string]map[string]any `json:"channels"` + DefaultCapacity int64 `json:"default_capacity"` + Channels map[string]channelConfig `json:"channels"` } type groupChat struct { @@ -58,15 +67,15 @@ func (gc *groupChat) Initialize(sub *subTavern, cfg configDocument) error { sub.apiFuncs.registApiFunction("BroadcastMessageOnChannel", gc.BroadcastMessageOnChannel) sub.apiFuncs.registApiFunction("QueryPlayerChattingChannel", gc.QueryPlayerChattingChannel) sub.apiFuncs.registApiFunction("SendMessageOnChannel", gc.SendMessageOnChannel) - sub.apiFuncs.registApiFunction("UpdateChannelDocument", gc.UpdateChannelDocument) for name, cfg := range gc.chatConfig.Channels { - if _, ok := cfg["capacity"]; !ok { - cfg["capacity"] = gc.chatConfig.DefaultCapacity - } else { - cfg["capacity"] = int64(cfg["capacity"].(float64)) + if cfg.Capacity == 0 { + cfg.Capacity = gc.chatConfig.DefaultCapacity } - cfg["key"] = name - cfg["size"] = int32(0) + cfg.Key = name + cfg.Size = 0 + + jm, _ := json.Marshal(cfg) + cfg.emptyJson = fmt.Sprintf("[%s]", string(jm)) _, err := gc.rh.JSONSet(name, "$", cfg) if *devflag && err != nil { @@ -100,27 +109,9 @@ func (gc *groupChat) Initialize(sub *subTavern, cfg configDocument) error { func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandler.WebSocketMessageType, message any) { if mt == wshandler.Disconnected { - rooms := message.([]string) - for _, chanid := range rooms { - if _, ok := gc.chatConfig.Channels[chanid]; !ok { - continue - } - - size, err := gc.rh.JSONNumIncrBy(chanid, "$.size", -1) - if err != nil { - logger.Println("JSONMGet failed :", err) - continue - } - gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + chanid, - Body: map[string]any{"size": size}, - Tag: []string{"ChattingChannelProperties"}, - }) - } if _, err := gc.rh.Del(gc.rh.Context(), accidHex(sender.Accid)).Result(); err != nil { logger.Println(err) } - } else if mt == wshandler.BinaryMessage { commandline := message.([]any) cmd := commandline[0].(string) @@ -132,11 +123,22 @@ func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandle size, err := gc.rh.JSONGetInt64(chanid, "$.size") if err != nil || len(size) == 0 { logger.Println("JSONGetInt64 failed :", chanid, err) - } else if size[0] < cfg["capacity"].(int64) { + } else if size[0] < cfg.Capacity { // 입장 newsize, err := gc.rh.JSONNumIncrBy(chanid, "$.size", 1) if err == nil { gc.enterRoom(chanid, sender.Accid) + sender.RegistDisconnectedCallback(chanid, func() { + size, err := gc.rh.JSONNumIncrBy(chanid, "$.size", -1) + if err == nil { + gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "#" + chanid, + Body: map[string]any{"size": size}, + Tag: []string{"ChattingChannelProperties"}, + }) + } + }) + gc.rh.HSet(gc.rh.Context(), accidHex(sender.Accid), "cc_pub", chanid) gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ Target: "#" + chanid, @@ -156,13 +158,8 @@ func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandle chanid := args[0].(string) gc.rh.HDel(gc.rh.Context(), accidHex(sender.Accid), "cc_pub") gc.leaveRoom(chanid, sender.Accid) - newsize, err := gc.rh.JSONNumIncrBy(chanid, "$.size", 1) - if err == nil { - gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + chanid, - Body: map[string]any{"size": newsize[0]}, - Tag: []string{"ChattingChannelProperties"}, - }) + if f := sender.PopDisconnectedCallback(chanid); f != nil { + f() } case "TextMessage": @@ -191,6 +188,18 @@ func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandle return } gc.enterRoom(channel, sender.Accid) + + sender.RegistDisconnectedCallback(channel, func() { + gc.rh.JSONDel(channel, "$."+sender.Accid.Hex()) + cnt, _ := gc.rh.HDel(gc.rh.Context(), accidHex(sender.Accid), "cc_"+typename).Result() + if cnt > 0 { + gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "#" + channel, + Body: map[string]any{"sender": sender.Alias, "typename": typename}, + Tag: []string{"LeavePrivateChannel"}, + }) + } + }) } gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ @@ -204,17 +213,11 @@ func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandle }) case "LeavePrivateChannel": - typename := args[0].(string) channel := args[1].(string) - cnt, _ := gc.rh.HDel(gc.rh.Context(), accidHex(sender.Accid), "cc_"+typename).Result() - if cnt > 0 { - gc.leaveRoom(channel, sender.Accid) + gc.leaveRoom(channel, sender.Accid) + if f := sender.PopDisconnectedCallback(channel); f != nil { + f() } - gc.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + channel, - Body: map[string]any{"sender": sender.Alias, "typename": typename}, - Tag: []string{"LeavePrivateChannel"}, - }) } } } @@ -227,23 +230,26 @@ func (gc *groupChat) FetchChattingChannels(w http.ResponseWriter, r *http.Reques return } - keys, err := gc.rh.Keys(gc.rh.Context(), prefix+"*").Result() - if err != nil { - logger.Println("FetchChattingChannel failed. Keys return err :", err) - w.WriteHeader(http.StatusInternalServerError) - return - } - var rows []string - for _, key := range keys { - onechan, err := gc.rh.JSONGet(key, "$") - if err != nil { + for name, cfg := range gc.chatConfig.Channels { + if len(prefix) > 0 { + if !strings.HasPrefix(name, prefix) { + continue + } + } + + onechan, err := gc.rh.JSONGet(name, "$") + if err != nil && err != redis.Nil { logger.Println("FetchChattingChannel failed. HGetAll return err :", err) w.WriteHeader(http.StatusInternalServerError) return } - row := onechan.(string) - rows = append(rows, row) + + if err == redis.Nil || onechan == nil { + rows = append(rows, cfg.emptyJson) + } else { + rows = append(rows, onechan.(string)) + } } if len(rows) == 0 { @@ -291,58 +297,6 @@ func (gc *groupChat) QueryPlayerChattingChannel(w http.ResponseWriter, r *http.R enc.Encode(output) } -func (gc *groupChat) UpdateChannelDocument(w http.ResponseWriter, r *http.Request) { - channel, _ := gocommon.ReadStringFormValue(r.Form, "channel") - key, _ := gocommon.ReadStringFormValue(r.Form, "key") - ret, _ := gocommon.ReadStringFormValue(r.Form, "return") - if len(channel) == 0 || len(key) == 0 { - w.WriteHeader(http.StatusBadRequest) - return - } - - bt, err := io.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - var doc map[string]any - if err := bson.Unmarshal(bt, &doc); err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - if ret == "before" { - before, err2 := gc.rh.JSONGet(channel, "$") - if err2 != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - defer func() { - if err == nil { - w.Write([]byte(before.(string))) - } - }() - } else if ret == "after" { - defer func() { - if err == nil { - after, err2 := gc.rh.JSONGet(channel, "$") - if err2 != nil { - w.WriteHeader(http.StatusInternalServerError) - } else { - w.Write([]byte(after.(string))) - } - } - }() - } - _, err = gc.rh.JSONSet(channel, "$."+key, doc) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } -} - func (gc *groupChat) SendMessageOnChannel(w http.ResponseWriter, r *http.Request) { channel, _ := gocommon.ReadStringFormValue(r.Form, "channel") target, _ := gocommon.ReadStringFormValue(r.Form, "target") diff --git a/core/tavern.go b/core/tavern.go index 8d6b808..3dbf9d8 100644 --- a/core/tavern.go +++ b/core/tavern.go @@ -245,28 +245,24 @@ func (sub *subTavern) OnClientMessageReceived(sender *wshandler.Sender, messageT } func (sub *subTavern) OnRoomCreated(region, name string) { - created, err := sub.redison.JSONSet(name, "$", map[string]any{ - "_refcnt": 1, - }, gocommon.RedisonSetOptionNX) + cnt, err := sub.redison.IncrBy(sub.redison.Context(), "_ref_"+name, 1).Result() if err != nil && !errors.Is(err, redis.Nil) { logger.Println("OnRoomCreated JSONSet failed :", err) return } - if !created { - _, err = sub.redison.JSONNumIncrBy(name, "$._refcnt", 1) - if err != nil { - logger.Println("OnRoomCreated JSONSet failed :", err) - return - } + + if cnt == 1 { + sub.redison.JSONSet(name, "$", map[string]any{}) } } func (sub *subTavern) OnRoomDestroyed(region, name string) { - cnt, err := sub.redison.JSONNumIncrBy(name, "$._refcnt", -1) - if err != nil || len(cnt) == 0 { + cnt, err := sub.redison.IncrBy(sub.redison.Context(), "_ref_"+name, -1).Result() + if err != nil { logger.Println("OnRoomDestroyed JSONNumIncrBy failed :", err) - } else if cnt[0] == 0 { - sub.redison.Del(sub.redison.Context(), name) + } else if cnt == 0 { + sub.redison.Del(sub.redison.Context(), "_ref_"+name) + sub.redison.JSONDel(name, "$") } } diff --git a/go.mod b/go.mod index 501b16c..11cbdce 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/go-redis/redis/v8 v8.11.5 go.mongodb.org/mongo-driver v1.11.7 - repositories.action2quare.com/ayo/gocommon v0.0.0-20230811100012-b88bcff3893e + repositories.action2quare.com/ayo/gocommon v0.0.0-20230812103033-a5b7e119644d ) require ( diff --git a/go.sum b/go.sum index cde1a0d..5065672 100644 --- a/go.sum +++ b/go.sum @@ -130,3 +130,9 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230810063516-a57de9715cb7 h1 repositories.action2quare.com/ayo/gocommon v0.0.0-20230810063516-a57de9715cb7/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230811100012-b88bcff3893e h1:nX3QnIxzdWwejKQuhYpOiE6Qc8GzbLs/3J2WW7jYZkU= repositories.action2quare.com/ayo/gocommon v0.0.0-20230811100012-b88bcff3893e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812101822-05478f54aee5 h1:9bJ86X3UuyaCwmK+zB6hdaClHQsGTjdRlhMvvHw52h4= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812101822-05478f54aee5/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812102616-4e69b5b9fb0d h1:9pgdqjqVaK8vUoC051oMSyM4GasaqiqfrsJLyrjVRoU= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812102616-4e69b5b9fb0d/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812103033-a5b7e119644d h1:x5bLUeaOBCg2kiJuX++0FdaQlqR5a7YvW7cHsI4rr8o= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230812103033-a5b7e119644d/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=