Skip to content

Commit 2ac78fe

Browse files
add non ready player sanitization for player variables
1 parent 89035c9 commit 2ac78fe

7 files changed

Lines changed: 76 additions & 14 deletions

File tree

Code/VariableSystem/Variables/PlayerVariable.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,39 @@ namespace SER.Code.VariableSystem.Variables;
66

77
public class PlayerVariable(string name, PlayerValue value) : Variable<PlayerValue>
88
{
9+
private PlayerValue _value = value;
10+
911
public override string Name => name;
1012
public override string FriendlyName => "player variable";
11-
public override PlayerValue Value => value;
13+
public override PlayerValue Value
14+
{
15+
get
16+
{
17+
var sanitizedValue = RetainReadyPlayers(_value);
18+
if (!ReferenceEquals(sanitizedValue, _value))
19+
{
20+
_value = sanitizedValue;
21+
}
22+
23+
return _value;
24+
}
25+
}
26+
1227
public Player[] Players => Value.Players;
28+
29+
protected static PlayerValue RetainReadyPlayers(PlayerValue playerValue)
30+
{
31+
var players = playerValue.Players;
32+
var readyPlayers = Player.ReadyList.ToArray();
33+
var retainedPlayers = players
34+
.Where(player => readyPlayers.Any(readyPlayer => ReferenceEquals(readyPlayer, player)))
35+
.ToArray();
36+
37+
return retainedPlayers.Length == players.Length
38+
? playerValue
39+
: new PlayerValue(retainedPlayers);
40+
}
1341

1442
[UsedImplicitly]
1543
public PlayerVariable() : this("temp", null!) {}
16-
}
44+
}

Code/VariableSystem/Variables/PredefinedPlayerVariable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ namespace SER.Code.VariableSystem.Variables;
66
public class PredefinedPlayerVariable(string name, Func<List<Player>> value, string category)
77
: PlayerVariable(name, null!)
88
{
9-
public override PlayerValue Value => new(value());
9+
public override PlayerValue Value => RetainReadyPlayers(new(value()));
1010
public string Category => category;
1111

1212
[UsedImplicitly]
1313
public PredefinedPlayerVariable() : this("temp", null!, "temp") {}
14-
}
14+
}

docs/language/variables-and-properties.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ Predefined variables such as `@all`, `@alivePlayers`, and `@scpPlayers` are
2626
provided by SER. Event and command flags can inject additional local variables.
2727
Run `serhelp variables` for the current predefined list.
2828

29+
## Player variable readiness
30+
31+
Every read of a player variable checks its entries against LabAPI's
32+
`Player.ReadyList`. Entries which are not in that list are silently and
33+
permanently removed before the value reaches a method, property, loop, or
34+
argument. Ready dummy players remain; unauthenticated players, the host,
35+
non-ready NPCs, and disconnected wrappers do not. An empty player value is
36+
valid.
37+
38+
The check compares the current player wrapper itself, not only an account or
39+
round ID. If somebody disconnects and reconnects, the new player is not restored
40+
to an older stored variable. Pruning also does not add later joiners or update a
41+
stored selection after role, team, or zone changes. Reassign a predefined player
42+
group when a fresh snapshot is required. Predefined groups are generated from
43+
the current ready-player list each time they are read.
44+
2945
## Text interpolation
3046

3147
Inside quoted text, wrap a variable or expression in braces:

docs/tutorial/decisions-and-time.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ wait_until {AmountOf @scpPlayers} is 0
8080
Broadcast @all 5s "No living SCPs remain."
8181
```
8282

83-
After a long wait, remember that players may disconnect and item or room
84-
references may become invalid. Recheck important state before using it.
83+
After a long wait, remember that players may disconnect and item, room, or any
84+
other references may become invalid.
8585

8686
## Try one change
8787

docs/tutorial/player-targets.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,13 @@ Heal @all 20
5656
```
5757

5858
Combine player groups with `Join`, `Except`, and `Intersect`. `Intersect` keeps
59-
only players that appear in every supplied group while preserving the order of
60-
the first group:
59+
only players that appear in every supplied argument:
6160

6261
```ser
63-
@connectedParticipants = Intersect @eventParticipants @all
62+
@classDInHcz = Intersect @heavyContainmentPlayers @classDPlayers
6463
```
6564

66-
This is useful when a stored group may contain players who have since
67-
disconnected. Additional players in `@all` are not added because they were not
68-
in `@eventParticipants`.
65+
In this case, `@classDInHcz` players are both Class-D and in heavy containment.
6966

7067
Be careful when reading a property such as a name or health value. Those
7168
questions normally make sense for exactly one player, not a group. We will
@@ -80,4 +77,10 @@ remove the `Damage` line, and change the message.
8077
You now have methods, triggers, and useful targets—the core of many SER
8178
scripts. Variables become worthwhile only when you need to remember a result.
8279

80+
## What if a player leaves?
81+
82+
They get removed from the variable. If your `@classDInHcz` variable had three
83+
players and one leaves, the variable will contain two players after that.
84+
Keep that in mind!
85+
8386
Next: [remember values and inspect players](variables-and-properties.md).

docs/tutorial/variables-and-properties.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,10 @@ first or use a value which is guaranteed to identify one player:
9090
$name = @target -> name
9191
```
9292

93-
There is still one problem: `@alivePlayers` might be empty. The next lesson
94-
makes this safe by adding a decision before the property is read.
93+
There is still one problem: `@alivePlayers` might be empty, and a previously
94+
selected player might disconnect before the property is read. Automatic cleanup
95+
prevents a stale player from being used, but the next lesson still checks that
96+
exactly one player remains before reading the property.
9597

9698
The reference has the full rules for [variable families and properties](../language/variables-and-properties.md).
9799
It saves the unusual details about visibility and lifetime for when you need

language_specification.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ but do not create separate access namespaces.
4444
and removed when the function call finishes. They are not stored in a separate
4545
lexical environment.
4646

47+
### Player Variable Readiness
48+
49+
Every player-variable read is sanitized against LabAPI's `Player.ReadyList`
50+
before its value reaches a method, property, loop, or argument. Players which
51+
are no longer ready are silently and permanently removed, and the variable may
52+
become empty. Ready dummy players remain; unauthenticated players, the host,
53+
non-ready NPCs, and disconnected wrappers are removed.
54+
55+
Stored player variables are snapshots: cleanup does not add later joiners or
56+
re-evaluate role, team, or zone membership. A reconnect creates a new player
57+
wrapper and does not restore the old entry. Predefined player groups are rebuilt
58+
from the current ready-player list whenever they are read.
59+
4760
---
4861

4962
## 2. Text, Math, & Syntax

0 commit comments

Comments
 (0)