using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.ConstrainedExecution; using NLog; using Amazon.DynamoDBv2.Model; using Newtonsoft.Json; using Pipelines.Sockets.Unofficial.Buffers; using StackExchange.Redis; using Google.Protobuf.Collections; using ServerControlCenter; using Amazon.S3.Model; using ServerCore; using ServerBase; using SESSION_ID = System.Int32; using WORLD_ID = System.UInt32; using META_ID = System.UInt32; using TRANSACTION_NAME = System.String; using TRANSACTION_GUID = System.String; 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; namespace ServerBase; public class ReservedSlotOnInven { public class ReservedSlot { private Int16 m_reserved_count = 0; private EntityBase? m_equip_entity_nullable; private EntityBase? m_unequip_entity_nullable; public ReservedSlot() { } public void incReservedCount(Int16 reservedItemCount) { m_reserved_count += reservedItemCount; } public void decReservedCount(Int16 reservedItemCount) { m_reserved_count -= reservedItemCount; } public Int16 getReservedCount() => m_reserved_count; public void setEquipEntity(EntityBase? entityBase) => m_equip_entity_nullable = entityBase; public EntityBase? getEquipEntity() => m_equip_entity_nullable; public void setUnequipEntity(EntityBase? entityBase) => m_unequip_entity_nullable = entityBase; public EntityBase? getUnequipEntity() => m_unequip_entity_nullable; } private EntityType m_entity_inven_type = EntityType.None; private readonly Dictionary m_reserved_slots = new(); public ReservedSlotOnInven(EntityType entityInvenType) { m_entity_inven_type = entityInvenType; } public void addOrInc(string slotKey, Int16 incReservedCount) { if(false == m_reserved_slots.TryGetValue(slotKey, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlot(); m_reserved_slots.Add(slotKey, found_reserved_slot); } found_reserved_slot.incReservedCount(incReservedCount); } public void addOrDec(string slotKey, Int16 decReservedCount) { if (false == m_reserved_slots.TryGetValue(slotKey, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlot(); m_reserved_slots.Add(slotKey, found_reserved_slot); } found_reserved_slot.decReservedCount(decReservedCount); } public Dictionary getReservedSlots() => m_reserved_slots; public EntityType getEntityType() => m_entity_inven_type; } public partial class TransactionRunner : IDisposable { private readonly EntityBase m_owner; private readonly string m_trans_id = string.Empty; private readonly TransactionIdType m_transaction_id_type = TransactionIdType.None; private readonly ConcurrentDictionary> m_entity_attribute_transactors = new(); private readonly ConcurrentQueue m_to_db_query_transactors = new(); // Db Query용 (순서 보장 중요) private readonly ConcurrentDictionary> m_reserved_inven_slots_of_owners = new(); private readonly ConcurrentDictionary m_entity_common_results = new(); //IRemoteTransaction private readonly List> m_remote_transaction_funcs = new(); private string m_transaction_name; private bool m_is_binding = false; public TransactionRunner(EntityBase owner, TransactionIdType idType, string transactionName) { m_owner = owner; m_trans_id = System.Guid.NewGuid().ToString("N"); m_transaction_id_type = idType; m_transaction_name = transactionName; } public TransactionRunner(EntityBase owner, string transId, TransactionIdType idType, string transactionName) { m_owner = owner; m_trans_id = transId; m_transaction_id_type = idType; m_transaction_name = transactionName; } public Result beginTransaction() { var result = new Result(); var err_msg = string.Empty; result = m_owner.bindTransactionRunner(this); if(result.isFail()) { return result; } setBinding(); return result; } public void clearTransaction() { m_entity_attribute_transactors.Clear(); m_reserved_inven_slots_of_owners.Clear(); m_to_db_query_transactors.Clear(); } // override IDisposable.Dispose public void Dispose() { var err_msg = string.Empty; if (false == isBinding()) { return; } var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); if (false == m_owner.unbindTransactionRunner(this)) { err_msg = $"Failed to unbindTransactionRunner() !!! : {toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return; } } public void resetTransaction(string transactionName) { clearTransaction(); m_transaction_name = transactionName; } public async Task onCommitResults(QueryBatchBase? queryBatch = null) { await Task.CompletedTask; var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var result = new Result(); var err_msg = string.Empty; var transactors = getEntityAttributeTransactorAll(); // 메모리를 동기화 한다. foreach (var to_sync_merge in transactors) { var origin_entity_attribute = to_sync_merge.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); var to_merge_attribute = origin_entity_attribute as IMergeWithEntityAttribute; if (null == to_merge_attribute) { err_msg = $"Failed to cast IMergeWithEntityAttribute !!! : OriginEntityAttribute:{origin_entity_attribute.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } var cloned_entity_attribute = to_sync_merge.getClonedEntityAttribute(); if (null == cloned_entity_attribute) { err_msg = $"getClonedEntityAttribute() is null !!! : OriginEntityAttribute:{origin_entity_attribute.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } var comon_result_filler = cloned_entity_attribute as IWithCommonResultFiller; if (null != comon_result_filler) { (result, var entity_common_result) = getOrNewEntityCommonResult(cloned_entity_attribute); if (result.isFail()) { Log.getLogger().fatal(result.toBasicString()); } else { NullReferenceCheckHelper.throwIfNull(entity_common_result, () => $"entity_common_result is null !!! - {owner.toBasicString()}"); comon_result_filler.onFillCommonResult(entity_common_result, origin_entity_attribute, queryBatch); } } result = to_merge_attribute.onMerge(cloned_entity_attribute); if (result.isFail()) { err_msg = $"Failed to onMerge() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().debug(err_msg); continue; } } var reserved_iven_slots_of_owners = getReservedInvenSlotAllOfOwners(); foreach(var each in reserved_iven_slots_of_owners) { var inven_owner = each.Key; var reserved_inven_slots = each.Value; var to_merge_inventory = inven_owner as IMergeWithInventory; if (null != to_merge_inventory) { result = await to_merge_inventory.onMerge(reserved_inven_slots.Values.ToList(), this); if (result.isFail()) { err_msg = $"Failed to IMergeWithInventory.onMerge() !!! : {result.toBasicString()} - {inven_owner.toBasicString()}, {owner.toBasicString()}"; Log.getLogger().fatal(err_msg); continue; } } } reserved_iven_slots_of_owners.Clear(); await tryStartRemoteTransaction(); return result; } public async Task onCommitResults4DbQuery(List dbQueriedTransactors, QueryBatchBase? queryBatch = null) { await Task.CompletedTask; var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); NullReferenceCheckHelper.throwIfNull(dbQueriedTransactors, () => $"dbQueriedTransactors is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; foreach (var to_sync_merge in dbQueriedTransactors) { var origin_entity_attribute = to_sync_merge.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); var to_merge_attribute = origin_entity_attribute as IMergeWithEntityAttribute; if (null == to_merge_attribute) { err_msg = $"Failed to cast IMergeWithEntityAttribute !!! : OriginEntityAttribute:{origin_entity_attribute.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } var cloned_entity_attribute = to_sync_merge.getClonedEntityAttribute(); if (null == cloned_entity_attribute) { err_msg = $"getClonedEntityAttribute() is null !!! : OriginEntityAttribute:{origin_entity_attribute.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } if(true == m_entity_attribute_transactors.TryGetValue(origin_entity_attribute.GetType(), out var found_transactors)) { // DB 처리후 여기서 origin_entity_attribute Merge를 하므로 // onCommitResults() 함수로 중복된 Merge를 시도할 수 있으므로 m_entity_attribute_transactors 에서 제거 한다. - kangms found_transactors.Remove(origin_entity_attribute.getOwner().getEntityGuid(), out _); } var comon_result_filler = cloned_entity_attribute as IWithCommonResultFiller; if (null != comon_result_filler) { (result, var entity_common_result) = getOrNewEntityCommonResult(cloned_entity_attribute); if (result.isFail()) { Log.getLogger().fatal(result.toBasicString()); } else { NullReferenceCheckHelper.throwIfNull(entity_common_result, () => $"entity_common_result is null !!! - {owner.toBasicString()}"); comon_result_filler.onFillCommonResult(entity_common_result, origin_entity_attribute, queryBatch); } } result = to_merge_attribute.onMerge(cloned_entity_attribute); if (result.isFail()) { err_msg = $"Failed to onMerge() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } var reserved_iven_slots_of_owners = getReservedInvenSlotAllOfOwners(); foreach (var each in reserved_iven_slots_of_owners) { var inven_owner = each.Key; var reserved_inven_slots = each.Value; var to_merge_inventory = inven_owner as IMergeWithInventory; if (null != to_merge_inventory) { result = await to_merge_inventory.onMerge(reserved_inven_slots.Values.ToList(), this); if (result.isFail()) { err_msg = $"Failed to IMergeWithInventory.onMerge() !!! : {result.toBasicString()} - {inven_owner.toBasicString()}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } } reserved_iven_slots_of_owners.Clear(); await tryStartRemoteTransaction(); return result; } public ConcurrentDictionary getEntityAttributeTransactorAll(Type targetEntityAttribute) { var err_msg = string.Empty; var entity_attribute_transactors = new List(); if(false == m_entity_attribute_transactors.TryGetValue(targetEntityAttribute, out var found_transactors)) { return new ConcurrentDictionary(); } return found_transactors; } public ConcurrentDictionary getEntityAttributeTransactorAll() where TEntityAttributeType : EntityAttributeBase { var err_msg = string.Empty; var entity_attribute_transactors = new List(); var target_type = typeof(TEntityAttributeType); if(false == m_entity_attribute_transactors.TryGetValue(target_type, out var found_transactors)) { return new ConcurrentDictionary(); } return found_transactors; } public List getEntityAttributeTransactorAll() { var err_msg = string.Empty; var entity_attribute_transactors = new List(); foreach (var each_attribute_type in m_entity_attribute_transactors) { foreach (var each_attribute_transactor in each_attribute_type.Value) { var transactor_entity_guid = each_attribute_transactor.Key; var transactor = each_attribute_transactor.Value; if (null == transactor) { continue; } entity_attribute_transactors.Add(transactor); } } return entity_attribute_transactors; } public List getEntityAttributeTransactorAll4DbQuery() { var err_msg = string.Empty; var transactors = new List(); while (false == m_to_db_query_transactors.IsEmpty) { if (true == m_to_db_query_transactors.TryDequeue(out var transactor)) { transactors.Add(transactor); } } return transactors; } public TEntityAttribute? getEntityAttribute(TEntityAttribute fromOriginEntityAttributeBase) where TEntityAttribute : EntityAttributeBase { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(fromOriginEntityAttributeBase, () => $"fromOriginEntityAttributeBase is null !!! - {owner.toBasicString()}"); var to_add_type = fromOriginEntityAttributeBase.GetType(); var entity_guid = fromOriginEntityAttributeBase.getOwner().getEntityGuid(); ConcurrentDictionary? found_transactors; lock (m_entity_attribute_transactors) { if (false == m_entity_attribute_transactors.TryGetValue(to_add_type, out found_transactors)) { found_transactors = new(); m_entity_attribute_transactors[to_add_type] = found_transactors; } } IEntityAttributeTransactor found_transactor; var add_transactor = delegate (ENTITY_GUID entityGuid) { found_transactor = fromOriginEntityAttributeBase.onNewEntityAttributeTransactor(); if (false == found_transactor.cloneFromOriginEntityAttribute(fromOriginEntityAttributeBase)) { var err_msg = $"Failed to cloneFromOriginEntityAttribute() !!! - {toBasicString()}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return null; } m_to_db_query_transactors.Enqueue(found_transactor); return found_transactor; }; found_transactor = found_transactors.GetOrAdd(entity_guid, add_transactor!); if (null == found_transactor) { var err_msg = $"Failed to GetOrAdd() !!! : {fromOriginEntityAttributeBase.toBasicString()} - {toBasicString()}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); } else { return found_transactor.getClonedEntityAttribute() as TEntityAttribute; } return fromOriginEntityAttributeBase as TEntityAttribute; } public TEntityAttribute? getEntityAttributeWithReadOnly(TEntityAttribute fromOriginEntityAttributeBase) where TEntityAttribute : EntityAttributeBase { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(fromOriginEntityAttributeBase, () => $"fromOriginEntityAttributeBase is null !!! - {owner.toBasicString()}"); var to_add_type = fromOriginEntityAttributeBase.GetType(); var entity_guid = fromOriginEntityAttributeBase.getOwner().getEntityGuid(); ConcurrentDictionary? found_transactors; lock (m_entity_attribute_transactors) { m_entity_attribute_transactors.TryGetValue(to_add_type, out found_transactors); IEntityAttributeTransactor? found_transactor = null; if (null != found_transactors) { found_transactors.TryGetValue(entity_guid, out found_transactor); } if (null != found_transactor) { return found_transactor.getClonedEntityAttribute() as TEntityAttribute; } return fromOriginEntityAttributeBase as TEntityAttribute; } } public Result tryIncReserveSlotCount(EntityBase invenOwner, EntityType entityInvenType, string slotType) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(invenOwner, () => $"invenOwner is null !!! - {owner.toBasicString()}"); var result = new Result(); ConcurrentDictionary? found_reserved_inven_slots; lock (m_reserved_inven_slots_of_owners) { if (false == m_reserved_inven_slots_of_owners.TryGetValue(invenOwner, out found_reserved_inven_slots)) { found_reserved_inven_slots = new ConcurrentDictionary(); m_reserved_inven_slots_of_owners[invenOwner] = found_reserved_inven_slots; } } var add_recorder = delegate (EntityType invenType) { var reserved_slot = new ReservedSlotOnInven(entityInvenType); reserved_slot.addOrInc(slotType, 1); return reserved_slot; }; var update_recorder = delegate (EntityType invenType, ReservedSlotOnInven currValue) { if (false == currValue.getReservedSlots().TryGetValue(slotType, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlotOnInven.ReservedSlot(); currValue.getReservedSlots().Add(slotType, found_reserved_slot); } found_reserved_slot.incReservedCount(1); return currValue; }; found_reserved_inven_slots.AddOrUpdate(entityInvenType, add_recorder, update_recorder); return result; } public Result tryDecToReserveSlotCount(EntityBase invenOwner, EntityType entityInvenType, string slotType) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(invenOwner, () => $"invenOwner is null !!! - {owner.toBasicString()}"); var result = new Result(); ConcurrentDictionary? found_reserved_inven_slots; lock (m_reserved_inven_slots_of_owners) { if (false == m_reserved_inven_slots_of_owners.TryGetValue(invenOwner, out found_reserved_inven_slots)) { found_reserved_inven_slots = new ConcurrentDictionary(); m_reserved_inven_slots_of_owners[invenOwner] = found_reserved_inven_slots; } } var add_recorder = delegate (EntityType invenType) { var reserved_slot = new ReservedSlotOnInven(entityInvenType); reserved_slot.addOrDec(slotType, 1); return reserved_slot; }; var update_recorder = delegate (EntityType invenType, ReservedSlotOnInven currValue) { if (false == currValue.getReservedSlots().TryGetValue(slotType, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlotOnInven.ReservedSlot(); currValue.getReservedSlots().Add(slotType, found_reserved_slot); } found_reserved_slot.decReservedCount(1); return currValue; }; found_reserved_inven_slots.AddOrUpdate(entityInvenType, add_recorder, update_recorder); return result; } public Result tryEquipToReserveSlotWithEntityBase(EntityBase invenOwner, EntityType entityInvenType, string slotType, EntityBase? entityBase) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(invenOwner, () => $"invenOwner is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; ConcurrentDictionary? found_reserved_inven_slots; lock (m_reserved_inven_slots_of_owners) { if (false == m_reserved_inven_slots_of_owners.TryGetValue(invenOwner, out found_reserved_inven_slots)) { found_reserved_inven_slots = new ConcurrentDictionary(); m_reserved_inven_slots_of_owners[invenOwner] = found_reserved_inven_slots; } } var slot_key = slotType; var add_recorder = delegate (EntityType invenType) { var reserved_slot_on_inven = new ReservedSlotOnInven(entityInvenType); reserved_slot_on_inven.addOrInc(slot_key, 1); reserved_slot_on_inven.getReservedSlots()[slot_key].setEquipEntity(entityBase); return reserved_slot_on_inven; }; var update_recorder = delegate (EntityType invenType, ReservedSlotOnInven currValue) { if (false == currValue.getReservedSlots().TryGetValue(slot_key, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlotOnInven.ReservedSlot(); currValue.getReservedSlots()[slot_key] = found_reserved_slot; } var equip_entity = found_reserved_slot.getEquipEntity(); if (null != equip_entity) { err_msg = $"Already reserved equip item !!! : reservedEquipItem:{equip_entity.toBasicString()} - {entityBase?.toBasicString()}, {toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.SlotsAlreadyReservedEquip, err_msg); Log.getLogger().warn(result.toBasicString()); return currValue; } found_reserved_slot.incReservedCount(1); found_reserved_slot.setEquipEntity(entityBase); return currValue; }; found_reserved_inven_slots.AddOrUpdate(entityInvenType, add_recorder, update_recorder); return result; } public Result tryUnequipToReserveSlotWithEntityBase(EntityBase invenOwner, EntityType entityInvenType, string slotType, EntityBase? entityBase) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(invenOwner, () => $"invenOwner is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var slot_key = slotType; ConcurrentDictionary? found_reserved_inven_slots = null; lock (m_reserved_inven_slots_of_owners) { if (false == m_reserved_inven_slots_of_owners.TryGetValue(invenOwner, out found_reserved_inven_slots)) { found_reserved_inven_slots = new ConcurrentDictionary(); m_reserved_inven_slots_of_owners[invenOwner] = found_reserved_inven_slots; } } var add_recorder = delegate (EntityType invenType) { var reserved_slot_on_inven = new ReservedSlotOnInven(entityInvenType); reserved_slot_on_inven.addOrDec(slot_key, 1); reserved_slot_on_inven.getReservedSlots()[slot_key].setUnequipEntity(entityBase); return reserved_slot_on_inven; }; var update_recorder = delegate (EntityType invenType, ReservedSlotOnInven currValue) { if (false == currValue.getReservedSlots().TryGetValue(slot_key, out var found_reserved_slot)) { found_reserved_slot = new ReservedSlotOnInven.ReservedSlot(); currValue.getReservedSlots()[slot_key] = found_reserved_slot; } var unequip_entity = found_reserved_slot.getUnequipEntity(); if (null != unequip_entity) { err_msg = $"Already reserved unequip item !!! : reservedUnequipItem:{unequip_entity.toBasicString()} - {entityBase?.toBasicString()}, {toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.SlotsAlreadyReservedUnequip, err_msg); Log.getLogger().warn(result.toBasicString()); return currValue; } found_reserved_slot.decReservedCount(1); found_reserved_slot.setUnequipEntity(entityBase); return currValue; }; found_reserved_inven_slots.AddOrUpdate(entityInvenType, add_recorder, update_recorder); return result; } public Result tryEquipToReserveSlot(EntityBase invenOwner, EntityType entityInvenType, string slotType) { return tryEquipToReserveSlotWithEntityBase(invenOwner, entityInvenType, slotType, null); } public Result tryUnequipToReserveSlot(EntityBase invenOwner, EntityType entityInvenType, string slotType) { return tryUnequipToReserveSlotWithEntityBase(invenOwner, entityInvenType, slotType, null); } public ReservedSlotOnInven.ReservedSlot? findReservedSlot(EntityBase invenOwner, EntityType entityInvenType, string slotType) { if(false == m_reserved_inven_slots_of_owners.TryGetValue(invenOwner, out var found_reserved_inven_slots)) { return null; } if (true == found_reserved_inven_slots.TryGetValue(entityInvenType, out var found_reserved_slot_on_inven)) { if(found_reserved_slot_on_inven.getReservedSlots().TryGetValue(slotType, out var found_reserved_slot)) { return found_reserved_slot; } } return null; } public (Result, EntityCommonResult?) getOrNewEntityCommonResult(EntityAttributeBase entityAttribute) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(entityAttribute, () => $"entityAttribute is null !!! - {owner.toBasicString()}"); var entity_of_owner_entity_type = entityAttribute.getEntityOfOwnerEntityType(); NullReferenceCheckHelper.throwIfNull(entity_of_owner_entity_type, () => $"entity_of_owner_entity_type is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var owner_guid = entity_of_owner_entity_type.onGetDbGuid(); if(owner_guid.isNullOrWhiteSpace()) { err_msg = $"Invlid OwnerGuid of onGetDbGuid() !!! : entityAttribute:{entityAttribute.toBasicString()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.OwnerGuidInvalid, err_msg); Log.getLogger().fatal(result.toBasicString()); return (result, null); } if (false == m_entity_common_results.TryGetValue(owner_guid, out var found_entity_common_result)) { found_entity_common_result = new EntityCommonResult(); found_entity_common_result.EntityType = entity_of_owner_entity_type.getEntityType(); found_entity_common_result.EntityGuid = owner_guid; found_entity_common_result.Money = new MoneyResult(); found_entity_common_result.Exp = new ExpResult(); found_entity_common_result.Item = new ItemResult(); m_entity_common_results[owner_guid] = found_entity_common_result; } return (result, found_entity_common_result); } public CommonResult getCommonResult() { var common_result = new CommonResult(); foreach(var each in m_entity_common_results) { common_result.EntityCommonResults.Add(each.Value); } return common_result; } public void addRemoteChargeAIPoint(IRemoteTransaction remoteTransaction, double beamDelta) { var fn_buy_cart = new Func(() => { return remoteTransaction.callRemoteChargeAIPoint(beamDelta); }); m_remote_transaction_funcs.Add(fn_buy_cart); } public void addNotifyCaliumEvent(IRemoteTransaction remoteTransaction, string eventName, CurrencyType currencyType, double delta) { var fn = new Func(() => remoteTransaction.callNotifyCaliumEvent(eventName, currencyType, delta)); m_remote_transaction_funcs.Add(fn); } public async Task tryStartRemoteTransaction() { foreach(var remote_transaction_funcs in m_remote_transaction_funcs) { await remote_transaction_funcs(); } } public string toBasicString() { return $"TransactionRunnger - transId:{m_trans_id}, transactionIdType:{m_transaction_id_type}, transactionName:{m_transaction_name}"; } }