feat: add Teams packet support (parsing, state tracking, /teams command, bot API)

Agent-Logs-Url: https://github.com/MCCTeam/Minecraft-Console-Client/sessions/72ea891e-ba62-4dfc-bbba-f197cd1d0404

Co-authored-by: milutinke <441903+milutinke@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-30 23:41:11 +00:00
parent cf65c6f241
commit fdbffcc5bb
8 changed files with 397 additions and 1 deletions

View file

@ -3035,6 +3035,84 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnUpdateScore(entityName, action3, objectiveName3, objectiveDisplayName3, objectiveValue2,
numberFormat2);
break;
case PacketTypesIn.Teams:
// Wire format per version:
// All versions: name (string), method (byte)
// method 0/2: displayName (component), options (byte),
// nameTagVisibility, collisionRule, color (VarInt),
// prefix (component), suffix (component)
// method 0/3/4: players list (VarInt count + strings)
// 1.21.9+ (protocol 773): nameTagVisibility and collisionRule are
// VarInt-encoded enum IDs instead of UTF strings.
var teamName = dataTypes.ReadNextString(packetData);
var teamMethod = dataTypes.ReadNextByte(packetData);
var teamDisplayName = string.Empty;
byte teamFriendlyFlags = 0;
var teamNameTagVisibility = string.Empty;
var teamCollisionRule = string.Empty;
var teamColor = -1;
var teamPrefix = string.Empty;
var teamSuffix = string.Empty;
if (teamMethod is 0 or 2)
{
teamDisplayName = dataTypes.ReadNextChat(packetData);
teamFriendlyFlags = dataTypes.ReadNextByte(packetData);
// nameTagVisibility
if (protocolVersion >= MC_1_21_9_Version)
{
// STREAM_CODEC: 0=always, 1=never, 2=hideForOtherTeams, 3=hideForOwnTeam
teamNameTagVisibility = dataTypes.ReadNextVarInt(packetData) switch
{
0 => "always",
1 => "never",
2 => "hideForOtherTeams",
3 => "hideForOwnTeam",
_ => "always"
};
}
else
{
teamNameTagVisibility = dataTypes.ReadNextString(packetData);
}
// collisionRule
if (protocolVersion >= MC_1_21_9_Version)
{
// STREAM_CODEC: 0=always, 1=never, 2=pushOtherTeams, 3=pushOwnTeam
teamCollisionRule = dataTypes.ReadNextVarInt(packetData) switch
{
0 => "always",
1 => "never",
2 => "pushOtherTeams",
3 => "pushOwnTeam",
_ => "always"
};
}
else
{
teamCollisionRule = dataTypes.ReadNextString(packetData);
}
teamColor = dataTypes.ReadNextVarInt(packetData);
teamPrefix = dataTypes.ReadNextChat(packetData);
teamSuffix = dataTypes.ReadNextChat(packetData);
}
var teamPlayers = new List<string>();
if (teamMethod is 0 or 3 or 4)
{
int playerCount = dataTypes.ReadNextVarInt(packetData);
for (int i = 0; i < playerCount; i++)
teamPlayers.Add(dataTypes.ReadNextString(packetData));
}
handler.OnTeam(teamName, teamMethod, teamDisplayName, teamFriendlyFlags,
teamNameTagVisibility, teamCollisionRule, teamColor,
teamPrefix, teamSuffix, teamPlayers);
break;
case PacketTypesIn.BlockChangedAck:
handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData));
break;

View file

@ -489,6 +489,23 @@ namespace MinecraftClient.Protocol
/// <param name="numberFormat">Number format: 0 - blank, 1 - styled, 2 - fixed</param>
void OnUpdateScore(string entityName, int action, string objectiveName, string objectiveDisplayName, int objectiveValue, int numberFormat);
/// <summary>
/// Called when a Teams packet is received from the server.
/// </summary>
/// <param name="teamName">Internal team name (up to 16 chars)</param>
/// <param name="method">0=create, 1=remove, 2=update, 3=add players, 4=remove players</param>
/// <param name="displayName">Display name (formatted). Present when method is 0 or 2.</param>
/// <param name="friendlyFlags">Bit 0=allowFriendlyFire, bit 1=seeFriendlyInvisibles. Present when method is 0 or 2.</param>
/// <param name="nameTagVisibility">Nametag visibility rule string. Present when method is 0 or 2.</param>
/// <param name="collisionRule">Collision rule string. Present when method is 0 or 2.</param>
/// <param name="color">ChatFormatting color value (-1=none). Present when method is 0 or 2.</param>
/// <param name="prefix">Member name prefix (formatted). Present when method is 0 or 2.</param>
/// <param name="suffix">Member name suffix (formatted). Present when method is 0 or 2.</param>
/// <param name="players">Player/entity names. Present when method is 0, 3, or 4.</param>
void OnTeam(string teamName, byte method, string displayName, byte friendlyFlags,
string nameTagVisibility, string collisionRule, int color,
string prefix, string suffix, List<string> players);
/// <summary>
/// Called when the client received the Tab Header and Footer
/// </summary>