CLEO Help Getting/Calculating CPed bytemap size

CLEO related
Status
Not open for further replies.

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
When I want to get all peds in a loop, I always loop through the Ped Pool using this FAMOUS method since they claim it to be faster than "looping opcode 0AE1":
Code:
0A8D: 0@ = read_memory 0xB74490 size 4 virtual_protect 0
0@ += 4
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
for 1@ = 0x0 to 8B00 step 0x100
    0A8D: 2@ = read_memory 0@ size 1 virtual_protect 0
    0@++
    if and
        2@ >= 0x00
        2@ < 0x80
    then
        005A: 2@ += 1@ // got the ped
        // do something...
    end
end
This script uses a CPed bytemap with size of 140(0x0 upto 139*256)

But I sometime encounter an issue when after taking a 30 minutes playtime with a total of 300 players on my current server, not all peds can be detectd, until the time that the script fails to get any ped.

So I suspected that maybe the constant bytemap of 140 is the issue.

That is why I was wondering How we can calculate the exact bytemap size of the cped. I read this and this which says that 0xB74498 actually holds the maximum allowable ped count. But I believe this fact was wrong since 0xB74498 holds the Building Pool's Pointer.

Any thoughts how to get the exact bytemap size? I appreciate if anybody can demonstrate or share some memory offsets.
 

88resu

Active member
Joined
Oct 1, 2019
Messages
110
Reaction score
46
Location
Uganda
i think looping samp pools would be a better option. (when u dont wanted to do cross-platform between samp versions )
i dont know about this method to get all peds but, it was designed for singleplayer things. maybe its limited in singleplayer

but i need to tell :D use c++ xd
 
Last edited:

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
i think looping samp pools would be a better option. (when u dont wanted to do cross-platform between samp versions )
i dont know about this method to get all peds but, it was designed for singleplayer things. maybe its limited in singleplayer
That seems reasonable, so this is what I ended up doing:
Code:
// REQUIRES SAMPFUNCS
0C8A: samp 0@ = get_max_player_id streamed_only true
for 1@ = 0 to 0@
    0B20: samp 2@ = actor_handle_by_samp_player_id 1@ // got the ped
    // do something...
end

Although this cannot track down Peds with No Player ID. So there are advantages and disadvantages between the two methods. But since everything is player related. I don't mind about not player actors. If there are best demo than this, I would gladly like to see it.
 

88resu

Active member
Joined
Oct 1, 2019
Messages
110
Reaction score
46
Location
Uganda
npc´s have an id, or what u mean by "cannot track down Peds with No Player ID " -> singleplayer dont have player ids, just index in loop
 

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
npc´s have an id, or what u mean by "cannot track down Peds with No Player ID " -> singleplayer dont have player ids, just index in loop

I haven't clarified my sentence. Player's and NPC's have Player ID, but there are some servers that have bots Pedestrians which does not have Player ID. That might sound weird but I always encounter this Bots that does not have ID at this Server, those Bots are scripted by to do something by Server but they don't seem to have any Player ID.

Looping though Ped Bytemap allows me to detect Any ped I want even Samp player's and those Bots with no player ID so that is a good thing for it, but like I said the method limits itself to 140 peds at once:
Code:
for 1@ = 0x0 to 8B00 step 0x100
And since nobody know how to calculate or know the proper bytemap size(instead on relying to a constant of 140). I used my own method on getting Samp player's in exchange of not detecting peds/bots that have no player ID.
 

0x_

Wtf I'm not new....
Administrator
Joined
Feb 18, 2013
Messages
1,118
Reaction score
166
I haven't clarified my sentence. Player's and NPC's have Player ID, but there are some servers that have bots Pedestrians which does not have Player ID. That might sound weird but I always encounter this Bots that does not have ID at this Server, those Bots are scripted by to do something by Server but they don't seem to have any Player ID.

Looping though Ped Bytemap allows me to detect Any ped I want even Samp player's and those Bots with no player ID so that is a good thing for it, but like I said the method limits itself to 140 peds at once:
Code:
for 1@ = 0x0 to 8B00 step 0x100
And since nobody know how to calculate or know the proper bytemap size(instead on relying to a constant of 140). I used my own method on getting Samp player's in exchange of not detecting peds/bots that have no player ID.
Those peds you are searching are called actors and yes they don't have any "playerid" instead they have an actorid and an own actor pool.
Refer to https://github.com/BlastHackNet/mod_s0beit_sa-1/blob/master/src/samp.h#L959
 

monday

Expert
Joined
Jun 23, 2014
Messages
1,127
Solutions
1
Reaction score
158
https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPools.cpp
https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPool.h

These files kinda show how it's done. It looks like it's necessary to dereference the 0xB74490 pointer before adding 8, which will then point to the size. So:
p = read_4_bytes(0xB74490)
size = read_4_bytes(p + 8)

That's what I'd check first (but idk)

Btw it's worth to notice what is the next value (at p+12), which is declared in CPool.h as "int m_nFirstFree;" and in the "GTA memory addresses" site as "Current number of peds in the pool". I think it could be used to optimize looping over every actor (by breaking the loop when the nth active player in the bytemap is reached)
 

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
It looks like it's necessary to dereference the 0xB74490 pointer before adding 8, which will then point to the size. So:
p = read_4_bytes(0xB74490)
size = read_4_bytes(p + 8)

That's what I'd check first (but idk)

Btw it's worth to notice what is the next value (at p+12), which is declared in CPool.h as "int m_nFirstFree;" and in the "GTA memory addresses" site as "Current number of peds in the pool". I think it could be used to optimize looping over every actor (by breaking the loop when the nth active player in the bytemap is reached)

OK so I did exactly what you suggested, and it seems to be working:
Code:
0A8D: 1@ = read_memory 0xB74490 size 4 virtual_protect 0 // ped pool usage information
0A8D: 0@ = read_memory 1@ size 4 virtual_protect 0 // pointer to FIRST ped in the pool(normally this is CJ structs)
1@ += 12 // current number of peds in pedpool
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 // max ped index
    0AD1: show_formatted_text_highpriority "count:%d" time 2000 1@
1@-=1 // normalize from 0 to n-1
1@ *= 0x7C4 // 0x7C4 = blocksize per ped index
005A: 1@ += 0@  // pointer to LAST ped in the pool
for 2@ = 0@ to 1@ step 0x7C4
    0AEA: 3@ = actor_struct 2@ handle
    if 856D: NOT actor 3@ defined
    then continue
    end
    // do something to actor 3@
end
- But I it seems that when I read (p+12) it sometimes give me wrong values. For example the number of peds existing as I count all peds manually is 5, but (p+12) says it is 2, then becomes 5,then becomes 7, then becomes 5 again? Can you try if this is happening to you @monday?
- Seems like gta sa delayed writes on (p+12). Although after waiting for 5 seconds (p+12) will return the right amount of pedcount. But that seems to be taking too long what do you think?
- I don't like (p+8) because it returns a 2000 max player count xD lags my PC as hell XD. ALthough this fixes my problem getting all peds around the world but lags as hell bro xD.
 

monday

Expert
Joined
Jun 23, 2014
Messages
1,127
Solutions
1
Reaction score
158
There's a logic mistake in the code, the max ped value should not be treated as an index of ped structure in memory. Why? Because ped memory is implemented as an array in efficient way, so when the ped at the begining of the array is removed, all the other remaining peds are not shifted in memory (to fill unused space), so that's the purpose of bytemap indicating which elements are in use. "p+12" value can't be used in place of bytemap, it should just break the loop after reaching specific number of active peds in that bytemap. So the logic should be:

C++:
p = read_4_bytes(0xB74490)
bytemap = read_4_bytes(p + 4) 
size = read_4_bytes(p + 8) 
current_count = read_4_bytes(p + 12) 

found_count = 0
for(int i = 0; i < size; i) {
         if (is_active(bytemap[i])) {
                found_count += 1;
                if (found_count == current_count) {
                            break;
                }
        }
}


// where is_active could be defined as "return (val > 0 && val < 128);". Why the value has to be lower than 128? Because 1 bit of the value indicates if it's 'empty' as shown in "tPoolObjectFlags" union of that CPool.h whatever that means (maybe it means 'unused' as indicated by bytemap but idk). The remaining 7 bits are some kind of ID*, which is used by cleo as ped handle.

* (there's no worry about "empty" bool bit interferring with remaining 7 bits, because if ped is active then that bit is always cleared to 0, so that's why these 7 bits don't have to be extracted, the whole byte can be read and used as an ID)
 

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
There's a logic mistake in the code, the max ped value should not be treated as an index of ped structure in memory. Why? Because ped memory is implemented as an array in efficient way, so when the ped at the begining of the array is removed, all the other remaining peds are not shifted in memory (to fill unused space), so that's the purpose of bytemap indicating which elements are in use. "p+12" value can't be used in place of bytemap, it should just break the loop after reaching specific number of active peds in that bytemap. So the logic should be:

C++:
p = read_4_bytes(0xB74490)
bytemap = read_4_bytes(p + 4)
size = read_4_bytes(p + 8)
current_count = read_4_bytes(p + 12)

found_count = 0
for(int i = 0; i < size; i) {
         if (is_active(bytemap[i])) {
                found_count += 1;
                if (found_count == current_count) {
                            break;
                }
        }
}


// where is_active could be defined as "return (val > 0 && val < 128);". Why the value has to be lower than 128? Because 1 bit of the value indicates if it's 'empty' as shown in "tPoolObjectFlags" union of that CPool.h whatever that means (maybe it means 'unused' as indicated by bytemap but idk). The remaining 7 bits are some kind of ID*, which is used by cleo as ped handle.

* (there's no worry about "empty" bool bit interferring with remaining 7 bits, because if ped is active then that bit is always cleared to 0, so that's why these 7 bits don't have to be extracted, the whole byte can be read and used as an ID)

That code is a good replacement instead of using a constant 140 bytemap size. But like I said this has issues:
Code:
current_count = read_4_bytes(p + 12)
The value of "current_count" does not return the right amount of ped count that exists. It sometimes return a lower value instead of the number of peds around.

As example case, ped the real amount of peds exists is equal to 7, "current_count" returns 3(not 7)

That means at this part:
Code:
if (found_count == current_count) {
       break;
Will fail to get the 4 remaining ped handles(base on my example) because it will stop at index 3

The result of how My code and your code is exactly the same. The difference is the method used on retrieving the peds.
  • I did not use the Bytemap,but instead I loop directly at the ped pool to get their individual ped struct
  • You loop at the Bytemap to get the ped handles
The issue both of our codes faces is that:
Code:
current_count = read_4_bytes(p + 12)
Does not return the right number of peds that exist. I think you need to try it so that you can confirm the issue.

Thank you for helping BTW I appreciate the time you offer Great One.
 

monday

Expert
Joined
Jun 23, 2014
Messages
1,127
Solutions
1
Reaction score
158
in my example, value of "i" may exceed the "current_count" (value from p+12), in your code, index never exceeds the value from p+12 (which would result in the exact problem you're describing). So I wouldn't call my code and your code behaviour "the same"
 

ajom

Well-known member
Joined
Apr 14, 2020
Messages
389
Solutions
2
Reaction score
268
Location
Pluto
Anyway, This is what I ended so far and fixes my problem using an index compensator of 20:

Code:
const
    DUMMYINDEX = 20 // inaccurate value compensator
end

0A8D: 1@ = read_memory 0xB74490 size 4 virtual_protect 0 // ped pool usage information
0A8D: 0@ = read_memory 1@ size 4 virtual_protect 0 // pointer to FIRST ped in the pool(normally this is CJ structs)

1@ += 12 // current number of peds in pedpool
0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 // return inaccurate value sometime.
    0AD1: show_formatted_text_highpriority "count:%d" time 2000 1@
1@ += DUMMYINDEX // compensate for the delay update of pointer (p+12)

1@-=1 // normalize from 0 to n-1
1@ *= 0x7C4 // 0x7C4 = blocksize per ped index
005A: 1@ += 0@  // pointer to LAST ped in the pool
for 2@ = 0@ to 1@ step 0x7C4
    0AEA: 3@ = actor_struct 2@ handle
    if 856D: NOT actor 3@ defined
    then continue
    end
    // do something to actor 3@
end
It's working completely fine even with large amount of actor around the world(tested on both single player and Samp)
 
Last edited:

monday

Expert
Joined
Jun 23, 2014
Messages
1,127
Solutions
1
Reaction score
158
I think I misunderstood what that value means. I tested it with 4 npcs on my server and when they went out of range and got back into it the value at "p+12" got repeatedly increased, gradually growing despite only 4 players being there, maybe it really supposed to stand for the highest index, but idk...
 
Status
Not open for further replies.
Top