Merge branch 'master' into item-api

# Conflicts:
#	src/main/java/net/minestom/server/item/ItemDisplay.java
#	src/main/java/net/minestom/server/utils/binary/BinaryReader.java
This commit is contained in:
TheMode 2021-04-09 23:19:38 +02:00
commit e19af0377c
83 changed files with 449 additions and 486 deletions

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '27 19 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -124,17 +124,17 @@ dependencies {
testCompileOnly "org.mockito:mockito-core:2.28.2"
// Netty
api 'io.netty:netty-handler:4.1.59.Final'
api 'io.netty:netty-codec:4.1.59.Final'
api 'io.netty:netty-transport-native-epoll:4.1.59.Final:linux-x86_64'
api 'io.netty:netty-transport-native-kqueue:4.1.59.Final:osx-x86_64'
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.4.Final:linux-x86_64'
// https://mvnrepository.com/artifact/org.apache.commons/commons-text
compile group: 'org.apache.commons', name: 'commons-text', version: '1.9'
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
api 'it.unimi.dsi:fastutil:8.5.2'
api 'it.unimi.dsi:fastutil:8.5.4'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
api 'com.google.code.gson:gson:2.8.6'
@ -144,7 +144,7 @@ dependencies {
api 'com.github.Articdive:Jnoise:1.0.0'
// https://mvnrepository.com/artifact/org.rocksdb/rocksdbjni
api 'org.rocksdb:rocksdbjni:6.15.2'
api 'org.rocksdb:rocksdbjni:6.16.4'
// Logging
api 'org.apache.logging.log4j:log4j-core:2.14.0'
@ -167,7 +167,7 @@ dependencies {
implementation "com.velocitypowered:velocity-native:1.1.0-SNAPSHOT"
// Path finding
api 'com.github.MadMartian:hydrazine-path-finding:1.5.4'
api 'com.github.MadMartian:hydrazine-path-finding:1.6.0'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${project.kotlinVersion}"
api "org.jetbrains.kotlin:kotlin-reflect:${project.kotlinVersion}"

View File

@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=fd591a34af7385730970399f473afabdb8b28d57fd97d6625c388d090039d6fd
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -139,8 +139,7 @@ public class EnumGenerator implements CodeGenerator {
// constructor
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder();
for (int i = 0; i < parameters.length; i++) {
ParameterSpec param = parameters[i];
for (ParameterSpec param : parameters) {
constructorBuilder.addParameter(param);
// property assignment

View File

@ -1,7 +1,6 @@
package net.minestom.codegen.enchantment;
import net.minestom.codegen.BasicEnumGenerator;
import net.minestom.codegen.stats.StatsEnumGenerator;
import net.minestom.server.registry.ResourceGatherer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,7 +1,6 @@
package net.minestom.codegen.fluids;
import net.minestom.codegen.BasicEnumGenerator;
import net.minestom.codegen.stats.StatsEnumGenerator;
import net.minestom.server.registry.ResourceGatherer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,7 +1,6 @@
package net.minestom.codegen.potions;
import net.minestom.codegen.BasicEnumGenerator;
import net.minestom.codegen.stats.StatsEnumGenerator;
import net.minestom.server.registry.ResourceGatherer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -12,7 +12,6 @@ import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.stream.Collectors;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
public final class OpenGLRendering {

View File

@ -7,8 +7,6 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class PaletteGenerator {

View File

@ -14,7 +14,6 @@ import org.lwjgl.system.MemoryStack;
import java.nio.ByteBuffer;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwTerminate;
import static org.lwjgl.opengl.GL11.*;
public abstract class GLFWCapableBuffer {

View File

@ -167,7 +167,7 @@ public class MapColorRenderer implements Runnable {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenQuadIndices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
};
}
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

View File

@ -1,11 +1,14 @@
package net.minestom.server;
import com.google.common.collect.Queues;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.player.NettyPlayerConnection;
import net.minestom.server.thread.PerInstanceThreadProvider;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.async.AsyncUtils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@ -80,6 +83,12 @@ public final class UpdateManager {
// Tick end callbacks
doTickCallback(tickEndCallbacks, tickTime / 1000000L);
// Flush all waiting packets
AsyncUtils.runAsync(() -> connectionManager.getOnlinePlayers().stream()
.filter(player -> player.getPlayerConnection() instanceof NettyPlayerConnection)
.map(player -> (NettyPlayerConnection) player.getPlayerConnection())
.forEach(NettyPlayerConnection::flush));
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
@ -175,13 +184,12 @@ public final class UpdateManager {
* WARNING: should be automatically done by the {@link Instance} implementation.
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param chunk the loaded chunk
*/
public synchronized void signalChunkLoad(Instance instance, int chunkX, int chunkZ) {
public synchronized void signalChunkLoad(Instance instance, @NotNull Chunk chunk) {
if (this.threadProvider == null)
return;
this.threadProvider.onChunkLoad(instance, chunkX, chunkZ);
this.threadProvider.onChunkLoad(instance, chunk);
}
/**
@ -190,13 +198,12 @@ public final class UpdateManager {
* WARNING: should be automatically done by the {@link Instance} implementation.
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param chunk the unloaded chunk
*/
public synchronized void signalChunkUnload(Instance instance, int chunkX, int chunkZ) {
public synchronized void signalChunkUnload(Instance instance, @NotNull Chunk chunk) {
if (this.threadProvider == null)
return;
this.threadProvider.onChunkUnload(instance, chunkX, chunkZ);
this.threadProvider.onChunkUnload(instance, chunk);
}
/**

View File

@ -7,7 +7,6 @@ import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Set;
/**

View File

@ -2,13 +2,10 @@ package net.minestom.server.adventure;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.entity.Entity;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.EntitySoundEffectPacket;

View File

@ -4,9 +4,7 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectMap;
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.format.*;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.minestom.server.color.Color;
import net.minestom.server.color.DyeColor;
import net.minestom.server.utils.validate.Check;

View File

@ -1,6 +1,5 @@
package net.minestom.server.color;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.chat.ChatColor;
import org.apache.commons.lang3.Validate;

View File

@ -2,7 +2,6 @@ package net.minestom.server.command;
import net.minestom.server.command.builder.CommandContext;
import net.kyori.adventure.audience.Audience;
import net.minestom.server.command.builder.Arguments;
import net.minestom.server.permission.Permission;
import org.jetbrains.annotations.NotNull;

View File

@ -220,6 +220,18 @@ public abstract class Argument<T> {
return suggestionCallback != null;
}
/**
* Maps this argument's output to another result.
*
* @param mapper The mapper to use (this argument's input = desired output)
* @param <O> The type of output expected.
* @return A new ArgumentMap that can get this complex object type.
*/
@Beta
public <O> @NotNull ArgumentMap<T, O> map(@NotNull ArgumentMap.Mapper<T, O> mapper) {
return new ArgumentMap<>(this, mapper);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -0,0 +1,57 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import org.jetbrains.annotations.NotNull;
/**
* Represents an argument that maps an existing argument to a value.
*
* @param <I> The input (any object)
* @param <O> The output (any object)
*/
public class ArgumentMap<I, O> extends Argument<O> {
final Argument<I> argument;
final Mapper<I, O> mapper;
protected ArgumentMap(@NotNull Argument<I> argument, @NotNull Mapper<I, O> mapper) {
super(argument.getId(), argument.allowSpace(), argument.useRemaining());
this.argument = argument;
this.mapper = mapper;
}
@Override
public @NotNull O parse(@NotNull String input) throws ArgumentSyntaxException {
return mapper.accept(argument.parse(input));
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
argument.processNodes(nodeMaker, executable);
}
/**
* Represents a lambda that can turn an input into an output
* that also allows the throwing of ArgumentSyntaxException
*
* @param <I> The input expected from the Argument
* @param <O> The desired output type from this lambda.
*/
@FunctionalInterface
public interface Mapper<I, O> {
/**
* Accepts I data from the argument and returns O output
*
* @param i The input processed from an argument
* @return The complex data type that came as a result from this argument
* @throws ArgumentSyntaxException If the input can not be turned into the desired output
* (E.X. an invalid extension name)
*/
O accept(I i) throws ArgumentSyntaxException;
}
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.command.builder.arguments.minecraft;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;

View File

@ -22,10 +22,10 @@ public interface CommandCondition {
* but will instead be the raw command string given by the sender.
* You should in this case warn the sender (eg by sending a message) if the condition is unsuccessful.
*
* @param source the sender of the command
* @param sender the sender of the command
* @param commandString the raw command string,
* null if this is an access request
* @return true if the sender has the right to use the command, false otherwise
*/
boolean canUse(@NotNull CommandSender source, @Nullable String commandString);
boolean canUse(@NotNull CommandSender sender, @Nullable String commandString);
}

View File

@ -1538,10 +1538,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
}
private UpdateOption getSynchronizationCooldown() {
if (this.customSynchronizationCooldown != null) {
return this.customSynchronizationCooldown;
}
return SYNCHRONIZATION_COOLDOWN;
return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN);
}
public enum Pose {

View File

@ -163,9 +163,8 @@ public class Metadata {
}
public static Value<NBT> NBT(@NotNull NBT nbt) {
return new Value<>(TYPE_NBT, nbt, writer -> {
writer.writeNBT("", nbt);
}, reader -> {
return new Value<>(TYPE_NBT, nbt, writer ->
writer.writeNBT("", nbt), reader -> {
try {
return reader.readTag();
} catch (IOException | NBTException e) {

View File

@ -59,8 +59,8 @@ import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.stat.PlayerStatistic;
import net.minestom.server.utils.*;
import net.minestom.server.utils.callback.OptionalCallback;
@ -2691,8 +2691,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private byte displayedSkinParts;
private MainHand mainHand;
private boolean firstRefresh = true;
/**
* The player game language.
*
@ -2768,8 +2766,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
metadata.setIndex((byte) 16, Metadata.Byte(displayedSkinParts));
this.firstRefresh = false;
// Client changed his view distance in the settings
if (viewDistanceChanged) {
refreshVisibleChunks();

View File

@ -1,11 +1,7 @@
package net.minestom.server.entity.damage;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.chat.RichMessage;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.Entity;

View File

@ -2,7 +2,6 @@ package net.minestom.server.entity.hologram;
import net.kyori.adventure.text.Component;
import net.minestom.server.Viewable;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.type.decoration.EntityArmorStand;

View File

@ -23,7 +23,7 @@ public class MooshroomMeta extends CowMeta {
public enum Variant {
RED,
BROWN;
BROWN
}
}

View File

@ -1,7 +1,6 @@
package net.minestom.server.entity.metadata.minecart;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;

View File

@ -1,6 +1,7 @@
package net.minestom.server.entity.pathfinding;
import com.extollit.gaming.ai.path.HydrazinePathFinder;
import com.extollit.gaming.ai.path.PathOptions;
import com.extollit.gaming.ai.path.model.IPath;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.entity.Entity;
@ -140,11 +141,14 @@ public class Navigator {
final Position targetPosition = position.clone();
final PathOptions pathOptions = new PathOptions()
.targetingStrategy(bestEffort ? PathOptions.TargetingStrategy.gravitySnap :
PathOptions.TargetingStrategy.none);
final IPath path = pathFinder.initiatePathTo(
targetPosition.getX(),
targetPosition.getY(),
targetPosition.getZ(),
bestEffort);
pathOptions);
this.path = path;
final boolean success = path != null;

View File

@ -45,6 +45,11 @@ public class PFPathingEntity implements IPathingEntity {
return (int) entity.getAliveTicks();
}
@Override
public boolean bound() {
return entity.hasVelocity();
}
@Override
public float searchRange() {
return searchRange;

View File

@ -2,7 +2,6 @@ package net.minestom.server.entity.type.animal;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Metadata;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.Position;
import org.jetbrains.annotations.NotNull;

View File

@ -13,7 +13,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Stream;
@ -52,7 +51,7 @@ public interface EventHandler extends IExtensionObserver {
if(extensionSource != null) {
MinecraftServer.getExtensionManager().getExtension(extensionSource).observe(this);
getExtensionCallbacks(extensionSource).add(eventCallback);
};
}
Collection<EventCallback> callbacks = getEventCallbacks(eventClass);
return callbacks.add(eventCallback);
@ -71,7 +70,7 @@ public interface EventHandler extends IExtensionObserver {
String extensionSource = MinestomRootClassLoader.findExtensionObjectOwner(eventCallback);
if(extensionSource != null) {
getExtensionCallbacks(extensionSource).remove(eventCallback);
};
}
return callbacks.remove(eventCallback);
}

View File

@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Called every time a {@link Player} write and send something in the chat.
@ -19,14 +20,18 @@ import java.util.function.Function;
public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
private final Collection<Player> recipients;
private final Supplier<Component> defaultChatFormat;
private String message;
private Function<PlayerChatEvent, Component> chatFormat;
private boolean cancelled;
public PlayerChatEvent(@NotNull Player player, @NotNull Collection<Player> recipients, @NotNull String message) {
public PlayerChatEvent(@NotNull Player player, @NotNull Collection<Player> recipients,
@NotNull Supplier<Component> defaultChatFormat,
@NotNull String message) {
super(player);
this.recipients = new ArrayList<>(recipients);
this.defaultChatFormat = defaultChatFormat;
this.message = message;
}
@ -57,8 +62,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
*
* @return a modifiable list of message targets
*/
@NotNull
public Collection<Player> getRecipients() {
public @NotNull Collection<Player> getRecipients() {
return recipients;
}
@ -67,8 +71,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
*
* @return the sender's message
*/
@NotNull
public String getMessage() {
public @NotNull String getMessage() {
return message;
}
@ -88,11 +91,14 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
*
* @return the chat format which will be used, null if this is the default one
*/
@Nullable
public Function<PlayerChatEvent, Component> getChatFormatFunction() {
public @Nullable Function<@NotNull PlayerChatEvent, @NotNull Component> getChatFormatFunction() {
return chatFormat;
}
public @NotNull Supplier<@NotNull Component> getDefaultChatFormat() {
return defaultChatFormat;
}
@Override
public boolean isCancelled() {
return cancelled;

View File

@ -1,6 +1,5 @@
package net.minestom.server.event.player;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;

View File

@ -53,7 +53,7 @@ import java.util.function.Consumer;
* WARNING: when making your own implementation registering the instance manually is required
* with {@link InstanceManager#registerInstance(Instance)}, and
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Instance, int, int)} and {@link UpdateManager#signalChunkUnload(Instance, int, int)}.
* {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and {@link UpdateManager#signalChunkUnload(Instance, Chunk)}.
*/
public abstract class Instance implements BlockModifier, EventHandler, DataContainer, PacketGroupingAudience {
@ -252,7 +252,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* Used when a {@link Chunk} is not currently loaded in memory and need to be retrieved from somewhere else.
* Could be read from disk, or generated from scratch.
* <p>
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, int, int)} and to cache
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and to cache
* that this chunk has been loaded.
* <p>
* WARNING: it has to retrieve a chunk, this is not optional and should execute the callback in all case.
@ -266,7 +266,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
/**
* Called to generated a new {@link Chunk} from scratch.
* <p>
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, int, int)} and to cache
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and to cache
* that this chunk has been loaded.
* <p>
* This is where you can put your chunk generation code.
@ -485,8 +485,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* @return an unmodifiable {@link Set} containing all the entities in a chunk,
* if {@code chunk} is unloaded, return an empty {@link HashSet}
*/
@NotNull
public Set<Entity> getChunkEntities(Chunk chunk) {
public @NotNull Set<Entity> getChunkEntities(Chunk chunk) {
if (!ChunkUtils.isLoaded(chunk))
return new HashSet<>();

View File

@ -1,8 +1,5 @@
package net.minestom.server.instance;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
import net.minestom.server.data.SerializableData;
@ -34,6 +31,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
@ -56,7 +54,7 @@ public class InstanceContainer extends Instance {
// the chunk generator used, can be null
private ChunkGenerator chunkGenerator;
// (chunk index -> chunk) map, contains all the chunks in the instance
private final Long2ObjectMap<Chunk> chunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
private final Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
// contains all the chunks to remove during the next instance tick, should be synchronized
protected final Set<Chunk> scheduledChunksToRemove = new HashSet<>();
@ -508,7 +506,7 @@ public class InstanceContainer extends Instance {
protected void retrieveChunk(int chunkX, int chunkZ, @Nullable ChunkCallback callback) {
final boolean loaded = chunkLoader.loadChunk(this, chunkX, chunkZ, chunk -> {
cacheChunk(chunk);
UPDATE_MANAGER.signalChunkLoad(this, chunkX, chunkZ);
UPDATE_MANAGER.signalChunkLoad(this, chunk);
// Execute callback and event in the instance thread
scheduleNextTick(instance -> {
callChunkLoadEvent(chunkX, chunkZ);
@ -546,7 +544,7 @@ public class InstanceContainer extends Instance {
OptionalCallback.execute(callback, chunk);
}
UPDATE_MANAGER.signalChunkLoad(this, chunkX, chunkZ);
UPDATE_MANAGER.signalChunkLoad(this, chunk);
callChunkLoadEvent(chunkX, chunkZ);
}
@ -643,7 +641,7 @@ public class InstanceContainer extends Instance {
final Chunk copiedChunk = chunk.copy(chunkX, chunkZ);
copiedInstance.cacheChunk(copiedChunk);
UPDATE_MANAGER.signalChunkLoad(copiedInstance, chunkX, chunkZ);
UPDATE_MANAGER.signalChunkLoad(copiedInstance, copiedChunk);
}
return copiedInstance;
@ -684,7 +682,7 @@ public class InstanceContainer extends Instance {
* Adds a {@link Chunk} to the internal instance map.
* <p>
* WARNING: the chunk will not automatically be sent to players and
* {@link net.minestom.server.UpdateManager#signalChunkLoad(Instance, int, int)} must be called manually.
* {@link net.minestom.server.UpdateManager#signalChunkLoad(Instance, Chunk)} must be called manually.
*
* @param chunk the chunk to cache
*/
@ -825,7 +823,7 @@ public class InstanceContainer extends Instance {
chunk.unload();
UPDATE_MANAGER.signalChunkUnload(this, chunkX, chunkZ);
UPDATE_MANAGER.signalChunkUnload(this, chunk);
}
this.scheduledChunksToRemove.clear();
}

View File

@ -42,7 +42,7 @@ public class StairsPlacementRule extends BlockPlacementRule {
OUTER_LEFT,
OUTER_RIGHT,
INNER_LEFT,
INNER_RIGHT;
INNER_RIGHT
}
private enum Facing {

View File

@ -10,8 +10,6 @@ import net.minestom.server.utils.clone.PublicCloneable;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_COUNT;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_SIZE;

View File

@ -291,7 +291,7 @@ public class Inventory extends AbstractInventory implements Viewable {
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
final InventoryClickResult clickResult = clickProcessor.leftClick(this, player, slot, clicked, cursor);
final InventoryClickResult clickResult = clickProcessor.leftClick(isInWindow ? this : null, player, slot, clicked, cursor);
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
@ -305,7 +305,7 @@ public class Inventory extends AbstractInventory implements Viewable {
setCursorPlayerItem(player, clickResult.getCursor());
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.LEFT_CLICK, clicked, cursor);
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@ -318,7 +318,7 @@ public class Inventory extends AbstractInventory implements Viewable {
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
final InventoryClickResult clickResult = clickProcessor.rightClick(this, player, slot, clicked, cursor);
final InventoryClickResult clickResult = clickProcessor.rightClick(isInWindow ? this : null, player, slot, clicked, cursor);
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
@ -332,7 +332,7 @@ public class Inventory extends AbstractInventory implements Viewable {
setCursorPlayerItem(player, clickResult.getCursor());
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.RIGHT_CLICK, clicked, cursor);
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@ -364,7 +364,7 @@ public class Inventory extends AbstractInventory implements Viewable {
}
}));
} else {
clickResult = clickProcessor.shiftClick(this, player, slot, clicked, cursor,
clickResult = clickProcessor.shiftClick(null, player, slot, clicked, cursor,
// Window loop
new InventoryClickLoopHandler(0, getSize(), 1,
i -> i,
@ -402,7 +402,7 @@ public class Inventory extends AbstractInventory implements Viewable {
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
final ItemStack heldItem = playerInventory.getItemStack(key);
final InventoryClickResult clickResult = clickProcessor.changeHeld(this, player, slot, key, clicked, heldItem);
final InventoryClickResult clickResult = clickProcessor.changeHeld(isInWindow ? this : null, player, slot, key, clicked, heldItem);
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
@ -416,7 +416,7 @@ public class Inventory extends AbstractInventory implements Viewable {
playerInventory.setItemStack(key, clickResult.getCursor());
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.CHANGE_HELD, clicked, getCursorItem(player));
callClickEvent(player, isInWindow ? this : null, slot, ClickType.CHANGE_HELD, clicked, getCursorItem(player));
// Weird synchronization issue when omitted
updateFromClick(clickResult, player);
@ -440,7 +440,7 @@ public class Inventory extends AbstractInventory implements Viewable {
ItemStack.AIR : (isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot));
final ItemStack cursor = getCursorItem(player);
final InventoryClickResult clickResult = clickProcessor.drop(this, player,
final InventoryClickResult clickResult = clickProcessor.drop(isInWindow ? this : null, player,
mode, slot, button, clicked, cursor);
if (clickResult.doRefresh()) {
@ -471,7 +471,7 @@ public class Inventory extends AbstractInventory implements Viewable {
ItemStack.AIR;
final ItemStack cursor = getCursorItem(player);
final InventoryClickResult clickResult = clickProcessor.dragging(this, player,
final InventoryClickResult clickResult = clickProcessor.dragging(isInWindow ? this : null, player,
slot, button,
clicked, cursor,
@ -503,8 +503,9 @@ public class Inventory extends AbstractInventory implements Viewable {
public boolean doubleClick(@NotNull Player player, int slot) {
final PlayerInventory playerInventory = player.getInventory();
final ItemStack cursor = getCursorItem(player);
final boolean isInWindow = isClickInWindow(slot);
final InventoryClickResult clickResult = clickProcessor.doubleClick(this, player, slot, cursor,
final InventoryClickResult clickResult = clickProcessor.doubleClick(isInWindow ? this : null, player, slot, cursor,
// Start by looping through the opened inventory
new InventoryClickLoopHandler(0, getSize(), 1,
i -> i,

View File

@ -1,46 +1,33 @@
package net.minestom.server.item.firework;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.exception.ExceptionManager;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.*;
import java.util.Objects;
import java.util.*;
public class FireworkEffect {
private final boolean flicker;
private final boolean trail;
private final FireworkEffectType type;
private final Color color;
private final Color fadeColor;
private final List<Color> color;
private final List<Color> fadeColor;
//FIXME: fix javadoc
/**
* Initializes a new firework effect.
*
* @param flicker {@code true} if this explosion has the Twinkle effect (glowstone dust), otherwise {@code false}.
* @param trail {@code true} if this explosion hsa the Trail effect (diamond), otherwise {@code false}.
* @param type The shape of this firework's explosion.
* @param color The primary color of this firework effect.
* @param fadeColor The secondary color of this firework effect.
* @deprecated Use {@link #FireworkEffect(boolean, boolean, FireworkEffectType, Color, Color)}
* @param color The primary colors of this firework effect.
* @param fadeColor The secondary colors of this firework effect.
*/
@Deprecated
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) {
this(flicker, trail, type, color.asColor(), fadeColor.asColor());
}
/**
* Initializes a new firework effect.
*
* @param flicker {@code true} if this explosion has the Twinkle effect (glowstone dust), otherwise {@code false}.
* @param trail {@code true} if this explosion hsa the Trail effect (diamond), otherwise {@code false}.
* @param type The shape of this firework's explosion.
* @param color The primary color of this firework effect.
* @param fadeColor The secondary color of this firework effect.
*/
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, Color color, Color fadeColor) {
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, List<Color> color, List<Color> fadeColor) {
this.flicker = flicker;
this.trail = trail;
this.type = type;
@ -56,18 +43,21 @@ public class FireworkEffect {
*/
public static FireworkEffect fromCompound(@NotNull NBTCompound compound) {
Color primaryColor = null;
Color secondaryColor = null;
List<Color> primaryColor = new ArrayList<>();
List<Color> secondaryColor = new ArrayList<>();
if (compound.containsKey("Colors")) {
int[] color = compound.getIntArray("Colors");
primaryColor = new Color(color[0], color[1], color[2]);
for (int i = 0; i < color.length; i++) {
primaryColor.add(new Color(color[i]));
}
}
if (compound.containsKey("FadeColors")) {
int[] fadeColor = compound.getIntArray("FadeColors");
secondaryColor = new Color(fadeColor[0], fadeColor[1], fadeColor[2]);
for (int i = 0; i < fadeColor.length; i++) {
secondaryColor.add(new Color(fadeColor[i]));
}
}
boolean flicker = compound.containsKey("Flicker") && compound.getByte("Flicker") == 1;
@ -119,11 +109,11 @@ public class FireworkEffect {
* @return An array of integer values corresponding to the primary colors of this firework's explosion.
*/
public int[] getColors() {
return new int[]{
this.color.getRed(),
this.color.getGreen(),
this.color.getBlue()
};
int[] primary = new int[color.size()];
for (int i = 0; i < color.size(); i++) {
primary[i] = color.get(i).asRGB();
}
return primary;
}
/**
@ -134,11 +124,11 @@ public class FireworkEffect {
* @return An array of integer values corresponding to the fading colors of this firework's explosion.
*/
public int[] getFadeColors() {
return new int[]{
this.fadeColor.getRed(),
this.fadeColor.getGreen(),
this.fadeColor.getBlue()
};
int[] secondary = new int[fadeColor.size()];
for (int i = 0; i < fadeColor.size(); i++) {
secondary[i] = fadeColor.get(i).asRGB();
}
return secondary;
}
/**

View File

@ -1,6 +1,5 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.potion.CustomPotionEffect;

View File

@ -10,6 +10,7 @@ import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.client.play.ClientChatMessagePacket;
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.Function;
@ -34,7 +35,8 @@ public class ChatMessageListener {
}
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, message);
String finalMessage = message;
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, finalMessage), message);
// Call the event
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
@ -48,7 +50,7 @@ public class ChatMessageListener {
textObject = formatFunction.apply(playerChatEvent);
} else {
// Default format
textObject = buildDefaultChatMessage(playerChatEvent);
textObject = playerChatEvent.getDefaultChatFormat().get();
}
final Collection<Player> recipients = playerChatEvent.getRecipients();
@ -64,15 +66,14 @@ public class ChatMessageListener {
}
private static Component buildDefaultChatMessage(PlayerChatEvent chatEvent) {
final String username = chatEvent.getPlayer().getUsername();
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {
final String username = player.getUsername();
return Component.translatable("chat.type.text")
.args(Component.text(username)
.insertion(username)
.clickEvent(ClickEvent.suggestCommand("/msg " + username + " "))
.hoverEvent(chatEvent.getPlayer()),
Component.text(chatEvent.getMessage())
.hoverEvent(player),
Component.text(message)
);
}

View File

@ -2,12 +2,10 @@ package net.minestom.server.network;
import io.netty.channel.Channel;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.fakeplayer.FakePlayer;

View File

@ -6,8 +6,6 @@ import net.minestom.server.utils.binary.Readable;
import net.minestom.server.utils.binary.Writeable;
import org.jetbrains.annotations.NotNull;
import java.nio.CharBuffer;
/**
* Represents a packet which can be sent to a player using {@link PlayerConnection#sendPacket(ServerPacket)}.
*/

View File

@ -37,7 +37,7 @@ public class BlockEntityDataPacket implements ServerPacket {
@Override
public void read(@NotNull BinaryReader reader) {
blockPosition = reader.readBlockPosition();;
blockPosition = reader.readBlockPosition();
action = reader.readByte();
try {
NBT tag = reader.readTag();

View File

@ -19,6 +19,7 @@ import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.cache.CacheablePacket;
import net.minestom.server.utils.cache.TemporaryCache;
import net.minestom.server.utils.cache.TemporaryPacketCache;
import net.minestom.server.utils.cache.TimedBuffer;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.world.biomes.Biome;
@ -34,8 +35,7 @@ import java.util.concurrent.TimeUnit;
public class ChunkDataPacket implements ServerPacket, CacheablePacket {
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
private static final TemporaryCache<TimedBuffer> CACHE = new TemporaryCache<>(5, TimeUnit.MINUTES,
notification -> notification.getValue().getBuffer().release());
private static final TemporaryCache<TimedBuffer> CACHE = new TemporaryPacketCache(5, TimeUnit.MINUTES);
public boolean fullChunk;
public Biome[] biomes;

View File

@ -8,8 +8,6 @@ import net.minestom.server.utils.binary.Readable;
import net.minestom.server.utils.binary.Writeable;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
public class DeclareCommandsPacket implements ServerPacket {
public Node[] nodes = new Node[0];

View File

@ -43,7 +43,7 @@ public class EntityMetaDataPacket implements ServerPacket {
break;
}
entries.add(new Metadata.Entry<Object>(reader));
entries.add(new Metadata.Entry<>(reader));
}
}

View File

@ -4,7 +4,6 @@ import net.kyori.adventure.sound.Sound;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;

View File

@ -17,7 +17,8 @@ public class MultiBlockChangePacket implements ServerPacket {
public boolean suppressLightUpdates = true;
public BlockChange[] blockChanges = new BlockChange[0];
public MultiBlockChangePacket() {}
public MultiBlockChangePacket() {
}
@Override
public void write(@NotNull BinaryWriter writer) {
@ -27,7 +28,7 @@ public class MultiBlockChangePacket implements ServerPacket {
final int length = blockChanges.length;
writer.writeVarInt(length);
for (final BlockChange blockChange : blockChanges) {
writer.writeVarLong(blockChange.newBlockId << 12 | getLocalBlockPosAsShort(blockChange.positionX, blockChange.positionY, blockChange.positionZ));
writer.writeVarLong((long) blockChange.newBlockId << 12 | getLocalBlockPosAsShort(blockChange.positionX, blockChange.positionY, blockChange.positionZ));
}
} else {
writer.writeVarInt(0);

View File

@ -4,7 +4,6 @@ import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;

View File

@ -3,7 +3,6 @@ package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;

View File

@ -6,8 +6,6 @@ import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
public class ParticlePacket implements ServerPacket {
public int particleId;

View File

@ -7,8 +7,6 @@ import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class PluginMessagePacket implements ServerPacket {
public String channel = "none";

View File

@ -2,7 +2,6 @@ package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;

View File

@ -10,7 +10,6 @@ import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;

View File

@ -6,20 +6,19 @@ import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.cache.CacheablePacket;
import net.minestom.server.utils.cache.TemporaryCache;
import net.minestom.server.utils.cache.TemporaryPacketCache;
import net.minestom.server.utils.cache.TimedBuffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class UpdateLightPacket implements ServerPacket, CacheablePacket {
private static final TemporaryCache<TimedBuffer> CACHE = new TemporaryCache<>(5, TimeUnit.MINUTES,
notification -> notification.getValue().getBuffer().release());
private static final TemporaryCache<TimedBuffer> CACHE = new TemporaryPacketCache(5, TimeUnit.MINUTES);
public int chunkX;
public int chunkZ;
@ -103,7 +102,7 @@ public class UpdateLightPacket implements ServerPacket, CacheablePacket {
skyLight.clear();
for (int i = 0; i < 14; i++) {
int length = reader.readVarInt();
if(length != 2048) {
if (length != 2048) {
throw new IllegalStateException("Length must be 2048.");
}
@ -115,7 +114,7 @@ public class UpdateLightPacket implements ServerPacket, CacheablePacket {
blockLight.clear();
for (int i = 0; i < 6; i++) {
int length = reader.readVarInt();
if(length != 2048) {
if (length != 2048) {
throw new IllegalStateException("Length must be 2048.");
}

View File

@ -71,22 +71,6 @@ public class NettyPlayerConnection extends PlayerConnection {
this.tickBuffer.ensureWritable(INITIAL_BUFFER_SIZE);
}
@Override
public void update() {
// Flush
final int bufferSize = tickBuffer.writerIndex();
if (bufferSize > 0) {
this.channel.eventLoop().submit(() -> {
if (channel.isActive()) {
writeWaitingPackets();
channel.flush();
}
});
}
// Network stats
super.update();
}
/**
* Sets the encryption key and add the codecs to the pipeline.
*
@ -134,12 +118,8 @@ public class NettyPlayerConnection extends PlayerConnection {
if (getPlayer() != null) {
// Flush happen during #update()
if (serverPacket instanceof CacheablePacket && MinecraftServer.hasPacketCaching()) {
// Check if the packet is cached or can be
final FramedPacket cachedPacket = CacheablePacket.getCache(serverPacket);
if (cachedPacket != null) {
write(cachedPacket);
} else {
write(serverPacket, skipTranslating);
synchronized (tickBuffer) {
CacheablePacket.writeCache(tickBuffer, serverPacket);
}
} else {
write(serverPacket, skipTranslating);
@ -196,7 +176,7 @@ public class NettyPlayerConnection extends PlayerConnection {
}
}
private void writeWaitingPackets() {
public void writeWaitingPackets() {
if (tickBuffer.writerIndex() == 0) {
// Nothing to write
return;
@ -221,6 +201,16 @@ public class NettyPlayerConnection extends PlayerConnection {
}
}
public void flush() {
final int bufferSize = tickBuffer.writerIndex();
if (bufferSize > 0) {
if (channel.isActive()) {
writeWaitingPackets();
channel.flush();
}
}
}
@NotNull
@Override
public SocketAddress getRemoteAddress() {

View File

@ -1,7 +1,6 @@
package net.minestom.server.resourcepack;
import net.minestom.server.entity.Player;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -1,7 +1,5 @@
package net.minestom.server.scoreboard;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.minestom.server.Viewable;
import net.minestom.server.adventure.audience.PacketGroupingAudience;

View File

@ -1,8 +1,6 @@
package net.minestom.server.scoreboard;
import com.google.common.collect.MapMaker;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;

View File

@ -1,6 +1,5 @@
package net.minestom.server.sound;
import net.kyori.adventure.sound.Sound;
import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.sound.Sound.*;

View File

@ -1,173 +0,0 @@
package net.minestom.server.thread;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
/**
* Separate chunks into group of linked chunks
* <p>
* (1 chunks group = 1 thread execution)
*/
public class PerGroupChunkProvider extends ThreadProvider {
/**
* Chunk -> its chunk group
*/
private final Map<Instance, Long2ObjectMap<LongSet>> instanceChunksGroupMap = new ConcurrentHashMap<>();
/**
* Used to know to which instance is linked a Set of chunks
*/
private final Map<Instance, Map<LongSet, Instance>> instanceInstanceMap = new ConcurrentHashMap<>();
@Override
public void onInstanceCreate(@NotNull Instance instance) {
this.instanceChunksGroupMap.putIfAbsent(instance, new Long2ObjectOpenHashMap<>());
this.instanceInstanceMap.putIfAbsent(instance, new HashMap<>());
}
@Override
public void onInstanceDelete(@NotNull Instance instance) {
this.instanceChunksGroupMap.remove(instance);
this.instanceInstanceMap.remove(instance);
}
@Override
public void onChunkLoad(@NotNull Instance instance, int chunkX, int chunkZ) {
final long loadedChunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ);
Long2ObjectMap<LongSet> chunksGroupMap = getChunksGroupMap(instance);
Map<LongSet, Instance> instanceMap = getInstanceMap(instance);
// List of groups which are neighbours
List<LongSet> neighboursGroups = new ArrayList<>();
final long[] chunks = ChunkUtils.getNeighbours(instance, chunkX, chunkZ);
boolean findGroup = false;
for (long chunkIndex : chunks) {
if (chunksGroupMap.containsKey(chunkIndex)) {
final LongSet group = chunksGroupMap.get(chunkIndex);
neighboursGroups.add(group);
chunksGroupMap.remove(chunkIndex);
instanceMap.remove(group);
findGroup = true;
}
}
if (!findGroup) {
// Create group of one chunk
LongSet chunkIndexes = new LongArraySet();
chunkIndexes.add(loadedChunkIndex);
chunksGroupMap.put(loadedChunkIndex, chunkIndexes);
instanceMap.put(chunkIndexes, instance);
return;
}
// The size of the final list, used as the initial capacity
final int size = neighboursGroups.stream().mapToInt(Set::size).sum() + 1;
// Represent the merged group of all the neighbours
LongSet finalGroup = new LongArraySet(size);
// Add the newly loaded chunk to the group
finalGroup.add(loadedChunkIndex);
// Add all the neighbours groups to the final one
for (LongSet chunkCoordinates : neighboursGroups) {
finalGroup.addAll(chunkCoordinates);
}
// Complete maps
for (long index : finalGroup) {
chunksGroupMap.put(index, finalGroup);
}
instanceMap.put(finalGroup, instance);
}
@Override
public void onChunkUnload(@NotNull Instance instance, int chunkX, int chunkZ) {
Long2ObjectMap<LongSet> chunksGroupMap = getChunksGroupMap(instance);
Map<LongSet, Instance> instanceMap = getInstanceMap(instance);
final long chunkIndex = ChunkUtils.getChunkIndex(chunkX, chunkZ);
if (chunksGroupMap.containsKey(chunkIndex)) {
// The unloaded chunk is part of a group, remove it from the group
LongSet chunkCoordinates = chunksGroupMap.get(chunkIndex);
chunkCoordinates.remove(chunkIndex);
chunksGroupMap.remove(chunkIndex);
if (chunkCoordinates.isEmpty()) {
// The chunk group is empty, remove it entirely
instanceMap.entrySet().removeIf(entry -> entry.getKey().isEmpty());
}
}
}
@NotNull
@Override
public List<Future<?>> update(long time) {
List<Future<?>> futures;
int potentialSize = 0;
// Compute the potential array size
{
for (Map<LongSet, Instance> longSetInstanceMap : instanceInstanceMap.values()) {
potentialSize += 1 + longSetInstanceMap.size();
}
futures = new ArrayList<>(potentialSize);
}
instanceInstanceMap.forEach((instance, instanceMap) -> {
// True if the instance ended its tick call
final CountDownLatch countDownLatch = new CountDownLatch(1);
// instance tick
futures.add(pool.submit(() -> {
updateInstance(instance, time);
countDownLatch.countDown();
}));
// Update all the chunks
instanceMap.keySet().forEach(chunksIndexes -> futures.add(pool.submit(() -> {
// Wait for the instance to be updated
// Needed because the instance tick is used to unload waiting chunks
try {
countDownLatch.await();
} catch (InterruptedException e) {
MinecraftServer.getExceptionManager().handleException(e);
}
// Tick all this chunk group
chunksIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
})));
});
return futures;
}
private Long2ObjectMap<LongSet> getChunksGroupMap(Instance instance) {
return instanceChunksGroupMap.computeIfAbsent(instance, inst -> new Long2ObjectOpenHashMap<>());
}
private Map<LongSet, Instance> getInstanceMap(Instance instance) {
return instanceInstanceMap.computeIfAbsent(instance, inst -> new HashMap<>());
}
}

View File

@ -1,15 +1,14 @@
package net.minestom.server.thread;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
/**
@ -17,11 +16,11 @@ import java.util.concurrent.Future;
*/
public class PerInstanceThreadProvider extends ThreadProvider {
private final Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
private final Map<Instance, Set<Chunk>> instanceChunkMap = new ConcurrentHashMap<>();
@Override
public void onInstanceCreate(@NotNull Instance instance) {
this.instanceChunkMap.putIfAbsent(instance, new LongArraySet());
this.instanceChunkMap.putIfAbsent(instance, ConcurrentHashMap.newKeySet());
}
@Override
@ -30,20 +29,16 @@ public class PerInstanceThreadProvider extends ThreadProvider {
}
@Override
public void onChunkLoad(@NotNull Instance instance, int chunkX, int chunkZ) {
public void onChunkLoad(@NotNull Instance instance, @NotNull Chunk chunk) {
// Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index);
Set<Chunk> chunks = getChunks(instance);
chunks.add(chunk);
}
@Override
public void onChunkUnload(@NotNull Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index);
public void onChunkUnload(@NotNull Instance instance, @NotNull Chunk chunk) {
Set<Chunk> chunks = getChunks(instance);
chunks.remove(chunk);
}
@NotNull
@ -51,17 +46,19 @@ public class PerInstanceThreadProvider extends ThreadProvider {
public List<Future<?>> update(long time) {
List<Future<?>> futures = new ArrayList<>();
instanceChunkMap.forEach((instance, chunkIndexes) -> futures.add(pool.submit(() -> {
instanceChunkMap.forEach((instance, chunks) -> futures.add(pool.submit(() -> {
// Tick instance
updateInstance(instance, time);
// Tick chunks
chunkIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
for (Chunk chunk : chunks) {
processChunkTick(instance, chunk, time);
}
})));
return futures;
}
private LongSet getChunkCoordinates(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
private Set<Chunk> getChunks(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> ConcurrentHashMap.newKeySet());
}
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.thread;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@ -33,12 +32,12 @@ public class SingleThreadProvider extends ThreadProvider {
}
@Override
public void onChunkLoad(@NotNull Instance instance, int chunkX, int chunkZ) {
public void onChunkLoad(@NotNull Instance instance, @NotNull Chunk chunk) {
}
@Override
public void onChunkUnload(@NotNull Instance instance, int chunkX, int chunkZ) {
public void onChunkUnload(@NotNull Instance instance, @NotNull Chunk chunk) {
}
@ -49,8 +48,7 @@ public class SingleThreadProvider extends ThreadProvider {
for (Instance instance : instances) {
updateInstance(instance, time);
for (Chunk chunk : instance.getChunks()) {
final long index = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
processChunkTick(instance, index, time);
processChunkTick(instance, chunk, time);
}
}
}));

View File

@ -61,19 +61,17 @@ public abstract class ThreadProvider {
* Be aware that this is possible for an instance to load chunks before being registered.
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param chunk the loaded chunk
*/
public abstract void onChunkLoad(@NotNull Instance instance, int chunkX, int chunkZ);
public abstract void onChunkLoad(@NotNull Instance instance, @NotNull Chunk chunk);
/**
* Called when a chunk is unloaded.
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @param chunk the unloaded chunk
*/
public abstract void onChunkUnload(@NotNull Instance instance, int chunkX, int chunkZ);
public abstract void onChunkUnload(@NotNull Instance instance, @NotNull Chunk chunk);
/**
* Performs a server tick for all chunks based on their linked thread.
@ -115,20 +113,15 @@ public abstract class ThreadProvider {
/**
* Processes a whole tick for a chunk.
*
* @param instance the instance of the chunk
* @param chunkIndex the index of the chunk {@link ChunkUtils#getChunkIndex(int, int)}
* @param time the time of the update in milliseconds
* @param instance the instance of the chunk
* @param chunk the chunk to update
* @param time the time of the update in milliseconds
*/
protected void processChunkTick(@NotNull Instance instance, long chunkIndex, long time) {
final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex);
final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex);
final Chunk chunk = instance.getChunk(chunkX, chunkZ);
protected void processChunkTick(@NotNull Instance instance, @NotNull Chunk chunk, long time) {
if (!ChunkUtils.isLoaded(chunk))
return;
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
@ -221,13 +214,15 @@ public abstract class ThreadProvider {
*/
protected void conditionalEntityUpdate(@NotNull Instance instance, @NotNull Chunk chunk, long time,
@Nullable EntityValidator condition) {
final Set<Entity> entities = instance.getChunkEntities(chunk);
if (!instance.getEntities().isEmpty()) {
final Set<Entity> entities = instance.getChunkEntities(chunk);
if (!entities.isEmpty()) {
for (Entity entity : entities) {
if (condition != null && !condition.isValid(entity))
continue;
entity.tick(time);
if (!entities.isEmpty()) {
for (Entity entity : entities) {
if (condition != null && !condition.isValid(entity))
continue;
entity.tick(time);
}
}
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@ -11,7 +10,7 @@ import java.util.Objects;
* Represents a namespaced ID
* https://minecraft.gamepedia.com/Namespaced_ID
*/
public class NamespaceID implements CharSequence, Keyed {
public class NamespaceID implements CharSequence, Key {
private static final Int2ObjectOpenHashMap<NamespaceID> cache = new Int2ObjectOpenHashMap<>();
private static final String legalLetters = "[0123456789abcdefghijklmnopqrstuvwxyz_-]+";
private static final String legalPathLetters = "[0123456789abcdefghijklmnopqrstuvwxyz./_-]+";
@ -19,7 +18,6 @@ public class NamespaceID implements CharSequence, Keyed {
private final String domain;
private final String path;
private final String full;
private final Key key;
/**
* Extracts the domain from the namespace ID. "minecraft:stone" would return "minecraft".
@ -80,7 +78,6 @@ public class NamespaceID implements CharSequence, Keyed {
}
this.full = toString();
validate();
this.key = Key.key(this.domain, this.path);
}
private NamespaceID(String domain, String path) {
@ -88,7 +85,6 @@ public class NamespaceID implements CharSequence, Keyed {
this.path = path;
this.full = toString();
validate();
this.key = Key.key(this.domain, this.path);
}
private void validate() {
@ -141,7 +137,17 @@ public class NamespaceID implements CharSequence, Keyed {
}
@Override
public @NotNull Key key() {
return this.key;
public @NotNull String namespace() {
return this.domain;
}
@Override
public @NotNull String value() {
return this.path;
}
@Override
public @NotNull String asString() {
return this.full;
}
}

View File

@ -20,7 +20,7 @@ public class Position implements PublicCloneable<Position> {
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.yaw = fixYaw(yaw);
this.pitch = pitch;
}
@ -356,7 +356,24 @@ public class Position implements PublicCloneable<Position> {
* @param yaw the new yaw
*/
public void setYaw(float yaw) {
this.yaw = yaw;
this.yaw = fixYaw(yaw);
}
/**
* Fixes a yaw value that is not between -180.0F and 180.0F
* So for example -1355.0F becomes 85.0F and 225.0F becomes -135.0F
*
* @param yaw The possible "wrong" yaw
* @return a fixed yaw
*/
private float fixYaw(float yaw) {
yaw = yaw % 360;
if(yaw < -180.0F) {
yaw += 360.0F;
} else if(yaw > 180.0F) {
yaw -= 360.0F;
}
return yaw;
}
/**

View File

@ -102,13 +102,19 @@ public class BinaryReader extends InputStream {
*
* @param maxLength the max length of the string
* @return the string
* @throws IllegalStateException if the string length is higher than {@code maxLength}
* @throws IllegalStateException if the string length is invalid or higher than {@code maxLength}
*/
public String readSizedString(int maxLength) {
final int length = readVarInt();
Check.stateCondition(length > maxLength,
Check.stateCondition(!buffer.isReadable(length),
"Trying to read a string that is too long (wanted {0}, only have {1})",
length,
buffer.readableBytes());
final String str = buffer.toString(buffer.readerIndex(), length, StandardCharsets.UTF_8);
buffer.skipBytes(length);
Check.stateCondition(str.length() > maxLength,
"String length ({0}) was higher than the max length of {1}", length, maxLength);
return buffer.readCharSequence(length, StandardCharsets.UTF_8).toString();
return str;
}
public byte[] readBytes(int length) {
@ -146,7 +152,7 @@ public class BinaryReader extends InputStream {
}
public byte[] readRemainingBytes() {
return readBytes(buffer.readableBytes());
return readBytes(available());
}
public BlockPosition readBlockPosition() {
@ -195,13 +201,13 @@ public class BinaryReader extends InputStream {
}
/**
* Creates a new object from the given supplier and calls its {@link Readable#read(BinaryReader)} method with this reader
* Creates a new object from the given supplier and calls its {@link Readable#read(BinaryReader)} method with this reader.
*
* @param supplier supplier to create new instances of your object
* @param <T>
* @param <T> the readable object type
* @return the read object
*/
public <T extends Readable> T read(Supplier<T> supplier) {
public <T extends Readable> T read(@NotNull Supplier<@NotNull T> supplier) {
T result = supplier.get();
result.read(this);
return result;
@ -212,16 +218,16 @@ public class BinaryReader extends InputStream {
* their respective {@link Readable#read(BinaryReader)} methods.
*
* @param supplier supplier to create new instances of your object
* @param <T>
* @param <T> the readable object type
* @return the read objects
*/
public <T extends Readable> T[] readArray(Supplier<T> supplier) {
T[] result = (T[]) new Object[readVarInt()];
public <T extends Readable> @NotNull T[] readArray(@NotNull Supplier<@NotNull T> supplier) {
Readable[] result = new Readable[readVarInt()];
for (int i = 0; i < result.length; i++) {
result[i] = supplier.get();
result[i].read(this);
}
return result;
return (T[]) result;
}
public ByteBuf getBuffer() {

View File

@ -281,17 +281,21 @@ public class BinaryWriter extends OutputStream {
*
* @param writeable the object to write
*/
public void write(Writeable writeable) {
public void write(@NotNull Writeable writeable) {
writeable.write(this);
}
public void write(@NotNull BinaryWriter writer) {
this.buffer.writeBytes(writer.getBuffer());
}
/**
* Writes an array of writeable objects to this writer. Will prepend the binary stream with a var int to denote the
* length of the array.
*
* @param writeables the array of writeables to write
*/
public void writeArray(Writeable[] writeables) {
public void writeArray(@NotNull Writeable[] writeables) {
writeVarInt(writeables.length);
for (Writeable w : writeables) {
write(w);
@ -343,7 +347,7 @@ public class BinaryWriter extends OutputStream {
*
* @return the raw buffer
*/
public ByteBuf getBuffer() {
public @NotNull ByteBuf getBuffer() {
return buffer;
}
@ -368,7 +372,7 @@ public class BinaryWriter extends OutputStream {
/**
* Returns a byte[] with the contents written via BinaryWriter
*/
public static byte[] makeArray(Consumer<BinaryWriter> writing) {
public static byte[] makeArray(@NotNull Consumer<@NotNull BinaryWriter> writing) {
BinaryWriter writer = new BinaryWriter();
writing.accept(writer);
return writer.toByteArray();

View File

@ -46,6 +46,9 @@ public interface CacheablePacket {
@Nullable
static FramedPacket getCache(@NotNull ServerPacket serverPacket) {
if (!(serverPacket instanceof CacheablePacket))
return null;
final CacheablePacket cacheablePacket = (CacheablePacket) serverPacket;
final UUID identifier = cacheablePacket.getIdentifier();
if (identifier == null) {
@ -72,4 +75,20 @@ public interface CacheablePacket {
}
}
static void writeCache(@NotNull ByteBuf buffer, @NotNull ServerPacket serverPacket) {
FramedPacket framedPacket = CacheablePacket.getCache(serverPacket);
if (framedPacket == null) {
PacketUtils.writeFramedPacket(buffer, serverPacket);
return;
}
final ByteBuf body = framedPacket.getBody();
synchronized (body) {
if (framedPacket.getBody().refCnt() != 0) {
buffer.writeBytes(body, body.readerIndex(), body.readableBytes());
} else {
PacketUtils.writeFramedPacket(buffer, serverPacket);
}
}
}
}

View File

@ -0,0 +1,16 @@
package net.minestom.server.utils.cache;
import io.netty.buffer.ByteBuf;
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();
synchronized (buffer) {
buffer.release();
}
});
}
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.utils.location;
import net.minestom.server.entity.Entity;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import org.jetbrains.annotations.Nullable;

View File

@ -1,6 +1,5 @@
package net.minestom.server.utils.location;
import net.minestom.server.entity.Entity;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
import org.jetbrains.annotations.Nullable;

View File

@ -2,6 +2,8 @@ package net.minestom.server.utils.time;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class UpdateOption {
private final long value;
@ -20,4 +22,17 @@ public class UpdateOption {
public TimeUnit getTimeUnit() {
return timeUnit;
}
@Override
public int hashCode() {
return Objects.hash(value, timeUnit);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UpdateOption updateOption = (UpdateOption) o;
return Objects.equals(value, updateOption.value) && Objects.equals(timeUnit, updateOption.timeUnit);
}
}

View File

@ -37,6 +37,13 @@ public final class Check {
}
}
@Contract("true, _, _ -> fail")
public static void argCondition(boolean condition, @NotNull String reason, Object... arguments) {
if (condition) {
throw new IllegalArgumentException(MessageFormat.format(reason, arguments));
}
}
@Contract("_ -> fail")
public static void fail(@NotNull String reason) {
throw new IllegalArgumentException(reason);

View File

@ -6,7 +6,6 @@ import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentEntity;
import net.minestom.server.entity.Entity;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.entity.EntityFinder;
import java.util.List;

View File

@ -5,7 +5,6 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentString;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;

View File

@ -4,7 +4,6 @@ import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentEntity;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType;
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
import net.minestom.server.command.builder.condition.Conditions;

View File

@ -5,13 +5,11 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentString;
import net.minestom.server.command.builder.arguments.ArgumentMap;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.extensions.Extension;
import net.minestom.server.extensions.ExtensionManager;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -20,14 +18,20 @@ import java.nio.charset.StandardCharsets;
public class UnloadExtensionCommand extends Command {
private final ArgumentString extensionName;
private final ArgumentMap<String, Extension> extensionName;
public UnloadExtensionCommand() {
super("unload");
setDefaultExecutor(this::usage);
extensionName = ArgumentType.String("extensionName");
extensionName = ArgumentType.String("extensionName").map((input) -> {
Extension extension = MinecraftServer.getExtensionManager().getExtension(input);
if (extension == null) throw new ArgumentSyntaxException("The specified extension was not found", input, 1);
return extension;
});
setArgumentCallback(this::extensionCallback, extensionName);
@ -39,29 +43,25 @@ public class UnloadExtensionCommand extends Command {
}
private void execute(CommandSender sender, CommandContext context) {
final String name = context.get(extensionName);
sender.sendMessage(Component.text("extensionName = " + name + "...."));
final Extension ext = context.get(extensionName);
sender.sendMessage(Component.text("extensionName = " + ext.getOrigin().getName() + "...."));
ExtensionManager extensionManager = MinecraftServer.getExtensionManager();
Extension ext = extensionManager.getExtension(name);
if (ext != null) {
try {
extensionManager.unloadExtension(ext.getOrigin().getName());
} catch (Throwable t) {
try {
extensionManager.unloadExtension(name);
} catch (Throwable t) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
t.printStackTrace();
t.printStackTrace(new PrintStream(baos));
baos.flush();
baos.close();
String contents = baos.toString(StandardCharsets.UTF_8);
contents.lines().map(Component::text).forEach(sender::sendMessage);
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
t.printStackTrace();
t.printStackTrace(new PrintStream(baos));
baos.flush();
baos.close();
String contents = baos.toString(StandardCharsets.UTF_8);
contents.lines().map(Component::text).forEach(sender::sendMessage);
} catch (IOException e) {
e.printStackTrace();
}
} else {
sender.sendMessage(Component.text("Extension '" + name + "' does not exist."));
}
}

View File

@ -1,20 +1,9 @@
package improveextensions.unloadextensiononstop;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.type.monster.EntityZombie;
import net.minestom.server.event.EventCallback;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.entity.EntityTickEvent;
import net.minestom.server.event.instance.InstanceTickEvent;
import net.minestom.server.extensions.Extension;
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.time.TimeUnit;
import org.junit.jupiter.api.Assertions;
import org.opentest4j.AssertionFailedError;
import java.util.concurrent.atomic.AtomicBoolean;
public class UnloadExtensionOnStop extends Extension {

View File

@ -1,8 +1,6 @@
package testextension;
import net.minestom.server.Bootstrap;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Mixins;
// To launch with VM arguments:
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/

View File

@ -1,13 +1,9 @@
package testextension.mixins;
import net.minestom.server.data.Data;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.CustomBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(InstanceContainer.class)