Merge branch 'master' into new-position-api

This commit is contained in:
TheMode 2021-06-29 20:10:52 +02:00
commit f8a9664fd9
70 changed files with 747 additions and 380 deletions

View File

@ -36,9 +36,6 @@ allprojects {
name 'sponge'
url 'https://repo.spongepowered.org/maven'
}
maven {
url "https://repo.velocitypowered.com/snapshots/"
}
}
javadoc {
options {
@ -111,11 +108,10 @@ dependencies {
testCompileOnly "org.mockito:mockito-core:2.28.2"
// Netty
api 'io.netty:netty-handler:4.1.63.Final'
api 'io.netty:netty-codec:4.1.63.Final'
api 'io.netty:netty-transport-native-epoll:4.1.63.Final:linux-x86_64'
api 'io.netty:netty-transport-native-kqueue:4.1.63.Final:osx-x86_64'
api 'io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.5.Final:linux-x86_64'
api 'io.netty:netty-handler:4.1.65.Final'
api 'io.netty:netty-codec:4.1.65.Final'
api 'io.netty:netty-transport-native-epoll:4.1.65.Final:linux-x86_64'
api 'io.netty:netty-transport-native-kqueue:4.1.65.Final:osx-x86_64'
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
api 'it.unimi.dsi:fastutil:8.5.4'
@ -142,6 +138,8 @@ dependencies {
// https://search.maven.org/artifact/org.fusesource.jansi/jansi/2.3.2/jar
implementation 'org.fusesource.jansi:jansi:2.3.2'
implementation 'com.github.ben-manes.caffeine:caffeine:3.0.2'
// Guava 21.0+ required for Mixin
api 'com.google.guava:guava:30.1-jre'
@ -153,9 +151,6 @@ dependencies {
api "org.ow2.asm:asm-commons:${asmVersion}"
api "org.spongepowered:mixin:${mixinVersion}"
// Compression
implementation "com.velocitypowered:velocity-native:1.1.0-SNAPSHOT"
// Path finding
api 'com.github.MadMartian:hydrazine-path-finding:1.6.0'

View File

@ -1,6 +1,5 @@
package net.minestom.server;
import com.google.common.collect.Queues;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
@ -34,8 +33,8 @@ public final class UpdateManager {
// TODO make configurable
private ThreadProvider threadProvider = new SingleThreadProvider();
private final Queue<LongConsumer> tickStartCallbacks = Queues.newConcurrentLinkedQueue();
private final Queue<LongConsumer> tickEndCallbacks = Queues.newConcurrentLinkedQueue();
private final Queue<LongConsumer> tickStartCallbacks = new ConcurrentLinkedQueue<>();
private final Queue<LongConsumer> tickEndCallbacks = new ConcurrentLinkedQueue<>();
private final List<Consumer<TickMonitor>> tickMonitors = new CopyOnWriteArrayList<>();
/**

View File

@ -1,6 +1,5 @@
package net.minestom.server.acquirable;
import com.google.common.annotations.Beta;
import net.minestom.server.entity.Entity;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.thread.TickThread;
@ -14,7 +13,7 @@ import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;
@Beta
@ApiStatus.Experimental
public interface Acquirable<T> {
/**

View File

@ -1,15 +1,15 @@
package net.minestom.server.acquirable;
import com.google.common.annotations.Beta;
import net.minestom.server.thread.TickThread;
import net.minestom.server.utils.async.AsyncUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
@Beta
@ApiStatus.Experimental
public class AcquirableCollection<E> implements Collection<Acquirable<E>> {
private final Collection<Acquirable<E>> acquirableCollection;

View File

@ -14,6 +14,7 @@ import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.message.ChatPosition;
import net.minestom.server.message.Messenger;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
@ -44,6 +45,14 @@ public interface PacketGroupingAudience extends ForwardingAudience {
*/
@NotNull Collection<Player> getPlayers();
/**
* Broadcast a ServerPacket to all players of this audience
* @param packet the packet to broadcast
*/
default void sendGroupedPacket(ServerPacket packet) {
PacketUtils.sendGroupedPacket(this.getPlayers(), packet);
}
@Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
Messenger.sendMessage(this.getPlayers(), message, ChatPosition.fromMessageType(type), source.uuid());
@ -51,28 +60,28 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void sendActionBar(@NotNull Component message) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ActionBarPacket(message));
sendGroupedPacket(new ActionBarPacket(message));
}
@Override
default void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new PlayerListHeaderAndFooterPacket(header, footer));
sendGroupedPacket(new PlayerListHeaderAndFooterPacket(header, footer));
}
@Override
default void showTitle(@NotNull Title title) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new SetTitleTextPacket(title.title()));
PacketUtils.sendGroupedPacket(this.getPlayers(), new SetTitleSubTitlePacket(title.subtitle()));
sendGroupedPacket(new SetTitleTextPacket(title.title()));
sendGroupedPacket(new SetTitleSubTitlePacket(title.subtitle()));
}
@Override
default void clearTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ClearTitlesPacket());
sendGroupedPacket(new ClearTitlesPacket());
}
@Override
default void resetTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ClearTitlesPacket(true));
sendGroupedPacket(new ClearTitlesPacket(true));
}
@Override
@ -87,13 +96,13 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void playSound(@NotNull Sound sound, double x, double y, double z) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
sendGroupedPacket(AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
}
@Override
default void playSound(@NotNull Sound sound, Sound.@NotNull Emitter emitter) {
if (emitter != Sound.Emitter.self()) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, emitter));
sendGroupedPacket(AdventurePacketConvertor.createSoundPacket(sound, emitter));
} else {
// if we're playing on self, we need to delegate to each audience member
for (Audience audience : this.audiences()) {
@ -104,7 +113,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void stopSound(@NotNull SoundStop stop) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundStopPacket(stop));
sendGroupedPacket(AdventurePacketConvertor.createSoundStopPacket(stop));
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.attribute;
import io.netty.util.internal.ThreadLocalRandom;
import net.minestom.server.utils.UniqueIdUtils;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* Represent an attribute modifier.

View File

@ -5,6 +5,7 @@ import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.permission.PermissionHandler;
import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.NotNull;
/**
@ -12,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
* <p>
* Main implementations are {@link Player} and {@link ConsoleSender}.
*/
public interface CommandSender extends PermissionHandler, Audience {
public interface CommandSender extends PermissionHandler, Audience, TagHandler {
/**
* Sends a raw string message.
@ -28,7 +29,7 @@ public interface CommandSender extends PermissionHandler, Audience {
*
* @param messages the messages to send
*/
default void sendMessage(@NotNull String @NotNull[] messages) {
default void sendMessage(@NotNull String @NotNull [] messages) {
for (String message : messages) {
sendMessage(message);
}
@ -39,9 +40,8 @@ public interface CommandSender extends PermissionHandler, Audience {
* If this is not a {@link Player}, only the content of the message will be sent as a string.
*
* @param text The {@link JsonMessage} to send.
*
* @deprecated Use {@link #sendMessage(Component)}
* */
*/
@Deprecated
default void sendMessage(@NotNull JsonMessage text) {
this.sendMessage(text.asComponent());

View File

@ -3,9 +3,12 @@ package net.minestom.server.command;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.minestom.server.permission.Permission;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -16,10 +19,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
* Represents the console when sending a command to the server.
*/
public class ConsoleSender implements CommandSender {
private static final PlainComponentSerializer PLAIN_SERIALIZER = PlainComponentSerializer.plain();
private static final PlainTextComponentSerializer PLAIN_SERIALIZER = PlainTextComponentSerializer.plainText();
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleSender.class);
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
private final NBTCompound nbtCompound = new NBTCompound();
@Override
public void sendMessage(@NotNull String message) {
@ -27,7 +31,7 @@ public class ConsoleSender implements CommandSender {
}
@Override
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
// we don't use the serializer here as we just need the plain text of the message
this.sendMessage(PLAIN_SERIALIZER.serialize(message));
}
@ -47,4 +51,14 @@ public class ConsoleSender implements CommandSender {
public ConsoleSender asConsole() {
return this;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(nbtCompound);
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbtCompound, value);
}
}

View File

@ -3,7 +3,10 @@ package net.minestom.server.command;
import net.minestom.server.command.builder.CommandContext;
import net.kyori.adventure.audience.Audience;
import net.minestom.server.permission.Permission;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collections;
import java.util.HashSet;
@ -19,10 +22,21 @@ import java.util.Set;
public class ServerSender implements CommandSender {
private final Set<Permission> permissions = Collections.unmodifiableSet(new HashSet<>());
private final NBTCompound nbtCompound = new NBTCompound();
@NotNull
@Override
public Set<Permission> getAllPermissions() {
return permissions;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(nbtCompound);
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbtCompound, value);
}
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.command.builder;
import com.google.common.annotations.Beta;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minestom.server.command.CommandSender;
@ -10,6 +9,7 @@ import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.ArgumentWord;
import net.minestom.server.command.builder.condition.CommandCondition;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@ -221,7 +221,7 @@ public class Command {
* @param format the syntax format
* @return the newly created {@link CommandSyntax syntaxes}.
*/
@Beta
@ApiStatus.Experimental
public @NotNull Collection<CommandSyntax> addSyntax(@NotNull CommandExecutor executor, @NotNull String format) {
return addSyntax(executor, ArgumentType.generate(format));
}
@ -302,7 +302,7 @@ public class Command {
public void globalListener(@NotNull CommandSender sender, @NotNull CommandContext context, @NotNull String command) {
}
@Beta
@ApiStatus.Experimental
public @NotNull Set<String> getSyntaxesStrings() {
Set<String> syntaxes = new HashSet<>();
@ -320,7 +320,7 @@ public class Command {
return syntaxes;
}
@Beta
@ApiStatus.Experimental
public @NotNull String getSyntaxesTree() {
Node commandNode = new Node();
commandNode.names.addAll(Arrays.asList(getNames()));

View File

@ -1,7 +1,7 @@
package net.minestom.server.command.builder;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
@ -25,7 +25,7 @@ public class CommandDispatcher {
private final Map<String, Command> commandMap = new HashMap<>();
private final Set<Command> commands = new HashSet<>();
private final Cache<String, CommandResult> cache = CacheBuilder.newBuilder()
private final Cache<String, CommandResult> cache = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.build();

View File

@ -1,6 +1,5 @@
package net.minestom.server.command.builder.arguments;
import com.google.common.annotations.Beta;
import net.minestom.server.command.builder.ArgumentCallback;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandExecutor;
@ -8,6 +7,7 @@ import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -210,8 +210,8 @@ public abstract class Argument<T> {
/**
* Gets the suggestion callback of the argument
*
* @see #setSuggestionCallback
* @return the suggestion callback of the argument, null if it doesn't exist
* @see #setSuggestionCallback
*/
@Nullable
public SuggestionCallback getSuggestionCallback() {
@ -247,7 +247,7 @@ public abstract class Argument<T> {
* @param <O> The type of output expected.
* @return A new ArgumentMap that can get this complex object type.
*/
@Beta
@ApiStatus.Experimental
public <O> @NotNull ArgumentMap<T, O> map(@NotNull ArgumentMap.Mapper<T, O> mapper) {
return new ArgumentMap<>(this, mapper);
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.command.builder.arguments;
import com.google.common.annotations.Beta;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.builder.CommandDispatcher;
import net.minestom.server.command.builder.CommandResult;
@ -8,6 +7,7 @@ import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
public class ArgumentCommand extends Argument<CommandResult> {
@ -69,7 +69,7 @@ public class ArgumentCommand extends Argument<CommandResult> {
return shortcut;
}
@Beta
@ApiStatus.Experimental
public ArgumentCommand setShortcut(@NotNull String shortcut) {
this.shortcut = shortcut;
return this;

View File

@ -1,6 +1,5 @@
package net.minestom.server.command.builder.arguments;
import com.google.common.annotations.Beta;
import net.minestom.server.command.builder.arguments.minecraft.*;
import net.minestom.server.command.builder.arguments.minecraft.registry.*;
import net.minestom.server.command.builder.arguments.number.ArgumentDouble;
@ -11,6 +10,7 @@ import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBl
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2;
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
import net.minestom.server.command.builder.parser.ArgumentParser;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@ -248,7 +248,7 @@ public class ArgumentType {
* <p>
* Note: this feature is in beta and is very likely to change depending on feedback.
*/
@Beta
@ApiStatus.Experimental
public static Argument<?>[] generate(@NotNull String format) {
return ArgumentParser.generate(format);
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.command.builder.parser;
import com.google.common.annotations.Beta;
import net.minestom.server.command.builder.arguments.*;
import net.minestom.server.command.builder.arguments.minecraft.*;
import net.minestom.server.command.builder.arguments.minecraft.registry.*;
@ -12,6 +11,7 @@ import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVe
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -63,7 +63,7 @@ public class ArgumentParser {
ARGUMENT_FUNCTION_MAP.put("relativevec2", ArgumentRelativeVec2::new);
}
@Beta
@ApiStatus.Experimental
public static @NotNull Argument<?>[] generate(@NotNull String format) {
List<Argument<?>> result = new ArrayList<>();

View File

@ -6,7 +6,10 @@ import org.jetbrains.annotations.Nullable;
* Represents an element which can have a {@link Data} attached to it.
* <p>
* The data will always be optional and can therefore be null.
*
* @deprecated switch to the Tag API instead
*/
@Deprecated
public interface DataContainer {
/**
@ -16,9 +19,10 @@ public interface DataContainer {
* meaning that this will be null if no data has been defined.
*
* @return the {@link Data} of this container, can be null
* @deprecated use the tag API https://wiki.minestom.com/feature/tags
*/
@Nullable
Data getData();
@Deprecated
@Nullable Data getData();
/**
* Sets the {@link Data} of this container.
@ -27,7 +31,8 @@ public interface DataContainer {
* on your use-case.
*
* @param data the new {@link Data} of this container, null to remove it
* @deprecated use the tag API https://wiki.minestom.com/feature/tags
*/
@Deprecated
void setData(@Nullable Data data);
}

View File

@ -1,7 +1,5 @@
package net.minestom.server.entity;
import com.google.common.annotations.Beta;
import com.google.common.collect.Queues;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
@ -35,6 +33,8 @@ import net.minestom.server.permission.PermissionHandler;
import net.minestom.server.potion.Potion;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.TimedPotion;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
@ -51,9 +51,11 @@ import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@ -65,7 +67,7 @@ import java.util.function.UnaryOperator;
* <p>
* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead.
*/
public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, DataContainer, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, DataContainer, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
private static final Map<Integer, Entity> ENTITY_BY_ID = new ConcurrentHashMap<>();
private static final Map<UUID, Entity> ENTITY_BY_UUID = new ConcurrentHashMap<>();
@ -93,9 +95,18 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
protected Vector velocity = new Vector(); // Movement in block per second
protected boolean hasPhysics = true;
/**
* The amount of drag applied on the Y axle.
* <p>
* Unit: 1/tick
*/
protected double gravityDragPerTick;
/**
* Acceleration on the Y axle due to gravity
* <p>
* Unit: blocks/tick
*/
protected double gravityAcceleration;
protected double gravityTerminalVelocity;
protected int gravityTickCount; // Number of tick where gravity tick was applied
private boolean autoViewable;
@ -103,6 +114,7 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
protected final Set<Player> viewers = ConcurrentHashMap.newKeySet();
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
private Data data;
private final NBTCompound nbtCompound = new NBTCompound();
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
protected UUID uuid;
@ -128,7 +140,7 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
private final List<TimedPotion> effects = new CopyOnWriteArrayList<>();
// list of scheduled tasks to be executed during the next entity tick
protected final Queue<Consumer<Entity>> nextTick = Queues.newConcurrentLinkedQueue();
protected final Queue<Consumer<Entity>> nextTick = new ConcurrentLinkedQueue<>();
// Tick related
private long ticks;
@ -159,6 +171,8 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
Entity.ENTITY_BY_UUID.put(uuid, this);
this.eventNode = EventNode.value("entity-" + uuid, EventFilter.ENTITY, this::equals);
initializeDefaultGravity();
}
public Entity(@NotNull EntityType entityType) {
@ -524,13 +538,11 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
Vector newVelocityOut = new Vector();
// Gravity force
final double gravityY = !hasNoGravity() ? Math.min(
gravityDragPerTick + (gravityAcceleration * (double) gravityTickCount),
gravityTerminalVelocity) : 0;
final double gravityY = hasNoGravity() ? 0 : gravityAcceleration;
final Vector deltaPos = new Vector(
getVelocity().getX() / tps,
(getVelocity().getY() - gravityY) / tps,
getVelocity().getY() / tps - gravityY,
getVelocity().getZ() / tps
);
@ -591,6 +603,8 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
this.velocity.setX(velocity.getX() * drag);
this.velocity.setZ(velocity.getZ() * drag);
if (!hasNoGravity())
this.velocity.setY(velocity.getY() * (1-gravityDragPerTick));
if (velocity.equals(new Vector())) {
this.velocity.zero();
@ -982,15 +996,6 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
return gravityAcceleration;
}
/**
* Gets the maximum gravity velocity.
*
* @return the maximum gravity velocity in block
*/
public double getGravityTerminalVelocity() {
return gravityTerminalVelocity;
}
/**
* Gets the number of tick this entity has been applied gravity.
*
@ -1005,13 +1010,11 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
*
* @param gravityDragPerTick the gravity drag per tick in block
* @param gravityAcceleration the gravity acceleration in block
* @param gravityTerminalVelocity the gravity terminal velocity (maximum) in block
* @see <a href="https://minecraft.gamepedia.com/Entity#Motion_of_entities">Entities motion</a>
*/
public void setGravity(double gravityDragPerTick, double gravityAcceleration, double gravityTerminalVelocity) {
public void setGravity(double gravityDragPerTick, double gravityAcceleration) {
this.gravityDragPerTick = gravityDragPerTick;
this.gravityAcceleration = gravityAcceleration;
this.gravityTerminalVelocity = gravityTerminalVelocity;
}
/**
@ -1620,16 +1623,127 @@ public class Entity implements Viewable, Tickable, EventHandler<EntityEvent>, Da
return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN);
}
@Beta
@ApiStatus.Experimental
public <T extends Entity> @NotNull Acquirable<T> getAcquirable() {
return (Acquirable<T>) acquirable;
}
@Beta
@ApiStatus.Experimental
public <T extends Entity> @NotNull Acquirable<T> getAcquirable(@NotNull Class<T> clazz) {
return (Acquirable<T>) acquirable;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(nbtCompound);
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbtCompound, value);
}
/**
* Sets the Entity's {@link gravityAcceleration} and {@link gravityDragPerTick} fields to
* the default values according to <a href="https://minecraft.fandom.com/wiki/Entity#Motion_of_entities">Motion of entities</a>
*/
@SuppressWarnings("JavadocReference")
private void initializeDefaultGravity() {
// TODO Add support for these values in the data generator
// Acceleration
switch (entityType) {
// 0
case ITEM_FRAME:
this.gravityAcceleration = 0;
break;
// 0.03
case EGG:
case FISHING_BOBBER:
case EXPERIENCE_BOTTLE:
case ENDER_PEARL:
case POTION:
case SNOWBALL:
this.gravityAcceleration = 0.03;
break;
// 0.04
case BOAT:
case TNT:
case FALLING_BLOCK:
case ITEM:
case MINECART:
this.gravityAcceleration = 0.04;
break;
// 0.05
case ARROW:
case SPECTRAL_ARROW:
case TRIDENT:
this.gravityAcceleration = 0.05;
break;
// 0.06
case LLAMA_SPIT:
this.gravityAcceleration = 0.06;
break;
// 0.1
case FIREBALL:
case WITHER_SKULL:
case DRAGON_FIREBALL:
this.gravityAcceleration = 0.1;
break;
// 0.08
default:
this.gravityAcceleration = 0.08;
break;
}
// Drag
switch (entityType) {
// 0
case BOAT:
this.gravityDragPerTick = 0;
break;
// 0.01
case LLAMA_SPIT:
case ENDER_PEARL:
case POTION:
case SNOWBALL:
case EGG:
case TRIDENT:
case SPECTRAL_ARROW:
case ARROW:
this.gravityDragPerTick = 0.01;
break;
// 0.05
case MINECART:
this.gravityDragPerTick = 0.05;
break;
// 0.08
case FISHING_BOBBER:
this.gravityDragPerTick = 0.08;
break;
// 0.02
default:
this.gravityDragPerTick = 0.02;
break;
}
}
/**
* Applies knockback to the entity
*
* @param strength the strength of the knockback, 0.4 is the vanilla value for a bare hand hit
* @param x knockback on x axle, for default knockback use the following formula <pre>sin(attacker.yaw * (pi/180))</pre>
* @param z knockback on z axle, for default knockback use the following formula <pre>-cos(attacker.yaw * (pi/180))</pre>
*/
public void takeKnockback(final float strength, final double x, final double z) {
if (strength > 0) {
//TODO check possible side effects of unnatural TPS (other than 20TPS)
final Vector velocityModifier = new Vector(x, 0d, z).normalize().multiply(strength * MinecraftServer.TICK_PER_SECOND / 2);
this.velocity.setX(velocity.getX() / 2d - velocityModifier.getX());
this.velocity.setY(onGround ? Math.min(.4d, velocity.getY() / 2d + strength) * MinecraftServer.TICK_PER_SECOND : velocity.getY());
this.velocity.setZ(velocity.getZ() / 2d - velocityModifier.getZ());
}
}
public enum Pose {
STANDING,
FALL_FLYING,

View File

@ -47,7 +47,6 @@ public class EntityProjectile extends Entity {
if (getEntityMeta() instanceof ProjectileMeta) {
((ProjectileMeta) getEntityMeta()).setShooter(this.shooter);
}
setGravity(0.02f, 0.04f, 1.96f);
}
@Nullable

View File

@ -17,7 +17,6 @@ public class ExperienceOrb extends Entity {
public ExperienceOrb(short experienceCount, @NotNull Position spawnPosition) {
super(EntityType.EXPERIENCE_ORB, spawnPosition);
setGravity(0.02f, 0.04f, 1.96f);
setBoundingBox(0.5f, 0.5f, 0.5f);
//todo vanilla sets random velocity here?
this.experienceCount = experienceCount;

View File

@ -43,7 +43,6 @@ public class ItemEntity extends Entity {
public ItemEntity(@NotNull ItemStack itemStack, @NotNull Position spawnPosition) {
super(EntityType.ITEM, spawnPosition);
setItemStack(itemStack);
setGravity(0.02f, 0.04f, 1.96f);
setBoundingBox(0.25f, 0.25f, 0.25f);
}

View File

@ -90,7 +90,6 @@ public class LivingEntity extends Entity implements EquipmentHandler {
*/
public LivingEntity(@NotNull EntityType entityType, @NotNull UUID uuid) {
this(entityType, uuid, new Position());
setGravity(0.02f, 0.08f, 3.92f);
initEquipments();
}
@ -104,7 +103,6 @@ public class LivingEntity extends Entity implements EquipmentHandler {
@Deprecated
public LivingEntity(@NotNull EntityType entityType, @NotNull UUID uuid, @NotNull Position spawnPosition) {
super(entityType, uuid, spawnPosition);
setGravity(0.02f, 0.08f, 3.92f);
initEquipments();
}
@ -781,4 +779,18 @@ public class LivingEntity extends Entity implements EquipmentHandler {
return null;
}
/**
* Applies knockback
* <p>
* Note: The strength is reduced based on knockback resistance
*
* @param strength the strength of the knockback, 0.4 is the vanilla value for a bare hand hit
* @param x knockback on x axle, for default knockback use the following formula <pre>sin(attacker.yaw * (pi/180))</pre>
* @param z knockback on z axle, for default knockback use the following formula <pre>-cos(attacker.yaw * (pi/180))</pre>
*/
@Override
public void takeKnockback(float strength, final double x, final double z) {
strength *= 1 - getAttributeValue(Attribute.KNOCKBACK_RESISTANCE);
super.takeKnockback(strength, x, z);
}
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.entity;
import com.google.common.collect.Queues;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.identity.Identified;
@ -87,6 +86,7 @@ import org.jetbrains.annotations.Nullable;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.UnaryOperator;
@ -119,7 +119,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private final AtomicInteger teleportId = new AtomicInteger();
private int receivedTeleportId;
private final Queue<ClientPlayPacket> packets = Queues.newConcurrentLinkedQueue();
private final Queue<ClientPlayPacket> packets = new ConcurrentLinkedQueue<>();
private final boolean levelFlat;
private final PlayerSettings settings;
private float exp;
@ -1379,9 +1379,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
* @param entity the entity to spectate
*/
public void spectate(@NotNull Entity entity) {
CameraPacket cameraPacket = new CameraPacket();
cameraPacket.cameraId = entity.getEntityId();
playerConnection.sendPacket(cameraPacket);
playerConnection.sendPacket(new CameraPacket(entity));
}
/**

View File

@ -24,7 +24,7 @@ public class DoNothingGoal extends GoalSelector {
public DoNothingGoal(EntityCreature entityCreature, long time, float chance) {
super(entityCreature);
this.time = time;
this.chance = MathUtils.clampFloat(chance, 0, 1);
this.chance = MathUtils.clamp(chance, 0, 1);
}
@Override

View File

@ -8,8 +8,11 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
/**
* Represents a type of damage, required when calling {@link LivingEntity#damage(DamageType, float)}
@ -19,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
* Be aware that this class implements {@link DataContainer}
* so you can add your own data to an already existing damage type without any wrapper.
*/
public class DamageType implements DataContainer {
public class DamageType implements TagHandler, DataContainer {
public static final DamageType VOID = new DamageType("attack.outOfWorld");
public static final DamageType GRAVITY = new DamageType("attack.fall");
@ -30,6 +33,8 @@ public class DamageType implements DataContainer {
}
};
private final String identifier;
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
/**
@ -159,4 +164,18 @@ public class DamageType implements DataContainer {
public void setData(Data data) {
this.data = data;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
}

View File

@ -29,6 +29,7 @@ import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ExtensionManager {
@ -379,8 +380,14 @@ public class ExtensionManager {
*/
@Nullable
private DiscoveredExtension discoverFromJar(@NotNull File file) {
try (ZipFile f = new ZipFile(file);
InputStreamReader reader = new InputStreamReader(f.getInputStream(f.getEntry("extension.json")))) {
try (ZipFile f = new ZipFile(file);) {
ZipEntry entry = f.getEntry("extension.json");
if (entry == null)
throw new IllegalStateException("Missing extension.json in extension " + file.getName() + ".");
InputStreamReader reader = new InputStreamReader(f.getInputStream(entry));
// Initialize DiscoveredExtension from GSON.
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);

View File

@ -1,7 +1,7 @@
package net.minestom.server.extras.velocity;
import com.google.common.net.InetAddresses;
import io.netty.buffer.ByteBuf;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.utils.binary.BinaryReader;
import org.jetbrains.annotations.NotNull;
@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -73,7 +74,12 @@ public final class VelocityProxy {
}
public static InetAddress readAddress(@NotNull BinaryReader reader) {
return InetAddresses.forString(reader.readSizedString());
try {
return InetAddress.getByName(reader.readSizedString());
} catch (UnknownHostException e) {
MinecraftServer.getExceptionManager().handleException(e);
return null;
}
}
public static PlayerSkin readSkin(@NotNull BinaryReader reader) {

View File

@ -16,6 +16,8 @@ import net.minestom.server.instance.block.CustomBlock;
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.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader;
@ -27,6 +29,7 @@ import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -47,7 +50,7 @@ import java.util.concurrent.ConcurrentHashMap;
* You generally want to avoid storing references of this object as this could lead to a huge memory leak,
* you should store the chunk coordinates instead.
*/
public abstract class Chunk implements Viewable, Tickable, DataContainer {
public abstract class Chunk implements Viewable, Tickable, TagHandler, DataContainer {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager();
@ -75,6 +78,7 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
protected PFColumnarSpace columnarSpace;
// Data
private final NBTCompound nbt = new NBTCompound();
protected Data data;
public Chunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ, boolean shouldGenerate) {
@ -480,6 +484,16 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
return unmodifiableViewers;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(nbt);
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbt, value);
}
@Nullable
@Override
public Data getData() {

View File

@ -1,6 +1,5 @@
package net.minestom.server.instance;
import com.google.common.collect.Queues;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.Pointers;
import net.minestom.server.MinecraftServer;
@ -29,6 +28,8 @@ import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.PacketUtils;
@ -44,9 +45,11 @@ import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
/**
@ -60,7 +63,7 @@ import java.util.function.Consumer;
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
*/
public abstract class Instance implements BlockModifier, Tickable, EventHandler<InstanceEvent>, DataContainer, PacketGroupingAudience {
public abstract class Instance implements BlockModifier, Tickable, TagHandler, PacketGroupingAudience, EventHandler<InstanceEvent>, DataContainer {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
@ -98,9 +101,11 @@ public abstract class Instance implements BlockModifier, Tickable, EventHandler<
protected UUID uniqueId;
// list of scheduled tasks to be executed during the next instance tick
protected final Queue<Consumer<Instance>> nextTick = Queues.newConcurrentLinkedQueue();
protected final Queue<Consumer<Instance>> nextTick = new ConcurrentLinkedQueue<>();
// instance custom data
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
// the explosion supplier
@ -1061,6 +1066,20 @@ public abstract class Instance implements BlockModifier, Tickable, EventHandler<
this.worldBorder.update();
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
/**
* Creates an explosion at the given position with the given strength.
* The algorithm used to compute damages is provided by {@link #getExplosionSupplier()}.

View File

@ -750,10 +750,7 @@ public class InstanceContainer extends Instance {
* @param blockStateId the new state of the block
*/
private void sendBlockChange(@NotNull Chunk chunk, @NotNull BlockPosition blockPosition, short blockStateId) {
BlockChangePacket blockChangePacket = new BlockChangePacket();
blockChangePacket.blockPosition = blockPosition;
blockChangePacket.blockStateId = blockStateId;
chunk.sendPacketToViewers(blockChangePacket);
chunk.sendPacketToViewers(new BlockChangePacket(blockPosition, blockStateId));
}
@Override

View File

@ -5,10 +5,13 @@ import net.minestom.server.data.DataContainer;
import net.minestom.server.inventory.click.InventoryClickProcessor;
import net.minestom.server.inventory.condition.InventoryCondition;
import net.minestom.server.item.ItemStack;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.ArrayList;
import java.util.Arrays;
@ -19,7 +22,7 @@ import java.util.function.UnaryOperator;
/**
* Represents an inventory where items can be modified/retrieved.
*/
public abstract class AbstractInventory implements InventoryClickHandler, DataContainer {
public abstract class AbstractInventory implements InventoryClickHandler, TagHandler, DataContainer {
private final int size;
protected final ItemStack[] itemStacks;
@ -29,6 +32,8 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
// the click processor which process all the clicks in the inventory
protected final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
protected AbstractInventory(int size) {
@ -210,6 +215,20 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
}
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
@Override
public @Nullable Data getData() {
return data;

View File

@ -403,16 +403,11 @@ public class PlayerInventory extends AbstractInventory implements EquipmentHandl
public boolean doubleClick(@NotNull Player player, int slot) {
final ItemStack cursor = getCursorItem();
final InventoryClickResult clickResult = clickProcessor.doubleClick(this, null, player, slot, cursor);
if (clickResult == null)
return false;
if (clickResult.doRefresh())
update();
setItemStack(slot, OFFSET, clickResult.getClicked());
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();
}
}

View File

@ -51,7 +51,7 @@ public class ItemMeta implements TagReadable, Writeable {
this.canDestroy = new HashSet<>(metaBuilder.canDestroy);
this.canPlaceOn = new HashSet<>(metaBuilder.canPlaceOn);
this.nbt = metaBuilder.nbt;
this.nbt = metaBuilder.nbt();
this.emptyBuilder = metaBuilder.getSupplier().get();
}
@ -117,11 +117,6 @@ public class ItemMeta implements TagReadable, Writeable {
return tag.read(nbt);
}
@Override
public boolean hasTag(@NotNull Tag<?> tag) {
return nbt.containsKey(tag.getKey());
}
public @NotNull NBTCompound toNBT() {
return nbt.deepClone();
}
@ -169,12 +164,7 @@ public class ItemMeta implements TagReadable, Writeable {
@Deprecated
@Contract(pure = true)
public <T> T getOrDefault(@NotNull Tag<T> tag, @Nullable T defaultValue) {
var key = tag.getKey();
if (nbt.containsKey(key)) {
return tag.read(toNBT());
} else {
return defaultValue;
}
return tag.defaultValue(defaultValue).read(toNBT());
}
/**

View File

@ -2,7 +2,6 @@ package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.tag.Tag;
@ -15,12 +14,17 @@ import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Consumer;
import java.util.function.Supplier;
public abstract class ItemMetaBuilder implements TagWritable {
protected NBTCompound nbt = new NBTCompound();
private static final AtomicReferenceFieldUpdater<ItemMetaBuilder, NBTCompound> NBT_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(ItemMetaBuilder.class, NBTCompound.class, "nbt");
protected volatile boolean built = false;
private volatile NBTCompound nbt = new NBTCompound();
protected int damage;
protected boolean unbreakable;
@ -36,21 +40,21 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("_ -> this")
public @NotNull ItemMetaBuilder damage(int damage) {
this.damage = damage;
this.nbt.setInt("Damage", damage);
mutateNbt(compound -> compound.setInt("Damage", damage));
return this;
}
@Contract("_ -> this")
public @NotNull ItemMetaBuilder unbreakable(boolean unbreakable) {
this.unbreakable = unbreakable;
this.nbt.setByte("Unbreakable", (byte) (unbreakable ? 1 : 0));
mutateNbt(compound -> compound.setByte("Unbreakable", (byte) (unbreakable ? 1 : 0)));
return this;
}
@Contract("_ -> this")
public @NotNull ItemMetaBuilder hideFlag(int hideFlag) {
this.hideFlag = hideFlag;
this.nbt.setInt("HideFlags", hideFlag);
mutateNbt(compound -> compound.setInt("HideFlags", hideFlag));
return this;
}
@ -79,7 +83,7 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("_ -> this")
public @NotNull ItemMetaBuilder lore(@NotNull List<@NotNull Component> lore) {
this.lore = lore;
this.lore = new ArrayList<>(lore);
handleCompound("display", nbtCompound -> {
final NBTList<NBTString> loreNBT = new NBTList<>(NBTTypes.TAG_String);
for (Component line : lore) {
@ -98,8 +102,8 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("_ -> this")
public @NotNull ItemMetaBuilder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
this.enchantmentMap = enchantments;
handleMap(enchantmentMap, "Enchantments", nbt, () -> {
this.enchantmentMap = new HashMap<>(enchantments);
handleMap(enchantmentMap, "Enchantments", () -> {
NBTUtils.writeEnchant(nbt, "Enchantments", enchantmentMap);
return nbt.get("Enchantments");
});
@ -115,16 +119,16 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("-> this")
public @NotNull ItemMetaBuilder clearEnchantment() {
this.enchantmentMap.clear();
this.enchantmentMap = Collections.emptyMap();
enchantments(enchantmentMap);
return this;
}
@Contract("_ -> this")
public @NotNull ItemMetaBuilder attributes(@NotNull List<@NotNull ItemAttribute> attributes) {
this.attributes = attributes;
this.attributes = new ArrayList<>(attributes);
handleCollection(attributes, "AttributeModifiers", nbt, () -> {
handleCollection(attributes, "AttributeModifiers", () -> {
NBTList<NBTCompound> attributesNBT = new NBTList<>(NBTTypes.TAG_Compound);
for (ItemAttribute itemAttribute : attributes) {
final UUID uuid = itemAttribute.getUuid();
@ -147,17 +151,16 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("_ -> this")
public @NotNull ItemMetaBuilder customModelData(int customModelData) {
this.customModelData = customModelData;
this.nbt.setInt("CustomModelData", customModelData);
mutateNbt(compound -> compound.setInt("CustomModelData", customModelData));
return this;
}
@Contract("_ -> this")
public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Set<@NotNull Block> blocks) {
this.canPlaceOn = blocks;
handleCollection(canPlaceOn, "CanPlaceOn", nbt, () -> {
this.canPlaceOn = new HashSet<>(blocks);
handleCollection(canPlaceOn, "CanPlaceOn", () -> {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
canPlaceOn.forEach(block -> list.add(new NBTString(block.getName())));
nbt.set("CanPlaceOn", list);
return list;
});
return this;
@ -170,11 +173,10 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Contract("_ -> this")
public @NotNull ItemMetaBuilder canDestroy(@NotNull Set<@NotNull Block> blocks) {
this.canDestroy = blocks;
handleCollection(canDestroy, "CanDestroy", nbt, () -> {
this.canDestroy = new HashSet<>(blocks);
handleCollection(canDestroy, "CanDestroy", () -> {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
canDestroy.forEach(block -> list.add(new NBTString(block.getName())));
nbt.set("CanDestroy", list);
return list;
});
return this;
@ -187,7 +189,7 @@ public abstract class ItemMetaBuilder implements TagWritable {
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbt, value);
mutateNbt(compound -> tag.write(compound, value));
}
public <T> @NotNull ItemMetaBuilder set(@NotNull Tag<T> tag, @Nullable T value) {
@ -202,63 +204,85 @@ public abstract class ItemMetaBuilder implements TagWritable {
protected abstract @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier();
protected synchronized void mutateNbt(Consumer<NBTCompound> consumer) {
if (built) {
built = false;
final var currentNbt = nbt;
NBT_UPDATER.compareAndSet(this, currentNbt, currentNbt.deepClone());
}
consumer.accept(nbt);
}
protected synchronized NBTCompound nbt() {
return nbt;
}
protected @NotNull ItemMeta generate() {
this.built = true;
return build();
}
protected void handleCompound(@NotNull String key,
@NotNull Consumer<@NotNull NBTCompound> consumer) {
NBTCompound compound = null;
boolean newNbt = false;
if (nbt.containsKey(key)) {
NBT dNbt = nbt.get(key);
if (dNbt instanceof NBTCompound) {
compound = (NBTCompound) dNbt;
}
} else {
compound = new NBTCompound();
newNbt = true;
}
if (compound != null) {
consumer.accept(compound);
if (newNbt && compound.getSize() > 0) {
this.nbt.set(key, compound);
} else if (!newNbt && compound.getSize() == 0) {
this.nbt.removeTag(key);
mutateNbt(nbt -> {
NBTCompound compound = null;
boolean newNbt = false;
if (nbt.containsKey(key)) {
NBT dNbt = nbt.get(key);
if (dNbt instanceof NBTCompound) {
compound = (NBTCompound) dNbt;
}
} else {
compound = new NBTCompound();
newNbt = true;
}
}
if (compound != null) {
consumer.accept(compound);
if (newNbt && compound.getSize() > 0) {
this.nbt.set(key, compound);
} else if (!newNbt && compound.getSize() == 0) {
this.nbt.removeTag(key);
}
}
});
}
protected void handleNullable(@Nullable Object value,
@NotNull String key,
@NotNull NBTCompound nbtCompound,
@NotNull Supplier<@NotNull NBT> supplier) {
if (value != null) {
nbtCompound.set(key, supplier.get());
} else {
nbtCompound.removeTag(key);
}
mutateNbt(compound -> {
if (value != null) {
compound.set(key, supplier.get());
} else {
compound.removeTag(key);
}
});
}
protected void handleCollection(@NotNull Collection<?> objects,
@NotNull String key,
@NotNull NBTCompound nbtCompound,
@NotNull Supplier<@NotNull NBT> supplier) {
if (!objects.isEmpty()) {
nbtCompound.set(key, supplier.get());
} else {
nbtCompound.removeTag(key);
}
mutateNbt(compound -> {
if (!objects.isEmpty()) {
compound.set(key, supplier.get());
} else {
compound.removeTag(key);
}
});
}
protected void handleMap(@NotNull Map<?, ?> objects,
@NotNull String key,
@NotNull NBTCompound nbtCompound,
@NotNull Supplier<@NotNull NBT> supplier) {
if (!objects.isEmpty()) {
nbtCompound.set(key, supplier.get());
} else {
nbtCompound.removeTag(key);
}
mutateNbt(compound -> {
if (!objects.isEmpty()) {
compound.set(key, supplier.get());
} else {
compound.removeTag(key);
}
});
}
@Contract(value = "_, _ -> new", pure = true)
@ -271,5 +295,4 @@ public abstract class ItemMetaBuilder implements TagWritable {
public interface Provider<T extends ItemMetaBuilder> {
}
}

View File

@ -203,11 +203,6 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
return meta.getTag(tag);
}
@Override
public boolean hasTag(@NotNull Tag<?> tag) {
return meta.hasTag(tag);
}
@Override
public @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(this.material,

View File

@ -1,8 +1,8 @@
package net.minestom.server.item;
import com.google.common.annotations.Beta;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.metadata.*;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -100,7 +100,7 @@ public class ItemStackBuilder {
return this;
}
@Beta
@ApiStatus.Experimental
@Contract(value = "_ -> this")
public @NotNull ItemStackBuilder stackingRule(@Nullable StackingRule stackingRule) {
this.stackingRule = stackingRule;
@ -109,9 +109,9 @@ public class ItemStackBuilder {
@Contract(value = "-> new", pure = true)
public @NotNull ItemStack build() {
if (amount > 0)
return new ItemStack(material, amount, metaBuilder.build(), stackingRule);
return ItemStack.AIR;
if (amount < 1)
return ItemStack.AIR;
return new ItemStack(material, amount, metaBuilder.generate(), stackingRule);
}
private static final class DefaultMeta extends ItemMetaBuilder {
@ -130,5 +130,4 @@ public class ItemStackBuilder {
return DefaultMeta::new;
}
}
}

View File

@ -45,18 +45,20 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<Co
public Builder lodestoneTracked(boolean lodestoneTracked) {
this.lodestoneTracked = lodestoneTracked;
this.nbt.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
mutateNbt(compound -> compound.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0)));
return this;
}
public Builder lodestoneDimension(@Nullable String lodestoneDimension) {
this.lodestoneDimension = lodestoneDimension;
if (lodestoneDimension != null) {
this.nbt.setString("LodestoneDimension", lodestoneDimension);
} else {
this.nbt.removeTag("LodestoneDimension");
}
mutateNbt(compound -> {
if (lodestoneDimension != null) {
compound.setString("LodestoneDimension", lodestoneDimension);
} else {
compound.removeTag("LodestoneDimension");
}
});
return this;
}
@ -64,15 +66,17 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<Co
public Builder lodestonePosition(@Nullable Position lodestonePosition) {
this.lodestonePosition = lodestonePosition;
if (lodestonePosition != null) {
NBTCompound posCompound = new NBTCompound();
posCompound.setInt("X", (int) lodestonePosition.getX());
posCompound.setInt("Y", (int) lodestonePosition.getY());
posCompound.setInt("Z", (int) lodestonePosition.getZ());
this.nbt.set("LodestonePos", posCompound);
} else {
this.nbt.removeTag("LodestonePos");
}
mutateNbt(compound -> {
if (lodestonePosition != null) {
NBTCompound posCompound = new NBTCompound();
posCompound.setInt("X", (int) lodestonePosition.getX());
posCompound.setInt("Y", (int) lodestonePosition.getY());
posCompound.setInt("Z", (int) lodestonePosition.getZ());
compound.set("LodestonePos", posCompound);
} else {
compound.removeTag("LodestonePos");
}
});
return this;
}

View File

@ -97,7 +97,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
if (!projectile.isAir()) {
chargedProjectiles.add(getItemCompound(projectile));
}
this.nbt.set("ChargedProjectiles", chargedProjectiles);
mutateNbt(compound -> compound.set("ChargedProjectiles", chargedProjectiles));
return this;
}
@ -123,7 +123,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
chargedProjectiles.add(getItemCompound(projectile1));
chargedProjectiles.add(getItemCompound(projectile2));
chargedProjectiles.add(getItemCompound(projectile3));
this.nbt.set("ChargedProjectiles", chargedProjectiles);
mutateNbt(compound -> compound.set("ChargedProjectiles", chargedProjectiles));
return this;
}
@ -135,7 +135,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
*/
public Builder charged(boolean charged) {
this.charged = charged;
this.nbt.setByte("Charged", (byte) (charged ? 1 : 0));
mutateNbt(compound -> compound.setByte("Charged", (byte) (charged ? 1 : 0)));
return this;
}

View File

@ -35,13 +35,13 @@ public class EnchantedBookMeta extends ItemMeta implements ItemMetaBuilder.Provi
private Map<Enchantment, Short> enchantments = new HashMap<>();
public @NotNull Builder enchantments(Map<Enchantment, Short> enchantments) {
public @NotNull Builder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
this.enchantments = enchantments;
NBTUtils.writeEnchant(nbt, "StoredEnchantments", enchantments);
mutateNbt(compound -> NBTUtils.writeEnchant(compound, "StoredEnchantments", enchantments));
return this;
}
public @NotNull Builder enchantment(Enchantment enchantment, short level) {
public @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) {
this.enchantments.put(enchantment, level);
enchantments(enchantments);
return this;

View File

@ -28,7 +28,7 @@ public class FireworkEffectMeta extends ItemMeta implements ItemMetaBuilder.Prov
public Builder effect(@Nullable FireworkEffect fireworkEffect) {
this.fireworkEffect = fireworkEffect;
this.nbt.set("Explosion", this.fireworkEffect.asCompound());
mutateNbt(compound -> compound.set("Explosion", this.fireworkEffect.asCompound()));
return this;
}

View File

@ -91,18 +91,18 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
public Builder mapId(int value) {
this.mapId = value;
this.nbt.setInt("map", mapId);
mutateNbt(compound -> compound.setInt("map", mapId));
return this;
}
public Builder mapScaleDirection(int value) {
this.mapScaleDirection = value;
this.nbt.setInt("map_scale_direction", value);
mutateNbt(compound -> compound.setInt("map_scale_direction", value));
return this;
}
public Builder decorations(List<MapDecoration> value) {
this.decorations = value;
this.decorations = new ArrayList<>(value);
NBTList<NBTCompound> decorationsList = new NBTList<>(NBTTypes.TAG_Compound);
for (MapDecoration decoration : decorations) {
@ -115,7 +115,7 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
decorationsList.add(decorationCompound);
}
this.nbt.set("Decorations", decorationsList);
mutateNbt(compound -> compound.set("Decorations", decorationsList));
return this;
}
@ -123,14 +123,16 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
public Builder mapColor(Color value) {
this.mapColor = value;
NBTCompound displayCompound;
if (nbt.containsKey("display")) {
displayCompound = nbt.getCompound("display");
} else {
displayCompound = new NBTCompound();
this.nbt.set("display", displayCompound);
}
displayCompound.setInt("MapColor", mapColor.asRGB());
mutateNbt(nbt -> {
NBTCompound displayCompound;
if (nbt.containsKey("display")) {
displayCompound = nbt.getCompound("display");
} else {
displayCompound = new NBTCompound();
nbt.set("display", displayCompound);
}
displayCompound.setInt("MapColor", mapColor.asRGB());
});
return this;
}

View File

@ -52,7 +52,7 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<Pot
public Builder potionType(@NotNull PotionType potionType) {
this.potionType = potionType;
this.nbt.setString("Potion", potionType.getNamespaceID().asString());
mutateNbt(compound -> compound.setString("Potion", potionType.getNamespaceID().asString()));
return this;
}
@ -71,14 +71,14 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<Pot
potionList.add(potionCompound);
}
this.nbt.set("CustomPotionEffects", potionList);
mutateNbt(compound -> compound.set("CustomPotionEffects", potionList));
return this;
}
public Builder color(@NotNull Color color) {
this.color = color;
this.nbt.setInt("CustomPotionColor", color.asRGB());
mutateNbt(compound -> compound.setInt("CustomPotionColor", color.asRGB()));
return this;
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.item.metadata;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.item.ItemMeta;
import net.minestom.server.item.ItemMetaBuilder;
import org.jetbrains.annotations.NotNull;
@ -53,22 +52,22 @@ public class WritableBookMeta extends ItemMeta implements ItemMetaBuilder.Provid
public Builder author(@Nullable String author) {
this.author = author;
handleNullable(author, "author", nbt,
handleNullable(author, "author",
() -> new NBTString(Objects.requireNonNull(author)));
return this;
}
public Builder title(@Nullable String title) {
this.title = title;
handleNullable(title, "title", nbt,
handleNullable(title, "title",
() -> new NBTString(Objects.requireNonNull(title)));
return this;
}
public Builder pages(@NotNull List<@NotNull Component> pages) {
this.pages = pages;
this.pages = new ArrayList<>(pages);
handleCollection(pages, "pages", nbt, () -> {
handleCollection(pages, "pages", () -> {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (Component page : pages) {
list.add(new NBTString(GsonComponentSerializer.gson().serialize(page)));

View File

@ -86,35 +86,35 @@ public class WrittenBookMeta extends ItemMeta implements ItemMetaBuilder.Provide
public Builder resolved(boolean resolved) {
this.resolved = resolved;
this.nbt.setByte("resolved", (byte) (resolved ? 1 : 0));
mutateNbt(compound -> compound.setByte("resolved", (byte) (resolved ? 1 : 0)));
return this;
}
public Builder generation(@Nullable WrittenBookGeneration generation) {
this.generation = generation;
handleNullable(generation, "generation", nbt,
handleNullable(generation, "generation",
() -> new NBTInt(Objects.requireNonNull(generation).ordinal()));
return this;
}
public Builder author(@Nullable String author) {
this.author = author;
handleNullable(author, "author", nbt,
handleNullable(author, "author",
() -> new NBTString(Objects.requireNonNull(author)));
return this;
}
public Builder title(@Nullable String title) {
this.title = title;
handleNullable(title, "title", nbt,
handleNullable(title, "title",
() -> new NBTString(Objects.requireNonNull(title)));
return this;
}
public Builder pages(@NotNull List<@NotNull Component> pages) {
this.pages = pages;
this.pages = new ArrayList<>(pages);
handleCollection(pages, "pages", nbt, () -> {
handleCollection(pages, "pages", () -> {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (Component page : pages) {
list.add(new NBTString(GsonComponentSerializer.gson().serialize(page)));

View File

@ -104,10 +104,7 @@ public class BlockPlacementListener {
//Send a block change with AIR as block to keep the client in sync,
//using refreshChunk results in the client not being in sync
//after rapid invalid block placements
BlockChangePacket blockChangePacket = new BlockChangePacket();
blockChangePacket.blockPosition = blockPosition;
blockChangePacket.blockStateId = Block.AIR.getBlockId();
player.getPlayerConnection().sendPacket(blockChangePacket);
player.getPlayerConnection().sendPacket(new BlockChangePacket(blockPosition, Block.AIR.getBlockId()));
}
return;
}

View File

@ -263,13 +263,6 @@ public class PlayerDiggingListener {
*/
private static void sendAcknowledgePacket(@NotNull Player player, @NotNull BlockPosition blockPosition, int blockStateId,
@NotNull ClientPlayerDiggingPacket.Status status, boolean success) {
AcknowledgePlayerDiggingPacket acknowledgePlayerDiggingPacket = new AcknowledgePlayerDiggingPacket();
acknowledgePlayerDiggingPacket.blockPosition = blockPosition;
acknowledgePlayerDiggingPacket.blockStateId = blockStateId;
acknowledgePlayerDiggingPacket.status = status;
acknowledgePlayerDiggingPacket.successful = success;
player.getPlayerConnection().sendPacket(acknowledgePlayerDiggingPacket);
player.getPlayerConnection().sendPacket(new AcknowledgePlayerDiggingPacket(blockPosition, blockStateId, status, success));
}
}

View File

@ -13,9 +13,6 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.incubator.channel.uring.IOUring;
import io.netty.incubator.channel.uring.IOUringEventLoopGroup;
import io.netty.incubator.channel.uring.IOUringServerSocketChannel;
import net.minestom.server.MinecraftServer;
import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.netty.channel.ClientChannel;
@ -82,14 +79,7 @@ public final class NettyServer {
// Find boss/worker event group
{
if (IOUring.isAvailable()) {
boss = new IOUringEventLoopGroup(2);
worker = new IOUringEventLoopGroup(workerThreadCount);
channel = IOUringServerSocketChannel.class;
LOGGER.info("Using io_uring");
} else if (Epoll.isAvailable()) {
if (Epoll.isAvailable()) {
boss = new EpollEventLoopGroup(2);
worker = new EpollEventLoopGroup(workerThreadCount);

View File

@ -16,10 +16,8 @@
package net.minestom.server.network.netty.codec;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.velocitypowered.natives.util.MoreByteBufUtils;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import io.netty.handler.codec.DecoderException;
@ -27,6 +25,8 @@ import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Utils;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
@ -34,7 +34,8 @@ public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
private final int threshold;
private final VelocityCompressor compressor = Natives.compress.get().create(4);
private final Deflater deflater = new Deflater();
private final Inflater inflater = new Inflater();
public PacketCompressor(int threshold) {
this.threshold = threshold;
@ -42,7 +43,7 @@ public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) {
PacketUtils.compressBuffer(compressor, from, to);
PacketUtils.compressBuffer(deflater, from, to);
}
@Override
@ -61,19 +62,17 @@ public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
throw new DecoderException("Badly compressed packet - size of " + claimedUncompressedSize + " is larger than protocol maximum of " + MAX_SIZE);
}
// TODO optimize to do not initialize arrays each time
ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, in);
ByteBuf uncompressed = MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, claimedUncompressedSize);
try {
compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
out.add(uncompressed);
in.clear();
} catch (Exception e) {
uncompressed.release();
throw e;
} finally {
compatibleIn.release();
}
byte[] input = new byte[in.readableBytes()];
in.readBytes(input);
inflater.setInput(input);
byte[] output = new byte[claimedUncompressedSize];
inflater.inflate(output);
inflater.reset();
out.add(Unpooled.wrappedBuffer(output));
}
}
}

View File

@ -10,12 +10,22 @@ import org.jetbrains.annotations.NotNull;
public class AcknowledgePlayerDiggingPacket implements ServerPacket {
public BlockPosition blockPosition = new BlockPosition(0,0,0);
public BlockPosition blockPosition;
public int blockStateId;
public ClientPlayerDiggingPacket.Status status = ClientPlayerDiggingPacket.Status.STARTED_DIGGING;
public ClientPlayerDiggingPacket.Status status;
public boolean successful;
public AcknowledgePlayerDiggingPacket() {}
public AcknowledgePlayerDiggingPacket(@NotNull BlockPosition blockPosition, int blockStateId,
@NotNull ClientPlayerDiggingPacket.Status status, boolean success) {
this.blockPosition = blockPosition;
this.blockStateId = blockStateId;
this.status = status;
this.successful = success;
}
public AcknowledgePlayerDiggingPacket() {
this(new BlockPosition(0, 0, 0), 0, ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
}
@Override
public void write(@NotNull BinaryWriter writer) {

View File

@ -9,13 +9,14 @@ import org.jetbrains.annotations.NotNull;
public class ActionBarPacket implements ServerPacket {
public Component actionBarText = Component.empty();
public Component actionBarText;
public ActionBarPacket() {
public ActionBarPacket(@NotNull Component actionBarText) {
this.actionBarText = actionBarText;
}
public ActionBarPacket(Component actionBarText) {
this.actionBarText = actionBarText;
public ActionBarPacket() {
this(Component.empty());
}
@Override

View File

@ -1,17 +1,30 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.entity.Entity;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AttachEntityPacket implements ServerPacket {
public int attachedEntityId;
public int holdingEntityId; // Or -1 to detach
public AttachEntityPacket() {}
public AttachEntityPacket(int attachedEntityId, int holdingEntityId) {
this.attachedEntityId = attachedEntityId;
this.holdingEntityId = holdingEntityId;
}
public AttachEntityPacket(@NotNull Entity attachedEntity, @Nullable Entity holdingEntity) {
this(attachedEntity.getEntityId(), holdingEntity != null ? holdingEntity.getEntityId() : -1);
}
public AttachEntityPacket() {
this(0, 0);
}
@Override
public void write(@NotNull BinaryWriter writer) {

View File

@ -12,8 +12,13 @@ public class BlockChangePacket implements ServerPacket {
public BlockPosition blockPosition;
public int blockStateId;
public BlockChangePacket(BlockPosition blockPosition, int blockStateId) {
this.blockPosition = blockPosition;
this.blockStateId = blockStateId;
}
public BlockChangePacket() {
blockPosition = new BlockPosition(0,0,0);
this(new BlockPosition(0, 0, 0), 0);
}
@Override

View File

@ -1,5 +1,6 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.entity.Entity;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryReader;
@ -10,7 +11,17 @@ public class CameraPacket implements ServerPacket {
public int cameraId;
public CameraPacket() {}
public CameraPacket(int cameraId) {
this.cameraId = cameraId;
}
public CameraPacket(@NotNull Entity camera) {
this(camera.getEntityId());
}
public CameraPacket() {
this(0);
}
@Override
public void write(@NotNull BinaryWriter writer) {

View File

@ -26,18 +26,16 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
public ChatPosition position;
public UUID uuid;
public ChatMessagePacket() {
this.message = Component.empty();
this.position = ChatPosition.SYSTEM_MESSAGE;
this.uuid = NULL_UUID;
}
public ChatMessagePacket(@NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) {
this.message = message;
this.position = position;
this.uuid = Objects.requireNonNullElse(uuid, NULL_UUID);
}
public ChatMessagePacket() {
this(Component.empty(), ChatPosition.SYSTEM_MESSAGE, NULL_UUID);
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeComponent(message);

View File

@ -13,7 +13,8 @@ public class ExplosionPacket implements ServerPacket {
public byte[] records = new byte[0];
public float playerMotionX, playerMotionY, playerMotionZ;
public ExplosionPacket() {}
public ExplosionPacket() {
}
@Override
public void write(@NotNull BinaryWriter writer) {
@ -21,7 +22,7 @@ public class ExplosionPacket implements ServerPacket {
writer.writeFloat(y);
writer.writeFloat(z);
writer.writeFloat(radius);
writer.writeInt(records.length/3); // each record is 3 bytes long
writer.writeVarInt(records.length / 3); // each record is 3 bytes long
for (byte record : records)
writer.writeByte(record);
writer.writeFloat(playerMotionX);

View File

@ -1,6 +1,5 @@
package net.minestom.server.scoreboard;
import com.google.common.collect.MapMaker;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.Pointers;
import net.kyori.adventure.text.Component;
@ -22,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@ -72,7 +72,7 @@ public class Team implements PacketGroupingAudience {
*/
private Component suffix;
private final Set<Player> playerMembers = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
private final Set<Player> playerMembers = ConcurrentHashMap.newKeySet();
private boolean isPlayerMembersUpToDate;
// Adventure

View File

@ -6,7 +6,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.nbt.SNBTParser;
import java.io.StringReader;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -21,29 +25,68 @@ import java.util.function.Supplier;
@ApiStatus.NonExtendable
public class Tag<T> {
/**
* Handles the snbt of the tag holder.
* <p>
* Writing will override all tags. Proceed with caution.
*/
@ApiStatus.Experimental
public static final Tag<String> SNBT = new Tag<>(null, NBTCompound::toSNBT, (original, snbt) -> {
try {
final var updated = new SNBTParser(new StringReader(snbt)).parse();
if (!(updated instanceof NBTCompound))
throw new IllegalArgumentException("'" + snbt + "' is not a compound!");
NBTCompound updatedCompound = (NBTCompound) updated;
original.clear();
updatedCompound.getKeys().forEach(s ->
original.set(s, Objects.requireNonNull(updatedCompound.get(s))));
} catch (NBTException e) {
e.printStackTrace();
}
}, null);
/**
* Handles the complete tag holder compound.
* <p>
* Writing will override all tags. Proceed with caution.
*/
@ApiStatus.Experimental
public static final Tag<NBTCompound> NBT = new Tag<>(null, NBTCompound::deepClone, (original, updated) -> {
original.clear();
updated.getKeys().forEach(s -> original.set(s, Objects.requireNonNull(updated.get(s))));
}, null);
private final String key;
private final Function<NBTCompound, T> readFunction;
private final BiConsumer<NBTCompound, T> writeConsumer;
private final Supplier<T> defaultValue;
protected Tag(@NotNull String key,
protected Tag(@Nullable String key,
@NotNull Function<NBTCompound, T> readFunction,
@NotNull BiConsumer<NBTCompound, T> writeConsumer,
@Nullable BiConsumer<NBTCompound, T> writeConsumer,
@Nullable Supplier<T> defaultValue) {
this.key = key;
this.readFunction = readFunction;
this.writeConsumer = writeConsumer;
this.writeConsumer = Objects.requireNonNullElse(writeConsumer, (compound, t) -> {
});
this.defaultValue = defaultValue;
}
protected Tag(@NotNull String key,
protected Tag(@Nullable String key,
@NotNull Function<NBTCompound, T> readFunction,
@NotNull BiConsumer<NBTCompound, T> writeConsumer) {
@Nullable BiConsumer<NBTCompound, T> writeConsumer) {
this(key, readFunction, writeConsumer, null);
}
public @NotNull String getKey() {
/**
* Returns the key used to navigate inside the holder nbt.
* <p>
* Can be null if unused (e.g. {@link #View(TagSerializer)}, {@link #SNBT} and {@link #NBT}).
*
* @return the tag key
*/
public @Nullable String getKey() {
return key;
}
@ -60,7 +103,7 @@ public class Tag<T> {
@Contract(value = "_, _ -> new", pure = true)
public <R> Tag<R> map(@NotNull Function<T, R> readMap,
@NotNull Function<R, T> writeMap) {
return new Tag<R>(key,
return new Tag<>(key,
// Read
nbtCompound -> {
final var old = readFunction.apply(nbtCompound);
@ -85,22 +128,26 @@ public class Tag<T> {
}
public @Nullable T read(@NotNull NBTCompound nbtCompound) {
if (nbtCompound.containsKey(key)) {
return readFunction.apply(nbtCompound);
} else {
T result = readFunction.apply(nbtCompound);
if (result == null) {
final var supplier = defaultValue;
return supplier != null ? supplier.get() : null;
result = supplier != null ? supplier.get() : null;
}
return result;
}
public void write(@NotNull NBTCompound nbtCompound, @Nullable T value) {
if (value != null) {
if (key == null || value != null) {
this.writeConsumer.accept(nbtCompound, value);
} else {
nbtCompound.removeTag(key);
}
}
public void writeUnsafe(@NotNull NBTCompound nbtCompound, @Nullable Object value) {
write(nbtCompound, (T) value);
}
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
return new Tag<>(key,
nbtCompound -> nbtCompound.getByte(key),
@ -149,17 +196,15 @@ public class Tag<T> {
(nbtCompound, value) -> nbtCompound.setString(key, value));
}
public static @NotNull Tag<NBT> NBT(@NotNull String key) {
public static <T extends NBT> @NotNull Tag<T> NBT(@NotNull String key) {
return new Tag<>(key,
nbt -> {
var currentNBT = nbt.get(key);
final var currentNBT = nbt.get(key);
// Avoid a NPE when cloning a null variable.
if (currentNBT == null) {
return null;
}
return currentNBT.deepClone();
return (T) currentNBT.deepClone();
},
((nbt, value) -> nbt.set(key, value.deepClone())));
}
@ -176,7 +221,15 @@ public class Tag<T> {
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
}
public static <T> @NotNull Tag<T> Custom(@NotNull String key, @NotNull TagSerializer<T> serializer) {
/**
* Create a wrapper around a compound.
*
* @param key the tag key
* @param serializer the tag serializer
* @param <T> the tag type
* @return the created tag
*/
public static <T> @NotNull Tag<T> Structure(@NotNull String key, @NotNull TagSerializer<T> serializer) {
return new Tag<>(key,
nbtCompound -> {
final var compound = nbtCompound.getCompound(key);
@ -194,4 +247,18 @@ public class Tag<T> {
serializer.write(TagWritable.fromCompound(compound), value);
});
}
public static <T> @NotNull Tag<T> View(@NotNull TagSerializer<T> serializer) {
return new Tag<>(null,
nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)),
(nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value));
}
/**
* @deprecated use {@link #Structure(String, TagSerializer)} instead
*/
@Deprecated
public static <T> @NotNull Tag<T> Custom(@NotNull String key, @NotNull TagSerializer<T> serializer) {
return Structure(key, serializer);
}
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.tag;
import com.google.common.annotations.Beta;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents an element which can read and write {@link Tag tags}.
*/
@Beta
@ApiStatus.Experimental
public interface TagHandler extends TagReadable, TagWritable {
}

View File

@ -24,7 +24,9 @@ public interface TagReadable {
* @param tag the tag to check
* @return true if the tag is present, false otherwise
*/
boolean hasTag(@NotNull Tag<?> tag);
default boolean hasTag(@NotNull Tag<?> tag) {
return getTag(tag) != null;
}
/**
* Converts an nbt compound to a tag reader.
@ -38,11 +40,6 @@ public interface TagReadable {
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(compound);
}
@Override
public boolean hasTag(@NotNull Tag<?> tag) {
return compound.containsKey(tag.getKey());
}
};
}
}

View File

@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface used to create custom types compatible with {@link Tag#Custom(String, TagSerializer)}.
* Interface used to create custom {@link Tag tags}.
*
* @param <T> the type to serialize
*/
@ -14,7 +14,7 @@ public interface TagSerializer<T> {
* Reads the custom tag from a {@link TagReadable}.
*
* @param reader the reader
* @return the deserialized value
* @return the deserialized value, null if invalid
*/
@Nullable T read(@NotNull TagReadable reader);
@ -22,7 +22,7 @@ public interface TagSerializer<T> {
* Writes the custom tag to a {@link TagWritable}.
*
* @param writer the writer
* @param value the value to serialize
* @param value the value to serialize, null to remove
*/
void write(@NotNull TagWritable writer, @NotNull T value);
void write(@NotNull TagWritable writer, @Nullable T value);
}

View File

@ -18,6 +18,10 @@ public interface TagWritable {
*/
<T> void setTag(@NotNull Tag<T> tag, @Nullable T value);
default void removeTag(@NotNull Tag<?> tag) {
setTag(tag, null);
}
/**
* Converts an nbt compound to a tag writer.
*

View File

@ -47,10 +47,6 @@ public final class MathUtils {
return Direction.HORIZONTAL[directionIndex];
}
public static float clampFloat(float t, float a, float b) {
return Math.max(a, Math.min(t, b));
}
public static boolean isBetween(byte number, byte min, byte max) {
return number >= min && number <= max;
}
@ -84,11 +80,15 @@ public final class MathUtils {
}
public static int clamp(int value, int min, int max) {
if (value < min) {
return min;
} else {
return Math.min(value, max);
}
return Math.min(Math.max(value, min), max);
}
public static float clamp(float value, float min, float max) {
return Math.min(Math.max(value, min), max);
}
public static double clamp(double value, double min, double max) {
return Math.min(Math.max(value, min), max);
}
public static double mod(final double a, final double b) {

View File

@ -1,12 +1,9 @@
package net.minestom.server.utils;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.adventure.MinestomAdventure;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Player;
@ -21,8 +18,9 @@ import net.minestom.server.utils.callback.validator.PlayerValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
/**
* Utils class for packets. Including writing a {@link ServerPacket} into a {@link ByteBuf}
@ -31,7 +29,7 @@ import java.util.zip.DataFormatException;
public final class PacketUtils {
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
private static final ThreadLocal<VelocityCompressor> COMPRESSOR = ThreadLocal.withInitial(() -> Natives.compress.get().create(4));
private static final ThreadLocal<Deflater> COMPRESSOR = ThreadLocal.withInitial(Deflater::new);
private PacketUtils() {
}
@ -189,27 +187,35 @@ public final class PacketUtils {
* <p>
* {@code packetBuffer} needs to be the packet content without any header (if you want to use it to write a Minecraft packet).
*
* @param compressor the deflater for zlib compression
* @param deflater the deflater for zlib compression
* @param packetBuffer the buffer containing all the packet fields
* @param compressionTarget the buffer which will receive the compressed version of {@code packetBuffer}
*/
public static void compressBuffer(@NotNull VelocityCompressor compressor, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) {
public static void compressBuffer(@NotNull Deflater deflater, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) {
final int packetLength = packetBuffer.readableBytes();
final boolean compression = packetLength > MinecraftServer.getCompressionThreshold();
Utils.writeVarInt(compressionTarget, compression ? packetLength : 0);
if (compression) {
compress(compressor, packetBuffer, compressionTarget);
compress(deflater, packetBuffer, compressionTarget);
} else {
compressionTarget.writeBytes(packetBuffer);
}
}
private static void compress(@NotNull VelocityCompressor compressor, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) {
try {
compressor.deflate(uncompressed, compressed);
} catch (DataFormatException e) {
e.printStackTrace();
private static void compress(@NotNull Deflater deflater, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) {
deflater.setInput(uncompressed.nioBuffer());
deflater.finish();
while (!deflater.finished()) {
ByteBuffer nioBuffer = compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes());
compressed.writerIndex(deflater.deflate(nioBuffer) + compressed.writerIndex());
if (compressed.writableBytes() == 0) {
compressed.ensureWritable(8192);
}
}
deflater.reset();
}
public static void writeFramedPacket(@NotNull ByteBuf buffer,

View File

@ -1,6 +1,5 @@
package net.minestom.server.utils;
import com.google.common.primitives.Doubles;
import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;
@ -167,7 +166,7 @@ public class Vector implements PublicCloneable<Vector> {
* @return angle in radians
*/
public float angle(@NotNull Vector other) {
double dot = Doubles.constrainToRange(dot(other) / (length() * other.length()), -1.0, 1.0);
double dot = MathUtils.clamp(dot(other) / (length() * other.length()), -1.0, 1.0);
return (float) Math.acos(dot);
}

View File

@ -1,8 +1,8 @@
package net.minestom.server.utils.cache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -24,7 +24,7 @@ public class TemporaryCache<T> {
* @param duration the time before considering an object unused
*/
public TemporaryCache(long duration, TimeUnit timeUnit, RemovalListener<UUID, T> removalListener) {
this.cache = CacheBuilder.newBuilder()
this.cache = Caffeine.newBuilder()
.expireAfterWrite(duration, timeUnit)
.removalListener(removalListener)
.build();

View File

@ -6,8 +6,10 @@ import java.util.concurrent.TimeUnit;
public class TemporaryPacketCache extends TemporaryCache<TimedBuffer> {
public TemporaryPacketCache(long duration, TimeUnit timeUnit) {
super(duration, timeUnit, notification -> {
final ByteBuf buffer = notification.getValue().getBuffer();
super(duration, timeUnit, (key, value, cause) -> {
if (value == null)
return;
final ByteBuf buffer = value.getBuffer();
synchronized (buffer) {
buffer.release();
}

View File

@ -28,4 +28,17 @@ public class RelativeBlockPosition extends RelativeLocation<BlockPosition> {
return new BlockPosition(x, y, z);
}
@Override
public BlockPosition fromView(@Nullable Position position) {
if (!relativeX && !relativeY && !relativeZ) {
return location.clone();
}
final Position entityPosition = position != null ? position : new Position();
final int x = location.getX() + (relativeX ? (int) entityPosition.getYaw() : 0);
final int z = location.getZ() + (relativeZ ? (int) entityPosition.getPitch() : 0);
return new BlockPosition(x, 0, z);
}
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.utils.location;
import net.minestom.server.entity.Entity;
import net.minestom.server.utils.Position;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -22,19 +23,6 @@ public abstract class RelativeLocation<T> {
this.relativeZ = relativeZ;
}
/**
* Gets the location based on the relative fields and {@code entity}.
*
* @param entity the entity to get the relative position from
* @return the location
*/
public T from(@Nullable Entity entity) {
final Position entityPosition = entity != null ? entity.getPosition() : new Position();
return from(entityPosition);
}
/**
* Gets the location based on the relative fields and {@code position}.
*
@ -43,6 +31,26 @@ public abstract class RelativeLocation<T> {
*/
public abstract T from(@Nullable Position position);
@ApiStatus.Experimental
public abstract T fromView(@Nullable Position position);
/**
* Gets the location based on the relative fields and {@code entity}.
*
* @param entity the entity to get the relative position from
* @return the location
*/
public T from(@Nullable Entity entity) {
final Position entityPosition = entity != null ? entity.getPosition() : new Position();
return from(entityPosition);
}
@ApiStatus.Experimental
public T fromView(@Nullable Entity entity) {
final Position entityPosition = entity != null ? entity.getPosition() : new Position();
return fromView(entityPosition);
}
/**
* Gets if the 'x' field is relative.
*

View File

@ -28,4 +28,17 @@ public class RelativeVec extends RelativeLocation<Vector> {
return new Vector(x, y, z);
}
@Override
public Vector fromView(@Nullable Position position) {
if (!relativeX && !relativeY && !relativeZ) {
return location.clone();
}
final Position entityPosition = position != null ? position : new Position();
final double x = location.getX() + (relativeX ? entityPosition.getYaw() : 0);
final double z = location.getZ() + (relativeZ ? entityPosition.getPitch() : 0);
return new Vector(x, 0, z);
}
}

View File

@ -1,7 +1,7 @@
package net.minestom.server.utils.mojang;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.minestom.server.MinecraftServer;
@ -17,12 +17,12 @@ import java.util.concurrent.TimeUnit;
*/
public final class MojangUtils {
private static final Cache<String, JsonObject> UUID_CACHE = CacheBuilder.newBuilder()
private static final Cache<String, JsonObject> UUID_CACHE = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.softValues()
.build();
private static final Cache<String, JsonObject> USERNAME_CACHE = CacheBuilder.newBuilder()
private static final Cache<String, JsonObject> USERNAME_CACHE = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.softValues()
.build();

View File

@ -55,16 +55,12 @@ public class PlayerInit {
.addListener(EntityAttackEvent.class, event -> {
final Entity source = event.getEntity();
final Entity entity = event.getTarget();
entity.takeKnockback(0.4f, Math.sin(source.getPosition().getYaw() * 0.017453292), -Math.cos(source.getPosition().getYaw() * 0.017453292));
if (entity instanceof Player) {
Player target = (Player) entity;
Vector velocity = source.getPosition().clone().getDirection().multiply(4);
velocity.setY(3.5f);
target.setVelocity(velocity);
target.damage(DamageType.fromEntity(source), 5);
} else {
Vector velocity = source.getPosition().clone().getDirection().multiply(3);
velocity.setY(3f);
entity.setVelocity(velocity);
}
if (source instanceof Player) {