Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: team change message isn't broadcast on spawn#3237

Open
lost-werewolf wants to merge 6 commits intoPryaxis:general-develfrom
lost-werewolf:team-handling-fix
Open

fix: team change message isn't broadcast on spawn#3237
lost-werewolf wants to merge 6 commits intoPryaxis:general-develfrom
lost-werewolf:team-handling-fix

Conversation

@lost-werewolf
Copy link
Contributor

  • Fix player spawn bypassing pvp with no team setting
  • Prevent malicious client from fast-switching team with spawn packet
  • Split up a check in HandlePlayerTeam to accomodate for team changes

As a side effect of the change to handling of team in the PlayerSpawn packet, it now must handle the vanilla logic on its own, to prevent malicious clients from changing their team too fast, and to also prevent this from bypassing the "pvpwithnoteam" pvp mode.

- fix player spawn bypassing pvp with no team
- prevent malicious client from fast-switching with spawn packet
- split up a check in HandlePlayerTeam to clarify debug message
@greptile-apps
Copy link

greptile-apps bot commented Mar 2, 2026

Greptile Summary

This PR fixes a security vulnerability where malicious clients could bypass the pvpwithnoteam pvp mode by sending team changes through spawn packets, and prevents fast team-switching exploits.

Key Changes:

  • HandlePlayerSpawn now validates team assignments and enforces pvpwithnoteam mode before processing spawns
  • Added InitialTeamChangePending mechanism to allow legitimate initial team selection for non-SSC players while preventing abuse
  • Split up HandlePlayerTeam and HandleTogglePvp checks to properly handle pvp mode restrictions separately from fast-switch prevention
  • OnSecondUpdate continuously enforces pvp mode settings (hostile flag and team) every second as a safety net
  • Non-SSC spawn handling now duplicates vanilla logic to maintain control over team assignments and broadcasts

Issues to Address:

  • Edge case bug exists where teamCorrectNeeded resets team to player's current non-zero team instead of enforcing team 0 for pvpwithnoteam mode (lines 2978-2979 and 3007-3008). This occurs when the fast-switch check triggers while pvp mode is pvpwithnoteam and player has a non-zero team. Suggested fix already provided in previous comments.

Confidence Score: 3/5

  • Safe to merge after fixing edge case bugs in team correction logic
  • Core security fix is sound, but edge case bugs at lines 2978-2979 and 3007-3008 could allow pvpwithnoteam bypass when fast-switch check triggers while player has non-zero team. Fix is straightforward and already suggested in previous comments.
  • TShockAPI/GetDataHandlers.cs requires fixes at lines 2978-2979 and 3007-3008 before merge

Important Files Changed

Filename Overview
TShockAPI/GetDataHandlers.cs Added team validation in HandlePlayerSpawn to prevent pvpwithnoteam bypass and fast-switching. Edge case exists where teamCorrectNeeded resets to non-zero team in pvpwithnoteam mode (lines 2978, 3007).
TShockAPI/TSPlayer.cs Added InitialTeamChangePending field to track pending initial team changes for non-SSC players. Clean addition with no issues.
TShockAPI/TShock.cs Added enforcement of pvp mode in OnSecondUpdate to continuously validate hostile flags and team assignments. Properly clears stale InitialTeamChangePending flags.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[PlayerSpawn packet received] --> B{pvpMode == pvpwithnoteam<br/>AND team != 0?}
    B -->|Yes| C[Force team = 0<br/>Set teamCorrectNeeded]
    B -->|No| D{Team change attempt<br/>AND < 5 sec since last?}
    C --> D
    D -->|Yes| E[Set teamCorrectNeeded]
    D -->|No| F{First spawn?}
    E --> F
    F -->|Yes| G{Non-SSC AND<br/>team != 0 AND<br/>!teamCorrectNeeded?}
    F -->|No| H{SSC enabled?}
    G -->|Yes| I[Set InitialTeamChangePending<br/>LastPvPTeamChange = Now]
    G -->|No| H
    I --> H
    H -->|Yes| J[SSC spawn path]
    H -->|No| K[Non-SSC spawn path]
    J --> L{team != TPlayer.team?}
    K --> M{teamCorrectNeeded?}
    L -->|Yes| N{teamCorrectNeeded?}
    L -->|No| R[Spawn player]
    N -->|Yes| O[BUG: team = TPlayer.team<br/>should enforce 0 for pvpwithnoteam]
    N -->|No| P[Update LastPvPTeamChange]
    O --> Q[TPlayer.team = team<br/>Broadcast spawn]
    P --> Q
    Q --> R
    M -->|Yes| O
    M -->|No| S{!InitialTeamChangePending<br/>AND team changed?}
    S -->|Yes| T[Update LastPvPTeamChange]
    S -->|No| U[TPlayer.team = team<br/>Broadcast spawn]
    T --> U
    R --> V{teamCorrectNeeded?}
    U --> V
    V -->|Yes| W[Send corrected team<br/>back to client]
    V -->|No| X[Done]
    W --> X
    
    style O fill:#ffcccc
    style C fill:#ccffcc
    style E fill:#ffffcc
Loading

Last reviewed commit: 1736112

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +2975 to +2978
if (teamCorrectNeeded)
{
team = (byte)args.Player.Team;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edge case bug: if pvpMode is "pvpwithnoteam" but args.Player.Team is somehow non-zero (e.g., admin changes pvpMode while players are online with teams), this will reset team to the player's current team instead of enforcing team 0. Consider preserving the forced team 0:

if (teamCorrectNeeded)
{
	if (pvpMode == "pvpwithnoteam")
		team = 0;
	else
		team = (byte)args.Player.Team;
}

Comment on lines +3003 to +3004
if (teamCorrectNeeded) // correction of malicious client's team change necessary
team = (byte)args.Player.Team;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same edge case as SSC path: if pvpMode is "pvpwithnoteam" and args.Player.Team is non-zero, this resets to the player's current team instead of enforcing 0. Apply the same fix as suggested for the SSC path.

lost-werewolf and others added 2 commits March 1, 2026 19:26
fix typo

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@lost-werewolf
Copy link
Contributor Author

@greptile review?

- correct player hostile flag & team in OnSecondUpdate
- use separate message in HandleTogglePvp for pvp mode rejection
@lost-werewolf
Copy link
Contributor Author

The edge case should be resolved by 55a819d, where I set the player's team to 0. Since it's already 0, there should be no edge case. Tested with a 3rd-party client using code execution to run code that sets my team and then spawns my player, I could not replicate anything.
To also fix another bug regarding PVP mode being switched while the server is running, and to prevent any more possible issues, TShock.OnSecondUpdate now checks it and appropriately sets the pvp status and team of the player.

@hakusaro
Copy link
Member

hakusaro commented Mar 2, 2026

@greptile review

@lost-werewolf
Copy link
Contributor Author

I don't really see the edge case here. Since the player's team is always set to 0 when using pvpwithnoteam as the pvp mode now, it shouldn't ever be able to not be 0 when team correction is needed. Especially since now, every second, the player's team is checked and corrected to 0 if it's not.

@hakusaro
Copy link
Member

hakusaro commented Mar 2, 2026

Please do not use conventional commits.

@lost-werewolf
Copy link
Contributor Author

My apologies. if you can clarify what you mean by that, I will avoid it in the future.

@lost-werewolf
Copy link
Contributor Author

Better off closing and redoing this PR to squash everything into one commit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants