local ipairs = ipairs local table = table local math = math local string = string local redisproxy = redisproxy local MsgPack = MsgPack local string_format = string.format local tonumber = tonumber local table_insert = table.insert local table_unpack = table.unpack local table_find = table.find local table_nums = table.nums local math_random = math.randomInt local _M = {} local function formatArray(tb) tb = tb or {} local t = {} for _, objectId in ipairs(tb) do t[tonumber(objectId)] = 1 end return t end local function checkFriendLimit(roleId) roleId = tonumber(roleId) local count = redisproxy:hlen(FRIEND_KEY:format(roleId)) return count < globalCsv.friendListLimit end local function addAndCheckApplyLimit(roleId, objId) roleId = tonumber(roleId) local dbKey = FRIEND_APPLY_KEY:format(roleId) local redret = redisproxy:pipelining(function (red) red:zadd(dbKey, skynet.timex(), objId) red:zremrangebyrank(dbKey, 0, -(globalCsv.friendApplyLimit + 1)) end) end local function checkBlackLimit(roleId) roleId = tonumber(roleId) local count = redisproxy:scard(FRIEND_BLACK_KEY:format(roleId)) return count < globalCsv.friendListLimit end local function getRoleInfo(roleId) local online, info = rpcRole(roleId, "friendSInfo") return online, info end local function getRoleAllInfo(roleId) local online, info = rpcRole(roleId, "friendInfo") return online, info end local function table_merge(tab1, tab2, filter) tab1 = tab1 or {} tab2 = tab2 or {} filter = filter or {} for k_, v_ in pairs(tab2) do if not filter[k_] then tab1[k_] = v_ end end return tab1 end function _M.searchRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local key = msg.key if not key then return end local objIds = {} local tempId = tonumber(key) if tempId then if redisproxy:exists(string_format("role:%d", tempId)) then objIds[tempId] = 1 end end local tempId = redisproxy:get(string_format("user:%s", string.upper(key))) if tempId then objIds[tonumber(tempId)] = 1 end objIds[roleId] = nil --不能有自己 local searchList = {} for objId, _ in pairs(objIds) do local online, info = getRoleInfo(objId) local redret = redisproxy:pipelining(function (red) red:hexists(FRIEND_KEY:format(roleId), objId) red:zscore(FRIEND_APPLY_KEY:format(objId), roleId) red:sismember(FRIEND_BLACK_KEY:format(roleId), objId) end) local isFriend = redret[1] == 1 and 1 or nil local hadApply = redret[2] and 1 or nil local inBlack = redret[3] == 1 and 1 or nil table.insert(searchList, table_merge({ roleId = objId, online = online, isFriend = isFriend, hadApply = hadApply, inBlack = inBlack, }, info, { })) end role:mylog("role_action", {desc = "searchFriend"}) SendPacket(actionCodes.Friend_searchRpc, MsgPack.pack({searchList = searchList})) return true end function _M.applyRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local objectId = msg.roleId if objectId == roleId then return end local result = nil local redret = redisproxy:pipelining(function (red) red:exists(string_format("role:%d", objectId)) red:hexists(FRIEND_KEY:format(roleId), objectId) red:zscore(FRIEND_APPLY_KEY:format(objectId), roleId) red:sismember(FRIEND_BLACK_KEY:format(objectId), roleId) red:sismember(FRIEND_BLACK_KEY:format(roleId), objectId) red:hlen(FRIEND_KEY:format(roleId)) red:hlen(FRIEND_KEY:format(objectId)) end) -- 玩家id不存在 if not result and redret[1] ~= 1 then result = 1 end -- 已经有这个好友 if not result and redret[2] == 1 then result = 2 end -- 已经申请 if not result and redret[3] then result = 3 end -- 对方把你拉黑 if not result and redret[4] == 1 then result = 4 end -- 你把对方拉黑了 if not result and redret[5] == 1 then result = 5 end -- 自己好友已经满 if not result and redret[6] >= globalCsv.friendListLimit then result = 6 end -- 对方的好友已满 if not result and redret[7] >= globalCsv.friendListLimit then result = 7 end if not result then addAndCheckApplyLimit(objectId, roleId) local myInfo = role:friendSInfo() myInfo.roleId = roleId myInfo.online = true myInfo.hadApply = true role:mylog("role_action", {desc = "addFriend", int1 = 1}) rpcRole(objectId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({newApply = 1, info = {myInfo}})) -- 通知对方 end SendPacket(actionCodes.Friend_applyRpc, MsgPack.pack({result = result})) return true end function _M.applyListRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local applyList = {} local friends = redisproxy:zrange(FRIEND_APPLY_KEY:format(roleId), 0, -1) for _ , id in pairs(friends) do id = tonumber(id) local online, info = getRoleInfo(id) table.insert(applyList, table_merge({ roleId = id, online = online }, info, { })) end SendPacket(actionCodes.Friend_applyListRpc, MsgPack.pack({list = applyList})) return true end local function checkHandleApply(roleId, objectId, needAddNew) needAddNew = needAddNew or 0 local redret = redisproxy:pipelining(function (red) red:hlen(FRIEND_KEY:format(roleId)) red:hlen(FRIEND_KEY:format(objectId)) red:sismember(FRIEND_BLACK_KEY:format(objectId), roleId) red:sismember(FRIEND_BLACK_KEY:format(roleId), objectId) end) --自己好友满了 if (redret[1] + needAddNew) >= globalCsv.friendListLimit then return 1 end -- 对方好友满了 if redret[2] >= globalCsv.friendListLimit then return 2 end -- 对方把你拉黑 if redret[3] == 1 then return 3 end -- 你把对方拉黑了 if redret[4] == 1 then return 4 end return nil, redret[1] end function _M.handleApplyRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local cmd = msg.cmd local newTag = MsgPack.pack({ skynet.timex(), 1}) local result = nil if cmd == 1 then --同意 local objectId = msg.roleId if not redisproxy:zscore(FRIEND_APPLY_KEY:format(roleId), objectId) then return end local curCount result, curCount = checkHandleApply(roleId, objectId) if not result then redisproxy:pipelining(function (red) red:ZREM(FRIEND_APPLY_KEY:format(roleId), objectId) red:ZREM(FRIEND_APPLY_KEY:format(objectId), roleId) red:hsetnx(FRIEND_KEY:format(roleId), objectId, newTag) red:hsetnx(FRIEND_KEY:format(objectId), roleId, newTag)--告知对放有新好友 end) local myInfo = role:friendSInfo() myInfo.online = true myInfo.roleId = roleId myInfo.isFriend = 1 local redret = redisproxy:pipelining(function (red) red:sismember(FRIEND_POINT:format(objectId), roleId) red:sismember(FRIEND_POINT:format(roleId), objectId) end) myInfo.pGet = redret[1] == 1 and 1 or nil rpcRole(objectId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({newFriend = 1, info = {myInfo}})) -- 通知对方 local online , otherInfo = getRoleInfo(objectId) otherInfo.roleId = objectId otherInfo.online = online otherInfo.isFriend = true otherInfo.pGet = redret[2] == 1 and 1 or nil SendPacket(actionCodes.Friend_updateProperty, MsgPack.pack({newFriend = 1, info = {otherInfo}})) role:log("friend_opt", { friend_opt_type = 100, -- 好友操作类型,见枚举表中 好友操作类型枚举表 friend_roleid = objectId, -- 好友账户下的角色id friend_cnt = curCount + 1, -- 操作后好友数量 }) role:mylog("role_action", {desc = "addFriend", int1 = 1}) end elseif cmd == 0 then -- 不同意 local objectId = msg.roleId if not redisproxy:zscore(FRIEND_APPLY_KEY:format(roleId), objectId) then return end redisproxy:ZREM(FRIEND_APPLY_KEY:format(roleId), objectId) elseif cmd == 2 then -- 一键拒绝 redisproxy:del(FRIEND_APPLY_KEY:format(roleId)) elseif cmd == 3 then -- 一键同意 local redret = redisproxy:pipelining(function(red) red:zrange(FRIEND_APPLY_KEY:format(roleId), 0, -1) red:SMEMBERS(FRIEND_POINT:format(roleId)) end) local allIds = redret[1] local fpoint = formatArray(redret[2]) local needAdd = {} local needAddMy = {} local needAddInfo = {} for _, objId in ipairs(allIds) do objId = tonumber(objId) local cr, curCount = checkHandleApply(roleId, objId, #needAdd) if not cr then table.insert(needAdd, objId) table.insert(needAddMy, objId) table.insert(needAddMy, newTag) local online, otherInfo = getRoleInfo(objId) otherInfo.online = true otherInfo.roleId = objId otherInfo.isFriend = true otherInfo.pGet = fpoint[objId] and 1 or nil table.insert(needAddInfo, otherInfo) role:log("friend_opt", { friend_opt_type = 100, -- 好友操作类型,见枚举表中 好友操作类型枚举表 friend_roleid = objId, -- 好友账户下的角色id friend_cnt = curCount + 1, -- 操作后好友数量 }) end end redisproxy:pipelining(function (red) if next(needAdd) then red:ZREM(FRIEND_APPLY_KEY:format(roleId), table_unpack(needAdd)) for _, objectId in pairs(needAdd) do red:ZREM(FRIEND_APPLY_KEY:format(objectId), roleId) red:hsetnx(FRIEND_KEY:format(objectId), roleId, newTag)--告知对放有新好友 end end if next(needAddMy) then red:HMSET(FRIEND_KEY:format(roleId), table_unpack(needAddMy)) end end) local myInfo = role:friendSInfo() myInfo.roleId = roleId myInfo.online = true myInfo.isFriend = 1 local giveFP = role.dailyData:getProperty("giveFP") for _, objectId in pairs(needAdd) do myInfo.pGet = giveFP[objectId] rpcRole(objectId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({newFriend = 1, info = {myInfo}})) -- 通知对方 end if next(needAdd) then role:mylog("role_action", {desc = "addFriend", int1 = #needAdd}) SendPacket(actionCodes.Friend_updateProperty, MsgPack.pack({newFriend = 1, info = needAddInfo})) else result = 1 end else --不存在 return end SendPacket(actionCodes.Friend_handleApplyRpc, MsgPack.pack({result = result})) return true end function _M.listRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local friendList = {} local redret = redisproxy:pipelining(function (red) red:hgetall(FRIEND_KEY:format(roleId)) red:SMEMBERS(FRIEND_POINT:format(roleId)) end) local friends = redret[1] local fpoint = formatArray(redret[2]) local hadGet = role.dailyData:getProperty("getFP") local hadGive = role.dailyData:getProperty("giveFP") local clearRed = {} for i = 1, #friends, 2 do local id = friends[i] local data = friends[i + 1] local friendInfo = MsgPack.unpack(data) id = tonumber(id) local online, info = getRoleInfo(id) local roleInfo = { roleId = id, online = online, addTime = friendInfo[1], isNew = friendInfo[2], pGive = hadGive[id], pGet = hadGet[id] and -1 or (fpoint[id] and 1 or nil) } roleInfo = table_merge(roleInfo, info, {}) friendList[#friendList + 1] = roleInfo if friendInfo[2] then friendInfo[2] = nil --清除新好友标记 clearRed[#clearRed + 1] = id clearRed[#clearRed + 1] = MsgPack.pack(friendInfo) end end if next(clearRed) then redisproxy:hmset(FRIEND_KEY:format(roleId), table_unpack(clearRed)) --清除新好友标记 role:checkTaskEnter("AddFriend", {count = #friendList}) end SendPacket(actionCodes.Friend_listRpc, MsgPack.pack({list = friendList})) return true end function _M.deleteRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local objectId = msg.roleId if not redisproxy:exists(string_format("role:%d", objectId)) then return end -- 是否在好友列表中 if redisproxy:hexists(FRIEND_KEY:format(roleId), objectId) then redisproxy:pipelining(function (red) red:hdel(FRIEND_KEY:format(roleId), objectId) red:hdel(FRIEND_KEY:format(objectId), roleId) red:ZREM(FRIEND_APPLY_KEY:format(roleId), objectId) red:ZREM(FRIEND_APPLY_KEY:format(objectId), roleId) end) end rpcRole(objectId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({deleteFriend = 1, roleId = roleId})) role:log("friend_opt", { friend_opt_type = 200, -- 好友操作类型,见枚举表中 好友操作类型枚举表 friend_roleid = objectId, -- 好友账户下的角色id friend_cnt = 0, -- 操作后好友数量 }) SendPacket(actionCodes.Friend_deleteRpc, MsgPack.pack("")) return true end function _M.blockRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local cmd = msg.cmd local objectId = msg.roleId if not redisproxy:exists(string_format("role:%d", objectId)) then return end local result = nil if cmd == 1 then -- 黑名单满了 if not result and not checkBlackLimit(roleId) then result = 1 end -- 删除好友 if not result then redisproxy:pipelining(function (red) red:hdel(FRIEND_KEY:format(roleId), objectId) red:hdel(FRIEND_KEY:format(objectId), roleId) red:ZREM(FRIEND_APPLY_KEY:format(roleId), objectId) red:ZREM(FRIEND_APPLY_KEY:format(objectId), roleId) red:sadd(FRIEND_BLACK_KEY:format(roleId), objectId) end) rpcRole(objectId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({deleteFriend = 1, roleId = roleId})) role:log("friend_opt", { friend_opt_type = 300, -- 好友操作类型,见枚举表中 好友操作类型枚举表 friend_roleid = objectId, -- 好友账户下的角色id friend_cnt = 0, -- 操作后好友数量 }) end elseif cmd == 2 then redisproxy:SREM(FRIEND_BLACK_KEY:format(roleId), objectId) else return end SendPacket(actionCodes.Friend_blockRpc, MsgPack.pack({result = result})) return true end function _M.blockListRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local blockList = {} local friends = redisproxy:SMEMBERS(FRIEND_BLACK_KEY:format(roleId)) for _ , id in pairs(friends) do id = tonumber(id) local online, info = getRoleInfo(id) table.insert(blockList, table_merge({ roleId = id, online = online }, info, { })) end SendPacket(actionCodes.Friend_blockListRpc, MsgPack.pack({list = blockList})) return true end function _M.infoRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local objectId = msg.roleId if not redisproxy:exists(string_format("role:%d", objectId)) then return end local online, info = getRoleAllInfo(objectId) local redret = redisproxy:pipelining(function (red) red:hexists(FRIEND_KEY:format(roleId), objectId) red:zscore(FRIEND_APPLY_KEY:format(objectId), roleId) red:sismember(FRIEND_BLACK_KEY:format(roleId), objectId) end) local isFriend = redret[1] == 1 and 1 or nil local hadApply = redret[2] == 1 and 1 or nil local inBlack = redret[3] == 1 and 1 or nil local objInfo = table_merge({ roleId = objectId, online = online, isFriend = isFriend, hadApply = hadApply, inBlack = inBlack, }, info, { }) SendPacket(actionCodes.Friend_infoRpc, MsgPack.pack({info = objInfo})) return true end function _M.pointRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local cmd = msg.cmd local result = nil local reward = {} if cmd == 1 then -- 赠送 local objId = msg.roleId local giveP = role.dailyData:getProperty("giveFP") if not result and giveP[objId] then result = 1 end if not result and not redisproxy:hexists(FRIEND_KEY:format(roleId), objId) then result = 2 end if not result then redisproxy:sadd(FRIEND_POINT:format(objId), roleId) giveP[objId] = 1 role.dailyData:updateProperty({field = "giveFP", value = giveP}) role:checkTaskEnter("GiveFriendP", {count = 1}) rpcRole(objId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({newPoint = 1, roleId = roleId})) role:mylog("role_action", {desc = "giveFPoint", int1 = 1}) end elseif cmd == 2 then -- 领取 local objId = msg.roleId local getP = role.dailyData:getProperty("getFP") if not result and table.numbers(getP) >= globalCsv.friendPointLimit then result = 1 end if not result and getP[objId] then result = 2 end if not redisproxy:sismember(FRIEND_POINT:format(roleId), objId) then result = 3 end if not result then getP[objId] = 1 reward = role:award({[ItemId.FriendPoint] = 1}, {log = {desc = "friendPoint"}}) role.dailyData:updateProperty({field = "getFP", value = getP}) role:checkTaskEnter("GetFriendP", {count = 1}) role:mylog("role_action", {desc = "getFPoint", int1 = 1}) end elseif cmd == 3 then -- 一键赠送领取 -- 赠送 local giveP = role.dailyData:getProperty("giveFP") local friends = redisproxy:hgetall(FRIEND_KEY:format(roleId)) local change = 0 redisproxy:pipelining(function(red) for i = 1, #friends , 2 do local objId = tonumber(friends[i]) if not giveP[objId] then giveP[objId] = 1 change = change + 1 red:sadd(FRIEND_POINT:format(objId), roleId) rpcRole(objId, "SendPacket", actionCodes.Friend_updateProperty, MsgPack.pack({newPoint = 1, roleId = roleId})) end end end) if change > 0 then role.dailyData:updateProperty({field = "giveFP", value = giveP}) role:checkTaskEnter("GiveFriendP", {count = change}) role:mylog("role_action", {desc = "giveFPoint", int1 = change}) else result = 1 end --领取 local getP = role.dailyData:getProperty("getFP") local curCount = table.numbers(getP) local getCount = 0 if curCount < globalCsv.friendPointLimit then for _, objId in pairs(redisproxy:SMEMBERS(FRIEND_POINT:format(roleId))) do local objId = tonumber(objId) if not getP[objId] then getCount = getCount + 1 curCount = curCount + 1 getP[objId] = 1 if curCount >= globalCsv.friendPointLimit then break end end end if getCount > 0 then reward = role:award({[ItemId.FriendPoint] = getCount}, {log = {desc = "friendPoint"}}) role.dailyData:updateProperty({field = "getFP", value = getP}) role:checkTaskEnter("GetFriendP", {count = getCount}) role:mylog("role_action", {desc = "getFPoint", int1 = getCount}) else result = (result or 0) + 2 end end else return end SendPacket(actionCodes.Friend_pointRpc, MsgPack.pack({result = result, reward = reward})) return true end function _M.randomRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local redret = redisproxy:pipelining(function (red) red:hgetall(FRIEND_KEY:format(roleId)) red:zrevrange(FRIEND_RECOMMEND, 0, globalCsv.friendRecommendLimit + globalCsv.friendListLimit + 10) --扩充10个 red:SMEMBERS(FRIEND_BLACK_KEY:format(roleId)) end) local friends = {} for i = 1, #redret[1], 2 do friends[tonumber(redret[1][i])] = redret[1][i + 1] end local newList = redret[2] local hadBlack = formatArray(redret[3]) local needRoleIds = {} for _, newId in pairs(newList) do local numNewId = tonumber(newId) if numNewId ~= roleId and not friends[numNewId] and not hadBlack[numNewId] then table.insert(needRoleIds, numNewId) end end local randomRoles = {} local redret = redisproxy:pipelining(function (red) for _, objId in ipairs(needRoleIds) do red:zscore(FRIEND_APPLY_KEY:format(objId), roleId) end end) for idx, objId in ipairs(needRoleIds) do if not redret[idx] then local online, info = getRoleInfo(objId) table.insert(randomRoles, table_merge({ roleId = objId, online = online, hadApply = hadApply, inBlack = inBlack, }, info, { })) if #randomRoles >= globalCsv.friendRecommendLimit then break end end end SendPacket(actionCodes.Friend_randomRpc, MsgPack.pack({list = randomRoles})) return true end function _M.battleInfoRpc(agent, data) local role = agent.role local roleId = role:getProperty("id") local msg = MsgPack.unpack(data) local objId = msg.roleId local online, matchInfo = rpcRole(objId, "friendBattleInfo") SendPacket(actionCodes.Friend_battleInfoRpc, MsgPack.pack({matchInfo = matchInfo})) return true end return _M