finish rewrite; port to cmake, loads of other changes

Theres documentation at https://sylvessa.zip/fourkit/ now. And a bunch of other changes. Check the discord server for a more comprehensive list
This commit is contained in:
sylvessa 2026-03-21 13:52:26 -05:00
parent ecb3f00bd6
commit f5f9aa1cf5
107 changed files with 14289 additions and 40 deletions

View file

@ -33,6 +33,70 @@
#include "..\Minecraft.World\LevelChunk.h"
#include "LevelRenderer.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\FourKitBridge.h"
#include "..\Minecraft.World\ChatPacket.h"
// todo: rework this to use string localization funcs instead of hardcoded
static std::wstring FormatDeathMessage(const shared_ptr<ChatPacket>& packet)
{
if (!packet) return L"";
const std::wstring& player = packet->m_stringArgs.size() > 0 ? packet->m_stringArgs[0] : L"";
const std::wstring& killer = packet->m_stringArgs.size() > 1 ? packet->m_stringArgs[1] : L"";
const std::wstring& item = packet->m_stringArgs.size() > 2 ? packet->m_stringArgs[2] : L"";
switch (packet->m_messageType)
{
case ChatPacket::e_ChatCustom: return player;
case ChatPacket::e_ChatDeathInFire: return player + L" went up in flames";
case ChatPacket::e_ChatDeathOnFire: return player + L" burned to death";
case ChatPacket::e_ChatDeathLava: return player + L" tried to swim in lava";
case ChatPacket::e_ChatDeathInWall: return player + L" suffocated in a wall";
case ChatPacket::e_ChatDeathDrown: return player + L" drowned";
case ChatPacket::e_ChatDeathStarve: return player + L" starved to death";
case ChatPacket::e_ChatDeathCactus: return player + L" was pricked to death";
case ChatPacket::e_ChatDeathFall: return player + L" hit the ground too hard";
case ChatPacket::e_ChatDeathOutOfWorld: return player + L" fell out of the world";
case ChatPacket::e_ChatDeathGeneric: return player + L" died";
case ChatPacket::e_ChatDeathExplosion: return player + L" blew up";
case ChatPacket::e_ChatDeathMagic: return player + L" was killed by magic";
case ChatPacket::e_ChatDeathWither: return player + L" withered away";
case ChatPacket::e_ChatDeathDragonBreath: return player + L" was killed by Ender Dragon breath";
case ChatPacket::e_ChatDeathAnvil: return player + L" was squashed by a falling Anvil.";
case ChatPacket::e_ChatDeathFallingBlock: return player + L" was squashed by a falling block.";
case ChatPacket::e_ChatDeathFellAccidentLadder: return player + L" fell off a ladder";
case ChatPacket::e_ChatDeathFellAccidentVines: return player + L" fell off some vines";
case ChatPacket::e_ChatDeathFellAccidentWater: return player + L" fell out of the water";
case ChatPacket::e_ChatDeathFellAccidentGeneric:return player + L" fell from a high place";
case ChatPacket::e_ChatDeathFellKiller: return player + L" was doomed to fall";
case ChatPacket::e_ChatDeathMob:
case ChatPacket::e_ChatDeathPlayer: return killer.empty() ? player + L" was slain" : player + L" was slain by " + killer;
case ChatPacket::e_ChatDeathArrow: return killer.empty() ? player + L" was shot" : player + L" was shot by " + killer;
case ChatPacket::e_ChatDeathFireball: return killer.empty() ? player + L" was fireballed" : player + L" was fireballed by " + killer;
case ChatPacket::e_ChatDeathThrown: return killer.empty() ? player + L" was pummeled" : player + L" was pummeled by " + killer;
case ChatPacket::e_ChatDeathIndirectMagic: return killer.empty() ? player + L" was killed by magic" : player + L" was killed by " + killer + L" using magic";
case ChatPacket::e_ChatDeathThorns: return killer.empty() ? player + L" died" : player + L" was killed trying to hurt " + killer;
case ChatPacket::e_ChatDeathExplosionPlayer: return killer.empty() ? player + L" blew up" : player + L" was blown up by " + killer;
case ChatPacket::e_ChatDeathInFirePlayer: return killer.empty() ? player + L" went up in flames" : player + L" walked into fire whilst fighting " + killer;
case ChatPacket::e_ChatDeathOnFirePlayer: return killer.empty() ? player + L" burned to death" : player + L" was burnt to a crisp whilst fighting " + killer;
case ChatPacket::e_ChatDeathLavaPlayer: return killer.empty() ? player + L" tried to swim in lava" : player + L" tried to swim in lava to escape " + killer;
case ChatPacket::e_ChatDeathDrownPlayer: return killer.empty() ? player + L" drowned" : player + L" drowned whilst trying to escape " + killer;
case ChatPacket::e_ChatDeathCactusPlayer: return killer.empty() ? player + L" was pricked to death" : player + L" walked into a cactus whilst trying to escape " + killer;
case ChatPacket::e_ChatDeathFellAssist: return killer.empty() ? player + L" was doomed to fall" : player + L" was doomed to fall by " + killer;
case ChatPacket::e_ChatDeathFellFinish: return killer.empty() ? player + L" fell too far" : player + L" fell too far and was finished by " + killer;
case ChatPacket::e_ChatDeathPlayerItem: return (killer.empty() ? player + L" was slain" : player + L" was slain by " + killer) + (item.empty() ? L"" : L" using " + item);
case ChatPacket::e_ChatDeathArrowItem: return (killer.empty() ? player + L" was shot" : player + L" was shot by " + killer) + (item.empty() ? L"" : L" with " + item);
case ChatPacket::e_ChatDeathFireballItem: return (killer.empty() ? player + L" was fireballed" : player + L" was fireballed by " + killer) + (item.empty() ? L"" : L" with " + item);
case ChatPacket::e_ChatDeathThrownItem: return (killer.empty() ? player + L" was pummeled" : player + L" was pummeled by " + killer) + (item.empty() ? L"" : L" using " + item);
case ChatPacket::e_ChatDeathIndirectMagicItem: return (killer.empty() ? player + L" was killed by magic" : player + L" was killed by " + killer) + (item.empty() ? L"" : L" using " + item);
case ChatPacket::e_ChatDeathFellAssistItem: return (killer.empty() ? player + L" was doomed to fall" : player + L" was doomed to fall by " + killer) + (item.empty() ? L"" : L" using " + item);
case ChatPacket::e_ChatDeathFellFinishItem: return (killer.empty() ? player + L" fell too far" : player + L" fell too far and was finished by " + killer) + (item.empty() ? L"" : L" using " + item);
default: return player + L" died";
}
}
#endif
ServerPlayer::ServerPlayer(MinecraftServer *server, Level *level, const wstring& name, ServerPlayerGameMode *gameMode) : Player(level, name)
{
@ -567,12 +631,38 @@ shared_ptr<ItemInstance> ServerPlayer::getCarried(int slot)
void ServerPlayer::die(DamageSource *source)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
shared_ptr<ChatPacket> deathPacket = getCombatTracker()->getDeathMessagePacket();
std::wstring deathMsg = FormatDeathMessage(deathPacket);
int exp = getExperienceReward(nullptr);
std::wstring outDeathMsg;
int keepInventory = 0;
int outNewExp = 0, outNewLevel = 0, outKeepLevel = 0;
int outExp = FourKitBridge::FirePlayerDeath(entityId, deathMsg, exp, outDeathMsg, &keepInventory,
&outNewExp, &outNewLevel, &outKeepLevel);
fk_hasDeathState = true;
fk_deathKeepInventory = (keepInventory != 0);
fk_deathKeepLevel = (outKeepLevel != 0);
fk_deathNewExp = outNewExp;
fk_deathNewLevel = outNewLevel;
if (!outDeathMsg.empty())
server->getPlayers()->broadcastAll(std::make_shared<ChatPacket>(outDeathMsg));
if (keepInventory == 0 && !level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->dropAll();
}
#else
server->getPlayers()->broadcastAll(getCombatTracker()->getDeathMessagePacket());
if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->dropAll();
}
#endif
vector<Objective *> *objectives = level->getScoreboard()->findObjectiveFor(ObjectiveCriteria::DEATH_COUNT);
if(objectives)
@ -765,6 +855,10 @@ void ServerPlayer::changeDimension(int i)
}
else
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
bool portalDestModified = false;
double portalOutX = 0, portalOutY = 0, portalOutZ = 0;
#endif
if (dimension == 0 && i == 1)
{
awardStat(GenericStats::theEnd(), GenericStats::param_theEnd());
@ -772,7 +866,24 @@ void ServerPlayer::changeDimension(int i)
Pos *pos = server->getLevel(i)->getDimensionSpecificSpawn();
if (pos != nullptr)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
{
double outX, outY, outZ;
bool cancelled = FourKitBridge::FirePlayerPortal(entityId,
x, y, z, dimension,
pos->x, pos->y, pos->z, i,
4,
&outX, &outY, &outZ);
if (cancelled)
{
delete pos;
return;
}
connection->teleport(outX, outY, outZ, 0, 0);
}
#else
connection->teleport(pos->x, pos->y, pos->z, 0, 0);
#endif
delete pos;
}
@ -780,10 +891,48 @@ void ServerPlayer::changeDimension(int i)
}
else
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
{
double scale = server->getLevel(i)->getLevelData()->getHellScale();
double toX = x, toY = y, toZ = z;
if (i == -1)
{
toX = x / scale;
toZ = z / scale;
}
else if (dimension == -1 && i == 0)
{
toX = x * scale;
toZ = z * scale;
}
double outX, outY, outZ;
bool cancelled = FourKitBridge::FirePlayerPortal(entityId,
x, y, z, dimension,
toX, toY, toZ, i,
3,
&outX, &outY, &outZ);
if (cancelled)
return;
if (outX != toX || outY != toY || outZ != toZ)
{
portalDestModified = true;
portalOutX = outX;
portalOutY = outY;
portalOutZ = outZ;
}
}
#endif
// 4J: Removed on the advice of the mighty King of Achievments (JV)
// awardStat(GenericStats::portal(), GenericStats::param_portal());
}
server->getPlayers()->toggleDimension( dynamic_pointer_cast<ServerPlayer>(shared_from_this()), i);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (portalDestModified)
{
connection->teleport(portalOutX, portalOutY, portalOutZ, yRot, xRot);
}
#endif
lastSentExp = -1;
lastSentHealth = -1;
lastSentFood = -1;
@ -874,10 +1023,18 @@ bool ServerPlayer::startCrafting(int x, int y, int z)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false));
containerMenu = new CraftingMenu(inventory, level, x, y, z);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::WORKBENCH, L"", 9))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false));
refreshContainer(containerMenu);
}
else
{
@ -892,20 +1049,36 @@ bool ServerPlayer::openFireworks(int x, int y, int z)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
containerMenu = new FireworksMenu(inventory, level, x, y, z);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FIREWORKS, L"", 9))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
refreshContainer(containerMenu);
}
else if(dynamic_cast<CraftingMenu *>(containerMenu) != nullptr)
{
closeContainer();
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
containerMenu = new FireworksMenu(inventory, level, x, y, z);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FIREWORKS, L"", 9))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false));
refreshContainer(containerMenu);
}
else
{
@ -920,10 +1093,18 @@ bool ServerPlayer::startEnchanting(int x, int y, int z, const wstring &name)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9, !name.empty()));
containerMenu = new EnchantmentMenu(inventory, level, x, y, z);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9, !name.empty()));
refreshContainer(containerMenu);
}
else
{
@ -938,10 +1119,18 @@ bool ServerPlayer::startRepairing(int x, int y, int z)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false));
containerMenu = new AnvilMenu(inventory, level, x, y, z, dynamic_pointer_cast<Player>(shared_from_this()));
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::REPAIR_TABLE, L"", 9))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false));
refreshContainer(containerMenu);
}
else
{
@ -961,11 +1150,18 @@ bool ServerPlayer::openContainer(shared_ptr<Container> container)
int containerType = container->getContainerType();
assert(containerType >= 0);
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
containerMenu = new ContainerMenu(inventory, container);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, containerType, container->getCustomName(), container->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -980,10 +1176,18 @@ bool ServerPlayer::openHopper(shared_ptr<HopperTileEntity> container)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
containerMenu = new HopperMenu(inventory, container);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -998,10 +1202,18 @@ bool ServerPlayer::openHopper(shared_ptr<MinecartHopper> container)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
containerMenu = new HopperMenu(inventory, container);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -1016,10 +1228,18 @@ bool ServerPlayer::openFurnace(shared_ptr<FurnaceTileEntity> furnace)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()));
containerMenu = new FurnaceMenu(inventory, furnace);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -1034,10 +1254,18 @@ bool ServerPlayer::openTrap(shared_ptr<DispenserTileEntity> trap)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName()));
containerMenu = new TrapMenu(inventory, trap);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -1052,10 +1280,18 @@ bool ServerPlayer::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingSt
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName()));
containerMenu = new BrewingStandMenu(inventory, brewingStand);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -1070,10 +1306,18 @@ bool ServerPlayer::openBeacon(shared_ptr<BeaconTileEntity> beacon)
if(containerMenu == inventoryMenu)
{
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName()));
containerMenu = new BeaconMenu(inventory, beacon);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName()));
refreshContainer(containerMenu);
}
else
{
@ -1093,7 +1337,15 @@ bool ServerPlayer::openTrading(shared_ptr<Merchant> traderTarget, const wstring
containerMenu->addSlotListener(this);
shared_ptr<Container> container = static_cast<MerchantMenu *>(containerMenu)->getTradeContainer();
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::TRADER_NPC, name.empty() ? L"" : name, container->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::TRADER_NPC, name.empty() ? L"" : name, container->getContainerSize(), !name.empty()));
refreshContainer(containerMenu);
MerchantRecipeList *offers = traderTarget->getOffers(dynamic_pointer_cast<Player>(shared_from_this()));
if (offers != nullptr)
@ -1123,10 +1375,18 @@ bool ServerPlayer::openHorseInventory(shared_ptr<EntityHorse> horse, shared_ptr<
closeContainer();
}
nextContainerCounter();
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId));
containerMenu = new HorseInventoryMenu(inventory, container, horse);
containerMenu->containerId = containerCounter;
containerMenu->addSlotListener(this);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (FourKitBridge::FireInventoryOpen(entityId, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize()))
{
doCloseContainer();
return true;
}
#endif
connection->send(std::make_shared<ContainerOpenPacket>(containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId));
refreshContainer(containerMenu);
return true;
}