using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; using PARTY_GUID = System.String; using USER_GUID = System.String; namespace GameServer; public class PartyInviteAction : EntityActionBase { public PartyInviteAction(PersonalParty owner) : base(owner) { } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } public override void onClear() { return; } public async Task<(Result result, Dictionary? err_invites)> inviteParty(List invite_users) { var result = new Result(); var personal_party = getOwner() as PersonalParty; NullReferenceCheckHelper.throwIfNull(personal_party, () => $"personal_party is null !!!"); var owner = getOwner().getRootParent() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); // 1. 파티 정보 획득 var global_party = GameServerApp.getServerLogic().findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(global_party, () => $"global_party is null !!!"); var global_party_action = global_party.getEntityAction(); NullReferenceCheckHelper.throwIfNull(global_party_action, () => $"global_party_action is null !!!"); var party = global_party_action.getGlobalPartyDetail(personal_party.getPartyGuid()); // 2. 초대 조건 체크 var check_invite_condition = await checkInviteConditions(party, invite_users); NullReferenceCheckHelper.throwIfNull(check_invite_condition.failed_invites, () => $"check_invite_condition.failed_invites is null !!!"); var err_invites = inputInvitePartyErrorMembers(check_invite_condition.failed_invites); NullReferenceCheckHelper.throwIfNull(err_invites, () => $"err_invites is null !!!"); if (check_invite_condition.result.isFail() || check_invite_condition.failed_invites.Count == invite_users.Count) { return (check_invite_condition.result, err_invites); } // 3. 파티 정보가 없으면 새로 생성 if (null == party) { (result, party) = await createParty(owner, personal_party.getPartyGuid(), global_party_action); if (result.isFail()) return (result, err_invites); ArgumentNullException.ThrowIfNull(party); } // 4. 초대하기 var invite_party_send_action = party.getEntityAction(); NullReferenceCheckHelper.throwIfNull(invite_party_send_action, () => $"invite_party_send_action is null !!!"); result = await invite_party_send_action.sendInviteParty(check_invite_condition.success_invites); return (result, err_invites); } private async Task<(Result result, GlobalPartyDetail? detail)> createParty(Player owner, PARTY_GUID party_guid, GlobalPartyAction global_party_action) { // 1. LoginCache 갱신 var user_login_cache_request = PartyHelper.getOwnUserLoginCacheRequest(owner); var user_login_cache = user_login_cache_request?.getLoginCache(); NullReferenceCheckHelper.throwIfNull(user_login_cache_request, () => $"user_login_cache_request is null !!!"); NullReferenceCheckHelper.throwIfNull(user_login_cache, () => $"user_login_cache is null !!!"); user_login_cache.PartyGuid = party_guid; var result = await user_login_cache_request.upsertLogin(); if (result.isFail()) { var err_msg = $"Failed to set party guid to user attribute !!! : {nameof(inviteParty)}"; result.setFail(ServerErrorCode.PartyCannotSetGuid, err_msg); Log.getLogger().error(err_msg); return (result, null); } // 3. party 신규 생성 result = await global_party_action.createParty(party_guid, owner.getUserGuid(), owner.getUserNickname()); if (result.isFail()) return (result, null); var detail = global_party_action.getGlobalPartyDetail(party_guid); ArgumentNullException.ThrowIfNull(detail); return (result, detail); } private async Task<(Result result, Dictionary? success_invites, Dictionary? failed_invites)> checkInviteConditions(GlobalPartyDetail? party, IReadOnlyList invite_users) { var result = new Result(); string err_msg; var owner = getOwner().getRootParent() as Player; ArgumentNullException.ThrowIfNull(owner); // 1. 파티장 확인 var party_attribute = party?.getEntityAttribute(); if (party != null && party_attribute != null && owner.getUserGuid() != party_attribute.PartyLeaderCharGuid) { err_msg = $"Failed to invite users !!! : not party leader - {owner.getUserGuid()}"; result.setFail(ServerErrorCode.NotPartyLeader,err_msg); Log.getLogger().error(err_msg); return (result, null, null); } // 2. 초대 인원수 확인 var party_member_count = party?.getEntityAttribute()?.getPartyMemberCount() ?? 0; if (party != null && party_member_count >= MetaHelper.GameConfigMeta.MaxPartyMembers) { err_msg = $"Failed to invite user !!! : party is full - member count : {party_member_count}"; result.set(ServerErrorCode.PartyIsFull, err_msg); Log.getLogger().error(err_msg); return (result, null, null); } // 3. 초대할 유저 상태 체크 var check_invitee_condition = await checkInviteesCondition(party, invite_users); ArgumentNullException.ThrowIfNull(check_invitee_condition); return (result, check_invitee_condition.success_invites, check_invitee_condition.failed_invites); } private async Task<(Dictionary success_invites, Dictionary failed_invites)> checkInviteesCondition(GlobalPartyDetail? party, IReadOnlyList invite_users) { var success_invites = new Dictionary(); var failed_invites = new Dictionary(); var owner = getOwner().getRootParent() as UserBase; ArgumentNullException.ThrowIfNull(owner); foreach (var invitee in invite_users) { var check = await checkCondition(party, owner, invitee); if (! check.isSuccess) { failed_invites.Add(invitee, check.err_code); continue; } success_invites.Add(invitee, check.current_server); } return (success_invites, failed_invites); } private async Task<(bool isSuccess, ServerErrorCode err_code, string current_server)> checkCondition(GlobalPartyDetail? party, UserBase? owner, USER_GUID invitee) { NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var login_cache = await PartyHelper.getOtherUserLoginCache(owner, invitee); // 3-1. login 체크 if (null == login_cache) { return (false, ServerErrorCode.TargetUserNotLogIn, string.Empty); } // 3-2. concert 체크 var location_action = owner.getEntityAction(); ArgumentNullException.ThrowIfNull(location_action); if (location_action.isInConcert()) { return (false, ServerErrorCode.InvalidInvitePlace, string.Empty); } // 3-3. user block 체크 ( invitor -> invitee ) var block_action = owner.getEntityAction(); ArgumentNullException.ThrowIfNull(block_action); if (block_action.isBlockUser(invitee)) { return (false, ServerErrorCode.BlockedByOther, string.Empty); } // 3-4. user block 체크 ( invitee -> invitor ) var is_block = await block_action.amIBockedFromOthers(invitee); if (is_block.reuslt.isFail() || is_block.isBlock) { return (false, ServerErrorCode.BlockedFromTarget, string.Empty); } // 3-5. 이미 보낸 초대자 인지 체크 var send_action = party?.getEntityAction(); if (null != party) { var check_send = await (send_action?.isExistCheck(invitee) ?? Task.FromResult(false)); if (check_send) { return (false, ServerErrorCode.AlreadyInviteParty, string.Empty); } } // 3-6. 파티원인지 체크 var check_party_member = await checkPartyMember(login_cache.PartyGuid); if (check_party_member) { return (false, ServerErrorCode.JoiningParty, string.Empty); } return (true, ServerErrorCode.Success, login_cache.CurrentServer); } private async Task checkPartyMember(PARTY_GUID party_guid) { if (string.IsNullOrEmpty(party_guid)) return false; var party_member_cache_request = new PartyMemberCacheRequest(party_guid, GameServerApp.getServerLogic().getRedisConnector()); var result = await party_member_cache_request.fetchPartyMemberCache(); if (result.isFail()) return false; var cache = party_member_cache_request.getPartyMemberCache(); ArgumentNullException.ThrowIfNull(cache); return cache.PartyMembers.Count > 1; } private Dictionary? inputInvitePartyErrorMembers(Dictionary? failed_invites) { return failed_invites?.ToDictionary(fail => fail.Key, fail => fail.Value); } }