244 lines
5.9 KiB
Go
244 lines
5.9 KiB
Go
package core
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"repositories.action2quare.com/ayo/gocommon"
|
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
|
"repositories.action2quare.com/ayo/gocommon/wshandler"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
type instantDoc struct {
|
|
Members map[string]primitive.M `json:"_members"`
|
|
Count int64 `json:"_count"`
|
|
Body primitive.M `json:"_body"`
|
|
Gid primitive.ObjectID `json:"_gid"`
|
|
|
|
rh *gocommon.RedisonHandler
|
|
idstr string
|
|
}
|
|
|
|
func (gd *instantDoc) strid() string {
|
|
if len(gd.idstr) == 0 {
|
|
gd.idstr = gd.Gid.Hex()
|
|
}
|
|
return gd.idstr
|
|
}
|
|
|
|
func (gd *instantDoc) tid(in accountID) string {
|
|
return makeTid(gd.Gid, in)
|
|
}
|
|
|
|
var errGroupAlreadyDestroyed = errors.New("instant group is already destroyed")
|
|
|
|
func (gd *instantDoc) removeMember(mid accountID) error {
|
|
counts, _ := gd.rh.JSONNumIncrBy(gd.strid(), "$._count", -1)
|
|
if len(counts) == 0 {
|
|
// 이미 지워진 인스턴트그룹
|
|
return errGroupAlreadyDestroyed
|
|
}
|
|
|
|
if _, err := gd.rh.JSONDel(gd.strid(), "$._members."+gd.tid(mid)); err != nil {
|
|
return err
|
|
}
|
|
|
|
gd.Count = counts[0]
|
|
return nil
|
|
}
|
|
|
|
type groupInstant struct {
|
|
sendUpstreamMessage func(*wshandler.UpstreamMessage)
|
|
enterRoom func(groupID, accountID)
|
|
leaveRoom func(groupID, accountID)
|
|
rh *gocommon.RedisonHandler
|
|
}
|
|
|
|
func (gi *groupInstant) Initialize(tv *Tavern) error {
|
|
gi.rh = tv.redison
|
|
gi.sendUpstreamMessage = func(msg *wshandler.UpstreamMessage) {
|
|
tv.wsh.SendUpstreamMessage(msg)
|
|
}
|
|
gi.enterRoom = func(gid groupID, accid accountID) {
|
|
tv.wsh.EnterRoom(gid.Hex(), accid)
|
|
}
|
|
gi.leaveRoom = func(gid groupID, accid accountID) {
|
|
tv.wsh.LeaveRoom(gid.Hex(), accid)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (gi *groupInstant) UpdateInstantDocument(w http.ResponseWriter, r *http.Request) {
|
|
var data struct {
|
|
Gid primitive.ObjectID
|
|
Doc primitive.M
|
|
Result string
|
|
}
|
|
if err := gocommon.MakeDecoder(r).Decode(&data); err != nil {
|
|
logger.Println("UpdateInstantDocument failed. Decode returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
gd := partyDoc{
|
|
id: data.Gid,
|
|
rh: gi.rh,
|
|
}
|
|
if err := gi.rh.JSONMSetRel(gd.strid(), "$.", data.Doc); err != nil {
|
|
logger.Println("UpdateInstantDocument failed. JSONMSetRel returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// 업데이트 알림
|
|
gi.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + gd.strid(),
|
|
Body: data.Doc,
|
|
Tag: []string{"GroupDocFragment"},
|
|
})
|
|
|
|
if data.Result == "after" {
|
|
fulldoc := gd.loadFull()
|
|
if fulldoc != nil {
|
|
tids := fulldoc["_members"].(map[string]any)
|
|
mids := make(map[string]any)
|
|
for k, v := range tids {
|
|
mid := midFromTid(data.Gid, k)
|
|
mids[mid.Hex()] = v
|
|
}
|
|
fulldoc["_members"] = mids
|
|
}
|
|
gocommon.MakeEncoder(w, r).Encode(fulldoc)
|
|
}
|
|
}
|
|
|
|
func (gi *groupInstant) Build(w http.ResponseWriter, r *http.Request) {
|
|
var data struct {
|
|
Members []struct {
|
|
Mid primitive.ObjectID
|
|
Character primitive.M
|
|
}
|
|
Body primitive.M
|
|
}
|
|
if err := gocommon.MakeDecoder(r).Decode(&data); err != nil {
|
|
logger.Println("JoinParty failed. DecodeGob returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// data.Members가 다 살아있는지 부터 확인
|
|
var result struct {
|
|
Gid primitive.ObjectID // 실패하면 없음
|
|
Success []primitive.ObjectID
|
|
}
|
|
for _, m := range data.Members {
|
|
count, err := gi.rh.Exists(gi.rh.Context(), m.Mid.Hex()).Result()
|
|
if err != nil {
|
|
logger.Println("instant.Build failed. Exists returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if count != 0 {
|
|
result.Success = append(result.Success, m.Mid)
|
|
}
|
|
}
|
|
|
|
if len(result.Success) < len(data.Members) {
|
|
// 이탈자가 있군
|
|
// 여기서 중단
|
|
gocommon.MakeEncoder(w, r).Encode(result)
|
|
return
|
|
}
|
|
|
|
newid := primitive.NewObjectID()
|
|
members := make(map[string]primitive.M)
|
|
for _, m := range data.Members {
|
|
tid := makeTid(newid, m.Mid)
|
|
members[tid] = m.Character
|
|
}
|
|
|
|
gd := &instantDoc{
|
|
Members: members,
|
|
Body: data.Body,
|
|
Count: int64(len(members)),
|
|
|
|
rh: gi.rh,
|
|
Gid: newid,
|
|
}
|
|
|
|
_, err := gi.rh.JSONSet(gd.strid(), "$", gd, gocommon.RedisonSetOptionNX)
|
|
if err != nil {
|
|
logger.Println("instant.Build failed. JSONSet returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
result.Gid = newid
|
|
for _, char := range members {
|
|
delete(char, "_id")
|
|
}
|
|
|
|
// 멤버에 그룹 전체를 알림
|
|
for _, m := range data.Members {
|
|
midstr := m.Mid.Hex()
|
|
gi.rh.JSONSet(midstr, "$.instant", bson.M{"id": gd.strid()})
|
|
gi.enterRoom(newid, m.Mid)
|
|
gi.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: midstr,
|
|
Body: gd,
|
|
Tag: []string{"GroupDocFull"},
|
|
})
|
|
}
|
|
|
|
gocommon.MakeEncoder(w, r).Encode(result)
|
|
}
|
|
|
|
func (gi *groupInstant) makeClientClean(accid primitive.ObjectID) {
|
|
gids, _ := gi.rh.JSONGetString(accid.Hex(), "$.instant.id")
|
|
|
|
if len(gids) > 0 && len(gids[0]) > 0 {
|
|
gidstr := gids[0]
|
|
gid, _ := primitive.ObjectIDFromHex(gidstr)
|
|
|
|
gi.rh.JSONDel(accid.Hex(), "$.instant.id")
|
|
gi.leaveRoom(gid, accid)
|
|
|
|
// gid에는 제거 메시지 보냄
|
|
gd := instantDoc{
|
|
Gid: gid,
|
|
rh: gi.rh,
|
|
}
|
|
|
|
gi.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + gd.strid(),
|
|
Body: bson.M{
|
|
gd.tid(accid): bson.M{},
|
|
},
|
|
Tag: []string{"MemberDocFull"},
|
|
})
|
|
|
|
gd.removeMember(accid)
|
|
if gd.Count == 0 {
|
|
gd.rh.Del(gd.rh.Context(), gd.strid()).Result()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (gi *groupInstant) MakeClientClean(w http.ResponseWriter, r *http.Request) {
|
|
var accid primitive.ObjectID
|
|
if err := gocommon.MakeDecoder(r).Decode(&accid); err != nil {
|
|
logger.Println("MakeClientClean failed. decode returns err :", err)
|
|
return
|
|
}
|
|
gi.makeClientClean(accid)
|
|
}
|
|
|
|
func (gi *groupInstant) ClientDisconnected(msg string, callby *wshandler.Sender) {
|
|
gi.makeClientClean(callby.Accid)
|
|
}
|