diff --git a/Minecraft.Server.FourKit/Entity/Entity.cs b/Minecraft.Server.FourKit/Entity/Entity.cs
index 586194496..95f52d697 100644
--- a/Minecraft.Server.FourKit/Entity/Entity.cs
+++ b/Minecraft.Server.FourKit/Entity/Entity.cs
@@ -5,7 +5,7 @@ using Minecraft.Server.FourKit.Util;
///
/// Represents a base entity in the world
///
-public abstract class Entity
+public class Entity
{
private Location _location = new();
private Guid _uniqueId = Guid.NewGuid();
@@ -101,6 +101,70 @@ public abstract class Entity
NativeBridge.SetVelocity?.Invoke(getEntityId(), velocity.getX(), velocity.getY(), velocity.getZ());
}
+ ///
+ /// Returns whether this entity is inside a vehicle.
+ ///
+ /// true if the entity is in a vehicle.
+ public bool isInsideVehicle()
+ {
+ return (NativeBridge.GetVehicleId?.Invoke(getEntityId()) ?? -1) >= 0;
+ }
+
+ ///
+ /// Leave the current vehicle. If the entity is currently in a vehicle
+ /// (and is removed from it), true will be returned, otherwise
+ /// false will be returned.
+ ///
+ /// true if the entity was in a vehicle.
+ public bool leaveVehicle()
+ {
+ return NativeBridge.LeaveVehicle?.Invoke(getEntityId()) != 0;
+ }
+
+ ///
+ /// Get the vehicle that this entity is inside. If there is no vehicle,
+ /// null will be returned.
+ ///
+ /// The current vehicle, or null.
+ public Entity? getVehicle()
+ {
+ int vehicleId = NativeBridge.GetVehicleId?.Invoke(getEntityId()) ?? -1;
+ if (vehicleId < 0) return null;
+ return FourKit.GetEntityByEntityId(vehicleId);
+ }
+
+ ///
+ /// Eject any passenger.
+ ///
+ /// true if there was a passenger.
+ public bool eject()
+ {
+ return NativeBridge.Eject?.Invoke(getEntityId()) != 0;
+ }
+
+ ///
+ /// Gets the primary passenger of a vehicle. For vehicles that could
+ /// have multiple passengers, this will only return the primary passenger.
+ ///
+ /// The passenger entity, or null.
+ public Entity? getPassenger()
+ {
+ int passengerId = NativeBridge.GetPassengerId?.Invoke(getEntityId()) ?? -1;
+ if (passengerId < 0) return null;
+ return FourKit.GetEntityByEntityId(passengerId);
+ }
+
+ ///
+ /// Set the passenger of a vehicle.
+ ///
+ /// The new passenger.
+ /// false if it could not be done for whatever reason.
+ public bool setPassenger(Entity passenger)
+ {
+ if (passenger == null || NativeBridge.SetPassenger == null) return false;
+ return NativeBridge.SetPassenger(getEntityId(), passenger.getEntityId()) != 0;
+ }
+
// INTERNAL
internal void SetLocation(Location location)
{
diff --git a/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs b/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs
index 416f55f29..4d5873e6e 100644
--- a/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs
+++ b/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs
@@ -6,6 +6,7 @@ using Minecraft.Server.FourKit.Inventory;
///
/// An abstract base class for events that describe an interaction between a
/// and the contents of an .
+/// This is currently not emitted anywhere, use instead.
///
public abstract class InventoryInteractEvent : InventoryEvent, Cancellable
{
diff --git a/Minecraft.Server.FourKit/FourKit.cs b/Minecraft.Server.FourKit/FourKit.cs
index a47184aa2..2c3a11b7b 100644
--- a/Minecraft.Server.FourKit/FourKit.cs
+++ b/Minecraft.Server.FourKit/FourKit.cs
@@ -97,6 +97,41 @@ public static class FourKit
}
}
+ internal static Entity.Entity? GetEntityByEntityId(int entityId)
+ {
+ var player = GetPlayerByEntityId(entityId);
+ if (player != null) return player;
+
+ if (NativeBridge.GetEntityInfo == null) return null;
+
+ IntPtr buf = System.Runtime.InteropServices.Marshal.AllocHGlobal(5 * sizeof(double));
+ try
+ {
+ NativeBridge.GetEntityInfo(entityId, buf);
+ double[] data = new double[5];
+ System.Runtime.InteropServices.Marshal.Copy(buf, data, 0, 5);
+
+ int typeId = (int)data[0];
+ if (typeId < 0) return null;
+
+ var entityType = Enum.IsDefined(typeof(Entity.EntityType), typeId)
+ ? (Entity.EntityType)typeId
+ : Entity.EntityType.UNKNOWN;
+ int dimId = (int)data[4];
+
+ var entity = new Entity.Entity();
+ entity.SetEntityIdInternal(entityId);
+ entity.SetEntityTypeInternal(entityType);
+ entity.SetDimensionInternal(dimId);
+ entity.SetLocation(new Location(getWorld(dimId), data[1], data[2], data[3]));
+ return entity;
+ }
+ finally
+ {
+ System.Runtime.InteropServices.Marshal.FreeHGlobal(buf);
+ }
+ }
+
internal static Player TrackPlayer(int entityId, string name)
{
lock (_playerLock)
diff --git a/Minecraft.Server.FourKit/FourKitHost.cs b/Minecraft.Server.FourKit/FourKitHost.cs
index 45594f359..092ac480d 100644
--- a/Minecraft.Server.FourKit/FourKitHost.cs
+++ b/Minecraft.Server.FourKit/FourKitHost.cs
@@ -638,6 +638,19 @@ public static class FourKitHost
}
}
+ [UnmanagedCallersOnly]
+ public static void SetVehicleCallbacks(IntPtr setPassenger, IntPtr leaveVehicle, IntPtr eject, IntPtr getVehicleId, IntPtr getPassengerId, IntPtr getEntityInfo)
+ {
+ try
+ {
+ NativeBridge.SetVehicleCallbacks(setPassenger, leaveVehicle, eject, getVehicleId, getPassengerId, getEntityInfo);
+ }
+ catch (Exception ex)
+ {
+ ServerLog.Error("fourkit", $"SetVehicleCallbacks error: {ex}");
+ }
+ }
+
[UnmanagedCallersOnly]
public static long FirePlayerDropItem(int entityId, int itemId, int itemCount, int itemAux,
IntPtr outItemIdPtr, IntPtr outItemCountPtr, IntPtr outItemAuxPtr)
diff --git a/Minecraft.Server.FourKit/NativeBridge.cs b/Minecraft.Server.FourKit/NativeBridge.cs
index f415c6613..1ec577872 100644
--- a/Minecraft.Server.FourKit/NativeBridge.cs
+++ b/Minecraft.Server.FourKit/NativeBridge.cs
@@ -156,6 +156,24 @@ internal static class NativeBridge
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSpawnParticleDelegate(int entityId, int particleId, float x, float y, float z, float offsetX, float offsetY, float offsetZ, float speed, int count);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int NativeSetPassengerDelegate(int entityId, int passengerEntityId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int NativeLeaveVehicleDelegate(int entityId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int NativeEjectDelegate(int entityId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int NativeGetVehicleIdDelegate(int entityId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int NativeGetPassengerIdDelegate(int entityId);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void NativeGetEntityInfoDelegate(int entityId, IntPtr outBuf);
+
internal static NativeDamageDelegate? DamagePlayer;
internal static NativeSetHealthDelegate? SetPlayerHealth;
@@ -208,6 +226,12 @@ internal static class NativeBridge
internal static NativeSetSaturationDelegate? SetSaturation;
internal static NativeSetExhaustionDelegate? SetExhaustion;
internal static NativeSpawnParticleDelegate? SpawnParticle;
+ internal static NativeSetPassengerDelegate? SetPassenger;
+ internal static NativeLeaveVehicleDelegate? LeaveVehicle;
+ internal static NativeEjectDelegate? Eject;
+ internal static NativeGetVehicleIdDelegate? GetVehicleId;
+ internal static NativeGetPassengerIdDelegate? GetPassengerId;
+ internal static NativeGetEntityInfoDelegate? GetEntityInfo;
internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
{
@@ -286,4 +310,14 @@ internal static class NativeBridge
{
SpawnParticle = Marshal.GetDelegateForFunctionPointer(spawnParticle);
}
+
+ internal static void SetVehicleCallbacks(IntPtr setPassenger, IntPtr leaveVehicle, IntPtr eject, IntPtr getVehicleId, IntPtr getPassengerId, IntPtr getEntityInfo)
+ {
+ SetPassenger = Marshal.GetDelegateForFunctionPointer(setPassenger);
+ LeaveVehicle = Marshal.GetDelegateForFunctionPointer(leaveVehicle);
+ Eject = Marshal.GetDelegateForFunctionPointer(eject);
+ GetVehicleId = Marshal.GetDelegateForFunctionPointer(getVehicleId);
+ GetPassengerId = Marshal.GetDelegateForFunctionPointer(getPassengerId);
+ GetEntityInfo = Marshal.GetDelegateForFunctionPointer(getEntityInfo);
+ }
}
diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp
index 4cecf840e..02b68c527 100644
--- a/Minecraft.Server/FourKitBridge.cpp
+++ b/Minecraft.Server/FourKitBridge.cpp
@@ -1,4 +1,5 @@
// todo: split into files for better readability
+// todo: this needs to be made way more neat and less duplicate stuff
#include "FourKitBridge.h"
#include "Common/StringUtils.h"
@@ -40,6 +41,7 @@
#include "..\Minecraft.World\SetHealthPacket.h"
#include "..\Minecraft.World\LevelSoundPacket.h"
#include "..\Minecraft.World\LevelParticlesPacket.h"
+#include "..\Minecraft.World\SetEntityLinkPacket.h"
#include "..\Minecraft.World\SimpleContainer.h"
#include "..\Minecraft.World\Slot.h"
#include "..\Minecraft.World\Tile.h"
@@ -182,6 +184,7 @@ typedef void(__stdcall *fn_fire_bed_leave)(int entityId, int dimId, int bedX, in
typedef void(__stdcall *fn_set_entity_callbacks)(void *setSneaking, void *setVelocity, void *setAllowFlight, void *playSound, void *setSleepingIgnored);
typedef void(__stdcall *fn_set_experience_callbacks)(void *setLevel, void *setExp, void *giveExp, void *giveExpLevels, void *setFoodLevel, void *setSaturation, void *setExhaustion);
typedef void(__stdcall *fn_set_particle_callbacks)(void *spawnParticle);
+typedef void(__stdcall *fn_set_vehicle_callbacks)(void *setPassenger, void *leaveVehicle, void *eject, void *getVehicleId, void *getPassengerId, void *getEntityInfo);
struct OpenContainerInfo
{
@@ -225,6 +228,7 @@ static fn_fire_bed_leave s_managedFireBedLeave = nullptr;
static fn_set_entity_callbacks s_managedSetEntityCallbacks = nullptr;
static fn_set_experience_callbacks s_managedSetExperienceCallbacks = nullptr;
static fn_set_particle_callbacks s_managedSetParticleCallbacks = nullptr;
+static fn_set_vehicle_callbacks s_managedSetVehicleCallbacks = nullptr;
static bool s_initialized = false;
@@ -1445,6 +1449,76 @@ static void __cdecl NativeSpawnParticle(int entityId, int particleId, float x, f
player->connection->send(std::make_shared(std::wstring(buf), x, y, z, offsetX, offsetY, offsetZ, speed, count));
}
+static int __cdecl NativeSetPassenger(int entityId, int passengerEntityId)
+{
+ auto entity = FindEntity(entityId);
+ auto passenger = FindEntity(passengerEntityId);
+ if (!entity || !passenger) return 0;
+ passenger->ride(entity);
+ PlayerList *list = MinecraftServer::getPlayerList();
+ if (list)
+ list->broadcastAll(std::make_shared(SetEntityLinkPacket::RIDING, passenger, entity), entity->dimension);
+ return 1;
+}
+
+static int __cdecl NativeLeaveVehicle(int entityId)
+{
+ auto entity = FindEntity(entityId);
+ if (!entity || !entity->riding) return 0;
+ int dim = entity->riding->dimension;
+ entity->ride(nullptr);
+ PlayerList *list = MinecraftServer::getPlayerList();
+ if (list)
+ list->broadcastAll(std::make_shared(SetEntityLinkPacket::RIDING, entity, nullptr), dim);
+ return 1;
+}
+
+static int __cdecl NativeEject(int entityId)
+{
+ auto entity = FindEntity(entityId);
+ if (!entity) return 0;
+ auto riderPtr = entity->rider.lock();
+ if (!riderPtr) return 0;
+ riderPtr->ride(nullptr);
+ PlayerList *list = MinecraftServer::getPlayerList();
+ if (list)
+ list->broadcastAll(std::make_shared(SetEntityLinkPacket::RIDING, riderPtr, nullptr), entity->dimension);
+ return 1;
+}
+
+static int __cdecl NativeGetVehicleId(int entityId)
+{
+ auto entity = FindEntity(entityId);
+ if (!entity || !entity->riding) return -1;
+ return entity->riding->entityId;
+}
+
+static int __cdecl NativeGetPassengerId(int entityId)
+{
+ auto entity = FindEntity(entityId);
+ if (!entity) return -1;
+ auto riderPtr = entity->rider.lock();
+ if (!riderPtr) return -1;
+ return riderPtr->entityId;
+}
+
+static void __cdecl NativeGetEntityInfo(int entityId, double *outData)
+{
+ // this stinks iof shit and more shit
+ outData[0] = -1;
+ outData[1] = 0;
+ outData[2] = 0;
+ outData[3] = 0;
+ outData[4] = 0;
+ auto entity = FindEntity(entityId);
+ if (!entity) return;
+ outData[0] = (double)MapEntityType((int)entity->GetType());
+ outData[1] = entity->x;
+ outData[2] = entity->y;
+ outData[3] = entity->z;
+ outData[4] = (double)entity->dimension;
+}
+
static std::wstring FindNet10SystemRoot()
{
// overengineered
@@ -1690,6 +1764,7 @@ void Initialize()
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetEntityCallbacks", (void **)&s_managedSetEntityCallbacks);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetExperienceCallbacks", (void **)&s_managedSetExperienceCallbacks);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetParticleCallbacks", (void **)&s_managedSetParticleCallbacks);
+ ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetVehicleCallbacks", (void **)&s_managedSetVehicleCallbacks);
if (!ok)
{
@@ -1765,6 +1840,14 @@ void Initialize()
s_managedSetParticleCallbacks(
(void *)&NativeSpawnParticle);
+ s_managedSetVehicleCallbacks(
+ (void *)&NativeSetPassenger,
+ (void *)&NativeLeaveVehicle,
+ (void *)&NativeEject,
+ (void *)&NativeGetVehicleId,
+ (void *)&NativeGetPassengerId,
+ (void *)&NativeGetEntityInfo);
+
LogInfo("fourkit", "FourKit initialized successfully.");
}