Raised This Month: $32 Target: $400
 8% 

Solved When are handles closed?


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
NIGathan
Senior Member
Join Date: Aug 2011
Location: /dev/null
Old 02-10-2019 , 22:05   When are handles closed?
Reply With Quote #1

I just finished writing a rather large and thorough plugin using the new transitional syntax with a bunch of enum structs and methodmap objects that hold either ArrayList's or StringMap's of data, sometimes that data is another ArrayList or StringMap (or methodmap of them).

I only just got to my first testing point and I'm getting a ton of "Invalid Handle" errors when trying to access a handle that was stored within an ArrayList or StringMap:
Code:
L 02/10/2019 - 18:59:21: [SM] Exception reported: Invalid Handle 19427ce8 (error 4)
My question is; are there any times that SourceMod will forcibly close a handle without the code explicitly telling it to (aside from the plugin ending)?

Or perhaps is there a limit to the number of currently active Handles (I assumed it would be like MAXINT or something)? I shouldn't be using more than a handful or two, tops and still receiving this error..

If I construct a methodmap using new and that constructor creates a new ArrayList, will the ArrayList's desctructor be called when that methodmap is out of scope?

Last edited by NIGathan; 02-12-2019 at 04:42.
NIGathan is offline
nosoop
Veteran Member
Join Date: Aug 2014
Old 02-10-2019 , 22:30   Re: When are handles closed?
Reply With Quote #2

I don't believe there's an instance where a handle would be closed implicitly in this case. For what it's worth, the error code (4) corresponds to HandleError_Index (though I don't know enough about the handle system to explain the meaning of that).

Would like to see a small reproduction case; could be some quirk in the enum structs or in your code.

----

There is a limit of (1<<15) handles, but it's unlikely you'd unknowingly hit that limit.
Handles are not destroyed without explicit calls to delete or CloseHandle, including those in container-like structures.
__________________
I do TF2, TF2 servers, and TF2 plugins.
I don't do DMs over Discord -- PM me on the forums regarding inquiries.
AlliedModders Releases / Github / TF2 Server / Donate (BTC / BCH / coffee)
nosoop is offline
NIGathan
Senior Member
Join Date: Aug 2011
Location: /dev/null
Old 02-10-2019 , 22:45   Re: When are handles closed?
Reply With Quote #3

That was my assumption, but perhaps ArrayList's or StringMap's destructors get called when they are out of scope?

Also it would take me quite a long time to reproduce in a small enough sample size to share... But I'll probably get to it eventually if I can't figure it out.

Edit 2: Well I went in and set all the temporary handles within local scopes to INVALID_HANDLE before leaving the scope and nothing has changed, so that takes that idea off the table...

Last edited by NIGathan; 02-10-2019 at 22:58.
NIGathan is offline
nosoop
Veteran Member
Join Date: Aug 2014
Old 02-10-2019 , 23:49   Re: When are handles closed?
Reply With Quote #4

Destructors are not called unless the handle is destroyed through the methods mentioned before; there's no RAII here (at this time).

The index error suggests your handle values aren't correct in some way; might want to debug print and make sure you're getting the same values that you're putting into your containers, and that those values do correspond to handles you've initialized.
__________________
I do TF2, TF2 servers, and TF2 plugins.
I don't do DMs over Discord -- PM me on the forums regarding inquiries.
AlliedModders Releases / Github / TF2 Server / Donate (BTC / BCH / coffee)
nosoop is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 02-11-2019 , 02:28   Re: When are handles closed?
Reply With Quote #5

Are these ArrayLists and StringMaps created by your plugin or a different plugin?
__________________
Not currently working on SourceMod plugin development.
Powerlord is offline
asherkin
SourceMod Developer
Join Date: Aug 2009
Location: OnGameFrame()
Old 02-11-2019 , 14:39   Re: When are handles closed?
Reply With Quote #6

Impossible to tell without seeing your code, but you may be hitting https://github.com/alliedmodders/sourcepawn/issues/310 if you're using enum struct methods.

HandleError_Index is generally "that is not, and could not have been, a handle", which is what makes me suspect that issue.
__________________
asherkin is offline
NIGathan
Senior Member
Join Date: Aug 2011
Location: /dev/null
Old 02-11-2019 , 22:33   Re: When are handles closed?
Reply With Quote #7

Quote:
Originally Posted by Powerlord View Post
Are these ArrayLists and StringMaps created by your plugin or a different plugin?
Yes, my plugin is creating them.
Quote:
Originally Posted by asherkin View Post
Impossible to tell without seeing your code, but you may be hitting https://github.com/alliedmodders/sourcepawn/issues/310 if you're using enum struct methods.

HandleError_Index is generally "that is not, and could not have been, a handle", which is what makes me suspect that issue.
No, I was the one who discovered that bug, I've been very careful to avoid it at all costs. The only enum struct methods I'm using are voids.

My plugin started as this and I expanded it so that another methodmap of an ArrayList is filled with the MapReward methodmaps.

Here are the snippets of my code in question:

enum struct used as a container to convert the StringMap data to->from:
PHP Code:
enum struct MapRewardEntity
{
    
int id;
    
int Edict;
    
char Name[MAXNAME];
    
char Entity[MAXENTITY];
    
char Model[MAXMODEL];
    
char Override[MAXSCRIPT];
    
char Dispatch[MAXSCRIPT];
    
char Property[MAXSCRIPT];
    
float SpawnOrigin[3];
    
float SpawnAngles[3];
    
float Angles[3];
    
float SpinAngles[3];
    
float SpinInterval;
    
Handle SpinTimer;
    
char Command[MAXCMD];
    
char HurtCommand[MAXCMD];
    
int Attribute;
    
float Health;
    
float Damage;
    
bool Evaluated;
    
float Respawn;
    
int Overlay;
    
void Evaluate()
    {
        if (!
this.Evaluated)
        {
            
bool beenSet[4];
            if (
strlen(this.Property) > 0)
            {
                
ScriptAliases.Get(this.Property,this.Property); // make sure these aren't resetting the variable if they aren't being set, otherwise an if Get to temp -> strcopy will be needed.
                
beenSet[3] = true;
            }
            if (
strlen(this.Dispatch) > 0)
            {
                
ScriptAliases.Get(this.Dispatch,this.Dispatch);
                
beenSet[2] = true;
            }
            if (
strlen(this.Override) > 0)
            {
                
ScriptAliases.Get(this.Override,this.Override);
                
beenSet[1] = true;
            }
            if (
strlen(this.Model) > 0)
            {
                
ModelAliases.Get(this.Model,this.Model);
                
beenSet[0] = true;
            }
            if (
strlen(this.Entity) > 0)
            {
                
MapRewardEntityAlias alias;
                
EntityAliases.Get(this.Entity,alias);
                
strcopy(this.Entity,MAXENTITY,alias.Entity);
                if ((!
beenSet[0]) && (strlen(alias.Model) > 0))
                    
strcopy(this.Model,MAXMODEL,alias.Model);
                if ((!
beenSet[1]) && (strlen(alias.Override) > 0))
                    
strcopy(this.Override,MAXSCRIPT,alias.Override);
                if ((!
beenSet[2]) && (strlen(alias.Dispatch) > 0))
                    
strcopy(this.Dispatch,MAXSCRIPT,alias.Dispatch);
                if ((!
beenSet[3]) && (strlen(alias.Property) > 0))
                    
strcopy(this.Property,MAXSCRIPT,alias.Property);
            }
        }
        if ((
strlen(this.Entity) < 1) || (strcmp(this.Entity,"null") == 0))
            
strcopy(this.Entity,MAXENTITY,"prop_physics_override");
        if (
strcmp(this.Model,"null") == 0)
            
strcopy(this.Model,MAXMODEL,"");
        if (
strcmp(this.Override,"null") == 0)
            
strcopy(this.Override,MAXSCRIPT,"");
        if (
strcmp(this.Dispatch,"null") == 0)
            
strcopy(this.Dispatch,MAXSCRIPT,"");
        if (
strcmp(this.Property,"null") == 0)
            
strcopy(this.Property,MAXSCRIPT,"");
    }
}; 
Here is the expanded MapReward methodmap, this.handle is what is being stored into the ArrayList:
PHP Code:
int rewardCount;

methodmap MapReward __nullable__
{
    public 
MapReward()
    {
        
StringMap handle = new StringMap();
        
handle.SetValue("id",rewardCount++);
        
handle.SetValue("edict",-1);
        
handle.SetString("name","");
        
handle.SetString("entity","");
        
handle.SetString("model","");
        
handle.SetString("override","");
        
handle.SetString("dispatch","");
        
handle.SetString("prop","");
        
float vec[3];
        
handle.SetArray("defOrigin",vec,3);
        
handle.SetArray("defAngles",vec,3);
        
handle.SetArray("angles",vec,3);
        
handle.SetArray("spinAngles",vec,3);
        
handle.SetValue("spinInt",0.0);
        
handle.SetValue("spinTimer",INVALID_HANDLE);
        
handle.SetString("command","");
        
handle.SetString("hurtCommand","");
        
handle.SetValue("attribute",0);
        
handle.SetValue("health",0.0);
        
handle.SetValue("damage",0.0);
        
handle.SetValue("eval",1);
        
handle.SetValue("respawn",-1.0);
        
handle.SetValue("overlay",FULLSPECTRUM);
        
handle.SetValue("attacker",-1);
        
handle.SetValue("inflictor",-1);
        
handle.SetValue("tickDamage",0.0);
        return 
view_as<MapReward>(handle);
    }
    
/*public MapReward(MapRewardEntity reward)
    {
        StringMap handle = new StringMap();
        handle.SetValue("edict",reward.Edict);
        handle.SetString("name",reward.Name);
        handle.SetString("entity",reward.Entity);
        handle.SetString("model",reward.Model);
        handle.SetArray("origin",reward.SpawnOrigin,3);
        handle.SetArray("angles",reward.SpawnAngles,3);
        return view_as<MapReward>(handle);
    }*/// Shame you can't overload :(
    /*public ~MapReward()
    {
        //CloseHandle(this.handle.get());
        delete this;
    }*/// Does the default destructor do this automatically? Shame they removed destructors...
    
property StringMap handle
    
{
        public 
get()
        {
            return 
view_as<StringMap>(this);
        }
    }
    
property bool Evaluated
    
{
        public 
get()
        {
            
int eval;
            
this.handle.GetValue("eval",eval);
            if (eval)
                return 
true;
            return 
false;
        }
        public 
set(bool eval)
        {
            if (eval)
            {
                
int val;
                
this.handle.GetValue("eval",val);
                if (!
val)
                {
                    
MapRewardEntity reward;
                    
this.handle.GetString("entity",reward.Entity,MAXENTITY);
                    
this.handle.GetString("model",reward.Model,MAXMODEL);
                    
this.handle.GetString("override",reward.Override,MAXSCRIPT);
                    
this.handle.GetString("dispatch",reward.Dispatch,MAXSCRIPT);
                    
this.handle.GetString("prop",reward.Property,MAXSCRIPT);
                    
reward.Evaluated false;
                    
reward.Evaluate();
                    
this.handle.SetString("entity",reward.Entity,true);
                    
this.handle.SetString("model",reward.Model,true);
                    
this.handle.SetString("override",reward.Override,true);
                    
this.handle.SetString("dispatch",reward.Dispatch,true);
                    
this.handle.SetString("prop",reward.Property,true);
                }    
                
this.handle.SetValue("eval",1,true);
            }
            else
                
this.handle.SetValue("eval",0,true);
        }
    }
    
property int ID
    
{
        public 
get()
        {
            
int id;
            
this.handle.GetValue("id",id);
            return 
id;
        }
    }
    
property int Edict
    
{
        public 
get()
        {
            
int edict;
            
this.handle.GetValue("edict",edict);
            return 
edict;
        }
        public 
set(int edict)
        {
            
this.handle.SetValue("edict",edict,true);
        }
    }
    public 
void GetData(MapRewardEntity reward)
    {
        
this.handle.GetValue("id",reward.id);
        
this.handle.GetValue("edict",reward.Edict);
        
this.handle.GetString("name",reward.Name,MAXNAME);
        
this.handle.GetString("entity",reward.Entity,MAXENTITY);
        
this.handle.GetString("model",reward.Model,MAXMODEL);
        
this.handle.GetString("override",reward.Override,MAXSCRIPT);
        
this.handle.GetString("dispatch",reward.Dispatch,MAXSCRIPT);
        
this.handle.GetString("prop",reward.Property,MAXSCRIPT);
        
float vec[3];
        
int sizevec sizeof(vec);
        
this.handle.GetArray("defOrigin",reward.SpawnOrigin,sizevec);
        
this.handle.GetArray("defAngles",reward.SpawnAngles,sizevec);
        
this.handle.GetArray("angles",reward.Angles,sizevec);
        
this.handle.GetArray("spinAngles",reward.SpinAngles,sizevec);
        
this.handle.GetValue("spinInt",reward.SpinInterval);
        
this.handle.GetValue("spinTimer",reward.SpinTimer);
        
this.handle.GetString("command",reward.Command,MAXCMD);
        
this.handle.GetString("hurtCommand",reward.HurtCommand,MAXCMD);
        
this.handle.GetValue("attribute",reward.Attribute);
        
this.handle.GetValue("health",reward.Health);
        
this.handle.GetValue("damage",reward.Damage);
        
this.handle.GetValue("eval",sizevec);
        
reward.Evaluated = (sizevec != 0);
        
this.handle.GetValue("respawn",reward.Respawn);
        
this.handle.GetValue("overlay",reward.Overlay);
    }
    public 
void SetData(const MapRewardEntity reward)
    {
        
int oldID = -1;
        
this.handle.GetValue("id",oldID);
        if ((
oldID 0) || (oldID >= rewardCount))
            
this.handle.SetValue("id",reward.id,true);
        
this.handle.SetValue("edict",reward.Edict,true);
        
this.handle.SetString("name",reward.Name,true);
        
this.handle.SetString("entity",reward.Entity,true);
        
this.handle.SetString("model",reward.Model,true);
        
this.handle.SetString("override",reward.Override,true);
        
this.handle.SetString("dispatch",reward.Dispatch,true);
        
this.handle.SetString("prop",reward.Property,true);
        
this.handle.SetArray("defOrigin",reward.SpawnOrigin,3,true);
        
this.handle.SetArray("defAngles",reward.SpawnAngles,3,true);
        
this.handle.SetArray("angles",reward.Angles,3,true);
        
this.handle.SetArray("spinAngles",reward.SpinAngles,3,true);
        
this.handle.SetValue("spinInt",reward.SpinInterval,true);
        
this.handle.SetValue("spinTimer",reward.SpinTimer,true);
        
this.handle.SetString("command",reward.Command,true);
        
this.handle.SetString("hurtCommand",reward.HurtCommand,true);
        
this.handle.SetValue("attribute",reward.Attribute,true);
        
this.handle.SetValue("health",reward.Health,true);
        
this.handle.SetValue("damage",reward.Damage,true);
        if (
reward.Evaluated)
            
this.handle.SetValue("eval",1,true);
        else
            
this.handle.SetValue("eval",0,true);
        
this.handle.SetValue("respawn",reward.Respawn,true);
        
this.handle.SetValue("overlay",reward.Overlay,true);
    }
    public 
bool IsValid()
    {
        
int edict this.Edict;
        if ((
edict > -1) && (IsValidEntity(edict)))
        {
            
char iName[MAXNAME];
            
char name[MAXNAME];
            
this.GetName(name);
            
GetEntPropString(edict,Prop_Data,"m_iName",iName,MAXNAME);
            if (
strcmp(iName,name) == 0)
                return 
true;
        }
        
this.Edict = -1;
        return 
false;
    }
    public 
void Kill()
    {
        if (
this.IsValid())
            
AcceptEntityInput(this.Edict,"Kill",0,0,0);
        
this.Edict = -1;
    }
    public 
bool Summon()
    {
        
this.Damage 0.0;
        
MapRewardEntity reward;
        
this.GetData(reward);
        if (
reward.Attribute HOOK_DEACTIVE)
        {
            if (
IsValidEntity(reward.Edict))
            {
                if (
this.IsValid())
                {
                    
this.Attribute &= ~HOOK_DEACTIVE;
                    if (
reward.Attribute HOOK_TOUCH)
                        
SDKHook(reward.Edict,SDKHook_StartTouch,MapRewardPickUp);
                    if (
reward.Attribute HOOK_HURT)
                        
SDKHook(reward.Edict,SDKHook_OnTakeDamage,MapRewardTakeDamage);
                    return 
true;
                }
            }
            else if (!(
reward.Attribute HOOK_STATIC))
            {
                if (
reward.Respawn 0.0)
                    
CreateTimer(cVars.respawnTime,TimerRespawnReward,view_as<MapReward>(this));
                else if (
reward.Respawn 0.0)
                    
CreateTimer(reward.Respawn,TimerRespawnReward,view_as<MapReward>(this));
                return 
true;
            }
        }
        
this.Kill();
        
reward.Edict CreateEntityByName(reward.Entity);
        if (
IsValidEntity(reward.Edict))
        {
            
SetEntPropString(reward.Edict,Prop_Data,"m_iName",reward.Name);
            
reward.Evaluate();
            if (
strlen(reward.Model) > 0)
                
//SetEntityModel(reward.Edict,reward.Model);
                
DispatchKeyValue(reward.Edict,"model",reward.Model);
            
DispatchSpawn(reward.Edict);
            if (
strlen(reward.Override) > 0)
                
DispatchKeyValue(reward.Edict,"overridescript",reward.Override);
            if (
strlen(reward.Dispatch) > 0)
            {
                
char strCurrent[MAXSCRIPT];
                
char strKeys[2][MAXSCRIPT];
                
char strType[16];
                
StrCat(reward.Dispatch,MAXSCRIPT,"&");
                while (
SplitString(reward.Dispatch,"&",strCurrent,MAXSCRIPT) > -1)
                {
                    if (
StrContains(strCurrent,",") < 0)
                        
AcceptEntityInput(reward.Edict,strCurrent,-1,-1,0);
                    else
                    {
                        
ExplodeString(strCurrent,",",strKeys,2,MAXSCRIPT,true);
                        
SplitString(strKeys[1],"=",strType,16);
                        
StrCat(strType,16,"=");
                        
ReplaceStringEx(strKeys[1],MAXSCRIPT,strType,"",-1,0);
                        if (
strcmp(strType,"float=") == 0)
                            
DispatchKeyValueFloat(reward.Edict,strKeys[0],StringToFloat(strKeys[1]));
                        else if (
strcmp(strType,"vec=") == 0)
                        {
                            
float vec[3];
                            
char strVec[3][16];
                            
ExplodeString(strKeys[1],",",strVec,3,16,true);
                            for (
int i 0;3;i++)
                                
vec[i] = StringToFloat(strVec[i]);
                            
DispatchKeyValueVector(reward.Edict,strKeys[0],vec);
                        }
                        else
                            
DispatchKeyValue(reward.Edict,strKeys[0],strKeys[1]);
                    }
                    
ReplaceStringEx(reward.Dispatch,MAXSCRIPT,strCurrent,"",-1,0);
                    if (
reward.Dispatch[0] == '&')
                        
StrErase(reward.Dispatch,0,1);
                }
            }
            if (
strlen(reward.Property) > 0)
            {
                
char strCurrent[MAXSCRIPT];
                
char strKeys[2][MAXSCRIPT];
                
char strType[16];
                
PropType propType;
                
StrCat(reward.Property,MAXSCRIPT,"&");
                while (
SplitString(reward.Property,"&",strCurrent,MAXSCRIPT) > -1)
                {
                    if (
StrContains(strCurrent,",") < 0)
                        
AcceptEntityInput(reward.Edict,strCurrent,-1,-1,0);
                    else
                    {
                        
propType Prop_Data;
                        if ((
strlen(strCurrent) > 2) && (strCurrent[1] == ':'))
                        {
                            if (
strCurrent[0] == '1')
                                
propType Prop_Send;
                            
StrErase(strCurrent,0,2);
                        }
                        
ExplodeString(strCurrent,",",strKeys,2,MAXSCRIPT,true);
                        
SplitString(strKeys[1],"=",strType,16);
                        
StrCat(strType,16,"=");
                        
ReplaceStringEx(strKeys[1],MAXSCRIPT,strType,"",-1,0);
                        if (
strcmp(strType,"float=") == 0)
                            
SetEntPropFloat(reward.Edict,propType,strKeys[0],StringToFloat(strKeys[1]));
                        else if (
strcmp(strType,"int=") == 0)
                            
SetEntProp(reward.Edict,propType,strKeys[0],StringToInt(strKeys[1]));
                        else if (
strcmp(strType,"bool=") == 0)
                            
SetEntProp(reward.Edict,propType,strKeys[0],StringToInt(strKeys[1]),1);
                        else if (
strcmp(strType,"vec=") == 0)
                        {
                            
float vec[3];
                            
char strVec[3][16];
                            
ExplodeString(strKeys[1],",",strVec,3,16,true);
                            for (
int i 0;3;i++)
                                
vec[i] = StringToFloat(strVec[i]);
                            
SetEntPropVector(reward.Edict,propType,strKeys[0],vec);
                        }
                        else if (
strcmp(strType,"ent=") == 0)
                            
SetEntPropEnt(reward.Edict,propType,strKeys[0],StringToInt(strKeys[1]));
                        else
                            
SetEntPropString(reward.Edict,propType,strKeys[0],strKeys[1]);
                    }
                    
ReplaceStringEx(reward.Property,MAXSCRIPT,strCurrent,"",-1,0);
                    if (
reward.Property[0] == '&')
                        
StrErase(reward.Property,0,1);
                }
            }
            if (
reward.Overlay != FULLSPECTRUM)
            {
                
int colors[4];
                
colors[0] = (reward.Overlay 0x000000FF);
                
colors[1] = (reward.Overlay 0x0000FF00) >> 8;
                
colors[2] = (reward.Overlay 0x00FF0000) >> 16;
                
colors[3] = (reward.Overlay 0xFF000000) >> 24;
                for (
int i 0;4;i++)
                    if (
colors[i] < 0)
                        
colors[i] = (colors[i] % 256) + 256;
                
SetEntityRenderMode(reward.Edict,RENDER_GLOW);
                
SetEntityRenderColor(reward.Edict,colors[0],colors[1],colors[2],colors[3]);
            }
            
float org[3];
            
float ang[3];
            for (
int i 0;3;i++)
            {
                
org[i] = reward.SpawnOrigin[i];
                
ang[i] = reward.SpawnAngles[i];
            }
            
TeleportEntity(reward.Edict,org,ang,NULL_VECTOR);
            
this.Edict reward.Edict;
            if (
reward.SpinInterval 0.0)
            {
                
this.SetSpinAngles(ang);
                
this.SetSpinTimer(CreateTimer(reward.SpinInterval,TimerSpin,view_as<MapReward>(this),TIMER_REPEAT));
            }
            if (
reward.Attribute HOOK_DEACTIVE)
            {
                if (
reward.Respawn 0.0)
                    
CreateTimer(cVars.respawnTime,TimerRespawnReward,view_as<MapReward>(this));
                else if (
reward.Respawn 0.0)
                    
CreateTimer(reward.Respawn,TimerRespawnReward,view_as<MapReward>(this));
            }
            else
            {
                if (
reward.Attribute HOOK_TOUCH)
                    
SDKHook(reward.Edict,SDKHook_StartTouch,MapRewardPickUp);
                if (
reward.Attribute HOOK_HURT)
                    
SDKHook(reward.Edict,SDKHook_OnTakeDamage,MapRewardTakeDamage);
            }
            return 
true;
        }
        return 
false;
    }
}; 
I left some bits out to try to shorten this.
Here is the methodmap that is filled with MapReward handles:
PHP Code:
methodmap MapRewardList __nullable__
{
    public 
MapRewardList()
    {
        
ArrayList handle = new ArrayList(1,1);
        return 
view_as<MapRewardList>(handle);
    }
    
property ArrayList handle
    
{
        public 
get()
        {
            return 
view_as<ArrayList>(this);
        }
    }
    
property int Count
    
{
        public 
get()
        {
            return 
this.handle.Length;
        }
    }
    public 
MapReward Get(int index)
    {
        if (-
index this.handle.Length)
            return 
view_as<MapReward>(this.handle.Get(index));
        return 
view_as<MapReward>(INVALID_HANDLE);
    }
    public 
MapReward GetEdict(int edictint &index = -1)
    {
        if (
IsValidEntity(edict))
        {
            
MapReward reward;
            for (
int i 0this.handle.Length;j;i++)
            {
                
reward view_as<MapReward>(this.handle.Get(i));
                if (
edict == reward.Edict)
                {
                    
index i;
                    return 
reward;
                }
            }
        }
        return 
view_as<MapReward>(INVALID_HANDLE);
    }
    public 
MapReward GetID(int idint &index = -1)
    {
        
MapReward reward;
        for (
int i 0this.handle.Length;j;i++)
        {
            
reward view_as<MapReward>(this.handle.Get(i));
            if (
id == reward.ID)
            {
                
index i;
                return 
reward;
            }
        }
        return 
view_as<MapReward>(INVALID_HANDLE);
    }
    public 
MapReward GetName(const char[] nameint &index = -1)
    {
        
MapReward reward;
        
char rewardName[MAXNAME];
        for (
int i 0this.handle.Length;j;i++)
        {
            
reward view_as<MapReward>(this.handle.Get(i));
            
reward.GetName(rewardName);
            if (
strcmp(name,rewardName) == 0)
            {
                
index i;
                return 
reward;
            }
        }
        return 
view_as<MapReward>(INVALID_HANDLE);
    }
    public 
bool IsValid(int idint &index = -1)
    {
        for (
int i 0this.handle.Length;j;i++)
        {
            if (
id == view_as<MapReward>(this.handle.Get(i)).ID)
            {
                
index i;
                return 
true;
            }
        }
        return 
false;
    }
    public 
int Add(MapReward reward)
    {
        return 
this.handle.Push(reward);
    }
    public 
int AddEntity(MapRewardEntity reward)
    {
        
MapReward entry = new MapReward();
        
entry.SetData(reward);
        
int r;
        
this.handle.Push(entry);
        
entry view_as<MapReward>(INVALID_HANDLE);
        return 
r;
    }
    public 
bool Remove(int indexbool kill true)
    {
        
StringMap reward;
        if (-
index this.handle.Length)
        {
            
reward this.handle.Get(index);
            if (
kill)
                
view_as<MapReward>(reward).Kill();
            
CloseHandle(reward);
            
this.handle.Erase(index);
            if (
this.handle.Length 1)
                
rewardCount 0;
            return 
true;
        }
        return 
false;
    }
    public 
void RemoveAll(bool kill true)
    {
        
StringMap reward;
        for (
int i 0,this.handle.Length;j;i++)
        {
            
reward this.handle.Get(i);
            if (
kill)
                
view_as<MapReward>(reward).Kill();
            
CloseHandle(reward);
        }
        
this.handle.Clear();
        
rewardCount 0;
    }
    public 
void Kill()
    {
        for (
int i 0this.handle.Length;j;i++)
            
view_as<MapReward>(this.handle.Get(i)).Kill();
    }
    public 
void Summon()
    {
        for (
int i 0,this.handle.Length;j;i++)
            
view_as<MapReward>(this.handle.Get(i)).Summon();
    }
};
MapRewardList MapRewards;

public 
void OnPluginStart()
{
    
cVars.Setup();
    
MapRewards = new MapRewardList();
    
EntityAliases = new MapRewardEntityAliases();
    
ModelAliases = new MapRewardModelAliases();
    
ScriptAliases = new MapRewardScriptAliases();
    
    
RegConsoleCmd("sm_mrw_info"InfoSpawnPoint"Displays info about a specific reward spawn point.");
    
RegConsoleCmd("sm_mrw_add"AddSpawnPoint"Sets a spawn point on the map for a reward.");

And here is a place where those MapReward's get recalled. This is the callback for an SKDHook_StartTouch event (created by MapReward::Summon()). It errors at MapRewardList::GetEdict():
PHP Code:
public Action MapRewardPickUp(int edictint client)
{
    if ((
cVars.enable) && (client 0) && (client <= MaxClients) && (IsClientInGame(client)))
    {
        
MapReward reward MapRewards.GetEdict(edict);
        if (
reward.handle != INVALID_HANDLE)
        {
            
reward.Trigger(client);
            if (
reward.Attribute HOOK_STATIC)
            {
                
SDKUnhook(edict,SDKHook_StartTouch,MapRewardPickUp);
                
reward.Attribute |= HOOK_DEACTIVE;
            }
            if (!(
reward.Attribute HOOK_CONSTANT))
            {
                if (!(
reward.Attribute HOOK_STATIC))
                    
reward.Kill();
                if (
reward.Respawn 0.0)
                    
CreateTimer(cVars.respawnTimeTimerRespawnRewardreward);
                else if (
reward.Respawn 0.0)
                    
CreateTimer(reward.RespawnTimerRespawnRewardreward);
            }
        }
        
reward view_as<MapReward>(INVALID_HANDLE);
    }

PHP Code:
public Action AddSpawnPoint(int clientint args)
{
    if (!
handleClientAccess(client,cVars.createFlag))
        return 
Plugin_Handled;
    if (
args 1)
    {
        
CRespondToCommand(client"[SM] Usage: sm_mrw_add [OPTIONS ...] [command ...]");
        
CRespondToCommand(client"[SM]   Use sm_mrw_add -h to see the full help. Note: It's long, may want to run it from console.");
        return 
Plugin_Handled;
    }
    
MapReward reward = new MapReward();
    
// Skipping tons of input-defined settings on the reward
    
if (cVars.enable)
    {
        
reward.Summon();
        if (
release)
            
CRespondToCommand(client"[SM] Added reward and released entity #%d",reward.Edict);
        else
            
CRespondToCommand(client"[SM] Added reward spawn point #%d(%d)",reward.ID,reward.Edict);
    }
    else
        
CRespondToCommand(client"[SM] Added reward spawn point #%d(%d)",reward.ID,reward.Edict);
    if (
release)
        
CloseHandle(reward.handle);
    else
    {
        
MapRewards.Add(reward);
        if (
client != 0)
            
autoSave(SAVE_EDIT,true);
    }
    
reward view_as<MapReward>(INVALID_HANDLE); // an attempt to reset the handle in case it was calling the destructor, the addition of this line changed nothing.
    
return Plugin_Handled;

Edit: I have attached the full plugin incase I left out something important above, sorry it's hard to pinpoint the exact location or reason of my issue.
Attached Files
File Type: sp Get Plugin or Get Source (transitional_test.sp - 88 views - 138.5 KB)

Last edited by NIGathan; 02-11-2019 at 22:54.
NIGathan is offline
NIGathan
Senior Member
Join Date: Aug 2011
Location: /dev/null
Old 02-12-2019 , 02:30   Re: When are handles closed?
Reply With Quote #8

Wow... Well I can't believe it, but I fixed it... The problem was initializing the ArrayList inside of MapRewardList to a size of 1 instead of 0. I guess when it was trying to access the 0 index it was retrieving an invalid handle... Sorry to have wasted everyone's time
NIGathan is offline
ddhoward
Veteran Member
Join Date: May 2012
Location: California
Old 02-12-2019 , 02:35   Re: When are handles closed?
Reply With Quote #9

For other readers:

The issue was that ArrayLists, when created with a size greater than 0 or resized to be larger than they were previously, do NOT have the new indexes initialized to 0.

https://sm.alliedmods.net/new-api/ad...List/ArrayList
Quote:
int startsize
Initial size of the array. Note that data will NOT be auto-initialized.
The error was caused by the plugin attempting to utilize garbage data as a Handle, which matches what asherkin said earlier:

Quote:
HandleError_Index is generally "that is not, and could not have been, a handle"
__________________

Last edited by ddhoward; 02-12-2019 at 02:35.
ddhoward is offline
NIGathan
Senior Member
Join Date: Aug 2011
Location: /dev/null
Old 02-12-2019 , 04:34   Re: When are handles closed?
Reply With Quote #10

I assumed an ArrayList would be more like std::vector from C++, where its size can be greater than it's actual contents. I assumed I was simply preparing the container to hold 1, not actually make it have 1. I also realize this causes further problems with the plugin. ArrayLists do not naturally resize back down when you remove an entry, nor can you Push data into "unused" indexes.
NIGathan is offline
Reply



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 16:49.


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