mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-04-25 16:37:21 +00:00
1226 lines
43 KiB
C#
1226 lines
43 KiB
C#
using System.Runtime.InteropServices;
|
|
using Minecraft.Server.FourKit.Block;
|
|
using Minecraft.Server.FourKit.Entity;
|
|
using Minecraft.Server.FourKit.Event;
|
|
using Minecraft.Server.FourKit.Event.Block;
|
|
using Minecraft.Server.FourKit.Event.Entity;
|
|
using Minecraft.Server.FourKit.Event.Player;
|
|
using Minecraft.Server.FourKit.Event.Inventory;
|
|
using Minecraft.Server.FourKit.Inventory;
|
|
|
|
namespace Minecraft.Server.FourKit;
|
|
|
|
// todo: clean up
|
|
|
|
public static class FourKitHost
|
|
{
|
|
private static PluginLoader? s_loader;
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void Initialize()
|
|
{
|
|
try
|
|
{
|
|
ServerLog.Info("fourkit", "Initializing plugin system...");
|
|
|
|
string pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins");
|
|
s_loader = new PluginLoader();
|
|
s_loader.LoadPlugins(pluginsDir);
|
|
s_loader.EnableAll();
|
|
|
|
ServerLog.Info("fourkit", "Plugin system ready.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"Initialize failed: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void FirePlayerJoin(int entityId, IntPtr namePtr, int nameByteLen, IntPtr uuidPtr, int uuidByteLen)
|
|
{
|
|
try
|
|
{
|
|
string name = nameByteLen > 0
|
|
? Marshal.PtrToStringUTF8(namePtr, nameByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
string uuidStr = uuidByteLen > 0
|
|
? Marshal.PtrToStringUTF8(uuidPtr, uuidByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
var player = FourKit.TrackPlayer(entityId, name);
|
|
player.SetPlayerUniqueIdInternal(ParseOrHashGuid(uuidStr));
|
|
SyncPlayerFromNative(player);
|
|
var evt = new PlayerJoinEvent(player);
|
|
FourKit.FireEvent(evt);
|
|
BroadcastNativeMessage(evt.getJoinMessage());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerJoin error: {ex}");
|
|
}
|
|
}
|
|
|
|
private static Guid ParseOrHashGuid(string s)
|
|
{
|
|
if (Guid.TryParse(s, out var g)) return g;
|
|
if (string.IsNullOrEmpty(s)) return Guid.NewGuid();
|
|
return new Guid(System.Security.Cryptography.MD5.HashData(System.Text.Encoding.UTF8.GetBytes(s)));
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void FirePlayerQuit(int entityId)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.UntrackPlayer(entityId);
|
|
if (player != null)
|
|
{
|
|
SyncPlayerFromNative(player);
|
|
var evt = new PlayerQuitEvent(player);
|
|
FourKit.FireEvent(evt);
|
|
BroadcastNativeMessage(evt.getQuitMessage());
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerQuit error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerKick(int entityId, int disconnectReason,
|
|
IntPtr reasonPtr, int reasonByteLen,
|
|
IntPtr outBuf, int outBufSize, IntPtr outLenPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var reason = Enum.IsDefined(typeof(DisconnectReason), disconnectReason)
|
|
? (DisconnectReason)disconnectReason
|
|
: DisconnectReason.NONE;
|
|
|
|
string defaultLeave = $"{player.getName()} was kicked from the game";
|
|
var evt = new PlayerKickEvent(player, reason, defaultLeave);
|
|
FourKit.FireEvent(evt);
|
|
|
|
if (evt.isCancelled())
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 1;
|
|
}
|
|
|
|
string leaveMessage = evt.getLeaveMessage();
|
|
if (!string.IsNullOrEmpty(leaveMessage))
|
|
{
|
|
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(leaveMessage);
|
|
if (utf8Bytes.Length < outBufSize)
|
|
{
|
|
Marshal.Copy(utf8Bytes, 0, outBuf, utf8Bytes.Length);
|
|
Marshal.WriteInt32(outLenPtr, utf8Bytes.Length);
|
|
}
|
|
else
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerKick error: {ex}");
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerMove(int entityId,
|
|
double fromX, double fromY, double fromZ,
|
|
double toX, double toY, double toZ,
|
|
IntPtr outCoords)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var from = new Location(fromX, fromY, fromZ);
|
|
var to = new Location(toX, toY, toZ);
|
|
var evt = new PlayerMoveEvent(player, from, to);
|
|
FourKit.FireEvent(evt);
|
|
|
|
var finalTo = evt.getTo();
|
|
Marshal.Copy(new double[] { finalTo.X, finalTo.Y, finalTo.Z }, 0, outCoords, 3);
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerMove error: {ex}");
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void UpdatePlayerEntityId(int oldEntityId, int newEntityId)
|
|
{
|
|
try
|
|
{
|
|
FourKit.UpdatePlayerEntityId(oldEntityId, newEntityId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"UpdatePlayerEntityId error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerChat(int entityId, IntPtr msgPtr, int msgByteLen,
|
|
IntPtr outBuf, int outBufSize, IntPtr outLenPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
string message = msgByteLen > 0
|
|
? Marshal.PtrToStringUTF8(msgPtr, msgByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
var evt = new PlayerChatEvent(player, message);
|
|
FourKit.FireEvent(evt);
|
|
|
|
if (evt.isCancelled())
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 1;
|
|
}
|
|
|
|
string formatted = JavaFormat(evt.getFormat(), player.getDisplayName(), evt.getMessage());
|
|
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(formatted);
|
|
if (utf8Bytes.Length < outBufSize)
|
|
{
|
|
Marshal.Copy(utf8Bytes, 0, outBuf, utf8Bytes.Length);
|
|
Marshal.WriteInt32(outLenPtr, utf8Bytes.Length);
|
|
}
|
|
else
|
|
{
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerChat error: {ex}");
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireBlockPlace(int entityId, int dimId,
|
|
int placedX, int placedY, int placedZ,
|
|
int againstX, int againstY, int againstZ,
|
|
int itemId, int itemCount, int canBuild)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
return 0;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var world = FourKit.getWorld(dimId);
|
|
var placedBlock = new Block.Block(world, placedX, placedY, placedZ);
|
|
var againstBlock = new Block.Block(world, againstX, againstY, againstZ);
|
|
|
|
Material mat = Enum.IsDefined(typeof(Material), itemId) ? (Material)itemId : Material.AIR;
|
|
var itemInHand = new ItemStack(mat, itemCount);
|
|
|
|
var evt = new BlockPlaceEvent(placedBlock, againstBlock, itemInHand, player, canBuild != 0);
|
|
FourKit.FireEvent(evt);
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireBlockPlace error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireBlockBreak(int entityId, int dimId,
|
|
int x, int y, int z, int tileId, int data, int exp)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
return exp;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var world = FourKit.getWorld(dimId);
|
|
var block = new Block.Block(world, x, y, z);
|
|
|
|
var evt = new BlockBreakEvent(block, player, exp);
|
|
FourKit.FireEvent(evt);
|
|
|
|
if (evt.isCancelled())
|
|
return -1;
|
|
|
|
return evt.getExpToDrop();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireBlockBreak error: {ex}");
|
|
return exp;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireEntityDamage(int entityId, int entityTypeId, int dimId,
|
|
double x, double y, double z, int causeId, double damage, IntPtr outDamage,
|
|
int damagerEntityId, int damagerEntityTypeId,
|
|
double damagerX, double damagerY, double damagerZ)
|
|
{
|
|
try
|
|
{
|
|
Entity.Entity? entity = FourKit.GetPlayerByEntityId(entityId);
|
|
if (entity is Player player)
|
|
{
|
|
SyncPlayerFromNative(player);
|
|
}
|
|
else
|
|
{
|
|
var entityType = Enum.IsDefined(typeof(EntityType), entityTypeId)
|
|
? (EntityType)entityTypeId
|
|
: EntityType.UNKNOWN;
|
|
entity = new LivingEntity(entityId, entityType, dimId, x, y, z);
|
|
}
|
|
|
|
var cause = Enum.IsDefined(typeof(EntityDamageEvent.DamageCause), causeId)
|
|
? (EntityDamageEvent.DamageCause)causeId
|
|
: EntityDamageEvent.DamageCause.CUSTOM;
|
|
|
|
EntityDamageByEntityEvent? byEntityEvt = null;
|
|
if (damagerEntityId >= 0)
|
|
{
|
|
Entity.Entity? damager = FourKit.GetPlayerByEntityId(damagerEntityId);
|
|
if (damager is Player damagerPlayer)
|
|
{
|
|
SyncPlayerFromNative(damagerPlayer);
|
|
}
|
|
else
|
|
{
|
|
var damagerType = Enum.IsDefined(typeof(EntityType), damagerEntityTypeId)
|
|
? (EntityType)damagerEntityTypeId
|
|
: EntityType.UNKNOWN;
|
|
damager = new LivingEntity(damagerEntityId, damagerType, dimId, damagerX, damagerY, damagerZ);
|
|
}
|
|
byEntityEvt = new EntityDamageByEntityEvent(damager, entity!, cause, damage);
|
|
FourKit.FireEvent(byEntityEvt);
|
|
damage = byEntityEvt.getDamage();
|
|
}
|
|
|
|
var evt = new EntityDamageEvent(entity!, cause, damage);
|
|
if (byEntityEvt != null && byEntityEvt.isCancelled())
|
|
evt.setCancelled(true);
|
|
FourKit.FireEvent(evt);
|
|
|
|
Marshal.Copy(new double[] { evt.getDamage() }, 0, outDamage, 1);
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireEntityDamage error: {ex}");
|
|
Marshal.Copy(new double[] { damage }, 0, outDamage, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void Shutdown()
|
|
{
|
|
try
|
|
{
|
|
ServerLog.Info("fourkit", "Shutting down plugin system...");
|
|
s_loader?.DisableAll();
|
|
s_loader = null;
|
|
ServerLog.Info("fourkit", "Plugin system shut down.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"Shutdown error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireSignChange(int entityId, int dimId,
|
|
int x, int y, int z,
|
|
IntPtr line0Ptr, int line0Len,
|
|
IntPtr line1Ptr, int line1Len,
|
|
IntPtr line2Ptr, int line2Len,
|
|
IntPtr line3Ptr, int line3Len,
|
|
IntPtr outBuf, int outBufSize, IntPtr outLensPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
WriteSignOutLens(outLensPtr, [0, 0, 0, 0]);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
string[] lines =
|
|
[
|
|
line0Len > 0 ? Marshal.PtrToStringUTF8(line0Ptr, line0Len) ?? string.Empty : string.Empty,
|
|
line1Len > 0 ? Marshal.PtrToStringUTF8(line1Ptr, line1Len) ?? string.Empty : string.Empty,
|
|
line2Len > 0 ? Marshal.PtrToStringUTF8(line2Ptr, line2Len) ?? string.Empty : string.Empty,
|
|
line3Len > 0 ? Marshal.PtrToStringUTF8(line3Ptr, line3Len) ?? string.Empty : string.Empty,
|
|
];
|
|
|
|
var world = FourKit.getWorld(dimId);
|
|
var block = new Block.Block(world, x, y, z);
|
|
var evt = new Event.Block.SignChangeEvent(block, player, lines);
|
|
FourKit.FireEvent(evt);
|
|
|
|
int offset = 0;
|
|
int[] lens = new int[4];
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes(evt.getLine(i));
|
|
if (offset + utf8.Length <= outBufSize)
|
|
{
|
|
Marshal.Copy(utf8, 0, outBuf + offset, utf8.Length);
|
|
lens[i] = utf8.Length;
|
|
offset += utf8.Length;
|
|
}
|
|
}
|
|
WriteSignOutLens(outLensPtr, lens);
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireSignChange error: {ex}");
|
|
WriteSignOutLens(outLensPtr, [0, 0, 0, 0]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireEntityDeath(int entityId, int entityTypeId, int dimId,
|
|
double x, double y, double z, int exp)
|
|
{
|
|
try
|
|
{
|
|
var entityType = Enum.IsDefined(typeof(EntityType), entityTypeId)
|
|
? (EntityType)entityTypeId
|
|
: EntityType.UNKNOWN;
|
|
var entity = new LivingEntity(entityId, entityType, dimId, x, y, z);
|
|
|
|
var drops = new List<ItemStack>();
|
|
var evt = new EntityDeathEvent(entity, drops, exp);
|
|
FourKit.FireEvent(evt);
|
|
|
|
return evt.getDroppedExp();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireEntityDeath error: {ex}");
|
|
return exp;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerDeath(int entityId,
|
|
IntPtr deathMsgPtr, int deathMsgByteLen, int exp,
|
|
IntPtr outMsgBuf, int outMsgBufSize, IntPtr outMsgLenPtr, IntPtr outKeepInventoryPtr,
|
|
IntPtr outNewExpPtr, IntPtr outNewLevelPtr, IntPtr outKeepLevelPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outMsgLenPtr, 0);
|
|
Marshal.WriteInt32(outKeepInventoryPtr, 0);
|
|
Marshal.WriteInt32(outNewExpPtr, 0);
|
|
Marshal.WriteInt32(outNewLevelPtr, 0);
|
|
Marshal.WriteInt32(outKeepLevelPtr, 0);
|
|
return exp;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
string deathMessage = deathMsgByteLen > 0
|
|
? Marshal.PtrToStringUTF8(deathMsgPtr, deathMsgByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
var drops = new List<ItemStack>();
|
|
|
|
var playerEvt = new PlayerDeathEvent(player, drops, exp, deathMessage);
|
|
FourKit.FireEvent(playerEvt);
|
|
|
|
var entityEvt = new EntityDeathEvent(player, playerEvt.getDrops(), playerEvt.getDroppedExp());
|
|
FourKit.FireEvent(entityEvt);
|
|
|
|
int finalExp = entityEvt.getDroppedExp();
|
|
|
|
string finalMsg = playerEvt.getDeathMessage();
|
|
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(finalMsg);
|
|
if (utf8Bytes.Length < outMsgBufSize)
|
|
{
|
|
Marshal.Copy(utf8Bytes, 0, outMsgBuf, utf8Bytes.Length);
|
|
Marshal.WriteInt32(outMsgLenPtr, utf8Bytes.Length);
|
|
}
|
|
else
|
|
{
|
|
Marshal.WriteInt32(outMsgLenPtr, 0);
|
|
}
|
|
|
|
Marshal.WriteInt32(outKeepInventoryPtr, playerEvt.getKeepInventory() ? 1 : 0);
|
|
Marshal.WriteInt32(outNewExpPtr, playerEvt.getNewExp());
|
|
Marshal.WriteInt32(outNewLevelPtr, playerEvt.getNewLevel());
|
|
Marshal.WriteInt32(outKeepLevelPtr, playerEvt.getKeepLevel() ? 1 : 0);
|
|
|
|
return finalExp;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerDeath error: {ex}");
|
|
Marshal.WriteInt32(outMsgLenPtr, 0);
|
|
Marshal.WriteInt32(outKeepInventoryPtr, 0);
|
|
Marshal.WriteInt32(outNewExpPtr, 0);
|
|
Marshal.WriteInt32(outNewLevelPtr, 0);
|
|
Marshal.WriteInt32(outKeepLevelPtr, 0);
|
|
return exp;
|
|
}
|
|
}
|
|
|
|
private static void WriteSignOutLens(IntPtr ptr, int[] lens)
|
|
{
|
|
Marshal.Copy(lens, 0, ptr, 4);
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void SetNativeCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
|
|
{
|
|
try
|
|
{
|
|
NativeBridge.SetCallbacks(damage, setHealth, teleport, setGameMode, broadcastMessage, setFallDistance, getPlayerSnapshot, sendMessage, setWalkSpeed, teleportEntity);
|
|
ServerLog.Info("fourkit", "Native callbacks registered.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"SetNativeCallbacks error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void SetWorldCallbacks(IntPtr getTileId, IntPtr setTile, IntPtr setTileData, IntPtr breakBlock, IntPtr getHighestBlockY, IntPtr getWorldInfo, IntPtr setWorldTime, IntPtr setWeather, IntPtr createExplosion, IntPtr strikeLightning, IntPtr setSpawnLocation, IntPtr dropItem)
|
|
{
|
|
try
|
|
{
|
|
NativeBridge.SetWorldCallbacks(getTileId, setTile, setTileData, breakBlock, getHighestBlockY, getWorldInfo, setWorldTime, setWeather, createExplosion, strikeLightning, setSpawnLocation, dropItem);
|
|
//ServerLog.Info("fourkit", "World native callbacks registered.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"SetWorldCallbacks error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void SetPlayerCallbacks(IntPtr kickPlayer, IntPtr banPlayer, IntPtr banPlayerIp, IntPtr getPlayerAddress)
|
|
{
|
|
try
|
|
{
|
|
NativeBridge.SetPlayerCallbacks(kickPlayer, banPlayer, banPlayerIp, getPlayerAddress);
|
|
//ServerLog.Info("fourkit", "Player native callbacks registered.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"SetPlayerCallbacks error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer)
|
|
{
|
|
try
|
|
{
|
|
NativeBridge.SetInventoryCallbacks(getPlayerInventory, setPlayerInventorySlot, getContainerContents, setContainerSlot, getContainerViewerEntityIds, closeContainer, openVirtualContainer);
|
|
//ServerLog.Info("fourkit", "Inventory native callbacks registered.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"SetInventoryCallbacks error: {ex}");
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static long FirePlayerDropItem(int entityId, int itemId, int itemCount, int itemAux,
|
|
IntPtr outItemIdPtr, IntPtr outItemCountPtr, IntPtr outItemAuxPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outItemIdPtr, itemId);
|
|
Marshal.WriteInt32(outItemCountPtr, itemCount);
|
|
Marshal.WriteInt32(outItemAuxPtr, itemAux);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
Material mat = Enum.IsDefined(typeof(Material), itemId) ? (Material)itemId : Material.AIR;
|
|
var itemStack = new ItemStack(mat, itemCount, (short)itemAux);
|
|
|
|
var evt = new PlayerDropItemEvent(player, itemStack);
|
|
FourKit.FireEvent(evt);
|
|
|
|
var result = evt.getItemDrop();
|
|
Marshal.WriteInt32(outItemIdPtr, result.getTypeId());
|
|
Marshal.WriteInt32(outItemCountPtr, result.getAmount());
|
|
Marshal.WriteInt32(outItemAuxPtr, result.getDurability());
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerDropItem error: {ex}");
|
|
Marshal.WriteInt32(outItemIdPtr, itemId);
|
|
Marshal.WriteInt32(outItemCountPtr, itemCount);
|
|
Marshal.WriteInt32(outItemAuxPtr, itemAux);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerInteract(int entityId, int action,
|
|
int itemId, int itemCount, int itemAux,
|
|
int clickedX, int clickedY, int clickedZ,
|
|
int blockFace, int dimId,
|
|
IntPtr outUseItemInHandPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outUseItemInHandPtr, 1);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var actionEnum = Enum.IsDefined(typeof(Block.Action), action)
|
|
? (Block.Action)action
|
|
: Block.Action.RIGHT_CLICK_AIR;
|
|
|
|
var faceEnum = Enum.IsDefined(typeof(Block.BlockFace), blockFace)
|
|
? (Block.BlockFace)blockFace
|
|
: Block.BlockFace.SELF;
|
|
|
|
ItemStack? itemStack = null;
|
|
if (itemId > 0)
|
|
itemStack = new ItemStack(itemId, itemCount, (short)itemAux);
|
|
|
|
Block.Block? clickedBlock = null;
|
|
bool hasBlock = actionEnum == Block.Action.LEFT_CLICK_BLOCK
|
|
|| actionEnum == Block.Action.RIGHT_CLICK_BLOCK
|
|
|| actionEnum == Block.Action.PHYSICAL;
|
|
if (hasBlock)
|
|
{
|
|
var world = FourKit.getWorld(dimId);
|
|
if (world != null)
|
|
clickedBlock = new Block.Block(world, clickedX, clickedY, clickedZ);
|
|
}
|
|
|
|
var evt = new PlayerInteractEvent(
|
|
player, actionEnum, itemStack, clickedBlock, faceEnum);
|
|
FourKit.FireEvent(evt);
|
|
|
|
Marshal.WriteInt32(outUseItemInHandPtr, evt.useItemInHand() ? 1 : 0);
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerInteract error: {ex}");
|
|
Marshal.WriteInt32(outUseItemInHandPtr, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerInteractEntity(int playerEntityId,
|
|
int targetEntityId, int targetEntityTypeId,
|
|
int dimId, double targetX, double targetY, double targetZ,
|
|
float targetHealth, float targetMaxHealth, float targetEyeHeight)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(playerEntityId);
|
|
if (player == null)
|
|
return 0;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
Entity.Entity? target = FourKit.GetPlayerByEntityId(targetEntityId);
|
|
if (target is Player targetPlayer)
|
|
{
|
|
SyncPlayerFromNative(targetPlayer);
|
|
}
|
|
else
|
|
{
|
|
var entityType = Enum.IsDefined(typeof(EntityType), targetEntityTypeId)
|
|
? (EntityType)targetEntityTypeId
|
|
: EntityType.UNKNOWN;
|
|
var living = new LivingEntity(targetEntityId, entityType, dimId, targetX, targetY, targetZ,
|
|
targetHealth, targetMaxHealth);
|
|
living.SetEyeHeightInternal(targetEyeHeight);
|
|
target = living;
|
|
}
|
|
|
|
var evt = new PlayerInteractEntityEvent(player, target);
|
|
FourKit.FireEvent(evt);
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerInteractEntity error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerPickupItem(int playerEntityId,
|
|
int itemEntityId, int dimId, double itemX, double itemY, double itemZ,
|
|
int itemId, int itemCount, int itemAux, int remaining,
|
|
IntPtr outItemIdPtr, IntPtr outItemCountPtr, IntPtr outItemAuxPtr)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(playerEntityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.WriteInt32(outItemIdPtr, itemId);
|
|
Marshal.WriteInt32(outItemCountPtr, itemCount);
|
|
Marshal.WriteInt32(outItemAuxPtr, itemAux);
|
|
// todo: fix
|
|
return 1;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var itemStack = new Inventory.ItemStack(itemId, itemCount, (short)itemAux);
|
|
var item = new Entity.Item(itemEntityId, dimId, itemX, itemY, itemZ, itemStack);
|
|
var evt = new PlayerPickupItemEvent(player, item, remaining);
|
|
FourKit.FireEvent(evt);
|
|
|
|
var result = evt.getItem().getItemStack();
|
|
Marshal.WriteInt32(outItemIdPtr, result.getTypeId());
|
|
Marshal.WriteInt32(outItemCountPtr, result.getAmount());
|
|
Marshal.WriteInt32(outItemAuxPtr, result.getDurability());
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerPickupItem error: {ex}");
|
|
Marshal.WriteInt32(outItemIdPtr, itemId);
|
|
Marshal.WriteInt32(outItemCountPtr, itemCount);
|
|
Marshal.WriteInt32(outItemAuxPtr, itemAux);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireInventoryOpen(int entityId, int nativeContainerType,
|
|
IntPtr titlePtr, int titleByteLen, int containerSize)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null) return 0;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
string title = titleByteLen > 0
|
|
? Marshal.PtrToStringUTF8(titlePtr, titleByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
InventoryType invType = MapNativeContainerType(nativeContainerType);
|
|
Inventory.Inventory topInv = CreateContainerInventory(invType, nativeContainerType, title, containerSize, entityId);
|
|
var bottomInv = player.getInventory();
|
|
|
|
var view = new InventoryView(topInv, bottomInv, player, invType);
|
|
var evt = new InventoryOpenEvent(view);
|
|
FourKit.FireEvent(evt);
|
|
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireInventoryOpen error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int HandlePlayerCommand(int entityId, IntPtr cmdUtf8, int cmdByteLen)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
return 0;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
string commandLine = cmdByteLen > 0
|
|
? Marshal.PtrToStringUTF8(cmdUtf8, cmdByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
if (string.IsNullOrEmpty(commandLine))
|
|
return 0;
|
|
|
|
return FourKit.DispatchCommand(player, commandLine) ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"HandlePlayerCommand error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int HandleConsoleCommand(IntPtr cmdUtf8, int cmdByteLen)
|
|
{
|
|
try
|
|
{
|
|
string commandLine = cmdByteLen > 0
|
|
? Marshal.PtrToStringUTF8(cmdUtf8, cmdByteLen) ?? string.Empty
|
|
: string.Empty;
|
|
|
|
if (string.IsNullOrEmpty(commandLine))
|
|
return 0;
|
|
|
|
string trimmed = commandLine.StartsWith('/') ? commandLine[1..] : commandLine;
|
|
string[] parts = trimmed.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
if (parts.Length == 0)
|
|
return 0;
|
|
|
|
if (!FourKit.HasCommand(parts[0]))
|
|
return 0;
|
|
|
|
FourKit.DispatchCommand(FourKit.getConsoleSender(), commandLine);
|
|
return 1;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"HandleConsoleCommand error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int GetPluginCommandHelp(IntPtr outBuf, int outBufSize, IntPtr outLenPtr)
|
|
{
|
|
try
|
|
{
|
|
var entries = FourKit.GetRegisteredCommandHelp();
|
|
if (entries.Count == 0)
|
|
{
|
|
if (outLenPtr != IntPtr.Zero)
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
|
|
var sb = new System.Text.StringBuilder();
|
|
foreach (var (usage, description) in entries)
|
|
{
|
|
sb.Append(usage);
|
|
sb.Append('\0');
|
|
sb.Append(description);
|
|
sb.Append('\0');
|
|
}
|
|
|
|
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
|
|
int copyLen = Math.Min(utf8.Length, outBufSize);
|
|
if (outBuf != IntPtr.Zero && copyLen > 0)
|
|
Marshal.Copy(utf8, 0, outBuf, copyLen);
|
|
if (outLenPtr != IntPtr.Zero)
|
|
Marshal.WriteInt32(outLenPtr, copyLen);
|
|
|
|
return entries.Count;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"GetPluginCommandHelp error: {ex}");
|
|
if (outLenPtr != IntPtr.Zero)
|
|
Marshal.WriteInt32(outLenPtr, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private static InventoryType MapNativeContainerType(int nativeType)
|
|
{
|
|
return nativeType switch
|
|
{
|
|
0 => InventoryType.CHEST, // CONTAINER
|
|
1 => InventoryType.WORKBENCH, // WORKBENCH
|
|
2 => InventoryType.FURNACE, // FURNACE
|
|
3 => InventoryType.DISPENSER, // TRAP (dispenser)
|
|
4 => InventoryType.ENCHANTING, // ENCHANTMENT
|
|
5 => InventoryType.BREWING, // BREWING_STAND
|
|
6 => InventoryType.MERCHANT, // TRADER_NPC
|
|
7 => InventoryType.BEACON, // BEACON
|
|
8 => InventoryType.ANVIL, // REPAIR_TABLE
|
|
9 => InventoryType.HOPPER, // HOPPER
|
|
10 => InventoryType.DROPPER, // DROPPER
|
|
11 => InventoryType.CHEST, // HORSE
|
|
12 => InventoryType.WORKBENCH, // FIREWORKS
|
|
13 => InventoryType.CHEST, // BONUS_CHEST
|
|
14 => InventoryType.CHEST, // LARGE_CHEST
|
|
15 => InventoryType.ENDER_CHEST, // ENDER_CHEST
|
|
16 => InventoryType.CHEST, // MINECART_CHEST
|
|
17 => InventoryType.HOPPER, // MINECART_HOPPER
|
|
_ => InventoryType.CHEST,
|
|
};
|
|
}
|
|
|
|
private static Inventory.Inventory CreateContainerInventory(InventoryType invType, int nativeType, string title, int containerSize, int entityId)
|
|
{
|
|
string name = string.IsNullOrEmpty(title) ? invType.getDefaultTitle() : title;
|
|
int size = containerSize > 0 ? containerSize : invType.getDefaultSize();
|
|
|
|
return invType switch
|
|
{
|
|
InventoryType.FURNACE => new Inventory.FurnaceInventory(name, size, entityId),
|
|
InventoryType.BEACON => new Inventory.BeaconInventory(name, size, entityId),
|
|
InventoryType.ENCHANTING => new Inventory.EnchantingInventory(name, size, entityId),
|
|
_ => nativeType switch
|
|
{
|
|
11 => new Inventory.HorseInventory(name, size, entityId), // HORSE
|
|
14 => new Inventory.DoubleChestInventory(name, size, entityId), // LARGE_CHEST
|
|
_ => new Inventory.Inventory(name, invType, size, entityId),
|
|
}
|
|
};
|
|
}
|
|
|
|
// double[11] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension }
|
|
private static void SyncPlayerFromNative(Player player)
|
|
{
|
|
if (NativeBridge.GetPlayerSnapshot == null)
|
|
return;
|
|
double[] buf = new double[11];
|
|
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
NativeBridge.GetPlayerSnapshot(player.getEntityId(), gh.AddrOfPinnedObject());
|
|
}
|
|
finally
|
|
{
|
|
gh.Free();
|
|
}
|
|
int dimId = (int)buf[10];
|
|
player.SetDimensionInternal(dimId);
|
|
var world = FourKit.getWorld(dimId);
|
|
player.SetLocation(new Location(world, buf[0], buf[1], buf[2], (float)buf[8], (float)buf[9]));
|
|
player.SetHealthInternal(buf[3]);
|
|
player.SetMaxHealthInternal(buf[4]);
|
|
player.SetFallDistanceInternal((float)buf[5]);
|
|
player.SetGameModeInternal((GameMode)(int)buf[6]);
|
|
player.SetWalkSpeedInternal((float)buf[7]);
|
|
}
|
|
|
|
private static void BroadcastNativeMessage(string message)
|
|
{
|
|
if (string.IsNullOrEmpty(message) || NativeBridge.BroadcastMessage == null)
|
|
return;
|
|
IntPtr ptr = Marshal.StringToCoTaskMemUTF8(message);
|
|
try
|
|
{
|
|
NativeBridge.BroadcastMessage(ptr, System.Text.Encoding.UTF8.GetByteCount(message));
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeCoTaskMem(ptr);
|
|
}
|
|
}
|
|
|
|
private static string JavaFormat(string format, params string[] args)
|
|
{
|
|
// hack to replicate java bukkit format
|
|
var sb = new System.Text.StringBuilder(format.Length + 64);
|
|
int seqIndex = 0;
|
|
|
|
for (int i = 0; i < format.Length; i++)
|
|
{
|
|
char c = format[i];
|
|
if (c == '%' && i + 1 < format.Length)
|
|
{
|
|
char next = format[i + 1];
|
|
|
|
if (next == '%')
|
|
{
|
|
sb.Append('%');
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
if (next == 's')
|
|
{
|
|
if (seqIndex < args.Length)
|
|
sb.Append(args[seqIndex]);
|
|
seqIndex++;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
if (char.IsDigit(next))
|
|
{
|
|
int numStart = i + 1;
|
|
int j = numStart;
|
|
while (j < format.Length && char.IsDigit(format[j]))
|
|
j++;
|
|
|
|
if (j + 1 < format.Length && format[j] == '$' && format[j + 1] == 's')
|
|
{
|
|
int argIndex = int.Parse(format.AsSpan(numStart, j - numStart)) - 1;
|
|
if (argIndex >= 0 && argIndex < args.Length)
|
|
sb.Append(args[argIndex]);
|
|
i = j + 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
sb.Append(c);
|
|
}
|
|
else
|
|
{
|
|
sb.Append(c);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerTeleport(int entityId,
|
|
double fromX, double fromY, double fromZ, int fromDimId,
|
|
double toX, double toY, double toZ, int toDimId,
|
|
int cause, IntPtr outCoords)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var fromWorld = FourKit.getWorld(fromDimId);
|
|
var toWorld = FourKit.getWorld(toDimId);
|
|
var from = new Location(fromWorld, fromX, fromY, fromZ);
|
|
var to = new Location(toWorld, toX, toY, toZ);
|
|
var teleportCause = cause >= 0 && cause <= (int)PlayerTeleportEvent.TeleportCause.UNKNOWN
|
|
? (PlayerTeleportEvent.TeleportCause)cause
|
|
: PlayerTeleportEvent.TeleportCause.UNKNOWN;
|
|
|
|
var evt = new PlayerTeleportEvent(player, from, to, teleportCause);
|
|
FourKit.FireEvent(evt);
|
|
|
|
var finalTo = evt.getTo();
|
|
Marshal.Copy(new double[] { finalTo.X, finalTo.Y, finalTo.Z }, 0, outCoords, 3);
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerTeleport error: {ex}");
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FirePlayerPortal(int entityId,
|
|
double fromX, double fromY, double fromZ, int fromDimId,
|
|
double toX, double toY, double toZ, int toDimId,
|
|
int cause, IntPtr outCoords)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null)
|
|
{
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
var fromWorld = FourKit.getWorld(fromDimId);
|
|
var toWorld = FourKit.getWorld(toDimId);
|
|
var from = new Location(fromWorld, fromX, fromY, fromZ);
|
|
var to = new Location(toWorld, toX, toY, toZ);
|
|
var teleportCause = cause >= 0 && cause <= (int)PlayerTeleportEvent.TeleportCause.UNKNOWN
|
|
? (PlayerTeleportEvent.TeleportCause)cause
|
|
: PlayerTeleportEvent.TeleportCause.UNKNOWN;
|
|
|
|
var evt = new PlayerPortalEvent(player, from, to, teleportCause);
|
|
FourKit.FireEvent(evt);
|
|
|
|
var finalTo = evt.getTo();
|
|
Marshal.Copy(new double[] { finalTo.X, finalTo.Y, finalTo.Z }, 0, outCoords, 3);
|
|
return evt.isCancelled() ? 1 : 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FirePlayerPortal error: {ex}");
|
|
Marshal.Copy(new double[] { toX, toY, toZ }, 0, outCoords, 3);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
[UnmanagedCallersOnly]
|
|
public static int FireInventoryClick(int entityId, int slot, int button, int clickType,
|
|
int nativeContainerType, int containerSize, IntPtr titleUtf8Ptr, int titleByteLen)
|
|
{
|
|
try
|
|
{
|
|
var player = FourKit.GetPlayerByEntityId(entityId);
|
|
if (player == null) return 0;
|
|
|
|
SyncPlayerFromNative(player);
|
|
|
|
ClickType click = MapNativeClickType(clickType, button);
|
|
InventoryAction action = DetermineInventoryAction(click, slot);
|
|
|
|
InventoryType invType;
|
|
Inventory.Inventory topInv;
|
|
if (nativeContainerType < 0)
|
|
{
|
|
invType = InventoryType.PLAYER;
|
|
topInv = player.getInventory();
|
|
}
|
|
else
|
|
{
|
|
invType = MapNativeContainerType(nativeContainerType);
|
|
int size = containerSize > 0 ? containerSize : invType.getDefaultSize();
|
|
string title = titleByteLen > 0 && titleUtf8Ptr != IntPtr.Zero
|
|
? Marshal.PtrToStringUTF8(titleUtf8Ptr, titleByteLen) ?? invType.getDefaultTitle()
|
|
: invType.getDefaultTitle();
|
|
topInv = CreateContainerInventory(invType, nativeContainerType, title, size, entityId);
|
|
}
|
|
|
|
var bottomInv = player.getInventory();
|
|
var view = new InventoryView(topInv, bottomInv, player, invType);
|
|
|
|
SlotType slotType = SlotType.CONTAINER;
|
|
if (slot == InventoryView.OUTSIDE)
|
|
slotType = SlotType.OUTSIDE;
|
|
|
|
Inventory.Inventory._slotModifiedByPlugin = false;
|
|
int hotbarKey = click == ClickType.NUMBER_KEY ? button : -1;
|
|
var evt = new InventoryClickEvent(view, slotType, slot, click, action, hotbarKey);
|
|
FourKit.FireEvent(evt);
|
|
|
|
if (evt.isCancelled()) return 1;
|
|
if (Inventory.Inventory._slotModifiedByPlugin) return 2;
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ServerLog.Error("fourkit", $"FireInventoryClick error: {ex}");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private static ClickType MapNativeClickType(int nativeClickType, int button)
|
|
{
|
|
return nativeClickType switch
|
|
{
|
|
0 => button == 0 ? ClickType.LEFT : ClickType.RIGHT, // CLICK
|
|
1 => button == 0 ? ClickType.SHIFT_LEFT : ClickType.SHIFT_RIGHT, // SHIFT_CLICK
|
|
2 => ClickType.NUMBER_KEY, // NUMBER_KEY
|
|
3 => ClickType.MIDDLE, // MIDDLE_CLICK
|
|
4 => button == 1 ? ClickType.CONTROL_DROP : ClickType.DROP, // DROP
|
|
5 => ClickType.UNKNOWN, // DRAG (does not exist lol but maybe in the future?)
|
|
6 => ClickType.DOUBLE_CLICK, // DOUBLE_CLICK
|
|
_ => ClickType.UNKNOWN,
|
|
};
|
|
}
|
|
|
|
private static InventoryAction DetermineInventoryAction(ClickType click, int slot)
|
|
{
|
|
if (slot == InventoryView.OUTSIDE)
|
|
{
|
|
return click switch
|
|
{
|
|
ClickType.LEFT => InventoryAction.DROP_ALL_CURSOR,
|
|
ClickType.RIGHT => InventoryAction.DROP_ONE_CURSOR,
|
|
ClickType.WINDOW_BORDER_LEFT => InventoryAction.DROP_ALL_CURSOR,
|
|
ClickType.WINDOW_BORDER_RIGHT => InventoryAction.DROP_ONE_CURSOR,
|
|
_ => InventoryAction.NOTHING,
|
|
};
|
|
}
|
|
|
|
return click switch
|
|
{
|
|
ClickType.LEFT => InventoryAction.PICKUP_ALL,
|
|
ClickType.RIGHT => InventoryAction.PICKUP_HALF,
|
|
ClickType.SHIFT_LEFT or ClickType.SHIFT_RIGHT => InventoryAction.MOVE_TO_OTHER_INVENTORY,
|
|
ClickType.NUMBER_KEY => InventoryAction.HOTBAR_SWAP,
|
|
ClickType.MIDDLE => InventoryAction.CLONE_STACK,
|
|
ClickType.DROP => InventoryAction.DROP_ONE_SLOT,
|
|
ClickType.CONTROL_DROP => InventoryAction.DROP_ALL_SLOT,
|
|
ClickType.DOUBLE_CLICK => InventoryAction.COLLECT_TO_CURSOR,
|
|
_ => InventoryAction.UNKNOWN,
|
|
};
|
|
}
|
|
}
|