using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; using Newtonsoft.Json; using ServerCore; using ServerBase; using ServerCommon.BusinessLogDomain; using SESSION_ID = System.Int32; using WORLD_ID = System.UInt32; using META_ID = System.UInt32; using ENTITY_GUID = System.String; using ACCOUNT_ID = System.String; using OWNER_GUID = System.String; using USER_GUID = System.String; using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; using Amazon.S3.Model; namespace ServerCommon; public static class MoneyAttributeExtensions { public static double getCurrencyFromType(this MoneyAttribute attribute, CurrencyType type) { var currency = type switch { CurrencyType.Gold => attribute.Gold, CurrencyType.Sapphire => attribute.Sapphire, CurrencyType.Calium => attribute.Calium, CurrencyType.Ruby => attribute.Ruby, _ => 0 }; return currency; } public static void setCurrencyFromType(this MoneyAttribute attribute, CurrencyType type, double currency) { var result = type switch { CurrencyType.Gold => attribute.Gold = currency, CurrencyType.Sapphire => attribute.Sapphire = currency, CurrencyType.Calium => attribute.Calium = currency, CurrencyType.Ruby => attribute.Ruby = currency, _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; } } public class MoneyAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute, IWithCommonResultFiller { [JsonProperty] public double Gold { get { return m_gold; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.MoneyGold, value - m_gold, value); } m_gold = value; } } private double m_gold = 0; [JsonProperty] public double Sapphire { get { return m_sapphire; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.MoneySaphire, value - m_sapphire, value); } m_sapphire = value; } } private double m_sapphire = 0; [JsonProperty] public double Calium { get { return m_calium; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.MoneyCalium, value - m_calium, value); } m_calium = value; } } private double m_calium = 0; [JsonProperty] public double Ruby { get { return m_ruby; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.MoneyRuby, value - m_ruby, value); } m_ruby = value; } } private double m_ruby = 0; //========================================================================================= // 기본 생성자 //========================================================================================= public MoneyAttribute(UserBase owner) : base(owner, true) { } public override void onClear() { Gold = 0; Sapphire = 0; Calium = 0; Ruby = 0; getAttributeState().reset(); } public override EntityAttributeBase onCloned() { var owner = getOwner() as UserBase; NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!"); var cloned = new MoneyAttribute(owner); cloned.Gold = Gold; cloned.Sapphire = Sapphire; cloned.Calium = Calium; cloned.Ruby = Ruby; return cloned; } public override IEntityAttributeTransactor onNewEntityAttributeTransactor() { return new MoneyAttributeTransactor(getOwner()); } public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) { var result = new Result(); var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var user_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {owner.toBasicString()}"); var user_guid = user_attribute.UserGuid; //===================================================================================== // Attribute => try pending Doc //===================================================================================== var try_pending_doc = getTryPendingDocBase() as MoneyDoc; if (null == try_pending_doc) { var to_copy_doc = new MoneyDoc(OwnerEntityType.User, user_guid); var origin_doc = getOriginDocBase(); if (null != origin_doc) { to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); } try_pending_doc = to_copy_doc; setTryPendingDocBase(try_pending_doc); } var to_copy_money_attrib = try_pending_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(to_copy_money_attrib, () => $"to_copy_money_attrib is null !!! - {owner.toBasicString()}"); to_copy_money_attrib.Gold = Gold; to_copy_money_attrib.Sapphire = Sapphire; to_copy_money_attrib.Calium = Calium; to_copy_money_attrib.Ruby = Ruby; if (false == isForQuery) { return (result, try_pending_doc); } //===================================================================================== // Doc QueryType 반영 //===================================================================================== (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); if (result.isFail()) { return (result, null); } return (result, to_query_doc); } public void onFillCommonResult(EntityCommonResult commonResult, EntityAttributeBase origin, QueryBatchBase? queryBatch = null) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var before = origin as MoneyAttribute; NullReferenceCheckHelper.throwIfNull(before, () => $"before is null !!! - {owner.toBasicString()}"); var after = this as MoneyAttribute; NullReferenceCheckHelper.throwIfNull(after, () => $"after is null !!! - {owner.toBasicString()}"); var recorder = after.getEntityRecorder(); NullReferenceCheckHelper.throwIfNull(recorder, () => $"recorder is null !!! - {owner.toBasicString()}"); var money_result = commonResult.Money; NullReferenceCheckHelper.throwIfNull(money_result, () => $"money_result is null !!! - {owner.toBasicString()}"); var target_types = EnumHelper.getValuesBeginEndBetweenWord("Money_"); foreach (var type in target_types) { var currency_type = type.toCurrencyType(); if (CurrencyType.None == currency_type) { var err_msg = $"Failed to toCurrencyType() !!! : EntityDeltaType:{type} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } var found_delta_counter = recorder.findDeltaCounter(type); if (null != found_delta_counter) { var currency_type_key = (Int32)currency_type; var delta_count = found_delta_counter.getDeltaCount(); if (false == money_result.Moneys.TryGetValue(currency_type_key, out var found_money)) { found_money = new Money(); money_result.Moneys[currency_type_key] = found_money; } found_money.Amount = found_delta_counter.getTotalCount(); if (false == money_result.Deltas.TryGetValue(currency_type_key, out var found_money_delta_amount)) { found_money_delta_amount = new MoneyDeltaAmount(); money_result.Deltas[currency_type_key] = found_money_delta_amount; } found_money_delta_amount.DeltaType = AmountDeltaType.Merge; found_money_delta_amount.Amount = delta_count; appendOrWriteBusinessLog4Money(currency_type, delta_count, found_money.Amount, queryBatch); } } } private void appendOrWriteBusinessLog4Money( CurrencyType currencyType, double deltaCount, double amount , QueryBatchBase? queryBatch = null ) { var owner = getOwner(); var delta_type = AmountDeltaType.None; if (deltaCount > 0) { delta_type = AmountDeltaType.Acquire; } else if (deltaCount < 0) { delta_type = AmountDeltaType.Consume; } var currency_delta_log = new CurrencyDeltaUpdateBusinessLog( currencyType , delta_type, deltaCount, amount ); if (null != queryBatch) { queryBatch.appendBusinessLog(currency_delta_log); } else { var log_actor = owner as IWithLogActor; NullReferenceCheckHelper.throwIfNull(log_actor, () => $"log_actor is null !!! - {owner.toBasicString()}"); BusinessLogger.collectLog(log_actor, currency_delta_log); } } public Result onMerge(EntityAttributeBase otherEntityAttribute) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var result = new Result(); var err_msg = string.Empty; //===================================================================================== // OtherAttribute => Attribute //===================================================================================== var money_attribute = otherEntityAttribute as MoneyAttribute; if (null == money_attribute) { err_msg = $"Failed to cast MoneyAttribute !!!, money_attribute is null - {toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); Log.getLogger().error(result.toBasicString()); return result; } Gold = money_attribute.Gold; Sapphire = money_attribute.Sapphire; Calium = money_attribute.Calium; Ruby = money_attribute.Ruby; //===================================================================================== // Attribute Try Pending Doc => Origin Doc //===================================================================================== var try_pending_doc = money_attribute.getTryPendingDocBase() as MoneyDoc; if (null != try_pending_doc) { money_attribute.resetTryPendingDocBase(); syncOriginDocBaseWithNewDoc(try_pending_doc); } var origin_doc_base = getOriginDocBase() as MoneyDoc; if (null == origin_doc_base) { // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! return result; } var money_attrib = origin_doc_base.getAttrib(); NullReferenceCheckHelper.throwIfNull(money_attrib, () => $"money_attrib is null !!! - {toBasicString()}, {owner.toBasicString()}"); money_attrib.Gold = Gold; money_attrib.Sapphire = Sapphire; money_attrib.Calium = Calium; money_attrib.Ruby = Ruby; return result; } public bool copyEntityAttributeFromDoc(DynamoDbDocBase newDocBase) { var owner = getOwner(); ArgumentNullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var err_msg = string.Empty; var to_cast_string = typeof(ItemDoc).Name; var money_doc = newDocBase as MoneyDoc; if (null == money_doc) { err_msg = $"money_doc is null !!!, in copyEntityAttributeFromDoc() : docName:{nameof(MoneyDoc)} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return false; } //===================================================================================== // New Doc => Origin Doc //===================================================================================== syncOriginDocBaseWithNewDoc(money_doc); //===================================================================================== // Origin Doc => Attribute //===================================================================================== var money_attrib = money_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(money_attrib, () => $"money_attrib is null !!! - {owner.toBasicString()}"); Gold = money_attrib.Gold; Sapphire = money_attrib.Sapphire; Calium = money_attrib.Calium; Ruby = money_attrib.Ruby; return true; } } public class MoneyAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute { public MoneyAttributeTransactor(EntityBase owner) : base(owner) { } public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) { var err_msg = string.Empty; var to_cast_string = typeof(MoneyAttribute).Name; var copy_from_money_attribute = entityAttributeBase as MoneyAttribute; if (null == copy_from_money_attribute) { err_msg = $"Failed to copyEntityAttributeTransactorFromEntityAttribute() !!!, copy_from_money_attribute is null :{to_cast_string}"; Log.getLogger().error(err_msg); return false; } var copy_to_money_attribute = getClonedEntityAttribute() as MoneyAttribute; if (null == copy_to_money_attribute) { err_msg = $"Failed to copyEntityAttributeTransactorFromEntityAttribute() !!!, copy_to_money_attribute is null :{to_cast_string}"; Log.getLogger().error(err_msg); return false; } copy_to_money_attribute.Gold = copy_to_money_attribute.Gold; copy_to_money_attribute.Sapphire = copy_to_money_attribute.Sapphire; copy_to_money_attribute.Calium = copy_to_money_attribute.Calium; copy_to_money_attribute.Ruby = copy_to_money_attribute.Ruby; return true; } }