2026-03-21 13:52:26 -05:00
// todo: split into files for better readability
# include "FourKitBridge.h"
# include "Common/StringUtils.h"
# include "ServerLogger.h"
# include "stdafx.h"
# include <string>
# include <vector>
# include <windows.h>
# include "..\Minecraft.Client\MinecraftServer.h"
# include "..\Minecraft.Client\PlayerConnection.h"
# include "..\Minecraft.Client\PlayerList.h"
# include "..\Minecraft.Client\ServerConnection.h"
# include "..\Minecraft.Client\ServerLevel.h"
# include "..\Minecraft.Client\ServerPlayer.h"
# include "..\Minecraft.Client\ServerPlayerGameMode.h"
# include "..\Minecraft.Client\Windows64\Network\WinsockNetLayer.h"
# include "..\Minecraft.World\AbstractContainerMenu.h"
# include "..\Minecraft.World\AddGlobalEntityPacket.h"
# include "..\Minecraft.World\ArrayWithLength.h"
# include "..\Minecraft.World\Class.h"
# include "..\Minecraft.World\CompoundContainer.h"
# include "..\Minecraft.World\Connection.h"
# include "..\Minecraft.World\ContainerOpenPacket.h"
# include "..\Minecraft.World\DamageSource.h"
# include "..\Minecraft.World\EntityDamageSource.h"
# include "..\Minecraft.World\Explosion.h"
# include "..\Minecraft.World\IndirectEntityDamageSource.h"
# include "..\Minecraft.World\ItemEntity.h"
# include "..\Minecraft.World\ItemInstance.h"
# include "..\Minecraft.World\LevelData.h"
# include "..\Minecraft.World\LevelSettings.h"
# include "..\Minecraft.World\LightningBolt.h"
# include "..\Minecraft.World\Player.h"
# include "..\Minecraft.World\PlayerAbilitiesPacket.h"
# include "..\Minecraft.World\SimpleContainer.h"
# include "..\Minecraft.World\Slot.h"
# include "..\Minecraft.World\Tile.h"
# include "..\Minecraft.World\net.minecraft.world.entity.player.h"
# include "Access\Access.h"
# include "Common\NetworkUtils.h"
# include "ServerLogManager.h"
// gay
typedef void * hostfxr_handle ;
typedef int ( __cdecl * hostfxr_initialize_for_runtime_config_fn ) (
const wchar_t * runtime_config_path ,
const void * parameters ,
hostfxr_handle * host_context_handle ) ;
enum hostfxr_delegate_type
{
hdt_com_activation = 0 ,
hdt_load_in_memory_assembly = 1 ,
hdt_winrt_activation = 2 ,
hdt_com_register = 3 ,
hdt_com_unregister = 4 ,
hdt_load_assembly_and_get_function_pointer = 5 ,
hdt_get_function_pointer = 6 ,
} ;
typedef int ( __cdecl * hostfxr_get_runtime_delegate_fn ) (
const hostfxr_handle host_context_handle ,
hostfxr_delegate_type type ,
void * * delegate ) ;
typedef int ( __cdecl * hostfxr_close_fn ) ( const hostfxr_handle host_context_handle ) ;
2026-03-21 16:40:07 -05:00
struct hostfxr_initialize_parameters
{
size_t size ;
const wchar_t * host_path ;
const wchar_t * dotnet_root ;
} ;
2026-03-21 13:52:26 -05:00
# define UNMANAGEDCALLERSONLY_METHOD ((const wchar_t *)-1)
typedef int ( __stdcall * load_assembly_and_get_function_pointer_fn ) (
const wchar_t * assembly_path ,
const wchar_t * type_name ,
const wchar_t * method_name ,
const wchar_t * delegate_type_name ,
void * reserved ,
void * * delegate ) ;
using ServerRuntime : : LogDebugf ;
using ServerRuntime : : LogError ;
using ServerRuntime : : LogInfo ;
using ServerRuntime : : LogInfof ;
using ServerRuntime : : LogWarn ;
namespace FourKitBridge
{
static hostfxr_initialize_for_runtime_config_fn s_initFn = nullptr ;
static hostfxr_get_runtime_delegate_fn s_getDelegateFn = nullptr ;
static hostfxr_close_fn s_closeFn = nullptr ;
2026-03-21 16:40:07 -05:00
static std : : wstring s_dotnetRoot ;
2026-03-21 13:52:26 -05:00
typedef void ( __stdcall * fn_initialize ) ( ) ;
typedef void ( __stdcall * fn_fire_player_join ) ( int entityId , const char * nameUtf8 , int nameByteLen , const char * uuidUtf8 , int uuidByteLen ) ;
typedef void ( __stdcall * fn_fire_player_quit ) ( int entityId ) ;
typedef int ( __stdcall * fn_fire_player_kick ) ( int entityId , int disconnectReason ,
const char * reasonUtf8 , int reasonByteLen ,
char * outBuf , int outBufSize , int * outLen ) ;
typedef void ( __stdcall * fn_shutdown ) ( ) ;
typedef int ( __stdcall * fn_fire_player_move ) ( int entityId ,
double fromX , double fromY , double fromZ ,
double toX , double toY , double toZ ,
double * outCoords ) ;
typedef void ( __stdcall * fn_set_native_callbacks ) ( void * damage , void * setHealth , void * teleport , void * setGameMode , void * broadcastMessage , void * setFallDistance , void * getPlayerSnapshot , void * sendMessage , void * setWalkSpeed , void * teleportEntity ) ;
2026-03-22 02:37:45 -05:00
typedef void ( __stdcall * fn_set_world_callbacks ) ( void * getTileId , void * getTileData , void * setTile , void * setTileData , void * breakBlock , void * getHighestBlockY , void * getWorldInfo , void * setWorldTime , void * setWeather , void * createExplosion , void * strikeLightning , void * setSpawnLocation , void * dropItem ) ;
2026-03-21 13:52:26 -05:00
typedef void ( __stdcall * fn_update_entity_id ) ( int oldEntityId , int newEntityId ) ;
typedef int ( __stdcall * fn_fire_player_chat ) ( int entityId , const char * msgUtf8 , int msgByteLen , char * outBuf , int outBufSize , int * outLen ) ;
typedef int ( __stdcall * fn_fire_block_place ) ( int entityId , int dimId ,
int placedX , int placedY , int placedZ ,
int againstX , int againstY , int againstZ ,
int itemId , int itemCount , int canBuild ) ;
typedef int ( __stdcall * fn_fire_block_break ) ( int entityId , int dimId ,
int x , int y , int z , int tileId , int data , int exp ) ;
typedef int ( __stdcall * fn_fire_entity_damage ) ( int entityId , int entityTypeId , int dimId ,
double x , double y , double z , int causeId , double damage , double * outDamage ,
int damagerEntityId , int damagerEntityTypeId , double damagerX , double damagerY , double damagerZ ) ;
typedef int ( __stdcall * fn_fire_sign_change ) ( int entityId , int dimId ,
int x , int y , int z ,
const char * line0 , int line0Len ,
const char * line1 , int line1Len ,
const char * line2 , int line2Len ,
const char * line3 , int line3Len ,
char * outBuf , int outBufSize , int * outLens ) ;
typedef int ( __stdcall * fn_fire_entity_death ) ( int entityId , int entityTypeId , int dimId ,
double x , double y , double z , int exp ) ;
typedef int ( __stdcall * fn_fire_player_death ) ( int entityId ,
const char * deathMsgUtf8 , int deathMsgByteLen , int exp ,
char * outMsgBuf , int outMsgBufSize , int * outMsgLen , int * outKeepInventory ,
int * outNewExp , int * outNewLevel , int * outKeepLevel ) ;
typedef void ( __stdcall * fn_set_player_callbacks ) ( void * kickPlayer , void * banPlayer , void * banPlayerIp , void * getPlayerAddress ) ;
typedef long long ( __stdcall * fn_fire_player_drop_item ) ( int entityId ,
int itemId , int itemCount , int itemAux ,
int * outItemId , int * outItemCount , int * outItemAux ) ;
typedef void ( __stdcall * fn_set_inventory_callbacks ) ( void * getPlayerInventory , void * setPlayerInventorySlot , void * getContainerContents , void * setContainerSlot , void * getContainerViewerEntityIds , void * closeContainer , void * openVirtualContainer ) ;
typedef int ( __stdcall * fn_fire_player_interact ) ( int entityId , int action ,
int itemId , int itemCount , int itemAux ,
int clickedX , int clickedY , int clickedZ ,
int blockFace , int dimId ,
int * outUseItemInHand ) ;
typedef int ( __stdcall * fn_fire_player_interact_entity ) ( int playerEntityId ,
int targetEntityId , int targetEntityTypeId ,
int dimId , double targetX , double targetY , double targetZ ,
float targetHealth , float targetMaxHealth , float targetEyeHeight ) ;
typedef int ( __stdcall * fn_fire_player_pickup_item ) ( int playerEntityId ,
int itemEntityId , int dimId , double itemX , double itemY , double itemZ ,
int itemId , int itemCount , int itemAux , int remaining ,
int * outItemId , int * outItemCount , int * outItemAux ) ;
typedef int ( __stdcall * fn_fire_inventory_open ) ( int entityId , int nativeContainerType ,
const char * titleUtf8 , int titleByteLen , int containerSize ) ;
typedef int ( __stdcall * fn_handle_player_command ) ( int entityId ,
const char * cmdUtf8 , int cmdByteLen ) ;
typedef int ( __stdcall * fn_handle_console_command ) ( const char * cmdUtf8 , int cmdByteLen ) ;
typedef int ( __stdcall * fn_get_plugin_command_help ) ( char * outBuf , int outBufSize , int * outLen ) ;
typedef int ( __stdcall * fn_fire_player_teleport ) ( int entityId ,
double fromX , double fromY , double fromZ , int fromDimId ,
double toX , double toY , double toZ , int toDimId ,
int cause , double * outCoords ) ;
typedef int ( __stdcall * fn_fire_player_portal ) ( int entityId ,
double fromX , double fromY , double fromZ , int fromDimId ,
double toX , double toY , double toZ , int toDimId ,
int cause , double * outCoords ) ;
typedef int ( __stdcall * fn_fire_inventory_click ) ( int entityId ,
2026-03-22 15:48:51 -05:00
int slot , int button , int clickType , int nativeContainerType , int containerSize ,
const char * titleUtf8 , int titleByteLen ) ;
typedef int ( __stdcall * fn_fire_bed_enter ) ( int entityId , int dimId , int bedX , int bedY , int bedZ ) ;
typedef void ( __stdcall * fn_fire_bed_leave ) ( int entityId , int dimId , int bedX , int bedY , int bedZ ) ;
2026-03-21 13:52:26 -05:00
struct OpenContainerInfo
{
int type ;
int size ;
std : : wstring title ;
} ;
static std : : unordered_map < int , OpenContainerInfo > s_openContainerInfo ;
static fn_initialize s_managedInit = nullptr ;
static fn_fire_player_join s_managedFireJoin = nullptr ;
static fn_update_entity_id s_managedUpdateEntityId = nullptr ;
static fn_fire_player_quit s_managedFireQuit = nullptr ;
static fn_fire_player_kick s_managedFireKick = nullptr ;
static fn_shutdown s_managedShutdown = nullptr ;
static fn_fire_player_move s_managedFireMove = nullptr ;
static fn_set_native_callbacks s_managedSetCallbacks = nullptr ;
static fn_set_world_callbacks s_managedSetWorldCallbacks = nullptr ;
static fn_fire_player_chat s_managedFireChat = nullptr ;
static fn_fire_block_place s_managedFireBlockPlace = nullptr ;
static fn_fire_block_break s_managedFireBlockBreak = nullptr ;
static fn_fire_entity_damage s_managedFireEntityDamage = nullptr ;
static fn_fire_sign_change s_managedFireSignChange = nullptr ;
static fn_fire_entity_death s_managedFireEntityDeath = nullptr ;
static fn_fire_player_death s_managedFirePlayerDeath = nullptr ;
static fn_set_player_callbacks s_managedSetPlayerCallbacks = nullptr ;
static fn_fire_player_drop_item s_managedFirePlayerDropItem = nullptr ;
static fn_set_inventory_callbacks s_managedSetInventoryCallbacks = nullptr ;
static fn_fire_player_interact s_managedFirePlayerInteract = nullptr ;
static fn_fire_player_interact_entity s_managedFirePlayerInteractEntity = nullptr ;
static fn_fire_player_pickup_item s_managedFirePlayerPickupItem = nullptr ;
static fn_fire_inventory_open s_managedFireInventoryOpen = nullptr ;
static fn_handle_player_command s_managedHandlePlayerCommand = nullptr ;
static fn_handle_console_command s_managedHandleConsoleCommand = nullptr ;
static fn_get_plugin_command_help s_managedGetPluginCommandHelp = nullptr ;
static fn_fire_player_teleport s_managedFirePlayerTeleport = nullptr ;
static fn_fire_player_portal s_managedFirePlayerPortal = nullptr ;
static fn_fire_inventory_click s_managedFireInventoryClick = nullptr ;
2026-03-22 15:48:51 -05:00
static fn_fire_bed_enter s_managedFireBedEnter = nullptr ;
static fn_fire_bed_leave s_managedFireBedLeave = nullptr ;
2026-03-21 13:52:26 -05:00
static bool s_initialized = false ;
static shared_ptr < ServerPlayer > FindPlayer ( int entityId )
{
PlayerList * list = MinecraftServer : : getPlayerList ( ) ;
if ( ! list )
{
return nullptr ;
}
for ( auto & p : list - > players )
{
if ( p & & p - > entityId = = entityId )
{
return p ;
}
}
return nullptr ;
}
static shared_ptr < Entity > FindEntity ( int entityId )
{
MinecraftServer * srv = MinecraftServer : : getInstance ( ) ;
if ( ! srv )
{
return nullptr ;
}
const int dims [ ] = { 0 , - 1 , 1 } ;
for ( int dim : dims )
{
ServerLevel * level = srv - > getLevel ( dim ) ;
if ( ! level )
{
continue ;
}
shared_ptr < Entity > entity = level - > getEntity ( entityId ) ;
if ( entity )
{
return entity ;
}
}
return nullptr ;
}
static void __cdecl NativeDamagePlayer ( int entityId , float amount )
{
// todo: move entity hurt to a seperate func (NativeDamageEntity)
auto player = FindPlayer ( entityId ) ;
if ( player )
{
player - > hurt ( DamageSource : : genericSource , amount ) ;
return ;
}
auto entity = FindEntity ( entityId ) ;
if ( entity )
{
entity - > hurt ( DamageSource : : genericSource , amount ) ;
}
}
static void __cdecl NativeSetPlayerHealth ( int entityId , float health )
{
auto player = FindPlayer ( entityId ) ;
if ( player )
{
player - > setHealth ( health ) ;
}
}
static void __cdecl NativeTeleportPlayer ( int entityId , double x , double y , double z )
{
auto player = FindPlayer ( entityId ) ;
if ( player & & player - > connection )
{
double outX , outY , outZ ;
bool cancelled = FirePlayerTeleport ( entityId ,
player - > x , player - > y , player - > z , player - > dimension ,
x , y , z , player - > dimension ,
2 /* PLUGIN */ ,
& outX , & outY , & outZ ) ;
if ( ! cancelled )
{
player - > connection - > teleport ( outX , outY , outZ , player - > yRot , player - > xRot ) ;
}
}
}
static void __cdecl NativeSetPlayerGameMode ( int entityId , int gameMode )
{
auto player = FindPlayer ( entityId ) ;
if ( player & & player - > gameMode )
{
GameType * type = GameType : : byId ( gameMode ) ;
if ( type )
{
player - > setGameMode ( type ) ;
}
}
}
static void __cdecl NativeSetFallDistance ( int entityId , float distance )
{
auto player = FindPlayer ( entityId ) ;
if ( player )
{
player - > fallDistance = distance ;
}
}
2026-03-22 15:48:51 -05:00
// double[13] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer }
2026-03-21 13:52:26 -05:00
static void __cdecl NativeGetPlayerSnapshot ( int entityId , double * outData )
{
auto player = FindPlayer ( entityId ) ;
if ( ! player )
{
2026-03-22 15:48:51 -05:00
memset ( outData , 0 , 13 * sizeof ( double ) ) ;
2026-03-21 13:52:26 -05:00
outData [ 3 ] = 20.0 ;
outData [ 4 ] = 20.0 ;
outData [ 7 ] = 0.1 ;
return ;
}
outData [ 0 ] = player - > x ;
outData [ 1 ] = player - > y ;
outData [ 2 ] = player - > z ;
outData [ 3 ] = ( double ) player - > getHealth ( ) ;
outData [ 4 ] = ( double ) player - > getMaxHealth ( ) ;
outData [ 5 ] = ( double ) player - > fallDistance ;
GameType * gm = player - > gameMode ? player - > gameMode - > getGameModeForPlayer ( ) : GameType : : SURVIVAL ;
outData [ 6 ] = ( double ) ( gm ? gm - > getId ( ) : 0 ) ;
outData [ 7 ] = ( double ) player - > abilities . getWalkingSpeed ( ) ;
outData [ 8 ] = ( double ) player - > yRot ;
outData [ 9 ] = ( double ) player - > xRot ;
outData [ 10 ] = ( double ) player - > dimension ;
2026-03-22 15:48:51 -05:00
outData [ 11 ] = player - > isSleeping ( ) ? 1.0 : 0.0 ;
outData [ 12 ] = ( double ) player - > getSleepTimer ( ) ;
2026-03-21 13:52:26 -05:00
}
static void __cdecl NativeBroadcastMessage ( const char * utf8 , int len )
{
if ( ! utf8 | | len < = 0 )
{
return ;
}
std : : wstring wide = ServerRuntime : : StringUtils : : Utf8ToWide ( utf8 ) ;
if ( wide . empty ( ) )
{
return ;
}
PlayerList * list = MinecraftServer : : getPlayerList ( ) ;
if ( list )
{
list - > broadcastAll ( std : : make_shared < ChatPacket > ( wide ) ) ;
}
}
static void __cdecl NativeSendMessage ( int entityId , const char * utf8 , int len )
{
if ( ! utf8 | | len < = 0 )
{
return ;
}
auto player = FindPlayer ( entityId ) ;
if ( player & & player - > connection )
{
std : : wstring wide = ServerRuntime : : StringUtils : : Utf8ToWide ( utf8 ) ;
if ( ! wide . empty ( ) )
{
player - > connection - > send ( std : : make_shared < ChatPacket > ( wide ) ) ;
}
}
}
static void __cdecl NativeSetWalkSpeed ( int entityId , float speed )
{
auto player = FindPlayer ( entityId ) ;
if ( player )
{
player - > abilities . setWalkingSpeed ( speed ) ;
if ( player - > connection )
{
player - > connection - > send ( std : : make_shared < PlayerAbilitiesPacket > ( & player - > abilities ) ) ;
}
}
}
static ServerLevel * GetLevel ( int dimId )
{
MinecraftServer * srv = MinecraftServer : : getInstance ( ) ;
if ( ! srv )
{
return nullptr ;
}
return srv - > getLevel ( dimId ) ;
}
static void __cdecl NativeTeleportEntity ( int entityId , int dimId , double x , double y , double z )
{
auto player = FindPlayer ( entityId ) ;
if ( player & & player - > connection )
{
player - > connection - > teleport ( x , y , z , player - > yRot , player - > xRot ) ;
return ;
}
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
shared_ptr < Entity > entity = level - > getEntity ( entityId ) ;
if ( entity )
{
entity - > moveTo ( x , y , z , entity - > yRot , entity - > xRot ) ;
}
}
static int __cdecl NativeGetTileId ( int dimId , int x , int y , int z )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
return level - > getTile ( x , y , z ) ;
}
static void __cdecl NativeSetTile ( int dimId , int x , int y , int z , int tileId , int data )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
level - > setTileAndData ( x , y , z , tileId , data , Tile : : UPDATE_ALL ) ;
}
2026-03-22 02:37:45 -05:00
static int __cdecl NativeGetTileData ( int dimId , int x , int y , int z )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
return level - > getData ( x , y , z ) ;
}
2026-03-21 13:52:26 -05:00
static void __cdecl NativeSetTileData ( int dimId , int x , int y , int z , int data )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
level - > setData ( x , y , z , data , Tile : : UPDATE_ALL ) ;
}
static int __cdecl NativeBreakBlock ( int dimId , int x , int y , int z )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
if ( level - > getTile ( x , y , z ) = = 0 )
{
return 0 ;
}
return level - > destroyTile ( x , y , z , true ) ? 1 : 0 ;
}
static int __cdecl NativeGetHighestBlockY ( int dimId , int x , int z )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
return level - > getHeightmap ( x , z ) ;
}
// double[7] = { spawnX, spawnY, spawnZ, seed, dayTime, isRaining, isThundering }
static void __cdecl NativeGetWorldInfo ( int dimId , double * outBuf )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
memset ( outBuf , 0 , 7 * sizeof ( double ) ) ;
return ;
}
LevelData * ld = level - > getLevelData ( ) ;
Pos * spawn = level - > getSharedSpawnPos ( ) ;
outBuf [ 0 ] = spawn ? ( double ) spawn - > x : 0.0 ;
outBuf [ 1 ] = spawn ? ( double ) spawn - > y : 64.0 ;
outBuf [ 2 ] = spawn ? ( double ) spawn - > z : 0.0 ;
outBuf [ 3 ] = ( double ) level - > getSeed ( ) ;
outBuf [ 4 ] = ( double ) level - > getDayTime ( ) ;
outBuf [ 5 ] = ld & & ld - > isRaining ( ) ? 1.0 : 0.0 ;
outBuf [ 6 ] = ld & & ld - > isThundering ( ) ? 1.0 : 0.0 ;
}
static void __cdecl NativeSetWorldTime ( int dimId , int64_t time )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
level - > setDayTime ( time ) ;
}
// note 2 sefl: pass -1 to leave a parameter unchanged
static void __cdecl NativeSetWeather ( int dimId , int storm , int thundering , int thunderDuration )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
LevelData * ld = level - > getLevelData ( ) ;
if ( ! ld )
{
return ;
}
if ( storm > = 0 )
{
ld - > setRaining ( storm ! = 0 ) ;
}
if ( thundering > = 0 )
{
ld - > setThundering ( thundering ! = 0 ) ;
}
if ( thunderDuration > = 0 )
{
ld - > setThunderTime ( thunderDuration ) ;
}
}
static int __cdecl NativeCreateExplosion ( int dimId , double x , double y , double z , float power , int setFire , int breakBlocks )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
Explosion explosion ( level , nullptr , x , y , z , power ) ;
explosion . fire = ( setFire ! = 0 ) ;
explosion . destroyBlocks = ( breakBlocks ! = 0 ) ;
explosion . explode ( ) ;
explosion . finalizeExplosion ( true ) ;
return 1 ;
}
static int __cdecl NativeStrikeLightning ( int dimId , double x , double y , double z , int effectOnly )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
std : : shared_ptr < Entity > lightning = std : : shared_ptr < Entity > ( new LightningBolt ( level , x , y , z ) ) ;
if ( effectOnly ! = 0 )
{
PlayerList * playerList = MinecraftServer : : getPlayerList ( ) ;
if ( playerList = = NULL )
{
return 0 ;
}
playerList - > broadcast ( x , y , z , 512.0 , dimId , std : : shared_ptr < Packet > ( new AddGlobalEntityPacket ( lightning ) ) ) ;
level - > playSound ( x , y , z , eSoundType_AMBIENT_WEATHER_THUNDER , 10000 , 0.8f + level - > random - > nextFloat ( ) * 0.2f ) ;
level - > playSound ( x , y , z , eSoundType_RANDOM_EXPLODE , 2 , 0.5f + level - > random - > nextFloat ( ) * 0.2f ) ;
return 1 ;
}
return level - > addGlobalEntity ( lightning ) ? 1 : 0 ;
}
static int __cdecl NativeSetSpawnLocation ( int dimId , int x , int y , int z )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return 0 ;
}
level - > setSpawnPos ( x , y , z ) ;
return 1 ;
}
static void __cdecl NativeDropItem ( int dimId , double x , double y , double z , int itemId , int count , int auxValue , int naturally )
{
ServerLevel * level = GetLevel ( dimId ) ;
if ( ! level )
{
return ;
}
if ( itemId < = 0 | | count < = 0 )
{
return ;
}
auto itemInstance = std : : make_shared < ItemInstance > ( itemId , count , auxValue ) ;
double spawnX = x , spawnY = y , spawnZ = z ;
if ( naturally )
{
float s = 0.7f ;
spawnX + = level - > random - > nextFloat ( ) * s + ( 1 - s ) * 0.5 ;
spawnY + = level - > random - > nextFloat ( ) * s + ( 1 - s ) * 0.5 ;
spawnZ + = level - > random - > nextFloat ( ) * s + ( 1 - s ) * 0.5 ;
}
auto item = std : : make_shared < ItemEntity > ( level , spawnX , spawnY , spawnZ , itemInstance ) ;
item - > throwTime = 10 ;
level - > addEntity ( item ) ;
}
static void __cdecl NativeKickPlayer ( int entityId , int reason )
{
auto player = FindPlayer ( entityId ) ;
if ( player & & player - > connection )
{
DisconnectPacket : : eDisconnectReason r = static_cast < DisconnectPacket : : eDisconnectReason > ( reason ) ;
player - > connection - > disconnect ( r ) ;
}
}
static int __cdecl NativeBanPlayer ( int entityId , const char * reasonUtf8 , int reasonByteLen )
{
if ( ! ServerRuntime : : Access : : IsInitialized ( ) )
{
return 0 ;
}
auto player = FindPlayer ( entityId ) ;
if ( ! player )
{
return 0 ;
}
std : : vector < PlayerUID > xuids ;
PlayerUID xuid1 = player - > getXuid ( ) ;
PlayerUID xuid2 = player - > getOnlineXuid ( ) ;
if ( xuid1 ! = INVALID_XUID )
{
xuids . push_back ( xuid1 ) ;
}
if ( xuid2 ! = INVALID_XUID & & xuid2 ! = xuid1 )
{
xuids . push_back ( xuid2 ) ;
}
if ( xuids . empty ( ) )
{
return 0 ;
}
std : : string reason = ( reasonUtf8 & & reasonByteLen > 0 ) ? std : : string ( reasonUtf8 , reasonByteLen ) : " Banned by plugin. " ;
std : : string playerName = ServerRuntime : : StringUtils : : WideToUtf8 ( player - > getName ( ) ) ;
ServerRuntime : : Access : : BanMetadata metadata = ServerRuntime : : Access : : BanManager : : BuildDefaultMetadata ( " Plugin " ) ;
metadata . reason = reason ;
for ( auto xuid : xuids )
{
if ( ! ServerRuntime : : Access : : IsPlayerBanned ( xuid ) )
{
ServerRuntime : : Access : : AddPlayerBan ( xuid , playerName , metadata ) ;
}
}
if ( player - > connection )
{
player - > connection - > disconnect ( DisconnectPacket : : eDisconnect_Banned ) ;
}
return 1 ;
}
static int __cdecl NativeBanPlayerIp ( int entityId , const char * reasonUtf8 , int reasonByteLen )
{
if ( ! ServerRuntime : : Access : : IsInitialized ( ) )
{
return 0 ;
}
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > connection | | ! player - > connection - > connection | | ! player - > connection - > connection - > getSocket ( ) )
{
return 0 ;
}
unsigned char smallId = player - > connection - > connection - > getSocket ( ) - > getSmallId ( ) ;
if ( smallId = = 0 )
{
return 0 ;
}
std : : string playerIp ;
if ( ! ServerRuntime : : ServerLogManager : : TryGetConnectionRemoteIp ( smallId , & playerIp ) )
{
return 0 ;
}
std : : string reason = ( reasonUtf8 & & reasonByteLen > 0 ) ? std : : string ( reasonUtf8 , reasonByteLen ) : " Banned by plugin. " ;
ServerRuntime : : Access : : BanMetadata metadata = ServerRuntime : : Access : : BanManager : : BuildDefaultMetadata ( " Plugin " ) ;
metadata . reason = reason ;
std : : string normalizedIp = ServerRuntime : : NetworkUtils : : NormalizeIpToken ( playerIp ) ;
if ( ServerRuntime : : Access : : IsIpBanned ( normalizedIp ) )
{
return 0 ;
}
if ( ! ServerRuntime : : Access : : AddIpBan ( normalizedIp , metadata ) )
{
return 0 ;
}
PlayerList * list = MinecraftServer : : getPlayerList ( ) ;
if ( list )
{
std : : vector < std : : shared_ptr < ServerPlayer > > snapshot = list - > players ;
for ( auto & p : snapshot )
{
if ( ! p | | ! p - > connection | | ! p - > connection - > connection | | ! p - > connection - > connection - > getSocket ( ) )
{
continue ;
}
unsigned char sid = p - > connection - > connection - > getSocket ( ) - > getSmallId ( ) ;
if ( sid = = 0 )
{
continue ;
}
std : : string pIp ;
if ( ! ServerRuntime : : ServerLogManager : : TryGetConnectionRemoteIp ( sid , & pIp ) )
{
continue ;
}
if ( ServerRuntime : : NetworkUtils : : NormalizeIpToken ( pIp ) = = normalizedIp )
{
if ( p - > connection )
{
p - > connection - > disconnect ( DisconnectPacket : : eDisconnect_Banned ) ;
}
}
}
}
return 1 ;
}
static int __cdecl NativeGetPlayerAddress ( int entityId , char * outIpBuf , int outIpBufSize , int * outPort )
{
if ( outPort )
{
* outPort = 0 ;
}
if ( outIpBuf & & outIpBufSize > 0 )
{
outIpBuf [ 0 ] = ' \0 ' ;
}
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > connection | | ! player - > connection - > connection | | ! player - > connection - > connection - > getSocket ( ) )
{
return 0 ;
}
unsigned char smallId = player - > connection - > connection - > getSocket ( ) - > getSmallId ( ) ;
if ( smallId = = 0 )
{
return 0 ;
}
std : : string playerIp ;
if ( ! ServerRuntime : : ServerLogManager : : TryGetConnectionRemoteIp ( smallId , & playerIp ) )
{
SOCKET sock = WinsockNetLayer : : GetSocketForSmallId ( smallId ) ;
if ( sock ! = INVALID_SOCKET )
{
sockaddr_in addr ;
int addrLen = sizeof ( addr ) ;
if ( getpeername ( sock , ( sockaddr * ) & addr , & addrLen ) = = 0 )
{
char ipBuf [ 64 ] = { } ;
if ( inet_ntop ( AF_INET , & addr . sin_addr , ipBuf , sizeof ( ipBuf ) ) )
{
playerIp = ipBuf ;
if ( outPort )
{
* outPort = ( int ) ntohs ( addr . sin_port ) ;
}
}
}
}
if ( playerIp . empty ( ) )
{
return 0 ;
}
}
else
{
SOCKET sock = WinsockNetLayer : : GetSocketForSmallId ( smallId ) ;
if ( sock ! = INVALID_SOCKET & & outPort )
{
sockaddr_in addr ;
int addrLen = sizeof ( addr ) ;
if ( getpeername ( sock , ( sockaddr * ) & addr , & addrLen ) = = 0 )
{
* outPort = ( int ) ntohs ( addr . sin_port ) ;
}
}
}
if ( outIpBuf & & outIpBufSize > 0 )
{
int copyLen = ( int ) playerIp . size ( ) ;
if ( copyLen > = outIpBufSize )
{
copyLen = outIpBufSize - 1 ;
}
memcpy ( outIpBuf , playerIp . c_str ( ) , copyLen ) ;
outIpBuf [ copyLen ] = ' \0 ' ;
}
return 1 ;
}
// outData: 40 slots * 3 ints (id, count, aux) + 1 int (selected slot) = 121 ints
static void __cdecl NativeGetPlayerInventory ( int entityId , int * outData )
{
memset ( outData , 0 , 121 * sizeof ( int ) ) ;
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > inventory )
{
return ;
}
unsigned int size = player - > inventory - > getContainerSize ( ) ;
if ( size > 40 )
{
size = 40 ;
}
for ( unsigned int i = 0 ; i < size ; i + + )
{
auto item = player - > inventory - > getItem ( i ) ;
if ( item )
{
outData [ i * 3 ] = item - > id ;
outData [ i * 3 + 1 ] = item - > count ;
outData [ i * 3 + 2 ] = item - > getAuxValue ( ) ;
}
}
outData [ 120 ] = player - > inventory - > selected ;
}
static void __cdecl NativeSetPlayerInventorySlot ( int entityId , int slot , int itemId , int count , int aux )
{
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > inventory )
{
return ;
}
if ( itemId < = 0 | | count < = 0 )
{
player - > inventory - > setItem ( slot , nullptr ) ;
}
else
{
auto item = std : : make_shared < ItemInstance > ( itemId , count , aux ) ;
player - > inventory - > setItem ( slot , item ) ;
}
}
static void __cdecl NativeGetContainerContents ( int entityId , int * outData , int maxSlots )
{
memset ( outData , 0 , maxSlots * 3 * sizeof ( int ) ) ;
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > containerMenu | | player - > containerMenu = = player - > inventoryMenu )
{
return ;
}
auto * menu = player - > containerMenu ;
auto * items = menu - > getItems ( ) ;
int count = ( int ) items - > size ( ) ;
if ( count > maxSlots )
{
count = maxSlots ;
}
for ( int i = 0 ; i < count ; i + + )
{
auto & item = ( * items ) [ i ] ;
if ( item )
{
outData [ i * 3 ] = item - > id ;
outData [ i * 3 + 1 ] = item - > count ;
outData [ i * 3 + 2 ] = item - > getAuxValue ( ) ;
}
}
delete items ;
}
static void __cdecl NativeSetContainerSlot ( int entityId , int slot , int itemId , int count , int aux )
{
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > containerMenu | | player - > containerMenu = = player - > inventoryMenu )
{
return ;
}
auto * menu = player - > containerMenu ;
if ( slot < 0 | | slot > = ( int ) menu - > slots . size ( ) )
{
return ;
}
if ( itemId < = 0 | | count < = 0 )
{
menu - > setItem ( slot , nullptr ) ;
}
else
{
menu - > setItem ( slot , std : : make_shared < ItemInstance > ( itemId , count , aux ) ) ;
}
menu - > broadcastChanges ( ) ;
}
static void __cdecl NativeCloseContainer ( int entityId )
{
auto player = FindPlayer ( entityId ) ;
if ( player )
{
player - > doCloseContainer ( ) ;
}
}
class VirtualContainer : public SimpleContainer
{
int m_containerType ;
public :
VirtualContainer ( int containerType , const std : : wstring & name , int size )
: SimpleContainer ( 0 , name , ! name . empty ( ) , size ) , m_containerType ( containerType )
{
}
virtual int getContainerType ( ) override
{
return m_containerType ;
}
} ;
static void __cdecl NativeOpenVirtualContainer ( int entityId , int nativeType , const char * titleUtf8 , int titleByteLen , int slotCount , int * itemsBuf )
{
auto player = FindPlayer ( entityId ) ;
if ( ! player )
{
return ;
}
if ( player - > containerMenu ! = player - > inventoryMenu )
{
player - > doCloseContainer ( ) ;
}
std : : wstring title = ServerRuntime : : StringUtils : : Utf8ToWide ( std : : string ( titleUtf8 , titleByteLen ) ) ;
auto container = std : : make_shared < VirtualContainer > ( nativeType , title , slotCount ) ;
for ( int i = 0 ; i < slotCount ; i + + )
{
int id = itemsBuf [ i * 3 ] ;
int count = itemsBuf [ i * 3 + 1 ] ;
int aux = itemsBuf [ i * 3 + 2 ] ;
if ( id > 0 & & count > 0 )
{
container - > setItem ( i , std : : make_shared < ItemInstance > ( id , count , aux ) ) ;
}
}
player - > openContainer ( container ) ;
}
static void __cdecl NativeGetContainerViewerEntityIds ( int entityId , int * outIds , int maxCount , int * outCount )
{
* outCount = 0 ;
auto player = FindPlayer ( entityId ) ;
if ( ! player | | ! player - > containerMenu | | player - > containerMenu = = player - > inventoryMenu )
{
return ;
}
auto * menu = player - > containerMenu ;
if ( menu - > slots . empty ( ) )
{
return ;
}
Container * myContainer = menu - > slots [ 0 ] - > container . get ( ) ;
if ( ! myContainer )
{
return ;
}
CompoundContainer * myCompound = dynamic_cast < CompoundContainer * > ( myContainer ) ;
if ( myCompound )
{
myContainer = myCompound - > getFirstContainer ( ) . get ( ) ;
}
PlayerList * list = MinecraftServer : : getPlayerList ( ) ;
if ( ! list )
{
return ;
}
int count = 0 ;
for ( auto & p : list - > players )
{
if ( ! p | | ! p - > containerMenu | | p - > containerMenu = = p - > inventoryMenu )
{
continue ;
}
if ( p - > containerMenu - > slots . empty ( ) )
{
continue ;
}
Container * theirContainer = p - > containerMenu - > slots [ 0 ] - > container . get ( ) ;
CompoundContainer * theirCompound = dynamic_cast < CompoundContainer * > ( theirContainer ) ;
if ( theirCompound )
{
theirContainer = theirCompound - > getFirstContainer ( ) . get ( ) ;
}
if ( theirContainer = = myContainer & & count < maxCount )
{
outIds [ count + + ] = p - > entityId ;
}
}
* outCount = count ;
}
2026-03-21 16:40:07 -05:00
static std : : wstring FindNet10SystemRoot ( )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
// overengineered
// trying to do a lot of safeguards here, setups can be all over the place
// fixes an issue aiden had
2026-03-21 13:52:26 -05:00
2026-03-21 16:40:07 -05:00
std : : vector < std : : wstring > candidates ;
wchar_t envRoot [ MAX_PATH ] = { } ;
DWORD len = GetEnvironmentVariableW ( L " DOTNET_ROOT " , envRoot , MAX_PATH ) ;
if ( len > 0 & & len < MAX_PATH )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
candidates . push_back ( std : : wstring ( envRoot ) ) ;
2026-03-21 13:52:26 -05:00
}
2026-03-21 16:40:07 -05:00
candidates . push_back ( L " C: \\ Program Files \\ dotnet " ) ;
2026-03-21 13:52:26 -05:00
2026-03-21 16:40:07 -05:00
for ( const auto & root : candidates )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
std : : wstring fxrDir = root + L " \\ host \\ fxr " ;
WIN32_FIND_DATAW fd ;
HANDLE h = FindFirstFileW ( ( fxrDir + L " \\ * " ) . c_str ( ) , & fd ) ;
if ( h = = INVALID_HANDLE_VALUE )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
continue ;
}
bool has10 = false ;
do
{
if ( ( fd . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) & & fd . cFileName [ 0 ] ! = L ' . ' )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
if ( std : : wstring ( fd . cFileName ) . substr ( 0 , 3 ) = = L " 10. " )
{
has10 = true ;
}
2026-03-21 13:52:26 -05:00
}
2026-03-21 16:40:07 -05:00
} while ( ! has10 & & FindNextFileW ( h , & fd ) ) ;
FindClose ( h ) ;
if ( has10 )
{
return root ;
2026-03-21 13:52:26 -05:00
}
}
2026-03-21 16:40:07 -05:00
return L " C: \\ Program Files \\ dotnet " ;
}
2026-03-21 13:52:26 -05:00
2026-03-21 16:40:07 -05:00
static bool TryLoadHostfxrFromPath ( const std : : wstring & path )
{
HMODULE lib = LoadLibraryW ( path . c_str ( ) ) ;
2026-03-21 13:52:26 -05:00
if ( ! lib )
{
return false ;
}
s_initFn = ( hostfxr_initialize_for_runtime_config_fn ) GetProcAddress ( lib , " hostfxr_initialize_for_runtime_config " ) ;
s_getDelegateFn = ( hostfxr_get_runtime_delegate_fn ) GetProcAddress ( lib , " hostfxr_get_runtime_delegate " ) ;
s_closeFn = ( hostfxr_close_fn ) GetProcAddress ( lib , " hostfxr_close " ) ;
2026-03-21 16:40:07 -05:00
if ( s_initFn & & s_getDelegateFn & & s_closeFn )
2026-03-21 13:52:26 -05:00
{
2026-03-21 16:40:07 -05:00
return true ;
2026-03-21 13:52:26 -05:00
}
2026-03-21 16:40:07 -05:00
s_initFn = nullptr ;
s_getDelegateFn = nullptr ;
s_closeFn = nullptr ;
FreeLibrary ( lib ) ;
return false ;
}
static bool LoadHostfxr ( )
{
// hardcoded for windows
// no linux support yet so no need
wchar_t exePath [ MAX_PATH ] = { } ;
GetModuleFileNameW ( NULL , exePath , MAX_PATH ) ;
std : : wstring exeDir ( exePath ) ;
size_t lastSlash = exeDir . find_last_of ( L " \\ / " ) ;
if ( lastSlash ! = std : : wstring : : npos )
{
exeDir = exeDir . substr ( 0 , lastSlash ) ;
}
if ( TryLoadHostfxrFromPath ( exeDir + L " \\ hostfxr.dll " ) )
{
s_dotnetRoot = FindNet10SystemRoot ( ) ;
return true ;
}
wchar_t dotnetRoot [ MAX_PATH ] = { } ;
DWORD len = GetEnvironmentVariableW ( L " DOTNET_ROOT " , dotnetRoot , MAX_PATH ) ; // sometimes this is set in windows
if ( len = = 0 | | len > = MAX_PATH )
{
wcscpy_s ( dotnetRoot , L " C: \\ Program Files \\ dotnet " ) ;
}
std : : wstring hostfxrDir = std : : wstring ( dotnetRoot ) + L " \\ host \\ fxr " ;
WIN32_FIND_DATAW fd ;
HANDLE hFind = FindFirstFileW ( ( hostfxrDir + L " \\ * " ) . c_str ( ) , & fd ) ;
if ( hFind ! = INVALID_HANDLE_VALUE )
{
std : : wstring bestVersion ;
do
{
if ( ( fd . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) & & fd . cFileName [ 0 ] ! = L ' . ' )
{
std : : wstring ver ( fd . cFileName ) ;
if ( ver . substr ( 0 , 3 ) = = L " 10. " & & ver > bestVersion )
{
bestVersion = ver ;
}
}
} while ( FindNextFileW ( hFind , & fd ) ) ;
FindClose ( hFind ) ;
if ( ! bestVersion . empty ( ) )
{
if ( TryLoadHostfxrFromPath ( hostfxrDir + L " \\ " + bestVersion + L " \\ hostfxr.dll " ) )
{
s_dotnetRoot = std : : wstring ( dotnetRoot ) ;
return true ;
}
}
}
LogError ( " fourkit " , " hostfxr.dll not found. Install the .NET 10 x64 runtime (https://aka.ms/dotnet/download) or copy hostfxr.dll from C: \\ Program Files \\ dotnet \\ host \\ fxr \\ 10.x.x \\ next to the server executable. " ) ;
return false ;
2026-03-21 13:52:26 -05:00
}
static bool GetManagedEntryPoint (
load_assembly_and_get_function_pointer_fn loadAssembly ,
const wchar_t * assemblyPath ,
const wchar_t * typeName ,
const wchar_t * methodName ,
void * * outFnPtr )
{
int rc = loadAssembly (
assemblyPath ,
typeName ,
methodName ,
UNMANAGEDCALLERSONLY_METHOD ,
nullptr ,
outFnPtr ) ;
if ( rc ! = 0 | | * outFnPtr = = nullptr )
{
char methodNarrow [ 256 ] ;
sprintf_s ( methodNarrow , " %S::%S " , typeName , methodName ) ;
LogError ( " fourkit " , ( std : : string ( " Failed to resolve managed entry point: " ) + methodNarrow ) . c_str ( ) ) ;
return false ;
}
return true ;
}
void Initialize ( )
{
LogInfo ( " fourkit " , " FourKit initializing... " ) ;
if ( ! LoadHostfxr ( ) )
{
return ;
}
wchar_t exePath [ MAX_PATH ] ;
GetModuleFileNameW ( NULL , exePath , MAX_PATH ) ;
std : : wstring exeDir ( exePath ) ;
size_t lastSlash = exeDir . find_last_of ( L " \\ / " ) ;
if ( lastSlash ! = std : : wstring : : npos )
{
exeDir = exeDir . substr ( 0 , lastSlash ) ;
}
std : : wstring runtimeConfigPath = exeDir + L " \\ Minecraft.Server.FourKit.runtimeconfig.json " ;
std : : wstring assemblyPath = exeDir + L " \\ Minecraft.Server.FourKit.dll " ;
2026-03-21 16:40:07 -05:00
hostfxr_initialize_parameters initParams = { } ;
initParams . size = sizeof ( hostfxr_initialize_parameters ) ;
initParams . host_path = exePath ;
initParams . dotnet_root = s_dotnetRoot . c_str ( ) ;
2026-03-21 13:52:26 -05:00
hostfxr_handle ctx = nullptr ;
2026-03-21 16:40:07 -05:00
int rc = s_initFn ( runtimeConfigPath . c_str ( ) , & initParams , & ctx ) ;
2026-03-21 13:52:26 -05:00
if ( rc ! = 0 | | ctx = = nullptr )
{
char msg [ 256 ] ;
sprintf_s ( msg , " hostfxr_initialize_for_runtime_config failed (0x%08X). Check runtimeconfig.json path. " , rc ) ;
LogError ( " fourkit " , msg ) ;
if ( ctx )
{
s_closeFn ( ctx ) ;
}
return ;
}
load_assembly_and_get_function_pointer_fn loadAssembly = nullptr ;
rc = s_getDelegateFn ( ctx , hdt_load_assembly_and_get_function_pointer , ( void * * ) & loadAssembly ) ;
s_closeFn ( ctx ) ;
if ( rc ! = 0 | | loadAssembly = = nullptr )
{
LogError ( " fourkit " , " Failed to get load_assembly_and_get_function_pointer delegate. " ) ;
return ;
}
const wchar_t * typeName = L " Minecraft.Server.FourKit.FourKitHost, Minecraft.Server.FourKit " ;
bool ok = true ;
// lol
// this is horrid
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " Initialize " , ( void * * ) & s_managedInit ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerJoin " , ( void * * ) & s_managedFireJoin ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerQuit " , ( void * * ) & s_managedFireQuit ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerKick " , ( void * * ) & s_managedFireKick ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerMove " , ( void * * ) & s_managedFireMove ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " Shutdown " , ( void * * ) & s_managedShutdown ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " SetNativeCallbacks " , ( void * * ) & s_managedSetCallbacks ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " SetWorldCallbacks " , ( void * * ) & s_managedSetWorldCallbacks ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " UpdatePlayerEntityId " , ( void * * ) & s_managedUpdateEntityId ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerChat " , ( void * * ) & s_managedFireChat ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireBlockPlace " , ( void * * ) & s_managedFireBlockPlace ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireBlockBreak " , ( void * * ) & s_managedFireBlockBreak ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireEntityDamage " , ( void * * ) & s_managedFireEntityDamage ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireSignChange " , ( void * * ) & s_managedFireSignChange ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireEntityDeath " , ( void * * ) & s_managedFireEntityDeath ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerDeath " , ( void * * ) & s_managedFirePlayerDeath ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " SetPlayerCallbacks " , ( void * * ) & s_managedSetPlayerCallbacks ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerDropItem " , ( void * * ) & s_managedFirePlayerDropItem ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " SetInventoryCallbacks " , ( void * * ) & s_managedSetInventoryCallbacks ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerInteract " , ( void * * ) & s_managedFirePlayerInteract ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerInteractEntity " , ( void * * ) & s_managedFirePlayerInteractEntity ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerPickupItem " , ( void * * ) & s_managedFirePlayerPickupItem ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireInventoryOpen " , ( void * * ) & s_managedFireInventoryOpen ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " HandlePlayerCommand " , ( void * * ) & s_managedHandlePlayerCommand ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " HandleConsoleCommand " , ( void * * ) & s_managedHandleConsoleCommand ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " GetPluginCommandHelp " , ( void * * ) & s_managedGetPluginCommandHelp ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerTeleport " , ( void * * ) & s_managedFirePlayerTeleport ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FirePlayerPortal " , ( void * * ) & s_managedFirePlayerPortal ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireInventoryClick " , ( void * * ) & s_managedFireInventoryClick ) ;
2026-03-22 15:48:51 -05:00
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireBedEnter " , ( void * * ) & s_managedFireBedEnter ) ;
ok = ok & & GetManagedEntryPoint ( loadAssembly , assemblyPath . c_str ( ) , typeName , L " FireBedLeave " , ( void * * ) & s_managedFireBedLeave ) ;
2026-03-21 13:52:26 -05:00
if ( ! ok )
{
LogError ( " fourkit " , " Not all managed entry points resolved. FourKit will be disabled. " ) ;
return ;
}
s_initialized = true ;
s_managedInit ( ) ;
s_managedSetCallbacks (
( void * ) & NativeDamagePlayer ,
( void * ) & NativeSetPlayerHealth ,
( void * ) & NativeTeleportPlayer ,
( void * ) & NativeSetPlayerGameMode ,
( void * ) & NativeBroadcastMessage ,
( void * ) & NativeSetFallDistance ,
( void * ) & NativeGetPlayerSnapshot ,
( void * ) & NativeSendMessage ,
( void * ) & NativeSetWalkSpeed ,
( void * ) & NativeTeleportEntity ) ;
s_managedSetWorldCallbacks (
( void * ) & NativeGetTileId ,
2026-03-22 02:37:45 -05:00
( void * ) & NativeGetTileData ,
2026-03-21 13:52:26 -05:00
( void * ) & NativeSetTile ,
( void * ) & NativeSetTileData ,
( void * ) & NativeBreakBlock ,
( void * ) & NativeGetHighestBlockY ,
( void * ) & NativeGetWorldInfo ,
( void * ) & NativeSetWorldTime ,
( void * ) & NativeSetWeather ,
( void * ) & NativeCreateExplosion ,
( void * ) & NativeStrikeLightning ,
( void * ) & NativeSetSpawnLocation ,
( void * ) & NativeDropItem ) ;
s_managedSetPlayerCallbacks (
( void * ) & NativeKickPlayer ,
( void * ) & NativeBanPlayer ,
( void * ) & NativeBanPlayerIp ,
( void * ) & NativeGetPlayerAddress ) ;
s_managedSetInventoryCallbacks (
( void * ) & NativeGetPlayerInventory ,
( void * ) & NativeSetPlayerInventorySlot ,
( void * ) & NativeGetContainerContents ,
( void * ) & NativeSetContainerSlot ,
( void * ) & NativeGetContainerViewerEntityIds ,
( void * ) & NativeCloseContainer ,
( void * ) & NativeOpenVirtualContainer ) ;
LogInfo ( " fourkit " , " FourKit initialized successfully. " ) ;
}
void Shutdown ( )
{
if ( ! s_initialized )
{
return ;
}
LogInfo ( " fourkit " , " FourKit shutting down... " ) ;
s_managedShutdown ( ) ;
s_initialized = false ;
LogInfo ( " fourkit " , " FourKit shut down. " ) ;
}
void FirePlayerJoin ( int entityId , const std : : wstring & name , const std : : wstring & uuid )
{
if ( ! s_initialized | | ! s_managedFireJoin )
{
return ;
}
std : : string nameUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( name ) ;
std : : string uuidUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( uuid ) ;
s_managedFireJoin ( entityId ,
nameUtf8 . empty ( ) ? " " : nameUtf8 . data ( ) , ( int ) nameUtf8 . size ( ) ,
uuidUtf8 . empty ( ) ? " " : uuidUtf8 . data ( ) , ( int ) uuidUtf8 . size ( ) ) ;
LogDebugf ( " fourkit " , " Fired PlayerJoin: entityId=%d " , entityId ) ;
}
bool FirePlayerQuit ( int entityId )
{
s_openContainerInfo . erase ( entityId ) ;
if ( ! s_initialized | | ! s_managedFireQuit )
{
return false ;
}
s_managedFireQuit ( entityId ) ;
LogDebugf ( " fourkit " , " Fired PlayerQuit: entityId=%d " , entityId ) ;
return true ;
}
bool FirePlayerKick ( int entityId , int disconnectReason ,
std : : wstring & outLeaveMessage )
{
if ( ! s_initialized | | ! s_managedFireKick )
{
return false ;
}
std : : string reasonUtf8 = std : : to_string ( disconnectReason ) ;
const int kBufSize = 2048 ;
char outBuf [ kBufSize ] = { } ;
int outLen = 0 ;
int cancelled = s_managedFireKick ( entityId , disconnectReason ,
reasonUtf8 . empty ( ) ? " " : reasonUtf8 . data ( ) , ( int ) reasonUtf8 . size ( ) ,
outBuf , kBufSize , & outLen ) ;
if ( outLen > 0 & & outLen < kBufSize )
{
std : : string resultUtf8 ( outBuf , outLen ) ;
outLeaveMessage = ServerRuntime : : StringUtils : : Utf8ToWide ( resultUtf8 ) ;
}
LogDebugf ( " fourkit " , " Fired PlayerKick: entityId=%d cancelled=%d " , entityId , cancelled ) ;
return cancelled ! = 0 ;
}
void UpdatePlayerEntityId ( int oldEntityId , int newEntityId )
{
auto it = s_openContainerInfo . find ( oldEntityId ) ;
if ( it ! = s_openContainerInfo . end ( ) )
{
s_openContainerInfo [ newEntityId ] = it - > second ;
s_openContainerInfo . erase ( it ) ;
}
if ( ! s_initialized | | ! s_managedUpdateEntityId )
{
return ;
}
s_managedUpdateEntityId ( oldEntityId , newEntityId ) ;
LogDebugf ( " fourkit " , " UpdatePlayerEntityId: %d -> %d " , oldEntityId , newEntityId ) ;
}
bool FirePlayerMove ( int entityId ,
double fromX , double fromY , double fromZ ,
double toX , double toY , double toZ ,
double * outToX , double * outToY , double * outToZ )
{
if ( ! s_initialized | | ! s_managedFireMove )
{
return false ;
}
double outCoords [ 3 ] = { toX , toY , toZ } ;
int cancelled = s_managedFireMove ( entityId ,
fromX , fromY , fromZ ,
toX , toY , toZ ,
outCoords ) ;
* outToX = outCoords [ 0 ] ;
* outToY = outCoords [ 1 ] ;
* outToZ = outCoords [ 2 ] ;
return cancelled ! = 0 ;
}
bool FirePlayerChat ( int entityId ,
const std : : wstring & message ,
std : : wstring & outFormatted )
{
if ( ! s_initialized | | ! s_managedFireChat )
{
return false ;
}
std : : string msgUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( message ) ;
const int kBufSize = 2048 ;
char outBuf [ kBufSize ] = { } ;
int outLen = 0 ;
int cancelled = s_managedFireChat ( entityId ,
msgUtf8 . empty ( ) ? " " : msgUtf8 . data ( ) , ( int ) msgUtf8 . size ( ) ,
outBuf , kBufSize , & outLen ) ;
if ( cancelled ! = 0 )
{
return true ;
}
if ( outLen > 0 & & outLen < kBufSize )
{
std : : string resultUtf8 ( outBuf , outLen ) ;
outFormatted = ServerRuntime : : StringUtils : : Utf8ToWide ( resultUtf8 ) ;
}
return false ;
}
bool FireBlockPlace ( int entityId , int dimId ,
int placedX , int placedY , int placedZ ,
int againstX , int againstY , int againstZ ,
int itemId , int itemCount , bool canBuild )
{
if ( ! s_initialized | | ! s_managedFireBlockPlace )
{
return false ;
}
int cancelled = s_managedFireBlockPlace ( entityId , dimId ,
placedX , placedY , placedZ ,
againstX , againstY , againstZ ,
itemId , itemCount , canBuild ? 1 : 0 ) ;
return cancelled ! = 0 ;
}
int FireBlockBreak ( int entityId , int dimId ,
int x , int y , int z , int tileId , int data , int exp )
{
if ( ! s_initialized | | ! s_managedFireBlockBreak )
{
return exp ;
}
return s_managedFireBlockBreak ( entityId , dimId , x , y , z , tileId , data , exp ) ;
}
int MapEntityType ( int nativeType )
{
eINSTANCEOF type = ( eINSTANCEOF ) nativeType ;
const int ARROW = 0 , BAT = 1 , BLAZE = 2 , BOAT = 3 , CAVE_SPIDER = 4 ;
const int CHICKEN = 5 , COW = 7 , CREEPER = 8 , DROPPED_ITEM = 9 ;
const int EGG = 10 , ENDER_CRYSTAL = 11 , ENDER_DRAGON = 12 ;
const int ENDER_PEARL = 13 , ENDER_SIGNAL = 14 , ENDERMAN = 15 ;
const int EXPERIENCE_ORB = 16 , FALLING_BLOCK = 17 , FIREBALL = 18 ;
const int FIREWORK = 19 , FISHING_HOOK = 20 , GHAST = 21 , GIANT = 22 ;
const int HORSE = 23 , IRON_GOLEM = 24 , ITEM_FRAME = 25 ;
const int LEASH_HITCH = 26 , LIGHTNING = 27 , MAGMA_CUBE = 28 ;
const int MINECART = 29 , MINECART_CHEST = 30 , MINECART_FURNACE = 32 ;
const int MINECART_HOPPER = 33 , MINECART_MOB_SPAWNER = 34 ;
const int MINECART_TNT = 35 , MUSHROOM_COW = 36 , OCELOT = 37 ;
const int PAINTING = 38 , PIG = 39 , PIG_ZOMBIE = 40 , PLAYER = 41 ;
const int PRIMED_TNT = 42 , SHEEP = 43 , SILVERFISH = 44 ;
const int SKELETON = 45 , SLIME = 46 , SMALL_FIREBALL = 47 ;
const int SNOWBALL = 48 , SNOWMAN = 49 , SPIDER = 50 ;
const int SPLASH_POTION = 51 , SQUID = 52 , THROWN_EXP_BOTTLE = 53 ;
const int UNKNOWN = 54 , VILLAGER = 55 , WITCH = 57 ;
const int WITHER = 58 , WITHER_SKULL = 59 , WOLF = 60 , ZOMBIE = 61 ;
switch ( type )
{
case eTYPE_ARROW :
return ARROW ;
case eTYPE_BAT :
return BAT ;
case eTYPE_BLAZE :
return BLAZE ;
case eTYPE_BOAT :
return BOAT ;
case eTYPE_CAVESPIDER :
return CAVE_SPIDER ;
case eTYPE_CHICKEN :
return CHICKEN ;
case eTYPE_COW :
return COW ;
case eTYPE_CREEPER :
return CREEPER ;
case eTYPE_ITEMENTITY :
return DROPPED_ITEM ;
case eTYPE_THROWNEGG :
return EGG ;
case eTYPE_NETHER_SPHERE :
return ENDER_CRYSTAL ;
case eTYPE_ENDERDRAGON :
return ENDER_DRAGON ;
case eTYPE_THROWNENDERPEARL :
return ENDER_PEARL ;
case eTYPE_EYEOFENDERSIGNAL :
return ENDER_SIGNAL ;
case eTYPE_ENDERMAN :
return ENDERMAN ;
case eTYPE_EXPERIENCEORB :
return EXPERIENCE_ORB ;
case eTYPE_FALLINGTILE :
return FALLING_BLOCK ;
case eTYPE_LARGE_FIREBALL :
return FIREBALL ;
case eTYPE_FIREWORKS_ROCKET :
return FIREWORK ;
case eTYPE_FISHINGHOOK :
return FISHING_HOOK ;
case eTYPE_GHAST :
return GHAST ;
case eTYPE_GIANT :
return GIANT ;
case eTYPE_HORSE :
return HORSE ;
case eTYPE_VILLAGERGOLEM :
return IRON_GOLEM ;
case eTYPE_ITEM_FRAME :
return ITEM_FRAME ;
case eTYPE_LEASHFENCEKNOT :
return LEASH_HITCH ;
case eTYPE_LIGHTNINGBOLT :
return LIGHTNING ;
case eTYPE_LAVASLIME :
return MAGMA_CUBE ;
case eTYPE_MINECART_RIDEABLE :
return MINECART ;
case eTYPE_MINECART_CHEST :
return MINECART_CHEST ;
case eTYPE_MINECART_FURNACE :
return MINECART_FURNACE ;
case eTYPE_MINECART_HOPPER :
return MINECART_HOPPER ;
case eTYPE_MINECART_SPAWNER :
return MINECART_MOB_SPAWNER ;
case eTYPE_MINECART_TNT :
return MINECART_TNT ;
case eTYPE_MUSHROOMCOW :
return MUSHROOM_COW ;
case eTYPE_OCELOT :
return OCELOT ;
case eTYPE_PAINTING :
return PAINTING ;
case eTYPE_PIG :
return PIG ;
case eTYPE_PIGZOMBIE :
return PIG_ZOMBIE ;
case eTYPE_PLAYER :
return PLAYER ;
case eTYPE_SERVERPLAYER :
return PLAYER ;
case eTYPE_REMOTEPLAYER :
return PLAYER ;
case eTYPE_LOCALPLAYER :
return PLAYER ;
case eTYPE_PRIMEDTNT :
return PRIMED_TNT ;
case eTYPE_SHEEP :
return SHEEP ;
case eTYPE_SILVERFISH :
return SILVERFISH ;
case eTYPE_SKELETON :
return SKELETON ;
case eTYPE_SLIME :
return SLIME ;
case eTYPE_SMALL_FIREBALL :
return SMALL_FIREBALL ;
case eTYPE_SNOWBALL :
return SNOWBALL ;
case eTYPE_SNOWMAN :
return SNOWMAN ;
case eTYPE_SPIDER :
return SPIDER ;
case eTYPE_THROWNPOTION :
return SPLASH_POTION ;
case eTYPE_SQUID :
return SQUID ;
case eTYPE_THROWNEXPBOTTLE :
return THROWN_EXP_BOTTLE ;
case eTYPE_VILLAGER :
return VILLAGER ;
case eTYPE_WITCH :
return WITCH ;
case eTYPE_WITHERBOSS :
return WITHER ;
case eTYPE_WITHER_SKULL :
return WITHER_SKULL ;
case eTYPE_WOLF :
return WOLF ;
case eTYPE_ZOMBIE :
return ZOMBIE ;
default :
return UNKNOWN ;
}
}
int MapDamageCause ( void * sourcePtr )
{
DamageSource * source = ( DamageSource * ) sourcePtr ;
const int CONTACT = 1 , CUSTOM = 2 , DROWNING = 3 ;
const int ENTITY_ATTACK = 4 , ENTITY_EXPLOSION = 5 ;
const int FALL = 6 , FALLING_BLOCK = 7 , FIRE = 8 , FIRE_TICK = 9 ;
const int LAVA = 10 , MAGIC = 12 ;
const int PROJECTILE = 15 , STARVATION = 16 , SUFFOCATION = 17 ;
const int CAUSE_VOID = 20 , CAUSE_WITHER = 21 ;
if ( source = = nullptr )
{
return CUSTOM ;
}
if ( source = = DamageSource : : inFire )
{
return FIRE ;
}
if ( source = = DamageSource : : onFire )
{
return FIRE_TICK ;
}
if ( source = = DamageSource : : lava )
{
return LAVA ;
}
if ( source = = DamageSource : : inWall )
{
return SUFFOCATION ;
}
if ( source = = DamageSource : : drown )
{
return DROWNING ;
}
if ( source = = DamageSource : : starve )
{
return STARVATION ;
}
if ( source = = DamageSource : : cactus )
{
return CONTACT ;
}
if ( source = = DamageSource : : fall )
{
return FALL ;
}
if ( source = = DamageSource : : outOfWorld )
{
return CAUSE_VOID ;
}
if ( source = = DamageSource : : genericSource )
{
return CUSTOM ;
}
if ( source = = DamageSource : : magic )
{
return MAGIC ;
}
if ( source = = DamageSource : : wither )
{
return CAUSE_WITHER ;
}
if ( source = = DamageSource : : anvil )
{
return FALLING_BLOCK ;
}
if ( source = = DamageSource : : fallingBlock )
{
return FALLING_BLOCK ;
}
if ( source - > isExplosion ( ) )
{
return ENTITY_EXPLOSION ;
}
if ( source - > isProjectile ( ) )
{
return PROJECTILE ;
}
if ( source - > isMagic ( ) )
{
return MAGIC ;
}
if ( dynamic_cast < EntityDamageSource * > ( source ) ! = nullptr )
{
return ENTITY_ATTACK ;
}
return CUSTOM ;
}
bool FireEntityDamage ( int entityId , int entityTypeId , int dimId ,
double x , double y , double z , int causeId , double damage ,
double * outDamage ,
int damagerEntityId , int damagerEntityTypeId ,
double damagerX , double damagerY , double damagerZ )
{
if ( ! s_initialized | | ! s_managedFireEntityDamage )
{
* outDamage = damage ;
return false ;
}
double outDmg = damage ;
int cancelled = s_managedFireEntityDamage ( entityId , entityTypeId , dimId ,
x , y , z , causeId , damage , & outDmg ,
damagerEntityId , damagerEntityTypeId , damagerX , damagerY , damagerZ ) ;
* outDamage = outDmg ;
return cancelled ! = 0 ;
}
bool FireSignChange ( int entityId , int dimId ,
int x , int y , int z ,
const std : : wstring & line0 , const std : : wstring & line1 ,
const std : : wstring & line2 , const std : : wstring & line3 ,
std : : wstring outLines [ 4 ] )
{
if ( ! s_initialized | | ! s_managedFireSignChange )
{
return false ;
}
std : : string l0 = ServerRuntime : : StringUtils : : WideToUtf8 ( line0 ) ;
std : : string l1 = ServerRuntime : : StringUtils : : WideToUtf8 ( line1 ) ;
std : : string l2 = ServerRuntime : : StringUtils : : WideToUtf8 ( line2 ) ;
std : : string l3 = ServerRuntime : : StringUtils : : WideToUtf8 ( line3 ) ;
const int kBufSize = 512 ;
char outBuf [ kBufSize ] = { } ;
int outLens [ 4 ] = { } ;
int cancelled = s_managedFireSignChange ( entityId , dimId ,
x , y , z ,
l0 . empty ( ) ? " " : l0 . data ( ) , ( int ) l0 . size ( ) ,
l1 . empty ( ) ? " " : l1 . data ( ) , ( int ) l1 . size ( ) ,
l2 . empty ( ) ? " " : l2 . data ( ) , ( int ) l2 . size ( ) ,
l3 . empty ( ) ? " " : l3 . data ( ) , ( int ) l3 . size ( ) ,
outBuf , kBufSize , outLens ) ;
int offset = 0 ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( outLens [ i ] > 0 & & offset + outLens [ i ] < = kBufSize )
{
std : : string s ( outBuf + offset , outLens [ i ] ) ;
outLines [ i ] = ServerRuntime : : StringUtils : : Utf8ToWide ( s ) ;
}
else
{
outLines [ i ] = ( i = = 0 ? line0 : ( i = = 1 ? line1 : ( i = = 2 ? line2 : line3 ) ) ) ;
}
offset + = outLens [ i ] ;
}
return cancelled ! = 0 ;
}
int FireEntityDeath ( int entityId , int entityTypeId , int dimId ,
double x , double y , double z , int exp )
{
if ( ! s_initialized | | ! s_managedFireEntityDeath )
{
return exp ;
}
return s_managedFireEntityDeath ( entityId , entityTypeId , dimId , x , y , z , exp ) ;
}
int FirePlayerDeath ( int entityId , const std : : wstring & deathMessage , int exp ,
std : : wstring & outDeathMessage , int * outKeepInventory ,
int * outNewExp , int * outNewLevel , int * outKeepLevel )
{
if ( ! s_initialized | | ! s_managedFirePlayerDeath )
{
outDeathMessage = deathMessage ;
* outKeepInventory = 0 ;
* outNewExp = 0 ;
* outNewLevel = 0 ;
* outKeepLevel = 0 ;
return exp ;
}
std : : string msgUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( deathMessage ) ;
const int kBufSize = 2048 ;
char outMsgBuf [ kBufSize ] = { } ;
int outMsgLen = 0 ;
int keepInv = 0 ;
int newExp = 0 , newLevel = 0 , keepLevel = 0 ;
int outExp = s_managedFirePlayerDeath ( entityId ,
msgUtf8 . empty ( ) ? " " : msgUtf8 . data ( ) , ( int ) msgUtf8 . size ( ) , exp ,
outMsgBuf , kBufSize , & outMsgLen , & keepInv ,
& newExp , & newLevel , & keepLevel ) ;
if ( outMsgLen > 0 & & outMsgLen < kBufSize )
{
std : : string resultUtf8 ( outMsgBuf , outMsgLen ) ;
outDeathMessage = ServerRuntime : : StringUtils : : Utf8ToWide ( resultUtf8 ) ;
}
else
{
outDeathMessage = deathMessage ;
}
* outKeepInventory = keepInv ;
* outNewExp = newExp ;
* outNewLevel = newLevel ;
* outKeepLevel = keepLevel ;
return outExp ;
}
bool FirePlayerDropItem ( int entityId , int itemId , int itemCount , int itemAux ,
int * outItemId , int * outItemCount , int * outItemAux )
{
* outItemId = itemId ;
* outItemCount = itemCount ;
* outItemAux = itemAux ;
if ( ! s_initialized | | ! s_managedFirePlayerDropItem )
{
return false ;
}
int newId = itemId , newCount = itemCount , newAux = itemAux ;
long long result = s_managedFirePlayerDropItem ( entityId , itemId , itemCount , itemAux ,
& newId , & newCount , & newAux ) ;
* outItemId = newId ;
* outItemCount = newCount ;
* outItemAux = newAux ;
return result ! = 0 ;
}
bool FirePlayerInteract ( int entityId , int action ,
int itemId , int itemCount , int itemAux ,
int clickedX , int clickedY , int clickedZ ,
int blockFace , int dimId ,
int * outUseItemInHand )
{
* outUseItemInHand = 1 ;
if ( ! s_initialized | | ! s_managedFirePlayerInteract )
{
return false ;
}
int useItem = 1 ;
int result = s_managedFirePlayerInteract ( entityId , action ,
itemId , itemCount , itemAux ,
clickedX , clickedY , clickedZ ,
blockFace , dimId ,
& useItem ) ;
* outUseItemInHand = useItem ;
return result ! = 0 ;
}
bool FirePlayerInteractEntity ( int playerEntityId ,
int targetEntityId , int targetEntityTypeId ,
int dimId , double targetX , double targetY , double targetZ ,
float targetHealth , float targetMaxHealth , float targetEyeHeight )
{
if ( ! s_initialized | | ! s_managedFirePlayerInteractEntity )
{
return false ;
}
int result = s_managedFirePlayerInteractEntity ( playerEntityId ,
targetEntityId , targetEntityTypeId ,
dimId , targetX , targetY , targetZ ,
targetHealth , targetMaxHealth , targetEyeHeight ) ;
return result ! = 0 ;
}
bool FirePlayerPickupItem ( int playerEntityId ,
int itemEntityId , int dimId , double itemX , double itemY , double itemZ ,
int itemId , int itemCount , int itemAux , int remaining ,
int * outItemId , int * outItemCount , int * outItemAux )
{
if ( ! s_initialized | | ! s_managedFirePlayerPickupItem )
{
* outItemId = itemId ;
* outItemCount = itemCount ;
* outItemAux = itemAux ;
return false ;
}
* outItemId = itemId ;
* outItemCount = itemCount ;
* outItemAux = itemAux ;
int result = s_managedFirePlayerPickupItem ( playerEntityId ,
itemEntityId , dimId , itemX , itemY , itemZ ,
itemId , itemCount , itemAux , remaining ,
outItemId , outItemCount , outItemAux ) ;
return result ! = 0 ;
}
bool FireInventoryOpen ( int entityId , int nativeContainerType ,
const std : : wstring & title , int containerSize )
{
if ( ! s_initialized | | ! s_managedFireInventoryOpen )
{
return false ;
}
s_openContainerInfo [ entityId ] = { nativeContainerType , containerSize , title } ;
std : : string titleUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( title ) ;
int cancelled = s_managedFireInventoryOpen ( entityId , nativeContainerType ,
titleUtf8 . empty ( ) ? " " : titleUtf8 . data ( ) , ( int ) titleUtf8 . size ( ) ,
containerSize ) ;
return cancelled ! = 0 ;
}
bool HandlePlayerCommand ( int entityId , const std : : wstring & commandLine )
{
if ( ! s_initialized | | ! s_managedHandlePlayerCommand )
{
return false ;
}
std : : string cmdUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( commandLine ) ;
int handled = s_managedHandlePlayerCommand ( entityId ,
cmdUtf8 . empty ( ) ? " " : cmdUtf8 . data ( ) , ( int ) cmdUtf8 . size ( ) ) ;
return handled ! = 0 ;
}
bool HandleConsoleCommand ( const std : : string & commandLine )
{
if ( ! s_initialized | | ! s_managedHandleConsoleCommand )
{
return false ;
}
int handled = s_managedHandleConsoleCommand (
commandLine . empty ( ) ? " " : commandLine . data ( ) , ( int ) commandLine . size ( ) ) ;
return handled ! = 0 ;
}
int GetPluginCommandHelp ( char * outBuf , int outBufSize , int * outLen )
{
if ( ! s_initialized | | ! s_managedGetPluginCommandHelp )
{
return 0 ;
}
return s_managedGetPluginCommandHelp ( outBuf , outBufSize , outLen ) ;
}
bool FirePlayerTeleport ( int entityId ,
double fromX , double fromY , double fromZ , int fromDimId ,
double toX , double toY , double toZ , int toDimId ,
int cause ,
double * outToX , double * outToY , double * outToZ )
{
if ( ! s_initialized | | ! s_managedFirePlayerTeleport )
{
return false ;
}
double outCoords [ 3 ] = { toX , toY , toZ } ;
int cancelled = s_managedFirePlayerTeleport ( entityId ,
fromX , fromY , fromZ , fromDimId ,
toX , toY , toZ , toDimId ,
cause , outCoords ) ;
* outToX = outCoords [ 0 ] ;
* outToY = outCoords [ 1 ] ;
* outToZ = outCoords [ 2 ] ;
return cancelled ! = 0 ;
}
bool FirePlayerPortal ( int entityId ,
double fromX , double fromY , double fromZ , int fromDimId ,
double toX , double toY , double toZ , int toDimId ,
int cause ,
double * outToX , double * outToY , double * outToZ )
{
if ( ! s_initialized | | ! s_managedFirePlayerPortal )
{
return false ;
}
double outCoords [ 3 ] = { toX , toY , toZ } ;
int cancelled = s_managedFirePlayerPortal ( entityId ,
fromX , fromY , fromZ , fromDimId ,
toX , toY , toZ , toDimId ,
cause , outCoords ) ;
* outToX = outCoords [ 0 ] ;
* outToY = outCoords [ 1 ] ;
* outToZ = outCoords [ 2 ] ;
return cancelled ! = 0 ;
}
int FireInventoryClick ( int entityId , int slot , int button , int clickType )
{
if ( ! s_initialized | | ! s_managedFireInventoryClick )
{
return 0 ;
}
int nativeContainerType = - 1 ;
int containerSize = 0 ;
std : : string titleUtf8 ;
auto it = s_openContainerInfo . find ( entityId ) ;
if ( it ! = s_openContainerInfo . end ( ) )
{
nativeContainerType = it - > second . type ;
containerSize = it - > second . size ;
if ( ! it - > second . title . empty ( ) )
{
titleUtf8 = ServerRuntime : : StringUtils : : WideToUtf8 ( it - > second . title ) ;
}
}
return s_managedFireInventoryClick ( entityId , slot , button , clickType , nativeContainerType , containerSize ,
titleUtf8 . empty ( ) ? nullptr : titleUtf8 . data ( ) , ( int ) titleUtf8 . size ( ) ) ;
}
2026-03-22 15:48:51 -05:00
bool FireBedEnter ( int entityId , int dimId , int bedX , int bedY , int bedZ )
{
if ( ! s_initialized | | ! s_managedFireBedEnter )
return false ;
return s_managedFireBedEnter ( entityId , dimId , bedX , bedY , bedZ ) ! = 0 ;
}
void FireBedLeave ( int entityId , int dimId , int bedX , int bedY , int bedZ )
{
if ( ! s_initialized | | ! s_managedFireBedLeave )
return ;
s_managedFireBedLeave ( entityId , dimId , bedX , bedY , bedZ ) ;
}
2026-03-21 13:52:26 -05:00
} // namespace FourKitBridge