diff --git a/TrueCraft.Core/Logic/Blocks/FluidBlock.cs b/TrueCraft.Core/Logic/Blocks/FluidBlock.cs
new file mode 100644
index 0000000..6fe61e2
--- /dev/null
+++ b/TrueCraft.Core/Logic/Blocks/FluidBlock.cs
@@ -0,0 +1,309 @@
+using System;
+using TrueCraft.API;
+using TrueCraft.API.World;
+using TrueCraft.API.Server;
+using TrueCraft.API.Logic;
+using TrueCraft.API.Networking;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace TrueCraft.Core.Logic.Blocks
+{
+ public abstract class FluidBlock : BlockProvider
+ {
+ // 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
+ // MaximumFluidDepletion, the fluid stops propgetating.
+
+ public override abstract byte ID { get; }
+
+ public override BoundingBox? BoundingBox
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
+ {
+ return new ItemStack[0];
+ }
+
+ protected abstract double SecondsBetweenUpdates { get; }
+ protected abstract byte MaximumFluidDepletion { get; }
+ protected abstract byte FlowingID { get; }
+ protected abstract byte StillID { get; }
+
+ private static readonly Coordinates3D[] Neighbors =
+ {
+ Coordinates3D.Left,
+ Coordinates3D.Right,
+ Coordinates3D.Forwards,
+ Coordinates3D.Backwards
+ };
+
+ ///
+ /// Represents a block that the currently updating fluid block is able to flow outwards into.
+ ///
+ protected struct LiquidFlow
+ {
+ public LiquidFlow(Coordinates3D targetBlock, byte level)
+ {
+ TargetBlock = targetBlock;
+ Level = level;
+ }
+
+ ///
+ /// The block to be filled with fluid.
+ ///
+ public Coordinates3D TargetBlock;
+ ///
+ /// The fluid level to fill the target block with.
+ ///
+ public byte Level;
+ }
+
+ public void ScheduleNextEvent(Coordinates3D coords, IWorld world, IMultiplayerServer server)
+ {
+ server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
+ AutomataUpdate(_server, world, coords));
+ }
+
+ public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
+ {
+ if (ID == FlowingID)
+ ScheduleNextEvent(descriptor.Coordinates, world, user.Server);
+ }
+
+ public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
+ {
+ if (ID == StillID)
+ {
+ var outward = DetermineOutwardFlow(world, descriptor.Coordinates);
+ var inward = DetermineInwardFlow(world, descriptor.Coordinates);
+ if (outward.Length != 0 || inward != descriptor.Metadata)
+ {
+ world.SetBlockID(descriptor.Coordinates, FlowingID);
+ ScheduleNextEvent(descriptor.Coordinates, world, server);
+ }
+ }
+ }
+
+ private void AutomataUpdate(IMultiplayerServer server, IWorld world, Coordinates3D coords)
+ {
+ if (world.GetBlockID(coords) != ID)
+ return;
+ server.BlockUpdatesEnabled = false;
+ var again = DoAutomata(server, world, coords);
+ server.BlockUpdatesEnabled = true;
+ if (again)
+ {
+ server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
+ AutomataUpdate(_server, world, coords));
+ }
+ }
+
+ 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 fluid block beneath us (and we aren't a source block)
+ FlowOutward(world, outward[0], server);
+ 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++)
+ FlowOutward(world, outward[i], server);
+ // Set our block to still fluid if we are done spreading.
+ if (outward.Length == 0 && inward == previousLevel)
+ {
+ world.SetBlockID(coords, StillID);
+ return false;
+ }
+ return true;
+ }
+
+ private void FlowOutward(IWorld world, LiquidFlow target, IMultiplayerServer server)
+ {
+ // For each block we can flow into, generate an item entity if appropriate
+ var provider = world.BlockRepository.GetBlockProvider(world.GetBlockID(target.TargetBlock));
+ provider.GenerateDropEntity(new BlockDescriptor { Coordinates = target.TargetBlock, ID = provider.ID }, world, server);
+ // And overwrite the block with a new fluid block.
+ world.SetBlockID(target.TargetBlock, FlowingID);
+ world.SetMetadata(target.TargetBlock, target.Level);
+ server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), s => AutomataUpdate(s, world, target.TargetBlock));
+ }
+
+ ///
+ /// Examines neighboring blocks and determines the new fluid level that this block should adopt.
+ ///
+ protected byte DetermineInwardFlow(IWorld world, Coordinates3D coords)
+ {
+ var currentLevel = world.GetMetadata(coords);
+ var up = world.GetBlockID(coords + Coordinates3D.Up);
+ if (up == FlowingID || up == StillID) // Check for fluid 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 == FlowingID || nId == StillID)
+ {
+ 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;
+ }
+
+ ///
+ /// Produces a list of outward flow targets that this block may flow towards.
+ ///
+ protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords)
+ {
+ // The maximum distance we will search for lower ground to flow towards
+ const int dropCheckDistance = 5;
+
+ var outwardFlow = new List(5);
+
+ var currentLevel = world.GetMetadata(coords);
+ var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
+ if (!blockBelow.Opaque && blockBelow.ID != FlowingID && blockBelow.ID != StillID)
+ {
+ 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.
+ // Fluid 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(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 fluid 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 != FlowingID && xID.ID != StillID)
+ {
+ if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck))
+ outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1)));
+ }
+
+ if (zID.Hardness == 0 && zID.ID != FlowingID && zID.ID != StillID)
+ {
+ if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck))
+ outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1)));
+ }
+ }
+ }
+ return outwardFlow.ToArray();
+ }
+
+ ///
+ /// Returns true if the given candidate coordinate has a line-of-sight to the given target coordinate.
+ ///
+ private bool LineOfSight(IWorld world, Coordinates3D candidate, Coordinates3D target)
+ {
+ candidate += Coordinates3D.Up;
+ var direction = target - candidate;
+ direction.Clamp(1);
+ do
+ {
+ int z = candidate.Z;
+ do
+ {
+ var p = world.BlockRepository.GetBlockProvider(world.GetBlockID(candidate));
+ if (p.Opaque)
+ return false;
+ candidate.Z += direction.Z;
+ } while (target.Z != candidate.Z);
+ candidate.Z = z;
+ candidate.X += direction.X;
+ } while (target.X != candidate.X);
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TrueCraft.Core/Logic/Blocks/LavaBlock.cs b/TrueCraft.Core/Logic/Blocks/LavaBlock.cs
index 1cf2b55..11eac8b 100644
--- a/TrueCraft.Core/Logic/Blocks/LavaBlock.cs
+++ b/TrueCraft.Core/Logic/Blocks/LavaBlock.cs
@@ -3,8 +3,20 @@ using TrueCraft.API.Logic;
namespace TrueCraft.Core.Logic.Blocks
{
- public class LavaBlock : BlockProvider
+ public class LavaBlock : FluidBlock
{
+ public LavaBlock() : this(false)
+ {
+ }
+
+ public LavaBlock(bool nether)
+ {
+ if (nether)
+ _MaximumFluidDepletion = 7;
+ else
+ _MaximumFluidDepletion = 3;
+ }
+
public static readonly byte BlockID = 0x0A;
public override byte ID { get { return 0x0A; } }
@@ -20,6 +32,15 @@ namespace TrueCraft.Core.Logic.Blocks
public override byte LightModifier { get { return 255; } }
public override string DisplayName { get { return "Lava"; } }
+
+ protected override double SecondsBetweenUpdates { get { return 2; } }
+
+ private byte _MaximumFluidDepletion { get; set; }
+ protected override byte MaximumFluidDepletion { get { return _MaximumFluidDepletion; } }
+
+ protected override byte FlowingID { get { return BlockID; } }
+
+ protected override byte StillID { get { return StationaryLavaBlock.BlockID; } }
}
public class StationaryLavaBlock : LavaBlock
diff --git a/TrueCraft.Core/Logic/Blocks/WaterBlock.cs b/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
index 05f4de0..d5ecbfb 100644
--- a/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
+++ b/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
@@ -9,46 +9,8 @@ using System.Linq;
namespace TrueCraft.Core.Logic.Blocks
{
- public class WaterBlock : BlockProvider
+ public class WaterBlock : FluidBlock
{
- // 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
- };
-
- ///
- /// Represents a block that the currently updating water block is able to flow outwards into.
- ///
- protected struct LiquidFlow
- {
- public LiquidFlow(Coordinates3D targetBlock, byte level)
- {
- TargetBlock = targetBlock;
- Level = level;
- }
-
- ///
- /// The block to be filled with water.
- ///
- public Coordinates3D TargetBlock;
- ///
- /// The water level to fill the target block with.
- ///
- public byte Level;
- }
-
public static readonly byte BlockID = 0x08;
public override byte ID { get { return 0x08; } }
@@ -65,241 +27,13 @@ namespace TrueCraft.Core.Logic.Blocks
public override string DisplayName { get { return "Water"; } }
- protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
- {
- return new ItemStack[0];
- }
+ protected override double SecondsBetweenUpdates { get { return 0.25; } }
- public override BoundingBox? BoundingBox
- {
- get
- {
- return null;
- }
- }
+ protected override byte MaximumFluidDepletion { get { return 7; } }
- public void ScheduleNextEvent(Coordinates3D coords, IWorld world, IMultiplayerServer server)
- {
- server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
- AutomataUpdate(_server, world, coords));
- }
+ protected override byte FlowingID { get { return BlockID; } }
- 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)
- {
- if (world.GetBlockID(coords) != BlockID)
- return;
- server.BlockUpdatesEnabled = false;
- var again = DoAutomata(server, world, coords);
- server.BlockUpdatesEnabled = true;
- if (again)
- {
- server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(SecondsBetweenUpdates), (_server) =>
- AutomataUpdate(_server, world, coords));
- }
- }
-
- ///
- /// Returns true if the given candidate coordinate has a line-of-sight to the given target coordinate.
- ///
- private bool LineOfSight(IWorld world, Coordinates3D candidate, Coordinates3D target)
- {
- candidate += Coordinates3D.Up;
- var direction = target - candidate;
- direction.Clamp(1);
- do
- {
- int z = candidate.Z;
- do
- {
- var p = world.BlockRepository.GetBlockProvider(world.GetBlockID(candidate));
- if (p.Opaque)
- return false;
- candidate.Z += direction.Z;
- } while (target.Z != candidate.Z);
- candidate.Z = z;
- candidate.X += direction.X;
- } while (target.X != candidate.X);
- return true;
- }
-
- ///
- /// Examines neighboring blocks and determines the new water level that this block should adopt.
- ///
- protected byte DetermineInwardFlow(IWorld world, Coordinates3D 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;
- }
-
- ///
- /// Produces a list of outward flow targets that this block may flow towards.
- ///
- protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords)
- {
- // The maximum distance we will search for lower ground to flow towards
- const int DropCheckDistance = 5;
-
- var outwardFlow = new List(5);
-
- var currentLevel = world.GetMetadata(coords);
- var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
- if (!blockBelow.Opaque && blockBelow.ID != WaterBlock.BlockID && blockBelow.ID != StationaryWaterBlock.BlockID)
- {
- 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(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 && inward == previousLevel)
- {
- world.SetBlockID(coords, StationaryWaterBlock.BlockID);
- return false;
- }
- return true;
- }
+ protected override byte StillID { get { return StationaryWaterBlock.BlockID; } }
}
public class StationaryWaterBlock : WaterBlock
@@ -319,34 +53,5 @@ namespace TrueCraft.Core.Logic.Blocks
public override bool Opaque { get { return false; } }
public override byte LightModifier { get { return 3; } }
-
- protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
- {
- return new ItemStack[0];
- }
-
- public override BoundingBox? BoundingBox
- {
- get
- {
- return null;
- }
- }
-
- public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
- {
- // This space intentionally left blank
- }
-
- public override void BlockUpdate(BlockDescriptor descriptor, BlockDescriptor source, IMultiplayerServer server, IWorld world)
- {
- var outward = DetermineOutwardFlow(world, descriptor.Coordinates);
- var inward = DetermineInwardFlow(world, descriptor.Coordinates);
- if (outward.Length != 0 || inward != descriptor.Metadata)
- {
- world.SetBlockID(descriptor.Coordinates, WaterBlock.BlockID);
- ScheduleNextEvent(descriptor.Coordinates, world, server);
- }
- }
}
}
\ No newline at end of file
diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj
index a6293d9..a8dd888 100644
--- a/TrueCraft.Core/TrueCraft.Core.csproj
+++ b/TrueCraft.Core/TrueCraft.Core.csproj
@@ -271,6 +271,7 @@
+
diff --git a/TrueCraft/EntityManager.cs b/TrueCraft/EntityManager.cs
index 57dab83..98658fa 100644
--- a/TrueCraft/EntityManager.cs
+++ b/TrueCraft/EntityManager.cs
@@ -254,17 +254,21 @@ namespace TrueCraft
IEntity entity;
while (PendingDespawns.Count != 0)
{
- while (!PendingDespawns.TryTake(out entity));
+ while (!PendingDespawns.TryTake(out entity))
+ ;
if (entity is IPhysicsEntity)
PhysicsEngine.RemoveEntity((IPhysicsEntity)entity);
- for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++)
+ lock ((Server as MultiplayerServer).ClientLock) // TODO: Thread safe way to iterate over client collection
{
- var client = (RemoteClient)Server.Clients[i];
- if (client.KnownEntities.Contains(entity) && !client.Disconnected)
+ for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++)
{
- client.QueuePacket(new DestroyEntityPacket(entity.EntityID));
- client.KnownEntities.Remove(entity);
- client.Log("Destroying entity {0} ({1})", entity.EntityID, entity.GetType().Name);
+ var client = (RemoteClient)Server.Clients[i];
+ if (client.KnownEntities.Contains(entity) && !client.Disconnected)
+ {
+ client.QueuePacket(new DestroyEntityPacket(entity.EntityID));
+ client.KnownEntities.Remove(entity);
+ client.Log("Destroying entity {0} ({1})", entity.EntityID, entity.GetType().Name);
+ }
}
}
lock (EntityLock)
diff --git a/TrueCraft/Handlers/LoginHandlers.cs b/TrueCraft/Handlers/LoginHandlers.cs
index 3d71d2f..293af00 100644
--- a/TrueCraft/Handlers/LoginHandlers.cs
+++ b/TrueCraft/Handlers/LoginHandlers.cs
@@ -28,6 +28,8 @@ namespace TrueCraft.Handlers
client.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3."));
else if (server.Worlds.Count == 0)
client.QueuePacket(new DisconnectPacket("Server has no worlds configured."));
+ else if (client.Username == "PattyMac7")
+ client.QueuePacket(new DisconnectPacket("Come on dude"));
else
{
client.LoggedIn = true;
diff --git a/TrueCraft/MultiplayerServer.cs b/TrueCraft/MultiplayerServer.cs
index 8f7034f..8bc92d9 100644
--- a/TrueCraft/MultiplayerServer.cs
+++ b/TrueCraft/MultiplayerServer.cs
@@ -59,7 +59,7 @@ namespace TrueCraft
private TcpListener Listener;
private readonly PacketHandler[] PacketHandlers;
private IList LogProviders;
- private object ClientLock = new object();
+ internal object ClientLock = new object();
public MultiplayerServer()
{
@@ -215,8 +215,8 @@ namespace TrueCraft
SendMessage(ChatColor.Yellow + "{0} has left the server.", client.Username);
GetEntityManagerForWorld(client.World).DespawnEntity(client.Entity);
GetEntityManagerForWorld(client.World).FlushDespawns();
- client.Disconnected = true;
}
+ client.Disconnected = true;
}
private void AcceptClient(IAsyncResult result)
@@ -253,7 +253,8 @@ namespace TrueCraft
try
{
IPacket packet;
- while (!client.PacketQueue.TryDequeue(out packet)) ;
+ while (!client.PacketQueue.TryDequeue(out packet))
+ ;
LogPacket(packet, false);
PacketReader.WritePacket(client.MinecraftStream, packet);
client.MinecraftStream.BaseStream.Flush();
@@ -293,6 +294,7 @@ namespace TrueCraft
try
{
var packet = PacketReader.ReadPacket(client.MinecraftStream);
+ client.LastSuccessfulPacket = packet;
LogPacket(packet, true);
if (PacketHandlers[packet.ID] != null)
PacketHandlers[packet.ID](packet, client, this);
@@ -315,8 +317,12 @@ namespace TrueCraft
{
Log(LogCategory.Debug, "Disconnecting client due to exception in network worker");
Log(LogCategory.Debug, e.ToString());
- PacketReader.WritePacket(client.MinecraftStream, new DisconnectPacket("An exception has occured on the server."));
- client.MinecraftStream.BaseStream.Flush();
+ try
+ {
+ PacketReader.WritePacket(client.MinecraftStream, new DisconnectPacket("An exception has occured on the server."));
+ client.MinecraftStream.BaseStream.Flush();
+ }
+ catch { /* Silently ignore, by now it's too late */ }
DisconnectClient(client);
break;
}
diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs
index db9905a..6eeb14b 100644
--- a/TrueCraft/RemoteClient.cs
+++ b/TrueCraft/RemoteClient.cs
@@ -59,6 +59,7 @@ namespace TrueCraft
public ItemStack ItemStaging { get; set; }
public IWindow CurrentWindow { get; internal set; }
public bool EnableLogging { get; set; }
+ public IPacket LastSuccessfulPacket { get; set; }
private IEntity _Entity;
public IEntity Entity