From ead70db0d72a82dc4303c54ff0342c1ff3ea93bd Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Sat, 27 Jun 2026 16:38:49 +0200 Subject: [PATCH 1/5] Reviewed the old code and improved it to support multiple sources --- TBot/Workers/AutoDiscoveryWorker.cs | 569 +++++++++++----------------- TBot/instance_settings.json | 10 +- 2 files changed, 231 insertions(+), 348 deletions(-) diff --git a/TBot/Workers/AutoDiscoveryWorker.cs b/TBot/Workers/AutoDiscoveryWorker.cs index e4b2c26..b833ff1 100644 --- a/TBot/Workers/AutoDiscoveryWorker.cs +++ b/TBot/Workers/AutoDiscoveryWorker.cs @@ -21,21 +21,7 @@ public class AutoDiscoveryWorker : WorkerBase { private readonly IFleetScheduler _fleetScheduler; private readonly ICalculationService _calculationService; private readonly ITBotOgamedBridge _tbotOgameBridge; - - private sealed class OriginCursor { - public int System; - public int NextPosition; - } - private readonly Dictionary _originCursors = new(); - - private sealed class DiscoveryRunContext { - public int FleetsToSend; - public bool Stop; - public int Skips; - public int GlobalAttempts; - public int MaxGlobalAttempts; - } - + private readonly SemaphoreSlim _asyncLock = new(1, 1); public AutoDiscoveryWorker(ITBotMain parentInstance, IOgameService ogameService, IFleetScheduler fleetScheduler, @@ -48,379 +34,270 @@ public AutoDiscoveryWorker(ITBotMain parentInstance, _tbotOgameBridge = tbotOgameBridge; } - private sealed class CoordinateComparer : IEqualityComparer { - public bool Equals(Coordinate x, Coordinate y) { - if (ReferenceEquals(x, y)) return true; - if (x is null || y is null) return false; - return x.Galaxy == y.Galaxy && x.System == y.System && x.Position == y.Position && x.Type == y.Type; - } - - public int GetHashCode(Coordinate obj) { - if (obj is null) return 0; - unchecked { - int hash = 17; - hash = hash * 31 + obj.Galaxy.GetHashCode(); - hash = hash * 31 + obj.System.GetHashCode(); - hash = hash * 31 + obj.Position.GetHashCode(); - hash = hash * 31 + obj.Type.GetHashCode(); - return hash; - } - } - } - - private static readonly IEqualityComparer _coordinateComparer = new CoordinateComparer(); - - private void EnsureBlacklistInitialized() { - if (_tbotInstance?.UserData == null) return; - - if (_tbotInstance.UserData.discoveryBlackList == null) { - _tbotInstance.UserData.discoveryBlackList = new Dictionary(_coordinateComparer); - return; - } - - if (!ReferenceEquals(_tbotInstance.UserData.discoveryBlackList.Comparer, _coordinateComparer)) { - var old = _tbotInstance.UserData.discoveryBlackList; - var fresh = new Dictionary(_coordinateComparer); - foreach (var kv in old) { - - if (fresh.TryGetValue(kv.Key, out var existing)) { - if (kv.Value > existing) fresh[kv.Key] = kv.Value; - } else { - fresh[kv.Key] = kv.Value; - } - } - _tbotInstance.UserData.discoveryBlackList = fresh; - } - } - - private void CleanupExpiredBlacklistAll(DateTime now) { - if (_tbotInstance?.UserData?.discoveryBlackList == null) return; - var toRemove = _tbotInstance.UserData.discoveryBlackList - .Where(kv => kv.Value <= now) - .Select(kv => kv.Key) - .ToList(); - foreach (var k in toRemove) - _tbotInstance.UserData.discoveryBlackList.Remove(k); - } - - private bool IsBlacklistedAndActive(Coordinate c, DateTime now) { - if (_tbotInstance?.UserData?.discoveryBlackList == null) return false; - return _tbotInstance.UserData.discoveryBlackList.TryGetValue(c, out var until) && until > now; - } - - private void UpsertBlacklist(Coordinate c, DateTime until) { - if (_tbotInstance?.UserData?.discoveryBlackList == null) return; - _tbotInstance.UserData.discoveryBlackList[c] = until; - } - - private List GetConfiguredOrigins(List celestials) { - var result = new List(); - if (celestials == null || celestials.Count == 0) return result; - - var originsCfg = _tbotInstance?.InstanceSettings?.AutoDiscovery?.Origin; - if (originsCfg == null) return result; - - foreach (var o in originsCfg) { - int galaxy = Convert.ToInt32(o.Galaxy); - int system = Convert.ToInt32(o.System); - int position = Convert.ToInt32(o.Position); - string typeStr = Convert.ToString(o.Type) ?? "Planet"; - - Celestials wantedType = Celestials.Planet; - if (Enum.TryParse(typeStr, true, out Celestials parsed)) - wantedType = parsed; - - var found = celestials.FirstOrDefault(c => - c?.Coordinate != null && - c.Coordinate.Galaxy == galaxy && - c.Coordinate.System == system && - c.Coordinate.Position == position && - c.Coordinate.Type == wantedType - ); - - if (found != null) - result.Add(found); - else - _tbotInstance.log(LogLevel.Warning, LogSender.AutoDiscovery, - $"AutoDiscovery origin not found in user celestials: {galaxy}:{system}:{position} ({typeStr})"); - } - - return result; - } - - private string OriginKey(Celestial origin) { - - return $"{origin.Coordinate.Galaxy}:{origin.Coordinate.System}:{origin.Coordinate.Position}:{origin.Coordinate.Type}"; - } - - private OriginCursor GetOrInitCursor(Celestial origin) { - var key = OriginKey(origin); - if (!_originCursors.TryGetValue(key, out var cursor) || cursor == null) { - cursor = new OriginCursor { - System = origin.Coordinate.System, - NextPosition = 1 - }; - _originCursors[key] = cursor; - } - - int maxSystem = _tbotInstance.UserData.serverData.Systems; - if (cursor.System < 1 || cursor.System > maxSystem) cursor.System = origin.Coordinate.System; - if (cursor.NextPosition < 1 || cursor.NextPosition > 15) cursor.NextPosition = 1; - - return cursor; - } - - private int AdvanceSystem(int system) { - int maxSystem = _tbotInstance.UserData.serverData.Systems; - system++; - if (system > maxSystem) system = 1; - return system; - } - - private async Task ProcessOneSystemForOrigin( - Celestial origin, - OriginCursor cursor, - int discoveries, - DiscoveryRunContext ctx) { - - if (origin?.Coordinate == null) return discoveries; - if (discoveries <= 0 || ctx.FleetsToSend <= 0 || ctx.Stop) return discoveries; - - int systemToDo = cursor.System; - - DateTime now = await _tbotOgameBridge.GetDateTime(); - int resumeNextPos = cursor.NextPosition; - for (int pos = cursor.NextPosition; pos <= 15; pos++) { - resumeNextPos = pos; - - if (discoveries <= 0 || ctx.FleetsToSend <= 0 || ctx.Stop) break; - if (_tbotInstance.UserData.slots.Free <= (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree) { ctx.Stop = true; break; } - - if (++ctx.GlobalAttempts > ctx.MaxGlobalAttempts) { ctx.Stop = true; break; } - - var dest = new Coordinate { - Galaxy = origin.Coordinate.Galaxy, - System = systemToDo, - Position = pos, - Type = Celestials.Planet - }; - - if (IsBlacklistedAndActive(dest, now)) { ctx.Skips++; continue; } - - var ok = await _ogameService.SendDiscovery(origin, dest); - if (!ok) { - DoLog(LogLevel.Warning, $"Failed to send discovery fleet to {dest} from {origin}."); - UpsertBlacklist(dest, now.AddDays(7)); - } else { - DoLog(LogLevel.Information, $"Discovery fleet sent to {dest} from {origin}."); - UpsertBlacklist(dest, now.AddDays(7)); - discoveries--; - ctx.FleetsToSend--; - } - - resumeNextPos = pos + 1; - - _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); - } - - if (!ctx.Stop && resumeNextPos > 15 && discoveries > 0 && ctx.FleetsToSend > 0 && - _tbotInstance.UserData.slots.Free > (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree) { - - cursor.System = AdvanceSystem(systemToDo); - cursor.NextPosition = 1; - } else { - - if (resumeNextPos < 1) resumeNextPos = 1; - if (resumeNextPos > 15) resumeNextPos = 15; - cursor.NextPosition = resumeNextPos; - } - - return discoveries; - } - - - private long CalcFallbackIntervalMs() { - return RandomizeHelper.CalcRandomInterval( - (int)_tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMin, - (int)_tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMax - ); - } - - private long CalcNextIntervalFromDiscoveryFleetsOrFallback() { - var discFleet = _tbotInstance.UserData.fleets - .Where(f => f.Mission == Missions.Discovery && f.BackIn.HasValue) - .OrderBy(f => f.BackIn.Value) - .FirstOrDefault(); - - if (discFleet == null) return CalcFallbackIntervalMs(); - - long backInSec = discFleet.BackIn ?? 0; - long interval = backInSec * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds); - if (interval <= 0) interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds); - return interval; - } - protected override async Task Execute() { bool delay = false; bool stop = false; int skips = 0; - + var rand = new Random(); try { DoLog(LogLevel.Information, $"Starting AutoDiscovery..."); - - EnsureBlacklistInitialized(); - - if (_tbotInstance.UserData.isSleeping) { - stop = true; - return; + if (_tbotInstance.UserData.discoveryBlackList == null) { + _tbotInstance.UserData.discoveryBlackList = new Dictionary(); + } else { + if (_tbotInstance.UserData.discoveryBlackList.First().Value < DateTime.Now || _tbotInstance.UserData.discoveryBlackList.Last().Value < DateTime.Now) { + foreach(var (blacklistedCoord, blacklistedTime) in _tbotInstance.UserData.discoveryBlackList.ToList()) { + if (blacklistedTime < DateTime.Now) + _tbotInstance.UserData.discoveryBlackList.Remove(blacklistedCoord); + } + } } + if (!_tbotInstance.UserData.isSleeping) { + if ((bool) _tbotInstance.InstanceSettings.SleepMode.Active) { + DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep); + DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp); + DateTime timeSleep = await _tbotOgameBridge.GetDateTime(); + if (GeneralHelper.ShouldSleep(timeSleep, goToSleep, wakeUp)) { + DoLog(LogLevel.Warning, "Unable to send discovery fleet: bed time has passed"); + stop = true; + return; + } + } - if ((bool)_tbotInstance.InstanceSettings.SleepMode.Active) { - DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep); - DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp); - DateTime timeSleep = await _tbotOgameBridge.GetDateTime(); - if (GeneralHelper.ShouldSleep(timeSleep, goToSleep, wakeUp)) { - DoLog(LogLevel.Warning, "Unable to send discovery fleet: bed time has passed"); - stop = true; + _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); + _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets(); + List rankSlotsPriority = new() { + new RankSlotsPriority(Feature.BrainAutoMine, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain, + ((bool)_tbotInstance.InstanceSettings.Brain.Active && + (bool)_tbotInstance.InstanceSettings.Brain.Transports.Active && + ((bool)_tbotInstance.InstanceSettings.Brain.AutoMine.Active || + (bool)_tbotInstance.InstanceSettings.Brain.AutoResearch.Active || + (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || + (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)), + (int)_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots, + (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport) + ), + new RankSlotsPriority(Feature.Expeditions, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions, + (bool)_tbotInstance.InstanceSettings.Expeditions.Active, + (int)_tbotInstance.UserData.slots.ExpTotal, + (int)_tbotInstance.UserData.slots.ExpInUse + ), + new RankSlotsPriority(Feature.AutoFarm, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm, + (bool)_tbotInstance.InstanceSettings.AutoFarm.Active, + (int)_tbotInstance.InstanceSettings.AutoFarm.MaxSlots, + (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack) + ), + new RankSlotsPriority(Feature.Colonize, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoColonize, + (bool)_tbotInstance.InstanceSettings.AutoColonize.Active, + (bool)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ? + (int)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots : 1, + (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize) + ), + new RankSlotsPriority(Feature.AutoDiscovery, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery, + (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active, + (int)_tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots, + (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery) + ), + new RankSlotsPriority(Feature.Harvest, + (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest, + (bool)_tbotInstance.InstanceSettings.AutoHarvest.Active, + (int)_tbotInstance.InstanceSettings.AutoHarvest.MaxSlots, + (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest) + ) + }; + int fleetsToSend = _calculationService.CalcSlotsPriority(Feature.AutoDiscovery, + rankSlotsPriority, + _tbotInstance.UserData.slots, + _tbotInstance.UserData.fleets, + (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree + ); + + if (fleetsToSend <= 0) { + delay = true; return; } - } - - DateTime now = await _tbotOgameBridge.GetDateTime(); - CleanupExpiredBlacklistAll(now); - - _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); - _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets(); - - List rankSlotsPriority = new() { - new RankSlotsPriority(Feature.BrainAutoMine, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain, - ((bool)_tbotInstance.InstanceSettings.Brain.Active && (bool)_tbotInstance.InstanceSettings.Brain.Transports.Active && - ((bool)_tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool)_tbotInstance.InstanceSettings.Brain.AutoResearch.Active || - (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)), - (int)_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots, - (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport) - ), - new RankSlotsPriority(Feature.Expeditions, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions, - (bool)_tbotInstance.InstanceSettings.Expeditions.Active, - (int)_tbotInstance.UserData.slots.ExpTotal, - (int)_tbotInstance.UserData.slots.ExpInUse - ), - new RankSlotsPriority(Feature.AutoFarm, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm, - (bool)_tbotInstance.InstanceSettings.AutoFarm.Active, - (int)_tbotInstance.InstanceSettings.AutoFarm.MaxSlots, - (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack) - ), - new RankSlotsPriority(Feature.Colonize, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize, - (bool)_tbotInstance.InstanceSettings.AutoColonize.Active, - (bool)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ? (int)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots : 1, - (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize) - ), - new RankSlotsPriority(Feature.AutoDiscovery, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery, - (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active, - (int)_tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots, - (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery) - ), - new RankSlotsPriority(Feature.Harvest, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest, - (bool)_tbotInstance.InstanceSettings.AutoHarvest.Active, - (int)_tbotInstance.InstanceSettings.AutoHarvest.MaxSlots, - (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest) - ) - }; - int fleetsToSend = _calculationService.CalcSlotsPriority( - Feature.AutoDiscovery, rankSlotsPriority, - _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, - (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree - ); - - if (fleetsToSend <= 0) { - delay = true; - return; - } - - var celestials = await _ogameService.GetCelestials(); - var origins = GetConfiguredOrigins(celestials); - - if (origins == null || origins.Count == 0) { - stop = true; - DoLog(LogLevel.Warning, "Unable to parse AutoDiscovery origin."); - return; - } + var celestials = await _ogameService.GetCelestials(); + /* + var configureddestinationCoord = _tbotInstance.InstanceSettings.AutoDiscovery.Origin; + Celestial origin = celestials.FirstOrDefault(c => c.Coordinate.Galaxy == configureddestinationCoord.Galaxy && + c.Coordinate.System == configureddestinationCoord.System && + c.Coordinate.Position == configureddestinationCoord.Position && + c.Coordinate.Type.ToString().Equals(configureddestinationCoord.Type, StringComparison.OrdinalIgnoreCase)); + */ + + List origins = new(); + if (_tbotInstance.InstanceSettings.AutoDiscovery.Origin.Length > 0) { + try { + foreach (var origin in _tbotInstance.InstanceSettings.AutoDiscovery.Origin) { + Coordinate customOriginCoords = new( + (int) origin.Galaxy, + (int) origin.System, + (int) origin.Position, + Enum.Parse(origin.Type.ToString()) + ); + Celestial customOrigin = _tbotInstance.UserData.celestials + .Unique() + .Single(planet => planet.HasCoords(customOriginCoords)); + customOrigin = await _tbotOgameBridge.UpdatePlanet(customOrigin, UpdateTypes.Ships); + customOrigin = await _tbotOgameBridge.UpdatePlanet(customOrigin, UpdateTypes.LFBonuses); + origins.Add(customOrigin); + } + origins = origins.OrderBy(planet => planet.Coordinate.Galaxy) + .ThenBy(planet => planet.Coordinate.System) + .ThenBy(planet => planet.Coordinate.Position) + .ToList(); + } catch (Exception e) { + DoLog(LogLevel.Debug, $"Exception: {e.Message}"); + DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}"); + DoLog(LogLevel.Warning, "Unable to parse custom origin"); + + origins.Add(_tbotInstance.UserData.celestials + .OrderBy(planet => planet.Coordinate.Type == Celestials.Planet) + .ThenByDescending(planet => planet.Resources) + .First() + ); + } + } else { + origins.Add(_tbotInstance.UserData.celestials + .OrderBy(planet => planet.Coordinate.Type == Celestials.Planet) + .ThenByDescending(planet => planet.Resources) + .First() + ); + } - var ctx = new DiscoveryRunContext { - FleetsToSend = fleetsToSend, - Stop = false, - Skips = 0, - GlobalAttempts = 0, - MaxGlobalAttempts = 600 - }; - - foreach (var origin in origins) { - if (ctx.Stop) break; - if (ctx.FleetsToSend <= 0) break; - if (origin?.Coordinate == null) continue; + if (origins.Count == 0) { + stop = true; + DoLog(LogLevel.Warning, "Unable to parse AutoDiscovery origin"); + return; + } - int discoveries = await _ogameService.GetAvailableDiscoveries(origin); + int discoveries = await _ogameService.GetAvailableDiscoveries(origins.First()); if (discoveries <= 0) { - DoLog(LogLevel.Information, $"No discoveries available from {origin} right now."); - continue; + DoLog(LogLevel.Information, "No discoveries available at the moment."); + stop = true; + return; + } + DoLog(LogLevel.Information, $"There are {discoveries} discoveries available."); + + Dictionary destinationCoord = new(); + if (!((bool) _tbotInstance.InstanceSettings.AutoDiscovery.RandomizeDestination)) { + int maxGalaxy = _tbotInstance.UserData.serverData.Galaxies; + int maxSystem = _tbotInstance.UserData.serverData.Systems; + for (int galaxy = 1; galaxy <= maxGalaxy; galaxy++) { + for (int system = 1; system <= maxSystem; system++) { + for (int position = 1; position <= 15; position++) { + destinationCoord.Add( + new Coordinate { Galaxy = galaxy, System = system, Position = position }, + origins.OrderBy(o => _calculationService.CalcDistance(o.Coordinate, new Coordinate { Galaxy = galaxy, System = system }, _tbotInstance.UserData.serverData)).First() + ); + } + } + } + + destinationCoord = destinationCoord.Where(c => !_tbotInstance.UserData.discoveryBlackList.Keys.Any(k => k.Galaxy == c.Key.Galaxy && k.System == c.Key.System && k.Position == c.Key.Position)) + .OrderBy(c => _calculationService.CalcDistance(new Coordinate { Galaxy = c.Value.Coordinate.Galaxy, System = c.Value.Coordinate.System }, new Coordinate { Galaxy = c.Key.Galaxy, System = c.Key.System }, _tbotInstance.UserData.serverData)) + .ThenBy(c => c.Value.Coordinate.Galaxy) + .ThenBy(c => c.Value.Coordinate.System) + .ToDictionary(c => c.Key, c => c.Value); + + /*destinationCoord = destinationCoord.OrderBy(c => c.Value.Coordinate.Galaxy) + .ThenBy(c => c.Value.Coordinate.System) + .ToDictionary(c => c.Key, c => c.Value);*/ } - var cursor = GetOrInitCursor(origin); - DoLog(LogLevel.Information, $"Origin {origin} cursor system={cursor.System}, nextPos={cursor.NextPosition} (discoveries={discoveries})"); + while (discoveries > 0 && !stop && fleetsToSend > 0 && _tbotInstance.UserData.slots.Free > (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree) { + Dictionary dest = new(); + if ((bool) _tbotInstance.InstanceSettings.AutoDiscovery.RandomizeDestination) { + int randomIndex = Random.Shared.Next(0, origins.Count); + dest.Add( + origins[randomIndex], + new Coordinate { Galaxy = origins[randomIndex].Coordinate.Galaxy, System = Random.Shared.Next(1, _tbotInstance.UserData.serverData.Systems + 1), Position = Random.Shared.Next(1, 16) } + ); + } else { + dest.Add(destinationCoord.Values.First(), destinationCoord.Keys.First()); + destinationCoord.Remove(destinationCoord.Keys.First()); + } + Coordinate blacklistedCoord = _tbotInstance.UserData.discoveryBlackList.Keys.SingleOrDefault(c => c.Galaxy == dest.Values.First().Galaxy && c.System == dest.Values.First().System && c.Position == dest.Values.First().Position); + if (blacklistedCoord != null) { + if (_tbotInstance.UserData.discoveryBlackList[blacklistedCoord] > DateTime.Now) { + skips++; + continue; + } else { + _tbotInstance.UserData.discoveryBlackList.Remove(blacklistedCoord); + } + } + + var result = await _ogameService.SendDiscovery(dest.Keys.First(), dest.Values.First()); + _tbotInstance.UserData.discoveryBlackList.Add(dest.Values.First(), DateTime.Now.AddDays(7)); + if (!result) { + DoLog(LogLevel.Warning, $"Failed to send discovery fleet to {dest.Values.First().ToString()} from {dest.Keys.First().ToString()}."); + } else { + DoLog(LogLevel.Information, $"Discovery fleet sent to {dest.Values.First().ToString()} from {dest.Keys.First().ToString()}."); + discoveries--; + fleetsToSend--; + } + + if (_tbotInstance.UserData.slots.Free <= (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree) { + DoLog(LogLevel.Information, "No more fleet slots available for AutoDiscovery in this cycle."); + stop = true; + break; + } + _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); + } - discoveries = await ProcessOneSystemForOrigin(origin, cursor, discoveries, ctx); + if (skips > 0) + DoLog(LogLevel.Information, $"{skips} positions skipped (blacklisted)"); - fleetsToSend = ctx.FleetsToSend; - stop = ctx.Stop; - skips = ctx.Skips; + _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets(); + long interval = (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Discovery).OrderByDescending(f => f.BackIn).ToList().First().BackIn * 1000 ?? 0) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds); + DateTime time = await _tbotOgameBridge.GetDateTime(); + if (interval < 0) + interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds); + time = time.AddMilliseconds(interval); + ChangeWorkerPeriod(interval); + DoLog(LogLevel.Information, $"Next check at {time.ToString()}"); + await _tbotOgameBridge.CheckCelestials(); + } else { + stop = true; } - - if (skips > 0) - DoLog(LogLevel.Information, $"{skips} positions skipped (blacklisted)"); - - _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets(); - - long interval = CalcNextIntervalFromDiscoveryFleetsOrFallback(); - DateTime time = await _tbotOgameBridge.GetDateTime(); - var nextTime = time.AddMilliseconds(interval); - - ChangeWorkerPeriod(interval); - DoLog(LogLevel.Information, $"Next AutoDiscovery check at {nextTime.ToString()}"); - await _tbotOgameBridge.CheckCelestials(); - } catch (Exception e) { + } catch (Exception e) { DoLog(LogLevel.Error, $"An error occured: {e.Message}"); DoLog(LogLevel.Debug, e.StackTrace); - } finally { + } finally { if (stop) { DoLog(LogLevel.Information, $"Stopping feature."); await EndExecution(); } - if (delay) { DoLog(LogLevel.Information, $"Delaying..."); var timeDelay = await _tbotOgameBridge.GetDateTime(); _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets(); - - long intervalDelay = CalcNextIntervalFromDiscoveryFleetsOrFallback(); + long intervalDelay = 0; + try { + intervalDelay = (_tbotInstance.UserData.fleets.Where(fleet => fleet.Mission == Missions.Discovery).OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds); + } catch { + intervalDelay = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMax); + } var newTimeDelay = timeDelay.AddMilliseconds(intervalDelay); - ChangeWorkerPeriod(intervalDelay); DoLog(LogLevel.Information, $"Next AutoDiscovery check at {newTimeDelay.ToString()}"); } - await _tbotOgameBridge.CheckCelestials(); } } public override bool IsWorkerEnabledBySettings() { try { - return (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active; + return (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active; } catch (Exception) { return false; } } - public override string GetWorkerName() { return "AutoDiscovery"; } @@ -432,4 +309,4 @@ public override LogSender GetLogSender() { return LogSender.AutoDiscovery; } } -} +} \ No newline at end of file diff --git a/TBot/instance_settings.json b/TBot/instance_settings.json index b95bf88..6a31636 100644 --- a/TBot/instance_settings.json +++ b/TBot/instance_settings.json @@ -21,7 +21,7 @@ "Height": 1080, "Timezone": "Europe/London", "Lang": "en" - } + } }, "General": { "Host": "localhost", @@ -544,6 +544,12 @@ "AutoDiscovery": { "Active": false, "Origin": [ + { + "Galaxy": 1, + "System": 234, + "Position": 5, + "Type": "Planet" + }, { "Galaxy": 5, "System": 64, @@ -551,7 +557,7 @@ "Type": "Planet" } ], - "MaxSlots": 5, + "MaxSlots": 15, "MaxFailures": 5, "RandomizeDestination": false, "CheckIntervalMin": 30, From 4ca6e9aaebfcb6c863ea3781025f53c1d28d9904 Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Sat, 27 Jun 2026 16:39:55 +0200 Subject: [PATCH 2/5] ixed an issue where locations were sometimes counted twice --- TBot/Includes/CalculationService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TBot/Includes/CalculationService.cs b/TBot/Includes/CalculationService.cs index f45390c..a743caa 100644 --- a/TBot/Includes/CalculationService.cs +++ b/TBot/Includes/CalculationService.cs @@ -4916,10 +4916,8 @@ public int CalcSlotsPriority(Feature feature, List rankSlotsP } else { if (actualFeature.Rank > 0) { _logger.WriteLog(LogLevel.Information, logsender, $"Slots Total: {slots.Total}, Reserved: {reservedSlots + slotsToLeaveFree}, Other: {usedSlots + otherSlots}. Available: {slotsAvailable}."); - if (slotsAvailable < actualFeature.MaxSlots) { + if (slotsAvailable < actualFeature.MaxSlots) _logger.WriteLog(LogLevel.Information, logsender, $"Less slots available ({slotsAvailable}) than maximum usable slots ({actualFeature.MaxSlots}). Stepping down to usable slots."); - actualFeature.MaxSlots = slotsAvailable; - } slotsAvailable = Math.Min(slotsAvailable, actualFeature.MaxSlots - actualFeature.SlotsUsed); _logger.WriteLog(LogLevel.Information, logsender, $"{feature} will use {slotsAvailable} slots ({actualFeature.SlotsUsed}/{actualFeature.MaxSlots} already used)."); } else { From 86ec7d043295b62d1310bcaaf393dcb9d784ed8c Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Mon, 20 Jul 2026 11:52:18 +0200 Subject: [PATCH 3/5] Rollback fix CalcSlotsPriority() --- TBot/Includes/CalculationService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TBot/Includes/CalculationService.cs b/TBot/Includes/CalculationService.cs index a743caa..f45390c 100644 --- a/TBot/Includes/CalculationService.cs +++ b/TBot/Includes/CalculationService.cs @@ -4916,8 +4916,10 @@ public int CalcSlotsPriority(Feature feature, List rankSlotsP } else { if (actualFeature.Rank > 0) { _logger.WriteLog(LogLevel.Information, logsender, $"Slots Total: {slots.Total}, Reserved: {reservedSlots + slotsToLeaveFree}, Other: {usedSlots + otherSlots}. Available: {slotsAvailable}."); - if (slotsAvailable < actualFeature.MaxSlots) + if (slotsAvailable < actualFeature.MaxSlots) { _logger.WriteLog(LogLevel.Information, logsender, $"Less slots available ({slotsAvailable}) than maximum usable slots ({actualFeature.MaxSlots}). Stepping down to usable slots."); + actualFeature.MaxSlots = slotsAvailable; + } slotsAvailable = Math.Min(slotsAvailable, actualFeature.MaxSlots - actualFeature.SlotsUsed); _logger.WriteLog(LogLevel.Information, logsender, $"{feature} will use {slotsAvailable} slots ({actualFeature.SlotsUsed}/{actualFeature.MaxSlots} already used)."); } else { From 10b144e08335b6c5f8713906c2d1459026bae2ed Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Mon, 20 Jul 2026 11:53:52 +0200 Subject: [PATCH 4/5] Add CalcSlotsPriority() to Expeditions --- TBot/Workers/ExpeditionsWorker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TBot/Workers/ExpeditionsWorker.cs b/TBot/Workers/ExpeditionsWorker.cs index cf09d0b..dd10ab9 100644 --- a/TBot/Workers/ExpeditionsWorker.cs +++ b/TBot/Workers/ExpeditionsWorker.cs @@ -120,8 +120,7 @@ protected override async Task Execute() { (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest)) }; - int MaxSlots = _tbotInstance.UserData.slots.Total - - (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree; + int MaxSlots = _calculationService.CalcSlotsPriority(Feature.Expeditions, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree); if (MaxSlots < 0) MaxSlots = 0; From 20b36467e80643accd79eabbe19fbe0422c6d8e0 Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Mon, 20 Jul 2026 15:45:17 +0200 Subject: [PATCH 5/5] missing initialization of profiles --- TBot/Services/TBotMain.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TBot/Services/TBotMain.cs b/TBot/Services/TBotMain.cs index 634b256..4246566 100644 --- a/TBot/Services/TBotMain.cs +++ b/TBot/Services/TBotMain.cs @@ -294,6 +294,7 @@ public async Task Init(string settingPath, userData.researches = await _tbotOgameBridge.UpdateResearches(); userData.scheduledFleets = new(); userData.farmTargets = new(); + userData.runningProfiles = new(); if (userData.celestials.Count == 1) { await EditSettings(userData.celestials.First());