using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using AutoMapper; using CommonDomain; using CommonDomain.BaseModel; using CommonDomain.Enum; using DotNetCore.CAP; using Microsoft.Extensions.Logging; using MongoRepository; using NewLife; using RedisService; using RedisService.Param; namespace AdminService.GameConfigs { public class GameConfigService : IGameConfigService { private readonly IMongoBaseRepository _mongo; private readonly IMapper _mapper; private IRedisService _redis; private readonly ICapPublisher _capPublisher; private readonly IMongoBaseRepository _botProfileRepo; private readonly IMongoBaseRepository _emptyRoomRateRepo; private readonly ILogger _logger; public GameConfigService(IMongoBaseRepository mongo, IMapper mapper, IRedisService redis, ICapPublisher capPublisher, IMongoBaseRepository botProfileRepo, IMongoBaseRepository emptyRoomRateRepo, ILogger logger) { _mongo = mongo; _mapper = mapper; _redis = redis; _capPublisher = capPublisher; _botProfileRepo = botProfileRepo; _emptyRoomRateRepo = emptyRoomRateRepo; _logger = logger; } public async Task Get() { var gameConfig = _redis.GetGameConfig(); if (gameConfig == null) { gameConfig = await _mongo.FindOneAsync(c => true); if (gameConfig != null) _redis.SetGameConfig(gameConfig); } return gameConfig; } public async Task<(bool isok, string msg)> Update(GameConfig gameConfig) { NormalizeFishReferences(gameConfig); var operationId = Guid.NewGuid().ToString("N"); var startedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); bool isadd = false; var model = await Get(); if (model == null) { isadd = true; gameConfig.Id = Guid.NewGuid().ToString("N"); } else { gameConfig.Id = model.Id; } var saveResult = isadd ? await _mongo.InsertOneAsync(gameConfig) : await _mongo.ReplaceOneAsync(gameConfig); if (!saveResult.ok) { return saveResult; } _redis.SetGameConfig(gameConfig); var killConfig = new FishingKillConfig { FishTypeConfigs = gameConfig.FishTypeConfigs?.Select(f => new FishTypeBriefConfig { FishNumber = f.FishNumber, FishName = f.FishName, FishType = f.FishType, FishScore = f.FishScore, KillWeight = f.KillWeight }).ToList(), KillConfig = gameConfig.KillConfig, BossConfigs = gameConfig.BossConfigs, BossDropConfig = gameConfig.BossDropConfig, BattleOrderIsOpen = _redis.GetActivityConfig()?.BattleOrderConfig?.IsOpen ?? false }; _redis.SetFishingKillConfig(killConfig); try { var fishSpawnJson = FishSpawnExtractor.ExtractToJson(gameConfig); _redis.SetFishSpawnConfig(fishSpawnJson); } catch (Exception ex) { _logger.LogError(ex, "[FishSpawn] 出鱼配置提取失败"); } try { var publishedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _capPublisher.Publish(TopicKeys.配置变更, new ConfigChangedMessage { ConfigType = "GameConfig", OperationId = operationId, PublishedAtUnixMs = publishedAt }); } catch (Exception ex) { _logger.LogError(ex, "[GameConfig] CAP配置变更事件发布失败 operationId={OperationId}", operationId); } return saveResult; } private static void NormalizeFishReferences(GameConfig gameConfig) { if (gameConfig == null) return; gameConfig.FishTypeConfigs ??= new List(); gameConfig.FishArrayConfigs ??= new List(); gameConfig.RoomFishConfigs ??= new List(); var validFishNumbers = gameConfig.FishTypeConfigs .Where(f => f != null && !string.IsNullOrWhiteSpace(f.FishNumber)) .Select(f => f.FishNumber) .ToHashSet(StringComparer.Ordinal); gameConfig.FishArrayConfigs = gameConfig.FishArrayConfigs .Where(a => a != null && (string.IsNullOrWhiteSpace(a.FishNumber) || validFishNumbers.Contains(a.FishNumber)) && (string.IsNullOrWhiteSpace(a.BossFishNumber) || validFishNumbers.Contains(a.BossFishNumber))) .ToList(); var validArrayNumbers = gameConfig.FishArrayConfigs .Where(a => !string.IsNullOrWhiteSpace(a.FishArrayNumber)) .Select(a => a.FishArrayNumber) .ToHashSet(StringComparer.Ordinal); bool IsValidRoomFish(RoomFishDetailConfig item) => item != null && item.Type switch { 1 => validFishNumbers.Contains(item.FishNumber), 2 => validArrayNumbers.Contains(item.FishNumber), _ => false }; foreach (var roomFishConfig in gameConfig.RoomFishConfigs.Where(c => c != null)) { roomFishConfig.NormalFishList = (roomFishConfig.NormalFishList ?? new List()) .Where(IsValidRoomFish).ToList(); roomFishConfig.RoomFishLevelList = (roomFishConfig.RoomFishLevelList ?? new List()) .Where(IsValidRoomFish).ToList(); } static void CleanFishNumbers(BossConfig.BaseBossConfig config, HashSet validNumbers) { if (config == null) return; config.FishNumber = (config.FishNumber ?? new List()) .Where(validNumbers.Contains).Distinct().ToList(); } var boss = gameConfig.BossConfigs; if (boss != null) { boss.LuckWheel = (boss.LuckWheel ?? new List()) .Where(c => c != null).ToList(); boss.LuckWheel.ForEach(c => CleanFishNumbers(c, validFishNumbers)); boss.LuckWheel = boss.LuckWheel.Where(c => c.FishNumber.Count > 0).ToList(); CleanFishNumbers(boss.LuckPoker, validFishNumbers); boss.LuckFlyDisk = (boss.LuckFlyDisk ?? new List()) .Where(c => c != null).ToList(); boss.LuckFlyDisk.ForEach(c => CleanFishNumbers(c, validFishNumbers)); boss.LuckFlyDisk = boss.LuckFlyDisk.Where(c => c.FishNumber.Count > 0).ToList(); CleanFishNumbers(boss.FullScreen, validFishNumbers); CleanFishNumbers(boss.Range, validFishNumbers); CleanFishNumbers(boss.AmmunitionBox, validFishNumbers); CleanFishNumbers(boss.InterstellarCruise, validFishNumbers); CleanFishNumbers(boss.FlameStorm, validFishNumbers); CleanFishNumbers(boss.Bomb, validFishNumbers); CleanFishNumbers(boss.Scorpion, validFishNumbers); boss.Probability = (boss.Probability ?? new List()) .Where(c => c != null).ToList(); boss.Probability.ForEach(c => CleanFishNumbers(c, validFishNumbers)); boss.Probability = boss.Probability.Where(c => c.FishNumber.Count > 0).ToList(); boss.LuckDragon = (boss.LuckDragon ?? new List()) .Where(c => c != null).ToList(); boss.LuckDragon.ForEach(c => CleanFishNumbers(c, validFishNumbers)); boss.LuckDragon = boss.LuckDragon.Where(c => c.FishNumber.Count > 0).ToList(); } if (gameConfig.RobotKillConfig != null) { gameConfig.RobotKillConfig.Scenes = (gameConfig.RobotKillConfig.Scenes ?? new List()) .Where(s => s != null && validFishNumbers.Contains(s.FishNumber)) .ToList(); } } public async Task<(bool isok, string msg)> Delete() { _redis.RemoveGameConfig(); return await _mongo.DeleteOneAsync(c=>true); } public async Task> InitRoom(GameTypeEnum gameType = GameTypeEnum.None, int roomLevel = 0, GameConfig gameConfig = null) { List result = new List(); if (gameConfig == null) { gameConfig = await Get(); } if (gameConfig == null || gameConfig.RoomConfigs == null || gameConfig.RoomConfigs.Count == 0) { return result; } var roomConfig = gameConfig.RoomConfigs.AsEnumerable(); if (gameType != GameTypeEnum.None) roomConfig = roomConfig.Where(c => c.GameType == gameType); if (roomLevel > 0) roomConfig = roomConfig.Where(c => c.RoomLevel == roomLevel); var roomList = roomConfig.ToList(); if (roomList.Count == 0) return result; var initBotProfiles = RoomBotFactory.FixedBotSeatsEnabled ? _botProfileRepo.FilterBy(p => true).ToList() : null; foreach (var item in roomList.Where(c=>c.GameType!= GameTypeEnum.中国象棋)) { var rateConfig = _emptyRoomRateRepo .FilterBy(c => c.GameType == item.GameType && c.RoomLevel == item.RoomLevel && !c.IsDelete) .FirstOrDefault(); var botsEnabled = gameConfig.BotConfig?.Enabled == true; var emptyRate = botsEnabled ? Math.Clamp(rateConfig?.EmptyRoomRate ?? 0, 0, 100) : 100; var totalRoomCount = item.RoomPartitionNum * item.RoomCount; var roomsWithBots = emptyRate > 0 ? (int)Math.Floor(totalRoomCount * (100 - emptyRate) / 100.0) : totalRoomCount; var botRoomIndexes = Enumerable.Range(0, totalRoomCount) .OrderBy(_ => global::System.Random.Shared.Next()) .Take(roomsWithBots) .ToHashSet(); for (var i = 1; i <= item.RoomPartitionNum; i++) { _redis.RemoveGameRoom($"{(int)item.GameType}{item.RoomLevel}{i}"); for (int c = 0; c < item.RoomCount; c++) { RedisGameRoom room = new RedisGameRoom() { RoomName = item.RoomName, GameType = item.GameType, RoomLevel = item.RoomLevel, RoomPartition = i, RoomNumber = c, MaxPlayerCount = (item.GameType == GameTypeEnum.星际迷航 || item.GameType == GameTypeEnum.中国象棋) ? 2 : 4, CurrentOnlinePlayerCount = 0, }; var roomIndex = (i - 1) * item.RoomCount + c; room.RobotCount = botsEnabled && botRoomIndexes.Contains(roomIndex) ? global::System.Random.Shared.Next(1, room.MaxPlayerCount) : 0; room.RoomId = $"{(int)room.GameType}{room.RoomLevel}{room.RoomPartition}{c.ToString("00")}"; if (room.RobotCount > 0 && RoomBotFactory.FixedBotSeatsEnabled) { var fixedBots = RoomBotFactory.CreateFixedSeatBots(room, item, gameConfig, initBotProfiles); if (fixedBots.Count > 0) { room.Players.AddRange(fixedBots); room.RobotCount = fixedBots.Count; } } result.Add(room); } } } foreach (var item in result) { _redis.SetGameRoom(item); } return result; } public Task InitDefaultConfig() { return Task.FromResult("初始化配置请使用GameConfigController的Init接口"); } public async Task ResetDefaultRoomConfigs() { var config = await Get(); if (config == null) return; var chessConfigs = config.RoomConfigs?.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList() ?? new List(); config.RoomConfigs = new List(); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20011", RoomName = "初级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20012", RoomName = "中级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20013", RoomName = "高级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20021", RoomName = "初级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20022", RoomName = "中级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20023", RoomName = "高级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20041", RoomName = "初级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20042", RoomName = "中级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20043", RoomName = "高级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20051", RoomName = "初级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20052", RoomName = "中级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } }); config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20053", RoomName = "高级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } }); if (chessConfigs.Any()) config.RoomConfigs.AddRange(chessConfigs); await Update(config); } } }