Fix netsync of race checkpoints and associated map lines

- Use srb2::MobjList to keep checkpoint objects list
  intact after savegame load
- Use std::unordered_map of line tag and vector of line_t
  pointers
  - Use line tag for the key so multiple checkpoints may
    be associated to the same set of lines
This commit is contained in:
James R 2024-10-12 02:09:12 -07:00
parent f579d25770
commit e68bb676db

View file

@ -9,6 +9,8 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include <algorithm> #include <algorithm>
#include <unordered_map>
#include <vector>
#include <fmt/format.h> #include <fmt/format.h>
@ -35,8 +37,6 @@
#include "../sounds.h" #include "../sounds.h"
#include "../tables.h" #include "../tables.h"
using std::vector;
using std::pair;
using std::min; using std::min;
using std::max; using std::max;
using std::clamp; using std::clamp;
@ -45,7 +45,6 @@ extern mobj_t* svg_checkpoints;
#define checkpoint_id(o) ((o)->thing_args[0]) #define checkpoint_id(o) ((o)->thing_args[0])
#define checkpoint_linetag(o) ((o)->thing_args[1]) #define checkpoint_linetag(o) ((o)->thing_args[1])
#define checkpoint_extralength(o) ((o)->thing_args[2])
#define checkpoint_other(o) ((o)->target) #define checkpoint_other(o) ((o)->target)
#define checkpoint_orb(o) ((o)->tracer) #define checkpoint_orb(o) ((o)->tracer)
#define checkpoint_arm(o) ((o)->hnext) #define checkpoint_arm(o) ((o)->hnext)
@ -132,6 +131,7 @@ struct Checkpoint : mobj_t
struct Arm : mobj_t {}; struct Arm : mobj_t {};
INT32 id() const { return checkpoint_id(this); } INT32 id() const { return checkpoint_id(this); }
INT32 linetag() const { return checkpoint_linetag(this); }
Checkpoint* other() const { return static_cast<Checkpoint*>(checkpoint_other(this)); } Checkpoint* other() const { return static_cast<Checkpoint*>(checkpoint_other(this)); }
void other(Checkpoint* n) { P_SetTarget(&checkpoint_other(this), n); } void other(Checkpoint* n) { P_SetTarget(&checkpoint_other(this), n); }
@ -453,39 +453,16 @@ struct CheckpointManager
auto begin() { return list_.begin(); } auto begin() { return list_.begin(); }
auto end() { return list_.end(); } auto end() { return list_.end(); }
auto find_checkpoint(INT32 id) { auto find_checkpoint(INT32 id)
auto it = find_if(list_.begin(), list_.end(), [id](auto pair) { return pair.first->id() == id; }); {
if (it != list_.end()) auto it = std::find_if(begin(), end(), [id](Checkpoint* chk) { return chk->id() == id; });
{ return it != end() ? *it : nullptr;
return it->first; }
}
return static_cast<Checkpoint*>(nullptr); void remove_checkpoint(Checkpoint* end) { list_.erase(end); }
}
void link_checkpoint(Checkpoint* chk)
// auto find_pair(Checkpoint* chk) {
// pair<Checkpoint*, vector<line_t*>> retpair;
// auto it = find_if(list_.begin(), list_.end(), [chk](auto pair) { return pair.first == chk; });
// if (it != list_.end())
// {
// retpair = *it;
// return retpair;
// }
// return static_cast<pair<Checkpoint*, vector<line_t*>>>(nullptr);
// }
void remove_checkpoint(mobj_t* end)
{
auto chk = static_cast<Checkpoint*>(end);
auto it = find_if(list_.begin(), list_.end(), [&](auto pair) { return pair.first == chk; });
if (it != list_.end())
{
list_.erase(it);
}
}
void link_checkpoint(mobj_t* end)
{ {
auto chk = static_cast<Checkpoint*>(end);
auto id = chk->id(); auto id = chk->id();
if (chk->spawnpoint && id == 0) if (chk->spawnpoint && id == 0)
{ {
@ -517,29 +494,39 @@ struct CheckpointManager
} }
else // Checkpoint isn't in the list, find any associated tagged lines and make the pair else // Checkpoint isn't in the list, find any associated tagged lines and make the pair
{ {
vector<line_t*> checklines; if (chk->linetag())
if (checkpoint_linetag(chk)) lines_.try_emplace(chk->linetag(), std::move(tagged_lines(chk->linetag())));
{ list_.push_front(chk);
INT32 li;
INT32 tag = checkpoint_linetag(chk);
TAG_ITER_LINES(tag, li)
{
line_t* line = lines + li;
checklines.push_back(line);
}
}
list_.emplace_back(chk, move(checklines));
} }
chk->gingerbread(); chk->gingerbread();
} }
void clear() { list_.clear(); } void clear() { lines_.clear(); }
auto count() { return list_.size(); } auto count() { return list_.count(); }
const std::vector<line_t*>* lines_for(const Checkpoint* chk) const
{
auto it = lines_.find(chk->linetag());
return it != lines_.end() ? &it->second : nullptr;
}
private: private:
vector<pair<Checkpoint*, vector<line_t*>>> list_; srb2::MobjList<Checkpoint, svg_checkpoints> list_;
std::unordered_map<INT32, std::vector<line_t*>> lines_;
static std::vector<line_t*> tagged_lines(INT32 tag)
{
std::vector<line_t*> checklines;
INT32 li;
TAG_ITER_LINES(tag, li)
{
line_t* line = lines + li;
checklines.push_back(line);
}
return checklines;
}
}; };
CheckpointManager g_checkpoints; CheckpointManager g_checkpoints;
@ -548,13 +535,13 @@ CheckpointManager g_checkpoints;
void Obj_LinkCheckpoint(mobj_t* end) void Obj_LinkCheckpoint(mobj_t* end)
{ {
g_checkpoints.link_checkpoint(end); g_checkpoints.link_checkpoint(static_cast<Checkpoint*>(end));
} }
void Obj_UnlinkCheckpoint(mobj_t* end) void Obj_UnlinkCheckpoint(mobj_t* end)
{ {
auto chk = static_cast<Checkpoint*>(end); auto chk = static_cast<Checkpoint*>(end);
g_checkpoints.remove_checkpoint(end); g_checkpoints.remove_checkpoint(chk);
P_RemoveMobj(chk->orb()); P_RemoveMobj(chk->orb());
P_RemoveMobj(chk->arm()); P_RemoveMobj(chk->arm());
} }
@ -575,20 +562,20 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
{ {
LineOnDemand ray(old_x, old_y, player->mo->x, player->mo->y, player->mo->radius); LineOnDemand ray(old_x, old_y, player->mo->x, player->mo->y, player->mo->radius);
auto it = find_if( auto it = std::find_if(
g_checkpoints.begin(), g_checkpoints.begin(),
g_checkpoints.end(), g_checkpoints.end(),
[&](auto chkpair) [&](Checkpoint* chk)
{ {
Checkpoint* chk = chkpair.first;
if (!chk->valid()) if (!chk->valid())
{ {
return false; return false;
} }
LineOnDemand* gate; LineOnDemand* gate;
const std::vector<line_t*>* lines = g_checkpoints.lines_for(chk);
if (chkpair.second.empty()) if (!lines || lines->empty())
{ {
LineOnDemand dyngate = chk->crossing_line(); LineOnDemand dyngate = chk->crossing_line();
if (!ray.overlaps(dyngate)) if (!ray.overlaps(dyngate))
@ -598,15 +585,15 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
else else
{ {
auto it = find_if( auto it = find_if(
chkpair.second.begin(), lines->begin(),
chkpair.second.end(), lines->end(),
[&](const line_t* line) [&](const line_t* line)
{ {
return ray.overlaps(*line); return ray.overlaps(*line);
} }
); );
if (it == chkpair.second.end()) if (it == lines->end())
{ {
return false; return false;
} }
@ -640,7 +627,7 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
return; return;
} }
Checkpoint* chk = it->first; Checkpoint* chk = *it;
if (player->checkpointId == chk->id()) if (player->checkpointId == chk->id())
{ {
@ -657,9 +644,8 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
if (gametyperules & GTR_CHECKPOINTS) if (gametyperules & GTR_CHECKPOINTS)
{ {
for (auto chkpair : g_checkpoints) for (Checkpoint* chk : g_checkpoints)
{ {
Checkpoint* chk = chkpair.first;
if (chk->valid()) if (chk->valid())
{ {
chk->untwirl(); chk->untwirl();
@ -741,13 +727,12 @@ void Obj_ClearCheckpoints()
void Obj_DeactivateCheckpoints() void Obj_DeactivateCheckpoints()
{ {
for (auto chkpair : g_checkpoints) for (Checkpoint* chk : g_checkpoints)
{ {
Checkpoint* chk = chkpair.first;
if (chk->valid()) if (chk->valid())
{ {
chk->untwirl(); chk->untwirl();
chk->other()->untwirl(); chk->other()->untwirl();
} }
} }
} }