using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sog; using ProtoCSStruct; using Google.Protobuf.Collections; namespace Friend { public class RecommenderFriendSearch { public bool isCacheStructFriendOne; public long index; } /// /// 推荐好友 /// public static class FriendRecommenderSvc { // 最多一次推荐多少个 private const int MaxRecommenderFriendCount = 20; //最多获取1000个好友数量用来随机 private static int m_MaxOutlistCount = 1000; public static void OnCliFriendQueryInfoByUidReq(uint remoteAppID, StructPacket packet) { PlayerInfoFriend player = FriendServerUtils.GetPlayerTableOp().GetPlayerInfo(packet.ObjectID); if (player == null) { TraceLog.Error("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} no player", packet.ObjectID); return; } ref FriendCacheInfoStruct friendinfo = ref FriendOp.GetFriendInfoByUid(player.UserID); if (friendinfo.IsNull()) { TraceLog.Error("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} no friendcacheinfo", player.UserID); return; } ref CSFriendQueryInfoByUidReq req = ref packet.GetMessage(); TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} target {1}", player.UserID, req.TargetUid); CSFriendQueryInfoByUidRes res = new CSFriendQueryInfoByUidRes {TargetUid = req.TargetUid}; if (req.TargetUid == player.UserID) { FriendUtils.CopySelfToFriendOne(ref friendinfo.Self, ref res.Info); FriendServerUtils.GetPacketSender().SendToServerByID(player.WorldServerID, (int) CSGameMsgID.FriendQueryInfoByUidRes, ref res, player.UserID); return; } if (req.TargetUid < 10000) { TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} invliad target {1}" , player.UserID, req.TargetUid); return; } if (FriendUtils.IsPlayerBelongThisServer(req.TargetUid)) { ref FriendCacheInfoStruct targetInfo = ref FriendOp.GetFriendInfoByUid(req.TargetUid); // 内存里没有,向db查询 if (targetInfo.IsNull()) { TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} target {1} query DB" , player.UserID, req.TargetUid); FriendCacheSvc.SendDBOrFriendSvrQueryFriendInfo(req.TargetUid, player.UserID, QueryFriendInfoCacheReason.QueryByUid); return; } //不是同一个LogicWorld if (!FriendServerUtils.CheckIsSameLogicWorld(player.realmId, targetInfo.Self.RealmId)) { TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} {1} target {1} {2} not SameLogicWorld" , player.UserID, player.realmId, targetInfo.Self.Uid, targetInfo.Self.RealmId); return; } FriendUtils.CopySelfToFriendOne(ref targetInfo.Self, ref res.Info); FriendServerUtils.GetPacketSender().SendToServerByID(player.WorldServerID, (int) CSGameMsgID.FriendQueryInfoByUidRes, ref res, player.UserID); return; } ref RemoteFriendOneStruct remoteFriend = ref FriendOp.GetRemoteFriendOneByUid(req.TargetUid); if (remoteFriend.IsNull()) { TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} target {1} query other FriendSvr" , player.UserID, req.TargetUid); FriendCacheSvc.SendFriendSvrQueryFriendInfoCache(req.TargetUid, player.UserID, QueryFriendInfoCacheReason.QueryByUid); return; } //不是同一个LogicWorld if(!FriendServerUtils.CheckIsSameLogicWorld(remoteFriend.RealmId, player.realmId)) { TraceLog.Trace("FriendRecommenderSvc.OnCliFriendQueryInfoByUidReq uid {0} {1} target {1} {2} not SameLogicWorld" , player.UserID, player.realmId, remoteFriend.Uid, remoteFriend.RealmId); return; } FriendUtils.CopyRemoteFriendToDBFriend(ref remoteFriend, ref res.Info); FriendServerUtils.GetPacketSender().SendToServerByID(player.WorldServerID, (int) CSGameMsgID.FriendQueryInfoByUidRes, ref res, player.UserID); } public static void OnQueryFriendInfoCacheResFromDB(ref SSQueryFriendInfoCacheRes ssres) { // 其他服务器发来的请求, 转到该服务器处理 if (! FriendUtils.IsPlayerBelongThisServer(ssres.FromUid)) { FriendServerUtils.GetPacketSender().SendToFriendServer((int)SSGameMsgID.QueryFriendInfoCacheRes, ref ssres, ssres.FromUid); return; } PlayerInfoFriend player = FriendServerUtils.GetPlayerTableOp().GetPlayerInfo(ssres.FromUid); if (player == null) { TraceLog.Error("FriendRecommenderSvc.OnQueryFriendInfoCacheResFromDB can not find player uid {0}" , ssres.FromUid); return; } var res = new CSFriendQueryInfoByUidRes{TargetUid = ssres.Uid}; // uid不存在 if (ssres.Self.Uid == 0) { TraceLog.Error("FriendRecommenderSvc.OnQueryFriendInfoCacheResFromDB uid {0} target {1} not in db" , ssres.FromUid, ssres.Uid); res.Ret = -1; } else { TraceLog.Trace("FriendRecommenderSvc.OnQueryFriendInfoCacheResFromDB uid {0} target {1} query succ" , ssres.FromUid, ssres.Uid); if(FriendServerUtils.CheckIsSameLogicWorld(player.realmId, ssres.Self.RealmId)) { FriendUtils.CopySelfToFriendOne(ref ssres.Self, ref res.Info); res.MainlineHeros = ssres.Self.MainlineHeros; } else { res.Ret = -1; TraceLog.Error("FriendRecommenderSvc.OnQueryFriendInfoCacheResFromDB uid {0} {1} target {2} {3} not in same logicWorld" , player.UserID, player.realmId, ssres.Self.Uid, ssres.Self.RealmId); } } FriendServerUtils.GetPacketSender().SendToServerByID(player.WorldServerID, (int) CSGameMsgID.FriendQueryInfoByUidRes, ref res, player.UserID); } public static void OnCliFriendGetRecommenderReq(uint remoteAppID, StructPacket packet) { PlayerInfoFriend player = FriendServerUtils.GetPlayerTableOp().GetPlayerInfo(packet.ObjectID); if (player == null) { TraceLog.Error("FriendRecommenderSvc.OnCliFriendGetRecommenderReq uid {0} no player", packet.ObjectID); return; } if (player.roleBase.Level == 0) { TraceLog.Error("FriendRecommenderSvc.OnCliFriendGetRecommenderReq uid {0} level {1}", player.UserID, player.roleBase.Level); return; } ref FriendCacheInfoStruct selfInfo = ref FriendOp.GetFriendInfoByUid(player.UserID); if (selfInfo.IsNull()) { return; } CSFriendGetRecommenderRes res = new CSFriendGetRecommenderRes(); HashSet visited = new HashSet(); int level = player.roleBase.Level; int need = MaxRecommenderFriendCount; int down = 1, up = 1; int step = 0; while ((down >= 0 || up <= FriendRecommendInfo.MaxLevel) && need > 0) { if (step == 0) { GenRecommendByLevel(ref selfInfo, level, need, visited, ref res); need = MaxRecommenderFriendCount - res.List.Count; } else { // 向下查找 down = level - step * 10; if (down >= 0 && need > 0) { GenRecommendByLevel(ref selfInfo, down, need, visited, ref res); need = MaxRecommenderFriendCount - res.List.Count; } // 向上查找 up = level + step * 10; if (up <= FriendRecommendInfo.MaxLevel && need > 0) { GenRecommendByLevel(ref selfInfo, up, need, visited, ref res); need = MaxRecommenderFriendCount - res.List.Count; } } step++; } TraceLog.Trace("FriendRecommenderSvc.OnCliFriendGetRecommenderReq uid {0} list count {1}" , player.UserID, res.List.Count); FriendServerUtils.GetPacketSender().SendToServerByID(remoteAppID, (int)CSGameMsgID.FriendGetRecommenderRes,ref res, player.UserID); } private static void GenRecommendByLevel(ref FriendCacheInfoStruct selfInfo, int level, int need, HashSet visited, ref CSFriendGetRecommenderRes res) { if (need <= 0) { return; } var uidList = FriendRecommenTableSvc.GetRecommendUid(ref selfInfo, level, need); if (uidList == null || uidList.Count == 0) { return; } for (int i = 0; i < uidList.Count; i++) { long uid = uidList[i]; // 为了方便随机使用了List, 为了提高效率每分钟清理一次cache中不属于当前等级的玩家uid // 在游戏初期升级很快的时候可能会出现同一个玩家在两个不同等级的cache中, 要去重 if (!visited.Contains(uid)) { visited.Add(uid); ref FriendCacheInfoStruct friendinfo = ref FriendOp.GetFriendInfoByUid(uid); if (!friendinfo.IsNull()) { DBFriendOne friendOne = new DBFriendOne(); FriendUtils.CopySelfToFriendOne(ref friendinfo.Self, ref friendOne); res.List.Add(ref friendOne); } } } } /// /// 好友的好友,但是不是我的好友,暂时先不考虑效率,直接循环 /// /// /// /// private static void CalcRecommenderByFriendOfFriend(PlayerInfoFriend player, ref FriendCacheInfoStruct friendinfo, Dictionary outlist) { TraceLog.Trace("FriendRecommenderSvc.CalcRecommenderByFriendOfFriend player uid {0} list count {1}" , player.UserID, outlist.Count); if (outlist.Count >= m_MaxOutlistCount) { return; } //遍历我的所有好友 for (int i = 0;i < friendinfo.FriendList.iCount;i++) { ref DBFriendOneStruct one = ref FriendOp.GetFriendOneByIndex(ref friendinfo, i); //获取我的好友的内存cache信息 ref FriendCacheInfoStruct myfriendCache = ref FriendOp.GetFriendInfoByUid(one.oneFriend.Uid); //没有就算了 if (myfriendCache.IsNull()) { continue; } //我的好友的好友,有可能有我自己哦 for(int j=0;i < myfriendCache.FriendList.iCount;i++) { ref DBFriendOneStruct oneStruct = ref FriendOp.GetFriendOneByIndex(ref myfriendCache, j); //自己 if(oneStruct.oneFriend.Uid == player.UserID) { continue; } ref FriendCacheInfoStruct myfriendfriendCache = ref FriendOp.GetFriendInfoByUid(oneStruct.oneFriend.Uid); if (myfriendfriendCache.IsNull()) { continue; } //好友是不是满了 if (FriendSvc.IsFriendListFull(ref myfriendfriendCache)) { continue; } //我的好友列表里有这个家伙 if (! FriendOp.GetFriendOneByUid(friendinfo.Self.Uid, oneStruct.oneFriend.Uid).IsNull()) { continue; } //我的陌生人列表里有这个家伙 if(!FriendOp.GetStrangerByUid(friendinfo.Self.Uid, oneStruct.oneFriend.Uid).IsNull()) { continue; } if (outlist.ContainsKey(oneStruct.oneFriend.Uid) == false) { outlist.Add(oneStruct.oneFriend.Uid,new RecommenderFriendSearch(){isCacheStructFriendOne = true,index = oneStruct.GetObjectID()}); } if (outlist.Count >= m_MaxOutlistCount) { return; } } } } private static void CalcRecommenderRandom(PlayerInfoFriend player, ref FriendCacheInfoStruct friendinfo, Dictionary outlist) { TraceLog.Trace("FriendRecommenderSvc.CalcRecommenderRandom player uid {0} list count {1}" , player.UserID, outlist.Count); if (outlist.Count >= m_MaxOutlistCount) { return; } var listUpdate = FriendInfoCache.m_selfIDMap; foreach (var i in listUpdate) { ref FriendCacheInfoStruct cache = ref FriendInfoCache.m_cacheStructFriendInfo.GetByIndex(i.Value); if (cache.IsNull()) { continue; } if(cache.Self.Uid == player.UserID) { continue; } //好友是不是满了 if (FriendSvc.IsFriendListFull(ref cache)) { continue; } //我的好友列表里有这个家伙 if (!FriendOp.GetFriendOneByUid(friendinfo.Self.Uid, cache.Self.Uid).IsNull()) { continue; } //我的陌生人列表里有这个家伙 if (!FriendOp.GetStrangerByUid(friendinfo.Self.Uid, cache.Self.Uid).IsNull()) { continue; } if (outlist.ContainsKey(cache.Self.Uid) == false) { outlist.Add(cache.Self.Uid, new RecommenderFriendSearch(){isCacheStructFriendOne = false,index = cache.GetObjectID()}); } if (outlist.Count >= m_MaxOutlistCount) { return; } } } private static void AddToList(ref CSFriendGetRecommenderRes res, Dictionary outlist) { List randList = new List(); randList.AddRange(outlist.Keys); //简单随机一下 while (res.List.Count < MaxRecommenderFriendCount) { if (outlist.Count == 0) { break; } int randomIndex = FriendServerUtils.GetApp().Rand.Next(randList.Count); long uid = randList[randomIndex]; randList.RemoveAt(randomIndex); if (outlist.TryGetValue(uid, out var value)) { if (value.isCacheStructFriendOne) { ref DBFriendOneStruct one = ref FriendInfoCache.m_cacheStructFriendOne.GetByIndex(value.index); if (one.IsNull() == false) { res.List.Add(ref one.oneFriend); } } else { ref FriendCacheInfoStruct oneInfo = ref FriendInfoCache.m_cacheStructFriendInfo.GetByIndex(value.index); if (oneInfo.IsNull() == false) { DBFriendOne friendOne = new DBFriendOne(); FriendUtils.CopySelfToFriendOne(ref oneInfo.Self, ref friendOne); res.List.Add(ref friendOne); } } } } } } }