mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-27 14:13:24 +02:00
Allow dynamic entity view distance change
This commit is contained in:
parent
ae1d089603
commit
f85b2c4aad
@ -439,10 +439,11 @@ public class MinecraftServer {
|
||||
* Changes the chunk view distance of the server.
|
||||
*
|
||||
* @param chunkViewDistance the new chunk view distance
|
||||
* @throws IllegalStateException if this is called after the server started
|
||||
* @throws IllegalArgumentException if {@code chunkViewDistance} is not between 2 and 32
|
||||
*/
|
||||
public static void setChunkViewDistance(int chunkViewDistance) {
|
||||
Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), "The chunk view distance must be between 2 and 32");
|
||||
Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32),
|
||||
"The chunk view distance must be between 2 and 32");
|
||||
MinecraftServer.chunkViewDistance = chunkViewDistance;
|
||||
if (started) {
|
||||
UpdateViewDistancePacket updateViewDistancePacket = new UpdateViewDistancePacket();
|
||||
@ -476,11 +477,20 @@ public class MinecraftServer {
|
||||
* WARNING: this need to be called before {@link #start(String, int, ResponseDataConsumer)}.
|
||||
*
|
||||
* @param entityViewDistance the new entity view distance
|
||||
* @throws IllegalStateException if this is called after the server started
|
||||
* @throws IllegalArgumentException if {@code entityViewDistance} is not between 0 and 32
|
||||
*/
|
||||
public static void setEntityViewDistance(int entityViewDistance) {
|
||||
Check.stateCondition(started, "The entity view distance cannot be changed after the server has been started.");
|
||||
Check.argCondition(!MathUtils.isBetween(entityViewDistance, 0, 32),
|
||||
"The entity view distance must be between 0 and 32");
|
||||
MinecraftServer.entityViewDistance = entityViewDistance;
|
||||
if (started) {
|
||||
connectionManager.getOnlinePlayers().forEach(player -> {
|
||||
final Chunk playerChunk = player.getChunk();
|
||||
if (playerChunk != null) {
|
||||
player.refreshVisibleEntities(playerChunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,6 @@ import net.minestom.server.instance.WorldBorder;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.network.packet.server.play.*;
|
||||
import net.minestom.server.thread.ThreadProvider;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.Vector;
|
||||
@ -84,7 +83,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
||||
private boolean autoViewable;
|
||||
private final int id;
|
||||
private Data data;
|
||||
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
protected final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
|
||||
protected UUID uuid;
|
||||
private boolean isActive; // False if entity has only been instanced without being added somewhere
|
||||
@ -1007,7 +1006,12 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
||||
instance.removeEntityFromChunk(this, lastChunk);
|
||||
instance.addEntityToChunk(this, newChunk);
|
||||
}
|
||||
updateView(lastChunk, newChunk);
|
||||
if (this instanceof Player) {
|
||||
// Refresh player view
|
||||
final Player player = (Player) this;
|
||||
player.refreshVisibleChunks(newChunk);
|
||||
player.refreshVisibleEntities(newChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1020,72 +1024,6 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages viewable entities automatically if {@link #isAutoViewable()} is enabled.
|
||||
* <p>
|
||||
* Called by {@link #refreshPosition(float, float, float)} when the new position is in a different {@link Chunk}.
|
||||
*
|
||||
* @param lastChunk the previous {@link Chunk} of this entity
|
||||
* @param newChunk the new {@link Chunk} of this entity
|
||||
*/
|
||||
private void updateView(@NotNull Chunk lastChunk, @NotNull Chunk newChunk) {
|
||||
final boolean isPlayer = this instanceof Player;
|
||||
|
||||
if (isPlayer)
|
||||
((Player) this).refreshVisibleChunks(newChunk); // Refresh loaded chunk
|
||||
|
||||
// Refresh entity viewable list
|
||||
final int entityViewDistance = MinecraftServer.getEntityViewDistance();
|
||||
final long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), entityViewDistance);
|
||||
final long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), entityViewDistance);
|
||||
|
||||
// Remove from previous chunks
|
||||
final int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
|
||||
for (int index : oldChunksEntity) {
|
||||
final long chunkIndex = lastVisibleChunksEntity[index];
|
||||
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
|
||||
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
|
||||
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
|
||||
if (chunk == null)
|
||||
continue;
|
||||
instance.getChunkEntities(chunk).forEach(ent -> {
|
||||
if (ent instanceof Player) {
|
||||
final Player player = (Player) ent;
|
||||
if (isAutoViewable())
|
||||
removeViewer(player);
|
||||
if (isPlayer) {
|
||||
player.removeViewer((Player) this);
|
||||
}
|
||||
} else if (isPlayer) {
|
||||
ent.removeViewer((Player) this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add to new chunks
|
||||
final int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
|
||||
for (int index : newChunksEntity) {
|
||||
final long chunkIndex = updatedVisibleChunksEntity[index];
|
||||
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
|
||||
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
|
||||
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
|
||||
if (chunk == null)
|
||||
continue;
|
||||
instance.getChunkEntities(chunk).forEach(ent -> {
|
||||
if (ent instanceof Player) {
|
||||
Player player = (Player) ent;
|
||||
if (isAutoViewable())
|
||||
addViewer(player);
|
||||
if (this instanceof Player) {
|
||||
player.addViewer((Player) this);
|
||||
}
|
||||
} else if (isPlayer) {
|
||||
ent.addViewer((Player) this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the entity view internally.
|
||||
* <p>
|
||||
|
@ -80,6 +80,7 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
|
||||
private String username;
|
||||
protected final PlayerConnection playerConnection;
|
||||
// All the entities that this player can see
|
||||
protected final Set<Entity> viewableEntities = new CopyOnWriteArraySet<>();
|
||||
|
||||
private int latency;
|
||||
@ -1406,12 +1407,12 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
|
||||
/**
|
||||
* Called when the player changes chunk (move from one to another).
|
||||
* Can also be used to refresh the list of chunks that the client should see.
|
||||
* Can also be used to refresh the list of chunks that the client should see based on {@link #getChunkRange()}.
|
||||
* <p>
|
||||
* It does remove and add the player from the chunks viewers list when removed or added.
|
||||
* It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}.
|
||||
*
|
||||
* @param newChunk the current/new player chunk
|
||||
* @param newChunk the current/new player chunk (can be the current one)
|
||||
*/
|
||||
public void refreshVisibleChunks(@NotNull Chunk newChunk) {
|
||||
// Previous chunks indexes
|
||||
@ -1420,7 +1421,7 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
).toArray();
|
||||
|
||||
// New chunks indexes
|
||||
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), getChunkRange());
|
||||
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(newChunk.toPosition(), getChunkRange());
|
||||
|
||||
// Find the difference between the two arrays¬
|
||||
final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
|
||||
@ -1461,6 +1462,52 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the list of entities that the player should be able to see based on {@link MinecraftServer#getEntityViewDistance()}
|
||||
* and {@link Entity#isAutoViewable()}.
|
||||
*
|
||||
* @param newChunk the new chunk of the player (can be the current one)
|
||||
*/
|
||||
public void refreshVisibleEntities(@NotNull Chunk newChunk) {
|
||||
final int entityViewDistance = MinecraftServer.getEntityViewDistance();
|
||||
final float maximalDistance = entityViewDistance * Chunk.CHUNK_SECTION_SIZE;
|
||||
|
||||
// Manage already viewable entities
|
||||
this.viewableEntities.forEach(entity -> {
|
||||
final float distance = entity.getDistance(this);
|
||||
if (distance > maximalDistance) {
|
||||
// Entity shouldn't be viewable anymore
|
||||
if (isAutoViewable()) {
|
||||
entity.removeViewer(this);
|
||||
}
|
||||
if (entity instanceof Player && entity.isAutoViewable()) {
|
||||
removeViewer((Player) entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Manage entities in unchecked chunks
|
||||
final long[] chunksInRange = ChunkUtils.getChunksInRange(newChunk.toPosition(), entityViewDistance);
|
||||
|
||||
for (long chunkIndex : chunksInRange) {
|
||||
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
|
||||
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
|
||||
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
|
||||
if (chunk == null)
|
||||
continue;
|
||||
instance.getChunkEntities(chunk).forEach(entity -> {
|
||||
if (isAutoViewable() && !entity.viewers.contains(this)) {
|
||||
entity.addViewer(this);
|
||||
}
|
||||
|
||||
if (entity instanceof Player && entity.isAutoViewable()) {
|
||||
addViewer((Player) entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(@NotNull Position position, @Nullable Runnable callback) {
|
||||
super.teleport(position, () -> {
|
||||
|
@ -19,6 +19,7 @@ import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.chunk.ChunkCallback;
|
||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||
@ -291,6 +292,15 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Position} object based on this chunk.
|
||||
*
|
||||
* @return the position of this chunk
|
||||
*/
|
||||
public Position toPosition() {
|
||||
return new Position(CHUNK_SIZE_Z * getChunkX(), 0, CHUNK_SIZE_Z * getChunkZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this chunk will or had been loaded with a {@link ChunkGenerator}.
|
||||
* <p>
|
||||
|
@ -7,6 +7,7 @@ import net.minestom.server.benchmark.BenchmarkManager;
|
||||
import net.minestom.server.chat.ColoredText;
|
||||
import net.minestom.server.entity.*;
|
||||
import net.minestom.server.entity.damage.DamageType;
|
||||
import net.minestom.server.entity.type.monster.EntityZombie;
|
||||
import net.minestom.server.event.entity.EntityAttackEvent;
|
||||
import net.minestom.server.event.item.ItemDropEvent;
|
||||
import net.minestom.server.event.item.PickupItemEvent;
|
||||
@ -14,7 +15,6 @@ import net.minestom.server.event.player.*;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.instance.SharedInstance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
@ -144,13 +144,8 @@ public class PlayerInit {
|
||||
Vector velocity = player.getPosition().copy().getDirection().multiply(6);
|
||||
itemEntity.setVelocity(velocity);
|
||||
|
||||
Instance instance = player.getInstance();
|
||||
InstanceContainer instanceContainer = instance instanceof InstanceContainer ? (InstanceContainer) instance :
|
||||
((SharedInstance) instance).getInstanceContainer();
|
||||
SharedInstance sharedInstance = MinecraftServer.getInstanceManager().createSharedInstance(instanceContainer);
|
||||
player.setInstance(sharedInstance);
|
||||
player.sendMessage("New instance");
|
||||
|
||||
EntityZombie entityZombie = new EntityZombie(player.getPosition());
|
||||
entityZombie.setInstance(player.getInstance());
|
||||
});
|
||||
|
||||
player.addEventCallback(PlayerDisconnectEvent.class, event -> {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package demo.commands;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
@ -17,9 +18,7 @@ public class TestCommand extends Command {
|
||||
//addSyntax(this::execute, dynamicWord);
|
||||
}
|
||||
|
||||
Argument test = ArgumentType.Word("wordT");
|
||||
Argument testt = ArgumentType.Word("wordTt");
|
||||
Argument test2 = ArgumentType.StringArray("array");
|
||||
Argument test = ArgumentType.Integer("number");
|
||||
|
||||
setDefaultExecutor((source, args) -> {
|
||||
System.out.println("DEFAULT");
|
||||
@ -27,12 +26,10 @@ public class TestCommand extends Command {
|
||||
});
|
||||
|
||||
addSyntax((source, args) -> {
|
||||
System.out.println(1);
|
||||
int number = args.getInteger("number");
|
||||
source.sendMessage("set view to " + number);
|
||||
MinecraftServer.setEntityViewDistance(number);
|
||||
}, test);
|
||||
|
||||
addSyntax((source, args) -> {
|
||||
System.out.println(2);
|
||||
}, test, test2);
|
||||
}
|
||||
|
||||
private void usage(CommandSender sender, Arguments arguments) {
|
||||
|
Loading…
Reference in New Issue
Block a user