Refactor cluster filtering

Double-clustering still needs to be done
This commit is contained in:
yamamama 2025-12-13 12:26:31 -05:00
parent 701ff411d5
commit 75bdd14cef
4 changed files with 97 additions and 31 deletions

View file

@ -39,7 +39,50 @@ static UINT32 K_GetDTFDifference(player_t *source, player_t *destination)
extern "C" {
player_t *closesttocluster;
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec)
boolean K_ClusterFilter_NoFilter(player_t *player, UINT32 fval)
{
// Ignores literally every parameter and returns true.
return true;
}
// Store our last memory of the cluster player (because we're clearing it out for the second cluster).
static boolean clusterplayer_memory[MAXPLAYERS];
// Filters players based on various factors, such as playercount and position.
// fval = pingame
boolean K_ClusterFilter_BaseFilter(player_t *player, UINT32 fval)
{
UINT8 pingame = static_cast<UINT8>(fval & 0xFF);
if (player->position >= pingame)
{
return false; // Ignore last place. *They* need help the most!
}
if (pingame > 6)
{
// FIXME: Double-cluster algorithm.
// Can't do it from within this function, or we're going to have too much overhead.
// However: at above 18 players, we shouldn't even *consider* double-clustering.
if (!K_IsPlayerLosing(player))
{
return false; // Ignore winning players to prevent sandbagging.
}
}
else if (pingame > 4)
{
if (player->position <= 1)
{
return false; // Ignore the frontrunner.
}
}
// Nothing to filter.
return true;
}
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out, void *nodevec)
{
INT64 meanx, meany, meanz;
INT32 N;
@ -99,11 +142,11 @@ INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, v
if (!player->mo)
continue;
if (!K_IsPlayerLosing(player))
continue; // Ignore winning players to prevent sandbagging.
if (player->position >= pingame)
continue; // Ignore last place. *They* need help the most!
if (cluster_filter)
{
if (!cluster_filter(player, filterval))
continue;
}
// Scan all points in the database
fixed_t dist = K_PlayerDistance3D(sourcePlayer, player);
@ -121,7 +164,7 @@ INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, v
static_cast<std::vector<player_t *> *>(nodevec)->push_back(player);
// Scan our neighbor for any players close to them.
K_CountNeighboringPlayersByDistance(player, eps, &neighborvector, nodevec);
K_CountNeighboringPlayersByDistance(player, eps, cluster_filter, filterval, &neighborvector, nodevec);
}
}
@ -191,17 +234,22 @@ static UINT32 K_GetU32Diff(UINT32 a, UINT32 b)
return (b > a) ? (b - a) : (a - b);
}
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
INT32 K_CountNeighboringPlayersByDTFEx(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out, void *nodevec)
{
INT64 mean;
INT32 N = 0;
vector3_t neighborvector;
if (!nodevec)
{
// Nice goddamn try.
return 0;
}
std::vector<player_t *> node_dtf = *static_cast<std::vector<player_t *> *>(nodevec);
if (!sourcePlayer->mo)
return 0;
if (cleardtf)
dtf_vec.clear();
mean = 0;
@ -235,31 +283,34 @@ INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector
{
//CONS_Printf("%d is less than %d, adding to nodes vector\n", K_GetDTFDifference(sourcePlayer, player), eps);
clusterplayer[i] = true;
dtf_vec.push_back(player); // Add to result
cleardtf = false;
if (nodevec)
{
// Add to the result.
static_cast<std::vector<player_t *> *>(nodevec)->push_back(player);
}
// Scan our neighbor for any players close to them.
K_CountNeighboringPlayersByDTF(player, eps, &neighborvector);
cleardtf = true;
K_CountNeighboringPlayersByDTFEx(player, eps, cluster_filter, filterval, &neighborvector, nodevec);
}
}
N = dtf_vec.size();
N = node_dtf.size();
CONS_Printf("node count: %d\n", N);
if (N != 0)
{
// Return the average center point of this cluster.
for (i = 0; i < N; i++)
{
if (!dtf_vec[i])
if (!node_dtf[i])
continue;
if (!dtf_vec[i]->mo)
if (!node_dtf[i]->mo)
continue;
mean += dtf_vec[i]->distancetofinish;
mean += node_dtf[i]->distancetofinish;
}
mean /= N;
@ -272,11 +323,11 @@ INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector
bestdist = UINT32_MAX;
for (i = 0; i < N; i++)
{
disttocluster = K_GetU32Diff(dtf_vec[i]->distancetofinish, mean);
disttocluster = K_GetU32Diff(node_dtf[i]->distancetofinish, mean);
if (disttocluster < bestdist)
{
closesttocluster = dtf_vec[i];
closesttocluster = node_dtf[i];
bestdist = disttocluster;
}
}
@ -289,14 +340,22 @@ INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector
return N;
}
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out)
{
if (cleardtf)
dtf_vec.clear();
return K_CountNeighboringPlayersByDTFEx(sourcePlayer, eps, cluster_filter, filterval, out, &dtf_vec);
}
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out)
{
// Dummy vector so the game doesn't SIGSEGV.
// Turns out, it's also necessary for this system not to be placebo!
std::vector<player_t *> dummy = {0};
dummy.clear();
INT32 result = K_CountNeighboringPlayersByDistance(sourcePlayer, eps, out, &dummy);
INT32 result = K_CountNeighboringPlayersByDistance(sourcePlayer, eps, cluster_filter, filterval, out, &dummy);
return result;
}

View file

@ -7,11 +7,18 @@ extern "C"
{
#endif
typedef boolean (playerfilter_f)(player_t *player, UINT32 filter_value);
extern player_t *closesttocluster;
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec);
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
// Literally allows any player to pass through.
boolean K_ClusterFilter_NoFilter(player_t *player, UINT32 fval);
boolean K_ClusterFilter_BaseFilter(player_t *player, UINT32 fval);
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out, void *nodevec);
INT32 K_CountNeighboringPlayersByDTFEx(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out, void *nodevec);
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out);
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, playerfilter_f *cluster_filter, UINT32 filterval, vector3_t *out);
#ifdef __cplusplus
}

View file

@ -1591,7 +1591,7 @@ static void K_DoGrowShrink(player_t *player, boolean shrinking)
if (shrinking)
{
// Find neighbors
INT32 n = K_CountNeighboringPlayers(player, ALTSHRINK_EPSILON, &(vector3_t){});
INT32 n = K_CountNeighboringPlayers(player, ALTSHRINK_EPSILON, &K_ClusterFilter_NoFilter, 0, &(vector3_t){});
// For every neighbor, add some iframes for a clean breakaway.
UINT32 iframes = BASE_IFRAMES + n * NEIGHBOR_IFRAMES;

View file

@ -9519,7 +9519,7 @@ static UINT32 K_UpdateDistanceFromCluster(player_t *player)
player->invincibilitybottleneck = (UINT16)(-1); // No bottlenecking!
return 0;
}
else if ((pingame > 3) && (pingame < MINCLUSTERPLAYERS))
else if ((pingame == 3))
{
// "Second place" in this case is actually second to last.
// In very small lobbies, it's harder for players to cluster together.
@ -9854,7 +9854,7 @@ static UINT32 K_UndoMapScaling(UINT32 distance)
// Based on DBSCAN, so it "chain-scans" neighboring players as well for a more accurate result.
UINT32 clusterid = 0;
static vector3_t *K_FindPlayerCluster(fixed_t eps, INT32 (*func)(player_t *, fixed_t, vector3_t *), vector3_t *out)
static vector3_t *K_FindPlayerCluster(fixed_t eps, INT32 (*func)(player_t *, fixed_t, playerfilter_f *, UINT32, vector3_t *), vector3_t *out)
{
INT32 bestdensity = 0; // Cluster counter
vector3_t tempclusterpoint;
@ -9882,7 +9882,7 @@ static vector3_t *K_FindPlayerCluster(fixed_t eps, INT32 (*func)(player_t *, fix
continue;
// Find neighbors
INT32 N = func(player, eps, &tempclusterpoint);
INT32 N = func(player, eps, &K_ClusterFilter_BaseFilter, 0, &tempclusterpoint);
// Double-remove the clusterplayer flag. Bad hack, I know...
clusterplayer[i] = false;