// Block Breaking mechanic private void BreakBlock() { if (!current.active) { return; } ChunkPos toUpdate = new ChunkPos(current.chunkX, current.chunkZ); ushort blockCode = loader.chunks[toUpdate].data.GetCell(current.blockX, current.blockY, current.blockZ); // If doesn't has special break handling if (!loader.blockBook.CheckCustomBreak(blockCode)) { // Actually breaks new block and updates chunk loader.chunks[toUpdate].data.SetCell(current.blockX, current.blockY, current.blockZ, 0); loader.chunks[toUpdate].metadata.Reset(current.blockX, current.blockY, current.blockZ); // Triggers OnBreak if (blockCode <= ushort.MaxValue / 2) { loader.blockBook.blocks[blockCode].OnBreak(toUpdate, current.blockX, current.blockY, current.blockZ, loader); } else { loader.blockBook.objects[ushort.MaxValue - blockCode].OnBreak(toUpdate, current.blockX, current.blockY, current.blockZ, loader); } EmitBlockUpdate("break", current.GetWorldX(), current.GetWorldY(), current.GetWorldZ(), 0, loader); loader.budscheduler.ScheduleReload(toUpdate, 0, x: current.blockX, y: current.blockY, z: current.blockZ); } // If has special break handlings else { if (blockCode <= ushort.MaxValue / 2) { loader.blockBook.blocks[blockCode].OnBreak(toUpdate, current.blockX, current.blockY, current.blockZ, loader); } else { loader.blockBook.objects[ushort.MaxValue - blockCode].OnBreak(toUpdate, current.blockX, current.blockY, current.blockZ, loader); loader.regionHandler.SaveChunk(loader.chunks[toUpdate]); } } }
// Activates OnBreak event -> Emits normal BUD, emits special BUD to breadt-first search leaves public override int OnBreak(ChunkPos pos, int blockX, int blockY, int blockZ, ChunkLoader cl) { CastCoord thisCoord = new CastCoord(pos, blockX, blockY, blockZ); EmitBlockUpdate("break", thisCoord.GetWorldX(), thisCoord.GetWorldY(), thisCoord.GetWorldZ(), 0, cl); TreeCheck(thisCoord, cl); GetSurroundingLeaves(thisCoord, decayDistance, cl); RunLeavesRecursion(cl, thisCoord); return(0); }
// Does Search for invalid leaves private void RunLeavesRecursion(ChunkLoader cl, CastCoord init) { while (openList.Count > 0) { GetSurroundingLeaves(openList[0], distances[openList[0]] - 1, cl); openList.RemoveAt(0); } // Applies DECAY BUD to distant leaves foreach (CastCoord c in distances.Keys) { if (distances[c] == 0) { EmitBUDTo("decay", c.GetWorldX(), c.GetWorldY(), c.GetWorldZ(), 1, cl); } } // Applies DECAY BUD to around blocks if there's no wood around EmitDelayedBUD("decay", init.GetWorldX(), init.GetWorldY(), init.GetWorldZ(), 2, 15, cl); distances.Clear(); }
// Handles the emittion of BUD to neighboring blocks public void EmitBlockUpdate(string type, int x, int y, int z, int tickOffset, ChunkLoader cl) { CastCoord thisPos = new CastCoord(new Vector3(x, y, z)); CastCoord[] neighbors = { thisPos.Add(1, 0, 0), thisPos.Add(-1, 0, 0), thisPos.Add(0, 1, 0), thisPos.Add(0, -1, 0), thisPos.Add(0, 0, 1), thisPos.Add(0, 0, -1) }; int[] facings = { 2, 0, 4, 5, 1, 3 }; int faceCounter = 0; foreach (CastCoord c in neighbors) { cl.budscheduler.ScheduleBUD(new BUDSignal(type, c.GetWorldX(), c.GetWorldY(), c.GetWorldZ(), thisPos.GetWorldX(), thisPos.GetWorldY(), thisPos.GetWorldZ(), facings[faceCounter]), tickOffset); faceCounter++; } }
// Handles the emittion of BUD to neighboring blocks public void EmitBlockUpdate(BUDCode type, int x, int y, int z, int tickOffset, ChunkLoader_Server cl) { CastCoord thisPos = new CastCoord(new Vector3(x, y, z)); CastCoord[] neighbors = { thisPos.Add(1, 0, 0), thisPos.Add(-1, 0, 0), thisPos.Add(0, 1, 0), thisPos.Add(0, -1, 0), thisPos.Add(0, 0, 1), thisPos.Add(0, 0, -1) }; int[] facings = { 2, 0, 4, 5, 1, 3 }; int blockCode; int faceCounter = 0; foreach (CastCoord c in neighbors) { blockCode = cl.chunks[c.GetChunkPos()].data.GetCell(c.blockX, c.blockY, c.blockZ); cl.budscheduler.ScheduleBUD(new BUDSignal(type, c.GetWorldX(), c.GetWorldY(), c.GetWorldZ(), thisPos.GetWorldX(), thisPos.GetWorldY(), thisPos.GetWorldZ(), facings[faceCounter]), tickOffset); faceCounter++; } }
// Handles the emittion of BUD to neighboring blocks public void EmitDelayedBUD(string type, int x, int y, int z, int minOffset, int maxOffset, ChunkLoader cl) { CastCoord thisPos = new CastCoord(new Vector3(x, y, z)); cache.Clear(); cache.Add(thisPos.Add(1, 0, 0)); cache.Add(thisPos.Add(-1, 0, 0)); cache.Add(thisPos.Add(0, 1, 0)); cache.Add(thisPos.Add(0, -1, 0)); cache.Add(thisPos.Add(0, 0, 1)); cache.Add(thisPos.Add(0, 0, -1)); foreach (CastCoord c in cache) { cl.budscheduler.ScheduleBUD(new BUDSignal(type, c.GetWorldX(), c.GetWorldY(), c.GetWorldZ(), thisPos.GetWorldX(), thisPos.GetWorldY(), thisPos.GetWorldZ(), 0), Random.Range(minOffset, maxOffset)); } }
// Handles the emittion of BUD to neighboring blocks public void EmitBlockUpdate(string type, int x, int y, int z, int tickOffset, ChunkLoader cl) { CastCoord thisPos = GetCoordinates(x, y, z); CastCoord[] neighbors = { thisPos.Add(1, 0, 0), thisPos.Add(-1, 0, 0), thisPos.Add(0, 1, 0), thisPos.Add(0, -1, 0), thisPos.Add(0, 0, 1), thisPos.Add(0, 0, -1) }; int[] facings = { 2, 0, 4, 5, 1, 3 }; int blockCode; int faceCounter = 0; foreach (CastCoord c in neighbors) { // Ignores void updates if (c.blockY < 0 || c.blockY > Chunk.chunkDepth - 1) { continue; } blockCode = cl.chunks[c.GetChunkPos()].data.GetCell(c.blockX, c.blockY, c.blockZ); cachedBUD = new BUDSignal(type, c.GetWorldX(), c.GetWorldY(), c.GetWorldZ(), thisPos.GetWorldX(), thisPos.GetWorldY(), thisPos.GetWorldZ(), facings[faceCounter]); cl.budscheduler.ScheduleBUD(cachedBUD, tickOffset); faceCounter++; } }
// Block Placing mechanic private void PlaceBlock(ushort blockCode) { int translatedBlockCode; // Encodes for Block Mode if (blockCode <= ushort.MaxValue / 2) { translatedBlockCode = blockCode; // Won't happen if not raycasting something or if block is in player's body or head if (!current.active || (CastCoord.Eq(lastCoord, playerHead) && loader.blockBook.CheckSolid(blockCode)) || (CastCoord.Eq(lastCoord, playerBody) && loader.blockBook.CheckSolid(blockCode))) { return; } } // Encodes for Asset Mode else { translatedBlockCode = ushort.MaxValue - blockCode; // Won't happen if not raycasting something or if block is in player's body or head if (!current.active || (CastCoord.Eq(lastCoord, playerHead) && loader.blockBook.CheckSolid(blockCode)) || (CastCoord.Eq(lastCoord, playerBody) && loader.blockBook.CheckSolid(blockCode))) { return; } } ChunkPos toUpdate = new ChunkPos(lastCoord.chunkX, lastCoord.chunkZ); // Checks if specific block has specific placement rules that may hinder the placement if (blockCode <= ushort.MaxValue / 2) { if (!loader.blockBook.blocks[translatedBlockCode].PlacementRule(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader)) { return; } } else { if (!loader.blockBook.objects[translatedBlockCode].PlacementRule(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader)) { return; } } // If doesn't have special place handling if (!loader.blockBook.CheckCustomPlace(blockCode)) { // Actually places block/asset into terrain loader.chunks[toUpdate].data.SetCell(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, blockCode); loader.budscheduler.ScheduleReload(toUpdate, 0); EmitBlockUpdate("change", lastCoord.GetWorldX(), lastCoord.GetWorldY(), lastCoord.GetWorldZ(), 0, loader); // Applies OnPlace Event if (blockCode <= ushort.MaxValue / 2) { loader.blockBook.blocks[translatedBlockCode].OnPlace(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader); } else { loader.blockBook.objects[translatedBlockCode].OnPlace(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader); } } // If has special handling else { // Actually places block/asset into terrain loader.chunks[toUpdate].data.SetCell(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, blockCode); if (blockCode <= ushort.MaxValue / 2) { loader.blockBook.blocks[blockCode].OnPlace(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader); } else { loader.blockBook.objects[translatedBlockCode].OnPlace(toUpdate, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, loader); } } }
// Sends a direct action BUD to a block // ComesFromMessage flag is used to call this function from within the Server Code and not triggered by any message private void DirectBlockUpdate(byte[] data, ulong id, bool comesFromMessage = true) { ChunkPos pos; int x, y, z, facing; ushort blockCode, state, hp; BUDCode type; NetMessage message; pos = NetDecoder.ReadChunkPos(data, 1); x = NetDecoder.ReadInt(data, 9); y = NetDecoder.ReadInt(data, 13); z = NetDecoder.ReadInt(data, 17); facing = NetDecoder.ReadInt(data, 21); blockCode = NetDecoder.ReadUshort(data, 25); state = 0; hp = 0; if (comesFromMessage) { state = NetDecoder.ReadUshort(data, 27); hp = NetDecoder.ReadUshort(data, 29); type = (BUDCode)NetDecoder.ReadInt(data, 31); } else { type = (BUDCode)NetDecoder.ReadInt(data, 27); } CastCoord lastCoord = new CastCoord(pos, x, y, z); switch (type) { case BUDCode.PLACE: // if chunk is still loaded if (this.cl.chunks.ContainsKey(lastCoord.GetChunkPos())) { // if it's a block if (blockCode <= ushort.MaxValue / 2) { // if placement rules fail if (!cl.blockBook.blocks[blockCode].PlacementRule(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl)) { NetMessage denied = new NetMessage(NetCode.PLACEMENTDENIED); this.Send(denied.GetMessage(), denied.size, id); return; } } // if it's an object else { if (!cl.blockBook.objects[ushort.MaxValue - blockCode].PlacementRule(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl)) { NetMessage denied = new NetMessage(NetCode.PLACEMENTDENIED); this.Send(denied.GetMessage(), denied.size, id); return; } } // Check if is trying to put block on players if (this.playersInChunk.ContainsKey(pos)) { foreach (ulong code in this.playersInChunk[pos]) { if (code == id) { continue; } if (!this.cl.regionHandler.allPlayerData[code].CheckValidPlacement(lastCoord.GetWorldX(), lastCoord.GetWorldY(), lastCoord.GetWorldZ())) { NetMessage denied = new NetMessage(NetCode.PLACEMENTDENIED); this.Send(denied.GetMessage(), denied.size, id); return; } } } // If doesn't have special place handling if (!cl.blockBook.CheckCustomPlace(blockCode)) { // Actually places block/asset into terrain cl.chunks[lastCoord.GetChunkPos()].data.SetCell(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, blockCode); //cl.budscheduler.ScheduleReload(lastCoord.GetChunkPos(), 0); EmitBlockUpdate(BUDCode.CHANGE, lastCoord.GetWorldX(), lastCoord.GetWorldY(), lastCoord.GetWorldZ(), 0, cl); // Applies OnPlace Event if (blockCode <= ushort.MaxValue / 2) { cl.blockBook.blocks[blockCode].OnPlace(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl); } else { cl.blockBook.objects[ushort.MaxValue - blockCode].OnPlace(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl); } // Sends the updated voxel to loaded clients message = new NetMessage(NetCode.DIRECTBLOCKUPDATE); message.DirectBlockUpdate(BUDCode.PLACE, lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, blockCode, this.cl.chunks[lastCoord.GetChunkPos()].metadata.GetState(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ), this.cl.chunks[lastCoord.GetChunkPos()].metadata.GetHP(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ)); SendToClients(lastCoord.GetChunkPos(), message); this.cl.regionHandler.SaveChunk(this.cl.chunks[pos]); } // If has special handling else { // Actually places block/asset into terrain this.cl.chunks[lastCoord.GetChunkPos()].data.SetCell(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, blockCode); if (blockCode <= ushort.MaxValue / 2) { cl.blockBook.blocks[blockCode].OnPlace(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl); } else { cl.blockBook.objects[ushort.MaxValue - blockCode].OnPlace(lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, cl); } } // Make entities in this chunk update their TerrainVision this.entityHandler.SetRefreshVision(EntityType.DROP, lastCoord.GetChunkPos()); } break; case BUDCode.SETSTATE: if (this.cl.chunks.ContainsKey(lastCoord.GetChunkPos())) { this.cl.chunks[lastCoord.GetChunkPos()].metadata.SetState(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, blockCode); this.entityHandler.SetRefreshVision(EntityType.DROP, lastCoord.GetChunkPos()); } break; case BUDCode.BREAK: // If doesn't has special break handling if (!this.cl.blockBook.CheckCustomBreak(blockCode)) { // Actually breaks new block and updates chunk this.cl.chunks[pos].data.SetCell(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, 0); this.cl.chunks[pos].metadata.Reset(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ); // Triggers OnBreak if (blockCode <= ushort.MaxValue / 2) { this.cl.blockBook.blocks[blockCode].OnBreak(pos, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, this.cl); } else { this.cl.blockBook.objects[ushort.MaxValue - blockCode].OnBreak(pos, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, this.cl); } EmitBlockUpdate(BUDCode.BREAK, lastCoord.GetWorldX(), lastCoord.GetWorldY(), lastCoord.GetWorldZ(), 0, this.cl); } // If has special break handlings else { if (blockCode <= ushort.MaxValue / 2) { this.cl.blockBook.blocks[blockCode].OnBreak(pos, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, this.cl); } else { this.cl.blockBook.objects[ushort.MaxValue - blockCode].OnBreak(pos, lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, this.cl); } } // Sends the updated voxel to loaded clients this.entityHandler.SetRefreshVision(EntityType.DROP, lastCoord.GetChunkPos()); message = new NetMessage(NetCode.DIRECTBLOCKUPDATE); message.DirectBlockUpdate(BUDCode.BREAK, lastCoord.GetChunkPos(), lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ, facing, 0, ushort.MaxValue, ushort.MaxValue); SendToClients(lastCoord.GetChunkPos(), message); this.cl.regionHandler.SaveChunk(this.cl.chunks[pos]); break; case BUDCode.LOAD: // HP is set as the Chunk Coordinates vs World Coordinates flag if (hp == ushort.MaxValue) { lastCoord = new CastCoord(new Vector3(lastCoord.blockX, lastCoord.blockY, lastCoord.blockZ)); } blockCode = this.cl.GetBlock(lastCoord); if (this.cl.chunks.ContainsKey(lastCoord.GetChunkPos())) { if (blockCode <= ushort.MaxValue / 2) { this.cl.blockBook.blocks[blockCode].OnLoad(lastCoord, this.cl); } else { this.cl.blockBook.objects[ushort.MaxValue - blockCode].OnLoad(lastCoord, this.cl); } } break; default: break; } }