Skip to content

Commit

Permalink
Fix vanquishing API (#448)
Browse files Browse the repository at this point in the history
Pass Health/Energy from GWCA
Fix for enemy primary professions
Fix for LiveCharts2 memory leak
Update GWCA

Closes #447
Closes #446
Closes #445
Closes #444
Closes #442
  • Loading branch information
AlexMacocian authored Oct 29, 2023
1 parent c105257 commit 51d6fb0
Show file tree
Hide file tree
Showing 22 changed files with 90 additions and 72 deletions.
2 changes: 2 additions & 0 deletions Daybreak.GWCA/header/payloads/LivingEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Daybreak {
uint32_t Level = 0;
uint32_t EntityState = 0;
uint32_t EntityAllegiance = 0;
float Health;
float Energy;
};

void to_json(json& j, const LivingEntity& p);
Expand Down
2 changes: 2 additions & 0 deletions Daybreak.GWCA/header/payloads/StatePayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ namespace Daybreak {
float PosX = 0;
float PosY = 0;
uint32_t State = 0;
float Health = 0;
float Energy = 0;
};

void to_json(json& j, const StatePayload& p);
Expand Down
27 changes: 12 additions & 15 deletions Daybreak.GWCA/source/GameModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,6 @@ namespace Daybreak::Modules::GameModule {
return agentList;
}

std::list<GW::NPC> GetNpcs() {
std::list<GW::NPC> npcs;
auto worldContext = GW::GetWorldContext();
for (auto& npc : worldContext->npcs) {
npcs.push_back(npc);
}

return npcs;
}

std::vector<GW::TitleTier> GetTitleTiers() {
std::vector<GW::TitleTier> titles;
auto worldContext = GW::GetWorldContext();
Expand Down Expand Up @@ -186,6 +176,16 @@ namespace Daybreak::Modules::GameModule {
entity.Level = agent.level;
entity.PosX = agent.pos.x;
entity.PosY = agent.pos.y;
entity.Health = agent.hp;
entity.Energy = agent.energy;
if (agent.primary == 0) {
const auto npc = GW::Agents::GetNPCByID(agent.player_number);
if (npc) {
entity.PrimaryProfessionId = npc->primary;
}
}


entities.push_back(entity);
}

Expand Down Expand Up @@ -255,6 +255,8 @@ namespace Daybreak::Modules::GameModule {
player.NpcDefinition = agent->player_number;
player.PrimaryProfessionId = (uint32_t)agent->primary;
player.SecondaryProfessionId = (uint32_t)agent->secondary;
player.Health = agent->hp * (agent->max_hp > 0 ? agent->max_hp : 1);
player.Energy = agent->energy * (agent->max_energy > 0 ? agent->max_energy : 1);
}
else if (professionState) {
player.PrimaryProfessionId = (uint32_t)professionState->primary;
Expand Down Expand Up @@ -372,11 +374,6 @@ namespace Daybreak::Modules::GameModule {
return gamePayload;
}

auto npcs = GetNpcs();
if (npcs.empty()) {
return gamePayload;
}

auto attributes = GetPartyAttributes();
if (attributes.empty()) {
return gamePayload;
Expand Down
2 changes: 2 additions & 0 deletions Daybreak.GWCA/source/GameStateModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace Daybreak::Modules::GameStateModule {
state.PosX = agent.pos.x;
state.PosY = agent.pos.y;
state.State = agent.type_map;
state.Health = agent.hp * (agent.max_hp > 0 ? agent.max_hp : 1);
state.Energy = agent.energy * (agent.max_energy > 0 ? agent.max_energy : 1);
states.push_back(state);
}

Expand Down
51 changes: 10 additions & 41 deletions Daybreak.GWCA/source/ItemNameModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,27 @@

namespace Daybreak::Modules::ItemNameModule {
std::vector<std::tuple<uint32_t, std::promise<NamePayload>*, std::wstring*>> WaitingList;
std::queue<std::tuple<uint32_t, std::list<uint32_t>, std::promise<NamePayload>>*> PromiseQueue;
std::queue<std::tuple<uint32_t, std::vector<uint32_t>, std::promise<NamePayload>>*> PromiseQueue;
std::mutex GameThreadMutex;
GW::HookEntry GameThreadHook;
volatile bool initialized = false;


// TODO #442: Delete this once it is merged into GWCA
GW::Item* GetItemByModelIdAndModifiers(uint32_t modelid, const std::list<GW::ItemModifier> modifiers, int bagStart, int bagEnd) {
GW::Bag** bags = GW::Items::GetBagArray();
GW::Bag* bag = NULL;

for (int bagIndex = bagStart; bagIndex <= bagEnd; ++bagIndex) {
bag = bags[bagIndex];
if (!(bag && bag->items.valid())) continue;
for (GW::Item* item : bag->items) {
if (item && item->model_id == modelid && item->mod_struct_size == modifiers.size()) {
auto match = true;
auto listIt = modifiers.begin();
for (uint32_t i = 0; i < item->mod_struct_size; i++) {
// Compare each element from the array to the corresponding element in the list
if (!(item->mod_struct[i] == *listIt)) {
match = false;
break;
}
++listIt;
}

if (match) {
return item;
}
}
}
}

return NULL;
}

std::wstring* GetAsyncName(uint32_t id, std::list<uint32_t> modifiers) {
std::list<GW::ItemModifier> parsedModifiers;
for (auto mod : modifiers) {
std::wstring* GetAsyncName(uint32_t id, std::vector<uint32_t> modifiers) {
GW::ItemModifier parsedModifiers[64];
for (auto i = 0; i < modifiers.size(); i++) {

Check warning on line 26 in Daybreak.GWCA/source/ItemNameModule.cpp

View workflow job for this annotation

GitHub Actions / build (x86)

'<': signed/unsigned mismatch

Check warning on line 26 in Daybreak.GWCA/source/ItemNameModule.cpp

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'<': signed/unsigned mismatch

Check warning on line 26 in Daybreak.GWCA/source/ItemNameModule.cpp

View workflow job for this annotation

GitHub Actions / build (x86)

'<': signed/unsigned mismatch
GW::ItemModifier parsedModifier;
const auto mod = modifiers.at(i);
parsedModifier.mod = mod;
parsedModifiers.push_back(parsedModifier);
parsedModifiers[i] = parsedModifier;
}

auto item = GetItemByModelIdAndModifiers(id, parsedModifiers, 1, 23);
auto item = GW::Items::GetItemByModelIdAndModifiers(id, parsedModifiers, modifiers.size(), 1, 23);
if (!item) {
return nullptr;
}

auto name = new std::wstring();
GW::Items::AsyncGetItemByName(item, *name);
GW::Items::AsyncGetItemName(item, *name);
return name;
}

Expand Down Expand Up @@ -136,7 +105,7 @@ namespace Daybreak::Modules::ItemNameModule {
void GetName(const httplib::Request& req, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
uint32_t id = 0;
std::list<uint32_t> modifiers;
std::vector<uint32_t> modifiers;

auto id_it = req.params.find("id");
if (id_it == req.params.end()) {
Expand Down Expand Up @@ -177,7 +146,7 @@ namespace Daybreak::Modules::ItemNameModule {
}
}

auto response = new std::tuple<uint32_t, std::list<uint32_t>, std::promise<NamePayload>>();
auto response = new std::tuple<uint32_t, std::vector<uint32_t>, std::promise<NamePayload>>();
std::get<0>(*response) = id;
std::promise<NamePayload>& promise = std::get<2>(*response);
std::get<1>(*response) = modifiers;
Expand Down
2 changes: 2 additions & 0 deletions Daybreak.GWCA/source/MainPlayerModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ namespace Daybreak::Modules::MainPlayerModule {
player.NpcDefinition = agent->player_number;
player.PrimaryProfessionId = (uint32_t)agent->primary;
player.SecondaryProfessionId = (uint32_t)agent->secondary;
player.Health = agent->hp * (agent->max_hp > 0 ? agent->max_hp : 1);
player.Energy = agent->energy * (agent->max_energy > 0 ? agent->max_energy : 1);
}
else if (professionState) {
player.PrimaryProfessionId = (uint32_t)professionState->primary;
Expand Down
2 changes: 2 additions & 0 deletions Daybreak.GWCA/source/payloads/LivingEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ namespace Daybreak {
{"Level", p.Level},
{"EntityState", p.EntityState},
{"EntityAllegiance", p.EntityAllegiance},
{"Health", p.Health},
{"Energy", p.Energy}
};
}
}
2 changes: 1 addition & 1 deletion Daybreak.GWCA/source/payloads/SessionPayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Daybreak {
void to_json(json& j, const SessionPayload& p) {
j = json
{
{"FoedKilled", p.FoesKilled},
{"FoesKilled", p.FoesKilled},
{"FoesToKill", p.FoesToKill},
{"MapId", p.MapId},
{"InstanceTimer", p.InstanceTimer},
Expand Down
4 changes: 3 additions & 1 deletion Daybreak.GWCA/source/payloads/StatePayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ namespace Daybreak {
{"Id", p.Id},
{"PosX", p.PosX},
{"PosY", p.PosY},
{"State", p.State}
{"State", p.State},
{"Health", p.Health},
{"Energy", p.Energy}
};
}
}
19 changes: 15 additions & 4 deletions Daybreak/Controls/Minimap/GuildwarsMinimap.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,33 @@ this.GameData is null ||
return;
}

this.GameData.MainPlayer!.Position = this.GameState.States?.FirstOrDefault(state => state.Id == this.GameData.MainPlayer.Id)?.Position ?? new Position();
foreach(var worldPlayer in this.GameData.WorldPlayers!)
var mainPlayerState = this.GameState.States?.FirstOrDefault(state => state.Id == this.GameData.MainPlayer?.Id);
this.GameData.MainPlayer!.Position = mainPlayerState?.Position ?? new Position();
this.GameData.MainPlayer!.CurrentHealth = mainPlayerState?.Health ?? 0;
this.GameData.MainPlayer!.CurrentEnergy = mainPlayerState?.Energy ?? 0;
foreach (var worldPlayer in this.GameData.WorldPlayers!)
{
worldPlayer.Position = this.GameState.States?.FirstOrDefault(state => state.Id == worldPlayer.Id)?.Position ?? new Position();
var worldPlayerState = this.GameState.States?.FirstOrDefault(state => state.Id == worldPlayer.Id);
worldPlayer.Position = worldPlayerState?.Position ?? new Position();
worldPlayer.CurrentHealth = worldPlayerState?.Health ?? 0;
worldPlayer.CurrentEnergy = worldPlayerState?.Energy ?? 0;
}

foreach (var partyPlayer in this.GameData.Party!)
{
partyPlayer.Position = this.GameState.States?.FirstOrDefault(state => state.Id == partyPlayer.Id)?.Position ?? new Position();
var partyPlayerState = this.GameState.States?.FirstOrDefault(state => state.Id == partyPlayer.Id);
partyPlayer.Position = partyPlayerState?.Position ?? new Position();
partyPlayer.CurrentHealth = partyPlayerState?.Health ?? 0;
partyPlayer.CurrentEnergy = partyPlayerState?.Energy ?? 0;
}

foreach (var entity in this.GameData.LivingEntities!)
{
var state = this.GameState.States?.FirstOrDefault(state => state.Id == entity.Id);
entity.Position = state?.Position ?? new Position();
entity.State = state?.State ?? LivingEntityState.Unknown;
entity.Health = state?.Health ?? 0;
entity.Energy = state?.Energy ?? 0;
}
}

Expand Down
2 changes: 1 addition & 1 deletion Daybreak/Daybreak.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8.133</Version>
<Version>0.9.8.134</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
Expand Down
2 changes: 2 additions & 0 deletions Daybreak/Models/Guildwars/EntityGameState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ public sealed class EntityGameState
public int Id { get; set; }
public Position Position { get; set; }
public LivingEntityState State { get; set; }
public float Health { get; set; }
public float Energy { get; set; }
}
4 changes: 4 additions & 0 deletions Daybreak/Models/Guildwars/LivingEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public sealed class LivingEntity : IEntity
public LivingEntityState State { get; set; }

public LivingEntityAllegiance Allegiance { get; init; }

public float Health { get; set; }

public float Energy { get; set; }
}
4 changes: 2 additions & 2 deletions Daybreak/Models/Guildwars/MainPlayerInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public sealed class MainPlayerInformation : IEntity
public Profession? SecondaryProfession { get; init; }
public List<Profession>? UnlockedProfession { get; init; }
public Build? CurrentBuild { get; init; }
public float CurrentHealth { get; init; }
public float CurrentHealth { get; set; }
public float MaxHealth { get; init; }
public float CurrentEnergy { get; init; }
public float CurrentEnergy { get; set; }
public float MaxEnergy { get; init; }
public float HealthRegen { get; init; }
public float EnergyRegen { get; init; }
Expand Down
4 changes: 2 additions & 2 deletions Daybreak/Models/Guildwars/PlayerInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public sealed class PlayerInformation : IEntity
public Build? CurrentBuild { get; init; }
public Npc? NpcDefinition { get; init; }
public uint ModelType { get; init; }
public float CurrentHealth { get; init; }
public float CurrentHealth { get; set; }
public float MaxHealth { get; init; }
public float CurrentEnergy { get; init; }
public float CurrentEnergy { get; set; }
public float MaxEnergy { get; init; }
public float HealthRegen { get; init; }
public float EnergyRegen { get; init; }
Expand Down
4 changes: 2 additions & 2 deletions Daybreak/Models/Guildwars/WorldPlayerInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public sealed class WorldPlayerInformation : IEntity
public Profession? SecondaryProfession { get; init; }
public List<Profession>? UnlockedProfession { get; init; }
public Build? CurrentBuild { get; init; }
public float CurrentHealth { get; init; }
public float CurrentHealth { get; set; }
public float MaxHealth { get; init; }
public float CurrentEnergy { get; init; }
public float CurrentEnergy { get; set; }
public float MaxEnergy { get; init; }
public float HealthRegen { get; init; }
public float EnergyRegen { get; init; }
Expand Down
8 changes: 6 additions & 2 deletions Daybreak/Services/Scanner/GWCAMemoryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,9 @@ public async Task EnsureInitialized(Process process, CancellationToken cancellat
0x40008 => LivingEntityState.ToBeCleanedUp,
0x400000 => LivingEntityState.Player,
_ => LivingEntityState.Unknown
}
},
Health = state.Health,
Energy = state.Energy
};
}).ToList();

Expand Down Expand Up @@ -835,7 +837,9 @@ private static LivingEntity ParsePayload(LivingEntityPayload livingEntityPayload
{
X = livingEntityPayload.PosX,
Y = livingEntityPayload.PosY,
}
},
Health = livingEntityPayload.Health,
Energy = livingEntityPayload.Energy
};
}

Expand Down
2 changes: 2 additions & 0 deletions Daybreak/Services/Scanner/Models/LivingEntityPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ internal class LivingEntityPayload
public uint PrimaryProfessionId { get; set; }
public uint SecondaryProfessionId { get; set; }
public uint Timer { get; set; }
public float Health { get; set; }
public float Energy { get; set; }
}
2 changes: 2 additions & 0 deletions Daybreak/Services/Scanner/Models/StatePayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ internal sealed class StatePayload
public float PosX { get; set; }
public float PosY { get; set; }
public uint State { get; set; }
public float Health { get; set; }
public float Energy { get; set; }
}
4 changes: 4 additions & 0 deletions Daybreak/Views/FocusView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,10 @@ private void FocusView_Unloaded(object _, RoutedEventArgs e)
{
this.cancellationTokenSource?.Cancel();
this.cancellationTokenSource = null;
this.GameData = default;
this.GameState = default;
this.PathingData = default;
this.InventoryData = default;
}

private void Browser_MaximizeClicked(object _, EventArgs e)
Expand Down
11 changes: 11 additions & 0 deletions Daybreak/Views/MetricsView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
this.metricsService.MetricRecorded -= this.MetricsService_MetricRecorded;
this.metricsService.SetRecorded -= this.MetricsService_SetRecorded;

/*
* #444 - Due to a bug in LiveCharts2, metrics need to be cleared manually, otherwise they would
* cause a memory leak.
*/
foreach (var metric in this.Metrics)
{
metric.Metrics?.Clear();
}

this.Metrics.Clear();
}

private void CartesianChart_Loaded(object sender, RoutedEventArgs e)
Expand Down
2 changes: 1 addition & 1 deletion GWCA

0 comments on commit 51d6fb0

Please sign in to comment.