Skip to content

Commit

Permalink
Merge pull request #306 from ethanmoffat/timings_and_perf
Browse files Browse the repository at this point in the history
Fix animation timing issues. Reduce heap allocations when loading GFX
  • Loading branch information
ethanmoffat committed May 10, 2023
2 parents c795ced + 5ef4c76 commit bc2dba5
Show file tree
Hide file tree
Showing 36 changed files with 489 additions and 461 deletions.
41 changes: 24 additions & 17 deletions EOBot/TrainerBot.cs
Expand Up @@ -6,7 +6,6 @@
using EOLib.Domain.Item;
using EOLib.Domain.Login;
using EOLib.Domain.Map;
using EOLib.Domain.NPC;
using EOLib.IO;
using EOLib.IO.Pub;
using EOLib.IO.Repositories;
Expand All @@ -23,8 +22,8 @@ namespace EOBot
internal class TrainerBot : BotBase
{
private const int CONSECUTIVE_ATTACK_COUNT = 150;
private const int ATTACK_BACKOFF_MS = 500;
private const int WALK_BACKOFF_MS = 400;
private const int ATTACK_BACKOFF_MS = 600;
private const int WALK_BACKOFF_MS = 480;
private const int FACE_BACKOFF_MS = 120;

private static readonly int[] JunkItemIds = new[]
Expand All @@ -44,6 +43,7 @@ internal class TrainerBot : BotBase
private IItemActions _itemActions;

private ICharacterRepository _characterRepository;
private IFixedTimeStepRepository _fixedTimeStepRepository;

private IPubFile<EIFRecord> _itemData;
private IPubFile<ENFRecord> _npcData;
Expand Down Expand Up @@ -97,6 +97,8 @@ protected override async Task DoWorkAsync(CancellationToken ct)
_chatProvider = c.Resolve<IChatProvider>();
_cachedChat = new HashSet<ChatData>();

_fixedTimeStepRepository = c.Resolve<IFixedTimeStepRepository>();

var healItems = new List<InventoryItem>();
var healSpells = new List<InventorySpell>();

Expand Down Expand Up @@ -249,23 +251,23 @@ protected override async Task DoWorkAsync(CancellationToken ct)
}
}

await Task.Delay(TimeSpan.FromSeconds(1.0 / 8.0));
await Delay(120);
}
}

private async Task Attack(IMapCellState cellState)
{
cellState.NPC.MatchSome(npc => ConsoleHelper.WriteMessage(ConsoleHelper.Type.Attack, $"{npc.Index,7} - {_npcData.Single(x => x.ID == npc.ID).Name}"));
await TrySend(_characterActions.Attack);
await Task.Delay(TimeSpan.FromMilliseconds(ATTACK_BACKOFF_MS));
await Delay(ATTACK_BACKOFF_MS);
}

private async Task Walk()
{
var renderProps = _characterRepository.MainCharacter.RenderProperties;
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Walk, $"{renderProps.GetDestinationX(),3},{renderProps.GetDestinationY(),3}");
await TrySend(_characterActions.Walk);
await Task.Delay(TimeSpan.FromMilliseconds(WALK_BACKOFF_MS));
await Delay(WALK_BACKOFF_MS);
}

private async Task Face(EODirection direction)
Expand All @@ -278,7 +280,7 @@ private async Task Face(EODirection direction)
_characterRepository.MainCharacter = _characterRepository.MainCharacter
.WithRenderProperties(_characterRepository.MainCharacter.RenderProperties.WithDirection(direction));

await Task.Delay(TimeSpan.FromMilliseconds(FACE_BACKOFF_MS));
await Delay(FACE_BACKOFF_MS);
}

private async Task FaceCoordinateIfNeeded(MapCoordinate originalCoord, MapCoordinate targetCoord)
Expand Down Expand Up @@ -306,7 +308,7 @@ private async Task PickUpItems(IMapCellState cellState)

if (JunkItemIds.Contains(item.ItemID))
{
await Task.Delay(TimeSpan.FromSeconds(1));
await Delay(1000);
await JunkItem(item);
}
}
Expand All @@ -323,14 +325,14 @@ private async Task PickUpItem(MapItem item)
else
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Warning, $"Ignoring item {itemName} x{item.Amount} due to pickup error {pickupResult}", ConsoleColor.DarkYellow);
});
await Task.Delay(TimeSpan.FromMilliseconds(ATTACK_BACKOFF_MS));
await Delay(ATTACK_BACKOFF_MS);
}

private async Task JunkItem(MapItem item)
{
ConsoleHelper.WriteMessage(ConsoleHelper.Type.JunkItem, $"{item.Amount,7} - {_itemData.Single(x => x.ID == item.ItemID).Name}");
await TrySend(() => _itemActions.JunkItem(item.ItemID, item.Amount));
await Task.Delay(TimeSpan.FromMilliseconds(ATTACK_BACKOFF_MS));
await Delay(ATTACK_BACKOFF_MS);
}

private async Task CastHealSpell(IEnumerable<InventorySpell> healSpells)
Expand All @@ -344,10 +346,10 @@ private async Task CastHealSpell(IEnumerable<InventorySpell> healSpells)
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Cast, $"{spellToUse.HP,4} HP - {spellToUse.Name} - TP {stats[CharacterStat.TP]}/{stats[CharacterStat.MaxTP]}");

await TrySend(() => _characterActions.PrepareCastSpell(spellToUse.ID));
await Task.Delay((spellToUse.CastTime - 1) * 480 + 350);
await Delay((uint)spellToUse.CastTime * 480);

await TrySend(() => _characterActions.CastSpell(spellToUse.ID, _characterRepository.MainCharacter));
await Task.Delay(600); // 600ms cooldown between spell casts
await Delay(ATTACK_BACKOFF_MS); // 600ms cooldown between spell casts
}

private async Task UseHealItem(IEnumerable<InventoryItem> healItems)
Expand All @@ -361,8 +363,7 @@ private async Task UseHealItem(IEnumerable<InventoryItem> healItems)
ConsoleHelper.WriteMessage(ConsoleHelper.Type.UseItem, $"{itemToUse.Name} - {itemToUse.HP} HP - inventory: {amount - 1} - (other heal item types: {healItems.Count() - 1})");

await TrySend(() => _itemActions.UseItem(itemToUse.ID));

await Task.Delay(ATTACK_BACKOFF_MS);
await Delay(ATTACK_BACKOFF_MS);
}

private async Task ToggleSit()
Expand All @@ -373,9 +374,9 @@ private async Task ToggleSit()
await TrySend(_characterActions.ToggleSit);
}

private async Task TrySend(Action action, int attempts = 3)
private async Task TrySend(Action action, uint attempts = 3)
{
for (int i = 1; i <= attempts; i++)
for (uint i = 1; i <= attempts; i++)
{
try
{
Expand All @@ -388,9 +389,15 @@ private async Task TrySend(Action action, int attempts = 3)
if (i == attempts)
throw;

await Task.Delay(TimeSpan.FromSeconds(i * i));
await Delay(i * i * 1000);
}
}
}

private Task Delay(uint milliseconds)
{
_fixedTimeStepRepository.Tick(milliseconds / 10);
return Task.Delay((int)milliseconds);
}
}
}
2 changes: 0 additions & 2 deletions EOLib.Graphics.Test/EOLib.Graphics.Test.csproj
Expand Up @@ -16,8 +16,6 @@
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.0.0" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
</ItemGroup>
</Project>
50 changes: 0 additions & 50 deletions EOLib.Graphics.Test/LibraryGraphicPairTest.cs

This file was deleted.

30 changes: 10 additions & 20 deletions EOLib.Graphics.Test/NativeGraphicsLoaderTest.cs
@@ -1,9 +1,8 @@
using Moq;
using NUnit.Framework;
using PELoaderLib;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;

Expand All @@ -29,9 +28,7 @@ public void WhenLoadGFX_CallsPEFile_GetEmbeddedBitmapResourceByID()
{
var peFileMock = SetupPEFileForGFXType(GFXTypes.PreLoginUI, CreateDataArrayForBitmap());

using (var bmp = _nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, 1))
bmp.Dispose(); //hide warning for empty using statement

var data = _nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, 1);
peFileMock.Verify(x => x.GetEmbeddedBitmapResourceByID(It.IsAny<int>(), ExpectedCulture), Times.Once());
}

Expand All @@ -43,34 +40,27 @@ public void WhenLoadGFX_CallsPEFile_WithResourceValueIncreasedBy100()

var peFileMock = SetupPEFileForGFXType(GFXTypes.PreLoginUI, CreateDataArrayForBitmap());

using (var bmp = _nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, requestedResourceID))
bmp.Dispose(); //hide warning for empty using statement

var data = _nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, requestedResourceID);
peFileMock.Verify(x => x.GetEmbeddedBitmapResourceByID(expectedResourceID, ExpectedCulture));
}

[Test]
public void WhenLoadGFX_EmptyDataArray_ThrowsException()
public void WhenLoadGFX_EmptyDataArray_ThrowsInDebug_EmptyInRelease()
{
const int requestedResourceID = 1;

SetupPEFileForGFXType(GFXTypes.PreLoginUI, new byte[] { });

#if DEBUG
Assert.Throws<GFXLoadException>(() => _nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, requestedResourceID));
#else
Assert.That(_nativeGraphicsLoader.LoadGFX(GFXTypes.PreLoginUI, requestedResourceID), Has.Length.EqualTo(0));
#endif
}

private byte[] CreateDataArrayForBitmap()
{
byte[] array;
using (var retBmp = new Image<Rgb24>(10, 10))
{
using (var ms = new MemoryStream())
{
retBmp.Save(ms, new BmpEncoder());
array = ms.ToArray();
}
}
return array;
return new byte[1];
}

private Mock<IPEFile> SetupPEFileForGFXType(GFXTypes type, byte[] array)
Expand Down

0 comments on commit bc2dba5

Please sign in to comment.