diff --git a/Components/MineSharp.Commands/CommandTree.cs b/Components/MineSharp.Commands/CommandTree.cs index 89dbb742..d3fee719 100644 --- a/Components/MineSharp.Commands/CommandTree.cs +++ b/Components/MineSharp.Commands/CommandTree.cs @@ -77,7 +77,7 @@ private static CommandNode ReadNode(PacketBuffer buffer, MinecraftData data) Identifier name; - if (data.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { // in 1.18.x, the parser was specified by its name. name = buffer.ReadIdentifier(); diff --git a/Components/MineSharp.Commands/Parser/ParserRegistry.cs b/Components/MineSharp.Commands/Parser/ParserRegistry.cs index eb5c9b5a..28fe8be8 100644 --- a/Components/MineSharp.Commands/Parser/ParserRegistry.cs +++ b/Components/MineSharp.Commands/Parser/ParserRegistry.cs @@ -221,8 +221,8 @@ public static Identifier GetParserNameById(int parserId, MinecraftData data) { var mapping = data.Version.Protocol switch { - ProtocolVersion.V_1_19 => Mapping119, - ProtocolVersion.V_1_19_2 => Mapping119, + ProtocolVersion.V_1_19_0 => Mapping119, + ProtocolVersion.V_1_19_1 => Mapping119, ProtocolVersion.V_1_19_3 => Mapping1193, >= ProtocolVersion.V_1_19_4 and < ProtocolVersion.V_1_20_3 => Mapping1194, >= ProtocolVersion.V_1_20_3 => Mapping1203, diff --git a/Components/MineSharp.PacketSourceGenerator/CommonSymbolHolder.cs b/Components/MineSharp.PacketSourceGenerator/CommonSymbolHolder.cs new file mode 100644 index 00000000..ae0dea51 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/CommonSymbolHolder.cs @@ -0,0 +1,105 @@ +using System; +using System.CodeDom.Compiler; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator; + +public record CommonSymbolHolder( + Compilation Compilation, + + INamedTypeSymbol PacketBuffer, + INamedTypeSymbol MinecraftData, + INamedTypeSymbol PacketType, + INamedTypeSymbol ProtocolVersion, + + INamedTypeSymbol IPacket, + INamedTypeSymbol IPacketStatic, + INamedTypeSymbol IPacketStaticOfTSelf, + IMethodSymbol IPacketStatic_Read, + IPropertySymbol IPacketStatic_StaticType, + + INamedTypeSymbol IPacketClientbound, + INamedTypeSymbol IPacketServerbound, + + INamedTypeSymbol IPacketVersionSubType, + INamedTypeSymbol IPacketVersionSubTypeOfTBasePacket, + INamedTypeSymbol IPacketVersionSubTypeStatic, + INamedTypeSymbol IPacketVersionSubTypeStaticOfTBasePacket, + INamedTypeSymbol IPacketVersionSubTypeStaticOfTSelfAndTBasePacket, + IMethodSymbol IPacketVersionSubTypeStatic_Read, + + INamedTypeSymbol PacketVersionSubTypeLookup, + + INamedTypeSymbol ISerializable, + INamedTypeSymbol ISerializableWithMinecraftData, + + INamedTypeSymbol Object, + INamedTypeSymbol GeneratedCodeAttribute +) +{ + private static T CheckNotNull(T? symbol, [CallerArgumentExpression(nameof(symbol))] string? symbolArgumentExpression = null) + { + if (symbol is null) + { + throw new ArgumentNullException(symbolArgumentExpression); + } + return symbol; + } + + public const string PacketNamespace = "MineSharp.Protocol.Packets"; + public const string SerializationNamespace = "MineSharp.Core.Serialization"; + + public static CommonSymbolHolder Create(Compilation compilation) + { + var PacketType = CheckNotNull(compilation.GetTypeByMetadataName($"MineSharp.Data.Protocol.PacketType")); + var IPacket = CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacket")); + var IPacketStatic = CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketStatic")); + var IPacketVersionSubTypeStatic = CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketVersionSubTypeStatic")); + + return new CommonSymbolHolder( + compilation, + + CheckNotNull(compilation.GetTypeByMetadataName($"{SerializationNamespace}.PacketBuffer")), + CheckNotNull(compilation.GetTypeByMetadataName($"MineSharp.Data.MinecraftData")), + PacketType, + CheckNotNull(compilation.GetTypeByMetadataName($"MineSharp.Core.ProtocolVersion")), + + CheckNotNull(IPacket), + IPacketStatic, + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketStatic`1")), + CheckNotNull(GetReadMethodSymbol(IPacketStatic, IPacket)), + CheckNotNull(GetStaticTypePropertySymbol(IPacketStatic, PacketType)), + + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketClientbound")), + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketServerbound")), + + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketVersionSubType")), + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketVersionSubType`1")), + IPacketVersionSubTypeStatic, + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketVersionSubTypeStatic`1")), + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.IPacketVersionSubTypeStatic`2")), + CheckNotNull(GetReadMethodSymbol(IPacketVersionSubTypeStatic, IPacket)), + + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.PacketVersionSubTypeLookup`1")), + + CheckNotNull(compilation.GetTypeByMetadataName($"{SerializationNamespace}.ISerializable`1")), + CheckNotNull(compilation.GetTypeByMetadataName($"{PacketNamespace}.NetworkTypes.ISerializableWithMinecraftData`1")), + + CheckNotNull(compilation.GetSpecialType(SpecialType.System_Object)), + CheckNotNull(compilation.GetTypeByMetadataName(typeof(GeneratedCodeAttribute).FullName)) + ); + } + + public static IMethodSymbol? GetReadMethodSymbol(INamedTypeSymbol type, INamedTypeSymbol packet) + { + return type.GetMembers("Read").OfType().FirstOrDefault(p => SymbolEqualityComparer.IncludeNullability.Equals(p.ReturnType, packet.WithNullable(false))); + } + + private static IPropertySymbol? GetStaticTypePropertySymbol(INamedTypeSymbol type, INamedTypeSymbol packetType) + { + return type.GetMembers("StaticType").OfType().FirstOrDefault(p => SymbolEqualityComparer.IncludeNullability.Equals(p.Type, packetType.WithNullable(false))); + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/GeneratedSourceFileInfo.cs b/Components/MineSharp.PacketSourceGenerator/GeneratedSourceFileInfo.cs new file mode 100644 index 00000000..33ff1d2c --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/GeneratedSourceFileInfo.cs @@ -0,0 +1,24 @@ +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace MineSharp.PacketSourceGenerator; + +public sealed record GeneratedSourceFileInfo(string Name, string Content) +{ + private static readonly ImmutableArray s_disallowedFileChars = ImmutableArray.Create('<', '>', ':', '"', '/', '\\', '|', '?', '*', // not allowed in file names + ','); // not nice to have in file names + + public static string EscapeFileName(string fileName) + { + return s_disallowedFileChars.Aggregate(new StringBuilder(fileName), (s, c) => s.Replace(c, '_')).ToString(); + } + + public static string BuildFileName(INamedTypeSymbol type) + { + var symbolDisplayString = type.ToDisplayString(); + var fileName = $"{EscapeFileName(symbolDisplayString)}.{SourceGenerator.SourceGeneratorShortName}.g.cs"; + return fileName; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/GeneratorOptions.cs b/Components/MineSharp.PacketSourceGenerator/GeneratorOptions.cs new file mode 100644 index 00000000..c4f0a5ae --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/GeneratorOptions.cs @@ -0,0 +1,13 @@ +namespace MineSharp.PacketSourceGenerator; + +public readonly struct GeneratorOptions +{ + public readonly string Indent; + public readonly string NewLine; + + public GeneratorOptions(string indent, string newLine) + { + Indent = indent; + NewLine = newLine; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Generators/AbstractPacketGenerator.cs b/Components/MineSharp.PacketSourceGenerator/Generators/AbstractPacketGenerator.cs new file mode 100644 index 00000000..82055876 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Generators/AbstractPacketGenerator.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator.Generators; + +public abstract class AbstractPacketGenerator +{ + #region Constants + + protected const string InheritDocComment = "/// "; + + #endregion + + private static string BuildGeneratedCodeAttributeDeclaration(string attributeTypeName) + { + return $"[{attributeTypeName}(\"{SourceGenerator.SourceGeneratorName}\", \"{SourceGenerator.SourceGeneratorVersionString}\")]"; + } + + public readonly GeneratorArguments Args; + protected readonly HashSet Imports = new(); + + protected AbstractPacketGenerator(GeneratorArguments args) + { + Args = args; + } + + protected string GeneratedCodeAttributeDeclaration => BuildGeneratedCodeAttributeDeclaration(BuildTypeName(Args.CommonSymbolHolder.GeneratedCodeAttribute)); + + public abstract string? BuildBodyForType(); + + public virtual string? GenerateFileSource() + { + return BuildFileSource(); + } + + public virtual GeneratedSourceFileInfo? GenerateFile() + { + var source = BuildFileSource(); + if (source is null) + { + return null; + } + + var fileName = GeneratedSourceFileInfo.BuildFileName(Args.TypeSymbol); + return new GeneratedSourceFileInfo(fileName, source); + } + + private string? BuildFileSource(bool includeSelf = false) + { + var result = BuildBodyForType(); + if (result is null) + { + return null; + } + + var containingSymbols = Args.TypeSymbol.GetContainingNamespaceAndTypes(includeSelf); + foreach (var symbol in containingSymbols) + { + if (symbol.IsNamespace) + { + var namespaceName = symbol.ToDisplayString(SymbolHelper.FullyQualifiedFormatWithoutGlobalPrefix); + + var importsStr = BuildImports(); + if (!string.IsNullOrEmpty(importsStr)) + { + importsStr += Args.GeneratorOptions.NewLine; + } + + var newResult = $$""" + {{GeneratorHelper.AutoGeneratedSourceFileHeader}} + + {{importsStr ?? ""}} + """; + + if (!string.IsNullOrEmpty(namespaceName)) + { + newResult += $$""" + namespace {{namespaceName}} + { + {{result.IndentLines(indentString: Args.GeneratorOptions.Indent, ignoreFirstLine: true)}} + } + """; + } + else + { + newResult += result; + } + + result = newResult; + } + else + { + var keyword = ((INamedTypeSymbol)symbol).GetTypeKeywordFromSymbol(); + + var minimalTypeName = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + result = $$""" + partial {{keyword}} {{minimalTypeName}} + { + {{result.IndentLines(indentString: Args.GeneratorOptions.Indent, ignoreFirstLine: true)}} + } + """; + } + } + + return result; + } + + protected string BuildTypeSource(IReadOnlyCollection methods, string extendsString = "") + { + var keyword = Args.TypeSymbol.GetTypeKeywordFromSymbol(); + + var minimalTypeName = Args.TypeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + return $$""" + partial {{keyword}} {{minimalTypeName}}{{extendsString}} + { + {{methods.Join(Args.GeneratorOptions.NewLine.Repeat(2)).IndentLines(indentString: Args.GeneratorOptions.Indent, ignoreFirstLine: true)}} + } + """; + } + + #region Methods + + protected string BuildPacketReadMethod(IMethodSymbol interfaceMethodToImplement) + { + var interfaceTypeString = BuildTypeName(interfaceMethodToImplement.ContainingType); + var interfaceMethodName = interfaceMethodToImplement.Name; + var returnTypeString = BuildTypeName(interfaceMethodToImplement.ReturnType); + + // sanity check that the method has the correct signature + if (!interfaceMethodToImplement.IsStatic) + { + throw new InvalidOperationException("Read method must be static."); + } + if (interfaceMethodToImplement.Parameters.Length != 2) + { + throw new InvalidOperationException("Read method must have exactly 2 parameters."); + } + var packetBufferType = Args.CommonSymbolHolder.PacketBuffer; + var minecraftDataType = Args.CommonSymbolHolder.MinecraftData; + if (!interfaceMethodToImplement.Parameters.Select(p => p.Type).SequenceEqual([packetBufferType, minecraftDataType], SymbolEqualityComparer.Default)) + { + throw new InvalidOperationException($"First parameter of Read method must be of type {packetBufferType.Name} and the second must be of type {minecraftDataType.Name}."); + } + var packetBufferTypeString = BuildTypeName(packetBufferType); + var minecraftDataTypeString = BuildTypeName(minecraftDataType); + + return $$""" + {{GeneratedCodeAttributeDeclaration}} + static {{returnTypeString}} {{interfaceTypeString}}.{{interfaceMethodName}}({{packetBufferTypeString}} buffer, {{minecraftDataTypeString}} data) + { + return Read(buffer, data); + } + """; + } + + #endregion + + #region Imports + + private string? BuildImports() + { + var sb = new StringBuilder(); + + foreach (var import in Imports.OrderBy(x => x)) + { + sb.Append(import.ToString()); + sb.Append(Args.GeneratorOptions.NewLine); + } + + if (sb.Length == 0) + { + return null; + } + + return sb.ToString(); + } + + protected void AddTypeImport(ITypeSymbol typeSymbol) + { + var import = typeSymbol.GetFqnPath(addGlobalPrefix: false); + if (import is not null) + { + Imports.Add(import.Value); + } + + // also add the generic type argument types + if (typeSymbol is INamedTypeSymbol namedTypeSymbol) + { + foreach (var typeParameter in namedTypeSymbol.TypeParameters) + { + import = typeParameter.GetFqnPath(addGlobalPrefix: false); + if (import is not null) + { + Imports.Add(import.Value); + } + } + } + } + + protected string BuildTypeName(ITypeSymbol typeSymbol, bool skipImport = false) + { + if (DebugHelper.UseFullyQualifiedNames) + { + return typeSymbol.ToFqn(); + } + + var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + if (!skipImport) + { + AddTypeImport(typeSymbol); + } + return typeName; + } + + #endregion +} diff --git a/Components/MineSharp.PacketSourceGenerator/Generators/BasePacketGenerator.cs b/Components/MineSharp.PacketSourceGenerator/Generators/BasePacketGenerator.cs new file mode 100644 index 00000000..415ded9f --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Generators/BasePacketGenerator.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using MineSharp.PacketSourceGenerator.Protocol; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator.Generators; + +public sealed class BasePacketGenerator : AbstractPacketGenerator +{ + public readonly IReadOnlyList VersionSubTypes; + + public BasePacketGenerator(GeneratorArguments args, IReadOnlyList versionSubTypes) + : base(args) + { + VersionSubTypes = versionSubTypes; + } + + public override string? BuildBodyForType() + { + var packetFlowInterfaceType = Args.NamespaceParseResult.PacketFlow switch + { + PacketFlow.Serverbound => Args.CommonSymbolHolder.IPacketServerbound, + PacketFlow.Clientbound => Args.CommonSymbolHolder.IPacketClientbound, + _ => throw new NotImplementedException() + }; + var packetFlowInterfaceTypeString = BuildTypeName(packetFlowInterfaceType); + + var extendsString = $": {packetFlowInterfaceTypeString}"; + List methods = new(); + + if (VersionSubTypes.Count > 0) + { + var packetVersionSubTypeLookupType = Args.CommonSymbolHolder.PacketVersionSubTypeLookup.Construct(Args.TypeSymbol); + var packetVersionSubTypeLookupTypeString = BuildTypeName(packetVersionSubTypeLookupType); + + methods.Add(BuildPacketVersionSubTypeLookupField(packetVersionSubTypeLookupTypeString)); + methods.Add(BuildInitializeVersionPacketsMethod(packetVersionSubTypeLookupTypeString)); + + methods.Add(BuildPacketVersionSubTypeWriteMethod()); + // ISerializableWithMinecraftData<> non explicit + methods.Add(BuildPacketVersionSubTypeReadMethod()); + } + + // IPacketStatic<>.Read + methods.Add(BuildPacketReadMethod(Args.CommonSymbolHolder.IPacketStatic_Read)); + + return BuildTypeSource(methods, extendsString); + } + + private string BuildPacketVersionSubTypeLookupField(string packetVersionSubTypeLookupTypeString) + { + return $$""" + {{GeneratedCodeAttributeDeclaration}} + public static readonly {{packetVersionSubTypeLookupTypeString}} PacketVersionSubTypeLookup = InitializeVersionPackets(); + """; + } + + private string BuildInitializeVersionPacketsMethod(string packetVersionSubTypeLookupTypeString) + { + List registerVersionPacketCalls = new(); + foreach (var versionSubType in VersionSubTypes) + { + var versionSubTypeString = BuildTypeName(versionSubType); + var call = $"lookup.RegisterVersionPacket<{versionSubTypeString}>();"; + registerVersionPacketCalls.Add(call); + } + + return $$""" + {{GeneratedCodeAttributeDeclaration}} + private static {{packetVersionSubTypeLookupTypeString}} InitializeVersionPackets() + { + {{packetVersionSubTypeLookupTypeString}} lookup = new(); + + {{registerVersionPacketCalls.Join(Args.GeneratorOptions.NewLine).IndentLines(indentString: Args.GeneratorOptions.Indent, ignoreFirstLine: true)}} + + lookup.Freeze(); + return lookup; + } + """; + } + + private string BuildPacketVersionSubTypeWriteMethod() + { + var packetBufferType = Args.CommonSymbolHolder.PacketBuffer; + var minecraftDataType = Args.CommonSymbolHolder.MinecraftData; + var packetBufferTypeString = BuildTypeName(packetBufferType); + var minecraftDataTypeString = BuildTypeName(minecraftDataType); + + return $$""" + {{InheritDocComment}} + {{GeneratedCodeAttributeDeclaration}} + public abstract void Write({{packetBufferTypeString}} buffer, {{minecraftDataTypeString}} data); + """; + } + + private string BuildPacketVersionSubTypeReadMethod() + { + var returnTypeString = BuildTypeName(Args.TypeSymbol); + + var packetBufferType = Args.CommonSymbolHolder.PacketBuffer; + var minecraftDataType = Args.CommonSymbolHolder.MinecraftData; + var packetBufferTypeString = BuildTypeName(packetBufferType); + var minecraftDataTypeString = BuildTypeName(minecraftDataType); + + return $$""" + {{InheritDocComment}} + {{GeneratedCodeAttributeDeclaration}} + public static {{returnTypeString}} Read({{packetBufferTypeString}} buffer, {{minecraftDataTypeString}} data) + { + return PacketVersionSubTypeLookup.Read(buffer, data); + } + """; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Generators/GeneratorArguments.cs b/Components/MineSharp.PacketSourceGenerator/Generators/GeneratorArguments.cs new file mode 100644 index 00000000..06185cf0 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Generators/GeneratorArguments.cs @@ -0,0 +1,12 @@ +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace MineSharp.PacketSourceGenerator.Generators; + +public record GeneratorArguments( + CommonSymbolHolder CommonSymbolHolder, + INamedTypeSymbol TypeSymbol, + PacketValidator.PacketNamespaceParseResult NamespaceParseResult, + GeneratorOptions GeneratorOptions, + CancellationToken CancellationToken +); diff --git a/Components/MineSharp.PacketSourceGenerator/Generators/SubTypePacketGenerator.cs b/Components/MineSharp.PacketSourceGenerator/Generators/SubTypePacketGenerator.cs new file mode 100644 index 00000000..7bd77cf2 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Generators/SubTypePacketGenerator.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace MineSharp.PacketSourceGenerator.Generators; + +public sealed class SubTypePacketGenerator : AbstractPacketGenerator +{ + public SubTypePacketGenerator(GeneratorArguments args) + : base(args) + { + } + + public override string? BuildBodyForType() + { + var basePacketType = Args.TypeSymbol.BaseType!; // must not be null otherwise we shouldn't be here + var genericIPacketVersionSubTypeStatic2 = Args.CommonSymbolHolder.IPacketVersionSubTypeStaticOfTSelfAndTBasePacket.Construct(Args.TypeSymbol, basePacketType); + var genericIPacketVersionSubTypeStatic2String = BuildTypeName(genericIPacketVersionSubTypeStatic2); + + var extendsString = $": {genericIPacketVersionSubTypeStatic2String}"; + List methods = new(); + + // FirstVersionUsed and FirstVersionUsedStatic + methods.Add(BuildFirstVersionUsedProperties()); + + // IPacketVersionSubTypeStatic.Read + methods.Add(BuildPacketReadMethod(Args.CommonSymbolHolder.IPacketVersionSubTypeStatic_Read)); + // IPacketVersionSubTypeStatic<>.Read + var genericIPacketVersionSubTypeStatic = Args.CommonSymbolHolder.IPacketVersionSubTypeStaticOfTBasePacket.Construct(basePacketType); + var genericIPacketVersionSubTypeStatic_Read = CommonSymbolHolder.GetReadMethodSymbol(genericIPacketVersionSubTypeStatic, basePacketType); + methods.Add(BuildPacketReadMethod(genericIPacketVersionSubTypeStatic_Read ?? throw new InvalidOperationException($"Read method not found for {genericIPacketVersionSubTypeStatic}"))); + + return BuildTypeSource(methods, extendsString); + } + + private string BuildFirstVersionUsedProperties() + { + var protocolVersionTypeString = BuildTypeName(Args.CommonSymbolHolder.ProtocolVersion); + var parseResult = PacketValidator.ParsePacketSubType(Args.TypeSymbol.Name); + // parseResult must not be null otherwise we shouldn't be here + var protocolVersionString = parseResult!.Value.MinecraftVersion.ToString(); + + return $$""" + {{InheritDocComment}} + {{GeneratedCodeAttributeDeclaration}} + public {{protocolVersionTypeString}} FirstVersionUsed => FirstVersionUsedStatic; + {{InheritDocComment}} + {{GeneratedCodeAttributeDeclaration}} + public static {{protocolVersionTypeString}} FirstVersionUsedStatic => {{protocolVersionTypeString}}.{{protocolVersionString}}; + """; + } + +} diff --git a/Components/MineSharp.PacketSourceGenerator/MineSharp.PacketSourceGenerator.csproj b/Components/MineSharp.PacketSourceGenerator/MineSharp.PacketSourceGenerator.csproj new file mode 100644 index 00000000..f3d3f65d --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/MineSharp.PacketSourceGenerator.csproj @@ -0,0 +1,48 @@ + + + + netstandard2.0 + true + + true + + false + + disable + enable + 12.0 + + true + true + + + + + $(GetTargetPathDependsOn);GetDependencyTargetPaths + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Components/MineSharp.PacketSourceGenerator/PacketSourceGenerator.cs b/Components/MineSharp.PacketSourceGenerator/PacketSourceGenerator.cs new file mode 100644 index 00000000..aae7d428 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/PacketSourceGenerator.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using MineSharp.PacketSourceGenerator.Generators; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator; + +public class PacketSourceGenerator +{ + public readonly CommonSymbolHolder CommonSymbolHolder; + public readonly GeneratorOptions GeneratorOptions; + public readonly CancellationToken CancellationToken; + + public readonly HashSet KnownValidPacketTypes = new(SymbolEqualityComparer.Default); + // base packet type -> list of subtypes + public readonly Dictionary> KnownValidPacketVersionSubTypes = new(SymbolEqualityComparer.Default); + + public PacketSourceGenerator(CommonSymbolHolder commonSymbolHolder, GeneratorOptions generatorOptions, CancellationToken cancellationToken) + { + CommonSymbolHolder = commonSymbolHolder; + GeneratorOptions = generatorOptions; + CancellationToken = cancellationToken; + } + + private (INamedTypeSymbol BasePacketType, TaskCompletionSource> Tcs)? _lastBasePacketGeneratorArgumentsTcsBundle; + + private void CompleteLastBasePacketGeneratorArgumentsTcsBundle() + { + if (_lastBasePacketGeneratorArgumentsTcsBundle is not null) + { + var (basePacketType, tcs) = _lastBasePacketGeneratorArgumentsTcsBundle.Value; + if (!KnownValidPacketVersionSubTypes.TryGetValue(basePacketType, out var versionSubTypes)) + { + // If the dictionary doesn't contain the base packet type, we use an empty list + versionSubTypes = new(); + } + tcs.TrySetResult(versionSubTypes); + } + _lastBasePacketGeneratorArgumentsTcsBundle = null; + } + + /// + /// IMPORTANT: This method must be called after all packetNamespaceType are processed. + /// + public void ProcessPacketNamespaceTypeComplete() + { + CompleteLastBasePacketGeneratorArgumentsTcsBundle(); + } + + /// + /// IMPORTANT: This method must not be run concurrently. + /// If this method is called multiple time that the packetNamespaceType are in root to leaf order. + /// Meaning the subtype packet must be processed after the base packet. + /// + public async Task> ProcessPacketNamespaceTypeAsync(INamedTypeSymbol packetNamespaceType, PacketValidator.PacketNamespaceParseResult namespaceParseResult) + { + var checkPacketNamespaceArguments = new PacketValidator.CheckPacketNamespaceArguments(CommonSymbolHolder, packetNamespaceType, namespaceParseResult, CancellationToken); + + if (packetNamespaceType.ContainingType is null) + { + CompleteLastBasePacketGeneratorArgumentsTcsBundle(); + + // All types that are not nested should be valid packet types + // So we check them here + + var (packetTypeParseResult, errors) = await PacketValidator.CheckValidPacketType(checkPacketNamespaceArguments); + if (errors.Length > 0 || packetTypeParseResult is null) + { + ProcessCheckErrors(packetNamespaceType, errors); + return Task.FromResult(null); + } + + KnownValidPacketTypes.Add(packetNamespaceType); + var tcs = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously); + _lastBasePacketGeneratorArgumentsTcsBundle = (packetNamespaceType, tcs); + return CreateGeneratorTask(checkPacketNamespaceArguments, async args => new BasePacketGenerator(args, await tcs.Task)); + } + else + { + // Nested types are either version specific subtypes of the packet ... + var baseType = packetNamespaceType.BaseType; + if (baseType is not null && KnownValidPacketTypes.Contains(baseType)) + { + var errors = await PacketValidator.CheckValidPacketSubType(checkPacketNamespaceArguments); + if (errors.Length > 0) + { + ProcessCheckErrors(packetNamespaceType, errors); + return Task.FromResult(null); + } + + AddPacketSubType(baseType, packetNamespaceType); + + return CreateGeneratorTask(checkPacketNamespaceArguments, args => Task.FromResult(new SubTypePacketGenerator(args))); + } + else + { + // ... or they are data container types + var errors = await PacketValidator.CheckValidDataContainerType(checkPacketNamespaceArguments); + if (errors.Length > 0) + { + ProcessCheckErrors(packetNamespaceType, errors); + return Task.FromResult(null); + } + } + } + + return Task.FromResult(null); + } + + private void AddPacketSubType(INamedTypeSymbol baseType, INamedTypeSymbol subType) + { + if (!KnownValidPacketVersionSubTypes.TryGetValue(baseType, out var subTypes)) + { + subTypes = new(); + KnownValidPacketVersionSubTypes.Add(baseType, subTypes); + } + + subTypes.Add(subType); + } + + private Task CreateGeneratorTask(PacketValidator.CheckPacketNamespaceArguments checkPacketNamespaceArguments, Func> generatorFactory) + where TGenerator : AbstractPacketGenerator + { + var type = checkPacketNamespaceArguments.PacketNamespaceType; + var generatorArguments = new GeneratorArguments(CommonSymbolHolder, type, checkPacketNamespaceArguments.NamespaceParseResult, GeneratorOptions, CancellationToken); + return Task.Run(async () => (await generatorFactory(generatorArguments)).GenerateFile(), CancellationToken); + } + + // TODO: throw RaiseDiagnosticsException with fancy Diagnostics instead of logging errors + private void ProcessCheckErrors(INamedTypeSymbol type, ImmutableArray errors) + { + if (errors.Length > 0) + { + Debugger.Log(0, "Type Error", $"Error for type '{type.ToDisplayString()}': {errors.Join(GeneratorOptions.NewLine)}{GeneratorOptions.NewLine}"); + } + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/PacketValidator.cs b/Components/MineSharp.PacketSourceGenerator/PacketValidator.cs new file mode 100644 index 00000000..30fb5226 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/PacketValidator.cs @@ -0,0 +1,249 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using MineSharp.PacketSourceGenerator.Protocol; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator; + +public static class PacketValidator +{ + public static readonly Regex PacketNamespaceRegex; + public static readonly Regex PacketTypeRegex; + public static readonly Regex PacketNameRegex; + public static readonly Regex PacketSubTypeNameRegex; + public static readonly Regex EnumHelperClassNameRegex; + + static PacketValidator() + { + PacketNamespaceRegex = new($@"^{Regex.Escape(CommonSymbolHolder.PacketNamespace)}\.{RegexHelper.BuildRegexCaptureGroupPatternForLookup(PacketFlowHelper.ByNameLookup)}\.{RegexHelper.BuildRegexCaptureGroupPatternForLookup(GameStateHelper.ByNameLookup)}$", RegexOptions.Compiled); + PacketTypeRegex = new($@"^{RegexHelper.BuildRegexCaptureGroupPatternForLookup(PacketFlowHelper.ByShortNameLookup)}_{RegexHelper.BuildRegexCaptureGroupPatternForLookup(GameStateHelper.ByNameLookup)}_(?<{nameof(PacketTypeParseResult.PacketTypeName)}>\w+)$", RegexOptions.Compiled); + var packetNamePattern = $@"(?(?\w+)Packet)"; + PacketNameRegex = new($@"^{packetNamePattern}$", RegexOptions.Compiled); + PacketSubTypeNameRegex = new($@"^{packetNamePattern}(?V_(?\d+)_(?\d+)_(?\d+))$", RegexOptions.Compiled); + EnumHelperClassNameRegex = new($@"^(?\w+)(?Helper|Extensions)$", RegexOptions.Compiled); + } + + public record struct PacketNamespaceParseResult(PacketFlow PacketFlow, GameState GameState); + + public static PacketNamespaceParseResult? ParsePacketNamespace(string packetNamespace) + { + var match = PacketNamespaceRegex.Match(packetNamespace); + if (!match.Success) + { + return null; + } + + // We can safely assume that the groups are always present because of the regex pattern + var packetFlow = match.GetRegexGroupAsValue(PacketFlowHelper.ByNameLookup)!.Value; + var gameState = match.GetRegexGroupAsValue(GameStateHelper.ByNameLookup)!.Value; + return new(packetFlow, gameState); + } + + public record struct PacketTypeParseResult(PacketFlow PacketFlow, GameState GameState, string PacketTypeName); + + public static PacketTypeParseResult? ParsePacketType(string packetType) + { + var match = PacketTypeRegex.Match(packetType); + if (!match.Success) + { + return null; + } + + // We can safely assume that the groups are always present because of the regex pattern + var packetFlow = match.GetRegexGroupAsValue(PacketFlowHelper.ByShortNameLookup)!.Value; + var gameState = match.GetRegexGroupAsValue(GameStateHelper.ByNameLookup)!.Value; + var packetTypeName = match.Groups[nameof(PacketTypeParseResult.PacketTypeName)].Value; + return new(packetFlow, gameState, packetTypeName); + } + + public record struct PacketSubTypeVersion(ushort Major, ushort Minor, ushort Patch) + { + public override string ToString() + { + return $"V_{Major}_{Minor}_{Patch}"; + } + } + public record struct PacketSubTypeParseResult(string PacketBaseName, PacketSubTypeVersion MinecraftVersion); + + public static PacketSubTypeParseResult? ParsePacketSubType(string packetSubTypeName) + { + var match = PacketSubTypeNameRegex.Match(packetSubTypeName); + if (!match.Success) + { + return null; + } + + // We can safely assume that the groups are always present because of the regex pattern + var packetBaseName = match.Groups[nameof(PacketSubTypeParseResult.PacketBaseName)].Value; + var major = match.Groups[nameof(PacketSubTypeVersion.Major)].Value; + var minor = match.Groups[nameof(PacketSubTypeVersion.Minor)].Value; + var patch = match.Groups[nameof(PacketSubTypeVersion.Patch)].Value; + var minecraftVersion = new PacketSubTypeVersion(ushort.Parse(major), ushort.Parse(minor), ushort.Parse(patch)); + return new(packetBaseName, minecraftVersion); + } + + + private static async Task GetPacketTypeFromSymbol(ISymbol implementingSymbol, CancellationToken cancellationToken) + { + foreach (var syntaxReference in implementingSymbol.DeclaringSyntaxReferences) + { + var syntax = await syntaxReference.GetSyntaxAsync(cancellationToken); + var arrowExprSyntax = syntax.DescendantNodes().OfType().FirstOrDefault(); + if (arrowExprSyntax is not null && arrowExprSyntax.Expression is MemberAccessExpressionSyntax expression + && expression.Expression is IdentifierNameSyntax leftIdentifierSyntax + && expression.Name is IdentifierNameSyntax rightIdentifierSyntax) + { + return rightIdentifierSyntax.Identifier.ToString(); + } + } + return null; + } + + public sealed record CheckPacketNamespaceArguments(CommonSymbolHolder SymbolHolder, INamedTypeSymbol PacketNamespaceType, PacketNamespaceParseResult NamespaceParseResult, CancellationToken CancellationToken); + + public static async Task<(PacketTypeParseResult? PacketTypeParseResult, ImmutableArray Errors)> CheckValidPacketType(CheckPacketNamespaceArguments arguments) + { + var (symbolHolder, packetType, namespaceParseResult, cancellationToken) = arguments; + + List errorMessages = new(); + PacketTypeParseResult? packetTypeParseResult = null; + + if (PacketNameRegex.MatchEntireString(packetType.Name) is null) + { + errorMessages.Add($"The packet type '{packetType}' does not match the expected packet name pattern"); + } + + if (packetType.DeclaredAccessibility != Accessibility.Public) + { + errorMessages.Add($"The packet type '{packetType}' must be public"); + } + + if (!packetType.IsAbstract && !packetType.IsSealed) + { + errorMessages.Add($"The packet type '{packetType}' must be either abstract or sealed"); + } + + var expectedPacketInterfaceType = symbolHolder.IPacketStaticOfTSelf.Construct(packetType); + if (!packetType.AllInterfaces.Contains(expectedPacketInterfaceType, SymbolEqualityComparer.Default)) + { + errorMessages.Add($"The packet type '{packetType}' does not implement the expected interface '{expectedPacketInterfaceType}'"); + } + + var implementingSymbol = packetType.FindImplementationForInterfaceMember(symbolHolder.IPacketStatic_StaticType); + if (implementingSymbol is null) + { + errorMessages.Add($"The packet type '{packetType}' does not implement the static property '{symbolHolder.IPacketStatic_StaticType.ToDisplayString()}'"); + } + else + { + var packetTypeName = await GetPacketTypeFromSymbol(implementingSymbol, cancellationToken); + packetTypeParseResult = ParsePacketType(packetTypeName ?? ""); + if (packetTypeParseResult is null) + { + errorMessages.Add($"The packet type '{packetType}' does not return a valid value for the static property '{symbolHolder.IPacketStatic_StaticType.ToDisplayString()}'"); + } + else + { + if (packetTypeParseResult.Value.PacketFlow != namespaceParseResult.PacketFlow) + { + errorMessages.Add($"The packet type '{packetType}' has a PacketType value with different packet flow than the packet namespace '{namespaceParseResult.PacketFlow}'"); + } + if (packetTypeParseResult.Value.GameState != namespaceParseResult.GameState) + { + errorMessages.Add($"The packet type '{packetType}' has a PacketType value with different game state than the packet namespace '{namespaceParseResult.GameState}'"); + } + } + } + return (packetTypeParseResult, errorMessages.ToImmutableArray()); + } + + // caller must only pass in packet sub types that have a valid packet type as base type + public static Task> CheckValidPacketSubType(CheckPacketNamespaceArguments arguments) + { + var (symbolHolder, packetSubType, namespaceParseResult, cancellationToken) = arguments; + + List errorMessages = new(); + + if (PacketSubTypeNameRegex.MatchEntireString(packetSubType.Name) is null) + { + errorMessages.Add($"The packet sub type '{packetSubType}' does not match the expected packet sub type name pattern"); + } + + if (packetSubType.DeclaredAccessibility != Accessibility.Public) + { + errorMessages.Add($"The packet sub type '{packetSubType}' must be public"); + } + + if (!packetSubType.IsSealed) + { + errorMessages.Add($"The packet sub type '{packetSubType}' must be sealed"); + } + + // we do not check for the interface IPacketVersionSubTypeStatic<,> because we generate the implementation + + return Task.FromResult(errorMessages.ToImmutableArray()); + } + + public static Task> CheckValidDataContainerType(CheckPacketNamespaceArguments arguments) + { + var (symbolHolder, dataContainerType, namespaceParseResult, cancellationToken) = arguments; + + var basePacketType = dataContainerType.ContainingType; + + bool HasISerializableInterface() + { + var expectedInterface = symbolHolder.ISerializable.Construct(dataContainerType); + return dataContainerType.AllInterfaces.Contains(expectedInterface, SymbolEqualityComparer.Default); + } + bool HasISerializableWithMinecraftDataInterface() + { + var expectedInterface = symbolHolder.ISerializableWithMinecraftData.Construct(dataContainerType); + return dataContainerType.AllInterfaces.Contains(expectedInterface, SymbolEqualityComparer.Default); + } + bool IsRegistryClass() + { + return dataContainerType.TypeKind == TypeKind.Class && dataContainerType.IsStatic && dataContainerType.Name.EndsWith("Registry"); + } + bool IsEnumHelperClass() + { + if (!(dataContainerType.TypeKind == TypeKind.Class && dataContainerType.IsStatic)) + { + return false; + } + + var match = EnumHelperClassNameRegex.Match(dataContainerType.Name); + if (!match.Success) + { + return false; + } + var enumName = match.Groups["EnumName"].Value; + return basePacketType.GetTypeMembers(enumName).Any(t => t.TypeKind == TypeKind.Enum); + } + + List errorMessages = new(); + + var allowedDataContainerType = dataContainerType.TypeKind == TypeKind.Enum + || dataContainerType.TypeKind == TypeKind.Interface + || IsRegistryClass() + || IsEnumHelperClass() + || HasISerializableInterface() || HasISerializableWithMinecraftDataInterface(); + + if (!allowedDataContainerType) + { + errorMessages.Add($"The sub type '{dataContainerType}' is not valid as data container type. It must be an enum, interface, static class ending with 'Registry', static helper class for an enum or a class/record implementing either {symbolHolder.ISerializable} or {symbolHolder.ISerializableWithMinecraftData}"); + } + + if (dataContainerType.DeclaredAccessibility != Accessibility.Public) + { + errorMessages.Add($"The sub type '{dataContainerType}' must be public"); + } + + return Task.FromResult(errorMessages.ToImmutableArray()); + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Protocol/GameState.cs b/Components/MineSharp.PacketSourceGenerator/Protocol/GameState.cs new file mode 100644 index 00000000..0d38d69b --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Protocol/GameState.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using static MineSharp.PacketSourceGenerator.Protocol.GameState; + +namespace MineSharp.PacketSourceGenerator.Protocol; + +/// +/// Specifies the GameState +/// +public enum GameState +{ +#pragma warning disable CS1591 + Handshaking = 0, + Status = 1, + Login = 2, + Play = 3, + Configuration = 4, +#pragma warning restore CS1591 +} + +public static class GameStateHelper +{ + public static readonly IReadOnlyDictionary ByNameLookup = new Dictionary() + { + { nameof(Handshaking), Handshaking }, + { "Handshake", Handshaking }, // Alias + { nameof(Status), Status }, + { nameof(Login), Login }, + { nameof(Play), Play }, + { nameof(Configuration), Configuration } + }; +} diff --git a/Components/MineSharp.PacketSourceGenerator/Protocol/PacketFlow.cs b/Components/MineSharp.PacketSourceGenerator/Protocol/PacketFlow.cs new file mode 100644 index 00000000..93ecba46 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Protocol/PacketFlow.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using static MineSharp.PacketSourceGenerator.Protocol.PacketFlow; + +namespace MineSharp.PacketSourceGenerator.Protocol; + +/// +/// Specifies the direction of a packet +/// +public enum PacketFlow +{ + /// + /// Packets sent to the client by the server + /// + Clientbound, + + /// + /// Packets sent to the server by the client + /// + Serverbound +} + +public static class PacketFlowHelper +{ + public static readonly IReadOnlyDictionary ByNameLookup = new Dictionary() + { + { nameof(Clientbound), Clientbound }, + { nameof(Serverbound), Serverbound } + }; + + public static readonly IReadOnlyDictionary ByShortNameLookup = new Dictionary() + { + { "CB", Clientbound }, + { "SB", Serverbound } + }; +} diff --git a/Components/MineSharp.PacketSourceGenerator/RaiseDiagnosticsException.cs b/Components/MineSharp.PacketSourceGenerator/RaiseDiagnosticsException.cs new file mode 100644 index 00000000..7b016c5b --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/RaiseDiagnosticsException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace MineSharp.PacketSourceGenerator; + +public class RaiseDiagnosticsException : Exception +{ + public readonly ImmutableArray Diagnostics; + + public RaiseDiagnosticsException(ImmutableArray diagnostics) + : base($"{diagnostics.Length} diagnostics were raised.") + { + Diagnostics = diagnostics; + } + + public RaiseDiagnosticsException(Diagnostic diagnostic) + : this(ImmutableArray.Create(diagnostic)) + { + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/SourceGenerator.cs b/Components/MineSharp.PacketSourceGenerator/SourceGenerator.cs new file mode 100644 index 00000000..ae4213f9 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/SourceGenerator.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using MineSharp.PacketSourceGenerator.Utils; + +namespace MineSharp.PacketSourceGenerator; + +[Generator(LanguageNames.CSharp)] +public class SourceGenerator : IIncrementalGenerator +{ + public const string SourceGeneratorShortName = "PacketSourceGenerator"; + public const string SourceGeneratorName = $"MineSharp.{SourceGeneratorShortName}"; + public const string SourceGeneratorVersionString = "1.0.0.0"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + DebugHelper.LaunchDebugger(); + + // using the Compilation deep in the pipeline is bad for performance + // because every changed character will trigger a full run of the generator + // but we need the compilation by using it with dynamic values + // (it might be possible to precompute all possible values that might be used on the compilation. But this is for a later optimization phase) + context.RegisterSourceOutput(context.CompilationProvider, static (spc, compilation) => + { + var generatorOptions = new GeneratorOptions(GeneratorHelper.DefaultIndent, GeneratorHelper.SourceCodeNewLine); + + Execute(spc, compilation, generatorOptions).Wait(); + }); + } + + private static IEnumerable<(INamedTypeSymbol Type, PacketValidator.PacketNamespaceParseResult NamespaceParseResult)> GetAllTypesInPacketNamespace(CommonSymbolHolder symbolHolder) + { + var globalNamespace = symbolHolder.Compilation.SourceModule.GlobalNamespace; + var typeMembers = globalNamespace.GetAllNamedTypeSymbols(false); + + // filter out all types that are not in the packet namespace + foreach (var type in typeMembers) + { + var typeNamespace = type.ContainingNamespace; + var namespaceName = typeNamespace.ToDisplayString(SymbolHelper.FullyQualifiedFormatWithoutGlobalPrefix); + var packetNamespaceParseResult = PacketValidator.ParsePacketNamespace(namespaceName); + if (packetNamespaceParseResult is null) + { + continue; + } + yield return (type, packetNamespaceParseResult.Value); + } + } + + private static async Task Execute(SourceProductionContext productionContext, Compilation compilation, GeneratorOptions generatorOptions) + { + var symbolHolder = CommonSymbolHolder.Create(compilation); + + var packetSourceGenerator = new PacketSourceGenerator(symbolHolder, generatorOptions, productionContext.CancellationToken); + + List<(Task SourceTask, INamedTypeSymbol Type)> sourceTasks = new(); + + foreach (var (type, namespaceParseResult) in GetAllTypesInPacketNamespace(symbolHolder)) + { + productionContext.CancellationToken.ThrowIfCancellationRequested(); + await HandleGeneratorActionErrors(productionContext, type, async () => + { + var sourceTask = await packetSourceGenerator.ProcessPacketNamespaceTypeAsync(type, namespaceParseResult); + sourceTasks.Add((sourceTask, type)); + }); + } + packetSourceGenerator.ProcessPacketNamespaceTypeComplete(); + + await foreach (var tuple in TaskHelper.AsTheyComplete(sourceTasks, t => t.SourceTask)) + { + productionContext.CancellationToken.ThrowIfCancellationRequested(); + await HandleGeneratorActionErrors(productionContext, tuple.Type, async () => + { + var type = tuple.Type; + var source = await tuple.SourceTask; + if (source is not null) + { + productionContext.AddSource(source.Name, source.Content); + } + }); + } + } + + private static async Task HandleGeneratorActionErrors(SourceProductionContext productionContext, INamedTypeSymbol type, Func action) + { + try + { + await action(); + } + catch (RaiseDiagnosticsException e) + { + foreach (var diagnostic in e.Diagnostics) + { + productionContext.ReportDiagnostic(diagnostic); + } + } + catch (Exception e) + { + var symbolDisplayString = type.ToDisplayString(); + Debugger.Log(0, "Source Generator Error", $"Error while processing type '{symbolDisplayString}': {e}{GeneratorHelper.SourceCodeNewLine}"); + } + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/SystemPolyFill.cs b/Components/MineSharp.PacketSourceGenerator/SystemPolyFill.cs new file mode 100644 index 00000000..dbb6107a --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/SystemPolyFill.cs @@ -0,0 +1,14 @@ +namespace System.Runtime.CompilerServices; + +internal class IsExternalInit { } + +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +internal sealed class CallerArgumentExpressionAttribute : Attribute +{ + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/DebugHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/DebugHelper.cs new file mode 100644 index 00000000..7c787361 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/DebugHelper.cs @@ -0,0 +1,50 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class DebugHelper +{ + /// + /// Can be disabled to use short names in the generated code. + /// Disabling this will make the generated code more readable, but may cause conflicts with other types. + /// Because of this, it is for debugging purposes only. + /// + public static readonly bool UseFullyQualifiedNames = true; + +#pragma warning disable RS1035 // Do not used banned APIs + [Conditional("DEBUG")] + public static void LaunchDebugger([CallerFilePath] string? sourceFilePath = null) + { + // required to debug the source generator + + // this check filters whether the source gen is called from the IDE or from the compiler + // compiler = 4 + // IDE = 6 (or higher) + if (Environment.Version.Major == 4 + && DoesDebugFileExist(sourceFilePath)) + { + Debugger.Launch(); + } + } + + private const string DebugFileName = $"debugSourceGenerator.txt.user"; + + private static bool DoesDebugFileExist(string? sourceFilePath) + { + if (sourceFilePath is null) + { + return false; + } + + var debugFilePath = Path.Combine(Path.GetDirectoryName(sourceFilePath), DebugFileName); + if (debugFilePath is null) + { + return false; + } + return File.Exists(debugFilePath); + } +#pragma warning restore RS1035 +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/GeneratorHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/GeneratorHelper.cs new file mode 100644 index 00000000..44d6994d --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/GeneratorHelper.cs @@ -0,0 +1,32 @@ +using System.Text.RegularExpressions; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class GeneratorHelper +{ + public const string SourceCodeNewLine = """ + + + """; + + public const string DefaultIndent = "\t"; + + public const string AutoGeneratedSourceFileHeader = """ + // ################################################################################ + // # _ _ _ ____ _ _ _ # + // # | | / \ _ _| |_ ___ / ___| ___ _ __ ___ _ __ __ _| |_ ___ __| | | # + // # | | / _ \| | | | __/ _ \ | | _ / _ \ '_ \ / _ \ '__/ _` | __/ _ \/ _` | | # + // # |_|/ ___ \ |_| | || (_) | | |_| | __/ | | | __/ | | (_| | || __/ (_| |_| # + // # (_)_/ \_\__,_|\__\___/ \____|\___|_| |_|\___|_| \__,_|\__\___|\__,_(_) # + // # # + // ################################################################################ + // This file is auto generated. + // !!DO NOT EDIT THIS FILE!! + """; + + public static string IndentLines(this string str, int indentCount = 1, string indentString = DefaultIndent, bool ignoreEmptyLines = true, bool ignoreFirstLine = false, Regex? newLineRegex = null, Regex? emptyLineContentRegex = null) + { + return str.AddBeforeEachLine(indentString.Repeat(indentCount), ignoreEmptyLines, ignoreFirstLine, newLineRegex, emptyLineContentRegex); + } + +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/ImportItem.cs b/Components/MineSharp.PacketSourceGenerator/Utils/ImportItem.cs new file mode 100644 index 00000000..062d0564 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/ImportItem.cs @@ -0,0 +1,23 @@ +using System; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public record struct ImportItem(string Namespace, bool Static) : IComparable +{ + public int CompareTo(ImportItem other) + { + var staticInt = Static ? 1 : 0; + var staticIntOther = other.Static ? 1 : 0; + var staticIntResult = staticInt.CompareTo(staticIntOther); + if (staticIntResult != 0) + { + return staticIntResult; + } + return Namespace.CompareTo(other.Namespace); + } + + public override readonly string ToString() + { + return $"using {(Static ? "static " : "")}{Namespace};"; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/RegexHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/RegexHelper.cs new file mode 100644 index 00000000..410fd9c9 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/RegexHelper.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class RegexHelper +{ + public static readonly Regex NewLineRegex = new(@"\r\n|\n|\r", RegexOptions.Compiled); + + public static Match? MatchEntireString(this Regex regex, string input) + { + var match = regex.Match(input); + if (match.Success && match.Value.Length == input.Length) + { + return match; + } + return null; + } + + /// + /// This method tries to get a group with the name from and then tries to lookup that value in the dictionary. + /// If the regex does not contain a group with that name then will be returned. + /// If the does not contain a value for the group's value then a exception will be thrown. + /// + /// The looked up value from the group's value or if the group was not matched. + public static TValue? GetRegexGroupAsValue(this Match match, string groupName, IReadOnlyDictionary valueLookup) + where TValue : unmanaged + { + var group = match.Groups[groupName]; + return group.Success ? valueLookup[group.Value] : null; + } + + public static TValue? GetRegexGroupAsValue(this Match match, IReadOnlyDictionary valueLookup) + where TValue : unmanaged + { + return GetRegexGroupAsValue(match, typeof(TValue).Name, valueLookup); + } + + public static string BuildRegexPatternForMultipleValues(IEnumerable values) + { + var sb = new StringBuilder(); + foreach (var value in values) + { + var regexPattern = Regex.Escape(value); + sb.Append(regexPattern); + sb.Append('|'); + } + + if (sb.Length > 0) + { + sb.Length--; // Remove the last '|' + } + + return sb.ToString(); + } + + public static string BuildRegexCaptureGroupPatternForType(IEnumerable values) + where TValue : unmanaged + { + var valuesPattern = BuildRegexPatternForMultipleValues(values); + + return $"(?<{typeof(TValue).Name}>{valuesPattern})"; + } + + public static string BuildRegexCaptureGroupPatternForLookup(IReadOnlyDictionary valueLookup) + where TValue : unmanaged + { + return BuildRegexCaptureGroupPatternForType(valueLookup.Keys); + } + +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/SpanHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/SpanHelper.cs new file mode 100644 index 00000000..9795883b --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/SpanHelper.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class SpanHelper +{ + public static bool ContainsOnlyAllowedItems(this ReadOnlySpan items, ReadOnlySpan allowedItems, IEqualityComparer? equalityComparer = null) + { + if (items.Length == 0) + { + return true; + } + + equalityComparer ??= EqualityComparer.Default; + + for (var i = 0; i < items.Length; i++) + { + var item = items[i]; + var isAllowed = false; + for (var j = 0; j < allowedItems.Length; j++) + { + var allowedItem = allowedItems[j]; + if (equalityComparer.Equals(item, allowedItem)) + { + isAllowed = true; + break; + } + } + + if (!isAllowed) + { + return false; + } + } + + return true; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/StringHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/StringHelper.cs new file mode 100644 index 00000000..19043134 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/StringHelper.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text; +using System.Text.RegularExpressions; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class StringHelper +{ + public static readonly ImmutableArray NewLineStrings = ImmutableArray.Create("\r\n", "\r", "\n"); + + public static string ReplaceLineEndings(this string text, string newLineEndings) + { + return RegexHelper.NewLineRegex.Replace(text, match => newLineEndings); + } + + public static string Join(this IEnumerable enumerable, string separator, bool addSeparatorAtEnd = false, bool addSeparatorAtStart = false) + { + var s = string.Join(separator, enumerable); + if (s.Length > 0) + { + if (addSeparatorAtStart) + { + s = separator + s; + } + if (addSeparatorAtEnd && s.Length > 0) + { + s += separator; + } + } + return s; + } + + public static string Repeat(this string input, int count) + { + if (string.IsNullOrEmpty(input) || count == 1) + { + return input; + } + + if (count == 0) + { + return ""; + } + + var builder = new StringBuilder(input.Length * count); + + for (var i = 0; i < count; i++) + { + builder.Append(input); + } + + return builder.ToString(); + } + + private static readonly char[] s_defaultEmptyLineAllowedChars = new char[] { ' ', '\t' }; + + public static string AddBeforeEachLine(this string str, string linePrefix, bool ignoreEmptyLines, bool ignoreFirstLine = false, Regex? newLineRegex = null, Regex? emptyLineContentRegex = null) + { + if (string.IsNullOrEmpty(str)) + { + return str; + } + + newLineRegex ??= RegexHelper.NewLineRegex; + + bool CheckIgnoreLine(int nextLineStartIndex, Match nextNewLineMatch) + { + if (ignoreEmptyLines) + { + var nextLineEndIndex = nextNewLineMatch.Success ? nextNewLineMatch.Index : str.Length; + var nextLineStr = str.Substring(nextLineStartIndex, nextLineEndIndex - nextLineStartIndex); + + var isEmptyLine = false; + if (emptyLineContentRegex is null) + { + isEmptyLine = nextLineStr.AsSpan().ContainsOnlyAllowedItems(s_defaultEmptyLineAllowedChars); + } + else + { + isEmptyLine = emptyLineContentRegex.MatchEntireString(nextLineStr)?.Success ?? false; + } + + return isEmptyLine; + } + return false; + } + + var retStr = newLineRegex.Replace(str, (match) => + { + var nextNewLineMatch = match.NextMatch(); + var nextLineStartIndex = match.Index + match.Length; + var ignoreLine = CheckIgnoreLine(nextLineStartIndex, nextNewLineMatch); + return ignoreLine ? match.Value : match.Value + linePrefix; + }); + + if (!ignoreFirstLine) + { + var firstMatch = newLineRegex.Match(str); + var ignoreLine = CheckIgnoreLine(0, firstMatch); + if (!ignoreLine) + { + retStr = linePrefix + retStr; + } + } + + return retStr; + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/SymbolHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/SymbolHelper.cs new file mode 100644 index 00000000..c3767914 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/SymbolHelper.cs @@ -0,0 +1,146 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class SymbolHelper +{ + private static readonly SymbolDisplayFormat s_qualifiedNameOnlyFormat = new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); + + public static readonly SymbolDisplayFormat FullyQualifiedFormatWithoutGlobalPrefix = + SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted); + + public static string ToFqn(this ISymbol symbol, bool addGlobalPrefix = true) + { + var format = SymbolDisplayFormat.FullyQualifiedFormat; + format = format.WithGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters) + .WithGlobalNamespaceStyle(addGlobalPrefix ? SymbolDisplayGlobalNamespaceStyle.Included : SymbolDisplayGlobalNamespaceStyle.Omitted); + + return symbol.ToDisplayString(format); + } + + public static ImportItem? GetFqnPath(this ISymbol symbol, bool addGlobalPrefix = true) + { + var format = s_qualifiedNameOnlyFormat + .WithGlobalNamespaceStyle(addGlobalPrefix ? SymbolDisplayGlobalNamespaceStyle.Included : SymbolDisplayGlobalNamespaceStyle.Omitted); + + var parts = symbol.ToDisplayParts(format).Select(x => x.ToString()).ToList(); + var lastIndexOfDot = parts.LastIndexOf("."); + if (lastIndexOfDot == -1) + { + return null; + } + var path = parts.Take(lastIndexOfDot).Join(""); + + var namespaceName = symbol.ContainingNamespace.ToDisplayString(format); + return new(path, path != namespaceName); + } + + public static INamedTypeSymbol? GetNullableBaseType(this INamedTypeSymbol symbol) + { + if (symbol.IsGenericType && symbol.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T) + { + return symbol.TypeArguments[0] as INamedTypeSymbol; + } + + return null; + } + + public static ITypeSymbol WithNullable(this ITypeSymbol typeSymbol, bool nullable) + { + var nullableAnnotation = nullable ? NullableAnnotation.Annotated : NullableAnnotation.NotAnnotated; + return typeSymbol.WithNullableAnnotation(nullableAnnotation); + } + + public static ITypeSymbol WithNullable(this ITypeSymbol typeSymbol, bool nullable, bool globalUseNullableReferenceTypes) + { + var nullableAnnotation = !globalUseNullableReferenceTypes ? NullableAnnotation.None : nullable ? NullableAnnotation.Annotated : NullableAnnotation.NotAnnotated; + return typeSymbol.WithNullableAnnotation(nullableAnnotation); + } + + public static string GetTypeKeywordFromSymbol(this ITypeSymbol typeSymbol) + { + var typeDeclarationSyntax = typeSymbol.DeclaringSyntaxReferences + .Select(x => x.GetSyntax()) + .OfType() + .First(); + + if (typeDeclarationSyntax.IsKind(SyntaxKind.RecordStructDeclaration)) + { + return "record struct"; + } + + return typeDeclarationSyntax.Keyword.ValueText; + } + + public static IEnumerable GetAllNamedTypeSymbols(this INamespaceOrTypeSymbol startSymbol, bool includeSelf) + { + var stack = new Stack(); + stack.Push(startSymbol); + + if (includeSelf && startSymbol is INamedTypeSymbol startTypeSymbol) + { + yield return startTypeSymbol; + } + + while (stack.Count > 0) + { + var symbol = stack.Pop(); + + if (symbol is INamedTypeSymbol symbolAsNamedTypeSymbol) + { + yield return symbolAsNamedTypeSymbol; + } + + foreach (var member in symbol.GetMembers()) + { + if (member is INamespaceSymbol memberAsNamespace) + { + stack.Push(memberAsNamespace); + } + else if (member is INamedTypeSymbol memberAsNamedTypeSymbol) + { + stack.Push(memberAsNamedTypeSymbol); + } + } + } + } + + public static IEnumerable GetContainingNamespaceAndTypes(this ISymbol symbol, bool includeSelf) + { + foreach (var item in symbol.GetAllContainingNamespacesAndTypes(includeSelf)) + { + yield return item; + + if (item.IsNamespace) + { + yield break; + } + } + } + + public static IEnumerable GetAllContainingNamespacesAndTypes(this ISymbol symbol, bool includeSelf) + { + if (includeSelf && symbol is INamespaceOrTypeSymbol self) + { + yield return self; + } + + while (true) + { + symbol = symbol.ContainingSymbol; + + if (symbol is not INamespaceOrTypeSymbol namespaceOrTypeSymbol) + { + yield break; + } + + yield return namespaceOrTypeSymbol; + } + } +} diff --git a/Components/MineSharp.PacketSourceGenerator/Utils/TaskHelper.cs b/Components/MineSharp.PacketSourceGenerator/Utils/TaskHelper.cs new file mode 100644 index 00000000..7cb37512 --- /dev/null +++ b/Components/MineSharp.PacketSourceGenerator/Utils/TaskHelper.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MineSharp.PacketSourceGenerator.Utils; + +public static class TaskHelper +{ + // created with inspiration from https://devblogs.microsoft.com/pfxteam/processing-tasks-as-they-complete/ + public static async IAsyncEnumerable AsTheyComplete(IEnumerable enumerable, Func taskSelector) + where TTask : Task + { + var inputList = enumerable.ToList(); + + var buckets = new TaskCompletionSource[inputList.Count]; + for (var i = 0; i < buckets.Length; i++) + { + buckets[i] = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + + int nextTaskIndex = -1; + Action continuation = (completedTask, input) => + { + var bucket = buckets[Interlocked.Increment(ref nextTaskIndex)]; + bucket.TrySetResult((TInput)input); + }; + + foreach (var input in inputList) + { + var inputTask = taskSelector(input); + _ = inputTask.ContinueWith(continuation, input, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + + foreach (var bucket in buckets) + { + yield return await bucket.Task; + } + } + +} diff --git a/Components/MineSharp.Protocol/Exceptions/MineSharpPacketVersionException.cs b/Components/MineSharp.Protocol/Exceptions/MineSharpPacketVersionException.cs index bdc665f5..2e026337 100644 --- a/Components/MineSharp.Protocol/Exceptions/MineSharpPacketVersionException.cs +++ b/Components/MineSharp.Protocol/Exceptions/MineSharpPacketVersionException.cs @@ -1,9 +1,10 @@ -using MineSharp.Core.Exceptions; +using MineSharp.Core; +using MineSharp.Core.Exceptions; namespace MineSharp.Protocol.Exceptions; /// -/// Thrown when a packet has invalid data for it's minecraft version. +/// Thrown when a packet has invalid data for it's Minecraft version. /// -public class MineSharpPacketVersionException(string valueName, int protocol) +public class MineSharpPacketVersionException(string valueName, ProtocolVersion protocol) : MineSharpException($"expected {valueName} to be set for protocol version {protocol}"); diff --git a/Components/MineSharp.Protocol/MineSharp.Protocol.csproj b/Components/MineSharp.Protocol/MineSharp.Protocol.csproj index 1e46f5a6..db183f78 100644 --- a/Components/MineSharp.Protocol/MineSharp.Protocol.csproj +++ b/Components/MineSharp.Protocol/MineSharp.Protocol.csproj @@ -17,9 +17,17 @@ git minecraft, protocol, minecraft-protocol LICENSE + + + true + + + diff --git a/Components/MineSharp.Protocol/MinecraftClient.cs b/Components/MineSharp.Protocol/MinecraftClient.cs index 498d2677..a9b857bc 100644 --- a/Components/MineSharp.Protocol/MinecraftClient.cs +++ b/Components/MineSharp.Protocol/MinecraftClient.cs @@ -36,13 +36,14 @@ public sealed class MinecraftClient : IAsyncDisposable, IDisposable /// /// Delegate for handling packets async /// - public delegate Task AsyncPacketHandler(IPacket packet); + public delegate Task AsyncPacketHandler(IPacketClientbound packet); /// /// Delegate for handling a specific packet async /// - /// - public delegate Task AsyncPacketHandler(T packet) where T : IPacket; + /// + public delegate Task AsyncPacketHandler(TPacket packet) + where TPacket : IPacketStatic, IPacketClientbound; /// /// The latest version supported @@ -261,8 +262,10 @@ public async Task Connect(GameState nextState) /// /// The packet to send. /// Optional cancellation token. + /// The type of the packet to be sent. /// A task that resolves once the packet was actually sent. - public async Task SendPacket(IPacket packet, CancellationToken cancellation = default) + public async Task SendPacket(TPacket packet, CancellationToken cancellation = default) + where TPacket : IPacketServerbound { var sendingTask = new PacketSendTask(packet, cancellation, new(TaskCreationOptions.RunContinuationsAsynchronously)); try @@ -325,12 +328,12 @@ private async Task DisconnectInternal(Chat? reason = null) } /// - /// Represents a registration for a packet handler that will be called whenever a packet of type is received. + /// Represents a registration for a packet handler that will be called whenever a packet of type is received. /// This registration can be used to unregister the handler. /// - /// The type of the packet. - public sealed class OnPacketRegistration : AbstractPacketReceiveRegistration - where T : IPacket + /// The type of the packet. + public sealed class OnPacketRegistration : AbstractPacketReceiveRegistration + where TPacket : IPacketStatic, IPacketClientbound { internal OnPacketRegistration(MinecraftClient client, AsyncPacketHandler handler) : base(client, handler) @@ -340,7 +343,7 @@ internal OnPacketRegistration(MinecraftClient client, AsyncPacketHandler handler /// protected override void Unregister() { - var key = T.StaticType; + var key = TPacket.StaticType; if (Client.packetHandlers.TryGetValue(key, out var handlers)) { handlers.TryRemove(Handler); @@ -353,12 +356,13 @@ protected override void Unregister() /// is received /// /// A delegate that will be called when a packet of type T is received - /// The type of the packet + /// The type of the packet /// A registration object that can be used to unregister the handler. - public OnPacketRegistration? On(AsyncPacketHandler handler) where T : IPacket + public OnPacketRegistration? On(AsyncPacketHandler handler) + where TPacket : IPacketStatic, IPacketClientbound { - var key = T.StaticType; - AsyncPacketHandler rawHandler = packet => handler((T)packet); + var key = TPacket.StaticType; + AsyncPacketHandler rawHandler = packet => handler((TPacket)packet); var added = packetHandlers.GetOrAdd(key, _ => new ConcurrentHashSet()) .Add(rawHandler); return added ? new(this, rawHandler) : null; @@ -367,18 +371,18 @@ protected override void Unregister() /// /// Waits until a packet of the specified type is received and matches the given condition. /// - /// The type of the packet. + /// The type of the packet. /// A function that evaluates the packet and returns true if the condition is met. /// A token to cancel the wait for the matching packet. /// A task that completes once a packet matching the condition is received. - public Task WaitForPacketWhere(Func> condition, CancellationToken cancellationToken = default) - where T : IPacket + public Task WaitForPacketWhere(Func> condition, CancellationToken cancellationToken = default) + where TPacket : IPacketStatic, IPacketClientbound { // linked token is required to cancel the task when the client is disconnected var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, CancellationToken); var token = cts.Token; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - async Task PacketHandler(T packet) + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + async Task PacketHandler(TPacket packet) { try { @@ -388,7 +392,7 @@ async Task PacketHandler(T packet) } if (await condition(packet).WaitAsync(token)) { - tcs.TrySetResult(); + tcs.TrySetResult(packet); } } catch (OperationCanceledException e) @@ -400,43 +404,46 @@ async Task PacketHandler(T packet) tcs.TrySetException(e); } } - var packetRegistration = On(PacketHandler); + var packetRegistration = On(PacketHandler); if (packetRegistration == null) { // TODO: Can this occur? cts.Dispose(); throw new InvalidOperationException("Could not register packet handler"); } - return tcs.Task.ContinueWith(_ => + // this registration is required because otherwise the task will only get cancelled when the next packet of that ype is received + var cancellationRegistration = token.Register(() => { + // cancelling the tcs will later dispose the other stuff + tcs.TrySetCanceled(token); + }); + tcs.Task.ContinueWith(_ => + { + cancellationRegistration.Dispose(); packetRegistration.Dispose(); cts.Dispose(); }, TaskContinuationOptions.ExecuteSynchronously); + return tcs.Task; } - /// - /// Waits until a packet of the specified type is received and matches the given condition. - /// - /// The type of the packet. - /// A function that evaluates the packet and returns true if the condition is met. - /// A token to cancel the wait for the matching packet. - /// A task that completes once a packet matching the condition is received. - public Task WaitForPacketWhere(Func condition, CancellationToken cancellationToken = default) - where T : IPacket + /// + public Task WaitForPacketWhere(Func condition, CancellationToken cancellationToken = default) + where TPacket : IPacketStatic, IPacketClientbound { - return WaitForPacketWhere(packet => Task.FromResult(condition(packet)), cancellationToken); + return WaitForPacketWhere(packet => Task.FromResult(condition(packet)), cancellationToken); } /// /// Waits until a packet of the specified type is received. /// - /// + /// The type of the packet /// A task that completes once the packet is received - public Task WaitForPacket() where T : IPacket + public Task WaitForPacket() + where TPacket : IPacketStatic, IPacketClientbound { - var packetType = T.StaticType; + var packetType = TPacket.StaticType; var tcs = packetWaiters.GetOrAdd(packetType, _ => new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously)); - return tcs.Task.ContinueWith(prev => (T)prev.Result); + return tcs.Task.ContinueWith(prev => (TPacket)prev.Result); } /// @@ -482,7 +489,7 @@ public Task WaitForGame() internal Task SendClientInformationPacket(GameState gameState) { - IPacket packet = gameState switch + IPacketServerbound packet = gameState switch { GameState.Configuration => new ConfigurationClientInformationPacket( Settings.Locale, @@ -550,7 +557,7 @@ internal void HandleBundleDelimiter() private async Task ProcessBundledPackets(ConcurrentQueue<(PacketType, PacketBuffer)> packets) { - Logger.Debug($"Processing {packets.Count} bundled packets"); + Logger.Trace($"Processing {packets.Count} bundled packets"); try { // wiki.vg: the client is guaranteed to process every packet in the bundle on the same tick @@ -581,8 +588,9 @@ private async Task StreamLoop() try { // run both tasks in parallel - var receiveTask = Task.Factory.StartNew(ReceivePackets, TaskCreationOptions.LongRunning); - var sendTask = Task.Factory.StartNew(SendPackets, TaskCreationOptions.LongRunning); + // because the task factory does not unwrap the tasks (like Task.Run) we need to do it manually + var receiveTask = Task.Factory.StartNew(ReceivePackets, TaskCreationOptions.LongRunning).Unwrap(); + var sendTask = Task.Factory.StartNew(SendPackets, TaskCreationOptions.LongRunning).Unwrap(); // extract the exception from the task that finished first await await Task.WhenAny(receiveTask, sendTask); @@ -601,79 +609,97 @@ private async Task StreamLoop() private async Task ReceivePackets() { - while (true) + try { - CancellationToken.ThrowIfCancellationRequested(); + while (true) + { + CancellationToken.ThrowIfCancellationRequested(); - var buffer = stream!.ReadPacket(); + var buffer = stream!.ReadPacket(); - var packetId = buffer.ReadVarInt(); - var gameState = gameStatePacketHandler.GameState; - var packetType = Data.Protocol.GetPacketType(PacketFlow.Clientbound, gameState, packetId); + var packetId = buffer.ReadVarInt(); + var gameState = gameStatePacketHandler.GameState; + var packetType = Data.Protocol.GetPacketType(PacketFlow.Clientbound, gameState, packetId); - Logger.Trace("Received packet {PacketType}. GameState = {GameState}, PacketId = {PacketId}", packetType, gameState, packetId); + Logger.Trace("Received packet {PacketType}. GameState = {GameState}, PacketId = {PacketId}", packetType, gameState, packetId); - // handle BundleDelimiter packet here, because there is a race condition where some - // packets may be read before HandleBundleDelimiter is invoked through a handler - if (packetType == PacketType.CB_Play_BundleDelimiter) - { - HandleBundleDelimiter(); - continue; - } + // handle BundleDelimiter packet here, because there is a race condition where some + // packets may be read before HandleBundleDelimiter is invoked through a handler + if (packetType == PacketType.CB_Play_BundleDelimiter) + { + HandleBundleDelimiter(); + continue; + } - if (gameState != GameState.Play) - { - await HandleIncomingPacket(packetType, buffer); - } - else - { - var bundledPackets = this.bundledPackets; - if (bundledPackets != null) + if (gameState != GameState.Play) { - bundledPackets.Enqueue((packetType, buffer)); + await HandleIncomingPacket(packetType, buffer); } else { - // handle the packet in a new task to prevent blocking the stream loop - _ = Task.Run(() => HandleIncomingPacket(packetType, buffer)); + var bundledPackets = this.bundledPackets; + if (bundledPackets != null) + { + bundledPackets.Enqueue((packetType, buffer)); + } + else + { + // handle the packet in a new task to prevent blocking the stream loop + _ = Task.Run(() => HandleIncomingPacket(packetType, buffer)); + } } } } + catch (Exception e) + { + Logger.Debug(e, "ReceivePackets loop ended with exception."); + throw; + } + // can never exit without exception because infinite loop without break } private async Task SendPackets() { - await foreach (var task in packetQueue.ReceiveAllAsync()) + try { - if (task.Token.IsCancellationRequested) + await foreach (var task in packetQueue.ReceiveAllAsync()) { - task.Task.TrySetCanceled(); - continue; - } + if (task.Token.IsCancellationRequested) + { + task.Task.TrySetCanceled(); + continue; + } - try - { - DispatchPacket(task.Packet); - task.Task.TrySetResult(); - } - catch (OperationCanceledException e) - { - task.Task.TrySetCanceled(e.CancellationToken); - // we should stop. So we do by rethrowing the exception - throw; - } - catch (Exception e) - { - Logger.Error(e, "Encountered exception while dispatching packet {PacketType}", task.Packet.Type); - task.Task.TrySetException(e); - if (e is SocketException) + try + { + DispatchPacket(task.Packet); + task.Task.TrySetResult(); + } + catch (OperationCanceledException e) { - // break the loop to prevent further packets from being sent - // because the connection is probably dead + task.Task.TrySetCanceled(e.CancellationToken); + // we should stop. So we do by rethrowing the exception throw; } + catch (Exception e) + { + Logger.Error(e, "Encountered exception while dispatching packet {PacketType}", task.Packet.Type); + task.Task.TrySetException(e); + if (e is SocketException) + { + // break the loop to prevent further packets from being sent + // because the connection is probably dead + throw; + } + } } } + catch (Exception e) + { + Logger.Debug(e, "SendPackets loop ended with exception."); + throw; + } + // can never exit without exception because infinite loop without break (because we never complete the BufferBlock we only cancel it) } private void DispatchPacket(IPacket packet) @@ -696,8 +722,7 @@ private void DispatchPacket(IPacket packet) } } - // TODO: object is bad but IPacket is not allowed as generic type - private async Task ParsePacket(PacketPalette.PacketFactory packetFactory, PacketType packetType, PacketBuffer buffer) + private async Task ParsePacket(PacketFactory packetFactory, PacketType packetType, PacketBuffer buffer) { var size = buffer.ReadableBytes; try @@ -707,7 +732,7 @@ private void DispatchPacket(IPacket packet) var unreadBytes = buffer.ReadableBytes; if (unreadBytes != 0) { - Logger.Warn("After reading the packet {PacketType}, the buffer still contains {unreadBytes}/{Size} bytes.", packetType, unreadBytes, size); + Logger.Warn("After reading the packet {PacketType}, the buffer still contains {UnreadBytes}/{Size} bytes.", packetType, unreadBytes, size); } return packet; @@ -733,7 +758,7 @@ private async Task HandleIncomingPacket(PacketType packetType, PacketBuffer buff // - The internal IPacketHandler Logger.Trace("Handling packet {PacketType}", packetType); - var factory = PacketPalette.GetFactory(packetType); + var factory = PacketPalette.GetClientboundFactory(packetType); if (factory == null) { await buffer.DisposeAsync(); @@ -762,7 +787,7 @@ private async Task HandleIncomingPacket(PacketType packetType, PacketBuffer buff return; } - var packet = (IPacket?)await ParsePacket(factory, packetType, buffer); + var packet = await ParsePacket(factory, packetType, buffer); if (packet == null) { diff --git a/Components/MineSharp.Protocol/MinecraftStream.cs b/Components/MineSharp.Protocol/MinecraftStream.cs index 0cc7ff36..623352be 100644 --- a/Components/MineSharp.Protocol/MinecraftStream.cs +++ b/Components/MineSharp.Protocol/MinecraftStream.cs @@ -1,5 +1,6 @@ using System.Net.Sockets; using ICSharpCode.SharpZipLib.Zip.Compression; +using MineSharp.Core; using MineSharp.Core.Serialization; using MineSharp.Protocol.Cryptography; using NLog; @@ -19,7 +20,7 @@ internal class MinecraftStream private readonly NetworkStream networkStream; - private readonly int protocolVersion; + private readonly ProtocolVersion protocolVersion; // Always acquire the readLock before the writeLock if both are needed private readonly object readLock = new(); @@ -33,7 +34,7 @@ internal class MinecraftStream private Stream stream; - public MinecraftStream(NetworkStream networkStream, int protocolVersion) + public MinecraftStream(NetworkStream networkStream, ProtocolVersion protocolVersion) { this.protocolVersion = protocolVersion; this.networkStream = networkStream; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/AddResourcePackPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/AddResourcePackPacket.cs index 214df5d2..4e554344 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/AddResourcePackPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/AddResourcePackPacket.cs @@ -15,7 +15,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// Whether the client is forced to use the resource pack. /// Whether a custom message should be used on the resource pack prompt. /// The custom message shown in the prompt, if present. -public sealed record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, bool Forced, bool HasPromptMessage, Chat? PromptMessage) : IPacket +public sealed partial record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, bool Forced, bool HasPromptMessage, Chat? PromptMessage) : IPacketStatic { /// public PacketType Type => StaticType; @@ -23,7 +23,7 @@ public sealed record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, b public static PacketType StaticType => PacketType.CB_Configuration_AddResourcePack; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(Uuid); buffer.WriteString(Url); @@ -37,7 +37,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static AddResourcePackPacket Read(PacketBuffer buffer, MinecraftData data) { var uuid = buffer.ReadUuid(); var url = buffer.ReadString(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/DisconnectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/DisconnectPacket.cs index 1cf4255d..3a1276b7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/DisconnectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/DisconnectPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// See https://wiki.vg/Protocol#Disconnect_.28configuration.29 /// /// Reason for disconnect -public sealed record DisconnectPacket(Chat Reason) : IPacket +public sealed partial record DisconnectPacket(Chat Reason) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,13 +18,13 @@ public sealed record DisconnectPacket(Chat Reason) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_Disconnect; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Reason); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DisconnectPacket Read(PacketBuffer buffer, MinecraftData data) { return new DisconnectPacket(buffer.ReadChatComponent()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FeatureFlagsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FeatureFlagsPacket.cs index e706cae6..b5962c2a 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FeatureFlagsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FeatureFlagsPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// See https://wiki.vg/Protocol#Feature_Flags /// /// The enabled feature flags -public sealed record FeatureFlagsPacket(Identifier[] FeatureFlags) : IPacket +public sealed partial record FeatureFlagsPacket(Identifier[] FeatureFlags) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,13 +18,13 @@ public sealed record FeatureFlagsPacket(Identifier[] FeatureFlags) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_FeatureFlags; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarIntArray(FeatureFlags, (buff, str) => buff.WriteIdentifier(str)); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static FeatureFlagsPacket Read(PacketBuffer buffer, MinecraftData data) { return new FeatureFlagsPacket(buffer.ReadVarIntArray(buff => buff.ReadIdentifier())); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FinishConfigurationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FinishConfigurationPacket.cs index feb6472f..9e4e81a9 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FinishConfigurationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/FinishConfigurationPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// Finish configuration packet /// See https://wiki.vg/Protocol#Finish_Configuration /// -public sealed record FinishConfigurationPacket : IPacket +public sealed partial record FinishConfigurationPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,11 +16,11 @@ public sealed record FinishConfigurationPacket : IPacket public static PacketType StaticType => PacketType.CB_Configuration_FinishConfiguration; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static FinishConfigurationPacket Read(PacketBuffer buffer, MinecraftData data) { return new FinishConfigurationPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/KeepAlivePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/KeepAlivePacket.cs index 2604ab43..065a1285 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/KeepAlivePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/KeepAlivePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// See https://wiki.vg/Protocol#Clientbound_Keep_Alive_.28configuration.29 /// /// The keep alive id -public sealed record KeepAlivePacket(long KeepAliveId) : IPacket +public sealed partial record KeepAlivePacket(long KeepAliveId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record KeepAlivePacket(long KeepAliveId) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_KeepAlive; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(KeepAliveId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static KeepAlivePacket Read(PacketBuffer buffer, MinecraftData data) { return new KeepAlivePacket(buffer.ReadLong()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PingPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PingPacket.cs index aca1d853..3bff684c 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PingPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PingPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// See https://wiki.vg/Protocol#Ping_.28configuration.29 /// /// The id of the ping -public sealed record PingPacket(int Id) : IPacket +public sealed partial record PingPacket(int Id) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record PingPacket(int Id) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_Ping; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(Id); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PingPacket Read(PacketBuffer buffer, MinecraftData data) { return new PingPacket(buffer.ReadInt()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PluginMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PluginMessagePacket.cs index b974b296..4d30939f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PluginMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PluginMessagePacket.cs @@ -8,9 +8,9 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// /// Plugin message packet /// -/// The name of the channel the data was sent +/// The name of the channel the data was sent /// The message data -public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) : IPacket +public sealed partial record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,18 +18,19 @@ public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) : public static PacketType StaticType => PacketType.CB_Configuration_CustomPayload; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteIdentifier(ChannelName); + buffer.WriteIdentifier(Channel); buffer.WriteBytes(Data); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PluginMessagePacket Read(PacketBuffer buffer, MinecraftData data) { - var channelName = buffer.ReadIdentifier(); - var data = buffer.RestBuffer(); - return new PluginMessagePacket(channelName, data); + var channel = buffer.ReadIdentifier(); + var pluginData = buffer.RestBuffer(); + + return new PluginMessagePacket(channel, pluginData); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RegistryDataPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RegistryDataPacket.cs index 65fb189d..6211abdd 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RegistryDataPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RegistryDataPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// See https://wiki.vg/Protocol#Registry_Data /// /// The registry data -public sealed record RegistryDataPacket(NbtCompound RegistryData) : IPacket +public sealed partial record RegistryDataPacket(NbtCompound RegistryData) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,13 +18,13 @@ public sealed record RegistryDataPacket(NbtCompound RegistryData) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_RegistryData; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteNbt(RegistryData); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RegistryDataPacket Read(PacketBuffer buffer, MinecraftData data) { var registryData = buffer.ReadNbtCompound(); registryData = registryData.NormalizeRegistryDataTopLevelIdentifiers(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RemoveResourcePackPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RemoveResourcePackPacket.cs index 5e7b9954..9233151f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RemoveResourcePackPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/RemoveResourcePackPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// Packet sent by the server to remove a resource pack (or all of them). /// /// The UUID of the resource pack to be removed. Or all of the resource packs if this value is null. -public sealed record RemoveResourcePackPacket(Uuid? Uuid) : IPacket +public sealed partial record RemoveResourcePackPacket(Uuid? Uuid) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,7 +17,7 @@ public sealed record RemoveResourcePackPacket(Uuid? Uuid) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_RemoveResourcePack; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { var hasUuid = Uuid != null; buffer.WriteBool(hasUuid); @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RemoveResourcePackPacket Read(PacketBuffer buffer, MinecraftData data) { var hasUuid = buffer.ReadBool(); Uuid? uuid = hasUuid ? buffer.ReadUuid() : null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/UpdateTagsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/UpdateTagsPacket.cs index d0781961..d6af6301 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/UpdateTagsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Configuration/UpdateTagsPacket.cs @@ -1,8 +1,7 @@ -using MineSharp.Core.Common; -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; -using static MineSharp.Protocol.Packets.Clientbound.Configuration.UpdateTagsPacket; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets.Clientbound.Configuration; @@ -10,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration; /// Update Tags (configuration) packet /// /// Array of registries with their tags -public sealed record UpdateTagsPacket(Registry[] Registries) : IPacket +public sealed partial record UpdateTagsPacket(Registry[] Registries) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,90 +17,16 @@ public sealed record UpdateTagsPacket(Registry[] Registries) : IPacket public static PacketType StaticType => PacketType.CB_Configuration_Tags; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - WriteRegistries(buffer, Registries); + buffer.WriteVarIntArray(Registries, (buffer, registry) => registry.Write(buffer)); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateTagsPacket Read(PacketBuffer buffer, MinecraftData data) { - var registries = ReadRegistries(buffer); - return new UpdateTagsPacket(registries); - } - - private static void WriteRegistries(PacketBuffer buffer, Registry[] registries) - { - buffer.WriteVarInt(registries.Length); - foreach (var registry in registries) - { - buffer.WriteIdentifier(registry.Identifier); - WriteTags(buffer, registry.Tags); - } - } - - private static Registry[] ReadRegistries(PacketBuffer buffer) - { - var registryCount = buffer.ReadVarInt(); - var registries = new Registry[registryCount]; - for (int i = 0; i < registryCount; i++) - { - var registry = buffer.ReadIdentifier(); - var tags = ReadTags(buffer); - registries[i] = new Registry(registry, tags); - } - return registries; - } - - private static void WriteTags(PacketBuffer buffer, Tag[] tags) - { - buffer.WriteVarInt(tags.Length); - foreach (var tag in tags) - { - buffer.WriteIdentifier(tag.Name); - buffer.WriteVarInt(tag.Entries.Length); - foreach (var entry in tag.Entries) - { - buffer.WriteVarInt(entry); - } - } - } - - private static Tag[] ReadTags(PacketBuffer buffer) - { - var tagCount = buffer.ReadVarInt(); - var tags = new Tag[tagCount]; - for (int j = 0; j < tagCount; j++) - { - var tagName = buffer.ReadIdentifier(); - var entries = ReadEntries(buffer); - tags[j] = new Tag(tagName, entries); - } - return tags; - } + var registries = buffer.ReadVarIntArray(Registry.Read); - private static int[] ReadEntries(PacketBuffer buffer) - { - var entryCount = buffer.ReadVarInt(); - var entries = new int[entryCount]; - for (int k = 0; k < entryCount; k++) - { - entries[k] = buffer.ReadVarInt(); - } - return entries; + return new UpdateTagsPacket(registries); } - - /// - /// Represents a registry with its tags - /// - /// The registry identifier - /// Array of tags - public sealed record Registry(Identifier Identifier, Tag[] Tags); - - /// - /// Represents a tag with its entries - /// - /// The tag name - /// Array of entries - public sealed record Tag(Identifier Name, int[] Entries); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Login/DisconnectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Login/DisconnectPacket.cs index 4e6ac2a1..587c449a 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Login/DisconnectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Login/DisconnectPacket.cs @@ -1,6 +1,4 @@ using MineSharp.ChatComponent; -using MineSharp.ChatComponent.Components; -using MineSharp.Core.Common; using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -12,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Login; /// See https://wiki.vg/Protocol#Disconnect_.28login.29 /// /// The reason for being disconnected -public sealed record DisconnectPacket(Chat Reason) : IPacket +public sealed partial record DisconnectPacket(Chat Reason) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,14 +18,14 @@ public sealed record DisconnectPacket(Chat Reason) : IPacket public static PacketType StaticType => PacketType.CB_Login_Disconnect; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { // Disconnect (login) packet is always sent as JSON text component according to wiki.vg buffer.WriteString(Reason.ToJson().ToString()); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DisconnectPacket Read(PacketBuffer buffer, MinecraftData data) { var reason = buffer.ReadString(); // Disconnect (login) packet is always sent as JSON text component according to wiki.vg diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Login/EncryptionRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Login/EncryptionRequestPacket.cs index 1ebc4477..2433fc72 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Login/EncryptionRequestPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Login/EncryptionRequestPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Login; /// The hashed server id /// The public key of the server /// Verify token -public sealed record EncryptionRequestPacket(string ServerId, byte[] PublicKey, byte[] VerifyToken) : IPacket +public sealed partial record EncryptionRequestPacket(string ServerId, byte[] PublicKey, byte[] VerifyToken) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record EncryptionRequestPacket(string ServerId, byte[] PublicKey, public static PacketType StaticType => PacketType.CB_Login_EncryptionBegin; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(ServerId); buffer.WriteVarInt(PublicKey.Length); @@ -29,7 +29,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EncryptionRequestPacket Read(PacketBuffer buffer, MinecraftData data) { var serverId = buffer.ReadString(); var publicKey = new byte[buffer.ReadVarInt()]; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginPluginRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginPluginRequestPacket.cs index f653f89c..1726338d 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginPluginRequestPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginPluginRequestPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Login; /// The message id /// The channel identifier /// The raw message data -public sealed record LoginPluginRequestPacket(int MessageId, Identifier Channel, byte[] Data) : IPacket +public sealed partial record LoginPluginRequestPacket(int MessageId, Identifier Channel, byte[] Data) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record LoginPluginRequestPacket(int MessageId, Identifier Channel, public static PacketType StaticType => PacketType.CB_Login_LoginPluginRequest; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(MessageId); buffer.WriteIdentifier(Channel); @@ -27,12 +27,12 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LoginPluginRequestPacket Read(PacketBuffer buffer, MinecraftData data) { var messageId = buffer.ReadVarInt(); var channel = buffer.ReadIdentifier(); - var data = buffer.RestBuffer(); + var pluginData = buffer.RestBuffer(); - return new LoginPluginRequestPacket(messageId, channel, data); + return new LoginPluginRequestPacket(messageId, channel, pluginData); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginSuccessPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginSuccessPacket.cs index 9cc40318..560c9bf5 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginSuccessPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Login/LoginSuccessPacket.cs @@ -14,7 +14,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Login; /// Uuid /// Username of the client /// A list of properties sent for versions >= 1.19 -public sealed record LoginSuccessPacket(Uuid Uuid, string Username, Property[]? Properties = null) : IPacket +public sealed partial record LoginSuccessPacket(Uuid Uuid, string Username, Property[]? Properties = null) : IPacketStatic { /// public PacketType Type => StaticType; @@ -22,32 +22,32 @@ public sealed record LoginSuccessPacket(Uuid Uuid, string Username, Property[]? public static PacketType StaticType => PacketType.CB_Login_Success; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(Uuid); buffer.WriteString(Username); - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { return; } if (Properties == null) { - throw new MineSharpPacketVersionException(nameof(Properties), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(Properties), data.Version.Protocol); } buffer.WriteVarIntArray(Properties, (buffer, property) => property.Write(buffer)); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LoginSuccessPacket Read(PacketBuffer buffer, MinecraftData data) { var uuid = buffer.ReadUuid(); var username = buffer.ReadString(); Property[]? properties = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { properties = buffer.ReadVarIntArray(Property.Read); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Login/SetCompressionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Login/SetCompressionPacket.cs index 13d4310c..e6ad9bb6 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Login/SetCompressionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Login/SetCompressionPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Login; /// Set Compression packet /// /// Threshold for when to use compression -public sealed record SetCompressionPacket(int Threshold) : IPacket +public sealed partial record SetCompressionPacket(int Threshold) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetCompressionPacket(int Threshold) : IPacket public static PacketType StaticType => PacketType.CB_Login_Compress; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(Threshold); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetCompressionPacket Read(PacketBuffer buffer, MinecraftData data) { var threshold = buffer.ReadVarInt(); return new SetCompressionPacket(threshold); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AcknowledgeBlockChangePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AcknowledgeBlockChangePacket.cs index a3d51fe8..f31b5e61 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AcknowledgeBlockChangePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AcknowledgeBlockChangePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Acknowledge block change packet /// -public sealed record AcknowledgeBlockChangePacket : IPacket +public sealed partial record AcknowledgeBlockChangePacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -57,15 +57,15 @@ internal AcknowledgeBlockChangePacket(IPacketBody body) public IPacketBody Body { get; set; } /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { Body.Write(buffer); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static AcknowledgeBlockChangePacket Read(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { return new AcknowledgeBlockChangePacket(PacketBody118.Read(buffer)); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AddResourcePackPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AddResourcePackPacket.cs index d46e930f..5676fc70 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AddResourcePackPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AddResourcePackPacket.cs @@ -14,7 +14,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// A 40 character hexadecimal, case-insensitive SHA-1 hash of the resource pack file. /// Whether the client is forced to use the resource pack. /// The custom message shown in the prompt, if present. -public sealed record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, bool Forced, Chat? PromptMessage) : IPacket +public sealed partial record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, bool Forced, Chat? PromptMessage) : IPacketStatic { /// public PacketType Type => StaticType; @@ -22,7 +22,7 @@ public sealed record AddResourcePackPacket(Uuid Uuid, string Url, string Hash, b public static PacketType StaticType => PacketType.CB_Play_AddResourcePack; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(Uuid); buffer.WriteString(Url); @@ -37,7 +37,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static AddResourcePackPacket Read(PacketBuffer buffer, MinecraftData data) { var uuid = buffer.ReadUuid(); var url = buffer.ReadString(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AwardStatisticsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AwardStatisticsPacket.cs index 763c0e2b..88f80bd1 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/AwardStatisticsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/AwardStatisticsPacket.cs @@ -2,15 +2,15 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Clientbound.Play.AwardStatisticsPacket; namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Award statistics packet sent as a response to Client Command. /// -/// Number of elements in the statistics array. /// Array of statistics. -public sealed record AwardStatisticsPacket(int Count, AwardStatisticsPacket.Statistic[] Statistics) : IPacket +public sealed partial record AwardStatisticsPacket(Statistic[] Statistics) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,31 +18,17 @@ public sealed record AwardStatisticsPacket(int Count, AwardStatisticsPacket.Stat public static PacketType StaticType => PacketType.CB_Play_Statistics; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(Count); - foreach (var statistic in Statistics) - { - buffer.WriteVarInt((int)statistic.CategoryId); - buffer.WriteVarInt((int)statistic.StatisticId); - buffer.WriteVarInt(statistic.Value); - } + buffer.WriteVarIntArray(Statistics, (buffer, statistic) => statistic.Write(buffer)); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static AwardStatisticsPacket Read(PacketBuffer buffer, MinecraftData data) { - var count = buffer.ReadVarInt(); - var statistics = new Statistic[count]; - for (int i = 0; i < count; i++) - { - var categoryId = (CategoryId)buffer.ReadVarInt(); - var statisticId = (StatisticId)buffer.ReadVarInt(); - var value = buffer.ReadVarInt(); - statistics[i] = new Statistic(categoryId, statisticId, value); - } + var statistics = buffer.ReadVarIntArray(Statistic.Read); - return new AwardStatisticsPacket(count, statistics); + return new(statistics); } /// @@ -51,7 +37,26 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) /// The category ID of the statistic. /// The statistic ID. /// The value of the statistic. - public sealed record Statistic(CategoryId CategoryId, StatisticId StatisticId, int Value); + public sealed record Statistic(CategoryId CategoryId, StatisticId StatisticId, int Value) : ISerializable + { + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteVarInt((int)CategoryId); + buffer.WriteVarInt((int)StatisticId); + buffer.WriteVarInt(Value); + } + + /// + public static Statistic Read(PacketBuffer buffer) + { + var categoryId = (CategoryId)buffer.ReadVarInt(); + var statisticId = (StatisticId)buffer.ReadVarInt(); + var value = buffer.ReadVarInt(); + + return new(categoryId, statisticId, value); + } + } #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member /// diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockActionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockActionPacket.cs index 63f0e7a7..770d70d1 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockActionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockActionPacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Varies depending on block — see Block Actions. /// Varies depending on block — see Block Actions. /// The block type ID for the block. This value is unused by the Notchian client, as it will infer the type of block based on the given position. -public sealed record BlockActionPacket(Position Location, byte ActionId, byte ActionParameter, int BlockType) : IPacket +public sealed partial record BlockActionPacket(Position Location, byte ActionId, byte ActionParameter, int BlockType) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,7 +21,7 @@ public sealed record BlockActionPacket(Position Location, byte ActionId, byte Ac public static PacketType StaticType => PacketType.CB_Play_BlockAction; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WritePosition(Location); buffer.WriteByte(ActionId); @@ -30,7 +30,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static BlockActionPacket Read(PacketBuffer buffer, MinecraftData data) { var location = buffer.ReadPosition(); var actionId = buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockEntityDataPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockEntityDataPacket.cs index 4998ec82..1567d609 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockEntityDataPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockEntityDataPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The location of the block entity /// The type of the block entity /// The NBT data to set -public sealed record BlockEntityDataPacket(Position Location, int BlockEntityType, NbtTag? NbtData) : IPacket +public sealed partial record BlockEntityDataPacket(Position Location, int BlockEntityType, NbtTag? NbtData) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record BlockEntityDataPacket(Position Location, int BlockEntityTyp public static PacketType StaticType => PacketType.CB_Play_TileEntityData; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WritePosition(Location); buffer.WriteVarInt(BlockEntityType); @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static BlockEntityDataPacket Read(PacketBuffer buffer, MinecraftData data) { var location = buffer.ReadPosition(); var type = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockUpdatePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockUpdatePacket.cs index e74a6d31..d84f10ec 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockUpdatePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BlockUpdatePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The location of the block update /// The new state id -public sealed record BlockUpdatePacket(Position Location, int StateId) : IPacket +public sealed partial record BlockUpdatePacket(Position Location, int StateId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record BlockUpdatePacket(Position Location, int StateId) : IPacket public static PacketType StaticType => PacketType.CB_Play_BlockChange; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WritePosition(Location); buffer.WriteVarInt(StateId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static BlockUpdatePacket Read(PacketBuffer buffer, MinecraftData data) { var location = buffer.ReadPosition(); var stateId = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BossBarPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BossBarPacket.cs index 5715115e..156ffbee 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BossBarPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BossBarPacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Unique ID for this bar /// Determines the layout of the remaining packet -public sealed record BossBarPacket(Uuid Uuid, BossBarAction Action) : IPacket +public sealed partial record BossBarPacket(Uuid Uuid, IBossBarAction Action) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,7 +21,7 @@ public sealed record BossBarPacket(Uuid Uuid, BossBarAction Action) : IPacket public static PacketType StaticType => PacketType.CB_Play_BossBar; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(Uuid); buffer.WriteVarInt((int)Action.Type); @@ -29,32 +29,30 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static BossBarPacket Read(PacketBuffer buffer, MinecraftData data) { var uuid = buffer.ReadUuid(); var actionType = (BossBarActionType)buffer.ReadVarInt(); - var action = BossBarAction.Read(buffer, actionType); - return new BossBarPacket(uuid, action); + var action = BossBarActionRegistry.Read(buffer, actionType); + return new(uuid, action); } - #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - /// - /// Represents the action of a boss bar packet - /// - public interface IBossBarAction where T : BossBarAction + public interface IBossBarAction { - static abstract BossBarActionType StaticType { get; } - void Write(PacketBuffer buffer); - static abstract T Read(PacketBuffer buffer); + public BossBarActionType Type { get; } + public void Write(PacketBuffer buffer); } - public abstract record BossBarAction + public interface IBossBarActionStatic { - public abstract BossBarActionType Type { get; } - public abstract void Write(PacketBuffer buffer); + public static abstract BossBarActionType StaticType { get; } + public static abstract IBossBarAction Read(PacketBuffer buffer); + } - public static BossBarAction Read(PacketBuffer buffer, BossBarActionType type) + public static class BossBarActionRegistry + { + public static IBossBarAction Read(PacketBuffer buffer, BossBarActionType type) { if (BossActionReaders.TryGetValue(type, out var reader)) { @@ -63,15 +61,22 @@ public static BossBarAction Read(PacketBuffer buffer, BossBarActionType type) throw new ArgumentOutOfRangeException(); } - public static readonly FrozenDictionary> BossActionReaders = CreateBossActionReadersLookup(); + public static readonly FrozenDictionary> BossActionReaders; + + static BossBarActionRegistry() + { + BossActionReaders = InitializeBossActionReaders(); + } - private static FrozenDictionary> CreateBossActionReadersLookup() + private static FrozenDictionary> InitializeBossActionReaders() { - Dictionary> lookup = new(); + Dictionary> lookup = new(); - void Register() where T : BossBarAction, IBossBarAction + void Register() + where T : IBossBarAction, IBossBarActionStatic { - lookup.Add(T.StaticType, buffer => T.Read(buffer)); + var factory = T.Read; + lookup.Add(T.StaticType, factory); } Register(); @@ -115,12 +120,12 @@ public enum BossBarDivision TwentyNotches = 4 } - public sealed record AddBossBarAction(Chat Title, float Health, BossBarColor Color, BossBarDivision Division, byte Flags) : BossBarAction, IBossBarAction + public sealed record AddBossBarAction(Chat Title, float Health, BossBarColor Color, BossBarDivision Division, byte Flags) : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.Add; - public override void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer) { buffer.WriteChatComponent(Title); buffer.WriteFloat(Health); @@ -136,29 +141,39 @@ public static AddBossBarAction Read(PacketBuffer buffer) var color = (BossBarColor)buffer.ReadVarInt(); var division = (BossBarDivision)buffer.ReadVarInt(); var flags = buffer.ReadByte(); - return new AddBossBarAction(title, health, color, division, flags); + return new(title, health, color, division, flags); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } - public sealed record RemoveBossBarAction() : BossBarAction(), IBossBarAction + public sealed record RemoveBossBarAction() : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.Remove; - public override void Write(PacketBuffer buffer) { } + public void Write(PacketBuffer buffer) { } public static RemoveBossBarAction Read(PacketBuffer buffer) { - return new RemoveBossBarAction(); + return new(); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } - public sealed record UpdateHealthBossBarAction(float Health) : BossBarAction, IBossBarAction + public sealed record UpdateHealthBossBarAction(float Health) : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.UpdateHealth; - public override void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer) { buffer.WriteFloat(Health); } @@ -166,16 +181,21 @@ public override void Write(PacketBuffer buffer) public static UpdateHealthBossBarAction Read(PacketBuffer buffer) { var health = buffer.ReadFloat(); - return new UpdateHealthBossBarAction(health); + return new(health); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } - public sealed record UpdateTitleBossBarAction(Chat Title) : BossBarAction, IBossBarAction + public sealed record UpdateTitleBossBarAction(Chat Title) : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.UpdateTitle; - public override void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer) { buffer.WriteChatComponent(Title); } @@ -183,16 +203,21 @@ public override void Write(PacketBuffer buffer) public static UpdateTitleBossBarAction Read(PacketBuffer buffer) { var title = buffer.ReadChatComponent(); - return new UpdateTitleBossBarAction(title); + return new(title); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } - public sealed record UpdateStyleBossBarAction(BossBarColor Color, BossBarDivision Division) : BossBarAction, IBossBarAction + public sealed record UpdateStyleBossBarAction(BossBarColor Color, BossBarDivision Division) : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.UpdateStyle; - public override void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer) { buffer.WriteVarInt((int)Color); buffer.WriteVarInt((int)Division); @@ -202,16 +227,21 @@ public static UpdateStyleBossBarAction Read(PacketBuffer buffer) { var color = (BossBarColor)buffer.ReadVarInt(); var division = (BossBarDivision)buffer.ReadVarInt(); - return new UpdateStyleBossBarAction(color, division); + return new(color, division); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } - public sealed record UpdateFlagsBossBarAction(byte Flags) : BossBarAction, IBossBarAction + public sealed record UpdateFlagsBossBarAction(byte Flags) : IBossBarAction, IBossBarActionStatic, ISerializable { - public override BossBarActionType Type => StaticType; + public BossBarActionType Type => StaticType; public static BossBarActionType StaticType => BossBarActionType.UpdateFlags; - public override void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer) { buffer.WriteByte(Flags); } @@ -219,7 +249,12 @@ public override void Write(PacketBuffer buffer) public static UpdateFlagsBossBarAction Read(PacketBuffer buffer) { var flags = buffer.ReadByte(); - return new UpdateFlagsBossBarAction(flags); + return new(flags); + } + + static IBossBarAction IBossBarActionStatic.Read(PacketBuffer buffer) + { + return Read(buffer); } } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BundleDelimiterPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BundleDelimiterPacket.cs index 9ae9af88..8ad0c01e 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/BundleDelimiterPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/BundleDelimiterPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Bundle delimiter packet /// -public sealed record BundleDelimiterPacket : IPacket +public sealed partial record BundleDelimiterPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -15,11 +15,11 @@ public sealed record BundleDelimiterPacket : IPacket public static PacketType StaticType => PacketType.CB_Play_BundleDelimiter; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static BundleDelimiterPacket Read(PacketBuffer buffer, MinecraftData data) { return new BundleDelimiterPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChangeDifficultyPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChangeDifficultyPacket.cs index 09a87f19..75ac081a 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChangeDifficultyPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChangeDifficultyPacket.cs @@ -1,7 +1,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; -using static MineSharp.Protocol.Packets.Clientbound.Play.ChangeDifficultyPacket; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets.Clientbound.Play; @@ -10,39 +10,26 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The difficulty setting /// Whether the difficulty is locked -public sealed record ChangeDifficultyPacket(DifficultyLevel Difficulty, bool DifficultyLocked) : IPacket +public sealed partial record ChangeDifficultyPacket(DifficultyLevel Difficulty, bool DifficultyLocked) : IPacketStatic { - /// - public PacketType Type => StaticType; - /// - public static PacketType StaticType => PacketType.CB_Play_Difficulty; + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_Difficulty; - /// - public void Write(PacketBuffer buffer, MinecraftData version) - { - buffer.Write((byte)Difficulty); - buffer.WriteBool(DifficultyLocked); - } + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.Write((byte)Difficulty); + buffer.WriteBool(DifficultyLocked); + } - /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) - { - var difficulty = (DifficultyLevel)buffer.ReadByte(); - var difficultyLocked = buffer.ReadBool(); + /// + public static ChangeDifficultyPacket Read(PacketBuffer buffer, MinecraftData data) + { + var difficulty = (DifficultyLevel)buffer.ReadByte(); + var difficultyLocked = buffer.ReadBool(); - return new ChangeDifficultyPacket(difficulty, difficultyLocked); - } - - /// - /// Enum representing the difficulty levels - /// - public enum DifficultyLevel : byte - { -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - Peaceful = 0, - Easy = 1, - Normal = 2, - Hard = 3 -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } + return new ChangeDifficultyPacket(difficulty, difficultyLocked); + } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatPacket.cs index 89a41da4..008ae6d7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The chat message /// The position of the chat message /// The UUID of the message sender -public sealed record ChatPacket(string Message, byte Position, Uuid Sender) : IPacket +public sealed partial record ChatPacket(string Message, byte Position, Uuid Sender) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record ChatPacket(string Message, byte Position, Uuid Sender) : IP public static PacketType StaticType => PacketType.CB_Play_Chat; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Message); buffer.WriteByte(Position); @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChatPacket Read(PacketBuffer buffer, MinecraftData data) { return new ChatPacket( buffer.ReadString(), diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatSuggestionsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatSuggestionsPacket.cs index b7cf5930..1f4460c5 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatSuggestionsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChatSuggestionsPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The action to perform /// Number of elements in the following array /// Array of chat suggestions -public sealed record ChatSuggestionsPacket(ChatSuggestionAction Action, int Count, string[] Entries) : IPacket +public sealed partial record ChatSuggestionsPacket(ChatSuggestionAction Action, int Count, string[] Entries) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record ChatSuggestionsPacket(ChatSuggestionAction Action, int Coun public static PacketType StaticType => PacketType.CB_Play_ChatSuggestions; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Action); buffer.WriteVarInt(Count); @@ -27,7 +27,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChatSuggestionsPacket Read(PacketBuffer buffer, MinecraftData data) { var action = (ChatSuggestionAction)buffer.ReadVarInt(); var count = buffer.ReadVarInt(); @@ -36,15 +36,15 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) return new ChatSuggestionsPacket(action, count, entries); } - /// - /// Enum representing the action for chat suggestions - /// - public enum ChatSuggestionAction - { + /// + /// Enum representing the action for chat suggestions + /// + public enum ChatSuggestionAction + { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - Add = 0, + Add = 0, Remove = 1, Set = 2 #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } + } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchFinishedPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchFinishedPacket.cs index 1d50d2f2..a53a3ed5 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchFinishedPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchFinishedPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// https://wiki.vg/Protocol#Chunk_Batch_Finished /// /// The batch size -public sealed record ChunkBatchFinishedPacket(int BatchSize) : IPacket +public sealed partial record ChunkBatchFinishedPacket(int BatchSize) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record ChunkBatchFinishedPacket(int BatchSize) : IPacket public static PacketType StaticType => PacketType.CB_Play_ChunkBatchFinished; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(BatchSize); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChunkBatchFinishedPacket Read(PacketBuffer buffer, MinecraftData data) { return new ChunkBatchFinishedPacket( buffer.ReadVarInt()); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchStartPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchStartPacket.cs index 476dbcbf..098f7e70 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchStartPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBatchStartPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The ChunkBatchStart Packet, used since 1.20.2 /// https://wiki.vg/Protocol#Chunk_Batch_Start /// -public sealed record ChunkBatchStartPacket : IPacket +public sealed partial record ChunkBatchStartPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,11 +16,11 @@ public sealed record ChunkBatchStartPacket : IPacket public static PacketType StaticType => PacketType.CB_Play_ChunkBatchStart; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChunkBatchStartPacket Read(PacketBuffer buffer, MinecraftData data) { return new ChunkBatchStartPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBiomesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBiomesPacket.cs index 04dc460b..56825081 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBiomesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkBiomesPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Number of chunks /// Array of chunk biome data -public sealed record ChunkBiomesPacket(int NumberOfChunks, ChunkBiomeData[] ChunkBiomes) : IPacket +public sealed partial record ChunkBiomesPacket(int NumberOfChunks, ChunkBiomeData[] ChunkBiomes) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,30 +18,23 @@ public sealed record ChunkBiomesPacket(int NumberOfChunks, ChunkBiomeData[] Chun public static PacketType StaticType => PacketType.CB_Play_ChunkBiomes; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(NumberOfChunks); foreach (var chunk in ChunkBiomes) { - buffer.WriteInt(chunk.ChunkZ); - buffer.WriteInt(chunk.ChunkX); - buffer.WriteVarInt(chunk.Size); - buffer.WriteBytes(chunk.Data); + chunk.Write(buffer); } } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChunkBiomesPacket Read(PacketBuffer buffer, MinecraftData data) { var numberOfChunks = buffer.ReadVarInt(); var chunkBiomeData = new ChunkBiomeData[numberOfChunks]; for (int i = 0; i < numberOfChunks; i++) { - var chunkZ = buffer.ReadInt(); - var chunkX = buffer.ReadInt(); - var size = buffer.ReadVarInt(); - var data = buffer.ReadBytes(size); - chunkBiomeData[i] = new ChunkBiomeData(chunkZ, chunkX, size, data); + chunkBiomeData[i] = ChunkBiomeData.Read(buffer); } return new ChunkBiomesPacket(numberOfChunks, chunkBiomeData); @@ -54,5 +47,26 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) /// Chunk X coordinate /// Size of data in bytes /// Chunk data structure - public sealed record ChunkBiomeData(int ChunkZ, int ChunkX, int Size, byte[] Data); + public sealed record ChunkBiomeData(int ChunkZ, int ChunkX, int Size, byte[] Data) : ISerializable + { + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteInt(ChunkZ); + buffer.WriteInt(ChunkX); + buffer.WriteVarInt(Size); + buffer.WriteBytes(Data); + } + + /// + public static ChunkBiomeData Read(PacketBuffer buffer) + { + var chunkZ = buffer.ReadInt(); + var chunkX = buffer.ReadInt(); + var size = buffer.ReadVarInt(); + var data = buffer.ReadBytes(size); + + return new(chunkZ, chunkX, size, data); + } + } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkDataAndUpdateLightPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkDataAndUpdateLightPacket.cs index caf195e2..a5f5c44b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkDataAndUpdateLightPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ChunkDataAndUpdateLightPacket.cs @@ -23,7 +23,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// /// -public sealed record ChunkDataAndUpdateLightPacket( +public sealed partial record ChunkDataAndUpdateLightPacket( int X, int Z, NbtCompound Heightmaps, @@ -35,7 +35,7 @@ public sealed record ChunkDataAndUpdateLightPacket( long[] EmptySkyLightMask, long[] EmptyBlockLightMask, byte[][] SkyLight, - byte[][] BlockLight) : IPacket + byte[][] BlockLight) : IPacketStatic { /// public PacketType Type => StaticType; @@ -43,11 +43,11 @@ public sealed record ChunkDataAndUpdateLightPacket( public static PacketType StaticType => PacketType.CB_Play_MapChunk; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol < ProtocolVersion.V_1_20 && TrustEdges == null) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0 && TrustEdges == null) { - throw new MineSharpPacketVersionException(nameof(TrustEdges), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(TrustEdges), data.Version.Protocol); } buffer.WriteInt(X); @@ -62,7 +62,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBlockEntity(entity); } - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { buffer.WriteBool(TrustEdges!.Value); } @@ -88,7 +88,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChunkDataAndUpdateLightPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadInt(); var z = buffer.ReadInt(); @@ -103,7 +103,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) } bool? trustEdges = null; - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { trustEdges = buffer.ReadBool(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ClearTitlesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ClearTitlesPacket.cs index 3bdaac48..e9ac2394 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ClearTitlesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ClearTitlesPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Clear Titles packet /// /// Whether to reset the client's current title information -public sealed record ClearTitlesPacket(bool Reset) : IPacket +public sealed partial record ClearTitlesPacket(bool Reset) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record ClearTitlesPacket(bool Reset) : IPacket public static PacketType StaticType => PacketType.CB_Play_ClearTitles; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteBool(Reset); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ClearTitlesPacket Read(PacketBuffer buffer, MinecraftData data) { var reset = buffer.ReadBool(); return new ClearTitlesPacket(reset); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CloseWindowPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CloseWindowPacket.cs index 427c4234..8514f027 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CloseWindowPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CloseWindowPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Close window packet /// The id of the window to close /// -public sealed record CloseWindowPacket(byte WindowId) : IPacket +public sealed partial record CloseWindowPacket(byte WindowId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record CloseWindowPacket(byte WindowId) : IPacket public static PacketType StaticType => PacketType.CB_Play_CloseWindow; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static CloseWindowPacket Read(PacketBuffer buffer, MinecraftData data) { return new CloseWindowPacket(buffer.ReadByte()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CombatDeathPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CombatDeathPacket.cs index b3b7bb04..2060b4d8 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CombatDeathPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CombatDeathPacket.cs @@ -5,11 +5,11 @@ using MineSharp.Data.Protocol; namespace MineSharp.Protocol.Packets.Clientbound.Play; -#pragma warning disable CS1591 + /// /// Combat death packet /// -public sealed record CombatDeathPacket : IPacket +public sealed partial record CombatDeathPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -71,10 +71,10 @@ private CombatDeathPacket(int playerId, int? entityId, Chat message) public Chat Message { get; init; } /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(PlayerId); - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { buffer.WriteInt(EntityId!.Value); } @@ -83,11 +83,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static CombatDeathPacket Read(PacketBuffer buffer, MinecraftData data) { var playerId = buffer.ReadVarInt(); int? entityId = null; - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { entityId = buffer.ReadInt(); } @@ -96,4 +96,3 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) return new CombatDeathPacket(playerId, entityId, message); } } -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CommandSuggestionsResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CommandSuggestionsResponsePacket.cs index b8ab2471..0c37d731 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/CommandSuggestionsResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/CommandSuggestionsResponsePacket.cs @@ -13,41 +13,41 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Start of the text to replace /// Length of the text to replace /// Array of matches -public sealed record CommandSuggestionsResponsePacket(int Id, int Start, int Length, Match[] Matches) : IPacket +public sealed partial record CommandSuggestionsResponsePacket(int Id, int Start, int Length, Match[] Matches) : IPacketStatic { - /// - public PacketType Type => StaticType; - /// - public static PacketType StaticType => PacketType.CB_Play_TabComplete; + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_TabComplete; - /// - public void Write(PacketBuffer buffer, MinecraftData version) - { - buffer.WriteVarInt(Id); - buffer.WriteVarInt(Start); - buffer.WriteVarInt(Length); - buffer.WriteVarInt(Matches.Length); - foreach (var match in Matches) - { - match.Write(buffer); - } - } + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(Id); + buffer.WriteVarInt(Start); + buffer.WriteVarInt(Length); + buffer.WriteVarInt(Matches.Length); + foreach (var match in Matches) + { + match.Write(buffer); + } + } - /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) - { - var id = buffer.ReadVarInt(); - var start = buffer.ReadVarInt(); - var length = buffer.ReadVarInt(); - var count = buffer.ReadVarInt(); - var matches = new Match[count]; - for (int i = 0; i < count; i++) - { - matches[i] = Match.Read(buffer); - } + /// + public static CommandSuggestionsResponsePacket Read(PacketBuffer buffer, MinecraftData data) + { + var id = buffer.ReadVarInt(); + var start = buffer.ReadVarInt(); + var length = buffer.ReadVarInt(); + var count = buffer.ReadVarInt(); + var matches = new Match[count]; + for (int i = 0; i < count; i++) + { + matches[i] = Match.Read(buffer); + } - return new CommandSuggestionsResponsePacket(id, start, length, matches); - } + return new CommandSuggestionsResponsePacket(id, start, length, matches); + } /// /// Represents a match in the command suggestions response @@ -55,31 +55,31 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) /// The match string /// The tooltip text component or null public sealed record Match(string Value, Chat? Tooltip) : ISerializable - { - /// - public void Write(PacketBuffer buffer) - { - buffer.WriteString(Value); + { + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteString(Value); var hasTooltip = Tooltip != null; buffer.WriteBool(hasTooltip); - if (hasTooltip) - { - buffer.WriteChatComponent(Tooltip!); - } - } + if (hasTooltip) + { + buffer.WriteChatComponent(Tooltip!); + } + } - /// - public static Match Read(PacketBuffer buffer) - { - var match = buffer.ReadString(); - var hasTooltip = buffer.ReadBool(); - Chat? tooltip = null; - if (hasTooltip) - { - tooltip = buffer.ReadChatComponent(); - } + /// + public static Match Read(PacketBuffer buffer) + { + var match = buffer.ReadString(); + var hasTooltip = buffer.ReadBool(); + Chat? tooltip = null; + if (hasTooltip) + { + tooltip = buffer.ReadChatComponent(); + } - return new Match(match, tooltip); - } - } + return new Match(match, tooltip); + } + } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DamageEventPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DamageEventPacket.cs index 909b7ca9..ed25f225 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DamageEventPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DamageEventPacket.cs @@ -15,7 +15,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The X coordinate of the source position, if present /// The Y coordinate of the source position, if present /// The Z coordinate of the source position, if present -public sealed record DamageEventPacket( +public sealed partial record DamageEventPacket( int EntityId, int SourceTypeId, int SourceCauseId, @@ -23,7 +23,7 @@ public sealed record DamageEventPacket( bool HasSourcePosition, double? SourcePositionX, double? SourcePositionY, - double? SourcePositionZ) : IPacket + double? SourcePositionZ) : IPacketStatic { /// public PacketType Type => StaticType; @@ -31,7 +31,7 @@ public sealed record DamageEventPacket( public static PacketType StaticType => PacketType.CB_Play_DamageEvent; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarInt(SourceTypeId); @@ -47,7 +47,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DamageEventPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var sourceTypeId = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeclareCommandsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeclareCommandsPacket.cs index 1053a2f2..6362870e 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeclareCommandsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeclareCommandsPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Raw buffer. The Command tree is not parsed here /// -public sealed record DeclareCommandsPacket(PacketBuffer RawBuffer) : IPacket +public sealed partial record DeclareCommandsPacket(PacketBuffer RawBuffer) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,15 +18,15 @@ public sealed record DeclareCommandsPacket(PacketBuffer RawBuffer) : IPacket public static PacketType StaticType => PacketType.CB_Play_DeclareCommands; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteBytes(RawBuffer.GetBuffer()); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DeclareCommandsPacket Read(PacketBuffer buffer, MinecraftData data) { - var clone = new PacketBuffer(buffer.ReadBytes((int)buffer.ReadableBytes), version.Version.Protocol); + var clone = new PacketBuffer(buffer.ReadBytes((int)buffer.ReadableBytes), data.Version.Protocol); return new DeclareCommandsPacket(clone); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeleteMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeleteMessagePacket.cs index 3163eabf..d2bef8ab 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeleteMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DeleteMessagePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The message ID + 1, used for validating message signature. /// The previous message's signature. Always 256 bytes and not length-prefixed. -public sealed record DeleteMessagePacket(int MessageId, byte[]? Signature) : IPacket +public sealed partial record DeleteMessagePacket(int MessageId, byte[]? Signature) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,7 +18,7 @@ public sealed record DeleteMessagePacket(int MessageId, byte[]? Signature) : IPa public static PacketType StaticType => PacketType.CB_Play_HideMessage; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(MessageId); if (MessageId == 0 && Signature != null) @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DeleteMessagePacket Read(PacketBuffer buffer, MinecraftData data) { var messageId = buffer.ReadVarInt(); byte[]? signature = null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisconnectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisconnectPacket.cs index bc6cc656..5baf71bb 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisconnectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisconnectPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The reason for being disconnected /// -public sealed record DisconnectPacket(Chat Reason) : IPacket +public sealed partial record DisconnectPacket(Chat Reason) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,13 +19,13 @@ public sealed record DisconnectPacket(Chat Reason) : IPacket public static PacketType StaticType => PacketType.CB_Play_KickDisconnect; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Reason); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DisconnectPacket Read(PacketBuffer buffer, MinecraftData data) { return new DisconnectPacket(buffer.ReadChatComponent()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisguisedChatMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisguisedChatMessagePacket.cs index e649c865..2381598a 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisguisedChatMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisguisedChatMessagePacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The type of chat /// The name associated with the chat /// The target of the chat message (optional) -public sealed record DisguisedChatMessagePacket(Chat Message, int ChatType, Chat Name, Chat? Target) : IPacket +public sealed partial record DisguisedChatMessagePacket(Chat Message, int ChatType, Chat Name, Chat? Target) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record DisguisedChatMessagePacket(Chat Message, int ChatType, Chat public static PacketType StaticType => PacketType.CB_Play_ProfilelessChat; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Message); buffer.WriteVarInt(ChatType); @@ -33,7 +33,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DisguisedChatMessagePacket Read(PacketBuffer buffer, MinecraftData data) { return new DisguisedChatMessagePacket( buffer.ReadChatComponent(), diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisplayObjectivePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisplayObjectivePacket.cs index f0bb9da6..c6787b2d 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisplayObjectivePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/DisplayObjectivePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The position of the scoreboard. /// The unique name for the scoreboard to be displayed. -public sealed record DisplayObjectivePacket(int Position, string ScoreName) : IPacket +public sealed partial record DisplayObjectivePacket(int Position, string ScoreName) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record DisplayObjectivePacket(int Position, string ScoreName) : IP public static PacketType StaticType => PacketType.CB_Play_ScoreboardDisplayObjective; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(Position); buffer.WriteString(ScoreName); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static DisplayObjectivePacket Read(PacketBuffer buffer, MinecraftData data) { var position = buffer.ReadVarInt(); var scoreName = buffer.ReadString(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EndCombatPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EndCombatPacket.cs index 4b0a2e36..8fb4353f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EndCombatPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EndCombatPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// End Combat packet /// /// Length of the combat in ticks -public sealed record EndCombatPacket(int Duration) : IPacket +public sealed partial record EndCombatPacket(int Duration) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record EndCombatPacket(int Duration) : IPacket public static PacketType StaticType => PacketType.CB_Play_EndCombatEvent; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(Duration); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EndCombatPacket Read(PacketBuffer buffer, MinecraftData data) { var duration = buffer.ReadVarInt(); return new EndCombatPacket(duration); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EnterCombatPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EnterCombatPacket.cs index c8412768..c6aca435 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EnterCombatPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EnterCombatPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Enter Combat packet /// -public sealed record EnterCombatPacket() : IPacket +public sealed partial record EnterCombatPacket() : IPacketStatic { /// public PacketType Type => StaticType; @@ -15,13 +15,13 @@ public sealed record EnterCombatPacket() : IPacket public static PacketType StaticType => PacketType.CB_Play_EnterCombatEvent; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { // No fields to write } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EnterCombatPacket Read(PacketBuffer buffer, MinecraftData data) { // No fields to read return new EnterCombatPacket(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityAnimationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityAnimationPacket.cs index 69f42cc5..ec58b4c2 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityAnimationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityAnimationPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// The animation ID -public sealed record EntityAnimationPacket(int EntityId, EntityAnimation Animation) : IPacket +public sealed partial record EntityAnimationPacket(int EntityId, EntityAnimation Animation) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record EntityAnimationPacket(int EntityId, EntityAnimation Animati public static PacketType StaticType => PacketType.CB_Play_Animation; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteByte((byte)Animation); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntityAnimationPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var animation = (EntityAnimation)buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityEffectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityEffectPacket.cs new file mode 100644 index 00000000..3d798cfd --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityEffectPacket.cs @@ -0,0 +1,105 @@ +using fNbt; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Clientbound.Play.EntityEffectPacket; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Represents the entity effect packet sent by the server to the client. +/// +/// The entity ID +/// The effect ID +/// The amplifier of the effect +/// The duration of the effect in ticks. -1 for infinity +/// The flags for the effect +/// Indicates if the effect has factor data +/// The factor codec data +public sealed partial record EntityEffectPacket( + int EntityId, + int EffectId, + byte Amplifier, + int Duration, + EffectFlags Flags, + bool HasFactorData, + NbtTag? FactorCodec) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_EntityEffect; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(EntityId); + buffer.WriteVarInt(EffectId); + buffer.WriteByte(Amplifier); + buffer.WriteVarInt(Duration); + buffer.WriteByte((byte)Flags); + buffer.WriteBool(HasFactorData); + if (HasFactorData) + { + buffer.WriteNbt(FactorCodec!); + } + } + + /// + public static EntityEffectPacket Read(PacketBuffer buffer, MinecraftData data) + { + var entityId = buffer.ReadVarInt(); + var effectId = buffer.ReadVarInt(); + var amplifier = buffer.ReadByte(); + var duration = buffer.ReadVarInt(); + var flags = (EffectFlags)buffer.ReadByte(); + var hasFactorData = buffer.ReadBool(); + NbtTag? factorCodec = null; + if (hasFactorData) + { + // TODO: Check if this is correct + factorCodec = buffer.ReadNbt(); + } + + return new EntityEffectPacket( + entityId, + effectId, + amplifier, + duration, + flags, + hasFactorData, + factorCodec); + } + + /// + /// Effect flags used in the entity effect packet. + /// + [Flags] + public enum EffectFlags + { + /// + /// No flags + /// + None = 0, + /// + /// Is ambient - was the effect spawned from a beacon? + /// All beacon-generated effects are ambient. + /// Ambient effects use a different icon in the HUD (blue border rather than gray). + /// If all effects on an entity are ambient, the "Is potion effect ambient" living metadata field should be set to true. + /// Usually should not be enabled. + /// + IsAmbient = 0x01, + /// + /// Show particles - should all particles from this effect be hidden? + /// Effects with particles hidden are not included in the calculation of the effect color, + /// and are not rendered on the HUD (but are still rendered within the inventory). + /// Usually should be enabled. + /// + ShowParticles = 0x02, + /// + /// Show icon - should the icon be displayed on the client? + /// Usually should be enabled. + /// + ShowIcon = 0x04 + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionAndRotationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionAndRotationPacket.cs index 0e70a4ad..264b4de7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionAndRotationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionAndRotationPacket.cs @@ -14,7 +14,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The yaw rotation /// The pitch rotation /// Whether the entity is on the ground -public sealed record EntityPositionAndRotationPacket(int EntityId, short DeltaX, short DeltaY, short DeltaZ, sbyte Yaw, sbyte Pitch, bool OnGround) : IPacket +public sealed partial record EntityPositionAndRotationPacket(int EntityId, short DeltaX, short DeltaY, short DeltaZ, sbyte Yaw, sbyte Pitch, bool OnGround) : IPacketStatic { /// public PacketType Type => StaticType; @@ -22,7 +22,7 @@ public sealed record EntityPositionAndRotationPacket(int EntityId, short DeltaX, public static PacketType StaticType => PacketType.CB_Play_EntityMoveLook; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteShort(DeltaX); @@ -34,7 +34,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntityPositionAndRotationPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var deltaX = buffer.ReadShort(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionPacket.cs index 288e2445..c25d3318 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityPositionPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The change in Y position /// The change in Z position /// Whether the entity is on the ground -public sealed record EntityPositionPacket(int EntityId, short DeltaX, short DeltaY, short DeltaZ, bool OnGround) : IPacket +public sealed partial record EntityPositionPacket(int EntityId, short DeltaX, short DeltaY, short DeltaZ, bool OnGround) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record EntityPositionPacket(int EntityId, short DeltaX, short Delt public static PacketType StaticType => PacketType.CB_Play_RelEntityMove; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteShort(DeltaX); @@ -30,7 +30,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntityPositionPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var deltaX = buffer.ReadShort(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityRotationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityRotationPacket.cs index 4303e478..cfdfc065 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityRotationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityRotationPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The yaw rotation /// The pitch rotation /// Whether the entity is on the ground -public sealed record EntityRotationPacket(int EntityId, sbyte Yaw, sbyte Pitch, bool OnGround) : IPacket +public sealed partial record EntityRotationPacket(int EntityId, sbyte Yaw, sbyte Pitch, bool OnGround) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record EntityRotationPacket(int EntityId, sbyte Yaw, sbyte Pitch, public static PacketType StaticType => PacketType.CB_Play_EntityLook; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteSByte(Yaw); @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntityRotationPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var yaw = buffer.ReadSByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntitySoundEffectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntitySoundEffectPacket.cs index c2678cc3..07117b7d 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntitySoundEffectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntitySoundEffectPacket.cs @@ -5,7 +5,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record EntitySoundEffectPacket( +public sealed partial record EntitySoundEffectPacket( int SoundId, Identifier? SoundName, bool? HasFixedRange, @@ -15,14 +15,14 @@ public sealed record EntitySoundEffectPacket( float Volume, float Pitch, long Seed -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_EntitySoundEffect; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(SoundId); if (SoundId == 0) @@ -41,7 +41,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteLong(Seed); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntitySoundEffectPacket Read(PacketBuffer buffer, MinecraftData data) { var soundId = buffer.ReadVarInt(); Identifier? soundName = null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityStatusPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityStatusPacket.cs index ce7314f1..8a5d6573 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityStatusPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/EntityStatusPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// The status byte -public sealed record EntityStatusPacket(int EntityId, byte Status) : IPacket +public sealed partial record EntityStatusPacket(int EntityId, sbyte Status) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,18 +17,22 @@ public sealed record EntityStatusPacket(int EntityId, byte Status) : IPacket public static PacketType StaticType => PacketType.CB_Play_EntityStatus; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(EntityId); - buffer.WriteByte(Status); + buffer.WriteInt(EntityId); + buffer.WriteSByte(Status); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EntityStatusPacket Read(PacketBuffer buffer, MinecraftData data) { - return new EntityStatusPacket( - buffer.ReadInt(), - buffer.ReadByte()); + var entityId = buffer.ReadInt(); + var status = buffer.ReadSByte(); + + return new EntityStatusPacket(entityId, status); } -} + // TODO: Add all the meanings of the status + // not all statuses are valid for all entities + // https://wiki.vg/Entity_statuses +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ExplosionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ExplosionPacket.cs new file mode 100644 index 00000000..cf255bd0 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ExplosionPacket.cs @@ -0,0 +1,149 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; +using static MineSharp.Protocol.Packets.Clientbound.Play.ExplosionPacket; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Explosion packet sent when an explosion occurs (creepers, TNT, and ghast fireballs). +/// +/// The X coordinate of the explosion. +/// The Y coordinate of the explosion. +/// The Z coordinate of the explosion. +/// The strength of the explosion. +/// The affected block records. +/// The X velocity of the player being pushed by the explosion. +/// The Y velocity of the player being pushed by the explosion. +/// The Z velocity of the player being pushed by the explosion. +/// The block interaction type. +/// The particle ID for small explosion particles. +/// The particle data for small explosion particles. +/// The particle ID for large explosion particles. +/// The particle data for large explosion particles. +/// The sound played during the explosion. +public sealed partial record ExplosionPacket( + double X, + double Y, + double Z, + float Strength, + (byte X, byte Y, byte Z)[] Records, + float PlayerMotionX, + float PlayerMotionY, + float PlayerMotionZ, + BlockInteractionType BlockInteraction, + int SmallExplosionParticleID, + IParticleData? SmallExplosionParticleData, + int LargeExplosionParticleID, + IParticleData? LargeExplosionParticleData, + ExplosionSound Sound +) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_Explosion; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteDouble(X); + buffer.WriteDouble(Y); + buffer.WriteDouble(Z); + buffer.WriteFloat(Strength); + buffer.WriteVarInt(Records.Length); + foreach (var record in Records) + { + buffer.WriteByte(record.X); + buffer.WriteByte(record.Y); + buffer.WriteByte(record.Z); + } + buffer.WriteFloat(PlayerMotionX); + buffer.WriteFloat(PlayerMotionY); + buffer.WriteFloat(PlayerMotionZ); + buffer.WriteVarInt((int)BlockInteraction); + buffer.WriteVarInt(SmallExplosionParticleID); + SmallExplosionParticleData?.Write(buffer, data); + buffer.WriteVarInt(LargeExplosionParticleID); + LargeExplosionParticleData?.Write(buffer, data); + Sound.Write(buffer); + } + + /// + public static ExplosionPacket Read(PacketBuffer buffer, MinecraftData data) + { + var x = buffer.ReadDouble(); + var y = buffer.ReadDouble(); + var z = buffer.ReadDouble(); + var strength = buffer.ReadFloat(); + var recordCount = buffer.ReadVarInt(); + var records = new (byte X, byte Y, byte Z)[recordCount]; + for (int i = 0; i < recordCount; i++) + { + records[i] = (buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte()); + } + var playerMotionX = buffer.ReadFloat(); + var playerMotionY = buffer.ReadFloat(); + var playerMotionZ = buffer.ReadFloat(); + var blockInteraction = (BlockInteractionType)buffer.ReadVarInt(); + var smallExplosionParticleID = buffer.ReadVarInt(); + var smallExplosionParticleData = ParticleDataRegistry.Read(buffer, data, smallExplosionParticleID); + var largeExplosionParticleID = buffer.ReadVarInt(); + var largeExplosionParticleData = ParticleDataRegistry.Read(buffer, data, largeExplosionParticleID); + var sound = ExplosionSound.Read(buffer); + + return new ExplosionPacket( + x, y, z, strength, records, playerMotionX, playerMotionY, playerMotionZ, + blockInteraction, smallExplosionParticleID, smallExplosionParticleData, + largeExplosionParticleID, largeExplosionParticleData, sound + ); + } + + /// + /// Represents the sound played during an explosion. + /// + /// The name of the sound played. + /// The fixed range of the sound. + public sealed record ExplosionSound( + Identifier SoundName, + float? Range + ) : ISerializable + { + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteIdentifier(SoundName); + var hasFixedRange = Range.HasValue; + buffer.WriteBool(hasFixedRange); + if (hasFixedRange) + { + buffer.WriteFloat(Range!.Value); + } + } + + /// + public static ExplosionSound Read(PacketBuffer buffer) + { + var soundName = buffer.ReadIdentifier(); + var hasFixedRange = buffer.ReadBool(); + float? range = hasFixedRange ? buffer.ReadFloat() : null; + + return new ExplosionSound(soundName, range); + } + } + + /// + /// Enum representing the block interaction type during an explosion. + /// + public enum BlockInteractionType + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + Keep = 0, + Destroy = 1, + DestroyWithDecay = 2, + TriggerBlock = 3 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/GameEventPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/GameEventPacket.cs index c04cdff1..989f704e 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/GameEventPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/GameEventPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The game event /// The value associated with the event -public sealed record GameEventPacket(GameEvent Event, float Value) : IPacket +public sealed partial record GameEventPacket(GameEvent Event, float Value) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record GameEventPacket(GameEvent Event, float Value) : IPacket public static PacketType StaticType => PacketType.CB_Play_GameStateChange; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte((byte)Event); buffer.WriteFloat(Value); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static GameEventPacket Read(PacketBuffer buffer, MinecraftData data) { var @event = buffer.ReadByte(); var value = buffer.ReadFloat(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/HurtAnimationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/HurtAnimationPacket.cs index 2be5c4e5..01f11b1e 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/HurtAnimationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/HurtAnimationPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The ID of the entity taking damage /// The direction the damage is coming from in relation to the entity -public sealed record HurtAnimationPacket(int EntityId, float Yaw) : IPacket +public sealed partial record HurtAnimationPacket(int EntityId, float Yaw) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record HurtAnimationPacket(int EntityId, float Yaw) : IPacket public static PacketType StaticType => PacketType.CB_Play_HurtAnimation; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteFloat(Yaw); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static HurtAnimationPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var yaw = buffer.ReadFloat(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/InitializeWorldBorderPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/InitializeWorldBorderPacket.cs index 6f086d2e..bafbddc1 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/InitializeWorldBorderPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/InitializeWorldBorderPacket.cs @@ -15,7 +15,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Resulting coordinates from a portal teleport are limited to ±value /// Warning distance in meters /// Warning time in seconds -public sealed record InitializeWorldBorderPacket( +public sealed partial record InitializeWorldBorderPacket( double X, double Z, double OldDiameter, @@ -23,7 +23,7 @@ public sealed record InitializeWorldBorderPacket( long Speed, int PortalTeleportBoundary, int WarningBlocks, - int WarningTime) : IPacket + int WarningTime) : IPacketStatic { /// public PacketType Type => StaticType; @@ -31,7 +31,7 @@ public sealed record InitializeWorldBorderPacket( public static PacketType StaticType => PacketType.CB_Play_InitializeWorldBorder; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Z); @@ -44,7 +44,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static InitializeWorldBorderPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var z = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/KeepAlivePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/KeepAlivePacket.cs index 80e0f6bf..fccc196b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/KeepAlivePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/KeepAlivePacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Keep alive packet /// /// The keep-alive ID -public sealed record KeepAlivePacket(long KeepAliveId) : IPacket +public sealed partial record KeepAlivePacket(long KeepAliveId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -15,13 +15,13 @@ public sealed record KeepAlivePacket(long KeepAliveId) : IPacket public static PacketType StaticType => PacketType.CB_Play_KeepAlive; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(KeepAliveId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static KeepAlivePacket Read(PacketBuffer buffer, MinecraftData data) { var id = buffer.ReadLong(); return new KeepAlivePacket(id); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/LinkEntitiesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LinkEntitiesPacket.cs new file mode 100644 index 00000000..55d65a80 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LinkEntitiesPacket.cs @@ -0,0 +1,34 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Packet sent when an entity has been leashed to another entity. +/// +/// The attached entity's ID +/// The ID of the entity holding the lead. Set to -1 to detach. +public sealed partial record LinkEntitiesPacket(int AttachedEntityId, int HoldingEntityId) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_AttachEntity; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteInt(AttachedEntityId); + buffer.WriteInt(HoldingEntityId); + } + + /// + public static LinkEntitiesPacket Read(PacketBuffer buffer, MinecraftData data) + { + var attachedEntityId = buffer.ReadInt(); + var holdingEntityId = buffer.ReadInt(); + + return new LinkEntitiesPacket(attachedEntityId, holdingEntityId); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/LoginPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LoginPacket.cs index 6f8ecf7a..0ac22d7b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/LoginPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LoginPacket.cs @@ -33,7 +33,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The death location. /// The portal cooldown. /// Indicates if limited crafting is enabled. -public sealed record LoginPacket( +public sealed partial record LoginPacket( int EntityId, bool IsHardcore, byte GameMode, @@ -54,7 +54,7 @@ public sealed record LoginPacket( Identifier? DeathDimensionName, Position? DeathLocation, int? PortalCooldown, - bool? DoLimitedCrafting) : IPacket + bool? DoLimitedCrafting) : IPacketStatic { /// public PacketType Type => StaticType; @@ -62,9 +62,9 @@ public sealed record LoginPacket( public static PacketType StaticType => PacketType.CB_Play_Login; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { throw new NotSupportedException( $"{nameof(LoginPacket)}.Write() is not supported for versions before 1.19."); @@ -72,14 +72,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteInt(EntityId); buffer.WriteBool(IsHardcore); - if (version.Version.Protocol < ProtocolVersion.V_1_20_2) + if (data.Version.Protocol < ProtocolVersion.V_1_20_2) { buffer.WriteByte(GameMode); buffer.WriteSByte(PreviousGameMode); } buffer.WriteVarIntArray(DimensionNames, (buf, val) => buf.WriteIdentifier(val)); - if (version.Version.Protocol < ProtocolVersion.V_1_20_2) + if (data.Version.Protocol < ProtocolVersion.V_1_20_2) { buffer.WriteOptionalNbt(RegistryCodec); buffer.WriteIdentifier(DimensionType); @@ -92,7 +92,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteVarInt(SimulationDistance); buffer.WriteBool(ReducedDebugInfo); buffer.WriteBool(EnableRespawnScreen); - if (version.Version.Protocol >= ProtocolVersion.V_1_20_2) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_2) { buffer.WriteBool(DoLimitedCrafting!.Value); buffer.WriteIdentifier(DimensionType); @@ -112,11 +112,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WritePosition(DeathLocation ?? throw new InvalidOperationException($"{nameof(DeathLocation)} must not be null if {nameof(HasDeathLocation)} is true.")); } - if (version.Version.Protocol >= ProtocolVersion.V_1_20) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_0) { if (PortalCooldown == null) { - throw new MineSharpPacketVersionException(nameof(PortalCooldown), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(PortalCooldown), data.Version.Protocol); } buffer.WriteVarInt(PortalCooldown.Value); @@ -124,11 +124,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LoginPacket Read(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol >= ProtocolVersion.V_1_20_2) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_2) { - return ReadV1_20_2(buffer, version); + return ReadV1_20_2(buffer, data); } var entityId = buffer.ReadInt(); @@ -140,7 +140,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) registryCodec = registryCodec?.NormalizeRegistryDataTopLevelIdentifiers(); Identifier dimensionType; - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { var dimensionTypeNbt = buffer.ReadNbtCompound(); dimensionType = Identifier.Parse(dimensionTypeNbt.Get("effects")!.Value); @@ -163,7 +163,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) Identifier? deathDimensionName = null; Position? deathLocation = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { hasDeathLocation = buffer.ReadBool(); if (hasDeathLocation.Value) @@ -174,7 +174,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) } int? portalCooldown = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_20) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_0) { portalCooldown = buffer.ReadVarInt(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/LookAtPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LookAtPacket.cs index 23965fb0..2c339d3b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/LookAtPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/LookAtPacket.cs @@ -15,7 +15,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// If true, additional information about an entity is provided. /// The entity to face towards. Only if IsEntity is true. /// Whether to look at the entity's eyes or feet. Same values and meanings as FeetOrEyes, just for the entity's head/feet. Only if IsEntity is true. -public sealed record LookAtPacket(LookAtPosition FeetOrEyes, double TargetX, double TargetY, double TargetZ, bool IsEntity, int? EntityId, LookAtPosition? EntityFeetOrEyes) : IPacket +public sealed partial record LookAtPacket(LookAtPosition FeetOrEyes, double TargetX, double TargetY, double TargetZ, bool IsEntity, int? EntityId, LookAtPosition? EntityFeetOrEyes) : IPacketStatic { /// public PacketType Type => StaticType; @@ -23,7 +23,7 @@ public sealed record LookAtPacket(LookAtPosition FeetOrEyes, double TargetX, dou public static PacketType StaticType => PacketType.CB_Play_FacePlayer; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)FeetOrEyes); buffer.WriteDouble(TargetX); @@ -39,7 +39,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LookAtPacket Read(PacketBuffer buffer, MinecraftData data) { var feetOrEyes = (LookAtPosition)buffer.ReadVarInt(); var targetX = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MapDataPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MapDataPacket.cs index 5a0857bd..e9949aa2 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MapDataPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MapDataPacket.cs @@ -15,7 +15,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Indicates if the map has icons /// Array of icons on the map /// Details of the color patch update -public sealed record MapDataPacket(int MapId, byte Scale, bool Locked, bool HasIcons, Icon[]? Icons, ColorPatchInfo ColorPatch) : IPacket +public sealed partial record MapDataPacket(int MapId, byte Scale, bool Locked, bool HasIcons, Icon[]? Icons, ColorPatchInfo ColorPatch) : IPacketStatic { /// public PacketType Type => StaticType; @@ -23,7 +23,7 @@ public sealed record MapDataPacket(int MapId, byte Scale, bool Locked, bool HasI public static PacketType StaticType => PacketType.CB_Play_Map; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(MapId); buffer.WriteByte(Scale); @@ -46,7 +46,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static MapDataPacket Read(PacketBuffer buffer, MinecraftData data) { var mapId = buffer.ReadVarInt(); var scale = buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MerchantOffersPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MerchantOffersPacket.cs index 44c0211a..385ed81b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MerchantOffersPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MerchantOffersPacket.cs @@ -1,5 +1,4 @@ -using MineSharp.Core.Common; -using MineSharp.Core.Common.Items; +using MineSharp.Core.Common.Items; using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -18,7 +17,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Total experience for this villager (always 0 for the wandering trader). /// True if this is a regular villager; false for the wandering trader. /// True for regular villagers and false for the wandering trader. -public sealed record MerchantOffersPacket(int WindowId, int Size, Trade[] Trades, int VillagerLevel, int Experience, bool IsRegularVillager, bool CanRestock) : IPacket +public sealed partial record MerchantOffersPacket(int WindowId, int Size, Trade[] Trades, int VillagerLevel, int Experience, bool IsRegularVillager, bool CanRestock) : IPacketStatic { /// public PacketType Type => StaticType; @@ -26,13 +25,13 @@ public sealed record MerchantOffersPacket(int WindowId, int Size, Trade[] Trades public static PacketType StaticType => PacketType.CB_Play_TradeList; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(WindowId); buffer.WriteVarInt(Size); foreach (var trade in Trades) { - trade.Write(buffer, version); + trade.Write(buffer, data); } buffer.WriteVarInt(VillagerLevel); buffer.WriteVarInt(Experience); @@ -41,14 +40,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static MerchantOffersPacket Read(PacketBuffer buffer, MinecraftData data) { var windowId = buffer.ReadVarInt(); var size = buffer.ReadVarInt(); var trades = new Trade[size]; for (int i = 0; i < size; i++) { - trades[i] = Trade.Read(buffer, version); + trades[i] = Trade.Read(buffer, data); } var villagerLevel = buffer.ReadVarInt(); var experience = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MoveVehiclePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MoveVehiclePacket.cs index d05510d4..cb10f6dd 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MoveVehiclePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MoveVehiclePacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Absolute position (Z coordinate) /// Absolute rotation on the vertical axis, in degrees /// Absolute rotation on the horizontal axis, in degrees -public sealed record MoveVehiclePacket(double X, double Y, double Z, float Yaw, float Pitch) : IPacket +public sealed partial record MoveVehiclePacket(double X, double Y, double Z, float Yaw, float Pitch) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record MoveVehiclePacket(double X, double Y, double Z, float Yaw, public static PacketType StaticType => PacketType.CB_Play_VehicleMove; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Y); @@ -30,7 +30,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static MoveVehiclePacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var y = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MultiBlockUpdatePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MultiBlockUpdatePacket.cs index d3e9e3d5..ed688485 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/MultiBlockUpdatePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/MultiBlockUpdatePacket.cs @@ -6,7 +6,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record MultiBlockUpdatePacket : IPacket +public sealed partial record MultiBlockUpdatePacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -50,15 +50,15 @@ public MultiBlockUpdatePacket(long chunkSection, long[] blocks) public bool? SuppressLightUpdates { get; init; } public long[] Blocks { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol < ProtocolVersion.V_1_20 && SuppressLightUpdates == null) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0 && SuppressLightUpdates == null) { - throw new MineSharpPacketVersionException(nameof(SuppressLightUpdates), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(SuppressLightUpdates), data.Version.Protocol); } buffer.WriteLong(ChunkSection); - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { buffer.WriteBool(SuppressLightUpdates!.Value); } @@ -66,11 +66,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteVarIntArray(Blocks, (buf, val) => buf.WriteLong(val)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static MultiBlockUpdatePacket Read(PacketBuffer buffer, MinecraftData data) { var chunkSection = buffer.ReadLong(); bool? suppressLightUpdates = null; - if (version.Version.Protocol < ProtocolVersion.V_1_20) + if (data.Version.Protocol < ProtocolVersion.V_1_20_0) { suppressLightUpdates = buffer.ReadBool(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenBookPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenBookPacket.cs index 7896533a..36ad70b9 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenBookPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenBookPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Sent when a player right clicks with a signed book. This tells the client to open the book GUI. /// /// The hand used to open the book. -public sealed record OpenBookPacket(PlayerHand Hand) : IPacket +public sealed partial record OpenBookPacket(PlayerHand Hand) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record OpenBookPacket(PlayerHand Hand) : IPacket public static PacketType StaticType => PacketType.CB_Play_OpenBook; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Hand); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static OpenBookPacket Read(PacketBuffer buffer, MinecraftData data) { var hand = (PlayerHand)buffer.ReadVarInt(); return new OpenBookPacket(hand); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenHorseScreenPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenHorseScreenPacket.cs index b828788e..9020252a 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenHorseScreenPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenHorseScreenPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The window ID /// The number of slots in the horse inventory /// The entity ID of the horse -public sealed record OpenHorseScreenPacket(byte WindowId, int SlotCount, int EntityId) : IPacket +public sealed partial record OpenHorseScreenPacket(byte WindowId, int SlotCount, int EntityId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,7 +18,7 @@ public sealed record OpenHorseScreenPacket(byte WindowId, int SlotCount, int Ent public static PacketType StaticType => PacketType.CB_Play_OpenHorseWindow; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); buffer.WriteVarInt(SlotCount); @@ -26,7 +26,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static OpenHorseScreenPacket Read(PacketBuffer buffer, MinecraftData data) { var windowId = buffer.ReadByte(); var slotCount = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenSignEditorPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenSignEditorPacket.cs index 04543ad4..cfa70ddc 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenSignEditorPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenSignEditorPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The position of the sign /// Whether the opened editor is for the front or on the back of the sign -public sealed record OpenSignEditorPacket(Position Location, bool IsFrontText) : IPacket +public sealed partial record OpenSignEditorPacket(Position Location, bool IsFrontText) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,14 +19,14 @@ public sealed record OpenSignEditorPacket(Position Location, bool IsFrontText) : public static PacketType StaticType => PacketType.CB_Play_OpenSignEntity; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WritePosition(Location); buffer.WriteBool(IsFrontText); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static OpenSignEditorPacket Read(PacketBuffer buffer, MinecraftData data) { var location = buffer.ReadPosition(); var isFrontText = buffer.ReadBool(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenWindowPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenWindowPacket.cs index 270bf6be..37155df6 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenWindowPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/OpenWindowPacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The type of the inventory. /// The title of the window. /// The chat component of the window title. -public sealed record OpenWindowPacket(int WindowId, int InventoryType, string WindowTitle, Chat? WindowTitleChat = null) : IPacket +public sealed partial record OpenWindowPacket(int WindowId, int InventoryType, string WindowTitle, Chat? WindowTitleChat = null) : IPacketStatic { /// public PacketType Type => StaticType; @@ -26,7 +26,7 @@ public OpenWindowPacket(int windowId, int inventoryType, Chat windowTitle) } /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(WindowId); buffer.WriteVarInt(InventoryType); @@ -34,9 +34,9 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static OpenWindowPacket Read(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol == ProtocolVersion.V_1_20_3) + if (data.Version.Protocol == ProtocolVersion.V_1_20_3) { return new OpenWindowPacket( buffer.ReadVarInt(), diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ParticlePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ParticlePacket.cs index 644d9c6d..bfd02841 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ParticlePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ParticlePacket.cs @@ -1,6 +1,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets.Clientbound.Play; @@ -21,7 +22,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Data depends on the particle id. /// Will be an empty buffer for most particles. /// -public sealed record ParticlePacket( +public sealed partial record ParticlePacket( int ParticleId, bool LongDistance, double X, @@ -32,8 +33,8 @@ public sealed record ParticlePacket( float OffsetZ, float MaxSpeed, int ParticleCount, - PacketBuffer Data -) : IPacket + IParticleData? Data +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -41,7 +42,7 @@ PacketBuffer Data public static PacketType StaticType => PacketType.CB_Play_WorldParticles; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(ParticleId); buffer.WriteBool(LongDistance); @@ -53,11 +54,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteFloat(OffsetZ); buffer.WriteFloat(MaxSpeed); buffer.WriteVarInt(ParticleCount); - buffer.WriteBytes(Data.GetBuffer()); + Data?.Write(buffer, data); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ParticlePacket Read(PacketBuffer buffer, MinecraftData data) { var particleId = buffer.ReadVarInt(); var longDistance = buffer.ReadBool(); @@ -69,9 +70,8 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) var offsetZ = buffer.ReadFloat(); var maxSpeed = buffer.ReadFloat(); var particleCount = buffer.ReadVarInt(); - var byteBuffer = buffer.RestBuffer(); - var data = new PacketBuffer(byteBuffer, buffer.ProtocolVersion); + var particleData = ParticleDataRegistry.Read(buffer, data, particleId); - return new ParticlePacket(particleId, longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, data); + return new ParticlePacket(particleId, longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, particleData); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PickupItemPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PickupItemPacket.cs index 602531ab..65d76762 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PickupItemPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PickupItemPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The ID of the collected entity /// The ID of the collector entity /// The number of items picked up. Seems to be 1 for XP orbs, otherwise the number of items in the stack. -public sealed record PickupItemPacket(int CollectedEntityId, int CollectorEntityId, int PickupItemCount) : IPacket +public sealed partial record PickupItemPacket(int CollectedEntityId, int CollectorEntityId, int PickupItemCount) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record PickupItemPacket(int CollectedEntityId, int CollectorEntity public static PacketType StaticType => PacketType.CB_Play_Collect; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(CollectedEntityId); buffer.WriteVarInt(CollectorEntityId); @@ -27,7 +27,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PickupItemPacket Read(PacketBuffer buffer, MinecraftData data) { var collectedEntityId = buffer.ReadVarInt(); var collectorEntityId = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingPacket.cs index a173e792..df6896f4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Ping Packet https://wiki.vg/Protocol#Ping_.28play.29 /// /// The id of the ping -public sealed record PingPacket(int Id) : IPacket +public sealed partial record PingPacket(int Id) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record PingPacket(int Id) : IPacket public static PacketType StaticType => PacketType.CB_Play_Ping; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(Id); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PingPacket Read(PacketBuffer buffer, MinecraftData data) { return new PingPacket(buffer.ReadInt()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingResponsePacket.cs new file mode 100644 index 00000000..b976d4e8 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PingResponsePacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Ping response packet +/// +/// The payload, should be the same as sent by the client +public sealed partial record PingResponsePacket(long Payload) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_PingResponse; + + /// + public void Write(PacketBuffer buffer, MinecraftData version) + { + buffer.WriteLong(Payload); + } + + /// + public static PingResponsePacket Read(PacketBuffer buffer, MinecraftData version) + { + var payload = buffer.ReadLong(); + + return new(payload); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlaceGhostRecipePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlaceGhostRecipePacket.cs index 36cb9279..7173fb36 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlaceGhostRecipePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlaceGhostRecipePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The window ID /// A recipe ID -public sealed record PlaceGhostRecipePacket(byte WindowId, Identifier Recipe) : IPacket +public sealed partial record PlaceGhostRecipePacket(byte WindowId, Identifier Recipe) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record PlaceGhostRecipePacket(byte WindowId, Identifier Recipe) : public static PacketType StaticType => PacketType.CB_Play_CraftRecipeResponse; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); buffer.WriteIdentifier(Recipe); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlaceGhostRecipePacket Read(PacketBuffer buffer, MinecraftData data) { var windowId = buffer.ReadByte(); var recipe = buffer.ReadIdentifier(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerAbilitiesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerAbilitiesPacket.cs index da9fec4a..f56f35f7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerAbilitiesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerAbilitiesPacket.cs @@ -1,7 +1,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; -using static MineSharp.Protocol.Packets.Clientbound.Play.PlayerAbilitiesPacket; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets.Clientbound.Play; @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Bit field indicating various abilities. /// The flying speed of the player. /// Modifies the field of view, like a speed potion. -public sealed record PlayerAbilitiesPacket(PlayerAbilitiesFlags Flags, float FlyingSpeed, float FieldOfViewModifier) : IPacket +public sealed partial record PlayerAbilitiesPacket(PlayerAbilitiesFlags Flags, float FlyingSpeed, float FieldOfViewModifier) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,45 +19,20 @@ public sealed record PlayerAbilitiesPacket(PlayerAbilitiesFlags Flags, float Fly public static PacketType StaticType => PacketType.CB_Play_Abilities; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteByte((byte)Flags); + buffer.WriteSByte((sbyte)Flags); buffer.WriteFloat(FlyingSpeed); buffer.WriteFloat(FieldOfViewModifier); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerAbilitiesPacket Read(PacketBuffer buffer, MinecraftData data) { - var flags = (PlayerAbilitiesFlags)buffer.ReadByte(); - var flyingSpeed = buffer.ReadByte(); - var fieldOfViewModifier = buffer.ReadByte(); + var flags = (PlayerAbilitiesFlags)buffer.ReadSByte(); + var flyingSpeed = buffer.ReadFloat(); + var fieldOfViewModifier = buffer.ReadFloat(); return new PlayerAbilitiesPacket(flags, flyingSpeed, fieldOfViewModifier); } - - /// - /// Flags representing various player abilities. - /// - [Flags] - public enum PlayerAbilitiesFlags : byte - { - /// - /// Player is invulnerable. - /// - Invulnerable = 0x01, - /// - /// Player is flying. - /// - Flying = 0x02, - /// - /// Player is allowed to fly. - /// - AllowFlying = 0x04, - /// - /// Player is in creative mode. - /// And can instantly break blocks. - /// - CreativeMode = 0x08, - } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerChatPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerChatPacket.cs index 60d14ab1..3d5b155f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerChatPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerChatPacket.cs @@ -9,28 +9,28 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record PlayerChatPacket(IChatMessageBody Body) : IPacket +public sealed partial record PlayerChatPacket(IChatMessageBody Body) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_PlayerChat; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - Body.Write(buffer, version); + Body.Write(buffer, data); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerChatPacket Read(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol == ProtocolVersion.V_1_19) + if (data.Version.Protocol == ProtocolVersion.V_1_19_0) { return new PlayerChatPacket(V119Body.Read(buffer)); } - if (version.Version.Protocol >= ProtocolVersion.V_1_19_2) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_1) { - return new PlayerChatPacket(V11923Body.Read(buffer, version)); + return new PlayerChatPacket(V11923Body.Read(buffer, data)); } throw new NotImplementedException(); @@ -38,7 +38,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) public interface IChatMessageBody { - void Write(PacketBuffer buffer, MinecraftData version); + void Write(PacketBuffer buffer, MinecraftData data); } /// @@ -66,7 +66,7 @@ byte[] Signature ) : IChatMessageBody { /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(SignedChat); @@ -262,9 +262,9 @@ public V11923Body( public Chat NetworkName { get; init; } public Chat? NetworkTargetName { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { var hasPreviousSignature = PreviousSignature != null; buffer.WriteBool(hasPreviousSignature); @@ -277,7 +277,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteUuid(Sender); - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { buffer.WriteVarInt(Signature!.Length); buffer.WriteBytes(Signature); @@ -294,7 +294,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteString(PlainMessage); - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { var hasFormattedMessage = FormattedMessage != null; buffer.WriteBool(hasFormattedMessage); @@ -332,7 +332,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } } - public static V11923Body Read(PacketBuffer buffer, MinecraftData version) + public static V11923Body Read(PacketBuffer buffer, MinecraftData data) { byte[]? previousSignature = null; Uuid sender; @@ -350,7 +350,7 @@ public static V11923Body Read(PacketBuffer buffer, MinecraftData version) Chat networkName; Chat? networkTargetName = null; - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { var hasPreviousSignature = buffer.ReadBool(); if (hasPreviousSignature) @@ -362,7 +362,7 @@ public static V11923Body Read(PacketBuffer buffer, MinecraftData version) sender = buffer.ReadUuid(); - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { signature = new byte[buffer.ReadVarInt()]; buffer.ReadBytes(signature); @@ -381,7 +381,7 @@ public static V11923Body Read(PacketBuffer buffer, MinecraftData version) plainMessage = buffer.ReadString(); - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { var hasFormattedMessage = buffer.ReadBool(); formattedMessage = null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoRemovePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoRemovePacket.cs index 76dba485..aa7c6ae4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoRemovePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoRemovePacket.cs @@ -5,19 +5,19 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record PlayerInfoRemovePacket(Uuid[] Players) : IPacket +public sealed partial record PlayerInfoRemovePacket(Uuid[] Players) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_PlayerRemove; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarIntArray(Players, (buffer, uuid) => buffer.WriteUuid(uuid)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerInfoRemovePacket Read(PacketBuffer buffer, MinecraftData data) { var players = buffer.ReadVarIntArray(buffer => buffer.ReadUuid()); return new PlayerInfoRemovePacket(players); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoUpdatePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoUpdatePacket.cs index 627e425b..5931c687 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoUpdatePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerInfoUpdatePacket.cs @@ -4,6 +4,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; using static MineSharp.Protocol.Packets.Clientbound.Play.PlayerInfoUpdatePacket; using static MineSharp.Protocol.Packets.Clientbound.Play.PlayerInfoUpdatePacket.AddPlayerAction; using static MineSharp.Protocol.Packets.Clientbound.Play.PlayerInfoUpdatePacket.InitializeChatAction; @@ -11,16 +12,16 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record PlayerInfoUpdatePacket(int Action, ActionEntry[] Data) : IPacket +public sealed partial record PlayerInfoUpdatePacket(int Action, ActionEntry[] Data) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_PlayerInfo; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { buffer.WriteSByte((sbyte)Action); } @@ -29,32 +30,29 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteVarInt(Action); } - buffer.WriteVarInt(Data.Length); - foreach (var data in Data) - { - data.Write(buffer); - } + buffer.WriteVarIntArray(Data, (buffer, actionData) => actionData.Write(buffer, data)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerInfoUpdatePacket Read(PacketBuffer buffer, MinecraftData data) { int action; - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { - action = buffer.ReadByte(); + action = buffer.ReadSByte(); } else { action = buffer.ReadVarInt(); } - var data = buffer.ReadVarIntArray(buffer => ActionEntry.Read(buffer, version, action)); - return new PlayerInfoUpdatePacket(action, data); + var actionData = buffer.ReadVarIntArray(buffer => ActionEntry.Read(buffer, data, action)); + + return new(action, actionData); } - public sealed record ActionEntry(Uuid Player, IPlayerInfoAction[] Actions) + public sealed record ActionEntry(Uuid Player, IPlayerInfoAction[] Actions) : ISerializableWithMinecraftData { - public void Write(PacketBuffer buffer) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(Player); foreach (var action in Actions) @@ -63,11 +61,16 @@ public void Write(PacketBuffer buffer) } } - public static ActionEntry Read(PacketBuffer buffer, MinecraftData version, int action) + public static ActionEntry Read(PacketBuffer buffer, MinecraftData data) + { + throw new NotImplementedException("This type does not support the normal Read method."); + } + + public static ActionEntry Read(PacketBuffer buffer, MinecraftData data, int action) { var uuid = buffer.ReadUuid(); var actions = new List(); - if (version.Version.Protocol <= ProtocolVersion.V_18_2) + if (data.Version.Protocol <= ProtocolVersion.V_1_18_2) { switch (action) { @@ -152,7 +155,7 @@ public interface IPlayerInfoActionStatic public static abstract IPlayerInfoAction Read(PacketBuffer buffer); } - public sealed record AddPlayerAction(string Name, Property[] Properties) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record AddPlayerAction(string Name, Property[] Properties) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x01; public int Mask => StaticMask; @@ -175,7 +178,7 @@ static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) return Read(buffer); } - public sealed record Property(string Name, string Value, string? Signature) + public sealed record Property(string Name, string Value, string? Signature) : ISerializable { public void Write(PacketBuffer buffer) { @@ -206,7 +209,7 @@ public static Property Read(PacketBuffer buffer) } } - public sealed record UpdateGameModeAction(GameMode GameMode) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record UpdateGameModeAction(GameMode GameMode) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x04; public int Mask => StaticMask; @@ -228,7 +231,7 @@ static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) } } - public sealed record UpdateListedAction(bool Listed) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record UpdateListedAction(bool Listed) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x08; public int Mask => StaticMask; @@ -250,7 +253,7 @@ static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) } } - public sealed record UpdateLatencyAction(int Ping) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record UpdateLatencyAction(int Ping) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x10; public int Mask => StaticMask; @@ -272,7 +275,7 @@ static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) } } - public sealed record UpdateDisplayName(string? DisplayName) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record UpdateDisplayName(string? DisplayName) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x20; public int Mask => StaticMask; @@ -305,7 +308,7 @@ static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) } } - public sealed record InitializeChatAction(InitializeChatActionData? Data) : IPlayerInfoAction, IPlayerInfoActionStatic + public sealed record InitializeChatAction(InitializeChatActionData? Data) : IPlayerInfoAction, IPlayerInfoActionStatic, ISerializable { public static int StaticMask => 0x02; public int Mask => StaticMask; @@ -315,24 +318,40 @@ public sealed record InitializeChatActionData( long PublicKeyExpiryTime, byte[] EncodedPublicKey, byte[] PublicKeySignature - ); + ) : ISerializable + { + public void Write(PacketBuffer buffer) + { + buffer.WriteUuid(ChatSessionId); + buffer.WriteLong(PublicKeyExpiryTime); + buffer.WriteVarInt(EncodedPublicKey.Length); + buffer.WriteBytes(EncodedPublicKey); + buffer.WriteVarInt(PublicKeySignature.Length); + buffer.WriteBytes(PublicKeySignature); + } + + public static InitializeChatActionData Read(PacketBuffer buffer) + { + var chatSessionId = buffer.ReadUuid(); + var publicKeyExpiryTime = buffer.ReadLong(); + var encodedPublicKey = new byte[buffer.ReadVarInt()]; + buffer.ReadBytes(encodedPublicKey); + var publicKeySignature = new byte[buffer.ReadVarInt()]; + buffer.ReadBytes(publicKeySignature); + + return new(chatSessionId, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); + } + } public void Write(PacketBuffer buffer) { var present = Data != null; buffer.WriteBool(present); - if (!present) + if (present) { - return; + Data!.Write(buffer); } - - buffer.WriteUuid(Data!.ChatSessionId); - buffer.WriteLong(Data!.PublicKeyExpiryTime); - buffer.WriteVarInt(Data!.EncodedPublicKey.Length); - buffer.WriteBytes(Data!.EncodedPublicKey); - buffer.WriteVarInt(Data!.PublicKeySignature.Length); - buffer.WriteBytes(Data!.PublicKeySignature); } public static InitializeChatAction Read(PacketBuffer buffer) @@ -343,14 +362,9 @@ public static InitializeChatAction Read(PacketBuffer buffer) return new InitializeChatAction((InitializeChatActionData?)null); } - var chatSessionId = buffer.ReadUuid(); - var publicKeyExpiryTime = buffer.ReadLong(); - var encodedPublicKey = new byte[buffer.ReadVarInt()]; - buffer.ReadBytes(encodedPublicKey); - var publicKeySignature = new byte[buffer.ReadVarInt()]; - buffer.ReadBytes(publicKeySignature); + var data = InitializeChatActionData.Read(buffer); - return new InitializeChatAction(new InitializeChatActionData(chatSessionId, publicKeyExpiryTime, encodedPublicKey, publicKeySignature)); + return new(data); } static IPlayerInfoAction IPlayerInfoActionStatic.Read(PacketBuffer buffer) diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerPositionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerPositionPacket.cs index 8502b454..06cef166 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerPositionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PlayerPositionPacket.cs @@ -6,7 +6,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record PlayerPositionPacket : IPacket +public sealed partial record PlayerPositionPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -73,7 +73,7 @@ public PlayerPositionPacket(double x, double y, double z, float yaw, float pitch public int TeleportId { get; init; } public bool? DismountVehicle { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Y); @@ -83,20 +83,20 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteSByte((sbyte)Flags); buffer.WriteVarInt(TeleportId); - if (version.Version.Protocol >= ProtocolVersion.V_1_19_4) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_4) { return; } if (!DismountVehicle.HasValue) { - throw new MineSharpPacketVersionException(nameof(DismountVehicle), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(DismountVehicle), data.Version.Protocol); } buffer.WriteBool(DismountVehicle.Value); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerPositionPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var y = buffer.ReadDouble(); @@ -106,7 +106,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) var flags = (PositionFlags)buffer.ReadSByte(); var teleportId = buffer.ReadVarInt(); - if (version.Version.Protocol >= ProtocolVersion.V_1_19_4) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_4) { return new PlayerPositionPacket(x, y, z, yaw, pitch, flags, teleportId); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PluginMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PluginMessagePacket.cs index 044c46d4..d5a0ea81 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/PluginMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/PluginMessagePacket.cs @@ -9,8 +9,8 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Clientbound Plugin Message packet /// /// Name of the plugin channel used to send the data -/// Any data. The length of this array must be inferred from the packet length -public sealed record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacket +/// Any data, depending on the channel +public sealed partial record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,18 +18,18 @@ public sealed record PluginMessagePacket(Identifier Channel, byte[] Data) : IPac public static PacketType StaticType => PacketType.CB_Play_CustomPayload; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteIdentifier(Channel); buffer.WriteBytes(Data); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PluginMessagePacket Read(PacketBuffer buffer, MinecraftData data) { var channel = buffer.ReadObject(); - var data = buffer.RestBuffer(); + var pluginData = buffer.RestBuffer(); - return new PluginMessagePacket(channel, data); + return new PluginMessagePacket(channel, pluginData); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntitiesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntitiesPacket.cs index 196f01e4..aa9adc87 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntitiesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntitiesPacket.cs @@ -4,19 +4,19 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record RemoveEntitiesPacket(int[] EntityIds) : IPacket +public sealed partial record RemoveEntitiesPacket(int[] EntityIds) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_EntityDestroy; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarIntArray(EntityIds, (buf, i) => buf.WriteVarInt(i)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RemoveEntitiesPacket Read(PacketBuffer buffer, MinecraftData data) { var entityIds = buffer.ReadVarIntArray(buf => buf.ReadVarInt()); return new RemoveEntitiesPacket(entityIds); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntityEffectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntityEffectPacket.cs index 1dab7f86..ffa2638c 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntityEffectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveEntityEffectPacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// The effect ID -public sealed record RemoveEntityEffectPacket(int EntityId, int EffectId) : IPacket +public sealed partial record RemoveEntityEffectPacket(int EntityId, int EffectId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record RemoveEntityEffectPacket(int EntityId, int EffectId) : IPac public static PacketType StaticType => PacketType.CB_Play_RemoveEntityEffect; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarInt(EffectId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RemoveEntityEffectPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var effectId = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveResourcePackPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveResourcePackPacket.cs index 8298275d..7dd12aad 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveResourcePackPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RemoveResourcePackPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to remove a resource pack. /// /// The UUID of the resource pack to be removed. -public sealed record RemoveResourcePackPacket(Uuid? Uuid) : IPacket +public sealed partial record RemoveResourcePackPacket(Uuid? Uuid) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,7 +17,7 @@ public sealed record RemoveResourcePackPacket(Uuid? Uuid) : IPacket public static PacketType StaticType => PacketType.CB_Play_RemoveResourcePack; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { var hasUuid = Uuid != null; buffer.WriteBool(hasUuid); @@ -28,7 +28,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RemoveResourcePackPacket Read(PacketBuffer buffer, MinecraftData data) { var hasUuid = buffer.ReadBool(); Uuid? uuid = hasUuid ? buffer.ReadUuid() : null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ResetScorePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ResetScorePacket.cs index 09d308ec..ce37b66f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ResetScorePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ResetScorePacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The entity whose score this is. For players, this is their username; for other entities, it is their UUID. /// Whether the score should be removed for the specified objective, or for all of them. /// The name of the objective the score belongs to. Only present if the previous field is true. -public sealed record ResetScorePacket(string EntityName, bool HasObjectiveName, string? ObjectiveName) : IPacket +public sealed partial record ResetScorePacket(string EntityName, bool HasObjectiveName, string? ObjectiveName) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,7 +18,7 @@ public sealed record ResetScorePacket(string EntityName, bool HasObjectiveName, public static PacketType StaticType => PacketType.CB_Play_ResetScore; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(EntityName); buffer.WriteBool(HasObjectiveName); @@ -29,7 +29,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ResetScorePacket Read(PacketBuffer buffer, MinecraftData data) { var entityName = buffer.ReadString(); var hasObjectiveName = buffer.ReadBool(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RespawnPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RespawnPacket.cs index ef4f020b..cac293f9 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/RespawnPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/RespawnPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record RespawnPacket( +public sealed partial record RespawnPacket( Identifier DimensionType, Identifier DimensionName, long HashedSeed, @@ -21,16 +21,16 @@ public sealed record RespawnPacket( Identifier? DeathDimensionName, Position? DeathLocation, int? PortalCooldown -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_Respawn; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol <= ProtocolVersion.V_1_19) + if (data.Version.Protocol <= ProtocolVersion.V_1_19_0) { throw new NotSupportedException( $"{nameof(RespawnPacket)}.Write() is not supported for versions before 1.19."); @@ -56,17 +56,17 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WritePosition(DeathLocation ?? throw new InvalidOperationException($"{nameof(DeathLocation)} must not be null if {nameof(HasDeathLocation)} is true.")); } - if (version.Version.Protocol >= ProtocolVersion.V_1_20) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_0) { buffer.WriteVarInt(PortalCooldown ?? 0); } } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static RespawnPacket Read(PacketBuffer buffer, MinecraftData data) { Identifier dimensionType; - if (version.Version.Protocol <= ProtocolVersion.V_1_19) + if (data.Version.Protocol <= ProtocolVersion.V_1_19_0) { var dimensionNbt = buffer.ReadNbtCompound(); dimensionType = Identifier.Parse(dimensionNbt.Get("effects")!.Value); @@ -87,7 +87,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) bool? hasDeathLocation = null; Identifier? deathDimensionName = null; Position? deathLocation = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { hasDeathLocation = buffer.ReadBool(); if (hasDeathLocation.Value) @@ -98,7 +98,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) } int? portalCooldown = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_20) + if (data.Version.Protocol >= ProtocolVersion.V_1_20_0) { portalCooldown = buffer.ReadVarInt(); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SelectAdvancementTabPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SelectAdvancementTabPacket.cs index fdf64eef..3987e346 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SelectAdvancementTabPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SelectAdvancementTabPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Sent either when the client switches tab in the GUI or when an advancement in another tab is made. /// /// The identifier of the advancement tab. If no or an invalid identifier is sent, the client will switch to the first tab in the GUI. -public sealed record SelectAdvancementTabPacket(Identifier? Identifier) : IPacket +public sealed partial record SelectAdvancementTabPacket(Identifier? Identifier) : IPacketStatic { /// public PacketType Type => StaticType; @@ -31,7 +31,7 @@ public sealed record SelectAdvancementTabPacket(Identifier? Identifier) : IPacke }.ToFrozenSet(); /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { var hasId = Identifier is not null; buffer.WriteBool(hasId); @@ -42,7 +42,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SelectAdvancementTabPacket Read(PacketBuffer buffer, MinecraftData data) { var hasId = buffer.ReadBool(); Identifier? identifier = hasId ? buffer.ReadIdentifier() : null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ServerDataPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ServerDataPacket.cs index d5ff4f51..2f81272c 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/ServerDataPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/ServerDataPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Indicates if the server has an icon. /// Optional icon bytes in the PNG format. /// Indicates if the server enforces secure chat. -public sealed record ServerDataPacket(Chat Motd, bool HasIcon, byte[]? Icon, bool EnforcesSecureChat) : IPacket +public sealed partial record ServerDataPacket(Chat Motd, bool HasIcon, byte[]? Icon, bool EnforcesSecureChat) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record ServerDataPacket(Chat Motd, bool HasIcon, byte[]? Icon, boo public static PacketType StaticType => PacketType.CB_Play_ServerData; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Motd); buffer.WriteBool(HasIcon); @@ -33,7 +33,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ServerDataPacket Read(PacketBuffer buffer, MinecraftData data) { var motd = buffer.ReadChatComponent(); var hasIcon = buffer.ReadBool(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetActionBarTextPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetActionBarTextPacket.cs index 95218632..b5f97105 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetActionBarTextPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetActionBarTextPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// except that chat message blocking isn't performed. Used by the Notchian server only to implement the /title command. /// /// The text to display in the action bar -public sealed record SetActionBarTextPacket(Chat ActionBarText) : IPacket +public sealed partial record SetActionBarTextPacket(Chat ActionBarText) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,13 +18,13 @@ public sealed record SetActionBarTextPacket(Chat ActionBarText) : IPacket public static PacketType StaticType => PacketType.CB_Play_ActionBar; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(ActionBarText); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetActionBarTextPacket Read(PacketBuffer buffer, MinecraftData data) { var actionBarText = buffer.ReadChatComponent(); return new SetActionBarTextPacket(actionBarText); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBlockDestroyStagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBlockDestroyStagePacket.cs index 3c930b61..a863e7ca 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBlockDestroyStagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBlockDestroyStagePacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The ID of the entity breaking the block /// Block Position /// 0–9 to set it, any other value to remove it -public sealed record SetBlockDestroyStagePacket(int EntityId, Position Location, byte DestroyStage) : IPacket +public sealed partial record SetBlockDestroyStagePacket(int EntityId, Position Location, byte DestroyStage) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record SetBlockDestroyStagePacket(int EntityId, Position Location, public static PacketType StaticType => PacketType.CB_Play_BlockBreakAnimation; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WritePosition(Location); @@ -27,7 +27,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBlockDestroyStagePacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var location = buffer.ReadPosition(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderCenterPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderCenterPacket.cs index 439cafe9..3cdf7857 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderCenterPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderCenterPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The X coordinate of the world border center. /// The Z coordinate of the world border center. -public sealed record SetBorderCenterPacket(double X, double Z) : IPacket +public sealed partial record SetBorderCenterPacket(double X, double Z) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record SetBorderCenterPacket(double X, double Z) : IPacket public static PacketType StaticType => PacketType.CB_Play_WorldBorderCenter; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Z); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBorderCenterPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var z = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderLerpSizePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderLerpSizePacket.cs index cc153246..5f46e6fb 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderLerpSizePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderLerpSizePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Current length of a single side of the world border, in meters. /// Target length of a single side of the world border, in meters. /// Number of real-time milliseconds until New Diameter is reached. -public sealed record SetBorderLerpSizePacket(double OldDiameter, double NewDiameter, long Speed) : IPacket +public sealed partial record SetBorderLerpSizePacket(double OldDiameter, double NewDiameter, long Speed) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,7 +18,7 @@ public sealed record SetBorderLerpSizePacket(double OldDiameter, double NewDiame public static PacketType StaticType => PacketType.CB_Play_WorldBorderLerpSize; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(OldDiameter); buffer.WriteDouble(NewDiameter); @@ -26,7 +26,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBorderLerpSizePacket Read(PacketBuffer buffer, MinecraftData data) { var oldDiameter = buffer.ReadDouble(); var newDiameter = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderSizePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderSizePacket.cs index dd068c1b..3f995329 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderSizePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderSizePacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to set the world border size. /// /// Length of a single side of the world border, in meters. -public sealed record SetBorderSizePacket(double Diameter) : IPacket +public sealed partial record SetBorderSizePacket(double Diameter) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetBorderSizePacket(double Diameter) : IPacket public static PacketType StaticType => PacketType.CB_Play_WorldBorderSize; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(Diameter); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBorderSizePacket Read(PacketBuffer buffer, MinecraftData data) { var diameter = buffer.ReadDouble(); return new SetBorderSizePacket(diameter); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDelayPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDelayPacket.cs index 49a6dc56..0dff2f88 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDelayPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDelayPacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to set the border warning delay. /// /// The warning time in seconds as set by /worldborder warning time. -public sealed record SetBorderWarningDelayPacket(int WarningTime) : IPacket +public sealed partial record SetBorderWarningDelayPacket(int WarningTime) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetBorderWarningDelayPacket(int WarningTime) : IPacket public static PacketType StaticType => PacketType.CB_Play_WorldBorderWarningDelay; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(WarningTime); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBorderWarningDelayPacket Read(PacketBuffer buffer, MinecraftData data) { var warningTime = buffer.ReadVarInt(); return new SetBorderWarningDelayPacket(warningTime); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDistancePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDistancePacket.cs index e1c87f37..ee4ac975 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDistancePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetBorderWarningDistancePacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to set the border warning distance. /// /// The warning distance in meters. -public sealed record SetBorderWarningDistancePacket(int WarningBlocks) : IPacket +public sealed partial record SetBorderWarningDistancePacket(int WarningBlocks) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetBorderWarningDistancePacket(int WarningBlocks) : IPacket public static PacketType StaticType => PacketType.CB_Play_WorldBorderWarningReach; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(WarningBlocks); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetBorderWarningDistancePacket Read(PacketBuffer buffer, MinecraftData data) { var warningBlocks = buffer.ReadVarInt(); return new SetBorderWarningDistancePacket(warningBlocks); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCameraPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCameraPacket.cs index 6c914e6b..0ea03e1f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCameraPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCameraPacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Sets the entity that the player renders from. This is normally used when the player left-clicks an entity while in spectator mode. /// /// ID of the entity to set the client's camera to. -public sealed record SetCameraPacket(int CameraId) : IPacket +public sealed partial record SetCameraPacket(int CameraId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetCameraPacket(int CameraId) : IPacket public static PacketType StaticType => PacketType.CB_Play_Camera; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(CameraId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetCameraPacket Read(PacketBuffer buffer, MinecraftData data) { var cameraId = buffer.ReadVarInt(); return new SetCameraPacket(cameraId); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCenterChunkPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCenterChunkPacket.cs index 21e1afdd..84ebd573 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCenterChunkPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCenterChunkPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Chunk X coordinate of the loading area center. /// Chunk Z coordinate of the loading area center. -public sealed record SetCenterChunkPacket(int ChunkX, int ChunkZ) : IPacket +public sealed partial record SetCenterChunkPacket(int ChunkX, int ChunkZ) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record SetCenterChunkPacket(int ChunkX, int ChunkZ) : IPacket public static PacketType StaticType => PacketType.CB_Play_UpdateViewPosition; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(ChunkX); buffer.WriteVarInt(ChunkZ); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetCenterChunkPacket Read(PacketBuffer buffer, MinecraftData data) { var chunkX = buffer.ReadVarInt(); var chunkZ = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetContainerPropertyPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetContainerPropertyPacket.cs new file mode 100644 index 00000000..fe70d769 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetContainerPropertyPacket.cs @@ -0,0 +1,42 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Set Container Property packet +/// +/// The window ID +/// The property to be updated +/// The new value for the property +public sealed partial record SetContainerPropertyPacket(byte WindowId, short Property, short Value) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_CraftProgressBar; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteByte(WindowId); + buffer.WriteShort(Property); + buffer.WriteShort(Value); + } + + /// + public static SetContainerPropertyPacket Read(PacketBuffer buffer, MinecraftData data) + { + var windowId = buffer.ReadByte(); + var property = buffer.ReadShort(); + var value = buffer.ReadShort(); + + return new SetContainerPropertyPacket(windowId, property, value); + } + + // TODO: Add all the meanings of the properties + // depends on the type of container but we only get the window ID here + // so we need static methods that have the container type as a parameter + // https://wiki.vg/index.php?title=Protocol&oldid=19208#Set_Container_Content +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCooldownPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCooldownPacket.cs index 4454c8dc..6ab5143f 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCooldownPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetCooldownPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Numeric ID of the item to apply a cooldown to. /// Number of ticks to apply a cooldown for, or 0 to clear the cooldown. -public sealed record SetCooldownPacket(int ItemId, int CooldownTicks) : IPacket +public sealed partial record SetCooldownPacket(int ItemId, int CooldownTicks) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record SetCooldownPacket(int ItemId, int CooldownTicks) : IPacket public static PacketType StaticType => PacketType.CB_Play_SetCooldown; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(ItemId); buffer.WriteVarInt(CooldownTicks); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetCooldownPacket Read(PacketBuffer buffer, MinecraftData data) { var itemId = buffer.ReadVarInt(); var cooldownTicks = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetDefaultSpawnPositionPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetDefaultSpawnPositionPacket.cs index 769b8894..2da4dbfd 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetDefaultSpawnPositionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetDefaultSpawnPositionPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The spawn location /// The angle at which to respawn at -public sealed record SetDefaultSpawnPositionPacket(Position Location, float Angle) : IPacket +public sealed partial record SetDefaultSpawnPositionPacket(Position Location, float Angle) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record SetDefaultSpawnPositionPacket(Position Location, float Angl public static PacketType StaticType => PacketType.CB_Play_SpawnPosition; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WritePosition(Location); buffer.WriteFloat(Angle); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetDefaultSpawnPositionPacket Read(PacketBuffer buffer, MinecraftData data) { var location = buffer.ReadPosition(); var angle = buffer.ReadFloat(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityMetadataPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityMetadataPacket.cs index c0dbdaaf..da90fb16 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityMetadataPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityMetadataPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// The entity metadata -public sealed record SetEntityMetadataPacket(int EntityId, EntityMetadata Metadata) : IPacket +public sealed partial record SetEntityMetadataPacket(int EntityId, EntityMetadata Metadata) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,17 +19,17 @@ public sealed record SetEntityMetadataPacket(int EntityId, EntityMetadata Metada public static PacketType StaticType => PacketType.CB_Play_EntityMetadata; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); - Metadata.Write(buffer, version); + Metadata.Write(buffer, data); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetEntityMetadataPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); - var metadata = EntityMetadata.Read(buffer, version); + var metadata = EntityMetadata.Read(buffer, data); return new SetEntityMetadataPacket(entityId, metadata); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityVelocityPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityVelocityPacket.cs index 845d6468..a9d7e0e4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityVelocityPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEntityVelocityPacket.cs @@ -4,14 +4,14 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SetEntityVelocityPacket(int EntityId, short VelocityX, short VelocityY, short VelocityZ) : IPacket +public sealed partial record SetEntityVelocityPacket(int EntityId, short VelocityX, short VelocityY, short VelocityZ) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_EntityVelocity; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteShort(VelocityX); @@ -19,7 +19,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteShort(VelocityZ); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetEntityVelocityPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var velocityX = buffer.ReadShort(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEquipmentPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEquipmentPacket.cs index f00ceaf4..7e681624 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEquipmentPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetEquipmentPacket.cs @@ -1,5 +1,4 @@ -using MineSharp.Core.Common; -using MineSharp.Core.Common.Items; +using MineSharp.Core.Common.Items; using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -13,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// The equipment list -public sealed record SetEquipmentPacket(int EntityId, EquipmentEntry[] Equipment) : IPacket +public sealed partial record SetEquipmentPacket(int EntityId, EquipmentEntry[] Equipment) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,19 +20,19 @@ public sealed record SetEquipmentPacket(int EntityId, EquipmentEntry[] Equipment public static PacketType StaticType => PacketType.CB_Play_EntityEquipment; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); for (int i = 0; i < Equipment.Length; i++) { var entry = Equipment[i]; var isLastEntry = i == Equipment.Length - 1; - entry.Write(buffer, version, isLastEntry); + entry.Write(buffer, data, isLastEntry); } } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetEquipmentPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var equipment = new List(); @@ -42,7 +41,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) var slot = buffer.Peek(); // wiki.vg says: "has the top bit set if another entry follows, and otherwise unset if this is the last item in the array" var isLast = (slot & 0x80) == 0; - equipment.Add(EquipmentEntry.Read(buffer, version)); + equipment.Add(EquipmentEntry.Read(buffer, data)); if (isLast) { break; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetExperiencePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetExperiencePacket.cs index d21ee6fa..9b631539 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetExperiencePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetExperiencePacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -7,31 +7,156 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Set Experience packet /// -/// The experience bar value between 0 and 1 -/// The experience level -/// The total experience points -public sealed record SetExperiencePacket(float ExperienceBar, int Level, int TotalExperience) : IPacket +public abstract partial record SetExperiencePacket : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_Experience; - /// - public void Write(PacketBuffer buffer, MinecraftData version) + // all versions contain these fields: + /// + /// The experience bar value between 0 and 1 + /// + public abstract float ExperienceBar { get; init; } + /// + /// The experience level + /// + public abstract int Level { get; init; } + /// + /// The total experience points + /// + public abstract int TotalExperience { get; init; } + + // may only be called from sub class in this class + private SetExperiencePacket() { - buffer.WriteFloat(ExperienceBar); - buffer.WriteVarInt(Level); - buffer.WriteVarInt(TotalExperience); } - /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + /// + /// Version specific for + /// + public sealed partial record SetExperiencePacketV_1_7_0(float ExperienceBar, short ShortLevel, short ShortTotalExperience) : SetExperiencePacket + { + /// + public override int Level + { + get + { + return ShortLevel; + } + init + { + ShortLevel = (short)value; + } + } + /// + public override int TotalExperience + { + get + { + return ShortTotalExperience; + } + init + { + ShortTotalExperience = (short)value; + } + } + + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(ExperienceBar); + buffer.WriteShort(ShortLevel); + buffer.WriteShort(ShortTotalExperience); + } + + /// + public static new SetExperiencePacketV_1_7_0 Read(PacketBuffer buffer, MinecraftData data) + { + var experienceBar = buffer.ReadFloat(); + var level = buffer.ReadShort(); + var totalExperience = buffer.ReadShort(); + + return new(experienceBar, level, totalExperience); + } + } + + /// + /// Version specific for . + /// + /// Level and TotalExperience become VarInt in 1.8.0. + /// + public sealed partial record SetExperiencePacketV_1_8_0(float ExperienceBar, int Level, int TotalExperience) : SetExperiencePacket + { + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(ExperienceBar); + buffer.WriteVarInt(Level); + buffer.WriteVarInt(TotalExperience); + } + + /// + public static new SetExperiencePacketV_1_8_0 Read(PacketBuffer buffer, MinecraftData data) + { + var experienceBar = buffer.ReadFloat(); + var level = buffer.ReadVarInt(); + var totalExperience = buffer.ReadVarInt(); + + return new(experienceBar, level, totalExperience); + } + } + + /// + /// Version specific for . + /// + /// Level and TotalExperience are swapped in 1.19.3. + /// + public sealed partial record SetExperiencePacketV_1_19_3(float ExperienceBar, int Level, int TotalExperience) : SetExperiencePacket + { + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(ExperienceBar); + buffer.WriteVarInt(TotalExperience); + buffer.WriteVarInt(Level); + } + + /// + public static new SetExperiencePacketV_1_19_3 Read(PacketBuffer buffer, MinecraftData data) + { + var experienceBar = buffer.ReadFloat(); + var totalExperience = buffer.ReadVarInt(); + var level = buffer.ReadVarInt(); + + return new(experienceBar, level, totalExperience); + } + } + + /// + /// Version specific for . + /// + /// Level and TotalExperience are swapped back again in 1.20.2. + /// + public sealed partial record SetExperiencePacketV_1_20_2(float ExperienceBar, int Level, int TotalExperience) : SetExperiencePacket { - var experienceBar = buffer.ReadFloat(); - var level = buffer.ReadVarInt(); - var totalExperience = buffer.ReadVarInt(); + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(ExperienceBar); + buffer.WriteVarInt(Level); + buffer.WriteVarInt(TotalExperience); + } + + /// + public static new SetExperiencePacketV_1_20_2 Read(PacketBuffer buffer, MinecraftData data) + { + var experienceBar = buffer.ReadFloat(); + var level = buffer.ReadVarInt(); + var totalExperience = buffer.ReadVarInt(); - return new SetExperiencePacket(experienceBar, level, totalExperience); + return new(experienceBar, level, totalExperience); + } } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeadRotationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeadRotationPacket.cs index 9a37b78f..64879f02 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeadRotationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeadRotationPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The entity ID /// New angle, not a delta -public sealed record SetHeadRotationPacket(int EntityId, byte HeadYaw) : IPacket +public sealed partial record SetHeadRotationPacket(int EntityId, byte HeadYaw) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record SetHeadRotationPacket(int EntityId, byte HeadYaw) : IPacket public static PacketType StaticType => PacketType.CB_Play_EntityHeadRotation; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteByte(HeadYaw); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetHeadRotationPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var headYaw = buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHealthPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHealthPacket.cs index 528ac20a..fd7f1149 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHealthPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHealthPacket.cs @@ -4,21 +4,21 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SetHealthPacket(float Health, int Food, float Saturation) : IPacket +public sealed partial record SetHealthPacket(float Health, int Food, float Saturation) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_UpdateHealth; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteFloat(Health); buffer.WriteVarInt(Food); buffer.WriteFloat(Saturation); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetHealthPacket Read(PacketBuffer buffer, MinecraftData data) { var health = buffer.ReadFloat(); var food = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeldItemPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeldItemPacket.cs index 064a6120..64a8eebd 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeldItemPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetHeldItemPacket.cs @@ -4,19 +4,19 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SetHeldItemPacket(sbyte Slot) : IPacket +public sealed partial record SetHeldItemPacket(sbyte Slot) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_HeldItemSlot; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteSByte(Slot); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetHeldItemPacket Read(PacketBuffer buffer, MinecraftData data) { return new SetHeldItemPacket( buffer.ReadSByte()); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs index 0337868e..379353b3 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs @@ -5,20 +5,20 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SetPassengersPacket(int EntityId, int[] PassengersId) : IPacket +public sealed partial record SetPassengersPacket(int EntityId, int[] PassengersId) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_SetPassengers; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarIntArray(PassengersId, (buf, elem) => buffer.WriteVarInt(elem)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetPassengersPacket Read(PacketBuffer buffer, MinecraftData data) { return new SetPassengersPacket( buffer.ReadVarInt(), diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetRenderDistancePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetRenderDistancePacket.cs index 34b6740b..bc570aa5 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetRenderDistancePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetRenderDistancePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// This packet is sent by the server when the client reappears in the overworld after leaving the end. /// /// Render distance (2-32). -public sealed record SetRenderDistancePacket(int ViewDistance) : IPacket +public sealed partial record SetRenderDistancePacket(int ViewDistance) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record SetRenderDistancePacket(int ViewDistance) : IPacket public static PacketType StaticType => PacketType.CB_Play_UpdateViewDistance; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(ViewDistance); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetRenderDistancePacket Read(PacketBuffer buffer, MinecraftData data) { var viewDistance = buffer.ReadVarInt(); return new SetRenderDistancePacket(viewDistance); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSimulationDistancePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSimulationDistancePacket.cs index 4e2fd85e..11832fbe 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSimulationDistancePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSimulationDistancePacket.cs @@ -1,4 +1,4 @@ -using MineSharp.Core.Serialization; +using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Set Simulation Distance packet /// /// The distance that the client will process specific things, such as entities. -public sealed record SetSimulationDistancePacket(int SimulationDistance) : IPacket +public sealed partial record SetSimulationDistancePacket(int SimulationDistance) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record SetSimulationDistancePacket(int SimulationDistance) : IPack public static PacketType StaticType => PacketType.CB_Play_SimulationDistance; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(SimulationDistance); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetSimulationDistancePacket Read(PacketBuffer buffer, MinecraftData data) { var simulationDistance = buffer.ReadVarInt(); return new SetSimulationDistancePacket(simulationDistance); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSubtitleTextPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSubtitleTextPacket.cs index 94d49e3f..2ee8def4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSubtitleTextPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetSubtitleTextPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to set the subtitle text. /// /// The subtitle text to be displayed. -public sealed record SetSubtitleTextPacket(Chat SubtitleText) : IPacket +public sealed partial record SetSubtitleTextPacket(Chat SubtitleText) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record SetSubtitleTextPacket(Chat SubtitleText) : IPacket public static PacketType StaticType => PacketType.CB_Play_SetTitleSubtitle; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(SubtitleText); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetSubtitleTextPacket Read(PacketBuffer buffer, MinecraftData data) { var subtitleText = buffer.ReadChatComponent(); return new SetSubtitleTextPacket(subtitleText); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTabListHeaderFooterPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTabListHeaderFooterPacket.cs index af6c78af..85a52ec4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTabListHeaderFooterPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTabListHeaderFooterPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The header text component /// The footer text component -public sealed record SetTabListHeaderFooterPacket(Chat Header, Chat Footer) : IPacket +public sealed partial record SetTabListHeaderFooterPacket(Chat Header, Chat Footer) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record SetTabListHeaderFooterPacket(Chat Header, Chat Footer) : IP public static PacketType StaticType => PacketType.CB_Play_PlayerlistHeader; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Header); buffer.WriteChatComponent(Footer); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetTabListHeaderFooterPacket Read(PacketBuffer buffer, MinecraftData data) { var header = buffer.ReadChatComponent(); var footer = buffer.ReadChatComponent(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTickingStatePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTickingStatePacket.cs index 9230384c..bad7bbe8 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTickingStatePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTickingStatePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The tick rate of the client /// Whether the client is frozen -public sealed record SetTickingStatePacket(float TickRate, bool IsFrozen) : IPacket +public sealed partial record SetTickingStatePacket(float TickRate, bool IsFrozen) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record SetTickingStatePacket(float TickRate, bool IsFrozen) : IPac public static PacketType StaticType => PacketType.CB_Play_SetTickingState; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteFloat(TickRate); buffer.WriteBool(IsFrozen); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetTickingStatePacket Read(PacketBuffer buffer, MinecraftData data) { var tickRate = buffer.ReadFloat(); var isFrozen = buffer.ReadBool(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleAnimationTimesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleAnimationTimesPacket.cs index 5d3ccaef..b2e7fca5 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleAnimationTimesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleAnimationTimesPacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Ticks to spend fading in. /// Ticks to keep the title displayed. /// Ticks to spend fading out, not when to start fading out. -public sealed record SetTitleAnimationTimesPacket(int FadeIn, int Stay, int FadeOut) : IPacket +public sealed partial record SetTitleAnimationTimesPacket(int FadeIn, int Stay, int FadeOut) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,7 +18,7 @@ public sealed record SetTitleAnimationTimesPacket(int FadeIn, int Stay, int Fade public static PacketType StaticType => PacketType.CB_Play_SetTitleTime; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(FadeIn); buffer.WriteInt(Stay); @@ -26,7 +26,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetTitleAnimationTimesPacket Read(PacketBuffer buffer, MinecraftData data) { var fadeIn = buffer.ReadInt(); var stay = buffer.ReadInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleTextPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleTextPacket.cs index 281b3db9..36a75ab7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleTextPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetTitleTextPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet sent by the server to set the title text in the client. /// /// The title text to be displayed -public sealed record SetTitleTextPacket(Chat TitleText) : IPacket +public sealed partial record SetTitleTextPacket(Chat TitleText) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,13 +17,13 @@ public sealed record SetTitleTextPacket(Chat TitleText) : IPacket public static PacketType StaticType => PacketType.CB_Play_SetTitleText; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(TitleText); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetTitleTextPacket Read(PacketBuffer buffer, MinecraftData data) { var titleText = buffer.ReadChatComponent(); return new SetTitleTextPacket(titleText); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SoundEffectPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SoundEffectPacket.cs index c3e20998..789366a7 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SoundEffectPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SoundEffectPacket.cs @@ -5,7 +5,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SoundEffectPacket( +public sealed partial record SoundEffectPacket( int SoundId, Identifier? SoundName, bool? HasFixedRange, @@ -17,14 +17,14 @@ public sealed record SoundEffectPacket( float Volume, float Pitch, long Seed -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_SoundEffect; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(SoundId); if (SoundId == 0) @@ -45,7 +45,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteLong(Seed); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SoundEffectPacket Read(PacketBuffer buffer, MinecraftData data) { var soundId = buffer.ReadVarInt(); Identifier? soundName = null; diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnEntityPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnEntityPacket.cs index f3a5c0c9..e23be0d3 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnEntityPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnEntityPacket.cs @@ -6,7 +6,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record SpawnEntityPacket( +public sealed partial record SpawnEntityPacket( int EntityId, Uuid ObjectUuid, int EntityType, @@ -20,7 +20,7 @@ public sealed record SpawnEntityPacket( short VelocityX, short VelocityY, short VelocityZ -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -31,8 +31,8 @@ short VelocityZ /// Writes the packet data to the buffer. /// /// The buffer to write to. - /// The Minecraft version. - public void Write(PacketBuffer buffer, MinecraftData version) + /// The Minecraft data. + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteUuid(ObjectUuid); @@ -43,7 +43,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteSByte(Pitch); buffer.WriteSByte(Yaw); - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { buffer.WriteSByte(HeadPitch); buffer.WriteVarInt(ObjectData); @@ -62,9 +62,9 @@ public void Write(PacketBuffer buffer, MinecraftData version) /// Reads the packet data from the buffer. /// /// The buffer to read from. - /// The Minecraft version. + /// The Minecraft data. /// A new instance of . - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SpawnEntityPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var objectUuid = buffer.ReadUuid(); @@ -76,7 +76,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) var yaw = buffer.ReadSByte(); sbyte headPitch = 0; var objectData = 0; - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { headPitch = buffer.ReadSByte(); objectData = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnExperienceOrbPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnExperienceOrbPacket.cs index cb325080..02b120bc 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnExperienceOrbPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnExperienceOrbPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The Y coordinate /// The Z coordinate /// The amount of experience this orb will reward once collected -public sealed record SpawnExperienceOrbPacket(int EntityId, double X, double Y, double Z, short Count) : IPacket +public sealed partial record SpawnExperienceOrbPacket(int EntityId, double X, double Y, double Z, short Count) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record SpawnExperienceOrbPacket(int EntityId, double X, double Y, public static PacketType StaticType => PacketType.CB_Play_SpawnEntityExperienceOrb; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteDouble(X); @@ -30,7 +30,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SpawnExperienceOrbPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var x = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnLivingEntityPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnLivingEntityPacket.cs index 5160159d..c03b43a1 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnLivingEntityPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnLivingEntityPacket.cs @@ -20,7 +20,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The X velocity of the entity. /// The Y velocity of the entity. /// The Z velocity of the entity. -public sealed record SpawnLivingEntityPacket( +public sealed partial record SpawnLivingEntityPacket( int EntityId, Uuid EntityUuid, int EntityType, @@ -33,7 +33,7 @@ public sealed record SpawnLivingEntityPacket( short VelocityX, short VelocityY, short VelocityZ -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -41,7 +41,7 @@ short VelocityZ public static PacketType StaticType => PacketType.CB_Play_SpawnEntityLiving; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteUuid(EntityUuid); @@ -58,7 +58,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SpawnLivingEntityPacket Read(PacketBuffer buffer, MinecraftData data) { return new SpawnLivingEntityPacket( buffer.ReadVarInt(), diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPaintingPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPaintingPacket.cs index 66f4faeb..d313b2c8 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPaintingPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPaintingPacket.cs @@ -14,13 +14,13 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The title of the painting. /// The location of the painting. /// The direction the painting is facing. -public sealed record SpawnPaintingPacket( +public sealed partial record SpawnPaintingPacket( int EntityId, Uuid EntityUuid, int Title, Position Location, sbyte Direction -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -28,7 +28,7 @@ sbyte Direction public static PacketType StaticType => PacketType.CB_Play_SpawnEntityPainting; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteUuid(EntityUuid); @@ -38,7 +38,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SpawnPaintingPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var entityUuid = buffer.ReadUuid(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPlayerPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPlayerPacket.cs index f8ff8579..1e69bc88 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPlayerPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SpawnPlayerPacket.cs @@ -16,7 +16,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The Z coordinate of the player. /// The yaw of the player. /// The pitch of the player. -public sealed record SpawnPlayerPacket( +public sealed partial record SpawnPlayerPacket( int EntityId, Uuid PlayerUuid, double X, @@ -24,7 +24,7 @@ public sealed record SpawnPlayerPacket( double Z, byte Yaw, byte Pitch -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -32,7 +32,7 @@ byte Pitch public static PacketType StaticType => PacketType.CB_Play_NamedEntitySpawn; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteUuid(PlayerUuid); @@ -44,7 +44,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SpawnPlayerPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var playerUuid = buffer.ReadUuid(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StartConfigurationPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StartConfigurationPacket.cs index 78394dbb..56a6bcc3 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StartConfigurationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StartConfigurationPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Sent during gameplay in order to redo the configuration process. /// The client must respond with Acknowledge Configuration for the process to start. /// -public sealed record StartConfigurationPacket() : IPacket +public sealed partial record StartConfigurationPacket() : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record StartConfigurationPacket() : IPacket public static PacketType StaticType => PacketType.CB_Play_StartConfiguration; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { // No fields to write } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static StartConfigurationPacket Read(PacketBuffer buffer, MinecraftData data) { // No fields to read return new StartConfigurationPacket(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StepTickPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StepTickPacket.cs index 1c4e7c03..5c114412 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StepTickPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StepTickPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Advances the client processing by the specified number of ticks. Has no effect unless client ticking is frozen. /// /// The number of tick steps to advance -public sealed record StepTickPacket(int TickSteps) : IPacket +public sealed partial record StepTickPacket(int TickSteps) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record StepTickPacket(int TickSteps) : IPacket public static PacketType StaticType => PacketType.CB_Play_StepTick; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(TickSteps); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static StepTickPacket Read(PacketBuffer buffer, MinecraftData data) { var tickSteps = buffer.ReadVarInt(); return new StepTickPacket(tickSteps); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StopSoundPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StopSoundPacket.cs index 5fdadda7..76679d1b 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/StopSoundPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/StopSoundPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// Optional category of the sound. If not present, then sounds from all sources are cleared. /// Optional sound effect name. If not present, then all sounds are cleared. -public sealed record StopSoundPacket(SoundCategory? Category, Identifier? Sound) : IPacket +public sealed partial record StopSoundPacket(SoundCategory? Category, Identifier? Sound) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,7 +19,7 @@ public sealed record StopSoundPacket(SoundCategory? Category, Identifier? Sound) public static PacketType StaticType => PacketType.CB_Play_StopSound; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { byte flags = 0; flags |= (byte)(Category.HasValue ? 0x1 : 0); @@ -37,7 +37,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static StopSoundPacket Read(PacketBuffer buffer, MinecraftData data) { var flags = buffer.ReadByte(); SoundCategory? category = null; @@ -55,22 +55,22 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) return new StopSoundPacket(category, sound); } - /// - /// Enum representing sound categories - /// - public enum SoundCategory - { + /// + /// Enum representing sound categories + /// + public enum SoundCategory + { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member Master = 0, Music = 1, - Record = 2, - Weather = 3, - Block = 4, - Hostile = 5, - Neutral = 6, - Player = 7, - Ambient = 8, - Voice = 9 + Record = 2, + Weather = 3, + Block = 4, + Hostile = 5, + Neutral = 6, + Player = 7, + Ambient = 8, + Voice = 9 #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } + } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs index d813307d..f3f038f2 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// Packet for system messages displayed in chat or hotbar /// See https://wiki.vg/Protocol#System_Chat_Message /// -public abstract record SystemChatMessagePacket(Chat Message) : IPacket +public abstract partial record SystemChatMessagePacket(Chat Message) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,24 +18,24 @@ public abstract record SystemChatMessagePacket(Chat Message) : IPacket public static PacketType StaticType => PacketType.CB_Play_SystemChat; /// - public abstract void Write(PacketBuffer buffer, MinecraftData version); + public abstract void Write(PacketBuffer buffer, MinecraftData data); /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SystemChatMessagePacket Read(PacketBuffer buffer, MinecraftData data) { - return version.Version.Protocol switch + return data.Version.Protocol switch { - >= ProtocolVersion.V_1_19_2 => Since192._Read(buffer, version), - < ProtocolVersion.V_1_19_2 => Before192._Read(buffer, version) + >= ProtocolVersion.V_1_19_1 => Since191._Read(buffer, data), + < ProtocolVersion.V_1_19_1 => Before191._Read(buffer, data) }; } /// ///
/// - /// Used before Minecraft Java 1.19.2 + /// Used before Minecraft Java 1.19.1 ///
- public sealed record Before192(Chat Message, int ChatType) : SystemChatMessagePacket(Message) + public sealed record Before191(Chat Message, int ChatType) : SystemChatMessagePacket(Message) { /// public override void Write(PacketBuffer buffer, MinecraftData data) @@ -44,7 +44,7 @@ public override void Write(PacketBuffer buffer, MinecraftData data) buffer.WriteVarInt(ChatType); } - internal static Before192 _Read(PacketBuffer buffer, MinecraftData version) + internal static Before191 _Read(PacketBuffer buffer, MinecraftData data) { return new(buffer.ReadChatComponent(), buffer.ReadInt()); } @@ -53,18 +53,18 @@ internal static Before192 _Read(PacketBuffer buffer, MinecraftData version) /// ///
/// - /// Used since Minecraft Java 1.19.2 + /// Used since Minecraft Java 1.19.1 ///
- public sealed record Since192(Chat Message, bool IsOverlay) : SystemChatMessagePacket(Message) + public sealed record Since191(Chat Message, bool IsOverlay) : SystemChatMessagePacket(Message) { /// - public override void Write(PacketBuffer buffer, MinecraftData version) + public override void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteChatComponent(Message); buffer.WriteBool(IsOverlay); } - internal static Since192 _Read(PacketBuffer buffer, MinecraftData version) + internal static Since191 _Read(PacketBuffer buffer, MinecraftData data) { return new(buffer.ReadChatComponent(), buffer.ReadBool()); } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/TagQueryResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/TagQueryResponsePacket.cs index 071b1221..d7a3ae7d 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/TagQueryResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/TagQueryResponsePacket.cs @@ -10,7 +10,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The transaction ID /// The NBT of the block or entity -public sealed record TagQueryResponsePacket(int TransactionId, NbtTag? Nbt) : IPacket +public sealed partial record TagQueryResponsePacket(int TransactionId, NbtTag? Nbt) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,14 +18,14 @@ public sealed record TagQueryResponsePacket(int TransactionId, NbtTag? Nbt) : IP public static PacketType StaticType => PacketType.CB_Play_NbtQueryResponse; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(TransactionId); buffer.WriteOptionalNbt(Nbt); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static TagQueryResponsePacket Read(PacketBuffer buffer, MinecraftData data) { var transactionId = buffer.ReadVarInt(); var nbt = buffer.ReadOptionalNbt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/TeleportEntityPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/TeleportEntityPacket.cs index a26aafaa..009132e0 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/TeleportEntityPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/TeleportEntityPacket.cs @@ -4,7 +4,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record TeleportEntityPacket( +public sealed partial record TeleportEntityPacket( int EntityId, double X, double Y, @@ -12,14 +12,14 @@ public sealed record TeleportEntityPacket( sbyte Yaw, sbyte Pitch, bool OnGround -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_EntityTeleport; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteDouble(X); @@ -30,7 +30,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(OnGround); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static TeleportEntityPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var x = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UnloadChunkPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UnloadChunkPacket.cs index e1e3f8a8..a7548f29 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UnloadChunkPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UnloadChunkPacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The X coordinate of the chunk. /// The Z coordinate of the chunk. -public sealed record UnloadChunkPacket(int X, int Z) : IPacket +public sealed partial record UnloadChunkPacket(int X, int Z) : IPacketStatic { /// public PacketType Type => StaticType; @@ -17,14 +17,14 @@ public sealed record UnloadChunkPacket(int X, int Z) : IPacket public static PacketType StaticType => PacketType.CB_Play_UnloadChunk; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(X); buffer.WriteInt(Z); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UnloadChunkPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadInt(); var z = buffer.ReadInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAdvancementsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAdvancementsPacket.cs new file mode 100644 index 00000000..992461b9 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAdvancementsPacket.cs @@ -0,0 +1,313 @@ +using MineSharp.ChatComponent; +using MineSharp.Core.Common; +using MineSharp.Core.Common.Items; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; +using static MineSharp.Protocol.Packets.Clientbound.Play.UpdateAdvancementsPacket; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Update Advancements packet +/// +/// Whether to reset/clear the current advancements +/// The advancement mappings +/// The identifiers of the advancements that should be removed +/// The progress mappings +public sealed partial record UpdateAdvancementsPacket( + bool ResetClear, + KeyValuePair[] AdvancementMappings, + Identifier[] Identifiers, + KeyValuePair[] ProgressMappings) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_Advancements; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteBool(ResetClear); + buffer.WriteVarInt(AdvancementMappings.Length); + foreach (var (key, value) in AdvancementMappings) + { + buffer.WriteIdentifier(key); + value.Write(buffer, data); + } + buffer.WriteVarInt(Identifiers.Length); + foreach (var identifier in Identifiers) + { + buffer.WriteIdentifier(identifier); + } + buffer.WriteVarInt(ProgressMappings.Length); + foreach (var (key, value) in ProgressMappings) + { + buffer.WriteIdentifier(key); + value.Write(buffer); + } + } + + /// + public static UpdateAdvancementsPacket Read(PacketBuffer buffer, MinecraftData data) + { + var resetClear = buffer.ReadBool(); + var advancementMappingsCount = buffer.ReadVarInt(); + var advancementMappings = new KeyValuePair[advancementMappingsCount]; + for (int i = 0; i < advancementMappingsCount; i++) + { + var key = buffer.ReadIdentifier(); + var value = Advancement.Read(buffer, data); + advancementMappings[i] = new(key, value); + } + var identifiersCount = buffer.ReadVarInt(); + var identifiers = new Identifier[identifiersCount]; + for (int i = 0; i < identifiersCount; i++) + { + identifiers[i] = buffer.ReadIdentifier(); + } + var progressMappingsCount = buffer.ReadVarInt(); + var progressMappings = new KeyValuePair[progressMappingsCount]; + for (int i = 0; i < progressMappingsCount; i++) + { + var key = buffer.ReadIdentifier(); + var value = AdvancementProgress.Read(buffer); + progressMappings[i] = new(key, value); + } + + return new UpdateAdvancementsPacket( + resetClear, + advancementMappings, + identifiers, + progressMappings); + } + + /// + /// Represents an advancement + /// + /// The identifier of the parent advancement + /// The display data + /// The requirements + /// Whether the client should include this achievement in the telemetry data when it's completed + public sealed record Advancement( + Identifier? ParentId, + AdvancementDisplay? DisplayData, + string[][] Requirements, + bool SendsTelemetryData) : ISerializableWithMinecraftData + { + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + var hasParent = ParentId != null; + buffer.WriteBool(hasParent); + if (hasParent) + { + buffer.WriteIdentifier(ParentId!); + } + var hasDisplay = DisplayData != null; + buffer.WriteBool(hasDisplay); + if (hasDisplay) + { + DisplayData!.Write(buffer, data); + } + buffer.WriteVarInt(Requirements.Length); + foreach (var requirement in Requirements) + { + buffer.WriteVarInt(requirement.Length); + foreach (var req in requirement) + { + buffer.WriteString(req); + } + } + buffer.WriteBool(SendsTelemetryData); + } + + /// + public static Advancement Read(PacketBuffer buffer, MinecraftData data) + { + var hasParent = buffer.ReadBool(); + Identifier? parentId = null; + if (hasParent) + { + parentId = buffer.ReadIdentifier(); + } + var hasDisplay = buffer.ReadBool(); + AdvancementDisplay? displayData = null; + if (hasDisplay) + { + displayData = AdvancementDisplay.Read(buffer, data); + } + var requirementsCount = buffer.ReadVarInt(); + var requirements = new string[requirementsCount][]; + for (int i = 0; i < requirementsCount; i++) + { + var innerCount = buffer.ReadVarInt(); + var innerList = new string[innerCount]; + for (int j = 0; j < innerCount; j++) + { + innerList[j] = buffer.ReadString(); + } + requirements[i] = innerList; + } + var sendsTelemetryData = buffer.ReadBool(); + + return new Advancement( + parentId, + displayData, + requirements, + sendsTelemetryData); + } + } + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public enum FrameType + { + Task = 0, + Challenge = 1, + Goal = 2 + } + + [Flags] + public enum AdvancementFlags + { + None = 0, + HasBackgroundTexture = 0x01, + ShowToast = 0x02, + Hidden = 0x04 + } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + + /// + /// Represents the display data of an advancement + /// + /// The title of the advancement + /// The description of the advancement + /// The icon of the advancement + /// The frame type of the advancement + /// The flags of the advancement + /// The background texture location + /// The X coordinate of the advancement + /// The Y coordinate of the advancement + public sealed record AdvancementDisplay( + Chat Title, + Chat Description, + Item Icon, + FrameType FrameType, + AdvancementFlags Flags, + Identifier? BackgroundTexture, + float XCoord, + float YCoord) : ISerializableWithMinecraftData + { + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteChatComponent(Title); + buffer.WriteChatComponent(Description); + buffer.WriteOptionalItem(Icon); + buffer.WriteVarInt((int)FrameType); + buffer.WriteInt((int)Flags); + if (Flags.HasFlag(AdvancementFlags.HasBackgroundTexture)) + { + buffer.WriteIdentifier(BackgroundTexture ?? throw new InvalidOperationException($"{nameof(BackgroundTexture)} must not be null if {nameof(Flags)} indicates that the background texture is present.")); + } + buffer.WriteFloat(XCoord); + buffer.WriteFloat(YCoord); + } + + /// + public static AdvancementDisplay Read(PacketBuffer buffer, MinecraftData data) + { + var title = buffer.ReadChatComponent(); + var description = buffer.ReadChatComponent(); + var icon = buffer.ReadOptionalItem(data)!; + var frameType = (FrameType)buffer.ReadVarInt(); + var flags = (AdvancementFlags)buffer.ReadInt(); + Identifier? backgroundTexture = null; + if (flags.HasFlag(AdvancementFlags.HasBackgroundTexture)) + { + backgroundTexture = buffer.ReadIdentifier(); + } + var xCoord = buffer.ReadFloat(); + var yCoord = buffer.ReadFloat(); + + return new AdvancementDisplay( + title, + description, + icon, + frameType, + flags, + backgroundTexture, + xCoord, + yCoord); + } + } + + /// + /// Represents the progress of an advancement + /// + /// The criteria of the advancement progress + public sealed record AdvancementProgress( + KeyValuePair[] Criteria) : ISerializable + { + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteVarInt(Criteria.Length); + foreach (var (key, value) in Criteria) + { + buffer.WriteIdentifier(key); + value.Write(buffer); + } + } + + /// + public static AdvancementProgress Read(PacketBuffer buffer) + { + var criteriaCount = buffer.ReadVarInt(); + var criteria = new KeyValuePair[criteriaCount]; + for (int i = 0; i < criteriaCount; i++) + { + var key = buffer.ReadIdentifier(); + var value = CriterionProgress.Read(buffer); + criteria[i] = new(key, value); + } + + return new AdvancementProgress(criteria); + } + } + + /// + /// Represents the progress of a criterion + /// + /// The date of achieving the criterion + public sealed record CriterionProgress( + long? DateOfAchieving) : ISerializable + { + /// + public void Write(PacketBuffer buffer) + { + var achieved = DateOfAchieving != null; + buffer.WriteBool(achieved); + if (achieved) + { + buffer.WriteLong(DateOfAchieving!.Value); + } + } + + /// + public static CriterionProgress Read(PacketBuffer buffer) + { + var achieved = buffer.ReadBool(); + long? dateOfAchieving = null; + if (achieved) + { + dateOfAchieving = buffer.ReadLong(); + } + + return new CriterionProgress( + dateOfAchieving); + } + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAttributesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAttributesPacket.cs index 2ac7b818..9ebfe68e 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAttributesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateAttributesPacket.cs @@ -5,23 +5,23 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; #pragma warning disable CS1591 -public sealed record UpdateAttributesPacket( +public sealed partial record UpdateAttributesPacket( int EntityId, Attribute[] Attributes -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.CB_Play_EntityUpdateAttributes; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarIntArray(Attributes, (buffer, attribute) => attribute.Write(buffer)); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateAttributesPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var attributes = buffer.ReadVarIntArray(Attribute.Read); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateLightPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateLightPacket.cs index 034f5e6e..96aba1f4 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateLightPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateLightPacket.cs @@ -17,7 +17,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// BitSet for empty block light sections /// Array of sky light data /// Array of block light data -public sealed record UpdateLightPacket( +public sealed partial record UpdateLightPacket( int ChunkX, int ChunkZ, BitSet SkyLightMask, @@ -26,7 +26,7 @@ public sealed record UpdateLightPacket( BitSet EmptyBlockLightMask, LightArray[] SkyLightArrays, LightArray[] BlockLightArrays -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -34,7 +34,7 @@ LightArray[] BlockLightArrays public static PacketType StaticType => PacketType.CB_Play_UpdateLight; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(ChunkX); buffer.WriteVarInt(ChunkZ); @@ -55,7 +55,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateLightPacket Read(PacketBuffer buffer, MinecraftData data) { var chunkX = buffer.ReadVarInt(); var chunkZ = buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateObjectivesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateObjectivesPacket.cs index b052d766..50ce96c9 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateObjectivesPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateObjectivesPacket.cs @@ -15,14 +15,14 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The text to be displayed for the score /// The type of the objective /// The number format for the score -public sealed record UpdateObjectivesPacket( +public sealed partial record UpdateObjectivesPacket( string ObjectiveName, ObjectiveMode Mode, Chat? ObjectiveValue, // this field can not be named "Type" or "ObjectiveType" ObjectiveType? ObjectiveTypeValue, IScoreboardNumberFormat? NumberFormat -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -30,7 +30,7 @@ public sealed record UpdateObjectivesPacket( public static PacketType StaticType => PacketType.CB_Play_ScoreboardObjective; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(ObjectiveName); buffer.WriteByte((byte)Mode); @@ -51,7 +51,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateObjectivesPacket Read(PacketBuffer buffer, MinecraftData data) { var objectiveName = buffer.ReadString(); var mode = (ObjectiveMode)buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipeBookPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipeBookPacket.cs index 3d93568a..47c70b2d 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipeBookPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipeBookPacket.cs @@ -20,7 +20,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// If true, the filtering option is active when the player opens its inventory. /// List of recipe IDs. /// Optional list of recipe IDs, only present if action is Init. -public sealed record UpdateRecipeBookPacket( +public sealed partial record UpdateRecipeBookPacket( RecipeBookAction Action, bool CraftingRecipeBookOpen, bool CraftingRecipeBookFilterActive, @@ -31,7 +31,7 @@ public sealed record UpdateRecipeBookPacket( bool SmokerRecipeBookOpen, bool SmokerRecipeBookFilterActive, Identifier[] RecipeIds, - Identifier[]? OptionalRecipeIds) : IPacket + Identifier[]? OptionalRecipeIds) : IPacketStatic { /// public PacketType Type => StaticType; @@ -39,7 +39,7 @@ public sealed record UpdateRecipeBookPacket( public static PacketType StaticType => PacketType.CB_Play_UnlockRecipes; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Action); buffer.WriteBool(CraftingRecipeBookOpen); @@ -69,7 +69,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateRecipeBookPacket Read(PacketBuffer buffer, MinecraftData data) { var action = (RecipeBookAction)buffer.ReadVarInt(); var craftingRecipeBookOpen = buffer.ReadBool(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipesPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipesPacket.cs new file mode 100644 index 00000000..265109e7 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateRecipesPacket.cs @@ -0,0 +1,427 @@ +using System.Collections.Frozen; +using MineSharp.Core.Common; +using MineSharp.Core.Common.Items; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; +using static MineSharp.Protocol.Packets.Clientbound.Play.UpdateRecipesPacket; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Represents a packet sent by the server to update the list of recipes. +/// +public sealed partial record UpdateRecipesPacket( + Recipe[] Recipes +) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_DeclareRecipes; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(Recipes.Length); + foreach (var recipe in Recipes) + { + recipe.Write(buffer, data); + } + } + + /// + public static UpdateRecipesPacket Read(PacketBuffer buffer, MinecraftData data) + { + var numRecipes = buffer.ReadVarInt(); + var recipes = new Recipe[numRecipes]; + for (int i = 0; i < numRecipes; i++) + { + recipes[i] = Recipe.Read(buffer, data); + } + return new UpdateRecipesPacket(recipes); + } + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public interface IUpdateRecipesData + { + public void Write(PacketBuffer buffer, MinecraftData data); + } + + public interface IUpdateRecipesDataStatic + { + public static abstract IUpdateRecipesData Read(PacketBuffer buffer, MinecraftData data); + } + + public static class UpdateRecipesDataRegistry + { + public static IUpdateRecipesData Read(PacketBuffer buffer, MinecraftData data, Identifier type) + { + if (!UpdateRecipesDataTypeFactories.TryGetValue(type, out var reader)) + { + throw new InvalidOperationException($"Unsupported data type: {type}"); + } + return reader(buffer, data); + } + + public static readonly FrozenDictionary> UpdateRecipesDataTypeFactories; + + static UpdateRecipesDataRegistry() + { + UpdateRecipesDataTypeFactories = InitializeUpdateRecipesDataTypes(); + } + + private static FrozenDictionary> InitializeUpdateRecipesDataTypes() + { + var dict = new Dictionary>(); + + void Register(params Identifier[] identifiers) + where T : IUpdateRecipesData, IUpdateRecipesDataStatic + { + var factory = T.Read; + foreach (var identifier in identifiers) + { + dict.Add(identifier, factory); + } + } + + // TODO: Does this data come from some registry? + Register( + Identifier.Parse("minecraft:crafting_shapeless") + ); + Register( + Identifier.Parse("minecraft:crafting_shaped") + ); + Register( + Identifier.Parse("minecraft:crafting_special_armordye"), + Identifier.Parse("minecraft:crafting_special_bookcloning"), + Identifier.Parse("minecraft:crafting_special_mapcloning"), + Identifier.Parse("minecraft:crafting_special_mapextending"), + Identifier.Parse("minecraft:crafting_special_firework_rocket"), + Identifier.Parse("minecraft:crafting_special_firework_star"), + Identifier.Parse("minecraft:crafting_special_firework_star_fade"), + Identifier.Parse("minecraft:crafting_special_repairitem"), + Identifier.Parse("minecraft:crafting_special_tippedarrow"), + Identifier.Parse("minecraft:crafting_special_bannerduplicate"), + Identifier.Parse("minecraft:crafting_special_shielddecoration"), + Identifier.Parse("minecraft:crafting_special_shulkerboxcoloring"), + Identifier.Parse("minecraft:crafting_special_suspiciousstew"), + Identifier.Parse("minecraft:crafting_decorated_pot") + ); + Register( + Identifier.Parse("minecraft:smelting"), + Identifier.Parse("minecraft:blasting"), + Identifier.Parse("minecraft:smoking"), + Identifier.Parse("minecraft:campfire_cooking") + ); + Register( + Identifier.Parse("minecraft:stonecutting") + ); + Register( + Identifier.Parse("minecraft:smithing_transform") + ); + Register( + Identifier.Parse("minecraft:smithing_trim") + ); + + return dict.ToFrozenDictionary(); + } + } + + /// + /// Represents a recipe in the update recipes packet. + /// + public sealed record Recipe( + Identifier Type, + Identifier RecipeId, + IUpdateRecipesData Data + ) : ISerializableWithMinecraftData + { + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteIdentifier(Type); + buffer.WriteIdentifier(RecipeId); + Data.Write(buffer, data); + } + + /// + public static Recipe Read(PacketBuffer buffer, MinecraftData data) + { + var type = buffer.ReadIdentifier(); + var recipeId = buffer.ReadIdentifier(); + var recipeData = UpdateRecipesDataRegistry.Read(buffer, data, type); + return new Recipe(type, recipeId, recipeData); + } + } + + public enum CraftingCategory + { + Building, + Redstone, + Equipment, + Misc + } + + public sealed record ShapelessCraftingData( + string Group, + CraftingCategory Category, + Ingredient[] Ingredients, + Item Result + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Group); + buffer.WriteVarInt((int)Category); + buffer.WriteVarInt(Ingredients.Length); + foreach (var ingredient in Ingredients) + { + ingredient.Write(buffer, data); + } + buffer.WriteOptionalItem(Result); + } + + /// + public static ShapelessCraftingData Read(PacketBuffer buffer, MinecraftData data) + { + var group = buffer.ReadString(); + var category = (CraftingCategory)buffer.ReadVarInt(); + var ingredientCount = buffer.ReadVarInt(); + var ingredients = new Ingredient[ingredientCount]; + for (int i = 0; i < ingredientCount; i++) + { + ingredients[i] = Ingredient.Read(buffer, data); + } + var result = buffer.ReadOptionalItem(data)!; + return new ShapelessCraftingData(group, category, ingredients, result); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record ShapedCraftingData( + string Group, + CraftingCategory Category, + int Width, + int Height, + Ingredient[] Ingredients, + Item Result, + bool ShowNotification + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Group); + buffer.WriteVarInt((int)Category); + buffer.WriteVarInt(Width); + buffer.WriteVarInt(Height); + foreach (var ingredient in Ingredients) + { + ingredient.Write(buffer, data); + } + buffer.WriteOptionalItem(Result); + buffer.WriteBool(ShowNotification); + } + + public static ShapedCraftingData Read(PacketBuffer buffer, MinecraftData data) + { + var group = buffer.ReadString(); + var category = (CraftingCategory)buffer.ReadVarInt(); + var width = buffer.ReadVarInt(); + var height = buffer.ReadVarInt(); + var ingredients = new Ingredient[width * height]; + for (int i = 0; i < ingredients.Length; i++) + { + ingredients[i] = Ingredient.Read(buffer, data); + } + var result = buffer.ReadOptionalItem(data)!; + var showNotification = buffer.ReadBool(); + return new ShapedCraftingData(group, category, width, height, ingredients, result, showNotification); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record SpecialCraftingData( + CraftingCategory Category + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt((int)Category); + } + + public static SpecialCraftingData Read(PacketBuffer buffer, MinecraftData data) + { + var category = (CraftingCategory)buffer.ReadVarInt(); + return new SpecialCraftingData(category); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public enum SmeltingCraftingCategory + { + Food, + Blocks, + Misc + } + + public sealed record SmeltingData( + string Group, + SmeltingCraftingCategory Category, + Ingredient Ingredient, + Item Result, + float Experience, + int CookingTime + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Group); + buffer.WriteVarInt((int)Category); + Ingredient.Write(buffer, data); + buffer.WriteOptionalItem(Result); + buffer.WriteFloat(Experience); + buffer.WriteVarInt(CookingTime); + } + + public static SmeltingData Read(PacketBuffer buffer, MinecraftData data) + { + var group = buffer.ReadString(); + var category = (SmeltingCraftingCategory)buffer.ReadVarInt(); + var ingredient = Ingredient.Read(buffer, data); + var result = buffer.ReadOptionalItem(data)!; + var experience = buffer.ReadFloat(); + var cookingTime = buffer.ReadVarInt(); + return new SmeltingData(group, category, ingredient, result, experience, cookingTime); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record StonecuttingData( + string Group, + Ingredient Ingredient, + Item Result + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Group); + Ingredient.Write(buffer, data); + buffer.WriteOptionalItem(Result); + } + + public static StonecuttingData Read(PacketBuffer buffer, MinecraftData data) + { + var group = buffer.ReadString(); + var ingredient = Ingredient.Read(buffer, data); + var result = buffer.ReadOptionalItem(data)!; + return new StonecuttingData(group, ingredient, result); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record SmithingTransformData( + Ingredient Template, + Ingredient Base, + Ingredient Addition, + Item Result + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + Template.Write(buffer, data); + Base.Write(buffer, data); + Addition.Write(buffer, data); + buffer.WriteOptionalItem(Result); + } + + public static SmithingTransformData Read(PacketBuffer buffer, MinecraftData data) + { + var template = Ingredient.Read(buffer, data); + var baseItem = Ingredient.Read(buffer, data); + var addition = Ingredient.Read(buffer, data); + var result = buffer.ReadOptionalItem(data)!; + return new SmithingTransformData(template, baseItem, addition, result); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record SmithingTrimData( + Ingredient Template, + Ingredient Base, + Ingredient Addition + ) : IUpdateRecipesData, IUpdateRecipesDataStatic, ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + Template.Write(buffer, data); + Base.Write(buffer, data); + Addition.Write(buffer, data); + } + + public static SmithingTrimData Read(PacketBuffer buffer, MinecraftData data) + { + var template = Ingredient.Read(buffer, data); + var baseItem = Ingredient.Read(buffer, data); + var addition = Ingredient.Read(buffer, data); + return new SmithingTrimData(template, baseItem, addition); + } + + static IUpdateRecipesData IUpdateRecipesDataStatic.Read(PacketBuffer buffer, MinecraftData data) + { + return Read(buffer, data); + } + } + + public sealed record Ingredient( + Item[] Items + ) : ISerializableWithMinecraftData + { + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(Items.Length); + foreach (var item in Items) + { + buffer.WriteOptionalItem(item); + } + } + + public static Ingredient Read(PacketBuffer buffer, MinecraftData data) + { + var count = buffer.ReadVarInt(); + var items = new Item[count]; + for (int i = 0; i < count; i++) + { + items[i] = buffer.ReadOptionalItem(data)!; + } + return new Ingredient(items); + } + } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateScorePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateScorePacket.cs index 791290bd..5ec54279 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateScorePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateScorePacket.cs @@ -14,13 +14,13 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The score to be displayed next to the entry /// The custom display name /// The number format for the score -public sealed record UpdateScorePacket( +public sealed partial record UpdateScorePacket( string EntityName, string ObjectiveName, int Value, Chat? DisplayName, IScoreboardNumberFormat? NumberFormat -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -28,7 +28,7 @@ public sealed record UpdateScorePacket( public static PacketType StaticType => PacketType.CB_Play_ScoreboardScore; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(EntityName); buffer.WriteString(ObjectiveName); @@ -52,7 +52,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateScorePacket Read(PacketBuffer buffer, MinecraftData data) { var entityName = buffer.ReadString(); var objectiveName = buffer.ReadString(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTagsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTagsPacket.cs new file mode 100644 index 00000000..0aeb8c7f --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTagsPacket.cs @@ -0,0 +1,32 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; + +namespace MineSharp.Protocol.Packets.Clientbound.Play; + +/// +/// Update Tags packet +/// +/// Array of registries with their tags +public sealed partial record UpdateTagsPacket(Registry[] Registries) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.CB_Play_Tags; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarIntArray(Registries, (buffer, registry) => registry.Write(buffer)); + } + + /// + public static UpdateTagsPacket Read(PacketBuffer buffer, MinecraftData data) + { + var registries = buffer.ReadVarIntArray(Registry.Read); + + return new UpdateTagsPacket(registries); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTeamsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTeamsPacket.cs index dc702a84..f0126996 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTeamsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTeamsPacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// A unique name for the team /// The data for the method type of this packet -public sealed record UpdateTeamsPacket(string TeamName, IUpdateTeamsMethod MethodData) : IPacket +public sealed partial record UpdateTeamsPacket(string TeamName, IUpdateTeamsMethod MethodData) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,7 +21,7 @@ public sealed record UpdateTeamsPacket(string TeamName, IUpdateTeamsMethod Metho public static PacketType StaticType => PacketType.CB_Play_Teams; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(TeamName); buffer.WriteByte((byte)MethodData.MethodType); @@ -29,7 +29,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateTeamsPacket Read(PacketBuffer buffer, MinecraftData data) { var teamName = buffer.ReadString(); var method = (UpdateTeamsMethodType)buffer.ReadByte(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTimePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTimePacket.cs index 30d253eb..f06ed888 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTimePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/UpdateTimePacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// The world age in ticks /// The time of day in ticks -public sealed record UpdateTimePacket(long WorldAge, long TimeOfDay) : IPacket +public sealed partial record UpdateTimePacket(long WorldAge, long TimeOfDay) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,14 +21,14 @@ public sealed record UpdateTimePacket(long WorldAge, long TimeOfDay) : IPacket public static PacketType StaticType => PacketType.CB_Play_UpdateTime; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(WorldAge); buffer.WriteLong(TimeOfDay); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UpdateTimePacket Read(PacketBuffer buffer, MinecraftData data) { var worldAge = buffer.ReadLong(); var timeOfDay = buffer.ReadLong(); diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowItemsPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowItemsPacket.cs index a4d0f81e..04b4eadf 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowItemsPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowItemsPacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The state ID of the window. /// The items in the window. /// The selected item in the window. -public sealed record WindowItemsPacket(byte WindowId, int StateId, Item?[] Items, Item? SelectedItem) : IPacket +public sealed partial record WindowItemsPacket(byte WindowId, int StateId, Item?[] Items, Item? SelectedItem) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,7 +21,7 @@ public sealed record WindowItemsPacket(byte WindowId, int StateId, Item?[] Items public static PacketType StaticType => PacketType.CB_Play_WindowItems; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); buffer.WriteVarInt(StateId); @@ -30,13 +30,13 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static WindowItemsPacket Read(PacketBuffer buffer, MinecraftData data) { return new WindowItemsPacket( buffer.ReadByte(), buffer.ReadVarInt(), - buffer.ReadVarIntArray(buff => buff.ReadOptionalItem(version)), - buffer.ReadOptionalItem(version)); + buffer.ReadVarIntArray(buff => buff.ReadOptionalItem(data)), + buffer.ReadOptionalItem(data)); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowSetSlotPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowSetSlotPacket.cs index 71dea1c8..62c21fef 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowSetSlotPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WindowSetSlotPacket.cs @@ -12,7 +12,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// The ID of the window. /// The state ID of the window. /// The slot to be set. -public sealed record WindowSetSlotPacket(sbyte WindowId, int StateId, Slot Slot) : IPacket +public sealed partial record WindowSetSlotPacket(sbyte WindowId, int StateId, Slot Slot) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record WindowSetSlotPacket(sbyte WindowId, int StateId, Slot Slot) public static PacketType StaticType => PacketType.CB_Play_SetSlot; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteSByte(WindowId); buffer.WriteVarInt(StateId); @@ -28,12 +28,12 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static WindowSetSlotPacket Read(PacketBuffer buffer, MinecraftData data) { return new WindowSetSlotPacket( buffer.ReadSByte(), buffer.ReadVarInt(), - buffer.ReadSlot(version)); + buffer.ReadSlot(data)); } } diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WorldEventPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WorldEventPacket.cs index 5c125e0a..73085336 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/WorldEventPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/WorldEventPacket.cs @@ -16,7 +16,7 @@ namespace MineSharp.Protocol.Packets.Clientbound.Play; /// /// If true, the effect is played from 2 blocks away in the correct direction, ignoring distance-based volume adjustment. /// -public sealed record WorldEventPacket(EventType Event, Position Location, int Data, bool DisableRelativeVolume) : IPacket +public sealed partial record WorldEventPacket(EventType Event, Position Location, int Data, bool DisableRelativeVolume) : IPacketStatic { /// public PacketType Type => StaticType; @@ -24,7 +24,7 @@ public sealed record WorldEventPacket(EventType Event, Position Location, int Da public static PacketType StaticType => PacketType.CB_Play_WorldEvent; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt((int)Event); buffer.WritePosition(Location); @@ -33,14 +33,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static WorldEventPacket Read(PacketBuffer buffer, MinecraftData data) { var eventType = (EventType)buffer.ReadInt(); var location = buffer.ReadPosition(); - var data = buffer.ReadInt(); + var eventData = buffer.ReadInt(); var disableRelativeVolume = buffer.ReadBool(); - return new WorldEventPacket(eventType, location, data, disableRelativeVolume); + return new WorldEventPacket(eventType, location, eventData, disableRelativeVolume); } #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Status/PingResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Status/PingResponsePacket.cs index 1d9b5862..406ef542 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Status/PingResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Status/PingResponsePacket.cs @@ -3,12 +3,12 @@ using MineSharp.Data.Protocol; namespace MineSharp.Protocol.Packets.Clientbound.Status; -#pragma warning disable CS1591 + /// /// Packet for ping response /// /// The payload of the ping response -public sealed record PingResponsePacket(long Payload) : IPacket +public sealed partial record PingResponsePacket(long Payload) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,15 +16,14 @@ public sealed record PingResponsePacket(long Payload) : IPacket public static PacketType StaticType => PacketType.CB_Status_Ping; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(Payload); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PingResponsePacket Read(PacketBuffer buffer, MinecraftData data) { return new PingResponsePacket(buffer.ReadLong()); } } -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Status/StatusResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Status/StatusResponsePacket.cs index f8c7ca30..de795ebf 100644 --- a/Components/MineSharp.Protocol/Packets/Clientbound/Status/StatusResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Clientbound/Status/StatusResponsePacket.cs @@ -3,12 +3,12 @@ using MineSharp.Data.Protocol; namespace MineSharp.Protocol.Packets.Clientbound.Status; -#pragma warning disable CS1591 + /// /// Packet for server status response /// /// The server response -public sealed record StatusResponsePacket(string Response) : IPacket +public sealed partial record StatusResponsePacket(string Response) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,15 +16,14 @@ public sealed record StatusResponsePacket(string Response) : IPacket public static PacketType StaticType => PacketType.CB_Status_ServerInfo; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Response); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static StatusResponsePacket Read(PacketBuffer buffer, MinecraftData data) { return new StatusResponsePacket(buffer.ReadString()); } } -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs index ad4af480..65a2edfe 100644 --- a/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs +++ b/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs @@ -88,7 +88,7 @@ private async Task HandleEncryptionRequest(EncryptionRequestPacket packet) var encVerToken = rsa.Encrypt(packet.VerifyToken!, RSAEncryptionPadding.Pkcs1); EncryptionResponsePacket response; - if (ProtocolVersion.IsBetween(data.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2) + if (data.Version.Protocol.IsBetween(ProtocolVersion.V_1_19_0, ProtocolVersion.V_1_19_1) && client.Session.OnlineSession && client.Session.Certificate is not null) { diff --git a/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs index 7765e370..deb89437 100644 --- a/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs +++ b/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs @@ -64,7 +64,7 @@ private Task HandleLogin(LoginPacket packet) { return data.Version.Protocol switch { - <= ProtocolVersion.V_1_20 => client.SendClientInformationPacket(GameState), + <= ProtocolVersion.V_1_20_0 => client.SendClientInformationPacket(GameState), _ => Task.CompletedTask }; } diff --git a/Components/MineSharp.Protocol/Packets/HandshakeProtocol.cs b/Components/MineSharp.Protocol/Packets/HandshakeProtocol.cs index d7323ea0..1c65ba9a 100644 --- a/Components/MineSharp.Protocol/Packets/HandshakeProtocol.cs +++ b/Components/MineSharp.Protocol/Packets/HandshakeProtocol.cs @@ -5,6 +5,7 @@ using MineSharp.Data; using MineSharp.Protocol.Packets.Serverbound.Handshaking; using MineSharp.Protocol.Packets.Serverbound.Login; +using static MineSharp.Protocol.Packets.Serverbound.Login.LoginStartPacket; namespace MineSharp.Protocol.Packets; @@ -30,23 +31,32 @@ public static async Task PerformHandshake(MinecraftClient client, GameState next internal static LoginStartPacket GetLoginPacket(MinecraftData data, Session session) { - LoginStartPacket.SignatureContainer? signature = null; - - if (data.Version.Protocol >= ProtocolVersion.V_1_19_3 && session.Certificate != null) - { - signature = new( - new DateTimeOffset(session.Certificate.ExpiresAt).ToUnixTimeMilliseconds(), - session.Certificate.Keys.PublicKey, - data.Version.Protocol >= ProtocolVersion.V_1_19_2 - ? session.Certificate.PublicKeySignatureV2 - : session.Certificate.PublicKeySignature - ); - } - + var username = session.Username; Uuid? uuid = session.OnlineSession || data.Version.Protocol >= ProtocolVersion.V_1_20_2 ? session.Uuid : null; - return new(session.Username, signature, uuid); + if (data.Version.Protocol >= ProtocolVersion.V_1_20_2) + { + return new LoginStartPacketV_1_20_2(username, uuid!.Value); + } + else if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) + { + return new LoginStartPacketV_1_19_3(username, uuid); + } + else if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) + { + var signature = session.Certificate == null ? null : + new SignatureContainer( + new DateTimeOffset(session.Certificate.ExpiresAt).ToUnixTimeMilliseconds(), + session.Certificate.Keys.PublicKey, + data.Version.Protocol >= ProtocolVersion.V_1_19_1 + ? session.Certificate.PublicKeySignatureV2 + : session.Certificate.PublicKeySignature + ); + return new LoginStartPacketV_1_19_0(username, signature, uuid); + } + + return new LoginStartPacketV_1_7_0(username); } } diff --git a/Components/MineSharp.Protocol/Packets/IPacket.cs b/Components/MineSharp.Protocol/Packets/IPacket.cs index 709e534e..5ff801ee 100644 --- a/Components/MineSharp.Protocol/Packets/IPacket.cs +++ b/Components/MineSharp.Protocol/Packets/IPacket.cs @@ -1,6 +1,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets; @@ -12,28 +13,61 @@ public interface IPacket /// /// The corresponding /// - /// + /// /// public PacketType Type { get; } /// - /// The corresponding . - /// The same as but static. + /// Serialize the packet data into the buffer. /// - public static abstract PacketType StaticType { get; } + /// + /// + public void Write(PacketBuffer buffer, MinecraftData data); +} +/// +/// Represents a Minecraft packet +/// +public interface IPacketStatic : IPacket +{ /// - /// Serialize the packet data into the buffer. + /// The corresponding . + /// The same as but static. /// - /// - /// - public void Write(PacketBuffer buffer, MinecraftData version); + public static abstract PacketType StaticType { get; } /// /// Read the packet from the buffer /// /// - /// + /// /// - public static abstract IPacket Read(PacketBuffer buffer, MinecraftData version); + public static abstract IPacket Read(PacketBuffer buffer, MinecraftData data); } + +/// +/// Represents a Minecraft packet +/// +public interface IPacketStatic : IPacketStatic, ISerializableWithMinecraftData + where TSelf : IPacketStatic +{ +} + +/// +/// Represents a Minecraft packet that is sent from the server to the client +/// +public interface IPacketClientbound : IPacket +{ +} + +/// +/// Represents a Minecraft packet that is sent from the client to the server +/// +public interface IPacketServerbound : IPacket +{ +} + +public delegate TPacket PacketFactory(PacketBuffer buffer, MinecraftData data) + where TPacket : IPacket; + +public delegate IPacket PacketFactory(PacketBuffer buffer, MinecraftData data); diff --git a/Components/MineSharp.Protocol/Packets/IPacketVersionSubType.cs b/Components/MineSharp.Protocol/Packets/IPacketVersionSubType.cs new file mode 100644 index 00000000..753f1efd --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/IPacketVersionSubType.cs @@ -0,0 +1,47 @@ +using MineSharp.Core; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Protocol.Packets.NetworkTypes; + +namespace MineSharp.Protocol.Packets; + +public interface IPacketVersionSubType : IPacket +{ + public ProtocolVersion FirstVersionUsed { get; } +} + +public interface IPacketVersionSubType : IPacketVersionSubType + where TBasePacket : IPacketStatic +{ +} + +public interface IPacketVersionSubTypeStatic : IPacketVersionSubType +{ + public static abstract ProtocolVersion FirstVersionUsedStatic { get; } + + /// + /// Read the packet from the buffer + /// + /// + /// + /// + public static abstract IPacket Read(PacketBuffer buffer, MinecraftData data); +} + +public interface IPacketVersionSubTypeStatic : IPacketVersionSubTypeStatic + where TBasePacket : IPacketStatic +{ + /// + /// Read the packet from the buffer + /// + /// + /// + /// + public static new abstract TBasePacket Read(PacketBuffer buffer, MinecraftData data); +} + +public interface IPacketVersionSubTypeStatic : IPacketVersionSubTypeStatic, ISerializableWithMinecraftData + where TSelf : IPacketVersionSubTypeStatic + where TBasePacket : IPacketStatic +{ +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/ChatMessageItem.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/ChatMessageItem.cs index 252cdd30..b6bf1756 100644 --- a/Components/MineSharp.Protocol/Packets/NetworkTypes/ChatMessageItem.cs +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/ChatMessageItem.cs @@ -49,10 +49,9 @@ private ChatMessageItem(Uuid? sender, byte[]? signature) /// Serialize the ChatMessageItem into buffer /// /// - /// public void Write(PacketBuffer buffer) { - if (buffer.ProtocolVersion == ProtocolVersion.V_1_19_2) + if (buffer.ProtocolVersion == ProtocolVersion.V_1_19_1) { buffer.WriteUuid(Sender!.Value); buffer.WriteVarInt(Signature!.Length); @@ -72,14 +71,13 @@ public void Write(PacketBuffer buffer) /// Deserialize a ChatMessageItem from the buffer /// /// - /// /// public static ChatMessageItem Read(PacketBuffer buffer) { Uuid? uuid = null; byte[]? signature = null; - if (buffer.ProtocolVersion == ProtocolVersion.V_1_19_2) + if (buffer.ProtocolVersion == ProtocolVersion.V_1_19_1) { uuid = buffer.ReadUuid(); signature = new byte[buffer.ReadVarInt()]; @@ -95,7 +93,6 @@ public static ChatMessageItem Read(PacketBuffer buffer) } } - return new(uuid, signature); } } diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/DifficultyLevel.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/DifficultyLevel.cs new file mode 100644 index 00000000..a462eee7 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/DifficultyLevel.cs @@ -0,0 +1,14 @@ +namespace MineSharp.Protocol.Packets.NetworkTypes; + +/// +/// Enum representing the difficulty levels +/// +public enum DifficultyLevel : byte +{ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + Peaceful = 0, + Easy = 1, + Normal = 2, + Hard = 3 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/ISerializableWithMinecraftData.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/ISerializableWithMinecraftData.cs index f80eb65a..5ae5c7b9 100644 --- a/Components/MineSharp.Protocol/Packets/NetworkTypes/ISerializableWithMinecraftData.cs +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/ISerializableWithMinecraftData.cs @@ -1,13 +1,15 @@ -using MineSharp.Data; +using MineSharp.Core.Serialization; +using MineSharp.Data; -namespace MineSharp.Core.Serialization; +namespace MineSharp.Protocol.Packets.NetworkTypes; /// /// Interface for serializing and deserializing objects from and to /// while being aware of the Minecraft version /// /// -public interface ISerializableWithMinecraftData where T : ISerializableWithMinecraftData +public interface ISerializableWithMinecraftData + where T : ISerializableWithMinecraftData { /// /// Serialize the object into the buffer diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/ParticleData.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/ParticleData.cs new file mode 100644 index 00000000..2803eb46 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/ParticleData.cs @@ -0,0 +1,369 @@ +using System.Collections.Frozen; +using MineSharp.Core.Common; +using MineSharp.Core.Common.Items; +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; + +namespace MineSharp.Protocol.Packets.NetworkTypes; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +public interface IParticleData +{ + public void Write(PacketBuffer buffer, MinecraftData data); +} + +public interface IParticleDataStatic +{ + public static abstract IParticleData Read(PacketBuffer buffer, MinecraftData data); +} + +public static class ParticleDataRegistry +{ + public static IParticleData? Read(PacketBuffer buffer, MinecraftData data, int particleId) + { + var particleTypeIdentifier = data.Particles.GetName(particleId); + return Read(buffer, data, particleTypeIdentifier); + } + + public static IParticleData? Read(PacketBuffer buffer, MinecraftData data, Identifier typeIdentifier) + { + if (!ParticleDataFactories.TryGetValue(typeIdentifier, out var reader)) + { + return null; + } + return reader(buffer, data); + } + + public static readonly FrozenDictionary> ParticleDataFactories; + + static ParticleDataRegistry() + { + ParticleDataFactories = InitializeParticleData(); + } + + private static FrozenDictionary> InitializeParticleData() + { + var dict = new Dictionary>(); + + void Register(params Identifier[] identifiers) + where T : IParticleData, IParticleDataStatic + { + var factory = T.Read; + foreach (var identifier in identifiers) + { + dict.Add(identifier, factory); + } + } + + Register( + Identifier.Parse("minecraft:block"), + Identifier.Parse("minecraft:block_marker"), + Identifier.Parse("minecraft:falling_dust"), + Identifier.Parse("minecraft:dust_pillar") + ); + Register( + Identifier.Parse("minecraft:dust") + ); + Register( + Identifier.Parse("minecraft:dust_color_transition") + ); + Register( + Identifier.Parse("minecraft:entity_effect") + ); + Register( + Identifier.Parse("minecraft:sculk_charge") + ); + Register( + Identifier.Parse("minecraft:item") + ); + Register( + Identifier.Parse("minecraft:vibration") + ); + Register( + Identifier.Parse("minecraft:shriek") + ); + + return dict.ToFrozenDictionary(); + } +} +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + +/// +/// Represents particle data that includes a block state. +/// +/// The ID of the block state. +public sealed record BlockStateParticleData(int BlockState) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(BlockState); + } + + /// + public static BlockStateParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var blockState = buffer.ReadVarInt(); + + return new(blockState); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "dust" particle. +/// +/// The red RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The green RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The blue RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The scale, will be clamped between 0.01 and 4. +public sealed record DustParticleData( + float Red, + float Green, + float Blue, + float Scale +) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(Red); + buffer.WriteFloat(Green); + buffer.WriteFloat(Blue); + buffer.WriteFloat(Scale); + } + + /// + public static DustParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var red = buffer.ReadFloat(); + var green = buffer.ReadFloat(); + var blue = buffer.ReadFloat(); + var scale = buffer.ReadFloat(); + + return new DustParticleData(red, green, blue, scale); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "dust_color_transition" particle. +/// +/// The start red RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The start green RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The start blue RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The end red RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The end green RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The end blue RGB value, between 0 and 1. Divide actual RGB value by 255. +/// The scale, will be clamped between 0.01 and 4. +public sealed record DustColorTransitionParticleData( + float FromRed, + float FromGreen, + float FromBlue, + float ToRed, + float ToGreen, + float ToBlue, + float Scale +) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(FromRed); + buffer.WriteFloat(FromGreen); + buffer.WriteFloat(FromBlue); + buffer.WriteFloat(ToRed); + buffer.WriteFloat(ToGreen); + buffer.WriteFloat(ToBlue); + buffer.WriteFloat(Scale); + } + + /// + public static DustColorTransitionParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var fromRed = buffer.ReadFloat(); + var fromGreen = buffer.ReadFloat(); + var fromBlue = buffer.ReadFloat(); + var toRed = buffer.ReadFloat(); + var toGreen = buffer.ReadFloat(); + var toBlue = buffer.ReadFloat(); + var scale = buffer.ReadFloat(); + + return new(fromRed, fromGreen, fromBlue, toRed, toGreen, toBlue, scale); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "entity_effect" particle. +/// +/// The ARGB components of the color encoded as an Int. +public sealed record EntityEffectParticleData(int Color) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteInt(Color); + } + + /// + public static EntityEffectParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var color = buffer.ReadInt(); + + return new(color); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "sculk_charge" particle. +/// +/// How much the particle will be rotated when displayed. +public sealed record SculkChargeParticleData(float Roll) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(Roll); + } + + /// + public static SculkChargeParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var roll = buffer.ReadFloat(); + + return new(roll); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "item" particle. +/// +/// The item that will be used. +public sealed record ItemParticleData(Item Item) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteOptionalItem(Item); + } + + /// + public static ItemParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var item = buffer.ReadOptionalItem(data)!; + + return new(item); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +public enum PositionSourceType +{ + Block = 0, + Entity = 1 +} +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + +/// +/// Represents the data for the "vibration" particle. +/// +/// The source type of the position (Block or Entity). +/// TThe position of the block the vibration originated from, if the source type is Block. +/// The ID of the entity the vibration originated from, if the source type is Entity. +/// The height of the entity's eye relative to the entity, if the source type is Entity. +/// The amount of ticks it takes for the vibration to travel from its source to its destination. +public sealed record VibrationParticleData( + PositionSourceType PositionSourceType, + Position? BlockPosition, + int? EntityId, + float? EntityEyeHeight, + int Ticks +) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + public static readonly Identifier BlockPositionSourceType = Identifier.Parse("minecraft:block"); + public static readonly Identifier EntityPositionSourceType = Identifier.Parse("minecraft:entity"); + + public static readonly FrozenDictionary PositionSourceTypes = new Dictionary + { + { PositionSourceType.Block, BlockPositionSourceType }, + { PositionSourceType.Entity, EntityPositionSourceType }, + }.ToFrozenDictionary(); + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt((int)PositionSourceType); + var positionSourceTypeIdentifier = PositionSourceTypes[PositionSourceType]; + + if (positionSourceTypeIdentifier == BlockPositionSourceType) + { + buffer.WritePosition(BlockPosition ?? throw new InvalidOperationException($"{nameof(BlockPosition)} must be set when {nameof(PositionSourceType)} is {PositionSourceType}")); + } + else if (positionSourceTypeIdentifier == EntityPositionSourceType) + { + buffer.WriteVarInt(EntityId ?? throw new InvalidOperationException($"{nameof(EntityId)} must be set when {nameof(PositionSourceType)} is {PositionSourceType}")); + buffer.WriteFloat(EntityEyeHeight ?? throw new InvalidOperationException($"{nameof(EntityEyeHeight)} must be set when {nameof(PositionSourceType)} is {PositionSourceType}")); + } + + buffer.WriteVarInt(Ticks); + } + + /// + public static VibrationParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var positionSourceType = (PositionSourceType)buffer.ReadVarInt(); + var positionSourceTypeIdentifier = PositionSourceTypes[positionSourceType]; + + Position? blockPosition = null; + int? entityId = null; + float? entityEyeHeight = null; + if (positionSourceTypeIdentifier == BlockPositionSourceType) + { + blockPosition = buffer.ReadPosition(); + } + else if (positionSourceTypeIdentifier == EntityPositionSourceType) + { + entityId = buffer.ReadVarInt(); + entityEyeHeight = buffer.ReadFloat(); + } + var ticks = buffer.ReadVarInt(); + + return new(positionSourceType, blockPosition, entityId, entityEyeHeight, ticks); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} + +/// +/// Represents the data for the "shriek" particle. +/// +/// The time in ticks before the particle is displayed. +public sealed record ShriekParticleData(int Delay) : IParticleData, IParticleDataStatic, ISerializableWithMinecraftData +{ + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(Delay); + } + + /// + public static ShriekParticleData Read(PacketBuffer buffer, MinecraftData data) + { + var delay = buffer.ReadVarInt(); + + return new(delay); + } + + static IParticleData IParticleDataStatic.Read(PacketBuffer buffer, MinecraftData data) => Read(buffer, data); +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerAbilitiesFlags.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerAbilitiesFlags.cs new file mode 100644 index 00000000..6a9f3e93 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerAbilitiesFlags.cs @@ -0,0 +1,26 @@ +namespace MineSharp.Protocol.Packets.NetworkTypes; + +/// +/// Flags representing various player abilities. +/// +[Flags] +public enum PlayerAbilitiesFlags : byte +{ + /// + /// Player is invulnerable. + /// + Invulnerable = 0x01, + /// + /// Player is flying. + /// + Flying = 0x02, + /// + /// Player is allowed to fly. + /// + AllowFlying = 0x04, + /// + /// Player is in creative mode. + /// And can instantly break blocks. + /// + CreativeMode = 0x08, +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerActionStatus.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerActionStatus.cs new file mode 100644 index 00000000..162e0581 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/PlayerActionStatus.cs @@ -0,0 +1,52 @@ +namespace MineSharp.Protocol.Packets.NetworkTypes; + +/// +/// Specifies the status of a player action +/// +public enum PlayerActionStatus +{ + /// + /// Sent when the player starts digging a block. + /// If the block was instamined or the player is in creative mode, the client will not send Status = Finished digging, and will assume the server completed the destruction. + /// To detect this, it is necessary to calculate the block destruction speed server-side. + /// + StartDigging = 0, + /// + /// Sent when the player lets go of the Mine Block key (default: left click). + /// + /// Face is always set to -Y. + /// + CancelledDigging = 1, + /// + /// Sent when the client thinks it is finished. + /// + FinishedDigging = 2, + /// + /// Triggered by using the Drop Item key (default: Q) with the modifier to drop the entire selected stack (default: Control or Command, depending on OS). + /// + /// Location is always set to 0/0/0, Face is always set to -Y. + /// Sequence is always set to 0. + /// + DropItemStack = 3, + /// + /// Triggered by using the Drop Item key (default: Q). + /// + /// Location is always set to 0/0/0, Face is always set to -Y. + /// Sequence is always set to 0. + /// + DropItem = 4, + /// + /// Indicates that the currently held item should have its state updated such as eating food, pulling back bows, using buckets, etc. + /// + /// Location is always set to 0/0/0, Face is always set to -Y. + /// Sequence is always set to 0. + /// + ShootArrowOrFinishEating = 5, + /// + /// Used to swap or assign an item to the second hand. + /// + /// Location is always set to 0/0/0, Face is always set to -Y. + /// Sequence is always set to 0. + /// + SwapItemInHand = 6, +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/ResourcePackResult.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/ResourcePackResult.cs new file mode 100644 index 00000000..ec4a46b4 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/ResourcePackResult.cs @@ -0,0 +1,18 @@ +namespace MineSharp.Protocol.Packets.NetworkTypes; + +/// +/// Enum representing the possible results of a resource pack response +/// +public enum ResourcePackResult +{ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + Success = 0, + Declined = 1, + FailedDownload = 2, + Accepted = 3, + Downloaded = 4, + InvalidUrl = 5, + FailedToReload = 6, + Discarded = 7 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member +} diff --git a/Components/MineSharp.Protocol/Packets/NetworkTypes/TagRegistry.cs b/Components/MineSharp.Protocol/Packets/NetworkTypes/TagRegistry.cs new file mode 100644 index 00000000..51202c9b --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/NetworkTypes/TagRegistry.cs @@ -0,0 +1,70 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; + +namespace MineSharp.Protocol.Packets.NetworkTypes; + +/// +/// Registry record +/// +/// Registry identifier +/// Array of tags +public sealed record Registry(Identifier Identifier, Tag[] Tags) : ISerializable +{ + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteIdentifier(Identifier); + buffer.WriteVarInt(Tags.Length); + foreach (var tag in Tags) + { + tag.Write(buffer); + } + } + + /// + public static Registry Read(PacketBuffer buffer) + { + var identifier = buffer.ReadIdentifier(); + var length = buffer.ReadVarInt(); + var tags = new Tag[length]; + for (var i = 0; i < length; i++) + { + tags[i] = Tag.Read(buffer); + } + + return new Registry(identifier, tags); + } +} + +/// +/// Tag record +/// +/// Tag name +/// Array of entries +public sealed record Tag(Identifier TagName, int[] Entries) : ISerializable +{ + /// + public void Write(PacketBuffer buffer) + { + buffer.WriteIdentifier(TagName); + buffer.WriteVarInt(Entries.Length); + foreach (var entry in Entries) + { + buffer.WriteVarInt(entry); + } + } + + /// + public static Tag Read(PacketBuffer buffer) + { + var tagName = buffer.ReadIdentifier(); + var length = buffer.ReadVarInt(); + var entries = new int[length]; + for (var i = 0; i < length; i++) + { + entries[i] = buffer.ReadVarInt(); + } + + return new Tag(tagName, entries); + } +} diff --git a/Components/MineSharp.Protocol/Packets/PacketPalette.cs b/Components/MineSharp.Protocol/Packets/PacketPalette.cs index 2de2b3d4..986c8bff 100644 --- a/Components/MineSharp.Protocol/Packets/PacketPalette.cs +++ b/Components/MineSharp.Protocol/Packets/PacketPalette.cs @@ -1,12 +1,10 @@ using System.Collections.Frozen; -using MineSharp.Core.Serialization; -using MineSharp.Data; using MineSharp.Data.Protocol; using MineSharp.Protocol.Packets.Clientbound.Configuration; using MineSharp.Protocol.Packets.Clientbound.Login; using MineSharp.Protocol.Packets.Clientbound.Play; using MineSharp.Protocol.Packets.Clientbound.Status; -using MineSharp.Protocol.Packets.Serverbound.Configuration; +using MineSharp.Protocol.Packets.NetworkTypes; using MineSharp.Protocol.Packets.Serverbound.Handshaking; using MineSharp.Protocol.Packets.Serverbound.Login; using MineSharp.Protocol.Packets.Serverbound.Play; @@ -14,17 +12,29 @@ using NLog; using CBChatPacket = MineSharp.Protocol.Packets.Clientbound.Play.ChatPacket; using CBCloseWindowPacket = MineSharp.Protocol.Packets.Clientbound.Play.CloseWindowPacket; +using CBConfigurationAddResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.AddResourcePackPacket; using CBConfigurationKeepAlivePacket = MineSharp.Protocol.Packets.Clientbound.Configuration.KeepAlivePacket; +using CBConfigurationPluginMessagePacket = MineSharp.Protocol.Packets.Clientbound.Configuration.PluginMessagePacket; +using CBConfigurationRemoveResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.RemoveResourcePackPacket; +using CBConfigurationUpdateTagsPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.UpdateTagsPacket; using CBFinishConfigurationPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.FinishConfigurationPacket; using CBKeepAlivePacket = MineSharp.Protocol.Packets.Clientbound.Play.KeepAlivePacket; -using CBConfigurationPluginMessagePacket = MineSharp.Protocol.Packets.Clientbound.Configuration.PluginMessagePacket; +using CBPlayAddResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Play.AddResourcePackPacket; +using CBPlayChangeDifficultyPacket = MineSharp.Protocol.Packets.Clientbound.Play.ChangeDifficultyPacket; +using CBPlayMoveVehiclePacket = MineSharp.Protocol.Packets.Clientbound.Play.MoveVehiclePacket; +using CBPlayPingResponsePacket = MineSharp.Protocol.Packets.Clientbound.Play.PingResponsePacket; +using CBPlayPlayerAbilitiesPacket = MineSharp.Protocol.Packets.Clientbound.Play.PlayerAbilitiesPacket; +using CBPlayPluginMessagePacket = MineSharp.Protocol.Packets.Clientbound.Play.PluginMessagePacket; +using CBPlayRemoveResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Play.RemoveResourcePackPacket; +using CBPlayUpdateTagsPacket = MineSharp.Protocol.Packets.Clientbound.Play.UpdateTagsPacket; using CBSetHeldItemPacket = MineSharp.Protocol.Packets.Clientbound.Play.SetHeldItemPacket; -using ConfClientInformation = MineSharp.Protocol.Packets.Serverbound.Configuration.ClientInformationPacket; +using CBStatusPingResponsePacket = MineSharp.Protocol.Packets.Clientbound.Status.PingResponsePacket; +using ConfClientInformationPacket = MineSharp.Protocol.Packets.Serverbound.Configuration.ClientInformationPacket; using ConfigurationDisconnectPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.DisconnectPacket; using ConfPingPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.PingPacket; using ConfPongPacket = MineSharp.Protocol.Packets.Serverbound.Configuration.PongPacket; using LoginDisconnectPacket = MineSharp.Protocol.Packets.Clientbound.Login.DisconnectPacket; -using PlayClientInformation = MineSharp.Protocol.Packets.Serverbound.Play.ClientInformationPacket; +using PlayClientInformationPacket = MineSharp.Protocol.Packets.Serverbound.Play.ClientInformationPacket; using PlayDisconnectPacket = MineSharp.Protocol.Packets.Clientbound.Play.DisconnectPacket; using PlayPingPacket = MineSharp.Protocol.Packets.Clientbound.Play.PingPacket; using PlayPongPacket = MineSharp.Protocol.Packets.Serverbound.Play.PongPacket; @@ -32,33 +42,35 @@ using SBChatPacket = MineSharp.Protocol.Packets.Serverbound.Play.ChatPacket; using SBCloseWindowPacket = MineSharp.Protocol.Packets.Serverbound.Play.CloseWindowPacket; using SBConfigurationKeepAlivePacket = MineSharp.Protocol.Packets.Serverbound.Configuration.KeepAlivePacket; +using SBConfigurationPluginMessagePacket = MineSharp.Protocol.Packets.Serverbound.Configuration.PluginMessagePacket; +using SBConfigurationResourcePackResponsePacket = MineSharp.Protocol.Packets.Serverbound.Configuration.ResourcePackResponsePacket; using SBFinishConfigurationPacket = MineSharp.Protocol.Packets.Serverbound.Configuration.FinishConfigurationPacket; using SBKeepAlivePacket = MineSharp.Protocol.Packets.Serverbound.Play.KeepAlivePacket; -using SBConfigurationPluginMessagePacket = MineSharp.Protocol.Packets.Serverbound.Configuration.PluginMessagePacket; +using SBPlayChangeDifficultyPacket = MineSharp.Protocol.Packets.Serverbound.Play.ChangeDifficultyPacket; +using SBPlayMoveVehiclePacket = MineSharp.Protocol.Packets.Serverbound.Play.MoveVehiclePacket; +using SBPlayPingRequestPacket = MineSharp.Protocol.Packets.Serverbound.Play.PingRequestPacket; +using SBPlayPlayerAbilitiesPacket = MineSharp.Protocol.Packets.Serverbound.Play.PlayerAbilitiesPacket; +using SBPlayPluginMessagePacket = MineSharp.Protocol.Packets.Serverbound.Play.PluginMessagePacket; using SBSetHeldItemPacket = MineSharp.Protocol.Packets.Serverbound.Play.SetHeldItemPacket; -using CBPlayPluginMessagePacket = MineSharp.Protocol.Packets.Clientbound.Play.PluginMessagePacket; -using CBConfigurationAddResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.AddResourcePackPacket; -using CBConfigurationRemoveResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Configuration.RemoveResourcePackPacket; -using CBPlayAddResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Play.AddResourcePackPacket; -using CBPlayRemoveResourcePackPacket = MineSharp.Protocol.Packets.Clientbound.Play.RemoveResourcePackPacket; +using StatusPingRequestPacket = MineSharp.Protocol.Packets.Serverbound.Status.PingRequestPacket; namespace MineSharp.Protocol.Packets; internal static class PacketPalette { - public delegate IPacket PacketFactory(PacketBuffer buffer, MinecraftData version); private static readonly ILogger Logger = LogManager.GetCurrentClassLogger(); - private static readonly FrozenDictionary PacketFactories; + private static readonly FrozenDictionary> ClientboundPacketFactories; + private static readonly FrozenDictionary> ServerboundPacketFactories; static PacketPalette() { - PacketFactories = InitializePackets(); + (ClientboundPacketFactories, ServerboundPacketFactories) = InitializePackets(); } - public static PacketFactory? GetFactory(PacketType packetType) + public static PacketFactory? GetClientboundFactory(PacketType packetType) { - if (!PacketFactories.TryGetValue(packetType, out var packet)) + if (!ClientboundPacketFactories.TryGetValue(packetType, out var packet)) { // Logger.Trace($"Unknown packet for state {state}, direction {direction} and id {id}."); return null; @@ -67,197 +79,261 @@ static PacketPalette() return packet; } - private static FrozenDictionary InitializePackets() + private static (FrozenDictionary> Clientbound, FrozenDictionary> Serverbound) InitializePackets() { - Dictionary packetFactories = new(); + Dictionary> clientboundPacketFactories = new(); + Dictionary> serverboundPacketFactories = new(); + + void RegisterPacketClientbound() + where TPacket : IPacketStatic, IPacketClientbound + { + PacketFactory readDelegate = (buffer, data) => GetPacketFactory()(buffer, data); + clientboundPacketFactories.Add(TPacket.StaticType, readDelegate); + } + + void RegisterPacketServerbound() + where TPacket : IPacketStatic, IPacketServerbound + { + PacketFactory readDelegate = (buffer, data) => GetPacketFactory()(buffer, data); + serverboundPacketFactories.Add(TPacket.StaticType, readDelegate); + } - void RegisterPacket() - where TPacket : IPacket + PacketFactory GetPacketFactory() + where TPacket : IPacket, ISerializableWithMinecraftData { - packetFactories.Add(TPacket.StaticType, TPacket.Read); + return TPacket.Read; } // Handshaking - RegisterPacket(); + // SB + RegisterPacketServerbound(); // Login - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // CB + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // SB + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); // Status - RegisterPacket(); - RegisterPacket(); + // CB + RegisterPacketClientbound(); + RegisterPacketClientbound(); - RegisterPacket(); - RegisterPacket(); + // SB + RegisterPacketServerbound(); + RegisterPacketServerbound(); // Configuration - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // CB + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // SB + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); // Play - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // CB + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); + RegisterPacketClientbound(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); - RegisterPacket(); + // SB + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); + RegisterPacketServerbound(); - return packetFactories.ToFrozenDictionary(); + return (clientboundPacketFactories.ToFrozenDictionary(), serverboundPacketFactories.ToFrozenDictionary()); } } diff --git a/Components/MineSharp.Protocol/Packets/PacketVersionSubTypeLookup.cs b/Components/MineSharp.Protocol/Packets/PacketVersionSubTypeLookup.cs new file mode 100644 index 00000000..f48760a0 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/PacketVersionSubTypeLookup.cs @@ -0,0 +1,85 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using MineSharp.Core; +using MineSharp.Core.Serialization; +using MineSharp.Data; + +namespace MineSharp.Protocol.Packets; + +public sealed class PacketVersionSubTypeLookup : IReadOnlyDictionary> + where TBasePacket : IPacketStatic +{ + private readonly SortedList> protocolVersionToPacketFactory = new(); + public bool Frozen { get; private set; } + + public void RegisterVersionPacket() + where TVersionPacket : IPacketVersionSubTypeStatic + { + if (Frozen) + { + throw new InvalidOperationException("This lookup is already frozen"); + } + + var firstVersionUsedStatic = TVersionPacket.FirstVersionUsedStatic; + PacketFactory readDelegate = TVersionPacket.Read; + if (!protocolVersionToPacketFactory.TryAdd(firstVersionUsedStatic, readDelegate)) + { + throw new InvalidOperationException($"There is already a version specific packet registered for protocol version: {firstVersionUsedStatic}"); + } + } + + public void Freeze() + { + Frozen = true; + } + + public bool TryGetPacketFactory(ProtocolVersion protocolVersion, [NotNullWhen(true)] out PacketFactory? packetFactory) + { + return protocolVersionToPacketFactory.TryGetLowerBound(protocolVersion, out packetFactory); + } + + public PacketFactory GetPacketFactory(ProtocolVersion protocolVersion) + { + if (!TryGetPacketFactory(protocolVersion, out var packetFactory)) + { + // TODO: create custom exception + throw new Exception($"There is no version specific packet registered that is suitable for the protocol version: {protocolVersion}"); + } + return packetFactory; + } + + public TBasePacket Read(PacketBuffer buffer, MinecraftData data) + { + var packetFactory = GetPacketFactory(buffer.ProtocolVersion); + return packetFactory(buffer, data); + } + + #region Implementation of IReadOnlyDictionary + + public IEnumerable Keys => protocolVersionToPacketFactory.Keys; + public IEnumerable> Values => protocolVersionToPacketFactory.Values; + public int Count => protocolVersionToPacketFactory.Count; + public PacketFactory this[ProtocolVersion key] => protocolVersionToPacketFactory[key]; + + public bool ContainsKey(ProtocolVersion key) + { + return protocolVersionToPacketFactory.ContainsKey(key); + } + + public bool TryGetValue(ProtocolVersion key, [MaybeNullWhen(false)] out PacketFactory value) + { + return protocolVersionToPacketFactory.TryGetValue(key, out value); + } + + public IEnumerator>> GetEnumerator() + { + return protocolVersionToPacketFactory.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return protocolVersionToPacketFactory.GetEnumerator(); + } + + #endregion +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ClientInformationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ClientInformationPacket.cs index b89c1ad2..f7bb873e 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ClientInformationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ClientInformationPacket.cs @@ -16,7 +16,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Configuration; /// The main hand setting /// Whether text filtering is enabled /// Whether server listings are allowed -public sealed record ClientInformationPacket( +public sealed partial record ClientInformationPacket( string Locale, byte ViewDistance, ChatMode ChatMode, @@ -25,7 +25,7 @@ public sealed record ClientInformationPacket( PlayerHand MainHand, bool EnableTextFiltering, bool AllowServerListings -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; @@ -33,7 +33,7 @@ bool AllowServerListings public static PacketType StaticType => PacketType.SB_Configuration_Settings; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Locale); buffer.WriteByte(ViewDistance); @@ -46,7 +46,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ClientInformationPacket Read(PacketBuffer buffer, MinecraftData data) { return new ClientInformationPacket( buffer.ReadString(), diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/FinishConfigurationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/FinishConfigurationPacket.cs index 47f11676..3020de3d 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/FinishConfigurationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/FinishConfigurationPacket.cs @@ -4,7 +4,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Configuration; #pragma warning disable CS1591 -public sealed record FinishConfigurationPacket : IPacket +public sealed partial record FinishConfigurationPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -12,11 +12,11 @@ public sealed record FinishConfigurationPacket : IPacket public static PacketType StaticType => PacketType.SB_Configuration_FinishConfiguration; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static FinishConfigurationPacket Read(PacketBuffer buffer, MinecraftData data) { return new FinishConfigurationPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/KeepAlivePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/KeepAlivePacket.cs index 4ed55555..3490ea5f 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/KeepAlivePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/KeepAlivePacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Configuration; /// Keep alive packet /// /// The keep-alive ID -public sealed record KeepAlivePacket(long KeepAliveId) : IPacket +public sealed partial record KeepAlivePacket(long KeepAliveId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record KeepAlivePacket(long KeepAliveId) : IPacket public static PacketType StaticType => PacketType.SB_Configuration_KeepAlive; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(KeepAliveId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static KeepAlivePacket Read(PacketBuffer buffer, MinecraftData data) { return new KeepAlivePacket(buffer.ReadLong()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PluginMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PluginMessagePacket.cs index 06126252..0f97cbcb 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PluginMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PluginMessagePacket.cs @@ -8,9 +8,9 @@ namespace MineSharp.Protocol.Packets.Serverbound.Configuration; /// /// Plugin message packet /// -/// The name of the channel +/// The name of the channel /// The data of the plugin message -public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) : IPacket +public sealed partial record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacketStatic { /// public PacketType Type => StaticType; @@ -18,17 +18,18 @@ public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) : public static PacketType StaticType => PacketType.SB_Configuration_CustomPayload; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteIdentifier(ChannelName); + buffer.WriteIdentifier(Channel); buffer.WriteBytes(Data); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PluginMessagePacket Read(PacketBuffer buffer, MinecraftData data) { - var channelName = buffer.ReadIdentifier(); - var data = buffer.RestBuffer(); - return new PluginMessagePacket(channelName, data); + var channel = buffer.ReadIdentifier(); + var pluginData = buffer.RestBuffer(); + + return new PluginMessagePacket(channel, pluginData); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PongPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PongPacket.cs index 1f966fe8..179b2d68 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PongPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/PongPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Configuration; /// Pong packet /// /// The ID of the pong packet -public sealed record PongPacket(int Id) : IPacket +public sealed partial record PongPacket(int Id) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record PongPacket(int Id) : IPacket public static PacketType StaticType => PacketType.SB_Configuration_Pong; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(Id); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PongPacket Read(PacketBuffer buffer, MinecraftData data) { return new PongPacket(buffer.ReadInt()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ResourcePackResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ResourcePackResponsePacket.cs index 3123b324..cbf62e0b 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ResourcePackResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Configuration/ResourcePackResponsePacket.cs @@ -2,13 +2,14 @@ using MineSharp.Data; using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; + namespace MineSharp.Protocol.Packets.Serverbound.Configuration; -#pragma warning disable CS1591 /// /// Resource pack response packet /// /// The result of the resource pack response -public sealed record ResourcePackResponsePacket(ResourcePackResponsePacket.ResourcePackResult Result) : IPacket +public sealed partial record ResourcePackResponsePacket(ResourcePackResult Result) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,32 +17,16 @@ public sealed record ResourcePackResponsePacket(ResourcePackResponsePacket.Resou public static PacketType StaticType => PacketType.SB_Configuration_ResourcePackReceive; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Result); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ResourcePackResponsePacket Read(PacketBuffer buffer, MinecraftData data) { var result = (ResourcePackResult)buffer.ReadVarInt(); - return new ResourcePackResponsePacket(result); - } - /// - /// Enum representing the possible results of a resource pack response - /// - public enum ResourcePackResult - { - Success = 0, - Declined = 1, - FailedDownload = 2, - Accepted = 3, - Downloaded = 4, - InvalidUrl = 5, - FailedToReload = 6, - Discarded = 7 + return new ResourcePackResponsePacket(result); } } - -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Handshaking/HandshakePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Handshaking/HandshakePacket.cs index 54130920..4b5b3032 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Handshaking/HandshakePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Handshaking/HandshakePacket.cs @@ -1,4 +1,5 @@ -using MineSharp.Core.Common.Protocol; +using MineSharp.Core; +using MineSharp.Core.Common.Protocol; using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; @@ -13,7 +14,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Handshaking; /// The host address /// The port number /// The next game state -public sealed record HandshakePacket(int ProtocolVersion, string Host, ushort Port, GameState NextState) : IPacket +public sealed partial record HandshakePacket(ProtocolVersion ProtocolVersion, string Host, ushort Port, GameState NextState) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,18 +22,18 @@ public sealed record HandshakePacket(int ProtocolVersion, string Host, ushort Po public static PacketType StaticType => PacketType.SB_Handshake_SetProtocol; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(ProtocolVersion); + buffer.WriteVarInt((int)ProtocolVersion); buffer.WriteString(Host); buffer.WriteUShort(Port); buffer.WriteVarInt((int)NextState); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static HandshakePacket Read(PacketBuffer buffer, MinecraftData data) { - var protocolVersion = buffer.ReadVarInt(); + var protocolVersion = (ProtocolVersion)buffer.ReadVarInt(); var host = buffer.ReadString(); var port = buffer.ReadUShort(); var nextState = (GameState)buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Login/EncryptionResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Login/EncryptionResponsePacket.cs index 5fe94389..054dcc94 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Login/EncryptionResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Login/EncryptionResponsePacket.cs @@ -13,7 +13,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Login; /// The shared secret /// The verify token /// The crypto container -public sealed record EncryptionResponsePacket(byte[] SharedSecret, byte[]? VerifyToken, CryptoContainer? Crypto) : IPacket +public sealed partial record EncryptionResponsePacket(byte[] SharedSecret, byte[]? VerifyToken, CryptoContainer? Crypto) : IPacketStatic { /// public PacketType Type => StaticType; @@ -21,12 +21,12 @@ public sealed record EncryptionResponsePacket(byte[] SharedSecret, byte[]? Verif public static PacketType StaticType => PacketType.SB_Login_EncryptionBegin; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(SharedSecret.Length); buffer.WriteBytes(SharedSecret); - if (ProtocolVersion.IsBetween(version.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)) + if (data.Version.Protocol.IsBetween(ProtocolVersion.V_1_19_0, ProtocolVersion.V_1_19_1)) { var hasVerifyToken = VerifyToken != null; buffer.WriteBool(hasVerifyToken); @@ -35,7 +35,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) { if (Crypto == null) { - throw new MineSharpPacketVersionException(nameof(Crypto), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(Crypto), data.Version.Protocol); } Crypto.Write(buffer); @@ -45,7 +45,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) if (VerifyToken == null) { - throw new MineSharpPacketVersionException(nameof(VerifyToken), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(VerifyToken), data.Version.Protocol); } buffer.WriteVarInt(VerifyToken.Length); @@ -53,14 +53,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static EncryptionResponsePacket Read(PacketBuffer buffer, MinecraftData data) { var sharedSecretLength = buffer.ReadVarInt(); var sharedSecret = buffer.ReadBytes(sharedSecretLength); CryptoContainer? crypto = null; byte[]? verifyToken = null; - if (ProtocolVersion.IsBetween(version.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)) + if (data.Version.Protocol.IsBetween(ProtocolVersion.V_1_19_0, ProtocolVersion.V_1_19_1)) { var hasVerifyToken = buffer.ReadBool(); buffer.WriteBool(hasVerifyToken); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginAcknowledgedPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginAcknowledgedPacket.cs index 96915af1..71068565 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginAcknowledgedPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginAcknowledgedPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Login; /// /// Login acknowledged packet /// -public sealed record LoginAcknowledgedPacket() : IPacket +public sealed partial record LoginAcknowledgedPacket() : IPacketStatic { /// public PacketType Type => StaticType; @@ -15,11 +15,11 @@ public sealed record LoginAcknowledgedPacket() : IPacket public static PacketType StaticType => PacketType.SB_Login_LoginAcknowledged; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LoginAcknowledgedPacket Read(PacketBuffer buffer, MinecraftData data) { return new LoginAcknowledgedPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginPluginResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginPluginResponsePacket.cs index 94f12880..0e8a63ed 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginPluginResponsePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginPluginResponsePacket.cs @@ -9,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Login; /// /// The message ID /// The data -public sealed record LoginPluginResponsePacket(int MessageId, byte[]? Data) : IPacket +public sealed partial record LoginPluginResponsePacket(int MessageId, byte[]? Data) : IPacketStatic { /// public PacketType Type => StaticType; @@ -20,7 +20,7 @@ public sealed record LoginPluginResponsePacket(int MessageId, byte[]? Data) : IP public bool Successful => Data != null; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(MessageId); buffer.WriteBool(Data != null); @@ -32,17 +32,17 @@ public void Write(PacketBuffer buffer, MinecraftData version) } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static LoginPluginResponsePacket Read(PacketBuffer buffer, MinecraftData data) { var messageId = buffer.ReadVarInt(); var hasData = buffer.ReadBool(); - var data = hasData switch + var pluginData = hasData switch { true => buffer.RestBuffer(), false => null }; - return new LoginPluginResponsePacket(messageId, data); + return new LoginPluginResponsePacket(messageId, pluginData); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginStartPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginStartPacket.cs index 846bb6ff..31bf818a 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginStartPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Login/LoginStartPacket.cs @@ -1,129 +1,167 @@ -using MineSharp.Core; -using MineSharp.Core.Common; +using MineSharp.Core.Common; using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; -using MineSharp.Protocol.Exceptions; namespace MineSharp.Protocol.Packets.Serverbound.Login; -#pragma warning disable CS1591 -public sealed record LoginStartPacket : IPacket + +public abstract partial record LoginStartPacket : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Login_LoginStart; - // Here is no non-argument constructor allowed - // Do not use -#pragma warning disable CS8618 + // all versions contain these fields: + public abstract string Username { get; init; } + + // may only be called from sub class in this class private LoginStartPacket() -#pragma warning restore CS8618 { } /// - /// Constructor for versions before 1.19 + /// Version specific for /// - /// - public LoginStartPacket(string username) + public sealed partial record LoginStartPacketV_1_7_0(string Username) : LoginStartPacket { - Username = username; - } + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Username); + } - /// - /// Constructor for versions >= 1.19.3 - /// - /// - /// - public LoginStartPacket(string username, Uuid? playerUuid) - { - Username = username; - PlayerUuid = playerUuid; + /// + public static new LoginStartPacketV_1_7_0 Read(PacketBuffer buffer, MinecraftData data) + { + var username = buffer.ReadString(); + return new(username); + } } /// - /// Constructor for version 1.19-1.19.2 + /// Version specific for /// - /// - /// - /// - public LoginStartPacket(string username, SignatureContainer? signature, Uuid? playerUuid = null) + public sealed partial record LoginStartPacketV_1_19_0( + string Username, + SignatureContainer? Signature, + Uuid? PlayerUuid + ) : LoginStartPacket { - Username = username; - Signature = signature; - PlayerUuid = playerUuid; - } - - public string Username { get; init; } - public SignatureContainer? Signature { get; init; } - public Uuid? PlayerUuid { get; init; } - - public void Write(PacketBuffer buffer, MinecraftData version) - { - buffer.WriteString(Username); - - if (ProtocolVersion.IsBetween(version.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)) + /// + public override void Write(PacketBuffer buffer, MinecraftData data) { + buffer.WriteString(Username); + var hasSignature = Signature != null; buffer.WriteBool(hasSignature); - Signature?.Write(buffer); - } + if (hasSignature) + { + Signature!.Write(buffer); + } - if (version.Version.Protocol < ProtocolVersion.V_1_19_2) - { - return; + WriteOptionalUuid(buffer, PlayerUuid); } - if (version.Version.Protocol < ProtocolVersion.V_1_20_2) + /// + public static new LoginStartPacketV_1_19_0 Read(PacketBuffer buffer, MinecraftData data) { - buffer.WriteBool(PlayerUuid.HasValue); - if (PlayerUuid.HasValue) + var username = buffer.ReadString(); + + SignatureContainer? signature = null; + var hasSignature = buffer.ReadBool(); + if (hasSignature) { - buffer.WriteUuid(PlayerUuid!.Value); + signature = SignatureContainer.Read(buffer); } - return; + Uuid? playerUuid = ReadOptionalUuid(buffer); + return new(username, signature, playerUuid); } + } - if (!PlayerUuid.HasValue) + /// + /// Version specific for + /// + public sealed partial record LoginStartPacketV_1_19_3( + string Username, + Uuid? PlayerUuid + ) : LoginStartPacket + { + /// + public override void Write(PacketBuffer buffer, MinecraftData data) { - throw new MineSharpPacketVersionException(nameof(PlayerUuid), version.Version.Protocol); + buffer.WriteString(Username); + WriteOptionalUuid(buffer, PlayerUuid); } - buffer.WriteUuid(PlayerUuid.Value); + /// + public static new LoginStartPacketV_1_19_3 Read(PacketBuffer buffer, MinecraftData data) + { + var username = buffer.ReadString(); + Uuid? playerUuid = ReadOptionalUuid(buffer); + return new(username, playerUuid); + } } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + /// + /// Version specific for + /// + public sealed partial record LoginStartPacketV_1_20_2( + string Username, + Uuid PlayerUuid + ) : LoginStartPacket { - var username = buffer.ReadString(); - SignatureContainer? signature = null; - Uuid? playerUuid = null; + /// + public override void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(Username); + buffer.WriteUuid(PlayerUuid); + } - if (version.Version.Protocol is >= ProtocolVersion.V_1_19 and <= ProtocolVersion.V_1_19_2) + /// + public static new LoginStartPacketV_1_20_2 Read(PacketBuffer buffer, MinecraftData data) { - signature = SignatureContainer.Read(buffer); + var username = buffer.ReadString(); + var playerUuid = buffer.ReadUuid(); + return new(username, playerUuid); } + } - if (version.Version.Protocol >= ProtocolVersion.V_1_19_2) + private static void WriteOptionalUuid(PacketBuffer buffer, Uuid? uuid) + { + var hasUuid = uuid.HasValue; + buffer.WriteBool(hasUuid); + if (hasUuid) { - playerUuid = buffer.ReadUuid(); + buffer.WriteUuid(uuid!.Value); } + } - return new LoginStartPacket(username, signature, playerUuid); + private static Uuid? ReadOptionalUuid(PacketBuffer buffer) + { + Uuid? uuid = null; + var hasUuid = buffer.ReadBool(); + if (hasUuid) + { + uuid = buffer.ReadUuid(); + } + return uuid; } public sealed record SignatureContainer(long Timestamp, byte[] PublicKey, byte[] Signature) : ISerializable { + /// public void Write(PacketBuffer buffer) { buffer.WriteLong(Timestamp); buffer.WriteVarInt(PublicKey.Length); - buffer.WriteBytes(PublicKey.AsSpan()); + buffer.WriteBytes(PublicKey); buffer.WriteVarInt(Signature.Length); - buffer.WriteBytes(Signature.AsSpan()); + buffer.WriteBytes(Signature); } + /// public static SignatureContainer Read(PacketBuffer buffer) { var timestamp = buffer.ReadLong(); @@ -136,4 +174,3 @@ public static SignatureContainer Read(PacketBuffer buffer) } } } -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/AcknowledgeConfigurationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/AcknowledgeConfigurationPacket.cs new file mode 100644 index 00000000..45a8ad4c --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/AcknowledgeConfigurationPacket.cs @@ -0,0 +1,29 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Acknowledge Configuration packet sent by the client upon receiving a Start Configuration packet from the server. +/// +public sealed partial record AcknowledgeConfigurationPacket() : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_ConfigurationAcknowledged; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + // No fields to write + } + + /// + public static AcknowledgeConfigurationPacket Read(PacketBuffer buffer, MinecraftData data) + { + // No fields to read + return new AcknowledgeConfigurationPacket(); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeContainerSlotStatePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeContainerSlotStatePacket.cs new file mode 100644 index 00000000..91c90197 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeContainerSlotStatePacket.cs @@ -0,0 +1,38 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Change Container Slot State packet. +/// This packet is sent by the client when toggling the state of a Crafter. +/// +/// The ID of the slot that was changed +/// The ID of the window that was changed +/// The new state of the slot. True for enabled, false for disabled +public sealed partial record ChangeContainerSlotStatePacket(int SlotId, int WindowId, bool State) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SetSlotState; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(SlotId); + buffer.WriteVarInt(WindowId); + buffer.WriteBool(State); + } + + /// + public static ChangeContainerSlotStatePacket Read(PacketBuffer buffer, MinecraftData data) + { + var slotId = buffer.ReadVarInt(); + var windowId = buffer.ReadVarInt(); + var state = buffer.ReadBool(); + + return new(slotId, windowId, state); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeDifficultyPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeDifficultyPacket.cs new file mode 100644 index 00000000..fb639ca7 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeDifficultyPacket.cs @@ -0,0 +1,32 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Change difficulty packet +/// +/// The new difficulty level +public sealed partial record ChangeDifficultyPacket(DifficultyLevel NewDifficulty) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SetDifficulty; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteByte((byte)NewDifficulty); + } + + /// + public static ChangeDifficultyPacket Read(PacketBuffer buffer, MinecraftData data) + { + var newDifficulty = (DifficultyLevel)buffer.ReadByte(); + + return new(newDifficulty); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeRecipeBookSettingsPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeRecipeBookSettingsPacket.cs new file mode 100644 index 00000000..5a45abae --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChangeRecipeBookSettingsPacket.cs @@ -0,0 +1,51 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.ChangeRecipeBookSettingsPacket; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Packet sent by the client to change recipe book settings. +/// +/// The ID of the recipe book. +/// Whether the book is open. +/// Whether the filter is active. +public sealed partial record ChangeRecipeBookSettingsPacket(RecipeBookType BookId, bool BookOpen, bool FilterActive) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_RecipeBook; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt((int)BookId); + buffer.WriteBool(BookOpen); + buffer.WriteBool(FilterActive); + } + + /// + public static ChangeRecipeBookSettingsPacket Read(PacketBuffer buffer, MinecraftData data) + { + var bookId = (RecipeBookType)buffer.ReadVarInt(); + var bookOpen = buffer.ReadBool(); + var filterActive = buffer.ReadBool(); + + return new(bookId, bookOpen, filterActive); + } + + /// + /// Enum representing the different types of recipe books. + /// + public enum RecipeBookType + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + Crafting = 0, + Furnace = 1, + BlastFurnace = 2, + Smoker = 3 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatCommandPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatCommandPacket.cs index b1dc971b..88845e85 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatCommandPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatCommandPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record ChatCommandPacket : IPacket +public sealed partial record ChatCommandPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -94,33 +94,33 @@ public ChatCommandPacket(string command, long timestamp, long salt, ArgumentSign public int? MessageCount { get; init; } public byte[]? Acknowledged { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Command); buffer.WriteLong(Timestamp); buffer.WriteLong(Salt); - buffer.WriteVarIntArray(Signatures, (buffer, signature) => signature.Write(buffer, version)); + buffer.WriteVarIntArray(Signatures, (buffer, signature) => signature.Write(buffer, data)); - if (ProtocolVersion.IsBetween(version.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)) + if (data.Version.Protocol.IsBetween(ProtocolVersion.V_1_19_0, ProtocolVersion.V_1_19_1)) { if (!SignedPreview.HasValue) { - throw new MineSharpPacketVersionException(nameof(SignedPreview), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(SignedPreview), data.Version.Protocol); } buffer.WriteBool(SignedPreview.Value!); } - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { if (Acknowledged == null) { - throw new MineSharpPacketVersionException(nameof(Acknowledged), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(Acknowledged), data.Version.Protocol); } if (!MessageCount.HasValue) { - throw new MineSharpPacketVersionException(nameof(MessageCount), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(MessageCount), data.Version.Protocol); } buffer.WriteVarInt(MessageCount.Value); @@ -128,14 +128,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) return; } - if (version.Version.Protocol != ProtocolVersion.V_1_19_2) + if (data.Version.Protocol != ProtocolVersion.V_1_19_1) { return; } if (PreviousMessages == null) { - throw new MineSharpPacketVersionException(nameof(PreviousMessages), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(PreviousMessages), data.Version.Protocol); } buffer.WriteVarIntArray(PreviousMessages, (buf, val) => val.Write(buf)); @@ -153,22 +153,22 @@ public void Write(PacketBuffer buffer, MinecraftData version) private const int AfterMc1192AcknowledgedLength = 20; - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChatCommandPacket Read(PacketBuffer buffer, MinecraftData data) { var command = buffer.ReadString(); var timestamp = buffer.ReadLong(); var salt = buffer.ReadLong(); - var signatures = buffer.ReadVarIntArray((buf) => ArgumentSignature.Read(buf, version)); + var signatures = buffer.ReadVarIntArray((buf) => ArgumentSignature.Read(buf, data)); bool? signedPreview = null; - if (ProtocolVersion.IsBetween(version.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)) + if (data.Version.Protocol.IsBetween(ProtocolVersion.V_1_19_0, ProtocolVersion.V_1_19_1)) { signedPreview = buffer.ReadBool(); } byte[]? acknowledged = null; int? messageCount = null; - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { messageCount = buffer.ReadVarInt(); acknowledged = buffer.ReadBytes(AfterMc1192AcknowledgedLength); @@ -176,7 +176,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) ChatMessageItem[]? previousMessages = null; ChatMessageItem? lastRejectedMessage = null; - if (version.Version.Protocol == ProtocolVersion.V_1_19_2) + if (data.Version.Protocol == ProtocolVersion.V_1_19_1) { previousMessages = buffer.ReadVarIntArray(ChatMessageItem.Read); var hasLastRejectedMessage = buffer.ReadBool(); @@ -200,13 +200,13 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) }; } - public sealed record ArgumentSignature(string ArgumentName, byte[] Signature) + public sealed record ArgumentSignature(string ArgumentName, byte[] Signature) : ISerializableWithMinecraftData { - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(ArgumentName); - if (version.Version.Protocol <= ProtocolVersion.V_1_19_2) + if (data.Version.Protocol <= ProtocolVersion.V_1_19_1) { buffer.WriteVarInt(Signature.Length); buffer.WriteBytes(Signature); @@ -219,11 +219,11 @@ public void Write(PacketBuffer buffer, MinecraftData version) private const int AfterMc1192SignatureLength = 256; - public static ArgumentSignature Read(PacketBuffer buffer, MinecraftData version) + public static ArgumentSignature Read(PacketBuffer buffer, MinecraftData data) { var argumentName = buffer.ReadString(); byte[] signature; - if (version.Version.Protocol <= ProtocolVersion.V_1_19_2) + if (data.Version.Protocol <= ProtocolVersion.V_1_19_1) { var length = buffer.ReadVarInt(); signature = buffer.ReadBytes(length); @@ -232,7 +232,7 @@ public static ArgumentSignature Read(PacketBuffer buffer, MinecraftData version) { signature = buffer.ReadBytes(AfterMc1192SignatureLength); } - return new ArgumentSignature(argumentName, signature); + return new(argumentName, signature); } } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatMessagePacket.cs index 48cb27ce..667d9d13 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatMessagePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatMessagePacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record ChatMessagePacket : IPacket +public sealed partial record ChatMessagePacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -65,13 +65,13 @@ public ChatMessagePacket(string message, long timestamp, long salt, byte[]? sign public int? MessageCount { get; init; } public byte[]? Acknowledged { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Message); buffer.WriteLong(Timestamp); buffer.WriteLong(Salt); - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { var hasSignature = Signature != null; buffer.WriteBool(hasSignature); @@ -88,7 +88,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) if (MessageCount == null) { - throw new MineSharpPacketVersionException(nameof(MessageCount), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(MessageCount), data.Version.Protocol); } buffer.WriteVarInt(MessageCount.Value); @@ -99,12 +99,12 @@ public void Write(PacketBuffer buffer, MinecraftData version) // only 1.19-1.19.2 if (Signature == null) { - throw new MineSharpPacketVersionException(nameof(Signature), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(Signature), data.Version.Protocol); } if (SignedPreview == null) { - throw new MineSharpPacketVersionException(nameof(SignedPreview), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(SignedPreview), data.Version.Protocol); } buffer.WriteVarInt(Signature.Length); @@ -112,14 +112,14 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(SignedPreview.Value); - if (version.Version.Protocol != ProtocolVersion.V_1_19_2) + if (data.Version.Protocol != ProtocolVersion.V_1_19_1) { return; } if (PreviousMessages == null) { - throw new MineSharpPacketVersionException(nameof(PreviousMessages), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(PreviousMessages), data.Version.Protocol); } buffer.WriteVarIntArray(PreviousMessages, (buf, val) => val.Write(buf)); @@ -135,7 +135,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) LastRejectedMessage!.Write(buffer); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChatMessagePacket Read(PacketBuffer buffer, MinecraftData data) { var message = buffer.ReadString(); var timestamp = buffer.ReadLong(); @@ -147,7 +147,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) int? messageCount; byte[]? acknowledged; - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { var hasSignature = buffer.ReadBool(); @@ -174,7 +174,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) signedPreview = buffer.ReadBool(); - if (version.Version.Protocol != ProtocolVersion.V_1_19_2) + if (data.Version.Protocol != ProtocolVersion.V_1_19_1) { return new ChatMessagePacket(message, timestamp, salt, signature, signedPreview.Value); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatPacket.cs index 03f3b8c3..500e3154 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChatPacket.cs @@ -7,19 +7,19 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// /// ChatPacket used before 1.19 to send a Chat message /// -public sealed record ChatPacket(string Message) : IPacket +public sealed partial record ChatPacket(string Message) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_Chat; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Message); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChatPacket Read(PacketBuffer buffer, MinecraftData data) { return new ChatPacket(buffer.ReadString()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChunkBatchReceivedPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChunkBatchReceivedPacket.cs index 873e86cb..16a17320 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChunkBatchReceivedPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ChunkBatchReceivedPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// The ChunkBatchReceived packet, used since 1.20.2. /// https://wiki.vg/Protocol#Chunk_Batch_Received /// -public sealed record ChunkBatchReceivedPacket(float ChunksPerTick) : IPacket +public sealed partial record ChunkBatchReceivedPacket(float ChunksPerTick) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record ChunkBatchReceivedPacket(float ChunksPerTick) : IPacket public static PacketType StaticType => PacketType.SB_Play_ChunkBatchReceived; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteFloat(ChunksPerTick); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ChunkBatchReceivedPacket Read(PacketBuffer buffer, MinecraftData data) { return new ChunkBatchReceivedPacket(buffer.ReadFloat()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClickContainerButtonPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClickContainerButtonPacket.cs new file mode 100644 index 00000000..0f0c1936 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClickContainerButtonPacket.cs @@ -0,0 +1,39 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Used when clicking on window buttons. Until 1.14, this was only used by enchantment tables. +/// +/// The ID of the window sent by Open Screen. +/// Meaning depends on window type; see below. +public sealed partial record ClickContainerButtonPacket(byte WindowId, byte ButtonId) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_EnchantItem; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteByte(WindowId); + buffer.WriteByte(ButtonId); + } + + /// + public static ClickContainerButtonPacket Read(PacketBuffer buffer, MinecraftData data) + { + var windowId = buffer.ReadByte(); + var buttonId = buffer.ReadByte(); + + return new(windowId, buttonId); + } + + // TODO: Add all the meanings of the properties + // depends on the type of container but we only get the window ID here + // so we need static methods that have the container type as a parameter + // https://wiki.vg/index.php?title=Protocol&oldid=19208#Click_Container_Button +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientCommandPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientCommandPacket.cs index bb0b7b73..5f4600ad 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientCommandPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientCommandPacket.cs @@ -1,6 +1,7 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.ClientCommandPacket; namespace MineSharp.Protocol.Packets.Serverbound.Play; @@ -8,7 +9,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// Represents a client command packet. /// /// The action ID of the client command. -public sealed record ClientCommandPacket(int ActionId) : IPacket +public sealed partial record ClientCommandPacket(ClientCommandAction ActionId) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,15 +17,31 @@ public sealed record ClientCommandPacket(int ActionId) : IPacket public static PacketType StaticType => PacketType.SB_Play_ClientCommand; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(ActionId); + buffer.WriteVarInt((int)ActionId); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ClientCommandPacket Read(PacketBuffer buffer, MinecraftData data) { - var actionId = buffer.ReadVarInt(); + var actionId = (ClientCommandAction)buffer.ReadVarInt(); + return new ClientCommandPacket(actionId); } + + /// + /// Represents the action ID of the client command. + /// + public enum ClientCommandAction + { + /// + /// Sent when the client is ready to complete login and when the client is ready to respawn after death. + /// + PerformRespawn = 0, + /// + /// Sent when the client opens the Statistics menu. + /// + RequestStats = 1, + } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientInformationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientInformationPacket.cs index 1c672da1..89f16ed1 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientInformationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ClientInformationPacket.cs @@ -5,7 +5,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record ClientInformationPacket( +public sealed partial record ClientInformationPacket( string Locale, byte ViewDistance, ChatMode ChatMode, @@ -14,14 +14,14 @@ public sealed record ClientInformationPacket( PlayerHand MainHand, bool EnableTextFiltering, bool AllowServerListings -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_Settings; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteString(Locale); buffer.WriteByte(ViewDistance); @@ -33,7 +33,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(AllowServerListings); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ClientInformationPacket Read(PacketBuffer buffer, MinecraftData data) { return new ClientInformationPacket( buffer.ReadString(), diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/CloseWindowPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CloseWindowPacket.cs index 2731c572..d5b62812 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/CloseWindowPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CloseWindowPacket.cs @@ -4,19 +4,19 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record CloseWindowPacket(byte WindowId) : IPacket +public sealed partial record CloseWindowPacket(byte WindowId) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_CloseWindow; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static CloseWindowPacket Read(PacketBuffer buffer, MinecraftData data) { return new CloseWindowPacket(buffer.ReadByte()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandBlockUpdatePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandBlockUpdatePacket.cs new file mode 100644 index 00000000..ce75c743 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandBlockUpdatePacket.cs @@ -0,0 +1,67 @@ +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.CommandBlockUpdatePacket; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Sent by the client when the player updated a command block. +/// +/// The position of the command block. +/// The command to be executed by the command block. +/// The mode of the command block. +/// The flags for the command block. +public sealed partial record CommandBlockUpdatePacket( + Position Location, + string Command, + CommandBlockMode Mode, + CommandBlockFlags Flags +) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_UpdateCommandBlock; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WritePosition(Location); + buffer.WriteString(Command); + buffer.WriteVarInt((int)Mode); + buffer.WriteSByte((sbyte)Flags); + } + + /// + public static CommandBlockUpdatePacket Read(PacketBuffer buffer, MinecraftData data) + { + var location = buffer.ReadPosition(); + var command = buffer.ReadString(); + var mode = (CommandBlockMode)buffer.ReadVarInt(); + var flags = (CommandBlockFlags)buffer.ReadSByte(); + + return new CommandBlockUpdatePacket(location, command, mode, flags); + } + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public enum CommandBlockMode + { + Sequence, + Auto, + Redstone + } + + [Flags] + public enum CommandBlockFlags : sbyte + { + /// + /// If not set, the output of the previous command will not be stored within the command block. + /// + TrackOutput = 0x01, + Conditional = 0x02, + Automatic = 0x04 + } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandSuggestionsRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandSuggestionsRequestPacket.cs new file mode 100644 index 00000000..7bbe4835 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/CommandSuggestionsRequestPacket.cs @@ -0,0 +1,34 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Command Suggestions Request packet +/// +/// The id of the transaction +/// All text behind the cursor without the '/' +public sealed partial record CommandSuggestionsRequestPacket(int TransactionId, string Text) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_TabComplete; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(TransactionId); + buffer.WriteString(Text); + } + + /// + public static CommandSuggestionsRequestPacket Read(PacketBuffer buffer, MinecraftData data) + { + var transactionId = buffer.ReadVarInt(); + var text = buffer.ReadString(); + + return new(transactionId, text); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ConfirmTeleportPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ConfirmTeleportPacket.cs index 746612b8..e16c8b5e 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ConfirmTeleportPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ConfirmTeleportPacket.cs @@ -4,19 +4,19 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record ConfirmTeleportPacket(int TeleportId) : IPacket +public sealed partial record ConfirmTeleportPacket(int TeleportId) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_TeleportConfirm; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(TeleportId); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static ConfirmTeleportPacket Read(PacketBuffer buffer, MinecraftData data) { var teleportId = buffer.ReadVarInt(); return new ConfirmTeleportPacket(teleportId); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/EditBookPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/EditBookPacket.cs new file mode 100644 index 00000000..fdc9376c --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/EditBookPacket.cs @@ -0,0 +1,43 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Edit Book packet sent by the client to edit or sign a book. +/// +/// The hotbar slot where the written book is located +/// Text from each page. Maximum array size is 200. Maximum string length is 8192 chars. +/// Title of the book. Only present if the book is being signed. +public sealed partial record EditBookPacket(int Slot, string[] Entries, string? Title) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_EditBook; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(Slot); + buffer.WriteVarIntArray(Entries, (buf, entry) => buf.WriteString(entry)); + var hasTitle = Title != null; + buffer.WriteBool(hasTitle); + if (hasTitle) + { + buffer.WriteString(Title!); + } + } + + /// + public static EditBookPacket Read(PacketBuffer buffer, MinecraftData data) + { + var slot = buffer.ReadVarInt(); + var entries = buffer.ReadVarIntArray(buf => buf.ReadString()); + var hasTitle = buffer.ReadBool(); + var title = hasTitle ? buffer.ReadString() : null; + + return new(slot, entries, title); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/EntityActionPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/EntityActionPacket.cs index 87ec2e9b..8611f240 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/EntityActionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/EntityActionPacket.cs @@ -3,22 +3,34 @@ using MineSharp.Data.Protocol; namespace MineSharp.Protocol.Packets.Serverbound.Play; -#pragma warning disable CS1591 -public sealed record EntityActionPacket(int EntityId, EntityActionPacket.EntityAction Action, int JumpBoost) : IPacket + +/// +/// Sent by the client to indicate that it has performed certain actions: sneaking (crouching), sprinting, exiting a bed, jumping with a horse, and opening a horse's inventory while riding it. +/// +/// Leave bed is only sent when the "Leave Bed" button is clicked on the sleep GUI, not when waking up in the morning. +/// +/// Open vehicle inventory is only sent when pressing the inventory key (default: E) while on a horse or chest boat — all other methods of opening such an inventory (involving right-clicking or shift-right-clicking it) do not use this packet. +/// +/// Player ID +/// The ID of the action +/// Only used by the “start jump with horse” action, in which case it ranges from 0 to 100. In all other cases it is 0. +public sealed partial record EntityActionPacket(int EntityId, EntityActionPacket.EntityAction Action, int JumpBoost) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_EntityAction; - public void Write(PacketBuffer buffer, MinecraftData version) + /// + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarInt((int)Action); buffer.WriteVarInt(JumpBoost); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + /// + public static EntityActionPacket Read(PacketBuffer buffer, MinecraftData data) { return new EntityActionPacket( buffer.ReadVarInt(), @@ -26,6 +38,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) buffer.ReadVarInt()); } +#pragma warning disable CS1591 public enum EntityAction { StartSneaking = 0, @@ -38,5 +51,5 @@ public enum EntityAction OpenVehicleInventory = 7, StartFlyingWithElytra = 8 } -} #pragma warning restore CS1591 +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/InteractPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/InteractPacket.cs index 61e5fae0..a3aae941 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/InteractPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/InteractPacket.cs @@ -6,7 +6,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record InteractPacket( +public sealed partial record InteractPacket( int EntityId, InteractionType Interaction, float? TargetX, @@ -14,7 +14,7 @@ public sealed record InteractPacket( float? TargetZ, PlayerHand? Hand, bool Sneaking -) : IPacket +) : IPacketStatic { /// /// Constructor for all interaction types except . @@ -32,7 +32,7 @@ public InteractPacket(int entityId, InteractionType interaction, bool sneaking) /// public static PacketType StaticType => PacketType.SB_Play_UseEntity; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt(EntityId); buffer.WriteVarInt((int)Interaction); @@ -47,7 +47,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(Sneaking); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static InteractPacket Read(PacketBuffer buffer, MinecraftData data) { var entityId = buffer.ReadVarInt(); var interaction = (InteractionType)buffer.ReadVarInt(); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/JigsawGeneratePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/JigsawGeneratePacket.cs new file mode 100644 index 00000000..eacbcb6f --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/JigsawGeneratePacket.cs @@ -0,0 +1,38 @@ +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Sent when Generate is pressed on the Jigsaw Block interface. +/// +/// Block entity location. +/// Value of the levels slider/max depth to generate. +/// Whether to keep jigsaws. +public sealed partial record JigsawGeneratePacket(Position Location, int Levels, bool KeepJigsaws) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_GenerateStructure; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WritePosition(Location); + buffer.WriteVarInt(Levels); + buffer.WriteBool(KeepJigsaws); + } + + /// + public static JigsawGeneratePacket Read(PacketBuffer buffer, MinecraftData data) + { + var location = buffer.ReadPosition(); + var levels = buffer.ReadVarInt(); + var keepJigsaws = buffer.ReadBool(); + + return new(location, levels, keepJigsaws); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/KeepAlivePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/KeepAlivePacket.cs index d0075138..9511fdc6 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/KeepAlivePacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/KeepAlivePacket.cs @@ -4,19 +4,19 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record KeepAlivePacket(long KeepAliveId) : IPacket +public sealed partial record KeepAlivePacket(long KeepAliveId) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_KeepAlive; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(KeepAliveId); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static KeepAlivePacket Read(PacketBuffer buffer, MinecraftData data) { var id = buffer.ReadLong(); return new KeepAlivePacket(id); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/LockDifficultyPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/LockDifficultyPacket.cs new file mode 100644 index 00000000..c2b7ba57 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/LockDifficultyPacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Lock Difficulty packet +/// +/// Indicates if the difficulty is locked +public sealed partial record LockDifficultyPacket(bool Locked) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_LockDifficulty; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteBool(Locked); + } + + /// + public static LockDifficultyPacket Read(PacketBuffer buffer, MinecraftData data) + { + var locked = buffer.ReadBool(); + + return new(locked); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/MessageAcknowledgementPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/MessageAcknowledgementPacket.cs index b7e4afb1..234f16ee 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/MessageAcknowledgementPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/MessageAcknowledgementPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record MessageAcknowledgementPacket : IPacket +public sealed partial record MessageAcknowledgementPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -41,13 +41,13 @@ public MessageAcknowledgementPacket(ChatMessageItem[]? previousMessages, ChatMes public ChatMessageItem[]? PreviousMessages { get; init; } public ChatMessageItem? LastRejectedMessage { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol >= ProtocolVersion.V_1_19_3) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_3) { if (Count == null) { - throw new MineSharpPacketVersionException(nameof(Count), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(Count), data.Version.Protocol); } buffer.WriteVarInt(Count.Value); @@ -56,7 +56,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) if (PreviousMessages == null) { - throw new MineSharpPacketVersionException(nameof(PreviousMessages), version.Version.Protocol); + throw new MineSharpPacketVersionException(nameof(PreviousMessages), data.Version.Protocol); } buffer.WriteVarIntArray(PreviousMessages, (buf, val) => val.Write(buf)); @@ -71,8 +71,9 @@ public void Write(PacketBuffer buffer, MinecraftData version) LastRejectedMessage!.Write(buffer); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static MessageAcknowledgementPacket Read(PacketBuffer buffer, MinecraftData data) { + // TODO: implement this method throw new NotImplementedException(); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/MoveVehiclePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/MoveVehiclePacket.cs new file mode 100644 index 00000000..20dbfdea --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/MoveVehiclePacket.cs @@ -0,0 +1,44 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Sent when a player moves in a vehicle. Fields are the same as in Set Player Position and Rotation. +/// Note that all fields use absolute positioning and do not allow for relative positioning. +/// +/// Absolute position (X coordinate). +/// Absolute position (Y coordinate). +/// Absolute position (Z coordinate). +/// Absolute rotation on the vertical axis, in degrees. +/// Absolute rotation on the horizontal axis, in degrees. +public sealed partial record MoveVehiclePacket(double X, double Y, double Z, float Yaw, float Pitch) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_VehicleMove; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteDouble(X); + buffer.WriteDouble(Y); + buffer.WriteDouble(Z); + buffer.WriteFloat(Yaw); + buffer.WriteFloat(Pitch); + } + + /// + public static MoveVehiclePacket Read(PacketBuffer buffer, MinecraftData data) + { + var x = buffer.ReadDouble(); + var y = buffer.ReadDouble(); + var z = buffer.ReadDouble(); + var yaw = buffer.ReadFloat(); + var pitch = buffer.ReadFloat(); + + return new(x, y, z, yaw, pitch); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PaddleBoatPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PaddleBoatPacket.cs new file mode 100644 index 00000000..4d6616c1 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PaddleBoatPacket.cs @@ -0,0 +1,34 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Used to visually update whether boat paddles are turning. +/// +/// Indicates if the left paddle is turning +/// Indicates if the right paddle is turning +public sealed partial record PaddleBoatPacket(bool LeftPaddleTurning, bool RightPaddleTurning) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SteerBoat; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteBool(LeftPaddleTurning); + buffer.WriteBool(RightPaddleTurning); + } + + /// + public static PaddleBoatPacket Read(PacketBuffer buffer, MinecraftData data) + { + var leftPaddleTurning = buffer.ReadBool(); + var rightPaddleTurning = buffer.ReadBool(); + + return new(leftPaddleTurning, rightPaddleTurning); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PickItemPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PickItemPacket.cs new file mode 100644 index 00000000..76ab07a5 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PickItemPacket.cs @@ -0,0 +1,42 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Used to swap out an empty space on the hotbar with the item in the given inventory slot. +/// The Notchian client uses this for pick block functionality (middle click) to retrieve items from the inventory. +/// +/// The server first searches the player's hotbar for an empty slot, starting from the current slot and looping around to the slot before it. +/// If there are no empty slots, it starts a second search from the current slot and finds the first slot that does not contain an enchanted item. +/// If there still are no slots that meet that criteria, then the server uses the currently selected slot. +/// +/// After finding the appropriate slot, the server swaps the items and sends 3 packets: +/// +/// Set Container Slot with window ID set to -2, updating the chosen hotbar slot. +/// Set Container Slot with window ID set to -2, updating the slot where the picked item used to be. +/// Set Held Item, switching to the newly chosen slot. +/// +/// The slot to use +public sealed partial record PickItemPacket(int SlotToUse) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_PickItem; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(SlotToUse); + } + + /// + public static PickItemPacket Read(PacketBuffer buffer, MinecraftData data) + { + var slotToUse = buffer.ReadVarInt(); + + return new(slotToUse); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PingRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PingRequestPacket.cs new file mode 100644 index 00000000..a2f85996 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PingRequestPacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Ping request packet sent by the client to the server. +/// +/// The payload, which may be any number. Notchian clients use a system-dependent time value counted in milliseconds. +public sealed partial record PingRequestPacket(long Payload) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_PingRequest; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteLong(Payload); + } + + /// + public static PingRequestPacket Read(PacketBuffer buffer, MinecraftData data) + { + var payload = buffer.ReadLong(); + + return new(payload); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceBlockPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceBlockPacket.cs index adf15fdf..578e4047 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceBlockPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceBlockPacket.cs @@ -1,4 +1,5 @@ using MineSharp.Core; +using MineSharp.Core.Common; using MineSharp.Core.Geometry; using MineSharp.Core.Serialization; using MineSharp.Data; @@ -6,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record PlaceBlockPacket : IPacket +public sealed partial record PlaceBlockPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -32,7 +33,7 @@ private PlaceBlockPacket() /// /// /// - public PlaceBlockPacket(int hand, Position location, BlockFace direction, float cursorX, float cursorY, + public PlaceBlockPacket(PlayerHand hand, Position location, BlockFace direction, float cursorX, float cursorY, float cursorZ, bool insideBlock, int sequenceId) { @@ -56,7 +57,7 @@ public PlaceBlockPacket(int hand, Position location, BlockFace direction, float /// /// /// - public PlaceBlockPacket(int hand, Position location, BlockFace direction, float cursorX, float cursorY, + public PlaceBlockPacket(PlayerHand hand, Position location, BlockFace direction, float cursorX, float cursorY, float cursorZ, bool insideBlock) { Hand = hand; @@ -68,7 +69,7 @@ public PlaceBlockPacket(int hand, Position location, BlockFace direction, float InsideBlock = insideBlock; } - public int Hand { get; init; } + public PlayerHand Hand { get; init; } public Position Location { get; init; } public BlockFace Direction { get; init; } public float CursorX { get; init; } @@ -77,9 +78,9 @@ public PlaceBlockPacket(int hand, Position location, BlockFace direction, float public bool InsideBlock { get; init; } public int? SequenceId { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(Hand); + buffer.WriteVarInt((int)Hand); buffer.WritePosition(Location); buffer.WriteVarInt((int)Direction); buffer.WriteFloat(CursorX); @@ -87,15 +88,15 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteFloat(CursorZ); buffer.WriteBool(InsideBlock); - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { buffer.WriteVarInt(SequenceId!.Value); } } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlaceBlockPacket Read(PacketBuffer buffer, MinecraftData data) { - var hand = buffer.ReadVarInt(); + var hand = (PlayerHand)buffer.ReadVarInt(); var position = buffer.ReadPosition(); var direction = buffer.ReadVarInt(); var cursorX = buffer.ReadFloat(); @@ -103,7 +104,7 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) var cursorZ = buffer.ReadFloat(); var insideBlock = buffer.ReadBool(); - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { return new PlaceBlockPacket(hand, position, (BlockFace)direction, cursorX, cursorY, cursorZ, insideBlock); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceRecipePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceRecipePacket.cs new file mode 100644 index 00000000..6f589ca9 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlaceRecipePacket.cs @@ -0,0 +1,38 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Place Recipe packet sent when a player clicks a recipe in the crafting book that is craftable (white border). +/// +/// The window ID +/// The recipe ID +/// Whether to make all items (true if shift is down when clicked) +public sealed partial record PlaceRecipePacket(byte WindowId, Identifier Recipe, bool MakeAll) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_CraftRecipeRequest; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteByte(WindowId); + buffer.WriteIdentifier(Recipe); + buffer.WriteBool(MakeAll); + } + + /// + public static PlaceRecipePacket Read(PacketBuffer buffer, MinecraftData data) + { + var windowId = buffer.ReadByte(); + var recipe = buffer.ReadIdentifier(); + var makeAll = buffer.ReadBool(); + + return new(windowId, recipe, makeAll); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerAbilitiesPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerAbilitiesPacket.cs new file mode 100644 index 00000000..611edbc9 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerAbilitiesPacket.cs @@ -0,0 +1,32 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Player abilities packet sent by the client to update the player's abilities. +/// +/// Bit mask indicating the player's abilities. Client may only send the flag. +public sealed partial record PlayerAbilitiesPacket(PlayerAbilitiesFlags Flags) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_Abilities; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteByte((byte)Flags); + } + + /// + public static PlayerAbilitiesPacket Read(PacketBuffer buffer, MinecraftData data) + { + var flags = (PlayerAbilitiesFlags)buffer.ReadByte(); + + return new(flags); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerActionPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerActionPacket.cs index 9af23661..4a77026b 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerActionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerActionPacket.cs @@ -3,10 +3,11 @@ using MineSharp.Core.Serialization; using MineSharp.Data; using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record PlayerActionPacket : IPacket +public sealed partial record PlayerActionPacket : IPacketStatic { /// public PacketType Type => StaticType; @@ -27,7 +28,7 @@ private PlayerActionPacket() /// /// /// - public PlayerActionPacket(int status, Position location, BlockFace face) + public PlayerActionPacket(PlayerActionStatus status, Position location, BlockFace face) { Status = status; Location = location; @@ -42,7 +43,7 @@ public PlayerActionPacket(int status, Position location, BlockFace face) /// /// /// - public PlayerActionPacket(int status, Position location, BlockFace face, int? sequenceId) + public PlayerActionPacket(PlayerActionStatus status, Position location, BlockFace face, int? sequenceId) { Status = status; Location = location; @@ -50,36 +51,36 @@ public PlayerActionPacket(int status, Position location, BlockFace face, int? se SequenceId = sequenceId; } - public int Status { get; init; } + public PlayerActionStatus Status { get; init; } public Position Location { get; init; } public BlockFace Face { get; init; } public int? SequenceId { get; init; } - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { - buffer.WriteVarInt(Status); + buffer.WriteVarInt((int)Status); buffer.WritePosition(Location); buffer.WriteByte((byte)Face); - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { buffer.WriteVarInt(SequenceId!.Value); } } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerActionPacket Read(PacketBuffer buffer, MinecraftData data) { - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { return new PlayerActionPacket( - buffer.ReadVarInt(), + (PlayerActionStatus)buffer.ReadVarInt(), buffer.ReadPosition(), (BlockFace)buffer.ReadByte(), buffer.ReadVarInt()); } return new PlayerActionPacket( - buffer.ReadVarInt(), + (PlayerActionStatus)buffer.ReadVarInt(), buffer.ReadPosition(), (BlockFace)buffer.ReadByte()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerInputPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerInputPacket.cs new file mode 100644 index 00000000..0ea8d9d3 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerInputPacket.cs @@ -0,0 +1,51 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.PlayerInputPacket; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Player input packet sent by the client to the server. +/// +/// Positive to the left of the player. +/// Positive forward. +/// Bit mask of flags. See . +public sealed partial record PlayerInputPacket(float Sideways, float Forward, PlayerInputFlags Flags) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SteerVehicle; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(Sideways); + buffer.WriteFloat(Forward); + buffer.WriteByte((byte)Flags); + } + + /// + public static PlayerInputPacket Read(PacketBuffer buffer, MinecraftData data) + { + var sideways = buffer.ReadFloat(); + var forward = buffer.ReadFloat(); + var flags = (PlayerInputFlags)buffer.ReadByte(); + + return new(sideways, forward, flags); + } + + /// + /// Flags indicating player actions. + /// + [Flags] + public enum PlayerInputFlags : byte + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + None = 0x0, + Jump = 0x1, + Unmount = 0x2 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerSessionPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerSessionPacket.cs index 90af38a4..8b86bfc2 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerSessionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PlayerSessionPacket.cs @@ -5,14 +5,14 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record PlayerSessionPacket(Uuid SessionId, long ExpiresAt, byte[] PublicKey, byte[] KeySignature) : IPacket +public sealed partial record PlayerSessionPacket(Uuid SessionId, long ExpiresAt, byte[] PublicKey, byte[] KeySignature) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_ChatSessionUpdate; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteUuid(SessionId); buffer.WriteLong(ExpiresAt); @@ -22,7 +22,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBytes(KeySignature); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PlayerSessionPacket Read(PacketBuffer buffer, MinecraftData data) { var sessionId = buffer.ReadUuid(); var expiresAt = buffer.ReadLong(); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PluginMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PluginMessagePacket.cs new file mode 100644 index 00000000..8f7755f3 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PluginMessagePacket.cs @@ -0,0 +1,35 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Serverbound Plugin Message packet +/// +/// Name of the plugin channel used to send the data +/// Any data, depending on the channel +public sealed partial record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_CustomPayload; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteIdentifier(Channel); + buffer.WriteBytes(Data); + } + + /// + public static PluginMessagePacket Read(PacketBuffer buffer, MinecraftData data) + { + var channel = buffer.ReadIdentifier(); + var pluginData = buffer.ReadBytes((int)buffer.ReadableBytes); + + return new(channel, pluginData); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PongPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PongPacket.cs index 90bdeff9..0e279b25 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/PongPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/PongPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// Pong Packet https://wiki.vg/Protocol#Ping_Response_.28play.29 /// /// -public sealed record PongPacket(int Id) : IPacket +public sealed partial record PongPacket(int Id) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,15 +16,16 @@ public sealed record PongPacket(int Id) : IPacket public static PacketType StaticType => PacketType.SB_Play_Pong; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteInt(Id); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PongPacket Read(PacketBuffer buffer, MinecraftData data) { - return new PongPacket( - buffer.ReadInt()); + var id = buffer.ReadInt(); + + return new PongPacket(id); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramJigsawBlockPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramJigsawBlockPacket.cs new file mode 100644 index 00000000..6aef36aa --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramJigsawBlockPacket.cs @@ -0,0 +1,70 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Sent when Done is pressed on the Jigsaw Block interface. +/// +/// Block entity location +/// Name identifier +/// Target identifier +/// Pool identifier +/// "Turns into" on the GUI, final_state in NBT +/// Joint type, rollable if the attached piece can be rotated, else aligned +/// Selection priority +/// Placement priority +public sealed partial record ProgramJigsawBlockPacket( + Position Location, + Identifier Name, + Identifier Target, + Identifier Pool, + string FinalState, + string JointType, + int SelectionPriority, + int PlacementPriority) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_UpdateJigsawBlock; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WritePosition(Location); + buffer.WriteIdentifier(Name); + buffer.WriteIdentifier(Target); + buffer.WriteIdentifier(Pool); + buffer.WriteString(FinalState); + buffer.WriteString(JointType); + buffer.WriteVarInt(SelectionPriority); + buffer.WriteVarInt(PlacementPriority); + } + + /// + public static ProgramJigsawBlockPacket Read(PacketBuffer buffer, MinecraftData data) + { + var location = buffer.ReadPosition(); + var name = buffer.ReadIdentifier(); + var target = buffer.ReadIdentifier(); + var pool = buffer.ReadIdentifier(); + var finalState = buffer.ReadString(); + var jointType = buffer.ReadString(); + var selectionPriority = buffer.ReadVarInt(); + var placementPriority = buffer.ReadVarInt(); + + return new( + location, + name, + target, + pool, + finalState, + jointType, + selectionPriority, + placementPriority); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramStructureBlockPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramStructureBlockPacket.cs new file mode 100644 index 00000000..ac4d378c --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ProgramStructureBlockPacket.cs @@ -0,0 +1,164 @@ +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.ProgramStructureBlockPacket; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; +/// +/// Program Structure Block packet +/// +/// Block entity location +/// An additional action to perform beyond simply saving the given data. See +/// One of +/// Name of the structure +/// Offset X, between -48 and 48 +/// Offset Y, between -48 and 48 +/// Offset Z, between -48 and 48 +/// Size X, between 0 and 48 +/// Size Y, between 0 and 48 +/// Size Z, between 0 and 48 +/// One of +/// One of +/// Metadata of the structure +/// Integrity, between 0 and 1 +/// Seed for the structure +/// Flags. See +public sealed partial record ProgramStructureBlockPacket( + Position Location, + StructureBlockAction Action, + StructureBlockMode Mode, + string Name, + sbyte OffsetX, + sbyte OffsetY, + sbyte OffsetZ, + sbyte SizeX, + sbyte SizeY, + sbyte SizeZ, + StructureBlockMirror Mirror, + StructureBlockRotation Rotation, + string Metadata, + float Integrity, + long Seed, + StructureBlockFlags Flags) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_UpdateStructureBlock; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WritePosition(Location); + buffer.WriteVarInt((int)Action); + buffer.WriteVarInt((int)Mode); + buffer.WriteString(Name); + buffer.WriteSByte(OffsetX); + buffer.WriteSByte(OffsetY); + buffer.WriteSByte(OffsetZ); + buffer.WriteSByte(SizeX); + buffer.WriteSByte(SizeY); + buffer.WriteSByte(SizeZ); + buffer.WriteVarInt((int)Mirror); + buffer.WriteVarInt((int)Rotation); + buffer.WriteString(Metadata); + buffer.WriteFloat(Integrity); + buffer.WriteVarLong(Seed); + buffer.WriteSByte((sbyte)Flags); + } + + /// + public static ProgramStructureBlockPacket Read(PacketBuffer buffer, MinecraftData data) + { + var location = buffer.ReadPosition(); + var action = (StructureBlockAction)buffer.ReadVarInt(); + var mode = (StructureBlockMode)buffer.ReadVarInt(); + var name = buffer.ReadString(); + var offsetX = buffer.ReadSByte(); + var offsetY = buffer.ReadSByte(); + var offsetZ = buffer.ReadSByte(); + var sizeX = buffer.ReadSByte(); + var sizeY = buffer.ReadSByte(); + var sizeZ = buffer.ReadSByte(); + var mirror = (StructureBlockMirror)buffer.ReadVarInt(); + var rotation = (StructureBlockRotation)buffer.ReadVarInt(); + var metadata = buffer.ReadString(); + var integrity = buffer.ReadFloat(); + var seed = buffer.ReadVarLong(); + var flags = (StructureBlockFlags)buffer.ReadSByte(); + + return new( + location, + action, + mode, + name, + offsetX, + offsetY, + offsetZ, + sizeX, + sizeY, + sizeZ, + mirror, + rotation, + metadata, + integrity, + seed, + flags); + } + + /// + /// Enum representing the action to perform on the structure block. + /// + public enum StructureBlockAction + { + UpdateData = 0, + SaveStructure = 1, + LoadStructure = 2, + DetectSize = 3 + } + + /// + /// Enum representing the mode of the structure block. + /// + public enum StructureBlockMode + { + Save = 0, + Load = 1, + Corner = 2, + Data = 3 + } + + /// + /// Enum representing the mirror type of the structure block. + /// + public enum StructureBlockMirror + { + None = 0, + LeftRight = 1, + FrontBack = 2 + } + + /// + /// Enum representing the rotation type of the structure block. + /// + public enum StructureBlockRotation + { + None = 0, + Clockwise90 = 1, + Clockwise180 = 2, + Counterclockwise90 = 3 + } + + /// + /// Enum representing the flags for the structure block. + /// + [Flags] + public enum StructureBlockFlags : sbyte + { + IgnoreEntities = 0x01, + ShowAir = 0x02, + ShowBoundingBox = 0x04 + } + +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryBlockEntityTagPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryBlockEntityTagPacket.cs new file mode 100644 index 00000000..63af5345 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryBlockEntityTagPacket.cs @@ -0,0 +1,36 @@ +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Query Block Entity Tag packet +/// Used when F3+I is pressed while looking at a block. +/// +/// An incremental ID so that the client can verify that the response matches. +/// The location of the block to check. +public sealed partial record QueryBlockEntityTagPacket(int TransactionId, Position Location) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_QueryBlockNbt; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(TransactionId); + buffer.WritePosition(Location); + } + + /// + public static QueryBlockEntityTagPacket Read(PacketBuffer buffer, MinecraftData data) + { + var transactionId = buffer.ReadVarInt(); + var location = buffer.ReadPosition(); + + return new(transactionId, location); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryEntityTagPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryEntityTagPacket.cs new file mode 100644 index 00000000..609cfe2b --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/QueryEntityTagPacket.cs @@ -0,0 +1,35 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Query Entity Tag packet. +/// Used when F3+I is pressed while looking at an entity. +/// +/// An incremental ID so that the client can verify that the response matches. +/// The ID of the entity to query. +public sealed partial record QueryEntityTagPacket(int TransactionId, int EntityId) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_QueryEntityNbt; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(TransactionId); + buffer.WriteVarInt(EntityId); + } + + /// + public static QueryEntityTagPacket Read(PacketBuffer buffer, MinecraftData data) + { + var transactionId = buffer.ReadVarInt(); + var entityId = buffer.ReadVarInt(); + + return new(transactionId, entityId); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/RenameItemPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/RenameItemPacket.cs new file mode 100644 index 00000000..fd7527b3 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/RenameItemPacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Sent as a player is renaming an item in an anvil. +/// +/// The new name of the item. +public sealed partial record RenameItemPacket(string ItemName) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_NameItem; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteString(ItemName); + } + + /// + public static RenameItemPacket Read(PacketBuffer buffer, MinecraftData data) + { + var itemName = buffer.ReadString(); + + return new(itemName); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/ResourcePackResponsePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ResourcePackResponsePacket.cs new file mode 100644 index 00000000..c0e64403 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/ResourcePackResponsePacket.cs @@ -0,0 +1,36 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using MineSharp.Protocol.Packets.NetworkTypes; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Resource Pack Response packet +/// +/// The unique identifier of the resource pack +/// The result of the resource pack response +public sealed partial record ResourcePackResponsePacket(Uuid Uuid, ResourcePackResult Result) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_ResourcePackReceive; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteUuid(Uuid); + buffer.WriteVarInt((int)Result); + } + + /// + public static ResourcePackResponsePacket Read(PacketBuffer buffer, MinecraftData data) + { + var uuid = buffer.ReadUuid(); + var result = (ResourcePackResult)buffer.ReadVarInt(); + + return new(uuid, result); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SeenAdvancementsPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SeenAdvancementsPacket.cs new file mode 100644 index 00000000..2e8ec40a --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SeenAdvancementsPacket.cs @@ -0,0 +1,56 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; +using static MineSharp.Protocol.Packets.Serverbound.Play.SeenAdvancementsPacket; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Seen Advancements packet +/// +/// The action taken +/// The identifier of the tab, only present if action is +public sealed partial record SeenAdvancementsPacket(SeenAdvancementsAction Action, Identifier? TabId) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_AdvancementTab; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt((int)Action); + if (Action == SeenAdvancementsAction.OpenedTab) + { + buffer.WriteIdentifier(TabId!); + } + } + + /// + public static SeenAdvancementsPacket Read(PacketBuffer buffer, MinecraftData data) + { + var action = (SeenAdvancementsAction)buffer.ReadVarInt(); + Identifier? tabId = null; + if (action == SeenAdvancementsAction.OpenedTab) + { + tabId = buffer.ReadIdentifier(); + } + + return new(action, tabId); + } + + /// + /// Enum representing the actions for the Seen Advancements packet + /// + [Flags] + public enum SeenAdvancementsAction + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + None = 0, + OpenedTab = 1 << 0, + ClosedScreen = 1 << 1 +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SelectTradePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SelectTradePacket.cs new file mode 100644 index 00000000..6e6c8ac7 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SelectTradePacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Select Trade packet sent by the client when a player selects a specific trade offered by a villager NPC. +/// +/// The selected slot in the player's current (trading) inventory. +public sealed partial record SelectTradePacket(int SelectedSlot) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SelectTrade; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(SelectedSlot); + } + + /// + public static SelectTradePacket Read(PacketBuffer buffer, MinecraftData data) + { + var selectedSlot = buffer.ReadVarInt(); + + return new(selectedSlot); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetBeaconEffectPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetBeaconEffectPacket.cs new file mode 100644 index 00000000..2bfc5880 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetBeaconEffectPacket.cs @@ -0,0 +1,50 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Changes the effect of the current beacon. +/// +/// The primary effect ID +/// The secondary effect ID +public sealed partial record SetBeaconEffectPacket(int? PrimaryEffect, int? SecondaryEffect) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_SetBeaconEffect; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + var hasPrimaryEffect = PrimaryEffect.HasValue; + buffer.WriteBool(hasPrimaryEffect); + if (hasPrimaryEffect) + { + buffer.WriteVarInt(PrimaryEffect!.Value); + } + + var hasSecondaryEffect = SecondaryEffect.HasValue; + buffer.WriteBool(hasSecondaryEffect); + if (hasSecondaryEffect) + { + buffer.WriteVarInt(SecondaryEffect!.Value); + } + } + + /// + public static SetBeaconEffectPacket Read(PacketBuffer buffer, MinecraftData data) + { + var hasPrimaryEffect = buffer.ReadBool(); + int? primaryEffect = hasPrimaryEffect ? buffer.ReadVarInt() : null; + + var hasSecondaryEffect = buffer.ReadBool(); + int? secondaryEffect = hasSecondaryEffect ? buffer.ReadVarInt() : null; + + return new( + primaryEffect, + secondaryEffect); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetCreativeSlotPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetCreativeSlotPacket.cs index 1220aa5f..2f74b5c9 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetCreativeSlotPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetCreativeSlotPacket.cs @@ -11,7 +11,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// /// The slot index /// The clicked Item -public sealed record SetCreativeSlotPacket(short SlotIndex, Item? Item) : IPacket +public sealed partial record SetCreativeSlotPacket(short SlotIndex, Item? Item) : IPacketStatic { /// public PacketType Type => StaticType; @@ -19,17 +19,17 @@ public sealed record SetCreativeSlotPacket(short SlotIndex, Item? Item) : IPacke public static PacketType StaticType => PacketType.SB_Play_SetCreativeSlot; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteShort(SlotIndex); buffer.WriteOptionalItem(Item); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetCreativeSlotPacket Read(PacketBuffer buffer, MinecraftData data) { var index = buffer.ReadShort(); - var item = buffer.ReadOptionalItem(version); + var item = buffer.ReadOptionalItem(data); return new SetCreativeSlotPacket(index, item); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetHeldItemPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetHeldItemPacket.cs index aaff55fb..49d62813 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetHeldItemPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetHeldItemPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// /// Sent when the player changes the slot selection /// -public sealed record SetHeldItemPacket(short Slot) : IPacket +public sealed partial record SetHeldItemPacket(short Slot) : IPacketStatic { /// public PacketType Type => StaticType; @@ -15,13 +15,13 @@ public sealed record SetHeldItemPacket(short Slot) : IPacket public static PacketType StaticType => PacketType.SB_Play_HeldItemSlot; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteShort(Slot); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetHeldItemPacket Read(PacketBuffer buffer, MinecraftData data) { return new SetHeldItemPacket(buffer.ReadShort()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerOnGroundPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerOnGroundPacket.cs new file mode 100644 index 00000000..8b664ed6 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerOnGroundPacket.cs @@ -0,0 +1,31 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// This packet is used to indicate whether the player is on ground (walking/swimming), or airborne (jumping/falling). +/// +/// True if the client is on the ground, false otherwise. +public sealed partial record SetPlayerOnGroundPacket(bool OnGround) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_Flying; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteBool(OnGround); + } + + /// + public static SetPlayerOnGroundPacket Read(PacketBuffer buffer, MinecraftData data) + { + var onGround = buffer.ReadBool(); + + return new(onGround); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionAndRotationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionAndRotationPacket.cs index 42ac2242..18a4b673 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionAndRotationPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionAndRotationPacket.cs @@ -4,14 +4,14 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record SetPlayerPositionAndRotationPacket(double X, double Y, double Z, float Yaw, float Pitch, bool IsOnGround) : IPacket +public sealed partial record SetPlayerPositionAndRotationPacket(double X, double Y, double Z, float Yaw, float Pitch, bool IsOnGround) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_PositionLook; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Y); @@ -21,7 +21,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(IsOnGround); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetPlayerPositionAndRotationPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var y = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionPacket.cs index d816dcc9..f2790a8a 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerPositionPacket.cs @@ -4,14 +4,14 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record SetPlayerPositionPacket(double X, double Y, double Z, bool IsOnGround) : IPacket +public sealed partial record SetPlayerPositionPacket(double X, double Y, double Z, bool IsOnGround) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_Position; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteDouble(X); buffer.WriteDouble(Y); @@ -19,7 +19,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteBool(IsOnGround); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SetPlayerPositionPacket Read(PacketBuffer buffer, MinecraftData data) { var x = buffer.ReadDouble(); var y = buffer.ReadDouble(); diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerRotationPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerRotationPacket.cs new file mode 100644 index 00000000..462abb49 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetPlayerRotationPacket.cs @@ -0,0 +1,37 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Updates the direction the player is looking in. +/// +/// Absolute rotation on the X Axis, in degrees. +/// Absolute rotation on the Y Axis, in degrees. +/// True if the client is on the ground, false otherwise. +public sealed partial record SetPlayerRotationPacket(float Yaw, float Pitch, bool OnGround) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_Look; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteFloat(Yaw); + buffer.WriteFloat(Pitch); + buffer.WriteBool(OnGround); + } + + /// + public static SetPlayerRotationPacket Read(PacketBuffer buffer, MinecraftData data) + { + var yaw = buffer.ReadFloat(); + var pitch = buffer.ReadFloat(); + var onGround = buffer.ReadBool(); + + return new(yaw, pitch, onGround); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetSeenRecipePacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetSeenRecipePacket.cs new file mode 100644 index 00000000..ba8b0aae --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SetSeenRecipePacket.cs @@ -0,0 +1,32 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Packet sent by the client when a recipe is first seen in the recipe book. +/// +/// The ID of the recipe. +public sealed partial record SetSeenRecipePacket(Identifier RecipeId) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_DisplayedRecipe; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteIdentifier(RecipeId); + } + + /// + public static SetSeenRecipePacket Read(PacketBuffer buffer, MinecraftData data) + { + var recipeId = buffer.ReadIdentifier(); + + return new(recipeId); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SwingArmPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SwingArmPacket.cs index 1421f166..01bbc092 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/SwingArmPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/SwingArmPacket.cs @@ -4,8 +4,12 @@ using MineSharp.Data.Protocol; namespace MineSharp.Protocol.Packets.Serverbound.Play; -#pragma warning disable CS1591 -public sealed record SwingArmPacket(PlayerHand Hand) : IPacket + +/// +/// Sent by the client when the player swings their arm. +/// +/// The hand used by the player. +public sealed partial record SwingArmPacket(PlayerHand Hand) : IPacketStatic { /// public PacketType Type => StaticType; @@ -13,16 +17,16 @@ public sealed record SwingArmPacket(PlayerHand Hand) : IPacket public static PacketType StaticType => PacketType.SB_Play_ArmAnimation; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Hand); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static SwingArmPacket Read(PacketBuffer buffer, MinecraftData data) { - return new SwingArmPacket( - (PlayerHand)buffer.ReadVarInt()); + var hand = (PlayerHand)buffer.ReadVarInt(); + + return new SwingArmPacket(hand); } } -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/TeleportToEntityPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/TeleportToEntityPacket.cs new file mode 100644 index 00000000..4cd0f788 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/TeleportToEntityPacket.cs @@ -0,0 +1,37 @@ +using MineSharp.Core.Common; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Teleports the player to the given entity. The player must be in spectator mode. +/// +/// The Notchian client only uses this to teleport to players, but it appears to accept any type of entity. +/// The entity does not need to be in the same dimension as the player; if necessary, the player will be respawned in the right world. +/// If the given entity cannot be found (or isn't loaded), this packet will be ignored. +/// It will also be ignored if the player attempts to teleport to themselves. +/// +/// UUID of the player to teleport to (can also be an entity UUID). +public sealed partial record TeleportToEntityPacket(Uuid TargetPlayer) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_Spectate; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteUuid(TargetPlayer); + } + + /// + public static TeleportToEntityPacket Read(PacketBuffer buffer, MinecraftData data) + { + var targetPlayer = buffer.ReadUuid(); + + return new(targetPlayer); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlock.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlock.cs deleted file mode 100644 index 79f2a1af..00000000 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlock.cs +++ /dev/null @@ -1,34 +0,0 @@ -using MineSharp.Core.Geometry; -using MineSharp.Core.Serialization; -using MineSharp.Data; -using MineSharp.Data.Protocol; - -namespace MineSharp.Protocol.Packets.Serverbound.Play; -#pragma warning disable CS1591 -public sealed record UpdateCommandBlock(Position Location, string Command, int Mode, byte Flags) : IPacket -{ - /// - public PacketType Type => StaticType; - /// - public static PacketType StaticType => PacketType.SB_Play_UpdateCommandBlock; - - /// - public void Write(PacketBuffer buffer, MinecraftData version) - { - buffer.WritePosition(Location); - buffer.WriteString(Command); - buffer.WriteVarInt(Mode); - buffer.WriteByte(Flags); - } - - /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) - { - return new UpdateCommandBlock( - buffer.ReadPosition(), - buffer.ReadString(), - buffer.ReadVarInt(), - buffer.ReadByte()); - } -} -#pragma warning restore CS1591 diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlockMinecartPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlockMinecartPacket.cs new file mode 100644 index 00000000..0a43e83d --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateCommandBlockMinecartPacket.cs @@ -0,0 +1,40 @@ +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Program Command Block Minecart packet +/// +/// The entity ID +/// The command to be executed +/// Whether to track the output of the command +public sealed partial record UpdateCommandBlockMinecartPacket(int EntityId, string Command, bool TrackOutput) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_UpdateCommandBlockMinecart; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WriteVarInt(EntityId); + buffer.WriteString(Command); + buffer.WriteBool(TrackOutput); + } + + /// + public static UpdateCommandBlockMinecartPacket Read(PacketBuffer buffer, MinecraftData data) + { + var entityId = buffer.ReadVarInt(); + var command = buffer.ReadString(); + var trackOutput = buffer.ReadBool(); + + return new( + entityId, + command, + trackOutput); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateSignPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateSignPacket.cs new file mode 100644 index 00000000..b67bb294 --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UpdateSignPacket.cs @@ -0,0 +1,57 @@ +using MineSharp.Core.Geometry; +using MineSharp.Core.Serialization; +using MineSharp.Data; +using MineSharp.Data.Protocol; + +namespace MineSharp.Protocol.Packets.Serverbound.Play; + +/// +/// Update Sign packet sent from the client to the server when the "Done" button is pushed after placing a sign. +/// +/// Block Coordinates +/// Whether the updated text is in front or on the back of the sign +/// First line of text in the sign +/// Second line of text in the sign +/// Third line of text in the sign +/// Fourth line of text in the sign +public sealed partial record UpdateSignPacket( + Position Location, + bool IsFrontText, + string Line1, + string Line2, + string Line3, + string Line4 +) : IPacketStatic +{ + /// + public PacketType Type => StaticType; + /// + public static PacketType StaticType => PacketType.SB_Play_UpdateSign; + + /// + public void Write(PacketBuffer buffer, MinecraftData data) + { + buffer.WritePosition(Location); + buffer.WriteBool(IsFrontText); + buffer.WriteString(Line1); + buffer.WriteString(Line2); + buffer.WriteString(Line3); + buffer.WriteString(Line4); + } + + /// + public static UpdateSignPacket Read(PacketBuffer buffer, MinecraftData data) + { + var location = buffer.ReadPosition(); + var isFrontText = buffer.ReadBool(); + var line1 = buffer.ReadString(); + var line2 = buffer.ReadString(); + var line3 = buffer.ReadString(); + var line4 = buffer.ReadString(); + + return new( + location, + isFrontText, + line1, line2, line3, line4); + } +} diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UseItemPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UseItemPacket.cs index 5df34ff8..59c7ef6e 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/UseItemPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/UseItemPacket.cs @@ -14,7 +14,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; /// Sequence id used to synchronize server and client. /// Only used for versions >= 1.19 /// -public sealed record UseItemPacket(PlayerHand Hand, int? SequenceId = null) : IPacket +public sealed partial record UseItemPacket(PlayerHand Hand, int? SequenceId = null) : IPacketStatic { /// public PacketType Type => StaticType; @@ -22,22 +22,22 @@ public sealed record UseItemPacket(PlayerHand Hand, int? SequenceId = null) : IP public static PacketType StaticType => PacketType.SB_Play_UseItem; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteVarInt((int)Hand); - if (version.Version.Protocol >= ProtocolVersion.V_1_19) + if (data.Version.Protocol >= ProtocolVersion.V_1_19_0) { buffer.WriteVarInt((int)SequenceId!); } } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static UseItemPacket Read(PacketBuffer buffer, MinecraftData data) { var hand = (PlayerHand)buffer.ReadVarInt(); - if (version.Version.Protocol < ProtocolVersion.V_1_19) + if (data.Version.Protocol < ProtocolVersion.V_1_19_0) { return new UseItemPacket(hand); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Play/WindowClickPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Play/WindowClickPacket.cs index 4563aa42..3bfb4305 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Play/WindowClickPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Play/WindowClickPacket.cs @@ -7,7 +7,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Play; #pragma warning disable CS1591 -public sealed record WindowClickPacket( +public sealed partial record WindowClickPacket( byte WindowId, int StateId, short Slot, @@ -15,14 +15,14 @@ public sealed record WindowClickPacket( int Mode, Slot[] ChangedSlots, Item? SelectedItem -) : IPacket +) : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Play_WindowClick; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteByte(WindowId); buffer.WriteVarInt(StateId); @@ -33,7 +33,7 @@ public void Write(PacketBuffer buffer, MinecraftData version) buffer.WriteOptionalItem(SelectedItem); } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static WindowClickPacket Read(PacketBuffer buffer, MinecraftData data) { return new WindowClickPacket( buffer.ReadByte(), @@ -41,8 +41,8 @@ public static IPacket Read(PacketBuffer buffer, MinecraftData version) buffer.ReadShort(), buffer.ReadSByte(), buffer.ReadVarInt(), - buffer.ReadVarIntArray(buff => buff.ReadSlot(version)), - buffer.ReadOptionalItem(version) + buffer.ReadVarIntArray(buff => buff.ReadSlot(data)), + buffer.ReadOptionalItem(data) ); } } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Status/PingRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Status/PingRequestPacket.cs index 711b8af7..50a82407 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Status/PingRequestPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Status/PingRequestPacket.cs @@ -8,7 +8,7 @@ namespace MineSharp.Protocol.Packets.Serverbound.Status; /// Packet for ping request /// /// The payload of the ping request -public sealed record PingRequestPacket(long Payload) : IPacket +public sealed partial record PingRequestPacket(long Payload) : IPacketStatic { /// public PacketType Type => StaticType; @@ -16,13 +16,13 @@ public sealed record PingRequestPacket(long Payload) : IPacket public static PacketType StaticType => PacketType.SB_Status_Ping; /// - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { buffer.WriteLong(Payload); } /// - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static PingRequestPacket Read(PacketBuffer buffer, MinecraftData data) { return new PingRequestPacket(buffer.ReadLong()); } diff --git a/Components/MineSharp.Protocol/Packets/Serverbound/Status/StatusRequestPacket.cs b/Components/MineSharp.Protocol/Packets/Serverbound/Status/StatusRequestPacket.cs index e8b87837..456ef9c8 100644 --- a/Components/MineSharp.Protocol/Packets/Serverbound/Status/StatusRequestPacket.cs +++ b/Components/MineSharp.Protocol/Packets/Serverbound/Status/StatusRequestPacket.cs @@ -4,17 +4,17 @@ namespace MineSharp.Protocol.Packets.Serverbound.Status; #pragma warning disable CS1591 -public sealed record StatusRequestPacket : IPacket +public sealed partial record StatusRequestPacket : IPacketStatic { /// public PacketType Type => StaticType; /// public static PacketType StaticType => PacketType.SB_Status_PingStart; - public void Write(PacketBuffer buffer, MinecraftData version) + public void Write(PacketBuffer buffer, MinecraftData data) { } - public static IPacket Read(PacketBuffer buffer, MinecraftData version) + public static StatusRequestPacket Read(PacketBuffer buffer, MinecraftData data) { return new StatusRequestPacket(); } diff --git a/Components/MineSharp.Protocol/Packets/SortedListExtensions.cs b/Components/MineSharp.Protocol/Packets/SortedListExtensions.cs new file mode 100644 index 00000000..bae3a86c --- /dev/null +++ b/Components/MineSharp.Protocol/Packets/SortedListExtensions.cs @@ -0,0 +1,69 @@ +namespace MineSharp.Protocol.Packets; + +internal static class SortedListExtensions +{ + /// + /// Finds the next lower bound in the . + /// + /// Taken from: and modified. + /// + public static bool TryGetLowerBound(this SortedList list, TKey key, out TValue? lowerBound) + where TKey : notnull + { + var comparer = list.Comparer; + + #region Short-Circuits + + if (list.Count == 0) + { + //empty list + lowerBound = default; + return false; + } + + if (list.TryGetValue(key, out lowerBound)) + { + // "The function should return the first element equal" + return true; + } + + var keys = list.Keys; + var lastKey = keys[keys.Count - 1]; + if (comparer.Compare(lastKey, key) < 0) + { + // if all elements are smaller, return the biggest element + lowerBound = list[lastKey]; + return true; + } + + if (comparer.Compare(keys[0], key) > 0) + { + // if the first element is greater, return not found + lowerBound = default; + return false; + } + + #endregion Short-Circuits + + int range = list.Count - 1; // the range between first and last element to check + int itemIndex = 1; // the default value of index is 1 since 0 was already checked above + while (range > 0) + { + int halfRange = range / 2; // cut the search range in half + int indexTmp = itemIndex + halfRange; // set the current item to compare + if (comparer.Compare(keys[indexTmp], key) < 0) + { + // the current item is lower than the given key + itemIndex = indexTmp + 1; // set the current item to the item after the compared item + range = (range - halfRange - 1); // move the remaining range to the right + } + else + { + // the current item is greater than the given key (we have already checked equal above in short-circuits) + range = halfRange; // move the remaining range to the left + } + } + lowerBound = list[keys[itemIndex]]; + return true; + } +} diff --git a/Components/MineSharp.Windows/MineSharp.Windows.csproj b/Components/MineSharp.Windows/MineSharp.Windows.csproj index 22451765..80443c9f 100644 --- a/Components/MineSharp.Windows/MineSharp.Windows.csproj +++ b/Components/MineSharp.Windows/MineSharp.Windows.csproj @@ -20,11 +20,12 @@ - + + - + - + True diff --git a/Components/MineSharp.Windows/Window.cs b/Components/MineSharp.Windows/Window.cs index 61fce142..707b6d46 100644 --- a/Components/MineSharp.Windows/Window.cs +++ b/Components/MineSharp.Windows/Window.cs @@ -1,6 +1,7 @@ using MineSharp.Core.Common; using MineSharp.Core.Common.Items; using MineSharp.Core.Events; +using MineSharp.Data.Windows; using MineSharp.Windows.Clicks; using NLog; @@ -202,6 +203,18 @@ public Slot[] GetInventorySlots() .ToArray(); } + /// + /// Returns only the hotbar slots of the inventory part of this window. + /// Only works for player inventories + /// + /// + public Slot[] GetHotbarSlots() + { + return GetInventorySlots() + .Where(x => x.SlotIndex >= (short)PlayerWindowSlots.HotbarStart && x.SlotIndex <= (short)PlayerWindowSlots.HotbarEnd) + .ToArray(); + } + /// /// Returns all slots in this window /// diff --git a/Components/MineSharp.World/DimensionInfo.cs b/Components/MineSharp.World/DimensionInfo.cs index 948c062f..5a39464e 100644 --- a/Components/MineSharp.World/DimensionInfo.cs +++ b/Components/MineSharp.World/DimensionInfo.cs @@ -1,5 +1,5 @@ -using MineSharp.Core.Common; -using MineSharp.Data; +using MineSharp.Core; +using MineSharp.Core.Common; namespace MineSharp.World; @@ -8,7 +8,7 @@ namespace MineSharp.World; /// public record DimensionInfo(Dimension Dimension, int WorldMinY, int WorldMaxY) { - public static readonly MinecraftVersion MinecraftVersionMajor118 = new("1.18", -1); + public static readonly MinecraftVersion MinecraftVersionMajor118 = new("1.18", ProtocolVersion.Unknown); // TODO: Select Version Specific Dimension Info // since we currently only support 1.18 and above this is fine diff --git a/Data/MineSharp.Data/Framework/INameAndProtocolNumberIndexedData.cs b/Data/MineSharp.Data/Framework/INameAndProtocolNumberIndexedData.cs index f73083bc..77b697b3 100644 --- a/Data/MineSharp.Data/Framework/INameAndProtocolNumberIndexedData.cs +++ b/Data/MineSharp.Data/Framework/INameAndProtocolNumberIndexedData.cs @@ -12,14 +12,14 @@ public interface INameAndProtocolNumberIndexedData /// The number of data entries /// public int Count { get; } - + /// /// Return the protocol number associated with the given identifier. /// /// /// public int GetProtocolId(Identifier name); - + /// /// Return the associated with the given protocol number. /// @@ -40,9 +40,9 @@ public interface INameAndProtocolNumberIndexedData : INameAndProtocolNumb public int GetProtocolId(TEnum type); /// - /// Return associated with the given protocol number. + /// Return associated with the given protocol number. /// /// /// public TEnum GetType(int id); -} +} diff --git a/Data/MineSharp.Data/MinecraftData.cs b/Data/MineSharp.Data/MinecraftData.cs index 57abde55..3fc71ce3 100644 --- a/Data/MineSharp.Data/MinecraftData.cs +++ b/Data/MineSharp.Data/MinecraftData.cs @@ -159,7 +159,7 @@ public static async Task FromVersion(string version) throw new MineSharpVersionNotSupportedException($"Version {version} is not supported."); } - var protocolVersion = (int)ProtocolVersions.Value[version].SelectToken("version")!; + var protocolVersion = (Core.ProtocolVersion)(int)ProtocolVersions.Value[version].SelectToken("version")!; var minecraftVersion = new MinecraftVersion(version, protocolVersion); var biomeToken = await LoadAsset("biomes", versionToken); @@ -253,8 +253,8 @@ private static IWindowData GetWindowData(MinecraftVersion version) { return version.Protocol switch { - >= 765 => new(new WindowVersion1203()), - >= 736 => new WindowData(new WindowVersion1161()), + >= Core.ProtocolVersion.V_1_20_3 => new(new WindowVersion1203()), + >= Core.ProtocolVersion.V_1_16_1 => new WindowData(new WindowVersion1161()), _ => throw new NotSupportedException() }; } diff --git a/MineSharp.Bot/Blocks/PlayerActionStatus.cs b/MineSharp.Bot/Blocks/PlayerActionStatus.cs deleted file mode 100644 index fb587037..00000000 --- a/MineSharp.Bot/Blocks/PlayerActionStatus.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace MineSharp.Bot.Blocks; - -/// -/// Specifies the status of a player action -/// -public enum PlayerActionStatus -{ - /// - /// Digging has started - /// - StartedDigging = 0, - - /// - /// Digging has been cancelled - /// - CancelledDigging = 1, - - /// - /// Digging is complete - /// - FinishedDigging = 2, - - /// - /// Drop the item stack - /// - DropItemStack = 3, - - /// - /// Drop a single item - /// - DropItem = 4, - - /// - /// Finished eating or shot bow - /// - ShootArrowFinishedEating = 5, - - /// - /// Swap item to off hand - /// - SwapItemHand = 6 -} diff --git a/MineSharp.Bot/Plugins/ChatPlugin.cs b/MineSharp.Bot/Plugins/ChatPlugin.cs index 969b00c4..bd7f7af6 100644 --- a/MineSharp.Bot/Plugins/ChatPlugin.cs +++ b/MineSharp.Bot/Plugins/ChatPlugin.cs @@ -41,7 +41,7 @@ public ChatPlugin(MineSharpBot bot) : base(bot) messageCollector = Bot.Data.Version.Protocol switch { >= ProtocolVersion.V_1_19_3 => new LastSeenMessageCollector1193(), - >= ProtocolVersion.V_1_19_2 => new LastSeenMessageCollector1192(), + >= ProtocolVersion.V_1_19_1 => new LastSeenMessageCollector1192(), _ => null }; @@ -84,7 +84,7 @@ await Bot.Client.SendPacket( /// public Task SendChat(string message) { - if (Bot.Data.Version.Protocol >= ProtocolVersion.V_1_19 + if (Bot.Data.Version.Protocol >= ProtocolVersion.V_1_19_0 && message.StartsWith('/')) { return SendCommand(message.Substring(1)); @@ -96,11 +96,12 @@ public Task SendChat(string message) // 1.19.3 >= ProtocolVersion.V_1_19_3 => SendChat1_19_3(message), - // 1.19.2 - >= ProtocolVersion.V_1_19_2 => SendChat1_19_2(message), - + // FIXME: Check whether this is still correct. wiki.vg says that 1.19.1 and 1.19.2 have the same protocol version // 1.19.1 - >= ProtocolVersion.V_1_19 => SendChat1_19_1(message), + >= ProtocolVersion.V_1_19_1 => SendChat1_19_1(message), + + // 1.19.0 + >= ProtocolVersion.V_1_19_0 => SendChat1_19_0(message), // Literally every version before. _ => Bot.Client.SendPacket(new SBChatMessage(message)) @@ -126,11 +127,12 @@ public Task SendCommand(string command) // 1.19.3 >= ProtocolVersion.V_1_19_3 => SendCommand1_19_3(command, arguments), - // 1.19.2 - >= ProtocolVersion.V_1_19_2 => SendCommand1_19_2(command, arguments), - + // FIXME: Check whether this is still correct. wiki.vg says that 1.19.1 and 1.19.2 have the same protocol version // 1.19.1 - >= ProtocolVersion.V_1_19 => SendCommand1_19_1(command, arguments), + >= ProtocolVersion.V_1_19_1 => SendCommand1_19_1(command, arguments), + + // 1.19.0 + >= ProtocolVersion.V_1_19_0 => SendCommand1_19_0(command, arguments), // Literally every version before _ => SendChat("/" + command) @@ -206,7 +208,7 @@ public Task SendCommand(string command) } - private Task SendChat1_19_1(string message) + private Task SendChat1_19_0(string message) { byte[] signature; var timestamp = DateTimeOffset.UtcNow; @@ -235,7 +237,7 @@ private Task SendChat1_19_1(string message) false)); } - private Task SendCommand1_19_1(string command, List<(string, string)> arguments) + private Task SendCommand1_19_0(string command, List<(string, string)> arguments) { var timestamp = DateTimeOffset.UtcNow; @@ -273,7 +275,7 @@ private Task SendCommand1_19_1(string command, List<(string, string)> arguments) false)); } - private Task SendChat1_19_2(string message) + private Task SendChat1_19_1(string message) { var collector = (LastSeenMessageCollector1192)messageCollector!; var messages = collector.AcknowledgeMessages(); @@ -312,7 +314,7 @@ private Task SendChat1_19_2(string message) )); } - private Task SendCommand1_19_2(string command, List<(string, string)> arguments) + private Task SendCommand1_19_1(string command, List<(string, string)> arguments) { var collector = (LastSeenMessageCollector1192)messageCollector!; var acknowledged = collector.AcknowledgeMessages(); @@ -533,13 +535,13 @@ private Task HandleSystemChatMessage(SystemChatMessagePacket packet) { return packet switch { - SystemChatMessagePacket.Before192 before192 - => HandleChatInternal(null, before192.Message, (ChatMessageType)before192.ChatType), + SystemChatMessagePacket.Before191 before191 + => HandleChatInternal(null, before191.Message, (ChatMessageType)before191.ChatType), - SystemChatMessagePacket.Since192 since192 + SystemChatMessagePacket.Since191 since191 => HandleChatInternal(null, - since192.Message, - since192.IsOverlay ? ChatMessageType.GameInfo : ChatMessageType.SystemMessage), + since191.Message, + since191.IsOverlay ? ChatMessageType.GameInfo : ChatMessageType.SystemMessage), _ => throw new UnreachableException() }; diff --git a/MineSharp.Bot/Plugins/PlayerPlugin.cs b/MineSharp.Bot/Plugins/PlayerPlugin.cs index 051b8078..f7de4f17 100644 --- a/MineSharp.Bot/Plugins/PlayerPlugin.cs +++ b/MineSharp.Bot/Plugins/PlayerPlugin.cs @@ -220,7 +220,7 @@ await Bot.Client.SendPacket( /// public Task Respawn() { - return Bot.Client.SendPacket(new ClientCommandPacket(0)); + return Bot.Client.SendPacket(new ClientCommandPacket(ClientCommandPacket.ClientCommandAction.PerformRespawn)); } @@ -380,7 +380,7 @@ private Task HandlePlayerInfoUpdate(PlayerInfoUpdatePacket packet) break; } - if (!updateListedAction.Listed && Bot.Data.Version.Protocol <= ProtocolVersion.V_1_19_2) + if (!updateListedAction.Listed && Bot.Data.Version.Protocol <= ProtocolVersion.V_1_19_1) { OnPlayerLeft.Dispatch(Bot, player); } diff --git a/MineSharp.Bot/Plugins/Plugin.cs b/MineSharp.Bot/Plugins/Plugin.cs index 897c80c6..35a74796 100644 --- a/MineSharp.Bot/Plugins/Plugin.cs +++ b/MineSharp.Bot/Plugins/Plugin.cs @@ -121,7 +121,7 @@ public Task WaitForInitialization() } private AsyncPacketHandler CreateAfterInitializationPacketHandlerWrapper(AsyncPacketHandler packetHandler, bool queuePacketsSentBeforeInitializationCompleted = false) - where TPacket : IPacket + where TPacket : IPacketStatic, IPacketClientbound { return async param => { @@ -147,7 +147,7 @@ private AsyncPacketHandler CreateAfterInitializationPacketHandlerWrappe /// The packet handler to be called. /// Whether packets sent before the plugin has been initialized should be queued and processed after initialization. public void OnPacketAfterInitialization(AsyncPacketHandler packetHandler, bool queuePacketsSentBeforeInitializationCompleted = true) - where TPacket : IPacket + where TPacket : IPacketStatic, IPacketClientbound { var registration = Bot.Client.On(CreateAfterInitializationPacketHandlerWrapper(packetHandler, queuePacketsSentBeforeInitializationCompleted)); if (registration != null) diff --git a/MineSharp.Bot/Plugins/WindowPlugin.cs b/MineSharp.Bot/Plugins/WindowPlugin.cs index a770c7e9..2c275604 100644 --- a/MineSharp.Bot/Plugins/WindowPlugin.cs +++ b/MineSharp.Bot/Plugins/WindowPlugin.cs @@ -4,6 +4,7 @@ using MineSharp.Core.Common; using MineSharp.Core.Common.Blocks; using MineSharp.Core.Common.Items; +using MineSharp.Core.Concurrency; using MineSharp.Core.Events; using MineSharp.Core.Geometry; using MineSharp.Data.Windows; @@ -104,12 +105,12 @@ public WindowPlugin(MineSharpBot bot) : base(bot) public AsyncEvent OnHeldItemChanged = new(); /// - protected override Task Init() + protected override async Task Init() { playerPlugin = Bot.GetPlugin(); + await playerPlugin.WaitForInitialization().WaitAsync(Bot.CancellationToken); CreateInventory(); - return base.Init(); } /// @@ -225,6 +226,199 @@ public Task UseItem(PlayerHand hand = PlayerHand.MainHand) return Bot.Client.SendPacket(packet); } + /// + /// Confirmation flags for eating a food item. + /// + [Flags] + public enum EatFoodItemConfirmation + { + /// + /// No confirmation. + /// + None = 0, + + /// + /// Confirm if food or saturation value increased. + /// + FoodOrSaturationValueIncreased = 1 << 0, + + /// + /// Confirm if food item count decreased. + /// + FoodItemCountDecreased = 1 << 1, + + /// + /// Confirm all conditions. + /// + All = FoodOrSaturationValueIncreased | FoodItemCountDecreased + } + + /// + /// Reasons why eating food was canceled. + /// + public enum EatFoodItemResult + { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + /// + /// This value is internal and should never be returned. + /// + Unknown, + SuccessfullyEaten, + CancelledUserRequested, + CancelledBotDisconnected, + CancelledBotDied, + CancelledFoodItemSlotChanged, +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + } + + /// + /// Makes the bot eat a food item from the specified hand. + /// + /// The hand from which to eat the food item. + /// The confirmation flags determining what checks should be made to ensure the food was eaten (server side). + /// The cancellation token to cancel the task. + /// A task that completes once the food was eaten or eating the food was cancelled. + public async Task EatFoodItem(PlayerHand hand = PlayerHand.MainHand, EatFoodItemConfirmation eatFoodItemConfirmation = EatFoodItemConfirmation.All, CancellationToken cancellationToken = default) + { + var foodSlot = await GetSlotForPlayerHand(hand); + + // TODO: Also check whether the item is eatable + if (foodSlot is null) + { + throw new Exception("No food item found in hand"); + } + + var result = EatFoodItemResult.Unknown; + + var cts = CancellationTokenSource.CreateLinkedTokenSource(Bot.CancellationToken, cancellationToken); + var token = cts.Token; + var botDiedTask = Bot.Client.WaitForPacketWhere(packet => packet.Health <= 0, token) + .ContinueWith(_ => + { + InterlockedHelper.CompareExchangeIfNot(ref result, EatFoodItemResult.CancelledBotDied, EatFoodItemResult.SuccessfullyEaten); + Logger.Debug("Bot died while eating food."); + return cts.CancelAsync(); + }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously); + + var currentFood = playerPlugin!.Food; + var currentSaturation = playerPlugin!.Saturation; + var foodValueIncreasedTask = eatFoodItemConfirmation.HasFlag(EatFoodItemConfirmation.FoodOrSaturationValueIncreased) + ? Bot.Client.WaitForPacketWhere(packet => packet.Food > currentFood || packet.Saturation > currentSaturation, token) + // for debugging race conditions + //.ContinueWith(t => + //{ + // var packet = t.Result; + // Logger.Debug("Eat finish SetHealthPacket: {Health} {Food} {Saturation}", packet.Health, packet.Food, packet.Saturation); + //}, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously) + : Task.CompletedTask; + + TaskCompletionSource? foodItemCountDecreasedTcs = null; + CancellationTokenRegistration cancellationTokenRegistration = default; + Task foodSlotUpdatedTask = Task.CompletedTask; + if (eatFoodItemConfirmation.HasFlag(EatFoodItemConfirmation.FoodItemCountDecreased)) + { + foodItemCountDecreasedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + cancellationTokenRegistration = token.Register(() => foodItemCountDecreasedTcs.TrySetCanceled()); + foodSlotUpdatedTask = foodItemCountDecreasedTcs.Task; + } + Inventory!.OnSlotChanged += HandleSlotUpdate; + Task HandleSlotUpdate(Window window, short index) + { + var slot = window.GetSlot(index); + if (slot.SlotIndex == foodSlot.SlotIndex) + { + var itemTypeMatches = slot.Item?.Info.Type == foodSlot.Item!.Info.Type; + var currentCount = slot.Item?.Count ?? 0; + if (currentCount == 0 || itemTypeMatches) + { + if (eatFoodItemConfirmation.HasFlag(EatFoodItemConfirmation.FoodItemCountDecreased)) + { + if (currentCount > foodSlot.Item!.Count) + { + // player picked up more food + // we need to update our expected food count + foodSlot.Item!.Count = currentCount; + } + else if (currentCount < foodSlot.Item!.Count) + { + // food item got removed from slot + foodItemCountDecreasedTcs!.TrySetResult(); + } + } + } + // not else if. Do both + if (!itemTypeMatches) + { + // food item got removed or replaced from slot + // the case that the food count reached zero can be both an cancel reason and a success reason + InterlockedHelper.CompareExchange(ref result, EatFoodItemResult.CancelledFoodItemSlotChanged, EatFoodItemResult.Unknown); + return cts.CancelAsync(); + } + } + return Task.CompletedTask; + } + await UseItem(hand); + + EatFoodItemResult finalResult = EatFoodItemResult.Unknown; + try + { + // wait for food to be eaten + // no WaitAsync here because both tasks will get canceled + await Task.WhenAll(foodValueIncreasedTask, foodSlotUpdatedTask); + // TODO: Maybe here is a race condition when the foodSlotUpdatedTask finishes (item == null) so that foodValueIncreasedTask gets canceled before the packet gets handled + // or that the player died but the died packet came in later + // ^^ only solution would be a packetReceiveIndex or synchronous packet handling + + // here we do not use interlocked because it commonly happens that the result is set to CancelledFoodItemSlotChanged (item == null) + // but that is a this a success case + result = EatFoodItemResult.SuccessfullyEaten; + Logger.Debug("Bot ate food of type {FoodType} at slot {SlotIndex}.", foodSlot.Item?.Info.Type, foodSlot.SlotIndex); + } + catch (OperationCanceledException) + { + if (cancellationToken.IsCancellationRequested) + { + InterlockedHelper.CompareExchange(ref result, EatFoodItemResult.CancelledUserRequested, EatFoodItemResult.Unknown); + } + else if (Bot.CancellationToken.IsCancellationRequested) + { + InterlockedHelper.CompareExchange(ref result, EatFoodItemResult.CancelledBotDisconnected, EatFoodItemResult.Unknown); + } + } + finally + { + Inventory!.OnSlotChanged -= HandleSlotUpdate; + cancellationTokenRegistration.Dispose(); + + // cancel the other WaitForPacket tasks (like botDiedTask) + await cts.CancelAsync(); + cts.Dispose(); + finalResult = result; + if (finalResult != EatFoodItemResult.SuccessfullyEaten) + { + Logger.Debug("Eating food of type {FoodType} at slot {SlotIndex} was cancelled with reason: {CancelReason}.", foodSlot.Item?.Info.Type, foodSlot.SlotIndex, finalResult); + } + } + // if it occurs that the cancelReason is not EatFoodCancelReason.None but the "Bot ate food" is logged than there is a race condition + return finalResult; + } + + /// + /// Gets the slot for the specified player hand. + /// + /// The hand to get the slot for. + /// The slot corresponding to the specified hand. + public async Task GetSlotForPlayerHand(PlayerHand hand) + { + await WaitForInventory(); + + var slot = hand == PlayerHand.MainHand + ? Inventory!.GetSlot((short)(PlayerWindowSlots.HotbarStart + SelectedHotbarIndex)) + : Inventory!.GetSlot(Inventory.Slot.OffHand); + + return slot; + } + /// /// Search inventory for item of type , and put it /// in the bots . @@ -234,11 +428,7 @@ public Task UseItem(PlayerHand hand = PlayerHand.MainHand) /// public async Task EquipItem(ItemType type, PlayerHand hand = PlayerHand.MainHand) { - await WaitForInventory(); - - var handSlot = hand == PlayerHand.MainHand - ? Inventory!.GetSlot((short)(PlayerWindowSlots.HotbarStart + SelectedHotbarIndex)) - : Inventory!.GetSlot(Inventory.Slot.OffHand); + var handSlot = await GetSlotForPlayerHand(hand); _EquipItem(type, handSlot); } diff --git a/MineSharp.Bot/Plugins/WorldPlugin.cs b/MineSharp.Bot/Plugins/WorldPlugin.cs index 1b9fee1d..a542bb19 100644 --- a/MineSharp.Bot/Plugins/WorldPlugin.cs +++ b/MineSharp.Bot/Plugins/WorldPlugin.cs @@ -4,10 +4,12 @@ using MineSharp.Core.Common.Effects; using MineSharp.Core.Geometry; using MineSharp.Protocol.Packets.Clientbound.Play; +using MineSharp.Protocol.Packets.NetworkTypes; using MineSharp.Protocol.Packets.Serverbound.Play; using MineSharp.World; using MineSharp.World.Chunks; using NLog; +using static MineSharp.Protocol.Packets.Serverbound.Play.CommandBlockUpdatePacket; namespace MineSharp.Bot.Plugins; @@ -92,14 +94,14 @@ public async Task WaitForChunks(int radius = 5) /// /// /// - public Task UpdateCommandBlock(Position location, string command, int mode, byte flags) + public Task UpdateCommandBlock(Position location, string command, CommandBlockMode mode, CommandBlockFlags flags) { if (playerPlugin?.Self?.GameMode != GameMode.Creative) { throw new("Player must be in creative mode."); } - var packet = new UpdateCommandBlock(location, command, mode, flags); + var packet = new CommandBlockUpdatePacket(location, command, mode, flags); return Bot.Client.SendPacket(packet); } @@ -141,7 +143,7 @@ public async Task MineBlock(Block block, BlockFace? face = null // first packet: Start digging var startPacket = new PlayerActionPacket( // TODO: PlayerActionPacket hardcoded values - (int)PlayerActionStatus.StartedDigging, + PlayerActionStatus.StartDigging, block.Position, face.Value, ++Bot.SequenceId); // Sequence Id is ignored when sending before 1.19 @@ -173,7 +175,7 @@ public async Task MineBlock(Block block, BlockFace? face = null await Task.Delay(time); var finishPacket = new PlayerActionPacket( - (int)PlayerActionStatus.FinishedDigging, + PlayerActionStatus.FinishedDigging, block.Position, face.Value, ++Bot.SequenceId); @@ -191,7 +193,7 @@ public async Task MineBlock(Block block, BlockFace? face = null if (ack.Body is AcknowledgeBlockChangePacket.PacketBody118 p118) { - if ((PlayerActionStatus)p118.Status != PlayerActionStatus.StartedDigging) + if ((PlayerActionStatus)p118.Status != PlayerActionStatus.StartDigging) { return MineBlockStatus.Failed; } @@ -206,7 +208,7 @@ public async Task MineBlock(Block block, BlockFace? face = null catch (TaskCanceledException) { var cancelPacket = new PlayerActionPacket( - (int)PlayerActionStatus.CancelledDigging, + PlayerActionStatus.CancelledDigging, block.Position, face.Value, ++Bot.SequenceId); @@ -229,7 +231,7 @@ public async Task PlaceBlock(Position position, PlayerHand hand = PlayerHand.Mai { // TODO: PlaceBlock: Hardcoded values var packet = new PlaceBlockPacket( - (int)hand, + hand, position, face, 0.5f, diff --git a/MineSharp.Core/Common/GameMode.cs b/MineSharp.Core/Common/GameMode.cs index 81fd4b14..f55c726b 100644 --- a/MineSharp.Core/Common/GameMode.cs +++ b/MineSharp.Core/Common/GameMode.cs @@ -3,7 +3,7 @@ /// /// Specifies the GameMode of a player /// -public enum GameMode +public enum GameMode : byte { /// /// Survival game mode diff --git a/MineSharp.Core/Common/MinecraftVersion.cs b/MineSharp.Core/Common/MinecraftVersion.cs index e6f1dfb0..0259f07b 100644 --- a/MineSharp.Core/Common/MinecraftVersion.cs +++ b/MineSharp.Core/Common/MinecraftVersion.cs @@ -10,7 +10,7 @@ public class MinecraftVersion /// /// /// - public MinecraftVersion(string version, int protocol) + public MinecraftVersion(string version, ProtocolVersion protocol) { var versionNumbers = version.Split(".").Select(x => Convert.ToInt32(x)).ToArray(); Major = versionNumbers[0]; @@ -22,7 +22,7 @@ public MinecraftVersion(string version, int protocol) /// /// The protocol version number /// - public int Protocol { get; } + public ProtocolVersion Protocol { get; } /// /// The major version diff --git a/MineSharp.Core/Concurrency/InterlockedHelper.cs b/MineSharp.Core/Concurrency/InterlockedHelper.cs new file mode 100644 index 00000000..99871573 --- /dev/null +++ b/MineSharp.Core/Concurrency/InterlockedHelper.cs @@ -0,0 +1,134 @@ +using System.Runtime.CompilerServices; + +namespace MineSharp.Core.Concurrency; + +public static class InterlockedHelper +{ + /// + /// Compares two 32-bit signed integers for equality and, if they are not equal to the specified value, replaces the first value. + /// + /// The variable to set to the specified value. + /// The value to which the is set if the comparison succeeds. + /// The value that is compared to the value at . + /// The original value of . + public static int CompareExchangeIfNot(ref int location1, int value, int notComparand) + { + var newCurrent = location1; + var oldCurrent = newCurrent; + do + { + oldCurrent = newCurrent; + if (oldCurrent == notComparand) + { + return oldCurrent; + } + } while ((newCurrent = Interlocked.CompareExchange(ref location1, value, oldCurrent)) != oldCurrent); + + return newCurrent; + } + + /// + /// Compares two 64-bit signed integers for equality and, if they are not equal to the specified value, replaces the first value. + /// + /// The variable to set to the specified value. + /// The value to which the is set if the comparison succeeds. + /// The value that is compared to the value at . + /// The original value of . + public static long CompareExchangeIfNot(ref long location1, long value, long notComparand) + { + var newCurrent = location1; + var oldCurrent = newCurrent; + do + { + oldCurrent = newCurrent; + if (oldCurrent == notComparand) + { + return oldCurrent; + } + } while ((newCurrent = Interlocked.CompareExchange(ref location1, value, oldCurrent)) != oldCurrent); + + return newCurrent; + } + + #region Enum + + /// + /// Exchanges the enum value of the specified location with the given value. + /// + /// Note: This is very slow and also a bit unsafe but it's the only way to do it before .NET 9 + /// + /// The type of the value. Must be an enum. + /// The variable to set to the specified value. + /// The value to which the is set. + /// The original value of . + public static T Exchange(ref T location1, T value) + where T : unmanaged, Enum + { + switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T)))) + { + case TypeCode.UInt32: + case TypeCode.Int32: + var resultInt = Interlocked.Exchange(ref Unsafe.As(ref location1), Unsafe.As(ref value)); + return Unsafe.As(ref resultInt); + case TypeCode.UInt64: + case TypeCode.Int64: + var resultLong = Interlocked.Exchange(ref Unsafe.As(ref location1), Unsafe.As(ref value)); + return Unsafe.As(ref resultLong); + default: + throw new NotSupportedException(); + } + } + + /// + /// Compares two enum values for equality and, if they are equal, replaces the first value. + /// + /// The type of the value. Must be an enum. + /// The variable to set to the specified value. + /// The value to which the is set if the comparison succeeds. + /// The value that is compared to the value at . + /// The original value of . + public static T CompareExchange(ref T location1, T value, T comparand) + where T : unmanaged, Enum + { + switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T)))) + { + case TypeCode.UInt32: + case TypeCode.Int32: + var resultInt = Interlocked.CompareExchange(ref Unsafe.As(ref location1), Unsafe.As(ref value), Unsafe.As(ref comparand)); + return Unsafe.As(ref resultInt); + case TypeCode.UInt64: + case TypeCode.Int64: + var resultLong = Interlocked.CompareExchange(ref Unsafe.As(ref location1), Unsafe.As(ref value), Unsafe.As(ref comparand)); + return Unsafe.As(ref resultLong); + default: + throw new NotSupportedException(); + } + } + + /// + /// Compares two enum values for equality and, if they are not equal to the specified value, replaces the first value. + /// + /// The type of the value. Must be an enum. + /// The variable to set to the specified value. + /// The value to which the is set if the comparison succeeds. + /// The value that is compared to the value at . + /// The original value of . + public static T CompareExchangeIfNot(ref T location1, T value, T notComparand) + where T : unmanaged, Enum + { + switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T)))) + { + case TypeCode.UInt32: + case TypeCode.Int32: + var resultInt = CompareExchangeIfNot(ref Unsafe.As(ref location1), Unsafe.As(ref value), Unsafe.As(ref notComparand)); + return Unsafe.As(ref resultInt); + case TypeCode.UInt64: + case TypeCode.Int64: + var resultLong = CompareExchangeIfNot(ref Unsafe.As(ref location1), Unsafe.As(ref value), Unsafe.As(ref notComparand)); + return Unsafe.As(ref resultLong); + default: + throw new NotSupportedException(); + } + } + #endregion +} diff --git a/MineSharp.Core/Events/AsyncEvent.cs b/MineSharp.Core/Events/AsyncEvent.cs index 2bf7ceb1..838adca9 100644 --- a/MineSharp.Core/Events/AsyncEvent.cs +++ b/MineSharp.Core/Events/AsyncEvent.cs @@ -424,7 +424,6 @@ public class AsyncEvent : AsyncEventBase /// Dispatch this event /// - /// Whether to block until all handlers are completed /// First argument for the handlers /// Second argument for the handlers /// Third argument for the handlers diff --git a/MineSharp.Core/Geometry/BlockFace.cs b/MineSharp.Core/Geometry/BlockFace.cs index 9a65d851..7e36cc6f 100644 --- a/MineSharp.Core/Geometry/BlockFace.cs +++ b/MineSharp.Core/Geometry/BlockFace.cs @@ -5,12 +5,28 @@ /// public enum BlockFace { -#pragma warning disable CS1591 + /// + /// Offset: -Y + /// Bottom = 0, + /// + /// Offset: +Y + /// Top = 1, + /// + /// Offset: -Z + /// North = 2, + /// + /// Offset: +Z + /// South = 3, + /// + /// Offset: -X + /// West = 4, + /// + /// Offset: +X + /// East = 5 -#pragma warning restore CS1591 } diff --git a/MineSharp.Core/ProtocolVersion.cs b/MineSharp.Core/ProtocolVersion.cs index 403b5fbb..acd0c9e0 100644 --- a/MineSharp.Core/ProtocolVersion.cs +++ b/MineSharp.Core/ProtocolVersion.cs @@ -1,65 +1,103 @@ namespace MineSharp.Core; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member /// -/// Utility class for Minecraft's protocol versions +/// Enum for all the protocol versions for non- preview/snapshot/beta versions of Minecraft Java Edition. +/// If multiple Minecraft versions have the same protocol version only the lowest Minecraft versions gets an enum value here. /// -public static class ProtocolVersion +public enum ProtocolVersion { + Unknown = -1, + // older Minecraft versions did not use Netty and not these protocol ids + + V_1_7_0 = 3, + V_1_7_2 = 4, + V_1_7_6 = 5, + V_1_8_0 = 47, + V_1_9_0 = 107, /// - /// Protocol version of Minecraft Java 1.20.3 and 1.20.4 + /// Pre-release /// - public const int V_1_20_3 = 765; - + V_1_9_1 = 108, + V_1_9_2 = 109, + V_1_9_3 = 110, + V_1_10_0 = 210, + V_1_11_0 = 315, + V_1_11_1 = 316, + V_1_12_0 = 335, + V_1_12_1 = 338, + V_1_12_2 = 340, /// - /// Protocol version of Minecraft Java 1.20.2 + /// Pre-release /// - public const int V_1_20_2 = 764; - + V_1_13_0 = 393, + V_1_13_1 = 401, + V_1_13_2 = 404, /// - /// Protocol version of Minecraft Java 1.20 and 1.20.1 + /// Pre-release /// - public const int V_1_20 = 763; - + V_1_14_0 = 477, /// - /// Protocol version of Minecraft Java 1.19.4 + /// Pre-release /// - public const int V_1_19_4 = 762; - + V_1_14_1 = 480, /// - /// Protocol version of Minecraft Java 1.19.3 + /// Pre-release /// - public const int V_1_19_3 = 761; - + V_1_14_2 = 485, /// - /// Protocol version of Minecraft Java 1.19.2 + /// Pre-release /// - public const int V_1_19_2 = 760; - + V_1_14_3 = 490, + V_1_14_4 = 498, /// - /// Protocol version of Minecraft Java 1.19 and 1.19.1 + /// Pre-release /// - public const int V_1_19 = 759; - + V_1_15_0 = 573, /// - /// Protocol version of Minecraft Java 1.18.2 + /// Pre-release /// - public const int V_18_2 = 758; - + V_1_15_1 = 575, + V_1_15_2 = 578, /// - /// Protocol version of Minecraft Java 1.18.1 + /// Pre-release /// - public const int V_18_1 = 757; - + V_1_16_0 = 735, + /// + /// Pre-release + /// + V_1_16_1 = 736, /// - /// Protocol version of Minecraft Java 1.18 + /// Pre-release /// - public const int V_18 = 757; + V_1_16_2 = 751, + V_1_16_3 = 753, + V_1_16_4 = 754, + V_1_17_0 = 755, + V_1_17_1 = 756, + V_1_18_0 = 757, + V_1_18_2 = 758, + V_1_19_0 = 759, + V_1_19_1 = 760, + V_1_19_3 = 761, + V_1_19_4 = 762, + V_1_20_0 = 763, + V_1_20_2 = 764, + V_1_20_3 = 765, + V_1_20_5 = 766, + V_1_21_0 = 767, + // add new protocol versions here +} + +public static class ProtocolVersionExtensions +{ /// /// Whether the is between and /// - public static bool IsBetween(int version, int min, int max) + public static bool IsBetween(this ProtocolVersion version, ProtocolVersion min, ProtocolVersion max) { return version >= min && version <= max; } } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/MineSharp.Core/Serialization/PacketBuffer.cs b/MineSharp.Core/Serialization/PacketBuffer.cs index 8002a8a9..3f309f20 100644 --- a/MineSharp.Core/Serialization/PacketBuffer.cs +++ b/MineSharp.Core/Serialization/PacketBuffer.cs @@ -20,7 +20,7 @@ public class PacketBuffer : IDisposable, IAsyncDisposable /// Create a new empty, writeable PacketBuffer /// /// - public PacketBuffer(int protocolVersion) + public PacketBuffer(ProtocolVersion protocolVersion) { ProtocolVersion = protocolVersion; buffer = new(); @@ -31,7 +31,7 @@ public PacketBuffer(int protocolVersion) /// /// /// - public PacketBuffer(byte[] bytes, int protocolVersion) + public PacketBuffer(byte[] bytes, ProtocolVersion protocolVersion) { ProtocolVersion = protocolVersion; buffer = new(bytes, false); @@ -56,12 +56,12 @@ public PacketBuffer(byte[] bytes, int protocolVersion) /// Whether to use anonymous nbt compounds. This is used since /// Minecraft Java 1.20.2 /// - public bool UseAnonymousNbt => ProtocolVersion >= Core.ProtocolVersion.V_1_20_2; + public bool UseAnonymousNbt => ProtocolVersion >= ProtocolVersion.V_1_20_2; /// /// The protocol version this packet buffer uses /// - public int ProtocolVersion { get; } + public ProtocolVersion ProtocolVersion { get; } /// /// Disposes the underlying diff --git a/MineSharp.sln b/MineSharp.sln index 9a47086d..cc935626 100644 --- a/MineSharp.sln +++ b/MineSharp.sln @@ -1,40 +1,45 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Core", "MineSharp.Core\MineSharp.Core.csproj", "{4D9DE515-02F9-432D-ABD3-DD1EAC8F033B}" +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35125.118 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Core", "MineSharp.Core\MineSharp.Core.csproj", "{4D9DE515-02F9-432D-ABD3-DD1EAC8F033B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Components", "Components", "{CAB43D13-C4AC-4D69-AB04-ECF9B514115D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Auth", "Components\MineSharp.Auth\MineSharp.Auth.csproj", "{63B60BB4-DB86-423A-9585-82FD060089F2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Auth", "Components\MineSharp.Auth\MineSharp.Auth.csproj", "{63B60BB4-DB86-423A-9585-82FD060089F2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Protocol", "Components\MineSharp.Protocol\MineSharp.Protocol.csproj", "{4F3F3C20-B7DA-47E1-8751-1F90F22ADDB7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Protocol", "Components\MineSharp.Protocol\MineSharp.Protocol.csproj", "{4F3F3C20-B7DA-47E1-8751-1F90F22ADDB7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.World", "Components\MineSharp.World\MineSharp.World.csproj", "{2B385CE0-26CA-4848-B791-3286600B2CFF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.World", "Components\MineSharp.World\MineSharp.World.csproj", "{2B385CE0-26CA-4848-B791-3286600B2CFF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5FB9D901-4E29-4F19-B20B-E099C21A2C88}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.World.Tests", "Components\Tests\MineSharp.World.Tests\MineSharp.World.Tests.csproj", "{EBC07383-378A-47CE-B97A-D22703E9EF43}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.World.Tests", "Components\Tests\MineSharp.World.Tests\MineSharp.World.Tests.csproj", "{EBC07383-378A-47CE-B97A-D22703E9EF43}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Windows", "Components\MineSharp.Windows\MineSharp.Windows.csproj", "{B756D51E-636F-45B9-9600-B50ADD64A61F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Windows", "Components\MineSharp.Windows\MineSharp.Windows.csproj", "{B756D51E-636F-45B9-9600-B50ADD64A61F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Windows.Tests", "Components\Tests\MineSharp.Windows.Tests\MineSharp.Windows.Tests.csproj", "{7DE700D4-E9C6-4C76-8276-CB7616AD959B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Windows.Tests", "Components\Tests\MineSharp.Windows.Tests\MineSharp.Windows.Tests.csproj", "{7DE700D4-E9C6-4C76-8276-CB7616AD959B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Physics", "Components\MineSharp.Physics\MineSharp.Physics.csproj", "{3DCD548E-7834-4665-BA10-10EB2BF83A8B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Physics", "Components\MineSharp.Physics\MineSharp.Physics.csproj", "{3DCD548E-7834-4665-BA10-10EB2BF83A8B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Bot", "MineSharp.Bot\MineSharp.Bot.csproj", "{EF690A10-E46C-4B38-9BBC-C72CBD3229A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Bot", "MineSharp.Bot\MineSharp.Bot.csproj", "{EF690A10-E46C-4B38-9BBC-C72CBD3229A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Commands", "Components\MineSharp.Commands\MineSharp.Commands.csproj", "{ACCD1366-DBDD-453D-80BD-150F2C5CDBDA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Commands", "Components\MineSharp.Commands\MineSharp.Commands.csproj", "{ACCD1366-DBDD-453D-80BD-150F2C5CDBDA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Bot.IntegrationTests", "MineSharp.Bot.IntegrationTests\MineSharp.Bot.IntegrationTests.csproj", "{1D8A5126-D720-4AEC-8FD1-5A70C69A0AD9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Bot.IntegrationTests", "MineSharp.Bot.IntegrationTests\MineSharp.Bot.IntegrationTests.csproj", "{1D8A5126-D720-4AEC-8FD1-5A70C69A0AD9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{7441DE2E-3B7A-42F5-B3FC-FD25FCF46B8F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Data", "Data\MineSharp.Data\MineSharp.Data.csproj", "{D6F1AE8D-ACA3-4E2A-A103-13BE9579A70D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Data", "Data\MineSharp.Data\MineSharp.Data.csproj", "{D6F1AE8D-ACA3-4E2A-A103-13BE9579A70D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.SourceGenerator", "Data\MineSharp.SourceGenerator\MineSharp.SourceGenerator.csproj", "{A1AC37F2-6209-473B-A787-0F1601998BEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.SourceGenerator", "Data\MineSharp.SourceGenerator\MineSharp.SourceGenerator.csproj", "{A1AC37F2-6209-473B-A787-0F1601998BEB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.ChatComponent", "Components\MineSharp.ChatComponent\MineSharp.ChatComponent.csproj", "{6CFF9878-00D1-4FE3-9146-B6B2EE746A0C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.ChatComponent", "Components\MineSharp.ChatComponent\MineSharp.ChatComponent.csproj", "{6CFF9878-00D1-4FE3-9146-B6B2EE746A0C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.Data.Tests", "Data\MineSharp.Data.Tests\MineSharp.Data.Tests.csproj", "{1B36A1EF-2A07-4606-B748-B6E708C697E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineSharp.Data.Tests", "Data\MineSharp.Data.Tests\MineSharp.Data.Tests.csproj", "{1B36A1EF-2A07-4606-B748-B6E708C697E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineSharp.PacketSourceGenerator", "Components\MineSharp.PacketSourceGenerator\MineSharp.PacketSourceGenerator.csproj", "{0642A83E-11AE-4DF3-B414-4942B10FEA56}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -102,6 +107,13 @@ Global {1B36A1EF-2A07-4606-B748-B6E708C697E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B36A1EF-2A07-4606-B748-B6E708C697E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B36A1EF-2A07-4606-B748-B6E708C697E3}.Release|Any CPU.Build.0 = Release|Any CPU + {0642A83E-11AE-4DF3-B414-4942B10FEA56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0642A83E-11AE-4DF3-B414-4942B10FEA56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0642A83E-11AE-4DF3-B414-4942B10FEA56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0642A83E-11AE-4DF3-B414-4942B10FEA56}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {63B60BB4-DB86-423A-9585-82FD060089F2} = {CAB43D13-C4AC-4D69-AB04-ECF9B514115D} @@ -117,5 +129,9 @@ Global {A1AC37F2-6209-473B-A787-0F1601998BEB} = {7441DE2E-3B7A-42F5-B3FC-FD25FCF46B8F} {6CFF9878-00D1-4FE3-9146-B6B2EE746A0C} = {CAB43D13-C4AC-4D69-AB04-ECF9B514115D} {1B36A1EF-2A07-4606-B748-B6E708C697E3} = {7441DE2E-3B7A-42F5-B3FC-FD25FCF46B8F} + {0642A83E-11AE-4DF3-B414-4942B10FEA56} = {CAB43D13-C4AC-4D69-AB04-ECF9B514115D} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {65B6B1B2-E12A-4D4B-8643-EC7B1A9EEF2F} EndGlobalSection EndGlobal