private BinaryTagNodeMultiple GetCompressedBinaryTagBlock(uint blockID, int len) { var currentNode = new BinaryTagNodeMultiple("_"); var blockNode = new BinaryTagNodeUShort("block"); var lengthNode = new BinaryTagNodeUShort("length"); //Biggest possible value is 4096 so ushort lengthNode.Value = (ushort)Math.Max(0, len); blockNode.Value = (ushort)blockID; currentNode.Add(lengthNode); currentNode.Add(blockNode); return(currentNode); }
public void PopulateFromBinaryTag(BinaryTagNodeMultiple tag) { #region Block Loading var blocksNode = (BinaryTagNodeList)tag.Get("blocks"); var blockCompressionType = (BinaryTagNodeByte)tag.Get("blockCompression"); if (blocksNode != null && blockCompressionType != null) { //No compression if (blockCompressionType.Value == 0) { for (int i = 0; i < blocksNode.Count; i++) { var node = (BinaryTagNodeUShort)blocksNode[i]; if (node != null) { var nodeName = node.Name; int pos = int.Parse(nodeName); int x, y, z; z = pos / (CHUNK_SIZE * CHUNK_SIZE); pos -= (z * CHUNK_SIZE * CHUNK_SIZE); y = pos / CHUNK_SIZE; x = pos % CHUNK_SIZE; chunk_blockdata[x, y, z] = node.Value; } } } //Compression Section else if (blockCompressionType.Value == 1) { //used for traversing the linear array int linearPosition = -1; for (int i = 0; i < blocksNode.Count; i++) { var nodeRoot = (BinaryTagNodeMultiple)blocksNode[i]; if (nodeRoot != null) { var blockID = (BinaryTagNodeUShort)nodeRoot.Get("block"); var length = (BinaryTagNodeUShort)nodeRoot.Get("length"); //null check if (blockID != null && length != null) { for (int j = 0; j < length.Value; j++) { linearPosition++; //Convert from linear to 3d var posTmp = linearPosition; int x, y, z; z = posTmp / (CHUNK_SIZE * CHUNK_SIZE); posTmp -= (z * CHUNK_SIZE * CHUNK_SIZE); y = posTmp / CHUNK_SIZE; x = posTmp % CHUNK_SIZE; chunk_blockdata[x, y, z] = blockID.Value; } } } } } } #endregion #region Block State Loading #endregion #region Lighting Loading #endregion #region Metadata Loading #endregion #region Block Updates var blockUpdatesNodes = (BinaryTagNodeList)tag.Get("updates"); if (blockUpdatesNodes != null) { for (int i = 0; i < blockUpdatesNodes.Count; i++) { var theThing = (BinaryTagNodeInt)blockUpdatesNodes[i]; if (theThing != null && theThing.Name == "block_update") { //Extract the X, Y, Z values from the int int x, y, z; z = theThing.Value / (CHUNK_SIZE * CHUNK_SIZE); theThing.Value -= (z * CHUNK_SIZE * CHUNK_SIZE); y = theThing.Value / CHUNK_SIZE; x = theThing.Value % CHUNK_SIZE; //Add the block update Vector3Int theVector = new Vector3Int(x, y, z); chunk_updates.Add(GetHash(x, y, z), theVector); } } } #endregion }
public void LoadChunks() { //TODO: Deserialize //ROADMAP: // // Get chunks folder // Get level data // Get player position // Apply level data // Get chunk at player pos // Read // Update => if(chunkManager.ChunkCount < ChunkThreshold) { return; } #region World Data BinaryTagNode levelData = FileUtil.DeseralizeFromFile(Path.Combine(WorldDirectory, "level.dat")); if (levelData.Type == TagType.TagEnd) { Logger.error(-1, "The file \"" + Path.Combine(WorldDirectory, "level.dat") + "\" doesn't exist, or is malformed!"); } if (levelData.Type == TagType.TagMultiple) { try { BinaryTagNodeMultiple levelRoot = (BinaryTagNodeMultiple)levelData; #region Player state BinaryTagNodeMultiple playerTag = (BinaryTagNodeMultiple)levelRoot.Get("player"); BinaryTagNodeMultiple playerPosTag = (BinaryTagNodeMultiple)playerTag.Get("position"); BinaryTagNodeMultiple playerRotTag = (BinaryTagNodeMultiple)playerTag.Get("rotation"); player.position.X = ((BinaryTagNodeFloat)playerPosTag.Get("X")).Value; player.position.Y = ((BinaryTagNodeFloat)playerPosTag.Get("Y")).Value; player.position.Z = ((BinaryTagNodeFloat)playerPosTag.Get("Z")).Value; player.camera.Pitch = ((BinaryTagNodeFloat)playerRotTag.Get("X")).Value; player.camera.Yaw = ((BinaryTagNodeFloat)playerRotTag.Get("Y")).Value; player.camera.Roll = ((BinaryTagNodeFloat)playerRotTag.Get("Z")).Value; player.HeldSlot = ((BinaryTagNodeByte)playerTag.Get("inventorySlot")).Value; //TODO: Deserialize inventory #endregion seed = ((BinaryTagNodeLong)levelRoot.Get("seed")).Value; } catch (Exception ex) { Logger.error(-1, "MalformedBinaryTagNodeException:\n" + ex.StackTrace); } } #endregion #region Chunks string chunkFolder = Path.Combine(WorldDirectory, "chunks"); if (Directory.Exists(chunkFolder)) { string[] fileEntries = Directory.GetFiles(chunkFolder, "*.cnk"); for (int i = 0; i < fileEntries.Length; i++) { //TODO: loop through chunks folder BinaryTagNode chunkData = FileUtil.DeseralizeFromFile(fileEntries[i]); if (chunkData.Type == TagType.TagEnd) { Logger.error(-1, "The file \"" + Path.Combine(chunkFolder, "level.dat") + "\" doesn't exist, or is malformed!"); } if (chunkData.Type == TagType.TagMultiple) { try { var chunkRoot = (BinaryTagNodeMultiple)chunkData; var chunkX = ((BinaryTagNodeInt)chunkRoot.Get("X")); var chunkY = ((BinaryTagNodeInt)chunkRoot.Get("Y")); var chunkZ = ((BinaryTagNodeInt)chunkRoot.Get("Z")); if (chunkX != null && chunkY != null && chunkZ != null) { var hash = Chunk.GetHash(chunkX.Value, chunkY.Value, chunkZ.Value); if (chunks.ContainsKey(hash)) { chunks[hash].PopulateFromBinaryTag(chunkRoot); } } } catch (Exception ex) { Logger.error(-1, "MalformedBinaryTagNodeException:\n" + ex.StackTrace); } } } } #endregion }
public BinaryTagNodeMultiple SerializeToBinaryTag() { BinaryTagNodeMultiple rootTag = new BinaryTagNodeMultiple("root"); //Position. Most important parts rootTag.Add(new BinaryTagNodeInt("X", posX)); rootTag.Add(new BinaryTagNodeInt("Y", posY)); rootTag.Add(new BinaryTagNodeInt("Z", posZ)); //Chunk versioning. Unused for now, to be used by the file upgrader API when written. rootTag.Add(new BinaryTagNodeUShort("version", 0)); #region Block Compression byte blockCompressionID = 1; //TODO: Compress with all algorithms, determine smallest and write smallest to file #region No Compression //Convert the block array to a flat linear array var blockFlatArr = new List <BinaryTagNode>(); for (int z = 0; z < CHUNK_SIZE; z++) { for (int y = 0; y < CHUNK_SIZE; y++) { for (int x = 0; x < CHUNK_SIZE; x++) { int pos = (z * CHUNK_SIZE * CHUNK_SIZE) + (y * CHUNK_SIZE) + x; blockFlatArr.Add(new BinaryTagNodeUShort(pos.ToString(), (ushort)chunk_blockdata[x, y, z])); } } } #endregion #region Compression Variant 1 var blockCompressedArr = new List <BinaryTagNode>(); uint size = 56; uint currentBlockID = 0; int blockLength = 0; for (int z = 0; z < CHUNK_SIZE; z++) { for (int y = 0; y < CHUNK_SIZE; y++) { for (int x = 0; x < CHUNK_SIZE; x++) { //get the voxel and check if its the current block if (chunk_blockdata[x, y, z] == currentBlockID) { blockLength++; } else { //Add data to the binarytag var currentNode = GetCompressedBinaryTagBlock(currentBlockID, Math.Max(0, blockLength)); blockCompressedArr.Add(currentNode); size += 56; //Assuming each part is 16 + 8 bits (BlockID) + 16 + 8 bits (Length) + 8 bits (TagMultiple) //Reset currentBlockID = chunk_blockdata[x, y, z]; blockLength = 1; } } } } blockCompressedArr.Add(GetCompressedBinaryTagBlock(currentBlockID, Math.Max(0, blockLength))); #endregion if (size >= 98304) //(16 + 8) bits * 4096 entries (no compression) { blockCompressionID = 0; } rootTag.Add(new BinaryTagNodeByte("blockCompression", blockCompressionID)); if (blockCompressionID == 0) { rootTag.Add(new BinaryTagNodeList("blocks", blockFlatArr)); } else if (blockCompressionID == 1) { rootTag.Add(new BinaryTagNodeList("blocks", blockCompressedArr)); } #endregion #region Block states //TODO: Compress blockstates #endregion #region Lighting //TODO: Compress sky light //TODO: Compress block light #endregion #region Metadata //TODO: Compress blockmeta #endregion #region Tickables //No updates means we don't bother adding it, since its the default state if (chunk_updates.Count > 0) { #region No Compression //Convert the block array to a flat linear array var tickFlatArr = new List <BinaryTagNode>(); for (int i = 0; i < chunk_updates.Count; i++) { int pos = (chunk_updates.Values[i].Z * CHUNK_SIZE * CHUNK_SIZE) + (chunk_updates.Values[i].Y * CHUNK_SIZE) + chunk_updates.Values[i].X; tickFlatArr.Add(new BinaryTagNodeInt("block_update", pos)); } #endregion rootTag.Add(new BinaryTagNodeList("updates", tickFlatArr)); } #endregion return(rootTag); }
public void SaveChunks() { //TODO: Serialize //ROADMAP: // // Get world folder // Construct Level NBT Data // Write File to folder // create chunks dir if non-existent // for (chunk in chunks) // { // NBT = chunk.Serialize(); // writeToFile(NBT); // } #region World Data var root = new BinaryTagNodeMultiple("root"); //Serialize player position #region Player State var plr = new BinaryTagNodeMultiple("player"); var plrPos = new BinaryTagNodeMultiple("position"); var plrRot = new BinaryTagNodeMultiple("rotation"); plrPos.Add(new BinaryTagNodeFloat("X", player.position.X)); plrPos.Add(new BinaryTagNodeFloat("Y", player.position.Y)); plrPos.Add(new BinaryTagNodeFloat("Z", player.position.Z)); plrRot.Add(new BinaryTagNodeFloat("X", player.camera.Pitch)); plrRot.Add(new BinaryTagNodeFloat("Y", player.camera.Yaw)); plrRot.Add(new BinaryTagNodeFloat("Z", player.camera.Roll)); plr.Add(plrPos); plr.Add(plrRot); plr.Add(new BinaryTagNodeByte("inventorySlot", (byte)player.HeldSlot)); //TODO: Serialize inventory root.Add(plr); #endregion root.Add(new BinaryTagNodeLong("seed", seed)); root.Add(new BinaryTagNode("")); #endregion FileUtil.SerializeToFile(Path.Combine(WorldDirectory, "level.dat"), root); string chunkFolder = Path.Combine(WorldDirectory, "chunks"); #region Chunks for (int i = 0; i < chunks.Count; i++) { var data = chunks[chunkHashes[i]].SerializeToBinaryTag(); FileUtil.SerializeToFile(Path.Combine(chunkFolder, chunks[chunkHashes[i]].posX + "." + chunks[chunkHashes[i]].posY + "." + chunks[chunkHashes[i]].posZ + "p.cnk"), data); } #endregion }