using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using GameServer.Contents.Battle.Tickers; using GameServer.Contents.GameMode.Mode_Battle.Manage; using Google.Protobuf.WellKnownTypes; using NeoSmart.AsyncLock; using Newtonsoft.Json; using ServerCore; using ServerBase; using ServerCommon; using Constant = System.Reflection.Metadata.Constant; namespace GameServer; public class BattleInstanceManager : Singleton { private AsyncLock m_lock_init = new(); //private ConcurrentDictionary m_battle_instances = new(); //인스턴스 생성 삭제를 주로 관리 하기 위한 Dictionary ... 아...이거 구조 별론데,.... private ConcurrentDictionary m_battle_events = new(); public async Task onInit() { //1. 이벤트 관리하는 Task 추가 var result = await createBattleEventCheckerTask(); } public async Task tryLeaveBattelInstance(Player player, string instanceRoomId) { Log.getLogger().debug($"tryLeaveBattelInstance start instanceRoomId : {instanceRoomId}"); var location_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location attribute is null !!! - {player.toBasicString()}"); //var instanceRoomId = location_attribute.CurrentIndunLocation.InstanceRoomId; if (instanceRoomId == string.Empty) { Log.getLogger().warn($"tryLeaveBattelInstance IntanceRoomId is Empty"); return; } if (false == GameModeManager.It.tryGetGameMode(instanceRoomId, out var gameMode)) { return; } var ffa = gameMode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!! - {player.toBasicString()}"); await ffa.removePodCombat(player); var host_user_guid = ffa.m_host_migrator.getHostUserGuid(); if (player.getUserGuid().Equals(host_user_guid)) { using (var releaser = await ffa.getAsyncLock()) { ffa.m_host_migrator.removeHost(); } } //여기서 instanceroom 이 is Destroy 면 DestroyBattleRoom 호출 해줄것 if (ffa.getInstanceRoom().isDestroy) { //kihoon todo : 이부분 수정 필요 //m_battle_instances.TryRemove(instanceRoomId, out var _); //await battleInstanceRoom.destroyBattleRoom(); Log.getLogger().debug($"tryLeaveBattelInstance destroy battle room instanceRoomId : {instanceRoomId}"); } } // public async Task tryDestroyBattleRoom(string instanceRoomId) // { // await Task.CompletedTask; // if (false == instanceRoomId.Contains(BattleConstant.PREFIX_BATTLE_INSTANCE_ROOM_ID)) return; // // // if (false == m_battle_instances.TryGetValue(instanceRoomId, out var battleInstanceRoom)) // // { // // Log.getLogger().error($"instanceRoomId : {instanceRoomId} is not exist instnaces"); // // return; // // } // // // var is_destroy = battleInstanceRoom.m_instance_room.isDestroy; // // if (is_destroy) // // { // // await battleInstanceRoom.destroyBattleRoom(); // // //m_battle_instances.TryRemove(instanceRoomId, out _); // // } // } public async Task LeaveBattleRoom(Player player, string instanceRoomId, bool disconnected = false) { (var result, var leeave_handler) = GameModeHelper.getGameModeLeaveHandler(player, instanceRoomId); if (result.isFail() || leeave_handler is null) return false; await leeave_handler.gameModeLeave(); Log.getLogger().debug($"leave battle room player : {player.toBasicString()}, instanceRoomId : {instanceRoomId}"); return true; } public async Task sendNtfAboutBattleInstance(GameModeTPSFreeForAll battieInstanceRoom, Player player) { var result = new Result(); BattleRoomNotifyHelper.send_GS2C_NTF_BATTLE_INSTANCE_STATE(battieInstanceRoom, player); BattleRoomNotifyHelper.send_GS2C_NTF_POD_COMBAT_STATE(battieInstanceRoom, player); sendNtfBattleObjectsState(battieInstanceRoom, player); var host_user_guid = battieInstanceRoom.m_host_migrator.getHostUserGuid(); if (!host_user_guid.Equals(string.Empty)) { BattleRoomNotifyHelper.send_GS2C_NTF_P2P_HOST_UPDATE(player, host_user_guid); } await Task.CompletedTask; return result; } private void sendNtfBattleObjectsState(GameModeTPSFreeForAll battleInstanceRoom, Player player) { List infos = new(); var attribute = battleInstanceRoom.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); var now = DateTimeHelper.Current; foreach (var weapon in attribute.m_combat_pod_mode.m_weapons.Values) { BattleObjectInfo info = new(); info.AnchorGuid = weapon.m_anchor_guid; info.IsActive = weapon.m_active_time <= now ? BoolType.True : BoolType.False; infos.Add(info); } foreach (var buff in attribute.m_combat_pod_mode.m_buffs.Values) { BattleObjectInfo info = new(); info.AnchorGuid = buff.m_anchor_guid; info.IsActive = buff.m_active_time <= now ? BoolType.True : BoolType.False; infos.Add(info); } foreach (BattleObjectPodStorage storage in attribute.m_combat_pod_mode.m_pod_storages.Values) { //storage.m_is_active = true; BattleObjectInfo info = new(); info.AnchorGuid = storage.m_anchor_guid; info.IsActive = storage.m_is_active ? BoolType.True : BoolType.False; infos.Add(info); } foreach (BattleObjectPickupPod pickup in attribute.m_combat_pod_mode.m_pickup_pods.Values) { BattleObjectInfo info = new(); info.AnchorGuid = pickup.m_anchor_guid; info.IsActive = pickup.m_is_active ? BoolType.True : BoolType.False; infos.Add(info); Log.getLogger().info($"pickup pod info anchor_guid : {info.AnchorGuid}, is Active : {info.IsActive}"); } BattleRoomNotifyHelper.send_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(infos, player); } public bool isBattleInstance(Player player) { var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); var current_indun_location = location_action.getCurrentLocation() as IndunLocation; NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {player.toBasicString()}"); var instance_room_Id = current_indun_location.InstanceRoomId; if (instance_room_Id == string.Empty) return false; Log.getLogger().debug($"is Battle Instance check : instance room id : {instance_room_Id}"); if (instance_room_Id.Contains("battle_instance")) { return true; } return false; //result.setFail(ServerErrorCode.BattleInstnaceNotUsedPacket, ""); } public List getAllProtoBattleEvents() { List infos = new(); foreach(var system_battle_event in m_battle_events.Values.ToList()) { BattleEventInfo battle_event_info = new(); battle_event_info.EventId = system_battle_event.m_event_id; battle_event_info.InstanceId = system_battle_event.m_instance_id; battle_event_info.StartTime = Timestamp.FromDateTime(system_battle_event.m_start_time); battle_event_info.ConfigDataId = system_battle_event.m_ffa_config_data_id; battle_event_info.RewardGroupId = system_battle_event.m_ffa_reward_group_id; battle_event_info.HotTime = system_battle_event.m_ffa_hot_time; battle_event_info.RoundCount = system_battle_event.m_round_count; infos.Add(battle_event_info); } return infos; } public BattleEventInfo makeProtoBattleEvent(SystemBattleEvent battleEvent) { BattleEventInfo battle_event_info = new(); battle_event_info.EventId = battleEvent.m_event_id; battle_event_info.InstanceId = battleEvent.m_instance_id; battle_event_info.StartTime = Timestamp.FromDateTime(battleEvent.m_start_time); battle_event_info.ConfigDataId = battleEvent.m_ffa_config_data_id; battle_event_info.RewardGroupId = battleEvent.m_ffa_reward_group_id; battle_event_info.HotTime = battleEvent.m_ffa_hot_time; battle_event_info.RoundCount = battleEvent.m_round_count; return battle_event_info; } // public async Task setBattleEventAttribs(List battleEventAttribs) // { // using (await m_lock_data_load.LockAsync()) // { // m_battle_event_attribs.Clear(); // m_battle_event_attribs = battleEventAttribs; // } // // } // public async Task> copyBattleEventAttribs() // { // List attribs = new(); // using (await m_lock_data_load.LockAsync()) // { // foreach (var attrib in m_battle_event_attribs) // { // attribs.Add(attrib.clone()); // } // } // // return attribs; // } public ConcurrentDictionary getSystemBattleEvents() { return m_battle_events; } public bool getSystemBattleEvent(Int32 eventId, [MaybeNullWhen(false)]out SystemBattleEvent battleEvent) { if (false == m_battle_events.TryGetValue(eventId, out battleEvent)) { Log.getLogger($"Not exsist Battle event !!! eventId : {eventId}"); return false; } return true; } public void removeSystemBattleEvents(List events) { foreach (var event_id in events) { if (false == m_battle_events.TryRemove(event_id, out var ev)) { Log.getLogger().warn($"remove battle event fali event_id : {event_id}"); } } } public bool existSystemBattleEvent(Int32 eventId) { return m_battle_events.ContainsKey(eventId); } public void logSystemBattleEvents() { Log.getLogger().info($"updated system battle events : {JsonConvert.SerializeObject(m_battle_events)}"); } public void setSystemBattleEvents(ConcurrentDictionary newEvents) { m_battle_events = newEvents; } public bool addNewSystemBattleEvent(SystemBattleEvent ev, bool needErrroLog) { if (false == m_battle_events.TryAdd(ev.m_event_id, ev)) { if (needErrroLog) { Log.getLogger().error($"System battle Event try Add Fail, eventId : {ev.m_event_id}, events : {JsonConvert.SerializeObject(m_battle_events)}"); } return false; } return true; } private async Task createBattleEventCheckerTask() { var result = new Result(); var battle_entity_ticker_initializers = new Initializers(); //채널 인던 다 필요한가? //이벤트 인스턴스 관련 생성 삭제에 대한 관리 Ticker //battle_entity_ticker_initializers.appendInitializer(new BattleEventCheckTicker(1000, null)); //battle_entity_ticker_initializers.appendInitializer(new BattleEventNotifyTicker(Constant.EVENT_UPDATE_INTERVAL_MMIN, null)); result = await battle_entity_ticker_initializers.init("BattleEntityTickers"); if (result.isFail()) { Log.getLogger().error($"Failed to init() !!! : {result.toBasicString()}"); return result; } var battle_event_check_ticker = new BattleEventCheckTicker(BattleConstant.BATTLE_EVENT_CHECK_INTERVAL, null); await battle_event_check_ticker.onInit(); await createTask(battle_event_check_ticker); var battle_event_notify_ticker = new BattleEventNotifyTicker(ServerCommon.Constant.EVENT_UPDATE_INTERVAL_MMIN, null); await battle_event_check_ticker.onInit(); await createTask(battle_event_notify_ticker); return result; } private async Task createTask(EntityTicker ticker) { var result = new Result(); await Task.CompletedTask; try { new PeriodicTaskTimer( ticker.getTypeName() , ticker.getOnTickIntervalMilliseconds(), ticker.getCancelToken(), ticker.onTaskTick); Log.getLogger().debug("createTask done"); } catch(Exception e) { var err_msg = $"Exception !!!, new PeriodicTaskTimer() : exception:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); return result; } return result; } }