Remove deprecated interfaces impl

This commit is contained in:
TheMode 2021-07-15 05:23:33 +02:00
parent 2e4975eb9a
commit 28b034103e
12 changed files with 41 additions and 226 deletions

View File

@ -15,16 +15,9 @@ import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.event.EventCallback;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.EventFilter;
import net.minestom.server.event.EventNode;
import net.minestom.server.event.entity.*;
import net.minestom.server.event.handler.EventHandler;
import net.minestom.server.event.trait.EntityEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
@ -65,7 +58,7 @@ import java.util.function.UnaryOperator;
* <p>
* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead.
*/
public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, DataContainer, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
private static final Map<Integer, Entity> ENTITY_BY_ID = new ConcurrentHashMap<>();
private static final Map<UUID, Entity> ENTITY_BY_UUID = new ConcurrentHashMap<>();
@ -103,7 +96,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
private final int id;
protected final Set<Player> viewers = ConcurrentHashMap.newKeySet();
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
private Data data;
private final NBTCompound nbtCompound = new NBTCompound();
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
@ -121,9 +113,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
private Duration customSynchronizationCooldown;
private long lastAbsoluteSynchronizationTime;
// Events
private final EventNode<EntityEvent> eventNode;
protected Metadata metadata = new Metadata(this);
protected EntityMeta entityMeta;
@ -159,8 +148,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
Entity.ENTITY_BY_ID.put(id, this);
Entity.ENTITY_BY_UUID.put(uuid, this);
this.eventNode = EventNode.value("entity-" + uuid, EventFilter.ENTITY, this::equals);
initializeDefaultGravity();
}
@ -405,16 +392,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
}
}
@Override
public Data getData() {
return data;
}
@Override
public void setData(@Nullable Data data) {
this.data = data;
}
@NotNull
@Override
public Set<Permission> getAllPermissions() {
@ -715,19 +692,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
}
}
@Override
public @NotNull EventNode<EntityEvent> getEventNode() {
return eventNode;
}
@Override
public synchronized <V extends EntityEvent> boolean addEventCallback(@NotNull Class<V> eventClass, @NotNull EventCallback<V> eventCallback) {
if (eventNode.getParent() == null) {
MinecraftServer.getGlobalEventHandler().addChild(eventNode);
}
return EventHandler.super.addEventCallback(eventClass, eventCallback);
}
/**
* Each entity has an unique id (server-wide) which will change after a restart.
*

View File

@ -7,6 +7,7 @@ import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.pathfinding.NavigableEntity;
import net.minestom.server.entity.pathfinding.Navigator;
import net.minestom.server.event.EventListener;
import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.network.ConnectionManager;
@ -54,12 +55,15 @@ public class FakePlayer extends Player implements NavigableEntity {
this.fakePlayerController = new FakePlayerController(this);
if (spawnCallback != null) {
addEventCallback(PlayerSpawnEvent.class,
event -> {
if (event.isFirstSpawn()) {
spawnCallback.accept(this);
}
});
// FIXME
MinecraftServer.getGlobalEventHandler().addListener(
EventListener.builder(PlayerSpawnEvent.class)
.expireCount(1)
.handler(event -> {
if (event.isFirstSpawn()) {
spawnCallback.accept(this);
}
}).build());
}
CONNECTION_MANAGER.startPlayState(this, option.isRegistered());

View File

@ -9,25 +9,18 @@ import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.ExperienceOrb;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.pathfinding.PFInstanceSpace;
import net.minestom.server.event.EventCallback;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.EventFilter;
import net.minestom.server.event.EventNode;
import net.minestom.server.event.handler.EventHandler;
import net.minestom.server.event.instance.AddEntityToInstanceEvent;
import net.minestom.server.event.instance.InstanceTickEvent;
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
import net.minestom.server.event.trait.InstanceEvent;
import net.minestom.server.instance.block.*;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.thread.ThreadProvider;
@ -61,7 +54,7 @@ import java.util.function.Consumer;
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
*/
public abstract class Instance implements BlockGetter, BlockSetter, Tickable, TagHandler, EventHandler<InstanceEvent>, DataContainer, PacketGroupingAudience {
public abstract class Instance implements BlockGetter, BlockSetter, Tickable, TagHandler, PacketGroupingAudience {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
@ -84,8 +77,6 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
// Field for tick events
private long lastTickAge = System.currentTimeMillis();
private final EventNode<InstanceEvent> eventNode;
// Entities present in this instance
protected final Set<Entity> entities = ConcurrentHashMap.newKeySet();
protected final Set<Player> players = ConcurrentHashMap.newKeySet();
@ -104,7 +95,6 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
// instance custom data
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
// the explosion supplier
private ExplosionSupplier explosionSupplier;
@ -129,8 +119,6 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
this.worldBorder = new WorldBorder(this);
this.eventNode = EventNode.value("instance-" + uniqueId, EventFilter.INSTANCE, this::equals);
this.pointers = Pointers.builder()
.withDynamic(Identity.UUID, this::getUniqueId)
.build();
@ -273,21 +261,6 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
*/
public abstract @NotNull Collection<@NotNull Chunk> getChunks();
/**
* Gets the instance {@link StorageLocation}.
*
* @return the {@link StorageLocation} of the instance
*/
@Nullable
public abstract StorageLocation getStorageLocation();
/**
* Changes the instance {@link StorageLocation}.
*
* @param storageLocation the new {@link StorageLocation} of the instance
*/
public abstract void setStorageLocation(@Nullable StorageLocation storageLocation);
/**
* When set to true, chunks will load automatically when requested.
* Otherwise using {@link #loadChunk(int, int)} will be required to even spawn a player
@ -578,35 +551,11 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
*
* @return the instance unique id
*/
@NotNull
public UUID getUniqueId() {
public @NotNull UUID getUniqueId() {
return uniqueId;
}
@Override
public Data getData() {
return data;
}
@Override
public void setData(@Nullable Data data) {
this.data = data;
}
@Override
public @NotNull EventNode<InstanceEvent> getEventNode() {
return eventNode;
}
@Override
public synchronized <V extends InstanceEvent> boolean addEventCallback(@NotNull Class<V> eventClass, @NotNull EventCallback<V> eventCallback) {
if (eventNode.getParent() == null) {
MinecraftServer.getGlobalEventHandler().addChild(eventNode);
}
return EventHandler.super.addEventCallback(eventClass, eventCallback);
}
// UNSAFE METHODS (need most of time to be synchronized)
// UNSAFE METHODS (need most of the time to be synchronized)
/**
* Used when called {@link Entity#setInstance(Instance)}, it is used to refresh viewable chunks

View File

@ -3,8 +3,6 @@ package net.minestom.server.instance;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.data.Data;
import net.minestom.server.data.SerializableData;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.instance.InstanceChunkLoadEvent;
@ -45,9 +43,6 @@ public class InstanceContainer extends Instance {
private static final String UUID_KEY = "uuid";
private static final String DATA_KEY = "data";
// the storage location of this instance, can be null
private StorageLocation storageLocation;
// the shared instances assigned to this instance
private final List<SharedInstance> sharedInstances = new CopyOnWriteArrayList<>();
@ -77,30 +72,17 @@ public class InstanceContainer extends Instance {
/**
* Creates an {@link InstanceContainer}.
*
* @param uniqueId the unique id of the instance
* @param dimensionType the dimension type of the instance
* @param storageLocation the {@link StorageLocation} of the instance,
* can be null if you do not wish to save the instance later on
* @param uniqueId the unique id of the instance
* @param dimensionType the dimension type of the instance
*/
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @Nullable StorageLocation storageLocation) {
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
super(uniqueId, dimensionType);
this.storageLocation = storageLocation;
// Set the default chunk supplier using DynamicChunk
setChunkSupplier(DynamicChunk::new);
// Set the default chunk loader which use the Anvil format
setChunkLoader(new AnvilLoader("world"));
// Get instance data from the saved data if a StorageLocation is defined
if (storageLocation != null) {
// Retrieve instance data
this.uniqueId = storageLocation.getOrDefault(UUID_KEY, UUID.class, uniqueId);
final Data data = storageLocation.getOrDefault(DATA_KEY, SerializableData.class, null);
setData(data);
}
}
@Override
@ -284,26 +266,6 @@ public class InstanceContainer extends Instance {
return chunks.get(index);
}
/**
* Saves the instance ({@link #getUniqueId()} {@link #getData()}) and call {@link #saveChunksToStorage()}.
* <p>
* WARNING: {@link #getData()} needs to be a {@link SerializableData} in order to be saved.
*/
public CompletableFuture<Void> saveInstance() {
Check.notNull(getStorageLocation(), "You cannot save the instance if no StorageLocation has been defined");
this.storageLocation.set(UUID_KEY, getUniqueId(), UUID.class);
final Data data = getData();
if (data != null) {
// Save the instance data
Check.stateCondition(!(data instanceof SerializableData),
"Instance#getData needs to be a SerializableData in order to be saved");
this.storageLocation.set(DATA_KEY, (SerializableData) getData(), SerializableData.class);
}
return saveChunksToStorage();
}
@Override
public @NotNull CompletableFuture<Void> saveChunkToStorage(@NotNull Chunk chunk) {
return chunkLoader.saveChunk(chunk);
@ -454,7 +416,7 @@ public class InstanceContainer extends Instance {
* @see #getSrcInstance() to retrieve the "creation source" of the copied instance
*/
public synchronized InstanceContainer copy() {
InstanceContainer copiedInstance = new InstanceContainer(UUID.randomUUID(), getDimensionType(), null);
InstanceContainer copiedInstance = new InstanceContainer(UUID.randomUUID(), getDimensionType());
copiedInstance.srcInstance = this;
copiedInstance.lastBlockChangeTime = lastBlockChangeTime;
@ -530,21 +492,11 @@ public class InstanceContainer extends Instance {
*
* @return the chunks of this instance
*/
@NotNull
public Collection<Chunk> getChunks() {
@Override
public @NotNull Collection<@NotNull Chunk> getChunks() {
return Collections.unmodifiableCollection(chunks.values());
}
@Override
public StorageLocation getStorageLocation() {
return storageLocation;
}
@Override
public void setStorageLocation(StorageLocation storageLocation) {
this.storageLocation = storageLocation;
}
/**
* Gets the {@link IChunkLoader} of this instance.
*

View File

@ -39,45 +39,20 @@ public final class InstanceManager {
* with the specified {@link DimensionType} and {@link StorageLocation}.
*
* @param dimensionType the {@link DimensionType} of the instance
* @param storageLocation the {@link StorageLocation} of the instance, can be null
* @return the created {@link InstanceContainer}
*/
@NotNull
public InstanceContainer createInstanceContainer(@NotNull DimensionType dimensionType, @Nullable StorageLocation storageLocation) {
final InstanceContainer instanceContainer = new InstanceContainer(UUID.randomUUID(), dimensionType, storageLocation);
public @NotNull InstanceContainer createInstanceContainer(@NotNull DimensionType dimensionType) {
final InstanceContainer instanceContainer = new InstanceContainer(UUID.randomUUID(), dimensionType);
registerInstance(instanceContainer);
return instanceContainer;
}
/**
* Creates and register an {@link InstanceContainer} with the specified {@link StorageLocation}.
*
* @param storageLocation the {@link StorageLocation} of the instance, can be null
* @return the created {@link InstanceContainer}
*/
@NotNull
public InstanceContainer createInstanceContainer(@Nullable StorageLocation storageLocation) {
return createInstanceContainer(DimensionType.OVERWORLD, storageLocation);
}
/**
* Creates and register an {@link InstanceContainer} with the specified {@link DimensionType}.
*
* @param dimensionType the {@link DimensionType} of the instance
* @return the created {@link InstanceContainer}
*/
@NotNull
public InstanceContainer createInstanceContainer(@NotNull DimensionType dimensionType) {
return createInstanceContainer(dimensionType, null);
}
/**
* Creates and register an {@link InstanceContainer}.
*
* @return the created {@link InstanceContainer}
*/
@NotNull
public InstanceContainer createInstanceContainer() {
public @NotNull InstanceContainer createInstanceContainer() {
return createInstanceContainer(DimensionType.OVERWORLD);
}
@ -90,8 +65,7 @@ public final class InstanceManager {
* @return the registered {@link SharedInstance}
* @throws NullPointerException if {@code sharedInstance} doesn't have an {@link InstanceContainer} assigned to it
*/
@NotNull
public SharedInstance registerSharedInstance(@NotNull SharedInstance sharedInstance) {
public @NotNull SharedInstance registerSharedInstance(@NotNull SharedInstance sharedInstance) {
final InstanceContainer instanceContainer = sharedInstance.getInstanceContainer();
Check.notNull(instanceContainer, "SharedInstance needs to have an InstanceContainer to be created!");
@ -107,8 +81,7 @@ public final class InstanceManager {
* @return the created {@link SharedInstance}
* @throws IllegalStateException if {@code instanceContainer} is not registered
*/
@NotNull
public SharedInstance createSharedInstance(@NotNull InstanceContainer instanceContainer) {
public @NotNull SharedInstance createSharedInstance(@NotNull InstanceContainer instanceContainer) {
Check.notNull(instanceContainer, "Instance container cannot be null when creating a SharedInstance!");
Check.stateCondition(!instanceContainer.isRegistered(), "The container needs to be register in the InstanceManager");
@ -149,8 +122,7 @@ public final class InstanceManager {
*
* @return an unmodifiable {@link Set} containing all the registered instances
*/
@NotNull
public Set<Instance> getInstances() {
public @NotNull Set<@NotNull Instance> getInstances() {
return Collections.unmodifiableSet(instances);
}
@ -160,8 +132,7 @@ public final class InstanceManager {
* @param uuid UUID of the instance
* @return the instance with the given UUID, null if not found
*/
@Nullable
public Instance getInstance(@NotNull UUID uuid) {
public @Nullable Instance getInstance(@NotNull UUID uuid) {
Optional<Instance> instance = getInstances()
.stream()
.filter(someInstance -> someInstance.getUniqueId().equals(uuid))
@ -181,5 +152,4 @@ public final class InstanceManager {
this.instances.add(instance);
MinecraftServer.getUpdateManager().signalInstanceCreate(instance);
}
}

View File

@ -4,10 +4,7 @@ import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.utils.chunk.ChunkCallback;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.UUID;
@ -88,16 +85,6 @@ public class SharedInstance extends Instance {
return instanceContainer.getChunks();
}
@Override
public StorageLocation getStorageLocation() {
return instanceContainer.getStorageLocation();
}
@Override
public void setStorageLocation(StorageLocation storageLocation) {
this.instanceContainer.setStorageLocation(storageLocation);
}
@Override
public void enableAutoChunkLoad(boolean enable) {
instanceContainer.enableAutoChunkLoad(enable);
@ -118,8 +105,7 @@ public class SharedInstance extends Instance {
*
* @return the associated {@link InstanceContainer}
*/
@NotNull
public InstanceContainer getInstanceContainer() {
public @NotNull InstanceContainer getInstanceContainer() {
return instanceContainer;
}
}

View File

@ -56,7 +56,7 @@ public class Main {
MinecraftServer.getSchedulerManager().buildShutdownTask(() -> System.out.println("Good night")).schedule();
MinecraftServer.getGlobalEventHandler().addEventCallback(ServerListPingEvent.class, event -> {
MinecraftServer.getGlobalEventHandler().addListener(ServerListPingEvent.class, event -> {
ResponseData responseData = event.getResponseData();
responseData.addEntry(NamedAndIdentified.named("The first line is separated from the others"));
responseData.addEntry(NamedAndIdentified.named("Could be a name, or a message"));

View File

@ -4,11 +4,7 @@ import com.google.common.collect.ImmutableList;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.ai.goal.RandomStrollGoal;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.entity.EntityAttackEvent;
import net.minestom.server.coordinate.Vec;
public class ChickenCreature extends EntityCreature {
@ -39,14 +35,6 @@ public class ChickenCreature extends EntityCreature {
// );
getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.1f);
addEventCallback(EntityAttackEvent.class, event -> {
//System.out.println("CALL ATTACK");
LivingEntity entity = (LivingEntity) event.getTarget();
Vec velocity = getPosition().direction().mul(6).withY(4);
entity.damage(DamageType.fromEntity(this), -1);
entity.setVelocity(velocity);
});
}
@Override

View File

@ -17,7 +17,7 @@ public class DisableEarlyLoad extends Extension {
@Override
public void initialize() {
// force load of InstanceContainer class
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD, null);
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
System.out.println(c.toString());
try {
Assertions.assertFalse(MixinIntoMinestomCore.success, "InstanceContainer must NOT have been mixed in with improveextensions.InstanceContainerMixin, because early loading has been disabled");

View File

@ -19,7 +19,7 @@ public class MixinIntoMinestomCore extends Extension {
@Override
public void initialize() {
// force load of InstanceContainer class
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD, null);
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
System.out.println(c.toString());
try {
Assertions.assertTrue(success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin");

View File

@ -24,7 +24,7 @@ public class MixinIntoMinestomCoreWithJava9ModuleOnClasspath extends Extension {
List mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn("Test");
// force load of InstanceContainer class
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD, null);
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
System.out.println(c.toString());
try {
Assertions.assertTrue(MixinIntoMinestomCore.success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin");

View File

@ -4,7 +4,6 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.event.EventCallback;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.entity.EntityTickEvent;
import net.minestom.server.event.instance.InstanceTickEvent;
@ -16,6 +15,7 @@ import org.junit.jupiter.api.Assertions;
import org.opentest4j.AssertionFailedError;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public class UnloadCallbacksExtension extends Extension {
@ -25,7 +25,7 @@ public class UnloadCallbacksExtension extends Extension {
private boolean tickedScheduledTransient = false;
private boolean zombieTicked = false;
private boolean instanceTicked = false;
private final EventCallback<InstanceTickEvent> callback = this::onTick;
private final Consumer<InstanceTickEvent> callback = this::onTick;
private void onTick(InstanceTickEvent e) {
ticked1 = true;
@ -35,20 +35,22 @@ public class UnloadCallbacksExtension extends Extension {
public void initialize() {
GlobalEventHandler globalEvents = MinecraftServer.getGlobalEventHandler();
// this callback will be automatically removed when unloading the extension
globalEvents.addEventCallback(InstanceTickEvent.class, callback);
globalEvents.addListener(InstanceTickEvent.class, callback);
// this one too
globalEvents.addEventCallback(InstanceTickEvent.class, e -> ticked2 = true);
globalEvents.addListener(InstanceTickEvent.class, e -> ticked2 = true);
Instance instance = MinecraftServer.getInstanceManager().getInstances().stream().findFirst().orElseThrow();
// add an event callback on an instance
instance.addEventCallback(InstanceTickEvent.class, e -> instanceTicked = true);
globalEvents.addListener(InstanceTickEvent.class, e -> instanceTicked = true);
instance.loadChunk(0, 0);
// add an event callback on an entity
EntityCreature zombie = new EntityCreature(EntityType.ZOMBIE);
zombie.addEventCallback(EntityTickEvent.class, e -> {
zombieTicked = true;
globalEvents.addListener(EntityTickEvent.class, entityTickEvent -> {
if (entityTickEvent.getEntity() == zombie) {
zombieTicked = true;
}
});
zombie.setInstance(instance, new Vec(8, 64, 8) /* middle of chunk */);