public static void SendLevel(ClassicProtocol session, Level level, int volume) { using (LevelChunkStream dst = new LevelChunkStream(session)) using (Stream stream = dst.CompressMapHeader(volume)) { if (level.MightHaveCustomBlocks()) { CompressMap(level, stream, dst); } else { CompressMapSimple(level, stream, dst); } } }
public void SendLevel(Level prev, Level level) { int volume = level.blocks.Length; if (player.Supports(CpeExt.FastMap)) { Send(Packet.LevelInitaliseExt(volume)); } else { Send(Packet.LevelInitalise()); } if (player.hasBlockDefs) { if (prev != null && prev != level) { RemoveOldLevelCustomBlocks(prev); } BlockDefinition.SendLevelCustomBlocks(player); if (player.Supports(CpeExt.InventoryOrder)) { BlockDefinition.SendLevelInventoryOrder(player); } } using (LevelChunkStream dst = new LevelChunkStream(player)) using (Stream stream = LevelChunkStream.CompressMapHeader(player, volume, dst)) { if (level.MightHaveCustomBlocks()) { LevelChunkStream.CompressMap(player, stream, dst); } else { LevelChunkStream.CompressMapSimple(player, stream, dst); } } // Force players to read the MOTD (clamped to 3 seconds at most) if (level.Config.LoadDelay > 0) { Thread.Sleep(level.Config.LoadDelay); } Send(Packet.LevelFinalise(level.Width, level.Height, level.Length)); }
public unsafe static void CompressMap(Player p, Stream stream, LevelChunkStream dst) { const int bufferSize = 64 * 1024; byte[] buffer = new byte[bufferSize]; int bIndex = 0; // Store on stack instead of performing function call for every block in map byte *conv = stackalloc byte[Block.ExtendedCount]; byte *convExt = conv + Block.Count; #if TEN_BIT_BLOCKS byte *convExt2 = conv + Block.Count * 2; byte *convExt3 = conv + Block.Count * 3; #endif CalcFallbacks(p, conv); Level lvl = p.level; byte[] blocks = lvl.blocks; float progScale = 100.0f / blocks.Length; // compress the map data in 64 kb chunks #if TEN_BIT_BLOCKS if (p.hasExtBlocks) { byte[] buffer2 = new byte[bufferSize]; using (LevelChunkStream dst2 = new LevelChunkStream(p)) using (Stream stream2 = LevelChunkStream.CompressMapHeader(p, blocks.Length, dst2)) { dst.chunkValue = 0; // 'classic' blocks dst2.chunkValue = 1; // 'extended' blocks for (int i = 0; i < blocks.Length; ++i) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 0; } else if (block == Block.custom_block_2) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 1; } else if (block == Block.custom_block_3) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 2; } else { buffer[bIndex] = conv[block]; buffer2[bIndex] = 0; } bIndex++; if (bIndex == bufferSize) { stream.Write(buffer, 0, bufferSize); stream2.Write(buffer2, 0, bufferSize); bIndex = 0; } } if (bIndex > 0) { stream2.Write(buffer2, 0, bIndex); } } } else if (p.hasBlockDefs) { for (int i = 0; i < blocks.Length; ++i) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); } else if (block == Block.custom_block_2) { buffer[bIndex] = convExt2[lvl.GetExtTile(i)]; } else if (block == Block.custom_block_3) { buffer[bIndex] = convExt3[lvl.GetExtTile(i)]; } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } else { for (int i = 0; i < blocks.Length; ++i) { byte block = blocks[i]; if (block == Block.custom_block) { block = convExt[lvl.GetExtTile(i)]; } else if (block == Block.custom_block_2) { block = convExt2[lvl.GetExtTile(i)]; } else if (block == Block.custom_block_3) { block = convExt3[lvl.GetExtTile(i)]; } buffer[bIndex] = conv[block]; bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } #else if (p.hasBlockDefs) { for (int i = 0; i < blocks.Length; ++i) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } else { for (int i = 0; i < blocks.Length; ++i) { byte block = blocks[i]; if (block == Block.custom_block) { block = convExt[lvl.GetExtTile(i)]; } buffer[bIndex] = conv[block]; bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } #endif if (bIndex > 0) { stream.Write(buffer, 0, bIndex); } }
unsafe static void CompressMap(Level lvl, Stream stream, LevelChunkStream dst) { const int bufferSize = 64 * 1024; byte[] buffer = new byte[bufferSize]; int bIndex = 0; ClassicProtocol s = dst.session; byte[] blocks = lvl.blocks; float progScale = 100.0f / blocks.Length; // Store on stack instead of performing function call for every block in map byte *conv = stackalloc byte[Block.SUPPORTED_COUNT]; byte *convExt = conv + 256; // 256 blocks per group/class #if TEN_BIT_BLOCKS byte *convExt2 = conv + 256 * 2; byte *convExt3 = conv + 256 * 3; #endif for (int j = 0; j < Block.SUPPORTED_COUNT; j++) { conv[j] = (byte)s.ConvertBlock((BlockID)j); } // compress the map data in 64 kb chunks #if TEN_BIT_BLOCKS if (s.hasExtBlocks) { // Initially assume all custom blocks are <= 255 int i; for (i = 0; i < blocks.Length; i++) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); } else if (block == Block.custom_block_2) { break; } else if (block == Block.custom_block_3) { break; } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { stream.Write(buffer, 0, bufferSize); bIndex = 0; } } // Check if map only used custom blocks <= 255 if (bIndex > 0) { stream.Write(buffer, 0, bIndex); } if (i == blocks.Length) { return; } bIndex = 0; // Nope - have to go slower path now using (LevelChunkStream dst2 = new LevelChunkStream(s)) using (Stream stream2 = dst2.CompressMapHeader(blocks.Length)) { dst2.chunkValue = 1; // 'extended' blocks byte[] buffer2 = new byte[bufferSize]; // Need to fill in all the upper 8 bits of blocks before this one with 0 for (int j = 0; j < i; j += bufferSize) { int len = Math.Min(bufferSize, i - j); stream2.Write(buffer2, 0, len); } for (; i < blocks.Length; i++) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 0; } else if (block == Block.custom_block_2) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 1; } else if (block == Block.custom_block_3) { buffer[bIndex] = lvl.GetExtTile(i); buffer2[bIndex] = 2; } else { buffer[bIndex] = conv[block]; buffer2[bIndex] = 0; } bIndex++; if (bIndex == bufferSize) { stream.Write(buffer, 0, bufferSize); stream2.Write(buffer2, 0, bufferSize); bIndex = 0; } } if (bIndex > 0) { stream2.Write(buffer2, 0, bIndex); } } } else { for (int i = 0; i < blocks.Length; i++) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = convExt[lvl.GetExtTile(i)]; } else if (block == Block.custom_block_2) { buffer[bIndex] = convExt2[lvl.GetExtTile(i)]; } else if (block == Block.custom_block_3) { buffer[bIndex] = convExt3[lvl.GetExtTile(i)]; } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } #else if (s.hasBlockDefs) { for (int i = 0; i < blocks.Length; i++) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = lvl.GetExtTile(i); } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } else { for (int i = 0; i < blocks.Length; i++) { byte block = blocks[i]; if (block == Block.custom_block) { buffer[bIndex] = convExt[lvl.GetExtTile(i)]; } else { buffer[bIndex] = conv[block]; } bIndex++; if (bIndex == bufferSize) { dst.chunkValue = (byte)(i * progScale); stream.Write(buffer, 0, bufferSize); bIndex = 0; } } } #endif if (bIndex > 0) { stream.Write(buffer, 0, bIndex); } }