Rework the target list, use a container instead of an hash set so the target list gets archived correctly

This fixes an issue when loading from a save. A variable referencing target list container would have random entities in it, which would cause errors and crashes when trying to do an action on the array of entities
This commit is contained in:
smallmodel 2024-10-22 20:05:53 +02:00 committed by GitHub
parent 590b3f5cbb
commit 3d454c90cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 155 additions and 162 deletions

View file

@ -2131,49 +2131,21 @@ void Entity::SetTeamEvent(Event *ev)
void Entity::TriggerEvent(Event *ev)
{
const char *name;
Event *event;
Entity *ent;
ConSimple *tlist;
int i;
int num;
SimpleEntity *ent;
ScriptVariable arrayVar;
int i;
name = ev->GetString(1);
// Check for object commands
if (name && name[0] == '$') {
str sName = str(name + 1);
tlist = world->GetTargetList(sName);
num = tlist->NumObjects();
for (i = 1; i <= num; i++) {
ent = (Entity *)tlist->ObjectAt(i).Pointer();
assert(ent);
event = new Event(EV_Activate);
arrayVar = ev->GetValue(1);
arrayVar.CastConstArrayValue();
for (i = arrayVar.arraysize(); i > 0; i--) {
const ScriptVariable *variable = arrayVar[i];
ent = variable->simpleEntityValue();
if (ent) {
Event* event = new Event(EV_Activate);
event->AddEntity(this);
ent->ProcessEvent(event);
}
} else if (name[0] == '*') // Check for entnum commands
{
if (!IsNumeric(&name[1])) {
gi.Printf("Expecting numeric value for * command, but found '%s'\n", &name[1]);
} else {
ent = G_GetEntity(atoi(&name[1]));
if (ent) {
event = new Event(EV_Activate);
event->AddEntity(this);
ent->ProcessEvent(event);
} else {
gi.Printf("Entity not found for * command\n");
}
}
return;
} else {
gi.Printf("Invalid entity reference '%s'.\n", name);
}
}

View file

@ -1121,30 +1121,19 @@ qboolean Sentient::HasSecondaryWeapon(void)
void Sentient::EventGiveTargetname(Event *ev)
{
int i;
ConSimple *tlist;
str name;
const char *ptr;
qboolean found;
int i;
str name;
qboolean found;
ScriptVariable var;
SimpleEntity *ent;
name = ev->GetString(1);
var = ev->GetValue(1);
var.CastConstArrayValue();
ptr = name.c_str();
// skip over the $
ptr++;
found = qfalse;
str sName = ptr;
tlist = world->GetTargetList(sName);
for (i = 1; i <= tlist->NumObjects(); i++) {
Entity *ent;
ent = (Entity *)tlist->ObjectAt(i).Pointer();
assert(ent);
if (ent->isSubclassOf(Item)) {
for (i = var.arraysize(); i > 0; i--) {
const ScriptVariable *variable = var[i];
ent = variable->simpleEntityValue();
if (ent && ent->IsSubclassOfItem()) {
Item *item;
item = (Item *)ent;

View file

@ -536,12 +536,18 @@ void SimpleEntity::GetUpVector(Event *ev)
SimpleEntity *SimpleEntity::Next(void)
{
SimpleEntity *ent = world->GetTarget(target, true);
Listener* ent;
if (!target.length()) {
return NULL;
}
ent = world->GetTarget(target, true);
if (!ent || !ent->isSubclassOf(SimpleEntity)) {
return NULL;
} else {
return ent;
return static_cast<SimpleEntity*>(ent);
}
}

View file

@ -994,24 +994,23 @@ void World::SetNorthYaw(Event *ev)
m_fNorth = anglemod(ev->GetFloat(1));
}
SimpleEntity *World::GetTarget(str targetname, bool quiet)
Listener *World::GetTarget(str targetname, bool quiet)
{
return GetTarget(Director.AddString(targetname), quiet);
}
TargetList* targetList = GetTargetList(targetname);
SimpleEntity *World::GetTarget(const_str targetname, bool quiet)
{
ConSimple *list = GetTargetList(targetname);
if (!targetList) {
return NULL;
}
if (list->NumObjects() == 1) {
return list->ObjectAt(1);
} else if (list->NumObjects() > 1) {
if (targetList->list.NumObjects() == 1) {
return targetList->list.ObjectAt(1);
} else if (targetList->list.NumObjects() > 1) {
if (!quiet) {
warning(
"World::GetTarget",
"There are %d entities with targetname '%s'. You are using a command that requires exactly one.",
list->NumObjects(),
Director.GetString(targetname).c_str()
targetList->list.NumObjects(),
targetname.c_str()
);
}
}
@ -1019,79 +1018,83 @@ SimpleEntity *World::GetTarget(const_str targetname, bool quiet)
return NULL;
}
SimpleEntity *World::GetScriptTarget(str targetname)
Listener *World::GetScriptTarget(str targetname)
{
return GetScriptTarget(Director.AddString(targetname));
}
TargetList *targetList = GetTargetList(targetname);
SimpleEntity *World::GetScriptTarget(const_str targetname)
{
ConSimple *list = GetTargetList(targetname);
if (list->NumObjects() == 1) {
return list->ObjectAt(1);
} else if (list->NumObjects() > 1) {
if (targetList->list.NumObjects() == 1) {
return targetList->list.ObjectAt(1);
} else if (targetList->list.NumObjects() > 1) {
ScriptError(
"There are %d entities with targetname '%s'. You are using a command that requires exactly one.",
list->NumObjects(),
Director.GetString(targetname).c_str()
targetList->list.NumObjects(),
targetname.c_str()
);
}
return NULL;
}
ConSimple *World::GetExistingTargetList(const str& targetname)
TargetList*World::GetExistingTargetList(const str& targetname)
{
return GetExistingTargetList(Director.AddString(targetname));
TargetList* targetList;
int i;
if (!targetname.length()) {
return NULL;
}
for (i = m_targetListContainer.NumObjects(); i > 0; i--) {
targetList = m_targetListContainer.ObjectAt(i);
if (targetname == targetList->targetname) {
return targetList;
}
}
return NULL;
}
ConSimple *World::GetExistingTargetList(const_str targetname)
TargetList *World::GetTargetList(str& targetname)
{
return m_targetList.findKeyValue(targetname);
}
TargetList* targetList;
int i;
ConSimple *World::GetTargetList(str& targetname)
{
return GetTargetList(Director.AddString(targetname));
}
if (!targetname.length()) {
return NULL;
}
ConSimple *World::GetTargetList(const_str targetname)
{
return &m_targetList.addKeyValue(targetname);
for (i = m_targetListContainer.NumObjects(); i > 0; i--) {
targetList = m_targetListContainer.ObjectAt(i);
if (targetname == targetList->targetname) {
return targetList;
}
}
targetList = new TargetList(targetname);
m_targetListContainer.AddObject(targetList);
return targetList;
}
void World::AddTargetEntity(SimpleEntity *ent)
{
str targetname = ent->TargetName();
TargetList *list = GetTargetList(ent->TargetName());
if (!targetname.length()) {
return;
}
ConSimple *list = GetTargetList(targetname);
list->AddObject(ent);
list->AddEntity(ent);
}
void World::AddTargetEntityAt(SimpleEntity *ent, int index)
{
str targetname = ent->TargetName();
TargetList *list = GetTargetList(ent->TargetName());
if (!targetname.length()) {
return;
}
ConSimple *list = GetTargetList(targetname);
list->AddObjectAt(index, ent);
list->AddEntityAt(ent, index);
}
int World::GetTargetnameIndex(SimpleEntity *ent)
{
ConSimple *list = GetTargetList(ent->TargetName());
TargetList *targetList = GetTargetList(ent->TargetName());
return list->IndexOfObject(ent);
return targetList->GetEntityIndex(ent);
}
void World::RemoveTargetEntity(SimpleEntity *ent)
@ -1100,55 +1103,75 @@ void World::RemoveTargetEntity(SimpleEntity *ent)
return;
}
const str targetname = ent->TargetName();
str targetname = ent->TargetName();
if (!targetname.length()) {
return;
}
ConSimple *list = GetExistingTargetList(targetname);
TargetList *list = GetExistingTargetList(targetname);
if (list) {
list->RemoveObject(ent);
if (list->NumObjects() <= 0) {
m_targetList.remove(Director.AddString(targetname));
}
list->RemoveEntity(ent);
}
}
SimpleEntity *World::GetNextEntity(str targetname, SimpleEntity *ent)
{
return GetNextEntity(Director.AddString(targetname), ent);
}
SimpleEntity *World::GetNextEntity(const_str targetname, SimpleEntity *ent)
{
ConSimple *list = GetTargetList(targetname);
int index;
if (ent) {
index = list->IndexOfObject(ent) + 1;
} else {
index = 1;
}
if (list->NumObjects() >= index) {
return list->ObjectAt(index);
} else {
TargetList* targetList = GetExistingTargetList(targetname);
if (!targetList) {
return NULL;
}
return targetList->GetNextEntity(ent);
}
void World::FreeTargetList()
{
m_targetList.clear();
int i;
for (i = 1; i <= m_targetListContainer.NumObjects(); i++) {
delete m_targetListContainer.ObjectAt(i);
}
m_targetListContainer.FreeObjectList();
}
void World::Archive(Archiver& arc)
{
// FIXME: this is not the original way of archiving target list
m_targetList.Archive(arc);
int num;
int num2;
int i;
TargetList *targetList;
if (arc.Loading()) {
str targetname;
arc.ArchiveInteger(&num);
for (i = 1; i <= num; i++) {
arc.ArchiveString(&targetname);
targetList = new TargetList(targetname);
m_targetListContainer.AddObject(targetList);
arc.ArchiveObjectPosition((LightClass*)&targetList->list);
arc.ArchiveInteger(&num2);
targetList->list.Resize(num2);
}
} else {
num = m_targetListContainer.NumObjects();
arc.ArchiveInteger(&num);
for (i = 1; i <= num; i++) {
targetList = m_targetListContainer.ObjectAt(i);
arc.ArchiveString(&targetList->targetname);
arc.ArchiveObjectPosition((LightClass*)&targetList->list);
num2 = targetList->list.NumObjects();
arc.ArchiveInteger(&num2);
}
}
Entity::Archive(arc);
@ -1236,7 +1259,15 @@ SimpleEntity *TargetList::GetNextEntity(SimpleEntity *ent)
{
int index;
for (index = list.IndexOfObject(ent); index <= list.NumObjects(); index++) {
if (ent) {
index = list.IndexOfObject(ent);
} else {
index = 0;
}
index++;
for (; index <= list.NumObjects(); index++) {
Listener *objptr;
objptr = list.ObjectAt(index);

View file

@ -97,8 +97,8 @@ public:
class World : public Entity
{
con_set<const_str, ConSimple> m_targetList; // moh could have used con_set instead of TargetList
qboolean world_dying;
Container<TargetList*> m_targetListContainer;
qboolean world_dying;
public:
// farplane variables
@ -144,17 +144,12 @@ public:
void FreeTargetList();
SimpleEntity *GetNextEntity(str targetname, SimpleEntity *ent);
SimpleEntity *GetNextEntity(const_str targetname, SimpleEntity *ent);
SimpleEntity *GetScriptTarget(str targetname);
SimpleEntity *GetScriptTarget(const_str targetname);
SimpleEntity *GetTarget(str targetname, bool quiet);
SimpleEntity *GetTarget(const_str targetname, bool quiet);
Listener *GetScriptTarget(str targetname);
Listener *GetTarget(str targetname, bool quiet);
int GetTargetnameIndex(SimpleEntity *ent);
ConSimple *GetExistingTargetList(const str& targetname);
ConSimple *GetExistingTargetList(const_str targetname);
ConSimple *GetTargetList(str& targetname);
ConSimple *GetTargetList(const_str targetname);
TargetList *GetExistingTargetList(const str& targetname);
TargetList *GetTargetList(str& targetname);
void SetFarClipOverride(Event *ev);
void SetFarPlaneColorOverride(Event *ev);

View file

@ -1007,7 +1007,7 @@ void ScriptVM::Execute(ScriptVariable *data, int dataSize, str label)
Event ev;
ScriptVariable *var = NULL;
ConSimple *targetList;
TargetList *targetList;
if (Director.stackCount >= MAX_STACK_DEPTH) {
state = STATE_EXECUTION;
@ -1769,9 +1769,9 @@ void ScriptVM::Execute(ScriptVariable *data, int dataSize, str label)
case OP_UN_TARGETNAME:
// retrieve the target name
targetList = world->GetExistingTargetList(m_VMStack.GetTop().constStringValue());
targetList = world->GetExistingTargetList(m_VMStack.GetTop().stringValue());
if (!targetList || !targetList->NumObjects()) {
if (!targetList || !targetList->list.NumObjects()) {
str targetname = m_VMStack.GetTop().stringValue();
// the target name was not found
m_VMStack.GetTop().setListenerValue(NULL);
@ -1780,12 +1780,12 @@ void ScriptVM::Execute(ScriptVariable *data, int dataSize, str label)
|| (*m_PrevCodePos >= OP_BOOL_UN_NOT && *m_PrevCodePos <= OP_UN_CAST_BOOLEAN)) {
ScriptError("Targetname '%s' does not exist.", targetname.c_str());
}
} else if (targetList->NumObjects() == 1) {
} else if (targetList->list.NumObjects() == 1) {
// single listener
m_VMStack.GetTop().setListenerValue(targetList->ObjectAt(1));
} else if (targetList->NumObjects() > 1) {
m_VMStack.GetTop().setListenerValue(targetList->list.ObjectAt(1));
} else if (targetList->list.NumObjects() > 1) {
// multiple listeners
m_VMStack.GetTop().setContainerValue((Container<SafePtr<Listener>> *)targetList);
m_VMStack.GetTop().setContainerValue((Container<SafePtr<Listener>> *)&targetList->list);
}
break;