diff --git a/Components/MineSharp.ChatComponent/Chat.cs b/Components/MineSharp.ChatComponent/Chat.cs
index d1b4046c..03165c63 100644
--- a/Components/MineSharp.ChatComponent/Chat.cs
+++ b/Components/MineSharp.ChatComponent/Chat.cs
@@ -3,6 +3,8 @@
using System.Text.RegularExpressions;
using MineSharp.Data;
using Newtonsoft.Json;
+using fNbt;
+using System.Diagnostics;
namespace MineSharp.ChatComponent;
@@ -16,12 +18,17 @@ namespace MineSharp.ChatComponent;
///
/// Represents a Chat Message object
///
-public class Chat
+public partial class Chat
{
///
/// The raw Json message
///
- public string Json { get; }
+ public string? Json { get; private set; }
+
+ ///
+ /// The raw NBT Tag message
+ ///
+ public NbtTag? NbtTag { get; private set; }
///
/// The message without any styling
@@ -36,6 +43,9 @@ public class Chat
private readonly MinecraftData data;
+ [GeneratedRegex(@"\\§[0-9a-fk-r]")]
+ private static partial Regex FormatTagRegex();
+
///
/// Create a new ChatComponent
///
@@ -49,7 +59,7 @@ public Chat(string json, MinecraftData data)
try
{
this.StyledMessage = this.ParseComponent(JToken.Parse(this.Json));
- this.Message = Regex.Replace(this.StyledMessage, "\\$[0-9a-fk-r]", "");
+ this.Message = FormatTagRegex().Replace(this.StyledMessage, "");
}
catch (JsonReaderException)
{
@@ -58,6 +68,27 @@ public Chat(string json, MinecraftData data)
}
}
+ ///
+ /// Create a new ChatComponent with nbt tag
+ ///
+ ///
+ ///
+ public Chat(NbtTag nbt, MinecraftData data)
+ {
+ this.NbtTag = nbt;
+ this.data = data;
+
+ try
+ {
+ this.StyledMessage = this.ParseComponent(nbt);
+ this.Message = Regex.Replace(this.StyledMessage, "\\§[0-9a-fk-r]", "");
+ } catch
+ {
+ this.StyledMessage = this.NbtTag.ToString();
+ this.Message = this.StyledMessage;
+ }
+ }
+
private string ParseComponent(JToken token, string styleCode = "")
{
return token.Type switch
@@ -70,6 +101,18 @@ private string ParseComponent(JToken token, string styleCode = "")
};
}
+ private string ParseComponent(NbtTag nbt, string styleCode = "")
+ {
+ return nbt.TagType switch
+ {
+ NbtTagType.List => ParseArray((NbtList)nbt, styleCode),
+ NbtTagType.Compound => ParseObject((NbtCompound) nbt, styleCode),
+ NbtTagType.String => nbt.StringValue,
+ NbtTagType.Int => nbt.StringValue,
+ _ => throw new Exception($"Type {nbt.TagType} is not supported")
+ }; ;
+ }
+
private string ParseObject(JObject jObject, string styleCode = "")
{
var sb = new StringBuilder();
@@ -125,6 +168,67 @@ private string ParseObject(JObject jObject, string styleCode = "")
+ sb.ToString();
}
+ private string ParseObject(NbtCompound nbt, string styleCode = "")
+ {
+ if (nbt.Names.First() == "")
+ {
+ return nbt[""].StringValue;
+ }
+
+ var sb = new StringBuilder();
+
+ var colorProp = nbt["color"];
+ if (colorProp != null)
+ {
+ var color = ParseComponent(colorProp.StringValue);
+ var style = TextStyle.GetTextStyle(color);
+ styleCode = style != null
+ ? style.ToString()
+ : string.Empty;
+ }
+
+ var extraProp = nbt["extra"];
+ if (extraProp != null)
+ {
+ var extras = (NbtList)extraProp!;
+
+ foreach (var item in extras)
+
+ sb.Append(this.ParseComponent(item, styleCode) + "§r");
+ }
+
+ var textProp = nbt["text"];
+ var translateProp = nbt["translate"];
+
+ if (textProp != null)
+ {
+ return styleCode + ParseComponent(textProp, styleCode) + sb.ToString();
+ }
+
+ if (translateProp == null)
+ return sb.ToString();
+
+ var usingData = new List();
+
+ var usingProp = nbt["using"];
+ var withProp = nbt["with"];
+ if (usingProp != null && withProp == null)
+ withProp = usingProp;
+
+ if (withProp != null)
+ {
+ var array = (NbtList)withProp;
+ for (int i = 0; i < array.Count; i++)
+ {
+ usingData.Add(this.ParseComponent(array[i], styleCode));
+ }
+ }
+
+ var ruleName = this.ParseComponent(translateProp);
+ return styleCode + TranslateString(ruleName, usingData.ToArray(), this.data)
+ + sb.ToString();
+ }
+
private string ParseArray(JArray jArray, string styleCode = "")
{
var sb = new StringBuilder();
@@ -136,6 +240,17 @@ private string ParseArray(JArray jArray, string styleCode = "")
return sb.ToString();
}
+ private string ParseArray(NbtList nbtList, string styleCode = "")
+ {
+ var sb = new StringBuilder();
+ foreach (var token in nbtList)
+ {
+ sb.Append(ParseComponent(token, styleCode));
+ }
+
+ return sb.ToString();
+ }
+
///
/// Translate a string using the given rule and format strings.
///
@@ -158,5 +273,5 @@ public static string TranslateString(string ruleName, string[] usings, Minecraft
}
///
- public override string ToString() => this.Json;
+ public override string ToString() => this.Json ?? this.NbtTag!.ToString();
}
diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs
new file mode 100644
index 00000000..1365db0a
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SetPassengersPacket.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MineSharp.Core.Common;
+using MineSharp.Data;
+using MineSharp.Data.Protocol;
+
+namespace MineSharp.Protocol.Packets.Clientbound.Play;
+
+#pragma warning disable CS1591
+public class SetPassengersPacket : IPacket
+{
+ public PacketType Type => PacketType.CB_Play_SetPassengers;
+
+ public int EntityId { get; set; }
+
+ public int[] PassengersId { get; set; }
+
+ public SetPassengersPacket(int entityId, int[] passengersId)
+ {
+ EntityId = entityId;
+ PassengersId = passengersId;
+ }
+
+ public void Write(PacketBuffer buffer, MinecraftData version)
+ {
+ buffer.WriteVarInt(this.EntityId);
+ buffer.WriteVarIntArray(this.PassengersId, (buf, elem) => buffer.WriteVarInt(elem));
+ }
+
+ public static IPacket Read(PacketBuffer buffer, MinecraftData version)
+ {
+ return new SetPassengersPacket(
+ buffer.ReadVarInt(),
+ buffer.ReadVarIntArray(buf => buf.ReadVarInt()));
+ }
+}
+#pragma warning restore CS1591
diff --git a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs
index 8e13f266..1191833f 100644
--- a/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs
+++ b/Components/MineSharp.Protocol/Packets/Clientbound/Play/SystemChatMessagePacket.cs
@@ -1,3 +1,4 @@
+using MineSharp.ChatComponent;
using MineSharp.Core.Common;
using MineSharp.Data;
using MineSharp.Data.Protocol;
@@ -9,6 +10,7 @@ public class SystemChatMessagePacket : IPacket
public PacketType Type => PacketType.CB_Play_SystemChat;
public string Content { get; set; }
+ public Chat? Message { get; set; }
public int? ChatType { get; set; }
public bool? IsOverlay { get; set; }
@@ -28,11 +30,19 @@ public SystemChatMessagePacket(string content, int chatType)
///
///
///
- public SystemChatMessagePacket(string content, bool isOverlay)
+ ///
+ public SystemChatMessagePacket(string content, bool isOverlay, Chat? message = null)
{
+ this.Message = message;
this.Content = content;
this.IsOverlay = isOverlay;
}
+ public SystemChatMessagePacket(Chat message, bool isOverlay)
+ {
+ this.Message = message;
+ this.Content = message.StyledMessage;
+ this.IsOverlay = isOverlay;
+ }
public void Write(PacketBuffer buffer, MinecraftData version)
{
@@ -45,11 +55,25 @@ public void Write(PacketBuffer buffer, MinecraftData version)
public static IPacket Read(PacketBuffer buffer, MinecraftData version)
{
- var content = buffer.ReadString();
- if (version.Version.Protocol >= ProtocolVersion.V_1_19_2)
- return new SystemChatMessagePacket(content, buffer.ReadBool());
+ if (version.Version.Protocol < ProtocolVersion.V_1_20_3)
+ {
+ var content = buffer.ReadString();
+ if (version.Version.Protocol >= ProtocolVersion.V_1_19_2)
+ return new SystemChatMessagePacket(content, buffer.ReadBool());
- return new SystemChatMessagePacket(content, buffer.ReadVarInt());
+ return new SystemChatMessagePacket(content, buffer.ReadVarInt());
+ } else
+ {
+ var content = buffer.ReadNbt();
+ try
+ {
+ var message = new Chat(content!, version);
+ return new SystemChatMessagePacket(message, buffer.ReadBool());
+ } catch
+ {
+ return new SystemChatMessagePacket(content!.ToString(), buffer.ReadBool());
+ }
+ }
}
}
#pragma warning restore CS1591
diff --git a/Components/MineSharp.Protocol/Packets/PacketPalette.cs b/Components/MineSharp.Protocol/Packets/PacketPalette.cs
index dc33f4df..eac037fa 100644
--- a/Components/MineSharp.Protocol/Packets/PacketPalette.cs
+++ b/Components/MineSharp.Protocol/Packets/PacketPalette.cs
@@ -151,7 +151,8 @@ private static void InitializePackets()
RegisterPacket(PacketType.CB_Play_ChunkBatchFinished);
RegisterPacket(PacketType.CB_Play_Ping);
RegisterPacket(PacketType.CB_Play_KickDisconnect);
-
+ RegisterPacket(PacketType.CB_Play_SetPassengers);
+
RegisterPacket(PacketType.SB_Play_KeepAlive);
RegisterPacket(PacketType.SB_Play_Position);
RegisterPacket(PacketType.SB_Play_PositionLook);
diff --git a/MineSharp.Bot/Plugins/ChatPlugin.cs b/MineSharp.Bot/Plugins/ChatPlugin.cs
index be142fc0..5e9a2ac1 100644
--- a/MineSharp.Bot/Plugins/ChatPlugin.cs
+++ b/MineSharp.Bot/Plugins/ChatPlugin.cs
@@ -1,4 +1,4 @@
-using MineSharp.Bot.Chat;
+using MineSharp.Bot.Chat;
using MineSharp.Commands;
using MineSharp.Core.Common;
using MineSharp.Protocol.Packets.Clientbound.Play;
@@ -469,7 +469,7 @@ private Task HandleChatMessagePacket(PlayerChatPacket packet)
(UUID sender, string message, int type) = packet.Body switch
{
PlayerChatPacket.V1_19Body v19 => (v19.Sender, v19.SignedChat, v19.MessageType),
- PlayerChatPacket.V1_19_2_3Body v19_2 => (v19_2.Sender, v19_2.PlainMessage, v19_2.Type),
+ PlayerChatPacket.V1_19_2_3Body v19_2 => (v19_2.Sender, v19_2.UnsignedContent ?? v19_2.FormattedMessage ?? v19_2.PlainMessage, v19_2.Type),
_ => throw new UnreachableException()
};
@@ -497,8 +497,14 @@ private Task HandleSystemChatMessage(SystemChatMessagePacket packet)
? ChatMessageType.GameInfo
: ChatMessageType.SystemMessage;
}
-
- this.HandleChatInternal(null, packet.Content, type);
+ if (packet.Message != null)
+ {
+ this.HandleChatInternal(null, packet.Message, type);
+ }
+ else
+ {
+ this.HandleChatInternal(null, packet.Content, type);
+ }
return Task.CompletedTask;
}
@@ -528,6 +534,11 @@ private void HandleChatInternal(UUID? sender, string message, ChatMessageType ty
this.OnChatMessageReceived?.Invoke(this.Bot, sender, new ChatComponent.Chat(message, this.Bot.Data), type, senderName);
}
+ private void HandleChatInternal(UUID? sender, ChatComponent.Chat message, ChatMessageType type, string? senderName = null)
+ {
+ this.OnChatMessageReceived?.Invoke(this.Bot, sender, message, type, senderName);
+ }
+
private ChatMessageType GetChatMessageTypeFromRegistry(int index)
{
var val = this.Bot.Registry["minecraft:chat_type"]["value"][index]["name"]!.StringValue!;
diff --git a/MineSharp.Bot/Plugins/EntityPlugin.cs b/MineSharp.Bot/Plugins/EntityPlugin.cs
index d70bd231..3f3bfe80 100644
--- a/MineSharp.Bot/Plugins/EntityPlugin.cs
+++ b/MineSharp.Bot/Plugins/EntityPlugin.cs
@@ -1,4 +1,4 @@
-using MineSharp.Bot.Utils;
+using MineSharp.Bot.Utils;
using MineSharp.Core.Common.Effects;
using MineSharp.Core.Common.Entities;
using MineSharp.Protocol.Packets.Clientbound.Play;
@@ -53,6 +53,7 @@ public EntityPlugin(MineSharpBot bot) : base(bot)
this.Bot.Client.On(this.HandleTeleportEntityPacket);
this.Bot.Client.On(this.HandleUpdateAttributesPacket);
this.Bot.Client.On(this.HandleSynchronizePlayerPosition);
+ this.Bot.Client.On(this.HandleSetPassengersPacket);
}
///
@@ -72,6 +73,36 @@ internal void AddEntity(Entity entity)
}
}
+ private void MountEntity(Entity? vehicle, Entity passenger)
+ {
+ DismountEntity(passenger);
+
+ passenger.Vehicle = vehicle;
+ if (vehicle != null)
+ {
+ vehicle.Passengers.Add(passenger);
+ }
+ }
+
+ private void DismountEntity(Entity passenger)
+ {
+ Entity? originalVehicle = passenger.Vehicle;
+ if (originalVehicle != null)
+ {
+ originalVehicle.Passengers.Remove(passenger);
+ }
+ passenger.Vehicle = null;
+ }
+
+ private void DismountPassengers(Entity vehicle)
+ {
+ foreach (var passenger in vehicle.Passengers)
+ {
+ DismountEntity(passenger);
+ }
+ vehicle.Passengers.Clear();
+ }
+
private Task HandleSpawnLivingEntityPacket(SpawnLivingEntityPacket packet)
{
if (!this.IsEnabled)
@@ -126,6 +157,7 @@ private Task HandleRemoveEntitiesPacket(RemoveEntitiesPacket packet)
if (!this.Entities.Remove(entityId, out var entity))
continue;
+ DismountPassengers(entity);
this.OnEntityDespawned?.Invoke(this.Bot, entity);
}
@@ -142,7 +174,7 @@ private Task HandleSetEntityVelocityPacket(SetEntityVelocityPacket packet)
return Task.CompletedTask;
}
- (entity.Position as MutableVector3)!.Set(
+ (entity.Position as MutableVector3)!.Add(
NetUtils.ConvertToVelocity(packet.VelocityX),
NetUtils.ConvertToVelocity(packet.VelocityY),
NetUtils.ConvertToVelocity(packet.VelocityZ)
@@ -159,7 +191,7 @@ private Task HandleUpdateEntityPositionPacket(EntityPositionPacket packet)
if (!this.Entities.TryGetValue(packet.EntityId, out var entity))
return Task.CompletedTask;
- (entity.Position as MutableVector3)!.Add(
+ (entity.Position as MutableVector3)!.Set(
NetUtils.ConvertDeltaPosition(packet.DeltaX),
NetUtils.ConvertDeltaPosition(packet.DeltaY),
NetUtils.ConvertDeltaPosition(packet.DeltaZ)
@@ -284,4 +316,23 @@ private async Task HandleSynchronizePlayerPosition(PlayerPositionPacket packet)
await this.Bot.Client.SendPacket(new ConfirmTeleportPacket(packet.TeleportId));
}
+
+ private Task HandleSetPassengersPacket(SetPassengersPacket packet)
+ {
+ if (packet.EntityId != -1 || !this.Entities.TryGetValue(packet.EntityId, out var vehicle))
+ {
+ return Task.CompletedTask;
+ }
+ vehicle = packet.EntityId == -1 ? null : vehicle;
+
+ foreach (int passengerId in packet.PassengersId)
+ {
+ if (!this.Entities.TryGetValue(packet.EntityId, out var passenger))
+ {
+ continue;
+ }
+ MountEntity(vehicle, passenger);
+ }
+ return Task.CompletedTask;
+ }
}
diff --git a/MineSharp.Core/Common/Entities/Entity.cs b/MineSharp.Core/Common/Entities/Entity.cs
index 31480ae4..a83c4ffc 100644
--- a/MineSharp.Core/Common/Entities/Entity.cs
+++ b/MineSharp.Core/Common/Entities/Entity.cs
@@ -1,4 +1,4 @@
-using MineSharp.Core.Common.Effects;
+using MineSharp.Core.Common.Effects;
using System.Collections.Concurrent;
using MineSharp.Core.Geometry;
using Attribute = MineSharp.Core.Common.Entities.Attributes.Attribute;
@@ -76,6 +76,10 @@ public class Entity(
///
public Entity? Vehicle { get; set; } = null;
+ ///
+ /// The Passengers of an entity (for example a boat can takes 2 passengers)
+ ///
+ public List Passengers = [];
///
/// Returns the attribute with the given name.