Skip to content
Open
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
6 changes: 5 additions & 1 deletion SSMP/Api/Client/Networking/ClientAddonNetworkReceiver.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using SSMP.Api.Networking;
using SSMP.Collection;
using SSMP.Networking.Packet;
using SSMP.Networking.Packet.Connection;
using SSMP.Networking.Packet.Update;

namespace SSMP.Api.Client.Networking;
Expand Down Expand Up @@ -55,10 +57,12 @@ public void CommitPacketHandlers() {
}

// Assign the addon packet info in the dictionary of the client update packet
ClientUpdatePacket.AddonPacketInfoDict[ClientAddon.Id.Value] = new AddonPacketInfo(
var addonPacketInfo = new AddonPacketInfo(
PacketInstantiator!,
PacketIdSize
);
ClientUpdatePacket.AddonPacketInfoDict[ClientAddon.Id.Value] = addonPacketInfo;
ClientConnectionPacket.AddonPacketInfoDict[ClientAddon.Id.Value] = addonPacketInfo;

foreach (var idHandlerPair in PacketHandlers) {
PacketManager.RegisterClientAddonUpdatePacketHandler(
Expand Down
35 changes: 33 additions & 2 deletions SSMP/Api/Client/Networking/ClientAddonNetworkSender.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using SSMP.Api.Networking;
using SSMP.Networking.Client;
using SSMP.Networking.Packet;

Expand Down Expand Up @@ -61,7 +62,8 @@ public void SendSingleData(TPacketId packetId, IPacketData packetData) {

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
InvalidPacketIdMsg);
InvalidPacketIdMsg
);
}

if (!_clientAddon.Id.HasValue) {
Expand All @@ -87,7 +89,8 @@ TPacketData packetData

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
InvalidPacketIdMsg);
InvalidPacketIdMsg
);
}

if (!_clientAddon.Id.HasValue) {
Expand All @@ -101,4 +104,32 @@ TPacketData packetData
packetData
);
}

/// <inheritdoc/>
public void SendChunkData(TPacketId packetId, IPacketData packetData) {
var (idValue, addonId) = ValidateCommon(packetId);
_netClient.UpdateManager.SendChunkPacket(
ChunkAddonPacketBuilder.BuildServerBound(idValue, addonId, packetData)
);
}

/// <summary>
/// Validates the common client-side preconditions required before sending chunk data.
/// </summary>
/// <param name="packetId">The addon packet identifier to validate and resolve.</param>
/// <returns>
/// The resolved packet ID byte value and the current addon ID.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if the client is not connected, the addon has no assigned ID, or the packet ID is invalid.
/// </exception>
private (byte idValue, byte addonId) ValidateCommon(TPacketId packetId) {
if (!_netClient.IsConnected) {
throw new InvalidOperationException(NotConnectedMsg);
}

return !_clientAddon.Id.HasValue
? throw new InvalidOperationException(NoClientAddonId)
: (ResolvePacketId(packetId, InvalidPacketIdMsg), _clientAddon.Id.Value);
}
}
8 changes: 8 additions & 0 deletions SSMP/Api/Client/Networking/IClientAddonNetworkSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@ void SendCollectionData<TPacketData>(
TPacketId packetId,
TPacketData packetData
) where TPacketData : IPacketData, new();

/// <summary>
/// Send a single instance of IPacketData over the network through the chunk system with the given packet ID.
/// This should be used for large packets (exceeding 64 KiB).
/// </summary>
/// <param name="packetId">The packet ID.</param>
/// <param name="packetData">An instance of IPacketData to send.</param>
void SendChunkData(TPacketId packetId, IPacketData packetData);
}
2 changes: 1 addition & 1 deletion SSMP/Api/Client/Networking/INetClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace SSMP.Api.Client.Networking;
/// <summary>
/// The net client for all network-related interaction.
/// </summary>
public interface INetClient {
public interface INetClient : IDisposable {
/// <summary>
/// Whether the client is currently connected to a server.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using SSMP.Collection;

namespace SSMP.Api.Client.Networking;
namespace SSMP.Api.Networking;

/// <summary>
/// Static class for addon network transmitters.
Expand Down Expand Up @@ -37,9 +37,19 @@ internal abstract class AddonNetworkTransmitter<TPacketId> where TPacketId : Enu
/// <summary>
/// A lookup for packet IDs and corresponding raw byte values.
/// </summary>
protected readonly BiLookup<TPacketId, byte> PacketIdLookup;
protected readonly BiLookup<TPacketId, byte> PacketIdLookup =
AddonNetworkTransmitter.ConstructPacketIdLookup<TPacketId>();

protected AddonNetworkTransmitter() {
PacketIdLookup = AddonNetworkTransmitter.ConstructPacketIdLookup<TPacketId>();
/// <summary>
/// Resolve the given addon packet ID to its wire-format byte value.
/// </summary>
/// <param name="packetId">The enum packet ID to resolve.</param>
/// <param name="invalidPacketIdMessage">Exception message used when the packet ID is unknown.</param>
/// <returns>The byte value for the packet ID.</returns>
/// <exception cref="InvalidOperationException">Thrown when the packet ID is not part of the lookup.</exception>
protected byte ResolvePacketId(TPacketId packetId, string invalidPacketIdMessage) {
return !PacketIdLookup.TryGetValue(packetId, out var idValue)
? throw new InvalidOperationException(invalidPacketIdMessage)
: idValue;
}
}
29 changes: 29 additions & 0 deletions SSMP/Api/Server/Networking/IServerAddonNetworkSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,33 @@ void BroadcastCollectionData<TPacketData>(
TPacketId packetId,
TPacketData packetData
) where TPacketData : IPacketData, new();

/// <summary>
/// Send a single instance of IPacketData with the given packet ID over the network to the player
/// with the given ID through the chunk system.
/// This should be used for large packets (exceeding 64 KiB).
/// </summary>
/// <param name="packetId">The packet ID.</param>
/// <param name="packetData">An instance of IPacketData to send.</param>
/// <param name="playerId">The ID of the player.</param>
void SendChunkData(TPacketId packetId, IPacketData packetData, ushort playerId);

/// <summary>
/// Send a single instance of IPacketData with the given packet ID over the network to the players
/// with the given IDs through the chunk system.
/// This should be used for large packets (exceeding 64 KiB).
/// </summary>
/// <param name="packetId">The packet ID.</param>
/// <param name="packetData">An instance of IPacketData to send.</param>
/// <param name="playerIds">The IDs of the players.</param>
void SendChunkData(TPacketId packetId, IPacketData packetData, params ushort[] playerIds);

/// <summary>
/// Send a single instance of IPacketData with the given packet ID over the network to all connected
/// players through the chunk system.
/// This should be used for large packets (exceeding 64 KiB).
/// </summary>
/// <param name="packetId">The packet ID.</param>
/// <param name="packetData">An instance of IPacketData to send.</param>
void BroadcastChunkData(TPacketId packetId, IPacketData packetData);
}
20 changes: 7 additions & 13 deletions SSMP/Api/Server/Networking/ServerAddonNetworkReceiver.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using SSMP.Api.Client.Networking;
using SSMP.Api.Networking;
using SSMP.Networking.Packet;

namespace SSMP.Api.Server.Networking;
Expand Down Expand Up @@ -42,10 +42,7 @@ PacketManager packetManager

/// <inheritdoc/>
public void RegisterPacketHandler(TPacketId packetId, Action<ushort> handler) {
if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
InvalidPacketIdMsg);
}
var idValue = ResolvePacketId(packetId, InvalidPacketIdMsg);

if (!_serverAddon.Id.HasValue) {
throw new InvalidOperationException(NoAddonIdMsg);
Expand All @@ -61,10 +58,7 @@ public void RegisterPacketHandler(TPacketId packetId, Action<ushort> handler) {
/// <inheritdoc/>
public void RegisterPacketHandler<TPacketData>(TPacketId packetId,
GenericServerPacketHandler<TPacketData> handler) where TPacketData : IPacketData {
if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
InvalidPacketIdMsg);
}
var idValue = ResolvePacketId(packetId, InvalidPacketIdMsg);

if (!_serverAddon.Id.HasValue) {
throw new InvalidOperationException(NoAddonIdMsg);
Expand All @@ -79,10 +73,10 @@ public void RegisterPacketHandler<TPacketData>(TPacketId packetId,

/// <inheritdoc/>
public void DeregisterPacketHandler(TPacketId packetId) {
if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
"Given packet ID was not part of enum when creating this network receiver");
}
var idValue = ResolvePacketId(
packetId,
"Given packet ID was not part of enum when creating this network receiver"
);

if (!_serverAddon.Id.HasValue) {
throw new InvalidOperationException(NoAddonIdMsg);
Expand Down
100 changes: 81 additions & 19 deletions SSMP/Api/Server/Networking/ServerAddonNetworkSender.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using SSMP.Api.Client.Networking;
using SSMP.Api.Networking;
using SSMP.Networking.Packet;
using SSMP.Networking.Server;

Expand Down Expand Up @@ -62,7 +62,8 @@ public void SendSingleData(TPacketId packetId, IPacketData packetData, ushort pl

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
PacketIdInvalidExceptionMsg);
PacketIdInvalidExceptionMsg
);
}

var updateManager = _netServer.GetUpdateManagerForClient(playerId);
Expand Down Expand Up @@ -97,21 +98,23 @@ public void BroadcastSingleData(TPacketId packetId, IPacketData packetData) {

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
PacketIdInvalidExceptionMsg);
PacketIdInvalidExceptionMsg
);
}

if (!_serverAddon.Id.HasValue) {
throw new InvalidOperationException(NoAddonIdMsg);
}

_netServer.SetDataForAllClients(updateManager => {
updateManager?.SetAddonData(
_serverAddon.Id.Value,
idValue,
_packetIdSize,
packetData
);
});
updateManager?.SetAddonData(
_serverAddon.Id.Value,
idValue,
_packetIdSize,
packetData
);
}
);
}

/// <inheritdoc/>
Expand All @@ -126,7 +129,8 @@ ushort playerId

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
PacketIdInvalidExceptionMsg);
PacketIdInvalidExceptionMsg
);
}

var updateManager = _netServer.GetUpdateManagerForClient(playerId);
Expand Down Expand Up @@ -168,20 +172,78 @@ TPacketData packetData

if (!PacketIdLookup.TryGetValue(packetId, out var idValue)) {
throw new InvalidOperationException(
PacketIdInvalidExceptionMsg);
PacketIdInvalidExceptionMsg
);
}

if (!_serverAddon.Id.HasValue) {
throw new InvalidOperationException(NoAddonIdMsg);
}

_netServer.SetDataForAllClients(updateManager => {
updateManager?.SetAddonDataAsCollection(
_serverAddon.Id.Value,
idValue,
_packetIdSize,
packetData
);
});
updateManager?.SetAddonDataAsCollection(
_serverAddon.Id.Value,
idValue,
_packetIdSize,
packetData
);
}
);
}

/// <inheritdoc/>
public void SendChunkData(TPacketId packetId, IPacketData packetData, ushort playerId) {
var (idValue, addonId) = ValidateCommon(packetId);

var updateManager = _netServer.GetUpdateManagerForClient(playerId);
if (updateManager == null) {
throw new InvalidOperationException($"Player with ID '{playerId}' is not connected");
}

updateManager.SendChunkPacket(
ChunkAddonPacketBuilder.BuildClientBound(idValue, addonId, packetData)
);
}

/// <inheritdoc/>
public void SendChunkData(TPacketId packetId, IPacketData packetData, params ushort[] playerIds) {
var (idValue, addonId) = ValidateCommon(packetId);
var packet = ChunkAddonPacketBuilder.BuildClientBound(idValue, addonId, packetData);

foreach (var playerId in playerIds) {
var updateManager = _netServer.GetUpdateManagerForClient(playerId);
if (updateManager == null) {
throw new InvalidOperationException($"Player with ID '{playerId}' is not connected");
}

updateManager.SendChunkPacket(packet);
}
}

/// <inheritdoc/>
public void BroadcastChunkData(TPacketId packetId, IPacketData packetData) {
var (idValue, addonId) = ValidateCommon(packetId);
var packet = ChunkAddonPacketBuilder.BuildClientBound(idValue, addonId, packetData);
_netServer.SetDataForAllClients(updateManager => updateManager?.SendChunkPacket(packet));
}

/// <summary>
/// Validates the common server-side preconditions required before sending chunk data.
/// </summary>
/// <param name="packetId">The addon packet identifier to validate and resolve.</param>
/// <returns>
/// The resolved packet ID byte value and the current addon ID.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if the server is not started, the addon has no assigned ID, or the packet ID is invalid.
/// </exception>
private (byte idValue, byte addonId) ValidateCommon(TPacketId packetId) {
if (!_netServer.IsStarted) {
throw new InvalidOperationException(ServerNotStartedExceptionMsg);
}

return !_serverAddon.Id.HasValue
? throw new InvalidOperationException(NoAddonIdMsg)
: (ResolvePacketId(packetId, PacketIdInvalidExceptionMsg), _serverAddon.Id.Value);
}
}
Loading
Loading