private void BeginDigging(Coordinates3D target) { // TODO: Adjust digging time to compensate for latency var block = Game.Client.World.GetBlockId(target); Game.TargetBlock = target; Game.StartDigging = DateTime.UtcNow; Game.EndDigging = Game.StartDigging.AddMilliseconds( BlockProvider.GetHarvestTime(block, Game.Client.Inventory.Hotbar[Game.Client.HotBarSelection].Id, out _)); Game.Client.QueuePacket(new PlayerDiggingPacket( PlayerDiggingPacket.Action.StartDigging, Game.TargetBlock.X, (sbyte)Game.TargetBlock.Y, Game.TargetBlock.Z, Game.HighlightedBlockFace)); NextAnimation = DateTime.UtcNow.AddSeconds(0.25); }
public static void HandlePlayerDiggingPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerDiggingPacket)_packet; var client = (RemoteClient)_client; var world = _client.World; 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: // Throwing item if (client.SelectedItem.Empty) { break; } var spawned = client.SelectedItem; spawned.Count = 1; var inventory = client.SelectedItem; inventory.Count--; var item = new ItemEntity(client.Entity.Position + new Vector3(0, PlayerEntity.Height, 0), spawned); item.Velocity = MathHelper.FowardVector(client.Entity.Yaw) * 0.3; client.Inventory[client.SelectedSlot] = inventory; server.GetEntityManagerForWorld(client.World).SpawnEntity(item); break; case PlayerDiggingPacket.Action.StartDigging: foreach (var nearbyClient in server.Clients) // TODO: Send this repeatedly during the course of the digging { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.SwingArm)); } } if (provider == null) { server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player digging)", descriptor.ID); } else { provider.BlockLeftClicked(descriptor, packet.Face, world, client); } // "But why on Earth does this behavior change if you use shears on leaves?" // "This is poor seperation of concerns" // "Let me do a git blame and flame whoever wrote the next line" // To answer all of those questions, here: // Minecraft sends a player digging packet when the player starts and stops digging a block (two packets) // However, it only sends ONE packet if the block would be mined immediately - which usually is only the case // for blocks that have a hardness equal to zero. // The exception to this rule is shears on leaves. Leaves normally have a hardness of 0.2, but when you mine them // using shears the client only sends the start digging packet and expects them to be mined immediately. // 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. 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) { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } } if (provider != null && descriptor.ID != 0) { 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; } }