Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/utility-css/src/Events/PracticePlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public HookResult OnPlayerBlind(EventPlayerBlind @event, GameEventInfo info)
private void OnClientAuthorized(int slot, SteamID steamId)
{
_library.Refresh(steamId.SteamId64);
_occupancyDirty = true;
}

// Joining a team is the moment somebody is actually in the server and able
Expand Down Expand Up @@ -71,6 +72,10 @@ public HookResult OnPlayerJoinTeam(EventPlayerTeam @event, GameEventInfo info)

private void OnClientDisconnect(int slot)
{
// Before the validity check: somebody left either way, and a controller
// we cannot resolve is exactly when the roster most needs re-reading.
_occupancyDirty = true;

CCSPlayerController? player = Utilities.GetPlayerFromSlot(slot);

if (player == null || !player.IsValid)
Expand Down
19 changes: 15 additions & 4 deletions apps/utility-css/src/UtilityPracticePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,26 @@ private void RespawnTheDead()
// one loop over the weapons they already have.
private int _occupancyTicks;

// Every few seconds, not every one: the panel only needs to know somebody
// is here, and the reaper's clocks are measured in minutes.
// Set by the connect and disconnect hooks, cleared by the send.
//
// The hooks flag rather than send because at OnClientDisconnect the leaving
// player is STILL in Utilities.GetPlayers() -- a snapshot taken there would
// report them as present, which is the exact staleness this exists to
// remove. Waiting for the next tick lets the engine drop them first, and
// folds a burst of joins into one post.
private bool _occupancyDirty;

// On the next tick after somebody comes or goes; otherwise a slow
// reconciler, because the snapshot is idempotent and the reaper's clocks
// are measured in minutes.
private void ReportOccupancy()
{
if (++_occupancyTicks < OccupancySeconds)
if (!_occupancyDirty && ++_occupancyTicks < OccupancySeconds)
{
return;
}

_occupancyDirty = false;
_occupancyTicks = 0;

var present = new List<ulong>();
Expand Down Expand Up @@ -358,7 +369,7 @@ private void RefreshEverything()
// reason this works at all.
private PracticeMapChangePending? _pendingMapLoad;

private const int OccupancySeconds = 15;
private const int OccupancySeconds = 60;
private const int WarmupRetrySeconds = 3;

private const float CfgReapplySeconds = 3f;
Expand Down
29 changes: 25 additions & 4 deletions apps/utility-sw/src/UtilityPracticePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ public override void Load(bool hotReload)
Core.Event.OnPrecacheResource += _precacheHandler;

_disconnectHandler = @event =>
{
// Outside ForPlayer: somebody left either way, and a player we
// cannot resolve is exactly when the roster most needs re-reading.
_occupancyDirty = true;

ForPlayer(
@event.PlayerId,
steamId =>
Expand All @@ -158,12 +163,17 @@ public override void Load(bool hotReload)
OnPlayerGone(steamId);
}
);
};
Core.Event.OnClientDisconnected += _disconnectHandler;

// Refresh FETCHES; it does not draw. Somebody who joins and runs no
// command should still see every lineup on the map.
_authorizeHandler = @event =>
{
_occupancyDirty = true;

ForPlayer(@event.PlayerId, steamId => RefreshAndShow(steamId));
};
Core.Event.OnClientSteamAuthorize += _authorizeHandler;

InitializeConnectClientHook();
Expand Down Expand Up @@ -924,15 +934,26 @@ private void RespawnTheDead()
// one loop over the weapons they already have.
private int _occupancyTicks;

// Every few seconds, not every one: the panel only needs to know somebody
// is here, and the reaper's clocks are measured in minutes.
// Set by the connect and disconnect hooks, cleared by the send.
//
// The hooks flag rather than send because at OnClientDisconnected the
// leaving player is STILL in Core.PlayerManager.GetAllPlayers() -- a
// snapshot taken there would report them as present, which is the exact
// staleness this exists to remove. Waiting for the next tick lets the
// engine drop them first, and folds a burst of joins into one post.
private bool _occupancyDirty;

// On the next tick after somebody comes or goes; otherwise a slow
// reconciler, because the snapshot is idempotent and the reaper's clocks
// are measured in minutes.
private void ReportOccupancy()
{
if (++_occupancyTicks < OccupancySeconds)
if (!_occupancyDirty && ++_occupancyTicks < OccupancySeconds)
{
return;
}

_occupancyDirty = false;
_occupancyTicks = 0;

var present = new List<ulong>();
Expand Down Expand Up @@ -1211,7 +1232,7 @@ private void OnSessionRefreshed(PracticeSessionData session)
// than a game mode cfg: a practice server may be a third-party dedicated
// box that no mode was ever selected for, and without this it sits in
// warmup with no money and no utility.
private const int OccupancySeconds = 15;
private const int OccupancySeconds = 60;
private const int WarmupRetrySeconds = 3;

private const float CfgReapplySeconds = 3f;
Expand Down
Loading