298 lines
13 KiB
C#
298 lines
13 KiB
C#
using Amazon.DynamoDBv2;
|
|
using Amazon.DynamoDBv2.Model;
|
|
|
|
|
|
using ServerCore;
|
|
using ServerBase;
|
|
using ServerCommon;
|
|
using ServerCommon.BusinessLogDomain;
|
|
using MetaAssets;
|
|
|
|
|
|
namespace GameServer;
|
|
|
|
public class CaliumEventAction : EntityActionBase
|
|
{
|
|
private enum ReTryStatus
|
|
{
|
|
None = 0,
|
|
Success = 1,
|
|
Fail = 2
|
|
}
|
|
|
|
public CaliumEventAction(CaliumStorageEntity owner) : base(owner) { }
|
|
|
|
public override async Task<Result> onInit() => await Task.FromResult(new Result());
|
|
|
|
public override void onClear() {}
|
|
|
|
public async Task<Result> sendCaliumEventFromDB(CaliumEventDoc sendDoc)
|
|
{
|
|
var owner = getOwner() as CaliumStorageEntity;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!");
|
|
|
|
var web3_action = owner.getEntityAction<CaliumWeb3Action>();
|
|
NullReferenceCheckHelper.throwIfNull(web3_action, () => $"calium_web3_action is null !!! - {getOwner().toBasicString()}");
|
|
|
|
// 0. 이벤트 전송
|
|
var request = CaliumStorageHelper.makeCaliumEventRequest(sendDoc);
|
|
var (result, response) = await web3_action.postCaliumEvent(request);
|
|
|
|
// 1. 성공시 리턴 ( 상태 수정 : Success )
|
|
if (result.isSuccess())
|
|
{
|
|
await updateCaliumEventStatus(sendDoc, CaliumEventStatus.Sending, CaliumEventStatus.Success);
|
|
return result;
|
|
}
|
|
|
|
var res = checkRetryFail(result.ErrorCode, response?.m_code);
|
|
|
|
// 2. 이미 처리된 데이터 ( 상태 수정 : Success )
|
|
if (res == ReTryStatus.Success)
|
|
{
|
|
await updateCaliumEventStatus(sendDoc, CaliumEventStatus.Sending, CaliumEventStatus.Success);
|
|
return new Result();
|
|
}
|
|
|
|
// 3. 실패시 재시도 (상태 수정 : Regist)
|
|
if (res == ReTryStatus.None)
|
|
{
|
|
await updateCaliumEventStatus(sendDoc, CaliumEventStatus.Sending, CaliumEventStatus.Regist);
|
|
return result;
|
|
}
|
|
|
|
// 3. 재시도 불가 기록 (상태 수정 : Failed)
|
|
await updateCaliumEventStatus(sendDoc, CaliumEventStatus.Sending, CaliumEventStatus.Failed);
|
|
|
|
// 4. 로그 기록
|
|
var log = new CaliumEchoSystemFailLogData { EventData = request };
|
|
log.FailCode = response?.m_code ?? string.Empty;
|
|
log.FailMessages = response?.m_messages ?? new();
|
|
log.EventData = request;
|
|
CaliumStorageHelper.writeFailEchoSystemLog(owner, log);
|
|
Log.getLogger().error($"Failed to send Calium Event !! : {owner.toBasicString()}");
|
|
|
|
return new Result();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 칼리움 소각시 Web3 통보 함수
|
|
/// </summary>
|
|
/// <param name="actor"> 소각 행위자 </param>
|
|
/// <param name="eventGuid"> Transaction ID </param>
|
|
/// <param name="userNickname"> 유저 닉네임 </param>
|
|
/// <param name="subType"> 소각 행위 구분 </param>
|
|
/// <param name="caliumDelta"> 칼리움 변화량 ( 음수값 ) </param>
|
|
/// <returns></returns>
|
|
public async Task<Result> sendCaliumBurnEvent(IWithLogActor actor, string eventGuid, string userNickname, string subType, double caliumDelta)
|
|
{
|
|
var result = new Result();
|
|
|
|
if (ServerCore.TypeHelper.NumericSignType.Positive == ServerCore.TypeHelper.checkNumericSignType<double>(caliumDelta))
|
|
{
|
|
caliumDelta *= -1;
|
|
}
|
|
|
|
var send = await sendCaliumEvent(actor, CaliumEventType.calium_burn, eventGuid, userNickname, subType, 0, caliumDelta, true);
|
|
|
|
if (false == send.is_success)
|
|
{
|
|
var err_msg =
|
|
$"fail to send calium event !! : eventGuid[{eventGuid}] subtype[{subType}] userNickanme[{userNickname}] caliumDelta[{caliumDelta}] / {nameof(sendCaliumEventFromPlayer)}";
|
|
result.setFail(ServerErrorCode.FailToSendEchoSystem, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<(bool is_success, CaliumEventData event_data)> sendCaliumEventFromPlayer(Player player, CaliumEventType type, string subType, double sapphireDelta, double caliumdelta, bool isReTry = true)
|
|
{
|
|
return await sendCaliumEvent(player, type, player.getUserGuid(), player.getUserNickname(), subType, sapphireDelta, caliumdelta, isReTry);
|
|
}
|
|
|
|
public async Task<(Result result, CaliumEventDoc? change_doc)> updateCaliumEventStatus(CaliumEventDoc targetDoc, CaliumEventStatus beforeStatus, CaliumEventStatus afterStatus)
|
|
{
|
|
var result = new Result();
|
|
|
|
var server_logic = GameServerApp.getServerLogic();
|
|
var dynamoDb_client = server_logic.getDynamoDbClient();
|
|
NullReferenceCheckHelper.throwIfNull(dynamoDb_client, () => $"dynamodb client is null !!! ");
|
|
|
|
var make_primary_key = targetDoc.getPrimaryKey();
|
|
|
|
var updateRequest = makeUpdateItemRequest(targetDoc, make_primary_key.toKeyWithAttributeValue(), nameof(CaliumEventAttrib.Status), beforeStatus, afterStatus);
|
|
if (updateRequest.result.isFail() || null == updateRequest.request)
|
|
{
|
|
if (updateRequest.result.isSuccess())
|
|
{
|
|
result.setFail(ServerErrorCode.FailToGetEchoSystemException, $"failed to make item request!!! - {getOwner().toBasicString()}");
|
|
}
|
|
return (result, null);
|
|
}
|
|
|
|
(result, var change_doc) = await dynamoDb_client.simpleQueryDocTypesWithUpdateItemRequest<CaliumEventDoc>(updateRequest.request);
|
|
return (result, change_doc);
|
|
}
|
|
|
|
private async Task<(bool is_success, CaliumEventData event_data)> sendCaliumEvent(IWithLogActor actor, CaliumEventType type, string eventGuid, string userNickname, string subType, double sapphireDelta, double caliumDelta, bool isRetry)
|
|
{
|
|
var result = new Result();
|
|
|
|
var owner = getOwner() as CaliumStorageEntity;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!");
|
|
|
|
var web3_action = owner.getEntityAction<CaliumWeb3Action>();
|
|
NullReferenceCheckHelper.throwIfNull(web3_action, () => $"calium_web3_action is null !!! - {getOwner().toBasicString()}");
|
|
|
|
// 0. echoSystem 전송
|
|
var request = await CaliumStorageHelper.makeCaliumEventRequest(owner, type, subType, userNickname, sapphireDelta, caliumDelta);
|
|
(result, var response) = await web3_action.postCaliumEvent(request);
|
|
|
|
// 1. 성공시 리턴
|
|
if (result.isSuccess()) return (true, request);
|
|
|
|
// 2. 실패시 DB 기록 및 Retry
|
|
if (isRetry)
|
|
{
|
|
var event_guid = await retryPostCaliumEvent(eventGuid, result.ErrorCode, request, response);
|
|
result.setSuccess();
|
|
return (true, request);
|
|
}
|
|
|
|
// 3. 실패시 실패 로그 기록
|
|
var fail_log = new CaliumEchoSystemFailLogData();
|
|
fail_log.FailCode = response?.m_code ?? string.Empty;
|
|
fail_log.FailMessages = response?.m_messages ?? new();
|
|
fail_log.ReTry = false;
|
|
fail_log.EventData = request;
|
|
|
|
CaliumStorageHelper.writeFailEchoSystemLog(actor, fail_log);
|
|
|
|
return (false, request);
|
|
}
|
|
|
|
private async Task<string> retryPostCaliumEvent(string userGuid, ServerErrorCode errCode, CaliumEventRequest request, EchoSystemBaseResponse? response)
|
|
{
|
|
var calium_entity = getOwner() as CaliumStorageEntity;
|
|
NullReferenceCheckHelper.throwIfNull(calium_entity, () => "calium_entity is null !!!");
|
|
|
|
string event_guid;
|
|
|
|
var res = checkRetryFail(errCode, response?.m_code);
|
|
|
|
// 0. 이미 처리된 메시지
|
|
if (res == ReTryStatus.Success)
|
|
{
|
|
event_guid = await saveCaliumEventToDB(userGuid, request, CaliumEventStatus.Success);
|
|
return event_guid;
|
|
}
|
|
|
|
// 1. 실패시 재시도 저장
|
|
if (res != ReTryStatus.Fail)
|
|
{
|
|
event_guid = await saveCaliumEventToDB(userGuid, request, CaliumEventStatus.Regist);
|
|
return event_guid;
|
|
}
|
|
|
|
// 2. 재시도 불가 기록
|
|
event_guid = await saveCaliumEventToDB(userGuid, request, CaliumEventStatus.Failed);
|
|
|
|
// 3. 로그 기록 ( 비즈니스? DB? )
|
|
var fail_log = new CaliumEchoSystemFailLogData();
|
|
fail_log.FailCode = response?.m_code ?? string.Empty;
|
|
fail_log.FailMessages = response?.m_messages ?? new();
|
|
fail_log.ReTry = true;
|
|
fail_log.EventData = request;
|
|
|
|
CaliumStorageHelper.writeFailEchoSystemLog(calium_entity, fail_log);
|
|
|
|
return event_guid;
|
|
}
|
|
|
|
private ReTryStatus checkRetryFail(ServerErrorCode errCode, string? code)
|
|
{
|
|
// 1. http 에러인 경우
|
|
if (ServerErrorCode.FailToGetEchoSystemHttpError == errCode) return ReTryStatus.Fail;
|
|
|
|
// 2. DB insert fail 인 경우
|
|
if (ServerErrorCode.GetFailEchoSystemResponse == errCode && code == "RET.WCE.101") return ReTryStatus.None;
|
|
|
|
// 3. 이미 처리된 Message 인 경우 ( 성공 처리 )
|
|
if (ServerErrorCode.GetFailEchoSystemResponse == errCode && code == "FIN_WCE_200") return ReTryStatus.Success;
|
|
|
|
return ReTryStatus.Fail;
|
|
}
|
|
|
|
private async Task<string> saveCaliumEventToDB(string userGuid, CaliumEventRequest request, CaliumEventStatus isRetry)
|
|
{
|
|
var doc = new CaliumEventDoc(request.m_event_id);
|
|
var attrib = doc.getAttrib<CaliumEventAttrib>();
|
|
NullReferenceCheckHelper.throwIfNull(attrib, () => $"calium event attrib is null !!! - userGuid[{userGuid}] / {getOwner().toBasicString()}");
|
|
|
|
attrib.UserGuid = userGuid;
|
|
attrib.EventData.m_server_type = request.m_server_type;
|
|
attrib.EventData.m_event_type = request.m_event_type;
|
|
attrib.EventData.m_sub_type = request.m_sub_type;
|
|
attrib.EventData.m_div_type = request.m_div_type;
|
|
attrib.EventData.m_div_id = request.m_div_id;
|
|
attrib.EventData.m_calium_delta = request.m_calium_delta;
|
|
attrib.EventData.m_sapphire_delta = request.m_sapphire_delta;
|
|
attrib.EventData.m_current_epoch = request.m_current_epoch;
|
|
attrib.EventData.m_current_inflation_rate = request.m_current_inflation_rate;
|
|
attrib.Status = isRetry;
|
|
|
|
var dynamoDb_client = GameServerApp.getServerLogic().getDynamoDbClient();
|
|
await dynamoDb_client.simpleUpsertDocumentWithDocType(doc);
|
|
|
|
return request.m_event_id;
|
|
}
|
|
|
|
private (Result result, UpdateItemRequest? request) makeUpdateItemRequest( CaliumEventDoc target_doc, Dictionary<string, AttributeValue> attributeValueWithPrimaryKey
|
|
, string targetAttribName
|
|
, CaliumEventStatus beforeStatus
|
|
, CaliumEventStatus afterStatus )
|
|
{
|
|
var server_logic = GameServerApp.getServerLogic();
|
|
var dynamoDb_client = server_logic.getDynamoDbClient();
|
|
NullReferenceCheckHelper.throwIfNull(dynamoDb_client, () => $"dynamodb client is null !!! ");
|
|
|
|
var result = new Result();
|
|
|
|
var query_builder = new DynamoDbItemRequestHelper.UpdateItemRequestBuilder(dynamoDb_client.getTableFullName(target_doc.TableName));
|
|
query_builder.withKeys(attributeValueWithPrimaryKey);
|
|
|
|
var attrib_path_json_string = target_doc.toJsonStringOfAttribs();
|
|
var target_key = JsonHelper.getJsonPropertyName<CaliumEventAttrib>(targetAttribName);
|
|
var (is_success, 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);
|
|
}
|
|
|
|
query_builder.withExpressionAttributeNames(DynamoDbClientHelper.toExpressionAttributeNamesFromJson(attrib_path_json_string, target_key));
|
|
|
|
// 변경값 설정
|
|
var update_expression = $"SET {attribute_expression} = :changeValue";
|
|
query_builder.withUpdateExpression(update_expression);
|
|
|
|
// 조건 추가
|
|
query_builder.withConditionExpression($"{attribute_expression} = :beforeValue");
|
|
|
|
// 설정값 등록
|
|
var expression_attribute_values = new Dictionary<string, AttributeValue>
|
|
{
|
|
{ ":changeValue", new AttributeValue { S = afterStatus.ToString() } },
|
|
{ ":beforeValue", new AttributeValue { S = beforeStatus.ToString() } }
|
|
};
|
|
query_builder.withExpressionAttributeValues(expression_attribute_values);
|
|
|
|
query_builder.withReturnValues(ReturnValue.ALL_NEW);
|
|
|
|
return query_builder.build();
|
|
}
|
|
} |