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.inventory;
019
020import com.google.common.collect.Sets;
021import net.tridentsdk.base.Substance;
022import net.tridentsdk.entity.living.Player;
023import net.tridentsdk.inventory.Inventory;
024import net.tridentsdk.inventory.InventoryType;
025import net.tridentsdk.inventory.Item;
026import net.tridentsdk.registry.Registered;
027import net.tridentsdk.server.data.Slot;
028import net.tridentsdk.server.packets.play.out.PacketPlayOutCloseWindow;
029import net.tridentsdk.server.packets.play.out.PacketPlayOutOpenWindow;
030import net.tridentsdk.server.packets.play.out.PacketPlayOutSetSlot;
031import net.tridentsdk.server.player.TridentPlayer;
032import net.tridentsdk.util.WeakEntity;
033
034import javax.annotation.concurrent.ThreadSafe;
035import java.util.Set;
036import java.util.concurrent.atomic.AtomicInteger;
037import java.util.concurrent.atomic.AtomicReferenceArray;
038
039/**
040 * An inventory inventory, wherever and whatever is holding it or having it open
041 *
042 * @author The TridentSDK Team
043 */
044@ThreadSafe
045public class TridentInventory implements Inventory {
046    /**
047     * Counter for inventory ids, initial value is 2 to avoid confusion with a inventory and a player inventory
048     */
049    private static final AtomicInteger counter = new AtomicInteger(2);
050
051    private final int id;
052    private final String name;
053    private final int length;
054    private final InventoryType type;
055    private final Set<WeakEntity<Player>> users = Sets.newConcurrentHashSet();
056    private final AtomicReferenceArray<Item> contents;
057
058    /**
059     * Builds a new inventory inventory
060     *
061     * @param name   the title of the inventory
062     * @param length the amount of slots in the inventory (should be multiple of 9)
063     */
064    private TridentInventory(String name, int length, InventoryType type, int id) {
065        this.name = name != null ? name : type.defaultName();
066        this.length = length;
067        this.id = id;
068        this.contents = new AtomicReferenceArray<>(length);
069        for (int i = 0; i < contents.length(); i++) {
070            contents.set(i, new Item(Substance.AIR));
071        }
072
073        this.type = type;
074    }
075
076    private TridentInventory(String name, int length, InventoryType type) {
077        this(name, length, type, counter.addAndGet(1));
078    }
079
080    /**
081     * Builds a new inventory inventory
082     *
083     * @param length the amount of slots in the inventory (should be multiple of 9)
084     */
085    public TridentInventory(int length) {
086        this("", length, InventoryType.CHEST, counter.addAndGet(1));
087    }
088
089    public TridentInventory(int length, int id) {
090        this("", length, InventoryType.CHEST, id);
091    }
092
093    public static TridentInventory create(String name, int length, InventoryType type) {
094        TridentInventory window = new TridentInventory(name, length, type);
095        Registered.inventories().register(window);
096        return window;
097    }
098
099    @Override
100    public int id() {
101        return this.id;
102    }
103
104    @Override
105    public Item[] items() {
106        Item[] array = new Item[contents.length()];
107        for (int i = 0; i < contents.length(); i++) {
108            array[i] = contents.get(i);
109        }
110
111        return array;
112    }
113
114    @Override
115    public int length() {
116        return this.length;
117    }
118
119    //@Override
120    public int itemLength() {
121        int counter = 0;
122        for (Item item : items()) {
123            if (item != null)
124                counter++;
125        }
126
127        return counter;
128    }
129
130    @Override
131    public String name() {
132        return this.name;
133    }
134
135    @Override
136    public Item itemAt(int slot) {
137        return items()[slot];
138    }
139
140    @Override
141    public void setSlot(int index, Item value) {
142        if(value != null && value.quantity() == 0) {
143            value = null;
144        }
145
146        contents.set(index, value);
147
148        PacketPlayOutSetSlot setSlot = new PacketPlayOutSetSlot();
149        setSlot.set("windowId", id()).set("slot", (short) index).set("item", new Slot(value));
150
151        for (WeakEntity<Player> player : WeakEntity.iterate(users)) {
152            ((TridentPlayer) player.obtain()).connection().sendPacket(setSlot);
153        }
154    }
155
156    @Override
157    public boolean putItem(Item item) {
158        int freeSlot = -1;
159        if(contents.length() == 45){
160            for(int i = 36; i < contents.length() && item.quantity() > 0; i++){
161                if(contents.get(i) == null || contents.get(i).type() == Substance.AIR){
162                    if(freeSlot == -1){
163                        freeSlot = i;
164                    }
165                }else if(contents.get(i).isSimilarIgnoreQuantity(item)){
166                    int available = contents.get(i).type().maxStackSize() - contents.get(i).quantity();
167                    if(available > item.quantity()){
168                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + item.quantity()));
169                    }else{
170                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + available));
171                    }
172                    item.setQuantity((short) (item.quantity() - available));
173                }
174            }
175
176            if(item.quantity() == 0){
177                return true;
178            }
179
180            for(int i = 0; i < 36 && item.quantity() > 0; i++){
181                if(contents.get(i) == null || contents.get(i).type() == Substance.AIR){
182                    if(i >= 9 && freeSlot == -1){
183                        freeSlot = i;
184                    }
185                }else if(contents.get(i).isSimilarIgnoreQuantity(item)){
186                    int available = contents.get(i).type().maxStackSize() - contents.get(i).quantity();
187                    if(available > item.quantity()){
188                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + item.quantity()));
189                    }else{
190                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + available));
191                    }
192                    item.setQuantity((short) (item.quantity() - available));
193                }
194            }
195        }else{
196            for(int i = 0; i < contents.length() && item.quantity() > 0; i++){
197                if(contents.get(i) == null || contents.get(i).type() == Substance.AIR){
198                    if(freeSlot == -1){
199                        freeSlot = i;
200                    }
201                }else if(contents.get(i).isSimilarIgnoreQuantity(item)){
202                    int available = contents.get(i).type().maxStackSize() - contents.get(i).quantity();
203                    if(available > item.quantity()){
204                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + item.quantity()));
205                    }else{
206                        contents.get(i).setQuantity((short) (contents.get(i).quantity() + available));
207                    }
208                    item.setQuantity((short) (item.quantity() - available));
209                }
210            }
211        }
212
213        if(item.quantity() <= 0){
214            return true;
215        }else if(freeSlot != -1){
216            contents.set(freeSlot, item.clone());
217            item.setQuantity((short) 0);
218            return true;
219        }
220
221        return false;
222    }
223
224    public void sendTo(TridentPlayer player) {
225        PacketPlayOutOpenWindow window = new PacketPlayOutOpenWindow();
226        window.set("windowId", id())
227                .set("inventoryType", type)
228                .set("windowTitle", name())
229                .set("slots", length())
230                .set("entityId", -1);
231        player.connection().sendPacket(window);
232
233        for (int i = 0; i < length(); i++) {
234            PacketPlayOutSetSlot setSlot = new PacketPlayOutSetSlot();
235            setSlot.set("windowId", id()).set("slot", (short) i).set("item", new Slot(items()[i]));
236            player.connection().sendPacket(setSlot);
237        }
238        users.add(WeakEntity.of(player));
239    }
240
241    public void close(Player player, boolean force) {
242        if (force) {
243            ((TridentPlayer) player).connection().sendPacket(new PacketPlayOutCloseWindow().set("windowId", id));
244        }
245
246        users.remove(WeakEntity.searchFor(player));
247    }
248}