초기커밋

This commit is contained in:
2025-05-01 07:20:41 +09:00
commit 98bb2e3c5c
2747 changed files with 646947 additions and 0 deletions

View File

@@ -0,0 +1,399 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
using USER_GUID = System.String;
namespace GameServer;
public class GlobalPartyDetailInfoAction : EntityActionBase
{
private readonly PartyCacheRequest m_party_cache_request;
private TaskCompletionSource m_vote_finish_task_source { get; set; } = new();
public GlobalPartyDetailInfoAction(GlobalPartyDetail owner) : base(owner)
{
m_party_cache_request =
new PartyCacheRequest(owner.PartyGuid, GameServerApp.getServerLogic().getRedisConnector());
}
public override async Task<Result> onInit() => await Task.FromResult(new Result());
public override void onClear()
{
return;
}
public async Task keep()
{
await m_party_cache_request.keepPartyCache();
}
public async Task<Result> loadParty()
{
var result = new Result();
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
result = await m_party_cache_request.fetchPartyCache();
if (result.isFail()) return result;
result = await ServerBase.DataCopyHelper.copyEntityAttributeFromCaches(party_attribute, new List<CacheBase> { m_party_cache_request.getPartyCache()! });
if (result.isFail())
{
Log.getLogger().error(result.toBasicString());
}
return result;
}
public async Task<Result> createParty(USER_GUID leader_guid, string leader_nickname)
{
var result = new Result();
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
// 1. cache 생성
result = await m_party_cache_request.createPartyCache(leader_guid, leader_nickname);
if (result.isFail()) return result;
// 2. attribute 복사
result = await ServerBase.DataCopyHelper.copyEntityAttributeFromCaches(party_attribute, new List<CacheBase> { m_party_cache_request.getPartyCache()! });
if (result.isFail())
{
Log.getLogger().error(result.toBasicString());
}
return result;
}
public async Task<Result> deleteParty()
{
// 1. cache 제거
var result = await m_party_cache_request.deletePartyCache();
if (result.isFail())
{
Log.getLogger().error(result.toBasicString());
}
// 2. attribute 제거
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
party_attribute.onClear();
return result;
}
public async Task<Result> changePartyLeader(USER_GUID next_leader_guid, bool is_upsert_cache)
{
var result = new Result();
// 1. attribute 수정
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
party_attribute.PartyLeaderCharGuid = next_leader_guid;
// 2. cache 수정
var party_cache = m_party_cache_request.getPartyCache();
NullReferenceCheckHelper.throwIfNull(party_cache, () => $"Party Cache is null !! - party name:{party_attribute.PartyName}");
party_cache.PartyLeaderCharGuid = next_leader_guid;
if(is_upsert_cache) result = await m_party_cache_request.upsertPartyCache();
return result;
}
public async Task<Result> changePartyName(string new_party_name, bool is_upsert_cache)
{
var result = new Result();
// 1. attribute 수정
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
party_attribute.PartyName = new_party_name;
// cache 수정
var party_cache = m_party_cache_request.getPartyCache();
NullReferenceCheckHelper.throwIfNull(party_cache, () => $"Party Cache is null !! - party name:{party_attribute.PartyName}");
party_cache.PartyName = new_party_name;
if(is_upsert_cache) result = await m_party_cache_request.upsertPartyCache();
return result;
}
public string getPartyName()
{
var party_attribute = getOwner().getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
return party_attribute.PartyName;
}
public async Task<Result> VoteParty(VoteType vote, USER_GUID voter_guid)
{
var result = new Result();
string err_msg;
var party = getOwner() as GlobalPartyDetail;
NullReferenceCheckHelper.throwIfNull(party, () => $"global party detail is null !!");
var party_attribute = party.getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
// 1. 진행 중인 Vote 확인
if (null == party_attribute.PartyVote)
{
err_msg = $"Failed to reply party vote !!! - not start party vote - {party.PartyGuid}";
result.setFail(ServerErrorCode.NoStartPartyVote, err_msg);
Log.getLogger().error(err_msg);
return result;
}
// 2. 투표 허용 시간 확인
var allow_vote_time = DateTime.UtcNow.AddSeconds(-1 * MetaHelper.GameConfigMeta.VoteTimeLimitSec + 5 );
if (party_attribute.PartyVote.StartVoteTime < allow_vote_time.ToTimestamp())
{
err_msg = $"Failed to reply party vote !!! - already passed party vote time - {party.PartyGuid}";
result.setFail(ServerErrorCode.AlreadyPassPartyVoteTime, err_msg);
Log.getLogger().error(err_msg);
return result;
}
// 3. 기 투표 여부 체크
if (party_attribute.PartyVote.Votes.TryGetValue(voter_guid, out var saved_vote) && saved_vote != VoteType.None)
{
err_msg = $"Failed to reply party vote !!! - already reply party vote - {voter_guid}";
result.setFail(ServerErrorCode.AlreadyReplyPartyVote, err_msg);
Log.getLogger().error(err_msg);
return result;
}
party_attribute.PartyVote.Votes[voter_guid] = vote;
// 4. 투표 종료 체크 ( party leader 가 있는 서버만 )
var player_message = GameServerApp.getServerLogic().getPlayerManager();
if (player_message.tryGetUserByPrimaryKey(party_attribute.PartyLeaderCharGuid, out _))
{
var is_finished = party_attribute.PartyVote.Votes.All(voted_type => voted_type.Value != VoteType.None);
if (is_finished) m_vote_finish_task_source.TrySetResult();
return result;
}
var message = new ServerMessage();
message.ReplyPartyVoteNoti = new();
message.ReplyPartyVoteNoti.PartyGuid = party.PartyGuid;
message.ReplyPartyVoteNoti.PartyVoterGuid = voter_guid;
message.ReplyPartyVoteNoti.Vote = vote;
await PartyHelper.sendToServerByTargetClient(party_attribute.PartyLeaderCharGuid, message);
return result;
}
public PartyVoteInfo? getPartyVoteInfo()
{
var party = getOwner() as GlobalPartyDetail;
NullReferenceCheckHelper.throwIfNull(party, () => $"global party detail is null !!");
var party_attribute = party.getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {getOwner().toBasicString()}");
return party_attribute.PartyVote;
}
public async Task<(Result result, PartyVoteInfo? vote)> registerPartyVote(string vote_title, Timestamp start_vote_time, bool is_start)
{
var result = new Result();
var party = getOwner() as GlobalPartyDetail;
NullReferenceCheckHelper.throwIfNull(party, () => $"global party detail is null !!");
var party_attribute = party.getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {party.toBasicString()}");
var party_member_action = party.getEntityAction<GlobalPartyDetailMemberAction>();
NullReferenceCheckHelper.throwIfNull(party_member_action, () => $"GlobalPartyDetailMemberAction is null !! - {party.toBasicString()}");
// 1. vote condition 체크
result = await checkVoteCondition(party, vote_title);
if (result.isFail()) return (result, null);
// 2. vote 정보 저장 : 시작시점의 유저 리스트 등록 - 추가(제외) / 감소(기권)
var members = party_member_action.getMembers();
var vote = new PartyVoteInfo
{
VoteTitle = vote_title,
StartVoteTime = start_vote_time
};
foreach (var member in members)
{
vote.Votes.Add(member, VoteType.None);
}
party_attribute.PartyVote = vote;
// 3. cache 저장
var cache = m_party_cache_request.getPartyCache();
NullReferenceCheckHelper.throwIfNull(cache, () => $"Party Cache is null !! - party name:{party_attribute.PartyName} / {party.toBasicString()}");
cache.LastVoteTime = start_vote_time;
party_attribute.LastVoteTime = start_vote_time;
if (is_start)
{
result = await m_party_cache_request.upsertPartyCache();
if (result.isFail())
{
party_attribute.PartyVote = null;
return (result, null);
}
// 4. Wait Vote Task 실행 ( fire and forget )
_ = Task.Run(waitPartyVoteFinish);
}
return (result, vote);
}
private async Task<Result> checkVoteCondition(GlobalPartyDetail party, string vote_title)
{
var result = new Result();
string err_msg;
var party_attribute = party.getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {party.toBasicString()}");
// 1. vote title 길이 체크
if (vote_title.Length > MetaHelper.GameConfigMeta.MaxVoteAgendaInput)
{
err_msg = $"fail to start party vote !!! : invalid vote title length - {vote_title.Length}";
result.setFail(ServerErrorCode.InvalidPartyStringLength, err_msg);
Log.getLogger().error(err_msg);
return result;
}
// 2. 진행 중인 Vote 체크
if (null != party_attribute.PartyVote)
{
err_msg = $"Failed to start party vote !!! : exist party vote - {party_attribute.PartyVote.VoteTitle}";
result.setFail(ServerErrorCode.AlreadyStartPartyVote, err_msg);
Log.getLogger().error(err_msg);
return result;
}
// 3. Last Vote Time 체크
var last_vote_time = party_attribute.LastVoteTime ?? DateTimeHelper.MinTime.ToTimestamp();
if (last_vote_time > DateTime.UtcNow.AddSeconds(-1 * MetaHelper.GameConfigMeta.VoteCoolTimeSec).ToTimestamp())
{
err_msg = $"Failed to start party vote !!! : invalid party vote time - {vote_title}";
result.setFail(ServerErrorCode.InvalidPartyVoteTime, err_msg);
Log.getLogger().error(err_msg);
return result;
}
return await Task.FromResult(result);
}
private async Task waitPartyVoteFinish()
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(MetaHelper.GameConfigMeta.VoteTimeLimitSec + 1));
m_vote_finish_task_source = new();
cts.Token.Register(() => { m_vote_finish_task_source.TrySetCanceled(); });
try
{
await m_vote_finish_task_source.Task;
}
catch (OperationCanceledException)
{
// Ignore...
}
catch (Exception e)
{
Log.getLogger().error($"Failed to wait party vote !!! : exception finish task - {e}");
return;
}
// 1. 파티 정보 획득
var party = getOwner() as GlobalPartyDetail;
NullReferenceCheckHelper.throwIfNull(party, () => $"global party detail is null !!");
// 2. 투표 결과 수집
var party_attribute = party.getEntityAttribute<PartyAttribute>();
NullReferenceCheckHelper.throwIfNull(party_attribute, () => $"PartyAttribute is null !! - {party.toBasicString()}");
// 3. 결과 수집
var agreement = party_attribute.getVoteCount(VoteType.Agreement);
var disAgreement = party_attribute.getVoteCount(VoteType.DisAgreement);
var abstain = party_attribute.getVoteCount(VoteType.Abstain);
abstain += party_attribute.getVoteCount(VoteType.None);
// 4. 결과 통보 ( to server )
var server_message = new ServerMessage();
server_message.PartyVoteResultNoti = new();
server_message.PartyVoteResultNoti.PartyGuid = party.PartyGuid;
server_message.PartyVoteResultNoti.VoteTitle = party_attribute.PartyVote?.VoteTitle ?? string.Empty;
server_message.PartyVoteResultNoti.ResultTrue = agreement;
server_message.PartyVoteResultNoti.ResultFalse = disAgreement;
server_message.PartyVoteResultNoti.Abstain = abstain;
PartyHelper.BroadcastToServers(party, server_message, true);
// 4. 결과 통보 ( to client )
var client_message = new ClientToGame();
client_message.Message = new();
client_message.Message.PartyVoteResultNoti = new();
client_message.Message.PartyVoteResultNoti.VoteTitle = party_attribute.PartyVote?.VoteTitle ?? string.Empty;
client_message.Message.PartyVoteResultNoti.ResultTrue = agreement;
client_message.Message.PartyVoteResultNoti.ResultFalse = disAgreement;
client_message.Message.PartyVoteResultNoti.Abstain = abstain;
PartyHelper.BroadcastToClients(party, client_message, new List<string>());
// 5. Business Log 기록
writeBusinessLog(party);
// 6. 투표 데이터 삭제
party_attribute.PartyVote = null;
}
private void writeBusinessLog(GlobalPartyDetail party)
{
var log_invokers = new List<ILogInvoker>(2);
// 1. 파티정보
var party_log_data = PartyBusinessLogHelper.toPartyLogData(party.PartyGuid, false);
var party_business_log = new PartyBusinessLog(party_log_data);
log_invokers.Add(party_business_log);
// 2. 파티 투표 정보
var party_vote_log = PartyBusinessLogHelper.toPartyVoteLogData(party.PartyGuid, false);
var party_vote_business_log = new PartyVoteBusinessLog(party_vote_log);
log_invokers.Add(party_vote_business_log);
BusinessLogger.collectLogs(new LogActionEx(LogActionType.EndPartyVote), party, log_invokers);
}
}