mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-04-25 08:27:28 +00:00
2430 lines
77 KiB
C++
2430 lines
77 KiB
C++
// 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\SetCarriedItemPacket.h"
|
|
#include "..\Minecraft.World\LevelSoundPacket.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);
|
|
|
|
struct hostfxr_initialize_parameters
|
|
{
|
|
size_t size;
|
|
const wchar_t *host_path;
|
|
const wchar_t *dotnet_root;
|
|
};
|
|
|
|
#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;
|
|
static std::wstring s_dotnetRoot;
|
|
|
|
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);
|
|
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);
|
|
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, void *getItemMeta, void *setItemMeta, void *setHeldItemSlot);
|
|
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,
|
|
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);
|
|
typedef void(__stdcall *fn_set_entity_callbacks)(void *setSneaking, void *setVelocity, void *setAllowFlight, void *playSound, void *setSleepingIgnored);
|
|
|
|
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;
|
|
static fn_fire_bed_enter s_managedFireBedEnter = nullptr;
|
|
static fn_fire_bed_leave s_managedFireBedLeave = nullptr;
|
|
static fn_set_entity_callbacks s_managedSetEntityCallbacks = nullptr;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// double[21] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored }
|
|
static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (!player)
|
|
{
|
|
memset(outData, 0, 21 * sizeof(double));
|
|
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;
|
|
outData[11] = player->isSleeping() ? 1.0 : 0.0;
|
|
outData[12] = (double)player->getSleepTimer();
|
|
outData[13] = player->isSneaking() ? 1.0 : 0.0;
|
|
outData[14] = player->isSprinting() ? 1.0 : 0.0;
|
|
outData[15] = player->onGround ? 1.0 : 0.0;
|
|
outData[16] = player->xd;
|
|
outData[17] = player->yd;
|
|
outData[18] = player->zd;
|
|
outData[19] = player->abilities.mayfly ? 1.0 : 0.0;
|
|
outData[20] = player->fk_sleepingIgnored ? 1.0 : 0.0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// [nameLen:int32][nameUTF8:bytes][loreCount:int32][lore0Len:int32][lore0UTF8:bytes]
|
|
// todo: des muass i no a bissl überarwatn
|
|
static int __cdecl NativeGetItemMeta(int entityId, int slot, char *outBuf, int bufSize)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (!player || !player->inventory)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned int size = player->inventory->getContainerSize();
|
|
if (slot < 0 || slot >= (int)size)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
auto item = player->inventory->getItem(slot);
|
|
if (!item || !item->hasTag())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
CompoundTag *tag = item->getTag();
|
|
if (!tag || !tag->contains(L"display"))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
CompoundTag *display = tag->getCompound(L"display");
|
|
bool hasName = display->contains(L"Name");
|
|
bool hasLore = display->contains(L"Lore");
|
|
|
|
if (!hasName && !hasLore)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int offset = 0;
|
|
|
|
if (hasName)
|
|
{
|
|
std::wstring wname = display->getString(L"Name");
|
|
std::string nameUtf8 = ServerRuntime::StringUtils::WideToUtf8(wname);
|
|
int nameLen = (int)nameUtf8.size();
|
|
if (offset + 4 + nameLen > bufSize) return 0;
|
|
memcpy(outBuf + offset, &nameLen, 4);
|
|
offset += 4;
|
|
memcpy(outBuf + offset, nameUtf8.data(), nameLen);
|
|
offset += nameLen;
|
|
}
|
|
else
|
|
{
|
|
int zero = 0;
|
|
if (offset + 4 > bufSize) return 0;
|
|
memcpy(outBuf + offset, &zero, 4);
|
|
offset += 4;
|
|
}
|
|
|
|
if (hasLore)
|
|
{
|
|
ListTag<StringTag> *lore = (ListTag<StringTag> *)display->getList(L"Lore");
|
|
int loreCount = lore->size();
|
|
if (offset + 4 > bufSize) return 0;
|
|
memcpy(outBuf + offset, &loreCount, 4);
|
|
offset += 4;
|
|
|
|
for (int i = 0; i < loreCount; i++)
|
|
{
|
|
std::wstring wline = lore->get(i)->data;
|
|
std::string lineUtf8 = ServerRuntime::StringUtils::WideToUtf8(wline);
|
|
int lineLen = (int)lineUtf8.size();
|
|
if (offset + 4 + lineLen > bufSize) return 0;
|
|
memcpy(outBuf + offset, &lineLen, 4);
|
|
offset += 4;
|
|
memcpy(outBuf + offset, lineUtf8.data(), lineLen);
|
|
offset += lineLen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int zero = 0;
|
|
if (offset + 4 > bufSize) return 0;
|
|
memcpy(outBuf + offset, &zero, 4);
|
|
offset += 4;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static void __cdecl NativeSetItemMeta(int entityId, int slot, const char *inBuf, int bufSize)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (!player || !player->inventory)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned int size = player->inventory->getContainerSize();
|
|
if (slot < 0 || slot >= (int)size)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto item = player->inventory->getItem(slot);
|
|
if (!item)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (inBuf == nullptr || bufSize <= 0)
|
|
{
|
|
item->resetHoverName();
|
|
if (item->hasTag())
|
|
{
|
|
CompoundTag *tag = item->getTag();
|
|
if (tag && tag->contains(L"display"))
|
|
{
|
|
CompoundTag *display = tag->getCompound(L"display");
|
|
display->remove(L"Lore");
|
|
if (display->isEmpty())
|
|
{
|
|
tag->remove(L"display");
|
|
if (tag->isEmpty())
|
|
{
|
|
item->setTag(nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
int offset = 0;
|
|
|
|
if (offset + 4 > bufSize) return;
|
|
int nameLen = 0;
|
|
memcpy(&nameLen, inBuf + offset, 4);
|
|
offset += 4;
|
|
|
|
if (nameLen > 0)
|
|
{
|
|
if (offset + nameLen > bufSize) return;
|
|
std::string nameUtf8(inBuf + offset, nameLen);
|
|
offset += nameLen;
|
|
std::wstring wname = ServerRuntime::StringUtils::Utf8ToWide(nameUtf8);
|
|
item->setHoverName(wname);
|
|
}
|
|
else
|
|
{
|
|
item->resetHoverName();
|
|
}
|
|
|
|
if (offset + 4 > bufSize) return;
|
|
int loreCount = 0;
|
|
memcpy(&loreCount, inBuf + offset, 4);
|
|
offset += 4;
|
|
|
|
if (loreCount > 0)
|
|
{
|
|
if (!item->hasTag()) item->setTag(new CompoundTag());
|
|
CompoundTag *tag = item->getTag();
|
|
if (!tag->contains(L"display")) tag->putCompound(L"display", new CompoundTag());
|
|
CompoundTag *display = tag->getCompound(L"display");
|
|
|
|
auto *loreList = new ListTag<StringTag>(L"Lore");
|
|
for (int i = 0; i < loreCount; i++)
|
|
{
|
|
if (offset + 4 > bufSize) break;
|
|
int lineLen = 0;
|
|
memcpy(&lineLen, inBuf + offset, 4);
|
|
offset += 4;
|
|
|
|
std::wstring wline;
|
|
if (lineLen > 0 && offset + lineLen <= bufSize)
|
|
{
|
|
std::string lineUtf8(inBuf + offset, lineLen);
|
|
offset += lineLen;
|
|
wline = ServerRuntime::StringUtils::Utf8ToWide(lineUtf8);
|
|
}
|
|
loreList->add(new StringTag(L"", wline));
|
|
}
|
|
display->put(L"Lore", loreList);
|
|
}
|
|
else
|
|
{
|
|
if (item->hasTag())
|
|
{
|
|
CompoundTag *tag = item->getTag();
|
|
if (tag && tag->contains(L"display"))
|
|
{
|
|
tag->getCompound(L"display")->remove(L"Lore");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void __cdecl NativeSetHeldItemSlot(int entityId, int slot)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (!player || !player->inventory) return;
|
|
if (slot < 0 || slot >= Inventory::getSelectionSize()) return;
|
|
player->inventory->selected = slot;
|
|
if (player->connection)
|
|
player->connection->queueSend(std::make_shared<SetCarriedItemPacket>(slot));
|
|
}
|
|
|
|
static void __cdecl NativeSetSneaking(int entityId, int sneak)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (player)
|
|
{
|
|
player->setSneaking(sneak != 0);
|
|
}
|
|
}
|
|
|
|
static void __cdecl NativeSetVelocity(int entityId, double x, double y, double z)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (player)
|
|
{
|
|
player->xd = x;
|
|
player->yd = y;
|
|
player->zd = z;
|
|
player->hurtMarked = true;
|
|
return;
|
|
}
|
|
auto entity = FindEntity(entityId);
|
|
if (entity)
|
|
{
|
|
entity->xd = x;
|
|
entity->yd = y;
|
|
entity->zd = z;
|
|
entity->hurtMarked = true;
|
|
}
|
|
}
|
|
|
|
static void __cdecl NativeSetAllowFlight(int entityId, int allowFlight)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (player)
|
|
{
|
|
player->abilities.mayfly = (allowFlight != 0);
|
|
if (!player->abilities.mayfly)
|
|
{
|
|
player->abilities.flying = false;
|
|
}
|
|
if (player->connection)
|
|
{
|
|
player->connection->send(std::make_shared<PlayerAbilitiesPacket>(&player->abilities));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void __cdecl NativePlaySound(int entityId, int soundId, double x, double y, double z, float volume, float pitch)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (player && player->connection)
|
|
{
|
|
player->connection->send(std::make_shared<LevelSoundPacket>(soundId, x, y, z, volume, pitch));
|
|
}
|
|
}
|
|
|
|
static void __cdecl NativeSetSleepingIgnored(int entityId, int ignored)
|
|
{
|
|
auto player = FindPlayer(entityId);
|
|
if (player)
|
|
{
|
|
player->fk_sleepingIgnored = (ignored != 0);
|
|
}
|
|
}
|
|
|
|
static std::wstring FindNet10SystemRoot()
|
|
{
|
|
// overengineered
|
|
// trying to do a lot of safeguards here, setups can be all over the place
|
|
// fixes an issue aiden had
|
|
|
|
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)
|
|
{
|
|
candidates.push_back(std::wstring(envRoot));
|
|
}
|
|
candidates.push_back(L"C:\\Program Files\\dotnet");
|
|
|
|
for (const auto &root : candidates)
|
|
{
|
|
std::wstring fxrDir = root + L"\\host\\fxr";
|
|
WIN32_FIND_DATAW fd;
|
|
HANDLE h = FindFirstFileW((fxrDir + L"\\*").c_str(), &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
{
|
|
continue;
|
|
}
|
|
bool has10 = false;
|
|
do
|
|
{
|
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && fd.cFileName[0] != L'.')
|
|
{
|
|
if (std::wstring(fd.cFileName).substr(0, 3) == L"10.")
|
|
{
|
|
has10 = true;
|
|
}
|
|
}
|
|
} while (!has10 && FindNextFileW(h, &fd));
|
|
FindClose(h);
|
|
if (has10)
|
|
{
|
|
return root;
|
|
}
|
|
}
|
|
|
|
return L"C:\\Program Files\\dotnet";
|
|
}
|
|
|
|
static bool TryLoadHostfxrFromPath(const std::wstring &path)
|
|
{
|
|
HMODULE lib = LoadLibraryW(path.c_str());
|
|
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");
|
|
|
|
if (s_initFn && s_getDelegateFn && s_closeFn)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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";
|
|
|
|
hostfxr_initialize_parameters initParams = {};
|
|
initParams.size = sizeof(hostfxr_initialize_parameters);
|
|
initParams.host_path = exePath;
|
|
initParams.dotnet_root = s_dotnetRoot.c_str();
|
|
|
|
hostfxr_handle ctx = nullptr;
|
|
int rc = s_initFn(runtimeConfigPath.c_str(), &initParams, &ctx);
|
|
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);
|
|
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);
|
|
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetEntityCallbacks", (void **)&s_managedSetEntityCallbacks);
|
|
|
|
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,
|
|
(void *)&NativeGetTileData,
|
|
(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,
|
|
(void *)&NativeGetItemMeta,
|
|
(void *)&NativeSetItemMeta,
|
|
(void *)&NativeSetHeldItemSlot);
|
|
|
|
s_managedSetEntityCallbacks(
|
|
(void *)&NativeSetSneaking,
|
|
(void *)&NativeSetVelocity,
|
|
(void *)&NativeSetAllowFlight,
|
|
(void *)&NativePlaySound,
|
|
(void *)&NativeSetSleepingIgnored);
|
|
|
|
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());
|
|
}
|
|
|
|
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);
|
|
}
|
|
} // namespace FourKitBridge
|