using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using NeoSmart.AsyncLock; using Newtonsoft.Json; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; namespace GameServer; public class BattleInstanceManager : Singleton { private AsyncLock m_lock_init = new(); public AsyncLock m_lock_data_load = 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 bool hasBattleInstance(string roomId) { return m_battle_instances.ContainsKey(roomId); } public BattleInstanceRoom? getBattleInstanceRoom(string roomId) { if (false == m_battle_instances.TryGetValue(roomId, out var room)) { return null; } return room; } public ConcurrentDictionary getBattleInstanceRooms() { return m_battle_instances; } // // public bool isLoadComplete(string roomId) // { // if (false == m_battle_instances.TryGetValue(roomId, out var battleInstanceRoom)) // { // return false; // } // return battleInstanceRoom.m_is_load_complete; // } public async Task battleInstanceInit(InstanceRoom instanceRoom, string roomId) { var result = new Result(); var err_msg = string.Empty; using (await m_lock_init.LockAsync()) { //1. 배틀 인스턴스에 대한 정보 생성 Log.getLogger().info($"Battle Instance Create And Init : {roomId}"); if (hasBattleInstance(roomId)) return result; // instance 정보 추가 등록 var server_logic = GameServerApp.getServerLogic(); var instance_room_storage = new InstanceRoomStorage(); instance_room_storage.Init(server_logic.getRedisDb(), ""); DateTime start_time = DateTimeHelper.Current; Int32 config_id = 1; Int32 reward_id = 1; Int32 hot_time = 1; Int32 round_count = 8; var system_battle_event = BattleRoomHelper.getBattleRoomStartTimeByEventId(roomId); if (system_battle_event is null) { Log.getLogger().error($"system_battle_event is null!!! : {result.toBasicString()} - instanceRoomId:{roomId}"); } else { start_time = system_battle_event.m_start_time; config_id = system_battle_event.m_ffa_config_data_id; reward_id = system_battle_event.m_ffa_reward_group_id; hot_time = system_battle_event.m_ffa_hot_time; round_count = system_battle_event.m_round_count; } result = await instance_room_storage.setInstanceRoomExtraInfo(roomId, EPlaceType.BattleRoom, start_time); if (result.isFail()) { Log.getLogger().error($"Failed to BattleRoom setInstanceRoomExtraInfo() !!! : {result.toBasicString()} - instanceRoomId:{roomId}"); return result; } BattleInstanceRoom battle_instance_room = new BattleInstanceRoom(instanceRoom, start_time, config_id, reward_id, hot_time, round_count); if (false == m_battle_instances.TryAdd(roomId, battle_instance_room)) { //이미 존재하는 경우는 onInit 절차를 거쳤다고 생각하고, init 처리 안해주고 리턴 err_msg = $"Battle Instance Room Already exist room_id : {roomId}"; result.setFail(ServerErrorCode.BattleInstanceInfoAlreadyExist, err_msg); Log.getLogger().warn(result.toBasicString()); return result; } //2. 초기화 result = await battle_instance_room.init(); if (result.isFail()) { Log.getLogger().warn(result.toBasicString()); return result; } } Log.getLogger().info($"battleInstanceInit done roomId : {roomId}"); return result; } public Result addOrUpatePlayer(string roomId, Player player) { var result = new Result(); string err_msg = string.Empty; if (false == m_battle_instances.TryGetValue(roomId, out var battle_instance_room)) { err_msg = $"m_battle_instances not exist battleRoomInstance, roomId : {roomId}"; result.setFail(ServerErrorCode.BattleInstanceInfoNotExist, err_msg); Log.getLogger().error(result.toBasicString()); return result; } //유저 정보 추가 해줘야 한다. result = battle_instance_room.AddOrUpdatePlayer(player); return result; } 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(ServerCommon.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; } public async Task tryLeaveBattelInstance(Player player, string instanceRoomId) { if (false == BattleRoomHelper.checkBattleActive()) return; 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"); removeBattlePlayer(player.getUserGuid()); return; } if (false == m_battle_instances.TryGetValue(instanceRoomId, out var battleInstanceRoom)) { removeBattlePlayer(player.getUserGuid()); return; } await battleInstanceRoom.removePodCombat(player); var host_user_guid = battleInstanceRoom.m_host_migrator.getHostUserGuid(); if (player.getUserGuid().Equals(host_user_guid)) { using (var releaser = await battleInstanceRoom.getAsyncLock()) { battleInstanceRoom.m_host_migrator.removeHost(); } } //여기서 instanceroom 이 is Destroy 면 DestroyBattleRoom 호출 해줄것 if (battleInstanceRoom.m_instance_room.isDestroy) { 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) { if (false == BattleRoomHelper.checkBattleActive()) return; 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 _); } } private void removeBattlePlayer(string userGuid) { foreach (var battle_instance_room in m_battle_instances.Values) { battle_instance_room.removeBattlePlayerIfExist(userGuid); } } public async Task LeaveBattleRoom(Player player, string instanceRoomId, bool disconnected = false) { if (false == m_battle_instances.TryGetValue(instanceRoomId, out var battleRoom)) { Log.getLogger().info($"LeaveBattleRoom m_battle_instances.TryGetValue false instanceRoomId : {instanceRoomId}"); return false; } await battleRoom.LeaveBattleRoom(player, instanceRoomId, disconnected); // if (battleRoom.m_instance_room.isDestroy) // { // InstanceRoomManager.Instance.DestroyRoom(instanceRoomId); // } Log.getLogger().debug($"leave battle room player : {player.toBasicString()}, instanceRoomId : {instanceRoomId}"); return true; } public async Task sendNtfAboutBattleInstance(BattleInstanceRoom 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(BattleInstanceRoom 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; } public BattleInstanceManager Self => this; }