001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package net.tridentsdk.server.packets.play.in;
019
020import io.netty.buffer.ByteBuf;
021import net.tridentsdk.base.Block;
022import net.tridentsdk.base.Position;
023import net.tridentsdk.base.Substance;
024import net.tridentsdk.effect.sound.SoundEffect;
025import net.tridentsdk.effect.sound.SoundEffectType;
026import net.tridentsdk.meta.block.ChestMeta;
027import net.tridentsdk.meta.block.FurnaceMeta;
028import net.tridentsdk.meta.component.MetaFactory;
029import net.tridentsdk.server.data.block.FurnaceMetaImpl;
030import net.tridentsdk.server.inventory.TridentInventory;
031import net.tridentsdk.server.netty.ClientConnection;
032import net.tridentsdk.server.netty.packet.InPacket;
033import net.tridentsdk.server.netty.packet.Packet;
034import net.tridentsdk.server.player.PlayerConnection;
035import net.tridentsdk.server.player.TridentPlayer;
036import net.tridentsdk.server.util.OwnedTridentBlock;
037import net.tridentsdk.util.Value;
038import net.tridentsdk.util.Vector;
039
040import static net.tridentsdk.meta.block.ByteArray.writeFirst;
041import static net.tridentsdk.meta.block.ByteArray.writeSecond;
042
043public class PacketPlayInBlockPlace extends InPacket {
044    /**
045     * Location of the block being placed
046     */
047    protected Position location;
048    protected byte direction;
049    /**
050     * PositionWritable of the cursor, incorrect use of a Vector
051     */
052    protected Vector cursorPosition;
053
054    @Override
055    public int id() {
056        return 0x1C;
057    }
058
059    @Override
060    public Packet decode(ByteBuf buf) {
061        long encodedLocation = buf.readLong();
062
063        this.location = Position.create(null, (double) (encodedLocation >> 38),
064                (double) ((encodedLocation >> 26) & 0xFFF), (double) (encodedLocation << 38 >> 38));
065        this.direction = buf.readByte();
066
067        // ignore held item
068        for (int i = 0; i < buf.readableBytes() - 3; i++) {
069            buf.readByte();
070        }
071
072        double x = (double) buf.readByte();
073        double y = (double) buf.readByte();
074        double z = (double) buf.readByte();
075
076        this.cursorPosition = new Vector(x, y, z);
077        return this;
078    }
079
080    public Position location() {
081        return this.location;
082    }
083
084    public byte blockDirection() {
085        return this.direction;
086    }
087
088    public Vector cursorPosition() {
089        return this.cursorPosition;
090    }
091
092    @Override
093    public void handleReceived(ClientConnection connection) {
094        TridentPlayer player = ((PlayerConnection) connection).player();
095        location.setWorld(player.world());
096
097        if(player.heldItem() == null) {
098            //TODO: add a check where this is called from so it will not be null while a player interacts with the item slots containing air while placing blocks.
099            return;
100        }
101        
102        Substance substance = player.heldItem().type();
103        Vector vector = determineOffset();
104        if (!substance.isBlock()) {
105            // TODO eat food or pull bow or release/obtain water in a bucket, etc
106            return;
107        }
108
109        if (location.y() + vector.y() > 255 || location.y() + vector.y() < 0) {
110            // Illegal block position
111            return;
112        }
113
114        Position position = location.block().substance().canBeReplaced() ? location : location.relative(vector);
115        Block block = new OwnedTridentBlock(player, position.block());
116
117        if (location.y() < 255 && location.block() != null && block.substance().isFunctional() && !player.isCrouching()) {
118            switch (block.substance()) {
119                case FURNACE:
120                case BURNING_FURNACE:
121                    ((FurnaceMetaImpl) block.obtainMeta(FurnaceMeta.class)).furnaceInventory().sendTo(player);
122                    break;
123                case CHEST:
124                    ((TridentInventory) block.obtainMeta(ChestMeta.class).inventory()).sendTo(player);
125                    break;
126            }
127            // TODO Add all functional blocks (workbench, furnace, anvil, etc)
128        } else if (player.heldItem() != null && player.heldItem().type() != Substance.AIR) {
129            short yaw = (short) (player.position().yaw() * 10);
130            short meta = player.heldItem().damageValue();
131            Value<Byte> result = Value.of((byte) 0);
132            Value<Substance> substanceValue = Value.of(substance);
133
134            boolean allow = MetaFactory.decode(block, substanceValue, new byte[]{
135                    writeFirst(yaw), writeSecond(yaw), direction,
136                    ((byte) cursorPosition.x()), ((byte) cursorPosition.y()), ((byte) cursorPosition.z()),
137                    writeFirst(meta), writeSecond(meta)
138            }, result);
139
140            if (allow) {
141                block.setSubstanceAndMeta(substanceValue.get(), result.get());
142
143                SoundEffectType soundEffectType = substanceValue.get().placeSound();
144                if (soundEffectType != null) {
145                    SoundEffect sound = location.world().playSound(soundEffectType);
146                    sound.setPosition(location);
147                    sound.apply();
148                }
149            }
150        }
151    }
152
153    private Vector determineOffset() {
154        int x = 0;
155        int y = 0;
156        int z = 0;
157
158        switch (blockDirection()) {
159            case 0:
160                y--;
161                break;
162            case 1:
163                y++;
164                break;
165            case 2:
166                z--;
167                break;
168            case 3:
169                z++;
170                break;
171            case 4:
172                x--;
173                break;
174            case 5:
175                x++;
176                break;
177            default:
178                return new Vector(0, 0, 0);
179        }
180
181        return new Vector(x, y, z);
182    }
183}