public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet)
        {
            var       packet         = (RightClickPacket)_packet;
            var       slot           = client.Entity.Inventory[client.Entity.SelectedSlot];
            var       position       = new Coordinates3D(packet.X, packet.Y, packet.Z);
            var       cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ);
            BlockInfo?block          = null;

            if (position != -Coordinates3D.One)
            {
                if (position.DistanceTo((Coordinates3D)client.Entity.Position) > client.Reach)
                {
                    return;
                }
                block = client.World.GetBlockInfo(position);
            }
            bool use = true;

            if (block != null)
            {
                use = client.World.RightClickBlock(position, packet.Face, cursorPosition, slot.AsItem());
            }
            if (!slot.Empty)
            {
                var item = slot.AsItem();
                if (use)
                {
                    if (block != null)
                    {
                        client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value);
                        if (item.Value.ItemId < 0x100)
                        {
                            client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId),
                                                                    position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch));
                        }
                        if (client.GameMode != GameMode.Creative)
                        {
                            slot.Count--; // TODO: This is probably a bad place to put this code
                            if (slot.Count == 0)
                            {
                                client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack;
                            }
                            else
                            {
                                client.Entity.Inventory[client.Entity.SelectedSlot] = slot;
                            }
                        }
                    }
                    else
                    {
                        client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value);
                        if (item.Value.ItemId < 0x100)
                        {
                            client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId),
                                                                    position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch));
                        }
                    }
                }
            }
        }
        public static void HandlePlayerBlockPlacementPacket(IPacket _packet, IRemoteClient _client,
                                                            IMultiPlayerServer server)
        {
            var packet = (PlayerBlockPlacementPacket)_packet;
            var client = (RemoteClient)_client;

            var             slot     = client.SelectedItem;
            var             position = new Coordinates3D(packet.X, packet.Y, packet.Z);
            BlockDescriptor?block;

            if (position != -Coordinates3D.One)
            {
                if (position.DistanceTo((Coordinates3D)client.Entity.Position) > 10 /* TODO: Reach */)
                {
                    return;
                }
                block = client.World.GetBlockData(position);
            }
            else
            {
                return;
            }

            var provider = server.BlockRepository.GetBlockProvider(block.Value.Id);

            if (provider == null)
            {
                server.SendMessage($"{ChatColor.Red}WARNING: block 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.UtcNow);
                return;
            }

            if (!provider.BlockRightClicked(block.Value, packet.Face, client.World, client))
            {
                position += MathHelper.BlockFaceToCoordinates(packet.Face);
                var oldId   = client.World.GetBlockId(position);
                var oldMeta = client.World.GetMetadata(position);
                client.QueuePacket(new BlockChangePacket(position.X, (sbyte)position.Y, position.Z, (sbyte)oldId, (sbyte)oldMeta));
                client.QueuePacket(new SetSlotPacket(0, client.SelectedSlot, client.SelectedItem.Id, client.SelectedItem.Count, client.SelectedItem.Metadata));
                return;
            }

            if (!slot.Empty)
            {
                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.UtcNow);
                }

                itemProvider?.ItemUsedOnBlock(position, slot, packet.Face, client.World, client);
            }
        }
Example #3
0
        public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal)
        {
            // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html
            var parents   = new Dictionary <Coordinates3D, Coordinates3D>();
            var costs     = new Dictionary <Coordinates3D, double>();
            var openset   = new PriorityQueue <Coordinates3D>();
            var closedset = new HashSet <Coordinates3D>();

            openset.Enqueue(start, 0);
            parents[start] = start;
            costs[start]   = start.DistanceTo(goal);

            while (openset.Count > 0)
            {
                var current = openset.Dequeue();
                if (current == goal)
                {
                    return(TracePath(start, goal, parents));
                }

                closedset.Add(current);

                foreach (var next in GetNeighbors(world, subject, current))
                {
                    if (closedset.Contains(next))
                    {
                        continue;
                    }
                    var cost = (int)(costs[current] + current.DistanceTo(next));
                    if (!costs.ContainsKey(next) || cost < costs[next])
                    {
                        costs[next] = cost;
                        var priority = cost + next.DistanceTo(goal);
                        openset.Enqueue(next, priority);
                        parents[next] = current;
                    }
                }
            }

            return(null);
        }
Example #4
0
        public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal)
        {
            // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html
            var parents = new Dictionary<Coordinates3D, Coordinates3D>();
            var costs = new Dictionary<Coordinates3D, double>();
            var openset = new PriorityQueue<Coordinates3D>();
            var closedset = new HashSet<Coordinates3D>();

            openset.Enqueue(start, 0);
            parents[start] = start;
            costs[start] = start.DistanceTo(goal);

            while (openset.Count > 0)
            {
                var current = openset.Dequeue();
                if (current == goal)
                    return TracePath(start, goal, parents);

                closedset.Add(current);

                foreach (var next in GetNeighbors(world, subject, current))
                {
                    if (closedset.Contains(next))
                        continue;
                    var cost = (int)(costs[current] + current.DistanceTo(next));
                    if (!costs.ContainsKey(next) || cost < costs[next])
                    {
                        costs[next] = cost;
                        var priority = cost + next.DistanceTo(goal);
                        openset.Enqueue(next, priority);
                        parents[next] = current;
                    }
                }
            }

            return null;
        }
Example #5
0
        public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal)
        {
            // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html

            var parents = new Dictionary<Coordinates3D, Coordinates3D>();
            var costs = new Dictionary<Coordinates3D, double>();
            var openset = new PriorityQueue<Coordinates3D>();
            var closedset = new HashSet<Coordinates3D>();

            openset.Enqueue(start, 0);
            parents[start] = start;
            costs[start] = start.DistanceTo(goal);

            while (openset.Count > 0)
            {
                var current = openset.Dequeue();
                if (current == goal)
                    return TracePath(start, goal, parents);

                closedset.Add(current);

                // Test directly adjacent voxels
                for (int i = 0; i < Neighbors.Length; i++)
                {
                    var next = Neighbors[i] + current;
                    if (closedset.Contains(next))
                        continue;

                    if (!CanOccupyVoxel(world, subject, next))
                        continue;

                    var cost = (int)(costs[current] + current.DistanceTo(next));
                    if (!costs.ContainsKey(next) || cost < costs[next])
                    {
                        costs[next] = cost;
                        var priority = cost + next.DistanceTo(goal);
                        openset.Enqueue(next, priority);
                        parents[next] = current;
                    }
                }

                // Test diagonally
                for (int i = 0; i < DiagonalNeighbors.Length; i++)
                {
                    var pair = DiagonalNeighbors[i];
                    var next = pair[0] + pair[1] + current;
                    if (closedset.Contains(next))
                        continue;

                    if (!CanOccupyVoxel(world, subject, next))
                        continue;
                    if (!CanOccupyVoxel(world, subject, pair[0] + current))
                        continue;
                    if (!CanOccupyVoxel(world, subject, pair[1] + current))
                        continue;

                    var cost = (int)(costs[current] + current.DistanceTo(next));
                    if (!costs.ContainsKey(next) || cost < costs[next])
                    {
                        costs[next] = cost;
                        var priority = cost + next.DistanceTo(goal);
                        openset.Enqueue(next, priority);
                        parents[next] = current;
                    }
                }
            }

            return null;
        }
Example #6
0
        /// <summary>
        /// Produces a list of outward flow targets that this block may flow towards.
        /// </summary>
        protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords)
        {
            // The maximum distance we will search for lower ground to flow towards
            const int dropCheckDistance = 5;

            var outwardFlow = new List<LiquidFlow>(5);

            var currentLevel = world.GetMetadata(coords);
            var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));
            if (blockBelow.Hardness == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID)
            {
                outwardFlow.Add(new LiquidFlow(coords + Coordinates3D.Down, 1));
                if (currentLevel != 0)
                    return outwardFlow.ToArray();
            }

            if (currentLevel < MaximumFluidDepletion)
            {
                // This code is responsible for seeking out candidates for flowing towards.
                // Fluid in Minecraft will flow in the direction of the nearest drop-off where
                // there is at least one block removed on the Y axis.
                // It will flow towards several equally strong candidates at once.

                var candidateFlowPoints = new List<Coordinates3D>(4);
                var furthestPossibleCandidate = new Coordinates3D(x: dropCheckDistance + 1, z: dropCheckDistance + 1) + Coordinates3D.Down;

                var nearestCandidate = furthestPossibleCandidate;
                for (int x = -dropCheckDistance; x < dropCheckDistance; x++)
                {
                    for (int z = -dropCheckDistance; z < dropCheckDistance; z++)
                    {
                        if (Math.Abs(z) + Math.Abs(x) > dropCheckDistance)
                            continue;
                        var check = new Coordinates3D(x: x, z: z) + Coordinates3D.Down;
                        var c = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords));
                        if (c.Hardness == 0)
                        {
                            if (!LineOfSight(world, check + coords, coords))
                                continue;
                            if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearestCandidate + coords))
                                candidateFlowPoints.Add(check);
                            if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearestCandidate + coords))
                            {
                                candidateFlowPoints.Clear();
                                nearestCandidate = check;
                            }
                        }
                    }
                }
                if (nearestCandidate == furthestPossibleCandidate)
                {
                    candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: dropCheckDistance + 1) + Coordinates3D.Down);
                    candidateFlowPoints.Add(new Coordinates3D(x: dropCheckDistance + 1, z: -dropCheckDistance - 1) + Coordinates3D.Down);
                    candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: -dropCheckDistance - 1) + Coordinates3D.Down);
                }
                candidateFlowPoints.Add(nearestCandidate);

                // For each candidate, determine if we are actually capable of flowing towards it.
                // We are able to flow through blocks with a hardness of zero, but no others. We are
                // not able to flow through established fluid blocks.
                for (int i = 0; i < candidateFlowPoints.Count; i++)
                {
                    var location = candidateFlowPoints[i];
                    location.Clamp(1);

                    var xCoordinateCheck = new Coordinates3D(x: location.X) + coords;
                    var zCoordinateCheck = new Coordinates3D(z: location.Z) + coords;

                    var xID = world.BlockRepository.GetBlockProvider(world.GetBlockID(xCoordinateCheck));
                    var zID = world.BlockRepository.GetBlockProvider(world.GetBlockID(zCoordinateCheck));

                    if (xID.Hardness == 0 && xID.ID != FlowingID && xID.ID != StillID)
                    {
                        if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck))
                            outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1)));
                    }

                    if (zID.Hardness == 0 && zID.ID != FlowingID && zID.ID != StillID)
                    {
                        if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck))
                            outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1)));
                    }
                }

                // Occasionally, there are scenarios where the nearest candidate hole is not acceptable, but
                // there is space immediately next to the block. We should fill that space.
                if (outwardFlow.Count == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID)
                {
                    for (int i = 0; i < Neighbors.Length; i++)
                    {
                        var b = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Neighbors[i]));
                        if (b.Hardness == 0 && b.ID != StillID && b.ID != FlowingID)
                            outwardFlow.Add(new LiquidFlow(Neighbors[i] + coords, (byte)(currentLevel + 1)));
                    }
                }
            }
            return outwardFlow.ToArray();
        }
Example #7
0
        /// <summary>
        /// Produces a list of outward flow targets that this block may flow towards.
        /// </summary>
        protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords)
        {
            // The maximum distance we will search for lower ground to flow towards
            const int dropCheckDistance = 5;

            var outwardFlow = new List <LiquidFlow>(5);

            var currentLevel = world.GetMetadata(coords);
            var blockBelow   = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down));

            if (blockBelow.Hardness == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID)
            {
                outwardFlow.Add(new LiquidFlow(coords + Coordinates3D.Down, 1));
                if (currentLevel != 0)
                {
                    return(outwardFlow.ToArray());
                }
            }

            if (currentLevel < MaximumFluidDepletion)
            {
                // This code is responsible for seeking out candidates for flowing towards.
                // Fluid in Minecraft will flow in the direction of the nearest drop-off where
                // there is at least one block removed on the Y axis.
                // It will flow towards several equally strong candidates at once.

                var candidateFlowPoints       = new List <Coordinates3D>(4);
                var furthestPossibleCandidate = new Coordinates3D(x: dropCheckDistance + 1, z: dropCheckDistance + 1) + Coordinates3D.Down;

                var nearestCandidate = furthestPossibleCandidate;
                for (int x = -dropCheckDistance; x < dropCheckDistance; x++)
                {
                    for (int z = -dropCheckDistance; z < dropCheckDistance; z++)
                    {
                        if (Math.Abs(z) + Math.Abs(x) > dropCheckDistance)
                        {
                            continue;
                        }
                        var check = new Coordinates3D(x: x, z: z) + Coordinates3D.Down;
                        var c     = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords));
                        if (c.Hardness == 0)
                        {
                            if (!LineOfSight(world, check + coords, coords))
                            {
                                continue;
                            }
                            if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearestCandidate + coords))
                            {
                                candidateFlowPoints.Add(check);
                            }
                            if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearestCandidate + coords))
                            {
                                candidateFlowPoints.Clear();
                                nearestCandidate = check;
                            }
                        }
                    }
                }
                if (nearestCandidate == furthestPossibleCandidate)
                {
                    candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: dropCheckDistance + 1) + Coordinates3D.Down);
                    candidateFlowPoints.Add(new Coordinates3D(x: dropCheckDistance + 1, z: -dropCheckDistance - 1) + Coordinates3D.Down);
                    candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: -dropCheckDistance - 1) + Coordinates3D.Down);
                }
                candidateFlowPoints.Add(nearestCandidate);

                // For each candidate, determine if we are actually capable of flowing towards it.
                // We are able to flow through blocks with a hardness of zero, but no others. We are
                // not able to flow through established fluid blocks.
                for (int i = 0; i < candidateFlowPoints.Count; i++)
                {
                    var location = candidateFlowPoints[i];
                    location.Clamp(1);

                    var xCoordinateCheck = new Coordinates3D(x: location.X) + coords;
                    var zCoordinateCheck = new Coordinates3D(z: location.Z) + coords;

                    var xID = world.BlockRepository.GetBlockProvider(world.GetBlockID(xCoordinateCheck));
                    var zID = world.BlockRepository.GetBlockProvider(world.GetBlockID(zCoordinateCheck));

                    if (xID.Hardness == 0 && xID.ID != FlowingID && xID.ID != StillID)
                    {
                        if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck))
                        {
                            outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1)));
                        }
                    }

                    if (zID.Hardness == 0 && zID.ID != FlowingID && zID.ID != StillID)
                    {
                        if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck))
                        {
                            outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1)));
                        }
                    }
                }

                // Occasionally, there are scenarios where the nearest candidate hole is not acceptable, but
                // there is space immediately next to the block. We should fill that space.
                if (outwardFlow.Count == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID)
                {
                    for (int i = 0; i < Neighbors.Length; i++)
                    {
                        var b = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Neighbors[i]));
                        if (b.Hardness == 0 && b.ID != StillID && b.ID != FlowingID)
                        {
                            outwardFlow.Add(new LiquidFlow(Neighbors[i] + coords, (byte)(currentLevel + 1)));
                        }
                    }
                }
            }
            return(outwardFlow.ToArray());
        }
 public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet)
 {
     var packet = (RightClickPacket)_packet;
     var slot = client.Entity.Inventory[client.Entity.SelectedSlot];
     var position = new Coordinates3D(packet.X, packet.Y, packet.Z);
     var cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ);
     BlockDescriptor? block = null;
     if (position != -Coordinates3D.One)
     {
         if (position.DistanceTo(client.Entity.Position) > client.Reach)
             return;
         block = client.World.GetBlock(position);
     }
     bool use = true;
     if (block != null)
         use = Block.OnBlockRightClicked(block.Value, client.World, position, AdjustByDirection(packet.Direction), cursorPosition);
     if (!slot.Empty)
     {
         var item = new ItemDescriptor(slot.Id, slot.Metadata);
         if (use)
         {
             if (block != null)
             {
                 Item.OnItemUsedOnBlock(item, client.World, position, AdjustByDirection(packet.Direction), cursorPosition);
                 if (client.GameMode != GameMode.Creative)
                 {
                     slot.Count--; // TODO: This is probably a bad place to put this code
                     if (slot.Count == 0)
                         client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack;
                     else
                         client.Entity.Inventory[client.Entity.SelectedSlot] = slot;
                 }
             }
             else
                 Item.OnItemUsed(item);
         }
     }
 }
        public static void HandlePlayerBlockPlacementPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server)
        {
            var packet = (PlayerBlockPlacementPacket)_packet;
            var client = (RemoteClient)_client;

            var slot = client.SelectedItem;
            var position = new Coordinates3D(packet.X, packet.Y, packet.Z);
            BlockDescriptor? block = null;
            if (position != -Coordinates3D.One)
            {
                if (position.DistanceTo((Coordinates3D)client.Entity.Position) > 10 /* TODO: Reach */)
                    return;
                block = client.World.GetBlockData(position);
            }
            else
            {
                // TODO: Handle situations like firing arrows and such? Is that how it works?
                return;
            }
            bool use = true;
            if (block != null)
            {
                var provider = server.BlockRepository.GetBlockProvider(block.Value.ID);
                if (provider == null)
                {
                    server.SendMessage(ChatColor.Red + "WARNING: block 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);
                    return;
                }
                if (!provider.BlockRightClicked(block.Value, packet.Face, client.World, client))
                {
                    position += MathHelper.BlockFaceToCoordinates(packet.Face);
                    var oldID = client.World.GetBlockID(position);
                    var oldMeta = client.World.GetMetadata(position);
                    client.QueuePacket(new BlockChangePacket(position.X, (sbyte)position.Y, position.Z, (sbyte)oldID, (sbyte)oldMeta));
                    client.QueuePacket(new SetSlotPacket(0, client.SelectedSlot, client.SelectedItem.ID, client.SelectedItem.Count, client.SelectedItem.Metadata));
                    return;
                }
            }
            if (!slot.Empty)
            {
                if (use)
                {
                    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)
                    {
                        if (itemProvider != null)
                            itemProvider.ItemUsedOnBlock(position, slot, packet.Face, client.World, client);
                    }
                    else
                    {
                        // TODO: Use item
                    }
                }
            }
        }
Example #10
0
 public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet)
 {
     var packet = (RightClickPacket)_packet;
     var slot = client.Entity.Inventory[client.Entity.SelectedSlot];
     var position = new Coordinates3D(packet.X, packet.Y, packet.Z);
     var cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ);
     BlockInfo? block = null;
     if (position != -Coordinates3D.One)
     {
         if (position.DistanceTo((Coordinates3D)client.Entity.Position) > client.Reach)
             return;
         block = client.World.GetBlockInfo(position);
     }
     bool use = true;
     if (block != null)
         use = client.World.RightClickBlock(position, packet.Face, cursorPosition, slot.AsItem());
     if (!slot.Empty)
     {
         var item = slot.AsItem();
         if (use)
         {
             if (block != null)
             {
                 client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value);
                 if (item.Value.ItemId < 0x100)
                 {
                     client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId),
                         position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch));
                 }
                 if (client.GameMode != GameMode.Creative)
                 {
                     slot.Count--; // TODO: This is probably a bad place to put this code
                     if (slot.Count == 0)
                         client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack;
                     else
                         client.Entity.Inventory[client.Entity.SelectedSlot] = slot;
                 }
             }
             else
             {
                 client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value);
                 if (item.Value.ItemId < 0x100)
                 {
                     client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId),
                         position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch));
                 }
             }
         }
     }
 }