diff --git a/TrueCraft.API/Coordinates3D.cs b/TrueCraft.API/Coordinates3D.cs
index 84d62a2..b835465 100644
--- a/TrueCraft.API/Coordinates3D.cs
+++ b/TrueCraft.API/Coordinates3D.cs
@@ -39,6 +39,21 @@ namespace TrueCraft.API
#region Math
+ ///
+ /// Clamps the coordinates to within the specified value.
+ ///
+ /// Value.
+ public void Clamp(int value)
+ {
+ // TODO: Fix for negative values
+ if (Math.Abs(X) > value)
+ X = value * (X < 0 ? -1 : 1);
+ if (Math.Abs(Y) > value)
+ Y = value * (Y < 0 ? -1 : 1);
+ if (Math.Abs(Z) > value)
+ Z = value * (Z < 0 ? -1 : 1);
+ }
+
///
/// Calculates the distance between two Coordinates3D objects.
///
@@ -222,6 +237,10 @@ namespace TrueCraft.API
public static readonly Coordinates3D North = new Coordinates3D(0, 0, -1);
public static readonly Coordinates3D South = new Coordinates3D(0, 0, 1);
+ public static readonly Coordinates3D OneX = new Coordinates3D(1, 0, 0);
+ public static readonly Coordinates3D OneY = new Coordinates3D(0, 1, 0);
+ public static readonly Coordinates3D OneZ = new Coordinates3D(0, 0, 1);
+
#endregion
public bool Equals(Coordinates3D other)
diff --git a/TrueCraft.Core/Logic/Blocks/WaterBlock.cs b/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
index e5f7c6f..646d5c2 100644
--- a/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
+++ b/TrueCraft.Core/Logic/Blocks/WaterBlock.cs
@@ -2,11 +2,16 @@ using System;
using TrueCraft.API.Logic;
using TrueCraft.API.Server;
using TrueCraft.API.World;
+using TrueCraft.API;
+using TrueCraft.API.Networking;
+using System.Collections.Generic;
namespace TrueCraft.Core.Logic.Blocks
{
public class WaterBlock : BlockProvider
{
+ private const byte MaxFlow = 7;
+
public static readonly byte BlockID = 0x08;
public override byte ID { get { return 0x08; } }
@@ -23,18 +28,288 @@ namespace TrueCraft.Core.Logic.Blocks
public override string DisplayName { get { return "Water"; } }
- public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
+ public override BoundingBox? BoundingBox
{
- base.BlockUpdate(descriptor, server, world);
+ get
+ {
+ return null;
+ }
+ }
+
+ private bool PlaceWater(IMultiplayerServer server, Coordinates3D coords, IWorld world, byte meta = 0)
+ {
+ 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;
+ }
+
+ 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(0.25), (_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();
+ 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();
+ 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)
+ {
+ if (PlaceWater(server, new Coordinates3D(location.X, 0, 0) + coords, world, (byte)(meta + 1)))
+ spread = true;
+ }
+
+ var zPotential = world.GetBlockID(new Coordinates3D(0, 0, location.Z) + coords);
+ if (zPotential == 0)
+ {
+ if (PlaceWater(server, new Coordinates3D(0, 0, location.Z) + coords, world, (byte)(meta + 1)))
+ spread = true;
+ }
+ }
+ if (!spread)
+ {
+ world.SetBlockID(coords, StationaryWaterBlock.BlockID);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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;
+ }
+
+ public void ScheduleNextEvent(Coordinates3D coords, IWorld world, IMultiplayerServer server)
+ {
+ server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(0.25), (_server) =>
+ AutomataUpdate(_server, world, coords));
+ }
+
+ public override void BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
+ {
+ ScheduleNextEvent(descriptor.Coordinates, world, user.Server);
}
}
- public class StationaryWaterBlock : WaterBlock
+ public class StationaryWaterBlock : BlockProvider
{
- public static readonly new byte BlockID = 0x09;
+ public static readonly byte BlockID = 0x09;
public override byte ID { get { return 0x09; } }
public override string DisplayName { get { return "Water (stationary)"; } }
+
+ public override double BlastResistance { get { return 500; } }
+
+ public override double Hardness { get { return 100; } }
+
+ public override byte Luminance { get { return 0; } }
+
+ public override bool Opaque { get { return false; } }
+
+ public override byte LightModifier { get { return 3; } }
+
+ public override BoundingBox? BoundingBox
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
+ {
+ var provider = server.BlockRepository.GetBlockProvider(WaterBlock.BlockID) as WaterBlock;
+ if (provider.CanFlow(world, descriptor.Coordinates))
+ {
+ world.SetBlockID(descriptor.Coordinates, provider.ID);
+ provider.ScheduleNextEvent(descriptor.Coordinates, world, server);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/TrueCraft.Core/Logic/Items/BucketItem.cs b/TrueCraft.Core/Logic/Items/BucketItem.cs
index 3a101ef..078b531 100644
--- a/TrueCraft.Core/Logic/Items/BucketItem.cs
+++ b/TrueCraft.Core/Logic/Items/BucketItem.cs
@@ -26,7 +26,7 @@ namespace TrueCraft.Core.Logic.Items
if (block == WaterBlock.BlockID || block == StationaryWaterBlock.BlockID)
{
var meta = world.GetMetadata(coordinates);
- if (meta == 15) // Is source block?
+ if (meta == 0) // Is source block?
{
user.Inventory[user.SelectedSlot] = new ItemStack(WaterBucketItem.ItemID);
world.SetBlockID(coordinates, 0);
@@ -35,7 +35,7 @@ namespace TrueCraft.Core.Logic.Items
else if (block == LavaBlock.BlockID || block == StationaryLavaBlock.BlockID)
{
var meta = world.GetMetadata(coordinates);
- if (meta == 15) // Is source block?
+ if (meta == 0) // Is source block?
{
user.Inventory[user.SelectedSlot] = new ItemStack(LavaBucketItem.ItemID);
world.SetBlockID(coordinates, 0);
@@ -52,8 +52,10 @@ namespace TrueCraft.Core.Logic.Items
var blockType = RelevantBlockType.Value;
user.Server.BlockUpdatesEnabled = false;
world.SetBlockID(coordinates, blockType);
- world.SetMetadata(coordinates, 15); // Source block
+ world.SetMetadata(coordinates, 0); // Source block
user.Server.BlockUpdatesEnabled = true;
+ var liquidProvider = world.BlockRepository.GetBlockProvider(blockType);
+ liquidProvider.BlockPlaced(new BlockDescriptor { Coordinates = coordinates }, face, world, user);
}
user.Inventory[user.SelectedSlot] = new ItemStack(BucketItem.ItemID);
}
diff --git a/TrueCraft.Core/TerrainGen/FlatlandGenerator.cs b/TrueCraft.Core/TerrainGen/FlatlandGenerator.cs
index 0e833a0..84d2705 100644
--- a/TrueCraft.Core/TerrainGen/FlatlandGenerator.cs
+++ b/TrueCraft.Core/TerrainGen/FlatlandGenerator.cs
@@ -19,7 +19,7 @@ namespace TrueCraft.Core.TerrainGen
public FlatlandGenerator()
{
GeneratorOptions = DefaultGeneratorOptions;
- SpawnPoint = new Vector3(0, 4, 0);
+ SpawnPoint = new Vector3(0, 5, 0);
}
public FlatlandGenerator(string generatorOptions)
diff --git a/TrueCraft/MultiplayerServer.cs b/TrueCraft/MultiplayerServer.cs
index f004c4d..72e4fe4 100644
--- a/TrueCraft/MultiplayerServer.cs
+++ b/TrueCraft/MultiplayerServer.cs
@@ -287,39 +287,35 @@ namespace TrueCraft
while (client.DataAvailable)
{
idle = false;
- var packet = PacketReader.ReadPacket(client.MinecraftStream);
- LogPacket(packet, true);
- if (PacketHandlers[packet.ID] != null)
+ try
{
- try
- {
+ var packet = PacketReader.ReadPacket(client.MinecraftStream);
+ LogPacket(packet, true);
+ if (PacketHandlers[packet.ID] != null)
PacketHandlers[packet.ID](packet, client, this);
- }
- catch (PlayerDisconnectException)
- {
- DisconnectClient(client);
- break;
- }
- catch (SocketException e)
- {
- Log(LogCategory.Debug, "Disconnecting client due to exception in network worker");
- Log(LogCategory.Debug, e.ToString());
- DisconnectClient(client);
- break;
- }
- catch (Exception e)
- {
- 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();
- DisconnectClient(client);
- break;
- }
+ else
+ client.Log("Unhandled packet {0}", packet.GetType().Name);
}
- else
+ catch (PlayerDisconnectException)
{
- client.Log("Unhandled packet {0}", packet.GetType().Name);
+ DisconnectClient(client);
+ break;
+ }
+ catch (SocketException e)
+ {
+ Log(LogCategory.Debug, "Disconnecting client due to exception in network worker");
+ Log(LogCategory.Debug, e.ToString());
+ DisconnectClient(client);
+ break;
+ }
+ catch (Exception e)
+ {
+ 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();
+ DisconnectClient(client);
+ break;
}
}
if (idle)