Senior Member
Join Date: Aug 2011
Location: /dev/null
|
02-11-2019
, 22:33
Re: When are handles closed?
|
#7
|
Quote:
Originally Posted by Powerlord
Are these ArrayLists and StringMaps created by your plugin or a different plugin?
|
Yes, my plugin is creating them.
Quote:
Originally Posted by asherkin
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;i < 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;i < 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;i < 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;i < 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 (-1 < index < this.handle.Length) return view_as<MapReward>(this.handle.Get(index)); return view_as<MapReward>(INVALID_HANDLE); } public MapReward GetEdict(int edict, int &index = -1) { if (IsValidEntity(edict)) { MapReward reward; for (int i = 0, j = this.handle.Length;i < 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 id, int &index = -1) { MapReward reward; for (int i = 0, j = this.handle.Length;i < 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[] name, int &index = -1) { MapReward reward; char rewardName[MAXNAME]; for (int i = 0, j = this.handle.Length;i < 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 id, int &index = -1) { for (int i = 0, j = this.handle.Length;i < 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; r = this.handle.Push(entry); entry = view_as<MapReward>(INVALID_HANDLE); return r; } public bool Remove(int index, bool kill = true) { StringMap reward; if (-1 < 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,j = this.handle.Length;i < 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 = 0, j = this.handle.Length;i < j;i++) view_as<MapReward>(this.handle.Get(i)).Kill(); } public void Summon() { for (int i = 0,j = this.handle.Length;i < 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 edict, int 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.respawnTime, TimerRespawnReward, reward); else if (reward.Respawn > 0.0) CreateTimer(reward.Respawn, TimerRespawnReward, reward); } } reward = view_as<MapReward>(INVALID_HANDLE); } }
PHP Code:
public Action AddSpawnPoint(int client, int 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.
Last edited by NIGathan; 02-11-2019 at 22:54.
|
|