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.BlockDirection; 023import net.tridentsdk.base.Position; 024import net.tridentsdk.base.Substance; 025import net.tridentsdk.effect.particle.ParticleEffect; 026import net.tridentsdk.effect.particle.ParticleEffectType; 027import net.tridentsdk.effect.sound.SoundEffect; 028import net.tridentsdk.effect.sound.SoundEffectType; 029import net.tridentsdk.entity.types.EntityType; 030import net.tridentsdk.event.Cancellable; 031import net.tridentsdk.event.Event; 032import net.tridentsdk.event.block.BlockBreakEvent; 033import net.tridentsdk.event.player.*; 034import net.tridentsdk.inventory.Item; 035import net.tridentsdk.meta.block.Tile; 036import net.tridentsdk.registry.Registered; 037import net.tridentsdk.server.entity.TridentDroppedItem; 038import net.tridentsdk.server.entity.projectile.TridentArrow; 039import net.tridentsdk.server.netty.ClientConnection; 040import net.tridentsdk.server.netty.packet.InPacket; 041import net.tridentsdk.server.netty.packet.Packet; 042import net.tridentsdk.server.packets.play.out.PacketPlayOutBlockChange; 043import net.tridentsdk.server.player.PlayerConnection; 044import net.tridentsdk.server.player.TridentPlayer; 045import net.tridentsdk.server.world.TridentChunk; 046import net.tridentsdk.server.world.TridentWorld; 047import net.tridentsdk.util.TridentLogger; 048import net.tridentsdk.util.Vector; 049import net.tridentsdk.world.settings.GameMode; 050 051public class PacketPlayInPlayerDig extends InPacket { 052 private short status; 053 private Position location; 054 private short blockFace; 055 056 @Override 057 public int id() { 058 return 0x13; 059 } 060 061 public short status() { 062 return this.status; 063 } 064 065 public Position location() { 066 return this.location; 067 } 068 069 public short blockFace() { 070 return this.blockFace; 071 } 072 073 @Override 074 public Packet decode(ByteBuf buf) { 075 this.status = (short) buf.readByte(); 076 long encodedLocation = buf.readLong(); 077 078 this.location = Position.create(null, (double) (encodedLocation >> 38), 079 (double) (encodedLocation << 26 >> 52), (double) (encodedLocation << 38 >> 38)); 080 this.blockFace = (short) buf.readByte(); 081 082 return this; 083 } 084 085 @Override 086 public void handleReceived(ClientConnection connection) { 087 TridentPlayer player = ((PlayerConnection) connection).player(); 088 DigStatus digStatus = DigStatus.getStatus(this.status); 089 BlockDirection face = null; 090 091 if (digStatus == DigStatus.DIG_START && player.gameMode() == GameMode.CREATIVE) { 092 digStatus = DigStatus.DIG_FINISH; 093 } 094 095 this.location.setWorld(player.world()); 096 097 switch (this.blockFace) { 098 case 0: 099 face = BlockDirection.BOTTOM; 100 break; 101 102 case 1: 103 face = BlockDirection.TOP; 104 break; 105 106 case 2: 107 // z-- 108 break; 109 110 case 3: 111 // z++ 112 break; 113 114 case 4: 115 // x-- 116 break; 117 118 case 5: 119 // x++ 120 break; 121 122 default: 123 TridentLogger.get().error(new IllegalArgumentException("Client sent invalid BlockFace!")); 124 } 125 126 Cancellable event = null; 127 128 Block block = location.block(); 129 switch (digStatus) { 130 case DIG_START: 131 case DIG_CANCEL: 132 case DIG_FINISH: 133 event = new PlayerDigEvent(player, face, this.status); 134 135 if(digStatus == DigStatus.DIG_FINISH) { 136 BlockBreakEvent blockBreak = new BlockBreakEvent(player, block, face, player.heldItem()); 137 138 if(blockBreak.isIgnored()) 139 return; 140 } 141 142 break; 143 144 case DROP_ITEMSTACK: 145 if(player.heldItem() == null || player.heldItem().type() == Substance.AIR){ 146 return; 147 } 148 149 event = new PlayerDropItemEvent(player, null); // todo: spawn item and call the event 150 break; 151 152 case DROP_ITEM: 153 if(player.heldItem() == null || player.heldItem().type() == Substance.AIR){ 154 return; 155 } 156 157 event = new PlayerDropItemEvent(player, null); 158 break; 159 160 case SHOOT_ARROW: 161 Item item = player.heldItem(); 162 if (item.type().isWeapon()) { 163 if (item.type() == Substance.BOW) // bow 164 event = new PlayerShootBowEvent(player, null); 165 else event = new PlayerInteractEvent(player, block); // other weapons 166 } else if (item.type().isEdible()) { 167 event = new PlayerConsumeEvent(player, item, 0.0); 168 } 169 // shoot bow, if player has a food item finish eating 170 break; 171 default: 172 } 173 174 Registered.events().fire((Event) event); 175 176 if (event == null || event.isIgnored()) 177 return; 178 179 // TODO act accordingly 180 181 switch(digStatus){ 182 case SHOOT_ARROW: 183 TridentArrow entity = (TridentArrow) location.world().spawn(EntityType.ARROW, location); 184 entity.setVelocity(player.position().asUnitVector()); 185 break; 186 187 case DIG_FINISH: 188 block.ownedMeta().iterate(e -> { 189 if (e.getValue() instanceof Tile) { 190 ((TridentWorld) location.world()).tilesInternal().remove(e.getValue()); 191 Position pos = block.position(); 192 ((TridentChunk) pos.chunk()).tilesInternal().remove(new Vector(pos.x(), pos.y(), pos.z())); 193 } 194 }); 195 196 int[] arr = {block.substance().id() + (block.meta() << 12)}; 197 198 ((TridentChunk) location().chunk()).setAt(location, Substance.AIR, (byte) 0, (byte) 255, (byte) 15); 199 TridentPlayer.sendAll(new PacketPlayOutBlockChange() 200 .set("location", location).set("blockId", Substance.AIR.id())); 201 202 ParticleEffect effect = location.world().spawnParticle(ParticleEffectType.BLOCK_CRACK); 203 effect.setCount(64); 204 effect.setLongDistance(false); 205 effect.setPosition(location.add(new Vector(0.5, 0.5, 0.5)).asVector()); 206 effect.setOffset(new Vector(0.45, 0.45, 0.45)); 207 effect.setData(arr); 208 effect.applyToEveryoneExcept(player); 209 210 SoundEffectType soundEffectType = block.substance().breakSound(); 211 if(soundEffectType != null){ 212 SoundEffect sound = location.world().playSound(soundEffectType); 213 sound.setPosition(location); 214 sound.applyToEveryoneExcept(player); 215 } 216 break; 217 218 case DROP_ITEM: 219 case DROP_ITEMSTACK: 220 short count = digStatus == DigStatus.DROP_ITEM ? 1 : player.heldItem().quantity(); 221 Item heldItem = player.heldItem().clone(); 222 heldItem.setQuantity(count); 223 TridentDroppedItem item = new TridentDroppedItem(player.headLocation(), heldItem); 224 item.spawn(); 225 item.setVelocity(player.position().toDirection().normalize().multiply(2000)); 226 player.heldItem().setQuantity((short) (player.heldItem().quantity() - count)); 227 player.setHeldItem(player.heldItem()); 228 break; 229 } 230 231 } 232 233 public enum DigStatus { 234 DIG_START(0), 235 DIG_CANCEL(1), 236 DIG_FINISH(2), 237 DROP_ITEMSTACK(3), 238 DROP_ITEM(4), 239 SHOOT_ARROW(5); 240 241 private final short id; 242 243 DigStatus(int id) { 244 this.id = (short) id; 245 } 246 247 public static DigStatus getStatus(short id) { 248 for (DigStatus status : DigStatus.values()) { 249 if (status.id == id) { 250 return status; 251 } 252 } 253 254 return null; 255 } 256 257 public short getId() { 258 return this.id; 259 } 260 } 261}