mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-08 09:27:58 +01:00
Merge branch 'master' into item-api
# Conflicts: # src/test/java/demo/PlayerInit.java
This commit is contained in:
commit
1d5262caf2
@ -2,7 +2,7 @@ package net.minestom.server;
|
|||||||
|
|
||||||
import net.minestom.server.advancements.AdvancementManager;
|
import net.minestom.server.advancements.AdvancementManager;
|
||||||
import net.minestom.server.adventure.bossbar.BossBarManager;
|
import net.minestom.server.adventure.bossbar.BossBarManager;
|
||||||
import net.minestom.server.benchmark.BenchmarkManager;
|
import net.minestom.server.monitoring.BenchmarkManager;
|
||||||
import net.minestom.server.command.CommandManager;
|
import net.minestom.server.command.CommandManager;
|
||||||
import net.minestom.server.data.DataManager;
|
import net.minestom.server.data.DataManager;
|
||||||
import net.minestom.server.data.DataType;
|
import net.minestom.server.data.DataType;
|
||||||
|
@ -4,6 +4,7 @@ import com.google.common.collect.Queues;
|
|||||||
import net.minestom.server.instance.Chunk;
|
import net.minestom.server.instance.Chunk;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.instance.InstanceManager;
|
import net.minestom.server.instance.InstanceManager;
|
||||||
|
import net.minestom.server.monitoring.TickMonitor;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.network.player.NettyPlayerConnection;
|
import net.minestom.server.network.player.NettyPlayerConnection;
|
||||||
import net.minestom.server.thread.PerInstanceThreadProvider;
|
import net.minestom.server.thread.PerInstanceThreadProvider;
|
||||||
@ -13,10 +14,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.Future;
|
import java.util.function.Consumer;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.LongConsumer;
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,6 +35,7 @@ public final class UpdateManager {
|
|||||||
|
|
||||||
private final Queue<LongConsumer> tickStartCallbacks = Queues.newConcurrentLinkedQueue();
|
private final Queue<LongConsumer> tickStartCallbacks = Queues.newConcurrentLinkedQueue();
|
||||||
private final Queue<LongConsumer> tickEndCallbacks = Queues.newConcurrentLinkedQueue();
|
private final Queue<LongConsumer> tickEndCallbacks = Queues.newConcurrentLinkedQueue();
|
||||||
|
private final List<Consumer<TickMonitor>> tickMonitors = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
{
|
{
|
||||||
// DEFAULT THREAD PROVIDER
|
// DEFAULT THREAD PROVIDER
|
||||||
@ -81,7 +81,14 @@ public final class UpdateManager {
|
|||||||
final long tickTime = System.nanoTime() - currentTime;
|
final long tickTime = System.nanoTime() - currentTime;
|
||||||
|
|
||||||
// Tick end callbacks
|
// Tick end callbacks
|
||||||
doTickCallback(tickEndCallbacks, tickTime / 1000000L);
|
doTickCallback(tickEndCallbacks, tickTime);
|
||||||
|
|
||||||
|
// Monitoring
|
||||||
|
if (!tickMonitors.isEmpty()) {
|
||||||
|
final double tickTimeMs = tickTime / 1e6D;
|
||||||
|
final TickMonitor tickMonitor = new TickMonitor(tickTimeMs);
|
||||||
|
this.tickMonitors.forEach(consumer -> consumer.accept(tickMonitor));
|
||||||
|
}
|
||||||
|
|
||||||
// Flush all waiting packets
|
// Flush all waiting packets
|
||||||
AsyncUtils.runAsync(() -> connectionManager.getOnlinePlayers().stream()
|
AsyncUtils.runAsync(() -> connectionManager.getOnlinePlayers().stream()
|
||||||
@ -246,6 +253,14 @@ public final class UpdateManager {
|
|||||||
this.tickEndCallbacks.remove(callback);
|
this.tickEndCallbacks.remove(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
|
||||||
|
this.tickMonitors.add(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
|
||||||
|
this.tickMonitors.remove(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the server loop.
|
* Stops the server loop.
|
||||||
*/
|
*/
|
||||||
|
@ -32,21 +32,22 @@ public class CollisionUtils {
|
|||||||
@NotNull Vector velocityOut) {
|
@NotNull Vector velocityOut) {
|
||||||
// TODO handle collisions with nearby entities (should it be done here?)
|
// TODO handle collisions with nearby entities (should it be done here?)
|
||||||
final Instance instance = entity.getInstance();
|
final Instance instance = entity.getInstance();
|
||||||
|
final Chunk originChunk = entity.getChunk();
|
||||||
final Position currentPosition = entity.getPosition();
|
final Position currentPosition = entity.getPosition();
|
||||||
final BoundingBox boundingBox = entity.getBoundingBox();
|
final BoundingBox boundingBox = entity.getBoundingBox();
|
||||||
|
|
||||||
Vector intermediaryPosition = new Vector();
|
Vector intermediaryPosition = new Vector();
|
||||||
final boolean yCollision = stepAxis(instance, currentPosition.toVector(), Y_AXIS, deltaPosition.getY(),
|
final boolean yCollision = stepAxis(instance, originChunk, currentPosition.toVector(), Y_AXIS, deltaPosition.getY(),
|
||||||
intermediaryPosition,
|
intermediaryPosition,
|
||||||
deltaPosition.getY() > 0 ? boundingBox.getTopFace() : boundingBox.getBottomFace()
|
deltaPosition.getY() > 0 ? boundingBox.getTopFace() : boundingBox.getBottomFace()
|
||||||
);
|
);
|
||||||
|
|
||||||
final boolean xCollision = stepAxis(instance, intermediaryPosition, X_AXIS, deltaPosition.getX(),
|
final boolean xCollision = stepAxis(instance, originChunk, intermediaryPosition, X_AXIS, deltaPosition.getX(),
|
||||||
intermediaryPosition,
|
intermediaryPosition,
|
||||||
deltaPosition.getX() < 0 ? boundingBox.getLeftFace() : boundingBox.getRightFace()
|
deltaPosition.getX() < 0 ? boundingBox.getLeftFace() : boundingBox.getRightFace()
|
||||||
);
|
);
|
||||||
|
|
||||||
final boolean zCollision = stepAxis(instance, intermediaryPosition, Z_AXIS, deltaPosition.getZ(),
|
final boolean zCollision = stepAxis(instance, originChunk, intermediaryPosition, Z_AXIS, deltaPosition.getZ(),
|
||||||
intermediaryPosition,
|
intermediaryPosition,
|
||||||
deltaPosition.getZ() > 0 ? boundingBox.getBackFace() : boundingBox.getFrontFace()
|
deltaPosition.getZ() > 0 ? boundingBox.getBackFace() : boundingBox.getFrontFace()
|
||||||
);
|
);
|
||||||
@ -80,7 +81,11 @@ public class CollisionUtils {
|
|||||||
* @param corners the corners to check against
|
* @param corners the corners to check against
|
||||||
* @return true if a collision has been found
|
* @return true if a collision has been found
|
||||||
*/
|
*/
|
||||||
private static boolean stepAxis(Instance instance, Vector startPosition, Vector axis, double stepAmount, Vector positionOut, Vector... corners) {
|
private static boolean stepAxis(Instance instance,
|
||||||
|
Chunk originChunk,
|
||||||
|
Vector startPosition, Vector axis,
|
||||||
|
double stepAmount, Vector positionOut,
|
||||||
|
Vector... corners) {
|
||||||
positionOut.copy(startPosition);
|
positionOut.copy(startPosition);
|
||||||
if (corners.length == 0)
|
if (corners.length == 0)
|
||||||
return false; // avoid degeneracy in following computations
|
return false; // avoid degeneracy in following computations
|
||||||
@ -99,7 +104,7 @@ public class CollisionUtils {
|
|||||||
// used to determine if 'remainingLength' should be used
|
// used to determine if 'remainingLength' should be used
|
||||||
boolean collisionFound = false;
|
boolean collisionFound = false;
|
||||||
for (int i = 0; i < Math.abs(blockLength); i++) {
|
for (int i = 0; i < Math.abs(blockLength); i++) {
|
||||||
if (!stepOnce(instance, axis, sign, cornersCopy, cornerPositions)) {
|
if (!stepOnce(instance, originChunk, axis, sign, cornersCopy, cornerPositions)) {
|
||||||
collisionFound = true;
|
collisionFound = true;
|
||||||
}
|
}
|
||||||
if (collisionFound) {
|
if (collisionFound) {
|
||||||
@ -111,7 +116,7 @@ public class CollisionUtils {
|
|||||||
if (!collisionFound) {
|
if (!collisionFound) {
|
||||||
Vector direction = new Vector();
|
Vector direction = new Vector();
|
||||||
direction.copy(axis);
|
direction.copy(axis);
|
||||||
collisionFound = !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions);
|
collisionFound = !stepOnce(instance, originChunk, direction, remainingLength, cornersCopy, cornerPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the corner which moved the least
|
// find the corner which moved the least
|
||||||
@ -138,7 +143,9 @@ public class CollisionUtils {
|
|||||||
* @param cornerPositions the corners, converted to BlockPosition (mutable)
|
* @param cornerPositions the corners, converted to BlockPosition (mutable)
|
||||||
* @return false if this method encountered a collision
|
* @return false if this method encountered a collision
|
||||||
*/
|
*/
|
||||||
private static boolean stepOnce(Instance instance, Vector axis, double amount, Vector[] cornersCopy, BlockPosition[] cornerPositions) {
|
private static boolean stepOnce(Instance instance,
|
||||||
|
Chunk originChunk,
|
||||||
|
Vector axis, double amount, Vector[] cornersCopy, BlockPosition[] cornerPositions) {
|
||||||
final double sign = Math.signum(amount);
|
final double sign = Math.signum(amount);
|
||||||
for (int cornerIndex = 0; cornerIndex < cornersCopy.length; cornerIndex++) {
|
for (int cornerIndex = 0; cornerIndex < cornersCopy.length; cornerIndex++) {
|
||||||
Vector corner = cornersCopy[cornerIndex];
|
Vector corner = cornersCopy[cornerIndex];
|
||||||
@ -148,10 +155,13 @@ public class CollisionUtils {
|
|||||||
blockPos.setY((int) Math.floor(corner.getY()));
|
blockPos.setY((int) Math.floor(corner.getY()));
|
||||||
blockPos.setZ((int) Math.floor(corner.getZ()));
|
blockPos.setZ((int) Math.floor(corner.getZ()));
|
||||||
|
|
||||||
final Chunk chunk = instance.getChunkAt(blockPos);
|
Chunk chunk = originChunk;
|
||||||
if (!ChunkUtils.isLoaded(chunk)) {
|
if (!ChunkUtils.same(originChunk, blockPos.getX(), blockPos.getZ())) {
|
||||||
// Collision at chunk border
|
chunk = instance.getChunkAt(blockPos);
|
||||||
return false;
|
if (!ChunkUtils.isLoaded(chunk)) {
|
||||||
|
// Collision at chunk border
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final short blockStateId = chunk.getBlockStateId(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
final short blockStateId = chunk.getBlockStateId(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||||
|
@ -79,10 +79,10 @@ public class ArgumentEntity extends Argument<EntityFinder> {
|
|||||||
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
|
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
|
||||||
byte mask = 0;
|
byte mask = 0;
|
||||||
if (this.isOnlySingleEntity()) {
|
if (this.isOnlySingleEntity()) {
|
||||||
mask += 1;
|
mask |= 0x01;
|
||||||
}
|
}
|
||||||
if (this.isOnlyPlayers()) {
|
if (this.isOnlyPlayers()) {
|
||||||
mask += 2;
|
mask |= 0x02;
|
||||||
}
|
}
|
||||||
packetWriter.writeByte(mask);
|
packetWriter.writeByte(mask);
|
||||||
});
|
});
|
||||||
|
@ -64,6 +64,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
private static final AtomicInteger LAST_ENTITY_ID = new AtomicInteger();
|
private static final AtomicInteger LAST_ENTITY_ID = new AtomicInteger();
|
||||||
|
|
||||||
protected Instance instance;
|
protected Instance instance;
|
||||||
|
protected Chunk currentChunk;
|
||||||
protected final Position position;
|
protected final Position position;
|
||||||
protected double lastX, lastY, lastZ;
|
protected double lastX, lastY, lastZ;
|
||||||
protected double cacheX, cacheY, cacheZ; // Used to synchronize with #getPosition
|
protected double cacheX, cacheY, cacheZ; // Used to synchronize with #getPosition
|
||||||
@ -462,7 +463,6 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the entity chunk is loaded
|
// Check if the entity chunk is loaded
|
||||||
final Chunk currentChunk = getChunk();
|
|
||||||
if (!ChunkUtils.isLoaded(currentChunk)) {
|
if (!ChunkUtils.isLoaded(currentChunk)) {
|
||||||
// No update for entities in unloaded chunk
|
// No update for entities in unloaded chunk
|
||||||
return;
|
return;
|
||||||
@ -566,11 +566,14 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
|
|
||||||
// World border collision
|
// World border collision
|
||||||
final Position finalVelocityPosition = CollisionUtils.applyWorldBorder(instance, position, newPosition);
|
final Position finalVelocityPosition = CollisionUtils.applyWorldBorder(instance, position, newPosition);
|
||||||
final Chunk finalChunk = instance.getChunkAt(finalVelocityPosition);
|
Chunk finalChunk = currentChunk;
|
||||||
|
if (!ChunkUtils.same(position, finalVelocityPosition)) {
|
||||||
|
finalChunk = instance.getChunkAt(finalVelocityPosition);
|
||||||
|
|
||||||
// Entity shouldn't be updated when moving in an unloaded chunk
|
// Entity shouldn't be updated when moving in an unloaded chunk
|
||||||
if (!ChunkUtils.isLoaded(finalChunk)) {
|
if (!ChunkUtils.isLoaded(finalChunk)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the position if changed
|
// Apply the position if changed
|
||||||
@ -637,9 +640,12 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
for (int y = minY; y <= maxY; y++) {
|
for (int y = minY; y <= maxY; y++) {
|
||||||
for (int x = minX; x <= maxX; x++) {
|
for (int x = minX; x <= maxX; x++) {
|
||||||
for (int z = minZ; z <= maxZ; z++) {
|
for (int z = minZ; z <= maxZ; z++) {
|
||||||
final Chunk chunk = instance.getChunkAt(x, z);
|
Chunk chunk = currentChunk;
|
||||||
if (!ChunkUtils.isLoaded(chunk))
|
if (!ChunkUtils.same(currentChunk, x, z)) {
|
||||||
continue;
|
chunk = instance.getChunkAt(x, z);
|
||||||
|
if (!ChunkUtils.isLoaded(chunk))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final CustomBlock customBlock = chunk.getCustomBlock(x, y, z);
|
final CustomBlock customBlock = chunk.getCustomBlock(x, y, z);
|
||||||
if (customBlock != null) {
|
if (customBlock != null) {
|
||||||
@ -816,9 +822,8 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
*
|
*
|
||||||
* @return the entity chunk, can be null even if unlikely
|
* @return the entity chunk, can be null even if unlikely
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable Chunk getChunk() {
|
||||||
public Chunk getChunk() {
|
return currentChunk;
|
||||||
return instance.getChunkAt(position.getX(), position.getZ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -826,8 +831,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
*
|
*
|
||||||
* @return the entity instance, can be null if the entity doesn't have an instance yet
|
* @return the entity instance, can be null if the entity doesn't have an instance yet
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable Instance getInstance() {
|
||||||
public Instance getInstance() {
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,6 +859,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
|
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
this.currentChunk = instance.getChunkAt(position.getX(), position.getZ());
|
||||||
instance.UNSAFE_addEntity(this);
|
instance.UNSAFE_addEntity(this);
|
||||||
spawn();
|
spawn();
|
||||||
EntitySpawnEvent entitySpawnEvent = new EntitySpawnEvent(this, instance);
|
EntitySpawnEvent entitySpawnEvent = new EntitySpawnEvent(this, instance);
|
||||||
@ -1309,20 +1314,26 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
|||||||
|
|
||||||
final Instance instance = getInstance();
|
final Instance instance = getInstance();
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
final Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
|
final int lastChunkX = currentChunk.getChunkX();
|
||||||
final Chunk newChunk = instance.getChunkAt(x, z);
|
final int lastChunkZ = currentChunk.getChunkZ();
|
||||||
|
|
||||||
Check.notNull(lastChunk, "The entity {0} was in an unloaded chunk at {1};{2}", getEntityId(), lastX, lastZ);
|
final int newChunkX = ChunkUtils.getChunkCoordinate(x);
|
||||||
Check.notNull(newChunk, "The entity {0} tried to move in an unloaded chunk at {1};{2}", getEntityId(), x, z);
|
final int newChunkZ = ChunkUtils.getChunkCoordinate(z);
|
||||||
|
|
||||||
if (lastChunk != newChunk) {
|
if (lastChunkX != newChunkX || lastChunkZ != newChunkZ) {
|
||||||
instance.UNSAFE_switchEntityChunk(this, lastChunk, newChunk);
|
// Entity moved in a new chunk
|
||||||
|
final Chunk newChunk = instance.getChunk(newChunkX, newChunkZ);
|
||||||
|
Check.notNull(newChunk, "The entity {0} tried to move in an unloaded chunk at {1};{2}", getEntityId(), x, z);
|
||||||
|
|
||||||
|
instance.UNSAFE_switchEntityChunk(this, currentChunk, newChunk);
|
||||||
if (this instanceof Player) {
|
if (this instanceof Player) {
|
||||||
// Refresh player view
|
// Refresh player view
|
||||||
final Player player = (Player) this;
|
final Player player = (Player) this;
|
||||||
player.refreshVisibleChunks(newChunk);
|
player.refreshVisibleChunks(newChunk);
|
||||||
player.refreshVisibleEntities(newChunk);
|
player.refreshVisibleEntities(newChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.currentChunk = newChunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,17 +664,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
// Load all the required chunks
|
// Load all the required chunks
|
||||||
final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, getChunkRange());
|
final long[] visibleChunks = ChunkUtils.getChunksInRange(spawnPosition, getChunkRange());
|
||||||
|
|
||||||
final ChunkCallback eachCallback = chunk -> {
|
|
||||||
if (chunk != null) {
|
|
||||||
final int chunkX = ChunkUtils.getChunkCoordinate(spawnPosition.getX());
|
|
||||||
final int chunkZ = ChunkUtils.getChunkCoordinate(spawnPosition.getZ());
|
|
||||||
if (chunk.getChunkX() == chunkX &&
|
|
||||||
chunk.getChunkZ() == chunkZ) {
|
|
||||||
updateViewPosition(chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final ChunkCallback endCallback = chunk -> {
|
final ChunkCallback endCallback = chunk -> {
|
||||||
// This is the last chunk to be loaded , spawn player
|
// This is the last chunk to be loaded , spawn player
|
||||||
spawnPlayer(instance, spawnPosition, firstSpawn, true, dimensionChange);
|
spawnPlayer(instance, spawnPosition, firstSpawn, true, dimensionChange);
|
||||||
@ -683,7 +672,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
// Chunk 0;0 always needs to be loaded
|
// Chunk 0;0 always needs to be loaded
|
||||||
instance.loadChunk(0, 0, chunk ->
|
instance.loadChunk(0, 0, chunk ->
|
||||||
// Load all the required chunks
|
// Load all the required chunks
|
||||||
ChunkUtils.optionalLoadAll(instance, visibleChunks, eachCallback, endCallback));
|
ChunkUtils.optionalLoadAll(instance, visibleChunks, null, endCallback));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// The player already has the good version of all the chunks.
|
// The player already has the good version of all the chunks.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package net.minestom.server.benchmark;
|
package net.minestom.server.monitoring;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2LongMap;
|
import it.unimi.dsi.fastutil.longs.Long2LongMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
|
@ -1,4 +1,4 @@
|
|||||||
package net.minestom.server.benchmark;
|
package net.minestom.server.monitoring;
|
||||||
|
|
||||||
public class ThreadResult {
|
public class ThreadResult {
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
package net.minestom.server.monitoring;
|
||||||
|
|
||||||
|
public class TickMonitor {
|
||||||
|
|
||||||
|
private final double tickTime;
|
||||||
|
|
||||||
|
public TickMonitor(double tickTime) {
|
||||||
|
this.tickTime = tickTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTickTime() {
|
||||||
|
return tickTime;
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,7 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler {
|
|||||||
register(0x27, ClientUpdateCommandBlockMinecartPacket::new);
|
register(0x27, ClientUpdateCommandBlockMinecartPacket::new);
|
||||||
register(0x28, ClientCreativeInventoryActionPacket::new);
|
register(0x28, ClientCreativeInventoryActionPacket::new);
|
||||||
//Update Jigsaw Block??
|
//Update Jigsaw Block??
|
||||||
//Update Structure Block??
|
register(0x2A, ClientUpdateStructureBlockPacket::new);
|
||||||
register(0x2B, ClientUpdateSignPacket::new);
|
register(0x2B, ClientUpdateSignPacket::new);
|
||||||
register(0x2C, ClientAnimationPacket::new);
|
register(0x2C, ClientAnimationPacket::new);
|
||||||
register(0x2D, ClientSpectatePacket::new);
|
register(0x2D, ClientSpectatePacket::new);
|
||||||
|
@ -48,7 +48,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket {
|
|||||||
|
|
||||||
// Velocity
|
// Velocity
|
||||||
if (VelocityProxy.isEnabled() && channel.equals(VelocityProxy.PLAYER_INFO_CHANNEL)) {
|
if (VelocityProxy.isEnabled() && channel.equals(VelocityProxy.PLAYER_INFO_CHANNEL)) {
|
||||||
if (data != null) {
|
if (data != null && data.length > 0) {
|
||||||
BinaryReader reader = new BinaryReader(data);
|
BinaryReader reader = new BinaryReader(data);
|
||||||
success = VelocityProxy.checkIntegrity(reader);
|
success = VelocityProxy.checkIntegrity(reader);
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -103,7 +103,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket {
|
|||||||
writer.writeVarInt(messageId);
|
writer.writeVarInt(messageId);
|
||||||
writer.writeBoolean(successful);
|
writer.writeBoolean(successful);
|
||||||
|
|
||||||
if(successful) {
|
if (successful) {
|
||||||
writer.writeBytes(data);
|
writer.writeBytes(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
package net.minestom.server.network.packet.client.play;
|
||||||
|
|
||||||
|
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
||||||
|
import net.minestom.server.utils.BlockPosition;
|
||||||
|
import net.minestom.server.utils.Rotation;
|
||||||
|
import net.minestom.server.utils.binary.BinaryReader;
|
||||||
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class ClientUpdateStructureBlockPacket extends ClientPlayPacket {
|
||||||
|
// Flag values
|
||||||
|
public static final byte IGNORE_ENTITIES = 0x1;
|
||||||
|
public static final byte SHOW_AIR = 0x2;
|
||||||
|
/**
|
||||||
|
* Requires the player to be in creative and have a permission level higher than 2.
|
||||||
|
*/
|
||||||
|
public static final byte SHOW_BOUNDING_BOX = 0x4;
|
||||||
|
|
||||||
|
public BlockPosition location = new BlockPosition(0, 0, 0);
|
||||||
|
public Action action = Action.UPDATE_DATA;
|
||||||
|
public Mode mode = Mode.DATA;
|
||||||
|
public String name = "";
|
||||||
|
public BlockPosition offset = new BlockPosition(0, 1, 0);
|
||||||
|
public BlockPosition size = new BlockPosition(1, 1, 1);
|
||||||
|
public Mirror mirror = Mirror.NONE;
|
||||||
|
public Rotation rotation = Rotation.NONE;
|
||||||
|
public String metadata = "";
|
||||||
|
public float integrity = 1.0f;
|
||||||
|
public long seed = 0;
|
||||||
|
public byte flags = 0x0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
|
writer.writeBlockPosition(location);
|
||||||
|
writer.writeVarInt(action.ordinal());
|
||||||
|
writer.writeVarInt(mode.ordinal());
|
||||||
|
writer.writeSizedString(name);
|
||||||
|
writer.writeByte((byte) offset.getX());
|
||||||
|
writer.writeByte((byte) offset.getY());
|
||||||
|
writer.writeByte((byte) offset.getZ());
|
||||||
|
writer.writeByte((byte) size.getX());
|
||||||
|
writer.writeByte((byte) size.getY());
|
||||||
|
writer.writeByte((byte) size.getZ());
|
||||||
|
writer.writeVarInt(mirror.ordinal());
|
||||||
|
writer.writeVarInt(toRestrictedRotation(rotation));
|
||||||
|
writer.writeSizedString(metadata);
|
||||||
|
writer.writeFloat(integrity);
|
||||||
|
writer.writeVarLong(seed);
|
||||||
|
writer.writeByte(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(@NotNull BinaryReader reader) {
|
||||||
|
location = reader.readBlockPosition();
|
||||||
|
action = Action.values()[reader.readVarInt()];
|
||||||
|
mode = Mode.values()[reader.readVarInt()];
|
||||||
|
name = reader.readSizedString(Short.MAX_VALUE);
|
||||||
|
offset = new BlockPosition(
|
||||||
|
reader.readByte(),
|
||||||
|
reader.readByte(),
|
||||||
|
reader.readByte()
|
||||||
|
);
|
||||||
|
size = new BlockPosition(
|
||||||
|
reader.readByte(),
|
||||||
|
reader.readByte(),
|
||||||
|
reader.readByte()
|
||||||
|
);
|
||||||
|
mirror = Mirror.values()[reader.readVarInt()];
|
||||||
|
rotation = fromRestrictedRotation(reader.readVarInt());
|
||||||
|
metadata = reader.readSizedString(Short.MAX_VALUE);
|
||||||
|
integrity = reader.readFloat();
|
||||||
|
seed = reader.readVarLong();
|
||||||
|
flags = reader.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update action, <code>UPDATE_DATA</code> indicates nothing special.
|
||||||
|
*/
|
||||||
|
public enum Action {
|
||||||
|
UPDATE_DATA, SAVE, LOAD, DETECT_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Mode {
|
||||||
|
SAVE, LOAD, CORNER, DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Mirror {
|
||||||
|
NONE, LEFT_RIGHT, FRONT_BACK
|
||||||
|
}
|
||||||
|
|
||||||
|
private int toRestrictedRotation(Rotation rotation) {
|
||||||
|
switch (rotation) {
|
||||||
|
case NONE: return 0;
|
||||||
|
case CLOCKWISE: return 1;
|
||||||
|
case FLIPPED: return 2;
|
||||||
|
case COUNTER_CLOCKWISE: return 3;
|
||||||
|
default: throw new IllegalArgumentException("ClientUpdateStructurePacket#rotation must be a valid 90-degree rotation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rotation fromRestrictedRotation(int rotation) {
|
||||||
|
switch (rotation) {
|
||||||
|
case 0: return Rotation.NONE;
|
||||||
|
case 1: return Rotation.CLOCKWISE;
|
||||||
|
case 2: return Rotation.FLIPPED;
|
||||||
|
case 3: return Rotation.COUNTER_CLOCKWISE;
|
||||||
|
default: throw new IllegalArgumentException("ClientUpdateStructurePacket#rotation must be a valid 90-degree rotation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,13 +22,13 @@ public class PlayerAbilitiesPacket implements ServerPacket {
|
|||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
byte flags = 0;
|
byte flags = 0;
|
||||||
if (invulnerable)
|
if (invulnerable)
|
||||||
flags += 1;
|
flags |= 0x01;
|
||||||
if (flying)
|
if (flying)
|
||||||
flags += 2;
|
flags |= 0x02;
|
||||||
if (allowFlying)
|
if (allowFlying)
|
||||||
flags += 4;
|
flags |= 0x04;
|
||||||
if (instantBreak)
|
if (instantBreak)
|
||||||
flags += 8;
|
flags |= 0x08;
|
||||||
|
|
||||||
writer.writeByte(flags);
|
writer.writeByte(flags);
|
||||||
writer.writeFloat(flyingSpeed);
|
writer.writeFloat(flyingSpeed);
|
||||||
|
@ -4,8 +4,11 @@ import com.velocitypowered.natives.compression.VelocityCompressor;
|
|||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.kyori.adventure.audience.Audience;
|
||||||
|
import net.kyori.adventure.audience.ForwardingAudience;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.adventure.AdventureSerializer;
|
import net.minestom.server.adventure.AdventureSerializer;
|
||||||
|
import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.listener.manager.PacketListenerManager;
|
import net.minestom.server.listener.manager.PacketListenerManager;
|
||||||
import net.minestom.server.network.netty.packet.FramedPacket;
|
import net.minestom.server.network.netty.packet.FramedPacket;
|
||||||
@ -18,6 +21,7 @@ import net.minestom.server.utils.callback.validator.PlayerValidator;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
@ -34,6 +38,39 @@ public final class PacketUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet to an audience. This method performs the following steps in the
|
||||||
|
* following order:
|
||||||
|
* <ol>
|
||||||
|
* <li>If {@code audience} is a {@link Player}, send the packet to them.</li>
|
||||||
|
* <li>Otherwise, if {@code audience} is a {@link PacketGroupingAudience}, call
|
||||||
|
* {@link #sendGroupedPacket(Collection, ServerPacket)} on the players that the
|
||||||
|
* grouping audience contains.</li>
|
||||||
|
* <li>Otherwise, if {@code audience} is a {@link ForwardingAudience.Single},
|
||||||
|
* call this method on the single audience inside the forwarding audience.</li>
|
||||||
|
* <li>Otherwise, if {@code audience} is a {@link ForwardingAudience}, call this
|
||||||
|
* method for each audience member of the forwarding audience.</li>
|
||||||
|
* <li>Otherwise, do nothing.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param audience the audience
|
||||||
|
* @param packet the packet
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("OverrideOnly") // we need to access the audiences inside ForwardingAudience
|
||||||
|
public static void sendPacket(@NotNull Audience audience, @NotNull ServerPacket packet) {
|
||||||
|
if (audience instanceof Player) {
|
||||||
|
((Player) audience).getPlayerConnection().sendPacket(packet);
|
||||||
|
} else if (audience instanceof PacketGroupingAudience) {
|
||||||
|
PacketUtils.sendGroupedPacket(((PacketGroupingAudience) audience).getPlayers(), packet);
|
||||||
|
} else if (audience instanceof ForwardingAudience.Single) {
|
||||||
|
PacketUtils.sendPacket(((ForwardingAudience.Single) audience).audience(), packet);
|
||||||
|
} else if (audience instanceof ForwardingAudience) {
|
||||||
|
for (Audience member : ((ForwardingAudience) audience).audiences()) {
|
||||||
|
PacketUtils.sendPacket(member, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a {@link ServerPacket} to multiple players.
|
* Sends a {@link ServerPacket} to multiple players.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -84,6 +84,20 @@ public final class ChunkUtils {
|
|||||||
return isLoaded(chunk);
|
return isLoaded(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean same(@NotNull Chunk chunk, double x, double z) {
|
||||||
|
final int chunkX = getChunkCoordinate(x);
|
||||||
|
final int chunkZ = getChunkCoordinate(z);
|
||||||
|
return chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean same(@NotNull Position pos1, @NotNull Position pos2) {
|
||||||
|
final int x1 = getChunkCoordinate(pos1.getX());
|
||||||
|
final int z1 = getChunkCoordinate(pos1.getZ());
|
||||||
|
final int x2 = getChunkCoordinate(pos2.getX());
|
||||||
|
final int z2 = getChunkCoordinate(pos2.getZ());
|
||||||
|
return x1 == x2 && z1 == z2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param xz the instance coordinate to convert
|
* @param xz the instance coordinate to convert
|
||||||
* @return the chunk X or Z based on the argument
|
* @return the chunk X or Z based on the argument
|
||||||
|
@ -22,8 +22,6 @@ public class Main {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
MinecraftServer minecraftServer = MinecraftServer.init();
|
MinecraftServer minecraftServer = MinecraftServer.init();
|
||||||
|
|
||||||
// MinecraftServer.setShouldProcessNettyErrors(true);
|
|
||||||
|
|
||||||
BlockManager blockManager = MinecraftServer.getBlockManager();
|
BlockManager blockManager = MinecraftServer.getBlockManager();
|
||||||
blockManager.registerCustomBlock(new CustomBlockSample());
|
blockManager.registerCustomBlock(new CustomBlockSample());
|
||||||
blockManager.registerCustomBlock(new UpdatableBlockDemo());
|
blockManager.registerCustomBlock(new UpdatableBlockDemo());
|
||||||
|
Loading…
Reference in New Issue
Block a user