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.world;
019
020import com.google.common.collect.Lists;
021import net.tridentsdk.base.Block;
022import net.tridentsdk.base.Position;
023import net.tridentsdk.base.Substance;
024import net.tridentsdk.docs.InternalUseOnly;
025import net.tridentsdk.meta.block.AbstractBlockMetaOwner;
026import net.tridentsdk.meta.block.BlockMeta;
027import net.tridentsdk.meta.component.MetaCollection;
028import net.tridentsdk.meta.component.MetaFactory;
029import net.tridentsdk.server.packets.play.out.PacketPlayOutBlockChange;
030import net.tridentsdk.server.player.TridentPlayer;
031import net.tridentsdk.util.Vector;
032
033import java.util.Collections;
034import java.util.List;
035
036public class TridentBlock extends AbstractBlockMetaOwner<Block> implements Block {
037    private final Position location;
038    /**
039     * The type for this block
040     */
041    protected volatile Substance material;
042    /**
043     * The block metadata
044     */
045    protected byte data;
046
047    /**
048     * Constructs the wrapper representing the block
049     *
050     * @param location Location of the Block
051     */
052    @InternalUseOnly
053    public TridentBlock(Position location) {
054        this.location = location;
055
056        // Note: Avoid recursion by not creating a new instance from World#blockAt(Location)
057        Block worldBlock = location.world().blockAt(location);
058        this.material = worldBlock.substance();
059    }
060
061    public TridentBlock(Position location, Substance substance, byte meta) {
062        this.location = location;
063        this.material = substance;
064        this.data = meta;
065    }
066
067    @Override
068    public Substance substance() {
069        return this.material;
070    }
071
072    @Override
073    public void setSubstance(Substance substance) {
074        setSubstanceAndMeta(substance, (byte) 0);
075    }
076
077    @Override
078    public Position position() {
079        return this.location;
080    }
081
082    @Override
083    public byte meta() {
084        return this.data;
085    }
086
087    @Override
088    public void setMeta(byte data) {
089        setSubstanceAndMeta(this.material, data);
090    }
091
092    @Override
093    public Block relativeBlock(Vector vector) {
094        return new TridentBlock(this.location.relative(vector));
095    }
096
097    @Override
098    public void setSubstanceAndMeta(Substance substance, byte data) {
099        this.material = substance;
100        this.data = data;
101
102        TridentPlayer.sendAll(new PacketPlayOutBlockChange()
103                .set("blockId", substance().id() << 4 | data)
104                .set("location", location));
105
106        ((TridentChunk) position().chunk()).setAt(location, substance, data, (byte) 255, (byte) 0);
107    }
108
109    @Override
110    protected MetaCollection<Block> collect() {
111        return MetaFactory.newCollection();
112    }
113
114    @Override
115    public void clearMeta() {
116        super.clearMeta();
117        this.data = 0;
118    }
119
120    @Override
121    public <M extends BlockMeta<Block>> boolean applyMeta(boolean replace, M... meta) {
122        TridentChunk chunk = ((TridentChunk) location.chunk());
123        Vector key = new Vector((int) location.x() & 15, location.y(), (int) location.z() & 15);
124        List<BlockMeta> tiles = chunk.tilesInternal().computeIfAbsent(key, k -> Lists.newCopyOnWriteArrayList());
125        Collections.addAll(tiles, meta);
126        chunk.tilesInternal().put(key, tiles);
127
128        return super.applyMeta(replace, meta);
129    }
130}