// placeBlock attempts to place a block. This is called by PlayerBlockInteract // in the situation where the player interacts with an attachable block // (potentially in a different chunk to the one where the block gets placed). func (chunk *Chunk) reqPlaceItem(player gamerules.IPlayerClient, target *BlockXyz, slot *gamerules.Slot) { // TODO defer a check for remaining items in slot, and do something with them // (send to player or drop on the ground). // TODO more flexible item checking for block placement (e.g placing seed // items on farmland doesn't fit this current simplistic model). The block // type for the block being placed against should probably contain this logic // (i.e farmland block should know about the seed item). heldBlockType, ok := slot.ItemTypeId.ToBlockId() if !ok || slot.Count < 1 { // Not a placeable item. return } index, subLoc, ok := chunk.getBlockIndexByBlockXyz(target) if !ok { return } // Blocks can only replace certain blocks. blockTypeId := index.BlockId(chunk.blocks) blockType, ok := gamerules.Blocks.Get(blockTypeId) if !ok || !blockType.Replaceable { return } // Safe to replace block. chunk.setBlock(target, subLoc, index, heldBlockType, byte(slot.Data)) slot.Decrement() }
func (player *Player) PacketPlayerBlockHit(status DigStatus, target *BlockXyz, face Face) { player.lock.Lock() defer player.lock.Unlock() // This packet handles 'throwing' an item as well, with status = 4, and // the zero values for target and face, so check for that. if status == DigDropItem && target.IsZero() && face == 0 { blockLoc := player.position.ToBlockXyz() shardClient, _, ok := player.chunkSubs.ShardClientForBlockXyz(blockLoc) if !ok { return } var itemToThrow gamerules.Slot player.inventory.TakeOneHeldItem(&itemToThrow) if !itemToThrow.IsEmpty() { velocity := physics.VelocityFromLook(player.look, 0.50) position := player.position position.Y += player.height shardClient.ReqDropItem(itemToThrow, position, velocity, TicksPerSecond/2) } return } // Validate that the player is actually somewhere near the block. targetAbsPos := target.MidPointToAbsXyz() if !targetAbsPos.IsWithinDistanceOf(&player.position, MaxInteractDistance) { log.Printf("Player/PacketPlayerBlockHit: ignoring player dig at %v (too far away)", target) return } // TODO measure the dig time on the target block and relay to the shard to // stop speed hacking (based on block type and tool used - non-trivial). shardClient, _, ok := player.chunkSubs.ShardClientForBlockXyz(target) if ok { held, _ := player.inventory.HeldItem() shardClient.ReqHitBlock(held, *target, status, face) } }
// Implementing IInventorySubscriber - relays inventory changes to the viewer // of the window. func (iv *inventoryView) SlotUpdate(slot *gamerules.Slot, slotId SlotId) { buf := new(bytes.Buffer) slot.SendUpdate(buf, iv.window.windowId, iv.startSlot+slotId) iv.window.viewer.TransmitPacket(buf.Bytes()) }