the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 569 lines 21 kB view raw
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}