Implement bed placement and supported blocks

This commit is contained in:
Drew DeVault 2015-02-01 18:53:10 -07:00
parent 91fdf59532
commit eda5dd2f82
19 changed files with 332 additions and 42 deletions

View File

@ -1,6 +1,6 @@
using System;
namespace TrueCraft.API.World
namespace TrueCraft.API.Logic
{
public struct BlockDescriptor
{

View File

@ -1,6 +1,7 @@
using System;
using TrueCraft.API.World;
using TrueCraft.API.Networking;
using TrueCraft.API.Server;
namespace TrueCraft.API.Logic
{
@ -17,7 +18,7 @@ namespace TrueCraft.API.Logic
bool BlockRightClicked(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
bool BlockPlaced(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
void BlockMined(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
void BlockUpdate(BlockDescriptor descriptor, IWorld world);
void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world);
void BlockScheduledEvent(BlockDescriptor descriptor, IWorld world, object data);
}
}

View File

@ -1,4 +1,7 @@
using System;
using TrueCraft.API.World;
using TrueCraft.API.Networking;
using TrueCraft.API.Entities;
namespace TrueCraft.API.Logic
{
@ -7,6 +10,8 @@ namespace TrueCraft.API.Logic
short ID { get; }
sbyte MaximumStack { get; }
string DisplayName { get; }
//bool ItemUsedOnBlock(ItemDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user);
void ItemUsedOnNothing(ItemStack item, IWorld world, IRemoteClient user);
void ItemUsedOnEntity(ItemStack item, IEntity usedOn, IWorld world, IRemoteClient user);
void ItemUsedOnBlock(Coordinates3D coordinates, ItemStack item, BlockFace face, IWorld world, IRemoteClient user);
}
}

View File

@ -25,6 +25,7 @@ namespace TrueCraft.API.Server
IEventScheduler Scheduler { get; }
IBlockRepository BlockRepository { get; }
IItemRepository ItemRepository { get; }
bool BlockUpdatesEnabled { get; set; }
void Start(IPEndPoint endPoint);
void RegisterPacketHandler(byte packetId, PacketHandler handler);

View File

@ -79,11 +79,11 @@
<Compile Include="BlockFace.cs" />
<Compile Include="Server\PlayerJoinedEventArgs.cs" />
<Compile Include="Logic\IBlockRepository.cs" />
<Compile Include="World\BlockDescriptor.cs" />
<Compile Include="Entities\IAABBEntity.cs" />
<Compile Include="Entities\IBlockPhysicsProvider.cs" />
<Compile Include="Entities\IPhysicsEntity.cs" />
<Compile Include="Logic\IItemRepository.cs" />
<Compile Include="Logic\BlockDescriptor.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup />

View File

@ -1,4 +1,5 @@
using System;
using TrueCraft.API.Logic;
namespace TrueCraft.API.World
{

View File

@ -1,4 +1,5 @@
using System;
using TrueCraft.API.Logic;
namespace TrueCraft.API.World
{
@ -19,6 +20,7 @@ namespace TrueCraft.API.World
byte GetMetadata(Coordinates3D coordinates);
byte GetSkyLight(Coordinates3D coordinates);
BlockDescriptor GetBlockData(Coordinates3D coordinates);
void SetBlockData(Coordinates3D coordinates, BlockDescriptor block);
void SetBlockID(Coordinates3D coordinates, byte value);
void SetMetadata(Coordinates3D coordinates, byte value);
void SetSkyLight(Coordinates3D coordinates, byte value);

View File

@ -19,6 +19,8 @@ namespace TrueCraft.Core.Entities
{
Position = position;
Item = item;
if (Item.ID == 0)
System.Diagnostics.Debugger.Break();
}
public ItemStack Item { get; set; }

View File

@ -4,13 +4,15 @@ using TrueCraft.API.World;
using TrueCraft.API;
using TrueCraft.API.Networking;
using TrueCraft.Core.Entities;
using TrueCraft.API.Entities;
using TrueCraft.API.Server;
namespace TrueCraft.Core.Logic
{
/// <summary>
/// Provides common implementations of block logic.
/// </summary>
public abstract class BlockProvider : IBlockProvider, IItemProvider
public abstract class BlockProvider : IItemProvider, IBlockProvider
{
public virtual bool BlockRightClicked(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
{
@ -24,18 +26,38 @@ namespace TrueCraft.Core.Logic
public virtual void BlockMined(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
{
var entityManager = user.Server.GetEntityManagerForWorld(world);
var items = GetDrop(descriptor);
foreach (var item in items)
{
entityManager.SpawnEntity(new ItemEntity(new Vector3(descriptor.Coordinates) + new Vector3(0.5), item));
}
GenerateDropEntity(descriptor, world, user.Server);
world.SetBlockID(descriptor.Coordinates, 0);
}
public virtual void BlockUpdate(BlockDescriptor descriptor, IWorld world)
private void GenerateDropEntity(BlockDescriptor descriptor, IWorld world, IMultiplayerServer server)
{
// This space intentionally left blank
var entityManager = server.GetEntityManagerForWorld(world);
var items = GetDrop(descriptor);
foreach (var item in items)
entityManager.SpawnEntity(new ItemEntity(new Vector3(descriptor.Coordinates) + new Vector3(0.5), item));
}
public virtual bool IsSupported(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
{
var support = GetSupportDirection(descriptor);
if (support != Coordinates3D.Zero)
{
var supportingBlock = server.BlockRepository.GetBlockProvider(world.GetBlockID(descriptor.Coordinates + support));
if (!supportingBlock.Opaque)
return false;
}
return true;
}
public virtual void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
{
if (!IsSupported(descriptor, server, world))
{
server.SendMessage(ChatColor.Red + "Removing block due to insufficient opaque support blocks");
GenerateDropEntity(descriptor, world, server);
world.SetBlockID(descriptor.Coordinates, 0);
}
}
public virtual void BlockScheduledEvent(BlockDescriptor descriptor, IWorld world, object data)
@ -48,6 +70,25 @@ namespace TrueCraft.Core.Logic
return new[] { new ItemStack(descriptor.ID, 1, descriptor.Metadata) };
}
public virtual void ItemUsedOnEntity(ItemStack item, IEntity usedOn, IWorld world, IRemoteClient user)
{
// This space intentionally left blank
}
public virtual void ItemUsedOnNothing(ItemStack item, IWorld world, IRemoteClient user)
{
// This space intentionally left blank
}
public virtual void ItemUsedOnBlock(Coordinates3D coordinates, ItemStack item, BlockFace face, IWorld world, IRemoteClient user)
{
coordinates += MathHelper.BlockFaceToCoordinates(face);
world.SetBlockID(coordinates, (byte)item.ID);
world.SetMetadata(coordinates, (byte)item.Metadata);
item.Count--;
user.Inventory[user.SelectedSlot] = item;
}
short IItemProvider.ID
{
get
@ -61,6 +102,11 @@ namespace TrueCraft.Core.Logic
/// </summary>
public abstract byte ID { get; }
public virtual Coordinates3D GetSupportDirection(BlockDescriptor descriptor)
{
return Coordinates3D.Zero;
}
/// <summary>
/// The maximum amount that can be in a single stack of this block.
/// </summary>

View File

@ -0,0 +1,26 @@
using System;
namespace TrueCraft.Core.Logic.Blocks
{
public class AirBlock : BlockProvider
{
public static readonly byte BlockID = 0x00;
public override byte ID { get { return 0x00; } }
public override double BlastResistance { get { return 0; } }
public override double Hardness { get { return 100000; } }
public override bool Opaque { get { return false; } }
public override byte Luminance { get { return 0; } }
public override string DisplayName { get { return "Air"; } }
public override Tuple<int, int> GetTextureMap(byte metadata)
{
return new Tuple<int, int>(0, 0);
}
}
}

View File

@ -1,10 +1,30 @@
using System;
using TrueCraft.API.Logic;
using TrueCraft.API;
using TrueCraft.Core.Logic.Items;
using TrueCraft.API.World;
using TrueCraft.API.Server;
namespace TrueCraft.Core.Logic.Blocks
{
public class BedBlock : BlockProvider
{
[Flags]
public enum BedDirection : byte
{
South = 0x0,
West = 0x1,
North = 0x2,
East = 0x3,
}
[Flags]
public enum BedType : byte
{
Foot = 0x0,
Head = 0x8,
}
public static readonly byte BlockID = 0x1A;
public override byte ID { get { return 0x1A; } }
@ -23,5 +43,54 @@ namespace TrueCraft.Core.Logic.Blocks
{
return new Tuple<int, int>(6, 8);
}
protected override ItemStack[] GetDrop(BlockDescriptor descriptor)
{
return new[] { new ItemStack(BedItem.ItemID) };
}
public bool ValidBedPosition(BlockDescriptor descriptor, IBlockRepository repository, IWorld world, bool checkNeighbor = true, bool checkSupport = false)
{
if (checkNeighbor)
{
var other = Coordinates3D.Zero;
switch ((BedDirection)(descriptor.Metadata & 0x3))
{
case BedDirection.East:
other = Coordinates3D.East;
break;
case BedDirection.West:
other = Coordinates3D.West;
break;
case BedDirection.North:
other = Coordinates3D.North;
break;
case BedDirection.South:
other = Coordinates3D.South;
break;
}
if ((descriptor.Metadata & (byte)BedType.Head) == (byte)BedType.Head)
other = -other;
if (world.GetBlockID(descriptor.Coordinates + other) != BedBlock.BlockID)
return false;
}
if (checkSupport)
{
var supportingBlock = repository.GetBlockProvider(world.GetBlockID(descriptor.Coordinates + Coordinates3D.Down));
if (!supportingBlock.Opaque)
return false;
}
return true;
}
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
{
if (!ValidBedPosition(descriptor, server.BlockRepository, world))
{
world.SetBlockID(descriptor.Coordinates, 0);
server.SendMessage(ChatColor.Red + "Removing block due to insufficient neighboring bed blocks");
}
base.BlockUpdate(descriptor, server, world);
}
}
}

View File

@ -17,8 +17,6 @@ namespace TrueCraft.Core.Logic.Blocks
public override byte Luminance { get { return 0; } }
public override bool Opaque { get { return false; } }
public override string DisplayName { get { return "Grass"; } }
public override Tuple<int, int> GetTextureMap(byte metadata)

View File

@ -4,6 +4,9 @@ using System.Linq;
using System.Text;
using TrueCraft.API.Logic;
using TrueCraft.API;
using TrueCraft.API.Entities;
using TrueCraft.API.World;
using TrueCraft.API.Networking;
namespace TrueCraft.Core.Logic
{
@ -14,5 +17,20 @@ namespace TrueCraft.Core.Logic
public virtual sbyte MaximumStack { get { return 64; } }
public virtual string DisplayName { get { return string.Empty; } }
public virtual void ItemUsedOnEntity(ItemStack item, IEntity usedOn, IWorld world, IRemoteClient user)
{
// This space intentionally left blank
}
public virtual void ItemUsedOnBlock(Coordinates3D coordinates, ItemStack item, BlockFace face, IWorld world, IRemoteClient user)
{
// This space intentionally left blank
}
public virtual void ItemUsedOnNothing(ItemStack item, IWorld world, IRemoteClient user)
{
// This space intentionally left blank
}
}
}

View File

@ -1,5 +1,9 @@
using System;
using TrueCraft.API.Logic;
using TrueCraft.API;
using TrueCraft.API.World;
using TrueCraft.API.Networking;
using TrueCraft.Core.Logic.Blocks;
namespace TrueCraft.Core.Logic.Items
{
@ -12,5 +16,48 @@ namespace TrueCraft.Core.Logic.Items
public override sbyte MaximumStack { get { return 1; } }
public override string DisplayName { get { return "Bed"; } }
public override void ItemUsedOnBlock(Coordinates3D coordinates, ItemStack item, BlockFace face, IWorld world, IRemoteClient user)
{
coordinates += MathHelper.BlockFaceToCoordinates(face);
var head = coordinates;
var foot = coordinates;
BedBlock.BedDirection direction = BedBlock.BedDirection.North;
switch (MathHelper.DirectionByRotationFlat(user.Entity.Yaw))
{
case Direction.North:
head += Coordinates3D.North;
direction = BedBlock.BedDirection.North;
break;
case Direction.South:
head += Coordinates3D.South;
direction = BedBlock.BedDirection.South;
break;
case Direction.East:
head += Coordinates3D.East;
direction = BedBlock.BedDirection.East;
break;
case Direction.West:
head += Coordinates3D.West;
direction = BedBlock.BedDirection.West;
break;
}
var bedProvider = (BedBlock)user.Server.BlockRepository.GetBlockProvider(BedBlock.BlockID);
if (!bedProvider.ValidBedPosition(new BlockDescriptor { Coordinates = head },
user.Server.BlockRepository, user.World, false, true) ||
!bedProvider.ValidBedPosition(new BlockDescriptor { Coordinates = foot },
user.Server.BlockRepository, user.World, false, true))
{
return;
}
user.Server.BlockUpdatesEnabled = false;
world.SetBlockData(head, new BlockDescriptor
{ ID = BedBlock.BlockID, Metadata = (byte)((byte)direction | (byte)BedBlock.BedType.Head) });
world.SetBlockData(foot, new BlockDescriptor
{ ID = BedBlock.BlockID, Metadata = (byte)((byte)direction | (byte)BedBlock.BedType.Foot) });
user.Server.BlockUpdatesEnabled = true;
item.Count--;
user.Inventory[user.SelectedSlot] = item;
}
}
}

View File

@ -267,6 +267,7 @@
<Compile Include="Entities\LivingEntity.cs" />
<Compile Include="Entities\ObjectEntity.cs" />
<Compile Include="Entities\PlayerEntity.cs" />
<Compile Include="Logic\Blocks\AirBlock.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Threading;
using TrueCraft.API;
using TrueCraft.API.World;
using TrueCraft.API.Logic;
namespace TrueCraft.Core.World
{
@ -159,6 +160,18 @@ namespace TrueCraft.Core.World
return GetBlockDataFromChunk(adjustedCoordinates, chunk, coordinates);
}
public void SetBlockData(Coordinates3D coordinates, BlockDescriptor descriptor)
{
// TODO: Figure out the best way to handle light in this scenario
IChunk chunk;
var adjustedCoordinates = FindBlockPosition(coordinates, out chunk);
var old = GetBlockDataFromChunk(adjustedCoordinates, chunk, coordinates);
chunk.SetBlockID(adjustedCoordinates, descriptor.ID);
chunk.SetMetadata(adjustedCoordinates,descriptor.Metadata);
if (BlockChanged != null)
BlockChanged(this, new BlockChangeEventArgs(coordinates, old, GetBlockDataFromChunk(adjustedCoordinates, chunk, coordinates)));
}
private BlockDescriptor GetBlockDataFromChunk(Coordinates3D adjustedCoordinates, IChunk chunk, Coordinates3D coordinates)
{
return new BlockDescriptor

View File

@ -6,6 +6,7 @@ using TrueCraft.API;
using TrueCraft.API.World;
using TrueCraft.Core;
using TrueCraft.Core.Windows;
using TrueCraft.API.Logic;
namespace TrueCraft.Handlers
{
@ -43,7 +44,7 @@ namespace TrueCraft.Handlers
if (c.KnownEntities.Contains(client.Entity))
c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None));
}
if (provider != null)
if (provider != null && descriptor.ID != 0)
provider.BlockMined(descriptor, packet.Face, world, client);
break;
}
@ -93,16 +94,17 @@ namespace TrueCraft.Handlers
{
if (use)
{
// Temporary: just place the damn thing
position += MathHelper.BlockFaceToCoordinates(packet.Face);
client.World.SetBlockID(position, (byte)slot.ID);
client.World.SetMetadata(position, (byte)slot.Metadata);
slot.Count--;
client.Inventory[client.SelectedSlot] = slot;
// End temporary
var itemProvider = server.ItemRepository.GetItemProvider(slot.ID);
if (itemProvider == null)
{
server.SendMessage(ChatColor.Red + "WARNING: item provider for ID {0} is null (player placing)", block.Value.ID);
server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates);
server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.Now);
}
if (block != null)
{
// TODO: Use item on block
if (itemProvider != null)
itemProvider.ItemUsedOnBlock(position, slot, packet.Face, client.World, client);
}
else
{

View File

@ -19,16 +19,11 @@ namespace TrueCraft
public IItemProvider GetItemProvider(short id)
{
int max = ItemProviders.Count - 1, min = 0;
while (max >= min)
// TODO: Binary search
for (int i = 0; i < ItemProviders.Count; i++)
{
int mid = (max - min / 2) + min;
if (ItemProviders[mid].ID == id)
return ItemProviders[mid];
else if(ItemProviders[mid].ID < id)
min = mid + 1;
else
max = min - 1;
if (ItemProviders[i].ID == id)
return ItemProviders[i];
}
return null;
}

View File

@ -29,11 +29,35 @@ namespace TrueCraft
public IBlockRepository BlockRepository { get; private set; }
public IItemRepository ItemRepository { get; private set; }
private bool _BlockUpdatesEnabled = true;
private struct BlockUpdate
{
public Coordinates3D Coordinates;
public IWorld World;
}
private Queue<BlockUpdate> PendingBlockUpdates { get; set; }
public bool BlockUpdatesEnabled
{
get
{
return _BlockUpdatesEnabled;
}
set
{
_BlockUpdatesEnabled = value;
if (_BlockUpdatesEnabled)
{
ProcessBlockUpdates();
}
}
}
private Timer EnvironmentWorker;
private Thread NetworkWorker;
private TcpListener Listener;
private readonly PacketHandler[] PacketHandlers;
private IList<ILogProvider> LogProviders;
private object ClientLock = new object();
public MultiplayerServer()
{
@ -53,6 +77,7 @@ namespace TrueCraft
var itemRepository = new ItemRepository();
itemRepository.DiscoverItemProviders();
ItemRepository = itemRepository;
PendingBlockUpdates = new Queue<BlockUpdate>();
reader.RegisterCorePackets();
Handlers.PacketHandlers.RegisterHandlers(this);
@ -93,6 +118,31 @@ namespace TrueCraft
(sbyte)e.NewBlock.ID, (sbyte)e.NewBlock.Metadata));
}
}
PendingBlockUpdates.Enqueue(new BlockUpdate { Coordinates = e.Position, World = sender as IWorld });
ProcessBlockUpdates();
}
private void ProcessBlockUpdates()
{
if (!BlockUpdatesEnabled)
return;
var adjacent = new[]
{
Coordinates3D.Up, Coordinates3D.Down,
Coordinates3D.Left, Coordinates3D.Right,
Coordinates3D.Forwards, Coordinates3D.Backwards
};
while (PendingBlockUpdates.Count != 0)
{
var update = PendingBlockUpdates.Dequeue();
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);
}
}
}
public void AddLogProvider(ILogProvider provider)
@ -164,6 +214,7 @@ namespace TrueCraft
{
var tcpClient = Listener.EndAcceptTcpClient(result);
var client = new RemoteClient(this, tcpClient.GetStream());
lock (ClientLock)
Clients.Add(client);
Listener.BeginAcceptTcpClient(AcceptClient, null);
}
@ -184,11 +235,17 @@ namespace TrueCraft
bool idle = true;
for (int i = 0; i < Clients.Count && i >= 0; i++)
{
Console.WriteLine("Running update " + DateTime.Now);
var client = Clients[i] as RemoteClient;
var sendTimeout = DateTime.Now.AddMilliseconds(50);
while (client.PacketQueue.Count != 0 && DateTime.Now < sendTimeout)
RemoteClient client;
lock (ClientLock)
client = Clients[i] as RemoteClient;
var sendTimeout = DateTime.Now.AddMilliseconds(100);
while (client.PacketQueue.Count != 0)
{
if (DateTime.Now > sendTimeout)
{
Console.WriteLine("Send timeout" + DateTime.Now);
break;
}
idle = false;
try
{
@ -225,9 +282,14 @@ namespace TrueCraft
Clients.RemoveAt(i);
break;
}
var receiveTimeout = DateTime.Now.AddMilliseconds(50);
while (client.DataAvailable && DateTime.Now < receiveTimeout)
var receiveTimeout = DateTime.Now.AddMilliseconds(100);
while (client.DataAvailable)
{
if (DateTime.Now > receiveTimeout)
{
Console.WriteLine("Receive timeout" + DateTime.Now);
break;
}
idle = false;
var packet = PacketReader.ReadPacket(client.MinecraftStream);
LogPacket(packet, true);
@ -268,6 +330,7 @@ namespace TrueCraft
Thread.Sleep(100);
if (client.Disconnected)
{
lock (ClientLock)
Clients.RemoveAt(i);
break;
}