Enforce digging time and damage items when used

Closes #11

Partially addresses #12
This commit is contained in:
Drew DeVault 2015-07-01 15:22:27 -06:00
parent a77822950b
commit fa8c4a6877
10 changed files with 216 additions and 13 deletions

View File

@ -8,27 +8,86 @@ namespace TrueCraft.API.Networking
{ {
public interface IRemoteClient public interface IRemoteClient
{ {
/// <summary>
/// Minecraft stream used to communicate with this client.
/// </summary>
IMinecraftStream MinecraftStream { get; } IMinecraftStream MinecraftStream { get; }
/// <summary>
/// Returns true if this client has data pending in the network stream.
/// </summary>
bool DataAvailable { get; } bool DataAvailable { get; }
/// <summary>
/// The world this client is present in.
/// </summary>
IWorld World { get; } IWorld World { get; }
/// <summary>
/// The entity associated with this client.
/// </summary>
IEntity Entity { get; } IEntity Entity { get; }
/// <summary>
/// This client's inventory.
/// </summary>
IWindow Inventory { get; } IWindow Inventory { get; }
/// <summary>
/// The username of the connected client. May be null if not yet ascertained.
/// </summary>
string Username { get; } string Username { get; }
/// <summary>
/// The slot index this user has selected in their hotbar.
/// </summary>
short SelectedSlot { get; } short SelectedSlot { get; }
/// <summary>
/// The item stack at the slot the user has selected in their hotbar.
/// </summary>
ItemStack SelectedItem { get; } ItemStack SelectedItem { get; }
/// <summary>
/// The server this user is playing on.
/// </summary>
IMultiplayerServer Server { get; } IMultiplayerServer Server { get; }
/// <summary>
/// If true, this client will be sent logging information as chat messages.
/// </summary>
bool EnableLogging { get; set; } bool EnableLogging { get; set; }
/// <summary>
/// The time the user is expected to complete the active digging operation,
/// depending on what kind of block they are mining and what tool they're using
/// to do it with.
/// </summary>
DateTime ExpectedDigComplete { get; set; }
/// <summary>
/// True if this client has been disconnected. You should cease sending packets and
/// so on, this client is just waiting to be reaped.
/// </summary>
bool Disconnected { get; } bool Disconnected { get; }
/// <summary>
/// Loads player data from disk for this client.
/// </summary>
bool Load(); bool Load();
/// <summary>
/// Saves player data to disk for this client.
/// </summary>
void Save(); void Save();
/// <summary>
/// Queues a packet to be sent to this client.
/// </summary>
void QueuePacket(IPacket packet); void QueuePacket(IPacket packet);
/// <summary>
/// Disconnects this client from the server.
/// </summary>
void Disconnect(); void Disconnect();
/// <summary>
/// Sends a chat message to this client.
/// </summary>
void SendMessage(string message); void SendMessage(string message);
/// <summary>
/// If logging is enabled, sends your message to the client as chat.
/// </summary>
void Log(string message, params object[] parameters); void Log(string message, params object[] parameters);
/// <summary>
/// Opens a window on the client. This sends the appropriate packets and tracks
/// this window as the currently open window.
/// </summary>
void OpenWindow(IWindow window); void OpenWindow(IWindow window);
} }
} }

View File

@ -9,6 +9,7 @@ using TrueCraft.API.Server;
using TrueCraft.Core.Logic.Blocks; using TrueCraft.Core.Logic.Blocks;
using System.Linq; using System.Linq;
using fNbt; using fNbt;
using TrueCraft.Core.Logic.Items;
namespace TrueCraft.Core.Logic namespace TrueCraft.Core.Logic
{ {
@ -242,5 +243,91 @@ namespace TrueCraft.Core.Logic
return new BoundingBox(Vector3.Zero, Vector3.One); return new BoundingBox(Vector3.Zero, Vector3.One);
} }
} }
/// <summary>
/// Gets the time required to mine the given block with the given item.
/// </summary>
/// <returns>The harvest time in milliseconds.</returns>
/// <param name="blockId">Block identifier.</param>
/// <param name="itemId">Item identifier.</param>
/// <param name="damage">Damage sustained by the item.</param>
public static int GetHarvestTime(byte blockId, short itemId, out short damage)
{
// Reference:
// http://minecraft.gamepedia.com/index.php?title=Breaking&oldid=138286
damage = 0;
var block = BlockRepository.GetBlockProvider(blockId);
var item = ItemRepository.GetItemProvider(itemId);
double hardness = block.Hardness;
if (hardness == -1)
return -1;
double time = hardness * 1.5;
var tool = ToolType.None;
var material = ToolMaterial.None;
if (item is ToolItem)
{
var _ = item as ToolItem;
tool = _.ToolType;
material = _.Material;
if ((block.EffectiveTools & tool) == 0 || (block.EffectiveToolMaterials & material) == 0)
{
time *= 3.33; // Add time for ineffective tools
}
if (material != ToolMaterial.None)
{
switch (material)
{
case ToolMaterial.Wood:
time /= 2;
break;
case ToolMaterial.Stone:
time /= 4;
break;
case ToolMaterial.Iron:
time /= 6;
break;
case ToolMaterial.Diamond:
time /= 8;
break;
case ToolMaterial.Gold:
time /= 12;
break;
}
}
damage = 1;
if (tool == ToolType.Shovel || tool == ToolType.Axe || tool == ToolType.Pickaxe)
{
damage = (short)(hardness != 0 ? 1 : 0);
}
else if (tool == ToolType.Sword)
{
damage = (short)(hardness != 0 ? 2 : 0);
time /= 1.5;
if (block is CobwebBlock)
time /= 1.5;
}
else if (tool == ToolType.Hoe)
damage = 0; // What? This doesn't seem right
else if (item is ShearsItem)
{
if (block is WoolBlock)
time /= 5;
else if (block is LeavesBlock || block is CobwebBlock)
time /= 15;
if (block is LeavesBlock || block is CobwebBlock || block is TallGrassBlock)
damage = 1;
else
damage = 0;
}
}
return (int)(time * 1000);
}
} }
} }

View File

@ -30,6 +30,11 @@ namespace TrueCraft.Core.Logic.Blocks
public override BoundingBox? BoundingBox { get { return null; } } public override BoundingBox? BoundingBox { get { return null; } }
public override Coordinates3D GetSupportDirection(BlockDescriptor descriptor)
{
return Coordinates3D.Down;
}
public override Tuple<int, int> GetTextureMap(byte metadata) public override Tuple<int, int> GetTextureMap(byte metadata)
{ {
return new Tuple<int, int>(7, 2); return new Tuple<int, int>(7, 2);

View File

@ -15,5 +15,27 @@ namespace TrueCraft.Core.Logic
public virtual short BaseDurability { get { return 0; } } public virtual short BaseDurability { get { return 0; } }
public override sbyte MaximumStack { get { return 1; } } public override sbyte MaximumStack { get { return 1; } }
public virtual int Uses
{
get
{
switch (Material)
{
case ToolMaterial.Gold:
return 33;
case ToolMaterial.Wood:
return 60;
case ToolMaterial.Stone:
return 132;
case ToolMaterial.Iron:
return 251;
case ToolMaterial.Diamond:
return 1562;
default:
return -1;
}
}
}
} }
} }

View File

@ -36,7 +36,7 @@
<HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath> <HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json"> <Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" /> <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages> </packages>

View File

@ -34,12 +34,12 @@
<Reference Include="Xwt"> <Reference Include="Xwt">
<HintPath>..\lib\Xwt.dll</HintPath> <HintPath>..\lib\Xwt.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ionic.Zip.Reduced"> <Reference Include="Ionic.Zip.Reduced">
<HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath> <HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
@ -96,10 +96,10 @@
<None Include="Xwt.WPF.dll"> <None Include="Xwt.WPF.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="packages.config" />
<None Include="truecraft"> <None Include="truecraft">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Content\truecraft-logo.png" /> <EmbeddedResource Include="Content\truecraft-logo.png" />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" /> <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages> </packages>

View File

@ -12,6 +12,7 @@ using fNbt;
using TrueCraft.Core.Logic.Blocks; using TrueCraft.Core.Logic.Blocks;
using System.Linq; using System.Linq;
using TrueCraft.Core.Logic.Items; using TrueCraft.Core.Logic.Items;
using TrueCraft.Core.Logic;
namespace TrueCraft.Handlers namespace TrueCraft.Handlers
{ {
@ -25,6 +26,8 @@ namespace TrueCraft.Handlers
var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var position = new Coordinates3D(packet.X, packet.Y, packet.Z);
var descriptor = world.GetBlockData(position); var descriptor = world.GetBlockData(position);
var provider = server.BlockRepository.GetBlockProvider(descriptor.ID); var provider = server.BlockRepository.GetBlockProvider(descriptor.ID);
short damage;
int time;
switch (packet.PlayerAction) switch (packet.PlayerAction)
{ {
case PlayerDiggingPacket.Action.DropItem: case PlayerDiggingPacket.Action.DropItem:
@ -64,9 +67,13 @@ namespace TrueCraft.Handlers
// So if you want to blame anyone, send flames to Notch for the stupid idea of not sending "stop digging" packets // So if you want to blame anyone, send flames to Notch for the stupid idea of not sending "stop digging" packets
// for hardness == 0 blocks. // for hardness == 0 blocks.
if (provider != null && provider.Hardness == 0 time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage);
|| (client.SelectedItem.ID == ShearsItem.ItemID && provider is LeavesBlock)) if (time <= 20)
{
provider.BlockMined(descriptor, packet.Face, world, client); provider.BlockMined(descriptor, packet.Face, world, client);
break;
}
client.ExpectedDigComplete = DateTime.UtcNow.AddMilliseconds(time);
break; break;
case PlayerDiggingPacket.Action.StopDigging: case PlayerDiggingPacket.Action.StopDigging:
foreach (var nearbyClient in server.Clients) foreach (var nearbyClient in server.Clients)
@ -76,7 +83,29 @@ namespace TrueCraft.Handlers
c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None));
} }
if (provider != null && descriptor.ID != 0) if (provider != null && descriptor.ID != 0)
provider.BlockMined(descriptor, packet.Face, world, client); {
time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage);
if (time <= 20)
break; // Already handled earlier
var diff = (DateTime.UtcNow - client.ExpectedDigComplete).TotalMilliseconds;
if (diff > -100) // Allow a small tolerance
{
provider.BlockMined(descriptor, packet.Face, world, client);
// Damage the item
if (damage != 0)
{
var tool = server.ItemRepository.GetItemProvider(client.SelectedItem.ID) as ToolItem;
if (tool != null && tool.Uses != -1)
{
var slot = client.SelectedItem;
slot.Metadata += damage;
if (slot.Metadata >= tool.Uses)
slot.Count = 0; // Destroy item
client.Inventory[client.SelectedSlot] = slot;
}
}
}
}
break; break;
} }
} }

View File

@ -70,6 +70,7 @@ namespace TrueCraft
public IWindow CurrentWindow { get; internal set; } public IWindow CurrentWindow { get; internal set; }
public bool EnableLogging { get; set; } public bool EnableLogging { get; set; }
public IPacket LastSuccessfulPacket { get; set; } public IPacket LastSuccessfulPacket { get; set; }
public DateTime ExpectedDigComplete { get; set; }
public Socket Connection { get; private set; } public Socket Connection { get; private set; }