diff --git a/TrueCraft.API/Networking/IRemoteClient.cs b/TrueCraft.API/Networking/IRemoteClient.cs index 3e66928..bbd1f5e 100644 --- a/TrueCraft.API/Networking/IRemoteClient.cs +++ b/TrueCraft.API/Networking/IRemoteClient.cs @@ -8,27 +8,86 @@ namespace TrueCraft.API.Networking { public interface IRemoteClient { + /// + /// Minecraft stream used to communicate with this client. + /// IMinecraftStream MinecraftStream { get; } + /// + /// Returns true if this client has data pending in the network stream. + /// bool DataAvailable { get; } + /// + /// The world this client is present in. + /// IWorld World { get; } + /// + /// The entity associated with this client. + /// IEntity Entity { get; } + /// + /// This client's inventory. + /// IWindow Inventory { get; } + /// + /// The username of the connected client. May be null if not yet ascertained. + /// string Username { get; } + /// + /// The slot index this user has selected in their hotbar. + /// short SelectedSlot { get; } + /// + /// The item stack at the slot the user has selected in their hotbar. + /// ItemStack SelectedItem { get; } + /// + /// The server this user is playing on. + /// IMultiplayerServer Server { get; } + /// + /// If true, this client will be sent logging information as chat messages. + /// bool EnableLogging { get; set; } - + /// + /// 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. + /// + DateTime ExpectedDigComplete { get; set; } + /// + /// True if this client has been disconnected. You should cease sending packets and + /// so on, this client is just waiting to be reaped. + /// bool Disconnected { get; } + /// + /// Loads player data from disk for this client. + /// bool Load(); + /// + /// Saves player data to disk for this client. + /// void Save(); + /// + /// Queues a packet to be sent to this client. + /// void QueuePacket(IPacket packet); - + /// + /// Disconnects this client from the server. + /// void Disconnect(); - + /// + /// Sends a chat message to this client. + /// void SendMessage(string message); + /// + /// If logging is enabled, sends your message to the client as chat. + /// void Log(string message, params object[] parameters); + /// + /// Opens a window on the client. This sends the appropriate packets and tracks + /// this window as the currently open window. + /// void OpenWindow(IWindow window); } } \ No newline at end of file diff --git a/TrueCraft.Core/Logic/BlockProvider.cs b/TrueCraft.Core/Logic/BlockProvider.cs index 7900713..b0f8fd9 100644 --- a/TrueCraft.Core/Logic/BlockProvider.cs +++ b/TrueCraft.Core/Logic/BlockProvider.cs @@ -9,6 +9,7 @@ using TrueCraft.API.Server; using TrueCraft.Core.Logic.Blocks; using System.Linq; using fNbt; +using TrueCraft.Core.Logic.Items; namespace TrueCraft.Core.Logic { @@ -242,5 +243,91 @@ namespace TrueCraft.Core.Logic return new BoundingBox(Vector3.Zero, Vector3.One); } } + + /// + /// Gets the time required to mine the given block with the given item. + /// + /// The harvest time in milliseconds. + /// Block identifier. + /// Item identifier. + /// Damage sustained by the item. + 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); + } } } \ No newline at end of file diff --git a/TrueCraft.Core/Logic/Blocks/TallGrassBlock.cs b/TrueCraft.Core/Logic/Blocks/TallGrassBlock.cs index 3597095..ddc66e2 100644 --- a/TrueCraft.Core/Logic/Blocks/TallGrassBlock.cs +++ b/TrueCraft.Core/Logic/Blocks/TallGrassBlock.cs @@ -30,6 +30,11 @@ namespace TrueCraft.Core.Logic.Blocks public override BoundingBox? BoundingBox { get { return null; } } + public override Coordinates3D GetSupportDirection(BlockDescriptor descriptor) + { + return Coordinates3D.Down; + } + public override Tuple GetTextureMap(byte metadata) { return new Tuple(7, 2); diff --git a/TrueCraft.Core/Logic/Items/ToolItem.cs b/TrueCraft.Core/Logic/Items/ToolItem.cs index f7b9b0a..22aed23 100644 --- a/TrueCraft.Core/Logic/Items/ToolItem.cs +++ b/TrueCraft.Core/Logic/Items/ToolItem.cs @@ -15,5 +15,27 @@ namespace TrueCraft.Core.Logic public virtual short BaseDurability { get { return 0; } } 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; + } + } + } } } \ No newline at end of file diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj index 672b76c..0dc194a 100644 --- a/TrueCraft.Core/TrueCraft.Core.csproj +++ b/TrueCraft.Core/TrueCraft.Core.csproj @@ -36,7 +36,7 @@ ..\lib\Ionic.Zip.Reduced.dll - ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll diff --git a/TrueCraft.Core/packages.config b/TrueCraft.Core/packages.config index 27ac881..284ec0a 100644 --- a/TrueCraft.Core/packages.config +++ b/TrueCraft.Core/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/TrueCraft.Launcher/TrueCraft.Launcher.csproj b/TrueCraft.Launcher/TrueCraft.Launcher.csproj index a1c8fc9..8f1eb47 100644 --- a/TrueCraft.Launcher/TrueCraft.Launcher.csproj +++ b/TrueCraft.Launcher/TrueCraft.Launcher.csproj @@ -34,12 +34,12 @@ ..\lib\Xwt.dll - - ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll - ..\lib\Ionic.Zip.Reduced.dll + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + @@ -96,10 +96,10 @@ PreserveNewest - PreserveNewest + diff --git a/TrueCraft.Launcher/packages.config b/TrueCraft.Launcher/packages.config index 27ac881..284ec0a 100644 --- a/TrueCraft.Launcher/packages.config +++ b/TrueCraft.Launcher/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/TrueCraft/Handlers/InteractionHandlers.cs b/TrueCraft/Handlers/InteractionHandlers.cs index 17f955c..d972741 100644 --- a/TrueCraft/Handlers/InteractionHandlers.cs +++ b/TrueCraft/Handlers/InteractionHandlers.cs @@ -12,6 +12,7 @@ using fNbt; using TrueCraft.Core.Logic.Blocks; using System.Linq; using TrueCraft.Core.Logic.Items; +using TrueCraft.Core.Logic; namespace TrueCraft.Handlers { @@ -25,6 +26,8 @@ namespace TrueCraft.Handlers var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var descriptor = world.GetBlockData(position); var provider = server.BlockRepository.GetBlockProvider(descriptor.ID); + short damage; + int time; switch (packet.PlayerAction) { 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 // for hardness == 0 blocks. - if (provider != null && provider.Hardness == 0 - || (client.SelectedItem.ID == ShearsItem.ItemID && provider is LeavesBlock)) + time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage); + if (time <= 20) + { provider.BlockMined(descriptor, packet.Face, world, client); + break; + } + client.ExpectedDigComplete = DateTime.UtcNow.AddMilliseconds(time); break; case PlayerDiggingPacket.Action.StopDigging: foreach (var nearbyClient in server.Clients) @@ -76,7 +83,29 @@ namespace TrueCraft.Handlers c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } 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; } } diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs index e467800..15970aa 100644 --- a/TrueCraft/RemoteClient.cs +++ b/TrueCraft/RemoteClient.cs @@ -70,6 +70,7 @@ namespace TrueCraft public IWindow CurrentWindow { get; internal set; } public bool EnableLogging { get; set; } public IPacket LastSuccessfulPacket { get; set; } + public DateTime ExpectedDigComplete { get; set; } public Socket Connection { get; private set; }