402 lines
17 KiB
C#
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();
|
|
}
|
|
}
|
|
} |