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 <unordered_map>
#include <vector>
#include <fmt/format.h>
@ -35,8 +37,6 @@
#include "../sounds.h"
#include "../tables.h"
using std::vector;
using std::pair;
using std::min;
using std::max;
using std::clamp;
@ -45,7 +45,6 @@ extern mobj_t* svg_checkpoints;
#define checkpoint_id(o) ((o)->thing_args[0])
#define checkpoint_linetag(o) ((o)->thing_args[1])
#define checkpoint_extralength(o) ((o)->thing_args[2])
#define checkpoint_other(o) ((o)->target)
#define checkpoint_orb(o) ((o)->tracer)
#define checkpoint_arm(o) ((o)->hnext)
@ -132,6 +131,7 @@ struct Checkpoint : mobj_t
struct Arm : mobj_t {};
INT32 id() const { return checkpoint_id(this); }
INT32 linetag() const { return checkpoint_linetag(this); }
Checkpoint* other() const { return static_cast<Checkpoint*>(checkpoint_other(this)); }
void other(Checkpoint* n) { P_SetTarget(&checkpoint_other(this), n); }
@ -453,39 +453,16 @@ struct CheckpointManager
auto begin() { return list_.begin(); }
auto end() { return list_.end(); }
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 find_checkpoint(INT32 id)
{
return it->first;
}
return static_cast<Checkpoint*>(nullptr);
auto it = std::find_if(begin(), end(), [id](Checkpoint* chk) { return chk->id() == id; });
return it != end() ? *it : nullptr;
}
// 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(Checkpoint* end) { list_.erase(end); }
void remove_checkpoint(mobj_t* end)
void link_checkpoint(Checkpoint* chk)
{
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();
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
{
vector<line_t*> checklines;
if (checkpoint_linetag(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));
if (chk->linetag())
lines_.try_emplace(chk->linetag(), std::move(tagged_lines(chk->linetag())));
list_.push_front(chk);
}
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:
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;
@ -548,13 +535,13 @@ CheckpointManager g_checkpoints;
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)
{
auto chk = static_cast<Checkpoint*>(end);
g_checkpoints.remove_checkpoint(end);
g_checkpoints.remove_checkpoint(chk);
P_RemoveMobj(chk->orb());
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);
auto it = find_if(
auto it = std::find_if(
g_checkpoints.begin(),
g_checkpoints.end(),
[&](auto chkpair)
[&](Checkpoint* chk)
{
Checkpoint* chk = chkpair.first;
if (!chk->valid())
{
return false;
}
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();
if (!ray.overlaps(dyngate))
@ -598,15 +585,15 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
else
{
auto it = find_if(
chkpair.second.begin(),
chkpair.second.end(),
lines->begin(),
lines->end(),
[&](const line_t* line)
{
return ray.overlaps(*line);
}
);
if (it == chkpair.second.end())
if (it == lines->end())
{
return false;
}
@ -640,7 +627,7 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
return;
}
Checkpoint* chk = it->first;
Checkpoint* chk = *it;
if (player->checkpointId == chk->id())
{
@ -657,9 +644,8 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
if (gametyperules & GTR_CHECKPOINTS)
{
for (auto chkpair : g_checkpoints)
for (Checkpoint* chk : g_checkpoints)
{
Checkpoint* chk = chkpair.first;
if (chk->valid())
{
chk->untwirl();
@ -741,9 +727,8 @@ void Obj_ClearCheckpoints()
void Obj_DeactivateCheckpoints()
{
for (auto chkpair : g_checkpoints)
for (Checkpoint* chk : g_checkpoints)
{
Checkpoint* chk = chkpair.first;
if (chk->valid())
{
chk->untwirl();