- let the prediction code handle all 4 threaded lists an actor gets linked into.

This is mostly a straight refactoring of the existing code to work independently of specific member variables in the involved classes, using a bit of template magic to avoid redundancy and moving the work code into subfunctions.
It still needs some testing to see if it a) helps fix the crash issues and b) doesn't break anything-
This commit is contained in:
Christoph Oelckers 2017-01-06 15:06:17 +01:00
parent 6c1fcdc34b
commit 76153dbccc
4 changed files with 173 additions and 127 deletions

View file

@ -38,9 +38,11 @@ class APlayerPawn;
struct line_t;
struct sector_t;
struct msecnode_t;
struct portnode_t;
struct secplane_t;
struct FCheckPosition;
struct FTranslatedLineTarget;
struct FLinePortal;
#include <stdlib.h>
@ -389,8 +391,14 @@ int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance,
FName damageType, int flags, int fulldamagedistance=0);
void P_DelSeclist(msecnode_t *, msecnode_t *sector_t::*seclisthead);
msecnode_t *P_AddSecnode(sector_t *s, AActor *thing, msecnode_t *nextnode, msecnode_t *&sec_thinglist);
msecnode_t* P_DelSecnode(msecnode_t *, msecnode_t *sector_t::*head);
void P_DelSeclist(portnode_t *, portnode_t *FLinePortal::*seclisthead);
template<class nodetype, class linktype>
nodetype *P_AddSecnode(linktype *s, AActor *thing, nodetype *nextnode, nodetype *&sec_thinglist);
template<class nodetype, class linktype>
nodetype* P_DelSecnode(nodetype *, nodetype *linktype::*head);
msecnode_t *P_CreateSecNodeList(AActor *thing, double radius, msecnode_t *sector_list, msecnode_t *sector_t::*seclisthead);
double P_GetMoveFactor(const AActor *mo, double *frictionp); // phares 3/6/98
double P_GetFriction(const AActor *mo, double *frictionfactor);

View file

@ -32,7 +32,7 @@
// Maintain a freelist of msecnode_t's to reduce memory allocs and frees.
//=============================================================================
msecnode_t *headsecnode = NULL;
msecnode_t *headsecnode = nullptr;
FMemArena secnodearena;
//=============================================================================
@ -55,7 +55,6 @@ msecnode_t *P_GetSecnode()
}
else
{
node =
node = (msecnode_t *)secnodearena.Alloc(sizeof(*node));
}
return node;
@ -87,9 +86,10 @@ void P_PutSecnode(msecnode_t *node)
//
//=============================================================================
msecnode_t *P_AddSecnode(sector_t *s, AActor *thing, msecnode_t *nextnode, msecnode_t *&sec_thinglist)
template<class nodetype, class linktype>
nodetype *P_AddSecnode(linktype *s, AActor *thing, nodetype *nextnode, nodetype *&sec_thinglist)
{
msecnode_t *node;
nodetype *node;
if (s == 0)
{
@ -110,21 +110,21 @@ msecnode_t *P_AddSecnode(sector_t *s, AActor *thing, msecnode_t *nextnode, msecn
// Couldn't find an existing node for this sector. Add one at the head
// of the list.
node = P_GetSecnode();
node = (nodetype*)P_GetSecnode();
// killough 4/4/98, 4/7/98: mark new nodes unvisited.
node->visited = 0;
node->m_sector = s; // sector
node->m_thing = thing; // mobj
node->m_tprev = NULL; // prev node on Thing thread
node->m_tprev = nullptr; // prev node on Thing thread
node->m_tnext = nextnode; // next node on Thing thread
if (nextnode)
nextnode->m_tprev = node; // set back link on Thing
// Add new node at head of sector thread starting at s->touching_thinglist
node->m_sprev = NULL; // prev node on sector thread
node->m_sprev = nullptr; // prev node on sector thread
node->m_snext = sec_thinglist; // next node on sector thread
if (sec_thinglist)
node->m_snext->m_sprev = node;
@ -132,22 +132,26 @@ msecnode_t *P_AddSecnode(sector_t *s, AActor *thing, msecnode_t *nextnode, msecn
return node;
}
template msecnode_t *P_AddSecnode<msecnode_t, sector_t>(sector_t *s, AActor *thing, msecnode_t *nextnode, msecnode_t *&sec_thinglist);
template portnode_t *P_AddSecnode<portnode_t, FLinePortal>(FLinePortal *s, AActor *thing, portnode_t *nextnode, portnode_t *&sec_thinglist);
//=============================================================================
//
// P_DelSecnode
//
// Deletes a sector node from the list of
// sectors this object appears in. Returns a pointer to the next node
// on the linked list, or NULL.
// on the linked list, or nullptr.
//
//=============================================================================
msecnode_t *P_DelSecnode(msecnode_t *node, msecnode_t *sector_t::*listhead)
template<class nodetype, class linktype>
nodetype *P_DelSecnode(nodetype *node, nodetype *linktype::*listhead)
{
msecnode_t* tp; // prev node on thing thread
msecnode_t* tn; // next node on thing thread
msecnode_t* sp; // prev node on sector thread
msecnode_t* sn; // next node on sector thread
nodetype* tp; // prev node on thing thread
nodetype* tn; // next node on thing thread
nodetype* sp; // prev node on sector thread
nodetype* sn; // next node on sector thread
if (node)
{
@ -175,12 +179,15 @@ msecnode_t *P_DelSecnode(msecnode_t *node, msecnode_t *sector_t::*listhead)
// Return this node to the freelist
P_PutSecnode(node);
P_PutSecnode((msecnode_t*)node);
return tn;
}
return NULL;
return nullptr;
} // phares 3/13/98
template msecnode_t *P_DelSecnode(msecnode_t *node, msecnode_t *sector_t::*listhead);
template portnode_t *P_DelSecnode(portnode_t *node, portnode_t *FLinePortal::*listhead);
//=============================================================================
//
// P_DelSeclist
@ -195,6 +202,13 @@ void P_DelSeclist(msecnode_t *node, msecnode_t *sector_t::*sechead)
node = P_DelSecnode(node, sechead);
}
void P_DelSeclist(portnode_t *node, portnode_t *FLinePortal::*sechead)
{
while (node)
node = P_DelSecnode(node, sechead);
}
//=============================================================================
// phares 3/14/98
//
@ -210,13 +224,13 @@ msecnode_t *P_CreateSecNodeList(AActor *thing, double radius, msecnode_t *sector
// First, clear out the existing m_thing fields. As each node is
// added or verified as needed, m_thing will be set properly. When
// finished, delete all nodes where m_thing is still NULL. These
// finished, delete all nodes where m_thing is still nullptr. These
// represent the sectors the Thing has vacated.
node = sector_list;
while (node)
{
node->m_thing = NULL;
node->m_thing = nullptr;
node = node->m_tnext;
}
@ -254,12 +268,12 @@ msecnode_t *P_CreateSecNodeList(AActor *thing, double radius, msecnode_t *sector
sector_list = P_AddSecnode(thing->Sector, thing, sector_list, thing->Sector->*seclisthead);
// Now delete any nodes that won't be used. These are the ones where
// m_thing is still NULL.
// m_thing is still nullptr.
node = sector_list;
while (node)
{
if (node->m_thing == NULL)
if (node->m_thing == nullptr)
{
if (node == sector_list)
sector_list = node->m_tnext;
@ -308,7 +322,7 @@ portnode_t *P_DelPortalnode(portnode_t *node)
if (sp)
sp->m_snext = sn;
else
node->m_portal->lineportal_thinglist = sn;
node->m_sector->lineportal_thinglist = sn;
if (sn)
sn->m_sprev = sp;
@ -316,7 +330,7 @@ portnode_t *P_DelPortalnode(portnode_t *node)
P_PutSecnode(reinterpret_cast<msecnode_t *>(node));
return tn;
}
return NULL;
return nullptr;
}
@ -340,16 +354,16 @@ portnode_t *P_AddPortalnode(FLinePortal *s, AActor *thing, portnode_t *nextnode)
// killough 4/4/98, 4/7/98: mark new nodes unvisited.
node->visited = 0;
node->m_portal = s; // portal
node->m_sector = s; // portal
node->m_thing = thing; // mobj
node->m_tprev = NULL; // prev node on Thing thread
node->m_tprev = nullptr; // prev node on Thing thread
node->m_tnext = nextnode; // next node on Thing thread
if (nextnode)
nextnode->m_tprev = node; // set back link on Thing
// Add new node at head of portal thread starting at s->touching_thinglist
node->m_sprev = NULL; // prev node on portal thread
node->m_sprev = nullptr; // prev node on portal thread
node->m_snext = s->lineportal_thinglist; // next node on portal thread
if (s->lineportal_thinglist)
node->m_snext->m_sprev = node;
@ -357,7 +371,6 @@ portnode_t *P_AddPortalnode(FLinePortal *s, AActor *thing, portnode_t *nextnode)
return node;
}
//==========================================================================
//
// Handle the lists used to render actors from other portal areas
@ -420,18 +433,12 @@ void AActor::UpdateRenderSectorList()
void AActor::ClearRenderSectorList()
{
msecnode_t *node = touching_sectorportallist;
while (node)
node = P_DelSecnode(node, &sector_t::sectorportal_thinglist);
touching_sectorportallist = NULL;
P_DelSeclist(touching_sectorportallist, &sector_t::sectorportal_thinglist);
touching_sectorportallist = nullptr;
}
void AActor::ClearRenderLineList()
{
portnode_t *node = touching_lineportallist;
while (node)
node = P_DelPortalnode(node);
touching_lineportallist = NULL;
P_DelSeclist(touching_lineportallist, &FLinePortal::lineportal_thinglist);
touching_lineportallist = nullptr;
}

View file

@ -95,9 +95,19 @@ static int PredictionLerptics;
static player_t PredictionPlayerBackup;
static BYTE PredictionActorBackup[sizeof(APlayerPawn)];
static TArray<sector_t *> PredictionTouchingSectorsBackup;
static TArray<AActor *> PredictionSectorListBackup;
static TArray<msecnode_t *> PredictionSector_sprev_Backup;
static TArray<sector_t *> PredictionTouchingSectorsBackup;
static TArray<msecnode_t *> PredictionTouchingSectors_sprev_Backup;
static TArray<sector_t *> PredictionRenderSectorsBackup;
static TArray<msecnode_t *> PredictionRenderSectors_sprev_Backup;
static TArray<sector_t *> PredictionPortalSectorsBackup;
static TArray<msecnode_t *> PredictionPortalSectors_sprev_Backup;
static TArray<FLinePortal *> PredictionPortalLinesBackup;
static TArray<portnode_t *> PredictionPortalLines_sprev_Backup;
// [GRB] Custom player classes
TArray<FPlayerClass> PlayerClasses;
@ -2775,6 +2785,100 @@ bool P_LerpCalculate(AActor *pmo, PredictPos from, PredictPos to, PredictPos &re
return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f);
}
template<class nodetype, class linktype>
void BackupNodeList(AActor *act, nodetype *head, nodetype *linktype::*otherlist, TArray<nodetype*, nodetype*> &prevbackup, TArray<linktype *, linktype *> &otherbackup)
{
// The ordering of the touching_sectorlist needs to remain unchanged
// Also store a copy of all previous sector_thinglist nodes
prevbackup.Clear();
otherbackup.Clear();
for (auto mnode = head; mnode != nullptr; mnode = mnode->m_tnext)
{
otherbackup.Push(mnode->m_sector);
for (auto snode = mnode->m_sector->*otherlist; snode; snode = snode->m_snext)
{
if (snode->m_thing == act)
{
prevbackup.Push(snode->m_sprev);
break;
}
}
}
}
template<class nodetype, class linktype>
nodetype *RestoreNodeList(AActor *act, nodetype *head, nodetype *linktype::*otherlist, TArray<nodetype*, nodetype*> &prevbackup, TArray<linktype *, linktype *> &otherbackup)
{
// Destroy old refrences
nodetype *node = head;
while (node)
{
node->m_thing = NULL;
node = node->m_tnext;
}
// Make the sector_list match the player's touching_sectorlist before it got predicted.
P_DelSeclist(head, otherlist);
head = NULL;
for (auto i = otherbackup.Size(); i-- > 0;)
{
head = P_AddSecnode(otherbackup[i], act, head, otherbackup[i]->*otherlist);
}
//act->touching_sectorlist = ctx.sector_list; // Attach to thing
//ctx.sector_list = NULL; // clear for next time
// In the old code this block never executed because of the commented-out NULL assignment above. Needs to be checked
node = head;
while (node)
{
if (node->m_thing == NULL)
{
if (node == head)
head = node->m_tnext;
node = P_DelSecnode(node, otherlist);
}
else
{
node = node->m_tnext;
}
}
nodetype *snode;
// Restore sector thinglist order
for (auto i = otherbackup.Size(); i-- > 0;)
{
// If we were already the head node, then nothing needs to change
if (prevbackup[i] == NULL)
continue;
for (snode = otherbackup[i]->*otherlist; snode; snode = snode->m_snext)
{
if (snode->m_thing == act)
{
if (snode->m_sprev)
snode->m_sprev->m_snext = snode->m_snext;
else
snode->m_sector->*otherlist = snode->m_snext;
if (snode->m_snext)
snode->m_snext->m_sprev = snode->m_sprev;
snode->m_sprev = prevbackup[i];
// At the moment, we don't exist in the list anymore, but we do know what our previous node is, so we set its current m_snext->m_sprev to us.
if (snode->m_sprev->m_snext)
snode->m_sprev->m_snext->m_sprev = snode;
snode->m_snext = snode->m_sprev->m_snext;
snode->m_sprev->m_snext = snode;
break;
}
}
}
return head;
}
void P_PredictPlayer (player_t *player)
{
int maxtic;
@ -2809,28 +2913,10 @@ void P_PredictPlayer (player_t *player)
act->flags2 &= ~MF2_PUSHWALL;
player->cheats |= CF_PREDICTING;
// The ordering of the touching_sectorlist needs to remain unchanged
// Also store a copy of all previous sector_thinglist nodes
msecnode_t *mnode = act->touching_sectorlist;
msecnode_t *snode;
PredictionSector_sprev_Backup.Clear();
PredictionTouchingSectorsBackup.Clear ();
while (mnode != NULL)
{
PredictionTouchingSectorsBackup.Push (mnode->m_sector);
for (snode = mnode->m_sector->touching_thinglist; snode; snode = snode->m_snext)
{
if (snode->m_thing == act)
{
PredictionSector_sprev_Backup.Push(snode->m_sprev);
break;
}
}
mnode = mnode->m_tnext;
}
BackupNodeList(act, act->touching_sectorlist, &sector_t::touching_thinglist, PredictionTouchingSectors_sprev_Backup, PredictionTouchingSectorsBackup);
BackupNodeList(act, act->touching_rendersectors, &sector_t::touching_renderthings, PredictionRenderSectors_sprev_Backup, PredictionRenderSectorsBackup);
BackupNodeList(act, act->touching_sectorportallist, &sector_t::sectorportal_thinglist, PredictionPortalSectors_sprev_Backup, PredictionPortalSectorsBackup);
BackupNodeList(act, act->touching_lineportallist, &FLinePortal::lineportal_thinglist, PredictionPortalLines_sprev_Backup, PredictionPortalLinesBackup);
// Keep an ordered list off all actors in the linked sector.
PredictionSectorListBackup.Clear();
@ -2940,6 +3026,12 @@ void P_UnPredictPlayer ()
player->camera = savedcamera;
FLinkContext ctx;
// Unlink from all list, includeing those which are not being handled by UnlinkFromWorld.
auto sectorportal_list = act->touching_sectorportallist;
auto lineportal_list = act->touching_lineportallist;
act->touching_sectorportallist = nullptr;
act->touching_lineportallist = nullptr;
act->UnlinkFromWorld(&ctx);
memcpy(&act->snext, PredictionActorBackup, sizeof(APlayerPawn) - ((BYTE *)&act->snext - (BYTE *)act));
@ -2968,71 +3060,10 @@ void P_UnPredictPlayer ()
*link = me;
}
// Destroy old refrences
msecnode_t *node = ctx.sector_list;
while (node)
{
node->m_thing = NULL;
node = node->m_tnext;
}
// Make the sector_list match the player's touching_sectorlist before it got predicted.
P_DelSeclist(ctx.sector_list, &sector_t::touching_thinglist);
ctx.sector_list = NULL;
for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
{
ctx.sector_list = P_AddSecnode(PredictionTouchingSectorsBackup[i], act, ctx.sector_list, PredictionTouchingSectorsBackup[i]->touching_thinglist);
}
act->touching_sectorlist = ctx.sector_list; // Attach to thing
ctx.sector_list = NULL; // clear for next time
// Huh???
node = ctx.sector_list;
while (node)
{
if (node->m_thing == NULL)
{
if (node == ctx.sector_list)
ctx.sector_list = node->m_tnext;
node = P_DelSecnode(node, &sector_t::touching_thinglist);
}
else
{
node = node->m_tnext;
}
}
msecnode_t *snode;
// Restore sector thinglist order
for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
{
// If we were already the head node, then nothing needs to change
if (PredictionSector_sprev_Backup[i] == NULL)
continue;
for (snode = PredictionTouchingSectorsBackup[i]->touching_thinglist; snode; snode = snode->m_snext)
{
if (snode->m_thing == act)
{
if (snode->m_sprev)
snode->m_sprev->m_snext = snode->m_snext;
else
snode->m_sector->touching_thinglist = snode->m_snext;
if (snode->m_snext)
snode->m_snext->m_sprev = snode->m_sprev;
snode->m_sprev = PredictionSector_sprev_Backup[i];
// At the moment, we don't exist in the list anymore, but we do know what our previous node is, so we set its current m_snext->m_sprev to us.
if (snode->m_sprev->m_snext)
snode->m_sprev->m_snext->m_sprev = snode;
snode->m_snext = snode->m_sprev->m_snext;
snode->m_sprev->m_snext = snode;
break;
}
}
}
act->touching_sectorlist = RestoreNodeList(act, ctx.sector_list, &sector_t::touching_thinglist, PredictionTouchingSectors_sprev_Backup, PredictionTouchingSectorsBackup);
act->touching_rendersectors = RestoreNodeList(act, ctx.render_list, &sector_t::touching_renderthings, PredictionRenderSectors_sprev_Backup, PredictionRenderSectorsBackup);
act->touching_sectorportallist = RestoreNodeList(act, sectorportal_list, &sector_t::sectorportal_thinglist, PredictionPortalSectors_sprev_Backup, PredictionPortalSectorsBackup);
act->touching_lineportallist = RestoreNodeList(act, lineportal_list, &FLinePortal::lineportal_thinglist, PredictionPortalLines_sprev_Backup, PredictionPortalLinesBackup);
}
// Now fix the pointers in the blocknode chain

View file

@ -1373,7 +1373,7 @@ struct msecnode_t
// use the same memory layout as msecnode_t so both can be used from the same freelist.
struct portnode_t
{
FLinePortal *m_portal; // a portal containing this object
FLinePortal *m_sector; // a portal containing this object (no, this isn't a sector, but if we want to use templates it needs the same variable names as msecnode_t.)
AActor *m_thing; // this object
struct portnode_t *m_tprev; // prev msecnode_t for this thing
struct portnode_t *m_tnext; // next msecnode_t for this thing