diff --git a/Chraft/Chraft.csproj b/Chraft/Chraft.csproj
index 63ca6bec..09ab0a69 100644
--- a/Chraft/Chraft.csproj
+++ b/Chraft/Chraft.csproj
@@ -256,6 +256,9 @@
+
+
+
diff --git a/Chraft/World/Blocks/BlockBase.cs b/Chraft/World/Blocks/BlockBase.cs
index 41148935..522462e8 100644
--- a/Chraft/World/Blocks/BlockBase.cs
+++ b/Chraft/World/Blocks/BlockBase.cs
@@ -157,6 +157,25 @@ public virtual void Destroy(EntityBase entity, StructBlock block)
NotifyNearbyBlocks(entity, block);
}
+ ///
+ /// Removes the block from the world. Don't drop anything.
+ ///
+ /// block that is being removed
+ public virtual void Remove(StructBlock block)
+ {
+ UpdateOnDestroy(block);
+ NotifyNearbyBlocks(null, block);
+ }
+
+ ///
+ /// Spawns the block in the world (not placed by the player)
+ ///
+ /// block that is being spawned
+ public virtual void Spawn(StructBlock block)
+ {
+ UpdateOnPlace(block);
+ }
+
///
/// Notifies the nearby block that the current block has been destroyed
/// May be used by recipient block to start the physic simulation etc
diff --git a/Chraft/World/Blocks/BlockGravel.cs b/Chraft/World/Blocks/BlockGravel.cs
index 31c70676..7fe685ef 100644
--- a/Chraft/World/Blocks/BlockGravel.cs
+++ b/Chraft/World/Blocks/BlockGravel.cs
@@ -5,6 +5,7 @@
using Chraft.Entity;
using Chraft.Interfaces;
using Chraft.Plugins.Events.Args;
+using Chraft.World.Blocks.Physics;
namespace Chraft.World.Blocks
{
@@ -39,5 +40,41 @@ protected override void DropItems(EntityBase entity, StructBlock block)
}
base.DropItems(entity, block);
}
+
+ public override void NotifyDestroy(EntityBase entity, StructBlock sourceBlock, StructBlock targetBlock)
+ {
+ if ((targetBlock.Coords.WorldY - sourceBlock.Coords.WorldY) == 1 &&
+ targetBlock.Coords.WorldX == sourceBlock.Coords.WorldX &&
+ targetBlock.Coords.WorldZ == sourceBlock.Coords.WorldZ)
+ {
+ StartPhysics(targetBlock);
+ }
+ base.NotifyDestroy(entity, sourceBlock, targetBlock);
+ }
+
+ public override void Place(EntityBase entity, StructBlock block, StructBlock targetBlock, BlockFace face)
+ {
+ if (!CanBePlacedOn(entity, block, targetBlock, face))
+ return;
+
+ if (!RaisePlaceEvent(entity, block))
+ return;
+
+ UpdateOnPlace(block);
+
+ RemoveItem(entity);
+
+ if (block.Coords.WorldY > 1)
+ if (block.World.GetBlockId(block.Coords.WorldX, block.Coords.WorldY - 1, block.Coords.WorldZ) == (byte)BlockData.Blocks.Air)
+ StartPhysics(block);
+ }
+
+ protected void StartPhysics(StructBlock block)
+ {
+ Remove(block);
+ FallingGravel fgBlock = new FallingGravel(block.World, new Location(block.Coords.WorldX + 0.5, block.Coords.WorldY + 0.5, block.Coords.WorldZ + 0.5));
+ fgBlock.Start();
+ block.World.PhysicsBlocks.TryAdd(fgBlock.EntityId, fgBlock);
+ }
}
}
diff --git a/Chraft/World/Blocks/BlockSand.cs b/Chraft/World/Blocks/BlockSand.cs
index 2091fb2b..2b7ab1c2 100644
--- a/Chraft/World/Blocks/BlockSand.cs
+++ b/Chraft/World/Blocks/BlockSand.cs
@@ -5,6 +5,7 @@
using Chraft.Entity;
using Chraft.Interfaces;
using Chraft.Plugins.Events.Args;
+using Chraft.World.Blocks.Physics;
namespace Chraft.World.Blocks
{
@@ -17,5 +18,41 @@ public BlockSand()
IsSolid = true;
LootTable.Add(new ItemStack((short)Type, 1));
}
+
+ public override void NotifyDestroy(EntityBase entity, StructBlock sourceBlock, StructBlock targetBlock)
+ {
+ if ((targetBlock.Coords.WorldY - sourceBlock.Coords.WorldY) == 1 &&
+ targetBlock.Coords.WorldX == sourceBlock.Coords.WorldX &&
+ targetBlock.Coords.WorldZ == sourceBlock.Coords.WorldZ)
+ {
+ StartPhysics(targetBlock);
+ }
+ base.NotifyDestroy(entity, sourceBlock, targetBlock);
+ }
+
+ public override void Place(EntityBase entity, StructBlock block, StructBlock targetBlock, BlockFace face)
+ {
+ if (!CanBePlacedOn(entity, block, targetBlock, face))
+ return;
+
+ if (!RaisePlaceEvent(entity, block))
+ return;
+
+ UpdateOnPlace(block);
+
+ RemoveItem(entity);
+
+ if (block.Coords.WorldY > 1)
+ if (block.World.GetBlockId(block.Coords.WorldX, block.Coords.WorldY - 1, block.Coords.WorldZ) == (byte)BlockData.Blocks.Air)
+ StartPhysics(block);
+ }
+
+ protected void StartPhysics(StructBlock block)
+ {
+ Remove(block);
+ FallingSand fsBlock = new FallingSand(block.World, new Location(block.Coords.WorldX + 0.5, block.Coords.WorldY + 0.5, block.Coords.WorldZ + 0.5));
+ fsBlock.Start();
+ block.World.PhysicsBlocks.TryAdd(fsBlock.EntityId, fsBlock);
+ }
}
}
diff --git a/Chraft/World/Blocks/Physics/BlockBasePhysics.cs b/Chraft/World/Blocks/Physics/BlockBasePhysics.cs
new file mode 100644
index 00000000..aaad6f13
--- /dev/null
+++ b/Chraft/World/Blocks/Physics/BlockBasePhysics.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Chraft.Net.Packets;
+using Chraft.Utils;
+
+namespace Chraft.World.Blocks.Physics
+{
+ public abstract class BlockBasePhysics
+ {
+ public int EntityId { get; protected set; }
+ public WorldManager World { get; protected set; }
+ public Location Position { get; protected set; }
+ public bool IsPlaying { get; protected set; }
+ public Vector3 Velocity { get; protected set; }
+ public AddObjectVehiclePacket.ObjectType Type;
+
+ protected BlockBasePhysics(WorldManager world, Location pos)
+ {
+ World = world;
+ Position = pos;
+ EntityId = world.Server.AllocateEntity();
+
+ CreateEntityPacket entity = new CreateEntityPacket { EntityId = EntityId };
+ foreach (var nearbyPlayer in World.Server.GetNearbyPlayers(World, new AbsWorldCoords(Position.X, Position.Y, Position.Z)))
+ {
+ nearbyPlayer.SendPacket(entity);
+ }
+ }
+
+ public virtual void Start()
+ {
+ if (IsPlaying)
+ return;
+ AddObjectVehiclePacket obj = new AddObjectVehiclePacket
+ {
+ EntityId = EntityId,
+ Type = Type,
+ UnknownFlag = 0,
+ X = Position.X,
+ Y = Position.Y,
+ Z = Position.Z
+ };
+ foreach (var nearbyPlayer in World.Server.GetNearbyPlayers(World, new AbsWorldCoords(Position.X, Position.Y, Position.Z)))
+ {
+ nearbyPlayer.SendPacket(obj);
+ }
+ IsPlaying = true;
+ }
+
+ public virtual void Simulate()
+ {
+ }
+
+ public virtual void Stop()
+ {
+ IsPlaying = false;
+ BlockBasePhysics unused = null;
+ World.PhysicsBlocks.TryRemove(EntityId, out unused);
+ DestroyEntityPacket entity = new DestroyEntityPacket { EntityId = EntityId };
+ foreach (var nearbyPlayer in World.Server.GetNearbyPlayers(World, new AbsWorldCoords(Position.X, Position.Y, Position.Z)))
+ {
+ nearbyPlayer.SendPacket(entity);
+ }
+ OnStop();
+ }
+
+ protected virtual void OnStop()
+ {
+ }
+ }
+}
diff --git a/Chraft/World/Blocks/Physics/FallingGravel.cs b/Chraft/World/Blocks/Physics/FallingGravel.cs
new file mode 100644
index 00000000..2ac03d53
--- /dev/null
+++ b/Chraft/World/Blocks/Physics/FallingGravel.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Chraft.Utils;
+
+namespace Chraft.World.Blocks.Physics
+{
+ public class FallingGravel : FallingSand
+ {
+ public FallingGravel(WorldManager world, Location pos) : base(world, pos)
+ {
+ Type = Net.Packets.AddObjectVehiclePacket.ObjectType.FallingGravel;
+ BlockId = (byte) BlockData.Blocks.Gravel;
+ }
+ }
+}
diff --git a/Chraft/World/Blocks/Physics/FallingSand.cs b/Chraft/World/Blocks/Physics/FallingSand.cs
new file mode 100644
index 00000000..3d0eb605
--- /dev/null
+++ b/Chraft/World/Blocks/Physics/FallingSand.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Chraft.Utils;
+
+namespace Chraft.World.Blocks.Physics
+{
+ public class FallingSand : BlockBasePhysics
+ {
+ protected byte BlockId;
+
+ public FallingSand(WorldManager world, Location pos) : base(world, pos)
+ {
+ Type = Net.Packets.AddObjectVehiclePacket.ObjectType.FallingSand;
+ BlockId = (byte) BlockData.Blocks.Sand;
+ Velocity = new Vector3(0, -0.4D, 0);
+ }
+
+ public override void Simulate()
+ {
+ int x = MathHelper.floor_double(Position.X);
+ int y = MathHelper.floor_double(Position.Y);
+ int z = MathHelper.floor_double(Position.Z);
+ byte blockId = World.GetBlockId(x, y, z);
+ if (blockId != (byte)BlockData.Blocks.Air)
+ {
+ Stop();
+ return;
+ }
+
+ if (Position.Y <= 1)
+ {
+ Stop();
+ return;
+ }
+
+ Position.Vector += Velocity;
+ }
+
+ protected override void OnStop()
+ {
+ UniversalCoords uc = UniversalCoords.FromWorld(MathHelper.floor_double(Position.X), MathHelper.floor_double(Position.Y) + 1, MathHelper.floor_double(Position.Z));
+ StructBlock block = new StructBlock(uc, BlockId, 0, World);
+ BlockHelper.Instance(BlockId).Spawn(block);
+ base.OnStop();
+ }
+ }
+}
diff --git a/Chraft/World/WorldManager.cs b/Chraft/World/WorldManager.cs
index ea205f5c..1ba8c2b5 100644
--- a/Chraft/World/WorldManager.cs
+++ b/Chraft/World/WorldManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Linq;
using System.Threading;
using Chraft.Net;
@@ -7,6 +8,7 @@
using System.IO;
using Chraft.Entity;
using Chraft.World.Blocks;
+using Chraft.World.Blocks.Physics;
using Chraft.World.Weather;
using Chraft.Plugins.Events.Args;
using System.Threading.Tasks;
@@ -40,6 +42,9 @@ public partial class WorldManager : IDisposable
public ConcurrentQueue ChunksToRecalculate;
+ public ConcurrentDictionary PhysicsBlocks;
+ private Task _PhysicsSimulationTask;
+
private readonly object ChunkLightUpdateLock = new object();
private Task _GrowStuffTask;
private Task _CollectTask;
@@ -175,6 +180,7 @@ public bool Load()
_ChunkProvider = new ChunkProvider(this);
Generator = _ChunkProvider.GetNewGenerator(GeneratorType.Custom, GetSeed());
ChunkManager = new WorldChunkManager(this);
+ PhysicsBlocks = new ConcurrentDictionary();
InitializeSpawn();
InitializeThreads();
@@ -354,7 +360,12 @@ private void GlobalTickProc(object state)
}
}
-
+ if (_PhysicsSimulationTask == null || _PhysicsSimulationTask.IsCompleted)
+ {
+ _PhysicsSimulationTask = new Task(PhysicsProc);
+ _PhysicsSimulationTask.Start();
+ }
+
}
public Chunk GetChunkFromPosition(int x, int z)
@@ -421,6 +432,14 @@ private void GrowProc()
}
}
+ private void PhysicsProc()
+ {
+ foreach (var physicsBlock in PhysicsBlocks)
+ {
+ physicsBlock.Value.Simulate();
+ }
+ }
+
private void EntityMoverStart()
{
Thread thread = new Thread(MovementThread);
@@ -705,21 +724,6 @@ private void UpdatePhysics(UniversalCoords coords, bool updateClients = true)
{
BlockData.Blocks type = (BlockData.Blocks)GetBlockId(coords);
UniversalCoords oneDown = UniversalCoords.FromWorld(coords.WorldX, coords.WorldY - 1, coords.WorldZ);
- if (type == BlockData.Blocks.Sand && coords.WorldY > 0 && GetBlockId(oneDown) == 0)
- {
- SetBlockAndData(coords, 0, 0);
- SetBlockAndData(oneDown, (byte)BlockData.Blocks.Sand, 0);
- Update(oneDown, updateClients);
- return;
- }
-
- if (type == BlockData.Blocks.Gravel && coords.WorldY > 0 && GetBlockId(UniversalCoords.FromWorld(coords.WorldX, coords.WorldY - 1, coords.WorldZ)) == 0)
- {
- SetBlockAndData(coords, 0, 0);
- SetBlockAndData(oneDown, (byte)BlockData.Blocks.Gravel, 0);
- Update(oneDown, updateClients);
- return;
- }
if (type == BlockData.Blocks.Water)
{