Files
K-C-Multiplayer/Riptide/Exceptions.cs
2025-12-14 21:08:19 +01:00

198 lines
14 KiB
C#

// This file is provided under The MIT License as part of RiptideNetworking.
// Copyright (c) Tom Weiland
// For additional information please see the included LICENSE.md file or view it on GitHub:
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
using Riptide.Utils;
using System;
using System.Reflection;
namespace Riptide
{
/// <summary>The exception that is thrown when a <see cref="Message"/> does not contain enough unwritten bits to perform an operation.</summary>
public class InsufficientCapacityException : Exception
{
/// <summary>The message with insufficient remaining capacity.</summary>
public readonly Message RiptideMessage;
/// <summary>The name of the type which could not be added to the message.</summary>
public readonly string TypeName;
/// <summary>The number of available bits the type requires in order to be added successfully.</summary>
public readonly int RequiredBits;
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance.</summary>
public InsufficientCapacityException() { }
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance with a specified error message.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public InsufficientCapacityException(string message) : base(message) { }
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance with a specified error message and a reference to the inner exception that is the cause of this exception.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="inner">The exception that is the cause of the current exception. If <paramref name="inner"/> is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public InsufficientCapacityException(string message, Exception inner) : base(message, inner) { }
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance and constructs an error message from the given information.</summary>
/// <param name="message">The message with insufficient remaining capacity.</param>
/// <param name="reserveBits">The number of bits which were attempted to be reserved.</param>
public InsufficientCapacityException(Message message, int reserveBits) : base(GetErrorMessage(message, reserveBits))
{
RiptideMessage = message;
TypeName = "reservation";
RequiredBits = reserveBits;
}
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance and constructs an error message from the given information.</summary>
/// <param name="message">The message with insufficient remaining capacity.</param>
/// <param name="typeName">The name of the type which could not be added to the message.</param>
/// <param name="requiredBits">The number of available bits required for the type to be added successfully.</param>
public InsufficientCapacityException(Message message, string typeName, int requiredBits) : base(GetErrorMessage(message, typeName, requiredBits))
{
RiptideMessage = message;
TypeName = typeName;
RequiredBits = requiredBits;
}
/// <summary>Initializes a new <see cref="InsufficientCapacityException"/> instance and constructs an error message from the given information.</summary>
/// <param name="message">The message with insufficient remaining capacity.</param>
/// <param name="arrayLength">The length of the array which could not be added to the message.</param>
/// <param name="typeName">The name of the array's type.</param>
/// <param name="requiredBits">The number of available bits required for a single element of the array to be added successfully.</param>
public InsufficientCapacityException(Message message, int arrayLength, string typeName, int requiredBits) : base(GetErrorMessage(message, arrayLength, typeName, requiredBits))
{
RiptideMessage = message;
TypeName = $"{typeName}[]";
RequiredBits = requiredBits * arrayLength;
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(Message message, int reserveBits)
{
return $"Cannot reserve {reserveBits} {Helper.CorrectForm(reserveBits, "bit")} in a message with {message.UnwrittenBits} " +
$"{Helper.CorrectForm(message.UnwrittenBits, "bit")} of remaining capacity!";
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(Message message, string typeName, int requiredBits)
{
return $"Cannot add a value of type '{typeName}' (requires {requiredBits} {Helper.CorrectForm(requiredBits, "bit")}) to " +
$"a message with {message.UnwrittenBits} {Helper.CorrectForm(message.UnwrittenBits, "bit")} of remaining capacity!";
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(Message message, int arrayLength, string typeName, int requiredBits)
{
requiredBits *= arrayLength;
return $"Cannot add an array of type '{typeName}[]' with {arrayLength} {Helper.CorrectForm(arrayLength, "element")} (requires {requiredBits} {Helper.CorrectForm(requiredBits, "bit")}) " +
$"to a message with {message.UnwrittenBits} {Helper.CorrectForm(message.UnwrittenBits, "bit")} of remaining capacity!";
}
}
/// <summary>The exception that is thrown when a method with a <see cref="MessageHandlerAttribute"/> is not marked as <see langword="static"/>.</summary>
public class NonStaticHandlerException : Exception
{
/// <summary>The type containing the handler method.</summary>
public readonly Type DeclaringType;
/// <summary>The name of the handler method.</summary>
public readonly string HandlerMethodName;
/// <summary>Initializes a new <see cref="NonStaticHandlerException"/> instance.</summary>
public NonStaticHandlerException() { }
/// <summary>Initializes a new <see cref="NonStaticHandlerException"/> instance with a specified error message.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public NonStaticHandlerException(string message) : base(message) { }
/// <summary>Initializes a new <see cref="NonStaticHandlerException"/> instance with a specified error message and a reference to the inner exception that is the cause of this exception.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="inner">The exception that is the cause of the current exception. If <paramref name="inner"/> is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public NonStaticHandlerException(string message, Exception inner) : base(message, inner) { }
/// <summary>Initializes a new <see cref="NonStaticHandlerException"/> instance and constructs an error message from the given information.</summary>
/// <param name="declaringType">The type containing the handler method.</param>
/// <param name="handlerMethodName">The name of the handler method.</param>
public NonStaticHandlerException(Type declaringType, string handlerMethodName) : base(GetErrorMessage(declaringType, handlerMethodName))
{
DeclaringType = declaringType;
HandlerMethodName = handlerMethodName;
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(Type declaringType, string handlerMethodName)
{
return $"'{declaringType.Name}.{handlerMethodName}' is an instance method, but message handler methods must be static!";
}
}
/// <summary>The exception that is thrown when a method with a <see cref="MessageHandlerAttribute"/> does not have an acceptable message handler method signature (either <see cref="Server.MessageHandler"/> or <see cref="Client.MessageHandler"/>).</summary>
public class InvalidHandlerSignatureException : Exception
{
/// <summary>The type containing the handler method.</summary>
public readonly Type DeclaringType;
/// <summary>The name of the handler method.</summary>
public readonly string HandlerMethodName;
/// <summary>Initializes a new <see cref="InvalidHandlerSignatureException"/> instance.</summary>
public InvalidHandlerSignatureException() { }
/// <summary>Initializes a new <see cref="InvalidHandlerSignatureException"/> instance with a specified error message.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public InvalidHandlerSignatureException(string message) : base(message) { }
/// <summary>Initializes a new <see cref="InvalidHandlerSignatureException"/> instance with a specified error message and a reference to the inner exception that is the cause of this exception.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="inner">The exception that is the cause of the current exception. If <paramref name="inner"/> is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public InvalidHandlerSignatureException(string message, Exception inner) : base(message, inner) { }
/// <summary>Initializes a new <see cref="InvalidHandlerSignatureException"/> instance and constructs an error message from the given information.</summary>
/// <param name="declaringType">The type containing the handler method.</param>
/// <param name="handlerMethodName">The name of the handler method.</param>
public InvalidHandlerSignatureException(Type declaringType, string handlerMethodName) : base(GetErrorMessage(declaringType, handlerMethodName))
{
DeclaringType = declaringType;
HandlerMethodName = handlerMethodName;
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(Type declaringType, string handlerMethodName)
{
return $"'{declaringType.Name}.{handlerMethodName}' doesn't match any acceptable message handler method signatures! Server message handler methods should have a 'ushort' and a '{nameof(Riptide.Message)}' parameter, while client message handler methods should only have a '{nameof(Riptide.Message)}' parameter.";
}
}
/// <summary>The exception that is thrown when multiple methods with <see cref="MessageHandlerAttribute"/>s are set to handle messages with the same ID <i>and</i> have the same method signature.</summary>
public class DuplicateHandlerException : Exception
{
/// <summary>The message ID with multiple handler methods.</summary>
public readonly ushort Id;
/// <summary>The type containing the first handler method.</summary>
public readonly Type DeclaringType1;
/// <summary>The name of the first handler method.</summary>
public readonly string HandlerMethodName1;
/// <summary>The type containing the second handler method.</summary>
public readonly Type DeclaringType2;
/// <summary>The name of the second handler method.</summary>
public readonly string HandlerMethodName2;
/// <summary>Initializes a new <see cref="DuplicateHandlerException"/> instance with a specified error message.</summary>
public DuplicateHandlerException() { }
/// <summary>Initializes a new <see cref="DuplicateHandlerException"/> instance with a specified error message.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public DuplicateHandlerException(string message) : base(message) { }
/// <summary>Initializes a new <see cref="DuplicateHandlerException"/> instance with a specified error message and a reference to the inner exception that is the cause of this exception.</summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="inner">The exception that is the cause of the current exception. If <paramref name="inner"/> is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public DuplicateHandlerException(string message, Exception inner) : base(message, inner) { }
/// <summary>Initializes a new <see cref="DuplicateHandlerException"/> instance and constructs an error message from the given information.</summary>
/// <param name="id">The message ID with multiple handler methods.</param>
/// <param name="method1">The first handler method's info.</param>
/// <param name="method2">The second handler method's info.</param>
public DuplicateHandlerException(ushort id, MethodInfo method1, MethodInfo method2) : base(GetErrorMessage(id, method1, method2))
{
Id = id;
DeclaringType1 = method1.DeclaringType;
HandlerMethodName1 = method1.Name;
DeclaringType2 = method2.DeclaringType;
HandlerMethodName2 = method2.Name;
}
/// <summary>Constructs the error message from the given information.</summary>
/// <returns>The error message.</returns>
private static string GetErrorMessage(ushort id, MethodInfo method1, MethodInfo method2)
{
return $"Message handler methods '{method1.DeclaringType.Name}.{method1.Name}' and '{method2.DeclaringType.Name}.{method2.Name}' are both set to handle messages with ID {id}! Only one handler method is allowed per message ID!";
}
}
}