diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml new file mode 100644 index 00000000..94366ef6 --- /dev/null +++ b/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/checkstyle.xml b/checkstyle.xml index 8b624164..c658f38f 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -20,6 +20,9 @@ + + + diff --git a/pom.xml b/pom.xml index 0620a5ca..d28c3afc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.bukkit bukkit - 1.18.1-R0.1-SNAPSHOT + 1.18.2-R0.1-SNAPSHOT jar Bukkit @@ -48,7 +48,7 @@ com.google.code.gson gson - 2.8.8 + 2.8.9 compile @@ -223,6 +223,7 @@ checkstyle.xml + checkstyle-suppressions.xml true diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index be61e4f5..43937284 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -27,6 +27,7 @@ import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.SpawnCategory; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.generator.ChunkGenerator; @@ -44,6 +45,7 @@ import org.bukkit.permissions.Permissible; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.profile.PlayerProfile; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scoreboard.ScoreboardManager; import org.bukkit.structure.StructureManager; @@ -411,7 +413,9 @@ public final class Bukkit { * Minecraft default: 400. * * @return the default ticks per animal spawns value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerAnimalSpawns() { return server.getTicksPerAnimalSpawns(); } @@ -434,7 +438,9 @@ public final class Bukkit { * Minecraft default: 1. * * @return the default ticks per monsters spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerMonsterSpawns() { return server.getTicksPerMonsterSpawns(); } @@ -456,7 +462,9 @@ public final class Bukkit { * Minecraft default: 1. * * @return the default ticks per water mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerWaterSpawns() { return server.getTicksPerWaterSpawns(); } @@ -478,7 +486,9 @@ public final class Bukkit { * Minecraft default: 1. * * @return the default ticks per ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerAmbientSpawns() { return server.getTicksPerAmbientSpawns(); } @@ -500,10 +510,13 @@ public final class Bukkit { * Minecraft default: 1. * * @return the default ticks per water ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerWaterAmbientSpawns() { return server.getTicksPerAmbientSpawns(); } + /** * Gets the default ticks per water underground creature spawns value. *

@@ -521,11 +534,38 @@ public final class Bukkit { * Minecraft default: 1. * * @return the default ticks per water underground creature spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public static int getTicksPerWaterUndergroundCreatureSpawns() { return server.getTicksPerWaterUndergroundCreatureSpawns(); } + /** + * Gets the default ticks per {@link SpawnCategory} spawns value. + *

+ * Example Usage: + *

    + *
  • A value of 1 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * every tick. + *
  • A value of 400 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * every 400th tick. + *
  • A value below 0 will be reset back to Minecraft's default. + *
+ *

+ * Note: If set to 0, {@link SpawnCategory} mobs spawning will be disabled. + *

+ * Minecraft default: 1. + *
+ * Note: the {@link SpawnCategory#MISC} are not consider. + * + * @param spawnCategory the category of spawn + * @return the default ticks per {@link SpawnCategory} mobs spawn value + */ + public static int getTicksPerSpawns(@NotNull SpawnCategory spawnCategory) { + return server.getTicksPerSpawns(spawnCategory); + } + /** * Gets a player object by the given username. *

@@ -1048,6 +1088,45 @@ public final class Bukkit { return server.getOfflinePlayer(id); } + /** + * Creates a new {@link PlayerProfile}. + * + * @param uniqueId the unique id + * @param name the name + * @return the new PlayerProfile + * @throws IllegalArgumentException if both the unique id is + * null and the name is null or blank + */ + @NotNull + public static PlayerProfile createPlayerProfile(@Nullable UUID uniqueId, @Nullable String name) { + return server.createPlayerProfile(uniqueId, name); + } + + /** + * Creates a new {@link PlayerProfile}. + * + * @param uniqueId the unique id + * @return the new PlayerProfile + * @throws IllegalArgumentException if the unique id is null + */ + @NotNull + public static PlayerProfile createPlayerProfile(@NotNull UUID uniqueId) { + return server.createPlayerProfile(uniqueId); + } + + /** + * Creates a new {@link PlayerProfile}. + * + * @param name the name + * @return the new PlayerProfile + * @throws IllegalArgumentException if the name is null or + * blank + */ + @NotNull + public static PlayerProfile createPlayerProfile(@NotNull String name) { + return server.createPlayerProfile(name); + } + /** * Gets a set containing all current IPs that are banned. * @@ -1282,7 +1361,9 @@ public final class Bukkit { * chunk. * * @return the monster spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getMonsterSpawnLimit() { return server.getMonsterSpawnLimit(); } @@ -1292,7 +1373,9 @@ public final class Bukkit { * chunk. * * @return the animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getAnimalSpawnLimit() { return server.getAnimalSpawnLimit(); } @@ -1302,7 +1385,9 @@ public final class Bukkit { * a chunk. * * @return the water animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getWaterAnimalSpawnLimit() { return server.getWaterAnimalSpawnLimit(); } @@ -1312,7 +1397,9 @@ public final class Bukkit { * in a chunk. * * @return the water ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getWaterAmbientSpawnLimit() { return server.getAmbientSpawnLimit(); } @@ -1320,8 +1407,11 @@ public final class Bukkit { /** * Get user-specified limit for number of water creature underground that can spawn * in a chunk. + * * @return the water underground creature limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getWaterUndergroundCreatureSpawnLimit() { return server.getWaterUndergroundCreatureSpawnLimit(); } @@ -1331,11 +1421,26 @@ public final class Bukkit { * a chunk. * * @return the ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated public static int getAmbientSpawnLimit() { return server.getAmbientSpawnLimit(); } + /** + * Gets user-specified limit for number of {@link SpawnCategory} mobs that can spawn in + * a chunk. + * + * Note: the {@link SpawnCategory#MISC} are not consider. + * + * @param spawnCategory the category spawn + * @return the {@link SpawnCategory} spawn limit + */ + public static int getSpawnLimit(@NotNull SpawnCategory spawnCategory) { + return server.getSpawnLimit(spawnCategory); + } + /** * Checks the current thread against the expected primary thread for the * server. diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java index 58313929..76e511e7 100644 --- a/src/main/java/org/bukkit/OfflinePlayer.java +++ b/src/main/java/org/bukkit/OfflinePlayer.java @@ -6,6 +6,7 @@ import org.bukkit.entity.AnimalTamer; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.permissions.ServerOperator; +import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +40,18 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio @NotNull public UUID getUniqueId(); + /** + * Gets a copy of the player's profile. + *

+ * If the player is online, the returned profile will be complete. + * Otherwise, only the unique id is guaranteed to be present. You can use + * {@link PlayerProfile#update()} to complete the returned profile. + * + * @return the player's profile + */ + @NotNull + PlayerProfile getPlayerProfile(); + /** * Checks if this player is banned or not * diff --git a/src/main/java/org/bukkit/Particle.java b/src/main/java/org/bukkit/Particle.java index dc514246..8d048a8f 100644 --- a/src/main/java/org/bukkit/Particle.java +++ b/src/main/java/org/bukkit/Particle.java @@ -36,13 +36,25 @@ public enum Particle { FLAME, LAVA, CLOUD, + /** + * Uses {@link Particle.DustOptions} as DataType + */ REDSTONE(DustOptions.class), SNOWBALL, SNOW_SHOVEL, SLIME, HEART, + /** + * Uses {@link ItemStack} as DataType + */ ITEM_CRACK(ItemStack.class), + /** + * Uses {@link BlockData} as DataType + */ BLOCK_CRACK(BlockData.class), + /** + * Uses {@link BlockData} as DataType + */ BLOCK_DUST(BlockData.class), WATER_DROP, MOB_APPEARANCE, @@ -50,6 +62,9 @@ public enum Particle { END_ROD, DAMAGE_INDICATOR, SWEEP_ATTACK, + /** + * Uses {@link BlockData} as DataType + */ FALLING_DUST(BlockData.class), TOTEM, SPIT, @@ -81,7 +96,13 @@ public enum Particle { LANDING_OBSIDIAN_TEAR, REVERSE_PORTAL, WHITE_ASH, + /** + * Uses {@link DustTransition} as DataType + */ DUST_COLOR_TRANSITION(DustTransition.class), + /** + * Uses {@link Vibration} as DataType + */ VIBRATION(Vibration.class), FALLING_SPORE_BLOSSOM, SPORE_BLOSSOM_AIR, @@ -97,10 +118,22 @@ public enum Particle { WAX_OFF, ELECTRIC_SPARK, SCRAPE, + /** + * Uses {@link BlockData} as DataType + */ BLOCK_MARKER(BlockData.class), // ----- Legacy Separator ----- + /** + * Uses {@link MaterialData} as DataType + */ LEGACY_BLOCK_CRACK(MaterialData.class), + /** + * Uses {@link MaterialData} as DataType + */ LEGACY_BLOCK_DUST(MaterialData.class), + /** + * Uses {@link MaterialData} as DataType + */ LEGACY_FALLING_DUST(MaterialData.class); private final Class dataType; diff --git a/src/main/java/org/bukkit/RegionAccessor.java b/src/main/java/org/bukkit/RegionAccessor.java index e285d544..a89fff5c 100644 --- a/src/main/java/org/bukkit/RegionAccessor.java +++ b/src/main/java/org/bukkit/RegionAccessor.java @@ -3,6 +3,7 @@ package org.bukkit; import java.util.Collection; import java.util.List; import java.util.Random; +import java.util.function.Predicate; import org.bukkit.block.Biome; import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; @@ -183,6 +184,26 @@ public interface RegionAccessor { */ boolean generateTree(@NotNull Location location, @NotNull Random random, @NotNull TreeType type, @Nullable Consumer stateConsumer); + /** + * Creates a tree at the given {@link Location} + *

+ * The provided predicate gets called for every block which gets changed + * as a result of the tree generation. When the predicate gets called no + * modifications to the world are done yet. Which means, that calling + * {@link #getBlockState(Location)} in the predicate will return the state + * of the block before the generation. + *

+ * If the predicate returns {@code true} the block gets set in the world. + * If it returns {@code false} the block won't get set in the world. + * + * @param location Location to spawn the tree + * @param random Random to use to generated the tree + * @param type Type of the tree to create + * @param statePredicate The predicate which should get used to test if a block should be set or not. + * @return true if the tree was created successfully, otherwise false + */ + boolean generateTree(@NotNull Location location, @NotNull Random random, @NotNull TreeType type, @Nullable Predicate statePredicate); + /** * Creates a entity at the given {@link Location} * diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 973e4712..fcbbb185 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -27,6 +27,7 @@ import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.SpawnCategory; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.generator.ChunkGenerator; @@ -45,6 +46,7 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.messaging.Messenger; import org.bukkit.plugin.messaging.PluginMessageRecipient; +import org.bukkit.profile.PlayerProfile; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scoreboard.ScoreboardManager; import org.bukkit.structure.StructureManager; @@ -340,7 +342,9 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 400. * * @return the default ticks per animal spawns value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerAnimalSpawns(); /** @@ -361,7 +365,9 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 1. * * @return the default ticks per monsters spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerMonsterSpawns(); /** @@ -381,7 +387,9 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 1. * * @return the default ticks per water mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerWaterSpawns(); /** @@ -401,7 +409,9 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 1. * * @return the default ticks per water ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerWaterAmbientSpawns(); /** @@ -421,7 +431,9 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 1. * * @return the default ticks per water underground creature spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerWaterUndergroundCreatureSpawns(); /** @@ -441,9 +453,34 @@ public interface Server extends PluginMessageRecipient { * Minecraft default: 1. * * @return the default ticks per ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public int getTicksPerAmbientSpawns(); + /** + * Gets the default ticks per {@link SpawnCategory} spawns value. + *

+ * Example Usage: + *

    + *
  • A value of 1 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * every tick. + *
  • A value of 400 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * every 400th tick. + *
  • A value below 0 will be reset back to Minecraft's default. + *
+ *

+ * Note: If set to 0, {@link SpawnCategory} mobs spawning will be disabled. + *

+ * Minecraft default: 1. + *
+ * Note: the {@link SpawnCategory#MISC} are not consider. + * + * @param spawnCategory the category of spawn + * @return the default ticks per {@link SpawnCategory} mobs spawn value + */ + public int getTicksPerSpawns(@NotNull SpawnCategory spawnCategory); + /** * Gets a player object by the given username. *

@@ -880,6 +917,39 @@ public interface Server extends PluginMessageRecipient { @NotNull public OfflinePlayer getOfflinePlayer(@NotNull UUID id); + /** + * Creates a new {@link PlayerProfile}. + * + * @param uniqueId the unique id + * @param name the name + * @return the new PlayerProfile + * @throws IllegalArgumentException if both the unique id is + * null and the name is null or blank + */ + @NotNull + PlayerProfile createPlayerProfile(@Nullable UUID uniqueId, @Nullable String name); + + /** + * Creates a new {@link PlayerProfile}. + * + * @param uniqueId the unique id + * @return the new PlayerProfile + * @throws IllegalArgumentException if the unique id is null + */ + @NotNull + PlayerProfile createPlayerProfile(@NotNull UUID uniqueId); + + /** + * Creates a new {@link PlayerProfile}. + * + * @param name the name + * @return the new PlayerProfile + * @throws IllegalArgumentException if the name is null or + * blank + */ + @NotNull + PlayerProfile createPlayerProfile(@NotNull String name); + /** * Gets a set containing all current IPs that are banned. * @@ -1078,7 +1148,9 @@ public interface Server extends PluginMessageRecipient { * chunk. * * @return the monster spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getMonsterSpawnLimit(); /** @@ -1086,7 +1158,9 @@ public interface Server extends PluginMessageRecipient { * chunk. * * @return the animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getAnimalSpawnLimit(); /** @@ -1094,7 +1168,9 @@ public interface Server extends PluginMessageRecipient { * a chunk. * * @return the water animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterAnimalSpawnLimit(); /** @@ -1102,14 +1178,18 @@ public interface Server extends PluginMessageRecipient { * in a chunk. * * @return the water ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterAmbientSpawnLimit(); /** * Get user-specified limit for number of water creature underground that can spawn * in a chunk. * @return the water underground creature limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterUndergroundCreatureSpawnLimit(); /** @@ -1117,9 +1197,22 @@ public interface Server extends PluginMessageRecipient { * a chunk. * * @return the ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getAmbientSpawnLimit(); + /** + * Gets user-specified limit for number of {@link SpawnCategory} mobs that can spawn in + * a chunk. + * + * Note: the {@link SpawnCategory#MISC} are not consider. + * + * @param spawnCategory the category spawn + * @return the {@link SpawnCategory} spawn limit + */ + int getSpawnLimit(@NotNull SpawnCategory spawnCategory); + /** * Checks the current thread against the expected primary thread for the * server. diff --git a/src/main/java/org/bukkit/StructureType.java b/src/main/java/org/bukkit/StructureType.java index 8acb2b9d..9d96f4b3 100644 --- a/src/main/java/org/bukkit/StructureType.java +++ b/src/main/java/org/bukkit/StructureType.java @@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable; * The registration of new {@link StructureType}s is case-sensitive. */ // Order is retrieved from WorldGenFactory -public final class StructureType { +public final class StructureType implements Keyed { private static final Map structureTypeMap = new HashMap<>(); @@ -161,7 +161,7 @@ public final class StructureType { * STRUCTURE TYPES REGISTERED ABOVE THIS * **************** */ - private final String name; + private final NamespacedKey key; private final MapCursor.Type mapCursor; /** @@ -176,7 +176,7 @@ public final class StructureType { */ private StructureType(@NotNull String name, @Nullable MapCursor.Type mapIcon) { Validate.notEmpty(name, "Structure name cannot be empty"); - this.name = name; + this.key = NamespacedKey.minecraft(name); this.mapCursor = mapIcon; } @@ -188,7 +188,7 @@ public final class StructureType { */ @NotNull public String getName() { - return name; + return key.getKey(); } /** @@ -211,20 +211,20 @@ public final class StructureType { return false; } StructureType that = (StructureType) other; - return this.name.equals(that.name) && this.mapCursor == that.mapCursor; + return this.key.equals(that.key) && this.mapCursor == that.mapCursor; } @Override public int hashCode() { int hash = 7; - hash = 71 * hash + Objects.hashCode(this.name); + hash = 71 * hash + Objects.hashCode(this.key); hash = 71 * hash + Objects.hashCode(this.mapCursor); return hash; } @Override public String toString() { - return "StructureType{name=" + this.name + ", cursor=" + this.mapCursor + "}"; + return "StructureType{key=" + this.key + ", cursor=" + this.mapCursor + "}"; } @NotNull @@ -244,4 +244,10 @@ public final class StructureType { public static Map getStructureTypes() { return ImmutableMap.copyOf(structureTypeMap); } + + @NotNull + @Override + public NamespacedKey getKey() { + return key; + } } diff --git a/src/main/java/org/bukkit/Tag.java b/src/main/java/org/bukkit/Tag.java index 5a25301a..107d12d4 100644 --- a/src/main/java/org/bukkit/Tag.java +++ b/src/main/java/org/bukkit/Tag.java @@ -348,6 +348,10 @@ public interface Tag extends Keyed { * Vanilla block tag representing all climbable blocks. */ Tag CLIMBABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("climbable"), Material.class); + /** + * Vanilla block tag representing all blocks which reset fall damage. + */ + Tag FALL_DAMAGE_RESETTING = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("fall_damage_resetting"), Material.class); /** * Vanilla block tag representing all shulker boxes. */ diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java index 20e8e212..965a5f3b 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -18,6 +18,7 @@ import org.bukkit.entity.Item; import org.bukkit.entity.LightningStrike; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.entity.SpawnCategory; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; @@ -25,6 +26,7 @@ import org.bukkit.generator.WorldInfo; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.metadata.Metadatable; +import org.bukkit.persistence.PersistentDataHolder; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.util.BoundingBox; @@ -38,7 +40,7 @@ import org.jetbrains.annotations.Nullable; /** * Represents a world, which may contain entities, chunks and blocks */ -public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable { +public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable, PersistentDataHolder { /** * Gets the {@link Block} at the given coordinates @@ -1628,7 +1630,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * Minecraft default: 400. * * @return The world's ticks per animal spawns value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerAnimalSpawns(); /** @@ -1655,7 +1659,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerAnimalSpawns the ticks per animal spawns value you want * to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns); /** @@ -1681,7 +1687,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * Minecraft default: 1. * * @return The world's ticks per monster spawns value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerMonsterSpawns(); /** @@ -1708,7 +1716,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerMonsterSpawns the ticks per monster spawns value you * want to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns); /** @@ -1732,7 +1742,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * Minecraft default: 1. * * @return The world's ticks per water mob spawns value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerWaterSpawns(); /** @@ -1757,7 +1769,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerWaterSpawns the ticks per water mob spawns value you * want to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerWaterSpawns(int ticksPerWaterSpawns); /** @@ -1777,7 +1791,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * Minecraft default: 1. * * @return the default ticks per water ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerWaterAmbientSpawns(); /** @@ -1802,7 +1818,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerAmbientSpawns the ticks per water ambient mob spawns value you * want to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerWaterAmbientSpawns(int ticksPerAmbientSpawns); /** @@ -1822,7 +1840,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * Minecraft default: 1. * * @return the default ticks per water underground creature spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerWaterUndergroundCreatureSpawns(); /** @@ -1847,7 +1867,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerWaterUndergroundCreatureSpawns the ticks per water underground creature spawns value you * want to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerWaterUndergroundCreatureSpawns(int ticksPerWaterUndergroundCreatureSpawns); /** @@ -1870,8 +1892,10 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient *

* Minecraft default: 1. * - * @return The world's ticks per ambient mob spawns value + * @return the default ticks per ambient mobs spawn value + * @deprecated Deprecated in favor of {@link #getTicksPerSpawns(SpawnCategory)} */ + @Deprecated public long getTicksPerAmbientSpawns(); /** @@ -1896,15 +1920,70 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @param ticksPerAmbientSpawns the ticks per ambient mob spawns value you * want to set the world to + * @deprecated Deprecated in favor of {@link #setTicksPerSpawns(SpawnCategory, int)} */ + @Deprecated public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns); + /** + * Gets the world's ticks per {@link SpawnCategory} mob spawns value + *

+ * This value determines how many ticks there are between attempts to + * spawn {@link SpawnCategory} mobs. + *

+ * Example Usage: + *

    + *
  • A value of 1 will mean the server will attempt to spawn {@link SpawnCategory} mobs in + * this world every tick. + *
  • A value of 400 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * in this world every 400th tick. + *
  • A value below 0 will be reset back to Minecraft's default. + *
+ *

+ * Note: + * If set to 0, {@link SpawnCategory} mobs spawning will be disabled for this world. + *

+ * Minecraft default: 1. + * + * @param spawnCategory the category spawn + * @return The world's ticks per {@link SpawnCategory} mob spawns value + */ + public long getTicksPerSpawns(@NotNull SpawnCategory spawnCategory); + + /** + * Sets the world's ticks per {@link SpawnCategory} mob spawns value + *

+ * This value determines how many ticks there are between attempts to + * spawn {@link SpawnCategory} mobs. + *

+ * Example Usage: + *

    + *
  • A value of 1 will mean the server will attempt to spawn {@link SpawnCategory} mobs in + * this world on every tick. + *
  • A value of 400 will mean the server will attempt to spawn {@link SpawnCategory} mobs + * in this world every 400th tick. + *
  • A value below 0 will be reset back to Minecraft's default. + *
+ *

+ * Note: + * If set to 0, {@link SpawnCategory} mobs spawning will be disabled for this world. + *

+ * Minecraft default: 1. + * + * @param spawnCategory the category spawn + * @param ticksPerCategorySpawn the ticks per {@link SpawnCategory} mob spawns value you + * want to set the world to + */ + public void setTicksPerSpawns(@NotNull SpawnCategory spawnCategory, int ticksPerCategorySpawn); + /** * Gets limit for number of monsters that can spawn in a chunk in this * world * * @return The monster spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getMonsterSpawnLimit(); /** @@ -1915,7 +1994,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)} */ + @Deprecated void setMonsterSpawnLimit(int limit); /** @@ -1923,7 +2004,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * world * * @return The animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getAnimalSpawnLimit(); /** @@ -1934,7 +2017,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated void setAnimalSpawnLimit(int limit); /** @@ -1942,7 +2027,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * this world * * @return The water animal spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterAnimalSpawnLimit(); /** @@ -1953,7 +2040,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)} */ + @Deprecated void setWaterAnimalSpawnLimit(int limit); /** @@ -1961,7 +2050,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * this world * * @return The water underground creature spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterUndergroundCreatureSpawnLimit(); /** @@ -1972,7 +2063,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)} */ + @Deprecated void setWaterUndergroundCreatureSpawnLimit(int limit); /** @@ -1980,7 +2073,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * in a chunk. * * @return the water ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getWaterAmbientSpawnLimit(); /** @@ -1991,7 +2086,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)} */ + @Deprecated void setWaterAmbientSpawnLimit(int limit); /** @@ -1999,7 +2096,9 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * this world * * @return The ambient spawn limit + * @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)} */ + @Deprecated int getAmbientSpawnLimit(); /** @@ -2010,11 +2109,34 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * server-wide spawn limit instead. * * @param limit the new mob limit + * @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)} */ + @Deprecated void setAmbientSpawnLimit(int limit); /** - * Play a Sound at the provided Location in the World + * Gets the limit for number of {@link SpawnCategory} entities that can spawn in a chunk in + * this world + * + * @param spawnCategory the entity category + * @return The ambient spawn limit + */ + int getSpawnLimit(@NotNull SpawnCategory spawnCategory); + + /** + * Sets the limit for number of {@link SpawnCategory} entities that can spawn in a chunk in + * this world + *

+ * Note: If set to a negative number the world will use the + * server-wide spawn limit instead. + * + * @param spawnCategory the entity category + * @param limit the new mob limit + */ + void setSpawnLimit(@NotNull SpawnCategory spawnCategory, int limit); + + /** + * Play a Sound at the provided Location in the World. *

* This function will fail silently if Location or Sound are null. * @@ -2067,6 +2189,31 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient */ void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch); + /** + * Play a Sound at the location of the provided entity in the World. + *

+ * This function will fail silently if Entity or Sound are null. + * + * @param entity The entity to play the sound + * @param sound The sound to play + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + void playSound(@NotNull Entity entity, @NotNull Sound sound, float volume, float pitch); + + /** + * Play a Sound at the location of the provided entity in the World. + *

+ * This function will fail silently if Entity or Sound are null. + * + * @param entity The entity to play the sound + * @param sound The sound to play + * @param category the category of the sound + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch); + /** * Get an array containing the names of all the {@link GameRule}s. * diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java index ce1e1e6c..b4094e04 100644 --- a/src/main/java/org/bukkit/block/Block.java +++ b/src/main/java/org/bukkit/block/Block.java @@ -448,11 +448,11 @@ public interface Block extends Metadatable { * into account tools, potion effects, whether or not the player is in * water, enchantments, etc. * - * The returned value is the amount of progress made in breaking the block. - * When the total breaking progress reaches {@code 1.0f}, the block is - * broken. Note that the break speed can change in the course of breaking a - * block, e.g. if a potion effect is applied or expires, or the player - * jumps/enters water. + * The returned value is the amount of progress made in breaking the block + * each tick. When the total breaking progress reaches {@code 1.0f}, the + * block is broken. Note that the break speed can change in the course of + * breaking a block, e.g. if a potion effect is applied or expires, or the + * player jumps/enters water. * * @param player player breaking the block * @return the speed at which the player breaks this block diff --git a/src/main/java/org/bukkit/block/Furnace.java b/src/main/java/org/bukkit/block/Furnace.java index c5a8c96f..ac3b24c5 100644 --- a/src/main/java/org/bukkit/block/Furnace.java +++ b/src/main/java/org/bukkit/block/Furnace.java @@ -1,5 +1,7 @@ package org.bukkit.block; +import java.util.Map; +import org.bukkit.inventory.CookingRecipe; import org.bukkit.inventory.FurnaceInventory; import org.jetbrains.annotations.NotNull; @@ -61,6 +63,17 @@ public interface Furnace extends Container { */ public void setCookTimeTotal(int cookTimeTotal); + /** + * Get the recipes used in this furnace. + * + * Note: These recipes used are reset when the result item is + * manually taken from the furnace. + * + * @return An immutable map with the recipes used and the times used + */ + @NotNull + public Map, Integer> getRecipesUsed(); + @NotNull @Override public FurnaceInventory getInventory(); diff --git a/src/main/java/org/bukkit/block/Skull.java b/src/main/java/org/bukkit/block/Skull.java index 943d751f..83ca284e 100644 --- a/src/main/java/org/bukkit/block/Skull.java +++ b/src/main/java/org/bukkit/block/Skull.java @@ -4,6 +4,7 @@ import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.SkullType; import org.bukkit.block.data.BlockData; +import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,6 +62,29 @@ public interface Skull extends TileState { */ public void setOwningPlayer(@NotNull OfflinePlayer player); + /** + * Gets the profile of the player who owns the skull. This player profile + * may appear as the texture depending on skull type. + * + * @return the profile of the owning player + */ + @Nullable + PlayerProfile getOwnerProfile(); + + /** + * Sets the profile of the player who owns the skull. This player profile + * may appear as the texture depending on skull type. + *

+ * The profile must contain both a unique id and a skin texture. If either + * of these is missing, the profile must contain a name by which the server + * will then attempt to look up the unique id and skin texture. + * + * @param profile the profile of the owning player + * @throws IllegalArgumentException if the profile does not contain the + * necessary information + */ + void setOwnerProfile(@Nullable PlayerProfile profile); + /** * Gets the rotation of the skull in the world (or facing direction if this * is a wall mounted skull). diff --git a/src/main/java/org/bukkit/command/TabCompleter.java b/src/main/java/org/bukkit/command/TabCompleter.java index e9cf71f5..ed6f6525 100644 --- a/src/main/java/org/bukkit/command/TabCompleter.java +++ b/src/main/java/org/bukkit/command/TabCompleter.java @@ -16,12 +16,12 @@ public interface TabCompleter { * command inside of a command block, this will be the player, not * the command block. * @param command Command which was executed - * @param alias The alias used + * @param label Alias of the command which was used * @param args The arguments passed to the command, including final - * partial argument to be completed and command label + * partial argument to be completed * @return A List of possible completions for the final argument, or null * to default to the command executor */ @Nullable - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args); + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args); } diff --git a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java index df250bf4..f9abe699 100644 --- a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java +++ b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java @@ -144,8 +144,8 @@ public class YamlConfiguration extends FileConfiguration { private void fromNodeTree(@NotNull MappingNode input, @NotNull ConfigurationSection section) { constructor.flattenMapping(input); for (NodeTuple nodeTuple : input.getValue()) { - ScalarNode key = (ScalarNode) nodeTuple.getKeyNode(); - String keyString = key.getValue(); + Node key = nodeTuple.getKeyNode(); + String keyString = String.valueOf(constructor.construct(key)); Node value = nodeTuple.getValueNode(); while (value instanceof AnchorNode) { @@ -169,7 +169,9 @@ public class YamlConfiguration extends FileConfiguration { private boolean hasSerializedTypeKey(MappingNode node) { for (NodeTuple nodeTuple : node.getValue()) { - String key = ((ScalarNode) nodeTuple.getKeyNode()).getValue(); + Node keyNode = nodeTuple.getKeyNode(); + if (!(keyNode instanceof ScalarNode)) continue; + String key = ((ScalarNode) keyNode).getValue(); if (key.equals(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { return true; } @@ -180,7 +182,7 @@ public class YamlConfiguration extends FileConfiguration { private MappingNode toNodeTree(@NotNull ConfigurationSection section) { List nodeTuples = new ArrayList<>(); for (Map.Entry entry : section.getValues(false).entrySet()) { - ScalarNode key = (ScalarNode) representer.represent(entry.getKey()); + Node key = representer.represent(entry.getKey()); Node value; if (entry.getValue() instanceof ConfigurationSection) { value = toNodeTree((ConfigurationSection) entry.getValue()); diff --git a/src/main/java/org/bukkit/configuration/file/YamlConstructor.java b/src/main/java/org/bukkit/configuration/file/YamlConstructor.java index 7052f8a5..09259942 100644 --- a/src/main/java/org/bukkit/configuration/file/YamlConstructor.java +++ b/src/main/java/org/bukkit/configuration/file/YamlConstructor.java @@ -22,7 +22,7 @@ public class YamlConstructor extends SafeConstructor { super.flattenMapping(node); } - @NotNull + @Nullable public Object construct(@NotNull Node node) { return constructObject(node); } diff --git a/src/main/java/org/bukkit/entity/Arrow.java b/src/main/java/org/bukkit/entity/Arrow.java index ec8443b6..8814519d 100644 --- a/src/main/java/org/bukkit/entity/Arrow.java +++ b/src/main/java/org/bukkit/entity/Arrow.java @@ -28,17 +28,17 @@ public interface Arrow extends AbstractArrow { /** * Gets the color of this arrow. * - * @return arrow color + * @return arrow {@link Color} or null if not color is set */ - @NotNull + @Nullable Color getColor(); /** * Sets the color of this arrow. Will be applied as a tint to its particles. * - * @param color arrow color + * @param color arrow color, null to clear the color */ - void setColor(@NotNull Color color); + void setColor(@Nullable Color color); /** * Checks for the presence of custom potion effects. diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java index 4ac66f71..046cf816 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java @@ -638,4 +638,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent */ @NotNull Pose getPose(); + + /** + * Get the category of spawn to which this entity belongs. + * + * @return the entity´s category spawn + */ + @NotNull + SpawnCategory getSpawnCategory(); } diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java index db4ba676..50ac6f03 100644 --- a/src/main/java/org/bukkit/entity/HumanEntity.java +++ b/src/main/java/org/bukkit/entity/HumanEntity.java @@ -198,6 +198,7 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder * * @param material the material to check * @return if a cooldown is active on the material + * @throws IllegalArgumentException if the material is not an item */ public boolean hasCooldown(@NotNull Material material); @@ -206,6 +207,7 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder * * @param material the material to check * @return the remaining cooldown time in ticks + * @throws IllegalArgumentException if the material is not an item */ public int getCooldown(@NotNull Material material); @@ -221,6 +223,7 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder * * @param material the material to set the cooldown for * @param ticks the amount of ticks to set or 0 to remove + * @throws IllegalArgumentException if the material is not an item */ public void setCooldown(@NotNull Material material, int ticks); diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java index 73f9118d..c5d88d5c 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -349,6 +349,31 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM */ public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch); + /** + * Play a sound for a player at the location of the entity. + *

+ * This function will fail silently if Entity or Sound are null. + * + * @param entity The entity to play the sound + * @param sound The sound to play + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + public void playSound(@NotNull Entity entity, @NotNull Sound sound, float volume, float pitch); + + /** + * Play a sound for a player at the location of the entity. + *

+ * This function will fail silently if Entity or Sound are null. + * + * @param entity The entity to play the sound + * @param sound The sound to play + * @param category The category of the sound + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + public void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch); + /** * Stop the specified sound from playing. * @@ -545,6 +570,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @Deprecated public void updateInventory(); + /** + * Gets this player's previous {@link GameMode} + * + * @return Previous game mode or null + */ + @Nullable + public GameMode getPreviousGameMode(); + /** * Sets the current time on the player's client. When relative is true the * player's time will be kept synchronized to its world time with the diff --git a/src/main/java/org/bukkit/entity/SpawnCategory.java b/src/main/java/org/bukkit/entity/SpawnCategory.java new file mode 100644 index 00000000..a2772ada --- /dev/null +++ b/src/main/java/org/bukkit/entity/SpawnCategory.java @@ -0,0 +1,43 @@ +package org.bukkit.entity; + +/** + * Represents groups of entities with shared spawn behaviors and mob caps. + * + * @see Minecraft Wiki + */ +public enum SpawnCategory { + + /** + * Entities related to Monsters, eg: Witch, Zombie, Creeper, etc. + */ + MONSTER, + /** + * Entities related to Animals, eg: Strider, Cow, Turtle, etc. + */ + ANIMAL, + /** + * Entities related to Water Animals, eg: Squid or Dolphin. + */ + WATER_ANIMAL, + /** + * Entities related to Water Ambient, eg: Cod, PufferFish, Tropical Fish, + * Salmon, etc. + */ + WATER_AMBIENT, + /** + * Entities related to Water Underground, eg: Glow Squid. + */ + WATER_UNDERGROUND_CREATURE, + /** + * Entities related to Ambient, eg: Bat. + */ + AMBIENT, + /** + * All the Axolotl are represented by this Category. + */ + AXOLOTL, + /** + * Entities not related to a mob, eg: Player, ArmorStand, Boat, etc. + */ + MISC; +} diff --git a/src/main/java/org/bukkit/event/block/BlockDamageAbortEvent.java b/src/main/java/org/bukkit/event/block/BlockDamageAbortEvent.java new file mode 100644 index 00000000..7d49b862 --- /dev/null +++ b/src/main/java/org/bukkit/event/block/BlockDamageAbortEvent.java @@ -0,0 +1,54 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a player stops damaging a Block. + */ +public class BlockDamageAbortEvent extends BlockEvent { + + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final ItemStack itemstack; + + public BlockDamageAbortEvent(@NotNull final Player player, @NotNull final Block block, @NotNull final ItemStack itemInHand) { + super(block); + this.player = player; + this.itemstack = itemInHand; + } + + /** + * Gets the player that stopped damaging the block involved in this event. + * + * @return The player that stopped damaging the block + */ + @NotNull + public Player getPlayer() { + return player; + } + + /** + * Gets the ItemStack for the item currently in the player's hand. + * + * @return The ItemStack for the item currently in the player's hand + */ + @NotNull + public ItemStack getItemInHand() { + return itemstack; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java b/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java index cb0d91c0..d843f4ad 100644 --- a/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java +++ b/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java @@ -174,9 +174,13 @@ public class CreatureSpawnEvent extends EntitySpawnEvent { */ BEEHIVE, /** - * When a piglin is converted to a zombified piglib. + * When a piglin is converted to a zombified piglin. */ PIGLIN_ZOMBIFIED, + /** + * When an entity is created by a cast spell. + */ + SPELL, /** * When an entity is shaking in Powder Snow and a new entity spawns. */ diff --git a/src/main/java/org/bukkit/event/entity/StriderTemperatureChangeEvent.java b/src/main/java/org/bukkit/event/entity/StriderTemperatureChangeEvent.java index 5778f9a6..26a4f88a 100644 --- a/src/main/java/org/bukkit/event/entity/StriderTemperatureChangeEvent.java +++ b/src/main/java/org/bukkit/event/entity/StriderTemperatureChangeEvent.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; /** * Called when a {@link Strider}'s temperature has changed as a result of - * entering or existing blocks it considers warm. + * entering or exiting blocks it considers warm. */ public class StriderTemperatureChangeEvent extends EntityEvent implements Cancellable { diff --git a/src/main/java/org/bukkit/event/player/PlayerHideEntityEvent.java b/src/main/java/org/bukkit/event/player/PlayerHideEntityEvent.java new file mode 100644 index 00000000..d8a73cd2 --- /dev/null +++ b/src/main/java/org/bukkit/event/player/PlayerHideEntityEvent.java @@ -0,0 +1,52 @@ +package org.bukkit.event.player; + +import org.bukkit.Warning; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a visible entity is hidden from a player. + * + * This event is only called when the entity's visibility status is actually + * changed. + * + * This event is called regardless of if the entity was within tracking range. + * + * @see Player#hideEntity(org.bukkit.plugin.Plugin, org.bukkit.entity.Entity) + * @deprecated draft API + */ +@Deprecated +@Warning(false) +public class PlayerHideEntityEvent extends PlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + private final Entity entity; + + public PlayerHideEntityEvent(@NotNull Player who, @NotNull Entity entity) { + super(who); + this.entity = entity; + } + + /** + * Gets the entity which has been hidden from the player. + * + * @return the hidden entity + */ + @NotNull + public Entity getEntity() { + return entity; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/bukkit/event/player/PlayerShowEntityEvent.java b/src/main/java/org/bukkit/event/player/PlayerShowEntityEvent.java new file mode 100644 index 00000000..5408a8c1 --- /dev/null +++ b/src/main/java/org/bukkit/event/player/PlayerShowEntityEvent.java @@ -0,0 +1,53 @@ +package org.bukkit.event.player; + +import org.bukkit.Warning; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a hidden entity is shown to a player. + * + * This event is only called when the entity's visibility status is actually + * changed. + * + * This event is called regardless of whether the entity was within tracking + * range. + * + * @see Player#showEntity(org.bukkit.plugin.Plugin, org.bukkit.entity.Entity) + * @deprecated draft API + */ +@Deprecated +@Warning(false) +public class PlayerShowEntityEvent extends PlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + private final Entity entity; + + public PlayerShowEntityEvent(@NotNull Player who, @NotNull Entity entity) { + super(who); + this.entity = entity; + } + + /** + * Gets the entity which has been shown to the player. + * + * @return the shown entity + */ + @NotNull + public Entity getEntity() { + return entity; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/bukkit/inventory/AnvilInventory.java b/src/main/java/org/bukkit/inventory/AnvilInventory.java index 4af56242..52519cd8 100644 --- a/src/main/java/org/bukkit/inventory/AnvilInventory.java +++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java @@ -16,6 +16,20 @@ public interface AnvilInventory extends Inventory { @Nullable String getRenameText(); + /** + * Get the item cost (in amount) to complete the current repair. + * + * @return the amount + */ + int getRepairCostAmount(); + + /** + * Set the item cost (in amount) to complete the current repair. + * + * @param amount the amount + */ + void setRepairCostAmount(int amount); + /** * Get the experience cost (in levels) to complete the current repair. * diff --git a/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java b/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java index fe239481..cdbcc8db 100644 --- a/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java +++ b/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java @@ -86,8 +86,8 @@ public interface FireworkMeta extends ItemMeta { * Sets the approximate power of the firework. Each level of power is half * a second of flight time. * - * @param power the power of the firework, from 0-128 - * @throws IllegalArgumentException if {@literal height<0 or height>128} + * @param power the power of the firework, from 0-127 + * @throws IllegalArgumentException if {@literal height<0 or height>127} */ void setPower(int power) throws IllegalArgumentException; diff --git a/src/main/java/org/bukkit/inventory/meta/Repairable.java b/src/main/java/org/bukkit/inventory/meta/Repairable.java index 0a1d543d..fe053bcb 100644 --- a/src/main/java/org/bukkit/inventory/meta/Repairable.java +++ b/src/main/java/org/bukkit/inventory/meta/Repairable.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull; /** * Represents an item that can be repaired at an anvil. */ -public interface Repairable { +public interface Repairable extends ItemMeta { /** * Checks to see if this has a repair penalty @@ -30,5 +30,6 @@ public interface Repairable { @SuppressWarnings("javadoc") @NotNull + @Override Repairable clone(); } diff --git a/src/main/java/org/bukkit/inventory/meta/SkullMeta.java b/src/main/java/org/bukkit/inventory/meta/SkullMeta.java index 496254f9..dcefd0ee 100644 --- a/src/main/java/org/bukkit/inventory/meta/SkullMeta.java +++ b/src/main/java/org/bukkit/inventory/meta/SkullMeta.java @@ -1,6 +1,7 @@ package org.bukkit.inventory.meta; import org.bukkit.OfflinePlayer; +import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -55,6 +56,29 @@ public interface SkullMeta extends ItemMeta { */ boolean setOwningPlayer(@Nullable OfflinePlayer owner); + /** + * Gets the profile of the player who owns the skull. This player profile + * may appear as the texture depending on skull type. + * + * @return the profile of the owning player + */ + @Nullable + PlayerProfile getOwnerProfile(); + + /** + * Sets the profile of the player who owns the skull. This player profile + * may appear as the texture depending on skull type. + *

+ * The profile must contain both a unique id and a skin texture. If either + * of these is missing, the profile must contain a name by which the server + * will then attempt to look up the unique id and skin texture. + * + * @param profile the profile of the owning player + * @throws IllegalArgumentException if the profile does not contain the + * necessary information + */ + void setOwnerProfile(@Nullable PlayerProfile profile); + @Override @NotNull SkullMeta clone(); diff --git a/src/main/java/org/bukkit/loot/LootTable.java b/src/main/java/org/bukkit/loot/LootTable.java index 73aa119b..b7529fc7 100644 --- a/src/main/java/org/bukkit/loot/LootTable.java +++ b/src/main/java/org/bukkit/loot/LootTable.java @@ -6,6 +6,7 @@ import org.bukkit.Keyed; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * LootTables are technical files that represent what items should be in @@ -25,7 +26,7 @@ public interface LootTable extends Keyed { * @return a list of ItemStacks */ @NotNull - Collection populateLoot(@NotNull Random random, @NotNull LootContext context); + Collection populateLoot(@Nullable Random random, @NotNull LootContext context); /** * Attempt to fill an inventory with this LootTable's loot. @@ -34,5 +35,5 @@ public interface LootTable extends Keyed { * @param random the random instance to use to generate loot * @param context context within to populate loot */ - void fillInventory(@NotNull Inventory inventory, @NotNull Random random, @NotNull LootContext context); + void fillInventory(@NotNull Inventory inventory, @Nullable Random random, @NotNull LootContext context); } diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java index 6bdd9f1d..930f3fe0 100644 --- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java @@ -132,10 +132,10 @@ final class PluginClassLoader extends URLClassLoader { seenIllegalAccess.add(provider.getName()); if (plugin != null) { - plugin.getLogger().log(Level.WARNING, "Loaded class {0} from {1} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{name, provider.getFullName()}); + plugin.getLogger().log(Level.WARNING, "Loaded class {0} from {1} which is not a depend or softdepend of this plugin.", new Object[]{name, provider.getFullName()}); } else { // In case the bad access occurs on construction - loader.server.getLogger().log(Level.WARNING, "[{0}] Loaded class {1} from {2} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{description.getName(), name, provider.getFullName()}); + loader.server.getLogger().log(Level.WARNING, "[{0}] Loaded class {1} from {2} which is not a depend or softdepend of this plugin.", new Object[]{description.getName(), name, provider.getFullName()}); } } } diff --git a/src/main/java/org/bukkit/profile/PlayerProfile.java b/src/main/java/org/bukkit/profile/PlayerProfile.java new file mode 100644 index 00000000..16ae1282 --- /dev/null +++ b/src/main/java/org/bukkit/profile/PlayerProfile.java @@ -0,0 +1,100 @@ +package org.bukkit.profile; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import org.bukkit.Server; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A player profile. + *

+ * A player profile always provides a unique id, a non-empty name, or both. Its + * unique id and name are immutable, but other properties (such as its textures) + * can be altered. + *

+ * New profiles can be created via + * {@link Server#createPlayerProfile(UUID, String)}. + */ +public interface PlayerProfile extends Cloneable, ConfigurationSerializable { + + /** + * Gets the player's unique id. + * + * @return the player's unique id, or null if not available + */ + @Nullable + UUID getUniqueId(); + + /** + * Gets the player name. + * + * @return the player name, or null if not available + */ + @Nullable + String getName(); + + /** + * Gets the {@link PlayerTextures} of this profile. + * + * @return the textures, not null + */ + @NotNull + PlayerTextures getTextures(); + + /** + * Copies the given textures. + * + * @param textures the textures to copy, or null to clear the + * textures + */ + void setTextures(@Nullable PlayerTextures textures); + + /** + * Checks whether this profile is complete. + *

+ * A profile is currently considered complete if it has a name, a unique id, + * and textures. + * + * @return true if this profile is complete + */ + boolean isComplete(); + + /** + * Produces an updated player profile based on this profile. + *

+ * This tries to produce a completed profile by filling in missing + * properties (name, unique id, textures, etc.), and updates existing + * properties (e.g. name, textures, etc.) to their official and up-to-date + * values. This operation does not alter the current profile, but produces a + * new updated {@link PlayerProfile}. + *

+ * If no player exists for the unique id or name of this profile, this + * operation yields a profile that is equal to the current profile, which + * might not be complete. + *

+ * This is an asynchronous operation: Updating the profile can result in an + * outgoing connection in another thread in order to fetch the latest + * profile properties. The returned {@link CompletableFuture} will be + * completed once the updated profile is available. In order to not block + * the server's main thread, you should not wait for the result of the + * returned CompletableFuture on the server's main thread. Instead, if you + * want to do something with the updated player profile on the server's main + * thread once it is available, you could do something like this: + *

+     * profile.update().thenAcceptAsync(updatedProfile -> {
+     *     // Do something with the updated profile:
+     *     // ...
+     * }, runnable -> Bukkit.getScheduler().runTask(plugin, runnable));
+     * 
+ * + * @return a completable future that gets completed with the updated + * PlayerProfile once it is available + */ + @NotNull + CompletableFuture update(); + + @NotNull + PlayerProfile clone(); +} diff --git a/src/main/java/org/bukkit/profile/PlayerTextures.java b/src/main/java/org/bukkit/profile/PlayerTextures.java new file mode 100644 index 00000000..cb2bd709 --- /dev/null +++ b/src/main/java/org/bukkit/profile/PlayerTextures.java @@ -0,0 +1,127 @@ +package org.bukkit.profile; + +import java.net.URL; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Provides access to the textures stored inside a {@link PlayerProfile}. + *

+ * Modifying the textures immediately invalidates and clears any previously + * present attributes that are specific to official player profiles, such as the + * {@link #getTimestamp() timestamp} and {@link #isSigned() signature}. + */ +public interface PlayerTextures { + + /** + * The different Minecraft skin models. + */ + enum SkinModel { + /** + * The classic Minecraft skin model. + */ + CLASSIC, + /** + * The slim model has slimmer arms than the classic model. + */ + SLIM; + } + + /** + * Checks if the profile stores no textures. + * + * @return true if the profile stores no textures + */ + boolean isEmpty(); + + /** + * Clears the textures. + */ + void clear(); + + /** + * Gets the URL that points to the player's skin. + * + * @return the URL of the player's skin, or null if not set + */ + @Nullable + URL getSkin(); + + /** + * Sets the player's skin to the specified URL, and the skin model to + * {@link SkinModel#CLASSIC}. + *

+ * The URL must point to the Minecraft texture server. Example URL: + *

+     * http://textures.minecraft.net/texture/b3fbd454b599df593f57101bfca34e67d292a8861213d2202bb575da7fd091ac
+     * 
+ * + * @param skinUrl the URL of the player's skin, or null to + * unset it + */ + void setSkin(@Nullable URL skinUrl); + + /** + * Sets the player's skin and {@link SkinModel}. + *

+ * The URL must point to the Minecraft texture server. Example URL: + *

+     * http://textures.minecraft.net/texture/b3fbd454b599df593f57101bfca34e67d292a8861213d2202bb575da7fd091ac
+     * 
+ *

+ * A skin model of null results in {@link SkinModel#CLASSIC} to + * be used. + * + * @param skinUrl the URL of the player's skin, or null to + * unset it + * @param skinModel the skin model, ignored if the skin URL is + * null + */ + void setSkin(@Nullable URL skinUrl, @Nullable SkinModel skinModel); + + /** + * Gets the model of the player's skin. + *

+ * This returns {@link SkinModel#CLASSIC} if no skin is set. + * + * @return the model of the player's skin + */ + @NotNull + SkinModel getSkinModel(); + + /** + * Gets the URL that points to the player's cape. + * + * @return the URL of the player's cape, or null if not set + */ + @Nullable + URL getCape(); + + /** + * Sets the URL that points to the player's cape. + *

+ * The URL must point to the Minecraft texture server. Example URL: + *

+     * http://textures.minecraft.net/texture/2340c0e03dd24a11b15a8b33c2a7e9e32abb2051b2481d0ba7defd635ca7a933
+     * 
+ * + * @param capeUrl the URL of the player's cape, or null to + * unset it + */ + void setCape(@Nullable URL capeUrl); + + /** + * Gets the timestamp at which the profile was last updated. + * + * @return the timestamp, or 0 if unknown + */ + long getTimestamp(); + + /** + * Checks if the textures are signed and the signature is valid. + * + * @return true if the textures are signed and the signature is + * valid + */ + boolean isSigned(); +} diff --git a/src/main/java/org/bukkit/profile/package-info.java b/src/main/java/org/bukkit/profile/package-info.java new file mode 100644 index 00000000..6f3ed4f0 --- /dev/null +++ b/src/main/java/org/bukkit/profile/package-info.java @@ -0,0 +1,4 @@ +/** + * Classes relevant to player profiles. + */ +package org.bukkit.profile; diff --git a/src/main/java/org/bukkit/scheduler/BukkitScheduler.java b/src/main/java/org/bukkit/scheduler/BukkitScheduler.java index c239c4de..5aefb7f2 100644 --- a/src/main/java/org/bukkit/scheduler/BukkitScheduler.java +++ b/src/main/java/org/bukkit/scheduler/BukkitScheduler.java @@ -76,7 +76,7 @@ public interface BukkitScheduler { public int scheduleSyncRepeatingTask(@NotNull Plugin plugin, @NotNull BukkitRunnable task, long delay, long period); /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Schedules a once off task to occur after a delay. This task will be @@ -93,7 +93,7 @@ public interface BukkitScheduler { public int scheduleAsyncDelayedTask(@NotNull Plugin plugin, @NotNull Runnable task, long delay); /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Schedules a once off task to occur as soon as possible. This task will @@ -109,7 +109,7 @@ public interface BukkitScheduler { public int scheduleAsyncDelayedTask(@NotNull Plugin plugin, @NotNull Runnable task); /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Schedules a repeating task. This task will be executed by a thread @@ -242,7 +242,7 @@ public interface BukkitScheduler { public BukkitTask runTask(@NotNull Plugin plugin, @NotNull BukkitRunnable task) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will run asynchronously. @@ -257,7 +257,7 @@ public interface BukkitScheduler { public BukkitTask runTaskAsynchronously(@NotNull Plugin plugin, @NotNull Runnable task) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will run asynchronously. @@ -321,7 +321,7 @@ public interface BukkitScheduler { public BukkitTask runTaskLater(@NotNull Plugin plugin, @NotNull BukkitRunnable task, long delay) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will run asynchronously after the specified number @@ -338,7 +338,7 @@ public interface BukkitScheduler { public BukkitTask runTaskLaterAsynchronously(@NotNull Plugin plugin, @NotNull Runnable task, long delay) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will run asynchronously after the specified number @@ -408,7 +408,7 @@ public interface BukkitScheduler { public BukkitTask runTaskTimer(@NotNull Plugin plugin, @NotNull BukkitRunnable task, long delay, long period) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will repeatedly run asynchronously until cancelled, @@ -427,7 +427,7 @@ public interface BukkitScheduler { public BukkitTask runTaskTimerAsynchronously(@NotNull Plugin plugin, @NotNull Runnable task, long delay, long period) throws IllegalArgumentException; /** - * Asynchronous tasks should never access any API in Bukkit. Great care + * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will repeatedly run asynchronously until cancelled, diff --git a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java index a0ff42ab..3522baa0 100644 --- a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java +++ b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java @@ -174,4 +174,31 @@ public class YamlConfigurationTest extends FileConfigurationTest { assertEquals(expected, result); } + + @Test + public void testUnusualMappingKeys() throws InvalidConfigurationException { + YamlConfiguration config = getConfig(); + String content = "[1]: odd\n" + + "{2}: choice\n" + + "\"3\": of\n" + + "4: keys\n" + + "null: A\n" + + "'null': B\n" + + "!!null null: C\n" + + "'!!null': X\n"; + // The keys are parsed as arbitrary Yaml types and then converted to their String representation during config loading. + // Consequently, the String representation used by the loaded config might be different to how these keys were originally represented. + // Since SnakeYaml does not preserve the original representation in its parsed node tree, we are also not able to reconstruct the original keys during config loading. + config.loadFromString(content); + + // This difference in representations also becomes apparent when the config is written back. + // Also note: All keys that are parsed as null overwrite each other's values. + String expected = "'[1]': odd\n" + + "'{2=null}': choice\n" + + "'3': of\n" + + "'4': keys\n" + + "'null': C\n" + + "'!!null': X\n"; + assertEquals(expected, config.saveToString()); + } }