Try
if( toRead != 0 )
The value of unused pointers (not every position in the array is being used) is 0, which you want to filter out.
Here is an example with ReClass:
CharacterArray:
After some scrolling I found these:
First one:
Second one:
Third one:
You can also clearly see the pointers to the vtables now (and by that how many parent classes ChCliCharacter has).
edit,
oh and for completeness, here is the agent + position of the first of the 3 characters...
And also, try to do as Juju says. About 2 months ago I asked him a really retarded question regarding memory. After following his tips I started to really understand how it all works, rather than just understanding bits of it and a lot of copy pasta.
And another tip, this is a snippet of a C++ version of the DatContext lib (the C# one):
Code:
std::vector<DatContext::ChCliCharacter> DatContext::ChCliContext::GetCharacters()
{
DWORD base = CharacterArrayBase();
int arrayCount = CharacterArrayCount();
std::vector<DatContext::ChCliCharacter> toReturn;
for (int i = 0; i < arrayCount; i++)
{
DWORD address = m_reader.Read<DWORD>(base + (i * 4));
if (address != 0)
toReturn.push_back(DatContext::ChCliCharacter(address));
}
return toReturn;
}
compare that to:
Code:
public List<ChCliCharacter> GetChCliCharacters()
{
IntPtr address = CharacterArray;
int arrayCount = CharacterArrayCount;
var toReturn = new List<ChCliCharacter>();
for (int i = 0; i < arrayCount; i++)
{
IntPtr toRead = Memory.Reader.Read<IntPtr>(address + (i*4));
if (toRead != (IntPtr) 0)
toReturn.Add(new ChCliCharacter(toRead));
}
return toReturn;
}
Really try to rely a lot on tools to visualize it for yourself. Seeing it all in ReClass/CheatEngine for example makes it all appear so logical and it will all make sense. Obviously Reclass gives the extra benefit of saving yourself some typing in the future, IE:
Code:
class ChCliContext;
class ChCliCharacter;
class ChCliHealth;
class CharacterArray;
class Agent;
class Position;
class ChCliContext
{
public:
char _0x0000[20];
DWORD CharacterArrayPTR; //0x0014
char _0x0018[4];
__int32 CharacterArrayCount; //0x001C
char _0x0020[24];
ChCliCharacter* LocalChCliCharacter; //0x0038
char _0x003C[88];
};//Size=0x0094
class ChCliCharacter
{
public:
char _0x0000[68];
Agent* PointerToAgent; //0x0044
char _0x0048[264];
ChCliHealth* PointerToChCliHealth; //0x0150
char _0x0154[44];
};//Size=0x0180
class ChCliHealth
{
public:
char _0x0000[8];
float Health; //0x0008
float HealthMax; //0x000C
};//Size=0x0010
class Agent
{
public:
char _0x0000[28];
Position* PointerToPositionBase; //0x001C
char _0x0020[32];
};//Size=0x0040
class Position
{
public:
char _0x0000[32];
float X; //0x0020
float Y; //0x0024
float Z; //0x0028
char _0x002C[20];
};//Size=0x0040
The above is the result of the 3 minutes of work I spent on this post.
Another edit:
Decided that I might as well use ReClass to dump a few members of some actual classes, just to show you how much easier it becomes when you have classes with the required padding (most of my GW2 stuff is fairly outdated so will have to do them over).
Code:
class ChCliContext;
class ChCliCharacter;
class ChCliHealth;
class Agent;
class ChCliCoreStats;
class ChCliEndurance;
class ChCliInventory;
class ChCliKennel;
class ChCliProfession;
class ChCliSkillbar;
class Position;
class AsContext;
class GdCliContext;
class ChCliContext
{
public:
CHAR _0x0000[20];
DWORD m_characterArray; //0x0014
CHAR _0x0018[4];
INT m_characterArraySize; //0x001C
CHAR _0x0020[8];
DWORD m_playerArray; //0x0028
CHAR _0x002C[4];
INT m_playerArraySize; //0x0030
CHAR _0x0034[4];
ChCliCharacter* m_localCharacter; //0x0038
CHAR _0x003C[4];
static ChCliContext* Singleton()
{
return *(ChCliContext**)0x16B366C;
}
};//Size=0x0040
class ChCliCharacter
{
public:
CHAR _0x0000[68];
Agent* m_agent; //0x0044
INT m_type; //0x0048
CHAR _0x004C[20];
INT m_attitudeToLocalCharacter; //0x0060
CHAR _0x0064[4];
INT m_isInWater; //0x0068
CHAR _0x006C[44];
BYTE m_flags; //0x0098
CHAR _0x0099[7];
INT m_healthStatus; //0x00A0
CHAR _0x00A4[132];
ChCliCoreStats* m_coreStats; //0x0128
CHAR _0x012C[32];
ChCliEndurance* m_endurance; //0x014C
ChCliHealth* m_health; //0x0150
ChCliInventory* m_inventory; //0x0154
ChCliKennel* m_kennel; //0x0158
CHAR _0x015C[40];
ChCliProfession* m_profession; //0x0184
ChCliSkillbar* m_skillbar; //0x0188
};//Size=0x018C
class ChCliHealth
{
public:
CHAR _0x0000[8];
float m_health; //0x0008
float m_healthMax; //0x000C
};//Size=0x0010
class Agent
{
public:
CHAR _0x0000[16];
INT m_id; //0x0010
CHAR _0x0014[8];
Position* m_position; //0x001C
};//Size=0x0020
class ChCliCoreStats
{
public:
CHAR _0x0000[41];
INT8 m_sex; //0x0029
CHAR _0x002A[90];
INT m_level; //0x0084
CHAR _0x0088[4];
INT m_power; //0x008C
INT m_precision; //0x0090
INT m_thoughness; //0x0094
INT m_vitality; //0x0098
CHAR _0x009C[16];
INT m_effectiveLevel; //0x00AC
INT m_totalExperience; //0x00B0
CHAR _0x00B4[44];
INT m_class; //0x00E0
};//Size=0x00E4
class ChCliEndurance
{
public:
CHAR _0x0000[4];
INT m_endurance; //0x0004
INT m_enduranceMax; //0x0008
};//Size=0x000C
class ChCliInventory
{
public:
CHAR _0x0000[80];
INT m_money; //0x0050
CHAR _0x0054[24];
DWORD m_itemArray; //0x006C
CHAR _0x0070[4];
INT m_itemArraySize; //0x0074
};//Size=0x0078
class ChCliKennel
{
public:
CHAR _0x0000[20];
DWORD m_pet; //0x0014
};//Size=0x0018
class ChCliProfession
{
public:
CHAR _0x0000[44];
float m_professionPower; //0x002C
float m_professionPowerMax; //0x0030
};//Size=0x0034
class ChCliSkillbar
{
public:
CHAR _0x0000[64];
INT m_pressedSkillSlot; //0x0040
};//Size=0x0044
class Position
{
public:
CHAR _0x0000[32];
float m_x; //0x0020
float m_y; //0x0024
float m_z; //0x0028
CHAR _0x002C[196];
float m_headingX; //0x00F0
float m_headingY; //0x00F4
};//Size=0x00F8
class AsContext
{
public:
CHAR _0x0000[48];
INT m_groundTargetCircleVisible; //0x0030
CHAR _0x0034[60];
Agent* m_targetAgent1; //0x0070
CHAR _0x0074[8];
Agent* m_mouseOverAgent; //0x007C
CHAR _0x0080[4];
Agent* m_targetAgent2; //0x0084
CHAR _0x0088[8];
Agent* m_targetAgent3; //0x0090
CHAR _0x0094[4];
float m_mousePositionFromCenterX; //0x0098
float m_mousePositionFromCenterY; //0x009C
CHAR _0x00A0[8];
float UnknownX; //0x00A8
float UnknownY; //0x00AC
float UnknownZ; //0x00B0
CHAR _0x00B4[4];
float m_mouseOverX; //0x00B8
float m_mouseOverY; //0x00BC
float m_mouseOverZ; //0x00C0
CHAR _0x00C4[60];
static AsContext* Singleton()
{
return (AsContext*)0x16B3570;
}
};//Size=0x0100
Lulzy dump (C code calling LUA functions, ignore the quick dump code, sample yadi yadi yada):
Code:
ChCliContext* chCliContext = ChCliContext::Singleton();
float fHealth = chCliContext->m_localCharacter->m_health->m_health;
float fHealthMax = chCliContext->m_localCharacter->m_health->m_healthMax;
float fPosX = chCliContext->m_localCharacter->m_agent->m_position->m_x;
float fPosY = chCliContext->m_localCharacter->m_agent->m_position->m_y;
float fPosZ = chCliContext->m_localCharacter->m_agent->m_position->m_z;
float fProfPower = chCliContext->m_localCharacter->m_profession->m_professionPower;
float fProfPowerMax = chCliContext->m_localCharacter->m_profession->m_professionPowerMax;
char cVal[32];
sprintf(cVal,"Health: %f", fHealth);
Log(cVal);
sprintf(cVal,"HealthMax: %f", fHealthMax);
Log(cVal);
sprintf(cVal,"Prof Power: %f", fProfPower);
Log(cVal);
sprintf(cVal,"Prof Power Max: %f", fProfPowerMax);
Log(cVal);
sprintf(cVal,"Position [X]: %f", fPosX);
Log(cVal);
sprintf(cVal,"Position [Y]: %f", fPosY);
Log(cVal);
sprintf(cVal,"Position [Z]: %f", fPosZ);
Log(cVal);
As you can see you can save yourself from a lot of drama regarding where objects are and all that. I didn't actually check if these pointers were valid, but the game wouldn't really work if my own character had invalid pointers... (you should normally for other players/mobs/gadgets etc). This is where C++ really shines, use it.