mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-23 16:41:35 +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
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.11
|
java-version: 11
|
||||||
- name: Run java checkstyle
|
- name: Run java checkstyle
|
||||||
uses: nikitasavinov/checkstyle-action@d87d526a914fc5cb0b003908e35038dbb2d6e1b7
|
uses: nikitasavinov/checkstyle-action@0.3.1
|
||||||
with:
|
with:
|
||||||
# Report level for reviewdog [info,warning,error]
|
# Report level for reviewdog [info,warning,error]
|
||||||
level: info
|
level: info
|
||||||
@ -28,4 +28,4 @@ jobs:
|
|||||||
fail_on_error: false
|
fail_on_error: false
|
||||||
# Checkstyle config file
|
# Checkstyle config file
|
||||||
checkstyle_config: minestom_checks.xml
|
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
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.11
|
java-version: 11
|
||||||
- name: Build javadoc
|
- name: Build javadoc
|
||||||
run: gradle javadoc
|
run: gradle javadoc
|
||||||
|
|
||||||
@ -25,3 +25,4 @@ jobs:
|
|||||||
BRANCH: javadoc
|
BRANCH: javadoc
|
||||||
FOLDER: docs
|
FOLDER: docs
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.11
|
java-version: 11
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
- name: Setup gradle cache
|
- name: Setup gradle cache
|
||||||
|
23
build.gradle
23
build.gradle
@ -2,9 +2,7 @@ import org.gradle.internal.os.OperatingSystem
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
id 'java'
|
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'net.ltgt.apt' version '0.10'
|
|
||||||
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
|
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
|
||||||
id 'checkstyle'
|
id 'checkstyle'
|
||||||
}
|
}
|
||||||
@ -12,7 +10,7 @@ plugins {
|
|||||||
group 'net.minestom.server'
|
group 'net.minestom.server'
|
||||||
version '1.0'
|
version '1.0'
|
||||||
|
|
||||||
sourceCompatibility = 1.11
|
sourceCompatibility = 11
|
||||||
project.ext.lwjglVersion = "3.2.3"
|
project.ext.lwjglVersion = "3.2.3"
|
||||||
|
|
||||||
switch (OperatingSystem.current()) {
|
switch (OperatingSystem.current()) {
|
||||||
@ -33,7 +31,6 @@ switch (OperatingSystem.current()) {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'https://libraries.minecraft.net' }
|
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
maven {
|
maven {
|
||||||
name 'sponge'
|
name 'sponge'
|
||||||
@ -54,7 +51,7 @@ allprojects {
|
|||||||
// see https://stackoverflow.com/a/56641766
|
// see https://stackoverflow.com/a/56641766
|
||||||
doLast {
|
doLast {
|
||||||
// Append the fix to the file
|
// 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' +
|
searchScript.append '\n\n' +
|
||||||
'getURLPrefix = function(ui) {\n' +
|
'getURLPrefix = function(ui) {\n' +
|
||||||
' return \'\';\n' +
|
' return \'\';\n' +
|
||||||
@ -63,7 +60,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
toolVersion "8.37"
|
toolVersion "8.42"
|
||||||
configFile file("${projectDir}/minestom_checks.xml")
|
configFile file("${projectDir}/minestom_checks.xml")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,6 +112,10 @@ test {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(Zip).configureEach {
|
||||||
|
duplicatesStrategy DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Junit Testing Framework
|
// Junit Testing Framework
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
|
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:netty-transport-native-kqueue:4.1.63.Final:osx-x86_64'
|
||||||
api 'io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.5.Final:linux-x86_64'
|
api 'io.netty.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
|
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
|
||||||
api 'it.unimi.dsi:fastutil:8.5.4'
|
api 'it.unimi.dsi:fastutil:8.5.4'
|
||||||
|
|
||||||
@ -154,9 +152,12 @@ dependencies {
|
|||||||
// https://mvnrepository.com/artifact/org.jline/jline
|
// https://mvnrepository.com/artifact/org.jline/jline
|
||||||
implementation group: 'org.jline', name: 'jline', version: '3.20.0'
|
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.google.guava:guava:30.1-jre'
|
||||||
api 'com.mojang:authlib:1.5.21'
|
|
||||||
|
|
||||||
// Code modification
|
// Code modification
|
||||||
api "org.ow2.asm:asm:${asmVersion}"
|
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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -277,7 +277,7 @@
|
|||||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="JavadocMethod">
|
<module name="JavadocMethod">
|
||||||
<property name="scope" value="public"/>
|
<property name="accessModifiers" value="public"/>
|
||||||
<property name="allowMissingParamTags" value="true"/>
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
<property name="allowMissingReturnTag" value="true"/>
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
<property name="allowedAnnotations" value="Override, Test"/>
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
@ -826,6 +826,6 @@ public final class MinecraftServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getThreadCount(@NotNull String property, int count) {
|
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.MinecraftServer;
|
||||||
import net.minestom.server.adventure.AdventurePacketConvertor;
|
import net.minestom.server.adventure.AdventurePacketConvertor;
|
||||||
import net.minestom.server.entity.Player;
|
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.PlayerListHeaderAndFooterPacket;
|
||||||
import net.minestom.server.network.packet.server.play.TitlePacket;
|
import net.minestom.server.network.packet.server.play.TitlePacket;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketUtils;
|
||||||
@ -46,7 +47,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
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
|
@Override
|
||||||
|
@ -2,7 +2,8 @@ package net.minestom.server.color;
|
|||||||
|
|
||||||
import net.kyori.adventure.util.RGBLike;
|
import net.kyori.adventure.util.RGBLike;
|
||||||
import net.minestom.server.chat.ChatColor;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -38,17 +39,15 @@ public class Color implements RGBLike {
|
|||||||
/**
|
/**
|
||||||
* Creates a color from red, green, and blue components.
|
* 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 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)
|
* @throws IllegalArgumentException if any component value is not between 0-255 (inclusive)
|
||||||
*/
|
*/
|
||||||
public Color(int red, int green, int blue) {
|
public Color(int red, int green, int blue) {
|
||||||
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);
|
||||||
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);
|
||||||
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.red = red;
|
this.red = red;
|
||||||
this.green = green;
|
this.green = green;
|
||||||
this.blue = blue;
|
this.blue = blue;
|
||||||
@ -69,7 +68,7 @@ public class Color implements RGBLike {
|
|||||||
* @param red the red component, from 0 to 255
|
* @param red the red component, from 0 to 255
|
||||||
*/
|
*/
|
||||||
public void setRed(int red) {
|
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;
|
this.red = red;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ public class Color implements RGBLike {
|
|||||||
* @param green the red component, from 0 to 255
|
* @param green the red component, from 0 to 255
|
||||||
*/
|
*/
|
||||||
public void setGreen(int green) {
|
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;
|
this.green = green;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +106,7 @@ public class Color implements RGBLike {
|
|||||||
* @param blue the red component, from 0 to 255
|
* @param blue the red component, from 0 to 255
|
||||||
*/
|
*/
|
||||||
public void setBlue(int blue) {
|
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;
|
this.blue = blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +131,6 @@ public class Color implements RGBLike {
|
|||||||
* @param colors the colors
|
* @param colors the colors
|
||||||
*/
|
*/
|
||||||
public void mixWith(@NotNull RGBLike... colors) {
|
public void mixWith(@NotNull RGBLike... colors) {
|
||||||
Validate.noNullElements(colors, "Colors cannot be null");
|
|
||||||
|
|
||||||
// store the current highest component
|
// store the current highest component
|
||||||
int max = Math.max(Math.max(this.red, this.green), this.blue);
|
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.*;
|
||||||
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.minestom.server.command.builder;
|
package net.minestom.server.command.builder;
|
||||||
|
|
||||||
import net.minestom.server.command.builder.arguments.Argument;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -44,10 +44,16 @@ public class CommandContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> T get(@NotNull String identifier) {
|
public <T> T get(@NotNull String identifier) {
|
||||||
return (T) args.computeIfAbsent(identifier, s -> {
|
return (T) args.get(identifier);
|
||||||
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.");
|
|
||||||
});
|
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) {
|
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.CommandQueryResult;
|
||||||
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
|
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
|
||||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.arguments.Argument;
|
||||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||||
import net.minestom.server.entity.Player;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.minestom.server.command.builder;
|
package net.minestom.server.command.builder;
|
||||||
|
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.command.CommandSender;
|
import net.minestom.server.command.CommandSender;
|
||||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
@ -54,11 +55,19 @@ public class ParsedCommand {
|
|||||||
final CommandCondition commandCondition = syntax.getCommandCondition();
|
final CommandCondition commandCondition = syntax.getCommandCondition();
|
||||||
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
|
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
|
||||||
context.retrieveDefaultValues(syntax.getDefaultValuesMap());
|
context.retrieveDefaultValues(syntax.getDefaultValuesMap());
|
||||||
executor.apply(source, context);
|
try {
|
||||||
|
executor.apply(source, context);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
MinecraftServer.getExceptionManager().handleException(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The executor is probably the default one
|
// 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) {
|
} else if (callback != null && argumentSyntaxException != null) {
|
||||||
// No syntax has been validated but the faulty argument with a callback has been found
|
// 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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default value supplier of the argument.
|
||||||
|
*
|
||||||
|
* @param defaultValue the default argument value
|
||||||
|
* @return 'this' for chaining
|
||||||
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public Argument<T> setDefaultValue(@Nullable T defaultValue) {
|
public Argument<T> setDefaultValue(@NotNull T defaultValue) {
|
||||||
this.defaultValue = () -> defaultValue;
|
this.defaultValue = () -> defaultValue;
|
||||||
return this;
|
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
|
@Nullable
|
||||||
public SuggestionCallback getSuggestionCallback() {
|
public SuggestionCallback getSuggestionCallback() {
|
||||||
return suggestionCallback;
|
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
|
@Beta
|
||||||
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
|
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
|
||||||
this.suggestionCallback = suggestionCallback;
|
this.suggestionCallback = suggestionCallback;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the argument has a suggestion.
|
||||||
|
*
|
||||||
|
* @return If this argument has a suggestion.
|
||||||
|
*/
|
||||||
public boolean hasSuggestion() {
|
public boolean hasSuggestion() {
|
||||||
return suggestionCallback != null;
|
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.NodeMaker;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class ArgumentCommand extends Argument<CommandResult> {
|
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.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
import net.minestom.server.utils.callback.validator.StringArrayValidator;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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.binary.BinaryWriter;
|
||||||
import net.minestom.server.utils.callback.validator.StringValidator;
|
import net.minestom.server.utils.callback.validator.StringValidator;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.command.builder.parser.CommandParser;
|
import net.minestom.server.command.builder.parser.CommandParser;
|
||||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
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.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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.NodeMaker;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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.NodeMaker;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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.binary.BinaryWriter;
|
||||||
import org.apache.commons.text.StringEscapeUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,7 +75,7 @@ public class ArgumentString extends Argument<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return StringEscapeUtils.unescapeJava(input);
|
return StringUtils.unescapeJavaString(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,8 +2,8 @@ package net.minestom.server.command.builder.arguments;
|
|||||||
|
|
||||||
import net.minestom.server.command.builder.NodeMaker;
|
import net.minestom.server.command.builder.NodeMaker;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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.binary.BinaryWriter;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
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.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
import net.minestom.server.utils.validate.Check;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.binary.BinaryWriter;
|
||||||
import net.minestom.server.utils.entity.EntityFinder;
|
import net.minestom.server.utils.entity.EntityFinder;
|
||||||
import net.minestom.server.utils.math.IntRange;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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.arguments.Argument;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class ArgumentResourceLocation extends Argument<String> {
|
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.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
import net.minestom.server.utils.location.RelativeBlockPosition;
|
import net.minestom.server.utils.location.RelativeBlockPosition;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import net.minestom.server.utils.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import net.minestom.server.utils.Vector;
|
import net.minestom.server.utils.Vector;
|
||||||
import net.minestom.server.utils.location.RelativeVec;
|
import net.minestom.server.utils.location.RelativeVec;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import net.minestom.server.utils.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import net.minestom.server.utils.Vector;
|
import net.minestom.server.utils.Vector;
|
||||||
import net.minestom.server.utils.location.RelativeVec;
|
import net.minestom.server.utils.location.RelativeVec;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import net.minestom.server.utils.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.ArgumentRelativeVec2;
|
||||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
|
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.CommandContext;
|
||||||
import net.minestom.server.command.builder.CommandSyntax;
|
import net.minestom.server.command.builder.CommandSyntax;
|
||||||
import net.minestom.server.command.builder.arguments.Argument;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
|||||||
final ChunkCallback endCallback = (chunk) -> {
|
final ChunkCallback endCallback = (chunk) -> {
|
||||||
refreshPosition(teleportPosition);
|
refreshPosition(teleportPosition);
|
||||||
|
|
||||||
synchronizePosition();
|
synchronizePosition(true);
|
||||||
|
|
||||||
OptionalCallback.execute(callback);
|
OptionalCallback.execute(callback);
|
||||||
};
|
};
|
||||||
@ -552,7 +552,10 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
|||||||
|
|
||||||
// Apply the position if changed
|
// Apply the position if changed
|
||||||
if (!finalVelocityPosition.isSimilar(position)) {
|
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...
|
// Synchronization and packets...
|
||||||
if (!isNettyClient) {
|
if (!isNettyClient) {
|
||||||
synchronizePosition();
|
synchronizePosition(true);
|
||||||
}
|
}
|
||||||
// Verify if velocity packet has to be sent
|
// Verify if velocity packet has to be sent
|
||||||
if (hasVelocity() || (!isNettyClient && gravityTickCount > 0)) {
|
if (hasVelocity() || (!isNettyClient && gravityTickCount > 0)) {
|
||||||
@ -665,7 +668,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
|||||||
|
|
||||||
// Scheduled synchronization
|
// Scheduled synchronization
|
||||||
if (!Cooldown.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
|
if (!Cooldown.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
|
||||||
synchronizePosition();
|
synchronizePosition(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldRemove() && !MinecraftServer.isStopping()) {
|
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
|
* Sends the correct packets to update the entity's position, should be called
|
||||||
* every tick. The movement is checked inside the method!
|
* every tick. The movement is checked inside the method!
|
||||||
* <p>
|
* <p>
|
||||||
* The following packets are sent to viewers (check are performed in this order):
|
* The following packets are sent to viewers (check are performed in this order):
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>{@link EntityTeleportPacket} if {@code distanceX > 8 || distanceY > 8 || distanceZ > 8}
|
* <li>{@link EntityTeleportPacket} if {@code distanceX > 8 || distanceY > 8 || distanceZ > 8}
|
||||||
* <i>(performed using {@link #synchronizePosition()})</i></li>
|
* <i>(performed using {@link #synchronizePosition(boolean)})</i></li>
|
||||||
* <li>{@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}</li>
|
* <li>{@link EntityPositionAndRotationPacket} if {@code positionChange && viewChange}</li>
|
||||||
* <li>{@link EntityPositionPacket} if {@code positionChange}</li>
|
* <li>{@link EntityPositionPacket} if {@code positionChange}</li>
|
||||||
* <li>{@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}</li>
|
* <li>{@link EntityRotationPacket} and {@link EntityHeadLookPacket} if {@code viewChange}</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket}
|
* In case of a player's position and/or view change an additional {@link PlayerPositionAndLookPacket}
|
||||||
* is sent to self.
|
* is sent to self.
|
||||||
*
|
*
|
||||||
* @param clientSide {@code true} if the client triggered this action
|
* @param clientSide {@code true} if the client triggered this action
|
||||||
*/
|
*/
|
||||||
protected void sendPositionUpdate(final boolean clientSide) {
|
protected void sendPositionUpdate(final boolean clientSide) {
|
||||||
final boolean viewChange = !position.hasSimilarView(lastSyncedPosition);
|
final boolean viewChange = !position.hasSimilarView(lastSyncedPosition);
|
||||||
final double distanceX = Math.abs(position.getX()-lastSyncedPosition.getX());
|
final double distanceX = Math.abs(position.getX() - lastSyncedPosition.getX());
|
||||||
final double distanceY = Math.abs(position.getY()-lastSyncedPosition.getY());
|
final double distanceY = Math.abs(position.getY() - lastSyncedPosition.getY());
|
||||||
final double distanceZ = Math.abs(position.getZ()-lastSyncedPosition.getZ());
|
final double distanceZ = Math.abs(position.getZ() - lastSyncedPosition.getZ());
|
||||||
final boolean positionChange = (distanceX+distanceY+distanceZ) > 0;
|
final boolean positionChange = (distanceX + distanceY + distanceZ) > 0;
|
||||||
|
|
||||||
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
|
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
|
||||||
synchronizePosition();
|
synchronizePosition(true);
|
||||||
// #synchronizePosition sets sync fields, it's safe to return
|
// #synchronizePosition sets sync fields, it's safe to return
|
||||||
return;
|
return;
|
||||||
} else if (positionChange && viewChange) {
|
} else if (positionChange && viewChange) {
|
||||||
@ -742,7 +745,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
|||||||
final PlayerPositionAndLookPacket playerPositionAndLookPacket = new PlayerPositionAndLookPacket();
|
final PlayerPositionAndLookPacket playerPositionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||||
playerPositionAndLookPacket.flags = 0b111;
|
playerPositionAndLookPacket.flags = 0b111;
|
||||||
playerPositionAndLookPacket.position = position.clone().subtract(lastSyncedPosition.getX(), lastSyncedPosition.getY(), lastSyncedPosition.getZ());
|
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);
|
((Player) this).getPlayerConnection().sendPacket(playerPositionAndLookPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1393,8 +1396,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
|||||||
/**
|
/**
|
||||||
* Updates internal fields and sends updates
|
* Updates internal fields and sends updates
|
||||||
*
|
*
|
||||||
* @param position the new position
|
* @param position the new position
|
||||||
*
|
|
||||||
* @see #refreshPosition(double, double, double)
|
* @see #refreshPosition(double, double, double)
|
||||||
* @see #refreshView(float, float)
|
* @see #refreshView(float, float)
|
||||||
* @see #sendPositionUpdate(boolean)
|
* @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
|
* {@link EntityTeleportPacket} to viewers, in case of a player this is
|
||||||
* overridden in order to send an additional {@link PlayerPositionAndLookPacket}
|
* overridden in order to send an additional {@link PlayerPositionAndLookPacket}
|
||||||
* to itself.
|
* 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
|
@ApiStatus.Internal
|
||||||
protected void synchronizePosition() {
|
protected void synchronizePosition(boolean includeSelf) {
|
||||||
final Position pos = position.clone();
|
final Position pos = position.clone();
|
||||||
final EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
final EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||||
entityTeleportPacket.entityId = getEntityId();
|
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.EntityDamageEvent;
|
||||||
import net.minestom.server.event.entity.EntityDeathEvent;
|
import net.minestom.server.event.entity.EntityDeathEvent;
|
||||||
import net.minestom.server.event.entity.EntityFireEvent;
|
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.event.item.PickupItemEvent;
|
||||||
import net.minestom.server.instance.Chunk;
|
import net.minestom.server.instance.Chunk;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
@ -127,8 +127,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
||||||
this.mainHandItem = itemStack;
|
this.mainHandItem = getEquipmentItem(itemStack, EquipmentSlot.MAIN_HAND);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -139,8 +139,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
||||||
this.offHandItem = itemStack;
|
this.offHandItem = getEquipmentItem(itemStack, EquipmentSlot.OFF_HAND);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
|
syncEquipment(EquipmentSlot.OFF_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -151,8 +151,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHelmet(@NotNull ItemStack itemStack) {
|
public void setHelmet(@NotNull ItemStack itemStack) {
|
||||||
this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
this.helmet = getEquipmentItem(itemStack, EquipmentSlot.HELMET);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
|
syncEquipment(EquipmentSlot.HELMET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -163,8 +163,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChestplate(@NotNull ItemStack itemStack) {
|
public void setChestplate(@NotNull ItemStack itemStack) {
|
||||||
this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
this.chestplate = getEquipmentItem(itemStack, EquipmentSlot.CHESTPLATE);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
|
syncEquipment(EquipmentSlot.CHESTPLATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -175,8 +175,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLeggings(@NotNull ItemStack itemStack) {
|
public void setLeggings(@NotNull ItemStack itemStack) {
|
||||||
this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
this.leggings = getEquipmentItem(itemStack, EquipmentSlot.LEGGINGS);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
|
syncEquipment(EquipmentSlot.LEGGINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -187,14 +187,14 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBoots(@NotNull ItemStack itemStack) {
|
public void setBoots(@NotNull ItemStack itemStack) {
|
||||||
this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
this.boots = getEquipmentItem(itemStack, EquipmentSlot.BOOTS);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
|
syncEquipment(EquipmentSlot.BOOTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
|
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull EquipmentSlot slot) {
|
||||||
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
|
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(this, itemStack, slot);
|
||||||
callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||||
return armorEquipEvent.getArmorItem();
|
return entityEquipEvent.getEquippedItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
* 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.Chunk;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.instance.block.CustomBlock;
|
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.Inventory;
|
||||||
import net.minestom.server.inventory.PlayerInventory;
|
import net.minestom.server.inventory.PlayerInventory;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.item.metadata.WrittenBookMeta;
|
import net.minestom.server.item.metadata.WrittenBookMeta;
|
||||||
import net.minestom.server.listener.PlayerDiggingListener;
|
import net.minestom.server.listener.PlayerDiggingListener;
|
||||||
|
import net.minestom.server.message.Messenger;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.network.ConnectionState;
|
import net.minestom.server.network.ConnectionState;
|
||||||
import net.minestom.server.network.PlayerProvider;
|
import net.minestom.server.network.PlayerProvider;
|
||||||
@ -661,7 +664,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dimensionChange || firstSpawn) {
|
if (dimensionChange || firstSpawn) {
|
||||||
synchronizePosition(); // So the player doesn't get stuck
|
synchronizePosition(true); // So the player doesn't get stuck
|
||||||
this.inventory.update();
|
this.inventory.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,8 +733,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
|
||||||
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid());
|
Messenger.sendMessage(this, message, ChatPosition.fromMessageType(type), source.uuid());
|
||||||
playerConnection.sendPacket(chatMessagePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1984,18 +1986,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Entity#synchronizePosition()
|
* @see Entity#synchronizePosition(boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
protected void synchronizePosition() {
|
protected void synchronizePosition(boolean includeSelf) {
|
||||||
final PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
if (includeSelf) {
|
||||||
positionAndLookPacket.position = position.clone();
|
final PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||||
positionAndLookPacket.flags = 0x00;
|
positionAndLookPacket.position = position.clone();
|
||||||
positionAndLookPacket.teleportId = teleportId.incrementAndGet();
|
positionAndLookPacket.flags = 0x00;
|
||||||
playerConnection.sendPacket(positionAndLookPacket);
|
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) {
|
public void refreshOnGround(boolean onGround) {
|
||||||
this.onGround = 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) {
|
public void refreshHeldSlot(byte slot) {
|
||||||
this.heldSlot = slot;
|
this.heldSlot = slot;
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||||
|
|
||||||
refreshEating(null);
|
refreshEating(null);
|
||||||
}
|
}
|
||||||
@ -2578,6 +2586,10 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
RIGHT
|
RIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated See {@link ChatMessageType}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public enum ChatMode {
|
public enum ChatMode {
|
||||||
ENABLED,
|
ENABLED,
|
||||||
COMMANDS_ONLY,
|
COMMANDS_ONLY,
|
||||||
@ -2588,7 +2600,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
|
|
||||||
private String locale;
|
private String locale;
|
||||||
private byte viewDistance;
|
private byte viewDistance;
|
||||||
private ChatMode chatMode;
|
private ChatMessageType chatMessageType;
|
||||||
private boolean chatColors;
|
private boolean chatColors;
|
||||||
private byte displayedSkinParts;
|
private byte displayedSkinParts;
|
||||||
private MainHand mainHand;
|
private MainHand mainHand;
|
||||||
@ -2619,9 +2631,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
* Gets the player chat mode.
|
* Gets the player chat mode.
|
||||||
*
|
*
|
||||||
* @return the player chat mode
|
* @return the player chat mode
|
||||||
|
* @deprecated Use {@link #getChatMessageType()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ChatMode getChatMode() {
|
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 locale the player locale
|
||||||
* @param viewDistance the player view distance
|
* @param viewDistance the player view distance
|
||||||
* @param chatMode the player chat mode
|
* @param chatMessageType the chat messages the player wishes to receive
|
||||||
* @param chatColors the player chat colors
|
* @param chatColors if chat colors should be displayed
|
||||||
* @param displayedSkinParts the player displayed skin parts
|
* @param displayedSkinParts the player displayed skin parts
|
||||||
* @param mainHand the player main hand
|
* @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) {
|
byte displayedSkinParts, MainHand mainHand) {
|
||||||
|
|
||||||
final boolean viewDistanceChanged = this.viewDistance != viewDistance;
|
final boolean viewDistanceChanged = this.viewDistance != viewDistance;
|
||||||
|
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
this.viewDistance = viewDistance;
|
this.viewDistance = viewDistance;
|
||||||
this.chatMode = chatMode;
|
this.chatMessageType = chatMessageType;
|
||||||
this.chatColors = chatColors;
|
this.chatColors = chatColors;
|
||||||
this.displayedSkinParts = displayedSkinParts;
|
this.displayedSkinParts = displayedSkinParts;
|
||||||
this.mainHand = mainHand;
|
this.mainHand = mainHand;
|
||||||
|
@ -124,17 +124,22 @@ public class FakePlayer extends Player implements NavigableEntity {
|
|||||||
super.setInstance(instance);
|
super.setInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean addViewer0(@NotNull Player player) {
|
||||||
|
final boolean result = super.addViewer0(player);
|
||||||
|
if (result) {
|
||||||
|
handleTabList(player.getPlayerConnection());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void showPlayer(@NotNull PlayerConnection connection) {
|
protected void showPlayer(@NotNull PlayerConnection connection) {
|
||||||
super.showPlayer(connection);
|
super.showPlayer(connection);
|
||||||
if (!option.isInTabList()) {
|
handleTabList(connection);
|
||||||
// Remove from tab-list
|
|
||||||
MinecraftServer.getSchedulerManager().buildTask(() -> connection.sendPacket(getRemovePlayerToList())).delay(20, TimeUnit.TICK).schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -142,4 +147,11 @@ public class FakePlayer extends Player implements NavigableEntity {
|
|||||||
public Navigator getNavigator() {
|
public Navigator getNavigator() {
|
||||||
return navigator;
|
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.Entity;
|
||||||
import net.minestom.server.entity.Metadata;
|
import net.minestom.server.entity.Metadata;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class FoxMeta extends AnimalMeta {
|
public class FoxMeta extends AnimalMeta {
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package net.minestom.server.entity.type.decoration;
|
package net.minestom.server.entity.type.decoration;
|
||||||
|
|
||||||
import net.minestom.server.entity.EntityType;
|
import net.minestom.server.entity.*;
|
||||||
import net.minestom.server.entity.Metadata;
|
import net.minestom.server.event.item.EntityEquipEvent;
|
||||||
import net.minestom.server.entity.ObjectEntity;
|
|
||||||
import net.minestom.server.entity.Player;
|
|
||||||
import net.minestom.server.event.item.ArmorEquipEvent;
|
|
||||||
import net.minestom.server.inventory.EquipmentHandler;
|
import net.minestom.server.inventory.EquipmentHandler;
|
||||||
import net.minestom.server.item.ItemStack;
|
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.Position;
|
||||||
import net.minestom.server.utils.Vector;
|
import net.minestom.server.utils.Vector;
|
||||||
import net.minestom.server.utils.binary.BitmaskUtil;
|
import net.minestom.server.utils.binary.BitmaskUtil;
|
||||||
@ -73,8 +69,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
||||||
this.mainHandItem = itemStack;
|
this.mainHandItem = getEquipmentItem(itemStack, EquipmentSlot.MAIN_HAND);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
syncEquipment(EquipmentSlot.MAIN_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -85,8 +81,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
||||||
this.offHandItem = itemStack;
|
this.offHandItem = getEquipmentItem(itemStack, EquipmentSlot.OFF_HAND);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
|
syncEquipment(EquipmentSlot.OFF_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -97,8 +93,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHelmet(@NotNull ItemStack itemStack) {
|
public void setHelmet(@NotNull ItemStack itemStack) {
|
||||||
this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
this.helmet = getEquipmentItem(itemStack, EquipmentSlot.HELMET);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
|
syncEquipment(EquipmentSlot.HELMET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -109,8 +105,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChestplate(@NotNull ItemStack itemStack) {
|
public void setChestplate(@NotNull ItemStack itemStack) {
|
||||||
this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
this.chestplate = getEquipmentItem(itemStack, EquipmentSlot.CHESTPLATE);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
|
syncEquipment(EquipmentSlot.CHESTPLATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -121,8 +117,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLeggings(@NotNull ItemStack itemStack) {
|
public void setLeggings(@NotNull ItemStack itemStack) {
|
||||||
this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
this.leggings = getEquipmentItem(itemStack, EquipmentSlot.LEGGINGS);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
|
syncEquipment(EquipmentSlot.LEGGINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -133,8 +129,8 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBoots(@NotNull ItemStack itemStack) {
|
public void setBoots(@NotNull ItemStack itemStack) {
|
||||||
this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
this.boots = getEquipmentItem(itemStack, EquipmentSlot.BOOTS);
|
||||||
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
|
syncEquipment(EquipmentSlot.BOOTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSmall() {
|
public boolean isSmall() {
|
||||||
@ -239,9 +235,9 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
|
|||||||
|
|
||||||
// Equipments
|
// Equipments
|
||||||
|
|
||||||
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
|
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull EquipmentSlot slot) {
|
||||||
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
|
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(this, itemStack, slot);
|
||||||
callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||||
return armorEquipEvent.getArmorItem();
|
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) {
|
default <E extends Event> void callEvent(@NotNull Class<E> eventClass, @NotNull E event) {
|
||||||
|
|
||||||
// Global listeners
|
try {
|
||||||
if (!(this instanceof GlobalEventHandler)) {
|
|
||||||
final GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
|
|
||||||
runEvent(globalEventHandler.getEventCallbacks(eventClass), event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local listeners
|
// Global listeners
|
||||||
final Collection<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
|
if (!(this instanceof GlobalEventHandler)) {
|
||||||
runEvent(eventCallbacks, event);
|
final GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
|
||||||
|
runEvent(globalEventHandler.getEventCallbacks(eventClass), 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -75,6 +76,8 @@ public final class DiscoveredExtension {
|
|||||||
/** The original jar this is from. */
|
/** The original jar this is from. */
|
||||||
transient private File originalJar;
|
transient private File originalJar;
|
||||||
|
|
||||||
|
transient private Path dataDirectory;
|
||||||
|
|
||||||
/** The class loader that powers it. */
|
/** The class loader that powers it. */
|
||||||
transient private MinestomExtensionClassLoader minestomExtensionClassLoader;
|
transient private MinestomExtensionClassLoader minestomExtensionClassLoader;
|
||||||
|
|
||||||
@ -130,6 +133,14 @@ public final class DiscoveredExtension {
|
|||||||
return originalJar;
|
return originalJar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NotNull Path getDataDirectory() {
|
||||||
|
return dataDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataDirectory(@NotNull Path dataDirectory) {
|
||||||
|
this.dataDirectory = dataDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
MinestomExtensionClassLoader removeMinestomExtensionClassLoader() {
|
MinestomExtensionClassLoader removeMinestomExtensionClassLoader() {
|
||||||
MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader();
|
MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader();
|
||||||
setMinestomExtensionClassLoader(null);
|
setMinestomExtensionClassLoader(null);
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
package net.minestom.server.extensions;
|
package net.minestom.server.extensions;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
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.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -77,6 +85,118 @@ public abstract class Extension {
|
|||||||
return logger;
|
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.
|
* Adds a new observer to this extension.
|
||||||
* Will be kept as a WeakReference.
|
* Will be kept as a WeakReference.
|
||||||
|
@ -24,6 +24,8 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
@ -44,6 +46,7 @@ public class ExtensionManager {
|
|||||||
|
|
||||||
private final File extensionFolder = new File("extensions");
|
private final File extensionFolder = new File("extensions");
|
||||||
private final File dependenciesFolder = new File(extensionFolder, ".libs");
|
private final File dependenciesFolder = new File(extensionFolder, ".libs");
|
||||||
|
private Path extensionDataRoot = extensionFolder.toPath();
|
||||||
private boolean loaded;
|
private boolean loaded;
|
||||||
|
|
||||||
// Option
|
// Option
|
||||||
@ -336,6 +339,7 @@ public class ExtensionManager {
|
|||||||
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
||||||
extension.files.add(new File(extensionClasses).toURI().toURL());
|
extension.files.add(new File(extensionClasses).toURI().toURL());
|
||||||
extension.files.add(new File(extensionResources).toURI().toURL());
|
extension.files.add(new File(extensionResources).toURI().toURL());
|
||||||
|
extension.setDataDirectory(getExtensionDataRoot().resolve(extension.getName()));
|
||||||
|
|
||||||
// Verify integrity and ensure defaults
|
// Verify integrity and ensure defaults
|
||||||
DiscoveredExtension.verifyIntegrity(extension);
|
DiscoveredExtension.verifyIntegrity(extension);
|
||||||
@ -365,6 +369,7 @@ public class ExtensionManager {
|
|||||||
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
||||||
extension.setOriginalJar(file);
|
extension.setOriginalJar(file);
|
||||||
extension.files.add(file.toURI().toURL());
|
extension.files.add(file.toURI().toURL());
|
||||||
|
extension.setDataDirectory(getExtensionDataRoot().resolve(extension.getName()));
|
||||||
|
|
||||||
// Verify integrity and ensure defaults
|
// Verify integrity and ensure defaults
|
||||||
DiscoveredExtension.verifyIntegrity(extension);
|
DiscoveredExtension.verifyIntegrity(extension);
|
||||||
@ -569,6 +574,14 @@ public class ExtensionManager {
|
|||||||
return extensionFolder;
|
return extensionFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NotNull Path getExtensionDataRoot() {
|
||||||
|
return extensionDataRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtensionDataRoot(@NotNull Path dataRoot) {
|
||||||
|
this.extensionDataRoot = dataRoot;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Collection<Extension> getExtensions() {
|
public Collection<Extension> getExtensions() {
|
||||||
return immutableExtensions.values();
|
return immutableExtensions.values();
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package net.minestom.server.extras;
|
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.MinecraftServer;
|
||||||
import net.minestom.server.extras.mojangAuth.MojangCrypt;
|
import net.minestom.server.extras.mojangAuth.MojangCrypt;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.Proxy;
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
|
||||||
public final class MojangAuth {
|
public final class MojangAuth {
|
||||||
@ -16,8 +12,6 @@ public final class MojangAuth {
|
|||||||
private static volatile boolean enabled = false;
|
private static volatile boolean enabled = false;
|
||||||
|
|
||||||
private static KeyPair keyPair;
|
private static KeyPair keyPair;
|
||||||
private static AuthenticationService authService;
|
|
||||||
private static MinecraftSessionService sessionService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables mojang authentication on the server.
|
* Enables mojang authentication on the server.
|
||||||
@ -32,8 +26,6 @@ public final class MojangAuth {
|
|||||||
|
|
||||||
// Generate necessary fields...
|
// Generate necessary fields...
|
||||||
keyPair = MojangCrypt.generateKeyPair();
|
keyPair = MojangCrypt.generateKeyPair();
|
||||||
authService = new YggdrasilAuthenticationService(Proxy.NO_PROXY, "");
|
|
||||||
sessionService = authService.createMinecraftSessionService();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEnabled() {
|
public static boolean isEnabled() {
|
||||||
@ -44,14 +36,4 @@ public final class MojangAuth {
|
|||||||
public static KeyPair getKeyPair() {
|
public static KeyPair getKeyPair() {
|
||||||
return keyPair;
|
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;
|
package net.minestom.server.instance.block.rule.vanilla;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.instance.block.Block;
|
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.BlockFace;
|
||||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -113,10 +113,10 @@ public class StairsPlacementRule extends BlockPlacementRule {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Shape getShapeFromSide(@NotNull Pair<Shape, Facing> side, @NotNull Facing facing, @NotNull Shape right, @NotNull Shape left) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
Facing sideFacing = side.getRight();
|
Facing sideFacing = side.right();
|
||||||
if (facing.equals(Facing.NORTH)) {
|
if (facing.equals(Facing.NORTH)) {
|
||||||
if (sideFacing.equals(Facing.EAST)) {
|
if (sideFacing.equals(Facing.EAST)) {
|
||||||
return right;
|
return right;
|
||||||
|
@ -53,6 +53,8 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
private int valuesPerLong;
|
private int valuesPerLong;
|
||||||
private boolean hasPalette;
|
private boolean hasPalette;
|
||||||
|
|
||||||
|
private short blockCount = 0;
|
||||||
|
|
||||||
protected Section(int bitsPerEntry, int bitsIncrement) {
|
protected Section(int bitsPerEntry, int bitsIncrement) {
|
||||||
this.bitsPerEntry = bitsPerEntry;
|
this.bitsPerEntry = bitsPerEntry;
|
||||||
this.bitsIncrement = bitsIncrement;
|
this.bitsIncrement = bitsIncrement;
|
||||||
@ -74,6 +76,9 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
blocks = new long[getSize(valuesPerLong)];
|
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
|
// Change to palette value
|
||||||
blockId = getPaletteIndex(blockId);
|
blockId = getPaletteIndex(blockId);
|
||||||
|
|
||||||
@ -87,10 +92,20 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
{
|
{
|
||||||
final long clear = MAGIC_MASKS[bitsPerEntry];
|
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 ^= clear << bitIndex;
|
block ^= clear << bitIndex;
|
||||||
block |= (long) blockId << 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;
|
blocks[index] = block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,6 +158,7 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
this.hasPalette = section.hasPalette;
|
this.hasPalette = section.hasPalette;
|
||||||
|
|
||||||
this.blocks = section.blocks;
|
this.blocks = section.blocks;
|
||||||
|
this.blockCount = section.blockCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,12 +187,22 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
this.blocks = new long[0];
|
this.blocks = new long[0];
|
||||||
this.paletteBlockMap = createPaletteBlockMap();
|
this.paletteBlockMap = createPaletteBlockMap();
|
||||||
this.blockPaletteMap = createBlockPaletteMap();
|
this.blockPaletteMap = createBlockPaletteMap();
|
||||||
|
this.blockCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getBlocks() {
|
public long[] getBlocks() {
|
||||||
return blocks;
|
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() {
|
public Short2ShortLinkedOpenHashMap getPaletteBlockMap() {
|
||||||
return paletteBlockMap;
|
return paletteBlockMap;
|
||||||
}
|
}
|
||||||
@ -288,6 +314,7 @@ public class Section implements PublicCloneable<Section> {
|
|||||||
section.blocks = blocks.clone();
|
section.blocks = blocks.clone();
|
||||||
section.paletteBlockMap = paletteBlockMap.clone();
|
section.paletteBlockMap = paletteBlockMap.clone();
|
||||||
section.blockPaletteMap = blockPaletteMap.clone();
|
section.blockPaletteMap = blockPaletteMap.clone();
|
||||||
|
section.blockCount = blockCount;
|
||||||
return section;
|
return section;
|
||||||
} catch (CloneNotSupportedException e) {
|
} catch (CloneNotSupportedException e) {
|
||||||
MinecraftServer.getExceptionManager().handleException(e);
|
MinecraftServer.getExceptionManager().handleException(e);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.inventory;
|
package net.minestom.server.inventory;
|
||||||
|
|
||||||
import net.minestom.server.entity.Entity;
|
import net.minestom.server.entity.Entity;
|
||||||
|
import net.minestom.server.entity.EquipmentSlot;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
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
|
* @param slot the equipment to get the item from
|
||||||
* @return the equipment {@link ItemStack}
|
* @return the equipment {@link ItemStack}
|
||||||
*/
|
*/
|
||||||
default @NotNull ItemStack getEquipment(@NotNull EntityEquipmentPacket.Slot slot) {
|
default @NotNull ItemStack getEquipment(@NotNull EquipmentSlot slot) {
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case MAIN_HAND:
|
case MAIN_HAND:
|
||||||
return getItemInMainHand();
|
return getItemInMainHand();
|
||||||
@ -157,7 +158,7 @@ public interface EquipmentHandler {
|
|||||||
throw new IllegalStateException("Something weird happened");
|
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) {
|
switch (slot) {
|
||||||
case MAIN_HAND:
|
case MAIN_HAND:
|
||||||
setItemInMainHand(itemStack);
|
setItemInMainHand(itemStack);
|
||||||
@ -187,7 +188,7 @@ public interface EquipmentHandler {
|
|||||||
*
|
*
|
||||||
* @param slot the slot of the equipment
|
* @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");
|
Check.stateCondition(!(this instanceof Entity), "Only accessible for Entity");
|
||||||
|
|
||||||
Entity entity = (Entity) this;
|
Entity entity = (Entity) this;
|
||||||
@ -196,7 +197,7 @@ public interface EquipmentHandler {
|
|||||||
|
|
||||||
EntityEquipmentPacket entityEquipmentPacket = new EntityEquipmentPacket();
|
EntityEquipmentPacket entityEquipmentPacket = new EntityEquipmentPacket();
|
||||||
entityEquipmentPacket.entityId = entity.getEntityId();
|
entityEquipmentPacket.entityId = entity.getEntityId();
|
||||||
entityEquipmentPacket.slots = new EntityEquipmentPacket.Slot[]{slot};
|
entityEquipmentPacket.slots = new EquipmentSlot[]{slot};
|
||||||
entityEquipmentPacket.itemStacks = new ItemStack[]{itemStack};
|
entityEquipmentPacket.itemStacks = new ItemStack[]{itemStack};
|
||||||
|
|
||||||
entity.sendPacketToViewers(entityEquipmentPacket);
|
entity.sendPacketToViewers(entityEquipmentPacket);
|
||||||
@ -213,12 +214,12 @@ public interface EquipmentHandler {
|
|||||||
|
|
||||||
final Entity entity = (Entity) this;
|
final Entity entity = (Entity) this;
|
||||||
|
|
||||||
final EntityEquipmentPacket.Slot[] slots = EntityEquipmentPacket.Slot.values();
|
final EquipmentSlot[] slots = EquipmentSlot.values();
|
||||||
|
|
||||||
List<ItemStack> itemStacks = new ArrayList<>(slots.length);
|
List<ItemStack> itemStacks = new ArrayList<>(slots.length);
|
||||||
|
|
||||||
// Fill items
|
// Fill items
|
||||||
for (EntityEquipmentPacket.Slot slot : slots) {
|
for (EquipmentSlot slot : slots) {
|
||||||
final ItemStack equipment = getEquipment(slot);
|
final ItemStack equipment = getEquipment(slot);
|
||||||
itemStacks.add(equipment);
|
itemStacks.add(equipment);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package net.minestom.server.inventory;
|
package net.minestom.server.inventory;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.EquipmentSlot;
|
||||||
import net.minestom.server.entity.Player;
|
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.ClickType;
|
||||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||||
import net.minestom.server.item.ItemStack;
|
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.SetSlotPacket;
|
||||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||||
import net.minestom.server.utils.MathUtils;
|
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);
|
"The slot {0} does not exist for player", slot);
|
||||||
Check.notNull(itemStack, "The ItemStack cannot be null, you can set air instead");
|
Check.notNull(itemStack, "The ItemStack cannot be null, you can set air instead");
|
||||||
|
|
||||||
EntityEquipmentPacket.Slot equipmentSlot;
|
EquipmentSlot equipmentSlot = null;
|
||||||
|
|
||||||
if (slot == player.getHeldSlot()) {
|
if (slot == player.getHeldSlot()) {
|
||||||
equipmentSlot = EntityEquipmentPacket.Slot.MAIN_HAND;
|
equipmentSlot = EquipmentSlot.MAIN_HAND;
|
||||||
} else if (slot == OFFHAND_SLOT) {
|
} else if (slot == OFFHAND_SLOT) {
|
||||||
equipmentSlot = EntityEquipmentPacket.Slot.OFF_HAND;
|
equipmentSlot = EquipmentSlot.OFF_HAND;
|
||||||
} else {
|
} else if (slot == HELMET_SLOT) {
|
||||||
ArmorEquipEvent armorEquipEvent = null;
|
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) {
|
if (equipmentSlot != null) {
|
||||||
armorEquipEvent = new ArmorEquipEvent(player, itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(player, itemStack, equipmentSlot);
|
||||||
} 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 (armorEquipEvent != null) {
|
player.callEvent(EntityEquipEvent.class, entityEquipEvent);
|
||||||
ArmorEquipEvent.ArmorSlot armorSlot = armorEquipEvent.getArmorSlot();
|
itemStack = entityEquipEvent.getEquippedItem();
|
||||||
equipmentSlot = EntityEquipmentPacket.Slot.fromArmorSlot(armorSlot);
|
|
||||||
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
|
||||||
itemStack = armorEquipEvent.getArmorItem();
|
|
||||||
} else {
|
|
||||||
equipmentSlot = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.itemStacks[slot] = itemStack;
|
this.itemStacks[slot] = itemStack;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package net.minestom.server.inventory.type;
|
package net.minestom.server.inventory.type;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.inventory.Inventory;
|
import net.minestom.server.inventory.Inventory;
|
||||||
import net.minestom.server.inventory.InventoryType;
|
import net.minestom.server.inventory.InventoryType;
|
||||||
import net.minestom.server.network.packet.server.play.TradeListPacket;
|
import net.minestom.server.network.packet.server.play.TradeListPacket;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
import net.minestom.server.utils.ArrayUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class VillagerInventory extends Inventory {
|
public class VillagerInventory extends Inventory {
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ public class VillagerInventory extends Inventory {
|
|||||||
final int length = oldTrades.length + 1;
|
final int length = oldTrades.length + 1;
|
||||||
TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length];
|
TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length];
|
||||||
System.arraycopy(oldTrades, 0, trades, 0, oldTrades.length);
|
System.arraycopy(oldTrades, 0, trades, 0, oldTrades.length);
|
||||||
trades[length] = trade;
|
trades[length - 1] = trade;
|
||||||
this.tradeListPacket.trades = trades;
|
this.tradeListPacket.trades = trades;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@ -79,6 +81,15 @@ public class VillagerInventory extends Inventory {
|
|||||||
sendPacketToViewers(tradeListPacket); // Refresh window
|
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() {
|
private void setupPacket() {
|
||||||
this.tradeListPacket = new TradeListPacket();
|
this.tradeListPacket = new TradeListPacket();
|
||||||
this.tradeListPacket.windowId = getWindowId();
|
this.tradeListPacket.windowId = getWindowId();
|
||||||
|
@ -4,6 +4,8 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.item.attribute.ItemAttribute;
|
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.BinaryWriter;
|
||||||
import net.minestom.server.utils.binary.Writeable;
|
import net.minestom.server.utils.binary.Writeable;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -13,8 +15,9 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
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 int damage;
|
||||||
private final boolean unbreakable;
|
private final boolean unbreakable;
|
||||||
@ -109,18 +112,14 @@ public class ItemMeta implements Writeable {
|
|||||||
return Collections.unmodifiableSet(canPlaceOn);
|
return Collections.unmodifiableSet(canPlaceOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Override
|
||||||
public <T> T getOrDefault(@NotNull ItemTag<T> tag, @Nullable T defaultValue) {
|
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
|
||||||
var key = tag.getKey();
|
return tag.read(nbt);
|
||||||
if (nbt.containsKey(key)) {
|
|
||||||
return tag.read(toNBT());
|
|
||||||
} else {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> @Nullable T get(@NotNull ItemTag<T> tag) {
|
@Override
|
||||||
return tag.read(toNBT());
|
public boolean hasTag(@NotNull Tag<?> tag) {
|
||||||
|
return nbt.containsKey(tag.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull NBTCompound toNBT() {
|
public @NotNull NBTCompound toNBT() {
|
||||||
@ -163,4 +162,26 @@ public class ItemMeta implements Writeable {
|
|||||||
writer.write(cachedBuffer);
|
writer.write(cachedBuffer);
|
||||||
this.cachedBuffer.resetReaderIndex();
|
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.adventure.AdventureSerializer;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.item.attribute.ItemAttribute;
|
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.NBTUtils;
|
||||||
import net.minestom.server.utils.Utils;
|
import net.minestom.server.utils.Utils;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -16,7 +18,7 @@ import java.util.*;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public abstract class ItemMetaBuilder {
|
public abstract class ItemMetaBuilder implements TagWritable {
|
||||||
|
|
||||||
protected NBTCompound nbt = new NBTCompound();
|
protected NBTCompound nbt = new NBTCompound();
|
||||||
|
|
||||||
@ -183,12 +185,13 @@ public abstract class ItemMetaBuilder {
|
|||||||
return canDestroy(Set.of(blocks));
|
return canDestroy(Set.of(blocks));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> @NotNull ItemMetaBuilder set(@NotNull ItemTag<T> tag, @Nullable T value) {
|
@Override
|
||||||
if (value != null) {
|
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||||
tag.write(nbt, value);
|
tag.write(nbt, value);
|
||||||
} else {
|
}
|
||||||
this.nbt.removeTag(tag.getKey());
|
|
||||||
}
|
public <T> @NotNull ItemMetaBuilder set(@NotNull Tag<T> tag, @Nullable T value) {
|
||||||
|
setTag(tag, value);
|
||||||
return this;
|
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.HoverEvent;
|
||||||
import net.kyori.adventure.text.event.HoverEventSource;
|
import net.kyori.adventure.text.event.HoverEventSource;
|
||||||
import net.minestom.server.item.rule.VanillaStackingRule;
|
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 net.minestom.server.utils.NBTUtils;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -22,7 +24,7 @@ import java.util.function.UnaryOperator;
|
|||||||
* <p>
|
* <p>
|
||||||
* An item stack cannot be null, {@link ItemStack#AIR} should be used instead.
|
* 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'.
|
* Constant AIR item. Should be used instead of 'null'.
|
||||||
@ -191,6 +193,21 @@ public final class ItemStack implements HoverEventSource<HoverEvent.ShowItem> {
|
|||||||
.stackingRule(stackingRule);
|
.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
|
@Override
|
||||||
public @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
|
public @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
|
||||||
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(this.material,
|
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(this.material,
|
||||||
|
@ -1,112 +1,65 @@
|
|||||||
package net.minestom.server.item;
|
package net.minestom.server.item;
|
||||||
|
|
||||||
|
import net.minestom.server.tag.Tag;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
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;
|
protected ItemTag(@NotNull String key, @NotNull Function<NBTCompound, T> readFunction, @NotNull BiConsumer<NBTCompound, T> writeConsumer) {
|
||||||
private final Function<NBTCompound, T> readFunction;
|
super(key, readFunction, writeConsumer);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull String getKey() {
|
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
||||||
return key;
|
return Tag.Byte(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T read(@NotNull NBTCompound nbtCompound) {
|
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
||||||
return readFunction.apply(nbtCompound);
|
return Tag.Short(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void write(@NotNull NBTCompound nbtCompound, @NotNull T value) {
|
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
||||||
this.writeConsumer.accept(nbtCompound, value);
|
return Tag.Integer(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Byte> Byte(@NotNull String key) {
|
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.Long(key);
|
||||||
nbtCompound -> nbtCompound.getByte(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Short> Short(@NotNull String key) {
|
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.Float(key);
|
||||||
nbtCompound -> nbtCompound.getShort(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Integer> Integer(@NotNull String key) {
|
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.Double(key);
|
||||||
nbtCompound -> nbtCompound.getInt(key),
|
|
||||||
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Long> Long(@NotNull String key) {
|
public static @NotNull Tag<byte[]> ByteArray(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.ByteArray(key);
|
||||||
nbtCompound -> nbtCompound.getLong(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Float> Float(@NotNull String key) {
|
public static @NotNull Tag<String> String(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.String(key);
|
||||||
nbtCompound -> nbtCompound.getFloat(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<Double> Double(@NotNull String key) {
|
public static @NotNull Tag<NBT> NBT(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.NBT(key);
|
||||||
nbtCompound -> nbtCompound.getDouble(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<byte[]> ByteArray(@NotNull String key) {
|
public static @NotNull Tag<int[]> IntArray(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.IntArray(key);
|
||||||
nbtCompound -> nbtCompound.getByteArray(key),
|
|
||||||
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ItemTag<String> String(@NotNull String key) {
|
public static @NotNull Tag<long[]> LongArray(@NotNull String key) {
|
||||||
return new ItemTag<>(key,
|
return Tag.LongArray(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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@ import net.minestom.server.MinecraftServer;
|
|||||||
import net.minestom.server.command.CommandManager;
|
import net.minestom.server.command.CommandManager;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.player.PlayerChatEvent;
|
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.ConnectionManager;
|
||||||
import net.minestom.server.network.packet.client.play.ClientChatMessagePacket;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -26,21 +26,30 @@ public class ChatMessageListener {
|
|||||||
final String cmdPrefix = CommandManager.COMMAND_PREFIX;
|
final String cmdPrefix = CommandManager.COMMAND_PREFIX;
|
||||||
if (message.startsWith(cmdPrefix)) {
|
if (message.startsWith(cmdPrefix)) {
|
||||||
// The message is a command
|
// 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
|
// Do not call chat event
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if we can receive messages
|
||||||
|
if (!Messenger.canReceiveMessage(player)) {
|
||||||
|
Messenger.sendRejectionMessage(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
|
final Collection<Player> players = CONNECTION_MANAGER.getOnlinePlayers();
|
||||||
String finalMessage = message;
|
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, message), message);
|
||||||
PlayerChatEvent playerChatEvent = new PlayerChatEvent(player, players, () -> buildDefaultChatMessage(player, finalMessage), message);
|
|
||||||
|
|
||||||
// Call the event
|
// Call the event
|
||||||
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
|
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
|
||||||
|
|
||||||
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
|
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
|
||||||
|
|
||||||
Component textObject;
|
Component textObject;
|
||||||
@ -55,15 +64,10 @@ public class ChatMessageListener {
|
|||||||
|
|
||||||
final Collection<Player> recipients = playerChatEvent.getRecipients();
|
final Collection<Player> recipients = playerChatEvent.getRecipients();
|
||||||
if (!recipients.isEmpty()) {
|
if (!recipients.isEmpty()) {
|
||||||
// Send the message with the correct player UUID
|
// delegate to the messenger to avoid sending messages we shouldn't be
|
||||||
ChatMessagePacket chatMessagePacket =
|
Messenger.sendMessage(recipients, textObject, ChatPosition.CHAT, player.getUuid());
|
||||||
new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid());
|
|
||||||
|
|
||||||
PacketUtils.sendGroupedPacket(recipients, chatMessagePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {
|
private static @NotNull Component buildDefaultChatMessage(@NotNull Player player, @NotNull String message) {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package net.minestom.server.listener;
|
package net.minestom.server.listener;
|
||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.player.PlayerStartSneakingEvent;
|
import net.minestom.server.event.player.*;
|
||||||
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.network.packet.client.play.ClientEntityActionPacket;
|
import net.minestom.server.network.packet.client.play.ClientEntityActionPacket;
|
||||||
|
|
||||||
public class EntityActionListener {
|
public class EntityActionListener {
|
||||||
@ -24,6 +21,9 @@ public class EntityActionListener {
|
|||||||
case STOP_SPRINTING:
|
case STOP_SPRINTING:
|
||||||
EntityActionListener.setSprinting(player, false);
|
EntityActionListener.setSprinting(player, false);
|
||||||
break;
|
break;
|
||||||
|
case START_FLYING_ELYTRA:
|
||||||
|
EntityActionListener.startFlyingElytra(player);
|
||||||
|
break;
|
||||||
// TODO do remaining actions
|
// 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) {
|
public static void listener(ClientSettingsPacket packet, Player player) {
|
||||||
Player.PlayerSettings settings = player.getSettings();
|
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);
|
PlayerSettingsChangeEvent playerSettingsChangeEvent = new PlayerSettingsChangeEvent(player);
|
||||||
player.callEvent(PlayerSettingsChangeEvent.class, playerSettingsChangeEvent);
|
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.entity.Player;
|
||||||
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
|
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
|
||||||
import net.minestom.server.network.packet.server.play.TabCompletePacket;
|
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.Arrays;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class TabCompleteListener {
|
public class TabCompleteListener {
|
||||||
|
|
||||||
@ -23,8 +24,7 @@ public class TabCompleteListener {
|
|||||||
String commandString = packet.text.replaceFirst(CommandManager.COMMAND_PREFIX, "");
|
String commandString = packet.text.replaceFirst(CommandManager.COMMAND_PREFIX, "");
|
||||||
String[] split = commandString.split(StringUtils.SPACE);
|
String[] split = commandString.split(StringUtils.SPACE);
|
||||||
String commandName = split[0];
|
String commandName = split[0];
|
||||||
|
String args = commandString.replaceFirst(Pattern.quote(commandName), "");
|
||||||
String args = commandString.replaceFirst(commandName, "");
|
|
||||||
|
|
||||||
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
|
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
|
||||||
if (commandQueryResult == null) {
|
if (commandQueryResult == null) {
|
||||||
@ -48,7 +48,7 @@ public class TabCompleteListener {
|
|||||||
final int inputLength = input.length();
|
final int inputLength = input.length();
|
||||||
|
|
||||||
final int commandLength = Arrays.stream(split).map(String::length).reduce(0, Integer::sum) +
|
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 trailingSpaces = !input.isEmpty() ? text.length() - text.trim().length() : 0;
|
||||||
|
|
||||||
final int start = commandLength - inputLength + 1 - trailingSpaces;
|
final int start = commandLength - inputLength + 1 - trailingSpaces;
|
||||||
|
@ -84,7 +84,12 @@ public final class PacketListenerManager {
|
|||||||
|
|
||||||
// Finally execute the listener
|
// Finally execute the listener
|
||||||
if (packetListenerConsumer != null) {
|
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.packet.server.play.KeepAlivePacket;
|
||||||
import net.minestom.server.network.player.NettyPlayerConnection;
|
import net.minestom.server.network.player.NettyPlayerConnection;
|
||||||
import net.minestom.server.network.player.PlayerConnection;
|
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.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.callback.validator.PlayerValidator;
|
import net.minestom.server.utils.callback.validator.PlayerValidator;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +84,6 @@ public final class ConnectionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the closest player matching a given username.
|
* Finds the closest player matching a given username.
|
||||||
* <p>
|
|
||||||
*
|
*
|
||||||
* @param username the player username (can be partial)
|
* @param username the player username (can be partial)
|
||||||
* @return the closest match, null if no players are online
|
* @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) {
|
public @Nullable Player findPlayer(@NotNull String username) {
|
||||||
Player exact = getPlayer(username);
|
Player exact = getPlayer(username);
|
||||||
if (exact != null) return exact;
|
if (exact != null) return exact;
|
||||||
|
final String username1 = username.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
String lowercase = username.toLowerCase();
|
Function<Player, Double> distanceFunction = player -> {
|
||||||
double currentDistance = 0;
|
final String username2 = player.getUsername().toLowerCase(Locale.ROOT);
|
||||||
for (Player player : getOnlinePlayers()) {
|
return StringUtils.jaroWinklerScore(username1, username2);
|
||||||
final JaroWinklerDistance jaroWinklerDistance = new JaroWinklerDistance();
|
};
|
||||||
final double distance = jaroWinklerDistance.apply(lowercase, player.getUsername().toLowerCase());
|
return getOnlinePlayers()
|
||||||
if (distance > currentDistance) {
|
.stream()
|
||||||
currentDistance = distance;
|
.min(Comparator.comparingDouble(distanceFunction::apply))
|
||||||
exact = player;
|
.filter(player -> distanceFunction.apply(player) > 0)
|
||||||
}
|
.orElse(null);
|
||||||
}
|
|
||||||
return exact;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,8 +187,12 @@ public final class ConnectionManager {
|
|||||||
* Gets all the listeners which are called for each packet received.
|
* Gets all the listeners which are called for each packet received.
|
||||||
*
|
*
|
||||||
* @return a list of packet's consumers
|
* @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
|
@NotNull
|
||||||
|
@Deprecated
|
||||||
public List<ClientPacketConsumer> getReceivePacketConsumers() {
|
public List<ClientPacketConsumer> getReceivePacketConsumers() {
|
||||||
return receiveClientPacketConsumers;
|
return receiveClientPacketConsumers;
|
||||||
}
|
}
|
||||||
@ -198,7 +201,11 @@ public final class ConnectionManager {
|
|||||||
* Adds a consumer to call once a packet is received.
|
* Adds a consumer to call once a packet is received.
|
||||||
*
|
*
|
||||||
* @param clientPacketConsumer the packet consumer
|
* @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) {
|
public void onPacketReceive(@NotNull ClientPacketConsumer clientPacketConsumer) {
|
||||||
this.receiveClientPacketConsumers.add(clientPacketConsumer);
|
this.receiveClientPacketConsumers.add(clientPacketConsumer);
|
||||||
}
|
}
|
||||||
@ -207,8 +214,12 @@ public final class ConnectionManager {
|
|||||||
* Gets all the listeners which are called for each packet sent.
|
* Gets all the listeners which are called for each packet sent.
|
||||||
*
|
*
|
||||||
* @return a list of packet's consumers
|
* @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
|
@NotNull
|
||||||
|
@Deprecated
|
||||||
public List<ServerPacketConsumer> getSendPacketConsumers() {
|
public List<ServerPacketConsumer> getSendPacketConsumers() {
|
||||||
return sendClientPacketConsumers;
|
return sendClientPacketConsumers;
|
||||||
}
|
}
|
||||||
@ -217,7 +228,11 @@ public final class ConnectionManager {
|
|||||||
* Adds a consumer to call once a packet is sent.
|
* Adds a consumer to call once a packet is sent.
|
||||||
*
|
*
|
||||||
* @param serverPacketConsumer the packet consumer
|
* @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) {
|
public void onPacketSend(@NotNull ServerPacketConsumer serverPacketConsumer) {
|
||||||
this.sendClientPacketConsumers.add(serverPacketConsumer);
|
this.sendClientPacketConsumers.add(serverPacketConsumer);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.minestom.server.network.packet.client.login;
|
package net.minestom.server.network.packet.client.login;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.google.gson.Gson;
|
||||||
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
import com.google.gson.JsonObject;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.data.type.array.ByteArrayData;
|
import net.minestom.server.data.type.array.ByteArrayData;
|
||||||
import net.minestom.server.extras.MojangAuth;
|
import net.minestom.server.extras.MojangAuth;
|
||||||
@ -15,11 +15,16 @@ import net.minestom.server.utils.binary.BinaryWriter;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class EncryptionResponsePacket implements ClientPreplayPacket {
|
public class EncryptionResponsePacket implements ClientPreplayPacket {
|
||||||
|
private static final Gson GSON = new Gson();
|
||||||
private byte[] sharedSecret;
|
private byte[] sharedSecret;
|
||||||
private byte[] verifyToken;
|
private byte[] verifyToken;
|
||||||
|
|
||||||
@ -44,7 +49,7 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
|||||||
MinecraftServer.LOGGER.error("{} tried to login with an invalid nonce!", loginUsername);
|
MinecraftServer.LOGGER.error("{} tried to login with an invalid nonce!", loginUsername);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!loginUsername.isEmpty()) {
|
if (loginUsername != null && !loginUsername.isEmpty()) {
|
||||||
|
|
||||||
final byte[] digestedData = MojangCrypt.digestData("", MojangAuth.getKeyPair().getPublic(), getSecretKey());
|
final byte[] digestedData = MojangCrypt.digestData("", MojangAuth.getKeyPair().getPublic(), getSecretKey());
|
||||||
|
|
||||||
@ -55,14 +60,24 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String string3 = new BigInteger(digestedData).toString(16);
|
// Query Mojang's sessionserver.
|
||||||
final GameProfile gameProfile = MojangAuth.getSessionService().hasJoinedServer(new GameProfile(null, loginUsername), string3);
|
final String serverId = new BigInteger(digestedData).toString(16);
|
||||||
nettyConnection.setEncryptionKey(getSecretKey());
|
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());
|
final JsonObject gameProfile = GSON.fromJson(new InputStreamReader(gameProfileStream), JsonObject.class);
|
||||||
CONNECTION_MANAGER.startPlayState(connection, gameProfile.getId(), gameProfile.getName(), true);
|
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);
|
MinecraftServer.getExceptionManager().handleException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -80,11 +95,11 @@ public class EncryptionResponsePacket implements ClientPreplayPacket {
|
|||||||
ByteArrayData.encodeByteArray(writer, verifyToken);
|
ByteArrayData.encodeByteArray(writer, verifyToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey getSecretKey() {
|
private SecretKey getSecretKey() {
|
||||||
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getNonce() {
|
private byte[] getNonce() {
|
||||||
return MojangAuth.getKeyPair().getPrivate() == null ?
|
return MojangAuth.getKeyPair().getPrivate() == null ?
|
||||||
this.verifyToken : MojangCrypt.decryptUsingKey(MojangAuth.getKeyPair().getPrivate(), this.verifyToken);
|
this.verifyToken : MojangCrypt.decryptUsingKey(MojangAuth.getKeyPair().getPrivate(), this.verifyToken);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.network.packet.client.play;
|
package net.minestom.server.network.packet.client.play;
|
||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.message.ChatMessageType;
|
||||||
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
import net.minestom.server.network.packet.client.ClientPlayPacket;
|
||||||
import net.minestom.server.utils.binary.BinaryReader;
|
import net.minestom.server.utils.binary.BinaryReader;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
@ -10,7 +11,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
|
|||||||
|
|
||||||
public String locale = "";
|
public String locale = "";
|
||||||
public byte viewDistance;
|
public byte viewDistance;
|
||||||
public Player.ChatMode chatMode = Player.ChatMode.ENABLED;
|
public ChatMessageType chatMessageType = ChatMessageType.FULL;
|
||||||
public boolean chatColors;
|
public boolean chatColors;
|
||||||
public byte displayedSkinParts;
|
public byte displayedSkinParts;
|
||||||
public Player.MainHand mainHand = Player.MainHand.RIGHT;
|
public Player.MainHand mainHand = Player.MainHand.RIGHT;
|
||||||
@ -19,7 +20,7 @@ public class ClientSettingsPacket extends ClientPlayPacket {
|
|||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
this.locale = reader.readSizedString(128);
|
this.locale = reader.readSizedString(128);
|
||||||
this.viewDistance = reader.readByte();
|
this.viewDistance = reader.readByte();
|
||||||
this.chatMode = Player.ChatMode.values()[reader.readVarInt()];
|
this.chatMessageType = ChatMessageType.fromPacketID(reader.readVarInt());
|
||||||
this.chatColors = reader.readBoolean();
|
this.chatColors = reader.readBoolean();
|
||||||
this.displayedSkinParts = reader.readByte();
|
this.displayedSkinParts = reader.readByte();
|
||||||
this.mainHand = Player.MainHand.values()[reader.readVarInt()];
|
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.");
|
throw new IllegalArgumentException("Locale cannot be longer than 128 characters.");
|
||||||
writer.writeSizedString(locale);
|
writer.writeSizedString(locale);
|
||||||
writer.writeByte(viewDistance);
|
writer.writeByte(viewDistance);
|
||||||
writer.writeVarInt(chatMode.ordinal());
|
writer.writeVarInt(chatMessageType.getPacketID());
|
||||||
writer.writeBoolean(chatColors);
|
writer.writeBoolean(chatColors);
|
||||||
writer.writeByte(displayedSkinParts);
|
writer.writeByte(displayedSkinParts);
|
||||||
writer.writeVarInt(mainHand.ordinal());
|
writer.writeVarInt(mainHand.ordinal());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.minestom.server.network.packet.server.play;
|
package net.minestom.server.network.packet.server.play;
|
||||||
|
|
||||||
import net.kyori.adventure.audience.MessageType;
|
|
||||||
import net.kyori.adventure.text.Component;
|
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.ComponentHoldingServerPacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||||
@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
@ -22,21 +23,19 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
|||||||
private static final UUID NULL_UUID = new UUID(0, 0);
|
private static final UUID NULL_UUID = new UUID(0, 0);
|
||||||
|
|
||||||
public Component message;
|
public Component message;
|
||||||
public Position position;
|
public ChatPosition position;
|
||||||
public UUID uuid;
|
public UUID uuid;
|
||||||
|
|
||||||
public ChatMessagePacket() {
|
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.message = message;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.uuid = uuid;
|
this.uuid = Objects.requireNonNullElse(uuid, NULL_UUID);
|
||||||
}
|
|
||||||
|
|
||||||
public ChatMessagePacket(Component message, Position position) {
|
|
||||||
this(message, position, NULL_UUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,7 +48,7 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
|||||||
@Override
|
@Override
|
||||||
public void read(@NotNull BinaryReader reader) {
|
public void read(@NotNull BinaryReader reader) {
|
||||||
message = reader.readComponent(Integer.MAX_VALUE);
|
message = reader.readComponent(Integer.MAX_VALUE);
|
||||||
position = Position.values()[reader.readByte()];
|
position = ChatPosition.fromPacketID(reader.readByte());
|
||||||
uuid = reader.readUuid();
|
uuid = reader.readUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,41 +66,4 @@ public class ChatMessagePacket implements ComponentHoldingServerPacket {
|
|||||||
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
|
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
|
||||||
return new ChatMessagePacket(operator.apply(message), position, uuid);
|
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;
|
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.item.ItemStack;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||||
@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
public class EntityEquipmentPacket implements ServerPacket {
|
public class EntityEquipmentPacket implements ServerPacket {
|
||||||
|
|
||||||
public int entityId;
|
public int entityId;
|
||||||
public Slot[] slots;
|
public EquipmentSlot[] slots;
|
||||||
public ItemStack[] itemStacks;
|
public ItemStack[] itemStacks;
|
||||||
|
|
||||||
public EntityEquipmentPacket() {
|
public EntityEquipmentPacket() {
|
||||||
@ -33,7 +33,7 @@ public class EntityEquipmentPacket implements ServerPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < slots.length; i++) {
|
for (int i = 0; i < slots.length; i++) {
|
||||||
final Slot slot = slots[i];
|
final EquipmentSlot slot = slots[i];
|
||||||
final ItemStack itemStack = itemStacks[i];
|
final ItemStack itemStack = itemStacks[i];
|
||||||
final boolean last = i == slots.length - 1;
|
final boolean last = i == slots.length - 1;
|
||||||
|
|
||||||
@ -52,17 +52,17 @@ public class EntityEquipmentPacket implements ServerPacket {
|
|||||||
entityId = reader.readVarInt();
|
entityId = reader.readVarInt();
|
||||||
|
|
||||||
boolean hasRemaining = true;
|
boolean hasRemaining = true;
|
||||||
List<Slot> slots = new LinkedList<>();
|
List<EquipmentSlot> slots = new LinkedList<>();
|
||||||
List<ItemStack> stacks = new LinkedList<>();
|
List<ItemStack> stacks = new LinkedList<>();
|
||||||
while (hasRemaining) {
|
while (hasRemaining) {
|
||||||
byte slotEnum = reader.readByte();
|
byte slotEnum = reader.readByte();
|
||||||
hasRemaining = (slotEnum & 0x80) == 0x80;
|
hasRemaining = (slotEnum & 0x80) == 0x80;
|
||||||
|
|
||||||
slots.add(Slot.values()[slotEnum & 0x7F]);
|
slots.add(EquipmentSlot.values()[slotEnum & 0x7F]);
|
||||||
stacks.add(reader.readItemStack());
|
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]);
|
this.itemStacks = stacks.toArray(new ItemStack[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,29 +71,4 @@ public class EntityEquipmentPacket implements ServerPacket {
|
|||||||
return ServerPacketIdentifier.ENTITY_EQUIPMENT;
|
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.TickUtils;
|
||||||
import net.minestom.server.utils.binary.BinaryReader;
|
import net.minestom.server.utils.binary.BinaryReader;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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.
|
* 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
|
* @param payload the payload
|
||||||
* @throws IllegalArgumentException if the action is not {@link Action#SET_TITLE},
|
* @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) {
|
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.action = action;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ public class TitlePacket implements ComponentHoldingServerPacket {
|
|||||||
*
|
*
|
||||||
* @param action the action
|
* @param action the action
|
||||||
* @throws IllegalArgumentException if the action is not {@link Action#RESET},
|
* @throws IllegalArgumentException if the action is not {@link Action#RESET},
|
||||||
* or {@link Action#HIDE}
|
* or {@link Action#HIDE}
|
||||||
*/
|
*/
|
||||||
public TitlePacket(@NotNull Action action) {
|
public TitlePacket(@NotNull Action action) {
|
||||||
this.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}.
|
* Constructs a new title packet for {@link Action#SET_TIMES_AND_DISPLAY}.
|
||||||
*
|
*
|
||||||
* @param fadeIn the fade in time
|
* @param fadeIn the fade in time
|
||||||
* @param stay the stay time
|
* @param stay the stay time
|
||||||
* @param fadeOut the fade out time
|
* @param fadeOut the fade out time
|
||||||
*/
|
*/
|
||||||
public TitlePacket(int fadeIn, int stay, int fadeOut) {
|
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>
|
* <p>
|
||||||
* Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers.
|
* 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.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import net.minestom.server.utils.StringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.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.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)
|
* Responsible for making sure Minestom has the necessary files to run (notably registry files)
|
||||||
@ -177,13 +188,19 @@ public class ResourceGatherer {
|
|||||||
}
|
}
|
||||||
// Verify checksum
|
// Verify checksum
|
||||||
try (FileInputStream fis = new FileInputStream(target)) {
|
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)) {
|
if (!sha1Target.equals(sha1Source)) {
|
||||||
LOGGER.debug("The checksum test failed after downloading the Minecraft server jar.");
|
LOGGER.error("The checksum test failed after downloading the Minecraft server jar.");
|
||||||
LOGGER.debug("The expected checksum was: {}.", sha1Source);
|
LOGGER.error("The expected checksum was: {}.", sha1Source);
|
||||||
LOGGER.debug("The calculated checksum was: {}.", sha1Target);
|
LOGGER.error("The calculated checksum was: {}.", sha1Target);
|
||||||
throw new IOException("Failed to download Minecraft server jar.");
|
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;
|
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;
|
package net.minestom.server.utils;
|
||||||
|
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import org.apache.commons.lang3.Validate;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.time.Duration;
|
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}.
|
* Creates a number of ticks from a given duration, based on {@link MinecraftServer#TICK_MS}.
|
||||||
|
*
|
||||||
* @param duration the duration
|
* @param duration the duration
|
||||||
* @return the number of ticks
|
* @return the number of ticks
|
||||||
* @throws IllegalArgumentException if duration is negative
|
* @throws IllegalArgumentException if duration is negative
|
||||||
@ -32,14 +33,14 @@ public class TickUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a number of ticks from a given duration.
|
* 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
|
* @param msPerTick the number of milliseconds per tick
|
||||||
* @return the number of ticks
|
* @return the number of ticks
|
||||||
* @throws IllegalArgumentException if duration is negative
|
* @throws IllegalArgumentException if duration is negative
|
||||||
*/
|
*/
|
||||||
public static int fromDuration(@NotNull Duration duration, int msPerTick) {
|
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);
|
return (int) (duration.toMillis() / msPerTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,16 +118,11 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeSectionBlocks(ByteBuf buffer, Section section) {
|
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();
|
final int bitsPerEntry = section.getBitsPerEntry();
|
||||||
|
|
||||||
//buffer.writeShort(count);
|
buffer.writeShort(blockCount);
|
||||||
// TODO count blocks
|
|
||||||
buffer.writeShort(200);
|
|
||||||
buffer.writeByte((byte) bitsPerEntry);
|
buffer.writeByte((byte) bitsPerEntry);
|
||||||
|
|
||||||
// Palette
|
// Palette
|
||||||
|
@ -26,7 +26,6 @@ public class TemporaryCache<T> {
|
|||||||
public TemporaryCache(long duration, TimeUnit timeUnit, RemovalListener<UUID, T> removalListener) {
|
public TemporaryCache(long duration, TimeUnit timeUnit, RemovalListener<UUID, T> removalListener) {
|
||||||
this.cache = CacheBuilder.newBuilder()
|
this.cache = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(duration, timeUnit)
|
.expireAfterWrite(duration, timeUnit)
|
||||||
.softValues()
|
|
||||||
.removalListener(removalListener)
|
.removalListener(removalListener)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ public final class MojangUtils {
|
|||||||
.softValues()
|
.softValues()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static JsonObject fromUuid(@NotNull String uuid) {
|
public static JsonObject fromUuid(@NotNull String uuid) {
|
||||||
|
|
||||||
|
@ -1,24 +1,32 @@
|
|||||||
package demo.commands;
|
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.Component;
|
||||||
import net.kyori.adventure.text.event.ClickEvent;
|
import net.kyori.adventure.text.event.ClickEvent;
|
||||||
import net.minestom.server.command.builder.Command;
|
import net.minestom.server.command.builder.Command;
|
||||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
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.ArgumentComponent;
|
||||||
|
import net.minestom.server.command.builder.arguments.minecraft.ArgumentUUID;
|
||||||
|
|
||||||
public class EchoCommand extends Command {
|
public class EchoCommand extends Command {
|
||||||
public EchoCommand() {
|
public EchoCommand() {
|
||||||
super("echo");
|
super("echo");
|
||||||
|
|
||||||
this.setDefaultExecutor((sender, context) -> sender.sendMessage(
|
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.")
|
.hoverEvent(Component.text("Click to get this command.")
|
||||||
.clickEvent(ClickEvent.suggestCommand("/echo ")))));
|
.clickEvent(ClickEvent.suggestCommand("/echo ")))));
|
||||||
|
|
||||||
ArgumentComponent json = ArgumentType.Component("json");
|
ArgumentComponent json = ArgumentType.Component("json");
|
||||||
|
ArgumentUUID uuid = ArgumentType.UUID("uuid");
|
||||||
|
|
||||||
this.addSyntax((sender, context) -> {
|
this.addSyntax((sender, context) -> {
|
||||||
sender.sendMessage(context.get(json));
|
sender.sendMessage(context.get(json));
|
||||||
}, 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 com.google.common.reflect.ClassPath;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.entity.EquipmentSlot;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.network.packet.client.ClientPacket;
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
@ -67,7 +68,7 @@ public class ReadWritePackets {
|
|||||||
// requires at least one slot and one item
|
// requires at least one slot and one item
|
||||||
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
EntityEquipmentPacket p = new EntityEquipmentPacket();
|
||||||
p.itemStacks = new ItemStack[]{ItemStack.AIR};
|
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;
|
packet = (T) p;
|
||||||
} else {
|
} else {
|
||||||
packet = (T) constructor.newInstance();
|
packet = (T) constructor.newInstance();
|
||||||
|
Loading…
Reference in New Issue
Block a user