Skip to content

Commit

Permalink
improve performance by using the new EntityList class
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Auto committed Jun 1, 2024
1 parent 0a284b8 commit 824ed21
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 89 deletions.
62 changes: 31 additions & 31 deletions include/Data/EntityList.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,33 @@
#include "read_helpers.h"
#include <cstdint>
#include <utility>
#include <vector>

namespace S2Plugin
{

// this shoudl only ever be used as temporary
class EntityList
{
public:
EntityList(uintptr_t _address) : address(_address){};
EntityList(uintptr_t address)
{
Script::Memory::Read(address, this, sizeof(EntityList), nullptr);
};
uintptr_t entities() const
{
return Script::Memory::ReadQword(address);
return mEntities;
}

uintptr_t uids() const
{
return Script::Memory::ReadQword(address + sizeof(uintptr_t));
return mUids;
}
uint32_t capacity() const
{
return Script::Memory::ReadDword(address + sizeof(uintptr_t) * 2);
return mCap;
}
uint32_t size() const
{
return Script::Memory::ReadDword(address + sizeof(uintptr_t) * 2 + sizeof(uint32_t));
return mSize;
}
struct Iterator
{
Expand All @@ -37,7 +40,7 @@ namespace S2Plugin
{
advance(index);
}
void advance(int count) noexcept // should probably be int64 ?
void advance(int count) noexcept // should probably be int64
{
addr.first += count * sizeof(uintptr_t);
addr.second += count * sizeof(uint32_t);
Expand Down Expand Up @@ -96,16 +99,12 @@ namespace S2Plugin

Iterator begin() const
{
uintptr_t pointers[2] = {0, 0};
// slightly faster then reading both thru ReadQword
Script::Memory::Read(address, &pointers, sizeof(uintptr_t) * 2, nullptr);
return {pointers[0], pointers[1]};
return {entities(), uids()};
}
Iterator end() const
{
auto full = getFullStruct();
uintptr_t entitiesEnd = full.entities + full.size * sizeof(uintptr_t);
uintptr_t uidsEnd = full.uids + full.size * sizeof(uint32_t);
uintptr_t entitiesEnd = entities() + size() * sizeof(uintptr_t);
uintptr_t uidsEnd = uids() + size() * sizeof(uint32_t);
return {entitiesEnd, uidsEnd};
}
const Iterator cbegin() const
Expand Down Expand Up @@ -136,24 +135,25 @@ namespace S2Plugin
}
return endIterator;
}

private:
uintptr_t address;
struct TrueEntityList
std::vector<uintptr_t> getAllEntities() const
{
uintptr_t entities{0};
uintptr_t uids{0};
uint32_t cap{0};
uint32_t size{0};

private:
TrueEntityList() = delete;
// TrueEntityList(const TrueEntityList&) = delete;
// TrueEntityList& operator=(const TrueEntityList&) = delete;
};
TrueEntityList getFullStruct() const
std::vector<uintptr_t> result;
result.resize(size());
Script::Memory::Read(entities(), result.data(), size() * sizeof(uintptr_t), nullptr);
return result;
}
std::vector<uint32_t> getAllUids() const
{
return Read<TrueEntityList>(address);
std::vector<uint32_t> result;
result.resize(size());
Script::Memory::Read(uids(), result.data(), size() * sizeof(uint32_t), nullptr);
return result;
}

private:
uintptr_t mEntities{0};
uintptr_t mUids{0};
uint32_t mCap{0};
uint32_t mSize{0};
};
}; // namespace S2Plugin
4 changes: 2 additions & 2 deletions include/Views/ViewEntities.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ namespace S2Plugin

QLineEdit* mFilterLineEdit;

uintptr_t mLayer0Offset = 0;
uintptr_t mLayer1Offset = 0;
uintptr_t mLayer0Address = 0;
uintptr_t mLayer1Address = 0;
uintptr_t mLayerMapOffset = 0;
};
} // namespace S2Plugin
13 changes: 5 additions & 8 deletions src/QtHelpers/WidgetSpelunkyLevel.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "QtHelpers/WidgetSpelunkyLevel.h"

#include "Configuration.h"
#include "Data/EntityList.h"
#include "Data/StdMap.h"
#include "Spelunky2.h"
#include "pluginmain.h"
Expand Down Expand Up @@ -154,6 +155,7 @@ void S2Plugin::WidgetSpelunkyLevel::updateLevel()
constexpr auto dataSize = (msLevelMaxHeight + 1) * ((msLevelMaxWidth + 1) * sizeof(uintptr_t));
Script::Memory::Read(gridAddr, &mLevelFloors, dataSize, nullptr);
}

for (auto& entity : mEntitiesToPaint)
entity.pos = entity.ent.abs_position();

Expand All @@ -166,14 +168,9 @@ void S2Plugin::WidgetSpelunkyLevel::updateLevel()
mEntitiesMaskCoordinates[bit_number].clear();
if ((mEntityMasksToPaint & key) != 0)
{
// TODO: change to proper struct when done
auto pointers = Script::Memory::ReadQword(value_ptr);
auto list_count = Script::Memory::ReadDword(value_ptr + 20);

mEntitiesMaskCoordinates[bit_number].reserve(list_count);
std::vector<uintptr_t> entities;
entities.resize(list_count);
Script::Memory::Read(pointers, entities.data(), list_count * sizeof(uintptr_t), nullptr);
EntityList entityList{value_ptr};
mEntitiesMaskCoordinates[bit_number].reserve(entityList.size());
std::vector<uintptr_t> entities = entityList.getAllEntities();

for (auto entityAddr : entities)
{
Expand Down
78 changes: 33 additions & 45 deletions src/Views/ViewEntities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Configuration.h"
#include "Data/Entity.h"
#include "Data/Entitylist.h"
#include "Data/StdMap.h"
#include "QtHelpers/TreeViewMemoryFields.h"
#include "QtPlugin.h"
Expand All @@ -20,8 +21,8 @@ S2Plugin::ViewEntities::ViewEntities(QWidget* parent) : QWidget(parent)
auto mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(5);
auto config = Configuration::get();
mLayer0Offset = config->offsetForField(MemoryFieldType::State, "layer0", Spelunky2::get()->get_StatePtr());
mLayer1Offset = config->offsetForField(MemoryFieldType::State, "layer1", Spelunky2::get()->get_StatePtr());
mLayer0Address = config->offsetForField(MemoryFieldType::State, "layer0", Spelunky2::get()->get_StatePtr());
mLayer1Address = config->offsetForField(MemoryFieldType::State, "layer1", Spelunky2::get()->get_StatePtr());
mLayerMapOffset = config->offsetForField(config->typeFieldsOfDefaultStruct("LayerPointer"), "entities_by_mask");

// initializeRefreshAndFilter
Expand Down Expand Up @@ -90,6 +91,7 @@ void S2Plugin::ViewEntities::refreshEntities()
enteredUID = mFilterLineEdit->text().toUInt(&isUIDlookupSuccess, 0);
}

size_t entitiesShown = 0;
auto AddEntity = [&](size_t entity_ptr)
{
auto entity = Entity{Script::Memory::ReadQword(entity_ptr)};
Expand All @@ -105,47 +107,43 @@ void S2Plugin::ViewEntities::refreshEntities()
field.name = "entity_uid_" + std::to_string(entity.uid());
field.type = MemoryFieldType::EntityPointer;
field.isPointer = true;
mMainTreeView->addMemoryField(field, "", entity_ptr, 0);
mMainTreeView->addMemoryField(field, {}, entity_ptr, 0);
++entitiesShown;
};

size_t totalEntities = 0;
auto layer0 = Script::Memory::ReadQword(mLayer0Offset);
auto layer0Count = Script::Memory::ReadDword(layer0 + 0x1C);
auto layer1 = Script::Memory::ReadQword(mLayer1Offset);
auto layer1Count = Script::Memory::ReadDword(layer1 + 0x1C);
mCheckboxLayer0->setText(QString("Front layer (%1)").arg(layer0Count));
mCheckboxLayer1->setText(QString("Back layer (%1)").arg(layer1Count));
auto layer0 = Script::Memory::ReadQword(mLayer0Address);
EntityList entListLayer0{layer0 + 0x8};
auto layer1 = Script::Memory::ReadQword(mLayer1Address);
EntityList entListLayer1{layer1 + 0x8};
mCheckboxLayer0->setText(QString("Front layer (%1)").arg(entListLayer0.size()));
mCheckboxLayer1->setText(QString("Back layer (%1)").arg(entListLayer1.size()));

auto check_layer0 = mCheckboxLayer0->checkState() == Qt::Checked;
auto check_layer1 = mCheckboxLayer1->checkState() == Qt::Checked;

if (isUIDlookupSuccess)
{
// loop thru all entities to find the uid
// TODO: change to proper struct when done
auto ent_list = Script::Memory::ReadQword(layer0 + 0x8);
auto uid_list = Script::Memory::ReadQword(layer0 + 0x10);
auto uidList0 = entListLayer0.getAllUids();
bool found_uid = false;
for (uint idx = 0; idx < layer0Count; ++idx)
for (uint idx = 0; idx < entListLayer0.size(); ++idx)
{
auto uid = Script::Memory::ReadDword(uid_list + idx * sizeof(uint32_t));
if (enteredUID == uid)
if (enteredUID == uidList0[idx])
{
AddEntity(ent_list + idx * sizeof(size_t));
AddEntity(entListLayer0.entities() + idx * sizeof(size_t));
found_uid = true;
break;
}
}
ent_list = Script::Memory::ReadQword(layer1 + 0x8);
uid_list = Script::Memory::ReadQword(layer1 + 0x10);

if (found_uid == false)
{
for (uint idx = 0; idx < layer1Count; ++idx)
auto uidList1 = entListLayer1.getAllUids();
for (uint idx = 0; idx < entListLayer1.size(); ++idx)
{
auto uid = Script::Memory::ReadDword(uid_list + idx * sizeof(uint32_t));
if (enteredUID == uid)
if (enteredUID == uidList1[idx])
{
AddEntity(ent_list + idx * sizeof(size_t));
AddEntity(entListLayer1.entities() + idx * sizeof(size_t));
break;
}
}
Expand All @@ -164,19 +162,14 @@ void S2Plugin::ViewEntities::refreshEntities()
auto itr = map0.find(checkbox.mask);
if (itr != map0.end())
{
// TODO: change to proper struct when done
auto ent_list = itr.value_ptr();
auto pointers = Script::Memory::ReadQword(ent_list);
auto list_count = Script::Memory::ReadDword(ent_list + 20);
field_count += list_count;
EntityList maskEntList{itr.value_ptr()};

field_count += maskEntList.size();
// loop only if uid was not entered and the mask was choosen
if (!isUIDlookupSuccess && totalEntities < 10000u && checkbox.mCheckbox->checkState() == Qt::Checked)
if (!isUIDlookupSuccess && checkbox.mCheckbox->checkState() == Qt::Checked)
{
for (size_t i = 0; i < list_count; ++i)
{
AddEntity(pointers + (i * sizeof(uintptr_t)));
++totalEntities;
}
for (size_t i = 0; i < maskEntList.size(); ++i)
AddEntity(maskEntList.entities() + i * sizeof(uintptr_t));
}
}
}
Expand All @@ -185,23 +178,18 @@ void S2Plugin::ViewEntities::refreshEntities()
auto itr = map1.find(checkbox.mask);
if (itr != map1.end())
{
auto ent_list = itr.value_ptr();
auto pointers = Script::Memory::ReadQword(ent_list);
auto list_count = Script::Memory::ReadDword(ent_list + 20);
field_count += list_count;
if (!isUIDlookupSuccess && totalEntities < 10000u && checkbox.mCheckbox->checkState() == Qt::Checked)
EntityList maskEntList{itr.value_ptr()};
field_count += maskEntList.size();
if (!isUIDlookupSuccess && checkbox.mCheckbox->checkState() == Qt::Checked)
{
for (size_t i = 0; i < list_count; ++i)
{
AddEntity(pointers + (i * sizeof(size_t)));
++totalEntities;
}
for (size_t i = 0; i < maskEntList.size(); ++i)
AddEntity(maskEntList.entities() + i * sizeof(uintptr_t));
}
}
}
checkbox.mCheckbox->setText(QString(checkbox.name + " (%1)").arg(field_count));
}
setWindowTitle(QString("%1 Entities").arg(totalEntities));
setWindowTitle(QString("%1 Entities").arg(entitiesShown));
mMainTreeView->updateTableHeader();
mMainTreeView->setColumnWidth(gsColField, 145);
mMainTreeView->setColumnWidth(gsColValueHex, 125);
Expand Down
9 changes: 6 additions & 3 deletions src/Views/ViewEntityList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ void S2Plugin::ViewEntityList::refreshEntityListContents()

EntityList entityList{mEntityListAddress};

for (auto entity : entityList)
// TODO using custom view instead of memory view would improve performance
auto entities = entityList.entities();
auto uids = entityList.getAllUids();
for (size_t idx = 0; idx < entityList.size(); ++idx)
{
MemoryField entityField;
entityField.name = "uid_" + std::to_string(entity.second);
entityField.name = "uid_" + std::to_string(uids[idx]);
entityField.isPointer = true;
entityField.type = MemoryFieldType::EntityPointer;
mMainTreeView->addMemoryField(entityField, {}, entity.first, 0);
mMainTreeView->addMemoryField(entityField, {}, entities + idx * sizeof(uintptr_t), 0);
}

mMainTreeView->updateTableHeader();
Expand Down

0 comments on commit 824ed21

Please sign in to comment.