Tout comme arma 2, arma 3 à une façon bien particulière de stocker les entités,
Il faudra parcourir 3 "master" table qui contiennent les offsets de chacune des "Slave" table qui contiennent a leur tour les Offsets des entités.
Il y a en tout 3 Master table et 4 Slave pour chacune d'entre elle, la 3émes master table comporte 2 slaves de plus
La place d'une entité dans l'une ou l'autre slave/master dépends essentiellement de la distance de cette entité par rapport a vous, elles sont donc amené a être
modifié en permanence selon vos déplacement ig.
Cas particulier pour la master 3, qui contient des slaves qui sont réservé pour certain type d’entité, comme les "Constructions" et les entités de type nature (Bâtiment, tente, abeille, serpent etc...) mais aussi les loots générer au sol par les joueurs (qui aurait par exemple jeté leur arme)
Offset de décalage pour atteindre les Master Table & slave table:
int[] MasterTableArray = { 0x46c, 0x710, 0x9b4 };
int[] SlaveTableArray = { 0x8, 0xb0, 0x158, 0x200, 0x2a8, 0x350 } //Les deux dernières slave sont utilisée uniquement pour la master 3
Comme pour tous les jeux, il existe une offset de base, qui nous permet d'atteindre toute les structures qui nous intéresse, en l’occurrence l’itération de nos entités, ici nous l’appelleront "GameBase".
int GameBase = 0x160ED88 //Cette valeur peut varier à chaque update du jeux
Maintenant mettons en utilisations Notre GameBase Pour atteindre nos Master Table :
int MasterArray = GameBase] +0x7c4] +0x0]
Appliquons maintenant les decallages contenut dans MasterTableArray
for(int i=0; i < MasterTableArray.Lenght; i++){
CurrentMasterptr = MasterArray + MasterTableArray[i]]
}
CurrentMasterptr contient donc l'adresse de debut de chaque master.
Nous pouvons donc maintenant nous attaquer au Slave Table de chaque master.
Rapellez vous, les slaves table sont lié a chaque master, il faudra donc iterer chaque slave dans la boucle
d'iteration des masters.
Cependant je vous rapelle que nous avons declarer 6 slave, les deux derniere sont utilisé uniquement pour la master 3, il
faudra donc sauter les deux derniere iteration des slave si l'on itere la master 1 et 2
for(int y=0; y < SlaveTableArray.Lenght; y++)
{
if(i < 2 && y > 3 ) continue; //Si on itere la master 1 & 2 et que l'on itere la slave 5 et 6 on saute
CurrentSlavePtr = CurrentMasterptr + SlaveTableArray[y]]
CurrentSlaveCount = CurrentMasterptr + SlaveTableArray[y] + 0x4]
}
Nous avons donc récupéré le CurrentslavePtr, point d'entré de notre listing d’entité ainsi que CurrentSlaveCount qui définit le nombre d'enregistrement à l’intérieur de notre CurrentSlavePtr.
une fois dans notre slave nous faisons comme suit pour recuperer l'ofs de nos entitiés
for(int z=0; z < CurrentSlaveCount; z++){
EntOfs = CurrentSlavePtr + z*0x4]
}
Et Voila on récupère un à un les Ofs de toute nos entités
Local entités
L’entité qui représente notre joueurs est présente dans les Master/slave mais il existe un chemin plus court pour obtenir son adresse
SelfEntOfs = 0x160ED88] +0x1704] +0x4]
La Struct de l'entité
Spoiler
VecPos Struct :
Spoiler
int ModelPtr : Pointeur qui nous donne accé au model1name et model2name
string model1name = ModelPtr + 0x38] + 0x8
String model2name = ModelPtr + 0x30] + 0x8
Pour le moment vous devrez utilisez le model1name pour déduire le type de l'entité (Humain/Ai, véhicule, lootable etc...)
"int PartPtr" et "int PartCount" : nous donne accès a un array de float qui decrive l'état des différentes partit d'une entité cassable (ex: vehicule), si = 1 -> complètement détruit, si 0 -> complètement neuf (un petit coup de Write et votre véhicule est tout neuf !)
int SideId : Représente l'id du camp de l'entité, Cette valeur change en fonction des serveurs, vous pouvez quand même l'utilisé pour retrouver les membres de votre équipe ou les membres de l'équipe adverse.
bool IsDead : Définit si l'entité est en vie ou non.
ItemOlderPtr : Nous donne accès au Older de l’entité (utile uniquement si l'entité peut contenir des items) (nous y reviendront)
WeaponPtr : Nous donne accès a l'array de struct weapon (munition infinie, balle plus rapide que l’éclair, c'est ici) (nous y reviendront)
BackPackPtr : Nous donne accès a l'ofs de l'entité qui représente notre sac a dos (même si il est sur le dos de quelqu’un le sac existe comme entité, cela nous permet donc de faire la liaison entre une entité et son sac)
WeaponId : Nous permet de retrouver dans WeaponPtr notre arme actuelle (nous y reviendront)
NameId : Nous permet de retrouver le Pseudo ou RealName d'une entité Joueur (nous y reviendront)
DriverPtr : Nous donne l'ofs de l'entité qui conduit
PassengerPtr et PassengerCount : Permet d'obtenir la liste des entités qui sont passager dans un véhicule
Réparé un Véhicule
vous ne pourrez réparer un véhicule uniquement que si vous êtes a la place conducteurs, et que ce dernier n'a pas explosé ;D
La technique est relativement simple :
- Récupérer l'ofs de votre entité
- Itérer les entités jusqu’à ce que DriverPtr soit égale a l'ofs de votre self entité.
Une fois cela fait, vous avez trouvé l'adresse de votre véhicule, a partir de la il vous suffit de lire PartPtr et PartCount.
PartPtr est un array de float de PartCount éléments
entOfs //Ofs de votre véhicule
int PartPtr = entOfs + 0x168]
int PartCount = entOfs + 0x16c]
for(int i=0; i < PartCount){
Write(Part + i*0x4, 1f)
}
Et voila votre véhicule est tout neuf !!!!
Itération des olders
Un older est un conteneur, une entité qui peut contenir un ou plusieurs item (par exemple un véhicule a un Older car il peut contenir des items)
Toute entités pouvant contenir des items peut être définit comme possédant un Older
Un Older est une structure qui contient 8 informations dont nous avons besoin, 4 pointer qui nous permette d'obtenir le nom des objets classé par type, ainsi que le count respectif de chaqu'un d'entre eux
Pour l'instant, nous avons recensé 4 type d'item :
- Arme
- Consommable (Ammo, food, water etc...)
- Tool (GPS, map, toolbox etc)
- BackPack (sac a dos)
Chaque type d'item est regroupé et est stocké dans un tableau d'item différent, dans un older nous avons donc 4 tableau et leur count associé
OlderWeapon:
ItemOlderPtr //Valeur de L'ItemOlderPtr de la current entité
WeaponOlderPtr = ItemOlderPtr + 0x28]
WeaponOlderCount = ItemOlderPtr + 0x2c]
for(int i=0; i < WeaponOlderCount; i++){
CurrentWeaponOlder = WeaponOlderPtr + 0x4 * i] + 0x28] + 0x1c]
string CurrentWeaponName = CurrentWeaponOlder + 0x8]
}
OlderConsomable:
ItemOlderPtr //Valeur de L'ItemOlderPtr de la current entité
ConsomableOlderPtr = ItemOlderPtr + 0x44]
ConsomableOlderCount = ItemOlderPtr + 0x48]
for(int i=0; i < ConsomableOlderCount; i++){
CurrentConsomableOlder = ConsomableOlderPtr + 0x4 * i] + 0x28]
string CurrentConsomableName = CurrentConsomableOlder + 0x8]
}
OlderTool:
ItemOlderPtr //Valeur de L'ItemOlderPtr de la current entité
ToolOlderPtr = ItemOlderPtr + 0x34]
ToolOlderCount = ItemOlderPtr + 0x38]
for(int i=0; i < ToolOlderCount; i++){
CurrentToolOlder = ToolOlderPtr + 0x4 * i] + 0x20] +0x18]
string CurrentToolName = CurrentToolOlder + 0x8]
}
OlderBackPack:
ItemOlderPtr //Valeur de L'ItemOlderPtr de la current entité
OlderBackPackOlderPtr = ItemOlderPtr + 0x70]
OlderBackPackOlderCount = ItemOlderPtr + 0x74]
for(int i=0; i < BackPackOlderCount; i++){
CurrentBackPackOlder = BackPackOlderPtr + 0x4 * i] + 0xD0] +0x30]
string CurrentBackPackName = CurrentBackPackOlder + 0x8]
}
Munition Infinie & Vélocité
Cette opération s'effectue sur notre entité, il faut avoir une arme, et avoir ou déjà posséder des balles depuis que l'arme est en main.
Nous allons intervenir sur la quantité de balle se trouvant dans le chargeur en cours d'utilisation, si vous ramassez une arme sans balle, le chargeur de l'arme ne sera pas initialiser en mémoire, il nous sera donc impossible d’écrire la quantité de balle que nous souhaitons.
La quantité de balle est gérer par un système de XOR entre deux valeur, si l'une d'elle ne correspond pas a la deuxième valeur vous vous retrouverez sans munition ;D
Pour récupérer l'offset qui nous intéresse nous allons avoir besoin de WeaponPtr et WeaponID de l'entité qui représente notre joueur
Nous allons dans un premier temps récupérer le maximum de munition que peut accueillir notre chargeur puis l’écrire en mémoire dans notre chargeur
int WeaponPtr = WeaponPtr + (WeaponID * 0x30) + 0c] int MaxMagCap = WeaponPtr + 0x34] + 0x40] int garbadge1Ofs = WeaponPtr + 0x38 int garbadge2Ofs = WeaponPtr + 0x58 int VelocityOfs = WeaponPtr + 0x34] + 0x48 int Garbadge1 = (int)(MaxMagCap ^ 0xBABAC8B6) << 1 int Garbadge2 = (int)(MaxMagCap ^ 0xBABAC8B6) - Garbadge1 Write(garbadge1Ofs, Garbadge1) Write(garbadge2Ofs, Garbadge2) Write(VelocityOfs, 99999f) //Vélocité à 99999
RealName / Pseudo
Lorsque vous itérer une entité de type joueur, vous n'avez pas dans la structure les informations nécessaire pour savoir comment ce dernier s’appelle.
Il existe un système de scoreboard qui contient les pseudo ainsi qu'un identifiant unique lié, le nameId, nous allons donc utiliser le nameID contenus dans l'entité pour la lié a un pseudo
La struct de type ScoreBoard contient :
int NameId; //0x4 int ptrSteamId; //0xc int ptrName; //0xa8
Itérer le ScoreBoardArray:
ScoreBoardArrayPtr = 0x15FD8B4] + 0x18]
ScoreBoardArrayCount = 0x15FD8B4] + 0x1c]
for(int i=0; i < ScoreBoardArrayCount; i++){
String NickName = ScoreBoardArrayPtr + (i*0x120)] + 0xa8] + 0x8]
int NameID = ScoreBoardArrayPtr + (i*0x120)] + 0x4]
}
A venir
- World to Screen




