diff --git a/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs b/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs index e1b7cc5..e93dcf3 100644 --- a/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs +++ b/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs @@ -57,6 +57,7 @@ namespace Substrate private TagList _tileEntities; private BlockLight _lightManager; + private BlockFluid _fluidManager; private BlockTileEntities _tileEntityManager; private bool _dirty = false; @@ -71,11 +72,16 @@ namespace Substrate _lightManager.ResolveNeighbor += delegate(int relx, int rely, int relz) { return value(relx, rely, relz); }; + _fluidManager.ResolveNeighbor += delegate(int relx, int rely, int relz) + { + return value(relx, rely, relz); + }; } remove { _lightManager = new BlockLight(this); + _fluidManager = new BlockFluid(this); } } @@ -107,6 +113,7 @@ namespace Substrate _tileEntities = properties.tileEntities; _lightManager = new BlockLight(this); + _fluidManager = new BlockFluid(this); _tileEntityManager = new BlockTileEntities(_blocks, _tileEntities); } @@ -452,6 +459,16 @@ namespace Substrate #endregion + public void ResetBlockFluid () + { + _fluidManager.ResetWater(); + _dirty = true; + } + public void RebuildBlockFluid () + { + _fluidManager.RebuildWater(); + _dirty = true; + } } } diff --git a/Substrate/SubstrateCS/Source/BlockFluid.cs b/Substrate/SubstrateCS/Source/BlockFluid.cs new file mode 100644 index 0000000..a43678e --- /dev/null +++ b/Substrate/SubstrateCS/Source/BlockFluid.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; + +namespace Substrate +{ + using Utility; + + public class BlockFluid + { + private IBoundedDataBlockCollection _blockset; + + private readonly int _xdim; + private readonly int _ydim; + private readonly int _zdim; + + private Dictionary _chunks; + + public delegate IBoundedDataBlockCollection NeighborLookupHandler (int relx, int rely, int relz); + + public event NeighborLookupHandler ResolveNeighbor; + + internal class BlockCoord + { + internal IBoundedDataBlockCollection chunk; + internal int lx; + internal int ly; + internal int lz; + + internal BlockCoord (IBoundedDataBlockCollection _chunk, int _lx, int _ly, int _lz) + { + chunk = _chunk; + lx = _lx; + ly = _ly; + lz = _lz; + } + } + + public BlockFluid (IBoundedDataBlockCollection blockset) + { + _blockset = blockset; + + _xdim = _blockset.XDim; + _ydim = _blockset.YDim; + _zdim = _blockset.ZDim; + + _chunks = new Dictionary(); + } + + public BlockFluid (BlockFluid bl) + { + _blockset = bl._blockset; + + _xdim = bl._xdim; + _ydim = bl._ydim; + _zdim = bl._zdim; + + _chunks = new Dictionary(); + } + + public void ResetWater () + { + int xdim = _xdim; + int ydim = _ydim; + int zdim = _zdim; + + for (int x = 0; x < xdim; x++) { + for (int z = 0; z < zdim; z++) { + for (int y = 0; y < ydim; y++) { + IDataBlock block = _blockset.GetBlockRef(x, y, z); + if ((block.ID == BlockType.STATIONARY_WATER || block.ID == BlockType.WATER) && block.Data != (int)WaterFlow.FULL) { + block.ID = BlockType.AIR; + block.Data = 0; + } + else if (block.ID == BlockType.WATER) { + block.ID = BlockType.STATIONARY_WATER; + } + } + } + } + } + + public void RebuildWater () + { + int xdim = _xdim; + int ydim = _ydim; + int zdim = _zdim; + + List buildQueue = new List(); + + for (int x = 0; x < xdim; x++) { + for (int z = 0; z < zdim; z++) { + for (int y = 0; y < ydim; y++) { + BlockInfo info = _blockset.GetInfo(x, y, z); + if (info.ID == BlockType.STATIONARY_WATER && _blockset.GetData(x, y, z) == (int)WaterFlow.FULL) { + buildQueue.Add(new BlockKey(x, y, z)); + } + } + } + } + + foreach (BlockKey key in buildQueue) { + SpreadWater(key.x, key.y, key.z); + } + } + + private void SpreadWater (int x, int y, int z) + { + Queue> targets = new Queue>(); + targets.Enqueue(new KeyValuePair(new BlockKey(x, y, z), 0)); + + while (targets.Count > 0) { + KeyValuePair skey = targets.Dequeue(); + + BlockKey source = skey.Key; + int sourceDist = skey.Value; + + int nearestDrop = 7; + bool foundDrop = false; + + Queue searchQueue = new Queue(); + Queue> sinkQueue = new Queue>(); + Dictionary marked = new Dictionary(); + + searchQueue.Enqueue(source); + marked.Add(source, sourceDist); + + // Identify sinks + while (searchQueue.Count > 0) { + BlockKey branch = searchQueue.Dequeue(); + int distance = marked[branch] & ~(int)LiquidState.FALLING; + bool falling = (marked[branch] & (int)LiquidState.FALLING) > 0; + + // Ignore blocks out of range + if (distance > nearestDrop) { + continue; + } + + // Ignore invalid blocks + BlockCoord branchHigh = TranslateCoord(branch.x, branch.y, branch.z); + if (branchHigh.chunk == null || branch.y == 0) { + marked.Remove(branch); + continue; + } + + // If we're not the magical source block... + if (distance > 0 && !falling) { + // Ignore blocks that block fluid (and thus could not become a fluid) + BlockInfo branchHighInfo = branchHigh.chunk.GetInfo(branchHigh.lx, branchHigh.ly, branchHigh.lz); + if (branchHighInfo.BlocksFluid) { + marked.Remove(branch); + continue; + } + + // Ignore blocks that have a "fuller" fluid level + if (branchHighInfo.ID == BlockType.STATIONARY_WATER) { + int cmp = branchHigh.chunk.GetData(branchHigh.lx, branchHigh.ly, branchHigh.lz); + if (cmp <= distance || (cmp & (int)LiquidState.FALLING) > 0) { + marked.Remove(branch); + continue; + } + } + } + + // If we found a hole, add as a sink, mark the sink distance. + BlockCoord branchLow = TranslateCoord(branch.x, branch.y - 1, branch.z); + BlockInfo branchLowInfo = branchLow.chunk.GetInfo(branchLow.lx, branchLow.ly, branchLow.lz); + if (!branchLowInfo.BlocksFluid) { + foundDrop = true; + nearestDrop = falling ? 0 : distance; + sinkQueue.Enqueue(new KeyValuePair(branch, falling ? distance | (int)LiquidState.FALLING : distance)); + continue; + } + + // Didn't find a hole, hit the bottom, reset distance + if (falling) { + distance = 0; + } + + // Expand to neighbors + if (distance < nearestDrop) { + BlockKey[] keys = { + new BlockKey(branch.x - 1, branch.y, branch.z), + new BlockKey(branch.x + 1, branch.y, branch.z), + new BlockKey(branch.x, branch.y, branch.z - 1), + new BlockKey(branch.x, branch.y, branch.z + 1), + }; + + for (int i = 0; i < 4; i++) { + if (!marked.ContainsKey(keys[i])) { + searchQueue.Enqueue(keys[i]); + marked.Add(keys[i], distance + 1); + } + } + } + } + + // If we didn't find any drops, fill out the mark table + if (!foundDrop) { + foreach (KeyValuePair mark in marked) { + BlockKey target = mark.Key; + + BlockCoord targetHigh = TranslateCoord(target.x, target.y, target.z); + targetHigh.chunk.SetID(targetHigh.lx, targetHigh.ly, targetHigh.lz, BlockType.STATIONARY_WATER); + targetHigh.chunk.SetData(targetHigh.lx, targetHigh.ly, targetHigh.lz, mark.Value); + } + + // Skip sinks and tracing + continue; + } + + // Create new target for each sink + Queue> traceQueue = new Queue>(); + while (sinkQueue.Count > 0) { + KeyValuePair tkey = sinkQueue.Dequeue(); + BlockKey target = tkey.Key; + int distance = tkey.Value; + + //int distance = marked[target]; + marked.Remove(target); + + targets.Enqueue(new KeyValuePair(new BlockKey(target.x, target.y - 1, target.z), distance | (int)LiquidState.FALLING)); + traceQueue.Enqueue(new KeyValuePair(target, distance)); + + /*BlockCoord targetLow = TranslateCoord(target.x, target.y - 1, target.z); + targetLow.chunk.SetID(targetLow.lx, targetLow.ly, targetHigh.lz, BlockType.STATIONARY_WATER); + targetLow.chunk.SetData(targetLow.lx, targetLow.ly, targetHigh.lz, tkey.Value);*/ + } + + // Trace back from sinks + while (traceQueue.Count > 0) { + KeyValuePair tkey = traceQueue.Dequeue(); + + BlockKey target = tkey.Key; + int distance = tkey.Value & ~(int)LiquidState.FALLING; + bool falling = (tkey.Value & (int)LiquidState.FALLING) > 0; + + //if (distance == 0) { + // distance |= (int)LiquidState.FALLING; + //} + + BlockCoord targetHigh = TranslateCoord(target.x, target.y, target.z); + targetHigh.chunk.SetID(targetHigh.lx, targetHigh.ly, targetHigh.lz, BlockType.STATIONARY_WATER); + targetHigh.chunk.SetData(targetHigh.lx, targetHigh.ly, targetHigh.lz, tkey.Value); + + if (falling) { + continue; + } + + BlockKey[] keys = { + new BlockKey(target.x - 1, target.y, target.z), + new BlockKey(target.x + 1, target.y, target.z), + new BlockKey(target.x, target.y, target.z - 1), + new BlockKey(target.x, target.y, target.z + 1), + }; + + for (int i = 0; i < 4; i++) { + int nval; + if (!marked.TryGetValue(keys[i], out nval)) { + continue; + } + + int ndist = nval & ~(int)LiquidState.FALLING; + bool nfall = (nval & (int)LiquidState.FALLING) > 0; + + marked.Remove(keys[i]); + if (ndist < distance || nfall) { + traceQueue.Enqueue(new KeyValuePair(keys[i], nval)); + } + } + } + } + } + + private BlockCoord TranslateCoord (int x, int y, int z) + { + IBoundedDataBlockCollection chunk = GetChunk(x, z); + + int lx = ((x % _xdim) + _xdim) % _xdim; + int lz = ((z % _zdim) + _zdim) % _zdim; + + return new BlockCoord(chunk, lx, y, lz); + } + + private IBoundedDataBlockCollection GetChunk (int x, int z) + { + int cx = x / _xdim + (x >> 31); + int cz = z / _zdim + (z >> 31); + + ChunkKey key = new ChunkKey(cx, cz); + + IBoundedDataBlockCollection chunk; + if (!_chunks.TryGetValue(key, out chunk)) { + chunk = OnResolveNeighbor(cx, 0, cz); + _chunks[key] = chunk; + } + + return chunk; + } + + private IBoundedDataBlockCollection OnResolveNeighbor (int relX, int relY, int relZ) + { + if (ResolveNeighbor != null) { + IBoundedDataBlockCollection n = ResolveNeighbor(relX, relY, relZ); + + if (n == null) { + return null; + } + + if (n.XDim != _xdim || + n.YDim != _ydim || + n.ZDim != _zdim) { + throw new InvalidOperationException("Subscriber returned incompatible IDataBlockCollection"); + } + + return n; + } + + return null; + } + } +} diff --git a/Substrate/SubstrateCS/Source/BlockInfo.cs b/Substrate/SubstrateCS/Source/BlockInfo.cs index 1dbde42..d818b8a 100644 --- a/Substrate/SubstrateCS/Source/BlockInfo.cs +++ b/Substrate/SubstrateCS/Source/BlockInfo.cs @@ -178,6 +178,7 @@ namespace Substrate private int _opacity = MAX_OPACITY; private int _luminance = MIN_LUMINANCE; private bool _transmitLight = false; + private bool _blocksFluid = true; private BlockState _state = BlockState.SOLID; @@ -221,6 +222,11 @@ namespace Substrate get { return _opacity > MIN_OPACITY || !_transmitLight; } } + public bool BlocksFluid + { + get { return _blocksFluid; } + } + public BlockState State { get { return _state; } @@ -276,6 +282,20 @@ namespace Substrate public BlockInfo SetState (BlockState state) { _state = state; + + if (_state == BlockState.SOLID) { + _blocksFluid = true; + } + else { + _blocksFluid = false; + } + + return this; + } + + public BlockInfo SetBlocksFluid (bool blocks) + { + _blocksFluid = blocks; return this; } @@ -500,6 +520,12 @@ namespace Substrate Farmland.SetLightTransmission(false); CobbleStairs.SetLightTransmission(false); + // Override default fluid blocking rules + + SignPost.SetBlocksFluid(true); + WallSign.SetBlocksFluid(true); + Cactus.SetBlocksFluid(false); + // Set Tile Entity Data Dispenser.SetTileEntity("Trap"); diff --git a/Substrate/SubstrateCS/Source/BlockLight.cs b/Substrate/SubstrateCS/Source/BlockLight.cs index 0afd08f..4440f9f 100644 --- a/Substrate/SubstrateCS/Source/BlockLight.cs +++ b/Substrate/SubstrateCS/Source/BlockLight.cs @@ -68,7 +68,7 @@ namespace Substrate BlockKey primary = new BlockKey(lx, ly, lz); _update.Enqueue(primary); - BlockInfo info = _blockset.GetInfo(lx, ly, lz); + //BlockInfo info = _blockset.GetInfo(lx, ly, lz); //if (info.Luminance > BlockInfo.MIN_LUMINANCE || info.TransmitsLight) { if (ly > 0) { diff --git a/Substrate/SubstrateCS/Source/ChunkKey.cs b/Substrate/SubstrateCS/Source/ChunkKey.cs index 267f483..3c44323 100644 --- a/Substrate/SubstrateCS/Source/ChunkKey.cs +++ b/Substrate/SubstrateCS/Source/ChunkKey.cs @@ -6,8 +6,8 @@ namespace Substrate { public struct ChunkKey : IEquatable { - readonly int cx; - readonly int cz; + public readonly int cx; + public readonly int cz; public ChunkKey (int _cx, int _cz) { diff --git a/Substrate/SubstrateCS/Source/EntityCollection.cs b/Substrate/SubstrateCS/Source/EntityCollection.cs index ab2b0e3..48bf1d2 100644 --- a/Substrate/SubstrateCS/Source/EntityCollection.cs +++ b/Substrate/SubstrateCS/Source/EntityCollection.cs @@ -150,14 +150,12 @@ namespace Substrate public class EntityEnumerator : IEnumerator { - private TagList _entities; private IEnumerator _enum; private Entity _cur; public EntityEnumerator (TagList entities) { - _entities = entities; _enum = entities.GetEnumerator(); } diff --git a/Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs b/Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs index 6276f73..35f779b 100644 --- a/Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs +++ b/Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs @@ -109,7 +109,6 @@ namespace Substrate.NBT private static void SerializeScaler (TagValue tag, StringBuilder str) { - TagType type = tag.GetTagType(); switch (tag.GetTagType()) { case TagType.TAG_STRING: str.Append("\"" + tag.ToTagString().Data + "\""); diff --git a/Substrate/SubstrateCS/Source/NBT/NBT.cs b/Substrate/SubstrateCS/Source/NBT/NBT.cs index 596b7c6..aac872e 100644 --- a/Substrate/SubstrateCS/Source/NBT/NBT.cs +++ b/Substrate/SubstrateCS/Source/NBT/NBT.cs @@ -279,7 +279,7 @@ namespace Substrate.NBT { TagType type = (TagType)_stream.ReadByte(); if (type == TagType.TAG_COMPOUND) { - string name = ReadString().ToTagString().Data; + ReadString(); // name return ReadValue(type) as TagCompound; } diff --git a/Substrate/SubstrateCS/Substrate.csproj b/Substrate/SubstrateCS/Substrate.csproj index 5dc035e..e618d2c 100644 --- a/Substrate/SubstrateCS/Substrate.csproj +++ b/Substrate/SubstrateCS/Substrate.csproj @@ -187,6 +187,9 @@ + + +