feat(api): expand block/item hooks and debug tools

This commit is contained in:
Jacobwasbeast
2026-03-12 20:25:45 -05:00
parent 24dbf8b96b
commit faada7fbc4
12 changed files with 2883 additions and 31 deletions

View File

@@ -16,12 +16,14 @@ public class ExampleMod : IMod
public static RegisteredBlock? RubyWoodPlanks;
public static RegisteredBlock? RubyChair;
public static RegisteredBlock? RubySand;
public static RegisteredBlock? DebugHooksBlock;
public static RegisteredSlabBlock? RubyStoneSlab;
public static RegisteredSlabBlock? RubyWoodSlab;
public static RegisteredBlock? RubyLamp;
public static RegisteredBlock? RubyLampLit;
public static RegisteredBlock? Orichalcum;
public static RegisteredItem? Ruby;
public static RegisteredItem? DebugHooksItemEntry;
public static RegisteredItem? RubyPickaxeItem;
public static RegisteredItem? RubyShovelItem;
public static RegisteredItem? RubyHoeItem;
@@ -37,9 +39,6 @@ public class ExampleMod : IMod
public override UseItemResult OnUseItem(UseItemContext context)
{
if (context.IsTestUseOnly)
return UseItemResult.CancelVanilla;
long now = Environment.TickCount64;
ref long nextUseAtMs = ref context.IsClientSide ? ref _nextClientUseAtMs : ref _nextServerUseAtMs;
if (now < nextUseAtMs)
@@ -190,6 +189,153 @@ public class ExampleMod : IMod
}
}
private sealed class DebugHooks : WeaveLoader.API.Block.Block
{
private static void Log(string hook, BlockUpdateContext block, string? extra = null)
{
if (string.IsNullOrWhiteSpace(extra))
Logger.Info($"DebugHooks::{hook} at ({block.X}, {block.Y}, {block.Z}) blockId={block.BlockId} client={block.IsClientSide}");
else
Logger.Info($"DebugHooks::{hook} at ({block.X}, {block.Y}, {block.Z}) blockId={block.BlockId} client={block.IsClientSide} {extra}");
}
public override void OnPlace(BlockUpdateContext context)
{
Log("OnPlace", context);
}
public override void OnNeighborChanged(BlockNeighborChangedContext context)
{
Log("OnNeighborChanged", context.Block, $"neighborId={context.NeighborBlockId}");
}
public override void OnScheduledTick(BlockTickContext context)
{
Log("OnScheduledTick", context.Block);
}
public override BlockActionResult OnUse(BlockUseContext context)
{
Log("OnUse", context.Block,
$"face={context.Face} click=({context.ClickX:0.00},{context.ClickY:0.00},{context.ClickZ:0.00}) soundOnly={context.SoundOnly}");
return BlockActionResult.ContinueVanilla;
}
public override void OnStepOn(BlockEntityContext context)
{
Log("OnStepOn", context.Block, $"entityPtr=0x{context.NativeEntityPtr.ToString("X")}");
}
public override void OnEntityInsideTile(BlockEntityContext context)
{
Log("OnEntityInsideTile", context.Block, $"entityPtr=0x{context.NativeEntityPtr.ToString("X")}");
}
public override void OnFallOn(BlockFallContext context)
{
Log("OnFallOn", context.Block, $"entityPtr=0x{context.NativeEntityPtr.ToString("X")} fall={context.FallDistance:0.00}");
}
public override void OnRemoving(BlockRemovingContext context)
{
Log("OnRemoving", context.Block, $"blockData={context.BlockData}");
}
public override void OnRemoved(BlockRemoveContext context)
{
Log("OnRemoved", context.Block, $"removedId={context.RemovedBlockId} removedData={context.RemovedBlockData}");
}
public override void OnDestroyed(BlockDestroyContext context)
{
Log("OnDestroyed", context.Block, $"blockData={context.BlockData}");
}
public override void OnPlayerDestroy(BlockPlayerDestroyContext context)
{
Log("OnPlayerDestroy", context.Block,
$"playerPtr=0x{context.NativePlayerPtr.ToString("X")} blockData={context.BlockData}");
}
public override void OnPlayerWillDestroy(BlockPlayerWillDestroyContext context)
{
Log("OnPlayerWillDestroy", context.Block,
$"playerPtr=0x{context.NativePlayerPtr.ToString("X")} blockData={context.BlockData}");
}
public override void OnPlacedBy(BlockPlacedByContext context)
{
Log("OnPlacedBy", context.Block,
$"placerPtr=0x{context.NativePlacerPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
}
}
private sealed class DebugHooksItem : Item
{
private static void Log(string hook, string? extra = null)
{
if (string.IsNullOrWhiteSpace(extra))
Logger.Info($"DebugItem::{hook}");
else
Logger.Info($"DebugItem::{hook} {extra}");
}
public override MineBlockResult OnMineBlock(MineBlockContext context)
{
Log("OnMineBlock", $"itemId={context.ItemId} tileId={context.TileId} pos=({context.X},{context.Y},{context.Z})");
return MineBlockResult.ContinueVanilla;
}
public override UseItemResult OnUseItem(UseItemContext context)
{
Log("OnUseItem",
$"itemId={context.ItemId} client={context.IsClientSide} " +
$"itemPtr=0x{context.NativeItemInstancePtr.ToString("X")} playerPtr=0x{context.NativePlayerPtr.ToString("X")}");
return UseItemResult.ContinueVanilla;
}
public override ItemActionResult OnUseOn(UseOnItemContext context)
{
Log("OnUseOn",
$"itemId={context.ItemId} client={context.IsClientSide} " +
$"pos=({context.X},{context.Y},{context.Z}) face={context.Face} " +
$"click=({context.ClickX:0.00},{context.ClickY:0.00},{context.ClickZ:0.00}) " +
$"playerPtr=0x{context.NativePlayerPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
return ItemActionResult.ContinueVanilla;
}
public override ItemActionResult OnInteractEntity(ItemEntityInteractionContext context)
{
Log("OnInteractEntity",
$"itemId={context.ItemId} playerPtr=0x{context.NativePlayerPtr.ToString("X")} " +
$"targetPtr=0x{context.NativeTargetEntityPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
return ItemActionResult.ContinueVanilla;
}
public override ItemActionResult OnHurtEntity(ItemEntityInteractionContext context)
{
Log("OnHurtEntity",
$"itemId={context.ItemId} playerPtr=0x{context.NativePlayerPtr.ToString("X")} " +
$"targetPtr=0x{context.NativeTargetEntityPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
return ItemActionResult.ContinueVanilla;
}
public override void OnInventoryTick(ItemInventoryTickContext context)
{
Log("OnInventoryTick",
$"itemId={context.ItemId} slot={context.Slot} selected={context.IsSelected} client={context.IsClientSide} " +
$"ownerPtr=0x{context.NativeOwnerEntityPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
}
public override void OnCraftedBy(ItemCraftedByContext context)
{
Log("OnCraftedBy",
$"itemId={context.ItemId} amount={context.Amount} client={context.IsClientSide} " +
$"playerPtr=0x{context.NativePlayerPtr.ToString("X")} itemPtr=0x{context.NativeItemInstancePtr.ToString("X")}");
}
}
public void OnInitialize()
{
GameEvents.OnWorldLoaded += (_, e) =>
@@ -262,6 +408,17 @@ public class ExampleMod : IMod
.InCreativeTab(CreativeTab.BuildingBlocks)
.Prepend());
DebugHooksBlock = Registry.Block.Register("examplemod:debug_hooks",
new DebugHooks(),
new BlockProperties()
.Material(MaterialType.Stone)
.Hardness(1.0f)
.Resistance(5.0f)
.Sound(SoundType.Stone)
.Icon("examplemod:block/ruby_stone")
.Name(Text.Translatable("block.examplemod.debug_hooks"))
.InCreativeTab(CreativeTab.BuildingBlocks));
RubyStoneSlab = (RegisteredSlabBlock)Registry.Block.Register("examplemod:ruby_stone_slab",
new SlabBlock(),
new BlockProperties()
@@ -336,6 +493,14 @@ public class ExampleMod : IMod
.Name(Text.Translatable("item.examplemod.ruby"))
.InCreativeTab(CreativeTab.Materials));
DebugHooksItemEntry = Registry.Item.Register("examplemod:debug_item",
new DebugHooksItem(),
new ItemProperties()
.MaxStackSize(1)
.Icon("examplemod:item/ruby")
.Name(Text.Translatable("item.examplemod.debug_item"))
.InCreativeTab(CreativeTab.ToolsAndWeapons));
Registry.Item.RegisterToolMaterial("examplemod:ruby_material",
new ToolMaterialDefinition()
.BaseTier(ToolTier.Diamond)

View File

@@ -11,7 +11,9 @@ block.examplemod.ruby_wood_slab_double=Ruby Wood Slab
block.examplemod.ruby_lamp=Ruby Lamp
block.examplemod.ruby_lamp_lit=Ruby Lamp
block.examplemod.orichalcum_ore=Orichalcum Ore
block.examplemod.debug_hooks=Debug Hooks Block
item.examplemod.ruby=Ruby
item.examplemod.debug_item=Debug Hooks Item
item.examplemod.ruby_sword=Ruby Sword
item.examplemod.ruby_shovel=Ruby Shovel
item.examplemod.ruby_pickaxe=Ruby Pickaxe

View File

@@ -2,35 +2,139 @@ using System.Runtime.InteropServices;
namespace WeaveLoader.API.Block;
/// <summary>
/// Base class for managed custom blocks.
/// Mods can inherit and override callbacks for block behavior.
/// </summary>
public abstract class Block
{
/// <summary>The namespaced ID used during registration.</summary>
public Identifier? Id { get; internal set; }
/// <summary>The numeric runtime ID allocated by the game.</summary>
public int NumericId { get; internal set; } = -1;
/// <summary>Optional override for the block to drop when broken.</summary>
public Identifier? DropAsBlockId { get; init; }
/// <summary>Optional override for the block to clone via pick-block.</summary>
public Identifier? CloneAsBlockId { get; init; }
/// <summary>
/// Called when the block is placed in the world.
/// </summary>
public virtual void OnPlace(BlockUpdateContext context)
{
}
/// <summary>
/// Called when a neighboring block changes.
/// </summary>
public virtual void OnNeighborChanged(BlockNeighborChangedContext context)
{
}
/// <summary>
/// Called when a scheduled tick for this block fires.
/// </summary>
public virtual void OnScheduledTick(BlockTickContext context)
{
}
/// <summary>
/// Called when the block is used (right-click).
/// Return <see cref="BlockActionResult.ContinueVanilla"/> to run vanilla logic,
/// or <see cref="BlockActionResult.CancelVanilla"/> to skip vanilla handling.
/// </summary>
public virtual BlockActionResult OnUse(BlockUseContext context) => BlockActionResult.ContinueVanilla;
/// <summary>
/// Called when an entity steps on the block.
/// </summary>
public virtual void OnStepOn(BlockEntityContext context)
{
}
/// <summary>
/// Called when an entity is inside the block's volume.
/// </summary>
public virtual void OnEntityInsideTile(BlockEntityContext context)
{
}
/// <summary>
/// Called when an entity falls onto the block.
/// </summary>
public virtual void OnFallOn(BlockFallContext context)
{
}
/// <summary>
/// Called before the block is removed from the world.
/// </summary>
public virtual void OnRemoving(BlockRemovingContext context)
{
}
/// <summary>
/// Called after the block is removed from the world.
/// </summary>
public virtual void OnRemoved(BlockRemoveContext context)
{
}
/// <summary>
/// Called when the block is destroyed by the world (non-player).
/// </summary>
public virtual void OnDestroyed(BlockDestroyContext context)
{
}
/// <summary>
/// Called when the block is destroyed by a player.
/// </summary>
public virtual void OnPlayerDestroy(BlockPlayerDestroyContext context)
{
}
/// <summary>
/// Called just before a player destroys the block.
/// </summary>
public virtual void OnPlayerWillDestroy(BlockPlayerWillDestroyContext context)
{
}
/// <summary>
/// Called when the block is placed by a player with an item.
/// </summary>
public virtual void OnPlacedBy(BlockPlacedByContext context)
{
}
}
/// <summary>Managed falling block base class.</summary>
public class FallingBlock : Block
{
}
/// <summary>Managed slab block base class.</summary>
public class SlabBlock : Block
{
}
/// <summary>
/// Result of managed block action callbacks.
/// </summary>
public enum BlockActionResult
{
ContinueVanilla = 0,
CancelVanilla = 1
}
/// <summary>
/// Runtime context for block update callbacks.
/// </summary>
public readonly struct BlockUpdateContext
{
public int BlockId { get; }
@@ -58,6 +162,9 @@ public readonly struct BlockUpdateContext
return NativeInterop.native_level_has_neighbor_signal(NativeLevelPtr, X, Y, Z) != 0;
}
/// <summary>
/// Set the block at this context's position.
/// </summary>
public bool SetBlock(Identifier id, int data = 0, int flags = 2)
{
int numericId = IdHelper.GetBlockNumericId(id);
@@ -67,6 +174,9 @@ public readonly struct BlockUpdateContext
return SetBlock(numericId, data, flags);
}
/// <summary>
/// Set the block at this context's position using a numeric ID.
/// </summary>
public bool SetBlock(int numericBlockId, int data = 0, int flags = 2)
{
if (NativeLevelPtr == 0 || numericBlockId < 0)
@@ -75,6 +185,9 @@ public readonly struct BlockUpdateContext
return NativeInterop.native_level_set_tile(NativeLevelPtr, X, Y, Z, numericBlockId, data, flags) != 0;
}
/// <summary>
/// Schedule a tick for this block after a delay (in ticks).
/// </summary>
public bool ScheduleTick(int delay)
{
if (NativeLevelPtr == 0 || delay < 0)
@@ -83,6 +196,9 @@ public readonly struct BlockUpdateContext
return NativeInterop.native_level_schedule_tick(NativeLevelPtr, X, Y, Z, BlockId, delay) != 0;
}
/// <summary>
/// Read a block ID relative to this context's position.
/// </summary>
public int GetBlockId(int offsetX = 0, int offsetY = 0, int offsetZ = 0)
{
if (NativeLevelPtr == 0)
@@ -92,6 +208,9 @@ public readonly struct BlockUpdateContext
}
}
/// <summary>
/// Runtime context for block neighbor-change callback.
/// </summary>
public readonly struct BlockNeighborChangedContext
{
public BlockUpdateContext Block { get; }
@@ -104,6 +223,9 @@ public readonly struct BlockNeighborChangedContext
}
}
/// <summary>
/// Runtime context for scheduled tick callback.
/// </summary>
public readonly struct BlockTickContext
{
public BlockUpdateContext Block { get; }
@@ -114,6 +236,161 @@ public readonly struct BlockTickContext
}
}
/// <summary>
/// Runtime context for block use callback.
/// </summary>
public readonly struct BlockUseContext
{
public BlockUpdateContext Block { get; }
public nint NativePlayerPtr { get; }
public int Face { get; }
public float ClickX { get; }
public float ClickY { get; }
public float ClickZ { get; }
public bool SoundOnly { get; }
internal BlockUseContext(BlockUpdateContext block, nint nativePlayerPtr, int face, float clickX, float clickY, float clickZ, bool soundOnly)
{
Block = block;
NativePlayerPtr = nativePlayerPtr;
Face = face;
ClickX = clickX;
ClickY = clickY;
ClickZ = clickZ;
SoundOnly = soundOnly;
}
}
/// <summary>
/// Runtime context for entity-on-block callbacks.
/// </summary>
public readonly struct BlockEntityContext
{
public BlockUpdateContext Block { get; }
public nint NativeEntityPtr { get; }
internal BlockEntityContext(BlockUpdateContext block, nint nativeEntityPtr)
{
Block = block;
NativeEntityPtr = nativeEntityPtr;
}
}
/// <summary>
/// Runtime context for block fall-on callback.
/// </summary>
public readonly struct BlockFallContext
{
public BlockUpdateContext Block { get; }
public nint NativeEntityPtr { get; }
public float FallDistance { get; }
internal BlockFallContext(BlockUpdateContext block, nint nativeEntityPtr, float fallDistance)
{
Block = block;
NativeEntityPtr = nativeEntityPtr;
FallDistance = fallDistance;
}
}
/// <summary>
/// Runtime context for block removing callback.
/// </summary>
public readonly struct BlockRemovingContext
{
public BlockUpdateContext Block { get; }
public int BlockData { get; }
internal BlockRemovingContext(BlockUpdateContext block, int blockData)
{
Block = block;
BlockData = blockData;
}
}
/// <summary>
/// Runtime context for block removed callback.
/// </summary>
public readonly struct BlockRemoveContext
{
public BlockUpdateContext Block { get; }
public int RemovedBlockId { get; }
public int RemovedBlockData { get; }
internal BlockRemoveContext(BlockUpdateContext block, int removedBlockId, int removedBlockData)
{
Block = block;
RemovedBlockId = removedBlockId;
RemovedBlockData = removedBlockData;
}
}
/// <summary>
/// Runtime context for block destroyed callback.
/// </summary>
public readonly struct BlockDestroyContext
{
public BlockUpdateContext Block { get; }
public int BlockData { get; }
internal BlockDestroyContext(BlockUpdateContext block, int blockData)
{
Block = block;
BlockData = blockData;
}
}
/// <summary>
/// Runtime context for player-destroyed callback.
/// </summary>
public readonly struct BlockPlayerDestroyContext
{
public BlockUpdateContext Block { get; }
public nint NativePlayerPtr { get; }
public int BlockData { get; }
internal BlockPlayerDestroyContext(BlockUpdateContext block, nint nativePlayerPtr, int blockData)
{
Block = block;
NativePlayerPtr = nativePlayerPtr;
BlockData = blockData;
}
}
/// <summary>
/// Runtime context for player-will-destroy callback.
/// </summary>
public readonly struct BlockPlayerWillDestroyContext
{
public BlockUpdateContext Block { get; }
public nint NativePlayerPtr { get; }
public int BlockData { get; }
internal BlockPlayerWillDestroyContext(BlockUpdateContext block, nint nativePlayerPtr, int blockData)
{
Block = block;
NativePlayerPtr = nativePlayerPtr;
BlockData = blockData;
}
}
/// <summary>
/// Runtime context for block placed-by callback.
/// </summary>
public readonly struct BlockPlacedByContext
{
public BlockUpdateContext Block { get; }
public nint NativePlacerPtr { get; }
public nint NativeItemInstancePtr { get; }
internal BlockPlacedByContext(BlockUpdateContext block, nint nativePlacerPtr, nint nativeItemInstancePtr)
{
Block = block;
NativePlacerPtr = nativePlacerPtr;
NativeItemInstancePtr = nativeItemInstancePtr;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockUpdateNativeArgs
{
@@ -132,6 +409,79 @@ internal struct BlockNeighborChangedNativeArgs
public int NeighborBlockId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockUseNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint PlayerPtr;
public int Face;
public float ClickX;
public float ClickY;
public float ClickZ;
public int SoundOnly;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockEntityNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint EntityPtr;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockFallNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint EntityPtr;
public float FallDistance;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockRemovingNativeArgs
{
public BlockUpdateNativeArgs Block;
public int BlockData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockRemoveNativeArgs
{
public BlockUpdateNativeArgs Block;
public int RemovedBlockId;
public int RemovedBlockData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockDestroyNativeArgs
{
public BlockUpdateNativeArgs Block;
public int BlockData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockPlayerDestroyNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint PlayerPtr;
public int BlockData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockPlayerWillDestroyNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint PlayerPtr;
public int BlockData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BlockPlacedByNativeArgs
{
public BlockUpdateNativeArgs Block;
public nint PlacerPtr;
public nint ItemInstancePtr;
}
internal static class ManagedBlockDispatcher
{
private static readonly object s_lock = new();
@@ -223,4 +573,275 @@ internal static class ManagedBlockDispatcher
nativeArgs.Z)));
return 1;
}
internal static int HandleUse(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockUseNativeArgs>())
return 0;
BlockUseNativeArgs nativeArgs = Marshal.PtrToStructure<BlockUseNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
var result = block.OnUse(new BlockUseContext(
update,
nativeArgs.PlayerPtr,
nativeArgs.Face,
nativeArgs.ClickX,
nativeArgs.ClickY,
nativeArgs.ClickZ,
nativeArgs.SoundOnly != 0));
return result == BlockActionResult.CancelVanilla ? 2 : 1;
}
internal static int HandleStepOn(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockEntityNativeArgs>())
return 0;
BlockEntityNativeArgs nativeArgs = Marshal.PtrToStructure<BlockEntityNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnStepOn(new BlockEntityContext(update, nativeArgs.EntityPtr));
return 1;
}
internal static int HandleEntityInsideTile(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockEntityNativeArgs>())
return 0;
BlockEntityNativeArgs nativeArgs = Marshal.PtrToStructure<BlockEntityNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnEntityInsideTile(new BlockEntityContext(update, nativeArgs.EntityPtr));
return 1;
}
internal static int HandleFallOn(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockFallNativeArgs>())
return 0;
BlockFallNativeArgs nativeArgs = Marshal.PtrToStructure<BlockFallNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnFallOn(new BlockFallContext(update, nativeArgs.EntityPtr, nativeArgs.FallDistance));
return 1;
}
internal static int HandleRemoving(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockRemovingNativeArgs>())
return 0;
BlockRemovingNativeArgs nativeArgs = Marshal.PtrToStructure<BlockRemovingNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnRemoving(new BlockRemovingContext(update, nativeArgs.BlockData));
return 1;
}
internal static int HandleRemoved(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockRemoveNativeArgs>())
return 0;
BlockRemoveNativeArgs nativeArgs = Marshal.PtrToStructure<BlockRemoveNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnRemoved(new BlockRemoveContext(update, nativeArgs.RemovedBlockId, nativeArgs.RemovedBlockData));
return 1;
}
internal static int HandleDestroyed(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockDestroyNativeArgs>())
return 0;
BlockDestroyNativeArgs nativeArgs = Marshal.PtrToStructure<BlockDestroyNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnDestroyed(new BlockDestroyContext(update, nativeArgs.BlockData));
return 1;
}
internal static int HandlePlayerDestroy(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockPlayerDestroyNativeArgs>())
return 0;
BlockPlayerDestroyNativeArgs nativeArgs = Marshal.PtrToStructure<BlockPlayerDestroyNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnPlayerDestroy(new BlockPlayerDestroyContext(update, nativeArgs.PlayerPtr, nativeArgs.BlockData));
return 1;
}
internal static int HandlePlayerWillDestroy(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockPlayerWillDestroyNativeArgs>())
return 0;
BlockPlayerWillDestroyNativeArgs nativeArgs = Marshal.PtrToStructure<BlockPlayerWillDestroyNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnPlayerWillDestroy(new BlockPlayerWillDestroyContext(update, nativeArgs.PlayerPtr, nativeArgs.BlockData));
return 1;
}
internal static int HandlePlacedBy(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<BlockPlacedByNativeArgs>())
return 0;
BlockPlacedByNativeArgs nativeArgs = Marshal.PtrToStructure<BlockPlacedByNativeArgs>(args);
Block? block;
lock (s_lock)
{
s_blocks.TryGetValue(nativeArgs.Block.BlockId, out block);
}
if (block == null)
return 0;
var update = new BlockUpdateContext(
nativeArgs.Block.BlockId,
nativeArgs.Block.IsClientSide != 0,
nativeArgs.Block.LevelPtr,
nativeArgs.Block.X,
nativeArgs.Block.Y,
nativeArgs.Block.Z);
block.OnPlacedBy(new BlockPlacedByContext(update, nativeArgs.PlacerPtr, nativeArgs.ItemInstancePtr));
return 1;
}
}

View File

@@ -27,6 +27,41 @@ public abstract class Item
/// or <see cref="UseItemResult.CancelVanilla"/> to skip vanilla handling.
/// </summary>
public virtual UseItemResult OnUseItem(UseItemContext context) => UseItemResult.ContinueVanilla;
/// <summary>
/// Called when this item is used on a block.
/// Return <see cref="ItemActionResult.ContinueVanilla"/> to run vanilla logic,
/// or <see cref="ItemActionResult.CancelVanilla"/> to skip vanilla handling.
/// </summary>
public virtual ItemActionResult OnUseOn(UseOnItemContext context) => ItemActionResult.ContinueVanilla;
/// <summary>
/// Called when this item interacts with an entity (right-click interaction).
/// Return <see cref="ItemActionResult.ContinueVanilla"/> to run vanilla logic,
/// or <see cref="ItemActionResult.CancelVanilla"/> to skip vanilla handling.
/// </summary>
public virtual ItemActionResult OnInteractEntity(ItemEntityInteractionContext context) => ItemActionResult.ContinueVanilla;
/// <summary>
/// Called when this item hurts an entity.
/// Return <see cref="ItemActionResult.ContinueVanilla"/> to run vanilla logic,
/// or <see cref="ItemActionResult.CancelVanilla"/> to skip vanilla handling.
/// </summary>
public virtual ItemActionResult OnHurtEntity(ItemEntityInteractionContext context) => ItemActionResult.ContinueVanilla;
/// <summary>
/// Called each tick while this item is in an inventory.
/// </summary>
public virtual void OnInventoryTick(ItemInventoryTickContext context)
{
}
/// <summary>
/// Called when this item is crafted by a player.
/// </summary>
public virtual void OnCraftedBy(ItemCraftedByContext context)
{
}
}
/// <summary>
@@ -47,6 +82,15 @@ public enum UseItemResult
CancelVanilla = 1
}
/// <summary>
/// Result of managed item action callbacks.
/// </summary>
public enum ItemActionResult
{
ContinueVanilla = 0,
CancelVanilla = 1
}
/// <summary>
/// Tool tier used by native tool constructors.
/// </summary>
@@ -117,16 +161,14 @@ public readonly struct MineBlockContext
public readonly struct UseItemContext
{
public int ItemId { get; }
public bool IsTestUseOnly { get; }
public bool IsClientSide { get; }
public nint NativeItemInstancePtr { get; }
public nint NativePlayerPtr { get; }
public nint NativePlayerSharedPtr { get; }
internal UseItemContext(int itemId, bool isTestUseOnly, bool isClientSide, nint nativeItemInstancePtr, nint nativePlayerPtr, nint nativePlayerSharedPtr)
internal UseItemContext(int itemId, bool isClientSide, nint nativeItemInstancePtr, nint nativePlayerPtr, nint nativePlayerSharedPtr)
{
ItemId = itemId;
IsTestUseOnly = isTestUseOnly;
IsClientSide = isClientSide;
NativeItemInstancePtr = nativeItemInstancePtr;
NativePlayerPtr = nativePlayerPtr;
@@ -177,6 +219,197 @@ public readonly struct UseItemContext
}
}
/// <summary>
/// Runtime context for item use-on-block callback.
/// </summary>
public readonly struct UseOnItemContext
{
public int ItemId { get; }
public bool IsClientSide { get; }
public nint NativeItemInstancePtr { get; }
public nint NativePlayerPtr { get; }
public nint NativePlayerSharedPtr { get; }
public nint NativeLevelPtr { get; }
public int X { get; }
public int Y { get; }
public int Z { get; }
public int Face { get; }
public float ClickX { get; }
public float ClickY { get; }
public float ClickZ { get; }
internal UseOnItemContext(
int itemId,
bool isClientSide,
nint nativeItemInstancePtr,
nint nativePlayerPtr,
nint nativePlayerSharedPtr,
nint nativeLevelPtr,
int x,
int y,
int z,
int face,
float clickX,
float clickY,
float clickZ)
{
ItemId = itemId;
IsClientSide = isClientSide;
NativeItemInstancePtr = nativeItemInstancePtr;
NativePlayerPtr = nativePlayerPtr;
NativePlayerSharedPtr = nativePlayerSharedPtr;
NativeLevelPtr = nativeLevelPtr;
X = x;
Y = y;
Z = z;
Face = face;
ClickX = clickX;
ClickY = clickY;
ClickZ = clickZ;
}
public bool ConsumeInventoryItem(Identifier id, int count = 1)
{
if (NativePlayerPtr == 0 || count <= 0)
return false;
int numericId = IdHelper.GetItemNumericId(id);
if (numericId < 0)
return false;
return NativeInterop.native_consume_item_from_player(NativePlayerPtr, numericId, count) != 0;
}
public bool DamageItem(int amount)
{
if (NativeItemInstancePtr == 0 || NativePlayerSharedPtr == 0 || amount <= 0)
return false;
return NativeInterop.native_damage_item_instance(NativeItemInstancePtr, amount, NativePlayerSharedPtr) != 0;
}
public bool SpawnEntityFromLook(Identifier id, double speed = 1.4, double spawnForward = 1.0, double spawnUp = 1.2)
{
int numericEntityId = IdHelper.GetEntityNumericId(id);
if (numericEntityId < 0)
return false;
return SpawnEntityFromLook(numericEntityId, speed, spawnForward, spawnUp);
}
public bool SpawnEntityFromLook(int numericEntityId, double speed = 1.4, double spawnForward = 1.0, double spawnUp = 1.2)
{
if (NativePlayerPtr == 0 || numericEntityId < 0)
return false;
return NativeInterop.native_spawn_entity_from_player_look(
NativePlayerPtr,
NativePlayerSharedPtr,
numericEntityId,
speed,
spawnForward,
spawnUp) != 0;
}
}
/// <summary>
/// Runtime context for item/entity interactions.
/// </summary>
public readonly struct ItemEntityInteractionContext
{
public int ItemId { get; }
public nint NativeItemInstancePtr { get; }
public nint NativePlayerPtr { get; }
public nint NativePlayerSharedPtr { get; }
public nint NativeTargetEntityPtr { get; }
internal ItemEntityInteractionContext(
int itemId,
nint nativeItemInstancePtr,
nint nativePlayerPtr,
nint nativePlayerSharedPtr,
nint nativeTargetEntityPtr)
{
ItemId = itemId;
NativeItemInstancePtr = nativeItemInstancePtr;
NativePlayerPtr = nativePlayerPtr;
NativePlayerSharedPtr = nativePlayerSharedPtr;
NativeTargetEntityPtr = nativeTargetEntityPtr;
}
public bool DamageItem(int amount)
{
if (NativeItemInstancePtr == 0 || NativePlayerSharedPtr == 0 || amount <= 0)
return false;
return NativeInterop.native_damage_item_instance(NativeItemInstancePtr, amount, NativePlayerSharedPtr) != 0;
}
}
/// <summary>
/// Runtime context for inventory tick callbacks.
/// </summary>
public readonly struct ItemInventoryTickContext
{
public int ItemId { get; }
public nint NativeItemInstancePtr { get; }
public nint NativeLevelPtr { get; }
public nint NativeOwnerEntityPtr { get; }
public int Slot { get; }
public bool IsSelected { get; }
public bool IsClientSide { get; }
internal ItemInventoryTickContext(
int itemId,
nint nativeItemInstancePtr,
nint nativeLevelPtr,
nint nativeOwnerEntityPtr,
int slot,
bool isSelected,
bool isClientSide)
{
ItemId = itemId;
NativeItemInstancePtr = nativeItemInstancePtr;
NativeLevelPtr = nativeLevelPtr;
NativeOwnerEntityPtr = nativeOwnerEntityPtr;
Slot = slot;
IsSelected = isSelected;
IsClientSide = isClientSide;
}
}
/// <summary>
/// Runtime context for crafted-by callbacks.
/// </summary>
public readonly struct ItemCraftedByContext
{
public int ItemId { get; }
public nint NativeItemInstancePtr { get; }
public nint NativeLevelPtr { get; }
public nint NativePlayerPtr { get; }
public nint NativePlayerSharedPtr { get; }
public int Amount { get; }
public bool IsClientSide { get; }
internal ItemCraftedByContext(
int itemId,
nint nativeItemInstancePtr,
nint nativeLevelPtr,
nint nativePlayerPtr,
nint nativePlayerSharedPtr,
int amount,
bool isClientSide)
{
ItemId = itemId;
NativeItemInstancePtr = nativeItemInstancePtr;
NativeLevelPtr = nativeLevelPtr;
NativePlayerPtr = nativePlayerPtr;
NativePlayerSharedPtr = nativePlayerSharedPtr;
Amount = amount;
IsClientSide = isClientSide;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct MineBlockNativeArgs
{
@@ -191,13 +424,64 @@ internal struct MineBlockNativeArgs
internal struct UseItemNativeArgs
{
public int ItemId;
public int IsTestUseOnly;
public int IsClientSide;
public nint ItemInstancePtr;
public nint PlayerPtr;
public nint PlayerSharedPtr;
}
[StructLayout(LayoutKind.Sequential)]
internal struct UseOnItemNativeArgs
{
public int ItemId;
public int IsClientSide;
public nint ItemInstancePtr;
public nint PlayerPtr;
public nint PlayerSharedPtr;
public nint LevelPtr;
public int X;
public int Y;
public int Z;
public int Face;
public float ClickX;
public float ClickY;
public float ClickZ;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ItemEntityInteractionNativeArgs
{
public int ItemId;
public nint ItemInstancePtr;
public nint PlayerPtr;
public nint PlayerSharedPtr;
public nint TargetEntityPtr;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ItemInventoryTickNativeArgs
{
public int ItemId;
public nint ItemInstancePtr;
public nint LevelPtr;
public nint OwnerEntityPtr;
public int Slot;
public int IsSelected;
public int IsClientSide;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ItemCraftedByNativeArgs
{
public int ItemId;
public nint ItemInstancePtr;
public nint LevelPtr;
public nint PlayerPtr;
public nint PlayerSharedPtr;
public int Amount;
public int IsClientSide;
}
internal static class ManagedItemDispatcher
{
private static readonly object s_lock = new();
@@ -259,7 +543,6 @@ internal static class ManagedItemDispatcher
var result = item.OnUseItem(new UseItemContext(
nativeArgs.ItemId,
nativeArgs.IsTestUseOnly != 0,
nativeArgs.IsClientSide != 0,
nativeArgs.ItemInstancePtr,
nativeArgs.PlayerPtr,
@@ -268,4 +551,144 @@ internal static class ManagedItemDispatcher
// 0 = no managed item, 1 = continue vanilla, 2 = cancel vanilla.
return result == UseItemResult.CancelVanilla ? 2 : 1;
}
internal static int HandleUseOn(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<UseOnItemNativeArgs>())
return 0;
UseOnItemNativeArgs nativeArgs = Marshal.PtrToStructure<UseOnItemNativeArgs>(args);
Item? item;
lock (s_lock)
{
s_items.TryGetValue(nativeArgs.ItemId, out item);
}
if (item == null)
return 0;
var result = item.OnUseOn(new UseOnItemContext(
nativeArgs.ItemId,
nativeArgs.IsClientSide != 0,
nativeArgs.ItemInstancePtr,
nativeArgs.PlayerPtr,
nativeArgs.PlayerSharedPtr,
nativeArgs.LevelPtr,
nativeArgs.X,
nativeArgs.Y,
nativeArgs.Z,
nativeArgs.Face,
nativeArgs.ClickX,
nativeArgs.ClickY,
nativeArgs.ClickZ));
return result == ItemActionResult.CancelVanilla ? 2 : 1;
}
internal static int HandleInteractEntity(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<ItemEntityInteractionNativeArgs>())
return 0;
ItemEntityInteractionNativeArgs nativeArgs = Marshal.PtrToStructure<ItemEntityInteractionNativeArgs>(args);
Item? item;
lock (s_lock)
{
s_items.TryGetValue(nativeArgs.ItemId, out item);
}
if (item == null)
return 0;
var result = item.OnInteractEntity(new ItemEntityInteractionContext(
nativeArgs.ItemId,
nativeArgs.ItemInstancePtr,
nativeArgs.PlayerPtr,
nativeArgs.PlayerSharedPtr,
nativeArgs.TargetEntityPtr));
return result == ItemActionResult.CancelVanilla ? 2 : 1;
}
internal static int HandleHurtEntity(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<ItemEntityInteractionNativeArgs>())
return 0;
ItemEntityInteractionNativeArgs nativeArgs = Marshal.PtrToStructure<ItemEntityInteractionNativeArgs>(args);
Item? item;
lock (s_lock)
{
s_items.TryGetValue(nativeArgs.ItemId, out item);
}
if (item == null)
return 0;
var result = item.OnHurtEntity(new ItemEntityInteractionContext(
nativeArgs.ItemId,
nativeArgs.ItemInstancePtr,
nativeArgs.PlayerPtr,
nativeArgs.PlayerSharedPtr,
nativeArgs.TargetEntityPtr));
return result == ItemActionResult.CancelVanilla ? 2 : 1;
}
internal static int HandleInventoryTick(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<ItemInventoryTickNativeArgs>())
return 0;
ItemInventoryTickNativeArgs nativeArgs = Marshal.PtrToStructure<ItemInventoryTickNativeArgs>(args);
Item? item;
lock (s_lock)
{
s_items.TryGetValue(nativeArgs.ItemId, out item);
}
if (item == null)
return 0;
item.OnInventoryTick(new ItemInventoryTickContext(
nativeArgs.ItemId,
nativeArgs.ItemInstancePtr,
nativeArgs.LevelPtr,
nativeArgs.OwnerEntityPtr,
nativeArgs.Slot,
nativeArgs.IsSelected != 0,
nativeArgs.IsClientSide != 0));
return 1;
}
internal static int HandleCraftedBy(IntPtr args, int sizeBytes)
{
if (args == IntPtr.Zero || sizeBytes < Marshal.SizeOf<ItemCraftedByNativeArgs>())
return 0;
ItemCraftedByNativeArgs nativeArgs = Marshal.PtrToStructure<ItemCraftedByNativeArgs>(args);
Item? item;
lock (s_lock)
{
s_items.TryGetValue(nativeArgs.ItemId, out item);
}
if (item == null)
return 0;
item.OnCraftedBy(new ItemCraftedByContext(
nativeArgs.ItemId,
nativeArgs.ItemInstancePtr,
nativeArgs.LevelPtr,
nativeArgs.PlayerPtr,
nativeArgs.PlayerSharedPtr,
nativeArgs.Amount,
nativeArgs.IsClientSide != 0));
return 1;
}
}

View File

@@ -123,6 +123,71 @@ public static class WeaveLoaderCore
}
}
public static int OnItemUseOn(IntPtr args, int sizeBytes)
{
try
{
return ManagedItemDispatcher.HandleUseOn(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnItemUseOn EXCEPTION: {ex}");
return 0;
}
}
public static int OnItemInteractEntity(IntPtr args, int sizeBytes)
{
try
{
return ManagedItemDispatcher.HandleInteractEntity(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnItemInteractEntity EXCEPTION: {ex}");
return 0;
}
}
public static int OnItemHurtEntity(IntPtr args, int sizeBytes)
{
try
{
return ManagedItemDispatcher.HandleHurtEntity(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnItemHurtEntity EXCEPTION: {ex}");
return 0;
}
}
public static int OnItemInventoryTick(IntPtr args, int sizeBytes)
{
try
{
return ManagedItemDispatcher.HandleInventoryTick(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnItemInventoryTick EXCEPTION: {ex}");
return 0;
}
}
public static int OnItemCraftedBy(IntPtr args, int sizeBytes)
{
try
{
return ManagedItemDispatcher.HandleCraftedBy(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnItemCraftedBy EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockPlace(IntPtr args, int sizeBytes)
{
try
@@ -162,6 +227,136 @@ public static class WeaveLoaderCore
}
}
public static int OnBlockUse(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleUse(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockUse EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockStepOn(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleStepOn(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockStepOn EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockEntityInsideTile(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleEntityInsideTile(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockEntityInsideTile EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockFallOn(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleFallOn(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockFallOn EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockRemoving(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleRemoving(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockRemoving EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockRemoved(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleRemoved(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockRemoved EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockDestroyed(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandleDestroyed(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockDestroyed EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockPlayerDestroy(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandlePlayerDestroy(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockPlayerDestroy EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockPlayerWillDestroy(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandlePlayerWillDestroy(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockPlayerWillDestroy EXCEPTION: {ex}");
return 0;
}
}
public static int OnBlockPlacedBy(IntPtr args, int sizeBytes)
{
try
{
return ManagedBlockDispatcher.HandlePlacedBy(args, sizeBytes);
}
catch (Exception ex)
{
Logger.Error($"OnBlockPlacedBy EXCEPTION: {ex}");
return 0;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct WorldLoadedNativeArgs
{

View File

@@ -24,9 +24,24 @@ static managed_entry_fn fn_Tick = nullptr;
static managed_entry_fn fn_Shutdown = nullptr;
static managed_entry_fn fn_ItemMineBlock = nullptr;
static managed_entry_fn fn_ItemUse = nullptr;
static managed_entry_fn fn_ItemUseOn = nullptr;
static managed_entry_fn fn_ItemInteractEntity = nullptr;
static managed_entry_fn fn_ItemHurtEntity = nullptr;
static managed_entry_fn fn_ItemInventoryTick = nullptr;
static managed_entry_fn fn_ItemCraftedBy = nullptr;
static managed_entry_fn fn_BlockOnPlace = nullptr;
static managed_entry_fn fn_BlockNeighborChanged = nullptr;
static managed_entry_fn fn_BlockTick = nullptr;
static managed_entry_fn fn_BlockUse = nullptr;
static managed_entry_fn fn_BlockStepOn = nullptr;
static managed_entry_fn fn_BlockEntityInsideTile = nullptr;
static managed_entry_fn fn_BlockFallOn = nullptr;
static managed_entry_fn fn_BlockRemoving = nullptr;
static managed_entry_fn fn_BlockRemoved = nullptr;
static managed_entry_fn fn_BlockDestroyed = nullptr;
static managed_entry_fn fn_BlockPlayerDestroy = nullptr;
static managed_entry_fn fn_BlockPlayerWillDestroy = nullptr;
static managed_entry_fn fn_BlockPlacedBy = nullptr;
static managed_entry_fn fn_EntitySummoned = nullptr;
static bool LoadHostfxr()
@@ -185,9 +200,24 @@ bool DotNetHost::Initialize()
ok &= resolve(L"Shutdown", &fn_Shutdown);
ok &= resolve(L"OnItemMineBlock", &fn_ItemMineBlock);
ok &= resolve(L"OnItemUse", &fn_ItemUse);
ok &= resolve(L"OnItemUseOn", &fn_ItemUseOn);
ok &= resolve(L"OnItemInteractEntity", &fn_ItemInteractEntity);
ok &= resolve(L"OnItemHurtEntity", &fn_ItemHurtEntity);
ok &= resolve(L"OnItemInventoryTick", &fn_ItemInventoryTick);
ok &= resolve(L"OnItemCraftedBy", &fn_ItemCraftedBy);
ok &= resolve(L"OnBlockPlace", &fn_BlockOnPlace);
ok &= resolve(L"OnBlockNeighborChanged", &fn_BlockNeighborChanged);
ok &= resolve(L"OnBlockTick", &fn_BlockTick);
ok &= resolve(L"OnBlockUse", &fn_BlockUse);
ok &= resolve(L"OnBlockStepOn", &fn_BlockStepOn);
ok &= resolve(L"OnBlockEntityInsideTile", &fn_BlockEntityInsideTile);
ok &= resolve(L"OnBlockFallOn", &fn_BlockFallOn);
ok &= resolve(L"OnBlockRemoving", &fn_BlockRemoving);
ok &= resolve(L"OnBlockRemoved", &fn_BlockRemoved);
ok &= resolve(L"OnBlockDestroyed", &fn_BlockDestroyed);
ok &= resolve(L"OnBlockPlayerDestroy", &fn_BlockPlayerDestroy);
ok &= resolve(L"OnBlockPlayerWillDestroy", &fn_BlockPlayerWillDestroy);
ok &= resolve(L"OnBlockPlacedBy", &fn_BlockPlacedBy);
ok &= resolve(L"OnEntitySummoned", &fn_EntitySummoned);
if (!ok)
@@ -253,6 +283,41 @@ int DotNetHost::CallItemUse(const void* args, int sizeBytes)
return fn_ItemUse(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallItemUseOn(const void* args, int sizeBytes)
{
if (!fn_ItemUseOn || !args || sizeBytes <= 0)
return 0;
return fn_ItemUseOn(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallItemInteractEntity(const void* args, int sizeBytes)
{
if (!fn_ItemInteractEntity || !args || sizeBytes <= 0)
return 0;
return fn_ItemInteractEntity(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallItemHurtEntity(const void* args, int sizeBytes)
{
if (!fn_ItemHurtEntity || !args || sizeBytes <= 0)
return 0;
return fn_ItemHurtEntity(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallItemInventoryTick(const void* args, int sizeBytes)
{
if (!fn_ItemInventoryTick || !args || sizeBytes <= 0)
return 0;
return fn_ItemInventoryTick(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallItemCraftedBy(const void* args, int sizeBytes)
{
if (!fn_ItemCraftedBy || !args || sizeBytes <= 0)
return 0;
return fn_ItemCraftedBy(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockOnPlace(const void* args, int sizeBytes)
{
if (!fn_BlockOnPlace || !args || sizeBytes <= 0)
@@ -274,6 +339,77 @@ int DotNetHost::CallBlockTick(const void* args, int sizeBytes)
return fn_BlockTick(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockUse(const void* args, int sizeBytes)
{
if (!fn_BlockUse || !args || sizeBytes <= 0)
return 0;
return fn_BlockUse(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockStepOn(const void* args, int sizeBytes)
{
if (!fn_BlockStepOn || !args || sizeBytes <= 0)
return 0;
return fn_BlockStepOn(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockEntityInsideTile(const void* args, int sizeBytes)
{
if (!fn_BlockEntityInsideTile || !args || sizeBytes <= 0)
return 0;
return fn_BlockEntityInsideTile(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockFallOn(const void* args, int sizeBytes)
{
if (!fn_BlockFallOn || !args || sizeBytes <= 0)
return 0;
return fn_BlockFallOn(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockRemoving(const void* args, int sizeBytes)
{
if (!fn_BlockRemoving || !args || sizeBytes <= 0)
return 0;
return fn_BlockRemoving(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockRemoved(const void* args, int sizeBytes)
{
if (!fn_BlockRemoved || !args || sizeBytes <= 0)
return 0;
return fn_BlockRemoved(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockDestroyed(const void* args, int sizeBytes)
{
if (!fn_BlockDestroyed || !args || sizeBytes <= 0)
return 0;
return fn_BlockDestroyed(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockPlayerDestroy(const void* args, int sizeBytes)
{
if (!fn_BlockPlayerDestroy || !args || sizeBytes <= 0)
return 0;
return fn_BlockPlayerDestroy(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockPlayerWillDestroy(const void* args, int sizeBytes)
{
if (!fn_BlockPlayerWillDestroy || !args || sizeBytes <= 0)
return 0;
return fn_BlockPlayerWillDestroy(const_cast<void*>(args), sizeBytes);
}
int DotNetHost::CallBlockPlacedBy(const void* args, int sizeBytes)
{
if (!fn_BlockPlacedBy || !args || sizeBytes <= 0)
return 0;
return fn_BlockPlacedBy(const_cast<void*>(args), sizeBytes);
}
void DotNetHost::CallEntitySummoned(int entityNumericId, float x, float y, float z)
{
if (!fn_EntitySummoned)

View File

@@ -17,8 +17,23 @@ namespace DotNetHost
void CallShutdown();
int CallItemMineBlock(const void* args, int sizeBytes);
int CallItemUse(const void* args, int sizeBytes);
int CallItemUseOn(const void* args, int sizeBytes);
int CallItemInteractEntity(const void* args, int sizeBytes);
int CallItemHurtEntity(const void* args, int sizeBytes);
int CallItemInventoryTick(const void* args, int sizeBytes);
int CallItemCraftedBy(const void* args, int sizeBytes);
int CallBlockOnPlace(const void* args, int sizeBytes);
int CallBlockNeighborChanged(const void* args, int sizeBytes);
int CallBlockTick(const void* args, int sizeBytes);
int CallBlockUse(const void* args, int sizeBytes);
int CallBlockStepOn(const void* args, int sizeBytes);
int CallBlockEntityInsideTile(const void* args, int sizeBytes);
int CallBlockFallOn(const void* args, int sizeBytes);
int CallBlockRemoving(const void* args, int sizeBytes);
int CallBlockRemoved(const void* args, int sizeBytes);
int CallBlockDestroyed(const void* args, int sizeBytes);
int CallBlockPlayerDestroy(const void* args, int sizeBytes);
int CallBlockPlayerWillDestroy(const void* args, int sizeBytes);
int CallBlockPlacedBy(const void* args, int sizeBytes);
void CallEntitySummoned(int entityNumericId, float x, float y, float z);
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,12 +32,28 @@ typedef void (__fastcall *ItemInstanceMineBlock_fn)(void* thisPtr, void* level,
typedef bool (__fastcall *ItemInstanceUseOn_fn)(void* thisPtr, void* playerSharedPtr, void* level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly);
typedef void* (__fastcall *ItemInstanceSave_fn)(void* thisPtr, void* compoundTagPtr);
typedef void (__fastcall *ItemInstanceLoad_fn)(void* thisPtr, void* compoundTagPtr);
typedef void (__fastcall *ItemInstanceInventoryTick_fn)(void* thisPtr, void* level, void* ownerSharedPtr, int slot, bool selected);
typedef void (__fastcall *ItemInstanceOnCraftedBy_fn)(void* thisPtr, void* level, void* playerSharedPtr, int amount);
typedef bool (__fastcall *ItemInstanceInteractEnemy_fn)(void* thisPtr, void* playerSharedPtr, void* targetSharedPtr);
typedef void (__fastcall *ItemInstanceHurtEnemy_fn)(void* thisPtr, void* targetSharedPtr, void* playerSharedPtr);
typedef bool (__fastcall *ItemMineBlock_fn)(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
typedef float (__fastcall *PickaxeGetDestroySpeed_fn)(void* thisPtr, void* itemInstanceSharedPtr, void* tilePtr);
typedef bool (__fastcall *PickaxeCanDestroySpecial_fn)(void* thisPtr, void* tilePtr);
typedef void (__fastcall *TileOnPlace_fn)(void* thisPtr, void* level, int x, int y, int z);
typedef void (__fastcall *TileNeighborChanged_fn)(void* thisPtr, void* level, int x, int y, int z, int type);
typedef void (__fastcall *TileTick_fn)(void* thisPtr, void* level, int x, int y, int z, void* random);
typedef bool (__fastcall *TileUse_fn)(void* thisPtr, void* level, int x, int y, int z, void* playerSharedPtr, int face, float clickX, float clickY, float clickZ, bool soundOnly);
typedef void (__fastcall *TileStepOn_fn)(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr);
typedef void (__fastcall *TileEntityInside_fn)(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr);
typedef void (__fastcall *TileFallOn_fn)(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr, float fallDistance);
typedef void (__fastcall *TileOnRemoving_fn)(void* thisPtr, void* level, int x, int y, int z, int data);
typedef void (__fastcall *TileOnRemove_fn)(void* thisPtr, void* level, int x, int y, int z, int id, int data);
typedef void (__fastcall *TileDestroy_fn)(void* thisPtr, void* level, int x, int y, int z, int data);
typedef void (__fastcall *TilePlayerDestroy_fn)(void* thisPtr, void* level, void* playerSharedPtr, int x, int y, int z, int data);
typedef void (__fastcall *TilePlayerWillDestroy_fn)(void* thisPtr, void* level, int x, int y, int z, int data, void* playerSharedPtr);
typedef void (__fastcall *TileSetPlacedBy_fn)(void* thisPtr, void* level, int x, int y, int z, void* livingEntitySharedPtr, void* itemInstanceSharedPtr);
typedef void (__fastcall *TileSharedAction_fn)(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr, float fallDistance);
typedef void (__fastcall *TileSharedLifecycle_fn)(void* thisPtr, void* level, int x, int y, int z, void* arg5, void* arg6);
typedef bool (__fastcall *LevelSetTileAndDataDispatch_fn)(void* thisPtr, int x, int y, int z, int tile, int data, int updateFlags);
typedef bool (__fastcall *LevelSetDataDispatch_fn)(void* thisPtr, int x, int y, int z, int data, int updateFlags, bool forceUpdate);
typedef void (__fastcall *LevelUpdateNeighborsAtDispatch_fn)(void* thisPtr, int x, int y, int z, int type);
@@ -61,6 +77,8 @@ typedef bool (__fastcall *MultiPlayerGameModeUseItemOn_fn)(void* thisPtr, void*
typedef bool (__fastcall *ServerPlayerGameModeUseItemOn_fn)(void* thisPtr, void* playerSharedPtr, void* level, void* itemInstanceSharedPtr, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly, bool* pbUsedItem);
typedef void (__fastcall *MinecraftSetLevel_fn)(void* thisPtr, void* level, int message, void* forceInsertPlayerSharedPtr, bool doForceStatsSave, bool bPrimaryPlayerSignedOut);
typedef bool (__fastcall *LevelAddEntity_fn)(void* thisPtr, void* entitySharedPtr);
typedef void (__fastcall *EntityPlayStepSound_fn)(void* thisPtr, int xt, int yt, int zt, int t);
typedef void (__fastcall *EntityCheckInsideTiles_fn)(void* thisPtr);
typedef void (__fastcall *EntityMoveTo_fn)(void* thisPtr, double x, double y, double z, float yRot, float xRot);
typedef void (__fastcall *EntityIONewById_fn)(void* outSharedPtr, int entityNumericId, void* level);
typedef void (__fastcall *EntitySetPos_fn)(void* thisPtr, double x, double y, double z);
@@ -130,6 +148,10 @@ namespace GameHooks
extern ItemInstanceUseOn_fn Original_ItemInstanceUseOn;
extern ItemInstanceSave_fn Original_ItemInstanceSave;
extern ItemInstanceLoad_fn Original_ItemInstanceLoad;
extern ItemInstanceInventoryTick_fn Original_ItemInstanceInventoryTick;
extern ItemInstanceOnCraftedBy_fn Original_ItemInstanceOnCraftedBy;
extern ItemInstanceInteractEnemy_fn Original_ItemInstanceInteractEnemy;
extern ItemInstanceHurtEnemy_fn Original_ItemInstanceHurtEnemy;
extern ItemMineBlock_fn Original_ItemMineBlock;
extern ItemMineBlock_fn Original_DiggerItemMineBlock;
extern PickaxeGetDestroySpeed_fn Original_PickaxeItemGetDestroySpeed;
@@ -139,6 +161,18 @@ namespace GameHooks
extern TileOnPlace_fn Original_TileOnPlace;
extern TileNeighborChanged_fn Original_TileNeighborChanged;
extern TileTick_fn Original_TileTick;
extern TileUse_fn Original_TileUse;
extern TileStepOn_fn Original_TileStepOn;
extern TileEntityInside_fn Original_TileEntityInside;
extern TileFallOn_fn Original_TileFallOn;
extern TileOnRemoving_fn Original_TileOnRemoving;
extern TileOnRemove_fn Original_TileOnRemove;
extern TileDestroy_fn Original_TileDestroy;
extern TilePlayerDestroy_fn Original_TilePlayerDestroy;
extern TilePlayerWillDestroy_fn Original_TilePlayerWillDestroy;
extern TileSetPlacedBy_fn Original_TileSetPlacedBy;
extern TileSharedAction_fn Original_TileSharedAction;
extern TileSharedLifecycle_fn Original_TileSharedLifecycle;
extern LevelSetTileAndDataDispatch_fn Original_LevelSetTileAndData;
extern LevelSetDataDispatch_fn Original_LevelSetData;
extern LevelUpdateNeighborsAtDispatch_fn Original_LevelUpdateNeighborsAt;
@@ -168,6 +202,8 @@ namespace GameHooks
extern ServerPlayerGameModeUseItemOn_fn Original_ServerPlayerGameModeUseItemOn;
extern MultiPlayerGameModeUseItemOn_fn Original_MultiPlayerGameModeUseItemOn;
extern MinecraftSetLevel_fn Original_MinecraftSetLevel;
extern EntityPlayStepSound_fn Original_EntityPlayStepSound;
extern EntityCheckInsideTiles_fn Original_EntityCheckInsideTiles;
extern TexturesBindTextureResource_fn Original_TexturesBindTextureResource;
extern TexturesLoadTextureByName_fn Original_TexturesLoadTextureByName;
extern TexturesLoadTextureByIndex_fn Original_TexturesLoadTextureByIndex;
@@ -229,6 +265,10 @@ namespace GameHooks
bool __fastcall Hooked_ItemInstanceUseOn(void* thisPtr, void* playerSharedPtr, void* level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly);
void* __fastcall Hooked_ItemInstanceSave(void* thisPtr, void* compoundTagPtr);
void __fastcall Hooked_ItemInstanceLoad(void* thisPtr, void* compoundTagPtr);
void __fastcall Hooked_ItemInstanceInventoryTick(void* thisPtr, void* level, void* ownerSharedPtr, int slot, bool selected);
void __fastcall Hooked_ItemInstanceOnCraftedBy(void* thisPtr, void* level, void* playerSharedPtr, int amount);
bool __fastcall Hooked_ItemInstanceInteractEnemy(void* thisPtr, void* playerSharedPtr, void* targetSharedPtr);
void __fastcall Hooked_ItemInstanceHurtEnemy(void* thisPtr, void* targetSharedPtr, void* playerSharedPtr);
bool __fastcall Hooked_ItemMineBlock(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
bool __fastcall Hooked_DiggerItemMineBlock(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
float __fastcall Hooked_PickaxeItemGetDestroySpeed(void* thisPtr, void* itemInstanceSharedPtr, void* tilePtr);
@@ -238,6 +278,18 @@ namespace GameHooks
void __fastcall Hooked_TileOnPlace(void* thisPtr, void* level, int x, int y, int z);
void __fastcall Hooked_TileNeighborChanged(void* thisPtr, void* level, int x, int y, int z, int type);
void __fastcall Hooked_TileTick(void* thisPtr, void* level, int x, int y, int z, void* random);
bool __fastcall Hooked_TileUse(void* thisPtr, void* level, int x, int y, int z, void* playerSharedPtr, int face, float clickX, float clickY, float clickZ, bool soundOnly);
void __fastcall Hooked_TileStepOn(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr);
void __fastcall Hooked_TileEntityInside(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr);
void __fastcall Hooked_TileFallOn(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr, float fallDistance);
void __fastcall Hooked_TileOnRemoving(void* thisPtr, void* level, int x, int y, int z, int data);
void __fastcall Hooked_TileOnRemove(void* thisPtr, void* level, int x, int y, int z, int id, int data);
void __fastcall Hooked_TileDestroy(void* thisPtr, void* level, int x, int y, int z, int data);
void __fastcall Hooked_TilePlayerDestroy(void* thisPtr, void* level, void* playerSharedPtr, int x, int y, int z, int data);
void __fastcall Hooked_TilePlayerWillDestroy(void* thisPtr, void* level, int x, int y, int z, int data, void* playerSharedPtr);
void __fastcall Hooked_TileSetPlacedBy(void* thisPtr, void* level, int x, int y, int z, void* livingEntitySharedPtr, void* itemInstanceSharedPtr);
void __fastcall Hooked_TileSharedAction(void* thisPtr, void* level, int x, int y, int z, void* entitySharedPtr, float fallDistance);
void __fastcall Hooked_TileSharedLifecycle(void* thisPtr, void* level, int x, int y, int z, void* arg5, void* arg6);
bool __fastcall Hooked_LevelSetTileAndData(void* thisPtr, int x, int y, int z, int tile, int data, int updateFlags);
bool __fastcall Hooked_LevelSetData(void* thisPtr, int x, int y, int z, int data, int updateFlags, bool forceUpdate);
void __fastcall Hooked_LevelUpdateNeighborsAt(void* thisPtr, int x, int y, int z, int type);
@@ -266,6 +318,8 @@ namespace GameHooks
bool __fastcall Hooked_ServerPlayerGameModeUseItemOn(void* thisPtr, void* playerSharedPtr, void* level, void* itemInstanceSharedPtr, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly, bool* pbUsedItem);
bool __fastcall Hooked_MultiPlayerGameModeUseItemOn(void* thisPtr, void* playerSharedPtr, void* level, void* itemInstanceSharedPtr, int x, int y, int z, int face, void* hitVec3Ptr, bool bTestUseOnly, bool* pbUsedItem);
void __fastcall Hooked_MinecraftSetLevel(void* thisPtr, void* level, int message, void* forceInsertPlayerSharedPtr, bool doForceStatsSave, bool bPrimaryPlayerSignedOut);
void __fastcall Hooked_EntityPlayStepSound(void* thisPtr, int xt, int yt, int zt, int t);
void __fastcall Hooked_EntityCheckInsideTiles(void* thisPtr);
void __fastcall Hooked_TexturesBindTextureResource(void* thisPtr, void* resourcePtr);
int __fastcall Hooked_TexturesLoadTextureByName(void* thisPtr, int texId, const std::wstring& resourceName);
int __fastcall Hooked_TexturesLoadTextureByIndex(void* thisPtr, int idx);

View File

@@ -184,6 +184,91 @@ bool HookManager::Install(const SymbolResolver& symbols)
}
}
if (symbols.Item.pItemInstanceInventoryTick)
{
if (MH_CreateHook(symbols.Item.pItemInstanceInventoryTick,
reinterpret_cast<void*>(&GameHooks::Hooked_ItemInstanceInventoryTick),
reinterpret_cast<void**>(&GameHooks::Original_ItemInstanceInventoryTick)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ItemInstance::inventoryTick");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ItemInstance::inventoryTick (managed item callbacks)");
}
}
if (symbols.Item.pItemInstanceOnCraftedBy)
{
if (MH_CreateHook(symbols.Item.pItemInstanceOnCraftedBy,
reinterpret_cast<void*>(&GameHooks::Hooked_ItemInstanceOnCraftedBy),
reinterpret_cast<void**>(&GameHooks::Original_ItemInstanceOnCraftedBy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ItemInstance::onCraftedBy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ItemInstance::onCraftedBy (managed item callbacks)");
}
}
if (symbols.Item.pItemInstanceInteractEnemy)
{
if (MH_CreateHook(symbols.Item.pItemInstanceInteractEnemy,
reinterpret_cast<void*>(&GameHooks::Hooked_ItemInstanceInteractEnemy),
reinterpret_cast<void**>(&GameHooks::Original_ItemInstanceInteractEnemy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ItemInstance::interactEnemy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ItemInstance::interactEnemy (managed item callbacks)");
}
}
if (symbols.Item.pItemInstanceHurtEnemy)
{
if (MH_CreateHook(symbols.Item.pItemInstanceHurtEnemy,
reinterpret_cast<void*>(&GameHooks::Hooked_ItemInstanceHurtEnemy),
reinterpret_cast<void**>(&GameHooks::Original_ItemInstanceHurtEnemy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ItemInstance::hurtEnemy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ItemInstance::hurtEnemy (managed item callbacks)");
}
}
if (symbols.Entity.pEntityPlayStepSound)
{
if (MH_CreateHook(symbols.Entity.pEntityPlayStepSound,
reinterpret_cast<void*>(&GameHooks::Hooked_EntityPlayStepSound),
reinterpret_cast<void**>(&GameHooks::Original_EntityPlayStepSound)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Entity::playStepSound");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Entity::playStepSound (step-on callbacks)");
}
}
if (symbols.Entity.pEntityCheckInsideTiles)
{
if (MH_CreateHook(symbols.Entity.pEntityCheckInsideTiles,
reinterpret_cast<void*>(&GameHooks::Hooked_EntityCheckInsideTiles),
reinterpret_cast<void**>(&GameHooks::Original_EntityCheckInsideTiles)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Entity::checkInsideTiles");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Entity::checkInsideTiles (entity-inside callbacks)");
}
}
if (symbols.Item.pItemInstanceGetIcon)
{
if (MH_CreateHook(symbols.Item.pItemInstanceGetIcon,
@@ -478,6 +563,162 @@ bool HookManager::Install(const SymbolResolver& symbols)
}
}
if (symbols.Tile.pTileUse)
{
if (MH_CreateHook(symbols.Tile.pTileUse,
reinterpret_cast<void*>(&GameHooks::Hooked_TileUse),
reinterpret_cast<void**>(&GameHooks::Original_TileUse)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::use");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::use (managed block callbacks)");
}
}
void* sharedActionTarget = symbols.Tile.pTileStepOn
? symbols.Tile.pTileStepOn
: symbols.Tile.pTileFallOn;
int sharedActionCount = 0;
if (sharedActionTarget)
{
if (symbols.Tile.pTileStepOn == sharedActionTarget) sharedActionCount++;
if (symbols.Tile.pTileFallOn == sharedActionTarget) sharedActionCount++;
}
const bool useSharedActionHook = sharedActionTarget && sharedActionCount >= 2;
if (useSharedActionHook)
{
if (MH_CreateHook(sharedActionTarget,
reinterpret_cast<void*>(&GameHooks::Hooked_TileSharedAction),
reinterpret_cast<void**>(&GameHooks::Original_TileSharedAction)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook shared Tile action stub");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked shared Tile action stub (stepOn/fallOn)");
}
}
else
{
if (symbols.Tile.pTileStepOn)
{
if (MH_CreateHook(symbols.Tile.pTileStepOn,
reinterpret_cast<void*>(&GameHooks::Hooked_TileStepOn),
reinterpret_cast<void**>(&GameHooks::Original_TileStepOn)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::stepOn");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::stepOn (managed block callbacks)");
}
}
if (symbols.Tile.pTileFallOn)
{
if (MH_CreateHook(symbols.Tile.pTileFallOn,
reinterpret_cast<void*>(&GameHooks::Hooked_TileFallOn),
reinterpret_cast<void**>(&GameHooks::Original_TileFallOn)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::fallOn");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::fallOn (managed block callbacks)");
}
}
}
void* sharedLifecycleTarget = symbols.Tile.pTileDestroy
? symbols.Tile.pTileDestroy
: symbols.Tile.pTileTick;
int sharedLifecycleCount = 0;
if (sharedLifecycleTarget)
{
if (symbols.Tile.pTileDestroy == sharedLifecycleTarget) sharedLifecycleCount++;
if (symbols.Tile.pTileOnRemoving == sharedLifecycleTarget) sharedLifecycleCount++;
if (symbols.Tile.pTileOnRemove == sharedLifecycleTarget) sharedLifecycleCount++;
if (symbols.Tile.pTileTick == sharedLifecycleTarget) sharedLifecycleCount++;
}
const bool useSharedLifecycleHook = sharedLifecycleTarget && sharedLifecycleCount >= 2;
if (useSharedLifecycleHook)
{
if (MH_CreateHook(sharedLifecycleTarget,
reinterpret_cast<void*>(&GameHooks::Hooked_TileSharedLifecycle),
reinterpret_cast<void**>(&GameHooks::Original_TileSharedLifecycle)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook shared Tile lifecycle stub");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked shared Tile lifecycle stub (destroy)");
}
}
else
{
if (symbols.Tile.pTileDestroy)
{
if (MH_CreateHook(symbols.Tile.pTileDestroy,
reinterpret_cast<void*>(&GameHooks::Hooked_TileDestroy),
reinterpret_cast<void**>(&GameHooks::Original_TileDestroy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::destroy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::destroy (managed block callbacks)");
}
}
}
if (symbols.Tile.pTilePlayerDestroy)
{
if (MH_CreateHook(symbols.Tile.pTilePlayerDestroy,
reinterpret_cast<void*>(&GameHooks::Hooked_TilePlayerDestroy),
reinterpret_cast<void**>(&GameHooks::Original_TilePlayerDestroy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::playerDestroy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::playerDestroy (managed block callbacks)");
}
}
if (symbols.Tile.pTilePlayerWillDestroy)
{
if (MH_CreateHook(symbols.Tile.pTilePlayerWillDestroy,
reinterpret_cast<void*>(&GameHooks::Hooked_TilePlayerWillDestroy),
reinterpret_cast<void**>(&GameHooks::Original_TilePlayerWillDestroy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::playerWillDestroy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::playerWillDestroy (managed block callbacks)");
}
}
if (symbols.Tile.pTileSetPlacedBy)
{
if (MH_CreateHook(symbols.Tile.pTileSetPlacedBy,
reinterpret_cast<void*>(&GameHooks::Hooked_TileSetPlacedBy),
reinterpret_cast<void**>(&GameHooks::Original_TileSetPlacedBy)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Tile::setPlacedBy");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Tile::setPlacedBy (managed block callbacks)");
}
}
if (symbols.Level.pServerLevelTickPendingTicks)
{
if (MH_CreateHook(symbols.Level.pServerLevelTickPendingTicks,

View File

@@ -67,6 +67,10 @@ namespace
static const char* SYM_ITEMINSTANCE_SAVE = "?save@ItemInstance@@QEAAPEAVCompoundTag@@PEAV2@@Z";
static const char* SYM_ITEMINSTANCE_LOAD = "?load@ItemInstance@@QEAAXPEAVCompoundTag@@@Z";
static const char* SYM_ITEMINSTANCE_HURTANDBREAK = "?hurtAndBreak@ItemInstance@@QEAAXHV?$shared_ptr@VLivingEntity@@@std@@@Z";
static const char* SYM_ITEMINSTANCE_INVENTORYTICK = "?inventoryTick@ItemInstance@@QEAAXPEAVLevel@@V?$shared_ptr@VEntity@@@std@@H_N@Z";
static const char* SYM_ITEMINSTANCE_ONCRAFTEDBY = "?onCraftedBy@ItemInstance@@QEAAXPEAVLevel@@V?$shared_ptr@VPlayer@@@std@@H@Z";
static const char* SYM_ITEMINSTANCE_INTERACTENEMY = "?interactEnemy@ItemInstance@@QEAA_NV?$shared_ptr@VPlayer@@@std@@V?$shared_ptr@VLivingEntity@@@3@@Z";
static const char* SYM_ITEMINSTANCE_HURTENEMY = "?hurtEnemy@ItemInstance@@QEAAXV?$shared_ptr@VLivingEntity@@@std@@V?$shared_ptr@VPlayer@@@3@@Z";
static const char* SYM_ITEM_MINEBLOCK = "?mineBlock@Item@@UEAA_NV?$shared_ptr@VItemInstance@@@std@@PEAVLevel@@HHHHV?$shared_ptr@VLivingEntity@@@3@@Z";
static const char* SYM_DIGGERITEM_MINEBLOCK = "?mineBlock@DiggerItem@@UEAA_NV?$shared_ptr@VItemInstance@@@std@@PEAVLevel@@HHHHV?$shared_ptr@VLivingEntity@@@3@@Z";
static const char* SYM_PICKAXEITEM_GETDESTROYSPEED = "?getDestroySpeed@PickaxeItem@@UEAAMV?$shared_ptr@VItemInstance@@@std@@PEAVTile@@@Z";
@@ -81,6 +85,16 @@ namespace
static const char* SYM_TILE_ONPLACE = "?onPlace@Tile@@UEAAXPEAVLevel@@HHH@Z";
static const char* SYM_TILE_NEIGHBORCHANGED = "?neighborChanged@Tile@@UEAAXPEAVLevel@@HHHH@Z";
static const char* SYM_TILE_TICK = "?tick@Tile@@UEAAXPEAVLevel@@HHHPEAVRandom@@@Z";
static const char* SYM_TILE_USE = "?use@Tile@@UEAA_NPEAVLevel@@HHHV?$shared_ptr@VPlayer@@@std@@HMMM_N@Z";
static const char* SYM_TILE_STEPON = "?stepOn@Tile@@UEAAXPEAVLevel@@HHHV?$shared_ptr@VEntity@@@std@@@Z";
static const char* SYM_TILE_ENTITYINSIDE = "?entityInside@Tile@@UEAAXPEAVLevel@@HHHV?$shared_ptr@VEntity@@@std@@@Z";
static const char* SYM_TILE_FALLON = "?fallOn@Tile@@UEAAXPEAVLevel@@HHHV?$shared_ptr@VEntity@@@std@@M@Z";
static const char* SYM_TILE_ONREMOVING = "?onRemoving@Tile@@UEAAXPEAVLevel@@HHHH@Z";
static const char* SYM_TILE_ONREMOVE = "?onRemove@Tile@@UEAAXPEAVLevel@@HHHHH@Z";
static const char* SYM_TILE_DESTROY = "?destroy@Tile@@UEAAXPEAVLevel@@HHHH@Z";
static const char* SYM_TILE_PLAYERDESTROY = "?playerDestroy@Tile@@UEAAXPEAVLevel@@V?$shared_ptr@VPlayer@@@std@@HHHH@Z";
static const char* SYM_TILE_PLAYERWILLDESTROY = "?playerWillDestroy@Tile@@UEAAXPEAVLevel@@HHHHV?$shared_ptr@VPlayer@@@std@@@Z";
static const char* SYM_TILE_SETPLACEDBY = "?setPlacedBy@Tile@@UEAAXPEAVLevel@@HHHV?$shared_ptr@VLivingEntity@@@std@@V?$shared_ptr@VItemInstance@@@4@@Z";
static const char* SYM_TILE_GETRESOURCE = "?getResource@Tile@@UEAAHHPEAVRandom@@H@Z";
static const char* SYM_TILE_GETPLACEDONFACEDATAVALUE = "?getPlacedOnFaceDataValue@Tile@@UEAAHPEAVLevel@@HHHHMMMH@Z";
static const char* SYM_TILE_CLONETILEID = "?cloneTileId@Tile@@UEAAHPEAVLevel@@HHH@Z";
@@ -138,7 +152,9 @@ namespace
static const char* SYM_LEVEL_ADDENTITY = "?addEntity@Level@@UEAA_NV?$shared_ptr@VEntity@@@std@@@Z";
static const char* SYM_ENTITYIO_NEWBYID = "?newById@EntityIO@@SA?AV?$shared_ptr@VEntity@@@std@@HPEAVLevel@@@Z";
static const char* SYM_ENTITY_MOVETO = "?moveTo@Entity@@QEAAXNNNMM@Z";
static const char* SYM_ENTITY_CHECKINSIDETILES = "?checkInsideTiles@Entity@@MEAAXXZ";
static const char* SYM_ENTITY_SETPOS = "?setPos@Entity@@QEAAXNNN@Z";
static const char* SYM_ENTITY_PLAYSTEPSOUND = "?playStepSound@Entity@@MEAAXHHHH@Z";
static const char* SYM_LIVINGENTITY_GETLOOKANGLE = "?getLookAngle@LivingEntity@@UEAAPEAVVec3@@XZ";
static const char* SYM_ENTITY_GETLOOKANGLE = "?getLookAngle@Entity@@UEAAPEAVVec3@@XZ";
static const char* SYM_LIVINGENTITY_GETVIEWVECTOR = "?getViewVector@LivingEntity@@UEAAPEAVVec3@@M@Z";
@@ -311,6 +327,10 @@ bool ItemSymbols::Resolve(SymbolResolver& resolver)
pItemInstanceSave = resolver.Resolve(SYM_ITEMINSTANCE_SAVE);
pItemInstanceLoad = resolver.Resolve(SYM_ITEMINSTANCE_LOAD);
pItemInstanceHurtAndBreak = resolver.Resolve(SYM_ITEMINSTANCE_HURTANDBREAK);
pItemInstanceInventoryTick = resolver.Resolve(SYM_ITEMINSTANCE_INVENTORYTICK);
pItemInstanceOnCraftedBy = resolver.Resolve(SYM_ITEMINSTANCE_ONCRAFTEDBY);
pItemInstanceInteractEnemy = resolver.Resolve(SYM_ITEMINSTANCE_INTERACTENEMY);
pItemInstanceHurtEnemy = resolver.Resolve(SYM_ITEMINSTANCE_HURTENEMY);
pItemMineBlock = resolver.Resolve(SYM_ITEM_MINEBLOCK);
pDiggerItemMineBlock = resolver.Resolve(SYM_DIGGERITEM_MINEBLOCK);
pPickaxeItemGetDestroySpeed = resolver.Resolve(SYM_PICKAXEITEM_GETDESTROYSPEED);
@@ -334,6 +354,10 @@ void ItemSymbols::Log() const
LogSym("ItemInstance::save", pItemInstanceSave);
LogSym("ItemInstance::load", pItemInstanceLoad);
LogSym("ItemInstance::hurtAndBreak", pItemInstanceHurtAndBreak);
LogSym("ItemInstance::inventoryTick", pItemInstanceInventoryTick);
LogSym("ItemInstance::onCraftedBy", pItemInstanceOnCraftedBy);
LogSym("ItemInstance::interactEnemy", pItemInstanceInteractEnemy);
LogSym("ItemInstance::hurtEnemy", pItemInstanceHurtEnemy);
LogSym("Item::mineBlock", pItemMineBlock);
LogSym("DiggerItem::mineBlock", pDiggerItemMineBlock);
LogSym("PickaxeItem::getDestroySpeed", pPickaxeItemGetDestroySpeed);
@@ -351,6 +375,16 @@ bool TileSymbols::Resolve(SymbolResolver& resolver)
pTileOnPlace = resolver.Resolve(SYM_TILE_ONPLACE);
pTileNeighborChanged = resolver.Resolve(SYM_TILE_NEIGHBORCHANGED);
pTileTick = resolver.Resolve(SYM_TILE_TICK);
pTileUse = resolver.Resolve(SYM_TILE_USE);
pTileStepOn = resolver.Resolve(SYM_TILE_STEPON);
pTileEntityInside = resolver.Resolve(SYM_TILE_ENTITYINSIDE);
pTileFallOn = resolver.Resolve(SYM_TILE_FALLON);
pTileOnRemoving = resolver.Resolve(SYM_TILE_ONREMOVING);
pTileOnRemove = resolver.Resolve(SYM_TILE_ONREMOVE);
pTileDestroy = resolver.Resolve(SYM_TILE_DESTROY);
pTilePlayerDestroy = resolver.Resolve(SYM_TILE_PLAYERDESTROY);
pTilePlayerWillDestroy = resolver.Resolve(SYM_TILE_PLAYERWILLDESTROY);
pTileSetPlacedBy = resolver.Resolve(SYM_TILE_SETPLACEDBY);
pTileGetResource = resolver.Resolve(SYM_TILE_GETRESOURCE);
pTileGetPlacedOnFaceDataValue = resolver.Resolve(SYM_TILE_GETPLACEDONFACEDATAVALUE);
pTileCloneTileId = resolver.Resolve(SYM_TILE_CLONETILEID);
@@ -391,6 +425,26 @@ bool TileSymbols::Resolve(SymbolResolver& resolver)
pTileNeighborChanged = resolver.ResolveExact("Tile::neighborChanged");
if (resolver.IsStub(pTileTick))
pTileTick = resolver.ResolveExact("Tile::tick");
if (resolver.IsStub(pTileUse))
pTileUse = resolver.ResolveExact("Tile::use");
if (resolver.IsStub(pTileStepOn))
pTileStepOn = resolver.ResolveExact("Tile::stepOn");
if (resolver.IsStub(pTileEntityInside))
pTileEntityInside = resolver.ResolveExact("Tile::entityInside");
if (resolver.IsStub(pTileFallOn))
pTileFallOn = resolver.ResolveExact("Tile::fallOn");
if (resolver.IsStub(pTileOnRemoving))
pTileOnRemoving = resolver.ResolveExact("Tile::onRemoving");
if (resolver.IsStub(pTileOnRemove))
pTileOnRemove = resolver.ResolveExact("Tile::onRemove");
if (resolver.IsStub(pTileDestroy))
pTileDestroy = resolver.ResolveExact("Tile::destroy");
if (resolver.IsStub(pTilePlayerDestroy))
pTilePlayerDestroy = resolver.ResolveExact("Tile::playerDestroy");
if (resolver.IsStub(pTilePlayerWillDestroy))
pTilePlayerWillDestroy = resolver.ResolveExact("Tile::playerWillDestroy");
if (resolver.IsStub(pTileSetPlacedBy))
pTileSetPlacedBy = resolver.ResolveExact("Tile::setPlacedBy");
if (resolver.IsStub(pWoodSlabRegisterIcons))
pWoodSlabRegisterIcons = resolver.ResolveExact("WoodSlabTile::registerIcons");
if (resolver.IsStub(pTileClip))
@@ -403,6 +457,16 @@ void TileSymbols::Log() const
LogSym("Tile::onPlace", pTileOnPlace);
LogSym("Tile::neighborChanged", pTileNeighborChanged);
LogSym("Tile::tick", pTileTick);
LogSym("Tile::use", pTileUse);
LogSym("Tile::stepOn", pTileStepOn);
LogSym("Tile::entityInside", pTileEntityInside);
LogSym("Tile::fallOn", pTileFallOn);
LogSym("Tile::onRemoving", pTileOnRemoving);
LogSym("Tile::onRemove", pTileOnRemove);
LogSym("Tile::destroy", pTileDestroy);
LogSym("Tile::playerDestroy", pTilePlayerDestroy);
LogSym("Tile::playerWillDestroy", pTilePlayerWillDestroy);
LogSym("Tile::setPlacedBy", pTileSetPlacedBy);
LogSym("Tile::getResource", pTileGetResource);
LogSym("Tile::getPlacedOnFaceDataValue", pTileGetPlacedOnFaceDataValue);
LogSym("Tile::cloneTileId", pTileCloneTileId);
@@ -501,8 +565,10 @@ bool EntitySymbols::Resolve(SymbolResolver& resolver)
pServerPlayerGameModeUseItemOn = resolver.Resolve(SYM_SERVER_PLAYER_GAMEMODE_USEITEMON);
pMultiPlayerGameModeUseItemOn = resolver.Resolve(SYM_MULTI_PLAYER_GAMEMODE_USEITEMON);
pLevelAddEntity = resolver.Resolve(SYM_LEVEL_ADDENTITY);
pEntityPlayStepSound = resolver.Resolve(SYM_ENTITY_PLAYSTEPSOUND);
pEntityIONewById = resolver.Resolve(SYM_ENTITYIO_NEWBYID);
pEntityMoveTo = resolver.Resolve(SYM_ENTITY_MOVETO);
pEntityCheckInsideTiles = resolver.Resolve(SYM_ENTITY_CHECKINSIDETILES);
pEntitySetPos = resolver.Resolve(SYM_ENTITY_SETPOS);
pEntityGetLookAngle = resolver.Resolve(SYM_LIVINGENTITY_GETLOOKANGLE);
pLivingEntityGetPos = resolver.Resolve(SYM_LIVINGENTITY_GETPOS);
@@ -524,8 +590,10 @@ void EntitySymbols::Log() const
LogSym("ServerPlayerGameMode::useItemOn", pServerPlayerGameModeUseItemOn);
LogSym("MultiPlayerGameMode::useItemOn", pMultiPlayerGameModeUseItemOn);
LogSym("Level::addEntity", pLevelAddEntity);
LogSym("Entity::playStepSound", pEntityPlayStepSound);
LogSym("EntityIO::newById", pEntityIONewById);
LogSym("Entity::moveTo", pEntityMoveTo);
LogSym("Entity::checkInsideTiles", pEntityCheckInsideTiles);
LogSym("Entity::setPos", pEntitySetPos);
LogSym("LivingEntity/Entity::getLookAngle", pEntityGetLookAngle);
LogSym("LivingEntity::getPos", pLivingEntityGetPos);

View File

@@ -81,6 +81,10 @@ struct ItemSymbols
void* pItemInstanceSave = nullptr;
void* pItemInstanceLoad = nullptr;
void* pItemInstanceHurtAndBreak = nullptr;
void* pItemInstanceInventoryTick = nullptr;
void* pItemInstanceOnCraftedBy = nullptr;
void* pItemInstanceInteractEnemy = nullptr;
void* pItemInstanceHurtEnemy = nullptr;
void* pItemMineBlock = nullptr;
void* pDiggerItemMineBlock = nullptr;
void* pPickaxeItemGetDestroySpeed = nullptr;
@@ -101,6 +105,16 @@ struct TileSymbols
void* pTileOnPlace = nullptr;
void* pTileNeighborChanged = nullptr;
void* pTileTick = nullptr;
void* pTileUse = nullptr;
void* pTileStepOn = nullptr;
void* pTileEntityInside = nullptr;
void* pTileFallOn = nullptr;
void* pTileOnRemoving = nullptr;
void* pTileOnRemove = nullptr;
void* pTileDestroy = nullptr;
void* pTilePlayerDestroy = nullptr;
void* pTilePlayerWillDestroy = nullptr;
void* pTileSetPlacedBy = nullptr;
void* pTileGetResource = nullptr;
void* pTileGetPlacedOnFaceDataValue = nullptr;
void* pTileCloneTileId = nullptr;
@@ -173,9 +187,11 @@ struct EntitySymbols
void* pServerPlayerGameModeUseItemOn = nullptr;
void* pMultiPlayerGameModeUseItemOn = nullptr;
void* pLevelAddEntity = nullptr;
void* pEntityPlayStepSound = nullptr;
void* pEntityIONewById = nullptr;
void* pEntityMoveTo = nullptr;
void* pEntitySetPos = nullptr;
void* pEntityCheckInsideTiles = nullptr;
void* pEntityGetLookAngle = nullptr;
void* pLivingEntityGetPos = nullptr;
void* pLivingEntityGetViewVector = nullptr;