the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "System.h"
3#include "BasicTypeContainers.h"
4#include "InputOutputStream.h"
5#include "net.minecraft.network.packet.h"
6#include "PacketListener.h"
7#include "Packet.h"
8#include "com.mojang.nbt.h"
9
10#ifndef _CONTENT_PACKAGE
11#include "..\Minecraft.Client\Minecraft.h"
12#include "..\Minecraft.Client\Gui.h"
13#endif
14
15void Packet::staticCtor()
16{
17 //nextPrint = 0;
18
19 // 4J - Note that item IDs are now defined in virtual method for each packet type
20
21 // 4J Stu - The values for canSendToAnyClient may not necessarily be the correct choices
22 map(0, true, true, true, false, typeid(KeepAlivePacket), KeepAlivePacket::create);
23 map(1, true, true, true, false, typeid(LoginPacket), LoginPacket::create);
24 map(2, true, true, true, false, typeid(PreLoginPacket), PreLoginPacket::create);
25 map(3, true, true, true, false, typeid(ChatPacket), ChatPacket::create);
26 map(4, true, false, false, true, typeid(SetTimePacket), SetTimePacket::create);
27 map(5, true, false, false, true, typeid(SetEquippedItemPacket), SetEquippedItemPacket::create);
28 map(6, true, false, true, true, typeid(SetSpawnPositionPacket), SetSpawnPositionPacket::create);
29 map(7, false, true, false, false, typeid(InteractPacket), InteractPacket::create);
30 map(8, true, false, true, true, typeid(SetHealthPacket), SetHealthPacket::create);
31 map(9, true, true, true, false, typeid(RespawnPacket), RespawnPacket::create);
32
33 map(10, true, true, true, false, typeid(MovePlayerPacket), MovePlayerPacket::create);
34 map(11, true, true, true, true, typeid(MovePlayerPacket::Pos), MovePlayerPacket::Pos::create);
35 map(12, true, true, true, true, typeid(MovePlayerPacket::Rot), MovePlayerPacket::Rot::create);
36 map(13, true, true, true, true, typeid(MovePlayerPacket::PosRot), MovePlayerPacket::PosRot::create);
37
38 map(14, false, true, false, false, typeid(PlayerActionPacket), PlayerActionPacket::create);
39 map(15, false, true, false, false, typeid(UseItemPacket), UseItemPacket::create);
40 map(16, true, true, true, false, typeid(SetCarriedItemPacket), SetCarriedItemPacket::create);
41 // 4J-PB - we need to send to any client for the sleep in bed
42 //map(17, true, false, false, false, EntityActionAtPositionPacket));
43 map(17, true, false, true, false, typeid(EntityActionAtPositionPacket), EntityActionAtPositionPacket::create);
44 // 4J-PB - we need to send to any client for the wake up from sleeping
45 //map(18, true, true, false, false, AnimatePacket));
46 map(18, true, true, true, false, typeid(AnimatePacket), AnimatePacket::create);
47 map(19, false, true, false, false, typeid(PlayerCommandPacket), PlayerCommandPacket::create);
48
49 map(20, true, false, false, true, typeid(AddPlayerPacket), AddPlayerPacket::create);
50 map(22, true, false, true, true, typeid(TakeItemEntityPacket), TakeItemEntityPacket::create);
51 map(23, true, false, false, true, typeid(AddEntityPacket), AddEntityPacket::create);
52 map(24, true, false, false, true, typeid(AddMobPacket), AddMobPacket::create);
53 map(25, true, false, false, false, typeid(AddPaintingPacket), AddPaintingPacket::create);
54 map(26, true, false, false, false, typeid(AddExperienceOrbPacket), AddExperienceOrbPacket::create); // TODO New for 1.8.2 - Needs sendToAny?
55 map(27, false, true, false, false, typeid(PlayerInputPacket), PlayerInputPacket::create);
56 // 4J-PB - needs to go to any player, due to the knockback effect when a played is hit
57 map(28, true, false, true, true, typeid(SetEntityMotionPacket), SetEntityMotionPacket::create);
58 map(29, true, false, false, true, typeid(RemoveEntitiesPacket), RemoveEntitiesPacket::create);
59
60 map(30, true, false, false, false, typeid(MoveEntityPacket), MoveEntityPacket::create);
61 map(31, true, false, false, true, typeid(MoveEntityPacket::Pos), MoveEntityPacket::Pos::create);
62 map(32, true, false, false, true, typeid(MoveEntityPacket::Rot), MoveEntityPacket::Rot::create);
63 map(33, true, false, false, true, typeid(MoveEntityPacket::PosRot), MoveEntityPacket::PosRot::create);
64 map(34, true, false, false, true, typeid(TeleportEntityPacket), TeleportEntityPacket::create);
65 map(35, true, false, false, false, typeid(RotateHeadPacket), RotateHeadPacket::create);
66
67 // 4J - needs to go to any player, to create sound effect when a player is hit
68 map(38, true, false, true, true, typeid(EntityEventPacket), EntityEventPacket::create);
69 map(39, true, false, true, false, typeid(SetEntityLinkPacket), SetEntityLinkPacket::create);
70 map(40, true, false, true, true, typeid(SetEntityDataPacket), SetEntityDataPacket::create);
71 map(41, true, false, true, false, typeid(UpdateMobEffectPacket), UpdateMobEffectPacket::create);
72 map(42, true, false, true, false, typeid(RemoveMobEffectPacket), RemoveMobEffectPacket::create);
73 map(43, true, false, true, false, typeid(SetExperiencePacket), SetExperiencePacket::create);
74 map(44, true, false, true, false, typeid(UpdateAttributesPacket), UpdateAttributesPacket::create);
75
76 map(50, true, false, true, true, typeid(ChunkVisibilityPacket), ChunkVisibilityPacket::create);
77 map(51, true, false, true, true, typeid(BlockRegionUpdatePacket), BlockRegionUpdatePacket::create); // Changed to LevelChunkPacket in Java but we aren't using that
78 map(52, true, false, true, true, typeid(ChunkTilesUpdatePacket), ChunkTilesUpdatePacket::create);
79 map(53, true, false, true, true, typeid(TileUpdatePacket), TileUpdatePacket::create);
80 map(54, true, false, true, true, typeid(TileEventPacket), TileEventPacket::create);
81 map(55, true, false, false, false, typeid(TileDestructionPacket), TileDestructionPacket::create);
82
83 map(60, true, false, true, false, typeid(ExplodePacket), ExplodePacket::create);
84 map(61, true, false, true, false, typeid(LevelEventPacket), LevelEventPacket::create);
85 // 4J-PB - don't see the need for this, we can use 61
86 map(62, true, false, true, false, typeid(LevelSoundPacket), LevelSoundPacket::create);
87 map(63, true, false, true, false, typeid(LevelParticlesPacket), LevelParticlesPacket::create);
88
89 map(70, true, false, false, false, typeid(GameEventPacket), GameEventPacket::create);
90 map(71, true, false, false, false, typeid(AddGlobalEntityPacket), AddGlobalEntityPacket::create);
91
92 map(100, true, false, true, false, typeid(ContainerOpenPacket), ContainerOpenPacket::create);
93 map(101, true, true, true, false, typeid(ContainerClosePacket), ContainerClosePacket::create);
94 map(102, false, true, false, false, typeid(ContainerClickPacket), ContainerClickPacket::create);
95#ifndef _CONTENT_PACKAGE
96 // 4J Stu - We have some debug code that uses this packet to send data back to the server from the client
97 // We may wish to add this into the real game at some point
98 map(103, true, true, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create);
99#else
100 map(103, true, false, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create);
101#endif
102 map(104, true, false, true, false, typeid(ContainerSetContentPacket), ContainerSetContentPacket::create);
103 map(105, true, false, true, false, typeid(ContainerSetDataPacket), ContainerSetDataPacket::create);
104 map(106, true, true, true, false, typeid(ContainerAckPacket), ContainerAckPacket::create);
105 map(107, true, true, true, false, typeid(SetCreativeModeSlotPacket), SetCreativeModeSlotPacket::create);
106 map(108, false, true, false, false, typeid(ContainerButtonClickPacket), ContainerButtonClickPacket::create);
107
108 map(130, true, true, true, false, typeid(SignUpdatePacket), SignUpdatePacket::create);
109 map(131, true, false, true, false, typeid(ComplexItemDataPacket), ComplexItemDataPacket::create);
110 map(132, true, false, false, false, typeid(TileEntityDataPacket), TileEntityDataPacket::create);
111 map(133, true, false, true, false, typeid(TileEditorOpenPacket), TileEditorOpenPacket::create);
112
113 // 4J Added
114 map(150, false, true, false, false, typeid(CraftItemPacket), CraftItemPacket::create);
115 map(151, false, true, true, false, typeid(TradeItemPacket), TradeItemPacket::create);
116 map(152, false, true, false, false, typeid(DebugOptionsPacket), DebugOptionsPacket::create);
117 map(153, true, true, false, false, typeid(ServerSettingsChangedPacket), ServerSettingsChangedPacket::create);
118 map(154, true, true, true, false, typeid(TexturePacket), TexturePacket::create);
119 map(155, true, false, true, true, typeid(ChunkVisibilityAreaPacket), ChunkVisibilityAreaPacket::create);
120 map(156, true, false, false, true, typeid(UpdateProgressPacket), UpdateProgressPacket::create);
121 map(157, true, true, true, false, typeid(TextureChangePacket), TextureChangePacket::create);
122 map(158, true, false, true, false, typeid(UpdateGameRuleProgressPacket), UpdateGameRuleProgressPacket::create);
123 map(159, false, true, false, false, typeid(KickPlayerPacket), KickPlayerPacket::create);
124 map(160, true, true, true, false, typeid(TextureAndGeometryPacket), TextureAndGeometryPacket::create);
125 map(161, true, true, true, false, typeid(TextureAndGeometryChangePacket), TextureAndGeometryChangePacket::create);
126
127 map(162, true, false, false, false, typeid(MoveEntityPacketSmall), MoveEntityPacketSmall::create);
128 map(163, true, false, false, true, typeid(MoveEntityPacketSmall::Pos), MoveEntityPacketSmall::Pos::create);
129 map(164, true, false, false, true, typeid( MoveEntityPacketSmall::Rot), MoveEntityPacketSmall::Rot::create);
130 map(165, true, false, false, true, typeid(MoveEntityPacketSmall::PosRot), MoveEntityPacketSmall::PosRot::create);
131 map(166, true, true, false, false, typeid(XZPacket), XZPacket::create);
132 map(167, false, true, false, false, typeid(GameCommandPacket), GameCommandPacket::create);
133
134 map(200, true, false, true, false, typeid(AwardStatPacket), AwardStatPacket::create);
135 map(201, true, true, false, false, typeid(PlayerInfoPacket), PlayerInfoPacket::create); // TODO New for 1.8.2 - Repurposed by 4J
136 map(202, true, true, true, false, typeid(PlayerAbilitiesPacket), PlayerAbilitiesPacket::create);
137 // 4J Stu - These added 1.3.2, but don't think we need them
138 //map(203, true, true, true, false, ChatAutoCompletePacket.class);
139 //map(204, false, true, true, false, ClientInformationPacket.class);
140 map(205, false, true, true, false, typeid(ClientCommandPacket), ClientCommandPacket::create);
141
142 map(206, true, false, true, false, typeid(SetObjectivePacket), SetObjectivePacket::create);
143 map(207, true, false, true, false, typeid(SetScorePacket), SetScorePacket::create);
144 map(208, true, false, true, false, typeid(SetDisplayObjectivePacket), SetDisplayObjectivePacket::create);
145 map(209, true, false, true, false, typeid(SetPlayerTeamPacket), SetPlayerTeamPacket::create);
146
147 map(250, true, true, true, false, typeid(CustomPayloadPacket), CustomPayloadPacket::create);
148 // 4J Stu - These added 1.3.2, but don't think we need them
149 //map(252, true, true, SharedKeyPacket.class);
150 //map(253, true, false, ServerAuthDataPacket.class);
151 map(254, false, true, false, false, typeid(GetInfoPacket), GetInfoPacket::create); // TODO New for 1.8.2 - Needs sendToAny?
152 map(255, true, true, true, false, typeid(DisconnectPacket), DisconnectPacket::create);
153}
154
155IllegalArgumentException::IllegalArgumentException(const wstring& information)
156{
157 this->information = information;
158}
159
160IOException::IOException(const wstring& information)
161{
162 this->information = information;
163}
164
165Packet::Packet() : createTime( System::currentTimeMillis() )
166{
167 shouldDelay = false;
168}
169
170unordered_map<int, packetCreateFn> Packet::idToCreateMap;
171
172unordered_set<int> Packet::clientReceivedPackets;
173unordered_set<int> Packet::serverReceivedPackets;
174unordered_set<int> Packet::sendToAnyClientPackets;
175
176// 4J Added
177unordered_map<int, Packet::PacketStatistics *> Packet::outgoingStatistics = unordered_map<int, Packet::PacketStatistics *>();
178vector<Packet::PacketStatistics *> Packet::renderableStats = vector<Packet::PacketStatistics *>();
179int Packet::renderPos = 0;
180
181// sendToAnyClient - true - send to anyone, false - Sends to one person per dimension per machine
182void Packet::map(int id, bool receiveOnClient, bool receiveOnServer, bool sendToAnyClient, bool renderStats, const type_info& clazz, packetCreateFn createFn)
183{
184#if 0
185 if (idToClassMap.count(id) > 0) throw new IllegalArgumentException(wstring(L"Duplicate packet id:") + _toString<int>(id));
186 if (classToIdMap.count(clazz) > 0) throw new IllegalArgumentException(L"Duplicate packet class:"); // TODO + clazz);
187#endif
188
189 idToCreateMap.insert( unordered_map<int, packetCreateFn>::value_type(id, createFn) );
190
191#ifndef _CONTENT_PACKAGE
192#if PACKET_ENABLE_STAT_TRACKING
193 Packet::PacketStatistics *packetStatistics = new PacketStatistics(id);
194 outgoingStatistics[id] = packetStatistics;
195
196 if(renderStats)
197 {
198 renderableStats.push_back( packetStatistics );
199 }
200#endif
201#endif
202
203 if (receiveOnClient)
204 {
205 clientReceivedPackets.insert(id);
206 }
207 if (receiveOnServer)
208 {
209 serverReceivedPackets.insert(id);
210 }
211 if(sendToAnyClient)
212 {
213 sendToAnyClientPackets.insert(id);
214 }
215}
216
217// 4J Added to record data for outgoing packets
218void Packet::recordOutgoingPacket(shared_ptr<Packet> packet, int playerIndex)
219{
220#ifndef _CONTENT_PACKAGE
221#if PACKET_ENABLE_STAT_TRACKING
222#if 0
223 int idx = packet->getId();
224#else
225 int idx = playerIndex;
226 if( packet->getId() != 51 )
227 {
228 idx = 100;
229 }
230#endif
231 AUTO_VAR(it, outgoingStatistics.find(idx));
232
233 if( it == outgoingStatistics.end() )
234 {
235 Packet::PacketStatistics *packetStatistics = new PacketStatistics(idx);
236 outgoingStatistics[idx] = packetStatistics;
237 packetStatistics->addPacket(packet->getEstimatedSize());
238 }
239 else
240 {
241 it->second->addPacket(packet->getEstimatedSize());
242 }
243#endif
244#endif
245}
246
247void Packet::updatePacketStatsPIX()
248{
249#ifndef _CONTENT_PACKAGE
250#if PACKET_ENABLE_STAT_TRACKING
251
252 for( AUTO_VAR(it, outgoingStatistics.begin()); it != outgoingStatistics.end(); it++ )
253 {
254 Packet::PacketStatistics *stat = it->second;
255 __int64 count = stat->getRunningCount();
256 wchar_t pixName[256];
257 swprintf_s(pixName,L"Packet count %d",stat->id);
258// PIXReportCounter(pixName,(float)count);
259 __int64 total = stat->getRunningTotal();
260 swprintf_s(pixName,L"Packet bytes %d",stat->id);
261 PIXReportCounter(pixName,(float)total);
262 stat->IncrementPos();
263 }
264#endif
265#endif
266}
267
268shared_ptr<Packet> Packet::getPacket(int id)
269{
270 // 4J: Removed try/catch
271 return idToCreateMap[id]();
272}
273
274void Packet::writeBytes(DataOutputStream *dataoutputstream, byteArray bytes)
275{
276 dataoutputstream->writeShort(bytes.length);
277 dataoutputstream->write(bytes);
278}
279
280byteArray Packet::readBytes(DataInputStream *datainputstream)
281{
282 int size = datainputstream->readShort();
283 if (size < 0)
284 {
285 app.DebugPrintf("Key was smaller than nothing! Weird key!");
286#ifndef _CONTENT_PACKAGE
287 __debugbreak();
288#endif
289 return byteArray();
290 //throw new IOException("Key was smaller than nothing! Weird key!");
291 }
292
293 byteArray bytes(size);
294 datainputstream->readFully(bytes);
295
296 return bytes;
297}
298
299
300bool Packet::canSendToAnyClient(shared_ptr<Packet> packet)
301{
302 int packetId = packet->getId();
303
304 return sendToAnyClientPackets.count(packetId) != 0;
305}
306
307// 4J - now a pure virtual method
308/*
309int Packet::getId()
310{
311return id;
312}
313*/
314
315unordered_map<int, Packet::PacketStatistics *> Packet::statistics = unordered_map<int, Packet::PacketStatistics *>();
316
317//int Packet::nextPrint = 0;
318
319shared_ptr<Packet> Packet::readPacket(DataInputStream *dis, bool isServer) // throws IOException TODO 4J JEV, should this declare a throws?
320{
321 int id = 0;
322 shared_ptr<Packet> packet = nullptr;
323
324 // 4J - removed try/catch
325 // try
326 // {
327 id = dis->read();
328 if (id == -1) return nullptr;
329
330 if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end()))
331 {
332 //app.DebugPrintf("Bad packet id %d\n", id);
333 __debugbreak();
334 assert(false);
335 // throw new IOException(wstring(L"Bad packet id ") + _toString<int>(id));
336 }
337
338 packet = getPacket(id);
339 if (packet == NULL) assert(false);//throw new IOException(wstring(L"Bad packet id ") + _toString<int>(id));
340
341 //app.DebugPrintf("%s reading packet %d\n", isServer ? "Server" : "Client", packet->getId());
342 packet->read(dis);
343 // }
344 // catch (EOFException e)
345 // {
346 // // reached end of stream
347 // OutputDebugString("Reached end of stream");
348 // return NULL;
349 // }
350
351 // 4J - Don't bother tracking stats in a content package
352 // 4J Stu - This changes a bit in 1.0.1, but we don't really use it so stick with what we have
353#ifndef _CONTENT_PACKAGE
354#if PACKET_ENABLE_STAT_TRACKING
355 AUTO_VAR(it, statistics.find(id));
356
357 if( it == statistics.end() )
358 {
359 Packet::PacketStatistics *packetStatistics = new PacketStatistics(id);
360 statistics[id] = packetStatistics;
361 packetStatistics->addPacket(packet->getEstimatedSize());
362 }
363 else
364 {
365 it->second->addPacket(packet->getEstimatedSize());
366 }
367#endif
368#endif
369
370 return packet;
371}
372
373void Packet::writePacket(shared_ptr<Packet> packet, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws?
374{
375 //app.DebugPrintf("Writing packet %d\n", packet->getId());
376 dos->write(packet->getId());
377 packet->write(dos);
378}
379
380void Packet::writeUtf(const wstring& value, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws?
381{
382#if 0
383 if (value.length() > Short::MAX_VALUE)
384 {
385 throw new IOException(L"String too big");
386 }
387#endif
388
389 dos->writeShort((short)value.length());
390 dos->writeChars(value);
391}
392
393wstring Packet::readUtf(DataInputStream *dis, int maxLength) // throws IOException TODO 4J JEV, should this declare a throws?
394{
395
396 short stringLength = dis->readShort();
397 if (stringLength > maxLength)
398 {
399 wstringstream stream;
400 stream << L"Received string length longer than maximum allowed (" << stringLength << " > " << maxLength << ")";
401 assert(false);
402 // throw new IOException( stream.str() );
403 }
404 if (stringLength < 0)
405 {
406 assert(false);
407 // throw new IOException(L"Received string length is less than zero! Weird string!");
408 }
409
410 wstring builder = L"";
411 for (int i = 0; i < stringLength; i++)
412 {
413 wchar_t rc = dis->readChar();
414 builder.push_back( rc );
415 }
416
417 return builder;
418}
419
420Packet::PacketStatistics::PacketStatistics(int id) : id( id ), count( 0 ), totalSize( 0 ), samplesPos( 0 )
421{
422 memset(countSamples, 0, sizeof(countSamples));
423 memset(sizeSamples, 0, sizeof(sizeSamples));
424}
425
426void Packet::PacketStatistics::addPacket(int bytes)
427{
428 countSamples[samplesPos]++;
429 sizeSamples[samplesPos] += bytes;
430 timeSamples[samplesPos] = System::currentTimeMillis();
431 totalSize += bytes;
432 count++;
433}
434
435int Packet::PacketStatistics::getCount()
436{
437 return count;
438}
439
440double Packet::PacketStatistics::getAverageSize()
441{
442 if (count == 0)
443 {
444 return 0;
445 }
446 return (double) totalSize / count;
447}
448
449int Packet::PacketStatistics::getTotalSize()
450{
451 return totalSize;
452}
453
454__int64 Packet::PacketStatistics::getRunningTotal()
455{
456 __int64 total = 0;
457 __int64 currentTime = System::currentTimeMillis();
458 for( int i = 0; i < TOTAL_TICKS; i++ )
459 {
460 if( currentTime - timeSamples[i] <= 1000 )
461 {
462 total += sizeSamples[i];
463 }
464 }
465 return total;
466}
467
468__int64 Packet::PacketStatistics::getRunningCount()
469{
470 __int64 total = 0;
471 __int64 currentTime = System::currentTimeMillis();
472 for( int i = 0; i < TOTAL_TICKS; i++ )
473 {
474 if( currentTime - timeSamples[i] <= 1000 )
475 {
476 total += countSamples[i];
477 }
478 }
479 return total;
480}
481
482void Packet::PacketStatistics::IncrementPos()
483{
484 samplesPos = ( samplesPos + 1 ) % TOTAL_TICKS;
485 countSamples[samplesPos] = 0;
486 sizeSamples[samplesPos] = 0;
487 timeSamples[samplesPos] = 0;
488}
489
490bool Packet::canBeInvalidated()
491{
492 return false;
493}
494
495bool Packet::isInvalidatedBy(shared_ptr<Packet> packet)
496{
497 return false;
498}
499
500bool Packet::isAync()
501{
502 return false;
503}
504
505// 4J Stu - Brought these functions forward for enchanting/game rules
506shared_ptr<ItemInstance> Packet::readItem(DataInputStream *dis)
507{
508 shared_ptr<ItemInstance> item = nullptr;
509 int id = dis->readShort();
510 if (id >= 0)
511 {
512 int count = dis->readByte();
513 int damage = dis->readShort();
514
515 item = shared_ptr<ItemInstance>( new ItemInstance(id, count, damage) );
516 // 4J Stu - Always read/write the tag
517 //if (Item.items[id].canBeDepleted() || Item.items[id].shouldOverrideMultiplayerNBT())
518 {
519 item->tag = readNbt(dis);
520 }
521 }
522
523 return item;
524}
525
526void Packet::writeItem(shared_ptr<ItemInstance> item, DataOutputStream *dos)
527{
528 if (item == NULL)
529 {
530 dos->writeShort(-1);
531 }
532 else
533 {
534 dos->writeShort(item->id);
535 dos->writeByte(item->count);
536 dos->writeShort(item->getAuxValue());
537 // 4J Stu - Always read/write the tag
538 //if (item.getItem().canBeDepleted() || item.getItem().shouldOverrideMultiplayerNBT())
539 {
540 writeNbt(item->tag, dos);
541 }
542 }
543}
544
545CompoundTag *Packet::readNbt(DataInputStream *dis)
546{
547 int size = dis->readShort();
548 if (size < 0) return NULL;
549 byteArray buff(size);
550 dis->readFully(buff);
551 CompoundTag *result = (CompoundTag *) NbtIo::decompress(buff);
552 delete [] buff.data;
553 return result;
554}
555
556void Packet::writeNbt(CompoundTag *tag, DataOutputStream *dos)
557{
558 if (tag == NULL)
559 {
560 dos->writeShort(-1);
561 }
562 else
563 {
564 byteArray buff = NbtIo::compress(tag);
565 dos->writeShort((short) buff.length);
566 dos->write(buff);
567 delete [] buff.data;
568 }
569}