Raised This Month: $299 Target: $400
 74% 

[CS:GO] How to properly get the free edict count


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
poggu
Junior Member
Join Date: Dec 2021
Old 01-17-2022 , 03:56   [CS:GO] How to properly get the free edict count
Reply With Quote #1

How to properly get the free edict count

Compared to other source games CSGO has a pretty different way of handling edicts, there's 2048 slots that can be allocated and then used.
Edict slots can be in 3 different states. They can be allocated or not allocated, they can be free, they can be on a timer.
Because of these states it can be pretty hard to get the number of used/free edicts on a server as the engine doesn't store such number anywhere.

What most plugins do to count the number of edicts is to loop over all entities and use IsValidEdict(edict);. Now here's the part why that's wrong.
The first 66 edict slots will ALWAYS be reserved for players and cannot be assigned to other entities, so all edict counts in plugins are off at least by 66 - clientCount. Source

The second reason why these counters will be off is because everytime an edict slot is freed an internal cooldown will be set on that slot. While this cooldown is still present that edict slot cannot be used. Source

Why is GetEntityCount() not a viable option?
GetEntityCount returns sv.num_edicts which represents the number of allocated edicts. Not the number of unused edicts. Once an edict is allocated once it will never be unallocated until next restart, it will only be freed. Source

Proof of concept
Now to the most interesting part, how to actually get the real number of free edicts.

PHP Code:
#define EDICT_FREETIME    1.0

float cacheTTL;
int cacheLastValue;
Address g_EdictFreeTime;

public 
void OnPluginStart()
{
  
GameData hGameConf;

  
hGameConf LoadGameConfigFile("EdictLimiter");
  if(!
hGameConf)
  {
    
SetFailState("Failed to find EdictLimiter");
  }

  
g_EdictFreeTime view_as<Address>(LoadFromAddress(hGameConf.GetAddress("g_edictFreeTime"), NumberType_Int32));

  
delete hGameConf;
}

int GetFreeEdicts()
{
  
// I recommend having a cache for this function if you're going to call it on every entity creation
  
if(GetEngineTime() < cacheTTL)
    return 
cacheLastValue;

  
int edict_ents;
  for(
int i MAXPLAYERS 12048i++)
  {
    if(!
IsValidEdict(i)) // Check if slot is not allocated or is free, sourcemod checks if the slot edict->IsFree()
    
{
      
float edictFreeTime GetEdictFreeTime(i);
      if(
edictFreeTime || (CBaseServer_GetTime() - GetEdictFreeTime(i)) >= EDICT_FREETIME// Optional, game will ignore this if the server is about to reach the max edict limit
        
edict_ents++;
    }
  }

  
cacheTTL GetEngineTime() + 0.5;
  
cacheLastValue edict_ents;
  return 
edict_ents;
}

float CBaseServer_GetTime()
{
  return 
GetGameTickCount() * GetTickInterval();
}

float GetEdictFreeTime(int edict)
{
  return 
view_as<float>(LoadFromAddress(g_EdictFreeTime view_as<Address>(edict 4), NumberType_Int32));

PHP Code:
"Signatures"
{
    
"g_edictFreeTime"
    
{
        
"library" "engine"
        "windows" "\xF3\x0F\x10\x0C\xBD\x2A\x2A\x2A\x2A\x3B\xFB"
        "linux" "\xF3\x0F\x10\x04\x9D\x2A\x2A\x2A\x2A\x39\xD3"
    
}
}
"Offsets"
{
    
"g_edictFreeTime"
    
{
        
"signature"    "g_edictFreeTime"
        "offset" "5"
    
}

About EDICT_FREETIME
As a last resort the game will use edict slots even if they are in a cooldown, this is supposed to prevent some cheats in dota2. If this feature wasn't a thing many CSGO maps would crash the server on every new round.
If you want to use the edict count code to prevent edict limit crashes you should remove the freetime checks as those will not be changing any edict behaviour if the server is about to hit the edict limit

Last edited by poggu; 01-17-2022 at 13:48.
poggu is offline
Dragokas
Veteran Member
Join Date: Nov 2017
Location: Ukraine
Old 01-22-2022 , 19:17   Re: [CS:GO] How to properly get the free edict count
Reply With Quote #2

Thanks for the research and explanation.

However, IMHO, I would call it "order of indices using" rather than "free edict count", because as you already told, they all are available for using regardless of the "timer" state.

I just doubled your paraphrased source:
Quote:
// We don't have any available edicts that are newer than
// EDICT_FREETIME. Rather than crash try to find an edict that
// was deleted less than EDICT_FREETIME ago. This will protect us
// against potential server hacks like those used to crash
// dota2 servers.
so only those reserved for client indices are really excepted.

Are there other cases when that's not true?
__________________
Expert of CMD/VBS/VB6. Malware analyst. L4D fun (Bloody Witch)
[My plugins] [My tools] [GitHub] [Articles] [Donate]
Dragokas is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 22:48.


Powered by vBulletin®
Copyright ©2000 - 2022, vBulletin Solutions, Inc.
Theme made by Freecode