Refactor the water block provider
This makes the fluid dynamics model much easier to understand and maintain.
This commit is contained in:
parent
df79e10f57
commit
4dd99e4e93
@ -14,7 +14,7 @@ namespace TrueCraft.API
|
||||
X = Y = Z = value;
|
||||
}
|
||||
|
||||
public Coordinates3D(int x, int y, int z)
|
||||
public Coordinates3D(int x = 0, int y = 0, int z = 0)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
|
@ -20,7 +20,7 @@ namespace TrueCraft.API.Logic
|
||||
bool BlockRightClicked(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
|
||||
void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
|
||||
void BlockMined(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
|
||||
void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world);
|
||||
void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world);
|
||||
void BlockScheduledEvent(BlockDescriptor descriptor, IWorld world, object data);
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ namespace TrueCraft.Core.Logic
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
|
||||
public virtual void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
|
||||
{
|
||||
if (!IsSupported(descriptor, server, world))
|
||||
{
|
||||
|
@ -83,11 +83,11 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
|
||||
{
|
||||
if (!ValidBedPosition(descriptor, server.BlockRepository, world))
|
||||
world.SetBlockID(descriptor.Coordinates, 0);
|
||||
base.BlockUpdate(descriptor, server, world);
|
||||
base.BlockUpdate(descriptor, source, server, world);
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
}
|
||||
}
|
||||
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
|
||||
{
|
||||
if (world.GetBlockID(descriptor.Coordinates + Coordinates3D.Down) != FarmlandBlock.BlockID)
|
||||
{
|
||||
|
@ -74,7 +74,7 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
|
||||
{
|
||||
if (!ValidPlacement(descriptor, world))
|
||||
{
|
||||
|
@ -5,12 +5,49 @@ using TrueCraft.API.World;
|
||||
using TrueCraft.API;
|
||||
using TrueCraft.API.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TrueCraft.Core.Logic.Blocks
|
||||
{
|
||||
public class WaterBlock : BlockProvider
|
||||
{
|
||||
private const byte MaxFlow = 7;
|
||||
// Fluids in Minecraft propegate according to a set of rules as cellular automata.
|
||||
// Source blocks start at zero and each block progressively further from the source
|
||||
// is one greater than the largest value nearby. When they reach 7, the water stops
|
||||
// propgetating.
|
||||
|
||||
private const double SecondsBetweenUpdates = 0.25;
|
||||
|
||||
private const byte MaximumFluidDepletion = 7;
|
||||
|
||||
private static readonly Coordinates3D[] Neighbors =
|
||||
{
|
||||
Coordinates3D.Left,
|
||||
Coordinates3D.Right,
|
||||
Coordinates3D.Forwards,
|
||||
Coordinates3D.Backwards
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a block that the currently updating water block is able to flow outwards into.
|
||||
/// </summary>
|
||||
protected struct LiquidFlow
|
||||
{
|
||||
public LiquidFlow(Coordinates3D targetBlock, byte level)
|
||||
{
|
||||
TargetBlock = targetBlock;
|
||||
Level = level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block to be filled with water.
|
||||
/// </summary>
|
||||
public Coordinates3D TargetBlock;
|
||||
/// <summary>
|
||||
/// The water level to fill the target block with.
|
||||
/// </summary>
|
||||
public byte Level;
|
||||
}
|
||||
|
||||
public static readonly byte BlockID = 0x08;
|
||||
|
||||
@ -28,6 +65,11 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
|
||||
public override string DisplayName { get { return "Water"; } }
|
||||
|
||||
protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
|
||||
{
|
||||
return new ItemStack[0];
|
||||
}
|
||||
|
||||
public override BoundingBox? BoundingBox
|
||||
{
|
||||
get
|
||||
@ -36,16 +78,15 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
}
|
||||
}
|
||||
|
||||
private bool PlaceWater(IMultiplayerServer server, Coordinates3D coords, IWorld world, byte meta = 0)
|
||||
public void ScheduleNextEvent(Coordinates3D coords, IWorld world, IMultiplayerServer server)
|
||||
{
|
||||
var old = world.GetBlockID(coords);
|
||||
if (old == WaterBlock.BlockID || old == StationaryWaterBlock.BlockID)
|
||||
return false;
|
||||
world.SetBlockID(coords, BlockID);
|
||||
world.SetMetadata(coords, meta);
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(0.25), (s) =>
|
||||
AutomataUpdate(s, world, coords));
|
||||
return true;
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
|
||||
AutomataUpdate(_server, world, coords));
|
||||
}
|
||||
|
||||
public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
|
||||
{
|
||||
ScheduleNextEvent(descriptor.Coordinates, world, user.Server);
|
||||
}
|
||||
|
||||
private void AutomataUpdate(IMultiplayerServer server, IWorld world, Coordinates3D coords)
|
||||
@ -57,200 +98,14 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
server.BlockUpdatesEnabled = true;
|
||||
if (again)
|
||||
{
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(0.25), (_server) =>
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
|
||||
DoAutomata(_server, world, coords));
|
||||
}
|
||||
}
|
||||
|
||||
internal bool CanFlow(IWorld world, Coordinates3D coords)
|
||||
{
|
||||
var down = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
|
||||
if (!down.Opaque)
|
||||
return true;
|
||||
const int maxDistance = 5;
|
||||
var extraLocations = new List<Coordinates3D>();
|
||||
var nearest = new Coordinates3D(maxDistance + 1, -1, maxDistance + 1);
|
||||
for (int x = -maxDistance; x < maxDistance; x++)
|
||||
{
|
||||
for (int z = -maxDistance; z < maxDistance; z++)
|
||||
{
|
||||
if (Math.Abs(z) + Math.Abs(x) > maxDistance)
|
||||
continue;
|
||||
var check = new Coordinates3D(x, -1, z);
|
||||
var c = world.GetBlockID(check + coords);
|
||||
if (c == 0 || c == WaterBlock.BlockID || c == StationaryWaterBlock.BlockID)
|
||||
{
|
||||
if (!LineOfSight(world, check + coords, coords))
|
||||
continue;
|
||||
if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearest + coords))
|
||||
extraLocations.Add(check);
|
||||
if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearest + coords))
|
||||
{
|
||||
extraLocations.Clear();
|
||||
nearest = check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearest == new Coordinates3D(maxDistance + 1, -1, maxDistance + 1))
|
||||
{
|
||||
extraLocations.Add(new Coordinates3D(-maxDistance - 1, -1, maxDistance + 1));
|
||||
extraLocations.Add(new Coordinates3D(maxDistance + 1, -1, -maxDistance - 1));
|
||||
extraLocations.Add(new Coordinates3D(-maxDistance - 1, -1, -maxDistance - 1));
|
||||
}
|
||||
extraLocations.Add(nearest);
|
||||
bool spread = false;
|
||||
for (int i = 0; i < extraLocations.Count; i++)
|
||||
{
|
||||
var location = extraLocations[i];
|
||||
location.Clamp(1);
|
||||
var xPotential = world.GetBlockID(new Coordinates3D(location.X, 0, 0) + coords);
|
||||
if (xPotential == 0)
|
||||
{
|
||||
var old = world.GetBlockID(coords);
|
||||
return old != WaterBlock.BlockID && old != StationaryWaterBlock.BlockID;
|
||||
}
|
||||
|
||||
var zPotential = world.GetBlockID(new Coordinates3D(0, 0, location.Z) + coords);
|
||||
if (zPotential == 0)
|
||||
{
|
||||
var old = world.GetBlockID(coords);
|
||||
return old != WaterBlock.BlockID && old != StationaryWaterBlock.BlockID;
|
||||
}
|
||||
}
|
||||
return spread;
|
||||
}
|
||||
|
||||
public bool DoAutomata(IMultiplayerServer server, IWorld world, Coordinates3D coords)
|
||||
{
|
||||
var meta = world.GetMetadata(coords);
|
||||
Coordinates3D[] neighbors =
|
||||
{
|
||||
Coordinates3D.Left,
|
||||
Coordinates3D.Right,
|
||||
Coordinates3D.Forwards,
|
||||
Coordinates3D.Backwards
|
||||
};
|
||||
var up = world.GetBlockID(coords + Coordinates3D.Up);
|
||||
var down = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
|
||||
|
||||
if (!down.Opaque)
|
||||
{
|
||||
PlaceWater(server, coords + Coordinates3D.Down, world, 1);
|
||||
if (meta != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check inward flow
|
||||
if (up == WaterBlock.BlockID || up == StationaryWaterBlock.BlockID)
|
||||
meta = 1;
|
||||
else
|
||||
{
|
||||
if (meta != 0)
|
||||
{
|
||||
byte minMeta = 15;
|
||||
int sources = 0;
|
||||
for (int i = 0; i < neighbors.Length; i++)
|
||||
{
|
||||
var nId = world.GetBlockID(coords + neighbors[i]);
|
||||
if (nId == WaterBlock.BlockID || nId == StationaryWaterBlock.BlockID)
|
||||
{
|
||||
var _meta = world.GetMetadata(coords + neighbors[i]);
|
||||
if (_meta < minMeta)
|
||||
minMeta = _meta;
|
||||
if (_meta == 0)
|
||||
sources++;
|
||||
}
|
||||
}
|
||||
if (sources >= 2)
|
||||
{
|
||||
world.SetMetadata(coords, 0);
|
||||
return true;
|
||||
}
|
||||
if (minMeta > 0)
|
||||
{
|
||||
meta = (byte)(minMeta + 1);
|
||||
if (meta >= MaxFlow + 1)
|
||||
{
|
||||
world.SetBlockID(coords, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
world.SetMetadata(coords, meta);
|
||||
|
||||
// Check outward flow
|
||||
if (meta < MaxFlow)
|
||||
{
|
||||
const int maxDistance = 5;
|
||||
var extraLocations = new List<Coordinates3D>();
|
||||
var nearest = new Coordinates3D(maxDistance + 1, -1, maxDistance + 1);
|
||||
for (int x = -maxDistance; x < maxDistance; x++)
|
||||
{
|
||||
for (int z = -maxDistance; z < maxDistance; z++)
|
||||
{
|
||||
if (Math.Abs(z) + Math.Abs(x) > maxDistance)
|
||||
continue;
|
||||
var check = new Coordinates3D(x, -1, z);
|
||||
var c = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords));
|
||||
if (!c.Opaque)
|
||||
{
|
||||
if (!LineOfSight(world, check + coords, coords))
|
||||
continue;
|
||||
if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearest + coords))
|
||||
extraLocations.Add(check);
|
||||
if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearest + coords))
|
||||
{
|
||||
extraLocations.Clear();
|
||||
nearest = check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearest == new Coordinates3D(maxDistance + 1, -1, maxDistance + 1))
|
||||
{
|
||||
extraLocations.Add(new Coordinates3D(-maxDistance - 1, -1, maxDistance + 1));
|
||||
extraLocations.Add(new Coordinates3D(maxDistance + 1, -1, -maxDistance - 1));
|
||||
extraLocations.Add(new Coordinates3D(-maxDistance - 1, -1, -maxDistance - 1));
|
||||
}
|
||||
extraLocations.Add(nearest);
|
||||
bool spread = false;
|
||||
for (int i = 0; i < extraLocations.Count; i++)
|
||||
{
|
||||
var location = extraLocations[i];
|
||||
location.Clamp(1);
|
||||
var xPotential = world.BlockRepository.GetBlockProvider(world.GetBlockID(new Coordinates3D(location.X, 0, 0) + coords));
|
||||
if (xPotential.Hardness == 0 && xPotential.ID != WaterBlock.BlockID && xPotential.ID != StationaryWaterBlock.BlockID)
|
||||
{
|
||||
if (PlaceWater(server, new Coordinates3D(location.X, 0, 0) + coords, world, (byte)(meta + 1)))
|
||||
{
|
||||
spread = true;
|
||||
xPotential.GenerateDropEntity(new BlockDescriptor
|
||||
{ Coordinates = new Coordinates3D(location.X, 0, 0) + coords, ID = xPotential.ID }, world, server);
|
||||
}
|
||||
}
|
||||
|
||||
var zPotential = world.BlockRepository.GetBlockProvider(world.GetBlockID(new Coordinates3D(0, 0, location.Z) + coords));
|
||||
if (zPotential.Hardness == 0 && zPotential.ID != WaterBlock.BlockID && zPotential.ID != StationaryWaterBlock.BlockID)
|
||||
{
|
||||
if (PlaceWater(server, new Coordinates3D(0, 0, location.Z) + coords, world, (byte)(meta + 1)))
|
||||
{
|
||||
spread = true;
|
||||
zPotential.GenerateDropEntity(new BlockDescriptor
|
||||
{ Coordinates = new Coordinates3D(0, 0, location.Z) + coords, ID = zPotential.ID }, world, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!spread)
|
||||
{
|
||||
world.SetBlockID(coords, StationaryWaterBlock.BlockID);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given candidate coordinate has a line-of-sight to the given target coordinate.
|
||||
/// </summary>
|
||||
private bool LineOfSight(IWorld world, Coordinates3D candidate, Coordinates3D target)
|
||||
{
|
||||
candidate += Coordinates3D.Up;
|
||||
@ -272,21 +127,184 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ScheduleNextEvent(Coordinates3D coords, IWorld world, IMultiplayerServer server)
|
||||
/// <summary>
|
||||
/// Examines neighboring blocks and determines the new water level that this block should adopt.
|
||||
/// </summary>
|
||||
protected byte DetermineInwardFlow(IWorld world, Coordinates3D coords)
|
||||
{
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(0.25), (_server) =>
|
||||
AutomataUpdate(_server, world, coords));
|
||||
var currentLevel = world.GetMetadata(coords);
|
||||
var up = world.GetBlockID(coords + Coordinates3D.Up);
|
||||
if (up == WaterBlock.BlockID || up == StationaryWaterBlock.BlockID) // Check for water above us
|
||||
return currentLevel;
|
||||
else
|
||||
{
|
||||
if (currentLevel != 0)
|
||||
{
|
||||
byte highestNeighboringFluid = 15;
|
||||
int neighboringSourceBlocks = 0;
|
||||
for (int i = 0; i < Neighbors.Length; i++)
|
||||
{
|
||||
var nId = world.GetBlockID(coords + Neighbors[i]);
|
||||
if (nId == WaterBlock.BlockID || nId == StationaryWaterBlock.BlockID)
|
||||
{
|
||||
var neighborLevel = world.GetMetadata(coords + Neighbors[i]);
|
||||
if (neighborLevel < highestNeighboringFluid)
|
||||
highestNeighboringFluid = neighborLevel;
|
||||
if (neighborLevel == 0)
|
||||
neighboringSourceBlocks++;
|
||||
}
|
||||
}
|
||||
if (neighboringSourceBlocks >= 2)
|
||||
currentLevel = 0;
|
||||
if (highestNeighboringFluid > 0)
|
||||
currentLevel = (byte)(highestNeighboringFluid + 1);
|
||||
}
|
||||
}
|
||||
return currentLevel;
|
||||
}
|
||||
|
||||
public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
|
||||
/// <summary>
|
||||
/// Produces a list of outward flow targets that this block may flow towards.
|
||||
/// </summary>
|
||||
protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords)
|
||||
{
|
||||
ScheduleNextEvent(descriptor.Coordinates, world, user.Server);
|
||||
// The maximum distance we will search for lower ground to flow towards
|
||||
const int DropCheckDistance = 5;
|
||||
|
||||
var outwardFlow = new List<LiquidFlow>(5);
|
||||
|
||||
var currentLevel = world.GetMetadata(coords);
|
||||
var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
|
||||
if (!blockBelow.Opaque)
|
||||
{
|
||||
outwardFlow.Add(new LiquidFlow(coords + Coordinates3D.Down, 1));
|
||||
if (currentLevel != 0)
|
||||
return outwardFlow.ToArray();
|
||||
}
|
||||
|
||||
if (currentLevel < MaximumFluidDepletion)
|
||||
{
|
||||
// This code is responsible for seeking out candidates for flowing towards.
|
||||
// Water in Minecraft will flow in the direction of the nearest drop-off where
|
||||
// there is at least one block removed on the Y axis.
|
||||
// It will flow towards several equally strong candidates at once.
|
||||
|
||||
var candidateFlowPoints = new List<Coordinates3D>(4);
|
||||
var furthestPossibleCandidate = new Coordinates3D(x: DropCheckDistance + 1, z: DropCheckDistance + 1) + Coordinates3D.Down;
|
||||
|
||||
var nearestCandidate = furthestPossibleCandidate;
|
||||
for (int x = -DropCheckDistance; x < DropCheckDistance; x++)
|
||||
{
|
||||
for (int z = -DropCheckDistance; z < DropCheckDistance; z++)
|
||||
{
|
||||
if (Math.Abs(z) + Math.Abs(x) > DropCheckDistance)
|
||||
continue;
|
||||
var check = new Coordinates3D(x: x, z: z) + Coordinates3D.Down;
|
||||
var c = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords));
|
||||
if (!c.Opaque)
|
||||
{
|
||||
if (!LineOfSight(world, check + coords, coords))
|
||||
continue;
|
||||
if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearestCandidate + coords))
|
||||
candidateFlowPoints.Add(check);
|
||||
if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearestCandidate + coords))
|
||||
{
|
||||
candidateFlowPoints.Clear();
|
||||
nearestCandidate = check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearestCandidate == furthestPossibleCandidate)
|
||||
{
|
||||
candidateFlowPoints.Add(new Coordinates3D(x: -DropCheckDistance - 1, z: DropCheckDistance + 1) + Coordinates3D.Down);
|
||||
candidateFlowPoints.Add(new Coordinates3D(x: DropCheckDistance + 1, z: -DropCheckDistance - 1) + Coordinates3D.Down);
|
||||
candidateFlowPoints.Add(new Coordinates3D(x: -DropCheckDistance - 1, z: -DropCheckDistance - 1) + Coordinates3D.Down);
|
||||
}
|
||||
candidateFlowPoints.Add(nearestCandidate);
|
||||
|
||||
// For each candidate, determine if we are actually capable of flowing towards it.
|
||||
// We are able to flow through blocks with a hardness of zero, but no others. We are
|
||||
// not able to flow through established water blocks.
|
||||
for (int i = 0; i < candidateFlowPoints.Count; i++)
|
||||
{
|
||||
var location = candidateFlowPoints[i];
|
||||
location.Clamp(1);
|
||||
|
||||
var xCoordinateCheck = new Coordinates3D(x: location.X) + coords;
|
||||
var zCoordinateCheck = new Coordinates3D(z: location.Z) + coords;
|
||||
|
||||
var xID = world.BlockRepository.GetBlockProvider(world.GetBlockID(xCoordinateCheck));
|
||||
var zID = world.BlockRepository.GetBlockProvider(world.GetBlockID(zCoordinateCheck));
|
||||
|
||||
if (xID.Hardness == 0 && xID.ID != WaterBlock.BlockID && xID.ID != StationaryWaterBlock.BlockID)
|
||||
{
|
||||
if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck))
|
||||
outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1)));
|
||||
}
|
||||
|
||||
if (zID.Hardness == 0 && zID.ID != WaterBlock.BlockID && zID.ID != StationaryWaterBlock.BlockID)
|
||||
{
|
||||
if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck))
|
||||
outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return outwardFlow.ToArray();
|
||||
}
|
||||
|
||||
public bool DoAutomata(IMultiplayerServer server, IWorld world, Coordinates3D coords)
|
||||
{
|
||||
var previousLevel = world.GetMetadata(coords);
|
||||
|
||||
var inward = DetermineInwardFlow(world, coords);
|
||||
var outward = DetermineOutwardFlow(world, coords);
|
||||
|
||||
if (outward.Length == 1 && outward[0].TargetBlock == coords + Coordinates3D.Down)
|
||||
{
|
||||
// Exit early if we have placed a water block beneath us (and we aren't a source block)
|
||||
if (previousLevel != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process inward flow
|
||||
if (inward > MaximumFluidDepletion)
|
||||
{
|
||||
world.SetBlockID(coords, 0);
|
||||
return true;
|
||||
}
|
||||
world.SetMetadata(coords, inward);
|
||||
if (inward == 0 && previousLevel != 0)
|
||||
{
|
||||
// Exit early if we have become a source block
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process outward flow
|
||||
for (int i = 0; i < outward.Length; i++)
|
||||
{
|
||||
var target = outward[i].TargetBlock;
|
||||
// For each block we can flow into, generate an item entity if appropriate
|
||||
var provider = world.BlockRepository.GetBlockProvider(world.GetBlockID(target));
|
||||
provider.GenerateDropEntity(new BlockDescriptor { Coordinates = target, ID = provider.ID }, world, server);
|
||||
// And overwrite the block with a new water block.
|
||||
world.SetBlockID(target, WaterBlock.BlockID);
|
||||
world.SetMetadata(target, outward[i].Level);
|
||||
server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), s => AutomataUpdate(s, world, target));
|
||||
}
|
||||
// Set our block to still water if we are done spreading.
|
||||
if (outward.Length == 0)
|
||||
{
|
||||
world.SetBlockID(coords, StationaryWaterBlock.BlockID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class StationaryWaterBlock : BlockProvider
|
||||
public class StationaryWaterBlock : WaterBlock
|
||||
{
|
||||
public static readonly byte BlockID = 0x09;
|
||||
public static readonly new byte BlockID = 0x09;
|
||||
|
||||
public override byte ID { get { return 0x09; } }
|
||||
|
||||
@ -302,6 +320,11 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
|
||||
public override byte LightModifier { get { return 3; } }
|
||||
|
||||
protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
|
||||
{
|
||||
return new ItemStack[0];
|
||||
}
|
||||
|
||||
public override BoundingBox? BoundingBox
|
||||
{
|
||||
get
|
||||
@ -310,13 +333,20 @@ namespace TrueCraft.Core.Logic.Blocks
|
||||
}
|
||||
}
|
||||
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
|
||||
public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
|
||||
{
|
||||
var provider = server.BlockRepository.GetBlockProvider(WaterBlock.BlockID) as WaterBlock;
|
||||
if (provider.CanFlow(world, descriptor.Coordinates))
|
||||
// This space intentionally left blank
|
||||
}
|
||||
|
||||
public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
|
||||
{
|
||||
if (source.ID == StationaryWaterBlock.BlockID || source.ID == WaterBlock.BlockID)
|
||||
return;
|
||||
var outward = DetermineOutwardFlow(world, descriptor.Coordinates);
|
||||
if (outward.Length != 0)
|
||||
{
|
||||
world.SetBlockID(descriptor.Coordinates, provider.ID);
|
||||
provider.ScheduleNextEvent(descriptor.Coordinates, world, server);
|
||||
world.SetBlockID(descriptor.Coordinates, WaterBlock.BlockID);
|
||||
ScheduleNextEvent(descriptor.Coordinates, world, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,12 +142,13 @@ namespace TrueCraft
|
||||
while (PendingBlockUpdates.Count != 0)
|
||||
{
|
||||
var update = PendingBlockUpdates.Dequeue();
|
||||
var source = update.World.GetBlockData(update.Coordinates);
|
||||
foreach (var offset in adjacent)
|
||||
{
|
||||
var descriptor = update.World.GetBlockData(update.Coordinates + offset);
|
||||
var provider = BlockRepository.GetBlockProvider(descriptor.ID);
|
||||
if (provider != null)
|
||||
provider.BlockUpdate(descriptor, this, update.World);
|
||||
provider.BlockUpdate(descriptor, source, this, update.World);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +285,9 @@ namespace TrueCraft
|
||||
Clients.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
while (client.DataAvailable)
|
||||
const long maxTicks = 100000 * 200; // 200ms
|
||||
var start = DateTime.Now;
|
||||
while (client.DataAvailable && (DateTime.Now.Ticks - start.Ticks) < maxTicks)
|
||||
{
|
||||
idle = false;
|
||||
try
|
||||
|
Reference in New Issue
Block a user