Entity.Pos compatibility for API break between 1.21 and 1.22

This commit is contained in:
2026-03-13 08:18:33 +01:00
parent 4b613123b1
commit a5f2f8d1ae
3 changed files with 58 additions and 2 deletions

View File

@@ -242,7 +242,7 @@ internal partial class PreviewTargetProvider(ICoreClientAPI api, Config config)
if (player == null || playerEntity == null) if (player == null || playerEntity == null)
return false; return false;
return inventory.CanPlayerAccess(player, playerEntity.Pos); return inventory.CanPlayerAccess(player, playerEntity.GetPos());
} }
private bool IsContainerAllowed(Block block) private bool IsContainerAllowed(Block block)

View File

@@ -1,6 +1,7 @@
using Cairo; using Cairo;
using ChestPreview.Configs; using ChestPreview.Configs;
using ChestPreview.Models; using ChestPreview.Models;
using ChestPreview.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@@ -50,7 +51,7 @@ internal class CardRenderer(ICoreClientAPI api, Config config) : IDisposable
IPlayer? player = api.World.Player; IPlayer? player = api.World.Player;
EntityPlayer? playerEntity = player?.Entity; EntityPlayer? playerEntity = player?.Entity;
if (player == null || playerEntity == null || !inventory.CanPlayerAccess(player, playerEntity.Pos)) if (player == null || playerEntity == null || !inventory.CanPlayerAccess(player, playerEntity.GetPos()))
return false; return false;
string targetKey = CreateTargetKey(target.BlockEntity.Pos); string targetKey = CreateTargetKey(target.BlockEntity.Pos);

View File

@@ -0,0 +1,55 @@
using System;
using Vintagestory.API.Common.Entities;
namespace ChestPreview.Utils;
/// <summary>
/// When the mod is built for 1.21, and then you run it with 1.22, this happens when referring to Entity.Pos,
/// which was changed from a field to a property (and that being the reason this happens):
///
/// System.MissingFieldException: Field not found: 'Vintagestory.API.Common.Entities.Entity.Pos'.
/// at MonoMod.Core.Interop.CoreCLR.V60.InvokeCompileMethod(IntPtr functionPtr, IntPtr thisPtr, IntPtr corJitInfo, CORINFO_METHOD_INFO* methodInfo, UInt32 flags, Byte** nativeEntry, UInt32* nativeSizeOfCode)
/// at MonoMod.Core.Platforms.Runtimes.Core60Runtime.JitHookDelegateHolder.CompileMethodHook(IntPtr jit, IntPtr corJitInfo, CORINFO_METHOD_INFO* methodInfo, UInt32 flags, Byte** nativeEntry, UInt32* nativeSizeOfCode)
/// at ChestPreview.PreviewTargetProvider.CanAccessContainer(BlockEntity blockEntity) in E:\Code\VintageStory\ChestPreview\ChestPreview\ChestPreview\PreviewTargetProvider.cs:line 237
///
/// the PreviewTargetProvider.cs: around line 237 looks like this:
///
/// private bool CanAccessContainer(BlockEntity blockEntity)
/// {
/// if (blockEntity is not IBlockEntityContainer container || container.Inventory is not InventoryBase inventory)
/// return false;
///
/// IPlayer? player = api.World.Player;
/// EntityPlayer? playerEntity = player?.Entity;
/// if (player == null || playerEntity == null)
/// return false;
///
/// return inventory.CanPlayerAccess(player, playerEntity.Pos);
/// }
///
/// it happens because of the last line where we refer to "playerEntity.Pos" - this field doesn't exist now, because it's a property.
///
/// This class uses reflection to refer to either the property, or the field, whichever actually exists.
/// </summary>
internal static class CompatibilityUtil
{
#if !VERSION22
private static Func<Entity, EntityPos>? _getPos;
private static Func<Entity, EntityPos> GetPosFunc => _getPos ??= BuildGetPos();
private static Func<Entity, EntityPos> BuildGetPos()
{
var prop = typeof(Entity).GetProperty("Pos");
if (prop != null)
return (Func<Entity, EntityPos>)Delegate.CreateDelegate(typeof(Func<Entity, EntityPos>), prop.GetGetMethod()!);
var field = typeof(Entity).GetField("Pos")!;
return entity => (EntityPos)field.GetValue(entity)!;
}
public static EntityPos GetPos(this Entity entity) => GetPosFunc(entity);
#else
public static EntityPos GetPos(this Entity entity) => entity.Pos;
#endif
}