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}