초기커밋

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,70 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class UgcNpcCommunicationRankAction : EntityActionBase
{
private Dictionary<UgcNpcRankState, List<UgcNpcRank>> m_ranks { get; } = new();
public UgcNpcCommunicationRankAction(UgcNpcRankEntity owner) : base(owner)
{
}
public override async Task<Result> onInit() => await Task.FromResult(new Result());
public override void onClear()
{
}
public async Task<(Result result, List<UgcNpcRank>? list)> getRanks(UgcNpcRankState state)
{
if (!m_ranks.TryGetValue(state, out var ranks))
{
ranks = await loadRank(state);
}
return (new Result(), ranks);
}
public async Task<List<UgcNpcRank>?> loadRank(UgcNpcRankState state)
{
Result result;
AIChatRanking? chat_ranks;
List<UgcNpcRank>? list;
var ugc_npc_guids = new List<(string, long)>();
// 누적 랭킹 조회
if (state == UgcNpcRankState.Total)
{
(result, chat_ranks) = await AIChatServerConnector.getAllRanking(LanguageType.Ko, UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail() || chat_ranks == null) return null;
}
// 트랜드 랭킹 조회
else
{
(result, chat_ranks) = await AIChatServerConnector.getTodayRanking(LanguageType.Ko, UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail() || chat_ranks == null) return null;
}
foreach (var rank in chat_ranks.items)
{
ugc_npc_guids.Add((UgcNpcRankHelper.makeRankKey(rank.owner_guid, rank.char_guid), rank.total_chat_count));
}
list = await UgcNpcRankHelper.getUgcNpcSummaryFromMetaGuid(ugc_npc_guids);
Log.getLogger().info($"update ugc npc communication rank data : rank state[{state}] / rank count[{list.Count}]");
m_ranks[state] = list;
return list;
}
}

View File

@@ -0,0 +1,93 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class UgcNpcLikeRankAction : EntityActionBase
{
private readonly Dictionary<UgcNpcRankState, List<UgcNpcRank>> m_ranks;
public UgcNpcLikeRankAction(UgcNpcRankEntity owner) : base(owner)
{
m_ranks = new();
}
public override async Task<Result> onInit() => await Task.FromResult(new Result());
public override void onClear()
{
}
public async Task<(Result result, List<UgcNpcRank>? list)> getRanks(UgcNpcRankState state)
{
if (!m_ranks.TryGetValue(state, out var ranks))
{
ranks = await loadRank(state);
}
return (new Result(), ranks);
}
public async Task<List<UgcNpcRank>?> loadRank(UgcNpcRankState state)
{
Result result;
List<UgcNpcRank>? list;
var server_logic = GameServerApp.getServerLogic();
var redis_client = server_logic.getRedisConnector();
var rank_date = UgcNpcRankHelper.getCurrentRankDate();
// 누적 랭킹 조회
if (state == UgcNpcRankState.Total)
{
var total_cache_request = new UgcNpcTotalRankCacheRequest(UgcNpcRankType.Like, rank_date, redis_client);
result = await total_cache_request.getRanks(UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail()) return null;
var total_cache = total_cache_request.getTotalCache();
NullReferenceCheckHelper.throwIfNull(total_cache, () => $"total_cache is null !!!");
list = await UgcNpcRankHelper.getUgcNpcSummaryFromMetaGuid(total_cache.Ranks);
}
// 트랜드 랭킹 조회
else
{
var trend_cache_request = new UgcNpcTrendRankCacheRequest(UgcNpcRankType.Like, rank_date, redis_client);
result = await trend_cache_request.getRanks(UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail()) return null;
var trend_rank_cache = trend_cache_request.getTrendRankCache();
NullReferenceCheckHelper.throwIfNull(trend_rank_cache, () => $"trend_rank_cache is null !!!");
list = await UgcNpcRankHelper.getUgcNpcSummaryFromMetaGuid(trend_rank_cache.Ranks);
}
Log.getLogger().info($"update ugc npc like rank data : rank state[{state}] / rank count[{list.Count}]");
m_ranks[state] = list;
return list;
}
public async Task<Result> setRankScore(string ownerGuid, string targetUserGuid, string ugcNpcMetaGuid, bool isAdd)
{
var key = UgcNpcRankHelper.makeRankKey(targetUserGuid, ugcNpcMetaGuid);
var delta = isAdd ? 1 : -1;
var result = await UgcNpcRankHelper.updateUgcNpcRank(UgcNpcRankState.Total, UgcNpcRankType.Like, key, delta, null);
if (result.isFail()) return result;
result = await UgcNpcRankHelper.updateUgcNpcRank(UgcNpcRankState.Trend, UgcNpcRankType.Like, key, delta, UgcNpcRankHelper.makeCurrentOrganizationDate());
return result;
}
}

View File

@@ -0,0 +1,93 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class UgcNpcQuestRankAction : EntityActionBase
{
private readonly Dictionary<UgcNpcRankState, List<UgcNpcRank>> m_ranks;
public UgcNpcQuestRankAction(UgcNpcRankEntity owner) : base(owner)
{
m_ranks = new();
}
public override async Task<Result> onInit() => await Task.FromResult(new Result());
public override void onClear()
{
}
public async Task<(Result result, List<UgcNpcRank>? list)> getRanks(UgcNpcRankState state)
{
if (!m_ranks.TryGetValue(state, out var ranks))
{
ranks = await loadRank(state);
}
return (new Result(), ranks);
}
public async Task<List<UgcNpcRank>?> loadRank(UgcNpcRankState state)
{
Result result;
List<UgcNpcRank>? list;
var server_logic = GameServerApp.getServerLogic();
var redis_client = server_logic.getRedisConnector();
var rank_date = UgcNpcRankHelper.getCurrentRankDate();
// 누적 랭킹 조회
if (state == UgcNpcRankState.Total)
{
var total_cache_request = new UgcNpcTotalRankCacheRequest(UgcNpcRankType.Quest, rank_date, redis_client);
result = await total_cache_request.getRanks(UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail()) return null;
var total_cache = total_cache_request.getTotalCache();
NullReferenceCheckHelper.throwIfNull(total_cache, () => $"total_cache is null !!!");
list = await UgcNpcRankHelper.getUgcNpcSummaryFromMetaGuid(total_cache.Ranks);
}
// 트랜드 랭킹 조회
else
{
var trend_cache_request = new UgcNpcTrendRankCacheRequest(UgcNpcRankType.Quest, rank_date, redis_client);
result = await trend_cache_request.getRanks(UgcNpcRankEntity.DefaultPageNum, UgcNpcRankEntity.DefaultPageSize);
if (result.isFail()) return null;
var trend_rank_cache = trend_cache_request.getTrendRankCache();
NullReferenceCheckHelper.throwIfNull(trend_rank_cache, () => $"trend_rank_cache is null !!!");
list = await UgcNpcRankHelper.getUgcNpcSummaryFromMetaGuid(trend_rank_cache.Ranks);
}
Log.getLogger().info($"update ugc npc quest rank data : rank state[{state}] / rank count[{list.Count}]");
m_ranks[state] = list;
return list;
}
public async Task<Result> setRankScore(string ownerGuid, string targetUserGuid, string ugcNpcMetaGuid, bool isAdd)
{
var key = UgcNpcRankHelper.makeRankKey(targetUserGuid, ugcNpcMetaGuid);
var delta = isAdd ? 1 : -1;
var result = await UgcNpcRankHelper.updateUgcNpcRank(UgcNpcRankState.Total, UgcNpcRankType.Quest, key, delta, null);
if (result.isFail()) return result;
await UgcNpcRankHelper.updateUgcNpcRank(UgcNpcRankState.Trend, UgcNpcRankType.Quest, key, delta, UgcNpcRankHelper.makeCurrentOrganizationDate());
return result;
}
}

View File

@@ -0,0 +1,433 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class UgcNpcRankManageAction : EntityActionBase
{
private UgcNpcRankManageCacheRequest m_rank_manage_cache_request { get; set; }
public UgcNpcRankManageAction(UgcNpcRankEntity owner) : base(owner)
{
// DailyTimeEventManager 등록
var date = MetaHelper.GameConfigMeta.NpcRankingCalculateTime;
DailyTimeEventManager.Instance.tryAddTask("UgcNpcRankManage", date, onOrganizationUgcNpcRanking);
var businesslog_refresh_date = MetaHelper.GameConfigMeta.BusinessLogRefreshTime;
DailyTimeEventManager.Instance.tryAddTask("BusinessLogRefresh", businesslog_refresh_date, onBusunessLogRefresh);
m_rank_manage_cache_request =
new UgcNpcRankManageCacheRequest(GameServerApp.getServerLogic().getRedisConnector());
}
public override async Task<Result> onInit() => await Task.FromResult(new Result());
public override void onClear() {}
public async Task<Result> initRank()
{
// 1. 기본 정보 체크
var result = await checkBaseUgcNpcRank();
if (result.isFail()) return result;
// 2. Ranking 데이터 로딩
await reloadAllUgcNpcRanks();
return result;
}
public async Task reloadAllUgcNpcRanks()
{
try
{
var ranking_types = System.Enum.GetValues(typeof(UgcNpcRankType)).Cast<UgcNpcRankType>().ToList();
var tasks = new List<Task>(ranking_types.Count * 2);
foreach (var type in ranking_types)
{
tasks.Add(getUgcNpcRankingData(type, UgcNpcRankState.Total));
tasks.Add(getUgcNpcRankingData(type, UgcNpcRankState.Trend));
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
Log.getLogger().error($"fail to reload ugc npc rank : {e}");
}
}
public async Task<Result> notifyClientUgcNpcRankRefresh(bool includeAnotherServer)
{
var result = new Result();
// 1. Client 전송
var client_message = new ClientToGame();
client_message.Message = new ClientToGameMessage();
client_message.Message.NtfUgcNpcRankRefresh = new();
result = await UgcNpcRankHelper.BroadcastToAllClients(client_message);
// 2. Server Noti
if (!includeAnotherServer) return result;
var server_message = new ServerMessage();
server_message.NtfUgcNpcRankRefresh = new();
result = await UgcNpcRankHelper.BroadcastToAllServers(server_message, true);
return await Task.FromResult(result);
}
private async Task<Result> checkBaseUgcNpcRank()
{
var result = new Result();
// 1. total / like
result = await checkBaseUgcNpcRank(UgcNpcRankState.Total, UgcNpcRankType.Like);
if (result.isFail()) return result;
// 2. total / quest
result = await checkBaseUgcNpcRank(UgcNpcRankState.Total, UgcNpcRankType.Quest);
if (result.isFail()) return result;
// 3. trend / like
result = await checkBaseUgcNpcRank(UgcNpcRankState.Trend, UgcNpcRankType.Like);
if (result.isFail()) return result;
// 4. trend / quest
result = await checkBaseUgcNpcRank(UgcNpcRankState.Trend, UgcNpcRankType.Quest);
return result;
}
private async Task<Result> checkBaseUgcNpcRank(UgcNpcRankState state, UgcNpcRankType type)
{
var server_logic = GameServerApp.getServerLogic();
var dynamoDb_client = server_logic.getDynamoDbClient();
var organization_time = UgcNpcRankHelper.makeCurrentOrganizationDate();
var rank_date = organization_time.ToString("yyyy-MM-dd");
var primary_key = $"{UgcNpcRankDoc.getPrefixOfPK()}{state.ToString()}";
var second_key = state == UgcNpcRankState.Trend ? $"{type.ToString()}#{rank_date}" : type.ToString();
// 1. data 조회
var query_config = dynamoDb_client.makeQueryConfigForReadByPKSK(primary_key, second_key);
var (result, rank_doc) = await dynamoDb_client.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcRankDoc>(query_config);
if (result.isSuccess() && null != rank_doc) return result;
// 2. 빈 데이터 생성
rank_doc = state == UgcNpcRankState.Trend
? new UgcNpcRankDoc(state, type, rank_date, UgcNpcRankHelper.getUgcNpcRankDocTtlSeconds())
: new UgcNpcRankDoc(state, type, null);
result = await dynamoDb_client.simpleInsertDocumentWithDocType<UgcNpcRankDoc>(rank_doc);
return result;
}
private async Task onOrganizationUgcNpcRanking()
{
var result = new Result();
// 1. 시작 설정
var is_set_start = await m_rank_manage_cache_request.setStartUgcNpcRankManage();
if (!is_set_start) return;
// 2. 시작 조건 체크
var is_start = await checkStartCondition();
if (false == is_start) return;
// 3. total rank 데이터 처리
result = await copyToOrganization();
if (result.isFail()) return;
// 4. rank 데이터 등록
result = await registrationOrganizationRank();
if (result.isFail())
{
var err_msg = $"fail to organize ugc npc rank!! [{result.toBasicString()}]";
Log.getLogger().error(err_msg);
}
// 5. reload data
await reloadAllUgcNpcRanks();
// 6. Client 노티
_ = await notifyClientUgcNpcRankRefresh(true);
}
private async Task<bool> checkStartCondition()
{
var key = "";
var rank_date = UgcNpcRankHelper.getCurrentRankDate();
var sub_key = rank_date.ToString("yyyy-MM-dd");
// 1. total key 존재 여부 체크
key = $"total:{UgcNpcRankType.Like.ToString()}:{sub_key}";
var is_exist = await m_rank_manage_cache_request.isExistKey(key);
if (is_exist == false) return true;
key = $"total:{UgcNpcRankType.Quest.ToString()}:{sub_key}";
is_exist = await m_rank_manage_cache_request.isExistKey(key);
if (is_exist == false) return true;
// 2. trend key 존재 여부 체크
key = $"trend:{UgcNpcRankType.Like.ToString()}:{sub_key}";
is_exist = await m_rank_manage_cache_request.isExistKey(key);
if (is_exist == false) return true;
key = $"trend:{UgcNpcRankType.Quest.ToString()}:{sub_key}";
is_exist = await m_rank_manage_cache_request.isExistKey(key);
if (is_exist == false) return true;
return false;
}
private async Task<Result> copyToOrganization()
{
// 1. total like 정보 복사
var result = await copyTotalToOrganization(UgcNpcRankType.Like);
if (result.isFail()) return result;
// 2. total Quest 정보 복사
result = await copyTotalToOrganization(UgcNpcRankType.Quest);
if (result.isFail()) return result;
// 3. trend like doc 생성
result = await createTodayTrendRankDoc(UgcNpcRankType.Like);
if (result.isFail()) return result;
// 4. trend quest doc 생성
result = await createTodayTrendRankDoc(UgcNpcRankType.Quest);
if (result.isFail()) return result;
return result;
}
private async Task<Result> copyTotalToOrganization(UgcNpcRankType type)
{
var server_logic = GameServerApp.getServerLogic();
var dynamoDb_client = server_logic.getDynamoDbClient();
var delete_rank_keys = new List<string>();
var rank_date = UgcNpcRankHelper.getCurrentRankDate().ToString("yyyy-MM-dd");
// 1. 기존 정보가 있다면 ?? 패스
var primary_key = $"{UgcNpcRankDoc.getPrefixOfPK()}{UgcNpcRankState.Total.ToString()}";
var second_key = $"{type.ToString()}#{rank_date}";
var query_config = dynamoDb_client.makeQueryConfigForReadByPKSK(primary_key, second_key);
var (result, origin_total_rank_doc) = await dynamoDb_client.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcRankDoc>(query_config);
if (null != origin_total_rank_doc) return result;
// 2. Total 정보 조회
query_config = dynamoDb_client.makeQueryConfigForReadByPKSK(primary_key, type.ToString());
(result, var total_doc) = await dynamoDb_client.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcRankDoc>(query_config);
if (result.isFail()) return result;
NullReferenceCheckHelper.throwIfNull(total_doc, () => $"total_doc is null !!! - {getOwner()}");
var total_attrib = total_doc.getAttrib<UgcNpcRankAttrib>();
NullReferenceCheckHelper.throwIfNull(total_attrib, () => $"total_attrib is null !!! - {getOwner()}");
// 3. 랭킹 정보 생성
var total_rank_doc = new UgcNpcRankDoc(UgcNpcRankState.Total, type, rank_date, UgcNpcRankHelper.getUgcNpcRankDocTtlSeconds());
var total_rank_attrib = total_rank_doc.getAttrib<UgcNpcRankAttrib>();
NullReferenceCheckHelper.throwIfNull(total_rank_attrib, () => $"total_rank_attrib is null !!! - {getOwner()}");
var ranks = new Dictionary<string, long>(total_attrib.ranks.Count);
foreach (var rank in total_attrib.ranks)
{
if (rank.Value <= 0)
{
delete_rank_keys.Add(rank.Key);
continue;
}
ranks.Add(rank.Key, rank.Value);
}
// 4. 데이터 정렬
total_rank_attrib.ranks = sortRanks(ranks, UgcNpcRankEntity.DefaultPageSize);
result = await dynamoDb_client.simpleInsertDocumentWithDocType<UgcNpcRankDoc>(total_rank_doc);
if (result.isFail()) return result;
// 5. score:0 랭킹 정보 정리
foreach (var delete_key in delete_rank_keys)
{
total_attrib.ranks.Remove(delete_key);
}
result = await dynamoDb_client.simpleUpdateDocumentWithDocType<UgcNpcRankDoc>(total_doc);
return result;
}
private async Task<Result> createTodayTrendRankDoc(UgcNpcRankType type)
{
var result = new Result();
var server_logic = GameServerApp.getServerLogic();
var dynamoDb_client = server_logic.getDynamoDbClient();
var rank_date = UgcNpcRankHelper.makeCurrentOrganizationDate().ToString("yyyy-MM-dd");
var rank_doc = new UgcNpcRankDoc(UgcNpcRankState.Trend, type, rank_date, UgcNpcRankHelper.getUgcNpcRankDocTtlSeconds());
result = await dynamoDb_client.simpleInsertDocumentWithDocType<UgcNpcRankDoc>(rank_doc);
return result;
}
private async Task<Result> registrationOrganizationRank()
{
var result = new Result();
// 1. total / like
result = await registrationOrganizationRank(UgcNpcRankState.Total, UgcNpcRankType.Like);
if (result.isFail()) return result;
// 2. total / quest
result = await registrationOrganizationRank(UgcNpcRankState.Total, UgcNpcRankType.Quest);
if (result.isFail()) return result;
// 3. trend / like
result = await registrationOrganizationRank(UgcNpcRankState.Trend, UgcNpcRankType.Like);
if (result.isFail()) return result;
// 4. trend / quest
result = await registrationOrganizationRank(UgcNpcRankState.Trend, UgcNpcRankType.Quest);
return result;
}
private async Task<Result> registrationOrganizationRank(UgcNpcRankState state, UgcNpcRankType type)
{
var server_logic = GameServerApp.getServerLogic();
var dynamoDb_client = server_logic.getDynamoDbClient();
var rank_date = UgcNpcRankHelper.getCurrentRankDate().ToString("yyyy-MM-dd");
var result = new Result();
// 1. dynamoDb Data 가져오기
var primary_key = $"{UgcNpcRankDoc.getPrefixOfPK()}{state.ToString()}";
var second_key = $"{type.ToString()}#{rank_date}";
var query_config = dynamoDb_client.makeQueryConfigForReadByPKSK(primary_key, second_key);
(result, var origin_rank_doc) = await dynamoDb_client.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcRankDoc>(query_config);
if (result.isFail()) return result;
NullReferenceCheckHelper.throwIfNull(origin_rank_doc, () => $"origin_rank_doc is null !!! - {getOwner()}");
var rank_attrib = origin_rank_doc.getAttrib<UgcNpcRankAttrib>();
NullReferenceCheckHelper.throwIfNull(rank_attrib, () => $"rank_attrib is null !!! - {getOwner()}");
switch (state)
{
case UgcNpcRankState.Total:
result = await registrationOrganizationRankForTotalToRedis(rank_attrib, type);
break;
case UgcNpcRankState.Trend:
result = await registrationOrganizationRankForTrendToRedis(rank_attrib, type);
break;
}
return result;
}
private async Task<Result> registrationOrganizationRankForTotalToRedis(UgcNpcRankAttrib rankAttrib, UgcNpcRankType type)
{
var server_logic = GameServerApp.getServerLogic();
var redis_client = server_logic.getRedisConnector();
var rank_request = new UgcNpcTotalRankCacheRequest(type, UgcNpcRankHelper.getCurrentRankDate(), redis_client);
// 1. rank 설정
var result = await rank_request.initRankScore(rankAttrib.ranks);
if (result.isFail()) return result;
// 2. ttl 설정
result = await rank_request.setTtl();
return result;
}
private async Task<Result> registrationOrganizationRankForTrendToRedis(UgcNpcRankAttrib rankAttrib, UgcNpcRankType type)
{
var server_logic = GameServerApp.getServerLogic();
var redis_client = server_logic.getRedisConnector();
var rank_request = new UgcNpcTrendRankCacheRequest(type, UgcNpcRankHelper.getCurrentRankDate(), redis_client);
// 1. rank 설정
var result = await rank_request.initRankScore(rankAttrib.ranks);
if (result.isFail()) return result;
// 2. ttl 설정
result = await rank_request.setTtl();
return result;
}
private async Task getUgcNpcRankingData(UgcNpcRankType type, UgcNpcRankState state)
{
var entity = getOwner() as UgcNpcRankEntity;
NullReferenceCheckHelper.throwIfNull(entity, () => $"entity is null !!!");
switch (type)
{
case UgcNpcRankType.Like:
var like_action = entity.getEntityAction<UgcNpcLikeRankAction>();
NullReferenceCheckHelper.throwIfNull(like_action, () => $"like_action is null !!!");
await like_action.loadRank(state);
break;
case UgcNpcRankType.Quest:
var quest_action = entity.getEntityAction<UgcNpcQuestRankAction>();
NullReferenceCheckHelper.throwIfNull(quest_action, () => $"quest_action is null !!!");
await quest_action.loadRank(state);
break;
case UgcNpcRankType.Communication:
var communication_action = entity.getEntityAction<UgcNpcCommunicationRankAction>();
NullReferenceCheckHelper.throwIfNull(communication_action, () => $"communication_action is null !!!");
await communication_action.loadRank(state);
break;
}
}
private static Dictionary<string, long> sortRanks(Dictionary<string, long> ranks, int length)
{
var list = new Dictionary<string, long>(length);
var sorted_ranks = ranks.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
var count = 0;
foreach (var rank in sorted_ranks)
{
if (count >= length) break;
list.Add(rank.Key, rank.Value);
count++;
}
return list;
}
private async Task onBusunessLogRefresh()
{
await Task.CompletedTask;
var log_invokers = new List<ILogInvoker>(1);
var empty_business_refresh_with_log_actor = new EmptyBusinessWithLogActor();
DailyRefreshBusinessLog log = new();
log_invokers.Add(log);
BusinessLogger.collectLogs(new LogActionEx(LogActionType.TestBusinessLog), empty_business_refresh_with_log_actor, log_invokers);
Log.getLogger().info("EmptyBusinessLog write");
}
}

View File

@@ -0,0 +1,31 @@
using ServerCommon;
using ServerCore; using ServerBase;
namespace GameServer;
[ChatCommandAttribute("ugcnpcrank", typeof(ChatCommandGetUgcNpcRank), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)]
public class ChatCommandGetUgcNpcRank : ChatCommandBase
{
public override async Task invoke(Player player, string token, string[] args)
{
Log.getLogger().info($"ChatCommandGetUgcNpcRank");
if (args.Length < 2) return;
var message = new ClientToGame();
message.Request = new ClientToGameReq();
message.Request.ReqUgcNpcRank = new();
if(! EnumHelper.tryParse<UgcNpcRankType>(args[0], out var type)) return;
if (!EnumHelper.tryParse<UgcNpcRankState>(args[1], out var state)) return;
message.Request.ReqUgcNpcRank.Type = type;
message.Request.ReqUgcNpcRank.State = state;
var result = await GameServerApp.getServerLogic().onCallProtocolHandler(player, message);
if (result.isFail())
{
Log.getLogger().error($"fail to run ChatCommandGetUgcNpcRank!! : {result.toBasicString()}");
}
}
}

View File

@@ -0,0 +1,231 @@
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Nettention.Proud;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
using Guid = System.Guid;
namespace GameServer;
public class UgcNpcRankHelper
{
public const string RankKeySplit = "_";
public static string makeRankKey(string ownerGuid, string ugcNpcMetaGuid) => $"{ownerGuid}{RankKeySplit}{ugcNpcMetaGuid}";
public static DateTime getCurrentRankDate()
{
return makeCurrentOrganizationDate().AddDays(-1);
}
public static DateTime makeCurrentOrganizationDate()
{
var current_time = DateTimeHelper.Current;
var compare_current = Convert.ToDateTime(current_time.ToString("HH:mm:ss"));
var organizationTime = MetaHelper.GameConfigMeta.NpcRankingCalculateTime;
var check_time = compare_current < organizationTime ? current_time.AddDays(-1) : current_time;
var organization = new DateTime(check_time.Year, check_time.Month, check_time.Day, organizationTime.Hour,
organizationTime.Minute, organizationTime.Second, DateTimeKind.Utc);
return organization;
}
public static Int64 getUgcNpcRankDocTtlSeconds(DateTime? startDate = null)
{
var current = DateTimeHelper.Current;
var delta_period = 0;
if (null != startDate)
{
if (startDate > current) startDate = current;
var current_time = new DateTime(current.Year, current.Month, current.Day);
var delta = current_time - startDate;
delta_period = (int)delta.Value.TotalSeconds;
}
// retention period 는 Hour 단위임
var retention_period = MetaHelper.GameConfigMeta.NpcRankingRetentionPeriod;
return retention_period * 60 * 60 - delta_period;
}
public static (string ownerGuid, string ugcNpcMetaGuid) getDetailRankKey(string key)
{
var split = key.Split(RankKeySplit);
if (split.Length < 2) return (string.Empty, string.Empty);
return (split[0], split[1]);
}
public static async Task<List<UgcNpcRank>> getUgcNpcSummaryFromMetaGuid(List<(string guid, long score)> ranks)
{
var list = new List<UgcNpcRank>(ranks.Count);
var ranking = 1;
foreach (var rank in ranks)
{
var guids = UgcNpcRankHelper.getDetailRankKey(rank.guid);
var ugc_npc_attrib = await getUgcNpcFromMetaGuid(guids.ownerGuid, guids.ugcNpcMetaGuid);
if (ugc_npc_attrib == null) continue;
list.Add(await makeUgcNpcAttribToUgcNpcRank(ugc_npc_attrib, ranking, rank.score));
ranking++;
}
return list;
}
public static async Task<Result> BroadcastToAllClients(ClientToGame message)
{
var target_clients = new List<HostID>();
var users = GameServerApp.getServerLogic().getPlayerManager().getUsers();
foreach (var user in users)
{
target_clients.Add(user.Value.getHostId());
}
GameServerApp.getServerLogic().onSendPacketToHosts(target_clients.ToArray(), RmiContext.ReliableSend, message);
return await Task.FromResult(new Result());
}
public static async Task<Result> BroadcastToAllServers(ServerMessage message, bool except_me)
{
var server_logic = GameServerApp.getServerLogic();
var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMqConnector;
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
(var result, var server_infos) = await server_logic.getServerInfoAll();
foreach (var server in server_infos)
{
if (except_me && server.Name == server_logic.getServerName()) continue;
rabbit_mq.SendMessage(server.Name, message);
}
return await Task.FromResult(new Result());
}
private static async Task<UgcNpcAttrib?> getUgcNpcFromMetaGuid(string ownerGuid, string ugcNpcMetaGuid)
{
var dynamo_db_client = GameServerApp.getServerLogic().getDynamoDbClient();
// ugcNpcMeta 정보 획득
var ugc_npc_doc = new UgcNpcDoc(OwnerEntityType.User, ownerGuid, ugcNpcMetaGuid);
var ugc_npc_query_config = dynamo_db_client.makeQueryConfigForReadByPKSK(ugc_npc_doc.getPK(), ugc_npc_doc.getSK());
var (result, docs) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig<UgcNpcDoc>(ugc_npc_query_config);
if (result.isFail() || docs.Count <= 0)
{
var err_msg = $"Failed to find UgcNpc. OwnerGuid: {ownerGuid} / NpcGuid : {ugcNpcMetaGuid}";
result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg);
Log.getLogger().error(result.toBasicString());
return null;
}
return docs[0].getAttrib<UgcNpcAttrib>();
}
private static async Task<UgcNpcRank> makeUgcNpcAttribToUgcNpcRank(UgcNpcAttrib docAttrib, int ranking, long score)
{
var rank = new UgcNpcRank();
rank.Rank = ranking;
rank.Score = (int)score;
rank.Title = docAttrib.Title;
rank.NpcNickname = docAttrib.Nickname;
rank.UgcNpcMetaGuid = docAttrib.UgcNpcMetaGuid;
rank.OwnerUserGuid = docAttrib.OwnerGuid;
rank.BodyItemMetaId = (int)docAttrib.BodyItemMetaId;
var (result, nickname_doc_attrib) = await NicknameDoc.findNicknameFromGuid(rank.OwnerUserGuid);
if (result.isFail() || nickname_doc_attrib == null) rank.OwnerUserNickname = string.Empty;
else rank.OwnerUserNickname = nickname_doc_attrib.Nickname;
return rank;
}
public static async Task<Result> updateUgcNpcRank(UgcNpcRankState state, UgcNpcRankType type, string guid, int delta, DateTime? rankDate)
{
var dynamo_client = GameServerApp.getServerLogic().getDynamoDbClient();
var rank_date = rankDate?.ToString("yyyy-MM-dd");
var doc = new UgcNpcRankDoc(state, type, rank_date);
var primary_key = doc.getPrimaryKey();
primary_key.fillUpPrimaryKey(doc.getPK(), doc.getSK());
// data 변경
var update = makeUpdateItemRequestForUgcNpcRank( dynamo_client
, primary_key.toKeyWithAttributeValue(), nameof(UgcNpcRankAttrib.ranks), guid, delta);
NullReferenceCheckHelper.throwIfNull(update, () => $"update is null !!!");
var request = update.Item2;
NullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!!");
var (result, _) = await dynamo_client.simpleQueryDocTypesWithUpdateItemRequest<UgcNpcRankDoc>(request, Guid.NewGuid().ToString("N"));
return result;
}
private static (Result, UpdateItemRequest?) makeUpdateItemRequestForUgcNpcRank( DynamoDbClient dbClient
, Dictionary<string, AttributeValue> attributeValueWithPrimaryKey
, string targetAttribName
, string guid
, double deltaCount )
{
var result = new Result();
var table = dbClient.getTableByName(ServerBase.DynamoDbDefine.TableNames.Main);
var query_builder = new DynamoDbItemRequestHelper.UpdateItemRequestBuilder(table.TableName);
query_builder.withKeys(attributeValueWithPrimaryKey);
var target_doc = new UgcNpcRankDoc();
var attrib_path_json_string = target_doc.toJsonStringOfAttribs();
var target_key = JsonHelper.getJsonPropertyName<UgcNpcRankAttrib>(targetAttribName);
(var is_success, var attribute_expression) = DynamoDbClientHelper.toAttributeExpressionFromJson(attrib_path_json_string, target_key);
if (false == is_success)
{
var err_msg = $"Failed to DynamoDbClientHelper.toAttributeExpressionFromJson() !!! : attribPath:{attrib_path_json_string}, targetKey:{target_key}";
result.setFail(ServerErrorCode.AttribPathMakeFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
var attributeNames = DynamoDbClientHelper.toExpressionAttributeNamesFromJson(attrib_path_json_string, target_key);
attributeNames.Add($"#{guid}", guid);
query_builder.withExpressionAttributeNames(attributeNames);
attribute_expression += $".#{guid}";
var update_expression = (deltaCount >= 0)
? $"SET {attribute_expression} = if_not_exists({attribute_expression}, :start) + :changeValue"
: $"SET {attribute_expression} = if_not_exists({attribute_expression}, :start) - :changeValue";
query_builder.withUpdateExpression(update_expression);
var expression_attribute_values = new Dictionary<string, AttributeValue>
{
{ ":changeValue", new AttributeValue { N = Math.Abs(deltaCount).ToString() } },
{ ":start", new AttributeValue { N = "0" } }
};
query_builder.withExpressionAttributeValues(expression_attribute_values);
query_builder.withReturnValues(ReturnValue.ALL_NEW);
return query_builder.build();
}
}

View File

@@ -0,0 +1,128 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer.PacketHandler;
[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_UGC_NPC_RANK), typeof(GetUgcNpcRankPacketHandler), typeof(GameLoginListener))]
public class GetUgcNpcRankPacketHandler : PacketRecvHandler
{
public static bool send_GS2C_ACK_UGC_NPC_RANK(Player owner, Result result, UgcNpcRankType rankType, UgcNpcRankState rankState, List<UgcNpcRank>? ranking)
{
var ack_packet = new ClientToGame();
ack_packet.Response = new ClientToGameRes();
ack_packet.Response.ErrorCode = result.ErrorCode;
ack_packet.Response.AckUgcNpcRank = new();
if (result.isSuccess())
{
ack_packet.Response.AckUgcNpcRank.Type = rankType;
ack_packet.Response.AckUgcNpcRank.State = rankState;
ranking ??= new List<UgcNpcRank>();
ack_packet.Response.AckUgcNpcRank.UgcNpcRank.AddRange(ranking);
}
if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet))
{
return false;
}
return true;
}
public override async Task<Result> onProcessPacket(ISession entityWithSession, IMessage recvMessage)
{
var result = new Result();
string err_msg;
var player = entityWithSession as Player;
NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!");
var server_logic = GameServerApp.getServerLogic();
// 1. 기본 정보 체크
var request = (recvMessage as ClientToGame)?.Request.ReqUgcNpcRank;
if (null == request)
{
err_msg = $"Failed to get Request !!! : {nameof(ClientToGame.Request.ReqUgcNpcRank)}";
result.setFail(ServerErrorCode.InvalidArgument, err_msg);
Log.getLogger().error(result.toBasicString());
send_GS2C_ACK_UGC_NPC_RANK(player,result, UgcNpcRankType.None, UgcNpcRankState.None, null);
return result;
}
var ranking_entity = server_logic.findGlobalEntity<UgcNpcRankEntity>();
if (null == ranking_entity)
{
err_msg = $"Failed to get Request !!! : {nameof(ClientToGame.Request.ReqUgcNpcRank)}";
result.setFail(ServerErrorCode.UgcNpcRankEntityIsNotFound, err_msg);
Log.getLogger().error(result.toBasicString());
send_GS2C_ACK_UGC_NPC_RANK(player,result, request.Type, request.State, null);
return result;
}
// 2. request validation 체크
if (false == checkRequestValidation(request))
{
err_msg = $"Failed to get Request !!! : {nameof(ClientToGame.Request.ReqUgcNpcRank)}";
result.setFail(ServerErrorCode.InvalidArgument, err_msg);
Log.getLogger().error(result.toBasicString());
send_GS2C_ACK_UGC_NPC_RANK(player,result, request.Type, request.State, null);
return result;
}
// 3. ranking data 조회
(Result result, List<UgcNpcRank>? ranks) res_rank = new();
res_rank.result = result;
switch (request.Type)
{
case UgcNpcRankType.Like:
var like_action = ranking_entity.getEntityAction<UgcNpcLikeRankAction>();
NullReferenceCheckHelper.throwIfNull(like_action, () => $"UgcNpcLikeRankAction is null !!! - player:{player.toBasicString()}");
res_rank = await like_action.getRanks(request.State);
break;
case UgcNpcRankType.Communication:
var communication_action = ranking_entity.getEntityAction<UgcNpcCommunicationRankAction>();
NullReferenceCheckHelper.throwIfNull(communication_action, () => $"UgcNpcCommunicationRankAction is null !!! - player:{player.toBasicString()}");
res_rank = await communication_action.getRanks(request.State);
break;
case UgcNpcRankType.Quest:
var quest_action = ranking_entity.getEntityAction<UgcNpcQuestRankAction>();
NullReferenceCheckHelper.throwIfNull(quest_action, () => $"UgcNpcQuestRankAction is null !!! - player:{player.toBasicString()}");
res_rank = await quest_action.getRanks(request.State);
break;
}
send_GS2C_ACK_UGC_NPC_RANK(player, res_rank.result, request.Type, request.State, res_rank.ranks);
return result;
}
private bool checkRequestValidation(ClientToGameReq.Types.C2GS_REQ_UGC_NPC_RANK request)
{
// 1. state 체크
if (request.State == UgcNpcRankState.None) return false;
// 2. type 체크
if (request.Type == UgcNpcRankType.None) return false;
return true;
}
}

View File

@@ -0,0 +1,30 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class NotifyUgcNpcRankRefreshHandler
{
public async Task recvUgcNpcRankRefresh(ServerMessage.Types.GS2GS_NTF_UGC_NPC_RANK_REFRESH notify)
{
var ugc_npc_rank_entity = GameServerApp.getServerLogic().findGlobalEntity<UgcNpcRankEntity>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_entity, () => $"ugc_npc_rank_entity is null !!!");
var action = ugc_npc_rank_entity.getEntityAction<UgcNpcRankManageAction>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_entity, () => $"ugc_npc_rank_entity is null !!!");
NullReferenceCheckHelper.throwIfNull(action, () => $"action is null !!!");
// reload data
await action.reloadAllUgcNpcRanks();
// notify to client
await action.notifyClientUgcNpcRankRefresh(false);
}
}

View File

@@ -0,0 +1,49 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace GameServer;
public class UgcNpcRankEntity : EntityBase
{
public const int DefaultPageNum = 0;
public const int DefaultPageSize = 100;
public UgcNpcRankEntity() : base(EntityType.UgcNpcRank)
{
}
public override async Task<Result> onInit()
{
// attribute
addEntityAttribute(new UgcNpcLikeRankAttribute(this));
addEntityAttribute(new UgcNpcCommunicationRankAttribute(this));
addEntityAttribute(new UgcNpcQuestRankAttribute(this));
// action
addEntityAction(new UgcNpcLikeRankAction(this));
addEntityAction(new UgcNpcCommunicationRankAction(this));
addEntityAction(new UgcNpcQuestRankAction(this));
addEntityAction(new UgcNpcRankManageAction(this));
return await Task.FromResult(new Result());
}
public override string toBasicString()
{
return $"{this.getTypeName()}";
}
public override string toSummaryString()
{
return $"{this.getTypeName()}";
}
}