2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.instance;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2021-12-23 23:44:22 +01:00
|
|
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
|
2021-06-10 14:53:34 +02:00
|
|
|
import net.kyori.adventure.identity.Identity;
|
|
|
|
import net.kyori.adventure.pointer.Pointers;
|
2022-03-03 07:44:57 +01:00
|
|
|
import net.minestom.server.MinecraftServer;
|
2022-04-17 06:19:14 +02:00
|
|
|
import net.minestom.server.ServerProcess;
|
2021-03-11 20:54:30 +01:00
|
|
|
import net.minestom.server.Tickable;
|
2021-03-25 14:14:09 +01:00
|
|
|
import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
2021-07-08 18:56:40 +02:00
|
|
|
import net.minestom.server.coordinate.Point;
|
2021-03-21 11:38:48 +01:00
|
|
|
import net.minestom.server.entity.Entity;
|
|
|
|
import net.minestom.server.entity.EntityCreature;
|
|
|
|
import net.minestom.server.entity.ExperienceOrb;
|
|
|
|
import net.minestom.server.entity.Player;
|
2022-02-13 12:34:27 +01:00
|
|
|
import net.minestom.server.event.EventDispatcher;
|
2022-04-17 06:19:14 +02:00
|
|
|
import net.minestom.server.event.EventFilter;
|
|
|
|
import net.minestom.server.event.EventHandler;
|
|
|
|
import net.minestom.server.event.EventNode;
|
2020-12-08 00:16:46 +01:00
|
|
|
import net.minestom.server.event.instance.InstanceTickEvent;
|
2022-04-17 06:19:14 +02:00
|
|
|
import net.minestom.server.event.trait.InstanceEvent;
|
2021-11-12 18:07:42 +01:00
|
|
|
import net.minestom.server.instance.block.Block;
|
2023-01-17 00:57:18 +01:00
|
|
|
import net.minestom.server.instance.block.BlockFace;
|
2021-11-12 18:07:42 +01:00
|
|
|
import net.minestom.server.instance.block.BlockHandler;
|
2022-04-08 07:19:52 +02:00
|
|
|
import net.minestom.server.instance.generator.Generator;
|
2024-02-10 21:56:57 +01:00
|
|
|
import net.minestom.server.instance.light.Light;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.BlockActionPacket;
|
2020-07-22 20:54:30 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
|
2022-05-04 13:25:24 +02:00
|
|
|
import net.minestom.server.snapshot.*;
|
2021-06-26 00:31:04 +02:00
|
|
|
import net.minestom.server.tag.TagHandler;
|
2022-03-20 01:47:57 +01:00
|
|
|
import net.minestom.server.tag.Taggable;
|
2022-01-05 09:01:21 +01:00
|
|
|
import net.minestom.server.thread.ThreadDispatcher;
|
2021-12-16 00:15:55 +01:00
|
|
|
import net.minestom.server.timer.Schedulable;
|
|
|
|
import net.minestom.server.timer.Scheduler;
|
2022-03-03 07:44:57 +01:00
|
|
|
import net.minestom.server.utils.ArrayUtils;
|
2023-07-04 23:44:30 +02:00
|
|
|
import net.minestom.server.utils.NamespaceID;
|
2020-11-13 07:43:35 +01:00
|
|
|
import net.minestom.server.utils.PacketUtils;
|
2022-04-12 23:50:45 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkCache;
|
2023-05-28 01:41:14 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkSupplier;
|
2020-05-25 13:46:48 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
2021-03-31 19:17:37 +02:00
|
|
|
import net.minestom.server.utils.time.Cooldown;
|
2020-05-01 21:54:01 +02:00
|
|
|
import net.minestom.server.utils.time.TimeUnit;
|
2020-05-27 19:43:08 +02:00
|
|
|
import net.minestom.server.utils.validate.Check;
|
2020-07-13 14:12:21 +02:00
|
|
|
import net.minestom.server.world.DimensionType;
|
2021-03-26 21:31:57 +01:00
|
|
|
import org.jetbrains.annotations.ApiStatus;
|
2020-10-24 10:46:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
2022-02-09 23:07:18 +01:00
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2021-06-30 01:46:03 +02:00
|
|
|
import java.time.Duration;
|
2019-08-24 21:41:43 +02:00
|
|
|
import java.util.*;
|
2021-07-11 02:54:02 +02:00
|
|
|
import java.util.concurrent.CompletableFuture;
|
2022-03-03 07:44:57 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
2019-08-23 23:55:09 +02:00
|
|
|
import java.util.function.Consumer;
|
2021-11-01 18:04:00 +01:00
|
|
|
import java.util.stream.Collectors;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2020-07-21 18:48:15 +02:00
|
|
|
/**
|
2020-10-13 05:40:23 +02:00
|
|
|
* Instances are what are called "worlds" in Minecraft, you can add an entity in it using {@link Entity#setInstance(Instance)}.
|
2020-07-21 18:48:15 +02:00
|
|
|
* <p>
|
|
|
|
* An instance has entities and chunks, each instance contains its own entity list but the
|
2020-10-13 04:31:03 +02:00
|
|
|
* chunk implementation has to be defined, see {@link InstanceContainer}.
|
|
|
|
* <p>
|
2020-10-13 05:40:23 +02:00
|
|
|
* WARNING: when making your own implementation registering the instance manually is required
|
|
|
|
* with {@link InstanceManager#registerInstance(Instance)}, and
|
2022-01-05 09:01:21 +01:00
|
|
|
* you need to be sure to signal the {@link ThreadDispatcher} of every partition/element changes.
|
2020-07-21 18:48:15 +02:00
|
|
|
*/
|
2022-04-17 06:19:14 +02:00
|
|
|
public abstract class Instance implements Block.Getter, Block.Setter,
|
|
|
|
Tickable, Schedulable, Snapshotable, EventHandler<InstanceEvent>, Taggable, PacketGroupingAudience {
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2020-08-15 13:32:36 +02:00
|
|
|
private boolean registered;
|
|
|
|
|
2020-08-19 01:24:51 +02:00
|
|
|
private final DimensionType dimensionType;
|
2023-07-04 23:44:30 +02:00
|
|
|
private final String dimensionName;
|
2020-04-27 18:46:39 +02:00
|
|
|
|
2020-08-19 01:24:51 +02:00
|
|
|
private final WorldBorder worldBorder;
|
2020-05-26 19:22:47 +02:00
|
|
|
|
2020-07-22 20:54:30 +02:00
|
|
|
// Tick since the creation of the instance
|
|
|
|
private long worldAge;
|
|
|
|
|
|
|
|
// The time of the instance
|
|
|
|
private long time;
|
|
|
|
private int timeRate = 1;
|
2021-06-30 01:46:03 +02:00
|
|
|
private Duration timeUpdate = Duration.of(1, TimeUnit.SECOND);
|
2020-07-22 20:54:30 +02:00
|
|
|
private long lastTimeUpdate;
|
|
|
|
|
2024-03-19 17:44:51 +01:00
|
|
|
// Weather of the instance
|
|
|
|
private Weather targetWeather = new Weather(false, 0, 0);
|
|
|
|
private Weather currentWeather = new Weather(false, 0, 0);
|
|
|
|
private int remainingWeatherTransitionTicks;
|
|
|
|
|
2020-12-08 00:16:46 +01:00
|
|
|
// Field for tick events
|
|
|
|
private long lastTickAge = System.currentTimeMillis();
|
|
|
|
|
2021-11-01 18:04:00 +01:00
|
|
|
private final EntityTracker entityTracker = new EntityTrackerImpl();
|
2020-10-10 14:39:56 +02:00
|
|
|
|
2022-04-12 23:50:45 +02:00
|
|
|
private final ChunkCache blockRetriever = new ChunkCache(this, null, null);
|
|
|
|
|
2020-10-10 14:39:56 +02:00
|
|
|
// the uuid of this instance
|
2020-06-01 22:11:47 +02:00
|
|
|
protected UUID uniqueId;
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2020-10-12 06:41:47 +02:00
|
|
|
// instance custom data
|
2024-01-16 15:18:11 +01:00
|
|
|
protected TagHandler tagHandler = TagHandler.newHandler();
|
2021-12-16 00:15:55 +01:00
|
|
|
private final Scheduler scheduler = Scheduler.newScheduler();
|
2022-04-17 06:19:14 +02:00
|
|
|
private final EventNode<InstanceEvent> eventNode;
|
2021-12-16 00:15:55 +01:00
|
|
|
|
2020-10-12 06:41:47 +02:00
|
|
|
// the explosion supplier
|
2020-05-14 15:33:36 +02:00
|
|
|
private ExplosionSupplier explosionSupplier;
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2021-06-10 14:53:34 +02:00
|
|
|
// Adventure
|
2021-06-15 15:06:56 +02:00
|
|
|
private final Pointers pointers;
|
2021-06-10 14:53:34 +02:00
|
|
|
|
2020-08-30 21:42:21 +02:00
|
|
|
/**
|
2020-10-26 01:30:32 +01:00
|
|
|
* Creates a new instance.
|
2020-08-30 21:42:21 +02:00
|
|
|
*
|
2020-10-10 14:39:56 +02:00
|
|
|
* @param uniqueId the {@link UUID} of the instance
|
|
|
|
* @param dimensionType the {@link DimensionType} of the instance
|
2020-08-30 21:42:21 +02:00
|
|
|
*/
|
2020-10-26 01:30:32 +01:00
|
|
|
public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
|
2023-07-04 23:44:30 +02:00
|
|
|
this(uniqueId, dimensionType, dimensionType.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
|
|
|
*
|
|
|
|
* @param uniqueId the {@link UUID} of the instance
|
|
|
|
* @param dimensionType the {@link DimensionType} of the instance
|
|
|
|
*/
|
|
|
|
public Instance(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType, @NotNull NamespaceID dimensionName) {
|
2021-01-07 03:46:58 +01:00
|
|
|
Check.argCondition(!dimensionType.isRegistered(),
|
|
|
|
"The dimension " + dimensionType.getName() + " is not registered! Please use DimensionTypeManager#addDimension");
|
2019-08-24 21:41:43 +02:00
|
|
|
this.uniqueId = uniqueId;
|
2020-07-13 14:12:21 +02:00
|
|
|
this.dimensionType = dimensionType;
|
2023-07-04 23:44:30 +02:00
|
|
|
this.dimensionName = dimensionName.asString();
|
2020-05-26 19:22:47 +02:00
|
|
|
|
|
|
|
this.worldBorder = new WorldBorder(this);
|
2021-06-04 04:10:13 +02:00
|
|
|
|
2021-06-15 15:06:56 +02:00
|
|
|
this.pointers = Pointers.builder()
|
|
|
|
.withDynamic(Identity.UUID, this::getUniqueId)
|
|
|
|
.build();
|
2022-04-17 06:19:14 +02:00
|
|
|
|
|
|
|
final ServerProcess process = MinecraftServer.process();
|
|
|
|
if (process != null) {
|
|
|
|
this.eventNode = process.eventHandler().map(this, EventFilter.INSTANCE);
|
|
|
|
} else {
|
|
|
|
// Local nodes require a server process
|
|
|
|
this.eventNode = null;
|
|
|
|
}
|
2019-08-24 21:41:43 +02:00
|
|
|
}
|
2019-08-18 23:52:11 +02:00
|
|
|
|
2020-09-19 19:06:21 +02:00
|
|
|
/**
|
2020-10-26 01:30:32 +01:00
|
|
|
* Schedules a task to be run during the next instance tick.
|
2020-09-19 19:06:21 +02:00
|
|
|
*
|
|
|
|
* @param callback the task to execute during the next instance tick
|
|
|
|
*/
|
2020-10-26 01:30:32 +01:00
|
|
|
public void scheduleNextTick(@NotNull Consumer<Instance> callback) {
|
2021-12-16 00:15:55 +01:00
|
|
|
this.scheduler.scheduleNextTick(() -> callback.accept(this));
|
2020-06-28 23:11:40 +02:00
|
|
|
}
|
|
|
|
|
2023-07-16 13:48:05 +02:00
|
|
|
@Override
|
|
|
|
public void setBlock(int x, int y, int z, @NotNull Block block) {
|
|
|
|
setBlock(x, y, z, block, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setBlock(@NotNull Point blockPosition, @NotNull Block block, boolean doBlockUpdates) {
|
|
|
|
setBlock(blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), block, doBlockUpdates);
|
|
|
|
}
|
|
|
|
|
|
|
|
public abstract void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates);
|
|
|
|
|
2021-06-23 20:18:34 +02:00
|
|
|
@ApiStatus.Internal
|
2023-07-16 13:48:05 +02:00
|
|
|
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
|
|
|
|
return placeBlock(placement, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ApiStatus.Internal
|
|
|
|
public abstract boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
|
|
|
* and send particle packets
|
|
|
|
*
|
|
|
|
* @param player the {@link Player} who break the block
|
|
|
|
* @param blockPosition the position of the broken block
|
|
|
|
* @return true if the block has been broken, false if it has been cancelled
|
|
|
|
*/
|
|
|
|
@ApiStatus.Internal
|
|
|
|
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) {
|
|
|
|
return breakBlock(player, blockPosition, blockFace, true);
|
|
|
|
}
|
2021-06-22 23:51:01 +02:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
|
|
|
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
|
|
|
* and send particle packets
|
|
|
|
*
|
2020-10-12 06:41:47 +02:00
|
|
|
* @param player the {@link Player} who break the block
|
2021-07-05 09:10:03 +02:00
|
|
|
* @param blockPosition the position of the broken block
|
2023-07-16 13:48:05 +02:00
|
|
|
* @param doBlockUpdates true to do block updates, false otherwise
|
2020-05-27 20:30:13 +02:00
|
|
|
* @return true if the block has been broken, false if it has been cancelled
|
|
|
|
*/
|
2021-06-23 20:18:34 +02:00
|
|
|
@ApiStatus.Internal
|
2023-07-16 13:48:05 +02:00
|
|
|
public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates);
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Forces the generation of a {@link Chunk}, even if no file and {@link ChunkGenerator} are defined.
|
2020-05-27 20:30:13 +02:00
|
|
|
*
|
2021-07-11 02:54:02 +02:00
|
|
|
* @param chunkX the chunk X
|
|
|
|
* @param chunkZ the chunk Z
|
2021-07-11 03:26:08 +02:00
|
|
|
* @return a {@link CompletableFuture} completed once the chunk has been loaded
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @NotNull CompletableFuture<@NotNull Chunk> loadChunk(int chunkX, int chunkZ);
|
2019-08-20 22:40:57 +02:00
|
|
|
|
2021-07-11 03:56:01 +02:00
|
|
|
/**
|
|
|
|
* Loads the chunk at the given {@link Point} with a callback.
|
|
|
|
*
|
|
|
|
* @param point the chunk position
|
|
|
|
*/
|
|
|
|
public @NotNull CompletableFuture<@NotNull Chunk> loadChunk(@NotNull Point point) {
|
2021-09-04 15:29:20 +02:00
|
|
|
return loadChunk(point.chunkX(), point.chunkZ());
|
2021-07-11 03:56:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Loads the chunk if the chunk is already loaded or if
|
2020-10-12 06:41:47 +02:00
|
|
|
* {@link #hasEnabledAutoChunkLoad()} returns true.
|
2020-05-27 20:30:13 +02:00
|
|
|
*
|
2021-07-11 02:54:02 +02:00
|
|
|
* @param chunkX the chunk X
|
|
|
|
* @param chunkZ the chunk Z
|
2021-07-11 03:26:08 +02:00
|
|
|
* @return a {@link CompletableFuture} completed once the chunk has been processed, can be null if not loaded
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @NotNull CompletableFuture<@Nullable Chunk> loadOptionalChunk(int chunkX, int chunkZ);
|
2019-08-25 20:03:43 +02:00
|
|
|
|
2021-07-11 03:56:01 +02:00
|
|
|
/**
|
|
|
|
* Loads a {@link Chunk} (if {@link #hasEnabledAutoChunkLoad()} returns true)
|
|
|
|
* at the given {@link Point} with a callback.
|
|
|
|
*
|
|
|
|
* @param point the chunk position
|
|
|
|
* @return a {@link CompletableFuture} completed once the chunk has been processed, null if not loaded
|
|
|
|
*/
|
|
|
|
public @NotNull CompletableFuture<@Nullable Chunk> loadOptionalChunk(@NotNull Point point) {
|
2021-09-04 15:29:20 +02:00
|
|
|
return loadOptionalChunk(point.chunkX(), point.chunkZ());
|
2021-07-11 03:56:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Schedules the removal of a {@link Chunk}, this method does not promise when it will be done.
|
2020-06-02 00:03:03 +02:00
|
|
|
* <p>
|
2020-10-12 06:41:47 +02:00
|
|
|
* WARNING: during unloading, all entities other than {@link Player} will be removed.
|
2020-06-02 00:03:03 +02:00
|
|
|
*
|
2020-05-27 20:30:13 +02:00
|
|
|
* @param chunk the chunk to unload
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public abstract void unloadChunk(@NotNull Chunk chunk);
|
2020-04-20 23:43:09 +02:00
|
|
|
|
2021-07-11 03:56:01 +02:00
|
|
|
/**
|
|
|
|
* Unloads the chunk at the given position.
|
|
|
|
*
|
|
|
|
* @param chunkX the chunk X
|
|
|
|
* @param chunkZ the chunk Z
|
|
|
|
*/
|
|
|
|
public void unloadChunk(int chunkX, int chunkZ) {
|
|
|
|
final Chunk chunk = getChunk(chunkX, chunkZ);
|
|
|
|
Check.notNull(chunk, "The chunk at {0}:{1} is already unloaded", chunkX, chunkZ);
|
|
|
|
unloadChunk(chunk);
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the loaded {@link Chunk} at a position.
|
2020-10-04 23:33:36 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: this should only return already-loaded chunk, use {@link #loadChunk(int, int)} or similar to load one instead.
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2020-05-27 20:30:13 +02:00
|
|
|
* @param chunkX the chunk X
|
|
|
|
* @param chunkZ the chunk Z
|
|
|
|
* @return the chunk at the specified position, null if not loaded
|
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @Nullable Chunk getChunk(int chunkX, int chunkZ);
|
2019-08-20 22:40:57 +02:00
|
|
|
|
2021-09-19 20:37:39 +02:00
|
|
|
/**
|
|
|
|
* @param chunkX the chunk X
|
|
|
|
* @param chunkZ this chunk Z
|
|
|
|
* @return true if the chunk is loaded
|
|
|
|
*/
|
2021-09-19 20:54:13 +02:00
|
|
|
public boolean isChunkLoaded(int chunkX, int chunkZ) {
|
|
|
|
return getChunk(chunkX, chunkZ) != null;
|
|
|
|
}
|
2021-09-19 20:37:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param point coordinate of a block or other
|
|
|
|
* @return true if the chunk is loaded
|
|
|
|
*/
|
2021-09-19 20:54:13 +02:00
|
|
|
public boolean isChunkLoaded(Point point) {
|
|
|
|
return isChunkLoaded(point.chunkX(), point.chunkZ());
|
|
|
|
}
|
2021-09-19 20:37:39 +02:00
|
|
|
|
2021-08-09 01:16:51 +02:00
|
|
|
/**
|
|
|
|
* Saves the current instance tags.
|
|
|
|
* <p>
|
|
|
|
* Warning: only the global instance data will be saved, not chunks.
|
|
|
|
* You would need to call {@link #saveChunksToStorage()} too.
|
|
|
|
*
|
|
|
|
* @return the future called once the instance data has been saved
|
|
|
|
*/
|
|
|
|
@ApiStatus.Experimental
|
|
|
|
public abstract @NotNull CompletableFuture<Void> saveInstance();
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Saves a {@link Chunk} to permanent storage.
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2021-07-11 02:54:02 +02:00
|
|
|
* @param chunk the {@link Chunk} to save
|
2021-07-11 03:26:08 +02:00
|
|
|
* @return future called when the chunk is done saving
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @NotNull CompletableFuture<Void> saveChunkToStorage(@NotNull Chunk chunk);
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Saves multiple chunks to permanent storage.
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2021-07-11 03:26:08 +02:00
|
|
|
* @return future called when the chunks are done saving
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @NotNull CompletableFuture<Void> saveChunksToStorage();
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2022-04-08 07:19:52 +02:00
|
|
|
* Changes the instance {@link ChunkGenerator}.
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2022-04-08 07:19:52 +02:00
|
|
|
* @param chunkGenerator the new {@link ChunkGenerator} of the instance
|
|
|
|
* @deprecated Use {@link #setGenerator(Generator)}
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2022-04-08 07:19:52 +02:00
|
|
|
@Deprecated
|
|
|
|
public void setChunkGenerator(@Nullable ChunkGenerator chunkGenerator) {
|
|
|
|
setGenerator(chunkGenerator != null ? new ChunkGeneratorCompatibilityLayer(chunkGenerator) : null);
|
|
|
|
}
|
2020-05-27 20:30:13 +02:00
|
|
|
|
2023-05-28 01:41:14 +02:00
|
|
|
public abstract void setChunkSupplier(@NotNull ChunkSupplier chunkSupplier);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the chunk supplier of the instance.
|
|
|
|
* @return the chunk supplier of the instance
|
|
|
|
*/
|
|
|
|
public abstract ChunkSupplier getChunkSupplier();
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2022-04-08 07:19:52 +02:00
|
|
|
* Gets the generator associated with the instance
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2022-04-08 07:19:52 +02:00
|
|
|
* @return the generator if any
|
|
|
|
*/
|
|
|
|
public abstract @Nullable Generator generator();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the generator of the instance
|
|
|
|
*
|
|
|
|
* @param generator the new generator, or null to disable generation
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2022-04-08 07:19:52 +02:00
|
|
|
public abstract void setGenerator(@Nullable Generator generator);
|
2019-08-19 17:04:19 +02:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-12-13 01:33:54 +01:00
|
|
|
* Gets all the instance's loaded chunks.
|
2020-07-21 18:48:15 +02:00
|
|
|
*
|
2020-12-13 01:33:54 +01:00
|
|
|
* @return an unmodifiable containing all the instance chunks
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-11 03:26:08 +02:00
|
|
|
public abstract @NotNull Collection<@NotNull Chunk> getChunks();
|
2019-08-18 23:52:11 +02:00
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-01 19:57:19 +02:00
|
|
|
* When set to true, chunks will load automatically when requested.
|
2020-05-27 20:30:13 +02:00
|
|
|
* Otherwise using {@link #loadChunk(int, int)} will be required to even spawn a player
|
|
|
|
*
|
|
|
|
* @param enable enable the auto chunk load
|
|
|
|
*/
|
2019-08-25 20:03:43 +02:00
|
|
|
public abstract void enableAutoChunkLoad(boolean enable);
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the instance should auto load chunks.
|
2020-08-31 00:41:19 +02:00
|
|
|
*
|
2020-05-27 20:30:13 +02:00
|
|
|
* @return true if auto chunk load is enabled, false otherwise
|
|
|
|
*/
|
2019-08-25 20:03:43 +02:00
|
|
|
public abstract boolean hasEnabledAutoChunkLoad();
|
|
|
|
|
2020-04-27 20:33:08 +02:00
|
|
|
/**
|
2023-10-07 20:49:16 +02:00
|
|
|
* Determines whether a position in the void.
|
2020-04-27 22:38:11 +02:00
|
|
|
*
|
2021-07-06 20:44:24 +02:00
|
|
|
* @param point the point in the world
|
|
|
|
* @return true if the point is inside the void
|
2020-04-27 20:33:08 +02:00
|
|
|
*/
|
2021-07-06 20:44:24 +02:00
|
|
|
public abstract boolean isInVoid(@NotNull Point point);
|
2020-04-27 20:33:08 +02:00
|
|
|
|
2020-08-15 13:32:36 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the instance has been registered in {@link InstanceManager}.
|
2020-08-15 13:32:36 +02:00
|
|
|
*
|
|
|
|
* @return true if the instance has been registered
|
|
|
|
*/
|
|
|
|
public boolean isRegistered() {
|
|
|
|
return registered;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the registered field.
|
2020-08-15 13:32:36 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* WARNING: should only be used by {@link InstanceManager}.
|
2020-08-15 13:32:36 +02:00
|
|
|
*
|
|
|
|
* @param registered true to mark the instance as registered
|
|
|
|
*/
|
|
|
|
protected void setRegistered(boolean registered) {
|
|
|
|
this.registered = registered;
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the instance {@link DimensionType}.
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-05-27 20:30:13 +02:00
|
|
|
* @return the dimension of the instance
|
|
|
|
*/
|
2020-07-13 14:12:21 +02:00
|
|
|
public DimensionType getDimensionType() {
|
|
|
|
return dimensionType;
|
2020-04-27 18:46:39 +02:00
|
|
|
}
|
|
|
|
|
2023-07-04 23:44:30 +02:00
|
|
|
/**
|
|
|
|
* Gets the instance dimension name.
|
|
|
|
* @return the dimension name of the instance
|
|
|
|
*/
|
|
|
|
public @NotNull String getDimensionName() {
|
|
|
|
return dimensionName;
|
|
|
|
}
|
|
|
|
|
2020-07-22 20:54:30 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the age of this instance in tick.
|
2020-07-22 20:54:30 +02:00
|
|
|
*
|
|
|
|
* @return the age of this instance in tick
|
|
|
|
*/
|
|
|
|
public long getWorldAge() {
|
|
|
|
return worldAge;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the current time in the instance (sun/moon).
|
2020-07-22 20:54:30 +02:00
|
|
|
*
|
|
|
|
* @return the time in the instance
|
|
|
|
*/
|
|
|
|
public long getTime() {
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the current time in the instance, from 0 to 24000.
|
2020-07-22 20:54:30 +02:00
|
|
|
* <p>
|
2021-02-03 07:31:15 +01:00
|
|
|
* If the time is negative, the vanilla client will not move the sun.
|
|
|
|
* <p>
|
2020-07-22 20:54:30 +02:00
|
|
|
* 0 = sunrise
|
|
|
|
* 6000 = noon
|
|
|
|
* 12000 = sunset
|
|
|
|
* 18000 = midnight
|
|
|
|
* <p>
|
|
|
|
* This method is unaffected by {@link #getTimeRate()}
|
2020-07-22 21:00:57 +02:00
|
|
|
* <p>
|
|
|
|
* It does send the new time to all players in the instance, unaffected by {@link #getTimeUpdate()}
|
2020-07-22 20:54:30 +02:00
|
|
|
*
|
|
|
|
* @param time the new time of the instance
|
|
|
|
*/
|
|
|
|
public void setTime(long time) {
|
|
|
|
this.time = time;
|
2020-11-13 07:43:35 +01:00
|
|
|
PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
2020-07-22 20:54:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the rate of the time passing, it is 1 by default
|
2020-07-22 20:54:30 +02:00
|
|
|
*
|
|
|
|
* @return the time rate of the instance
|
|
|
|
*/
|
|
|
|
public int getTimeRate() {
|
|
|
|
return timeRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-16 09:12:31 +02:00
|
|
|
* Changes the time rate of the instance
|
2020-07-22 20:54:30 +02:00
|
|
|
* <p>
|
|
|
|
* 1 is the default value and can be set to 0 to be completely disabled (constant time)
|
|
|
|
*
|
|
|
|
* @param timeRate the new time rate of the instance
|
|
|
|
* @throws IllegalStateException if {@code timeRate} is lower than 0
|
|
|
|
*/
|
|
|
|
public void setTimeRate(int timeRate) {
|
|
|
|
Check.stateCondition(timeRate < 0, "The time rate cannot be lower than 0");
|
|
|
|
this.timeRate = timeRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the rate at which the client is updated with the current instance time
|
2020-07-22 20:54:30 +02:00
|
|
|
*
|
|
|
|
* @return the client update rate for time related packet
|
|
|
|
*/
|
2021-07-25 06:25:32 +02:00
|
|
|
public @Nullable Duration getTimeUpdate() {
|
2020-07-22 20:54:30 +02:00
|
|
|
return timeUpdate;
|
|
|
|
}
|
|
|
|
|
2021-06-30 01:46:03 +02:00
|
|
|
/**
|
|
|
|
* Changes the rate at which the client is updated about the time
|
|
|
|
* <p>
|
|
|
|
* Setting it to null means that the client will never know about time change
|
|
|
|
* (but will still change server-side)
|
|
|
|
*
|
|
|
|
* @param timeUpdate the new update rate concerning time
|
|
|
|
*/
|
|
|
|
public void setTimeUpdate(@Nullable Duration timeUpdate) {
|
2020-07-22 20:54:30 +02:00
|
|
|
this.timeUpdate = timeUpdate;
|
|
|
|
}
|
|
|
|
|
2020-09-19 19:06:21 +02:00
|
|
|
/**
|
2020-11-13 07:43:35 +01:00
|
|
|
* Creates a {@link TimeUpdatePacket} with the current age and time of this instance
|
2020-09-19 19:06:21 +02:00
|
|
|
*
|
2020-09-23 21:08:36 +02:00
|
|
|
* @return the {@link TimeUpdatePacket} with this instance data
|
2020-09-19 19:06:21 +02:00
|
|
|
*/
|
2021-11-01 18:04:00 +01:00
|
|
|
@ApiStatus.Internal
|
|
|
|
public @NotNull TimeUpdatePacket createTimePacket() {
|
2021-08-11 13:39:36 +02:00
|
|
|
long time = this.time;
|
2021-08-11 03:41:17 +02:00
|
|
|
if (timeRate == 0) {
|
2021-08-11 13:39:36 +02:00
|
|
|
//Negative values stop the sun and moon from moving
|
2021-08-11 03:41:17 +02:00
|
|
|
//0 as a long cannot be negative
|
2021-08-11 13:39:36 +02:00
|
|
|
time = time == 0 ? -24000L : -Math.abs(time);
|
2021-08-11 03:41:17 +02:00
|
|
|
}
|
2021-08-11 13:39:36 +02:00
|
|
|
return new TimeUpdatePacket(worldAge, time);
|
2020-07-22 21:00:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the instance {@link WorldBorder};
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return the {@link WorldBorder} linked to the instance
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-07-25 06:25:32 +02:00
|
|
|
public @NotNull WorldBorder getWorldBorder() {
|
2020-05-26 19:22:47 +02:00
|
|
|
return worldBorder;
|
|
|
|
}
|
|
|
|
|
2020-09-28 00:02:37 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entities in the instance;
|
2020-09-28 00:02:37 +02:00
|
|
|
*
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return an unmodifiable {@link Set} containing all the entities in the instance
|
2020-09-28 00:02:37 +02:00
|
|
|
*/
|
2021-07-25 06:25:32 +02:00
|
|
|
public @NotNull Set<@NotNull Entity> getEntities() {
|
2021-11-01 18:04:00 +01:00
|
|
|
return entityTracker.entities();
|
2020-09-28 00:02:37 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the players in the instance;
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return an unmodifiable {@link Set} containing all the players in the instance
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-03-25 14:14:09 +01:00
|
|
|
@Override
|
2021-08-15 17:50:38 +02:00
|
|
|
public @NotNull Set<@NotNull Player> getPlayers() {
|
2021-11-01 18:04:00 +01:00
|
|
|
return entityTracker.entities(EntityTracker.Target.PLAYERS);
|
2019-08-24 21:41:43 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the creatures in the instance;
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return an unmodifiable {@link Set} containing all the creatures in the instance
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-11-01 18:04:00 +01:00
|
|
|
@Deprecated
|
2021-08-15 17:50:38 +02:00
|
|
|
public @NotNull Set<@NotNull EntityCreature> getCreatures() {
|
2021-11-01 18:04:00 +01:00
|
|
|
return entityTracker.entities().stream()
|
|
|
|
.filter(EntityCreature.class::isInstance)
|
|
|
|
.map(entity -> (EntityCreature) entity)
|
|
|
|
.collect(Collectors.toUnmodifiableSet());
|
2019-08-24 21:41:43 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the experience orbs in the instance.
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return an unmodifiable {@link Set} containing all the experience orbs in the instance
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-11-01 18:04:00 +01:00
|
|
|
@Deprecated
|
2021-08-15 17:50:38 +02:00
|
|
|
public @NotNull Set<@NotNull ExperienceOrb> getExperienceOrbs() {
|
2021-11-01 18:04:00 +01:00
|
|
|
return entityTracker.entities().stream()
|
|
|
|
.filter(ExperienceOrb.class::isInstance)
|
|
|
|
.map(entity -> (ExperienceOrb) entity)
|
|
|
|
.collect(Collectors.toUnmodifiableSet());
|
2019-08-24 21:41:43 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 20:30:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entities located in the chunk.
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
2020-05-27 20:30:13 +02:00
|
|
|
* @param chunk the chunk to get the entities from
|
2020-09-28 00:04:55 +02:00
|
|
|
* @return an unmodifiable {@link Set} containing all the entities in a chunk,
|
2020-08-10 07:24:43 +02:00
|
|
|
* if {@code chunk} is unloaded, return an empty {@link HashSet}
|
2020-05-27 20:30:13 +02:00
|
|
|
*/
|
2021-08-15 17:50:38 +02:00
|
|
|
public @NotNull Set<@NotNull Entity> getChunkEntities(Chunk chunk) {
|
2021-12-23 23:44:22 +01:00
|
|
|
var chunkEntities = entityTracker.chunkEntities(chunk.toPosition(), EntityTracker.Target.ENTITIES);
|
|
|
|
return ObjectArraySet.ofUnchecked(chunkEntities.toArray(Entity[]::new));
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2021-08-22 16:47:52 +02:00
|
|
|
/**
|
|
|
|
* Gets nearby entities to the given position.
|
|
|
|
*
|
|
|
|
* @param point position to look at
|
|
|
|
* @param range max range from the given point to collect entities at
|
|
|
|
* @return entities that are not further than the specified distance from the transmitted position.
|
|
|
|
*/
|
|
|
|
public @NotNull Collection<Entity> getNearbyEntities(@NotNull Point point, double range) {
|
|
|
|
List<Entity> result = new ArrayList<>();
|
2021-11-01 18:04:00 +01:00
|
|
|
this.entityTracker.nearbyEntities(point, range, EntityTracker.Target.ENTITIES, result::add);
|
2021-08-22 16:47:52 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-05-28 15:11:55 +02:00
|
|
|
@Override
|
2021-07-11 20:44:37 +02:00
|
|
|
public @Nullable Block getBlock(int x, int y, int z, @NotNull Condition condition) {
|
2022-04-12 23:50:45 +02:00
|
|
|
final Block block = blockRetriever.getBlock(x, y, z, condition);
|
|
|
|
if (block == null) throw new NullPointerException("Unloaded chunk at " + x + "," + y + "," + z);
|
|
|
|
return block;
|
2019-08-11 07:42:56 +02:00
|
|
|
}
|
|
|
|
|
2020-09-19 19:06:21 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Sends a {@link BlockActionPacket} for all the viewers of the specific position.
|
2020-09-19 19:06:21 +02:00
|
|
|
*
|
2020-09-24 01:50:25 +02:00
|
|
|
* @param blockPosition the block position
|
2020-11-10 08:01:27 +01:00
|
|
|
* @param actionId the action id, depends on the block
|
|
|
|
* @param actionParam the action parameter, depends on the block
|
2020-10-06 21:03:00 +02:00
|
|
|
* @see <a href="https://wiki.vg/Protocol#Block_Action">BlockActionPacket</a> for the action id & param
|
2020-09-19 19:06:21 +02:00
|
|
|
*/
|
2021-07-08 18:56:40 +02:00
|
|
|
public void sendBlockAction(@NotNull Point blockPosition, byte actionId, byte actionParam) {
|
2021-05-23 00:28:31 +02:00
|
|
|
final Block block = getBlock(blockPosition);
|
2020-07-23 07:36:49 +02:00
|
|
|
final Chunk chunk = getChunkAt(blockPosition);
|
2021-07-24 07:45:08 +02:00
|
|
|
Check.notNull(chunk, "The chunk at {0} is not loaded!", blockPosition);
|
|
|
|
chunk.sendPacketToViewers(new BlockActionPacket(blockPosition, actionId, actionParam, block));
|
2020-04-23 23:46:36 +02:00
|
|
|
}
|
|
|
|
|
2020-07-29 06:09:48 +02:00
|
|
|
/**
|
2021-07-08 18:56:40 +02:00
|
|
|
* Gets the {@link Chunk} at the given block position, null if not loaded.
|
2020-07-29 06:09:48 +02:00
|
|
|
*
|
|
|
|
* @param x the X position
|
|
|
|
* @param z the Z position
|
|
|
|
* @return the chunk at the given position, null if not loaded
|
|
|
|
*/
|
2021-07-08 18:56:40 +02:00
|
|
|
public @Nullable Chunk getChunkAt(double x, double z) {
|
2021-07-24 07:45:08 +02:00
|
|
|
return getChunk(ChunkUtils.getChunkCoordinate(x), ChunkUtils.getChunkCoordinate(z));
|
2019-08-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
2020-07-29 06:09:48 +02:00
|
|
|
/**
|
2021-07-05 09:10:03 +02:00
|
|
|
* Gets the {@link Chunk} at the given {@link Point}, null if not loaded.
|
2020-07-29 06:09:48 +02:00
|
|
|
*
|
2021-09-07 18:24:24 +02:00
|
|
|
* @param point the position
|
2020-07-29 06:09:48 +02:00
|
|
|
* @return the chunk at the given position, null if not loaded
|
|
|
|
*/
|
2021-07-08 18:56:40 +02:00
|
|
|
public @Nullable Chunk getChunkAt(@NotNull Point point) {
|
2021-09-04 15:29:20 +02:00
|
|
|
return getChunk(point.chunkX(), point.chunkZ());
|
2019-08-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
2021-11-01 18:04:00 +01:00
|
|
|
@ApiStatus.Experimental
|
|
|
|
public EntityTracker getEntityTracker() {
|
|
|
|
return entityTracker;
|
|
|
|
}
|
|
|
|
|
2020-06-02 14:43:31 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the instance unique id.
|
2020-06-02 14:43:31 +02:00
|
|
|
*
|
|
|
|
* @return the instance unique id
|
|
|
|
*/
|
2021-07-15 05:23:33 +02:00
|
|
|
public @NotNull UUID getUniqueId() {
|
2019-08-24 21:41:43 +02:00
|
|
|
return uniqueId;
|
|
|
|
}
|
|
|
|
|
2020-05-04 21:06:13 +02:00
|
|
|
/**
|
2020-10-11 03:37:44 +02:00
|
|
|
* Performs a single tick in the instance, including scheduled tasks from {@link #scheduleNextTick(Consumer)}.
|
2020-09-19 19:06:21 +02:00
|
|
|
* <p>
|
2020-10-12 06:41:47 +02:00
|
|
|
* Warning: this does not update chunks and entities.
|
2020-05-08 06:54:33 +02:00
|
|
|
*
|
2020-10-24 09:34:19 +02:00
|
|
|
* @param time the tick time in milliseconds
|
2020-05-04 21:06:13 +02:00
|
|
|
*/
|
2021-03-11 20:54:30 +01:00
|
|
|
@Override
|
2020-05-08 06:54:33 +02:00
|
|
|
public void tick(long time) {
|
2020-12-08 00:16:46 +01:00
|
|
|
// Scheduled tasks
|
2021-12-16 00:15:55 +01:00
|
|
|
this.scheduler.processTick();
|
2020-12-08 00:16:46 +01:00
|
|
|
// Time
|
2020-07-22 20:54:30 +02:00
|
|
|
{
|
|
|
|
this.worldAge++;
|
2020-08-19 01:24:51 +02:00
|
|
|
this.time += timeRate;
|
2021-08-15 17:50:38 +02:00
|
|
|
// time needs to be sent to players
|
2021-03-31 19:17:37 +02:00
|
|
|
if (timeUpdate != null && !Cooldown.hasCooldown(time, lastTimeUpdate, timeUpdate)) {
|
2020-11-13 07:43:35 +01:00
|
|
|
PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
2020-07-22 20:57:05 +02:00
|
|
|
this.lastTimeUpdate = time;
|
2020-07-22 20:54:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2024-03-19 17:44:51 +01:00
|
|
|
// Weather
|
|
|
|
if (remainingWeatherTransitionTicks > 0) {
|
|
|
|
Weather previousWeather = currentWeather;
|
|
|
|
currentWeather = transitionWeather(remainingWeatherTransitionTicks);
|
|
|
|
sendWeatherPackets(previousWeather);
|
|
|
|
remainingWeatherTransitionTicks--;
|
|
|
|
}
|
2020-12-08 00:16:46 +01:00
|
|
|
// Tick event
|
|
|
|
{
|
|
|
|
// Process tick events
|
2022-02-13 12:34:27 +01:00
|
|
|
EventDispatcher.call(new InstanceTickEvent(this, time, lastTickAge));
|
2020-12-08 00:16:46 +01:00
|
|
|
// Set last tick age
|
2021-08-15 17:50:38 +02:00
|
|
|
this.lastTickAge = time;
|
2020-12-08 00:16:46 +01:00
|
|
|
}
|
2020-07-22 20:54:30 +02:00
|
|
|
this.worldBorder.update();
|
2020-05-08 06:54:33 +02:00
|
|
|
}
|
2020-05-14 15:33:36 +02:00
|
|
|
|
2024-03-19 17:44:51 +01:00
|
|
|
/**
|
|
|
|
* Gets the current weather on this instance
|
|
|
|
*
|
|
|
|
* @return the current weather
|
|
|
|
*/
|
|
|
|
public @NotNull Weather getWeather() {
|
|
|
|
return currentWeather;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the weather on this instance, transitions over time
|
|
|
|
*
|
|
|
|
* @param weather the new weather
|
|
|
|
* @param transitionTicks the ticks to transition to new weather
|
|
|
|
*/
|
|
|
|
public void setWeather(@NotNull Weather weather, int transitionTicks) {
|
|
|
|
Check.stateCondition(transitionTicks < 1, "Transition ticks cannot be lower than 1");
|
|
|
|
targetWeather = weather;
|
|
|
|
remainingWeatherTransitionTicks = transitionTicks;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendWeatherPackets(@NotNull Weather previousWeather) {
|
|
|
|
if (currentWeather.isRaining() != previousWeather.isRaining()) sendGroupedPacket(currentWeather.createIsRainingPacket());
|
|
|
|
if (currentWeather.rainLevel() != previousWeather.rainLevel()) sendGroupedPacket(currentWeather.createRainLevelPacket());
|
|
|
|
if (currentWeather.thunderLevel() != previousWeather.thunderLevel()) sendGroupedPacket(currentWeather.createThunderLevelPacket());
|
|
|
|
}
|
|
|
|
|
|
|
|
private @NotNull Weather transitionWeather(int remainingTicks) {
|
|
|
|
Weather target = targetWeather;
|
|
|
|
Weather current = currentWeather;
|
|
|
|
if (remainingTicks <= 1) {
|
|
|
|
return new Weather(target.isRaining(), target.isRaining() ? target.rainLevel() : 0,
|
|
|
|
target.isRaining() ? target.thunderLevel() : 0);
|
|
|
|
}
|
|
|
|
float rainLevel = current.rainLevel() + (target.rainLevel() - current.rainLevel()) * (1 / (float)remainingTicks);
|
|
|
|
float thunderLevel = current.thunderLevel() + (target.thunderLevel() - current.thunderLevel()) * (1 / (float)remainingTicks);
|
|
|
|
return new Weather(rainLevel > 0, rainLevel, thunderLevel);
|
|
|
|
}
|
|
|
|
|
2021-06-26 00:31:04 +02:00
|
|
|
@Override
|
2022-03-20 01:47:57 +01:00
|
|
|
public @NotNull TagHandler tagHandler() {
|
|
|
|
return tagHandler;
|
2021-06-26 00:31:04 +02:00
|
|
|
}
|
|
|
|
|
2021-12-16 00:15:55 +01:00
|
|
|
@Override
|
|
|
|
public @NotNull Scheduler scheduler() {
|
|
|
|
return scheduler;
|
|
|
|
}
|
|
|
|
|
2022-04-17 06:19:14 +02:00
|
|
|
@Override
|
|
|
|
@ApiStatus.Experimental
|
|
|
|
public @NotNull EventNode<InstanceEvent> eventNode() {
|
|
|
|
return eventNode;
|
|
|
|
}
|
|
|
|
|
2022-03-03 07:44:57 +01:00
|
|
|
@Override
|
|
|
|
public @NotNull InstanceSnapshot updateSnapshot(@NotNull SnapshotUpdater updater) {
|
|
|
|
final Map<Long, AtomicReference<ChunkSnapshot>> chunksMap = updater.referencesMapLong(getChunks(), ChunkUtils::getChunkIndex);
|
|
|
|
final int[] entities = ArrayUtils.mapToIntArray(entityTracker.entities(), Entity::getEntityId);
|
2022-05-04 13:25:24 +02:00
|
|
|
return new SnapshotImpl.Instance(updater.reference(MinecraftServer.process()),
|
2022-03-03 07:44:57 +01:00
|
|
|
getDimensionType(), getWorldAge(), getTime(), chunksMap, entities,
|
2022-03-20 01:47:57 +01:00
|
|
|
tagHandler.readableCopy());
|
2022-03-03 07:44:57 +01:00
|
|
|
}
|
|
|
|
|
2020-05-14 15:33:36 +02:00
|
|
|
/**
|
2020-06-02 14:43:31 +02:00
|
|
|
* Creates an explosion at the given position with the given strength.
|
|
|
|
* The algorithm used to compute damages is provided by {@link #getExplosionSupplier()}.
|
2020-05-17 01:42:07 +02:00
|
|
|
*
|
2020-08-07 09:14:50 +02:00
|
|
|
* @param centerX the center X
|
|
|
|
* @param centerY the center Y
|
|
|
|
* @param centerZ the center Z
|
|
|
|
* @param strength the strength of the explosion
|
2020-06-02 14:43:31 +02:00
|
|
|
* @throws IllegalStateException If no {@link ExplosionSupplier} was supplied
|
2020-05-14 15:33:36 +02:00
|
|
|
*/
|
|
|
|
public void explode(float centerX, float centerY, float centerZ, float strength) {
|
|
|
|
explode(centerX, centerY, centerZ, strength, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 14:43:31 +02:00
|
|
|
* Creates an explosion at the given position with the given strength.
|
|
|
|
* The algorithm used to compute damages is provided by {@link #getExplosionSupplier()}.
|
2020-05-17 01:42:07 +02:00
|
|
|
*
|
2020-10-03 19:07:23 +02:00
|
|
|
* @param centerX center X of the explosion
|
|
|
|
* @param centerY center Y of the explosion
|
|
|
|
* @param centerZ center Z of the explosion
|
|
|
|
* @param strength the strength of the explosion
|
2020-05-14 15:33:36 +02:00
|
|
|
* @param additionalData data to pass to the explosion supplier
|
2020-06-02 14:43:31 +02:00
|
|
|
* @throws IllegalStateException If no {@link ExplosionSupplier} was supplied
|
2020-05-14 15:33:36 +02:00
|
|
|
*/
|
2022-02-09 23:07:18 +01:00
|
|
|
public void explode(float centerX, float centerY, float centerZ, float strength, @Nullable NBTCompound additionalData) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final ExplosionSupplier explosionSupplier = getExplosionSupplier();
|
2020-10-03 19:07:23 +02:00
|
|
|
Check.stateCondition(explosionSupplier == null, "Tried to create an explosion with no explosion supplier");
|
2020-07-24 22:48:38 +02:00
|
|
|
final Explosion explosion = explosionSupplier.createExplosion(centerX, centerY, centerZ, strength, additionalData);
|
2020-05-14 15:33:36 +02:00
|
|
|
explosion.apply(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-09 18:08:26 +01:00
|
|
|
* Gets the registered {@link ExplosionSupplier}, or null if none was provided.
|
2020-05-17 01:42:07 +02:00
|
|
|
*
|
2020-06-02 14:43:31 +02:00
|
|
|
* @return the instance explosion supplier, null if none was provided
|
2020-05-14 15:33:36 +02:00
|
|
|
*/
|
2021-08-15 17:50:38 +02:00
|
|
|
public @Nullable ExplosionSupplier getExplosionSupplier() {
|
2020-05-14 15:33:36 +02:00
|
|
|
return explosionSupplier;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-09 18:08:26 +01:00
|
|
|
* Registers the {@link ExplosionSupplier} to use in this instance.
|
2020-05-17 01:42:07 +02:00
|
|
|
*
|
2020-06-02 14:43:31 +02:00
|
|
|
* @param supplier the explosion supplier
|
2020-05-14 15:33:36 +02:00
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void setExplosionSupplier(@Nullable ExplosionSupplier supplier) {
|
2020-05-14 15:33:36 +02:00
|
|
|
this.explosionSupplier = supplier;
|
|
|
|
}
|
2020-07-24 02:31:10 +02:00
|
|
|
|
2021-06-10 14:53:34 +02:00
|
|
|
@Override
|
|
|
|
public @NotNull Pointers pointers() {
|
|
|
|
return this.pointers;
|
|
|
|
}
|
2024-02-10 21:56:57 +01:00
|
|
|
|
|
|
|
public int getBlockLight(int blockX, int blockY, int blockZ) {
|
|
|
|
var chunk = getChunkAt(blockX, blockZ);
|
|
|
|
if (chunk == null) return 0;
|
|
|
|
Section section = chunk.getSectionAt(blockY);
|
|
|
|
Light light = section.blockLight();
|
2024-02-10 22:24:29 +01:00
|
|
|
int sectionCoordinate = ChunkUtils.getChunkCoordinate(blockY);
|
2024-02-10 21:56:57 +01:00
|
|
|
|
|
|
|
int coordX = ChunkUtils.toSectionRelativeCoordinate(blockX);
|
|
|
|
int coordY = ChunkUtils.toSectionRelativeCoordinate(blockY);
|
|
|
|
int coordZ = ChunkUtils.toSectionRelativeCoordinate(blockZ);
|
|
|
|
|
2024-02-10 22:33:45 +01:00
|
|
|
if (light.requiresUpdate()) LightingChunk.relightSection(chunk.getInstance(), chunk.chunkX, sectionCoordinate, chunk.chunkZ);
|
2024-02-10 21:56:57 +01:00
|
|
|
return light.getLevel(coordX, coordY, coordZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSkyLight(int blockX, int blockY, int blockZ) {
|
|
|
|
var chunk = getChunkAt(blockX, blockZ);
|
|
|
|
if (chunk == null) return 0;
|
|
|
|
Section section = chunk.getSectionAt(blockY);
|
|
|
|
Light light = section.skyLight();
|
2024-02-10 22:24:29 +01:00
|
|
|
int sectionCoordinate = ChunkUtils.getChunkCoordinate(blockY);
|
2024-02-10 21:56:57 +01:00
|
|
|
|
|
|
|
int coordX = ChunkUtils.toSectionRelativeCoordinate(blockX);
|
|
|
|
int coordY = ChunkUtils.toSectionRelativeCoordinate(blockY);
|
|
|
|
int coordZ = ChunkUtils.toSectionRelativeCoordinate(blockZ);
|
|
|
|
|
2024-02-10 22:33:45 +01:00
|
|
|
if (light.requiresUpdate()) LightingChunk.relightSection(chunk.getInstance(), chunk.chunkX, sectionCoordinate, chunk.chunkZ);
|
2024-02-10 21:56:57 +01:00
|
|
|
return light.getLevel(coordX, coordY, coordZ);
|
|
|
|
}
|
|
|
|
}
|