添加项目文件。
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
namespace FishGameService.Services;
|
||||
|
||||
public static class KillAnnouncePolicy
|
||||
{
|
||||
public static bool ShouldAnnounce(long gold, long configuredMinGold)
|
||||
{
|
||||
var minGold = configuredMinGold > 0 ? configuredMinGold : 10_000_000L;
|
||||
return gold >= minGold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
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)}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user