mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-08 17:37:42 +01:00
Merge branch 'master' into event-api
This commit is contained in:
commit
047d4a92ac
3
.github/javadoc-publish-clear
vendored
Normal file
3
.github/javadoc-publish-clear
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
**/*
|
||||
!.git
|
||||
!CNAME
|
6
.github/workflows/check-pr-style.yml
vendored
6
.github/workflows/check-pr-style.yml
vendored
@ -14,9 +14,9 @@ jobs:
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.11
|
||||
java-version: 11
|
||||
- name: Run java checkstyle
|
||||
uses: nikitasavinov/checkstyle-action@d87d526a914fc5cb0b003908e35038dbb2d6e1b7
|
||||
uses: nikitasavinov/checkstyle-action@0.3.1
|
||||
with:
|
||||
# Report level for reviewdog [info,warning,error]
|
||||
level: info
|
||||
@ -28,4 +28,4 @@ jobs:
|
||||
fail_on_error: false
|
||||
# Checkstyle config file
|
||||
checkstyle_config: minestom_checks.xml
|
||||
checkstyle_version: "8.37"
|
||||
checkstyle_version: "8.42"
|
||||
|
3
.github/workflows/javadoc.yml
vendored
3
.github/workflows/javadoc.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.11
|
||||
java-version: 11
|
||||
- name: Build javadoc
|
||||
run: gradle javadoc
|
||||
|
||||
@ -25,3 +25,4 @@ jobs:
|
||||
BRANCH: javadoc
|
||||
FOLDER: docs
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CLEAR_GLOBS_FILE: ".github/javadoc-publish-clear"
|
||||
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.11
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Setup gradle cache
|
||||
|
23
build.gradle
23
build.gradle
@ -2,9 +2,7 @@ import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id 'net.ltgt.apt' version '0.10'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
|
||||
id 'checkstyle'
|
||||
}
|
||||
@ -12,7 +10,7 @@ plugins {
|
||||
group 'net.minestom.server'
|
||||
version '1.0'
|
||||
|
||||
sourceCompatibility = 1.11
|
||||
sourceCompatibility = 11
|
||||
project.ext.lwjglVersion = "3.2.3"
|
||||
|
||||
switch (OperatingSystem.current()) {
|
||||
@ -33,7 +31,6 @@ switch (OperatingSystem.current()) {
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://libraries.minecraft.net' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven {
|
||||
name 'sponge'
|
||||
@ -54,7 +51,7 @@ allprojects {
|
||||
// see https://stackoverflow.com/a/56641766
|
||||
doLast {
|
||||
// Append the fix to the file
|
||||
def searchScript = new File(destinationDir.getAbsolutePath() + '/search.js')
|
||||
def searchScript = new File(destinationDir, '/search.js')
|
||||
searchScript.append '\n\n' +
|
||||
'getURLPrefix = function(ui) {\n' +
|
||||
' return \'\';\n' +
|
||||
@ -63,7 +60,7 @@ allprojects {
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion "8.37"
|
||||
toolVersion "8.42"
|
||||
configFile file("${projectDir}/minestom_checks.xml")
|
||||
}
|
||||
}
|
||||
@ -115,6 +112,10 @@ test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.withType(Zip).configureEach {
|
||||
duplicatesStrategy DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Junit Testing Framework
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
|
||||
@ -130,9 +131,6 @@ dependencies {
|
||||
api 'io.netty:netty-transport-native-kqueue:4.1.63.Final:osx-x86_64'
|
||||
api 'io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.5.Final:linux-x86_64'
|
||||
|
||||
// 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.4'
|
||||
|
||||
@ -154,9 +152,12 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/org.jline/jline
|
||||
implementation group: 'org.jline', name: 'jline', version: '3.20.0'
|
||||
|
||||
// Guava 21.0+ required for Mixin, but Authlib imports 17.0
|
||||
// Jline compatibility for windows
|
||||
// https://search.maven.org/artifact/org.fusesource.jansi/jansi/2.3.2/jar
|
||||
implementation 'org.fusesource.jansi:jansi:2.3.2'
|
||||
|
||||
// Guava 21.0+ required for Mixin
|
||||
api 'com.google.guava:guava:30.1-jre'
|
||||
api 'com.mojang:authlib:1.5.21'
|
||||
|
||||
// Code modification
|
||||
api "org.ow2.asm:asm:${asmVersion}"
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -277,7 +277,7 @@
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
|
@ -826,6 +826,6 @@ public final class MinecraftServer {
|
||||
}
|
||||
|
||||
private static int getThreadCount(@NotNull String property, int count) {
|
||||
return Integer.getInteger(property, Math.min(1, count));
|
||||
return Integer.getInteger(property, Math.max(1, count));
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ import net.kyori.adventure.title.Title;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.adventure.AdventurePacketConvertor;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.message.Messenger;
|
||||
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
|
||||
import net.minestom.server.network.packet.server.play.TitlePacket;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
@ -46,7 +47,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
|
||||
|
||||
@Override
|
||||
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
||||
PacketUtils.sendGroupedPacket(this.getPlayers(), new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()));
|
||||
Messenger.sendMessage(this.getPlayers(), message, ChatPosition.fromMessageType(type), source.uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,7 +2,8 @@ package net.minestom.server.color;
|
||||
|
||||
import net.kyori.adventure.util.RGBLike;
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -38,17 +39,15 @@ public class Color implements RGBLike {
|
||||
/**
|
||||
* Creates a color from red, green, and blue components.
|
||||
*
|
||||
* @param red the red component
|
||||
* @param red the red component
|
||||
* @param green the green component
|
||||
* @param blue the blue component
|
||||
*
|
||||
* @param blue the blue component
|
||||
* @throws IllegalArgumentException if any component value is not between 0-255 (inclusive)
|
||||
*/
|
||||
public Color(int red, int green, int blue) {
|
||||
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
|
||||
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
|
||||
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
|
||||
|
||||
Check.argCondition(!MathUtils.isBetween(red, 0, 255), "Red is not between 0-255: {0}", red);
|
||||
Check.argCondition(!MathUtils.isBetween(green, 0, 255), "Green is not between 0-255: {0}", green);
|
||||
Check.argCondition(!MathUtils.isBetween(blue, 0, 255), "Blue is not between 0-255: {0}", blue);
|
||||
this.red = red;
|
||||
this.green = green;
|
||||
this.blue = blue;
|
||||
@ -69,7 +68,7 @@ public class Color implements RGBLike {
|
||||
* @param red the red component, from 0 to 255
|
||||
*/
|
||||
public void setRed(int red) {
|
||||
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
|
||||
Check.argCondition(!MathUtils.isBetween(red, 0, 255), "Red is not between 0-255: {0}", red);
|
||||
this.red = red;
|
||||
}
|
||||
|
||||
@ -88,7 +87,7 @@ public class Color implements RGBLike {
|
||||
* @param green the red component, from 0 to 255
|
||||
*/
|
||||
public void setGreen(int green) {
|
||||
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
|
||||
Check.argCondition(!MathUtils.isBetween(green, 0, 255), "Green is not between 0-255: {0}", green);
|
||||
this.green = green;
|
||||
}
|
||||
|
||||
@ -107,7 +106,7 @@ public class Color implements RGBLike {
|
||||
* @param blue the red component, from 0 to 255
|
||||
*/
|
||||
public void setBlue(int blue) {
|
||||
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
|
||||
Check.argCondition(!MathUtils.isBetween(blue, 0, 255), "Blue is not between 0-255: {0}", blue);
|
||||
this.blue = blue;
|
||||
}
|
||||
|
||||
@ -132,8 +131,6 @@ public class Color implements RGBLike {
|
||||
* @param colors the colors
|
||||
*/
|
||||
public void mixWith(@NotNull RGBLike... colors) {
|
||||
Validate.noNullElements(colors, "Colors cannot be null");
|
||||
|
||||
// store the current highest component
|
||||
int max = Math.max(Math.max(this.red, this.green), this.blue);
|
||||
|
||||
|
@ -7,7 +7,7 @@ import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.arguments.*;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.minestom.server.command.builder;
|
||||
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -44,10 +44,16 @@ public class CommandContext {
|
||||
}
|
||||
|
||||
public <T> T get(@NotNull String identifier) {
|
||||
return (T) args.computeIfAbsent(identifier, s -> {
|
||||
throw new NullPointerException(
|
||||
"The argument with the id '" + identifier + "' has no value assigned, be sure to check your arguments id, your syntax, and that you do not change the argument id dynamically.");
|
||||
});
|
||||
return (T) args.get(identifier);
|
||||
}
|
||||
|
||||
public <T> T getOrDefault(@NotNull Argument<T> argument, T defaultValue) {
|
||||
return getOrDefault(argument.getId(), defaultValue);
|
||||
}
|
||||
|
||||
public <T> T getOrDefault(@NotNull String identifier, T defaultValue) {
|
||||
T value;
|
||||
return (value = get(identifier)) != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
public boolean has(@NotNull Argument<?> argument) {
|
||||
|
@ -10,7 +10,7 @@ import net.minestom.server.command.builder.parser.CommandParser;
|
||||
import net.minestom.server.command.builder.parser.CommandQueryResult;
|
||||
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
|
||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -3,7 +3,7 @@ package net.minestom.server.command.builder;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.entity.Player;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.minestom.server.command.builder;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
@ -54,11 +55,19 @@ public class ParsedCommand {
|
||||
final CommandCondition commandCondition = syntax.getCommandCondition();
|
||||
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
|
||||
context.retrieveDefaultValues(syntax.getDefaultValuesMap());
|
||||
executor.apply(source, context);
|
||||
try {
|
||||
executor.apply(source, context);
|
||||
} catch (Exception exception) {
|
||||
MinecraftServer.getExceptionManager().handleException(exception);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The executor is probably the default one
|
||||
executor.apply(source, context);
|
||||
try {
|
||||
executor.apply(source, context);
|
||||
} catch (Exception exception) {
|
||||
MinecraftServer.getExceptionManager().handleException(exception);
|
||||
}
|
||||
}
|
||||
} else if (callback != null && argumentSyntaxException != null) {
|
||||
// No syntax has been validated but the faulty argument with a callback has been found
|
||||
|
@ -195,23 +195,48 @@ public abstract class Argument<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value supplier of the argument.
|
||||
*
|
||||
* @param defaultValue the default argument value
|
||||
* @return 'this' for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public Argument<T> setDefaultValue(@Nullable T defaultValue) {
|
||||
public Argument<T> setDefaultValue(@NotNull T defaultValue) {
|
||||
this.defaultValue = () -> defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the suggestion callback of the argument
|
||||
*
|
||||
* @see #setSuggestionCallback
|
||||
* @return the suggestion callback of the argument, null if it doesn't exist
|
||||
*/
|
||||
@Nullable
|
||||
public SuggestionCallback getSuggestionCallback() {
|
||||
return suggestionCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the suggestion callback (for dynamic tab completion) of this argument.
|
||||
* <p>
|
||||
* Note: This will not automatically filter arguments by user input.
|
||||
*
|
||||
* @param suggestionCallback The suggestion callback to set.
|
||||
* @return 'this' for chaining
|
||||
*/
|
||||
@Beta
|
||||
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
|
||||
this.suggestionCallback = suggestionCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the argument has a suggestion.
|
||||
*
|
||||
* @return If this argument has a suggestion.
|
||||
*/
|
||||
public boolean hasSuggestion() {
|
||||
return suggestionCallback != null;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import net.minestom.server.command.builder.CommandResult;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentCommand extends Argument<CommandResult> {
|
||||
|
@ -7,7 +7,7 @@ import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.callback.validator.StringArrayValidator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -6,9 +6,9 @@ import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.callback.validator.StringValidator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.parser.CommandParser;
|
||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -3,7 +3,7 @@ package net.minestom.server.command.builder.arguments;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -4,8 +4,8 @@ import io.netty.util.internal.StringUtil;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@ -75,7 +75,7 @@ public class ArgumentString extends Argument<String> {
|
||||
}
|
||||
}
|
||||
|
||||
return StringEscapeUtils.unescapeJava(input);
|
||||
return StringUtils.unescapeJavaString(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,8 +2,8 @@ package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -10,7 +10,7 @@ import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.entity.EntityFinder;
|
||||
import net.minestom.server.utils.math.IntRange;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -4,7 +4,7 @@ import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentResourceLocation extends Argument<String> {
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.location.RelativeBlockPosition;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.location.RelativeVec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.location.RelativeVec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBl
|
||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2;
|
||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -7,7 +7,7 @@ import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -268,7 +268,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
final ChunkCallback endCallback = (chunk) -> {
|
||||
refreshPosition(teleportPosition);
|
||||
|
||||
synchronizePosition();
|
||||
synchronizePosition(true);
|
||||
|
||||
OptionalCallback.execute(callback);
|
||||
};
|
||||
@ -552,7 +552,10 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
|
||||
// Apply the position if changed
|
||||
if (!finalVelocityPosition.isSimilar(position)) {
|
||||
refreshPosition(finalVelocityPosition);
|
||||
refreshPosition(finalVelocityPosition.getX(),
|
||||
finalVelocityPosition.getY(),
|
||||
finalVelocityPosition.getZ());
|
||||
sendPositionUpdate(true);
|
||||
}
|
||||
|
||||
|
||||
@ -594,7 +597,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
|
||||
// Synchronization and packets...
|
||||
if (!isNettyClient) {
|
||||
synchronizePosition();
|
||||
synchronizePosition(true);
|
||||
}
|
||||
// Verify if velocity packet has to be sent
|
||||
if (hasVelocity() || (!isNettyClient && gravityTickCount > 0)) {
|
||||
@ -665,7 +668,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
|
||||
// Scheduled synchronization
|
||||
if (!Cooldown.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
|
||||
synchronizePosition();
|
||||
synchronizePosition(false);
|
||||
}
|
||||
|
||||
if (shouldRemove() && !MinecraftServer.isStopping()) {
|
||||
@ -677,28 +680,28 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
* Sends the correct packets to update the entity's position, should be called
|
||||
* every tick. The movement is checked inside the method!
|
||||
* <p>
|
||||
* The following packets are sent to viewers (check are performed in this order):
|
||||
* <ol>
|
||||
* <li>{@link EntityTeleportPacket} if {@code distanceX > 8 || distanceY > 8 || distanceZ > 8}
|
||||
* <i>(performed using {@link #synchronizePosition()})</i></li>
|
||||
* <li>{@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}</li>
|
||||
* <li>{@link EntityPositionPacket} if {@code positionChange}</li>
|
||||
* <li>{@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}</li>
|
||||
* </ol>
|
||||
* In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket}
|
||||
* is sent to self.
|
||||
* The following packets are sent to viewers (check are performed in this order):
|
||||
* <ol>
|
||||
* <li>{@link EntityTeleportPacket} if {@code distanceX > 8 || distanceY > 8 || distanceZ > 8}
|
||||
* <i>(performed using {@link #synchronizePosition(boolean)})</i></li>
|
||||
* <li>{@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}</li>
|
||||
* <li>{@link EntityPositionPacket} if {@code positionChange}</li>
|
||||
* <li>{@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}</li>
|
||||
* </ol>
|
||||
* In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket}
|
||||
* is sent to self.
|
||||
*
|
||||
* @param clientSide {@code true} if the client triggered this action
|
||||
*/
|
||||
protected void sendPositionUpdate(final boolean clientSide) {
|
||||
final boolean viewChange = !position.hasSimilarView(lastSyncedPosition);
|
||||
final double distanceX = Math.abs(position.getX()-lastSyncedPosition.getX());
|
||||
final double distanceY = Math.abs(position.getY()-lastSyncedPosition.getY());
|
||||
final double distanceZ = Math.abs(position.getZ()-lastSyncedPosition.getZ());
|
||||
final boolean positionChange = (distanceX+distanceY+distanceZ) > 0;
|
||||
final double distanceX = Math.abs(position.getX() - lastSyncedPosition.getX());
|
||||
final double distanceY = Math.abs(position.getY() - lastSyncedPosition.getY());
|
||||
final double distanceZ = Math.abs(position.getZ() - lastSyncedPosition.getZ());
|
||||
final boolean positionChange = (distanceX + distanceY + distanceZ) > 0;
|
||||
|
||||
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
|
||||
synchronizePosition();
|
||||
synchronizePosition(true);
|
||||
// #synchronizePosition sets sync fields, it's safe to return
|
||||
return;
|
||||
} else if (positionChange && viewChange) {
|
||||
@ -742,7 +745,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
final PlayerPositionAndLookPacket playerPositionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
playerPositionAndLookPacket.flags = 0b111;
|
||||
playerPositionAndLookPacket.position = position.clone().subtract(lastSyncedPosition.getX(), lastSyncedPosition.getY(), lastSyncedPosition.getZ());
|
||||
playerPositionAndLookPacket.teleportId = ((Player)this).getNextTeleportId();
|
||||
playerPositionAndLookPacket.teleportId = ((Player) this).getNextTeleportId();
|
||||
((Player) this).getPlayerConnection().sendPacket(playerPositionAndLookPacket);
|
||||
}
|
||||
|
||||
@ -1393,8 +1396,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
/**
|
||||
* Updates internal fields and sends updates
|
||||
*
|
||||
* @param position the new position
|
||||
*
|
||||
* @param position the new position
|
||||
* @see #refreshPosition(double, double, double)
|
||||
* @see #refreshView(float, float)
|
||||
* @see #sendPositionUpdate(boolean)
|
||||
@ -1583,9 +1585,12 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
* {@link EntityTeleportPacket} to viewers, in case of a player this is
|
||||
* overridden in order to send an additional {@link PlayerPositionAndLookPacket}
|
||||
* to itself.
|
||||
*
|
||||
* @param includeSelf if {@code true} and this is a {@link Player} an additional {@link PlayerPositionAndLookPacket}
|
||||
* will be sent to the player itself
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
protected void synchronizePosition() {
|
||||
protected void synchronizePosition(boolean includeSelf) {
|
||||
final Position pos = position.clone();
|
||||
final EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||
entityTeleportPacket.entityId = getEntityId();
|
||||
|
42
src/main/java/net/minestom/server/entity/EquipmentSlot.java
Normal file
42
src/main/java/net/minestom/server/entity/EquipmentSlot.java
Normal file
@ -0,0 +1,42 @@
|
||||
package net.minestom.server.entity;
|
||||
|
||||
import net.minestom.server.event.item.EntityEquipEvent;
|
||||
import net.minestom.server.item.attribute.AttributeSlot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public enum EquipmentSlot {
|
||||
MAIN_HAND,
|
||||
OFF_HAND,
|
||||
BOOTS,
|
||||
LEGGINGS,
|
||||
CHESTPLATE,
|
||||
HELMET;
|
||||
|
||||
public boolean isHand() {
|
||||
return this == MAIN_HAND || this == OFF_HAND;
|
||||
}
|
||||
|
||||
public boolean isArmor() {
|
||||
return !isHand();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static EquipmentSlot fromAttributeSlot(AttributeSlot attributeSlot) {
|
||||
switch (attributeSlot) {
|
||||
case MAINHAND:
|
||||
return MAIN_HAND;
|
||||
case OFFHAND:
|
||||
return OFF_HAND;
|
||||
case FEET:
|
||||
return BOOTS;
|
||||
case LEGS:
|
||||
return LEGGINGS;
|
||||
case CHEST:
|
||||
return CHESTPLATE;
|
||||
case HEAD:
|
||||
return HELMET;
|
||||
}
|
||||
throw new IllegalStateException("Something weird happened");
|
||||
}
|
||||
|
||||
}
|
@ -10,7 +10,7 @@ import net.minestom.server.entity.metadata.LivingEntityMeta;
|
||||
import net.minestom.server.event.entity.EntityDamageEvent;
|
||||
import net.minestom.server.event.entity.EntityDeathEvent;
|
||||
import net.minestom.server.event.entity.EntityFireEvent;
|
||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
||||
import net.minestom.server.event.item.EntityEquipEvent;
|
||||
import net.minestom.server.event.item.PickupItemEvent;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
@ -127,8 +127,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
||||
this.mainHandItem = itemStack;
|
||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
||||
this.mainHandItem = getEquipmentItem(itemStack, EquipmentSlot.MAIN_HAND);
|
||||
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -139,8 +139,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
||||
this.offHandItem = itemStack;
|
||||
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
|
||||
this.offHandItem = getEquipmentItem(itemStack, EquipmentSlot.OFF_HAND);
|
||||
syncEquipment(EquipmentSlot.OFF_HAND);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -151,8 +151,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setHelmet(@NotNull ItemStack itemStack) {
|
||||
this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
|
||||
this.helmet = getEquipmentItem(itemStack, EquipmentSlot.HELMET);
|
||||
syncEquipment(EquipmentSlot.HELMET);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -163,8 +163,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setChestplate(@NotNull ItemStack itemStack) {
|
||||
this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
|
||||
this.chestplate = getEquipmentItem(itemStack, EquipmentSlot.CHESTPLATE);
|
||||
syncEquipment(EquipmentSlot.CHESTPLATE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -175,8 +175,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setLeggings(@NotNull ItemStack itemStack) {
|
||||
this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
|
||||
this.leggings = getEquipmentItem(itemStack, EquipmentSlot.LEGGINGS);
|
||||
syncEquipment(EquipmentSlot.LEGGINGS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -187,14 +187,14 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setBoots(@NotNull ItemStack itemStack) {
|
||||
this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
|
||||
this.boots = getEquipmentItem(itemStack, EquipmentSlot.BOOTS);
|
||||
syncEquipment(EquipmentSlot.BOOTS);
|
||||
}
|
||||
|
||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
|
||||
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
|
||||
callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
||||
return armorEquipEvent.getArmorItem();
|
||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull EquipmentSlot slot) {
|
||||
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(this, itemStack, slot);
|
||||
callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||
return entityEquipEvent.getEquippedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -595,6 +595,14 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFlyingWithElytra() {
|
||||
return this.entityMeta.isFlyingWithElytra();
|
||||
}
|
||||
|
||||
public void setFlyingWithElytra(boolean isFlying) {
|
||||
this.entityMeta.setFlyingWithElytra(isFlying);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to change the {@code isDead} internal field.
|
||||
*
|
||||
|
@ -38,12 +38,15 @@ import net.minestom.server.event.player.*;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.message.ChatMessageType;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.item.metadata.WrittenBookMeta;
|
||||
import net.minestom.server.listener.PlayerDiggingListener;
|
||||
import net.minestom.server.message.Messenger;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.ConnectionState;
|
||||
import net.minestom.server.network.PlayerProvider;
|
||||
@ -661,7 +664,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
}
|
||||
|
||||
if (dimensionChange || firstSpawn) {
|
||||
synchronizePosition(); // So the player doesn't get stuck
|
||||
synchronizePosition(true); // So the player doesn't get stuck
|
||||
this.inventory.update();
|
||||
}
|
||||
|
||||
@ -730,8 +733,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
||||
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid());
|
||||
playerConnection.sendPacket(chatMessagePacket);
|
||||
Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1984,18 +1986,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Entity#synchronizePosition()
|
||||
* @see Entity#synchronizePosition(boolean)
|
||||
*/
|
||||
@Override
|
||||
@ApiStatus.Internal
|
||||
protected void synchronizePosition() {
|
||||
final PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
positionAndLookPacket.position = position.clone();
|
||||
positionAndLookPacket.flags = 0x00;
|
||||
positionAndLookPacket.teleportId = teleportId.incrementAndGet();
|
||||
playerConnection.sendPacket(positionAndLookPacket);
|
||||
protected void synchronizePosition(boolean includeSelf) {
|
||||
if (includeSelf) {
|
||||
final PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
positionAndLookPacket.position = position.clone();
|
||||
positionAndLookPacket.flags = 0x00;
|
||||
positionAndLookPacket.teleportId = teleportId.incrementAndGet();
|
||||
playerConnection.sendPacket(positionAndLookPacket);
|
||||
}
|
||||
|
||||
super.synchronizePosition();
|
||||
super.synchronizePosition(includeSelf);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2223,6 +2227,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
|
||||
public void refreshOnGround(boolean onGround) {
|
||||
this.onGround = onGround;
|
||||
if(this.onGround && this.isFlyingWithElytra()) {
|
||||
this.setFlyingWithElytra(false);
|
||||
this.callEvent(PlayerStopFlyingWithElytraEvent.class, new PlayerStopFlyingWithElytraEvent(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2256,7 +2264,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
*/
|
||||
public void refreshHeldSlot(byte slot) {
|
||||
this.heldSlot = slot;
|
||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
||||
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||
|
||||
refreshEating(null);
|
||||
}
|
||||
@ -2578,6 +2586,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
RIGHT
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated See {@link ChatMessageType}
|
||||
*/
|
||||
@Deprecated
|
||||
public enum ChatMode {
|
||||
ENABLED,
|
||||
COMMANDS_ONLY,
|
||||
@ -2588,7 +2600,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
|
||||
private String locale;
|
||||
private byte viewDistance;
|
||||
private ChatMode chatMode;
|
||||
private ChatMessageType chatMessageType;
|
||||
private boolean chatColors;
|
||||
private byte displayedSkinParts;
|
||||
private MainHand mainHand;
|
||||
@ -2619,9 +2631,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
* Gets the player chat mode.
|
||||
*
|
||||
* @return the player chat mode
|
||||
* @deprecated Use {@link #getChatMessageType()}
|
||||
*/
|
||||
@Deprecated
|
||||
public ChatMode getChatMode() {
|
||||
return chatMode;
|
||||
return ChatMode.values()[chatMessageType.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the messages this player wants to receive.
|
||||
*
|
||||
* @return the messages
|
||||
*/
|
||||
public @Nullable ChatMessageType getChatMessageType() {
|
||||
return chatMessageType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2653,19 +2676,19 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
*
|
||||
* @param locale the player locale
|
||||
* @param viewDistance the player view distance
|
||||
* @param chatMode the player chat mode
|
||||
* @param chatColors the player chat colors
|
||||
* @param chatMessageType the chat messages the player wishes to receive
|
||||
* @param chatColors if chat colors should be displayed
|
||||
* @param displayedSkinParts the player displayed skin parts
|
||||
* @param mainHand the player main hand
|
||||
*/
|
||||
public void refresh(String locale, byte viewDistance, ChatMode chatMode, boolean chatColors,
|
||||
public void refresh(String locale, byte viewDistance, ChatMessageType chatMessageType, boolean chatColors,
|
||||
byte displayedSkinParts, MainHand mainHand) {
|
||||
|
||||
final boolean viewDistanceChanged = this.viewDistance != viewDistance;
|
||||
|
||||
this.locale = locale;
|
||||
this.viewDistance = viewDistance;
|
||||
this.chatMode = chatMode;
|
||||
this.chatMessageType = chatMessageType;
|
||||
this.chatColors = chatColors;
|
||||
this.displayedSkinParts = displayedSkinParts;
|
||||
this.mainHand = mainHand;
|
||||
|
@ -124,17 +124,22 @@ public class FakePlayer extends Player implements NavigableEntity {
|
||||
super.setInstance(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean addViewer0(@NotNull Player player) {
|
||||
final boolean result = super.addViewer0(player);
|
||||
if (result) {
|
||||
handleTabList(player.getPlayerConnection());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void showPlayer(@NotNull PlayerConnection connection) {
|
||||
super.showPlayer(connection);
|
||||
if (!option.isInTabList()) {
|
||||
// Remove from tab-list
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> connection.sendPacket(getRemovePlayerToList())).delay(20, TimeUnit.TICK).schedule();
|
||||
}
|
||||
|
||||
handleTabList(connection);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -142,4 +147,11 @@ public class FakePlayer extends Player implements NavigableEntity {
|
||||
public Navigator getNavigator() {
|
||||
return navigator;
|
||||
}
|
||||
|
||||
private void handleTabList(PlayerConnection connection) {
|
||||
if (!option.isInTabList()) {
|
||||
// Remove from tab-list
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> connection.sendPacket(getRemovePlayerToList())).delay(20, TimeUnit.TICK).schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package net.minestom.server.entity.metadata.animal;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FoxMeta extends AnimalMeta {
|
||||
|
@ -1,13 +1,9 @@
|
||||
package net.minestom.server.entity.type.decoration;
|
||||
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.ObjectEntity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
||||
import net.minestom.server.entity.*;
|
||||
import net.minestom.server.event.item.EntityEquipEvent;
|
||||
import net.minestom.server.inventory.EquipmentHandler;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.binary.BitmaskUtil;
|
||||
@ -73,8 +69,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
||||
this.mainHandItem = itemStack;
|
||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
||||
this.mainHandItem = getEquipmentItem(itemStack, EquipmentSlot.MAIN_HAND);
|
||||
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -85,8 +81,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
||||
this.offHandItem = itemStack;
|
||||
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
|
||||
this.offHandItem = getEquipmentItem(itemStack, EquipmentSlot.OFF_HAND);
|
||||
syncEquipment(EquipmentSlot.OFF_HAND);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -97,8 +93,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setHelmet(@NotNull ItemStack itemStack) {
|
||||
this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
|
||||
this.helmet = getEquipmentItem(itemStack, EquipmentSlot.HELMET);
|
||||
syncEquipment(EquipmentSlot.HELMET);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -109,8 +105,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setChestplate(@NotNull ItemStack itemStack) {
|
||||
this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
|
||||
this.chestplate = getEquipmentItem(itemStack, EquipmentSlot.CHESTPLATE);
|
||||
syncEquipment(EquipmentSlot.CHESTPLATE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -121,8 +117,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setLeggings(@NotNull ItemStack itemStack) {
|
||||
this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
|
||||
this.leggings = getEquipmentItem(itemStack, EquipmentSlot.LEGGINGS);
|
||||
syncEquipment(EquipmentSlot.LEGGINGS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -133,8 +129,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
@Override
|
||||
public void setBoots(@NotNull ItemStack itemStack) {
|
||||
this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
||||
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
|
||||
this.boots = getEquipmentItem(itemStack, EquipmentSlot.BOOTS);
|
||||
syncEquipment(EquipmentSlot.BOOTS);
|
||||
}
|
||||
|
||||
public boolean isSmall() {
|
||||
@ -239,9 +235,9 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
||||
|
||||
// Equipments
|
||||
|
||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
|
||||
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
|
||||
callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
||||
return armorEquipEvent.getArmorItem();
|
||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull EquipmentSlot slot) {
|
||||
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(this, itemStack, slot);
|
||||
callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||
return entityEquipEvent.getEquippedItem();
|
||||
}
|
||||
}
|
||||
|
@ -108,22 +108,27 @@ public interface EventHandler extends IExtensionObserver {
|
||||
*/
|
||||
default <E extends Event> void callEvent(@NotNull Class<E> eventClass, @NotNull E event) {
|
||||
|
||||
// Global listeners
|
||||
if (!(this instanceof GlobalEventHandler)) {
|
||||
final GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
|
||||
runEvent(globalEventHandler.getEventCallbacks(eventClass), event);
|
||||
}
|
||||
try {
|
||||
|
||||
// Local listeners
|
||||
final Collection<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
|
||||
runEvent(eventCallbacks, event);
|
||||
|
||||
// Call the same event for the current entity instance
|
||||
if (this instanceof Entity) {
|
||||
final Instance instance = ((Entity) this).getInstance();
|
||||
if (instance != null) {
|
||||
runEvent(instance.getEventCallbacks(eventClass), event);
|
||||
// Global listeners
|
||||
if (!(this instanceof GlobalEventHandler)) {
|
||||
final GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
|
||||
runEvent(globalEventHandler.getEventCallbacks(eventClass), event);
|
||||
}
|
||||
|
||||
// Local listeners
|
||||
final Collection<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
|
||||
runEvent(eventCallbacks, event);
|
||||
|
||||
// Call the same event for the current entity instance
|
||||
if (this instanceof Entity) {
|
||||
final Instance instance = ((Entity) this).getInstance();
|
||||
if (instance != null) {
|
||||
runEvent(instance.getEventCallbacks(eventClass), event);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
MinecraftServer.getExceptionManager().handleException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
package net.minestom.server.event.item;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.event.Event;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArmorEquipEvent extends Event {
|
||||
|
||||
private final Entity entity;
|
||||
private ItemStack armorItem;
|
||||
private final ArmorSlot armorSlot;
|
||||
|
||||
public ArmorEquipEvent(@NotNull Entity entity, @NotNull ItemStack armorItem, @NotNull ArmorSlot armorSlot) {
|
||||
this.entity = entity;
|
||||
this.armorItem = armorItem;
|
||||
this.armorSlot = armorSlot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ItemStack getArmorItem() {
|
||||
return armorItem;
|
||||
}
|
||||
|
||||
public void setArmorItem(@NotNull ItemStack armorItem) {
|
||||
this.armorItem = armorItem;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ArmorSlot getArmorSlot() {
|
||||
return armorSlot;
|
||||
}
|
||||
|
||||
public enum ArmorSlot {
|
||||
HELMET,
|
||||
CHESTPLATE,
|
||||
LEGGINGS,
|
||||
BOOTS
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.minestom.server.event.item;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.event.Event;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EntityEquipEvent extends Event {
|
||||
|
||||
private final Entity entity;
|
||||
private ItemStack equippedItem;
|
||||
private final EquipmentSlot slot;
|
||||
|
||||
public EntityEquipEvent(@NotNull Entity entity, @NotNull ItemStack equippedItem, @NotNull EquipmentSlot slot) {
|
||||
this.entity = entity;
|
||||
this.equippedItem = equippedItem;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ItemStack getEquippedItem() {
|
||||
return equippedItem;
|
||||
}
|
||||
|
||||
public void setEquippedItem(@NotNull ItemStack armorItem) {
|
||||
this.equippedItem = armorItem;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EquipmentSlot getSlot() {
|
||||
return slot;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.minestom.server.event.player;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PlayerStartFlyingWithElytraEvent extends PlayerEvent {
|
||||
|
||||
public PlayerStartFlyingWithElytraEvent(@NotNull Player player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.minestom.server.event.player;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PlayerStopFlyingWithElytraEvent extends PlayerEvent {
|
||||
|
||||
public PlayerStopFlyingWithElytraEvent(@NotNull Player player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@ -75,6 +76,8 @@ public final class DiscoveredExtension {
|
||||
/** The original jar this is from. */
|
||||
transient private File originalJar;
|
||||
|
||||
transient private Path dataDirectory;
|
||||
|
||||
/** The class loader that powers it. */
|
||||
transient private MinestomExtensionClassLoader minestomExtensionClassLoader;
|
||||
|
||||
@ -130,6 +133,14 @@ public final class DiscoveredExtension {
|
||||
return originalJar;
|
||||
}
|
||||
|
||||
public @NotNull Path getDataDirectory() {
|
||||
return dataDirectory;
|
||||
}
|
||||
|
||||
public void setDataDirectory(@NotNull Path dataDirectory) {
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
MinestomExtensionClassLoader removeMinestomExtensionClassLoader() {
|
||||
MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader();
|
||||
setMinestomExtensionClassLoader(null);
|
||||
|
@ -1,11 +1,19 @@
|
||||
package net.minestom.server.extensions;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
@ -77,6 +85,118 @@ public abstract class Extension {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public @NotNull Path getDataDirectory() {
|
||||
return getOrigin().getDataDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a resource from the extension directory, or from inside the jar if it does not
|
||||
* exist in the extension directory.
|
||||
* <p>
|
||||
* If it does not exist in the extension directory, it will be copied from inside the jar.
|
||||
* <p>
|
||||
* The caller is responsible for closing the returned {@link InputStream}.
|
||||
*
|
||||
* @param fileName The file to read
|
||||
* @return The file contents, or null if there was an issue reading the file.
|
||||
*/
|
||||
public @Nullable InputStream getResource(@NotNull String fileName) {
|
||||
return getResource(Paths.get(fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a resource from the extension directory, or from inside the jar if it does not
|
||||
* exist in the extension directory.
|
||||
* <p>
|
||||
* If it does not exist in the extension directory, it will be copied from inside the jar.
|
||||
* <p>
|
||||
* The caller is responsible for closing the returned {@link InputStream}.
|
||||
*
|
||||
* @param target The file to read
|
||||
* @return The file contents, or null if there was an issue reading the file.
|
||||
*/
|
||||
public @Nullable InputStream getResource(@NotNull Path target) {
|
||||
final Path targetFile = getDataDirectory().resolve(target);
|
||||
try {
|
||||
// Copy from jar if the file does not exist in the extension data directory.
|
||||
if (!Files.exists(targetFile)) {
|
||||
savePackagedResource(target);
|
||||
}
|
||||
|
||||
return Files.newInputStream(targetFile);
|
||||
} catch (IOException ex) {
|
||||
getLogger().info("Failed to read resource {}.", target, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a resource from inside the extension jar.
|
||||
* <p>
|
||||
* The caller is responsible for closing the returned {@link InputStream}.
|
||||
*
|
||||
* @param fileName The file to read
|
||||
* @return The file contents, or null if there was an issue reading the file.
|
||||
*/
|
||||
public @Nullable InputStream getPackagedResource(@NotNull String fileName) {
|
||||
return getPackagedResource(Paths.get(fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a resource from inside the extension jar.
|
||||
* <p>
|
||||
* The caller is responsible for closing the returned {@link InputStream}.
|
||||
*
|
||||
* @param target The file to read
|
||||
* @return The file contents, or null if there was an issue reading the file.
|
||||
*/
|
||||
public @Nullable InputStream getPackagedResource(@NotNull Path target) {
|
||||
try {
|
||||
final URL url = getOrigin().getMinestomExtensionClassLoader().getResource(target.toString());
|
||||
if (url == null) {
|
||||
getLogger().debug("Resource not found: {}", target);
|
||||
return null;
|
||||
}
|
||||
|
||||
return url.openConnection().getInputStream();
|
||||
} catch (IOException ex) {
|
||||
getLogger().debug("Failed to load resource {}.", target, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a resource file to the extension directory, replacing any existing copy.
|
||||
*
|
||||
* @param fileName The resource to save
|
||||
* @return True if the resource was saved successfully, null otherwise
|
||||
*/
|
||||
public boolean savePackagedResource(@NotNull String fileName) {
|
||||
return savePackagedResource(Paths.get(fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a resource file to the extension directory, replacing any existing copy.
|
||||
*
|
||||
* @param target The resource to save
|
||||
* @return True if the resource was saved successfully, null otherwise
|
||||
*/
|
||||
public boolean savePackagedResource(@NotNull Path target) {
|
||||
final Path targetFile = getDataDirectory().resolve(target);
|
||||
try (InputStream is = getPackagedResource(target)) {
|
||||
if (is == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Files.createDirectories(targetFile.getParent());
|
||||
Files.copy(is, targetFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
getLogger().debug("Failed to save resource {}.", target, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new observer to this extension.
|
||||
* Will be kept as a WeakReference.
|
||||
|
@ -24,6 +24,8 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipFile;
|
||||
@ -44,6 +46,7 @@ public class ExtensionManager {
|
||||
|
||||
private final File extensionFolder = new File("extensions");
|
||||
private final File dependenciesFolder = new File(extensionFolder, ".libs");
|
||||
private Path extensionDataRoot = extensionFolder.toPath();
|
||||
private boolean loaded;
|
||||
|
||||
// Option
|
||||
@ -336,6 +339,7 @@ public class ExtensionManager {
|
||||
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
||||
extension.files.add(new File(extensionClasses).toURI().toURL());
|
||||
extension.files.add(new File(extensionResources).toURI().toURL());
|
||||
extension.setDataDirectory(getExtensionDataRoot().resolve(extension.getName()));
|
||||
|
||||
// Verify integrity and ensure defaults
|
||||
DiscoveredExtension.verifyIntegrity(extension);
|
||||
@ -365,6 +369,7 @@ public class ExtensionManager {
|
||||
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
||||
extension.setOriginalJar(file);
|
||||
extension.files.add(file.toURI().toURL());
|
||||
extension.setDataDirectory(getExtensionDataRoot().resolve(extension.getName()));
|
||||
|
||||
// Verify integrity and ensure defaults
|
||||
DiscoveredExtension.verifyIntegrity(extension);
|
||||
@ -569,6 +574,14 @@ public class ExtensionManager {
|
||||
return extensionFolder;
|
||||
}
|
||||
|
||||
public @NotNull Path getExtensionDataRoot() {
|
||||
return extensionDataRoot;
|
||||
}
|
||||
|
||||
public void setExtensionDataRoot(@NotNull Path dataRoot) {
|
||||
this.extensionDataRoot = dataRoot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<Extension> getExtensions() {
|
||||
return immutableExtensions.values();
|
||||
|
@ -1,14 +1,10 @@
|
||||
package net.minestom.server.extras;
|
||||
|
||||
import com.mojang.authlib.AuthenticationService;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extras.mojangAuth.MojangCrypt;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.security.KeyPair;
|
||||
|
||||
public final class MojangAuth {
|
||||
@ -16,8 +12,6 @@ public final class MojangAuth {
|
||||
private static volatile boolean enabled = false;
|
||||
|
||||
private static KeyPair keyPair;
|
||||
private static AuthenticationService authService;
|
||||
private static MinecraftSessionService sessionService;
|
||||
|
||||
/**
|
||||
* Enables mojang authentication on the server.
|
||||
@ -32,8 +26,6 @@ public final class MojangAuth {
|
||||
|
||||
// Generate necessary fields...
|
||||
keyPair = MojangCrypt.generateKeyPair();
|
||||
authService = new YggdrasilAuthenticationService(Proxy.NO_PROXY, "");
|
||||
sessionService = authService.createMinecraftSessionService();
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
@ -44,14 +36,4 @@ public final class MojangAuth {
|
||||
public static KeyPair getKeyPair() {
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AuthenticationService getAuthService() {
|
||||
return authService;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MinecraftSessionService getSessionService() {
|
||||
return sessionService;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.minestom.server.instance.block.rule.vanilla;
|
||||
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
@ -7,7 +8,6 @@ import net.minestom.server.instance.block.BlockAlternative;
|
||||
import net.minestom.server.instance.block.BlockFace;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -113,10 +113,10 @@ public class StairsPlacementRule extends BlockPlacementRule {
|
||||
|
||||
@Nullable
|
||||
private Shape getShapeFromSide(@NotNull Pair<Shape, Facing> side, @NotNull Facing facing, @NotNull Shape right, @NotNull Shape left) {
|
||||
if (side.getLeft() == null) {
|
||||
if (side.left() == null) {
|
||||
return null;
|
||||
}
|
||||
Facing sideFacing = side.getRight();
|
||||
Facing sideFacing = side.right();
|
||||
if (facing.equals(Facing.NORTH)) {
|
||||
if (sideFacing.equals(Facing.EAST)) {
|
||||
return right;
|
||||
|
@ -53,6 +53,8 @@ public class Section implements PublicCloneable<Section> {
|
||||
private int valuesPerLong;
|
||||
private boolean hasPalette;
|
||||
|
||||
private short blockCount = 0;
|
||||
|
||||
protected Section(int bitsPerEntry, int bitsIncrement) {
|
||||
this.bitsPerEntry = bitsPerEntry;
|
||||
this.bitsIncrement = bitsIncrement;
|
||||
@ -74,6 +76,9 @@ public class Section implements PublicCloneable<Section> {
|
||||
blocks = new long[getSize(valuesPerLong)];
|
||||
}
|
||||
|
||||
// Check if the new block is air, used for counting none air blocks.
|
||||
final boolean isAir = Block.fromStateId(blockId).isAir();
|
||||
|
||||
// Change to palette value
|
||||
blockId = getPaletteIndex(blockId);
|
||||
|
||||
@ -87,10 +92,20 @@ public class Section implements PublicCloneable<Section> {
|
||||
{
|
||||
final long clear = MAGIC_MASKS[bitsPerEntry];
|
||||
|
||||
final long value = block >> bitIndex & clear;
|
||||
final boolean isCurrentAir = Block.fromStateId(
|
||||
hasPalette ? paletteBlockMap.get((short) value) : (short) value).isAir();
|
||||
|
||||
block |= clear << bitIndex;
|
||||
block ^= clear << bitIndex;
|
||||
block |= (long) blockId << bitIndex;
|
||||
|
||||
if (!isCurrentAir && isAir) { // The old block isn't air & the new block is.
|
||||
this.blockCount--;
|
||||
} else if (isCurrentAir && !isAir) { // The old block is air & the new block isn't.
|
||||
this.blockCount++;
|
||||
} // If both block are air or not air then don't change the value.
|
||||
|
||||
blocks[index] = block;
|
||||
}
|
||||
}
|
||||
@ -143,6 +158,7 @@ public class Section implements PublicCloneable<Section> {
|
||||
this.hasPalette = section.hasPalette;
|
||||
|
||||
this.blocks = section.blocks;
|
||||
this.blockCount = section.blockCount;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,12 +187,22 @@ public class Section implements PublicCloneable<Section> {
|
||||
this.blocks = new long[0];
|
||||
this.paletteBlockMap = createPaletteBlockMap();
|
||||
this.blockPaletteMap = createBlockPaletteMap();
|
||||
this.blockCount = 0;
|
||||
}
|
||||
|
||||
public long[] getBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of non air blocks in this section.
|
||||
*
|
||||
* @return The amount of blocks in this section.
|
||||
*/
|
||||
public short getBlockCount() {
|
||||
return blockCount;
|
||||
}
|
||||
|
||||
public Short2ShortLinkedOpenHashMap getPaletteBlockMap() {
|
||||
return paletteBlockMap;
|
||||
}
|
||||
@ -288,6 +314,7 @@ public class Section implements PublicCloneable<Section> {
|
||||
section.blocks = blocks.clone();
|
||||
section.paletteBlockMap = paletteBlockMap.clone();
|
||||
section.blockPaletteMap = blockPaletteMap.clone();
|
||||
section.blockCount = blockCount;
|
||||
return section;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
||||
@ -139,7 +140,7 @@ public interface EquipmentHandler {
|
||||
* @param slot the equipment to get the item from
|
||||
* @return the equipment {@link ItemStack}
|
||||
*/
|
||||
default @NotNull ItemStack getEquipment(@NotNull EntityEquipmentPacket.Slot slot) {
|
||||
default @NotNull ItemStack getEquipment(@NotNull EquipmentSlot slot) {
|
||||
switch (slot) {
|
||||
case MAIN_HAND:
|
||||
return getItemInMainHand();
|
||||
@ -157,7 +158,7 @@ public interface EquipmentHandler {
|
||||
throw new IllegalStateException("Something weird happened");
|
||||
}
|
||||
|
||||
default void setEquipment(@NotNull EntityEquipmentPacket.Slot slot, @NotNull ItemStack itemStack) {
|
||||
default void setEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
|
||||
switch (slot) {
|
||||
case MAIN_HAND:
|
||||
setItemInMainHand(itemStack);
|
||||
@ -187,7 +188,7 @@ public interface EquipmentHandler {
|
||||
*
|
||||
* @param slot the slot of the equipment
|
||||
*/
|
||||
default void syncEquipment(@NotNull EntityEquipmentPacket.Slot slot) {
|
||||
default void syncEquipment(@NotNull EquipmentSlot slot) {
|
||||
Check.stateCondition(!(this instanceof Entity), "Only accessible for Entity");
|
||||
|
||||
Entity entity = (Entity) this;
|
||||
@ -196,7 +197,7 @@ public interface EquipmentHandler {
|
||||
|
||||
EntityEquipmentPacket entityEquipmentPacket = new EntityEquipmentPacket();
|
||||
entityEquipmentPacket.entityId = entity.getEntityId();
|
||||
entityEquipmentPacket.slots = new EntityEquipmentPacket.Slot[]{slot};
|
||||
entityEquipmentPacket.slots = new EquipmentSlot[]{slot};
|
||||
entityEquipmentPacket.itemStacks = new ItemStack[]{itemStack};
|
||||
|
||||
entity.sendPacketToViewers(entityEquipmentPacket);
|
||||
@ -213,12 +214,12 @@ public interface EquipmentHandler {
|
||||
|
||||
final Entity entity = (Entity) this;
|
||||
|
||||
final EntityEquipmentPacket.Slot[] slots = EntityEquipmentPacket.Slot.values();
|
||||
final EquipmentSlot[] slots = EquipmentSlot.values();
|
||||
|
||||
List<ItemStack> itemStacks = new ArrayList<>(slots.length);
|
||||
|
||||
// Fill items
|
||||
for (EntityEquipmentPacket.Slot slot : slots) {
|
||||
for (EquipmentSlot slot : slots) {
|
||||
final ItemStack equipment = getEquipment(slot);
|
||||
itemStacks.add(equipment);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
||||
import net.minestom.server.event.item.EntityEquipEvent;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
@ -170,33 +170,27 @@ public class PlayerInventory extends AbstractInventory implements EquipmentHandl
|
||||
"The slot {0} does not exist for player", slot);
|
||||
Check.notNull(itemStack, "The ItemStack cannot be null, you can set air instead");
|
||||
|
||||
EntityEquipmentPacket.Slot equipmentSlot;
|
||||
EquipmentSlot equipmentSlot = null;
|
||||
|
||||
if (slot == player.getHeldSlot()) {
|
||||
equipmentSlot = EntityEquipmentPacket.Slot.MAIN_HAND;
|
||||
equipmentSlot = EquipmentSlot.MAIN_HAND;
|
||||
} else if (slot == OFFHAND_SLOT) {
|
||||
equipmentSlot = EntityEquipmentPacket.Slot.OFF_HAND;
|
||||
} else {
|
||||
ArmorEquipEvent armorEquipEvent = null;
|
||||
equipmentSlot = EquipmentSlot.OFF_HAND;
|
||||
} else if (slot == HELMET_SLOT) {
|
||||
equipmentSlot = EquipmentSlot.HELMET;
|
||||
} else if (slot == CHESTPLATE_SLOT) {
|
||||
equipmentSlot = EquipmentSlot.CHESTPLATE;
|
||||
} else if (slot == LEGGINGS_SLOT) {
|
||||
equipmentSlot = EquipmentSlot.LEGGINGS;
|
||||
} else if (slot == BOOTS_SLOT) {
|
||||
equipmentSlot = EquipmentSlot.BOOTS;
|
||||
}
|
||||
|
||||
if (slot == HELMET_SLOT) {
|
||||
armorEquipEvent = new ArmorEquipEvent(player, itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
||||
} else if (slot == CHESTPLATE_SLOT) {
|
||||
armorEquipEvent = new ArmorEquipEvent(player, itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
||||
} else if (slot == LEGGINGS_SLOT) {
|
||||
armorEquipEvent = new ArmorEquipEvent(player, itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
||||
} else if (slot == BOOTS_SLOT) {
|
||||
armorEquipEvent = new ArmorEquipEvent(player, itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
||||
}
|
||||
if (equipmentSlot != null) {
|
||||
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(player, itemStack, equipmentSlot);
|
||||
|
||||
if (armorEquipEvent != null) {
|
||||
ArmorEquipEvent.ArmorSlot armorSlot = armorEquipEvent.getArmorSlot();
|
||||
equipmentSlot = EntityEquipmentPacket.Slot.fromArmorSlot(armorSlot);
|
||||
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
||||
itemStack = armorEquipEvent.getArmorItem();
|
||||
} else {
|
||||
equipmentSlot = null;
|
||||
}
|
||||
player.callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||
itemStack = entityEquipEvent.getEquippedItem();
|
||||
}
|
||||
|
||||
this.itemStacks[slot] = itemStack;
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.minestom.server.inventory.type;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import net.minestom.server.network.packet.server.play.TradeListPacket;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VillagerInventory extends Inventory {
|
||||
|
||||
@ -23,7 +25,7 @@ public class VillagerInventory extends Inventory {
|
||||
final int length = oldTrades.length + 1;
|
||||
TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length];
|
||||
System.arraycopy(oldTrades, 0, trades, 0, oldTrades.length);
|
||||
trades[length] = trade;
|
||||
trades[length - 1] = trade;
|
||||
this.tradeListPacket.trades = trades;
|
||||
update();
|
||||
}
|
||||
@ -79,6 +81,15 @@ public class VillagerInventory extends Inventory {
|
||||
sendPacketToViewers(tradeListPacket); // Refresh window
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addViewer(@NotNull Player player) {
|
||||
final boolean result = super.addViewer(player);
|
||||
if (result) {
|
||||
player.getPlayerConnection().sendPacket(tradeListPacket);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setupPacket() {
|
||||
this.tradeListPacket = new TradeListPacket();
|
||||
this.tradeListPacket.windowId = getWindowId();
|
||||
|
@ -4,6 +4,8 @@ import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.utils.binary.Writeable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@ -13,8 +15,9 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ItemMeta implements Writeable {
|
||||
public class ItemMeta implements TagReadable, Writeable {
|
||||
|
||||
private final int damage;
|
||||
private final boolean unbreakable;
|
||||
@ -109,18 +112,14 @@ public class ItemMeta implements Writeable {
|
||||
return Collections.unmodifiableSet(canPlaceOn);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public <T> T getOrDefault(@NotNull ItemTag<T> tag, @Nullable T defaultValue) {
|
||||
var key = tag.getKey();
|
||||
if (nbt.containsKey(key)) {
|
||||
return tag.read(toNBT());
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
@Override
|
||||
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
|
||||
public <T> @Nullable T get(@NotNull ItemTag<T> tag) {
|
||||
return tag.read(toNBT());
|
||||
@Override
|
||||
public boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return nbt.containsKey(tag.getKey());
|
||||
}
|
||||
|
||||
public @NotNull NBTCompound toNBT() {
|
||||
@ -163,4 +162,26 @@ public class ItemMeta implements Writeable {
|
||||
writer.write(cachedBuffer);
|
||||
this.cachedBuffer.resetReaderIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #getTag(Tag)} with {@link Tag#defaultValue(Supplier)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Contract(pure = true)
|
||||
public <T> T getOrDefault(@NotNull Tag<T> tag, @Nullable T defaultValue) {
|
||||
var key = tag.getKey();
|
||||
if (nbt.containsKey(key)) {
|
||||
return tag.read(toNBT());
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #getTag(Tag)}
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> @Nullable T get(@NotNull Tag<T> tag) {
|
||||
return getTag(tag);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.adventure.AdventureSerializer;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagWritable;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@ -16,7 +18,7 @@ import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ItemMetaBuilder {
|
||||
public abstract class ItemMetaBuilder implements TagWritable {
|
||||
|
||||
protected NBTCompound nbt = new NBTCompound();
|
||||
|
||||
@ -183,12 +185,13 @@ public abstract class ItemMetaBuilder {
|
||||
return canDestroy(Set.of(blocks));
|
||||
}
|
||||
|
||||
public <T> @NotNull ItemMetaBuilder set(@NotNull ItemTag<T> tag, @Nullable T value) {
|
||||
if (value != null) {
|
||||
tag.write(nbt, value);
|
||||
} else {
|
||||
this.nbt.removeTag(tag.getKey());
|
||||
}
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbt, value);
|
||||
}
|
||||
|
||||
public <T> @NotNull ItemMetaBuilder set(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
setTag(tag, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.event.HoverEventSource;
|
||||
import net.minestom.server.item.rule.VanillaStackingRule;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -22,7 +24,7 @@ import java.util.function.UnaryOperator;
|
||||
* <p>
|
||||
* An item stack cannot be null, {@link ItemStack#AIR} should be used instead.
|
||||
*/
|
||||
public final class ItemStack implements HoverEventSource<HoverEvent.ShowItem> {
|
||||
public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent.ShowItem> {
|
||||
|
||||
/**
|
||||
* Constant AIR item. Should be used instead of 'null'.
|
||||
@ -191,6 +193,21 @@ public final class ItemStack implements HoverEventSource<HoverEvent.ShowItem> {
|
||||
.stackingRule(stackingRule);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public <T> @NotNull ItemStack withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
return builder().meta(metaBuilder -> metaBuilder.set(tag, value)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
|
||||
return meta.getTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return meta.hasTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
|
||||
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(this.material,
|
||||
|
@ -1,112 +1,65 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.tag.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ItemTag<T> {
|
||||
/**
|
||||
* @deprecated use {@link Tag}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ItemTag<T> extends Tag<T> {
|
||||
|
||||
private final String key;
|
||||
private final Function<NBTCompound, T> readFunction;
|
||||
private final BiConsumer<NBTCompound, T> writeConsumer;
|
||||
|
||||
private ItemTag(@NotNull String key,
|
||||
@NotNull Function<NBTCompound, T> readFunction,
|
||||
@NotNull BiConsumer<NBTCompound, T> writeConsumer) {
|
||||
this.key = key;
|
||||
this.readFunction = readFunction;
|
||||
this.writeConsumer = writeConsumer;
|
||||
protected ItemTag(@NotNull String key, @NotNull Function<NBTCompound, T> readFunction, @NotNull BiConsumer<NBTCompound, T> writeConsumer) {
|
||||
super(key, readFunction, writeConsumer);
|
||||
}
|
||||
|
||||
public @NotNull String getKey() {
|
||||
return key;
|
||||
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
||||
return Tag.Byte(key);
|
||||
}
|
||||
|
||||
protected T read(@NotNull NBTCompound nbtCompound) {
|
||||
return readFunction.apply(nbtCompound);
|
||||
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
||||
return Tag.Short(key);
|
||||
}
|
||||
|
||||
protected void write(@NotNull NBTCompound nbtCompound, @NotNull T value) {
|
||||
this.writeConsumer.accept(nbtCompound, value);
|
||||
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
||||
return Tag.Integer(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Byte> Byte(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getByte(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
||||
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
||||
return Tag.Long(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Short> Short(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getShort(key),
|
||||
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
||||
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
||||
return Tag.Float(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Integer> Integer(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getInt(key),
|
||||
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
||||
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
||||
return Tag.Double(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Long> Long(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getLong(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
||||
public static @NotNull Tag<byte[]> ByteArray(@NotNull String key) {
|
||||
return Tag.ByteArray(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Float> Float(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getFloat(key),
|
||||
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
||||
public static @NotNull Tag<String> String(@NotNull String key) {
|
||||
return Tag.String(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<Double> Double(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getDouble(key),
|
||||
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
||||
public static @NotNull Tag<NBT> NBT(@NotNull String key) {
|
||||
return Tag.NBT(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<byte[]> ByteArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getByteArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
||||
public static @NotNull Tag<int[]> IntArray(@NotNull String key) {
|
||||
return Tag.IntArray(key);
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<String> String(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getString(key),
|
||||
(nbtCompound, value) -> nbtCompound.setString(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<NBT> NBT(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbt -> {
|
||||
var currentNBT = nbt.get(key);
|
||||
|
||||
// Avoid a NPE when cloning a null variable.
|
||||
if (currentNBT == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return currentNBT.deepClone();
|
||||
},
|
||||
((nbt, value) -> nbt.set(key, value.deepClone())));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<int[]> IntArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getIntArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setIntArray(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull ItemTag<long[]> LongArray(@NotNull String key) {
|
||||
return new ItemTag<>(key,
|
||||
nbtCompound -> nbtCompound.getLongArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
|
||||
public static @NotNull Tag<long[]> LongArray(@NotNull String key) {
|
||||
return Tag.LongArray(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,10 +6,10 @@ import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.CommandManager;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.player.PlayerChatEvent;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.message.Messenger;
|
||||
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;
|
||||
@ -26,21 +26,30 @@ public class ChatMessageListener {
|
||||
final String cmdPrefix = CommandManager.COMMAND_PREFIX;
|
||||
if (message.startsWith(cmdPrefix)) {
|
||||
// The message is a command
|
||||
message = message.replaceFirst(cmdPrefix, "");
|
||||
final String command = message.replaceFirst(cmdPrefix, "");
|
||||
|
||||
COMMAND_MANAGER.execute(player, message);
|
||||
// check if we can receive commands
|
||||
if (Messenger.canReceiveCommand(player)) {
|
||||
COMMAND_MANAGER.execute(player, command);
|
||||
} else {
|
||||
Messenger.sendRejectionMessage(player);
|
||||
}
|
||||
|
||||
// Do not call chat event
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we can receive messages
|
||||
if (!Messenger.canReceiveMessage(player)) {
|
||||
Messenger.sendRejectionMessage(player);
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
|
||||
String finalMessage = message;
|
||||
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, finalMessage), message);
|
||||
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, message), message);
|
||||
|
||||
// Call the event
|
||||
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
|
||||
|
||||
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
|
||||
|
||||
Component textObject;
|
||||
@ -55,15 +64,10 @@ public class ChatMessageListener {
|
||||
|
||||
final Collection<Player> recipients = playerChatEvent.getRecipients();
|
||||
if (!recipients.isEmpty()) {
|
||||
// Send the message with the correct player UUID
|
||||
ChatMessagePacket chatMessagePacket =
|
||||
new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid());
|
||||
|
||||
PacketUtils.sendGroupedPacket(recipients, chatMessagePacket);
|
||||
// delegate to the messenger to avoid sending messages we shouldn't be
|
||||
Messenger.sendMessage(recipients, textObject, ChatPosition.CHAT, player.getUuid());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {
|
||||
|
@ -1,10 +1,7 @@
|
||||
package net.minestom.server.listener;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.player.PlayerStartSneakingEvent;
|
||||
import net.minestom.server.event.player.PlayerStartSprintingEvent;
|
||||
import net.minestom.server.event.player.PlayerStopSneakingEvent;
|
||||
import net.minestom.server.event.player.PlayerStopSprintingEvent;
|
||||
import net.minestom.server.event.player.*;
|
||||
import net.minestom.server.network.packet.client.play.ClientEntityActionPacket;
|
||||
|
||||
public class EntityActionListener {
|
||||
@ -24,6 +21,9 @@ public class EntityActionListener {
|
||||
case STOP_SPRINTING:
|
||||
EntityActionListener.setSprinting(player, false);
|
||||
break;
|
||||
case START_FLYING_ELYTRA:
|
||||
EntityActionListener.startFlyingElytra(player);
|
||||
break;
|
||||
// TODO do remaining actions
|
||||
}
|
||||
}
|
||||
@ -55,4 +55,9 @@ public class EntityActionListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void startFlyingElytra(Player player) {
|
||||
player.setFlyingWithElytra(true);
|
||||
player.callEvent(PlayerStartFlyingWithElytraEvent.class, new PlayerStartFlyingWithElytraEvent(player));
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ public class SettingsListener {
|
||||
|
||||
public static void listener(ClientSettingsPacket packet, Player player) {
|
||||
Player.PlayerSettings settings = player.getSettings();
|
||||
settings.refresh(packet.locale, packet.viewDistance, packet.chatMode, packet.chatColors, packet.displayedSkinParts, packet.mainHand);
|
||||
settings.refresh(packet.locale, packet.viewDistance, packet.chatMessageType, packet.chatColors, packet.displayedSkinParts, packet.mainHand);
|
||||
|
||||
PlayerSettingsChangeEvent playerSettingsChangeEvent = new PlayerSettingsChangeEvent(player);
|
||||
player.callEvent(PlayerSettingsChangeEvent.class, playerSettingsChangeEvent);
|
||||
|
@ -11,9 +11,10 @@ import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
|
||||
import net.minestom.server.network.packet.server.play.TabCompletePacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TabCompleteListener {
|
||||
|
||||
@ -23,8 +24,7 @@ public class TabCompleteListener {
|
||||
String commandString = packet.text.replaceFirst(CommandManager.COMMAND_PREFIX, "");
|
||||
String[] split = commandString.split(StringUtils.SPACE);
|
||||
String commandName = split[0];
|
||||
|
||||
String args = commandString.replaceFirst(commandName, "");
|
||||
String args = commandString.replaceFirst(Pattern.quote(commandName), "");
|
||||
|
||||
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
|
||||
if (commandQueryResult == null) {
|
||||
@ -48,7 +48,7 @@ public class TabCompleteListener {
|
||||
final int inputLength = input.length();
|
||||
|
||||
final int commandLength = Arrays.stream(split).map(String::length).reduce(0, Integer::sum) +
|
||||
StringUtils.countMatches(args, StringUtils.SPACE);
|
||||
StringUtils.countMatches(args, StringUtils.SPACE_CHAR);
|
||||
final int trailingSpaces = !input.isEmpty() ? text.length() - text.trim().length() : 0;
|
||||
|
||||
final int start = commandLength - inputLength + 1 - trailingSpaces;
|
||||
|
@ -84,7 +84,12 @@ public final class PacketListenerManager {
|
||||
|
||||
// Finally execute the listener
|
||||
if (packetListenerConsumer != null) {
|
||||
packetListenerConsumer.accept(packet, player);
|
||||
try {
|
||||
packetListenerConsumer.accept(packet, player);
|
||||
} catch (Exception e) {
|
||||
// Packet is likely invalid
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
package net.minestom.server.message;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* The messages that a player is willing to receive.
|
||||
*/
|
||||
public enum ChatMessageType {
|
||||
/**
|
||||
* The client wants all chat messages.
|
||||
*/
|
||||
FULL(EnumSet.allOf(ChatPosition.class)),
|
||||
|
||||
/**
|
||||
* The client only wants messages from commands, or system messages.
|
||||
*/
|
||||
SYSTEM(EnumSet.of(ChatPosition.SYSTEM_MESSAGE, ChatPosition.GAME_INFO)),
|
||||
|
||||
/**
|
||||
* The client doesn't want any messages.
|
||||
*/
|
||||
NONE(EnumSet.of(ChatPosition.GAME_INFO));
|
||||
|
||||
private final EnumSet<ChatPosition> acceptedPositions;
|
||||
|
||||
ChatMessageType(@NotNull EnumSet<ChatPosition> acceptedPositions) {
|
||||
this.acceptedPositions = acceptedPositions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this message type is accepting of messages from a given position.
|
||||
*
|
||||
* @param chatPosition the position
|
||||
* @return if the message is accepted
|
||||
*/
|
||||
public boolean accepts(@NotNull ChatPosition chatPosition) {
|
||||
return this.acceptedPositions.contains(chatPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet ID for this chat message type.
|
||||
*
|
||||
* @return the packet ID
|
||||
*/
|
||||
public int getPacketID() {
|
||||
return this.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a chat message type from a packet ID.
|
||||
*
|
||||
* @param id the packet ID
|
||||
* @return the chat message type
|
||||
*/
|
||||
public static @NotNull ChatMessageType fromPacketID(int id) {
|
||||
switch (id) {
|
||||
case 0: return FULL;
|
||||
case 1: return SYSTEM;
|
||||
case 2: return NONE;
|
||||
default: throw new IllegalArgumentException("id must be between 0-2 (inclusive)");
|
||||
}
|
||||
}
|
||||
}
|
79
src/main/java/net/minestom/server/message/ChatPosition.java
Normal file
79
src/main/java/net/minestom/server/message/ChatPosition.java
Normal file
@ -0,0 +1,79 @@
|
||||
package net.minestom.server.message;
|
||||
|
||||
import net.kyori.adventure.audience.MessageType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The different positions for chat messages.
|
||||
*/
|
||||
public enum ChatPosition {
|
||||
/**
|
||||
* A player-initiated chat message.
|
||||
*/
|
||||
CHAT(MessageType.CHAT),
|
||||
|
||||
/**
|
||||
* Feedback from running a command or other system messages.
|
||||
*/
|
||||
SYSTEM_MESSAGE(MessageType.SYSTEM),
|
||||
|
||||
/**
|
||||
* Game state information displayed above the hot bar.
|
||||
*/
|
||||
GAME_INFO(null);
|
||||
|
||||
private final MessageType messageType;
|
||||
|
||||
ChatPosition(@NotNull MessageType messageType) {
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Adventure message type from this position. Note that there is no
|
||||
* message type for {@link #GAME_INFO}, as Adventure uses the title methods for this.
|
||||
*
|
||||
* @return the message type, if any
|
||||
*/
|
||||
public @Nullable MessageType getMessageType() {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet ID of this chat position.
|
||||
*
|
||||
* @return the ID
|
||||
*/
|
||||
public byte getID() {
|
||||
return (byte) this.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a position from an Adventure message type.
|
||||
*
|
||||
* @param messageType the message type
|
||||
* @return the position
|
||||
*/
|
||||
public static @NotNull ChatPosition fromMessageType(@NotNull MessageType messageType) {
|
||||
switch (messageType) {
|
||||
case CHAT: return CHAT;
|
||||
case SYSTEM: return SYSTEM_MESSAGE;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot get position from message type!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a position from a packet ID.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the chat position
|
||||
*/
|
||||
public static @NotNull ChatPosition fromPacketID(byte id) {
|
||||
switch (id) {
|
||||
case 0: return CHAT;
|
||||
case 1: return SYSTEM_MESSAGE;
|
||||
case 2: return GAME_INFO;
|
||||
default: throw new IllegalArgumentException("id must be between 0-2 (inclusive)");
|
||||
}
|
||||
}
|
||||
}
|
94
src/main/java/net/minestom/server/message/Messenger.java
Normal file
94
src/main/java/net/minestom/server/message/Messenger.java
Normal file
@ -0,0 +1,94 @@
|
||||
package net.minestom.server.message;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Utility class to handle client chat settings.
|
||||
*/
|
||||
public class Messenger {
|
||||
/**
|
||||
* The message sent to the client if they send a chat message but it is rejected by the server.
|
||||
*/
|
||||
public static final Component CANNOT_SEND_MESSAGE = Component.translatable("chat.cannotSend", NamedTextColor.RED);
|
||||
|
||||
private static final ChatMessagePacket CANNOT_SEND_PACKET = new ChatMessagePacket(CANNOT_SEND_MESSAGE, ChatPosition.SYSTEM_MESSAGE, null);
|
||||
|
||||
/**
|
||||
* Sends a message to a player, respecting their chat settings.
|
||||
*
|
||||
* @param player the player
|
||||
* @param message the message
|
||||
* @param position the position
|
||||
* @param uuid the UUID of the sender, if any
|
||||
* @return if the message was sent
|
||||
*/
|
||||
public static boolean sendMessage(@NotNull Player player, @NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) {
|
||||
if (getChatMessageType(player).accepts(position)) {
|
||||
player.getPlayerConnection().sendPacket(new ChatMessagePacket(message, position, uuid));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to some players, respecting their chat settings.
|
||||
*
|
||||
* @param players the players
|
||||
* @param message the message
|
||||
* @param position the position
|
||||
* @param uuid the UUID of the sender, if any
|
||||
*/
|
||||
public static void sendMessage(@NotNull Collection<Player> players, @NotNull Component message,
|
||||
@NotNull ChatPosition position, @Nullable UUID uuid) {
|
||||
PacketUtils.sendGroupedPacket(players, new ChatMessagePacket(message, position, uuid),
|
||||
player -> getChatMessageType(player).accepts(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server should receive messages from a player, given their chat settings.
|
||||
*
|
||||
* @param player the player
|
||||
* @return if the server should receive messages from them
|
||||
*/
|
||||
public static boolean canReceiveMessage(@NotNull Player player) {
|
||||
return getChatMessageType(player) == ChatMessageType.FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server should receive commands from a player, given their chat settings.
|
||||
*
|
||||
* @param player the player
|
||||
* @return if the server should receive commands from them
|
||||
*/
|
||||
public static boolean canReceiveCommand(@NotNull Player player) {
|
||||
return getChatMessageType(player) != ChatMessageType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the player informing them we are rejecting their message or command.
|
||||
*
|
||||
* @param player the player
|
||||
*/
|
||||
public static void sendRejectionMessage(@NotNull Player player) {
|
||||
player.getPlayerConnection().sendPacket(CANNOT_SEND_PACKET, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chat message type for a player, returning {@link ChatMessageType#FULL} if not set.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the chat message type
|
||||
*/
|
||||
private static @NotNull ChatMessageType getChatMessageType(@NotNull Player player) {
|
||||
return Objects.requireNonNullElse(player.getSettings().getChatMessageType(), ChatMessageType.FULL);
|
||||
}
|
||||
}
|
@ -20,10 +20,10 @@ import net.minestom.server.network.packet.server.play.DisconnectPacket;
|
||||
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
|
||||
import net.minestom.server.network.player.NettyPlayerConnection;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import net.minestom.server.utils.callback.validator.PlayerValidator;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@ -83,7 +84,6 @@ public final class ConnectionManager {
|
||||
|
||||
/**
|
||||
* Finds the closest player matching a given username.
|
||||
* <p>
|
||||
*
|
||||
* @param username the player username (can be partial)
|
||||
* @return the closest match, null if no players are online
|
||||
@ -91,18 +91,17 @@ public final class ConnectionManager {
|
||||
public @Nullable Player findPlayer(@NotNull String username) {
|
||||
Player exact = getPlayer(username);
|
||||
if (exact != null) return exact;
|
||||
final String username1 = username.toLowerCase(Locale.ROOT);
|
||||
|
||||
String lowercase = username.toLowerCase();
|
||||
double currentDistance = 0;
|
||||
for (Player player : getOnlinePlayers()) {
|
||||
final JaroWinklerDistance jaroWinklerDistance = new JaroWinklerDistance();
|
||||
final double distance = jaroWinklerDistance.apply(lowercase, player.getUsername().toLowerCase());
|
||||
if (distance > currentDistance) {
|
||||
currentDistance = distance;
|
||||
exact = player;
|
||||
}
|
||||
}
|
||||
return exact;
|
||||
Function<Player, Double> distanceFunction = player -> {
|
||||
final String username2 = player.getUsername().toLowerCase(Locale.ROOT);
|
||||
return StringUtils.jaroWinklerScore(username1, username2);
|
||||
};
|
||||
return getOnlinePlayers()
|
||||
.stream()
|
||||
.min(Comparator.comparingDouble(distanceFunction::apply))
|
||||
.filter(player -> distanceFunction.apply(player) > 0)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,8 +187,12 @@ public final class ConnectionManager {
|
||||
* Gets all the listeners which are called for each packet received.
|
||||
*
|
||||
* @return a list of packet's consumers
|
||||
* @deprecated all packet listening methods will ultimately be removed.
|
||||
* May or may not work depending on the packet.
|
||||
* It is instead recommended to use a proxy, improving scalability and increasing server performance
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public List<ClientPacketConsumer> getReceivePacketConsumers() {
|
||||
return receiveClientPacketConsumers;
|
||||
}
|
||||
@ -198,7 +201,11 @@ public final class ConnectionManager {
|
||||
* Adds a consumer to call once a packet is received.
|
||||
*
|
||||
* @param clientPacketConsumer the packet consumer
|
||||
* @deprecated all packet listening methods will ultimately be removed.
|
||||
* May or may not work depending on the packet.
|
||||
* It is instead recommended to use a proxy, improving scalability and increasing server performance
|
||||
*/
|
||||
@Deprecated
|
||||
public void onPacketReceive(@NotNull ClientPacketConsumer clientPacketConsumer) {
|
||||
this.receiveClientPacketConsumers.add(clientPacketConsumer);
|
||||
}
|
||||
@ -207,8 +214,12 @@ public final class ConnectionManager {
|
||||
* Gets all the listeners which are called for each packet sent.
|
||||
*
|
||||
* @return a list of packet's consumers
|
||||
* @deprecated all packet listening methods will ultimately be removed.
|
||||
* May or may not work depending on the packet.
|
||||
* It is instead recommended to use a proxy, improving scalability and increasing server performance
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public List<ServerPacketConsumer> getSendPacketConsumers() {
|
||||
return sendClientPacketConsumers;
|
||||
}
|
||||
@ -217,7 +228,11 @@ public final class ConnectionManager {
|
||||
* Adds a consumer to call once a packet is sent.
|
||||
*
|
||||
* @param serverPacketConsumer the packet consumer
|
||||
* @deprecated all packet listening methods will ultimately be removed.
|
||||
* May or may not work depending on the packet.
|
||||
* It is instead recommended to use a proxy, improving scalability and increasing server performance
|
||||
*/
|
||||
@Deprecated
|
||||
public void onPacketSend(@NotNull ServerPacketConsumer serverPacketConsumer) {
|
||||
this.sendClientPacketConsumers.add(serverPacketConsumer);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.minestom.server.network.packet.client.login;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.data.type.array.ByteArrayData;
|
||||
import net.minestom.server.extras.MojangAuth;
|
||||
@ -15,11 +15,16 @@ import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EncryptionResponsePacket implements ClientPreplayPacket {
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
private byte[] sharedSecret;
|
||||
private byte[] verifyToken;
|
||||
|
||||
@ -44,7 +49,7 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
||||
MinecraftServer.LOGGER.error("{} tried to login with an invalid nonce!", loginUsername);
|
||||
return;
|
||||
}
|
||||
if (!loginUsername.isEmpty()) {
|
||||
if (loginUsername != null && !loginUsername.isEmpty()) {
|
||||
|
||||
final byte[] digestedData = MojangCrypt.digestData("", MojangAuth.getKeyPair().getPublic(), getSecretKey());
|
||||
|
||||
@ -55,14 +60,24 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
||||
return;
|
||||
}
|
||||
|
||||
final String string3 = new BigInteger(digestedData).toString(16);
|
||||
final GameProfile gameProfile = MojangAuth.getSessionService().hasJoinedServer(new GameProfile(null, loginUsername), string3);
|
||||
nettyConnection.setEncryptionKey(getSecretKey());
|
||||
// Query Mojang's sessionserver.
|
||||
final String serverId = new BigInteger(digestedData).toString(16);
|
||||
InputStream gameProfileStream = new URL(
|
||||
"https://sessionserver.mojang.com/session/minecraft/hasJoined?"
|
||||
+ "username=" + loginUsername + "&"
|
||||
+ "serverId=" + serverId
|
||||
// TODO: Add ability to add ip query tag. See: https://wiki.vg/Protocol_Encryption#Authentication
|
||||
).openStream();
|
||||
|
||||
MinecraftServer.LOGGER.info("UUID of player {} is {}", loginUsername, gameProfile.getId());
|
||||
CONNECTION_MANAGER.startPlayState(connection, gameProfile.getId(), gameProfile.getName(), true);
|
||||
final JsonObject gameProfile = GSON.fromJson(new InputStreamReader(gameProfileStream), JsonObject.class);
|
||||
nettyConnection.setEncryptionKey(getSecretKey());
|
||||
UUID profileUUID = UUID.fromString(gameProfile.get("id").getAsString().replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
|
||||
String profileName = gameProfile.get("name").getAsString();
|
||||
|
||||
MinecraftServer.LOGGER.info("UUID of player {} is {}", loginUsername, profileUUID);
|
||||
CONNECTION_MANAGER.startPlayState(connection, profileUUID, profileName, true);
|
||||
}
|
||||
} catch (AuthenticationUnavailableException e) {
|
||||
} catch (IOException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
});
|
||||
@ -80,11 +95,11 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
||||
ByteArrayData.encodeByteArray(writer, verifyToken);
|
||||
}
|
||||
|
||||
public SecretKey getSecretKey() {
|
||||
private SecretKey getSecretKey() {
|
||||
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
||||
}
|
||||
|
||||
public byte[] getNonce() {
|
||||
private byte[] getNonce() {
|
||||
return MojangAuth.getKeyPair().getPrivate() == null ?
|
||||
this.verifyToken : MojangCrypt.decryptUsingKey(MojangAuth.getKeyPair().getPrivate(), this.verifyToken);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.network.packet.client.play;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.message.ChatMessageType;
|
||||
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
@ -10,7 +11,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
|
||||
|
||||
public String locale = "";
|
||||
public byte viewDistance;
|
||||
public Player.ChatMode chatMode = Player.ChatMode.ENABLED;
|
||||
public ChatMessageType chatMessageType = ChatMessageType.FULL;
|
||||
public boolean chatColors;
|
||||
public byte displayedSkinParts;
|
||||
public Player.MainHand mainHand = Player.MainHand.RIGHT;
|
||||
@ -19,7 +20,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
this.locale = reader.readSizedString(128);
|
||||
this.viewDistance = reader.readByte();
|
||||
this.chatMode = Player.ChatMode.values()[reader.readVarInt()];
|
||||
this.chatMessageType = ChatMessageType.fromPacketID(reader.readVarInt());
|
||||
this.chatColors = reader.readBoolean();
|
||||
this.displayedSkinParts = reader.readByte();
|
||||
this.mainHand = Player.MainHand.values()[reader.readVarInt()];
|
||||
@ -31,7 +32,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
|
||||
throw new IllegalArgumentException("Locale cannot be longer than 128 characters.");
|
||||
writer.writeSizedString(locale);
|
||||
writer.writeByte(viewDistance);
|
||||
writer.writeVarInt(chatMode.ordinal());
|
||||
writer.writeVarInt(chatMessageType.getPacketID());
|
||||
writer.writeBoolean(chatColors);
|
||||
writer.writeByte(displayedSkinParts);
|
||||
writer.writeVarInt(mainHand.ordinal());
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.minestom.server.network.packet.server.play;
|
||||
|
||||
import net.kyori.adventure.audience.MessageType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@ -22,21 +23,19 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
||||
private static final UUID NULL_UUID = new UUID(0, 0);
|
||||
|
||||
public Component message;
|
||||
public Position position;
|
||||
public ChatPosition position;
|
||||
public UUID uuid;
|
||||
|
||||
public ChatMessagePacket() {
|
||||
this(Component.empty(), Position.CHAT);
|
||||
this.message = Component.empty();
|
||||
this.position = ChatPosition.SYSTEM_MESSAGE;
|
||||
this.uuid = NULL_UUID;
|
||||
}
|
||||
|
||||
public ChatMessagePacket(Component message, Position position, UUID uuid) {
|
||||
public ChatMessagePacket(@NotNull Component message, @NotNull ChatPosition position, @Nullable UUID uuid) {
|
||||
this.message = message;
|
||||
this.position = position;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public ChatMessagePacket(Component message, Position position) {
|
||||
this(message, position, NULL_UUID);
|
||||
this.uuid = Objects.requireNonNullElse(uuid, NULL_UUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,7 +48,7 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
message = reader.readComponent(Integer.MAX_VALUE);
|
||||
position = Position.values()[reader.readByte()];
|
||||
position = ChatPosition.fromPacketID(reader.readByte());
|
||||
uuid = reader.readUuid();
|
||||
}
|
||||
|
||||
@ -67,41 +66,4 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
||||
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
|
||||
return new ChatMessagePacket(operator.apply(message), position, uuid);
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
CHAT(MessageType.CHAT),
|
||||
SYSTEM_MESSAGE(MessageType.SYSTEM),
|
||||
GAME_INFO(null);
|
||||
|
||||
private final MessageType messageType;
|
||||
|
||||
Position(MessageType messageType) {
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Adventure message type from this position. Note that there is no
|
||||
* message type for {@link #GAME_INFO}, as Adventure uses the title methods for this.
|
||||
*
|
||||
* @return the message type, if any
|
||||
*/
|
||||
public @Nullable MessageType getMessageType() {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a position from an Adventure message type.
|
||||
*
|
||||
* @param messageType the message type
|
||||
*
|
||||
* @return the position
|
||||
*/
|
||||
public static @NotNull Position fromMessageType(@NotNull MessageType messageType) {
|
||||
switch (messageType) {
|
||||
case CHAT: return CHAT;
|
||||
case SYSTEM: return SYSTEM_MESSAGE;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot get position from message type!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.minestom.server.network.packet.server.play;
|
||||
|
||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
@ -14,7 +14,7 @@ import java.util.List;
|
||||
public class EntityEquipmentPacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
public Slot[] slots;
|
||||
public EquipmentSlot[] slots;
|
||||
public ItemStack[] itemStacks;
|
||||
|
||||
public EntityEquipmentPacket() {
|
||||
@ -33,7 +33,7 @@ public class EntityEquipmentPacket implements ServerPacket {
|
||||
}
|
||||
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
final Slot slot = slots[i];
|
||||
final EquipmentSlot slot = slots[i];
|
||||
final ItemStack itemStack = itemStacks[i];
|
||||
final boolean last = i == slots.length - 1;
|
||||
|
||||
@ -52,17 +52,17 @@ public class EntityEquipmentPacket implements ServerPacket {
|
||||
entityId = reader.readVarInt();
|
||||
|
||||
boolean hasRemaining = true;
|
||||
List<Slot> slots = new LinkedList<>();
|
||||
List<EquipmentSlot> slots = new LinkedList<>();
|
||||
List<ItemStack> stacks = new LinkedList<>();
|
||||
while (hasRemaining) {
|
||||
byte slotEnum = reader.readByte();
|
||||
hasRemaining = (slotEnum & 0x80) == 0x80;
|
||||
|
||||
slots.add(Slot.values()[slotEnum & 0x7F]);
|
||||
slots.add(EquipmentSlot.values()[slotEnum & 0x7F]);
|
||||
stacks.add(reader.readItemStack());
|
||||
}
|
||||
|
||||
this.slots = slots.toArray(new Slot[0]);
|
||||
this.slots = slots.toArray(new EquipmentSlot[0]);
|
||||
this.itemStacks = stacks.toArray(new ItemStack[0]);
|
||||
}
|
||||
|
||||
@ -71,29 +71,4 @@ public class EntityEquipmentPacket implements ServerPacket {
|
||||
return ServerPacketIdentifier.ENTITY_EQUIPMENT;
|
||||
}
|
||||
|
||||
public enum Slot {
|
||||
MAIN_HAND,
|
||||
OFF_HAND,
|
||||
BOOTS,
|
||||
LEGGINGS,
|
||||
CHESTPLATE,
|
||||
HELMET;
|
||||
|
||||
@NotNull
|
||||
public static Slot fromArmorSlot(ArmorEquipEvent.ArmorSlot armorSlot) {
|
||||
switch (armorSlot) {
|
||||
case HELMET:
|
||||
return HELMET;
|
||||
case CHESTPLATE:
|
||||
return CHESTPLATE;
|
||||
case LEGGINGS:
|
||||
return LEGGINGS;
|
||||
case BOOTS:
|
||||
return BOOTS;
|
||||
}
|
||||
throw new IllegalStateException("Something weird happened");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
import net.minestom.server.utils.TickUtils;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -32,13 +32,13 @@ public class TitlePacket implements ComponentHoldingServerPacket {
|
||||
/**
|
||||
* Constructs a new title packet from an action that can take a component argument.
|
||||
*
|
||||
* @param action the action
|
||||
* @param action the action
|
||||
* @param payload the payload
|
||||
* @throws IllegalArgumentException if the action is not {@link Action#SET_TITLE},
|
||||
* {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
|
||||
* {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
|
||||
*/
|
||||
public TitlePacket(@NotNull Action action, @NotNull Component payload) {
|
||||
Validate.isTrue(action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR, "Invalid action type");
|
||||
Check.argCondition(action != SET_TITLE && action != SET_SUBTITLE && action != SET_ACTION_BAR, "Invalid action type");
|
||||
this.action = action;
|
||||
this.payload = payload;
|
||||
}
|
||||
@ -48,7 +48,7 @@ public class TitlePacket implements ComponentHoldingServerPacket {
|
||||
*
|
||||
* @param action the action
|
||||
* @throws IllegalArgumentException if the action is not {@link Action#RESET},
|
||||
* or {@link Action#HIDE}
|
||||
* or {@link Action#HIDE}
|
||||
*/
|
||||
public TitlePacket(@NotNull Action action) {
|
||||
this.action = action;
|
||||
@ -57,8 +57,8 @@ public class TitlePacket implements ComponentHoldingServerPacket {
|
||||
/**
|
||||
* Constructs a new title packet for {@link Action#SET_TIMES_AND_DISPLAY}.
|
||||
*
|
||||
* @param fadeIn the fade in time
|
||||
* @param stay the stay time
|
||||
* @param fadeIn the fade in time
|
||||
* @param stay the stay time
|
||||
* @param fadeOut the fade out time
|
||||
*/
|
||||
public TitlePacket(int fadeIn, int stay, int fadeOut) {
|
||||
|
@ -95,7 +95,7 @@ public abstract class PlayerConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the packet and send it to the client, skipping the translation phase.
|
||||
* Serializes the packet and send it to the client, optionally skipping the translation phase.
|
||||
* <p>
|
||||
* Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers.
|
||||
*
|
||||
|
@ -5,16 +5,27 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Responsible for making sure Minestom has the necessary files to run (notably registry files)
|
||||
@ -177,13 +188,19 @@ public class ResourceGatherer {
|
||||
}
|
||||
// Verify checksum
|
||||
try (FileInputStream fis = new FileInputStream(target)) {
|
||||
String sha1Target = DigestUtils.sha1Hex(fis);
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
|
||||
messageDigest.reset();
|
||||
// This just converts the sha1 back into a readable string.
|
||||
String sha1Target = new BigInteger(1, messageDigest.digest(fis.readAllBytes())).toString(16);
|
||||
if (!sha1Target.equals(sha1Source)) {
|
||||
LOGGER.debug("The checksum test failed after downloading the Minecraft server jar.");
|
||||
LOGGER.debug("The expected checksum was: {}.", sha1Source);
|
||||
LOGGER.debug("The calculated checksum was: {}.", sha1Target);
|
||||
LOGGER.error("The checksum test failed after downloading the Minecraft server jar.");
|
||||
LOGGER.error("The expected checksum was: {}.", sha1Source);
|
||||
LOGGER.error("The calculated checksum was: {}.", sha1Target);
|
||||
throw new IOException("Failed to download Minecraft server jar.");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error("Failed to find SHA-1 hashing algorithm in Java Environment.");
|
||||
throw new IOException("Failed to download Minecraft server jar.");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
197
src/main/java/net/minestom/server/tag/Tag.java
Normal file
197
src/main/java/net/minestom/server/tag/Tag.java
Normal file
@ -0,0 +1,197 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a key to retrieve or change a value.
|
||||
* <p>
|
||||
* All tags are serializable.
|
||||
*
|
||||
* @param <T> the tag type
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public class Tag<T> {
|
||||
|
||||
private final String key;
|
||||
private final Function<NBTCompound, T> readFunction;
|
||||
private final BiConsumer<NBTCompound, T> writeConsumer;
|
||||
|
||||
private final Supplier<T> defaultValue;
|
||||
|
||||
protected Tag(@NotNull String key,
|
||||
@NotNull Function<NBTCompound, T> readFunction,
|
||||
@NotNull BiConsumer<NBTCompound, T> writeConsumer,
|
||||
@Nullable Supplier<T> defaultValue) {
|
||||
this.key = key;
|
||||
this.readFunction = readFunction;
|
||||
this.writeConsumer = writeConsumer;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
protected Tag(@NotNull String key,
|
||||
@NotNull Function<NBTCompound, T> readFunction,
|
||||
@NotNull BiConsumer<NBTCompound, T> writeConsumer) {
|
||||
this(key, readFunction, writeConsumer, null);
|
||||
}
|
||||
|
||||
public @NotNull String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public Tag<T> defaultValue(@NotNull Supplier<T> defaultValue) {
|
||||
return new Tag<>(key, readFunction, writeConsumer, defaultValue);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public Tag<T> defaultValue(@NotNull T defaultValue) {
|
||||
return defaultValue(() -> defaultValue);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public <R> Tag<R> map(@NotNull Function<T, R> readMap,
|
||||
@NotNull Function<R, T> writeMap) {
|
||||
return new Tag<R>(key,
|
||||
// Read
|
||||
nbtCompound -> {
|
||||
final var old = readFunction.apply(nbtCompound);
|
||||
if (old == null) {
|
||||
return null;
|
||||
}
|
||||
return readMap.apply(old);
|
||||
},
|
||||
// Write
|
||||
(nbtCompound, r) -> {
|
||||
var n = writeMap.apply(r);
|
||||
writeConsumer.accept(nbtCompound, n);
|
||||
},
|
||||
// Default value
|
||||
() -> {
|
||||
if (defaultValue == null) {
|
||||
return null;
|
||||
}
|
||||
var old = defaultValue.get();
|
||||
return readMap.apply(old);
|
||||
});
|
||||
}
|
||||
|
||||
public @Nullable T read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey(key)) {
|
||||
return readFunction.apply(nbtCompound);
|
||||
} else {
|
||||
final var supplier = defaultValue;
|
||||
return supplier != null ? supplier.get() : null;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(@NotNull NBTCompound nbtCompound, @Nullable T value) {
|
||||
if (value != null) {
|
||||
this.writeConsumer.accept(nbtCompound, value);
|
||||
} else {
|
||||
nbtCompound.removeTag(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getByte(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getShort(key),
|
||||
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getInt(key),
|
||||
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getLong(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getFloat(key),
|
||||
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getDouble(key),
|
||||
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<byte[]> ByteArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getByteArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<String> String(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getString(key),
|
||||
(nbtCompound, value) -> nbtCompound.setString(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<NBT> NBT(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbt -> {
|
||||
var currentNBT = nbt.get(key);
|
||||
|
||||
// Avoid a NPE when cloning a null variable.
|
||||
if (currentNBT == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return currentNBT.deepClone();
|
||||
},
|
||||
((nbt, value) -> nbt.set(key, value.deepClone())));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<int[]> IntArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getIntArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setIntArray(key, value));
|
||||
}
|
||||
|
||||
public static @NotNull Tag<long[]> LongArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getLongArray(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
|
||||
}
|
||||
|
||||
public static <T> @NotNull Tag<T> Custom(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> {
|
||||
final var compound = nbtCompound.getCompound(key);
|
||||
if (compound == null) {
|
||||
return null;
|
||||
}
|
||||
return serializer.read(TagReadable.fromCompound(compound));
|
||||
},
|
||||
(nbtCompound, value) -> {
|
||||
var compound = nbtCompound.getCompound(key);
|
||||
if (compound == null) {
|
||||
compound = new NBTCompound();
|
||||
nbtCompound.set(key, compound);
|
||||
}
|
||||
serializer.write(TagWritable.fromCompound(compound), value);
|
||||
});
|
||||
}
|
||||
}
|
10
src/main/java/net/minestom/server/tag/TagHandler.java
Normal file
10
src/main/java/net/minestom/server/tag/TagHandler.java
Normal file
@ -0,0 +1,10 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
/**
|
||||
* Represents an element which can read and write {@link Tag tags}.
|
||||
*/
|
||||
@Beta
|
||||
public interface TagHandler extends TagReadable, TagWritable {
|
||||
}
|
48
src/main/java/net/minestom/server/tag/TagReadable.java
Normal file
48
src/main/java/net/minestom/server/tag/TagReadable.java
Normal file
@ -0,0 +1,48 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
/**
|
||||
* Represents an element which can read {@link Tag tags}.
|
||||
*/
|
||||
public interface TagReadable {
|
||||
|
||||
/**
|
||||
* Reads the specified tag.
|
||||
*
|
||||
* @param tag the tag to read
|
||||
* @param <T> the tag type
|
||||
* @return the read tag, null if not present
|
||||
*/
|
||||
<T> @Nullable T getTag(@NotNull Tag<T> tag);
|
||||
|
||||
/**
|
||||
* Returns if a tag is present.
|
||||
*
|
||||
* @param tag the tag to check
|
||||
* @return true if the tag is present, false otherwise
|
||||
*/
|
||||
boolean hasTag(@NotNull Tag<?> tag);
|
||||
|
||||
/**
|
||||
* Converts an nbt compound to a tag reader.
|
||||
*
|
||||
* @param compound the compound to convert
|
||||
* @return a {@link TagReadable} capable of reading {@code compound}
|
||||
*/
|
||||
static @NotNull TagReadable fromCompound(@NotNull NBTCompound compound) {
|
||||
return new TagReadable() {
|
||||
@Override
|
||||
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return compound.containsKey(tag.getKey());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
28
src/main/java/net/minestom/server/tag/TagSerializer.java
Normal file
28
src/main/java/net/minestom/server/tag/TagSerializer.java
Normal file
@ -0,0 +1,28 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface used to create custom types compatible with {@link Tag#Custom(String, TagSerializer)}.
|
||||
*
|
||||
* @param <T> the type to serialize
|
||||
*/
|
||||
public interface TagSerializer<T> {
|
||||
|
||||
/**
|
||||
* Reads the custom tag from a {@link TagReadable}.
|
||||
*
|
||||
* @param reader the reader
|
||||
* @return the deserialized value
|
||||
*/
|
||||
@Nullable T read(@NotNull TagReadable reader);
|
||||
|
||||
/**
|
||||
* Writes the custom tag to a {@link TagWritable}.
|
||||
*
|
||||
* @param writer the writer
|
||||
* @param value the value to serialize
|
||||
*/
|
||||
void write(@NotNull TagWritable writer, @NotNull T value);
|
||||
}
|
35
src/main/java/net/minestom/server/tag/TagWritable.java
Normal file
35
src/main/java/net/minestom/server/tag/TagWritable.java
Normal file
@ -0,0 +1,35 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
/**
|
||||
* Represents an element which can write {@link Tag tags}.
|
||||
*/
|
||||
public interface TagWritable {
|
||||
|
||||
/**
|
||||
* Writes the specified type.
|
||||
*
|
||||
* @param tag the tag to write
|
||||
* @param value the tag value, null to remove
|
||||
* @param <T> the tag type
|
||||
*/
|
||||
<T> void setTag(@NotNull Tag<T> tag, @Nullable T value);
|
||||
|
||||
/**
|
||||
* Converts an nbt compound to a tag writer.
|
||||
*
|
||||
* @param compound the compound to convert
|
||||
* @return a {@link TagWritable} capable of writing {@code compound}
|
||||
*/
|
||||
static @NotNull TagWritable fromCompound(@NotNull NBTCompound compound) {
|
||||
return new TagWritable() {
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(compound, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
161
src/main/java/net/minestom/server/utils/StringUtils.java
Normal file
161
src/main/java/net/minestom/server/utils/StringUtils.java
Normal file
@ -0,0 +1,161 @@
|
||||
package net.minestom.server.utils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
public static final String SPACE = " ";
|
||||
public static final char SPACE_CHAR = ' ';
|
||||
|
||||
public static int countMatches(@NotNull final CharSequence str, final char ch) {
|
||||
if (str.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
// We could also call str.toCharArray() for faster look ups but that would generate more garbage.
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
if (ch == str.charAt(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Jaro-Winkler distance algorithm to the given strings, providing information about the
|
||||
* similarity of them.
|
||||
*
|
||||
* @param s1 The first string that gets compared. May be null or empty.
|
||||
* @param s2 The second string that gets compared. May be null or empty.
|
||||
* @return The Jaro-Winkler score (between 0.0 and 1.0), with a higher value indicating larger similarity.
|
||||
* @author Thomas Trojer thomas@trojer.net
|
||||
*/
|
||||
public static double jaroWinklerScore(final String s1, final String s2) {
|
||||
// lowest score on empty strings
|
||||
if (s1 == null || s2 == null || s1.isEmpty() || s2.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
// highest score on equal strings
|
||||
if (s1.equals(s2)) {
|
||||
return 1;
|
||||
}
|
||||
// some score on different strings
|
||||
int prefixMatch = 0; // exact prefix matches
|
||||
int matches = 0; // matches (including prefix and ones requiring transpostion)
|
||||
int transpositions = 0; // matching characters that are not aligned but close together
|
||||
int maxLength = Math.max(s1.length(), s2.length());
|
||||
int maxMatchDistance = Math.max((int) Math.floor(maxLength / 2.0) - 1, 0); // look-ahead/-behind to limit transposed matches
|
||||
// comparison
|
||||
final String shorter = s1.length() < s2.length() ? s1 : s2;
|
||||
final String longer = s1.length() >= s2.length() ? s1 : s2;
|
||||
for (int i = 0; i < shorter.length(); i++) {
|
||||
// check for exact matches
|
||||
boolean match = shorter.charAt(i) == longer.charAt(i);
|
||||
if (match) {
|
||||
if (i < 4) {
|
||||
// prefix match (of at most 4 characters, as described by the algorithm)
|
||||
prefixMatch++;
|
||||
}
|
||||
matches++;
|
||||
continue;
|
||||
}
|
||||
// check fro transposed matches
|
||||
for (int j = Math.max(i - maxMatchDistance, 0); j < Math.min(i + maxMatchDistance, longer.length()); j++) {
|
||||
if (i == j) {
|
||||
// case already covered
|
||||
continue;
|
||||
}
|
||||
// transposition required to match?
|
||||
match = shorter.charAt(i) == longer.charAt(j);
|
||||
if (match) {
|
||||
transpositions++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// any matching characters?
|
||||
if (matches == 0) {
|
||||
return 0;
|
||||
}
|
||||
// modify transpositions (according to the algorithm)
|
||||
transpositions = (int) (transpositions / 2.0);
|
||||
// non prefix-boosted score
|
||||
double score = 0.3334 * (matches / (double) longer.length() + matches / (double) shorter.length() + (matches - transpositions)
|
||||
/ (double) matches);
|
||||
if (score < 0.7) {
|
||||
return score;
|
||||
}
|
||||
// we already have a good match, hence we boost the score proportional to the common prefix
|
||||
return score + prefixMatch * 0.1 * (1.0 - score);
|
||||
}
|
||||
|
||||
public static String unescapeJavaString(String st) {
|
||||
StringBuilder sb = new StringBuilder(st.length());
|
||||
|
||||
for (int i = 0; i < st.length(); i++) {
|
||||
char ch = st.charAt(i);
|
||||
if (ch == '\\') {
|
||||
char nextChar = (i == st.length() - 1) ? '\\' : st
|
||||
.charAt(i + 1);
|
||||
// Octal escape?
|
||||
if (nextChar >= '0' && nextChar <= '7') {
|
||||
String code = "" + nextChar;
|
||||
i++;
|
||||
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
|
||||
&& st.charAt(i + 1) <= '7') {
|
||||
code += st.charAt(i + 1);
|
||||
i++;
|
||||
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
|
||||
&& st.charAt(i + 1) <= '7') {
|
||||
code += st.charAt(i + 1);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
sb.append((char) Integer.parseInt(code, 8));
|
||||
continue;
|
||||
}
|
||||
switch (nextChar) {
|
||||
case '\\':
|
||||
ch = '\\';
|
||||
break;
|
||||
case 'b':
|
||||
ch = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
ch = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
ch = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
ch = '\r';
|
||||
break;
|
||||
case 't':
|
||||
ch = '\t';
|
||||
break;
|
||||
case '\"':
|
||||
ch = '\"';
|
||||
break;
|
||||
case '\'':
|
||||
ch = '\'';
|
||||
break;
|
||||
// Hex Unicode: u????
|
||||
case 'u':
|
||||
if (i >= st.length() - 5) {
|
||||
ch = 'u';
|
||||
break;
|
||||
}
|
||||
int code = Integer.parseInt(
|
||||
"" + st.charAt(i + 2) + st.charAt(i + 3)
|
||||
+ st.charAt(i + 4) + st.charAt(i + 5), 16);
|
||||
sb.append(Character.toChars(code));
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
sb.append(ch);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package net.minestom.server.utils;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Duration;
|
||||
@ -22,6 +22,7 @@ public class TickUtils {
|
||||
|
||||
/**
|
||||
* Creates a number of ticks from a given duration, based on {@link MinecraftServer#TICK_MS}.
|
||||
*
|
||||
* @param duration the duration
|
||||
* @return the number of ticks
|
||||
* @throws IllegalArgumentException if duration is negative
|
||||
@ -32,14 +33,14 @@ public class TickUtils {
|
||||
|
||||
/**
|
||||
* Creates a number of ticks from a given duration.
|
||||
* @param duration the duration
|
||||
*
|
||||
* @param duration the duration
|
||||
* @param msPerTick the number of milliseconds per tick
|
||||
* @return the number of ticks
|
||||
* @throws IllegalArgumentException if duration is negative
|
||||
*/
|
||||
public static int fromDuration(@NotNull Duration duration, int msPerTick) {
|
||||
Validate.isTrue(!duration.isNegative(), "Duration cannot be negative");
|
||||
|
||||
Check.argCondition(duration.isNegative(), "Duration cannot be negative");
|
||||
return (int) (duration.toMillis() / msPerTick);
|
||||
}
|
||||
}
|
||||
|
@ -118,16 +118,11 @@ public final class Utils {
|
||||
}
|
||||
|
||||
public static void writeSectionBlocks(ByteBuf buffer, Section section) {
|
||||
/*short count = 0;
|
||||
for (short id : blocksId)
|
||||
if (id != 0)
|
||||
count++;*/
|
||||
|
||||
final short blockCount = section.getBlockCount();
|
||||
final int bitsPerEntry = section.getBitsPerEntry();
|
||||
|
||||
//buffer.writeShort(count);
|
||||
// TODO count blocks
|
||||
buffer.writeShort(200);
|
||||
buffer.writeShort(blockCount);
|
||||
buffer.writeByte((byte) bitsPerEntry);
|
||||
|
||||
// Palette
|
||||
|
@ -26,7 +26,6 @@ public class TemporaryCache<T> {
|
||||
public TemporaryCache(long duration, TimeUnit timeUnit, RemovalListener<UUID, T> removalListener) {
|
||||
this.cache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(duration, timeUnit)
|
||||
.softValues()
|
||||
.removalListener(removalListener)
|
||||
.build();
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ public final class MojangUtils {
|
||||
.softValues()
|
||||
.build();
|
||||
|
||||
|
||||
@Nullable
|
||||
public static JsonObject fromUuid(@NotNull String uuid) {
|
||||
|
||||
|
@ -1,24 +1,32 @@
|
||||
package demo.commands;
|
||||
|
||||
import net.kyori.adventure.audience.MessageType;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.ArgumentComponent;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.ArgumentUUID;
|
||||
|
||||
public class EchoCommand extends Command {
|
||||
public EchoCommand() {
|
||||
super("echo");
|
||||
|
||||
this.setDefaultExecutor((sender, context) -> sender.sendMessage(
|
||||
Component.text("Usage: /echo <json>")
|
||||
Component.text("Usage: /echo <json> [uuid]")
|
||||
.hoverEvent(Component.text("Click to get this command.")
|
||||
.clickEvent(ClickEvent.suggestCommand("/echo ")))));
|
||||
|
||||
ArgumentComponent json = ArgumentType.Component("json");
|
||||
ArgumentUUID uuid = ArgumentType.UUID("uuid");
|
||||
|
||||
this.addSyntax((sender, context) -> {
|
||||
sender.sendMessage(context.get(json));
|
||||
}, json);
|
||||
|
||||
this.addSyntax((sender, context) -> {
|
||||
sender.sendMessage(Identity.identity(context.get(uuid)), context.get(json), MessageType.CHAT);
|
||||
}, uuid, json);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package readwritepackets;
|
||||
|
||||
import com.google.common.reflect.ClassPath;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.client.ClientPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
@ -67,7 +68,7 @@ public class ReadWritePackets {
|
||||
// requires at least one slot and one item
|
||||
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
||||
p.itemStacks = new ItemStack[]{ItemStack.AIR};
|
||||
p.slots = new EntityEquipmentPacket.Slot[]{EntityEquipmentPacket.Slot.MAIN_HAND};
|
||||
p.slots = new EquipmentSlot[]{EquipmentSlot.MAIN_HAND};
|
||||
packet = (T) p;
|
||||
} else {
|
||||
packet = (T) constructor.newInstance();
|
||||
|
Loading…
Reference in New Issue
Block a user