feat(hs): disable wings in-event + periodic HUD-state packet/broadcast
- HeykelSavasiContext.IsItemAllowedToEquip disallows wings/capes during Playing only (so entry is never rejected for wearing wings); auto-unequips any equipped wing/cape into a free inventory slot in OnGameStartAsync. - New S2C packet HeykelSavasiHudState (C1, code FB, length 11) carrying phase, team, counts, statue progress and remaining seconds, plus its view plugin/interface, broadcast once per second across registration/prep/ battle and one-shot on join/statue-break/game-end.
This commit is contained in:
@@ -6317,5 +6317,45 @@ public static class ConnectionExtensions
|
||||
return packet.Header.Length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(WritePacket).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a <see cref="HeykelSavasiHudState" /> to this connection.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection.</param>
|
||||
/// <param name="phase">0 = registration open, 1 = preparation countdown, 2 = battle, 3 = ended.</param>
|
||||
/// <param name="myTeam">0 = none, 1 = red, 2 = blue.</param>
|
||||
/// <param name="redCount">The red count.</param>
|
||||
/// <param name="blueCount">The blue count.</param>
|
||||
/// <param name="redProgress">The number (0-7) of statues the red team has destroyed of the blue team's line.</param>
|
||||
/// <param name="blueProgress">The number (0-7) of statues the blue team has destroyed of the red team's line.</param>
|
||||
/// <param name="remainingSeconds">The number of seconds left in the current phase.</param>
|
||||
/// <remarks>
|
||||
/// Is sent by the server when: Periodically (about once per second) while the Heykel Savasi (Statue War) event is open for registration, in its pre-battle countdown, or being played.
|
||||
/// Causes reaction on client side: The client updates its Heykel Savasi HUD panel (team counts, statue progress, and remaining time).
|
||||
/// </remarks>
|
||||
public static async ValueTask SendHeykelSavasiHudStateAsync(this IConnection? connection, byte @phase, byte @myTeam, byte @redCount, byte @blueCount, byte @redProgress, byte @blueProgress, ushort @remainingSeconds)
|
||||
{
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int WritePacket()
|
||||
{
|
||||
var length = HeykelSavasiHudStateRef.Length;
|
||||
var packet = new HeykelSavasiHudStateRef(connection.Output.GetSpan(length)[..length]);
|
||||
packet.Phase = @phase;
|
||||
packet.MyTeam = @myTeam;
|
||||
packet.RedCount = @redCount;
|
||||
packet.BlueCount = @blueCount;
|
||||
packet.RedProgress = @redProgress;
|
||||
packet.BlueProgress = @blueProgress;
|
||||
packet.RemainingSeconds = @remainingSeconds;
|
||||
|
||||
return packet.Header.Length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(WritePacket).ConfigureAwait(false);
|
||||
}}
|
||||
@@ -30759,6 +30759,139 @@ public readonly struct HeykelSavasiOpenTeamPanel
|
||||
/// <returns>The packet as byte span.</returns>
|
||||
public static implicit operator Memory<byte>(HeykelSavasiOpenTeamPanel packet) => packet._data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is sent by the server when: Periodically (about once per second) while the Heykel Savasi (Statue War) event is open for registration, in its pre-battle countdown, or being played.
|
||||
/// Causes reaction on client side: The client updates its Heykel Savasi HUD panel (team counts, statue progress, and remaining time).
|
||||
/// </summary>
|
||||
public readonly struct HeykelSavasiHudState
|
||||
{
|
||||
private readonly Memory<byte> _data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeykelSavasiHudState"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="data">The underlying data.</param>
|
||||
public HeykelSavasiHudState(Memory<byte> data)
|
||||
: this(data, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeykelSavasiHudState"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="data">The underlying data.</param>
|
||||
/// <param name="initialize">If set to <c>true</c>, the header data is automatically initialized and written to the underlying span.</param>
|
||||
private HeykelSavasiHudState(Memory<byte> data, bool initialize)
|
||||
{
|
||||
this._data = data;
|
||||
if (initialize)
|
||||
{
|
||||
var header = this.Header;
|
||||
header.Type = HeaderType;
|
||||
header.Code = Code;
|
||||
header.Length = (byte)Math.Min(data.Length, Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header type of this data packet.
|
||||
/// </summary>
|
||||
public static byte HeaderType => 0xC1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation code of this data packet.
|
||||
/// </summary>
|
||||
public static byte Code => 0xFB;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the initial length of this data packet. When the size is dynamic, this value may be bigger than actually needed.
|
||||
/// </summary>
|
||||
public static int Length => 11;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header of this packet.
|
||||
/// </summary>
|
||||
public C1Header Header => new (this._data);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 0 = registration open, 1 = preparation countdown, 2 = battle, 3 = ended.
|
||||
/// </summary>
|
||||
public byte Phase
|
||||
{
|
||||
get => this._data.Span[3];
|
||||
set => this._data.Span[3] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 0 = none, 1 = red, 2 = blue.
|
||||
/// </summary>
|
||||
public byte MyTeam
|
||||
{
|
||||
get => this._data.Span[4];
|
||||
set => this._data.Span[4] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the red count.
|
||||
/// </summary>
|
||||
public byte RedCount
|
||||
{
|
||||
get => this._data.Span[5];
|
||||
set => this._data.Span[5] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blue count.
|
||||
/// </summary>
|
||||
public byte BlueCount
|
||||
{
|
||||
get => this._data.Span[6];
|
||||
set => this._data.Span[6] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number (0-7) of statues the red team has destroyed of the blue team's line.
|
||||
/// </summary>
|
||||
public byte RedProgress
|
||||
{
|
||||
get => this._data.Span[7];
|
||||
set => this._data.Span[7] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number (0-7) of statues the blue team has destroyed of the red team's line.
|
||||
/// </summary>
|
||||
public byte BlueProgress
|
||||
{
|
||||
get => this._data.Span[8];
|
||||
set => this._data.Span[8] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds left in the current phase.
|
||||
/// </summary>
|
||||
public ushort RemainingSeconds
|
||||
{
|
||||
get => ReadUInt16BigEndian(this._data.Span[9..]);
|
||||
set => WriteUInt16BigEndian(this._data.Span[9..], value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from a Memory of bytes to a <see cref="HeykelSavasiHudState"/>.
|
||||
/// </summary>
|
||||
/// <param name="packet">The packet as span.</param>
|
||||
/// <returns>The packet as struct.</returns>
|
||||
public static implicit operator HeykelSavasiHudState(Memory<byte> packet) => new (packet, false);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from <see cref="HeykelSavasiHudState"/> to a Memory of bytes.
|
||||
/// </summary>
|
||||
/// <param name="packet">The packet as struct.</param>
|
||||
/// <returns>The packet as byte span.</returns>
|
||||
public static implicit operator Memory<byte>(HeykelSavasiHudState packet) => packet._data;
|
||||
}
|
||||
/// <summary>
|
||||
/// Defines the role of a guild member.
|
||||
/// </summary>
|
||||
|
||||
@@ -11088,6 +11088,57 @@
|
||||
</Field>
|
||||
</Fields>
|
||||
</Packet>
|
||||
<Packet>
|
||||
<HeaderType>C1Header</HeaderType>
|
||||
<Code>FB</Code>
|
||||
<Name>HeykelSavasiHudState</Name>
|
||||
<Length>11</Length>
|
||||
<Direction>ServerToClient</Direction>
|
||||
<SentWhen>Periodically (about once per second) while the Heykel Savasi (Statue War) event is open for registration, in its pre-battle countdown, or being played.</SentWhen>
|
||||
<CausedReaction>The client updates its Heykel Savasi HUD panel (team counts, statue progress, and remaining time).</CausedReaction>
|
||||
<Fields>
|
||||
<Field>
|
||||
<Index>3</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>Phase</Name>
|
||||
<Description>0 = registration open, 1 = preparation countdown, 2 = battle, 3 = ended.</Description>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>4</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>MyTeam</Name>
|
||||
<Description>0 = none, 1 = red, 2 = blue.</Description>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>5</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>RedCount</Name>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>6</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>BlueCount</Name>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>7</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>RedProgress</Name>
|
||||
<Description>The number (0-7) of statues the red team has destroyed of the blue team's line.</Description>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>8</Index>
|
||||
<Type>Byte</Type>
|
||||
<Name>BlueProgress</Name>
|
||||
<Description>The number (0-7) of statues the blue team has destroyed of the red team's line.</Description>
|
||||
</Field>
|
||||
<Field>
|
||||
<Index>9</Index>
|
||||
<Type>ShortBigEndian</Type>
|
||||
<Name>RemainingSeconds</Name>
|
||||
<Description>The number of seconds left in the current phase.</Description>
|
||||
</Field>
|
||||
</Fields>
|
||||
</Packet>
|
||||
</Packets>
|
||||
<Enums>
|
||||
<Enum>
|
||||
|
||||
@@ -29122,3 +29122,136 @@ public readonly ref struct HeykelSavasiOpenTeamPanelRef
|
||||
/// <returns>The packet as byte span.</returns>
|
||||
public static implicit operator Span<byte>(HeykelSavasiOpenTeamPanelRef packet) => packet._data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is sent by the server when: Periodically (about once per second) while the Heykel Savasi (Statue War) event is open for registration, in its pre-battle countdown, or being played.
|
||||
/// Causes reaction on client side: The client updates its Heykel Savasi HUD panel (team counts, statue progress, and remaining time).
|
||||
/// </summary>
|
||||
public readonly ref struct HeykelSavasiHudStateRef
|
||||
{
|
||||
private readonly Span<byte> _data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeykelSavasiHudStateRef"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="data">The underlying data.</param>
|
||||
public HeykelSavasiHudStateRef(Span<byte> data)
|
||||
: this(data, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeykelSavasiHudStateRef"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="data">The underlying data.</param>
|
||||
/// <param name="initialize">If set to <c>true</c>, the header data is automatically initialized and written to the underlying span.</param>
|
||||
private HeykelSavasiHudStateRef(Span<byte> data, bool initialize)
|
||||
{
|
||||
this._data = data;
|
||||
if (initialize)
|
||||
{
|
||||
var header = this.Header;
|
||||
header.Type = HeaderType;
|
||||
header.Code = Code;
|
||||
header.Length = (byte)Math.Min(data.Length, Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header type of this data packet.
|
||||
/// </summary>
|
||||
public static byte HeaderType => 0xC1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation code of this data packet.
|
||||
/// </summary>
|
||||
public static byte Code => 0xFB;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the initial length of this data packet. When the size is dynamic, this value may be bigger than actually needed.
|
||||
/// </summary>
|
||||
public static int Length => 11;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header of this packet.
|
||||
/// </summary>
|
||||
public C1HeaderRef Header => new (this._data);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 0 = registration open, 1 = preparation countdown, 2 = battle, 3 = ended.
|
||||
/// </summary>
|
||||
public byte Phase
|
||||
{
|
||||
get => this._data[3];
|
||||
set => this._data[3] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 0 = none, 1 = red, 2 = blue.
|
||||
/// </summary>
|
||||
public byte MyTeam
|
||||
{
|
||||
get => this._data[4];
|
||||
set => this._data[4] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the red count.
|
||||
/// </summary>
|
||||
public byte RedCount
|
||||
{
|
||||
get => this._data[5];
|
||||
set => this._data[5] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blue count.
|
||||
/// </summary>
|
||||
public byte BlueCount
|
||||
{
|
||||
get => this._data[6];
|
||||
set => this._data[6] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number (0-7) of statues the red team has destroyed of the blue team's line.
|
||||
/// </summary>
|
||||
public byte RedProgress
|
||||
{
|
||||
get => this._data[7];
|
||||
set => this._data[7] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number (0-7) of statues the blue team has destroyed of the red team's line.
|
||||
/// </summary>
|
||||
public byte BlueProgress
|
||||
{
|
||||
get => this._data[8];
|
||||
set => this._data[8] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds left in the current phase.
|
||||
/// </summary>
|
||||
public ushort RemainingSeconds
|
||||
{
|
||||
get => ReadUInt16BigEndian(this._data[9..]);
|
||||
set => WriteUInt16BigEndian(this._data[9..], value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from a Span of bytes to a <see cref="HeykelSavasiHudState"/>.
|
||||
/// </summary>
|
||||
/// <param name="packet">The packet as span.</param>
|
||||
/// <returns>The packet as struct.</returns>
|
||||
public static implicit operator HeykelSavasiHudStateRef(Span<byte> packet) => new (packet, false);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from <see cref="HeykelSavasiHudState"/> to a Span of bytes.
|
||||
/// </summary>
|
||||
/// <param name="packet">The packet as struct.</param>
|
||||
/// <returns>The packet as byte span.</returns>
|
||||
public static implicit operator Span<byte>(HeykelSavasiHudStateRef packet) => packet._data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user