Skip to content

Commit caa1ee8

Browse files
committed
Add smart den recovery system to fix stuck raid loops
Bot now detects and recovers from invisible dens automatically: - After 1st failure: checks/sets IsActive flag and re-injects seed - After 3rd failure: triggers map refresh - No more 2+ hour stuck loops on inactive dens
1 parent 6d832e5 commit caa1ee8

3 files changed

Lines changed: 171 additions & 5 deletions

File tree

SysBot.Pokemon/Helpers/RaidMemoryManager.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,50 @@ private static List<long> DeterminePointer(int index)
150150
};
151151
}
152152
}
153+
154+
/// <summary>
155+
/// Reads the IsActive flag for a raid at the specified index.
156+
/// </summary>
157+
/// <param name="index">The global raid index</param>
158+
/// <param name="token">Cancellation token</param>
159+
/// <returns>True if the raid is marked as active in memory</returns>
160+
public async Task<bool> ReadIsActiveFlag(int index, CancellationToken token)
161+
{
162+
try
163+
{
164+
var ptr = DeterminePointer(index);
165+
ptr[3] += 0x18;
166+
byte[] data = await _connection.PointerPeek(1, ptr, token).ConfigureAwait(false);
167+
return data[0] != 0;
168+
}
169+
catch
170+
{
171+
return false;
172+
}
173+
}
174+
175+
/// <summary>
176+
/// Sets the IsActive flag for a raid at the specified index.
177+
/// </summary>
178+
/// <param name="index">The global raid index</param>
179+
/// <param name="isActive">True to mark as active, false to mark as inactive</param>
180+
/// <param name="token">Cancellation token</param>
181+
/// <returns>True if the operation was successful</returns>
182+
public async Task<bool> SetIsActiveFlag(int index, bool isActive, CancellationToken token)
183+
{
184+
try
185+
{
186+
var ptr = DeterminePointer(index);
187+
ptr[3] += 0x18;
188+
byte[] flagByte = new byte[] { (byte)(isActive ? 1 : 0) };
189+
await _connection.PointerPoke(flagByte, ptr, token).ConfigureAwait(false);
190+
191+
byte[] verification = await _connection.PointerPeek(1, ptr, token).ConfigureAwait(false);
192+
return verification[0] == flagByte[0];
193+
}
194+
catch
195+
{
196+
return false;
197+
}
198+
}
153199
}

SysBot.Pokemon/Helpers/SVRaidBot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
public static class SVRaidBot
44
{
5-
public const string Version = "v8.7.5";
5+
public const string Version = "v8.7.6";
66
public const string Repo = "https://github.com/hexbyt3/SVRaidBot";
77
public const string ConfigPath = "config.json";
88
}

SysBot.Pokemon/SV/BotRaid/RotatingRaidBotSV.cs

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,17 @@ public record PlayerInfo
102102
private bool _isRecoveringFromReboot;
103103
private volatile bool _isPaused = false;
104104

105+
private int _consecutiveDenFailures = 0;
106+
private int _lastFailedDenIndex = -1;
107+
105108
// Constants for teleport retry logic
106109
private const int MaxTeleportRetries = 3;
107110
private const float TeleportDistanceThreshold = 2.5f;
108111

112+
// Constants for den recovery logic
113+
private const int DenFailuresBeforeForceActivate = 1;
114+
private const int DenFailuresBeforeMapRefresh = 3;
115+
109116
// Constants for raid type identification
110117
private const string MysteryRaidTitle = "Mystery Shiny Raid";
111118
private const string UserRequestedRaidSuffix = "'s Requested Raid";
@@ -647,10 +654,34 @@ private async Task InnerLoop(CancellationToken token)
647654
{
648655
string msg = $"Failed to create a lobby {_lobbyError} times";
649656
Log(msg);
650-
await CloseGame(_hub.Config, token).ConfigureAwait(false);
651-
await StartGameRaid(_hub.Config, token).ConfigureAwait(false);
652-
_lobbyError = 0;
653-
continue;
657+
658+
if (_consecutiveDenFailures >= DenFailuresBeforeMapRefresh)
659+
{
660+
Log($"Den at index {_seedIndexToReplace} has failed {_consecutiveDenFailures} times. Triggering map refresh.");
661+
_shouldRefreshMap = true;
662+
_consecutiveDenFailures = 0;
663+
_lastFailedDenIndex = -1;
664+
_lobbyError = 0;
665+
await CloseGame(_hub.Config, token).ConfigureAwait(false);
666+
await StartGameRaid(_hub.Config, token).ConfigureAwait(false);
667+
continue;
668+
}
669+
else if (_consecutiveDenFailures >= DenFailuresBeforeForceActivate)
670+
{
671+
Log($"Attempting den force-activation recovery (consecutive failures: {_consecutiveDenFailures})");
672+
await TryForceActivateDen(_seedIndexToReplace, token);
673+
await CloseGame(_hub.Config, token).ConfigureAwait(false);
674+
await StartGameRaid(_hub.Config, token).ConfigureAwait(false);
675+
_lobbyError = 0;
676+
continue;
677+
}
678+
else
679+
{
680+
await CloseGame(_hub.Config, token).ConfigureAwait(false);
681+
await StartGameRaid(_hub.Config, token).ConfigureAwait(false);
682+
_lobbyError = 0;
683+
continue;
684+
}
654685
}
655686
}
656687
}
@@ -721,6 +752,9 @@ private async Task InnerLoop(CancellationToken token)
721752
continue;
722753
}
723754

755+
_consecutiveDenFailures = 0;
756+
_lastFailedDenIndex = -1;
757+
724758
Ensure_currentRaidIndexInBounds();
725759
if (_settings.ActiveRaids[_currentRaidIndex].AddedByRACommand)
726760
{
@@ -1603,6 +1637,79 @@ private async Task TeleportToInjectedDenLocation(int index, TeraCrystalType crys
16031637
}
16041638
}
16051639

1640+
/// <summary>
1641+
/// Attempts to force activate a den using memory write and IsActive flag
1642+
/// </summary>
1643+
private async Task TryForceActivateDen(int index, CancellationToken token)
1644+
{
1645+
try
1646+
{
1647+
Log($"Attempting to force activate den at index {index}...");
1648+
1649+
if (index == -1)
1650+
{
1651+
Log("Invalid den index. Cannot force activate.");
1652+
return;
1653+
}
1654+
1655+
bool isActive = await _raidMemoryManager.ReadIsActiveFlag(index, token);
1656+
Log($"Den IsActive flag is currently: {isActive}");
1657+
1658+
if (!isActive)
1659+
{
1660+
Log("Den is not marked as active in memory. Forcing IsActive flag to true...");
1661+
bool flagSet = await _raidMemoryManager.SetIsActiveFlag(index, true, token);
1662+
1663+
if (flagSet)
1664+
{
1665+
Log("Successfully set IsActive flag to true. Saving game...");
1666+
await SaveGame(_hub.Config, token).ConfigureAwait(false);
1667+
Log("Game saved. Den should now be visible.");
1668+
}
1669+
else
1670+
{
1671+
Log("Failed to set IsActive flag. Will try re-injecting seed as backup.");
1672+
}
1673+
}
1674+
else
1675+
{
1676+
Log("IsActive flag is already true, but den still not appearing. Re-injecting seed...");
1677+
}
1678+
1679+
var currentRaid = _settings.ActiveRaids[_currentRaidIndex];
1680+
var seed = uint.Parse(currentRaid.Seed, NumberStyles.AllowHexSpecifier);
1681+
var crystalType = currentRaid.CrystalType;
1682+
1683+
bool seedSuccess = await _raidMemoryManager.InjectSeed(index, seed, crystalType, token);
1684+
if (seedSuccess)
1685+
{
1686+
Log($"Successfully re-injected seed {seed:X8} at index {index}.");
1687+
1688+
if (!isActive)
1689+
{
1690+
await _raidMemoryManager.SetIsActiveFlag(index, true, token);
1691+
Log("Re-applied IsActive flag after seed injection.");
1692+
await SaveGame(_hub.Config, token).ConfigureAwait(false);
1693+
Log("Game saved after seed injection.");
1694+
}
1695+
1696+
await Task.Delay(1_000, token).ConfigureAwait(false);
1697+
1698+
var verifyActive = await _raidMemoryManager.ReadIsActiveFlag(index, token);
1699+
var verifySeed = await _raidMemoryManager.ReadSeedAtIndex(index, token);
1700+
Log($"Verification - IsActive: {verifyActive}, Seed: {verifySeed:X8}");
1701+
}
1702+
else
1703+
{
1704+
Log($"Failed to re-inject seed during force activation.");
1705+
}
1706+
}
1707+
catch (Exception ex)
1708+
{
1709+
Log($"Error during force activate recovery: {ex.Message}");
1710+
}
1711+
}
1712+
16061713
/// <summary>
16071714
/// Creates a random shiny mystery raid
16081715
/// </summary>
@@ -2511,6 +2618,19 @@ private async Task<bool> GetLobbyReady(bool recovery, CancellationToken token)
25112618
{
25122619
Log("Failed to connect to lobby, restarting game incase we were in battle/bad connection.");
25132620
_lobbyError++;
2621+
2622+
if (_seedIndexToReplace == _lastFailedDenIndex)
2623+
{
2624+
_consecutiveDenFailures++;
2625+
Log($"Consecutive failures at den index {_seedIndexToReplace}: {_consecutiveDenFailures}");
2626+
}
2627+
else
2628+
{
2629+
_consecutiveDenFailures = 1;
2630+
_lastFailedDenIndex = _seedIndexToReplace;
2631+
Log($"First failure at den index {_seedIndexToReplace}");
2632+
}
2633+
25142634
await ReOpenGame(_hub.Config, token).ConfigureAwait(false);
25152635
Log("Attempting to restart routine!");
25162636
return false;

0 commit comments

Comments
 (0)