using System.Collections.Concurrent; using Axion.Collections.Concurrent; using Newtonsoft.Json; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; namespace GameServer; public class BattleInstanceRoom : EntityBase, IWithLogActor { //public string m_instance_room_guid { get; } = string.Empty; public bool m_is_load_complete { get; private set; } = false; public InstanceRoom m_instance_room { get; } private readonly CancellationTokenSource m_cancel_token = new(); public readonly IHostMigrator m_host_migrator; //생성자에서 모드에 맞춰서 할당한다. public BattlePlayMode m_play_mode { get; set; } = BattlePlayMode.PodCombat; public DateTime m_battle_instance_event_start_time { get; private set; } = DateTimeHelper.Current; //실제 이벤트 시작시간 //public DateTime m_battle_instance_load_time { get; } = DateTimeHelper.Current; //이벤트 시작시간과 관계 없이 메모리에 로드 될때의 시간 이 변수가 필요할지 모르겠다. //todo : 모드별 관리 메타를 따로 구성해야할듯... //todo : 제네릭으로 받아도 되려나?.. 고민 필요 우선 임시데이터 public Int32 m_pod_combat_reward_group_id = 1; public Int32 m_pod_combat_ffa_id = 1; public BattleFFAConfigData m_ffa_config_meta; public Dictionary m_ffa_reward_meta = new(); public Int32 m_hot_time_reward = 1; public Int32 m_round_count = 8; //public Int32 m_pickup_pod_reward_max_count { get; } = 2; //public int m_max_step_per_round { get; set; } = 10; public ConcurrentHashSet m_respawn_pos_anchors_meta { get; private set; } = new(); //이건 랜덤 돌릴 일이 잦아서 그냥 들고 있는다... public ConcurrentDictionary> m_battle_pod_storage_guid_group { get; private set; } = new(); //pod combat을 생성해줄때 필요 public ConcurrentDictionary> m_battle_pickup_pod_guid_group { get; private set; } = new(); public BattleInstanceRoom(InstanceRoom room, DateTime eventStartTime, Int32 configId, Int32 rewardId, Int32 hotTime, Int32 roundCount) : base(EntityType.BattleInstance) { m_instance_room = room; NullReferenceCheckHelper.throwIfNull(m_instance_room, () => $"m_instance_room is null !!!"); m_battle_instance_event_start_time = eventStartTime; m_play_mode = BattlePlayMode.PodCombat; m_host_migrator = BattleRoomHelper.createHostMigrator(m_play_mode); m_pod_combat_ffa_id = configId; m_pod_combat_reward_group_id = rewardId; m_hot_time_reward = hotTime; m_round_count = roundCount; //m_instance_room_guid = BattleConstant.PREFIX_BATTLE_TRANSACTION_GUID + System.Guid.NewGuid(); // BattleFFAConfigData if (false == MetaData.Instance._BattleFFAConfigMetaTable.TryGetValue(m_pod_combat_ffa_id, out var configData)) { var err_msg = $"Not exist Battle Conig Data id : {m_pod_combat_ffa_id}"; Log.getLogger().error(err_msg); NullReferenceCheckHelper.throwIfNull(configData, () => $"configData is null !!!"); } m_ffa_config_meta = new BattleFFAConfigData(new BattleFFAConfigDataMutable() { Id = configData.Id, Description = configData.Description, PlayerRespawnTime = configData.PlayerRespawnTime, DefaultRoundCount = configData.DefaultRoundCount, RoundTime = configData.RoundTime, NextRoundWaitTime = configData.NextRoundWaitTime, ResultUIWaitTime = configData.ResultUIWaitTime, GetRewardTime = configData.GetRewardTime, EntranceClosingTime = configData.EntranceClosingTime, CurrencyType = configData.CurrencyType, CurrencyCount = configData.CurrencyCount, TPSGuideURL = configData.TPSGuideURL }); if (false == MetaData.Instance._BattleFFARewardMetaTable.TryGetValue(m_pod_combat_reward_group_id, out var rewardMeta)) { var err_msg = $"Not exist reward Meta Data id : {m_pod_combat_ffa_id}"; Log.getLogger().error(err_msg); NullReferenceCheckHelper.throwIfNull(rewardMeta, () => $"rewardMeta is null !!!"); } foreach (var meta_set in rewardMeta) { var val = meta_set.Value; var key = meta_set.Key; BattleFFARewardData data = new BattleFFARewardData(new BattleFFARewardDataMutable() { Id = val.Id, GroupID = val.GroupID, ChargeLevel = val.ChargeLevel, ChargeTime = val.ChargeTime, RewardItemID = val.RewardItemID, RewardCount = val.RewardCount }); m_ffa_reward_meta.TryAdd(key, data); } Log.getLogger().info($"BattleInstanceRoom construct done roomId : " + $"{room.getMap().m_room_id}, eventStartTime : {eventStartTime}, configId : {configId}, rewardId : {rewardId}, hotTime : {hotTime}, roundCount : {roundCount}"); } public async Task init() { string err_msg = string.Empty; //Action 추가 addEntityAction(new BattleInstanceAction(this)); addEntityAction(new BattleInstanceUpdateAction(this)); addEntityAction(new BattleObjectInteractAction(this)); addEntityAction(new BattleObjectRewardAction(this)); //Attribute 추가 addEntityAttribute(new BattleInstanceSnapshotAttribute(this)); //Anchor 정보 로드 loadAnchorPos(); //스냅샷 데이터 로드, 없으면 신규 생성 var battle_instance_action = getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_instance_action, () => $"battle_instance_action is null !!!"); var result = await battle_instance_action.loadOrCreateSnapshot(); if (result.isFail()) { err_msg = $"loadSnapshot error instanceId : {m_instance_room.getInstanceId()}, roomId : {m_instance_room.getMap().m_room_id}"; Log.getLogger().error(err_msg); return result; } Log.getLogger().info($"load Snapshot done instanceId : {m_instance_room.getInstanceId()}, roomId : {m_instance_room.getMap().m_room_id}"); //태스크 생성 result = await createTask(); if (result.isFail()) { err_msg = $"battle instance room onCreateTask error instanceId : {m_instance_room.getInstanceId()}, roomId : {m_instance_room.getMap().m_room_id}"; Log.getLogger().error(err_msg); return result; } Log.getLogger().info($"createTask done instanceId : {m_instance_room.getInstanceId()}, roomId : {m_instance_room.getMap().m_room_id}"); m_is_load_complete = true; // P2P Host Migrator p2p 그룹 id 설정 m_host_migrator.setGroupHostId(m_instance_room.getMap().getP2PGroupId()); return result; } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } private async Task createTask() { var result = new Result(); //Timer에서 사용할 Action 초기화 var battle_update_action = getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_update_action, () => $"battle_update_action is null !!!"); try { //임시로 0.8초로 처리 나중에 이거 Meta로 빼야 된다. new PeriodicTaskTimer( GetType().Name, BattleConstant.BATTLE_ROOM_CHECK_INTERVAL, m_cancel_token, onTaskTick); await battle_update_action.onInit(); Log.getLogger().debug("battle_update_action.onInit() dome"); } catch(Exception e) { var err_msg = $"Exception !!!, new PeriodicTaskTimer() : exception:{e} - {toBasicString()}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); return result; } return result; } private void loadAnchorPos() { foreach (var anchor_info_dict in m_instance_room.getMap().getAnchors()) { var anchor_guid = anchor_info_dict.Key; var anchor_info = anchor_info_dict.Value; if (!MapDataTable.Instance.getAnchor(anchor_guid, out var anchor)) { Log.getLogger().error($"anchr_guid not MapDataTable.Instance.getAnchor, anchorGuid : {anchor_guid}"); continue; } var type = anchor.Type; var table_id = anchor_info.AnchorProp.TableId; loadRespawnPos(type, anchor_guid); loadBattleObjectGroups(type, anchor_guid, table_id); } } private void loadRespawnPos(string type, string anchorGuid) { if (!type.Equals(BattleConstant.RESPAWN_POS_ANCHOR_NAME)) return; if (false == m_respawn_pos_anchors_meta.Add(anchorGuid)) { Log.getLogger().warn($"respawnPos add fail type : {type}, anchorGuid : {anchorGuid}"); } Log.getLogger().info($"respawnPos add success type : {type}, anchorGuid : {anchorGuid}"); } // private void loadBattleObject(string type, string anchorGuid) // { // if (!type.Equals(BattleConstant.BATTLE_OBJECT_ANCHOR_NAME)) return; // // if (false == m_battle_object_anchors_meta.Add(anchorGuid)) // { // Log.getLogger().warn($"m_battle_object_anchors add fail type : {type}, anchorGuid : {anchorGuid}"); // } // Log.getLogger().info($"m_battle_object_anchors add success type : {type}, anchorGuid : {anchorGuid}"); // } private bool loadBattleObjectGroups(string type, string anchorGuid, Int32 tableID) { if (!type.Equals(BattleConstant.BATTLE_OBJECT_GROUP_ANCHOR_NAME)) return true; // if (false == m_battle_object_group_anchors_meta.Add(anchorGuid)) // { // Log.getLogger().warn($"m_battle_object_group_anchors add fail type : {type}, anchorGuid : {anchorGuid}"); // } // Log.getLogger().info($"m_battle_object_group_anchors add success type : {type}, anchorGuid : {anchorGuid}"); if (false == MetaData.Instance._BattleObjectSpawnGroupMetaTable.TryGetValue(tableID, out var battle_object_spawn_meta)) { Log.getLogger().warn($"battle_object_group add fail type : {type}, anchorGuid : {anchorGuid}, table_id : {tableID}"); return false; } var battle_object_id = battle_object_spawn_meta.BattleObjectID; if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(battle_object_id, out var battle_object_meta)) { Log.getLogger().warn($"battle_object_group add fail type : {type}, anchorGuid : {anchorGuid}, table_id : {tableID}, battle_object_id : {battle_object_id}"); return false; } EBattleObjectType object_type = battle_object_meta.ObjectType; var group_id = battle_object_spawn_meta.GroupID; if (object_type.Equals(EBattleObjectType.Pod_Combat) && battle_object_meta.Name.Equals(BattleConstant.BATTLE_POD_STORAGE_NAME)) { loadBattleObjectPodStorageGroup(anchorGuid, group_id); } else if (object_type.Equals(EBattleObjectType.Pod_Box)) { loadBattleObjectPickupPodGroup(anchorGuid, group_id); } return true; } private void loadBattleObjectPodStorageGroup(string anchorGuid, int groupId) { if (false == m_battle_pod_storage_guid_group.ContainsKey(groupId)) { m_battle_pod_storage_guid_group.TryAdd(groupId, new()); } if (false == m_battle_pod_storage_guid_group.TryGetValue(groupId, out var poses)) { Log.getLogger().error($"m_battle_pod_stand_group_pos_meta get fail anchorGuid : {anchorGuid}, group_id : {groupId}"); return; } poses.Add(anchorGuid); Log.getLogger().info($"m_battle_pod_stand_group_pos_meta Add anchorGuid : {anchorGuid}, group_id : {groupId}"); } private void loadBattleObjectPickupPodGroup(string anchorGuid, int groupId) { if (false == m_battle_pickup_pod_guid_group.ContainsKey(groupId)) { m_battle_pickup_pod_guid_group.TryAdd(groupId, new()); } if (false == m_battle_pickup_pod_guid_group.TryGetValue(groupId, out var poses)) { Log.getLogger().error($"m_battle_pod_box_group_pos_meta get fail anchorGuid : {anchorGuid}, group_id : {groupId}"); return; } poses.Add(anchorGuid); Log.getLogger().info($"m_battle_pod_box_group_pos_meta Add anchorGuid : {anchorGuid}, group_id : {groupId}"); } public Result AddOrUpdatePlayer(Player player) { var result = new Result(); // if (false == m_battle_players.TryAdd(player.getUserGuid(), new BattlePlayer(player))) // { // string err_msg = $"Player tryAdd fail guid : {player.getUserGuid()}"; // result.setFail(ServerErrorCode.BattleInstanceJoinPlayerError, err_msg); // Log.getLogger().error(result.toBasicString()); // return result; // } return result; } private async Task onTaskTick() { if (!m_is_load_complete) return; var battle_update_action = getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_update_action, () => $"battle_update_action is null !!!"); var result = await battle_update_action.update(); if (result.isFail()) { var err_msg = $"playr mode hander update error : {result.toBasicString()}"; Log.getLogger().error(err_msg); } } public ILogActor toLogActor() { var server_logic = GameServerApp.getServerLogic(); var region_id = server_logic.getServerConfig().getRegionId(); var server_name = server_logic.getServerName(); var log_info = new BattleInstanceActorLog(region_id, server_name, m_instance_room.getMap().m_room_id, m_instance_room.getInstanceId()); return log_info; } public async Task removePodCombat(Player player) { var attribute = getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); if (attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid.Equals(player.getUserGuid())) { using (var releaser = await getAsyncLock()) { attribute.m_combat_pod_mode.m_pod_combat.changeDropState(player.getCurrentPositionInfo().Pos); } BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(this); } } public void removeBattlePlayerIfExist(string userGuid) { // if (true == m_battle_players.ContainsKey(userGuid)) // { // if (false == m_battle_players.TryRemove(userGuid, out var player)) // { // Log.getLogger().warn($"m_battle_players try remove faial userGuid : {userGuid}"); // } // } } public async Task LeaveBattleRoom(Player player, string roomId, bool disconnected = false) { //BattleRoom에서 먼저 빼준다. //m_battle_players.TryRemove(player.getUserGuid(), out _); Log.getLogger().info($"LeaveBattleRoom, player : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); await m_instance_room.LeaveBattleInstanceRoom(player, disconnected); if (m_instance_room.isDestroy) { Log.getLogger().info($"m_instance_room.isDestroy, so destroyBattleRoom : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); await destroyBattleRoom(); InstanceRoomManager.Instance.DestroyRoom(roomId); Log.getLogger().info($"destroy InstanceRoomManager room Player : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); } return true; } public async Task destroyBattleRoom() { await Task.CompletedTask; //여기서 timer 종료 처리 m_cancel_token.Cancel(); Log.getLogger().debug($"battle instance room token canceled m_room_id :{m_instance_room.getMap().m_room_id}"); var fn_save_battle_instance = async delegate() { var server_logic = GameServerApp.getServerLogic(); var fn_result = new Result(); using (var releaser = await getAsyncLock()) { //여기서 battleRoomSnapshot 저장 var battle_instance_snapshot_attribute = getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(battle_instance_snapshot_attribute, () => $"battle_instance_snapshot_attribute is null !!!"); if (!battle_instance_snapshot_attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid.Equals(string.Empty)) { battle_instance_snapshot_attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid = string.Empty; } battle_instance_snapshot_attribute.modifiedEntityAttribute(true); var batch = new QueryBatchEx(this, LogActionType.BattleInstanceSnapshotSave, server_logic.getDynamoDbClient(), true); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } BattleSnapShotBusinessLog business_log = new(this, ""); batch.appendBusinessLog(business_log); fn_result = await QueryHelper.sendQueryAndBusinessLog(batch); if (fn_result.isSuccess()) { Log.getLogger().info($"save battle instance snapshot done attribute : {JsonConvert.SerializeObject(battle_instance_snapshot_attribute)}"); } } return fn_result; }; var result = await runTransactionRunnerSafely(TransactionIdType.PrivateContents, "SaveBattleInstanceByDestroy", fn_save_battle_instance); if (result.isFail()) { var err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); } } public override string toBasicString() { return $"BattleInstanceRoom room_id : {m_instance_room.getMap().m_room_id}"; } public void setEventStartTime(DateTime t) { m_battle_instance_event_start_time = t; } }