From 76153dbccc556718f5c43409905fabf491f9a51b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 6 Jan 2017 15:06:17 +0100 Subject: [PATCH] - 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- --- src/p_local.h | 12 ++- src/p_secnodes.cpp | 77 +++++++++-------- src/p_user.cpp | 209 ++++++++++++++++++++++++++------------------- src/r_defs.h | 2 +- 4 files changed, 173 insertions(+), 127 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index 93875df2f..6bb65ad97 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -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 @@ -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 +nodetype *P_AddSecnode(linktype *s, AActor *thing, nodetype *nextnode, nodetype *&sec_thinglist); + +template +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); diff --git a/src/p_secnodes.cpp b/src/p_secnodes.cpp index 38a9a5bab..8edfc0c70 100644 --- a/src/p_secnodes.cpp +++ b/src/p_secnodes.cpp @@ -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 +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(sector_t *s, AActor *thing, msecnode_t *nextnode, msecnode_t *&sec_thinglist); +template portnode_t *P_AddSecnode(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 +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(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, §or_t::sectorportal_thinglist); - touching_sectorportallist = NULL; + P_DelSeclist(touching_sectorportallist, §or_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; } - - diff --git a/src/p_user.cpp b/src/p_user.cpp index 6db4a57f5..3ea9f451f 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -95,9 +95,19 @@ static int PredictionLerptics; static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(APlayerPawn)]; -static TArray PredictionTouchingSectorsBackup; static TArray PredictionSectorListBackup; -static TArray PredictionSector_sprev_Backup; + +static TArray PredictionTouchingSectorsBackup; +static TArray PredictionTouchingSectors_sprev_Backup; + +static TArray PredictionRenderSectorsBackup; +static TArray PredictionRenderSectors_sprev_Backup; + +static TArray PredictionPortalSectorsBackup; +static TArray PredictionPortalSectors_sprev_Backup; + +static TArray PredictionPortalLinesBackup; +static TArray PredictionPortalLines_sprev_Backup; // [GRB] Custom player classes TArray 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 +void BackupNodeList(AActor *act, nodetype *head, nodetype *linktype::*otherlist, TArray &prevbackup, TArray &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 +nodetype *RestoreNodeList(AActor *act, nodetype *head, nodetype *linktype::*otherlist, TArray &prevbackup, TArray &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, §or_t::touching_thinglist, PredictionTouchingSectors_sprev_Backup, PredictionTouchingSectorsBackup); + BackupNodeList(act, act->touching_rendersectors, §or_t::touching_renderthings, PredictionRenderSectors_sprev_Backup, PredictionRenderSectorsBackup); + BackupNodeList(act, act->touching_sectorportallist, §or_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, §or_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, §or_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, §or_t::touching_thinglist, PredictionTouchingSectors_sprev_Backup, PredictionTouchingSectorsBackup); + act->touching_rendersectors = RestoreNodeList(act, ctx.render_list, §or_t::touching_renderthings, PredictionRenderSectors_sprev_Backup, PredictionRenderSectorsBackup); + act->touching_sectorportallist = RestoreNodeList(act, sectorportal_list, §or_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 diff --git a/src/r_defs.h b/src/r_defs.h index 67c9c8089..8e7cdd706 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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