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.Substance; 022import net.tridentsdk.event.player.PlayerClickItemEvent; 023import net.tridentsdk.inventory.Inventory; 024import net.tridentsdk.inventory.Item; 025import net.tridentsdk.registry.Registered; 026import net.tridentsdk.server.data.Slot; 027import net.tridentsdk.server.entity.TridentDroppedItem; 028import net.tridentsdk.server.event.EventProcessor; 029import net.tridentsdk.server.netty.ClientConnection; 030import net.tridentsdk.server.netty.packet.InPacket; 031import net.tridentsdk.server.netty.packet.Packet; 032import net.tridentsdk.server.player.PlayerConnection; 033import net.tridentsdk.server.player.TridentPlayer; 034import net.tridentsdk.util.TridentLogger; 035 036/** 037 * Packet sent by the player when it clicks on a slot in a inventory. 038 */ 039public class PacketPlayInPlayerClickWindow extends InPacket { 040 041 /** 042 * The id of the inventory which was clicked. 0 for player inventory. 043 */ 044 protected int windowId; 045 /** 046 * The button used in the click, dependent on action number TODO reference to wiki 047 */ 048 protected int clickedButton; 049 050 /** 051 * The clicked slot, -999 if not applicable 052 */ 053 protected short clickedSlot; 054 /** 055 * A unique number for the action, used for transaction handling 056 */ 057 protected short actionNumber; 058 /** 059 * Inventory operation mode 060 */ 061 protected byte modeId; 062 protected ClickAction mode; 063 /** 064 * Item clicked 065 */ 066 protected Slot clickedItem; 067 068 @Override 069 public int id() { 070 return 0x07; 071 } 072 073 public int windowId() { 074 return this.windowId; 075 } 076 077 public int clickedButton() { 078 return this.clickedButton; 079 } 080 081 public short clickedSlot() { 082 return this.clickedSlot; 083 } 084 085 public short actionNumber() { 086 return this.actionNumber; 087 } 088 089 public ClickAction mode() { 090 return this.mode; 091 } 092 093 public Slot clickedItem() { 094 return this.clickedItem; 095 } 096 097 @Override 098 public Packet decode(ByteBuf buf) { 099 this.windowId = (int) buf.readByte(); 100 this.clickedSlot = buf.readShort(); 101 this.clickedButton = (int) buf.readByte(); 102 this.actionNumber = buf.readShort(); 103 this.modeId = buf.readByte(); 104 this.mode = ClickAction.getAction(modeId, clickedButton, clickedSlot); 105 this.clickedItem = new Slot(buf); 106 return this; 107 } 108 109 @Override 110 public void handleReceived(ClientConnection connection) { 111 if(mode == null) { 112 return; 113 } 114 115 TridentPlayer player = ((PlayerConnection) connection).player(); 116 Inventory window = Registered.inventories().fromId(this.windowId); 117 Inventory originalWindow = window; 118 119 int originalSlot = clickedSlot; 120 if(clickedSlot >= window.length()) { 121 clickedSlot += (9 - window.length()); 122 window = player.window(); 123 } 124 125 PlayerClickItemEvent clickEvent = EventProcessor 126 .fire(new PlayerClickItemEvent(window, this.clickedSlot, (int) this.actionNumber)); 127 128 if(clickEvent.isIgnored()) { 129 return; 130 } 131 132 // TODO Implement all 133 switch(mode) { 134 case LEFT_CLICK: 135 case RIGHT_CLICK: 136 if(player.pickedItem() == null) { 137 if(window.itemAt(clickedSlot) != null && window.itemAt(clickedSlot).type() != Substance.AIR) { 138 if(window.itemAt(clickedSlot).isSimilar(clickedItem.item())) { 139 if(mode == ClickAction.LEFT_CLICK) { 140 player.setPickedItem(clickedItem.item()); 141 window.setSlot(clickedSlot, null); 142 } else { 143 Item cursor = clickedItem.item().clone(); 144 cursor.setQuantity((short) Math.ceil((cursor.quantity() / 2))); 145 player.setPickedItem(cursor); 146 window.itemAt(clickedSlot).setQuantity((short) (window.itemAt(clickedSlot).quantity() - cursor.quantity())); 147 window.setSlot(clickedSlot, window.itemAt(clickedSlot)); 148 } 149 } else { 150 TridentLogger.get().warn(player.name() + " tried to cheat items!"); 151 } 152 } 153 } else { 154 Item temp = window.itemAt(clickedSlot); 155 if(mode == ClickAction.LEFT_CLICK) { 156 window.setSlot(clickedSlot, player.pickedItem()); 157 if(temp != null && temp.type() != Substance.AIR) { 158 player.setPickedItem(temp); 159 } else { 160 player.setPickedItem(null); 161 } 162 } else { 163 if(temp == null || temp.type() == Substance.AIR) { 164 Item single = player.pickedItem().clone(); 165 single.setQuantity((short) 1); 166 window.setSlot(clickedSlot, single); 167 if(player.pickedItem().quantity() > 1){ 168 player.pickedItem().setQuantity((short) (player.pickedItem().quantity() - 1)); 169 }else{ 170 player.setPickedItem(null); 171 } 172 }else{ 173 window.setSlot(clickedSlot, player.pickedItem()); 174 if(temp.type() != Substance.AIR) { 175 player.setPickedItem(temp); 176 } else { 177 player.setPickedItem(null); 178 } 179 } 180 } 181 } 182 break; 183 case SHIFT_LEFT_CLICK: 184 case SHIFT_RIGHT_CLICK: 185 if(window.itemAt(clickedSlot) != null && window.itemAt(clickedSlot).type() != Substance.AIR){ 186 if(originalWindow.equals(window)){ 187 if(player.window().putItem(window.itemAt(clickedSlot))){ 188 window.setSlot(clickedSlot, null); 189 } 190 }else{ 191 if(originalWindow.putItem(window.itemAt(clickedSlot))){ 192 window.setSlot(clickedSlot, null); 193 } 194 } 195 } 196 break; 197 case NUMBER_KEY: 198 break; 199 case MIDDLE_CLICK: 200 break; 201 case DROP_KEY_ONE: 202 case DROP_KEY_STACK: 203 if(window.itemAt(clickedSlot) != null && window.itemAt(clickedSlot).type() != Substance.AIR){ 204 short amount = (mode == ClickAction.DROP_KEY_STACK) ? window.itemAt(clickedSlot).quantity() : 1; 205 Item item = window.itemAt(clickedSlot).clone(); 206 item.setQuantity(amount); 207 TridentDroppedItem droppedItem = new TridentDroppedItem(player.position(), item); 208 droppedItem.spawn(); 209 droppedItem.setVelocity(player.position().toDirection().normalize().multiply(2000)); 210 window.itemAt(clickedSlot).setQuantity((short) (window.itemAt(clickedSlot).quantity() - amount)); 211 if(window.itemAt(clickedSlot).quantity() == 0){ 212 window.setSlot(clickedSlot, null); 213 } 214 } 215 break; 216 case LEFT_CLICK_OUTSIDE: 217 break; 218 case RIGHT_CLICK_OUTSIDE: 219 break; 220 case START_LEFT_CLICK_DRAG: 221 case START_RIGHT_CLICK_DRAG: 222 if(player.drag() != null){ 223 TridentLogger.get().warn(player.name() + " tried to drag whilst already dragging!"); 224 break; 225 } 226 227 player.setDrag(mode); 228 break; 229 case ADD_SLOT_LEFT_CLICK_DRAG: 230 case ADD_SLOT_RIGHT_CLICK_DRAG: 231 if(player.drag() == null){ 232 TridentLogger.get().warn(player.name() + " tried to add drag slot, whilst not dragging!"); 233 break; 234 }else{ 235 if((mode == ClickAction.ADD_SLOT_LEFT_CLICK_DRAG && player.drag() == ClickAction.START_RIGHT_CLICK_DRAG) || 236 (mode == ClickAction.ADD_SLOT_RIGHT_CLICK_DRAG && player.drag() == ClickAction.START_LEFT_CLICK_DRAG)){ 237 TridentLogger.get().warn(player.name() + " tried to add drag slot, whilst dragging the wrong click!"); 238 break; 239 } 240 } 241 242 player.dragSlots().add(originalSlot); 243 break; 244 case END_LEFT_CLICK_DRAG: 245 if(player.drag() == null){ 246 TridentLogger.get().warn(player.name() + " tried to stop dragging, whilst not dragging!"); 247 break; 248 }else if(player.drag() == ClickAction.START_RIGHT_CLICK_DRAG){ 249 TridentLogger.get().warn(player.name() + " tried to stop dragging, whilst dragging the wrong click!"); 250 break; 251 } 252 253 int available = player.pickedItem().quantity(); 254 int split = (int) Math.floor(available / player.dragSlots().size()); 255 for (Integer i : player.dragSlots()){ 256 if(available == 0){ 257 break; 258 } 259 260 Inventory using = originalWindow; 261 if(i >= originalWindow.length()){ 262 using = player.window(); 263 } 264 265 Item current = using.itemAt(i); 266 if(current == null || current.type() == Substance.AIR){ 267 current = player.pickedItem().clone(); 268 current.setQuantity((short) split); 269 using.setSlot(i, current); 270 available -= split; 271 }else if(current.isSimilarIgnoreQuantity(player.pickedItem()) && current.quantity() < current.type().maxStackSize()){ 272 int canAdd = Math.min(split, current.type().maxStackSize() - current.quantity()); 273 current.setQuantity((short) (current.quantity() + canAdd)); 274 using.setSlot(i, current); 275 available -= canAdd; 276 } 277 } 278 279 if(available == 0){ 280 player.setPickedItem(null); 281 }else{ 282 player.pickedItem().setQuantity((short) available); 283 } 284 285 player.dragSlots().clear(); 286 player.setDrag(null); 287 break; 288 case END_RIGHT_CLICK_DRAG: 289 if(player.drag() == null){ 290 TridentLogger.get().warn(player.name() + " tried to stop dragging, whilst not dragging!"); 291 break; 292 }else if(player.drag() == ClickAction.START_LEFT_CLICK_DRAG){ 293 TridentLogger.get().warn(player.name() + " tried to stop dragging, whilst dragging the wrong click!"); 294 break; 295 } 296 297 available = player.pickedItem().quantity(); 298 for (Integer i : player.dragSlots()){ 299 if(available == 0){ 300 break; 301 } 302 303 Inventory using = originalWindow; 304 if(i >= originalWindow.length()){ 305 using = player.window(); 306 } 307 308 Item current = using.itemAt(i); 309 if(current == null || current.type() == Substance.AIR){ 310 current = player.pickedItem().clone(); 311 current.setQuantity((short) 1); 312 using.setSlot(i, current); 313 available--; 314 }else if(current.isSimilarIgnoreQuantity(player.pickedItem()) && current.quantity() < current.type().maxStackSize()){ 315 current.setQuantity((short) (current.quantity() + 1)); 316 using.setSlot(i, current); 317 available--; 318 } 319 } 320 321 if(available == 0){ 322 player.setPickedItem(null); 323 }else{ 324 player.pickedItem().setQuantity((short) available); 325 } 326 327 player.dragSlots().clear(); 328 player.setDrag(null); 329 break; 330 case DOUBLE_CLICK: 331 Item picking = window.itemAt(clickedSlot); 332 if(player.pickedItem() != null) { 333 picking = player.pickedItem(); 334 } 335 336 int count = picking.quantity(); 337 int slot = 0; 338 if(window.id() != windowId) { 339 window = originalWindow; 340 } 341 342 while (count <= picking.type().maxStackSize() && slot < window.length()) { 343 if(window.itemAt(slot) != null && window.itemAt(slot).isSimilarIgnoreQuantity(picking)) { 344 if(count + window.itemAt(slot).quantity() <= picking.type().maxStackSize()) { 345 count += window.itemAt(slot).quantity(); 346 window.setSlot(slot, null); 347 } else { 348 window.itemAt(slot).setQuantity((short) (window.itemAt(slot).quantity() - (picking.type().maxStackSize() - count))); 349 window.setSlot(slot, window.itemAt(slot)); 350 count = picking.type().maxStackSize(); 351 break; 352 } 353 } 354 slot++; 355 } 356 357 if(count < picking.type().maxStackSize() && windowId > 0) { 358 slot = 0; 359 Inventory pW = player.window(); 360 while (count <= picking.type().maxStackSize() && slot < pW.length()) { 361 if(pW.itemAt(slot) != null && pW.itemAt(slot).isSimilarIgnoreQuantity(picking)) { 362 if(count + pW.itemAt(slot).quantity() <= picking.type().maxStackSize()) { 363 count += pW.itemAt(slot).quantity(); 364 pW.setSlot(slot, null); 365 } else { 366 pW.itemAt(slot).setQuantity((short) (pW.itemAt(slot).quantity() - (picking.type().maxStackSize() - count))); 367 pW.setSlot(slot, pW.itemAt(slot)); 368 count = picking.type().maxStackSize(); 369 break; 370 } 371 } 372 slot++; 373 } 374 } 375 376 picking.setQuantity((short) count); 377 player.setPickedItem(picking); 378 break; 379 } 380 } 381 382 public enum ClickAction { 383 384 LEFT_CLICK, 385 RIGHT_CLICK, 386 387 SHIFT_LEFT_CLICK, 388 SHIFT_RIGHT_CLICK, 389 390 NUMBER_KEY, 391 392 MIDDLE_CLICK, 393 394 DROP_KEY_ONE, 395 DROP_KEY_STACK, 396 LEFT_CLICK_OUTSIDE, 397 RIGHT_CLICK_OUTSIDE, 398 399 START_LEFT_CLICK_DRAG, 400 START_RIGHT_CLICK_DRAG, 401 ADD_SLOT_LEFT_CLICK_DRAG, 402 ADD_SLOT_RIGHT_CLICK_DRAG, 403 END_LEFT_CLICK_DRAG, 404 END_RIGHT_CLICK_DRAG, 405 406 DOUBLE_CLICK; 407 408 public static ClickAction getAction(short mode, int button, int slot) { 409 switch(mode) { 410 case 0: 411 case 255: 412 switch(button) { 413 case 0: 414 return LEFT_CLICK; 415 case 1: 416 return RIGHT_CLICK; 417 } 418 break; 419 case 1: 420 switch(button) { 421 case 0: 422 return SHIFT_LEFT_CLICK; 423 case 1: 424 return SHIFT_RIGHT_CLICK; 425 } 426 break; 427 case 2: 428 return NUMBER_KEY; 429 case 3: 430 return MIDDLE_CLICK; 431 case 4: 432 if(slot == -999) { 433 switch(button) { 434 case 0: 435 return LEFT_CLICK_OUTSIDE; 436 case 1: 437 return RIGHT_CLICK_OUTSIDE; 438 } 439 } else { 440 switch(button) { 441 case 0: 442 return DROP_KEY_ONE; 443 case 1: 444 return DROP_KEY_STACK; 445 } 446 } 447 break; 448 case 5: 449 case 1535: 450 switch(button) { 451 case 0: 452 return START_LEFT_CLICK_DRAG; 453 case 4: 454 return START_RIGHT_CLICK_DRAG; 455 case 1: 456 return ADD_SLOT_LEFT_CLICK_DRAG; 457 case 5: 458 return ADD_SLOT_RIGHT_CLICK_DRAG; 459 case 2: 460 return END_LEFT_CLICK_DRAG; 461 case 6: 462 return END_RIGHT_CLICK_DRAG; 463 } 464 break; 465 case 6: 466 case 1791: 467 return DOUBLE_CLICK; 468 } 469 470 return null; 471 } 472 473 } 474 475}