From e8f74bcd19b680a89e17ed6de22e81357a85d33e Mon Sep 17 00:00:00 2001 From: mountain Date: Mon, 9 Oct 2023 17:59:15 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/group_instant.go | 220 +++++++++++++++++++++++++++--------------- 1 file changed, 144 insertions(+), 76 deletions(-) diff --git a/core/group_instant.go b/core/group_instant.go index 734ac92..4fb32ba 100644 --- a/core/group_instant.go +++ b/core/group_instant.go @@ -78,7 +78,7 @@ func (gd *instantDoc) mid(tid string) accountID { return out } -func (gd *instantDoc) addMember(mid accountID, character bson.M) (bson.M, error) { +func (gd *instantDoc) addMember(mid accountID, character any) (bson.M, error) { tid := gd.tid(mid) if _, err := gd.rh.JSONSet(gd.strid(), "$._members."+tid, character); err != nil { return nil, err @@ -95,14 +95,14 @@ func (gd *instantDoc) addMember(mid accountID, character bson.M) (bson.M, error) var errGroupAlreadyDestroyed = errors.New("instant group is already destroyed") -func (gd *instantDoc) removeMemberByTid(tid string) error { +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."+tid); err != nil { + if _, err := gd.rh.JSONDel(gd.strid(), "$._members."+gd.tid(mid)); err != nil { return err } @@ -110,11 +110,7 @@ func (gd *instantDoc) removeMemberByTid(tid string) error { return nil } -func (gd *instantDoc) removeMember(mid accountID) error { - return gd.removeMemberByTid(gd.tid(mid)) -} - -func (gd *instantDoc) getMembers() (map[string]any, error) { +func (gd *instantDoc) getMembers() (map[primitive.ObjectID]any, error) { res, err := gd.rh.JSONGet(gd.strid(), "$._members") if err != nil { return nil, err @@ -126,9 +122,9 @@ func (gd *instantDoc) getMembers() (map[string]any, error) { return nil, err } - out := make(map[string]any) + out := make(map[primitive.ObjectID]any) for k, v := range temp[0] { - out[gd.mid(k).Hex()] = v + out[gd.mid(k)] = v } return out, nil @@ -160,6 +156,35 @@ func (gi *groupInstant) RegisterApiFunctions() { } +func (gi *groupInstant) join(gd *instantDoc, mid primitive.ObjectID, character any) error { + // 내 정보 업데이트할 때에도 사용됨 + memdoc, err := gd.addMember(mid, character) + if err != nil { + return err + } + + // 기존 유저에게 새 유저 알림 + gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "#" + gd.strid(), + Body: map[string]any{ + gd.tid(mid): memdoc, + }, + Tag: []string{"MemberDocFull"}, + }) + + gi.rh.JSONSet(mid.Hex(), "$.instant", bson.M{"id": gd.strid()}) + + // 최초 입장이라면 새 멤버에 그룹 전체를 알림 + gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: mid.Hex(), + Body: gd.loadFull(), + Tag: []string{"GroupDocFull"}, + }) + gi.enterRoom(gd.Gid, mid) + + return nil +} + func (gi *groupInstant) Join(w http.ResponseWriter, r *http.Request) { var data struct { Gid primitive.ObjectID @@ -172,54 +197,25 @@ func (gi *groupInstant) Join(w http.ResponseWriter, r *http.Request) { return } - character := data.Character - gid := data.Gid - mid := data.Mid - - if gid.IsZero() || mid.IsZero() { - logger.Println("JoinParty failed. mid should be exist") + if data.Gid.IsZero() || data.Mid.IsZero() { + logger.Error("groupInstant.Join failed. gid or mid is zero") w.WriteHeader(http.StatusBadRequest) return } - gd, err := gi.find(gid) - if err != nil { + gd, err := gi.find(data.Gid) + if err != nil || gd == nil { + logger.Error("groupInstant.Join failed. gi find return err :", err) w.WriteHeader(http.StatusInternalServerError) return } - if gd == nil { - // 그룹이 없다. 실패 - w.WriteHeader(http.StatusBadRequest) - return - } - - // 내 정보 업데이트할 때에도 사용됨 - if memdoc, err := gd.addMember(mid, character); err == nil { - // 기존 유저에게 새 유저 알림 - gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gid.Hex(), - Body: map[string]any{ - gd.tid(mid): memdoc, - }, - Tag: []string{"MemberDocFull"}, - }) - - gi.enterRoom(gid, mid) - gi.rh.JSONSet(mid.Hex(), "$.instant", bson.M{"id": gd.strid()}) - - // 최초 입장이라면 새 멤버에 그룹 전체를 알림 - gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: mid.Hex(), - Body: gd.loadFull(), - Tag: []string{"GroupDocFull"}, - }) - } else if err != nil { - logger.Error("JoinParty failed :", err) + if err := gi.join(gd, data.Mid, data.Character); err != nil { + logger.Error("groupInstant.Join failed :", err) w.WriteHeader(http.StatusInternalServerError) + } else { + gocommon.MakeEncoder(w, r).Encode(gd.Count) } - - gocommon.MakeEncoder(w, r).Encode(gd.Count) } func (gi *groupInstant) Create(w http.ResponseWriter, r *http.Request) { @@ -262,6 +258,43 @@ func (gi *groupInstant) Delete(w http.ResponseWriter, r *http.Request) { } } +func (gi *groupInstant) leave(gd *instantDoc, mid primitive.ObjectID) error { + if err := gd.removeMember(mid); err != nil { + if err == errGroupAlreadyDestroyed { + // 정상 + gd.Count = 0 + return nil + } + return err + } + + gi.rh.JSONDel(mid.Hex(), "$.instant.id") + + // mid한테는 빈 GroupDocFull을 보낸다. 그러면 지워짐 + gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: mid.Hex(), + Body: bson.M{"_gid": gd.Gid}, + Tag: []string{"GroupDocFull", gd.strid()}, + }) + + // gid에는 제거 메시지 보냄 + gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "#" + gd.strid(), + Body: bson.M{ + gd.tid(mid): bson.M{}, + }, + Tag: []string{"MemberDocFull"}, + }) + + gi.leaveRoom(gd.Gid, mid) + + if gd.Count == 0 { + gd.rh.Del(gd.rh.Context(), gd.strid()).Result() + } + + return nil +} + func (gi *groupInstant) Leave(w http.ResponseWriter, r *http.Request) { var data struct { Gid primitive.ObjectID @@ -276,43 +309,78 @@ func (gi *groupInstant) Leave(w http.ResponseWriter, r *http.Request) { Gid: data.Gid, rh: gi.rh, } - if err := gd.removeMember(data.Mid); err != nil { - if err == errGroupAlreadyDestroyed { - // 정상 - gocommon.MakeEncoder(w, r).Encode(int64(0)) - return - } + if err := gi.leave(&gd, data.Mid); err != nil { logger.Println("groupInstant.Leave failed. gd.removeMember returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } - gi.rh.JSONDel(data.Mid.Hex(), "$.instant.id") + gocommon.MakeEncoder(w, r).Encode(gd.Count) +} - // mid한테는 빈 GroupDocFull을 보낸다. 그러면 지워짐 - gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: data.Mid.Hex(), - Body: bson.M{"_gid": data.Gid}, - Tag: []string{"GroupDocFull", gd.strid()}, - }) - - // gid에는 제거 메시지 보냄 - gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gd.strid(), - Body: bson.M{ - gd.tid(data.Mid): bson.M{}, - }, - Tag: []string{"MemberDocFull"}, - }) - - gi.leaveRoom(gd.Gid, data.Mid) - - if gd.Count == 0 { - gd.rh.Del(gd.rh.Context(), gd.strid()).Result() +func (gi *groupInstant) Merge(w http.ResponseWriter, r *http.Request) { + var data struct { + From primitive.ObjectID + Into primitive.ObjectID + Max int64 + } + if err := gocommon.MakeDecoder(r).Decode(&data); err != nil { + logger.Println("RemoveFromParty failed. Decode returns err :", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + // From에 있는 mid를 Into로 옮김 + gdinto, err := gi.find(data.Into) + if err != nil || gdinto == nil { + logger.Println("groupInstant.Merge failed. gd.getMembers returns err :", err) + w.WriteHeader(http.StatusInternalServerError) + return } - gocommon.MakeEncoder(w, r).Encode(gd.Count) + gdfrom := instantDoc{ + Gid: data.From, + rh: gi.rh, + } + fromMembers, err := gdfrom.getMembers() + if err != nil { + logger.Println("groupInstant.Merge failed. gd.getMembers returns err :", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + var movedmids []primitive.ObjectID + for mid, doc := range fromMembers { + gi.join(gdinto, mid, doc) + gi.leaveRoom(gdfrom.Gid, mid) + movedmids = append(movedmids, mid) + + if gdinto.Count == data.Max { + break + } + } + + if len(movedmids) == int(gdfrom.Count) { + gi.rh.JSONDel(gdfrom.strid(), "$") + } else { + for _, mid := range movedmids { + gdfrom.removeMember(mid) + + // gid에는 제거 메시지 보냄 + gi.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "#" + gdfrom.strid(), + Body: bson.M{ + gdfrom.tid(mid): bson.M{}, + }, + Tag: []string{"MemberDocFull"}, + }) + } + } + + gocommon.MakeEncoder(w, r).Encode(struct { + From int64 + Into int64 + }{From: gdfrom.Count, Into: gdinto.Count}) } func (gi *groupInstant) createInstantGroup(firstAcc primitive.ObjectID, firstChar primitive.M, instDoc primitive.M) (*instantDoc, error) {