diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 27ee68b65..98c1f00f2 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -1795,7 +1795,7 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) chunkPacketManagement_PostTick(); } - lastTime = getCurrentTimeMillis(); + //lastTime = getCurrentTimeMillis(); // int64_t afterall = System::currentTimeMillis(); // PIXReportCounter(L"Server time all",(float)(afterall-beforeall)); // PIXReportCounter(L"Server ticks",(float)tickcount); diff --git a/Minecraft.Server.FourKit/FourKitHost.cs b/Minecraft.Server.FourKit/FourKitHost.cs index fb086262a..72a55168c 100644 --- a/Minecraft.Server.FourKit/FourKitHost.cs +++ b/Minecraft.Server.FourKit/FourKitHost.cs @@ -53,44 +53,45 @@ public static partial class FourKitHost return new Guid(System.Security.Cryptography.MD5.HashData(System.Text.Encoding.UTF8.GetBytes(s))); } + static double[] s_playerSnapshotBuffer = new double[27]; + static GCHandle? s_playerSnapshotBuffer_Handle = null; + // double[27] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored, experienceLevel, experienceProgress, totalExperience, foodLevel, saturation, exhaustion } internal static void SyncPlayerFromNative(Player player) { if (NativeBridge.GetPlayerSnapshot == null) return; - double[] buf = new double[27]; - var gh = GCHandle.Alloc(buf, GCHandleType.Pinned); - try + + if (s_playerSnapshotBuffer_Handle == null) { - NativeBridge.GetPlayerSnapshot(player.getEntityId(), gh.AddrOfPinnedObject()); + s_playerSnapshotBuffer_Handle = GCHandle.Alloc(s_playerSnapshotBuffer, GCHandleType.Pinned); } - finally - { - gh.Free(); - } - int dimId = (int)buf[10]; + + NativeBridge.GetPlayerSnapshot(player.getEntityId(), s_playerSnapshotBuffer_Handle.GetValueOrDefault().AddrOfPinnedObject()); + + int dimId = (int)s_playerSnapshotBuffer[10]; player.SetDimensionInternal(dimId); var world = FourKit.getWorld(dimId); - player.SetLocation(new Location(world, buf[0], buf[1], buf[2], (float)buf[8], (float)buf[9])); - player.SetHealthInternal(buf[3]); - player.SetMaxHealthInternal(buf[4]); - player.SetFallDistanceInternal((float)buf[5]); - player.SetGameModeInternal((GameMode)(int)buf[6]); - player.SetWalkSpeedInternal((float)buf[7]); - player.SetSleepingInternal(buf[11] != 0.0); - player.SetSleepTicksInternal((int)buf[12]); - player.SetSneakingInternal(buf[13] != 0.0); - player.SetSprintingInternal(buf[14] != 0.0); - player.SetOnGroundInternal(buf[15] != 0.0); - player.SetVelocityInternal(buf[16], buf[17], buf[18]); - player.SetAllowFlightInternal(buf[19] != 0.0); - player.SetSleepingIgnoredInternal(buf[20] != 0.0); - player.SetLevelInternal((int)buf[21]); - player.SetExpInternal((float)buf[22]); - player.SetTotalExperienceInternal((int)buf[23]); - player.SetFoodLevelInternal((int)buf[24]); - player.SetSaturationInternal((float)buf[25]); - player.SetExhaustionInternal((float)buf[26]); + player.SetLocation(new Location(world, s_playerSnapshotBuffer[0], s_playerSnapshotBuffer[1], s_playerSnapshotBuffer[2], (float)s_playerSnapshotBuffer[8], (float)s_playerSnapshotBuffer[9])); + player.SetHealthInternal(s_playerSnapshotBuffer[3]); + player.SetMaxHealthInternal(s_playerSnapshotBuffer[4]); + player.SetFallDistanceInternal((float)s_playerSnapshotBuffer[5]); + player.SetGameModeInternal((GameMode)(int)s_playerSnapshotBuffer[6]); + player.SetWalkSpeedInternal((float)s_playerSnapshotBuffer[7]); + player.SetSleepingInternal(s_playerSnapshotBuffer[11] != 0.0); + player.SetSleepTicksInternal((int)s_playerSnapshotBuffer[12]); + player.SetSneakingInternal(s_playerSnapshotBuffer[13] != 0.0); + player.SetSprintingInternal(s_playerSnapshotBuffer[14] != 0.0); + player.SetOnGroundInternal(s_playerSnapshotBuffer[15] != 0.0); + player.SetVelocityInternal(s_playerSnapshotBuffer[16], s_playerSnapshotBuffer[17], s_playerSnapshotBuffer[18]); + player.SetAllowFlightInternal(s_playerSnapshotBuffer[19] != 0.0); + player.SetSleepingIgnoredInternal(s_playerSnapshotBuffer[20] != 0.0); + player.SetLevelInternal((int)s_playerSnapshotBuffer[21]); + player.SetExpInternal((float)s_playerSnapshotBuffer[22]); + player.SetTotalExperienceInternal((int)s_playerSnapshotBuffer[23]); + player.SetFoodLevelInternal((int)s_playerSnapshotBuffer[24]); + player.SetSaturationInternal((float)s_playerSnapshotBuffer[25]); + player.SetExhaustionInternal((float)s_playerSnapshotBuffer[26]); } internal static void BroadcastNativeMessage(string message) diff --git a/Minecraft.Server.FourKit/Inventory/Inventory.cs b/Minecraft.Server.FourKit/Inventory/Inventory.cs index 014151283..52fda5fc1 100644 --- a/Minecraft.Server.FourKit/Inventory/Inventory.cs +++ b/Minecraft.Server.FourKit/Inventory/Inventory.cs @@ -48,14 +48,29 @@ public class Inventory : IEnumerable for (int i = 0; i < _items.Length; i++) { - int id = buf[i * 3]; - int count = buf[i * 3 + 1]; - int aux = buf[i * 3 + 2]; + int id = buf[i * 3 + 0]; + int aux = buf[i * 3 + 1]; + int packed = buf[i * 3 + 2]; + + ushort count = (ushort)((packed >> 8) & 0xFFFF); + + //byte flags = (byte)((packed >> 24) & 0xFF); + //bool hasMetadata = (flags & 0x1) != 0; //unused here + _items[i]?.UnbindFromInventory(); if (id > 0 && count > 0) { - _items[i] = new ItemStack(id, count, (short)aux); - _items[i]!.BindToInventory(this, i); + if (_items[i] == null) + { + _items[i] = new ItemStack(id, count, (short)aux); + } + else + { + _items[i]!.setTypeId(id); + _items[i]!.setAmount(count); + _items[i]!.setDurability((short)aux); + } + _items[i]!.BindToInventory(this, i); //should we unbind and rebind or just keep the bind? } else { diff --git a/Minecraft.Server.FourKit/Inventory/PlayerInventory.cs b/Minecraft.Server.FourKit/Inventory/PlayerInventory.cs index 9d50fa088..e5941146f 100644 --- a/Minecraft.Server.FourKit/Inventory/PlayerInventory.cs +++ b/Minecraft.Server.FourKit/Inventory/PlayerInventory.cs @@ -19,9 +19,14 @@ public class PlayerInventory : Inventory private int _heldItemSlot; internal HumanEntity? _holder; + private int[] syncBuffer; + private GCHandle syncBufferHandle; + internal PlayerInventory() : base("Player", InventoryType.PLAYER, INVENTORY_SIZE) { + this.syncBuffer = new int[121]; + this.syncBufferHandle = GCHandle.Alloc(this.syncBuffer, GCHandleType.Pinned); } protected internal override void EnsureSynced() @@ -30,38 +35,57 @@ public class PlayerInventory : Inventory return; int entityId = _holder.getEntityId(); - int[] buf = new int[121]; - var gh = GCHandle.Alloc(buf, GCHandleType.Pinned); - try - { - NativeBridge.GetPlayerInventory(entityId, gh.AddrOfPinnedObject()); - } - finally - { - gh.Free(); - } + + NativeBridge.GetPlayerInventory(entityId, this.syncBufferHandle.AddrOfPinnedObject()); + + byte[]? metadataBuffer = null; + GCHandle? metadataBufferHandle = null; for (int i = 0; i < INVENTORY_SIZE; i++) { - int id = buf[i * 3]; - int count = buf[i * 3 + 1]; - int aux = buf[i * 3 + 2]; + int id = this.syncBuffer[i * 3 + 0]; + int aux = this.syncBuffer[i * 3 + 1]; + int packed = this.syncBuffer[i * 3 + 2]; + + ushort count = (ushort)((packed >> 8) & 0xFFFF); + + byte flags = (byte)((packed >> 24) & 0xFF); + bool hasMetadata = (flags & 0x1) != 0; + _items[i]?.UnbindFromInventory(); if (id > 0 && count > 0) { - var stack = new ItemStack(id, count, (short)aux); - var meta = ReadMetaFromNative(entityId, i); - if (meta != null) - stack.setItemMetaInternal(meta); - _items[i] = stack; - stack.BindToInventory(this, i); + if (_items[i] == null) + { + _items[i] = new ItemStack(id, count, (short)aux); + } + else + { + _items[i]!.setTypeId(id); + _items[i]!.setAmount(count); + _items[i]!.setDurability((short)aux); + } + + if (hasMetadata) + { + var meta = ReadMetaFromNative(entityId, i, metadataBuffer, metadataBufferHandle); + if (meta != null) + { + _items[i]!.setItemMetaInternal(meta); + } + + } + _items[i]!.BindToInventory(this, i); } else { _items[i] = null; } } - _heldItemSlot = buf[120]; + _heldItemSlot = this.syncBuffer[120]; + + if (metadataBufferHandle.HasValue) + metadataBufferHandle.Value.Free(); } /// @@ -167,7 +191,7 @@ public class PlayerInventory : Inventory /// The ItemStack to set. public void setItemInHand(ItemStack? stack) { - EnsureSynced(); + EnsureSynced(); //we need to sync the current held slot, hate doing this here during a set call setItem(_heldItemSlot, stack); } @@ -224,41 +248,37 @@ public class PlayerInventory : Inventory /// The HumanEntity that owns this inventory. public HumanEntity? getHolder() => _holder; - private static ItemMeta? ReadMetaFromNative(int entityId, int slot) + private static ItemMeta? ReadMetaFromNative(int entityId, int slot, byte[]? buffer, GCHandle? bufferHandle) { if (NativeBridge.GetItemMeta == null) return null; - byte[] buf = new byte[4096]; - int bytesWritten; - var gh = GCHandle.Alloc(buf, GCHandleType.Pinned); - try + if (buffer == null) { - bytesWritten = NativeBridge.GetItemMeta(entityId, slot, gh.AddrOfPinnedObject(), buf.Length); - } - finally - { - gh.Free(); + buffer = new byte[4096]; + bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); } + int bytesWritten = NativeBridge.GetItemMeta(entityId, slot, bufferHandle.GetValueOrDefault().AddrOfPinnedObject(), buffer!.Length); + if (bytesWritten <= 0) return null; int offset = 0; - int nameLen = BitConverter.ToInt32(buf, offset); + int nameLen = BitConverter.ToInt32(buffer, offset); offset += 4; string? displayName = null; if (nameLen > 0) { - displayName = Encoding.UTF8.GetString(buf, offset, nameLen); + displayName = Encoding.UTF8.GetString(buffer, offset, nameLen); offset += nameLen; } int loreCount = 0; if (offset + 4 <= bytesWritten) { - loreCount = BitConverter.ToInt32(buf, offset); + loreCount = BitConverter.ToInt32(buffer, offset); offset += 4; } @@ -269,11 +289,11 @@ public class PlayerInventory : Inventory for (int i = 0; i < loreCount; i++) { if (offset + 4 > bytesWritten) break; - int lineLen = BitConverter.ToInt32(buf, offset); + int lineLen = BitConverter.ToInt32(buffer, offset); offset += 4; if (lineLen > 0 && offset + lineLen <= bytesWritten) { - lore.Add(Encoding.UTF8.GetString(buf, offset, lineLen)); + lore.Add(Encoding.UTF8.GetString(buffer, offset, lineLen)); offset += lineLen; } else @@ -286,7 +306,7 @@ public class PlayerInventory : Inventory int enchantCount = 0; if (offset + 4 <= bytesWritten) { - enchantCount = BitConverter.ToInt32(buf, offset); + enchantCount = BitConverter.ToInt32(buffer, offset); offset += 4; } @@ -299,10 +319,10 @@ public class PlayerInventory : Inventory if (offset + (4 + 4) > bytesWritten) break; - int type = BitConverter.ToInt32(buf, offset); + int type = BitConverter.ToInt32(buffer, offset); offset += 4; - int level = BitConverter.ToInt32(buf, offset); + int level = BitConverter.ToInt32(buffer, offset); offset += 4; enchants.Add((EnchantmentType)type, level); diff --git a/Minecraft.Server/FourKitNatives.cpp b/Minecraft.Server/FourKitNatives.cpp index d9eceda88..79dcc89f4 100644 --- a/Minecraft.Server/FourKitNatives.cpp +++ b/Minecraft.Server/FourKitNatives.cpp @@ -612,9 +612,41 @@ int __cdecl NativeGetPlayerAddress(int entityId, char *outIpBuf, int outIpBufSiz return 1; } +void WriteInventoryItemData(std::shared_ptr item, int index, int* outBuffer) { + if (item) { + //ItemFlags Key: + // 0x1 = hasMetadata (has data that needs to be gotten from "ReadMetaFromNative") + + uint8_t itemFlags = 0; + if (item->getTag() == nullptr) goto doneWithMetadataFlag; + CompoundTag* itemTag = item->getTag(); + + if (itemTag->contains(L"ench")) { + itemFlags |= 0x1; + goto doneWithMetadataFlag; + } + else { //we just want to check one tag for metadata and return for this flag, not all of them + CompoundTag* displayTag = itemTag->getCompound(L"display"); + if (displayTag->contains(L"Name") || displayTag->contains(L"Lore")) { + itemFlags |= 0x1; + goto doneWithMetadataFlag; + } + } + + + doneWithMetadataFlag: + + outBuffer[(index * 3) + 0] = item->id; + outBuffer[(index * 3) + 1] = item->getAuxValue(); + outBuffer[(index * 3) + 2] = (((int)itemFlags << 24) | ((int)item->count << 8)); + } +} void __cdecl NativeGetPlayerInventory(int entityId, int *outData) { + // 9 slots per row, 3 slots in the inventory and the hotbar, 4 armor slots, 1 hand slot + // (((slotsPerRow * Rows) + ArmorSlots) * AmountOfIntsPerSlot) + hand slot + // (((9 * 4) + 4) * 3) + 1 = 121 memset(outData, 0, 121 * sizeof(int)); auto player = FindPlayer(entityId); @@ -627,13 +659,7 @@ void __cdecl NativeGetPlayerInventory(int entityId, int *outData) 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(); - } + WriteInventoryItemData(player->inventory->getItem(i), i, outData); } outData[120] = player->inventory->selected; @@ -667,13 +693,7 @@ void __cdecl NativeGetContainerContents(int entityId, int *outData, int 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(); - } + WriteInventoryItemData((*items)[i], i, outData); } delete items; }