mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-28 14:37:31 +02:00
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:
commit
e19af0377c
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
67
.github/workflows/codeql-analysis.yml
vendored
Normal 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
|
14
build.gradle
14
build.gradle
@ -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}"
|
||||
|
1
gradle/wrapper/gradle-wrapper.properties
vendored
1
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -23,7 +23,7 @@ public class MooshroomMeta extends CowMeta {
|
||||
|
||||
public enum Variant {
|
||||
RED,
|
||||
BROWN;
|
||||
BROWN
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<>();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class StairsPlacementRule extends BlockPlacementRule {
|
||||
OUTER_LEFT,
|
||||
OUTER_RIGHT,
|
||||
INNER_LEFT,
|
||||
INNER_RIGHT;
|
||||
INNER_RIGHT
|
||||
}
|
||||
|
||||
private enum Facing {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)}.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -43,7 +43,7 @@ public class EntityMetaDataPacket implements ServerPacket {
|
||||
break;
|
||||
}
|
||||
|
||||
entries.add(new Metadata.Entry<Object>(reader));
|
||||
entries.add(new Metadata.Entry<>(reader));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.*;
|
||||
|
@ -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<>());
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
16
src/main/java/net/minestom/server/utils/cache/TemporaryPacketCache.java
vendored
Normal file
16
src/main/java/net/minestom/server/utils/cache/TemporaryPacketCache.java
vendored
Normal 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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."));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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/
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user