#1266: Add support for virtual entities
By: Jishuna <joshl5324@gmail.com>
This commit is contained in:
@@ -64,6 +64,7 @@ import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
||||
import org.bukkit.craftbukkit.util.BlockStateListPopulator;
|
||||
@@ -528,6 +529,18 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
|
||||
|
||||
public abstract Iterable<net.minecraft.world.entity.Entity> getNMSEntities();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Entity> T createEntity(Location location, Class<T> clazz) throws IllegalArgumentException {
|
||||
net.minecraft.world.entity.Entity entity = createEntity(location, clazz, true);
|
||||
|
||||
if (!isNormalWorld()) {
|
||||
entity.generation = true;
|
||||
}
|
||||
|
||||
return (T) entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> T spawn(Location location, Class<T> clazz) throws IllegalArgumentException {
|
||||
return spawn(location, clazz, null, CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
@@ -553,6 +566,19 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
|
||||
return addEntity(entity, reason, function, randomizeData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Entity> T addEntity(T entity) {
|
||||
Preconditions.checkArgument(!entity.isInWorld(), "Entity has already been added to a world");
|
||||
net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
|
||||
if (nmsEntity.level() != getHandle().getLevel()) {
|
||||
nmsEntity = nmsEntity.changeDimension(getHandle().getLevel());
|
||||
}
|
||||
|
||||
addEntityWithPassengers(nmsEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
|
||||
return (T) nmsEntity.getBukkitEntity();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException {
|
||||
return addEntity(entity, reason, null, true);
|
||||
@@ -580,8 +606,10 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
|
||||
|
||||
public abstract void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason);
|
||||
|
||||
public abstract void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public net.minecraft.world.entity.Entity createEntity(Location location, Class<? extends Entity> clazz) throws IllegalArgumentException {
|
||||
public net.minecraft.world.entity.Entity makeEntity(Location location, Class<? extends Entity> clazz) throws IllegalArgumentException {
|
||||
return createEntity(location, clazz, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -822,6 +822,11 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
getHandle().addFreshEntity(entity, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, SpawnReason reason) {
|
||||
getHandle().tryAddFreshEntityWithPassengers(entity, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(Location location, double x, double y, double z) {
|
||||
return this.getNearbyEntities(location, x, y, z, null);
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
package org.bukkit.craftbukkit.block;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.InclusiveRange;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.random.SimpleWeightedRandomList;
|
||||
import net.minecraft.util.random.WeightedEntry.b;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
import net.minecraft.world.level.MobSpawnerData;
|
||||
import net.minecraft.world.level.block.entity.TileEntityMobSpawner;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.block.spawner.SpawnRule;
|
||||
import org.bukkit.block.spawner.SpawnerEntry;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntitySnapshot;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntityType;
|
||||
import org.bukkit.entity.EntitySnapshot;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
public class CraftCreatureSpawner extends CraftBlockEntityState<TileEntityMobSpawner> implements CreatureSpawner {
|
||||
@@ -46,6 +56,78 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<TileEntityMobSpa
|
||||
this.getSnapshot().setEntityId(CraftEntityType.bukkitToMinecraft(entityType), rand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntitySnapshot getSpawnedEntity() {
|
||||
MobSpawnerData spawnData = this.getSnapshot().getSpawner().nextSpawnData;
|
||||
if (spawnData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CraftEntitySnapshot.create(spawnData.getEntityToSpawn());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpawnedEntity(EntitySnapshot snapshot) {
|
||||
NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData();
|
||||
|
||||
this.getSnapshot().getSpawner().spawnPotentials = SimpleWeightedRandomList.empty();
|
||||
this.getSnapshot().getSpawner().nextSpawnData = new MobSpawnerData(compoundTag, Optional.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPotentialSpawn(EntitySnapshot snapshot, int weight, SpawnRule spawnRule) {
|
||||
NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData();
|
||||
|
||||
SimpleWeightedRandomList.a<MobSpawnerData> builder = SimpleWeightedRandomList.builder(); // PAIL rename Builder
|
||||
this.getSnapshot().getSpawner().spawnPotentials.unwrap().forEach(entry -> builder.add(entry.getData(), entry.getWeight().asInt()));
|
||||
builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnRule))), weight);
|
||||
this.getSnapshot().getSpawner().spawnPotentials = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPotentialSpawn(SpawnerEntry spawnerEntry) {
|
||||
addPotentialSpawn(spawnerEntry.getSnapshot(), spawnerEntry.getSpawnWeight(), spawnerEntry.getSpawnRule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPotentialSpawns(Collection<SpawnerEntry> entries) {
|
||||
SimpleWeightedRandomList.a<MobSpawnerData> builder = SimpleWeightedRandomList.builder();
|
||||
for (SpawnerEntry spawnerEntry : entries) {
|
||||
NBTTagCompound compoundTag = ((CraftEntitySnapshot) spawnerEntry.getSnapshot()).getData();
|
||||
builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnerEntry.getSpawnRule()))), spawnerEntry.getSpawnWeight());
|
||||
}
|
||||
this.getSnapshot().getSpawner().spawnPotentials = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SpawnerEntry> getPotentialSpawns() {
|
||||
List<SpawnerEntry> entries = new ArrayList<>();
|
||||
|
||||
for (b<MobSpawnerData> entry : this.getSnapshot().getSpawner().spawnPotentials.unwrap()) { // PAIL rename Wrapper
|
||||
CraftEntitySnapshot snapshot = CraftEntitySnapshot.create(entry.getData().getEntityToSpawn());
|
||||
|
||||
if (snapshot != null) {
|
||||
SpawnRule rule = entry.getData().customSpawnRules().map(this::fromMinecraftRule).orElse(null);
|
||||
entries.add(new SpawnerEntry(snapshot, entry.getWeight().asInt(), rule));
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
private MobSpawnerData.a toMinecraftRule(SpawnRule rule) { // PAIL rename CustomSpawnRules
|
||||
if (rule == null) {
|
||||
return null;
|
||||
}
|
||||
return new MobSpawnerData.a(new InclusiveRange<>(rule.getMinBlockLight(), rule.getMaxBlockLight()), new InclusiveRange<>(rule.getMinSkyLight(), rule.getMaxSkyLight()));
|
||||
}
|
||||
|
||||
private SpawnRule fromMinecraftRule(MobSpawnerData.a rule) {
|
||||
InclusiveRange<Integer> blockLight = rule.blockLightLimit();
|
||||
InclusiveRange<Integer> skyLight = rule.skyLightLimit();
|
||||
|
||||
return new SpawnRule(blockLight.maxInclusive(), blockLight.maxInclusive(), skyLight.minInclusive(), skyLight.maxInclusive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreatureTypeName() {
|
||||
MobSpawnerData spawnData = this.getSnapshot().getSpawner().nextSpawnData;
|
||||
|
||||
@@ -24,6 +24,7 @@ import net.minecraft.world.entity.EntityFlying;
|
||||
import net.minecraft.world.entity.EntityLightning;
|
||||
import net.minecraft.world.entity.EntityLiving;
|
||||
import net.minecraft.world.entity.EntityTameableAnimal;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
import net.minecraft.world.entity.GlowSquid;
|
||||
import net.minecraft.world.entity.Interaction;
|
||||
import net.minecraft.world.entity.Marker;
|
||||
@@ -164,6 +165,7 @@ import net.minecraft.world.entity.vehicle.EntityMinecartTNT;
|
||||
import net.minecraft.world.phys.AxisAlignedBB;
|
||||
import org.bukkit.EntityEffect;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
@@ -177,8 +179,10 @@ import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
|
||||
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
|
||||
import org.bukkit.craftbukkit.util.CraftChatMessage;
|
||||
import org.bukkit.craftbukkit.util.CraftLocation;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
|
||||
import org.bukkit.craftbukkit.util.CraftVector;
|
||||
import org.bukkit.entity.EntitySnapshot;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
@@ -641,7 +645,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return entity.isAlive() && entity.valid && entity.isChunkLoaded();
|
||||
return entity.isAlive() && entity.valid && entity.isChunkLoaded() && isInWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1114,6 +1118,42 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
return CraftSpawnCategory.toBukkit(getHandle().getType().getCategory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInWorld() {
|
||||
return getHandle().inWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntitySnapshot createSnapshot() {
|
||||
return CraftEntitySnapshot.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity copy() {
|
||||
Entity copy = copy(getHandle().level());
|
||||
Preconditions.checkArgument(copy != null, "Error creating new entity.");
|
||||
|
||||
return copy.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity copy(Location location) {
|
||||
Preconditions.checkArgument(location.getWorld() != null, "Location has no world");
|
||||
|
||||
Entity copy = copy(((CraftWorld) location.getWorld()).getHandle());
|
||||
Preconditions.checkArgument(copy != null, "Error creating new entity.");
|
||||
|
||||
copy.setPos(location.getX(), location.getY(), location.getZ());
|
||||
return location.getWorld().addEntity(copy.getBukkitEntity());
|
||||
}
|
||||
|
||||
private Entity copy(net.minecraft.world.level.World level) {
|
||||
NBTTagCompound compoundTag = new NBTTagCompound();
|
||||
getHandle().saveAsPassenger(compoundTag, false);
|
||||
|
||||
return EntityTypes.loadEntityRecursive(compoundTag, level, java.util.function.Function.identity());
|
||||
}
|
||||
|
||||
public void storeBukkitValues(NBTTagCompound c) {
|
||||
if (!this.persistentDataContainer.isEmpty()) {
|
||||
c.put("BukkitValues", this.persistentDataContainer.toTagCompound());
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.bukkit.craftbukkit.entity;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntitySnapshot;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
public class CraftEntitySnapshot implements EntitySnapshot {
|
||||
private final NBTTagCompound data;
|
||||
private final EntityType type;
|
||||
|
||||
private CraftEntitySnapshot(NBTTagCompound data, EntityType type) {
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity createEntity(World world) {
|
||||
net.minecraft.world.entity.Entity internal = createInternal(world);
|
||||
|
||||
return internal.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity createEntity(Location location) {
|
||||
Preconditions.checkArgument(location.getWorld() != null, "Location has no world");
|
||||
|
||||
net.minecraft.world.entity.Entity internal = createInternal(location.getWorld());
|
||||
|
||||
internal.setPos(location.getX(), location.getY(), location.getZ());
|
||||
return location.getWorld().addEntity(internal.getBukkitEntity());
|
||||
}
|
||||
|
||||
private net.minecraft.world.entity.Entity createInternal(World world) {
|
||||
net.minecraft.world.level.World nms = ((CraftWorld) world).getHandle();
|
||||
net.minecraft.world.entity.Entity internal = EntityTypes.loadEntityRecursive(data, nms, Function.identity());
|
||||
if (internal == null) { // Try creating by type
|
||||
internal = CraftEntityType.bukkitToMinecraft(type).create(nms);
|
||||
}
|
||||
|
||||
Preconditions.checkArgument(internal != null, "Error creating new entity."); // This should only fail if the stored NBTTagCompound is malformed.
|
||||
internal.load(data);
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
public NBTTagCompound getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static CraftEntitySnapshot create(CraftEntity entity) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
if (!entity.getHandle().saveAsPassenger(tag, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CraftEntitySnapshot(tag, entity.getType());
|
||||
}
|
||||
|
||||
public static CraftEntitySnapshot create(NBTTagCompound tag, EntityType type) {
|
||||
if (tag == null || tag.isEmpty() || type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CraftEntitySnapshot(tag, type);
|
||||
}
|
||||
|
||||
public static CraftEntitySnapshot create(NBTTagCompound tag) {
|
||||
EntityType type = EntityTypes.by(tag).map(CraftEntityType::minecraftToBukkit).orElse(null);
|
||||
return create(tag, type);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.entity.memory.CraftMemoryMapper;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.inventory.CraftContainer;
|
||||
@@ -684,4 +685,14 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
||||
boolean success = getHandle().level().addFreshEntity(fireworks, SpawnReason.CUSTOM);
|
||||
return success ? (Firework) fireworks.getBukkitEntity() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity copy() {
|
||||
throw new UnsupportedOperationException("Cannot copy human entities");
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity copy(Location location) {
|
||||
throw new UnsupportedOperationException("Cannot copy human entities");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,12 +116,12 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
|
||||
if (entity.isAlive()) {
|
||||
// check if entity is still in region or if it got teleported outside it
|
||||
Preconditions.checkState(region.contains(entity.getX(), entity.getY(), entity.getZ()), "Entity %s is not in the region", entity);
|
||||
access.addFreshEntity(entity);
|
||||
access.addFreshEntityWithPassengers(entity);
|
||||
}
|
||||
}
|
||||
|
||||
for (net.minecraft.world.entity.Entity entity : outsideEntities) {
|
||||
access.addFreshEntity(entity);
|
||||
access.addFreshEntityWithPassengers(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,4 +250,9 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
|
||||
public void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
||||
entities.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.bukkit.craftbukkit.inventory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Map;
|
||||
@@ -9,7 +10,9 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.resources.MinecraftKey;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.serialization.DelegateDeserialization;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntitySnapshot;
|
||||
import org.bukkit.craftbukkit.util.CraftLegacy;
|
||||
import org.bukkit.entity.EntitySnapshot;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.meta.SpawnEggMeta;
|
||||
import org.bukkit.material.MaterialData;
|
||||
@@ -216,6 +219,17 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
|
||||
throw new UnsupportedOperationException("Must change item type to set spawned type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntitySnapshot getSpawnedEntity() {
|
||||
return CraftEntitySnapshot.create(this.entityTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpawnedEntity(EntitySnapshot snapshot) {
|
||||
Preconditions.checkArgument(snapshot.getEntityType().isSpawnable(), "Entity is not spawnable");
|
||||
this.entityTag = ((CraftEntitySnapshot) snapshot).getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean equalsCommon(CraftMetaItem meta) {
|
||||
if (!super.equalsCommon(meta)) {
|
||||
|
||||
Reference in New Issue
Block a user