Files
caliverse_server/GameServer/Entity/Party/Action/GlobalPartyAction.cs
2025-05-01 07:20:41 +09:00

402 lines
17 KiB
C#

using System.Collections.Concurrent;
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 GlobalPartyAction : EntityActionBase
{
private ConcurrentDictionary<string, GlobalPartyDetail> m_parties { get; set; } = new();
public GlobalPartyAction(GlobalParty owner) : base(owner)
{
}
public override async Task<Result> onInit()
{
await Task.CompletedTask;
var result = new Result();
return result;
}
public override void onClear()
{
return;
}
public GlobalPartyDetail? getGlobalPartyDetail(PARTY_GUID party_guid)
{
if (string.IsNullOrEmpty(party_guid)) return null;
return m_parties.GetValueOrDefault(party_guid);
}
private void setGlobalPartyDetail(GlobalPartyDetail detail) => m_parties.TryAdd(detail.PartyGuid, detail);
public async Task<Result> createParty(PARTY_GUID party_guid, string leader_guid, string leader_nickname)
{
var result = new Result();
var owner = getOwner() as GlobalParty;
NullReferenceCheckHelper.throwIfNull(owner, () => $"GlobalParty is null !! - party_guid:{party_guid}");
var detail = new GlobalPartyDetail(owner, party_guid);
await detail.onInit();
// redis 등록
var detail_action = detail.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(detail_action, () => $"GlobalPartyDetailAction is null !! - party_guid:{party_guid}");
result = await detail_action.createPartyDetailInfo(leader_guid, leader_nickname);
// 메모리 등록
setGlobalPartyDetail(detail);
return result;
}
private async Task<Result> loadParty(PARTY_GUID party_guid)
{
var owner = getOwner() as GlobalParty;
NullReferenceCheckHelper.throwIfNull(owner, () => $"global party is null !!! - party guid: {party_guid}");
var detail = new GlobalPartyDetail(owner, party_guid);
await detail.onInit();
var detail_action = detail.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(detail_action, () => $"global party detail action is null !!! - {owner.toBasicString()}");
var result = await detail_action.loadAllFromCache();
if (result.isFail()) return result;
setGlobalPartyDetail(detail);
// 1-1. party server 정보에 server 추가
var global_party_server_action = detail.getEntityAction<GlobalPartyDetailServerAction>();
NullReferenceCheckHelper.throwIfNull(global_party_server_action, () => $"global party detail server action is null !!! - {owner.toBasicString()}");
result = await global_party_server_action.addPartyServer(GameServerApp.getServerLogic().getServerName());
if (result.isFail()) return result;
// 1-2. 서버 변경 알림
result = await global_party_server_action.notifyChangePartyServerToServers(BoolType.True, GameServerApp.getServerLogic().getServerName());
return result;
}
public async Task<(string? leader_guid, string? leader_nickname, int? member_count)> getPartyLeaderGuidAndMemberCount(PARTY_GUID party_guid)
{
var owner = getOwner() as GlobalParty;
NullReferenceCheckHelper.throwIfNull(owner, () => $"global party is null !!! - party guid: {party_guid}");
var detail = getGlobalPartyDetail(party_guid);
// 메모리에 파티정보가 있으면, 활용
if (null != detail)
{
var origin_detail_action = detail.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(origin_detail_action, () => $"global party detail action is null !!! - {owner.toBasicString()}");
var origin_detail_member_action = detail.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(origin_detail_member_action, () => $"global party detail member action is null !!! - {owner.toBasicString()}");
return (origin_detail_action.getLeaderGuid(), origin_detail_action.getLeaderNickname(), origin_detail_member_action.getMemberCount());
}
// 메모리에 파티 정보가 없으면, load 하여 활용 ( 단, 메모리 저장은 하지 않음 - 서버내 없는 파티 정보 조회 )
detail = new GlobalPartyDetail(owner, party_guid);
await detail.onInit();
var detail_action = detail.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(detail_action, () => $"global party detail action is null !!! - {owner.toBasicString()}");
var detail_member_action = detail.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(detail_member_action, () => $"global party detail member action is null !!! - {owner.toBasicString()}");
var result = await detail_action.loadPartyCache();
if (result.isFail()) return (null, null, null);
result = await detail_action.loadPartyMemberCache();
if (result.isFail()) return (null, null, null);
return (detail_action.getLeaderGuid(), detail_action.getLeaderNickname(), detail_member_action.getMemberCount());
}
public async Task<Result> joinParty(PARTY_GUID party_guid, PartyMemberInfo user)
{
var result = new Result();
var owner = getOwner() as GlobalParty;
if (null == owner)
{
var err_msg = $"Fail to find global entity !!! : {nameof(GlobalParty)}";
result.setFail(ServerErrorCode.EntityBaseNotFound, err_msg );
Log.getLogger().error(err_msg);
return result;
}
var party = getGlobalPartyDetail(party_guid);
// 1. 없으면, Redis 에서 Load 하여 채워 넣음
if (null == party)
{
result = await loadParty(party_guid);
if (result.isFail()) return result;
party = getGlobalPartyDetail(party_guid);
}
NullReferenceCheckHelper.throwIfNull(party, () => $"GlobalDetailParty is null !! - party_guid:{party_guid}");
// 2. member 추가
var global_party_member_action = party.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(global_party_member_action, () => $"GlobalPartyDetailMemberAction is null !! - party_guid:{party_guid}");
var add_member = await global_party_member_action.addJoinMember(user);
if (add_member.result.isFail()) return add_member.result;
// 3. Party p2p group host 전달 ( to client )
var party_action = party.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(party_action, () => $"global party detail action is null !!! - {party.toBasicString()}");
_ = await party_action.joinPartyP2PGroup(user.UserGuid);
_ = await party_action.checkPartyP2PState(user.UserGuid, true, true);
if (add_member.isAready == true) return add_member.result;
// 4. 파티 정보 전달
var send_party_members = new List<USER_GUID>();
var member_count = global_party_member_action.getMemberCount();
if (member_count == 2)
{
send_party_members.Add(party_action.getLeaderGuid());
}
send_party_members.Add(user.UserGuid);
await party_action.sendPartyInfo(send_party_members, party_action.getLeaderGuid(), false);
// 5. Party Instance 정보 전달
var party_instance_attribute = party.getEntityAttribute<PartyInstanceAttribute>();
NullReferenceCheckHelper.throwIfNull(party_instance_attribute, () => $"party instance attribute is null !!! - {party.toBasicString()}");
if (party_instance_attribute.InstanceId > 0)
{
party_action.sendPartyInstance(user.UserGuid);
}
return add_member.result;
}
public async Task<Result> leaveParty(PARTY_GUID party_guid, USER_GUID leave_user_guid, BoolType is_ban)
{
var result = new Result();
var global_party = getOwner() as GlobalParty;
NullReferenceCheckHelper.throwIfNull(global_party, () => $"global party is null !!! - party guid: {party_guid}");
var party = global_party.getParty(party_guid);
ArgumentNullException.ThrowIfNull(party);
// 1. 파티 멤버에서 삭제
var party_member_action = party.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(party_member_action, () => $"GlobalPartyDetailMemberAction is null !! - party_guid:{party_guid}, {party.toBasicString()}");
var delete_party_member = await party_member_action.deleteJoinMember(leave_user_guid, true);
if (delete_party_member.result.isFail()) return delete_party_member.result;
// 2. p2p 그룹에서 제외
var party_action = party.getEntityAction<GlobalPartyDetailAction>();
ArgumentNullException.ThrowIfNull(party_action);
_ = party_action.leavePartyP2PGroup(leave_user_guid);
_ = party_action.checkPartyP2PState(leave_user_guid, false, false);
// 3. 파티 탈퇴 알림
notifyLeavePartyMember(party, leave_user_guid, is_ban);
// 4. 초대 메시지 발송 체크 : 초대 메시지에 응답하여 Party 가 결정될 수 있기 때문에 파티원이 1명이어도 Party 를 파괴시키지 않음
var invite_send_action = party.getEntityAction<GlobalPartyInvitePartySendAction>();
NullReferenceCheckHelper.throwIfNull(invite_send_action, () => $"global party invite party send action is null !!! - {party.toBasicString()}");
var sends = await invite_send_action.getInviteSendsCount();
if (sends > 0 && delete_party_member.party_member_count >= 1)
{
Log.getLogger().debug($"Not Destory Party: Send Invite Count - {sends}");
return result;
}
// 5. 파티원이 1명으로 파티 파괴
if (delete_party_member.party_member_count <= 1)
{
result = await destroyParty(party_guid, true);
}
// 6. party server 체크
else
{
result = await checkPartyServer(party, leave_user_guid);
}
return result;
}
public async Task<Result> destroyParty(PARTY_GUID party_guid, bool is_notify_to_servers)
{
var global_party = getOwner() as GlobalParty;
ArgumentNullException.ThrowIfNull(global_party);
var party = global_party.getParty(party_guid);
if (null == party) return new Result();
// 0. business log 준비
var party_log_data = PartyBusinessLogHelper.toPartyLogData(party_guid, false);
var party_business_log = new PartyBusinessLog(new LogActionEx(LogActionType.DestroyParty), party_log_data);
// 1. party info 제거
var party_info_action = party.getEntityAction<GlobalPartyDetailInfoAction>();
NullReferenceCheckHelper.throwIfNull(party_info_action, () => $"global party detail info action is null !!! - {party.toBasicString()}");
_ = await party_info_action.deleteParty();
// 2. party member 제거
var party_member_action = party.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(party_member_action, () => $"global party detail member action is null !!! - {party.toBasicString()}");
var members = party_member_action.getMembers();
_ = await party_member_action.deletePartyMembers();
// 3. party server 제거
var party_server_action = party.getEntityAction<GlobalPartyDetailServerAction>();
NullReferenceCheckHelper.throwIfNull(party_server_action, () => $"global party detail server action is null !!! - {party.toBasicString()}");
var servers = party_server_action.getServers();
_ = await party_server_action.deleteAllPartyServers();
// 4. invite party send 제거
var party_invite_party_send_action = party.getEntityAction<GlobalPartyInvitePartySendAction>();
NullReferenceCheckHelper.throwIfNull(party_invite_party_send_action, () => $"global party invite party send action is null !!! - {party.toBasicString()}");
_ = await party_invite_party_send_action.deleteInvitePartySends();
// 5. instance 제거
var party_instance_action = party.getEntityAction<GlobalPartyDetailInstanceAction>();
NullReferenceCheckHelper.throwIfNull(party_instance_action, () => $"global party detail instance action is null !!! - {party.toBasicString()}");
_ = await party_instance_action.deletePartyInstance();
// 5. p2pgroup 제거
var party_action = party.getEntityAction<GlobalPartyDetailAction>();
NullReferenceCheckHelper.throwIfNull(party_action, () => $"global party detail action is null !!! - {party.toBasicString()}");
_ = party_action.destroyPartyP2PGroup();
// 6. member 들의 Party 정보 제거
await clearPersonalPartyWithMembers(members.ToList());
// 7. party 제거
m_parties.Remove(party_guid, out _);
// 8. Server 에 알림
if (is_notify_to_servers)
{
var server_message = new ServerMessage
{
NtfDestroyParty = new() { DestroyPartyGuid = party_guid }
};
PartyHelper.BroadcastToServers(servers.ToList(), server_message, true);
}
// 9. Client 에게 알림
var client_message = new ClientToGame
{
Message = new()
{
DestroyPartyNoti = new()
}
};
PartyHelper.BroadcastToClients(members.ToList(), client_message, new List<string>());
BusinessLogger.collectLog(party, party_business_log);
return new Result();
}
private void notifyLeavePartyMember(GlobalPartyDetail party, USER_GUID leave_user_guid, BoolType is_ban)
{
// 1. 파티원 탈퇴에 따른 알림 ( to server )
var server_message = new ServerMessage
{
LeavePartyMemberNoti = new()
{
IsBan = is_ban,
PartyGuid = party.PartyGuid,
LeavePartyUserGuid = leave_user_guid
}
};
PartyHelper.BroadcastToServers(party, server_message, true);
// 2. 파티원 탈퇴에 따른 알림 ( to client )
var client_message = new ClientToGame
{
Message = new()
{
LeavePartyMemberNoti = new()
{
IsBan = is_ban,
LeavePartyUserGuid = leave_user_guid
}
}
};
PartyHelper.BroadcastToClients(party, client_message, new List<string>());
}
public async Task<Result> checkPartyServer(GlobalPartyDetail party, USER_GUID leave_user_guid)
{
var result = new Result();
// 1. 서버 내 파티원 체크
var party_member_action = party.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(party_member_action, () => $"GlobalPartyDetailMemberAction is null !!! - {party.toBasicString()}");
var exist_members_without_me = party_member_action.checkExistPartyMembers(leave_user_guid);
if (exist_members_without_me) return result;
// 2. 서버 정보 제거
var party_server_action = party.getEntityAction<GlobalPartyDetailServerAction>();
NullReferenceCheckHelper.throwIfNull(party_server_action, () => $"GlobalPartyDetailServerAction is null !!! - {party.toBasicString()}");
result = await party_server_action.deletePartyServer(GameServerApp.getServerLogic().getServerName());
if (result.isFail()) return result;
// 3. 서버 정보 제거 알림
result = await party_server_action.notifyChangePartyServerToServers(BoolType.False, GameServerApp.getServerLogic().getServerName());
// 4. 파티 정보 제거
m_parties.Remove(party.PartyGuid, out _);
return result;
}
private async Task clearPersonalPartyWithMembers(IReadOnlyList<USER_GUID> members)
{
var player_manager = GameServerApp.getServerLogic().getPlayerManager();
foreach (var user in members)
{
if (!player_manager.tryGetUserByPrimaryKey(user, out var member)) continue;
if (null == member) continue;
var personal_party_action = member.getEntityAction<PersonalPartyAction>();
if (null == personal_party_action) continue;
await personal_party_action.clearPersonalParty();
}
}
}