More complete Javadoc
This commit is contained in:
parent
3d92c3afb6
commit
8f5f880754
@ -7,6 +7,9 @@ import net.kyori.adventure.text.ComponentLike;
|
|||||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to ease conversion between our Adventure backed Chat API and BungeeCord chat API.
|
||||||
|
*/
|
||||||
public class ChatBungee {
|
public class ChatBungee {
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,36 +11,107 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient enum to uses legacy format while keeping compatibility with modern chat format and API (Adventure, ...)
|
||||||
|
*/
|
||||||
public enum LegacyChatFormat {
|
public enum LegacyChatFormat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Black (0) color format code.
|
||||||
|
*/
|
||||||
BLACK('0'),
|
BLACK('0'),
|
||||||
|
/**
|
||||||
|
* Dark blue (1) color format code.
|
||||||
|
*/
|
||||||
DARK_BLUE('1'),
|
DARK_BLUE('1'),
|
||||||
|
/**
|
||||||
|
* Dark green (2) color format code.
|
||||||
|
*/
|
||||||
DARK_GREEN('2'),
|
DARK_GREEN('2'),
|
||||||
|
/**
|
||||||
|
* Dark aqua (3) color format code.
|
||||||
|
*/
|
||||||
DARK_AQUA('3'),
|
DARK_AQUA('3'),
|
||||||
|
/**
|
||||||
|
* Dark red (4) color format code.
|
||||||
|
*/
|
||||||
DARK_RED('4'),
|
DARK_RED('4'),
|
||||||
|
/**
|
||||||
|
* Dark purple (5) color format code.
|
||||||
|
*/
|
||||||
DARK_PURPLE('5'),
|
DARK_PURPLE('5'),
|
||||||
|
/**
|
||||||
|
* Gold (6) color format code.
|
||||||
|
*/
|
||||||
GOLD('6'),
|
GOLD('6'),
|
||||||
|
/**
|
||||||
|
* Gray (7) color format code.
|
||||||
|
*/
|
||||||
GRAY('7'),
|
GRAY('7'),
|
||||||
|
/**
|
||||||
|
* Dark gray (8) color format code.
|
||||||
|
*/
|
||||||
DARK_GRAY('8'),
|
DARK_GRAY('8'),
|
||||||
|
/**
|
||||||
|
* Blue (9) color format code.
|
||||||
|
*/
|
||||||
BLUE('9'),
|
BLUE('9'),
|
||||||
|
/**
|
||||||
|
* Green (A) color format code.
|
||||||
|
*/
|
||||||
GREEN('a'),
|
GREEN('a'),
|
||||||
|
/**
|
||||||
|
* Aqua (B) color format code.
|
||||||
|
*/
|
||||||
AQUA('b'),
|
AQUA('b'),
|
||||||
|
/**
|
||||||
|
* Red (C) color format code.
|
||||||
|
*/
|
||||||
RED('c'),
|
RED('c'),
|
||||||
|
/**
|
||||||
|
* Light purple (D) color format code.
|
||||||
|
*/
|
||||||
LIGHT_PURPLE('d'),
|
LIGHT_PURPLE('d'),
|
||||||
|
/**
|
||||||
|
* Yellow (E) color format code.
|
||||||
|
*/
|
||||||
YELLOW('e'),
|
YELLOW('e'),
|
||||||
|
/**
|
||||||
|
* White (F) color format code.
|
||||||
|
*/
|
||||||
WHITE('f'),
|
WHITE('f'),
|
||||||
MAGIC('k'),
|
/**
|
||||||
|
* Obfuscated (K) decoration format code.
|
||||||
|
*/
|
||||||
|
OBFUSCATED('k'),
|
||||||
|
/**
|
||||||
|
* Bold (L) decoration format code.
|
||||||
|
*/
|
||||||
BOLD('l'),
|
BOLD('l'),
|
||||||
|
/**
|
||||||
|
* Strikethrough (M) decoration format code.
|
||||||
|
*/
|
||||||
STRIKETHROUGH('m'),
|
STRIKETHROUGH('m'),
|
||||||
|
/**
|
||||||
|
* Underlined (N) decoration format code.
|
||||||
|
*/
|
||||||
UNDERLINED('n'),
|
UNDERLINED('n'),
|
||||||
|
/**
|
||||||
|
* Italic (O) decoration format code.
|
||||||
|
*/
|
||||||
ITALIC('o'),
|
ITALIC('o'),
|
||||||
|
/**
|
||||||
|
* Reset (R) format code.
|
||||||
|
*/
|
||||||
RESET('r');
|
RESET('r');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The character used by Minecraft for legacy chat format.
|
||||||
|
*/
|
||||||
public static final char COLOR_CHAR = LegacyComponentSerializer.SECTION_CHAR;
|
public static final char COLOR_CHAR = LegacyComponentSerializer.SECTION_CHAR;
|
||||||
|
|
||||||
|
/** {@link #COLOR_CHAR} but as a String! */
|
||||||
public static final String COLOR_STR_PREFIX = Character.toString(COLOR_CHAR);
|
public static final String COLOR_STR_PREFIX = Character.toString(COLOR_CHAR);
|
||||||
|
|
||||||
private static final Map<Character, LegacyChatFormat> BY_CHAR;
|
private static final Map<Character, LegacyChatFormat> BY_CHAR;
|
||||||
@ -48,10 +119,20 @@ public enum LegacyChatFormat {
|
|||||||
private static final Map<LegacyFormat, LegacyChatFormat> BY_LEGACY;
|
private static final Map<LegacyFormat, LegacyChatFormat> BY_LEGACY;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link LegacyChatFormat} from the provided chat color code.
|
||||||
|
* @param code the character code from [0-9A-Fa-fK-Ok-oRr].
|
||||||
|
* @return the {@link LegacyChatFormat} related to the provided code.
|
||||||
|
*/
|
||||||
public static LegacyChatFormat of(char code) {
|
public static LegacyChatFormat of(char code) {
|
||||||
return BY_CHAR.get(Character.toLowerCase(code));
|
return BY_CHAR.get(Character.toLowerCase(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link LegacyChatFormat} from the provided {@link TextFormat} instance.
|
||||||
|
* @param format the {@link TextFormat} instance.
|
||||||
|
* @return the {@link LegacyChatFormat} related to the provided format.
|
||||||
|
*/
|
||||||
public static LegacyChatFormat of(TextFormat format) {
|
public static LegacyChatFormat of(TextFormat format) {
|
||||||
LegacyChatFormat colorOrDecoration = BY_FORMAT.get(format);
|
LegacyChatFormat colorOrDecoration = BY_FORMAT.get(format);
|
||||||
if (colorOrDecoration != null)
|
if (colorOrDecoration != null)
|
||||||
@ -61,19 +142,24 @@ public enum LegacyChatFormat {
|
|||||||
throw new IllegalArgumentException("Unsupported format of type " + format.getClass());
|
throw new IllegalArgumentException("Unsupported format of type " + format.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link LegacyChatFormat} from the provided {@link LegacyFormat} instance.
|
||||||
|
* @param advLegacy the {@link LegacyFormat} instance.
|
||||||
|
* @return the {@link LegacyChatFormat} related to the provided format.
|
||||||
|
*/
|
||||||
public static LegacyChatFormat of(LegacyFormat advLegacy) {
|
public static LegacyChatFormat of(LegacyFormat advLegacy) {
|
||||||
return BY_LEGACY.get(advLegacy);
|
return BY_LEGACY.get(advLegacy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format code of this chat format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public final char code;
|
public final char code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Adventure legacy format instance related to this chat format.
|
||||||
|
*/
|
||||||
public final LegacyFormat advLegacyFormat;
|
public final LegacyFormat advLegacyFormat;
|
||||||
|
|
||||||
LegacyChatFormat(char code) {
|
LegacyChatFormat(char code) {
|
||||||
@ -81,22 +167,42 @@ public enum LegacyChatFormat {
|
|||||||
advLegacyFormat = LegacyComponentSerializer.parseChar(code);
|
advLegacyFormat = LegacyComponentSerializer.parseChar(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the related {@link TextColor}, or null if it's not a color.
|
||||||
|
* @return the related {@link TextColor}, or null if it's not a color.
|
||||||
|
*/
|
||||||
public TextColor getTextColor() {
|
public TextColor getTextColor() {
|
||||||
return advLegacyFormat.color();
|
return advLegacyFormat.color();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if this format is a color.
|
||||||
|
* @return true if this format is a color, false otherwise.
|
||||||
|
*/
|
||||||
public boolean isColor() {
|
public boolean isColor() {
|
||||||
return getTextColor() != null;
|
return getTextColor() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the related {@link TextDecoration}, or null if it's not a decoration.
|
||||||
|
* @return the related {@link TextDecoration}, or null if it's not a decoration.
|
||||||
|
*/
|
||||||
public TextDecoration getTextDecoration() {
|
public TextDecoration getTextDecoration() {
|
||||||
return advLegacyFormat.decoration();
|
return advLegacyFormat.decoration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if this format is a decoration (bold, italic, ...).
|
||||||
|
* @return true if this format is a decoration, false otherwise.
|
||||||
|
*/
|
||||||
public boolean isDecoration() {
|
public boolean isDecoration() {
|
||||||
return getTextDecoration() != null;
|
return getTextDecoration() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if this format is the reset.
|
||||||
|
* @return true if this format is the reset, false otherwise.
|
||||||
|
*/
|
||||||
public boolean isReset() {
|
public boolean isReset() {
|
||||||
return this == RESET;
|
return this == RESET;
|
||||||
}
|
}
|
||||||
@ -109,14 +215,16 @@ public enum LegacyChatFormat {
|
|||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
BY_CHAR = Arrays.stream(values()).sequential().collect(Collectors.toMap(e -> e.code, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
BY_CHAR = Arrays.stream(values()).sequential()
|
||||||
|
.collect(Collectors.toMap(e -> e.code, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
||||||
BY_FORMAT = Arrays.stream(values()).sequential()
|
BY_FORMAT = Arrays.stream(values()).sequential()
|
||||||
.filter(e -> e.isColor() || e.isDecoration())
|
.filter(e -> e.isColor() || e.isDecoration())
|
||||||
.collect(Collectors.toMap(e -> {
|
.collect(Collectors.toMap(e -> {
|
||||||
if (e.isColor())
|
if (e.isColor())
|
||||||
return e.getTextColor();
|
return e.getTextColor();
|
||||||
return e.getTextDecoration();
|
return e.getTextDecoration();
|
||||||
}, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
}, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
||||||
BY_LEGACY = Arrays.stream(values()).sequential().collect(Collectors.toMap(e -> e.advLegacyFormat, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
BY_LEGACY = Arrays.stream(values()).sequential()
|
||||||
|
.collect(Collectors.toMap(e -> e.advLegacyFormat, e -> e, (e1, e2) -> e1, LinkedHashMap::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,60 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic class holding configuration for {@link PaperBackupManager}.
|
||||||
|
*/
|
||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
public class PaperBackupConfig {
|
public class PaperBackupConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to enable worlds backup.
|
||||||
|
* Defaults to true.
|
||||||
|
*/
|
||||||
public boolean worldBackupEnabled = true;
|
public boolean worldBackupEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to enable the backup of the working directory.
|
||||||
|
* The workdir backup will already ignore the logs directory and any world folder (folder with a level.dat file in it).
|
||||||
|
* Defaults to true.
|
||||||
|
*/
|
||||||
public boolean workdirBackupEnabled = true;
|
public boolean workdirBackupEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to enable the backup of logs.
|
||||||
|
* Defaults to true.
|
||||||
|
*/
|
||||||
public boolean logsBackupEnabled = true;
|
public boolean logsBackupEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cron-formatted scheduling of the worlds and workdir backups.
|
||||||
|
* The default value is {@code "0 2 * * *"}, that is every day at 2am.
|
||||||
|
*/
|
||||||
public String scheduling = "0 2 * * *"; // cron format, here is every day at 2am
|
public String scheduling = "0 2 * * *"; // cron format, here is every day at 2am
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup target directory.
|
||||||
|
* Must be set (defaults to null).
|
||||||
|
*/
|
||||||
public File backupDirectory = null;
|
public File backupDirectory = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup cleaner for the worlds backup.
|
||||||
|
* Defaults to keep 1 backup every 3 month + the last 5 backups.
|
||||||
|
*/
|
||||||
public BackupCleaner worldBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5));
|
public BackupCleaner worldBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup cleaner for the workdir backup.
|
||||||
|
* Defaults to keep 1 backup every 3 month + the last 5 backups.
|
||||||
|
*/
|
||||||
public BackupCleaner workdirBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5));
|
public BackupCleaner workdirBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of files or directory to ignore.
|
||||||
|
* Defaults to none.
|
||||||
|
* The workdir backup will already ignore the logs directory and any world folder (folder with a level.dat file in it).
|
||||||
|
*/
|
||||||
public List<String> workdirIgnoreList = new ArrayList<>();
|
public List<String> workdirIgnoreList = new ArrayList<>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,19 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup manager for Paper servers.
|
||||||
|
*/
|
||||||
public class PaperBackupManager extends BackupManager implements Listener {
|
public class PaperBackupManager extends BackupManager implements Listener {
|
||||||
|
|
||||||
private final Map<String, PaperWorldProcess> compressWorlds = new HashMap<>();
|
private final Map<String, PaperWorldProcess> compressWorlds = new HashMap<>();
|
||||||
|
|
||||||
PaperBackupConfig config;
|
PaperBackupConfig config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new backup manager.
|
||||||
|
* @param config the configuration of the backups.
|
||||||
|
*/
|
||||||
public PaperBackupManager(PaperBackupConfig config) {
|
public PaperBackupManager(PaperBackupConfig config) {
|
||||||
super(config.backupDirectory);
|
super(config.backupDirectory);
|
||||||
setConfig(config);
|
setConfig(config);
|
||||||
@ -49,13 +56,17 @@ public class PaperBackupManager extends BackupManager implements Listener {
|
|||||||
super.addProcess(process);
|
super.addProcess(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the backups config
|
||||||
|
* @param config the new config.
|
||||||
|
*/
|
||||||
public void setConfig(PaperBackupConfig config) {
|
public void setConfig(PaperBackupConfig config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
backupQueue.forEach(this::updateProcessConfig);
|
backupQueue.forEach(this::updateProcessConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateProcessConfig(BackupProcess process) {
|
private void updateProcessConfig(BackupProcess process) {
|
||||||
if (process instanceof PaperWorkdirProcess) {
|
if (process instanceof PaperWorkdirProcess) {
|
||||||
process.setEnabled(config.workdirBackupEnabled);
|
process.setEnabled(config.workdirBackupEnabled);
|
||||||
process.setBackupCleaner(config.workdirBackupCleaner);
|
process.setBackupCleaner(config.workdirBackupCleaner);
|
||||||
|
@ -63,6 +63,7 @@ public class DirectionalVector {
|
|||||||
* contained in the provided {@link Location}.
|
* contained in the provided {@link Location}.
|
||||||
* {@link Location#getYaw()} and {@link Location#getPitch()} values are automatically
|
* {@link Location#getYaw()} and {@link Location#getPitch()} values are automatically
|
||||||
* converted to conform {@link #yaw} and {@link #pitch} specification.
|
* converted to conform {@link #yaw} and {@link #pitch} specification.
|
||||||
|
* @param l the location.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector(Location l) {
|
public DirectionalVector(Location l) {
|
||||||
this(
|
this(
|
||||||
@ -79,6 +80,7 @@ public class DirectionalVector {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Creates a new {@link DirectionalVector} from a simple {@link Vector}.
|
||||||
* @param v the vector representing the direction. If v.getX() and v.getZ() are 0,
|
* @param v the vector representing the direction. If v.getX() and v.getZ() are 0,
|
||||||
* the yaw will be 0. This may have inconsistency if the vector is calculated
|
* the yaw will be 0. This may have inconsistency if the vector is calculated
|
||||||
* from a {@link Location}'s yaw and pitch. In this case, prefer using
|
* from a {@link Location}'s yaw and pitch. In this case, prefer using
|
||||||
@ -126,7 +128,10 @@ public class DirectionalVector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Vector using the internal X, Y and Z values, that is a simple directional 3D vector.
|
||||||
|
* @return this vector as a simple 3D {@link Vector}.
|
||||||
|
*/
|
||||||
public Vector toVector() {
|
public Vector toVector() {
|
||||||
return new Vector(x, y, z);
|
return new Vector(x, y, z);
|
||||||
}
|
}
|
||||||
@ -135,7 +140,8 @@ public class DirectionalVector {
|
|||||||
/**
|
/**
|
||||||
* Set the yaw and the pitch of the provided {@link Location}
|
* Set the yaw and the pitch of the provided {@link Location}
|
||||||
* with the values inside the current {@link DirectionalVector}
|
* with the values inside the current {@link DirectionalVector}
|
||||||
* after conversion of these values
|
* after conversion of these values.
|
||||||
|
* @param l the location.
|
||||||
*/
|
*/
|
||||||
public void putIntoLocation(Location l) {
|
public void putIntoLocation(Location l) {
|
||||||
/* std : -PI/2 : <0 ? +2PI : MC
|
/* std : -PI/2 : <0 ? +2PI : MC
|
||||||
@ -148,7 +154,10 @@ public class DirectionalVector {
|
|||||||
l.setPitch((float) Math.toDegrees(-pitch));
|
l.setPitch((float) Math.toDegrees(-pitch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the vector pointing to the opposite direction.
|
||||||
|
* @return the opposite vector.
|
||||||
|
*/
|
||||||
public DirectionalVector getOpposite() {
|
public DirectionalVector getOpposite() {
|
||||||
return new DirectionalVector(
|
return new DirectionalVector(
|
||||||
-x,
|
-x,
|
||||||
@ -163,6 +172,7 @@ public class DirectionalVector {
|
|||||||
* If the current direction is the player face direction,
|
* If the current direction is the player face direction,
|
||||||
* this method return the direction of the back of the head.
|
* this method return the direction of the back of the head.
|
||||||
* This is an alias of {@link #getOpposite()}
|
* This is an alias of {@link #getOpposite()}
|
||||||
|
* @return the opposite of this vector.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector getBackDirection() {
|
public DirectionalVector getBackDirection() {
|
||||||
return getOpposite();
|
return getOpposite();
|
||||||
@ -171,6 +181,7 @@ public class DirectionalVector {
|
|||||||
/**
|
/**
|
||||||
* If the current direction is the player face direction,
|
* If the current direction is the player face direction,
|
||||||
* this method return the direction of the bottom of the head.
|
* this method return the direction of the bottom of the head.
|
||||||
|
* @return the vector pointing on the bottom, as if this vector was the front orientation of a player head.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector getBottomDirection() {
|
public DirectionalVector getBottomDirection() {
|
||||||
return new DirectionalVector(
|
return new DirectionalVector(
|
||||||
@ -182,6 +193,7 @@ public class DirectionalVector {
|
|||||||
/**
|
/**
|
||||||
* If the current direction is the player face direction,
|
* If the current direction is the player face direction,
|
||||||
* this method return the direction of the top of the head.
|
* this method return the direction of the top of the head.
|
||||||
|
* @return the vector pointing on the top, as if this vector was the front orientation of a player head.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector getTopDirection() {
|
public DirectionalVector getTopDirection() {
|
||||||
return new DirectionalVector(
|
return new DirectionalVector(
|
||||||
@ -194,6 +206,7 @@ public class DirectionalVector {
|
|||||||
/**
|
/**
|
||||||
* If the current direction is the player face direction,
|
* If the current direction is the player face direction,
|
||||||
* this method return the direction of the left of the head.
|
* this method return the direction of the left of the head.
|
||||||
|
* @return the vector pointing on the left, as if this vector was the front orientation of a player head.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector getLeftDirection() {
|
public DirectionalVector getLeftDirection() {
|
||||||
return new DirectionalVector(
|
return new DirectionalVector(
|
||||||
@ -206,6 +219,7 @@ public class DirectionalVector {
|
|||||||
/**
|
/**
|
||||||
* If the current direction is the player face direction,
|
* If the current direction is the player face direction,
|
||||||
* this method return the direction of the right of the head.
|
* this method return the direction of the right of the head.
|
||||||
|
* @return the vector pointing on the right, as if this vector was the front orientation of a player head.
|
||||||
*/
|
*/
|
||||||
public DirectionalVector getRightDirection() {
|
public DirectionalVector getRightDirection() {
|
||||||
return new DirectionalVector(
|
return new DirectionalVector(
|
||||||
|
@ -2,24 +2,29 @@ package fr.pandacube.lib.paper.geometry;
|
|||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
import org.bukkit.util.RayTraceResult;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class related to geometry and Minecraft.
|
||||||
|
*/
|
||||||
public class GeometryUtil {
|
public class GeometryUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value equal to <code>{@link Math#PI}</code>.
|
* Value equal to <code>{@link Math#PI}</code>.
|
||||||
*/
|
*/
|
||||||
public static final double PI = Math.PI;
|
static final double PI = Math.PI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value equal to <code>{@link Math#PI} / 2</code>.
|
* Value equal to <code>{@link Math#PI} / 2</code>.
|
||||||
*/
|
*/
|
||||||
public static final double PId2 = PI/2;
|
static final double PId2 = PI/2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value equal to <code>{@link Math#PI} * 2</code>.
|
* Value equal to <code>{@link Math#PI} * 2</code>.
|
||||||
*/
|
*/
|
||||||
public static final double PIx2 = PI*2;
|
static final double PIx2 = PI*2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -55,9 +60,21 @@ public class GeometryUtil {
|
|||||||
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
||||||
*/
|
*/
|
||||||
public static final double PLAYER_EYE_HEIGHT_SNEAK = 1.27;
|
public static final double PLAYER_EYE_HEIGHT_SNEAK = 1.27;
|
||||||
|
/**
|
||||||
|
* The size of a skin pixel.
|
||||||
|
*/
|
||||||
public static final double PLAYER_SKIN_PIXEL_SIZE = PLAYER_SKIN_HEIGHT / 32;
|
public static final double PLAYER_SKIN_PIXEL_SIZE = PLAYER_SKIN_HEIGHT / 32;
|
||||||
|
/**
|
||||||
|
* The height of the center of rotation of the head, that is the distance between neck and the player's foot.
|
||||||
|
*/
|
||||||
public static final double PLAYER_HEAD_ROTATION_HEIGHT = PLAYER_SKIN_PIXEL_SIZE * 24;
|
public static final double PLAYER_HEAD_ROTATION_HEIGHT = PLAYER_SKIN_PIXEL_SIZE * 24;
|
||||||
|
/**
|
||||||
|
* The height of the center of rotation of the head, that is the distance between neck and the player's foot, but when the player is sneaking.
|
||||||
|
*/
|
||||||
public static final double PLAYER_HEAD_ROTATION_HEIGHT_SNEAK = PLAYER_HEAD_ROTATION_HEIGHT - (PLAYER_SKIN_HEIGHT - PLAYER_SKIN_HEIGHT_SNEAK);
|
public static final double PLAYER_HEAD_ROTATION_HEIGHT_SNEAK = PLAYER_HEAD_ROTATION_HEIGHT - (PLAYER_SKIN_HEIGHT - PLAYER_SKIN_HEIGHT_SNEAK);
|
||||||
|
/**
|
||||||
|
* The size of the first layer of the players head.
|
||||||
|
*/
|
||||||
public static final double PLAYER_HEAD_SIZE = PLAYER_SKIN_PIXEL_SIZE * 8;
|
public static final double PLAYER_HEAD_SIZE = PLAYER_SKIN_PIXEL_SIZE * 8;
|
||||||
|
|
||||||
|
|
||||||
@ -72,11 +89,10 @@ public class GeometryUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link Location}s of the 8 vertex of the player head<br/>
|
* Get the {@link Location}s of the 8 vertex of the player head<br/>
|
||||||
* This method only work if the player is standing up
|
* This method only work if the player is standing up
|
||||||
* (not dead, not gliding, not sleeping).
|
* (not dead, not gliding, not sleeping, not swimming).
|
||||||
* @param playerLocation the location of the player, generally provided by {@link Player#getLocation()}
|
* @param playerLocation the location of the player, generally provided by {@link Player#getLocation()}
|
||||||
* @param isSneaking if the player is sneaking. Generally {@link Player#isSneaking()}
|
* @param isSneaking if the player is sneaking. Generally {@link Player#isSneaking()}
|
||||||
* @return an array of 8 {@link Location}s with x, y, and z values filled (yaw and pitch are ignored).
|
* @return an array of 8 {@link Location}s with x, y, and z values filled (yaw and pitch are ignored).
|
||||||
@ -129,27 +145,22 @@ public class GeometryUtil {
|
|||||||
/**
|
/**
|
||||||
* Check if the path from <i>start</i> location to <i>end</i> pass through
|
* Check if the path from <i>start</i> location to <i>end</i> pass through
|
||||||
* the axis aligned bounding box defined by <i>min</i> and <i>max</i>.
|
* the axis aligned bounding box defined by <i>min</i> and <i>max</i>.
|
||||||
|
* @param start the start of the path.
|
||||||
|
* @param end the end of the path.
|
||||||
|
* @param min the min of the bounding box.
|
||||||
|
* @param max the max of the bounding box.
|
||||||
|
* @return true if the path intersects the bounding box.
|
||||||
|
* @deprecated use {@link BoundingBox#rayTrace(Vector, Vector, double)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static boolean hasIntersection(Vector start, Vector end, Vector min, Vector max) {
|
public static boolean hasIntersection(Vector start, Vector end, Vector min, Vector max) {
|
||||||
final double epsilon = 0.0001f;
|
RayTraceResult res = BoundingBox.of(min, max).rayTrace(start, end.clone().subtract(start), end.distance(start));
|
||||||
|
return res != null;
|
||||||
Vector d = end.clone().subtract(start).multiply(0.5);
|
|
||||||
Vector e = max.clone().subtract(min).multiply(0.5);
|
|
||||||
Vector c = start.clone().add(d).subtract(min.clone().add(max).multiply(0.5));
|
|
||||||
Vector ad = d.clone();
|
|
||||||
ad.setX(Math.abs(ad.getX()));
|
|
||||||
ad.setY(Math.abs(ad.getY()));
|
|
||||||
ad.setZ(Math.abs(ad.getZ()));
|
|
||||||
|
|
||||||
return !(
|
|
||||||
Math.abs(c.getX()) > e.getX() + ad.getX()
|
|
||||||
|| Math.abs(c.getY()) > e.getY() + ad.getY()
|
|
||||||
|| Math.abs(c.getZ()) > e.getX() + ad.getZ()
|
|
||||||
|| Math.abs(d.getY() * c.getZ() - d.getZ() * c.getY()) > e.getY() * ad.getZ() + e.getZ() * ad.getY() + epsilon
|
|
||||||
|| Math.abs(d.getZ() * c.getX() - d.getX() * c.getZ()) > e.getZ() * ad.getX() + e.getX() * ad.getZ() + epsilon
|
|
||||||
|| Math.abs(d.getX() * c.getY() - d.getY() * c.getX()) > e.getX() * ad.getY() + e.getY() * ad.getX() + epsilon
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private GeometryUtil() {}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@ package fr.pandacube.lib.paper.geometry.blocks;
|
|||||||
|
|
||||||
import fr.pandacube.lib.util.RandomUtil;
|
import fr.pandacube.lib.util.RandomUtil;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
@ -30,10 +29,19 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
volume = original.volume;
|
volume = original.volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link AABBBlock} based on the two provided Bukkit's {@link Vector}.
|
||||||
|
* @param p1 the first vector.
|
||||||
|
* @param p2 the second vector.
|
||||||
|
*/
|
||||||
public AABBBlock(Vector p1, Vector p2) {
|
public AABBBlock(Vector p1, Vector p2) {
|
||||||
this(p1.getBlockX(), p1.getBlockY(), p1.getBlockZ(), p2.getBlockX(), p2.getBlockY(), p2.getBlockZ());
|
this(p1.getBlockX(), p1.getBlockY(), p1.getBlockZ(), p2.getBlockX(), p2.getBlockY(), p2.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link AABBBlock} based on the provided Bukkit's {@link BoundingBox}.
|
||||||
|
* @param bb the bounding box.
|
||||||
|
*/
|
||||||
public AABBBlock(BoundingBox bb) {
|
public AABBBlock(BoundingBox bb) {
|
||||||
pos1 = bb.getMin();
|
pos1 = bb.getMin();
|
||||||
pos2 = bb.getMax();
|
pos2 = bb.getMax();
|
||||||
@ -42,14 +50,34 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
bukkitBoundingBox = bb;
|
bukkitBoundingBox = bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link AABBBlock} based on the two provided Bukkit's {@link Location}.
|
||||||
|
* The worlds defined in the provided locations are ignored.
|
||||||
|
* @param l1 the first location.
|
||||||
|
* @param l2 the second location.
|
||||||
|
*/
|
||||||
public AABBBlock(Location l1, Location l2) {
|
public AABBBlock(Location l1, Location l2) {
|
||||||
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link AABBBlock} based on the two provided Bukkit's {@link BlockVector}.
|
||||||
|
* @param l1 the first block vector.
|
||||||
|
* @param l2 the second block vector.
|
||||||
|
*/
|
||||||
public AABBBlock(BlockVector l1, BlockVector l2) {
|
public AABBBlock(BlockVector l1, BlockVector l2) {
|
||||||
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link AABBBlock} based on the individual coordinates of the 2 vectors.
|
||||||
|
* @param p1x the x value of the first vector.
|
||||||
|
* @param p1y the y value of the first vector.
|
||||||
|
* @param p1z the z value of the first vector.
|
||||||
|
* @param p2x the x value of the second vector.
|
||||||
|
* @param p2y the y value of the second vector.
|
||||||
|
* @param p2z the z value of the second vector.
|
||||||
|
*/
|
||||||
public AABBBlock(int p1x, int p1y, int p1z, int p2x, int p2y, int p2z) {
|
public AABBBlock(int p1x, int p1y, int p1z, int p2x, int p2y, int p2z) {
|
||||||
/*
|
/*
|
||||||
* Prends les points extérieurs permettant de former un bounding box englobant
|
* Prends les points extérieurs permettant de former un bounding box englobant
|
||||||
@ -74,22 +102,45 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the "minimum" {@link Vector}, that is the vector with the lowest coordinates that is inside this bounding box.
|
||||||
|
* @return the minimum vector.
|
||||||
|
*/
|
||||||
public Vector getMin() {
|
public Vector getMin() {
|
||||||
return pos1.clone();
|
return pos1.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the "maximum" {@link Vector}, that is the vector with the highest coordinates that is inside this bounding box.
|
||||||
|
* @return the maximum vector.
|
||||||
|
*/
|
||||||
public Vector getMax() {
|
public Vector getMax() {
|
||||||
return pos2.clone();
|
return pos2.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link BlockVector} with the lowest coordinates in this bounding box.
|
||||||
|
* @return the minimum block vector.
|
||||||
|
*/
|
||||||
public BlockVector getMinBlock() {
|
public BlockVector getMinBlock() {
|
||||||
return pos1.toBlockVector();
|
return pos1.toBlockVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link BlockVector} with the highest coordinates in this bounding box.
|
||||||
|
* @return the maximum block vector.
|
||||||
|
*/
|
||||||
public BlockVector getMaxBlock() {
|
public BlockVector getMaxBlock() {
|
||||||
return pos2.clone().add(new Vector(-1, -1, -1)).toBlockVector();
|
return pos2.clone().add(new Vector(-1, -1, -1)).toBlockVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a new {@link AABBBlock} with its coordinates shifted by the provided amount.
|
||||||
|
* @param x the x shift.
|
||||||
|
* @param y the y shift.
|
||||||
|
* @param z the z shift.
|
||||||
|
* @return a shifted bounding box.
|
||||||
|
*/
|
||||||
public AABBBlock shift(int x, int y, int z) {
|
public AABBBlock shift(int x, int y, int z) {
|
||||||
return new AABBBlock(this, x, y, z);
|
return new AABBBlock(this, x, y, z);
|
||||||
}
|
}
|
||||||
@ -101,7 +152,6 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public boolean overlaps(BoundingBox bb) {
|
public boolean overlaps(BoundingBox bb) {
|
||||||
return asBukkitBoundingBox().overlaps(bb);
|
return asBukkitBoundingBox().overlaps(bb);
|
||||||
}
|
}
|
||||||
@ -110,6 +160,10 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
return asBukkitBoundingBox().contains(v);
|
return asBukkitBoundingBox().contains(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the coordinate of the center of this bounding box.
|
||||||
|
* @return the center of this bounding box.
|
||||||
|
*/
|
||||||
public Vector getCenter() {
|
public Vector getCenter() {
|
||||||
return center.clone();
|
return center.clone();
|
||||||
}
|
}
|
||||||
@ -118,6 +172,10 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Bukkit equivalent of this bounding box.
|
||||||
|
* @return a {@link BoundingBox} corresponding to this {@link AABBBlock}.
|
||||||
|
*/
|
||||||
public BoundingBox asBukkitBoundingBox() {
|
public BoundingBox asBukkitBoundingBox() {
|
||||||
if (bukkitBoundingBox == null) {
|
if (bukkitBoundingBox == null) {
|
||||||
bukkitBoundingBox = new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(),
|
bukkitBoundingBox = new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(),
|
||||||
@ -134,7 +192,7 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<BlockVector> iterator() {
|
public @NotNull Iterator<BlockVector> iterator() {
|
||||||
return new Iterator<>() {
|
return new Iterator<>() {
|
||||||
private int x = pos1.getBlockX(),
|
private int x = pos1.getBlockX(),
|
||||||
y = pos1.getBlockY(),
|
y = pos1.getBlockY(),
|
||||||
@ -164,22 +222,6 @@ public class AABBBlock implements BlockSet, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Iterable<Block> asBlockIterable(World w) {
|
|
||||||
return () -> new Iterator<>() {
|
|
||||||
final Iterator<BlockVector> nested = AABBBlock.this.iterator();
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return nested.hasNext();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Block next() {
|
|
||||||
BlockVector bv = nested.next();
|
|
||||||
return w.getBlockAt(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "{(" + pos1.getBlockX() +
|
return "{(" + pos1.getBlockX() +
|
||||||
|
@ -12,12 +12,22 @@ import java.util.Collection;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group of {@link AABBBlock}.
|
||||||
|
*/
|
||||||
public class AABBBlockGroup implements BlockSet {
|
public class AABBBlockGroup implements BlockSet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of {@link AABBBlock} contained in this group. This list is unmodifiable.
|
||||||
|
*/
|
||||||
public final List<AABBBlock> subAABB;
|
public final List<AABBBlock> subAABB;
|
||||||
|
|
||||||
private final AABBBlock englobingAABB;
|
private final AABBBlock englobingAABB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link AABBBlockGroup}.
|
||||||
|
* @param in the list of {@link AABBBlock} that will be contained in this group.
|
||||||
|
*/
|
||||||
public AABBBlockGroup(Collection<AABBBlock> in) {
|
public AABBBlockGroup(Collection<AABBBlock> in) {
|
||||||
if (in.isEmpty())
|
if (in.isEmpty())
|
||||||
throw new IllegalArgumentException("Provided collection must not be empty.");
|
throw new IllegalArgumentException("Provided collection must not be empty.");
|
||||||
@ -25,6 +35,10 @@ public class AABBBlockGroup implements BlockSet {
|
|||||||
englobingAABB = initEnglobingAABB();
|
englobingAABB = initEnglobingAABB();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link AABBBlockGroup}.
|
||||||
|
* @param in an array of {@link AABBBlock} that will be contained in this group.
|
||||||
|
*/
|
||||||
public AABBBlockGroup(AABBBlock... in) {
|
public AABBBlockGroup(AABBBlock... in) {
|
||||||
this(Arrays.asList(in));
|
this(Arrays.asList(in));
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,58 @@
|
|||||||
package fr.pandacube.lib.paper.geometry.blocks;
|
package fr.pandacube.lib.paper.geometry.blocks;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a set of blocks in a world.
|
||||||
|
*/
|
||||||
public interface BlockSet extends Iterable<BlockVector> {
|
public interface BlockSet extends Iterable<BlockVector> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a random coordinate that is inside this block set.
|
||||||
|
* @return a random coordinate inside this block set.
|
||||||
|
*/
|
||||||
Vector getRandomPosition();
|
Vector getRandomPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the volume, in blocks (or cubic meters), of this block set.
|
||||||
|
* @return the volume of this block set.
|
||||||
|
*/
|
||||||
long getVolume();
|
long getVolume();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the englobing bounding box if this block set.
|
||||||
|
* @return the englobing bounding box if this block set.
|
||||||
|
*/
|
||||||
AABBBlock getEnglobingAABB();
|
AABBBlock getEnglobingAABB();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if this block set overlaps the provided bounding box.
|
||||||
|
* @param bb the provided bounding box
|
||||||
|
* @return true if its overlaps, false otherwise.
|
||||||
|
*/
|
||||||
boolean overlaps(BoundingBox bb);
|
boolean overlaps(BoundingBox bb);
|
||||||
|
/**
|
||||||
|
* Tells if this block set overlaps the bounding box of the provided entity.
|
||||||
|
* @param e the provided entity.
|
||||||
|
* @return true if its overlaps, false otherwise.
|
||||||
|
*/
|
||||||
default boolean overlaps(Entity e) {
|
default boolean overlaps(Entity e) {
|
||||||
return overlaps(e.getBoundingBox());
|
return overlaps(e.getBoundingBox());
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Tells if this block set overlaps the provided one. that is there is at least one block in common.
|
||||||
|
* @param bs the provided block set.
|
||||||
|
* @return true if its overlaps, false otherwise.
|
||||||
|
*/
|
||||||
default boolean overlaps(BlockSet bs) {
|
default boolean overlaps(BlockSet bs) {
|
||||||
if (this instanceof AABBBlock b1) {
|
if (this instanceof AABBBlock b1) {
|
||||||
if (bs instanceof AABBBlock b2)
|
if (bs instanceof AABBBlock b2)
|
||||||
@ -37,20 +72,78 @@ public interface BlockSet extends Iterable<BlockVector> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the provided vector is inside this bounding box.
|
||||||
|
* @param v the vector.
|
||||||
|
* @return true if its inside, false otherwise.
|
||||||
|
*/
|
||||||
boolean isInside(Vector v);
|
boolean isInside(Vector v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the provided location is inside this bounding box.
|
||||||
|
* The world of the location is ignored.
|
||||||
|
* @param l the location.
|
||||||
|
* @return true if its inside, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isInside(Location l) {
|
default boolean isInside(Location l) {
|
||||||
return isInside(l.toVector());
|
return isInside(l.toVector());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the provided block is inside this bounding box.
|
||||||
|
* The world of the block is ignored.
|
||||||
|
* @param b the block.
|
||||||
|
* @return true if its inside, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isInside(Block b) {
|
default boolean isInside(Block b) {
|
||||||
return isInside(b.getLocation().add(.5, .5, .5));
|
return isInside(b.getLocation().add(.5, .5, .5));
|
||||||
}
|
}
|
||||||
default boolean isInside(Entity p) {
|
|
||||||
return isInside(p.getLocation());
|
/**
|
||||||
|
* Tells if the provided entity is inside this bounding box.
|
||||||
|
* It calls {@link #isInside(Location)} using the returned value of {@link Entity#getLocation()}.
|
||||||
|
* The world of the entity is ignored.
|
||||||
|
* @param e the entity.
|
||||||
|
* @return true if its inside, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isInside(Entity e) {
|
||||||
|
return isInside(e.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an iterator iterating through all the blocks of this block set.
|
||||||
|
* @param w the world of the blocks.
|
||||||
|
* @return a new iterator.
|
||||||
|
*/
|
||||||
|
default Iterable<Block> asBlockIterable(World w) {
|
||||||
|
return () -> new Iterator<>() {
|
||||||
|
final Iterator<BlockVector> nested = BlockSet.this.iterator();
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return nested.hasNext();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Block next() {
|
||||||
|
BlockVector bv = nested.next();
|
||||||
|
return w.getBlockAt(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the two block set overlap each other.
|
||||||
|
* This method works on any implementation of this interface, but they should override the
|
||||||
|
* {@link #overlaps(BlockSet)} method to provide a more optimized code.
|
||||||
|
* @param bs1 the first block set.
|
||||||
|
* @param bs2 the second block set.
|
||||||
|
* @return true if the two block set overlap, false otherwise.
|
||||||
|
*/
|
||||||
static boolean overlap(BlockSet bs1, BlockSet bs2) {
|
static boolean overlap(BlockSet bs1, BlockSet bs2) {
|
||||||
if (!bs1.getEnglobingAABB().overlaps(bs2.getEnglobingAABB()))
|
if (!bs1.getEnglobingAABB().overlaps(bs2.getEnglobingAABB()))
|
||||||
return false;
|
return false;
|
||||||
|
@ -69,7 +69,7 @@ public class GUIHotBar implements Listener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the hot bar elements to this player, or update them if applicable.
|
* Add the hot bar elements to this player, or update them if applicable.
|
||||||
*
|
* <br>
|
||||||
* The player is automatically removed when they quit. You can remove it before by calling {@link #removePlayer(Player, boolean)}.
|
* The player is automatically removed when they quit. You can remove it before by calling {@link #removePlayer(Player, boolean)}.
|
||||||
*/
|
*/
|
||||||
public void addPlayer(Player p) {
|
public void addPlayer(Player p) {
|
||||||
@ -144,7 +144,7 @@ public class GUIHotBar implements Listener {
|
|||||||
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||||
if (!currentPlayers.contains(event.getPlayer()))
|
if (!currentPlayers.contains(event.getPlayer()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ public class GUIHotBar implements Listener {
|
|||||||
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
void onPlayerInteract(PlayerInteractEvent event) {
|
||||||
if (!currentPlayers.contains(event.getPlayer()))
|
if (!currentPlayers.contains(event.getPlayer()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ public class GUIHotBar implements Listener {
|
|||||||
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onInventoryClick(InventoryClickEvent event) {
|
void onInventoryClick(InventoryClickEvent event) {
|
||||||
if (event.getClickedInventory() == null || !(event.getClickedInventory() instanceof PlayerInventory inv))
|
if (event.getClickedInventory() == null || !(event.getClickedInventory() instanceof PlayerInventory inv))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -213,20 +213,20 @@ public class GUIHotBar implements Listener {
|
|||||||
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
removePlayer(event.getPlayer());
|
removePlayer(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
void onPlayerDeath(PlayerDeathEvent event) {
|
||||||
if (!currentPlayers.contains(event.getEntity()))
|
if (!currentPlayers.contains(event.getEntity()))
|
||||||
return;
|
return;
|
||||||
event.getDrops().removeAll(itemsAndSetters.keySet());
|
event.getDrops().removeAll(itemsAndSetters.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerRespawn(PlayerRespawnEvent event) {
|
void onPlayerRespawn(PlayerRespawnEvent event) {
|
||||||
if (!currentPlayers.contains(event.getPlayer()))
|
if (!currentPlayers.contains(event.getPlayer()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@ public class GUIInventory implements Listener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the action when the player closes the inventory window.
|
||||||
|
* @param closeEventAction the action to run.
|
||||||
|
*/
|
||||||
protected void setCloseEvent(Consumer<InventoryCloseEvent> closeEventAction) {
|
protected void setCloseEvent(Consumer<InventoryCloseEvent> closeEventAction) {
|
||||||
onCloseEvent = closeEventAction;
|
onCloseEvent = closeEventAction;
|
||||||
}
|
}
|
||||||
@ -190,11 +194,11 @@ public class GUIInventory implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the number of inventory line needed to show the provided number of slot.
|
||||||
|
* @param nb the number of slot.
|
||||||
|
* @return the number of line.
|
||||||
|
*/
|
||||||
public static int nbLineForNbElements(int nb) {
|
public static int nbLineForNbElements(int nb) {
|
||||||
return nb / 9 + ((nb % 9 == 0) ? 0 : 1);
|
return nb / 9 + ((nb % 9 == 0) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,25 @@ import org.bukkit.event.Listener;
|
|||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
|
|
||||||
// simplified version of https://github.com/Camotoy/BambooCollisionFix/tree/c7d7d5327791cbb416d106de0b9eb0bf2461acbd/src/main/java/net/camotoy/bamboocollisionfix
|
// simplified version of
|
||||||
// we remove the bamboo bounding box due to bedrock clients not having the same placement for bamboos
|
|
||||||
|
/**
|
||||||
|
* Disables the server-side bounding box of the bamboo stalk blocks.
|
||||||
|
* Bamboo stalks are the thin bamboo plants that are shifted differently, depending on the X and Z coordinate.
|
||||||
|
* This X/Z dependent shift is different between Java and Bedrock implementation.
|
||||||
|
* But since this block as a collision box and players cannot go through them, Bedrock players are often rolled back
|
||||||
|
* when they are walking around bamboo stalk due to the server being in Java Edition and thinking the player tries to
|
||||||
|
* move through the bamboo.
|
||||||
|
* To avoid this issue, we reduce to 0 the size of the bounding box on the server.
|
||||||
|
* <br>
|
||||||
|
* See <a href="https://github.com/Camotoy/BambooCollisionFix/tree/c7d7d5327791cbb416d106de0b9eb0bf2461acbd/src/main/java/net/camotoy/bamboocollisionfix">the original implementation</a>.
|
||||||
|
*/
|
||||||
public final class BedrockBambooCollisionFixer implements Listener {
|
public final class BedrockBambooCollisionFixer implements Listener {
|
||||||
private final BoundingBox originalBambooBoundingBox = new BoundingBox(6.5D / 16D, 0.0D, 6.5D / 16.0D, 9.5D / 16.0D, 1D, 9.5D / 16.0D);
|
private final BoundingBox originalBambooBoundingBox = new BoundingBox(6.5D / 16D, 0.0D, 6.5D / 16.0D, 9.5D / 16.0D, 1D, 9.5D / 16.0D);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link BedrockBambooCollisionFixer}. There is no need for multiple instances.
|
||||||
|
*/
|
||||||
public BedrockBambooCollisionFixer() {
|
public BedrockBambooCollisionFixer() {
|
||||||
// Make the bamboo block have zero collision.
|
// Make the bamboo block have zero collision.
|
||||||
try {
|
try {
|
||||||
@ -34,7 +48,7 @@ public final class BedrockBambooCollisionFixer implements Listener {
|
|||||||
* our ability.
|
* our ability.
|
||||||
*/
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
void onBlockPlace(BlockPlaceEvent event) {
|
||||||
if (event.getBlockPlaced().getBlockData().getMaterial().equals(Material.BAMBOO)) {
|
if (event.getBlockPlaced().getBlockData().getMaterial().equals(Material.BAMBOO)) {
|
||||||
BoundingBox currentBambooBoundingBox = originalBambooBoundingBox.clone().shift(event.getBlockPlaced().getLocation());
|
BoundingBox currentBambooBoundingBox = originalBambooBoundingBox.clone().shift(event.getBlockPlaced().getLocation());
|
||||||
for (LivingEntity e : event.getBlock().getLocation().getNearbyLivingEntities(5)) {
|
for (LivingEntity e : event.getBlock().getLocation().getNearbyLivingEntities(5)) {
|
||||||
|
@ -5,8 +5,17 @@ import org.bukkit.World;
|
|||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a XZ chunk coordinates.
|
||||||
|
* @param x the x coordinate.
|
||||||
|
* @param z the z coordinate.
|
||||||
|
*/
|
||||||
public record ChunkCoord(int x, int z) {
|
public record ChunkCoord(int x, int z) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link ChunkCoord} of a {@link Chunk}.
|
||||||
|
* @param c the chunks from which to get its coordinates.
|
||||||
|
*/
|
||||||
public ChunkCoord(Chunk c) {
|
public ChunkCoord(Chunk c) {
|
||||||
this(c.getX(), c.getZ());
|
this(c.getX(), c.getZ());
|
||||||
}
|
}
|
||||||
@ -16,22 +25,48 @@ public record ChunkCoord(int x, int z) {
|
|||||||
return "(" + x + ";" + z + ")";
|
return "(" + x + ";" + z + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the coordinates of the region file.
|
||||||
|
* @return the {@link RegionCoord}.
|
||||||
|
*/
|
||||||
public RegionCoord getRegionCoord() {
|
public RegionCoord getRegionCoord() {
|
||||||
return new RegionCoord(x >> 5, z >> 5);
|
return new RegionCoord(x >> 5, z >> 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Chunk} at this coordinate in the provided World.
|
||||||
|
* @param w the {@link World}.
|
||||||
|
* @return a chunk, using {@link World#getChunkAt(int, int)}.
|
||||||
|
*/
|
||||||
public Chunk getChunk(World w) {
|
public Chunk getChunk(World w) {
|
||||||
return w.getChunkAt(x, z);
|
return w.getChunkAt(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Chunk} at this coordinate in the provided World.
|
||||||
|
* @param w the {@link World}.
|
||||||
|
* @param generate Whether the chunk should be fully generated or not.
|
||||||
|
* @return a chunk, using {@link World#getChunkAt(int, int, boolean)}.
|
||||||
|
*/
|
||||||
public Chunk getChunk(World w, boolean generate) {
|
public Chunk getChunk(World w, boolean generate) {
|
||||||
return w.getChunkAt(x, z, generate);
|
return w.getChunkAt(x, z, generate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Chunk} at this coordinate in the provided World, asynchronously.
|
||||||
|
* @param w the {@link World}.
|
||||||
|
* @return a completable future of a chunk, using {@link World#getChunkAtAsync(int, int)}.
|
||||||
|
*/
|
||||||
public CompletableFuture<Chunk> getChunkAsync(World w) {
|
public CompletableFuture<Chunk> getChunkAsync(World w) {
|
||||||
return w.getChunkAtAsync(x, z);
|
return w.getChunkAtAsync(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Chunk} at this coordinate in the provided World, asynchronously.
|
||||||
|
* @param w the {@link World}.
|
||||||
|
* @param generate Whether the chunk should be fully generated or not.
|
||||||
|
* @return a completable future of a chunk, using {@link World#getChunkAtAsync(int, int, boolean)}.
|
||||||
|
*/
|
||||||
public CompletableFuture<Chunk> getChunkAsync(World w, boolean generate) {
|
public CompletableFuture<Chunk> getChunkAsync(World w, boolean generate) {
|
||||||
return w.getChunkAtAsync(x, z, generate);
|
return w.getChunkAtAsync(x, z, generate);
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,42 @@ package fr.pandacube.lib.paper.world;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a XZ region coordinates.
|
||||||
|
* @param x the x coordinate.
|
||||||
|
* @param z the z coordinate.
|
||||||
|
*/
|
||||||
public record RegionCoord(int x, int z) {
|
public record RegionCoord(int x, int z) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the <a href="https://en.wikipedia.org/wiki/Chebyshev_distance">Chebyshev distance</a> from this region to the center (0, 0) region.
|
||||||
|
* The unit is one region, i.e. the region (1, 0) has a distance of 1 from the center.
|
||||||
|
* @return the distance.
|
||||||
|
*/
|
||||||
public int distToCenter() {
|
public int distToCenter() {
|
||||||
return Math.max(Math.abs(x), Math.abs(z));
|
return Math.max(Math.abs(x), Math.abs(z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file name that store this region.
|
||||||
|
* @return the region file name.
|
||||||
|
*/
|
||||||
public String getFileName() {
|
public String getFileName() {
|
||||||
return "r." + x + "." + z + ".mca";
|
return "r." + x + "." + z + ".mca";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the chunk with the lowest coordinates of this region.
|
||||||
|
* @return the chunk with the lowest coordinates of this region.
|
||||||
|
*/
|
||||||
public ChunkCoord getMinChunk() {
|
public ChunkCoord getMinChunk() {
|
||||||
return new ChunkCoord(x << 5, z << 5);
|
return new ChunkCoord(x << 5, z << 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the chunk with the highest coordinates of this region.
|
||||||
|
* @return the chunk with the highest coordinates of this region.
|
||||||
|
*/
|
||||||
public ChunkCoord getMaxChunk() {
|
public ChunkCoord getMaxChunk() {
|
||||||
return new ChunkCoord(x << 5 | 31, z << 5 | 31);
|
return new ChunkCoord(x << 5 | 31, z << 5 | 31);
|
||||||
}
|
}
|
||||||
@ -25,6 +47,12 @@ public record RegionCoord(int x, int z) {
|
|||||||
|
|
||||||
private static final Pattern REGION_FILE_NAME_PATTERN = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
|
private static final Pattern REGION_FILE_NAME_PATTERN = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the coordinate from a region file name.
|
||||||
|
* @param name the name of the file to parse
|
||||||
|
* @return a new {@link RegionCoord}
|
||||||
|
* @throws IllegalArgumentException if the provided file name is not a valid MCA file name (including the extension).
|
||||||
|
*/
|
||||||
public static RegionCoord fromFileName(String name) {
|
public static RegionCoord fromFileName(String name) {
|
||||||
Matcher m = REGION_FILE_NAME_PATTERN.matcher(name);
|
Matcher m = REGION_FILE_NAME_PATTERN.matcher(name);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user