139 lines
5.7 KiB
C#
139 lines
5.7 KiB
C#
using CommonDomain;
|
|
using DotNetCore.CAP;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using MongoRepository;
|
|
using RedisService;
|
|
|
|
namespace FishGameService.Services;
|
|
|
|
public class RobotKillAnnounceService : BackgroundService
|
|
{
|
|
private readonly ILogger<RobotKillAnnounceService> _logger;
|
|
private readonly IServiceScopeFactory _scopeFactory;
|
|
private readonly ICapPublisher _capPublisher;
|
|
private static readonly Random _random = new();
|
|
private const int MinimumAnnounceIntervalSeconds = 30;
|
|
private static readonly TimeSpan BotProfileCacheDuration = TimeSpan.FromMinutes(5);
|
|
private List<BotProfileEntity> _botProfiles = new();
|
|
private DateTime _botProfilesLastFetch = DateTime.MinValue;
|
|
private string? _lastConfigState;
|
|
private DateTime _lastConfigLogUtc = DateTime.MinValue;
|
|
|
|
public RobotKillAnnounceService(
|
|
ILogger<RobotKillAnnounceService> logger,
|
|
IServiceScopeFactory scopeFactory,
|
|
ICapPublisher capPublisher)
|
|
{
|
|
_logger = logger;
|
|
_scopeFactory = scopeFactory;
|
|
_capPublisher = capPublisher;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
var instanceId = Environment.GetEnvironmentVariable("INSTANCE_ID");
|
|
if (!string.IsNullOrWhiteSpace(instanceId) && instanceId != "1" && instanceId != "default")
|
|
{
|
|
|
|
return;
|
|
}
|
|
|
|
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
|
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var redisService = scope.ServiceProvider.GetRequiredService<IRedisService>();
|
|
var botRepo = scope.ServiceProvider.GetRequiredService<IMongoBaseRepository<BotProfileEntity>>();
|
|
|
|
var gameConfig = redisService.GetGameConfig();
|
|
var cfg = gameConfig?.RobotKillConfig;
|
|
var configState = cfg == null ? "Missing" : !cfg.IsEnabled ? "Disabled" : cfg.Scenes == null || cfg.Scenes.Count == 0 ? "NoScenes" : "Enabled";
|
|
if (configState != _lastConfigState || DateTime.UtcNow - _lastConfigLogUtc >= TimeSpan.FromMinutes(5))
|
|
{
|
|
|
|
_lastConfigState = configState;
|
|
_lastConfigLogUtc = DateTime.UtcNow;
|
|
}
|
|
|
|
if (cfg != null && cfg.IsEnabled && cfg.Scenes != null && cfg.Scenes.Count > 0)
|
|
{
|
|
var scene = PickSceneByWeight(cfg.Scenes);
|
|
if (scene != null)
|
|
{
|
|
var botProfiles = await GetBotProfilesAsync(botRepo);
|
|
var multiplier = scene.MultiplierMin >= scene.MultiplierMax
|
|
? scene.MultiplierMin
|
|
: _random.Next(scene.MultiplierMin, scene.MultiplierMax + 1);
|
|
var jackpot = scene.JackpotMin >= scene.JackpotMax
|
|
? scene.JackpotMin
|
|
: _random.NextInt64(scene.JackpotMin, scene.JackpotMax + 1);
|
|
var botName = GenerateBotName(botProfiles);
|
|
|
|
if (KillAnnouncePolicy.ShouldAnnounce(jackpot, gameConfig?.KillConfig?.GlobalAnnounceMinGold ?? 0))
|
|
{
|
|
_capPublisher.Publish(TopicKeys.全局击杀公告, new KillAnnounceParam
|
|
{
|
|
UserName = botName,
|
|
GameTypeName = scene.GameName,
|
|
RoomName = scene.LevelName,
|
|
FishName = scene.FishName,
|
|
Multiple = multiplier,
|
|
Gold = jackpot,
|
|
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
var configuredInterval = cfg?.IntervalSeconds > 0 ? cfg.IntervalSeconds : MinimumAnnounceIntervalSeconds;
|
|
var interval = Math.Max(configuredInterval, MinimumAnnounceIntervalSeconds) * 1000;
|
|
await Task.Delay(interval, stoppingToken);
|
|
}
|
|
catch (OperationCanceledException) { break; }
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "RobotKillAnnounceService 执行异常");
|
|
await Task.Delay(5000, stoppingToken);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<List<BotProfileEntity>> GetBotProfilesAsync(IMongoBaseRepository<BotProfileEntity> botRepo)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
if (_botProfiles.Count > 0 && now - _botProfilesLastFetch < BotProfileCacheDuration)
|
|
return _botProfiles;
|
|
|
|
var profiles = await botRepo.FilterByAsync(b => true) ?? new List<BotProfileEntity>();
|
|
_botProfiles = profiles;
|
|
_botProfilesLastFetch = now;
|
|
return _botProfiles;
|
|
}
|
|
|
|
private RobotKillScene? PickSceneByWeight(List<RobotKillScene> scenes)
|
|
{
|
|
var totalWeight = scenes.Sum(s => s.Weight);
|
|
if (totalWeight <= 0) return null;
|
|
var roll = _random.Next(totalWeight);
|
|
var accumulated = 0;
|
|
foreach (var scene in scenes)
|
|
{
|
|
accumulated += scene.Weight;
|
|
if (roll < accumulated) return scene;
|
|
}
|
|
return scenes.LastOrDefault();
|
|
}
|
|
|
|
private static string GenerateBotName(List<BotProfileEntity>? botProfiles)
|
|
{
|
|
if (botProfiles != null && botProfiles.Count > 0)
|
|
return botProfiles[_random.Next(botProfiles.Count)].Name;
|
|
return $"玩家{_random.Next(10000, 99999)}";
|
|
}
|
|
}
|