Merge branch 'master' into item-api

# Conflicts:
#	src/test/java/demo/PlayerInit.java
This commit is contained in:
TheMode 2021-04-12 00:44:47 +02:00
commit 1d5262caf2
16 changed files with 260 additions and 61 deletions

View File

@ -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;

View File

@ -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.
*/ */

View File

@ -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());

View File

@ -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);
}); });

View File

@ -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;
} }
} }

View File

@ -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.

View File

@ -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;

View File

@ -1,4 +1,4 @@
package net.minestom.server.benchmark; package net.minestom.server.monitoring;
public class ThreadResult { public class ThreadResult {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -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.");
}
}
}

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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());