mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
First Version of Structured Components
This commit is contained in:
parent
5a6fd577e5
commit
63b027d84a
11 changed files with 314 additions and 26 deletions
|
|
@ -6,6 +6,8 @@ using MinecraftClient.Inventory;
|
|||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Mapping.EntityPalettes;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
|
|
@ -13,7 +15,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <summary>
|
||||
/// Handle data types encoding / decoding
|
||||
/// </summary>
|
||||
class DataTypes
|
||||
public class DataTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Protocol version for adjusting data types
|
||||
|
|
@ -419,37 +421,92 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <returns>The item that was read or NULL for an empty slot</returns>
|
||||
public Item? ReadNextItemSlot(Queue<byte> cache, ItemPalette itemPalette)
|
||||
{
|
||||
// MC 1.13.2 and greater
|
||||
if (protocolversion >= Protocol18Handler.MC_1_13_Version)
|
||||
var itemId = -1;
|
||||
var itemCount = 0;
|
||||
var nbt = null as Dictionary<string, object>;
|
||||
var item = null as Item;
|
||||
var strcturedComponentsToAdd = new List<StructuredComponent>();
|
||||
|
||||
switch (protocolversion)
|
||||
{
|
||||
var itemPresent = ReadNextBool(cache);
|
||||
// MC 1.13.2 and greater
|
||||
case >= Protocol18Handler.MC_1_20_6_Version:
|
||||
itemCount = ReadNextVarInt(cache);
|
||||
|
||||
if (!itemPresent)
|
||||
return null;
|
||||
if (itemCount <= 0) return null;
|
||||
|
||||
itemId = ReadNextVarInt(cache);
|
||||
item = new Item(itemPalette.FromId(itemId), itemCount, null);
|
||||
|
||||
var numberOfComponentsToAdd = ReadNextVarInt(cache);
|
||||
var numberofComponentsToRemove = ReadNextVarInt(cache);
|
||||
|
||||
var itemId = ReadNextVarInt(cache);
|
||||
for (var i = 0; i < numberOfComponentsToAdd; i++)
|
||||
{
|
||||
var componentTypeId = ReadNextVarInt(cache);
|
||||
|
||||
if (itemId == -1)
|
||||
return null;
|
||||
var strcuturedComponentHandler = new StructuredComponentsHandler(protocolversion, this);
|
||||
strcturedComponentsToAdd.Add(strcuturedComponentHandler.Parse(componentTypeId, cache));
|
||||
}
|
||||
|
||||
var type = itemPalette.FromId(itemId);
|
||||
var itemCount = ReadNextByte(cache);
|
||||
var nbt = ReadNextNbt(cache);
|
||||
return new Item(type, itemCount, nbt);
|
||||
for (var i = 0; i < numberofComponentsToRemove; i++)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// TODO: Wire up the strctured components in the Item class (extract info, update fields, etc..)
|
||||
// Look at: https://wiki.vg/index.php?title=Slot_Data&oldid=19350#Structured_components
|
||||
|
||||
return item;
|
||||
case >= Protocol18Handler.MC_1_13_Version:
|
||||
{
|
||||
var itemPresent = ReadNextBool(cache);
|
||||
|
||||
if (!itemPresent)
|
||||
return null;
|
||||
|
||||
itemId = ReadNextVarInt(cache);
|
||||
|
||||
if (itemId == -1)
|
||||
return null;
|
||||
|
||||
var type = itemPalette.FromId(itemId);
|
||||
itemCount = ReadNextByte(cache);
|
||||
nbt = ReadNextNbt(cache);
|
||||
return new Item(type, itemCount, nbt);
|
||||
}
|
||||
default:
|
||||
{
|
||||
itemId = ReadNextShort(cache);
|
||||
|
||||
if (itemId == -1)
|
||||
return null;
|
||||
|
||||
itemCount = ReadNextByte(cache);
|
||||
var data = ReadNextShort(cache);
|
||||
nbt = ReadNextNbt(cache);
|
||||
|
||||
// For 1.8 - 1.12.2 we combine Item Id and Item Data/Damage to a single value using: (id << 16) | data
|
||||
return new Item(itemPalette.FromId((itemId << 16) | (ushort)data), itemCount, data, nbt);
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
private void ReadNextDetail(Queue<byte> cache)
|
||||
{
|
||||
var potionEffectId = ReadNextVarInt(cache);
|
||||
|
||||
// Details
|
||||
var potionEffectAmplifier = ReadNextVarInt(cache);
|
||||
var duration = ReadNextVarInt(cache); // -1 for infinite
|
||||
var ambient = ReadNextBool(cache);
|
||||
var showParticles = ReadNextBool(cache);
|
||||
var showIcon = ReadNextBool(cache);
|
||||
var hasHiddenEffect = ReadNextBool(cache);
|
||||
|
||||
if (hasHiddenEffect)
|
||||
{
|
||||
var itemId = ReadNextShort(cache);
|
||||
|
||||
if (itemId == -1)
|
||||
return null;
|
||||
|
||||
var itemCount = ReadNextByte(cache);
|
||||
var data = ReadNextShort(cache);
|
||||
var nbt = ReadNextNbt(cache);
|
||||
|
||||
// For 1.8 - 1.12.2 we combine Item Id and Item Data/Damage to a single value using: (id << 16) | data
|
||||
return new Item(itemPalette.FromId((itemId << 16) | (ushort)data), itemCount, data, nbt);
|
||||
ReadNextDetail(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <summary>
|
||||
/// Wrapper for handling unencrypted & encrypted socket
|
||||
/// </summary>
|
||||
class SocketWrapper
|
||||
public class SocketWrapper
|
||||
{
|
||||
readonly TcpClient c;
|
||||
AesCfb8Stream? s;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
|
||||
|
||||
public class TestSubComonent(DataTypes dataTypes) : SubComponent(dataTypes)
|
||||
{
|
||||
public int Test { get; set; }
|
||||
|
||||
public override void Parse(Queue<byte> data)
|
||||
{
|
||||
Test = DataTypes.ReadNextVarInt(data);
|
||||
}
|
||||
|
||||
public override Queue<byte> Serialize()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components;
|
||||
|
||||
public class TestComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, subComponentRegistry)
|
||||
{
|
||||
public int TestInt { get; set; }
|
||||
public string TestString { get; set; } = null!;
|
||||
|
||||
public TestSubComonent TestSubComonent { get; set; } = null!;
|
||||
|
||||
public override void Parse(Queue<byte> data)
|
||||
{
|
||||
TestInt = dataTypes.ReadNextVarInt(data);
|
||||
TestString = dataTypes.ReadNextString(data);
|
||||
TestSubComonent = (SubComponentRegistry.ParseSubComponent("TestSubComponent", data) as TestSubComonent)!;
|
||||
}
|
||||
|
||||
public override Queue<byte> Serialize()
|
||||
{
|
||||
var data = new List<byte>();
|
||||
data.AddRange(DataTypes.GetVarInt(TestInt));
|
||||
data.AddRange(DataTypes.GetString(TestString));
|
||||
data.AddRange(TestSubComonent.Serialize());
|
||||
return new Queue<byte>(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
public abstract class StructuredComponent(DataTypes dataTypes, SubComponentRegistry subComponentRegistry)
|
||||
{
|
||||
protected DataTypes DataTypes { get; private set; } = dataTypes;
|
||||
protected SubComponentRegistry SubComponentRegistry { get; private set; } = subComponentRegistry;
|
||||
|
||||
public abstract void Parse(Queue<byte> data);
|
||||
public abstract Queue<byte> Serialize();
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
public abstract class StructuredComponentRegistry(SubComponentRegistry subComponentRegistry, DataTypes dataTypes)
|
||||
{
|
||||
private Dictionary<string, Type> ComponentParsers { get; } = new();
|
||||
private Dictionary<int, string> IdToComponent { get; } = new();
|
||||
private Dictionary<string, int> ComponentToId { get; } = new();
|
||||
|
||||
protected void RegisterComponent<T>(int id, string name) where T : StructuredComponent
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
|
||||
name = name.ToLower();
|
||||
|
||||
if (ComponentParsers.ContainsKey(name) || IdToComponent.ContainsValue(name)
|
||||
|| ComponentToId.ContainsKey(name) || IdToComponent.ContainsKey(id))
|
||||
throw new InvalidOperationException($"A component with name '{name}' or id '{id}' is already registered.");
|
||||
|
||||
ComponentParsers[name] = typeof(T);
|
||||
IdToComponent[id] = name;
|
||||
ComponentToId[name] = id;
|
||||
}
|
||||
|
||||
public StructuredComponent ParseComponent(int id, Queue<byte> data)
|
||||
{
|
||||
if (IdToComponent.TryGetValue(id, out var name))
|
||||
{
|
||||
if (ComponentParsers.TryGetValue(name, out var type))
|
||||
{
|
||||
var component =
|
||||
Activator.CreateInstance(type, dataTypes, subComponentRegistry) as StructuredComponent
|
||||
?? throw new InvalidOperationException($"Could not instantiate a parser for a structured component type {name}");
|
||||
|
||||
component.Parse(data);
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"No parser found for component with ID {id}");
|
||||
}
|
||||
|
||||
public string GetComponentNameById(int id)
|
||||
{
|
||||
if (IdToComponent.TryGetValue(id, out var value))
|
||||
return value;
|
||||
|
||||
throw new Exception($"No component found for ID {id}");
|
||||
}
|
||||
|
||||
public int GetComponentIdByName(string name)
|
||||
{
|
||||
name = name.ToLower();
|
||||
|
||||
if (ComponentToId.TryGetValue(name, out var value))
|
||||
return value;
|
||||
|
||||
throw new Exception($"No ID found for component {name}");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
public abstract class SubComponent(DataTypes dataTypes)
|
||||
{
|
||||
protected DataTypes DataTypes { get; private set; } = dataTypes;
|
||||
|
||||
public abstract void Parse(Queue<byte> data);
|
||||
public abstract Queue<byte> Serialize();
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
public abstract class SubComponentRegistry(DataTypes dataTypes)
|
||||
{
|
||||
private readonly Dictionary<string, Type> _subComponentParsers = new();
|
||||
|
||||
protected void RegisterSubComponent<T>(string name) where T : SubComponent
|
||||
{
|
||||
if(_subComponentParsers.TryGetValue(name, out _))
|
||||
throw new Exception($"Sub component {name} already registered!");
|
||||
|
||||
_subComponentParsers.Add(name, typeof(T));
|
||||
}
|
||||
|
||||
public SubComponent ParseSubComponent(string name, Queue<byte> data)
|
||||
{
|
||||
if(!_subComponentParsers.TryGetValue(name, out var subComponentParserType))
|
||||
throw new Exception($"Sub component {name} not registered!");
|
||||
|
||||
var instance= Activator.CreateInstance(subComponentParserType, dataTypes) as SubComponent ??
|
||||
throw new InvalidOperationException($"Could not create instance of a sub component parser type: {subComponentParserType.Name}");
|
||||
|
||||
instance.Parse(data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries;
|
||||
|
||||
public class StructuredComponentsRegistry1206 : StructuredComponentRegistry
|
||||
{
|
||||
public StructuredComponentsRegistry1206(SubComponentRegistry subComponentRegistry, DataTypes dataTypes) : base(
|
||||
subComponentRegistry, dataTypes)
|
||||
{
|
||||
RegisterComponent<TestComponent>(0, "minecraft:test");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents;
|
||||
|
||||
public class TestSubComponentRegistry : SubComponentRegistry
|
||||
{
|
||||
public TestSubComponentRegistry(DataTypes dataTypes) : base(dataTypes)
|
||||
{
|
||||
RegisterSubComponent<TestSubComonent>("TestSubcomponent");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Registries;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Registries.Subcomponents;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.StructuredComponents;
|
||||
|
||||
public class StructuredComponentsHandler
|
||||
{
|
||||
private StructuredComponentRegistry ComponentRegistry { get; }
|
||||
|
||||
public StructuredComponentsHandler(
|
||||
int protocolVersion,
|
||||
DataTypes dataTypes)
|
||||
{
|
||||
// Get the appropriate subcomponent registry type based on the protocol version and then instantiate it
|
||||
var subcomponentRegistryType = protocolVersion switch
|
||||
{
|
||||
Protocol18Handler.MC_1_20_6_Version => typeof(TestSubComponentRegistry),
|
||||
_ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for subcomponent registries!")
|
||||
};
|
||||
|
||||
var subcomponentRegistry = Activator.CreateInstance(subcomponentRegistryType, dataTypes) as SubComponentRegistry
|
||||
?? throw new InvalidOperationException($"Failed to instantiate a component registry for type {nameof(subcomponentRegistryType)}");
|
||||
|
||||
// Get the appropriate component registry type based on the protocol version and then instantiate it
|
||||
var registryType = protocolVersion switch
|
||||
{
|
||||
Protocol18Handler.MC_1_20_6_Version => typeof(StructuredComponentsRegistry1206),
|
||||
_ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for structured component registries!")
|
||||
};
|
||||
|
||||
ComponentRegistry = Activator.CreateInstance(registryType, subcomponentRegistry, dataTypes) as StructuredComponentRegistry
|
||||
?? throw new InvalidOperationException($"Failed to instantiate a component registry for type {nameof(registryType)}");
|
||||
}
|
||||
|
||||
public StructuredComponent Parse(int componentId, Queue<byte> data)
|
||||
{
|
||||
return ComponentRegistry.ParseComponent(componentId, data);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue