Files
2026-10-12 16:22:04 +08:00

355 lines
17 KiB
C#

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<GameConfig> _mongo;
private readonly IMapper _mapper;
private IRedisService _redis;
private readonly ICapPublisher _capPublisher;
private readonly IMongoBaseRepository<BotProfileEntity> _botProfileRepo;
private readonly IMongoBaseRepository<EmptyRoomRateConfig> _emptyRoomRateRepo;
private readonly ILogger<GameConfigService> _logger;
public GameConfigService(IMongoBaseRepository<GameConfig> mongo, IMapper mapper, IRedisService redis, ICapPublisher capPublisher, IMongoBaseRepository<BotProfileEntity> botProfileRepo, IMongoBaseRepository<EmptyRoomRateConfig> emptyRoomRateRepo, ILogger<GameConfigService> logger)
{
_mongo = mongo;
_mapper = mapper;
_redis = redis;
_capPublisher = capPublisher;
_botProfileRepo = botProfileRepo;
_emptyRoomRateRepo = emptyRoomRateRepo;
_logger = logger;
}
public async Task<GameConfig> 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<FishTypeConfig>();
gameConfig.FishArrayConfigs ??= new List<FishArrayConfig>();
gameConfig.RoomFishConfigs ??= new List<RoomFishConfig>();
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<RoomFishDetailConfig>())
.Where(IsValidRoomFish).ToList();
roomFishConfig.RoomFishLevelList = (roomFishConfig.RoomFishLevelList ?? new List<RoomFishLevelConfig>())
.Where(IsValidRoomFish).ToList();
}
static void CleanFishNumbers(BossConfig.BaseBossConfig config, HashSet<string> validNumbers)
{
if (config == null) return;
config.FishNumber = (config.FishNumber ?? new List<string>())
.Where(validNumbers.Contains).Distinct().ToList();
}
var boss = gameConfig.BossConfigs;
if (boss != null)
{
boss.LuckWheel = (boss.LuckWheel ?? new List<BossConfig.LuckWheelConfig>())
.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<BossConfig.LuckFlyDiskConfig>())
.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<BossConfig.BossProbabilityConfig>())
.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<BossConfig.LuckDragonConfig>())
.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<RobotKillScene>())
.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<List<RedisGameRoom>> InitRoom(GameTypeEnum gameType = GameTypeEnum.None, int roomLevel = 0, GameConfig gameConfig = null)
{
List<RedisGameRoom> result = new List<RedisGameRoom>();
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<string> 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<RoomConfig>();
config.RoomConfigs = new List<RoomConfig>();
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20011", RoomName = "初级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 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<int> { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } });
if (chessConfigs.Any())
config.RoomConfigs.AddRange(chessConfigs);
await Update(config);
}
}
}