aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/Packet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Minecraft.World/Packet.cpp')
-rw-r--r--Minecraft.World/Packet.cpp596
1 files changed, 596 insertions, 0 deletions
diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp
new file mode 100644
index 00000000..0a59204c
--- /dev/null
+++ b/Minecraft.World/Packet.cpp
@@ -0,0 +1,596 @@
+#include "stdafx.h"
+#include "System.h"
+#include "BasicTypeContainers.h"
+#include "InputOutputStream.h"
+#include "net.minecraft.network.packet.h"
+#include "PacketListener.h"
+#include "Packet.h"
+#include "com.mojang.nbt.h"
+
+#ifndef _CONTENT_PACKAGE
+#include "..\Minecraft.Client\Minecraft.h"
+#include "..\Minecraft.Client\Gui.h"
+#endif
+
+void Packet::staticCtor()
+{
+ //nextPrint = 0;
+
+ // 4J - Note that item IDs are now defined in virtual method for each packet type
+
+ // 4J Stu - The values for canSendToAnyClient may not necessarily be the correct choices
+ map(0, true, true, true, false, typeid(KeepAlivePacket), KeepAlivePacket::create);
+ map(1, true, true, true, false, typeid(LoginPacket), LoginPacket::create);
+ map(2, true, true, true, false, typeid(PreLoginPacket), PreLoginPacket::create);
+ map(3, true, true, true, false, typeid(ChatPacket), ChatPacket::create);
+ map(4, true, false, false, true, typeid(SetTimePacket), SetTimePacket::create);
+ map(5, true, false, false, true, typeid(SetEquippedItemPacket), SetEquippedItemPacket::create);
+ map(6, true, false, true, true, typeid(SetSpawnPositionPacket), SetSpawnPositionPacket::create);
+ map(7, false, true, false, false, typeid(InteractPacket), InteractPacket::create);
+ map(8, true, false, true, true, typeid(SetHealthPacket), SetHealthPacket::create);
+ map(9, true, true, true, false, typeid(RespawnPacket), RespawnPacket::create);
+
+ map(10, true, true, true, false, typeid(MovePlayerPacket), MovePlayerPacket::create);
+ map(11, true, true, true, true, typeid(MovePlayerPacket::Pos), MovePlayerPacket::Pos::create);
+ map(12, true, true, true, true, typeid(MovePlayerPacket::Rot), MovePlayerPacket::Rot::create);
+ map(13, true, true, true, true, typeid(MovePlayerPacket::PosRot), MovePlayerPacket::PosRot::create);
+
+ map(14, false, true, false, false, typeid(PlayerActionPacket), PlayerActionPacket::create);
+ map(15, false, true, false, false, typeid(UseItemPacket), UseItemPacket::create);
+ map(16, false, true, false, false, typeid(SetCarriedItemPacket), SetCarriedItemPacket::create);
+ // 4J-PB - we need to send to any client for the sleep in bed
+ //map(17, true, false, false, false, EntityActionAtPositionPacket));
+ map(17, true, false, true, false, typeid(EntityActionAtPositionPacket), EntityActionAtPositionPacket::create);
+ // 4J-PB - we need to send to any client for the wake up from sleeping
+ //map(18, true, true, false, false, AnimatePacket));
+ map(18, true, true, true, false, typeid(AnimatePacket), AnimatePacket::create);
+ map(19, false, true, false, false, typeid(PlayerCommandPacket), PlayerCommandPacket::create);
+
+ map(20, true, false, false, true, typeid(AddPlayerPacket), AddPlayerPacket::create);
+ map(22, true, false, true, true, typeid(TakeItemEntityPacket), TakeItemEntityPacket::create);
+ map(23, true, false, false, true, typeid(AddEntityPacket), AddEntityPacket::create);
+ map(24, true, false, false, true, typeid(AddMobPacket), AddMobPacket::create);
+ map(25, true, false, false, false, typeid(AddPaintingPacket), AddPaintingPacket::create);
+ map(26, true, false, false, false, typeid(AddExperienceOrbPacket), AddExperienceOrbPacket::create); // TODO New for 1.8.2 - Needs sendToAny?
+ //map(27, false, true, false, false, PlayerInputPacket));
+ // 4J-PB - needs to go to any player, due to the knockback effect when a played is hit
+ map(28, true, false, true, true, typeid(SetEntityMotionPacket), SetEntityMotionPacket::create);
+ map(29, true, false, false, true, typeid(RemoveEntitiesPacket), RemoveEntitiesPacket::create);
+
+ map(30, true, false, false, false, typeid(MoveEntityPacket), MoveEntityPacket::create);
+ map(31, true, false, false, true, typeid(MoveEntityPacket::Pos), MoveEntityPacket::Pos::create);
+ map(32, true, false, false, true, typeid(MoveEntityPacket::Rot), MoveEntityPacket::Rot::create);
+ map(33, true, false, false, true, typeid(MoveEntityPacket::PosRot), MoveEntityPacket::PosRot::create);
+ map(34, true, false, false, true, typeid(TeleportEntityPacket), TeleportEntityPacket::create);
+ map(35, true, false, false, false, typeid(RotateHeadPacket), RotateHeadPacket::create);
+
+ // 4J - needs to go to any player, to create sound effect when a player is hit
+ map(38, true, false, true, true, typeid(EntityEventPacket), EntityEventPacket::create);
+ map(39, true, false, true, false, typeid(SetRidingPacket), SetRidingPacket::create);
+ map(40, true, false, true, true, typeid(SetEntityDataPacket), SetEntityDataPacket::create);
+ map(41, true, false, true, false, typeid(UpdateMobEffectPacket), UpdateMobEffectPacket::create);
+ map(42, true, false, true, false, typeid(RemoveMobEffectPacket), RemoveMobEffectPacket::create);
+ map(43, true, false, true, false, typeid(SetExperiencePacket), SetExperiencePacket::create);
+
+ map(50, true, false, true, true, typeid(ChunkVisibilityPacket), ChunkVisibilityPacket::create);
+ map(51, true, false, true, true, typeid(BlockRegionUpdatePacket), BlockRegionUpdatePacket::create); // Changed to LevelChunkPacket in Java but we aren't using that
+ map(52, true, false, true, true, typeid(ChunkTilesUpdatePacket), ChunkTilesUpdatePacket::create);
+ map(53, true, false, true, true, typeid(TileUpdatePacket), TileUpdatePacket::create);
+ map(54, true, false, true, true, typeid(TileEventPacket), TileEventPacket::create);
+ map(55, true, false, false, false, typeid(TileDestructionPacket), TileDestructionPacket::create);
+
+ map(60, true, false, true, false, typeid(ExplodePacket), ExplodePacket::create);
+ map(61, true, false, true, false, typeid(LevelEventPacket), LevelEventPacket::create);
+ // 4J-PB - don't see the need for this, we can use 61
+ map(62, true, false, true, false, typeid(LevelSoundPacket), LevelSoundPacket::create);
+ //map(62, true, false, true, false, typeid(LevelSoundPacket), LevelSoundPacket::create);
+
+ map(70, true, false, false, false, typeid(GameEventPacket), GameEventPacket::create);
+ map(71, true, false, false, false, typeid(AddGlobalEntityPacket), AddGlobalEntityPacket::create);
+
+ map(100, true, false, true, false, typeid(ContainerOpenPacket), ContainerOpenPacket::create);
+ map(101, true, true, true, false, typeid(ContainerClosePacket), ContainerClosePacket::create);
+ map(102, false, true, false, false, typeid(ContainerClickPacket), ContainerClickPacket::create);
+#ifndef _CONTENT_PACKAGE
+ // 4J Stu - We have some debug code that uses this packet to send data back to the server from the client
+ // We may wish to add this into the real game at some point
+ map(103, true, true, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create);
+#else
+ map(103, true, false, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create);
+#endif
+ map(104, true, false, true, false, typeid(ContainerSetContentPacket), ContainerSetContentPacket::create);
+ map(105, true, false, true, false, typeid(ContainerSetDataPacket), ContainerSetDataPacket::create);
+ map(106, true, true, true, false, typeid(ContainerAckPacket), ContainerAckPacket::create);
+ map(107, true, true, true, false, typeid(SetCreativeModeSlotPacket), SetCreativeModeSlotPacket::create);
+ map(108, false, true, false, false, typeid(ContainerButtonClickPacket), ContainerButtonClickPacket::create);
+
+ map(130, true, true, true, false, typeid(SignUpdatePacket), SignUpdatePacket::create);
+ map(131, true, false, true, false, typeid(ComplexItemDataPacket), ComplexItemDataPacket::create);
+ map(132, true, false, false, false, typeid(TileEntityDataPacket), TileEntityDataPacket::create);
+
+ // 4J Added
+ map(150, false, true, false, false, typeid(CraftItemPacket), CraftItemPacket::create);
+ map(151, false, true, true, false, typeid(TradeItemPacket), TradeItemPacket::create);
+ map(152, false, true, false, false, typeid(DebugOptionsPacket), DebugOptionsPacket::create);
+ map(153, true, true, false, false, typeid(ServerSettingsChangedPacket), ServerSettingsChangedPacket::create);
+ map(154, true, true, true, false, typeid(TexturePacket), TexturePacket::create);
+ map(155, true, false, true, true, typeid(ChunkVisibilityAreaPacket), ChunkVisibilityAreaPacket::create);
+ map(156, true, false, false, true, typeid(UpdateProgressPacket), UpdateProgressPacket::create);
+ map(157, true, true, true, false, typeid(TextureChangePacket), TextureChangePacket::create);
+ map(158, true, false, true, false, typeid(UpdateGameRuleProgressPacket), UpdateGameRuleProgressPacket::create);
+ map(159, false, true, false, false, typeid(KickPlayerPacket), KickPlayerPacket::create);
+ map(160, true, true, true, false, typeid(TextureAndGeometryPacket), TextureAndGeometryPacket::create);
+ map(161, true, true, true, false, typeid(TextureAndGeometryChangePacket), TextureAndGeometryChangePacket::create);
+
+ map(162, true, false, false, false, typeid(MoveEntityPacketSmall), MoveEntityPacketSmall::create);
+ map(163, true, false, false, true, typeid(MoveEntityPacketSmall::Pos), MoveEntityPacketSmall::Pos::create);
+ map(164, true, false, false, true, typeid( MoveEntityPacketSmall::Rot), MoveEntityPacketSmall::Rot::create);
+ map(165, true, false, false, true, typeid(MoveEntityPacketSmall::PosRot), MoveEntityPacketSmall::PosRot::create);
+ map(166, true, true, false, false, typeid(XZPacket), XZPacket::create);
+ map(167, false, true, false, false, typeid(GameCommandPacket), GameCommandPacket::create);
+
+ map(200, true, false, true, false, typeid(AwardStatPacket), AwardStatPacket::create);
+ map(201, true, true, false, false, typeid(PlayerInfoPacket), PlayerInfoPacket::create); // TODO New for 1.8.2 - Repurposed by 4J
+ map(202, true, true, true, false, typeid(PlayerAbilitiesPacket), PlayerAbilitiesPacket::create);
+ // 4J Stu - These added 1.3.2, but don't think we need them
+ //map(203, true, true, true, false, ChatAutoCompletePacket.class);
+ //map(204, false, true, true, false, ClientInformationPacket.class);
+ map(205, false, true, true, false, typeid(ClientCommandPacket), ClientCommandPacket::create);
+ map(250, true, true, true, false, typeid(CustomPayloadPacket), CustomPayloadPacket::create);
+ // 4J Stu - These added 1.3.2, but don't think we need them
+ //map(252, true, true, SharedKeyPacket.class);
+ //map(253, true, false, ServerAuthDataPacket.class);
+ map(254, false, true, false, false, typeid(GetInfoPacket), GetInfoPacket::create); // TODO New for 1.8.2 - Needs sendToAny?
+ map(255, true, true, true, false, typeid(DisconnectPacket), DisconnectPacket::create);
+}
+
+IllegalArgumentException::IllegalArgumentException(const wstring& information)
+{
+ this->information = information;
+}
+
+IOException::IOException(const wstring& information)
+{
+ this->information = information;
+}
+
+Packet::Packet() : createTime( System::currentTimeMillis() )
+{
+ shouldDelay = false;
+}
+
+unordered_map<int, packetCreateFn> Packet::idToCreateMap;
+
+unordered_set<int> Packet::clientReceivedPackets = unordered_set<int>();
+unordered_set<int> Packet::serverReceivedPackets = unordered_set<int>();
+unordered_set<int> Packet::sendToAnyClientPackets = unordered_set<int>();
+
+// 4J Added
+unordered_map<int, Packet::PacketStatistics *> Packet::outgoingStatistics = unordered_map<int, Packet::PacketStatistics *>();
+vector<Packet::PacketStatistics *> Packet::renderableStats = vector<Packet::PacketStatistics *>();
+int Packet::renderPos = 0;
+
+// sendToAnyClient - true - send to anyone, false - Sends to one person per dimension per machine
+void Packet::map(int id, bool receiveOnClient, bool receiveOnServer, bool sendToAnyClient, bool renderStats, const type_info& clazz, packetCreateFn createFn)
+{
+#if 0
+ if (idToClassMap.count(id) > 0) throw new IllegalArgumentException(wstring(L"Duplicate packet id:") + _toString<int>(id));
+ if (classToIdMap.count(clazz) > 0) throw new IllegalArgumentException(L"Duplicate packet class:"); // TODO + clazz);
+#endif
+
+ idToCreateMap.insert( unordered_map<int, packetCreateFn>::value_type(id, createFn) );
+
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ Packet::PacketStatistics *packetStatistics = new PacketStatistics(id);
+ outgoingStatistics[id] = packetStatistics;
+
+ if(renderStats)
+ {
+ renderableStats.push_back( packetStatistics );
+ }
+#endif
+#endif
+
+ if (receiveOnClient)
+ {
+ clientReceivedPackets.insert(id);
+ }
+ if (receiveOnServer)
+ {
+ serverReceivedPackets.insert(id);
+ }
+ if(sendToAnyClient)
+ {
+ sendToAnyClientPackets.insert(id);
+ }
+}
+
+// 4J Added to record data for outgoing packets
+void Packet::recordOutgoingPacket(shared_ptr<Packet> packet)
+{
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ AUTO_VAR(it, outgoingStatistics.find(packet->getId()));
+
+ if( it == outgoingStatistics.end() )
+ {
+ Packet::PacketStatistics *packetStatistics = new PacketStatistics(packet->getId());
+ outgoingStatistics[packet->getId()] = packetStatistics;
+ packetStatistics->addPacket(packet->getEstimatedSize());
+ }
+ else
+ {
+ it->second->addPacket(packet->getEstimatedSize());
+ }
+#endif
+#endif
+}
+
+void Packet::renderPacketStats(int id)
+{
+ AUTO_VAR(it, outgoingStatistics.find(id));
+
+ if( it != outgoingStatistics.end() )
+ {
+ it->second->renderStats();
+ }
+}
+
+void Packet::renderAllPacketStats()
+{
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ Minecraft *pMinecraft = Minecraft::GetInstance();
+ pMinecraft->gui->renderStackedGraph(Packet::renderPos, 512, renderableStats.size(), &Packet::getIndexedStatValue );
+
+ renderAllPacketStatsKey();
+
+ Packet::renderPos++;
+ Packet::renderPos%=511;
+#endif
+#endif
+}
+
+void Packet::renderAllPacketStatsKey()
+{
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ Minecraft *pMinecraft = Minecraft::GetInstance();
+ int total = Packet::renderableStats.size();
+ for(unsigned int i = 0; i < total; ++i)
+ {
+ Packet::PacketStatistics *stat = Packet::renderableStats[i];
+ float vary = (float)i/total;
+ int fColour = floor(vary * 0xffffff);
+
+ int colour = 0xff000000 + fColour;
+ pMinecraft->gui->drawString( pMinecraft->font, stat->getLegendString(), 900, 30 + (10 * i), colour);
+ }
+#endif
+#endif
+}
+
+__int64 Packet::getIndexedStatValue(unsigned int samplePos, unsigned int renderableId)
+{
+ __int64 val = 0;
+
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ val = renderableStats[renderableId]->getCountSample(samplePos);
+#endif
+#endif
+
+ return val;
+}
+
+
+shared_ptr<Packet> Packet::getPacket(int id)
+{
+ // 4J - removed try/catch
+ // try
+ // {
+ return idToCreateMap[id]();
+ // }
+ // catch (exception e)
+ // {
+ // // TODO 4J JEV print stack trace, newInstance doesnt throw an exception in c++ yet.
+ // printf("Skipping packet with id %d" , id);
+ // return NULL;
+ // }
+}
+
+void Packet::writeBytes(DataOutputStream *dataoutputstream, byteArray bytes)
+{
+ dataoutputstream->writeShort(bytes.length);
+ dataoutputstream->write(bytes);
+}
+
+byteArray Packet::readBytes(DataInputStream *datainputstream)
+{
+ int size = datainputstream->readShort();
+ if (size < 0)
+ {
+ app.DebugPrintf("Key was smaller than nothing! Weird key!");
+#ifndef _CONTENT_PACKAGE
+ __debugbreak();
+#endif
+ return byteArray();
+ //throw new IOException("Key was smaller than nothing! Weird key!");
+ }
+
+ byteArray bytes(size);
+ datainputstream->read(bytes);
+
+ return bytes;
+}
+
+
+bool Packet::canSendToAnyClient(shared_ptr<Packet> packet)
+{
+ int packetId = packet->getId();
+
+ return sendToAnyClientPackets.count(packetId) != 0;
+}
+
+// 4J - now a pure virtual method
+/*
+int Packet::getId()
+{
+return id;
+}
+*/
+
+unordered_map<int, Packet::PacketStatistics *> Packet::statistics = unordered_map<int, Packet::PacketStatistics *>();
+
+//int Packet::nextPrint = 0;
+
+shared_ptr<Packet> Packet::readPacket(DataInputStream *dis, bool isServer) // throws IOException TODO 4J JEV, should this declare a throws?
+{
+ int id = 0;
+ shared_ptr<Packet> packet = nullptr;
+
+ // 4J - removed try/catch
+ // try
+ // {
+ id = dis->read();
+ if (id == -1) return nullptr;
+
+ if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end()))
+ {
+ //app.DebugPrintf("Bad packet id %d\n", id);
+ __debugbreak();
+ assert(false);
+ // throw new IOException(wstring(L"Bad packet id ") + _toString<int>(id));
+ }
+
+ packet = getPacket(id);
+ if (packet == NULL) assert(false);//throw new IOException(wstring(L"Bad packet id ") + _toString<int>(id));
+
+ //app.DebugPrintf("%s reading packet %d\n", isServer ? "Server" : "Client", packet->getId());
+ packet->read(dis);
+ // }
+ // catch (EOFException e)
+ // {
+ // // reached end of stream
+ // OutputDebugString("Reached end of stream");
+ // return NULL;
+ // }
+
+ // 4J - Don't bother tracking stats in a content package
+ // 4J Stu - This changes a bit in 1.0.1, but we don't really use it so stick with what we have
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ AUTO_VAR(it, statistics.find(id));
+
+ if( it == statistics.end() )
+ {
+ Packet::PacketStatistics *packetStatistics = new PacketStatistics(id);
+ statistics[id] = packetStatistics;
+ packetStatistics->addPacket(packet->getEstimatedSize());
+ }
+ else
+ {
+ it->second->addPacket(packet->getEstimatedSize());
+ }
+#endif
+#endif
+
+ return packet;
+}
+
+void Packet::writePacket(shared_ptr<Packet> packet, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws?
+{
+ //app.DebugPrintf("Writing packet %d\n", packet->getId());
+ dos->write(packet->getId());
+ packet->write(dos);
+}
+
+void Packet::writeUtf(const wstring& value, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws?
+{
+#if 0
+ if (value.length() > Short::MAX_VALUE)
+ {
+ throw new IOException(L"String too big");
+ }
+#endif
+
+ dos->writeShort((short)value.length());
+ dos->writeChars(value);
+}
+
+wstring Packet::readUtf(DataInputStream *dis, int maxLength) // throws IOException TODO 4J JEV, should this declare a throws?
+{
+
+ short stringLength = dis->readShort();
+ if (stringLength > maxLength)
+ {
+ wstringstream stream;
+ stream << L"Received string length longer than maximum allowed (" << stringLength << " > " << maxLength << ")";
+ assert(false);
+ // throw new IOException( stream.str() );
+ }
+ if (stringLength < 0)
+ {
+ assert(false);
+ // throw new IOException(L"Received string length is less than zero! Weird string!");
+ }
+
+ wstring builder = L"";
+ for (int i = 0; i < stringLength; i++)
+ {
+ wchar_t rc = dis->readChar();
+ builder.push_back( rc );
+ }
+
+ return builder;
+}
+
+void Packet::PacketStatistics::addPacket(int bytes)
+{
+ if(count == 0)
+ {
+ firstSampleTime = System::currentTimeMillis();
+ }
+ count++;
+ totalSize += bytes;
+
+ // 4J Added
+ countSamples[samplesPos & (512 - 1)]++;
+ sizeSamples[samplesPos & (512 - 1)] += (unsigned int) bytes;
+}
+
+int Packet::PacketStatistics::getCount()
+{
+ return count;
+}
+
+double Packet::PacketStatistics::getAverageSize()
+{
+ if (count == 0)
+ {
+ return 0;
+ }
+ return (double) totalSize / count;
+}
+
+void Packet::PacketStatistics::renderStats( )
+{
+#ifndef _CONTENT_PACKAGE
+#if PACKET_ENABLE_STAT_TRACKING
+ samplesPos++;
+
+ countSamples[samplesPos & (512 - 1)] = 0;
+ sizeSamples[samplesPos & (512 - 1)] = 0;
+
+ Minecraft *pMinecraft = Minecraft::GetInstance();
+ pMinecraft->gui->renderGraph(512, samplesPos, countSamples, 1, 10, sizeSamples, 1, 50);
+#endif
+#endif
+}
+
+__int64 Packet::PacketStatistics::getCountSample(int samplePos)
+{
+ if(samplePos == 511)
+ {
+ samplesPos++;
+ countSamples[samplesPos & (512 - 1)] = 0;
+ sizeSamples[samplesPos & (512 - 1)] = 0;
+ }
+
+ return countSamples[samplePos] * 10;
+}
+
+wstring Packet::PacketStatistics::getLegendString()
+{
+ static wchar_t string[128];
+ double bps = 0.0;
+ if(firstSampleTime > 0)
+ {
+ float timeDiff = ((System::currentTimeMillis() - firstSampleTime)/1000);
+ if(timeDiff > 0) bps = totalSize / timeDiff;
+ }
+ swprintf(string, 128, L"id: %d , packets: %d , total: %d , bytes: %d, total: %d, %f Bps", id, countSamples[(samplesPos - 1) & (512 - 1)], count, sizeSamples[(samplesPos - 1) & (512 - 1)], totalSize, bps );
+ return string;
+}
+
+bool Packet::canBeInvalidated()
+{
+ return false;
+}
+
+bool Packet::isInvalidatedBy(shared_ptr<Packet> packet)
+{
+ return false;
+}
+
+bool Packet::isAync()
+{
+ return false;
+}
+
+// 4J Stu - Brought these functions forward for enchanting/game rules
+shared_ptr<ItemInstance> Packet::readItem(DataInputStream *dis)
+{
+ shared_ptr<ItemInstance> item = nullptr;
+ int id = dis->readShort();
+ if (id >= 0)
+ {
+ int count = dis->readByte();
+ int damage = dis->readShort();
+
+ item = shared_ptr<ItemInstance>( new ItemInstance(id, count, damage) );
+ // 4J Stu - Always read/write the tag
+ //if (Item.items[id].canBeDepleted() || Item.items[id].shouldOverrideMultiplayerNBT())
+ {
+ item->tag = readNbt(dis);
+ }
+ }
+
+ return item;
+}
+
+void Packet::writeItem(shared_ptr<ItemInstance> item, DataOutputStream *dos)
+{
+ if (item == NULL)
+ {
+ dos->writeShort(-1);
+ }
+ else
+ {
+ dos->writeShort(item->id);
+ dos->writeByte(item->count);
+ dos->writeShort(item->getAuxValue());
+ // 4J Stu - Always read/write the tag
+ //if (item.getItem().canBeDepleted() || item.getItem().shouldOverrideMultiplayerNBT())
+ {
+ writeNbt(item->tag, dos);
+ }
+ }
+}
+
+CompoundTag *Packet::readNbt(DataInputStream *dis)
+{
+ int size = dis->readShort();
+ if (size < 0) return NULL;
+ byteArray buff(size);
+ dis->readFully(buff);
+ CompoundTag *result = (CompoundTag *) NbtIo::decompress(buff);
+ delete [] buff.data;
+ return result;
+}
+
+void Packet::writeNbt(CompoundTag *tag, DataOutputStream *dos)
+{
+ if (tag == NULL)
+ {
+ dos->writeShort(-1);
+ }
+ else
+ {
+ byteArray buff = NbtIo::compress(tag);
+ dos->writeShort((short) buff.length);
+ dos->write(buff);
+ delete [] buff.data;
+ }
+}