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.entity; 019 020import com.google.common.util.concurrent.AtomicDouble; 021import net.tridentsdk.base.Position; 022import net.tridentsdk.effect.entity.EntityStatusEffect; 023import net.tridentsdk.effect.entity.EntityStatusEffectType; 024import net.tridentsdk.entity.Entity; 025import net.tridentsdk.entity.LivingEntity; 026import net.tridentsdk.entity.Projectile; 027import net.tridentsdk.entity.living.Player; 028import net.tridentsdk.entity.living.ai.AiHandler; 029import net.tridentsdk.entity.living.ai.AiModule; 030import net.tridentsdk.entity.living.ai.Path; 031import net.tridentsdk.entity.traits.EntityProperties; 032import net.tridentsdk.entity.types.EntityType; 033import net.tridentsdk.meta.nbt.*; 034import net.tridentsdk.server.data.MetadataType; 035import net.tridentsdk.server.data.ProtocolMetadata; 036import net.tridentsdk.server.effect.entity.TridentEntityStatusEffect; 037import net.tridentsdk.server.entity.ai.TridentAiHandler; 038import net.tridentsdk.server.packets.play.out.PacketPlayOutDestroyEntities; 039import net.tridentsdk.server.packets.play.out.PacketPlayOutSpawnMob; 040import net.tridentsdk.server.player.TridentPlayer; 041import net.tridentsdk.util.Vector; 042 043import java.util.List; 044import java.util.UUID; 045import java.util.concurrent.CopyOnWriteArrayList; 046import java.util.concurrent.atomic.AtomicInteger; 047 048/** 049 * An entity that has health 050 * 051 * @author The TridentSDK Team 052 */ 053public abstract class TridentLivingEntity extends TridentEntity implements LivingEntity { 054 private volatile AiModule ai; 055 private volatile Path path; 056 057 protected final List<EntityAttribute> attributes = new CopyOnWriteArrayList<>(); 058 protected final AtomicInteger invincibilityTicks = new AtomicInteger(0); 059 protected final AtomicInteger restTicks = new AtomicInteger(0); 060 /** 061 * The entity health 062 */ 063 protected final AtomicDouble health = new AtomicDouble(0.0); 064 /** 065 * Whether the entity is dead 066 */ 067 protected volatile boolean dead; 068 /** 069 * Whether the entity can pick up items 070 */ 071 protected volatile boolean canPickup = true; 072 /** 073 * The maximum available health 074 */ 075 protected volatile double maxHealth; 076 077 /** 078 * Inherits from {@link TridentEntity} 079 * 080 * <p>The entity is immediately set "non-dead" after {@code super} call</p> 081 */ 082 public TridentLivingEntity(UUID id, Position spawnLocation) { 083 super(id, spawnLocation); 084 085 this.dead = false; 086 } 087 088 @Override 089 protected void encodeMetadata(ProtocolMetadata protocolMeta) { 090 super.encodeMetadata(protocolMeta); 091 092 protocolMeta.setMeta(2, MetadataType.STRING, displayName); 093 protocolMeta.setMeta(3, MetadataType.BYTE, nameVisible ? (byte) 1 : (byte) 0); 094 protocolMeta.setMeta(6, MetadataType.FLOAT, health.floatValue()); 095 protocolMeta.setMeta(7, MetadataType.INT, 0); 096 protocolMeta.setMeta(8, MetadataType.BYTE, (byte) 1); // TODO (potion effects) 097 protocolMeta.setMeta(9, MetadataType.BYTE, (byte) 0); // TODO (arrows in entity) 098 protocolMeta.setMeta(15, MetadataType.BYTE, (ai == null) ? (byte) 1 : (byte) 0); 099 } 100 101 @Override 102 protected void doTick() { 103 performAiUpdate(); 104 } 105 106 @Override 107 public double health() { 108 return this.health.get(); 109 } 110 111 @Override 112 public void setHealth(double health) { 113 this.health.set(health); 114 } 115 116 @Override 117 public double maxHealth() { 118 return this.maxHealth; 119 } 120 121 @Override 122 public void setMaxHealth(double maxHealth) { 123 this.maxHealth = maxHealth; 124 } 125 126 @Override 127 public Position headLocation() { 128 return this.position().relative(new Vector(0.0d, 1.0d, 0.0d)); 129 } 130 131 @Override 132 public long remainingAir() { 133 return this.airTicks.get(); 134 } 135 136 @Override 137 public void setRemainingAir(long ticks) { 138 this.airTicks.set((int) ticks); 139 } 140 141 @Override 142 public boolean canCollectItems() { 143 return this.canPickup; 144 } 145 146 @Override 147 public boolean isDead() { 148 return this.dead; 149 } 150 151 @Override 152 public void remove() { 153 super.remove(); 154 dead = true; 155 } 156 157 @Override 158 public void setAiModule(AiModule module) { 159 this.ai = module; 160 } 161 162 @Override 163 public AiModule aiModule() { 164 AiModule module = this.ai; 165 if (module == null) { 166 return aiHandler().defaultAiFor(type()); 167 } else { 168 return module; 169 } 170 } 171 172 private static final AiHandler AI_HANDLER = new TridentAiHandler(); 173 174 public static AiHandler aiHandler() { 175 return AI_HANDLER; 176 } 177 178 @Override 179 public <T extends Projectile> T launchProjectile(EntityProperties properties) { 180 return null; 181 } 182 183 public void performAiUpdate() { 184 AiModule module = this.aiModule(); 185 186 if (this.restTicks.get() <= 0) { 187 this.restTicks.set(module.think(this)); 188 } else { 189 this.restTicks.getAndDecrement(); 190 // TODO: follow path 191 } 192 } 193 194 @Override 195 public Path path() { 196 return path; 197 } 198 199 public void setPath(Path path) { 200 this.path = path; 201 } 202 203 @Override 204 public void hide(Entity entity) { 205 PacketPlayOutDestroyEntities packet = new PacketPlayOutDestroyEntities(); 206 packet.set("destroyedEntities", new int[]{ entity.entityId() }); 207 208 if (this instanceof Player) { 209 ((TridentPlayer) this).connection().sendPacket(packet); 210 } 211 } 212 213 @Override 214 public void show(Entity entity) { 215 PacketPlayOutSpawnMob packet = new PacketPlayOutSpawnMob(); 216 ProtocolMetadata protocolMeta = new ProtocolMetadata(); 217 218 ((TridentEntity) entity).encodeMetadata(protocolMeta); 219 220 packet.set("entityId", entity.entityId()) 221 .set("entity", entity) 222 .set("metadata", protocolMeta); 223 224 if (this instanceof Player) { 225 ((TridentPlayer) this).connection().sendPacket(packet); 226 } 227 } 228 229 @Override 230 public void load(CompoundTag tag) { 231 super.load(tag); 232 233 if (type() == EntityType.PLAYER) { 234 return; // onlinePlayers do not inherit the living entity or "mob" NBT structure 235 } 236 237 if (tag.containsTag("HealF")) { 238 health.set(((FloatTag) tag.getTag("HealF")).value()); 239 } else { 240 health.set(((ShortTag) tag.getTag("Health")).value()); 241 } 242 243 FloatTag extraHealth = tag.getTagAs("AbsorptionAmount"); // health added if the entity has the absorption effect 244 245 ShortTag invincibilityTicks = tag.getTagAs("AttackTime"); // time in ticks that the entity is invincible 246 ShortTag hurtTime = tag.getTagAs("HurtTime"); // time in ticks that the entity is shown as "red" for being hit 247 ShortTag timeDead = tag.getTagAs("DeathTime"); // time in ticks entity has been dead for 248 249 ListTag attributes = tag.getTagAs("Attributes"); 250 ListTag potionEffects = tag.getTagAs("ActiveEffects"); 251 252 ByteTag canPickupLoot = tag.getTagAs("CanPickupLoot"); 253 ByteTag aiDisabled = tag.getTagAs("NoAI"); 254 ByteTag canRespawn = tag.getTagAs("PersistenceRequired"); 255 ByteTag leashed = tag.getTagAs("Leashed"); 256 257 health.addAndGet(extraHealth.value()); 258 this.invincibilityTicks.set(invincibilityTicks.value()); 259 260 for (NBTTag attribute : attributes.listTags()) { 261 this.attributes.add(NBTSerializer.deserialize(EntityAttribute.class, 262 attribute.asType(CompoundTag.class))); 263 } 264 } 265 266 @Override 267 public EntityStatusEffect createStatusEffect(EntityStatusEffectType status){ 268 return new TridentEntityStatusEffect(this, status); 269 } 270}