Merge pull request #157 from kezz/adventure

Adventure Time!
This commit is contained in:
TheMode 2021-03-26 19:46:41 +01:00 committed by GitHub
commit 74947251fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
136 changed files with 4571 additions and 802 deletions

View File

@ -165,6 +165,12 @@ dependencies {
api "com.github.Minestom:DependencyGetter:v1.0.1"
// Adventure, for user-interface
api "net.kyori:adventure-api:$adventureVersion"
api "net.kyori:adventure-text-serializer-gson:$adventureVersion"
api "net.kyori:adventure-text-serializer-plain:$adventureVersion"
api "net.kyori:adventure-text-serializer-legacy:$adventureVersion"
// LWJGL, for map rendering
lwjglApi platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
@ -184,6 +190,11 @@ dependencies {
generatorsImplementation("com.squareup:javapoet:1.13.0")
}
configurations.all {
// we use jetbrains annotations
exclude group: "org.checkerframework", module: "checker-qual"
}
publishing {
publications {
mavenJava(MavenPublication) {

View File

@ -1,4 +1,5 @@
asmVersion=9.0
mixinVersion=0.8.1
hephaistosVersion=v1.1.8
kotlinVersion=1.4.21
kotlinVersion=1.4.21
adventureVersion=4.7.0

View File

@ -1,29 +1,11 @@
package net.minestom.server.entity;
import java.util.function.BiFunction;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.entity.metadata.PlayerMeta;
import net.minestom.server.entity.metadata.ambient.BatMeta;
import net.minestom.server.entity.metadata.animal.BeeMeta;
import net.minestom.server.entity.metadata.animal.ChickenMeta;
import net.minestom.server.entity.metadata.animal.CowMeta;
import net.minestom.server.entity.metadata.animal.DonkeyMeta;
import net.minestom.server.entity.metadata.animal.FoxMeta;
import net.minestom.server.entity.metadata.animal.HoglinMeta;
import net.minestom.server.entity.metadata.animal.HorseMeta;
import net.minestom.server.entity.metadata.animal.LlamaMeta;
import net.minestom.server.entity.metadata.animal.MooshroomMeta;
import net.minestom.server.entity.metadata.animal.MuleMeta;
import net.minestom.server.entity.metadata.animal.OcelotMeta;
import net.minestom.server.entity.metadata.animal.PandaMeta;
import net.minestom.server.entity.metadata.animal.PigMeta;
import net.minestom.server.entity.metadata.animal.PolarBearMeta;
import net.minestom.server.entity.metadata.animal.RabbitMeta;
import net.minestom.server.entity.metadata.animal.SheepMeta;
import net.minestom.server.entity.metadata.animal.SkeletonHorseMeta;
import net.minestom.server.entity.metadata.animal.StriderMeta;
import net.minestom.server.entity.metadata.animal.TurtleMeta;
import net.minestom.server.entity.metadata.animal.ZombieHorseMeta;
import net.minestom.server.entity.metadata.animal.*;
import net.minestom.server.entity.metadata.animal.tameable.CatMeta;
import net.minestom.server.entity.metadata.animal.tameable.ParrotMeta;
import net.minestom.server.entity.metadata.animal.tameable.WolfMeta;
@ -35,73 +17,15 @@ import net.minestom.server.entity.metadata.flying.PhantomMeta;
import net.minestom.server.entity.metadata.golem.IronGolemMeta;
import net.minestom.server.entity.metadata.golem.ShulkerMeta;
import net.minestom.server.entity.metadata.golem.SnowGolemMeta;
import net.minestom.server.entity.metadata.item.EyeOfEnderMeta;
import net.minestom.server.entity.metadata.item.FireballMeta;
import net.minestom.server.entity.metadata.item.ItemEntityMeta;
import net.minestom.server.entity.metadata.item.SmallFireballMeta;
import net.minestom.server.entity.metadata.item.SnowballMeta;
import net.minestom.server.entity.metadata.item.ThrownEggMeta;
import net.minestom.server.entity.metadata.item.ThrownEnderPearlMeta;
import net.minestom.server.entity.metadata.item.ThrownExperienceBottleMeta;
import net.minestom.server.entity.metadata.item.ThrownPotionMeta;
import net.minestom.server.entity.metadata.minecart.ChestMinecartMeta;
import net.minestom.server.entity.metadata.minecart.CommandBlockMinecartMeta;
import net.minestom.server.entity.metadata.minecart.FurnaceMinecartMeta;
import net.minestom.server.entity.metadata.minecart.HopperMinecartMeta;
import net.minestom.server.entity.metadata.minecart.MinecartMeta;
import net.minestom.server.entity.metadata.minecart.SpawnerMinecartMeta;
import net.minestom.server.entity.metadata.minecart.TntMinecartMeta;
import net.minestom.server.entity.metadata.monster.BlazeMeta;
import net.minestom.server.entity.metadata.monster.CaveSpiderMeta;
import net.minestom.server.entity.metadata.monster.CreeperMeta;
import net.minestom.server.entity.metadata.monster.ElderGuardianMeta;
import net.minestom.server.entity.metadata.monster.EndermanMeta;
import net.minestom.server.entity.metadata.monster.EndermiteMeta;
import net.minestom.server.entity.metadata.monster.GiantMeta;
import net.minestom.server.entity.metadata.monster.GuardianMeta;
import net.minestom.server.entity.metadata.monster.PiglinBruteMeta;
import net.minestom.server.entity.metadata.monster.PiglinMeta;
import net.minestom.server.entity.metadata.monster.SilverfishMeta;
import net.minestom.server.entity.metadata.monster.SpiderMeta;
import net.minestom.server.entity.metadata.monster.VexMeta;
import net.minestom.server.entity.metadata.monster.WitherMeta;
import net.minestom.server.entity.metadata.monster.ZoglinMeta;
import net.minestom.server.entity.metadata.monster.raider.EvokerMeta;
import net.minestom.server.entity.metadata.monster.raider.IllusionerMeta;
import net.minestom.server.entity.metadata.monster.raider.PillagerMeta;
import net.minestom.server.entity.metadata.monster.raider.RavagerMeta;
import net.minestom.server.entity.metadata.monster.raider.VindicatorMeta;
import net.minestom.server.entity.metadata.monster.raider.WitchMeta;
import net.minestom.server.entity.metadata.item.*;
import net.minestom.server.entity.metadata.minecart.*;
import net.minestom.server.entity.metadata.monster.*;
import net.minestom.server.entity.metadata.monster.raider.*;
import net.minestom.server.entity.metadata.monster.skeleton.SkeletonMeta;
import net.minestom.server.entity.metadata.monster.skeleton.StrayMeta;
import net.minestom.server.entity.metadata.monster.skeleton.WitherSkeletonMeta;
import net.minestom.server.entity.metadata.monster.zombie.DrownedMeta;
import net.minestom.server.entity.metadata.monster.zombie.HuskMeta;
import net.minestom.server.entity.metadata.monster.zombie.ZombieMeta;
import net.minestom.server.entity.metadata.monster.zombie.ZombieVillagerMeta;
import net.minestom.server.entity.metadata.monster.zombie.ZombifiedPiglinMeta;
import net.minestom.server.entity.metadata.other.AreaEffectCloudMeta;
import net.minestom.server.entity.metadata.other.ArmorStandMeta;
import net.minestom.server.entity.metadata.other.BoatMeta;
import net.minestom.server.entity.metadata.other.DragonFireballMeta;
import net.minestom.server.entity.metadata.other.EndCrystalMeta;
import net.minestom.server.entity.metadata.other.EnderDragonMeta;
import net.minestom.server.entity.metadata.other.EvokerFangsMeta;
import net.minestom.server.entity.metadata.other.ExperienceOrbMeta;
import net.minestom.server.entity.metadata.other.FallingBlockMeta;
import net.minestom.server.entity.metadata.other.FireworkRocketMeta;
import net.minestom.server.entity.metadata.other.FishingHookMeta;
import net.minestom.server.entity.metadata.other.ItemFrameMeta;
import net.minestom.server.entity.metadata.other.LeashKnotMeta;
import net.minestom.server.entity.metadata.other.LightningBoltMeta;
import net.minestom.server.entity.metadata.other.LlamaSpitMeta;
import net.minestom.server.entity.metadata.other.MagmaCubeMeta;
import net.minestom.server.entity.metadata.other.PaintingMeta;
import net.minestom.server.entity.metadata.other.PrimedTntMeta;
import net.minestom.server.entity.metadata.other.ShulkerBulletMeta;
import net.minestom.server.entity.metadata.other.SlimeMeta;
import net.minestom.server.entity.metadata.other.TraderLlamaMeta;
import net.minestom.server.entity.metadata.other.WitherSkullMeta;
import net.minestom.server.entity.metadata.monster.zombie.*;
import net.minestom.server.entity.metadata.other.*;
import net.minestom.server.entity.metadata.villager.VillagerMeta;
import net.minestom.server.entity.metadata.villager.WanderingTraderMeta;
import net.minestom.server.entity.metadata.water.DolphinMeta;
@ -114,13 +38,15 @@ import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiFunction;
/**
* //==============================
* // AUTOGENERATED BY EnumGenerator
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum EntityType {
public enum EntityType implements Keyed {
AREA_EFFECT_CLOUD("minecraft:area_effect_cloud", 6.0, 0.5, AreaEffectCloudMeta::new, EntitySpawnType.BASE),
ARMOR_STAND("minecraft:armor_stand", 0.5, 1.975, ArmorStandMeta::new, EntitySpawnType.LIVING),
@ -352,6 +278,8 @@ public enum EntityType {
@NotNull
private final EntitySpawnType spawnType;
private final Key key;
EntityType(@NotNull String namespaceID, double width, double height,
@NotNull BiFunction<Entity, Metadata, EntityMeta> metaConstructor,
@NotNull EntitySpawnType spawnType) {
@ -361,6 +289,7 @@ public enum EntityType {
this.metaConstructor = metaConstructor;
this.spawnType = spawnType;
Registries.entityTypes.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public short getId() {
@ -393,4 +322,8 @@ public enum EntityType {
}
return null;
}
public Key key() {
return this.key;
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.fluids;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Fluid {
public enum Fluid implements Keyed {
EMPTY("minecraft:empty"),
FLOWING_WATER("minecraft:flowing_water"),
@ -20,11 +22,14 @@ public enum Fluid {
LAVA("minecraft:lava");
private String namespaceID;
private final String namespaceID;
private final Key key;
Fluid(String namespaceID) {
this.namespaceID = namespaceID;
Registries.fluids.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -35,6 +40,10 @@ public enum Fluid {
return namespaceID;
}
public Key key() {
return this.key;
}
public static Fluid fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];

View File

@ -2,6 +2,8 @@ package net.minestom.server.instance.block;
import java.util.Arrays;
import java.util.List;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.instance.block.states.AcaciaButton;
import net.minestom.server.instance.block.states.AcaciaDoor;
import net.minestom.server.instance.block.states.AcaciaFence;
@ -481,7 +483,7 @@ import org.jetbrains.annotations.Nullable;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Block {
public enum Block implements Keyed {
AIR("minecraft:air", (short) 0, 0.0, 0.0, true, false, null, true),
STONE("minecraft:stone", (short) 1, 1.5, 6.0, false, true, null, true),
@ -2480,25 +2482,27 @@ public enum Block {
}
@NotNull
private String namespaceID;
private final String namespaceID;
private short defaultID;
private final short defaultID;
private double hardness;
private final double hardness;
private double resistance;
private final double resistance;
private boolean isAir;
private final boolean isAir;
private boolean isSolid;
private final boolean isSolid;
@Nullable
private NamespaceID blockEntity;
private final NamespaceID blockEntity;
private boolean singleState;
private final boolean singleState;
private List<BlockAlternative> alternatives = new java.util.ArrayList<>();
private final Key key;
Block(@NotNull String namespaceID, short defaultID, double hardness, double resistance,
boolean isAir, boolean isSolid, @Nullable NamespaceID blockEntity,
boolean singleState) {
@ -2514,6 +2518,7 @@ public enum Block {
addBlockAlternative(new BlockAlternative(defaultID));
}
Registries.blocks.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public short getBlockId() {
@ -2586,4 +2591,8 @@ public enum Block {
public static Block fromStateId(short blockStateId) {
return BlockArray.blocks[blockStateId];
}
public Key key() {
return this.key;
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.item;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Enchantment {
public enum Enchantment implements Keyed {
PROTECTION("minecraft:protection"),
FIRE_PROTECTION("minecraft:fire_protection"),
@ -86,11 +88,14 @@ public enum Enchantment {
VANISHING_CURSE("minecraft:vanishing_curse");
private String namespaceID;
private final String namespaceID;
private final Key key;
Enchantment(String namespaceID) {
this.namespaceID = namespaceID;
Registries.enchantments.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -101,6 +106,10 @@ public enum Enchantment {
return namespaceID;
}
public Key key() {
return this.key;
}
public static Enchantment fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];

View File

@ -1,5 +1,7 @@
package net.minestom.server.item;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.instance.block.Block;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -12,7 +14,7 @@ import org.jetbrains.annotations.Nullable;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Material {
public enum Material implements Keyed {
AIR("minecraft:air", 64, Block.AIR),
STONE("minecraft:stone", 64, Block.STONE),
@ -1966,12 +1968,14 @@ public enum Material {
RESPAWN_ANCHOR("minecraft:respawn_anchor", 64, Block.RESPAWN_ANCHOR);
@NotNull
private String namespaceID;
private final String namespaceID;
private int maxDefaultStackSize;
private final int maxDefaultStackSize;
@Nullable
private Block correspondingBlock;
private final Block correspondingBlock;
private final Key key;
Material(@NotNull String namespaceID, int maxDefaultStackSize,
@Nullable Block correspondingBlock) {
@ -1979,6 +1983,7 @@ public enum Material {
this.maxDefaultStackSize = maxDefaultStackSize;
this.correspondingBlock = correspondingBlock;
Registries.materials.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public short getId() {
@ -2083,4 +2088,8 @@ public enum Material {
}
return isFood();
}
public Key key() {
return this.key;
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.particle;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Particle {
public enum Particle implements Keyed {
AMBIENT_ENTITY_EFFECT("minecraft:ambient_entity_effect"),
ANGRY_VILLAGER("minecraft:angry_villager"),
@ -154,11 +156,14 @@ public enum Particle {
WHITE_ASH("minecraft:white_ash");
private String namespaceID;
private final String namespaceID;
private final Key key;
Particle(String namespaceID) {
this.namespaceID = namespaceID;
Registries.particles.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -169,6 +174,10 @@ public enum Particle {
return namespaceID;
}
public Key key() {
return this.key;
}
public static Particle fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];

View File

@ -1,5 +1,7 @@
package net.minestom.server.potion;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum PotionEffect {
public enum PotionEffect implements Keyed {
SPEED("minecraft:speed"),
SLOWNESS("minecraft:slowness"),
@ -74,11 +76,14 @@ public enum PotionEffect {
HERO_OF_THE_VILLAGE("minecraft:hero_of_the_village");
private String namespaceID;
private final String namespaceID;
private final Key key;
PotionEffect(String namespaceID) {
this.namespaceID = namespaceID;
Registries.potionEffects.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -89,6 +94,10 @@ public enum PotionEffect {
return namespaceID;
}
public Key key() {
return this.key;
}
public static PotionEffect fromId(int id) {
if (id >= 0 && id < values().length + 1) {
return values()[id - 1];

View File

@ -1,5 +1,7 @@
package net.minestom.server.potion;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum PotionType {
public enum PotionType implements Keyed {
EMPTY("minecraft:empty"),
WATER("minecraft:water"),
@ -96,11 +98,14 @@ public enum PotionType {
LONG_SLOW_FALLING("minecraft:long_slow_falling");
private String namespaceID;
private final String namespaceID;
private final Key key;
PotionType(String namespaceID) {
this.namespaceID = namespaceID;
Registries.potionTypes.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -111,6 +116,10 @@ public enum PotionType {
return namespaceID;
}
public Key key() {
return this.key;
}
public static PotionType fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];

View File

@ -2,6 +2,7 @@
package net.minestom.server.registry;
import java.util.HashMap;
import net.kyori.adventure.key.Key;
import net.minestom.server.entity.EntityType;
import net.minestom.server.fluids.Fluid;
import net.minestom.server.instance.block.Block;
@ -10,7 +11,7 @@ import net.minestom.server.item.Material;
import net.minestom.server.particle.Particle;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionType;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.stat.StatisticType;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
@ -66,7 +67,7 @@ public final class Registries {
* Should only be used for internal code, please use the get* methods.
*/
@Deprecated
public static final HashMap<NamespaceID, Sound> sounds = new HashMap<>();
public static final HashMap<NamespaceID, SoundEvent> soundEvents = new HashMap<>();
/**
* Should only be used for internal code, please use the get* methods.
@ -96,6 +97,14 @@ public final class Registries {
return blocks.getOrDefault(id, Block.AIR);
}
/**
* Returns the corresponding Block matching the given key. Returns 'AIR' if none match.
*/
@NotNull
public static Block getBlock(Key key) {
return getBlock(NamespaceID.from(key));
}
/**
* Returns the corresponding Material matching the given id. Returns 'AIR' if none match.
*/
@ -112,6 +121,14 @@ public final class Registries {
return materials.getOrDefault(id, Material.AIR);
}
/**
* Returns the corresponding Material matching the given key. Returns 'AIR' if none match.
*/
@NotNull
public static Material getMaterial(Key key) {
return getMaterial(NamespaceID.from(key));
}
/**
* Returns the corresponding Enchantment matching the given id. Returns null if none match.
*/
@ -128,6 +145,14 @@ public final class Registries {
return enchantments.get(id);
}
/**
* Returns the corresponding Enchantment matching the given key. Returns null if none match.
*/
@Nullable
public static Enchantment getEnchantment(Key key) {
return getEnchantment(NamespaceID.from(key));
}
/**
* Returns the corresponding EntityType matching the given id. Returns null if none match.
*/
@ -144,6 +169,14 @@ public final class Registries {
return entityTypes.get(id);
}
/**
* Returns the corresponding EntityType matching the given key. Returns null if none match.
*/
@Nullable
public static EntityType getEntityType(Key key) {
return getEntityType(NamespaceID.from(key));
}
/**
* Returns the corresponding Particle matching the given id. Returns null if none match.
*/
@ -160,6 +193,14 @@ public final class Registries {
return particles.get(id);
}
/**
* Returns the corresponding Particle matching the given key. Returns null if none match.
*/
@Nullable
public static Particle getParticle(Key key) {
return getParticle(NamespaceID.from(key));
}
/**
* Returns the corresponding PotionType matching the given id. Returns null if none match.
*/
@ -176,6 +217,14 @@ public final class Registries {
return potionTypes.get(id);
}
/**
* Returns the corresponding PotionType matching the given key. Returns null if none match.
*/
@Nullable
public static PotionType getPotionType(Key key) {
return getPotionType(NamespaceID.from(key));
}
/**
* Returns the corresponding PotionEffect matching the given id. Returns null if none match.
*/
@ -193,19 +242,35 @@ public final class Registries {
}
/**
* Returns the corresponding Sound matching the given id. Returns null if none match.
* Returns the corresponding PotionEffect matching the given key. Returns null if none match.
*/
@Nullable
public static Sound getSound(String id) {
return getSound(NamespaceID.from(id));
public static PotionEffect getPotionEffect(Key key) {
return getPotionEffect(NamespaceID.from(key));
}
/**
* Returns the corresponding Sound matching the given id. Returns null if none match.
* Returns the corresponding SoundEvent matching the given id. Returns null if none match.
*/
@Nullable
public static Sound getSound(NamespaceID id) {
return sounds.get(id);
public static SoundEvent getSoundEvent(String id) {
return getSoundEvent(NamespaceID.from(id));
}
/**
* Returns the corresponding SoundEvent matching the given id. Returns null if none match.
*/
@Nullable
public static SoundEvent getSoundEvent(NamespaceID id) {
return soundEvents.get(id);
}
/**
* Returns the corresponding SoundEvent matching the given key. Returns null if none match.
*/
@Nullable
public static SoundEvent getSoundEvent(Key key) {
return getSoundEvent(NamespaceID.from(key));
}
/**
@ -224,6 +289,14 @@ public final class Registries {
return statisticTypes.get(id);
}
/**
* Returns the corresponding StatisticType matching the given key. Returns null if none match.
*/
@Nullable
public static StatisticType getStatisticType(Key key) {
return getStatisticType(NamespaceID.from(key));
}
/**
* Returns the corresponding Fluid matching the given id. Returns 'EMPTY' if none match.
*/
@ -239,4 +312,12 @@ public final class Registries {
public static Fluid getFluid(NamespaceID id) {
return fluids.getOrDefault(id, Fluid.EMPTY);
}
/**
* Returns the corresponding Fluid matching the given key. Returns 'EMPTY' if none match.
*/
@NotNull
public static Fluid getFluid(Key key) {
return getFluid(NamespaceID.from(key));
}
}

View File

@ -1,5 +1,8 @@
package net.minestom.server.sound;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.kyori.adventure.sound.Sound;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +12,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum Sound {
public enum SoundEvent implements Keyed, Sound.Type {
AMBIENT_CAVE("minecraft:ambient.cave"),
AMBIENT_BASALT_DELTAS_ADDITIONS("minecraft:ambient.basalt_deltas.additions"),
@ -1994,11 +1997,14 @@ public enum Sound {
ENTITY_ZOMBIE_VILLAGER_STEP("minecraft:entity.zombie_villager.step");
private String namespaceID;
private final String namespaceID;
Sound(String namespaceID) {
private final Key key;
SoundEvent(String namespaceID) {
this.namespaceID = namespaceID;
Registries.sounds.put(NamespaceID.from(namespaceID), this);
Registries.soundEvents.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -2009,7 +2015,11 @@ public enum Sound {
return namespaceID;
}
public static Sound fromId(int id) {
public Key key() {
return this.key;
}
public static SoundEvent fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.stat;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID;
* //==============================
*/
@SuppressWarnings({"deprecation"})
public enum StatisticType {
public enum StatisticType implements Keyed {
LEAVE_GAME("minecraft:leave_game"),
PLAY_ONE_MINUTE("minecraft:play_one_minute"),
@ -158,11 +160,14 @@ public enum StatisticType {
INTERACT_WITH_SMITHING_TABLE("minecraft:interact_with_smithing_table");
private String namespaceID;
private final String namespaceID;
private final Key key;
StatisticType(String namespaceID) {
this.namespaceID = namespaceID;
Registries.statisticTypes.put(NamespaceID.from(namespaceID), this);
this.key = Key.key(this.namespaceID);
}
public int getId() {
@ -173,6 +178,10 @@ public enum StatisticType {
return namespaceID;
}
public Key key() {
return this.key;
}
public static StatisticType fromId(int id) {
if (id >= 0 && id < values().length) {
return values()[id];

View File

@ -3,6 +3,8 @@ package net.minestom.codegen;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
@ -116,6 +118,12 @@ public abstract class BasicEnumGenerator extends MinestomEnumGenerator<BasicEnum
generator.appendToConstructor(code -> {
code.addStatement("$T." + CodeGenerator.decapitalize(getClassName()) + "s.put($T.from($N), this)", registriesClass, NamespaceID.class, "namespaceID");
});
// implement Keyed
generator.addSuperinterface(ClassName.get(Keyed.class));
generator.addField(ClassName.get(Key.class), "key", true);
generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)"));
generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key"));
}
@Override

View File

@ -1,6 +1,7 @@
package net.minestom.codegen;
import com.squareup.javapoet.*;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,9 +21,11 @@ public class EnumGenerator implements CodeGenerator {
private final String enumName;
private ParameterSpec[] parameters;
private List<TypeName> superinterfaces = new LinkedList<>();
private List<Method> methods = new LinkedList<>();
private List<Field> fields = new LinkedList<>();
private List<Field> staticFields = new LinkedList<>();
private List<Instance> instances = new LinkedList<>();
private List<Pair<Field, Boolean>> fields = new LinkedList<>();
private List<Field> hardcodedFields = new LinkedList<>();
private List<AnnotationSpec> annotations = new LinkedList<>();
private String enumPackage;
@ -35,6 +38,10 @@ public class EnumGenerator implements CodeGenerator {
this.enumName = enumName;
}
public void addSuperinterface(TypeName typeNames) {
superinterfaces.add(typeNames);
}
public void setParams(ParameterSpec... parameters) {
this.parameters = parameters;
}
@ -52,7 +59,7 @@ public class EnumGenerator implements CodeGenerator {
}
public void addStaticField(TypeName type, String name, String value) {
fields.add(new Field(type, name, value));
staticFields.add(new Field(type, name, value));
}
public void addInstance(String name, Object... parameters) {
@ -86,6 +93,9 @@ public class EnumGenerator implements CodeGenerator {
enumClass.addEnumConstant(instance.name, arguments);
}
// add superinterfaces
enumClass.addSuperinterfaces(superinterfaces);
if (staticBlock != null) {
enumClass.addStaticBlock(staticBlock);
}
@ -100,7 +110,7 @@ public class EnumGenerator implements CodeGenerator {
.build());
}
for (Field field : fields) {
for (Field field : staticFields) {
enumClass.addField(FieldSpec.builder(field.type, field.name)
.initializer("$L", field.value)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
@ -115,6 +125,18 @@ public class EnumGenerator implements CodeGenerator {
.build());
}
// normal fields
for (Pair<Field, Boolean> field : fields) {
FieldSpec.Builder builder = FieldSpec.builder(field.getLeft().type, field.getLeft().name)
.addModifiers(Modifier.PRIVATE);
if (field.getRight()) {
builder.addModifiers(Modifier.FINAL);
}
enumClass.addField(builder.build());
}
// constructor
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder();
for (int i = 0; i < parameters.length; i++) {
@ -166,6 +188,10 @@ public class EnumGenerator implements CodeGenerator {
constructorEnds.add(constructorEnding);
}
public void addField(TypeName type, String name, boolean isFinal) {
fields.add(Pair.of(new Field(type, name), isFinal));
}
public void addHardcodedField(TypeName type, String name, String value) {
hardcodedFields.add(new Field(type, name, value));
}
@ -210,6 +236,10 @@ public class EnumGenerator implements CodeGenerator {
private String name;
private String value;
public Field(TypeName type, String name) {
this(type, name, null);
}
public Field(TypeName type, String name, String value) {
this.type = type;
this.name = name;

View File

@ -1,6 +1,7 @@
package net.minestom.codegen;
import com.squareup.javapoet.*;
import net.kyori.adventure.key.Key;
import net.minestom.server.entity.EntityType;
import net.minestom.server.fluids.Fluid;
import net.minestom.server.instance.block.Block;
@ -10,7 +11,7 @@ import net.minestom.server.particle.Particle;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionType;
import net.minestom.server.registry.ResourceGatherer;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.stat.StatisticType;
import net.minestom.server.utils.NamespaceID;
import org.apache.commons.lang3.tuple.ImmutablePair;
@ -44,7 +45,7 @@ public class RegistriesGenerator implements CodeGenerator {
new ImmutablePair<>(Particle.class.getCanonicalName(), null),
new ImmutablePair<>(PotionType.class.getCanonicalName(), null),
new ImmutablePair<>(PotionEffect.class.getCanonicalName(), null),
new ImmutablePair<>(Sound.class.getCanonicalName(), null),
new ImmutablePair<>(SoundEvent.class.getCanonicalName(), null),
new ImmutablePair<>(StatisticType.class.getCanonicalName(), null),
new ImmutablePair<>(Fluid.class.getCanonicalName(), "EMPTY"),
};
@ -101,6 +102,7 @@ public class RegistriesGenerator implements CodeGenerator {
ParameterSpec namespaceIDParam = ParameterSpec.builder(ClassName.get(NamespaceID.class), "id")
.build();
ParameterSpec keyIDParam = ParameterSpec.builder(ClassName.get(Key.class), "key").build();
CodeBlock.Builder code = CodeBlock.builder();
Class<? extends Annotation> annotation;
@ -134,6 +136,16 @@ public class RegistriesGenerator implements CodeGenerator {
.addCode(code.build())
.addJavadoc(comment.toString())
.build());
// Key variant
registriesClass.addMethod(MethodSpec.methodBuilder("get" + simpleType)
.returns(type)
.addAnnotation(annotation)
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
.addParameter(keyIDParam)
.addStatement("return get$N(NamespaceID.from($N))", simpleType, keyIDParam)
.addJavadoc(comment.toString().replace(" id.", " key."))
.build());
}
JavaFile file = JavaFile.builder("net.minestom.server.registry", registriesClass.build())

View File

@ -5,6 +5,8 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.codegen.EnumGenerator;
import net.minestom.codegen.MinestomEnumGenerator;
import net.minestom.codegen.PrismarinePaths;
@ -298,6 +300,12 @@ public class BlockEnumGenerator extends MinestomEnumGenerator<BlockContainer> {
.endControlFlow()
.addStatement("$T.blocks.put($T.from(namespaceID), this)", Registries.class, NamespaceID.class);
});
// implement Keyed
generator.addSuperinterface(ClassName.get(Keyed.class));
generator.addField(ClassName.get(Key.class), "key", true);
generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)"));
generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key"));
}
@Override

View File

@ -4,6 +4,8 @@ import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.codegen.ConstructorLambda;
import net.minestom.codegen.EnumGenerator;
import net.minestom.codegen.MinestomEnumGenerator;
@ -177,6 +179,12 @@ public class EntityTypeEnumGenerator extends MinestomEnumGenerator<EntityTypeCon
.endControlFlow()
.addStatement("return null");
});
// implement Keyed
generator.addSuperinterface(ClassName.get(Keyed.class));
generator.addField(ClassName.get(Key.class), "key", true);
generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)"));
generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key"));
}
@Override

View File

@ -4,6 +4,8 @@ import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.codegen.EnumGenerator;
import net.minestom.codegen.MinestomEnumGenerator;
import net.minestom.codegen.PrismarinePaths;
@ -218,6 +220,12 @@ public class ItemEnumGenerator extends MinestomEnumGenerator<ItemContainer> {
.endControlFlow()
.addStatement("return isFood()");
});
// implement Keyed
generator.addSuperinterface(ClassName.get(Keyed.class));
generator.addField(ClassName.get(Key.class), "key", true);
generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)"));
generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key"));
}
@Override

View File

@ -1,7 +1,9 @@
package net.minestom.codegen.sounds;
import com.squareup.javapoet.ClassName;
import net.kyori.adventure.sound.Sound;
import net.minestom.codegen.BasicEnumGenerator;
import net.minestom.codegen.stats.StatsEnumGenerator;
import net.minestom.codegen.EnumGenerator;
import net.minestom.server.registry.ResourceGatherer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -45,6 +47,14 @@ public class SoundEnumGenerator extends BasicEnumGenerator {
super(targetFolder);
}
@Override
protected void prepare(EnumGenerator generator) {
super.prepare(generator);
// implement type as well
generator.addSuperinterface(ClassName.get(Sound.Type.class));
}
@Override
protected String getCategoryID() {
return "minecraft:sound_event";
@ -57,7 +67,7 @@ public class SoundEnumGenerator extends BasicEnumGenerator {
@Override
public String getClassName() {
return "Sound";
return "SoundEvent";
}
@Override

View File

@ -1,6 +1,9 @@
package net.minestom.server;
import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.benchmark.BenchmarkManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.data.DataManager;
@ -37,7 +40,7 @@ import net.minestom.server.potion.PotionType;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.ResourceGatherer;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.stat.StatisticType;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.storage.StorageManager;
@ -115,6 +118,8 @@ public final class MinecraftServer {
private static DimensionTypeManager dimensionTypeManager;
private static BiomeManager biomeManager;
private static AdvancementManager advancementManager;
private static BossBarManager bossBarManager;
private static Audiences audiences;
private static ExtensionManager extensionManager;
@ -158,7 +163,7 @@ public final class MinecraftServer {
PotionEffect.values();
Enchantment.values();
EntityType.values();
Sound.values();
SoundEvent.values();
Particle.values();
StatisticType.values();
Fluid.values();
@ -180,6 +185,8 @@ public final class MinecraftServer {
dimensionTypeManager = new DimensionTypeManager();
biomeManager = new BiomeManager();
advancementManager = new AdvancementManager();
bossBarManager = new BossBarManager();
audiences = new Audiences();
updateManager = new UpdateManager();
@ -427,6 +434,26 @@ public final class MinecraftServer {
return connectionManager;
}
/**
* Gets the boss bar manager.
*
* @return the boss bar manager
*/
public static BossBarManager getBossBarManager() {
checkInitStatus(bossBarManager);
return bossBarManager;
}
/**
* Gets the audiences instance.
*
* @return the audiences instance
*/
public static Audiences getAudiences() {
checkInitStatus(audiences);
return audiences;
}
/**
* Gets the object handling the client packets processing.
* <p>
@ -802,5 +829,4 @@ public final class MinecraftServer {
"You cannot access the manager before MinecraftServer#init, " +
"if you are developing an extension be sure to retrieve them at least after Extension#preInitialize");*/
}
}

View File

@ -1,10 +1,13 @@
package net.minestom.server;
import net.kyori.adventure.audience.Audience;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Set;
/**
@ -82,4 +85,23 @@ public interface Viewable {
default void sendPacketToViewersAndSelf(@NotNull ServerPacket packet) {
sendPacketToViewers(packet);
}
/**
* Gets the result of {@link #getViewers()} as an Adventure Audience.
*
* @return the audience
*/
default @NotNull Audience getViewersAsAudience() {
return PacketGroupingAudience.of(this.getViewers());
}
/**
* Gets the result of {@link #getViewers()} as an {@link Iterable} of Adventure
* {@link Audience}s.
*
* @return the audiences
*/
default @NotNull Iterable<? extends Audience> getViewersAsAudiences() {
return this.getViewers();
}
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.advancements;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.item.ItemStack;
@ -23,8 +24,8 @@ public class Advancement {
private boolean achieved;
private JsonMessage title;
private JsonMessage description;
private Component title;
private Component description;
private ItemStack icon;
@ -42,9 +43,29 @@ public class Advancement {
// Packet
private AdvancementsPacket.Criteria criteria;
/**
* @deprecated Use {@link #Advancement(Component, Component, ItemStack, FrameType, float, float)}
*/
@Deprecated
public Advancement(@NotNull JsonMessage title, JsonMessage description,
@NotNull ItemStack icon, @NotNull FrameType frameType,
float x, float y) {
this(title.asComponent(), description.asComponent(), icon, frameType, x, y);
}
/**
* @deprecated Use {@link #Advancement(Component, Component, Material, FrameType, float, float)}
*/
@Deprecated
public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description,
@NotNull Material icon, @NotNull FrameType frameType,
float x, float y) {
this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y);
}
public Advancement(@NotNull Component title, Component description,
@NotNull ItemStack icon, @NotNull FrameType frameType,
float x, float y) {
this.title = title;
this.description = description;
this.icon = icon;
@ -53,7 +74,7 @@ public class Advancement {
this.y = y;
}
public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description,
public Advancement(@NotNull Component title, @NotNull Component description,
@NotNull Material icon, @NotNull FrameType frameType,
float x, float y) {
this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y);
@ -94,14 +115,25 @@ public class Advancement {
this.tab = tab;
}
/**
* Gets the title of the advancement.
*
* @return the title
*/
public Component getTitle() {
return title;
}
/**
* Gets the title of the advancement.
*
* @return the advancement title
* @deprecated Use {@link #getTitle()}
*/
@NotNull
public JsonMessage getTitle() {
return title;
@Deprecated
public JsonMessage getTitleJson() {
return JsonMessage.fromComponent(title);
}
/**
@ -109,31 +141,67 @@ public class Advancement {
*
* @param title the new title
*/
public void setTitle(@NotNull JsonMessage title) {
public void setTitle(@NotNull Component title) {
this.title = title;
update();
}
/**
* Changes the advancement title.
*
* @param title the new title
* @deprecated Use {@link #setTitle(Component)}
*/
@Deprecated
public void setTitle(@NotNull JsonMessage title) {
this.title = title.asComponent();
update();
}
/**
* Gets the description of the advancement.
*
* @return the description title
*/
@NotNull
public JsonMessage getDescription() {
public Component getDescription() {
return description;
}
/**
* Gets the description of the advancement.
*
* @return the description title
* @deprecated Use {@link #getDescription()}
*/
@NotNull
@Deprecated
public JsonMessage getDescriptionJson() {
return JsonMessage.fromComponent(description);
}
/**
* Changes the description title.
*
* @param description the new description
*/
public void setDescription(@NotNull JsonMessage description) {
public void setDescription(@NotNull Component description) {
this.description = description;
update();
}
/**
* Changes the description title.
*
* @param description the new description
* @deprecated Use {@link #setDescription(Component)}
*/
@Deprecated
public void setDescription(@NotNull JsonMessage description) {
this.description = description.asComponent();
update();
}
/**
* Gets the advancement icon.
*

View File

@ -1,5 +1,6 @@
package net.minestom.server.advancements;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.item.ItemStack;
@ -15,6 +16,10 @@ import org.jetbrains.annotations.Nullable;
*/
public class AdvancementRoot extends Advancement {
/**
* @deprecated Use {@link #AdvancementRoot(Component, Component, ItemStack, FrameType, float, float, String)}
*/
@Deprecated
public AdvancementRoot(@NotNull JsonMessage title, @NotNull JsonMessage description,
@NotNull ItemStack icon, @NotNull FrameType frameType,
float x, float y,
@ -23,6 +28,10 @@ public class AdvancementRoot extends Advancement {
setBackground(background);
}
/**
* @deprecated Use {@link #AdvancementRoot(Component, Component, Material, FrameType, float, float, String)}
*/
@Deprecated
public AdvancementRoot(@NotNull JsonMessage title, @NotNull JsonMessage description,
@NotNull Material icon, FrameType frameType,
float x, float y,
@ -31,4 +40,20 @@ public class AdvancementRoot extends Advancement {
setBackground(background);
}
public AdvancementRoot(@NotNull Component title, @NotNull Component description,
@NotNull ItemStack icon, @NotNull FrameType frameType,
float x, float y,
@Nullable String background) {
super(title, description, icon, frameType, x, y);
setBackground(background);
}
public AdvancementRoot(@NotNull Component title, @NotNull Component description,
@NotNull Material icon, FrameType frameType,
float x, float y,
@Nullable String background) {
super(title, description, icon, frameType, x, y);
setBackground(background);
}
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.advancements.notifications;
import net.kyori.adventure.text.Component;
import net.minestom.server.advancements.FrameType;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.item.ItemStack;
@ -11,20 +12,47 @@ import org.jetbrains.annotations.NotNull;
*/
public class Notification {
private final JsonMessage title;
private final Component title;
private final FrameType frameType;
private final ItemStack icon;
/**
* @deprecated Use {@link #Notification(Component, FrameType, ItemStack)}
*/
@Deprecated
public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull ItemStack icon) {
this(title.asComponent(), frameType, icon);
}
/**
* @deprecated Use {@link #Notification(Component, FrameType, Material)}
*/
@Deprecated
public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull Material icon) {
this(title.asComponent(), frameType, icon);
}
public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull Material icon) {
this(title, frameType, new ItemStack(icon, (byte) 1));
}
public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull ItemStack icon) {
this.title = title;
this.frameType = frameType;
this.icon = icon;
}
public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull Material icon) {
this.title = title;
this.frameType = frameType;
this.icon = new ItemStack(icon, (byte) 1);
/**
* Gets the title of the notification.
*
* @return the notification title
*
* @deprecated Use {@link #getTitle()}
*/
@NotNull
@Deprecated
public JsonMessage getTitleJson() {
return JsonMessage.fromComponent(title);
}
/**
@ -32,8 +60,7 @@ public class Notification {
*
* @return the notification title
*/
@NotNull
public JsonMessage getTitle() {
public Component getTitle() {
return title;
}

View File

@ -1,6 +1,6 @@
package net.minestom.server.advancements.notifications;
import net.minestom.server.chat.ColoredText;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.AdvancementsPacket;
import net.minestom.server.network.player.PlayerConnection;
@ -84,7 +84,7 @@ public class NotificationCenter {
{
displayData.title = notification.getTitle();
// Description is required, but never shown/seen so, small Easter egg.
displayData.description = ColoredText.of("Articdive was here. #Minestom");
displayData.description = Component.text("Articdive was here. #Minestom");
displayData.icon = notification.getIcon();
displayData.frameType = notification.getFrameType();
displayData.flags = 0x6;

View File

@ -0,0 +1,148 @@
package net.minestom.server.adventure;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.NamedSoundEffectPacket;
import net.minestom.server.network.packet.server.play.SoundEffectPacket;
import net.minestom.server.network.packet.server.play.StopSoundPacket;
import net.minestom.server.registry.Registries;
import net.minestom.server.sound.SoundEvent;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Utility methods to convert adventure enums to their packet values.
*/
public class AdventurePacketConvertor {
private static final Object2IntMap<NamedTextColor> NAMED_TEXT_COLOR_ID_MAP = new Object2IntArrayMap<>(16);
static {
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.BLACK, 0);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_BLUE, 1);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_GREEN, 2);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_AQUA, 3);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_RED, 4);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_PURPLE, 5);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.GOLD, 6);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.GRAY, 7);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.DARK_GRAY, 8);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.BLUE, 9);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.GREEN, 10);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.AQUA, 11);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.RED, 12);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.LIGHT_PURPLE, 13);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.YELLOW, 14);
NAMED_TEXT_COLOR_ID_MAP.put(NamedTextColor.WHITE, 15);
}
/**
* Gets the int value of a boss bar overlay.
* @param overlay the overlay
* @return the value
*/
public static int getBossBarOverlayValue(@NotNull BossBar.Overlay overlay) {
return overlay.ordinal();
}
/**
* Gets the byte value of a collection of boss bar flags.
* @param flags the flags
* @return the value
*/
public static byte getBossBarFlagValue(@NotNull Collection<BossBar.Flag> flags) {
byte val = 0x0;
for (BossBar.Flag flag : flags) {
val |= flag.ordinal();
}
return val;
}
/**
* Gets the int value of a boss bar color.
* @param color the color
* @return the value
*/
public static int getBossBarColorValue(@NotNull BossBar.Color color) {
return color.ordinal();
}
/**
* Gets the int value of a sound source.
* @param source the source
* @return the value
*/
public static int getSoundSourceValue(@NotNull Sound.Source source) {
return source.ordinal();
}
/**
* Gets the int value from a named text color.
* @param color the color
* @return the int value
*/
public static int getNamedTextColorValue(@NotNull NamedTextColor color) {
return NAMED_TEXT_COLOR_ID_MAP.getInt(color);
}
/**
* Creates a sound packet from a sound and a location.
* @param sound the sound
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return the sound packet
*/
public static ServerPacket createSoundPacket(@NotNull Sound sound, double x, double y, double z) {
SoundEvent minestomSound = Registries.getSoundEvent(sound.name());
if (minestomSound == null) {
NamedSoundEffectPacket packet = new NamedSoundEffectPacket();
packet.soundName = sound.name().asString();
packet.soundSource = sound.source();
packet.x = (int) x;
packet.y = (int) y;
packet.z = (int) z;
packet.volume = sound.volume();
packet.pitch = sound.pitch();
return packet;
} else {
SoundEffectPacket packet = new SoundEffectPacket();
packet.soundId = minestomSound.getId();
packet.soundSource = sound.source();
packet.x = (int) x;
packet.y = (int) y;
packet.z = (int) z;
packet.volume = sound.volume();
packet.pitch = sound.pitch();
return packet;
}
}
/**
* Creates a sound stop packet from a sound stop.
* @param stop the sound stop
* @return the sound stop packet
*/
public static ServerPacket createSoundStopPacket(@NotNull SoundStop stop) {
StopSoundPacket packet = new StopSoundPacket();
packet.flags = 0x0;
if (stop.source() != null) {
packet.flags |= 0x1;
packet.source = AdventurePacketConvertor.getSoundSourceValue(stop.source());
}
if (stop.sound() != null) {
packet.flags |= 0x2;
packet.sound = stop.sound().asString();
}
return packet;
}
}

View File

@ -0,0 +1,180 @@
package net.minestom.server.adventure;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.TranslationRegistry;
import net.kyori.adventure.translation.Translator;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
/**
* Manager class for handling Adventure serialization. By default AdventureSerializer will simply
* serialize components to Strings using {@link GsonComponentSerializer}. However, AdventureSerializer
* class can be used to change the way text is serialized. For example, a pre-JSON
* implementation of Minestom could change AdventureSerializer to the plain component serializer.
* <br><br>
* This manager also performs translation on all messages and the {@code serialize}
* method should be used when converting {@link Component}s into strings. This allows for
* messages with {@link TranslatableComponent} to be automatically translated into the locale
* of specific players, or other elements which implement {@link Localizable}. To add your
* own translations, use {@link GlobalTranslator#addSource(Translator)} with a
* {@link TranslationRegistry} or your own implementation of {@link Translator}.
*/
public class AdventureSerializer {
/**
* If components should be automatically translated in outgoing packets.
*/
public static final boolean AUTOMATIC_COMPONENT_TRANSLATION = false;
protected static final Localizable NULL_LOCALIZABLE = () -> null;
private static Function<Component, String> serializer = component -> GsonComponentSerializer.gson().serialize(component);
private static Locale defaultLocale = Locale.US;
private AdventureSerializer() {}
/**
* Gets the root serializer that is used to convert components into strings.
*
* @return the serializer
*/
public static @NotNull Function<Component, String> getSerializer() {
return AdventureSerializer.serializer;
}
/**
* Sets the root serializer that is used to convert components into strings.
*
* @param serializer the serializer
*/
public static void setSerializer(@NotNull Function<Component, String> serializer) {
AdventureSerializer.serializer = serializer;
}
/**
* Gets the default locale used to translate {@link TranslatableComponent} if, when
* {@link #translate(Component, Localizable)} is called with a localizable that
* does not have a locale.
*
* @return the default locale
*/
public static @NotNull Locale getDefaultLocale() {
return defaultLocale;
}
/**
* Sets the default locale used to translate {@link TranslatableComponent} if, when
* {@link #translate(Component, Localizable)} is called with a localizable that
* does not have a locale.
*
* @param defaultLocale the new default locale
*/
public static void setDefaultLocale(@NotNull Locale defaultLocale) {
AdventureSerializer.defaultLocale = defaultLocale;
}
/**
* Gets the global translator object used by AdventureSerializer manager. This is just shorthand for
* {@link GlobalTranslator#get()}.
*
* @return the global translator
*/
public static @NotNull GlobalTranslator getTranslator() {
return GlobalTranslator.get();
}
/**
* Prepares a component for serialization. This runs the component through the
* translator for the localizable's locale.
*
* @param component the component
* @param localizable the localizable
*
* @return the prepared component
*/
public static @NotNull Component translate(@NotNull Component component, @NotNull Localizable localizable) {
return GlobalTranslator.renderer().render(component, Objects.requireNonNullElse(localizable.getLocale(), AdventureSerializer.getDefaultLocale()));
}
/**
* Prepares a component for serialization. This runs the component through the
* translator for the locale.
*
* @param component the component
* @param locale the locale
*
* @return the prepared component
*/
public static @NotNull Component translate(@NotNull Component component, @NotNull Locale locale) {
return GlobalTranslator.renderer().render(component, locale);
}
/**
* Serializes a component into a string using {@link #getSerializer()}.
*
* @param component the component
*
* @return the serialized string
*/
public static @NotNull String serialize(@NotNull Component component) {
return AdventureSerializer.serializer.apply(component);
}
/**
* Prepares and then serializes a component.
*
* @param component the component
* @param localizable the localisable
*
* @return the string
*/
public static String translateAndSerialize(@NotNull Component component, @NotNull Localizable localizable) {
return AdventureSerializer.translateAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), AdventureSerializer.getDefaultLocale()));
}
/**
* Prepares and then serializes a component.
*
* @param component the component
* @param locale the locale
*
* @return the string
*/
public static String translateAndSerialize(@NotNull Component component, @NotNull Locale locale) {
return AdventureSerializer.serialize(AdventureSerializer.translate(component, locale));
}
/**
* Checks if a component can be translated server-side. This is done by running the
* component through the translator and seeing if the translated component is equal
* to the non translated component.
* @param component the component
* @return {@code true} if the component can be translated server-side,
* {@code false} otherwise
*/
public static boolean isTranslatable(@NotNull Component component) {
return !component.equals(AdventureSerializer.translate(component, AdventureSerializer.getDefaultLocale()));
}
/**
* Checks if any of a series of components are translatable server-side.
* @param components the components
* @return {@code true} if any of the components can be translated server-side,
* {@code false} otherwise
*/
public static boolean areAnyTranslatable(@NotNull Collection<Component> components) {
for (Component component : components) {
if (AdventureSerializer.isTranslatable(component)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,39 @@
package net.minestom.server.adventure;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
/**
* Represents an object that holds some amount of components.
* @param <T> the holding class
*/
public interface ComponentHolder<T> {
/**
* Gets the components held by this object.
* @return the components
*/
@NotNull Collection<Component> components();
/**
* Returns a copy of this object. For each component this object holds, the operator
* is applied to the copy before returning.
* @param operator the operator
* @return the copy
*/
@NotNull T copyWithOperator(@NotNull UnaryOperator<Component> operator);
/**
* Visits each component held by this object.
* @param visitor the visitor
*/
default void visitComponents(@NotNull Consumer<Component> visitor) {
for (Component component : this.components()) {
visitor.accept(component);
}
}
}

View File

@ -0,0 +1,37 @@
package net.minestom.server.adventure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
/**
* Represents something which can have a locale.
*/
public interface Localizable {
/**
* Gets a localizable that returns {@code null} for all calls to {@link #getLocale()}.
*
* @return the empty localizable
*/
static @NotNull Localizable empty() {
return AdventureSerializer.NULL_LOCALIZABLE;
}
/**
* Gets the locale.
*
* @return the locale, or {@code null} if they do not have a locale set
*/
@Nullable Locale getLocale();
/**
* Sets the locale. This can be set to {@code null} to remove a locale registration.
*
* @param locale the new locale
*/
default void setLocale(@Nullable Locale locale) {
throw new UnsupportedOperationException("You cannot set the locale for this object!");
}
}

View File

@ -0,0 +1,111 @@
package net.minestom.server.adventure.audience;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.function.Predicate;
/**
* A generic provider of {@link Audience}s or some subtype.
* @param <A> the type that is provided
*/
public interface AudienceProvider<A> {
/**
* Gets all audience members. This returns {@link #players()} combined with
* {@link #customs()} and {@link #console()}. This can be a costly operation, so it
* is often preferable to use {@link #server()} instead.
* @return all audience members
*/
@NotNull A all();
/**
* Gets all audience members that are of type {@link Player}.
* @return all players
*/
@NotNull A players();
/**
* Gets all audience members that are of type {@link Player} and match the predicate.
* @param filter the predicate
* @return all players matching the predicate
*/
@NotNull A players(@NotNull Predicate<Player> filter);
/**
* Gets the console as an audience.
* @return the console
*/
@NotNull A console();
/**
* Gets the combination of {@link #players()} and {@link #console()}.
* @return the audience of all players and the console
*/
@NotNull A server();
/**
* Gets all custom audience members stored using the given keyed object.
* @param keyed the keyed object
* @return all custom audience members stored using the key of the object
*/
default @NotNull A custom(@NotNull Keyed keyed) {
return this.custom(keyed.key());
}
/**
* Gets all custom audience members stored using the given key.
* @param key the key
* @return all custom audience members stored using the key
*/
@NotNull A custom(@NotNull Key key);
/**
* Gets all custom audience members stored using the given keyed object that match
* the given predicate.
* @param keyed the keyed object
* @param filter the predicate
* @return all custom audience members stored using the key
*/
default @NotNull A custom(@NotNull Keyed keyed, Predicate<Audience> filter) {
return this.custom(keyed.key(), filter);
}
/**
* Gets all custom audience members stored using the given key that match the
* given predicate.
* @param key the key
* @param filter the predicate
* @return all custom audience members stored using the key
*/
@NotNull A custom(@NotNull Key key, Predicate<Audience> filter);
/**
* Gets all custom audience members.
* @return all custom audience members
*/
@NotNull A customs();
/**
* Gets all custom audience members matching the given predicate.
* @param filter the predicate
* @return all matching custom audience members
*/
@NotNull A customs(@NotNull Predicate<Audience> filter);
/**
* Gets all audience members that match the given predicate.
* @param filter the predicate
* @return all matching audience members
*/
@NotNull A all(@NotNull Predicate<Audience> filter);
/**
* Gets the audience registry used to register custom audiences.
* @return the registry
*/
@NotNull AudienceRegistry registry();
}

View File

@ -0,0 +1,122 @@
package net.minestom.server.adventure.audience;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Holder of custom audiences.
*/
public class AudienceRegistry {
private final Map<Key, Collection<Audience>> registry;
private final Function<Key, Collection<Audience>> provider;
/**
* Creates a new audience registrar with a given backing map.
* @param backingMap the backing map
* @param backingCollection a provider for the backing collection
*/
public AudienceRegistry(@NotNull Map<Key, Collection<Audience>> backingMap, @NotNull Supplier<Collection<Audience>> backingCollection) {
this.registry = backingMap;
this.provider = key -> backingCollection.get();
}
/**
* Checks if this registry is empty.
* @return {@code true} if it is, {@code false} otherwise
*/
public boolean isEmpty() {
return this.registry.isEmpty();
}
/**
* Adds some audiences to the registry.
* @param keyed the provider of the key
* @param audiences the audiences
*/
public void register(@NotNull Keyed keyed, @NotNull Audience... audiences) {
this.register(keyed.key(), audiences);
}
/**
* Adds some audiences to the registry.
* @param keyed the provider of the key
* @param audiences the audiences
*/
public void register(@NotNull Keyed keyed, @NotNull Collection<Audience> audiences) {
this.register(keyed.key(), audiences);
}
/**
* Adds some audiences to the registry.
* @param key the key to store the audiences under
* @param audiences the audiences
*/
public void register(@NotNull Key key, @NotNull Audience... audiences) {
if (audiences == null || audiences.length == 0) {
return;
}
this.register(key, Arrays.asList(audiences));
}
/**
* Adds some audiences to the registry.
* @param key the key to store the audiences under
* @param audiences the audiences
*/
public void register(@NotNull Key key, @NotNull Collection<Audience> audiences) {
if (!audiences.isEmpty()) {
this.registry.computeIfAbsent(key, this.provider).addAll(audiences);
}
}
/**
* Gets every audience in the registry.
* @return an iterable containing every audience member
*/
public @NotNull Iterable<? extends Audience> all() {
if (this.isEmpty()) {
return Collections.emptyList();
} else {
return this.registry.values().stream().flatMap(Collection::stream).collect(Collectors.toUnmodifiableList());
}
}
/**
* Gets every audience in the registry under a specific key.
* @param keyed the key provider
* @return an iterable containing the audience members
*/
public @NotNull Iterable<? extends Audience> of(@NotNull Keyed keyed) {
return this.of(keyed.key());
}
/**
* Gets every audience in the registry under a specific key.
* @param key the key
* @return an iterable containing the audience members
*/
public @NotNull Iterable<? extends Audience> of(@NotNull Key key) {
return Collections.unmodifiableCollection(this.registry.getOrDefault(key, this.provider.apply(null)));
}
/**
* Gets every audience member in the registry who matches a given predicate.
* @param filter the predicate
* @return the matching audience members
*/
public @NotNull Iterable<? extends Audience> of(@NotNull Predicate<Audience> filter) {
return this.registry.values().stream().flatMap(Collection::stream).filter(filter).collect(Collectors.toUnmodifiableList());
}
}

View File

@ -0,0 +1,99 @@
package net.minestom.server.adventure.audience;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Utility class to access Adventure audiences.
*/
public class Audiences implements AudienceProvider<Audience> {
private final IterableAudienceProvider collection;
private final Audience players, server;
/**
* Creates a new audiences instance. <b>Do not</b> instantiate this class, instead
* you can obtain an instance using {@link #audiences()}.
*/
public Audiences() {
this.collection = new IterableAudienceProvider();
this.players = PacketGroupingAudience.of(MinecraftServer.getConnectionManager().getOnlinePlayers());
this.server = Audience.audience(this.players, MinecraftServer.getCommandManager().getConsoleSender());
}
/**
* Short-hand method for {@link MinecraftServer#getAudiences()}.
* @return the audiences instance
*/
public static @NotNull Audiences audiences() {
return MinecraftServer.getAudiences();
}
/**
* Gets the {@link IterableAudienceProvider} instance.
* @return the instance
*/
public @NotNull IterableAudienceProvider iterable() {
return this.collection;
}
@Override
public @NotNull Audience all() {
return Audience.audience(this.server, this.customs());
}
@Override
public @NotNull Audience players() {
return this.players;
}
@Override
public @NotNull Audience players(@NotNull Predicate<Player> filter) {
return PacketGroupingAudience.of(MinecraftServer.getConnectionManager().getOnlinePlayers().stream().filter(filter).collect(Collectors.toList()));
}
@Override
public @NotNull Audience console() {
return MinecraftServer.getCommandManager().getConsoleSender();
}
@Override
public @NotNull Audience server() {
return this.server;
}
@Override
public @NotNull Audience customs() {
return Audience.audience(this.iterable().customs());
}
@Override
public @NotNull Audience custom(@NotNull Key key) {
return Audience.audience(this.iterable().custom(key));
}
@Override
public @NotNull Audience custom(@NotNull Key key, Predicate<Audience> filter) {
return Audience.audience(this.iterable().custom(key, filter));
}
@Override
public @NotNull Audience customs(@NotNull Predicate<Audience> filter) {
return Audience.audience(this.iterable().customs(filter));
}
@Override
public @NotNull Audience all(@NotNull Predicate<Audience> filter) {
return Audience.audience(this.iterable().all(filter));
}
@Override
public @NotNull AudienceRegistry registry() {
return this.iterable().registry();
}
}

View File

@ -0,0 +1,80 @@
package net.minestom.server.adventure.audience;
import com.google.common.collect.Iterables;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.ConsoleSender;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* A provider of iterable audiences.
*/
public final class IterableAudienceProvider implements AudienceProvider<Iterable<? extends Audience>> {
private final Collection<ConsoleSender> console = Collections.singleton(MinecraftServer.getCommandManager().getConsoleSender());
private final AudienceRegistry registry = new AudienceRegistry(new ConcurrentHashMap<>(), CopyOnWriteArrayList::new);
@Override
public @NotNull Iterable<? extends Audience> all() {
return Iterables.concat(this.players(), this.console(), this.customs());
}
@Override
public @NotNull Iterable<? extends Audience> players() {
return MinecraftServer.getConnectionManager().getOnlinePlayers();
}
@Override
public @NotNull Iterable<? extends Audience> players(@NotNull Predicate<Player> filter) {
return MinecraftServer.getConnectionManager().getOnlinePlayers().stream().filter(filter).collect(Collectors.toList());
}
@Override
public @NotNull Iterable<? extends Audience> console() {
return this.console;
}
@Override
public @NotNull Iterable<? extends Audience> server() {
return Iterables.concat(this.players(), this.console());
}
@Override
public @NotNull Iterable<? extends Audience> customs() {
return this.registry.all();
}
@Override
public @NotNull Iterable<? extends Audience> custom(@NotNull Key key) {
return this.registry.of(key);
}
@Override
public @NotNull Iterable<? extends Audience> custom(@NotNull Key key, Predicate<Audience> filter) {
return StreamSupport.stream(this.registry.of(key).spliterator(), false).filter(filter).collect(Collectors.toList());
}
@Override
public @NotNull Iterable<? extends Audience> customs(@NotNull Predicate<Audience> filter) {
return this.registry.of(filter);
}
@Override
public @NotNull Iterable<? extends Audience> all(@NotNull Predicate<Audience> filter) {
return StreamSupport.stream(this.all().spliterator(), false).filter(filter).collect(Collectors.toList());
}
@Override
public @NotNull AudienceRegistry registry() {
return this.registry;
}
}

View File

@ -0,0 +1,101 @@
package net.minestom.server.adventure.audience;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
import net.minestom.server.network.packet.server.play.TitlePacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* An audience implementation that sends grouped packets if possible.
*/
public interface PacketGroupingAudience extends ForwardingAudience {
/**
* Creates a packet grouping audience that copies an iterable of players. The
* underlying collection is not copied, so changes to the collection will be
* reflected in the audience.
* @param players the players
* @return the audience
*/
static PacketGroupingAudience of(Collection<Player> players) {
return () -> players;
}
/**
* Gets an iterable of the players this audience contains.
* @return the connections
*/
@NotNull Collection<Player> getPlayers();
@Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()));
}
@Override
default void sendActionBar(@NotNull Component message) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, message));
}
@Override
default void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new PlayerListHeaderAndFooterPacket(header, footer));
}
@Override
default void showTitle(@NotNull Title title) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_TITLE, title.title()));
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.SET_SUBTITLE, title.subtitle()));
}
@Override
default void clearTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.HIDE));
}
@Override
default void resetTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new TitlePacket(TitlePacket.Action.RESET));
}
@Override
default void showBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().addBossBar(this.getPlayers(), bar);
}
@Override
default void hideBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().removeBossBar(this.getPlayers(), bar);
}
@Override
default void playSound(@NotNull Sound sound, double x, double y, double z) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
}
@Override
default void stopSound(@NotNull SoundStop stop) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundStopPacket(stop));
}
@Override
default @NotNull Iterable<? extends Audience> audiences() {
return this.getPlayers();
}
}

View File

@ -0,0 +1,102 @@
package net.minestom.server.adventure.bossbar;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.Viewable;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.BossBarPacket;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*;
/**
* A holder of a boss bar. This class is not intended for public use, instead you should
* use {@link BossBarManager} to manage boss bars for players.
*/
final class BossBarHolder implements Viewable {
final UUID uuid;
final BossBar bar;
final Set<Player> players;
boolean registered;
BossBarHolder(@NotNull BossBar bar) {
this.uuid = UUID.randomUUID();
this.bar = bar;
this.players = new CopyOnWriteArraySet<>();
this.registered = false;
}
@NotNull BossBarPacket createRemovePacket() {
return this.createGenericPacket(REMOVE, packet -> {});
}
@NotNull BossBarPacket createAddPacket() {
return this.createGenericPacket(ADD, packet -> {
packet.title = bar.name();
packet.color = bar.color();
packet.overlay = bar.overlay();
packet.health = bar.progress();
packet.flags = AdventurePacketConvertor.getBossBarFlagValue(bar.flags());
});
}
@NotNull BossBarPacket createPercentUpdate(float newPercent) {
return this.createGenericPacket(UPDATE_HEALTH, packet -> packet.health = newPercent);
}
@NotNull BossBarPacket createColorUpdate(@NotNull BossBar.Color color) {
return this.createGenericPacket(UPDATE_STYLE, packet -> {
packet.color = color;
packet.overlay = bar.overlay();
});
}
@NotNull BossBarPacket createTitleUpdate(@NotNull Component title) {
return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = title);
}
@NotNull BossBarPacket createFlagsUpdate() {
return createFlagsUpdate(bar.flags());
}
@NotNull BossBarPacket createFlagsUpdate(@NotNull Set<BossBar.Flag> newFlags) {
return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = AdventurePacketConvertor.getBossBarFlagValue(newFlags));
}
@NotNull BossBarPacket createOverlayUpdate(@NotNull BossBar.Overlay overlay) {
return this.createGenericPacket(UPDATE_STYLE, packet -> {
packet.overlay = overlay;
packet.color = bar.color();
});
}
private @NotNull BossBarPacket createGenericPacket(@NotNull BossBarPacket.Action action, @NotNull Consumer<BossBarPacket> consumer) {
BossBarPacket packet = new BossBarPacket();
packet.uuid = this.uuid;
packet.action = action;
consumer.accept(packet);
return packet;
}
@Override
public boolean addViewer(@NotNull Player player) {
return this.players.add(player);
}
@Override
public boolean removeViewer(@NotNull Player player) {
return this.players.remove(player);
}
@Override
public @NotNull Set<Player> getViewers() {
return Collections.unmodifiableSet(this.players);
}
}

View File

@ -0,0 +1,62 @@
package net.minestom.server.adventure.bossbar;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.function.Consumer;
/**
* A listener for boss bar updates. This class is not intended for public use and it is
* automatically added to boss bars shown to players using the methods in
* {@link Audience}, instead you should use {@link BossBarManager} to manage boss bars
* for players.
*/
class BossBarListener implements BossBar.Listener {
private final BossBarManager manager;
/**
* Creates a new boss bar listener.
* @param manager the manager instance
*/
BossBarListener(BossBarManager manager) {
this.manager = manager;
}
@Override
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createTitleUpdate(newName)));
}
@Override
public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createPercentUpdate(newProgress)));
}
@Override
public void bossBarColorChanged(@NotNull BossBar bar, @NotNull BossBar.Color oldColor, @NotNull BossBar.Color newColor) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createColorUpdate(newColor)));
}
@Override
public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createOverlayUpdate(newOverlay)));
}
@Override
public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set<BossBar.Flag> flagsAdded, @NotNull Set<BossBar.Flag> flagsRemoved) {
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createFlagsUpdate()));
}
private void doIfRegistered(@NotNull BossBar bar, @NotNull Consumer<BossBarHolder> consumer) {
BossBarHolder holder = this.manager.bars.get(bar);
if (holder != null && holder.registered) {
consumer.accept(holder);
}
}
}

View File

@ -0,0 +1,181 @@
package net.minestom.server.adventure.bossbar;
import com.google.common.collect.MapMaker;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.network.packet.server.play.BossBarPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Manages all boss bars known to this Minestom instance. Although this class can be used
* to show boss bars to players, it is preferable to use the boss bar methods in the
* {@link Audience} class instead.
*
* <p>This implementation is heavily based on
* <a href="https://github.com/VelocityPowered/Velocity">Velocity</a>'s boss bar
* management system.</p>
*
* @see Audience#showBossBar(BossBar)
* @see Audience#hideBossBar(BossBar)
*/
public class BossBarManager {
private final BossBarListener listener;
private final Map<UUID, Set<BossBarHolder>> playerBars;
final Map<BossBar, BossBarHolder> bars;
/**
* Creates a new boss bar manager.
*/
public BossBarManager() {
this.listener = new BossBarListener(this);
this.playerBars = new ConcurrentHashMap<>();
this.bars = new ConcurrentHashMap<>();
}
/**
* Adds the specified player to the boss bar's viewers and spawns the boss bar, registering the
* boss bar if needed.
*
* @param player the intended viewer
* @param bar the boss bar to show
*/
public void addBossBar(@NotNull Player player, @NotNull BossBar bar) {
BossBarHolder holder = this.getOrCreateHandler(bar);
if (holder.addViewer(player)) {
player.getPlayerConnection().sendPacket(holder.createAddPacket());
this.playerBars.computeIfAbsent(player.getUuid(), uuid -> new HashSet<>()).add(holder);
}
}
/**
* Removes the specified player from the boss bar's viewers and despawns the boss bar.
*
* @param player the intended viewer
* @param bar the boss bar to hide
*/
public void removeBossBar(@NotNull Player player, @NotNull BossBar bar) {
BossBarHolder holder = this.getOrCreateHandler(bar);
if (holder.removeViewer(player)) {
player.getPlayerConnection().sendPacket(holder.createRemovePacket());
this.removePlayer(player, holder);
}
}
/**
* Adds the specified players to the boss bar's viewers and spawns the boss bar, registering the
* boss bar if needed.
*
* @param players the players
* @param bar the boss bar
*/
public void addBossBar(@NotNull Collection<Player> players, @NotNull BossBar bar) {
BossBarHolder holder = this.getOrCreateHandler(bar);
Collection<Player> addedPlayers = new ArrayList<>();
for (Player player : players) {
if (holder.addViewer(player)) {
addedPlayers.add(player);
this.playerBars.computeIfAbsent(player.getUuid(), uuid -> new HashSet<>()).add(holder);
}
}
if (!addedPlayers.isEmpty()) {
PacketUtils.sendGroupedPacket(addedPlayers, holder.createAddPacket());
}
}
/**
* Removes the specified players from the boss bar's viewers and despawns the boss bar.
*
* @param players the intended viewers
* @param bar the boss bar to hide
*/
public void removeBossBar(@NotNull Collection<Player> players, @NotNull BossBar bar) {
BossBarHolder holder = this.getOrCreateHandler(bar);
Collection<Player> removedPlayers = new ArrayList<>();
for (Player player : players) {
if (holder.removeViewer(player)) {
removedPlayers.add(player);
this.removePlayer(player, holder);
}
}
if (!removedPlayers.isEmpty()) {
PacketUtils.sendGroupedPacket(removedPlayers, holder.createRemovePacket());
}
}
/**
* Completely destroys a boss bar, removing it from all players.
*
* @param bossBar the boss bar
*/
public void destroyBossBar(@NotNull BossBar bossBar) {
BossBarHolder holder = this.bars.remove(bossBar);
if (holder != null) {
PacketUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
for (Player player : holder.players) {
this.removePlayer(player, holder);
}
}
}
/**
* Removes a player from all of their boss bars. Note that this method does not
* send any removal packets to the player. It is meant to be used when a player is
* disconnecting from the server.
*
* @param player the player
*/
public void removeAllBossBars(@NotNull Player player) {
Set<BossBarHolder> holders = this.playerBars.remove(player.getUuid());
if (holders != null) {
for (BossBarHolder holder : holders) {
holder.removeViewer(player);
}
}
}
/**
* Gets or creates a handler for this bar.
*
* @param bar the bar
*
* @return the handler
*/
private @NotNull BossBarHolder getOrCreateHandler(@NotNull BossBar bar) {
return this.bars.computeIfAbsent(bar, key -> {
BossBarHolder holder = new BossBarHolder(key);
bar.addListener(this.listener);
return holder;
});
}
private void removePlayer(Player player, BossBarHolder holder) {
Set<BossBarHolder> holders = this.playerBars.get(player.getUuid());
if (holders != null) {
holders.remove(holder);
if (holders.isEmpty()) {
this.playerBars.remove(player.getUuid());
}
}
}
}

View File

@ -2,8 +2,11 @@ package net.minestom.server.benchmark;
import it.unimi.dsi.fastutil.longs.Long2LongMap;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.time.UpdateOption;
import net.minestom.server.utils.validate.Check;
@ -106,20 +109,25 @@ public final class BenchmarkManager {
@NotNull
public String getCpuMonitoringMessage() {
Check.stateCondition(!enabled, "CPU monitoring is only possible when the benchmark manager is enabled.");
StringBuilder benchmarkMessage = new StringBuilder();
TextComponent.Builder benchmarkMessage = Component.text();
for (Map.Entry<String, ThreadResult> resultEntry : resultMap.entrySet()) {
final String name = resultEntry.getKey();
final ThreadResult result = resultEntry.getValue();
benchmarkMessage.append(ChatColor.GRAY).append(name);
benchmarkMessage.append(": ");
benchmarkMessage.append(ChatColor.YELLOW.toString()).append(MathUtils.round(result.getCpuPercentage(), 2)).append("% CPU ");
benchmarkMessage.append(ChatColor.RED.toString()).append(MathUtils.round(result.getUserPercentage(), 2)).append("% USER ");
benchmarkMessage.append(ChatColor.PINK.toString()).append(MathUtils.round(result.getBlockedPercentage(), 2)).append("% BLOCKED ");
benchmarkMessage.append(ChatColor.BRIGHT_GREEN.toString()).append(MathUtils.round(result.getWaitedPercentage(), 2)).append("% WAITED ");
benchmarkMessage.append("\n");
benchmarkMessage.append(Component.text(name, NamedTextColor.GRAY));
benchmarkMessage.append(Component.text(": "));
benchmarkMessage.append(Component.text(MathUtils.round(result.getCpuPercentage(), 2), NamedTextColor.YELLOW));
benchmarkMessage.append(Component.text("% CPU "));
benchmarkMessage.append(Component.text(MathUtils.round(result.getUserPercentage(), 2), NamedTextColor.RED));
benchmarkMessage.append(Component.text("% USER "));
benchmarkMessage.append(Component.text(MathUtils.round(result.getBlockedPercentage(), 2), NamedTextColor.LIGHT_PURPLE));
benchmarkMessage.append(Component.text("% BLOCKED "));
benchmarkMessage.append(Component.text(MathUtils.round(result.getWaitedPercentage(), 2), NamedTextColor.GREEN));
benchmarkMessage.append(Component.text("% WAITED "));
benchmarkMessage.append(Component.newline());
}
return benchmarkMessage.toString();
return AdventureSerializer.serialize(benchmarkMessage.build());
}
private void refreshData() {

View File

@ -2,7 +2,9 @@ package net.minestom.server.bossbar;
/**
* Represents the displayed color of a {@link BossBar}.
* @deprecated Use {@link net.kyori.adventure.bossbar.BossBar}
*/
@Deprecated
public enum BarColor {
PINK,
BLUE,
@ -10,5 +12,9 @@ public enum BarColor {
GREEN,
YELLOW,
PURPLE,
WHITE
WHITE;
public net.kyori.adventure.bossbar.BossBar.Color asAdventureColor() {
return net.kyori.adventure.bossbar.BossBar.Color.valueOf(this.name());
}
}

View File

@ -2,11 +2,18 @@ package net.minestom.server.bossbar;
/**
* Used to define the number of segments on a {@link BossBar}.
*
* @deprecated Use {@link net.kyori.adventure.bossbar.BossBar}
*/
@Deprecated
public enum BarDivision {
SOLID,
SEGMENT_6,
SEGMENT_10,
SEGMENT_12,
SEGMENT_20
SEGMENT_20;
public net.kyori.adventure.bossbar.BossBar.Overlay asAdventureOverlay() {
return net.kyori.adventure.bossbar.BossBar.Overlay.values()[this.ordinal()];
}
}

View File

@ -19,7 +19,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
* and add the {@link Player} you want using {@link #addViewer(Player)} and remove them using {@link #removeViewer(Player)}.
* <p>
* You can retrieve all the boss bars of a {@link Player} with {@link #getBossBars(Player)}.
*
* @deprecated Use {@link net.kyori.adventure.audience.Audience#showBossBar(net.kyori.adventure.bossbar.BossBar)}
*/
@Deprecated
public class BossBar implements Viewable {
private static final int MAX_BOSSBAR = 7;
@ -249,10 +252,10 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.ADD;
bossBarPacket.title = title;
bossBarPacket.title = title.asComponent();
bossBarPacket.health = progress;
bossBarPacket.color = color;
bossBarPacket.division = division;
bossBarPacket.color = color.asAdventureColor();
bossBarPacket.overlay = division.asAdventureOverlay();
bossBarPacket.flags = flags;
player.getPlayerConnection().sendPacket(bossBarPacket);
}
@ -275,7 +278,7 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_TITLE;
bossBarPacket.title = title;
bossBarPacket.title = title.asComponent();
sendPacketToViewers(bossBarPacket);
}
@ -291,7 +294,7 @@ public class BossBar implements Viewable {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_STYLE;
bossBarPacket.color = color;
bossBarPacket.color = color.asAdventureColor();
sendPacketToViewers(bossBarPacket);
}
}

View File

@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a click event for a specific portion of the message.
*/
@Deprecated
public class ChatClickEvent {
private final String action;

View File

@ -4,6 +4,8 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectMap;
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.color.Color;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -18,6 +20,7 @@ import java.util.Map;
* <p>
* Immutable class.
*/
@Deprecated
public final class ChatColor {
// Special
@ -272,6 +275,18 @@ public final class ChatColor {
return id;
}
/**
* Gets the Adventure text color from this chat color.
* @return the text color
*/
public @NotNull TextColor asTextColor() {
return TextColor.color(red, blue, green);
}
public @NotNull Color asColor() {
return new Color(red, green, blue);
}
@NotNull
@Override
public String toString() {

View File

@ -1,15 +1,24 @@
package net.minestom.server.chat;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.UUID;
/**
* Represents a hover event for a specific portion of the message.
*/
@Deprecated
public class ChatHoverEvent {
private final String action;
@ -37,12 +46,12 @@ public class ChatHoverEvent {
}
@Nullable
protected String getValue() {
public String getValue() {
return value;
}
@Nullable
protected JsonObject getValueObject() {
public JsonObject getValueObject() {
return valueObject;
}
@ -80,8 +89,17 @@ public class ChatHoverEvent {
*/
@NotNull
public static ChatHoverEvent showItem(@NotNull ItemStack itemStack) {
final String json = itemStack.toNBT().toSNBT();
return new ChatHoverEvent("show_item", json);
HoverEvent<HoverEvent.ShowItem> event = HoverEvent.showItem(itemStack.getMaterial().key(), itemStack.getAmount());
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
obj = obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject();
if (itemStack.getItemMeta() != null) {
NBTCompound compound = new NBTCompound();
itemStack.getItemMeta().write(compound);
obj.add("tag", new JsonPrimitive(compound.toSNBT()));
}
return new ChatHoverEvent("show_item", obj);
}
/**
@ -92,9 +110,14 @@ public class ChatHoverEvent {
*/
@NotNull
public static ChatHoverEvent showEntity(@NotNull Entity entity) {
NBTCompound compound = new NBTCompound()
.setString("id", entity.getUuid().toString())
.setString("type", entity.getEntityType().getNamespaceID());
return new ChatHoverEvent("show_entity", compound.toSNBT());
HoverEvent<HoverEvent.ShowEntity> event = HoverEvent.showEntity(entity.getEntityType().key(), entity.getUuid());
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
return new ChatHoverEvent("show_entity", obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject());
}
public static ChatHoverEvent showEntity(UUID uuid, EntityType entityType) {
HoverEvent<HoverEvent.ShowEntity> event = HoverEvent.showEntity(entityType.key(), uuid);
JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject();
return new ChatHoverEvent("show_entity", obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject());
}
}

View File

@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Class used to convert JSON string to proper chat message representation.
*/
@Deprecated
public final class ChatParser {
public static final char COLOR_CHAR = (char) 0xA7; // Represent the character '§'

View File

@ -17,6 +17,7 @@ import java.util.regex.Pattern;
* To create one, you simply call one of the static methods like {@link #of(ChatColor, String)},
* you can then continue to append text with {@link #append(ChatColor, String)}.
*/
@Deprecated
public class ColoredText extends JsonMessage {
private static final char SEPARATOR_START = '{';

View File

@ -3,6 +3,9 @@ package net.minestom.server.chat;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@ -14,7 +17,8 @@ import java.util.Objects;
*
* @see <a href="https://wiki.vg/Chat">Chat Format</a>
*/
public abstract class JsonMessage {
@Deprecated
public abstract class JsonMessage implements ComponentLike {
// true if the compiled string is up-to-date, false otherwise
private boolean updated;
@ -49,6 +53,15 @@ public abstract class JsonMessage {
return getTextMessage(getJsonObject()).toString();
}
@Override
public @NotNull Component asComponent() {
return GsonComponentSerializer.gson().deserializeFromTree(this.getJsonObject());
}
public static @NotNull JsonMessage fromComponent(@NotNull Component component) {
return new RawJsonMessage(GsonComponentSerializer.gson().serializer().toJsonTree(component).getAsJsonObject());
}
/**
* Gets the Json representation.
* <p>
@ -102,6 +115,7 @@ public abstract class JsonMessage {
return message;
}
@Deprecated
public static class RawJsonMessage extends JsonMessage {
private final JsonObject jsonObject;

View File

@ -19,6 +19,7 @@ import java.util.List;
* events can be assigned with {@link #setClickEvent(ChatClickEvent)} and {@link #setHoverEvent(ChatHoverEvent)}
* and new text element can also be appended {@link #append(ColoredText)}.
*/
@Deprecated
public class RichMessage extends JsonMessage {
private final List<RichComponent> components = new ArrayList<>();
@ -159,7 +160,7 @@ public class RichMessage extends JsonMessage {
// The value is a JsonObject
hoverObject = new JsonObject();
hoverObject.addProperty("action", hoverEvent.getAction());
hoverObject.add("value", hoverEvent.getValueObject());
hoverObject.add("contents", hoverEvent.getValueObject());
} else {
// The value is a raw string
final String hoverValue = hoverEvent.getValue();

View File

@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a translatable component which can be used in {@link ColoredText}.
*/
@Deprecated
public class TranslatableText {
private final String code;

View File

@ -0,0 +1,204 @@
package net.minestom.server.color;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.chat.ChatColor;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* A general purpose class for representing colors.
*/
public class Color implements RGBLike {
private static final int BIT_MASK = 0xff;
private int red, green, blue;
/**
* Creates a color from an integer. This is done by reading each color component
* from the lowest order 24 bits of the integer, and creating a color from those
* components.
*
* @param rgb the integer
*/
public Color(int rgb) {
this((rgb >> 16) & BIT_MASK, (rgb >> 8) & BIT_MASK, rgb & BIT_MASK);
}
/**
* Creates a color from an RGB-like color.
*
* @param rgbLike the color
*/
public Color(RGBLike rgbLike) {
this(rgbLike.red(), rgbLike.blue(), rgbLike.green());
}
/**
* Creates a color from red, green, and blue components.
*
* @param red the red component
* @param green the green component
* @param blue the blue component
*
* @throws IllegalArgumentException if any component value is not between 0-255 (inclusive)
*/
public Color(int red, int green, int blue) {
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
this.red = red;
this.green = green;
this.blue = blue;
}
/**
* Gets the red component.
*
* @return red component, between 0-255 (inclusive)
*/
public int getRed() {
return this.red;
}
/**
* Creates a new Color object with specified component
*
* @param red the red component, from 0 to 255
*/
public void setRed(int red) {
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
this.red = red;
}
/**
* Gets the green component
*
* @return green component, from 0 to 255
*/
public int getGreen() {
return this.green;
}
/**
* Creates a new Color object with specified component
*
* @param green the red component, from 0 to 255
*/
public void setGreen(int green) {
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
this.green = green;
}
/**
* Gets the blue component
*
* @return blue component, from 0 to 255
*/
public int getBlue() {
return this.blue;
}
/**
* Sets the blue component of this color.
*
* @param blue the red component, from 0 to 255
*/
public void setBlue(int blue) {
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
this.blue = blue;
}
/**
* Gets the color as an RGB integer.
*
* @return An integer representation of this color, as 0xRRGGBB
*/
public int asRGB() {
int rgb = red;
rgb = (rgb << 8) + green;
return (rgb << 8) + blue;
}
/**
* Mixes this color with a series of other colors, as if they were combined in a
* crafting table. This function works out the average of each RGB component and then
* multiplies the components by a scale factor that is calculated from the average
* of all maximum values divided by the maximum of each average value. This is how
* Minecraft mixes colors.
*
* @param colors the colors
*/
public void mixWith(@NotNull RGBLike... colors) {
Validate.noNullElements(colors, "Colors cannot be null");
// store the current highest component
int max = Math.max(Math.max(this.red, this.green), this.blue);
// now combine all of the color components, adding to the max
for (RGBLike color : colors) {
this.red += color.red();
this.green += color.green();
this.blue += color.blue();
max += Math.max(Math.max(color.red(), color.green()), color.blue());
}
// work out the averages
float count = colors.length + 1;
float averageRed = this.red / count;
float averageGreen = this.green / count;
float averageBlue = this.blue / count;
float averageMax = max / count;
// work out the scale factor
float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
float gainFactor = averageMax / maximumOfAverages;
// round and multiply
this.red = Math.round(averageRed * gainFactor);
this.blue = Math.round(averageBlue * gainFactor);
this.green = Math.round(averageGreen * gainFactor);
}
/**
* Gets the ChatColor representation of this color.
*
* @return the chat color
* @deprecated ChatColor is deprecated and should not be used
*/
@Deprecated
public ChatColor asLegacyChatColor() {
return ChatColor.fromRGB((byte) red, (byte) blue, (byte) green);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Color color = (Color) o;
return red == color.red && green == color.green && blue == color.blue;
}
@Override
public int hashCode() {
return Objects.hash(red, green, blue);
}
@Override
public int red() {
return this.red;
}
@Override
public int green() {
return this.green;
}
@Override
public int blue() {
return this.blue;
}
}

View File

@ -0,0 +1,69 @@
package net.minestom.server.color;
import net.kyori.adventure.util.RGBLike;
import org.jetbrains.annotations.NotNull;
/**
* Color values for dyes, wool and cloth items.
*/
public enum DyeColor implements RGBLike {
WHITE(new Color(0xF9FFFE), new Color(0xF0F0F0)),
ORANGE(new Color(0xF9801D), new Color(0xEB8844)),
MAGENTA(new Color(0xC74EBD), new Color(0xC354CD)),
LIGHT_BLUE(new Color(0x3AB3DA), new Color(0x6689D3)),
YELLOW(new Color(0xFED83D), new Color(0xDECF2A)),
LIME(new Color(0x80C71F), new Color(0x41CD34)),
PINK(new Color(0xF38BAA), new Color(0xD88198)),
GRAY(new Color(0x474F52), new Color(0x434343)),
LIGHT_GRAY(new Color(0x9D9D97), new Color(0xABABAB)),
CYAN(new Color(0x169C9C), new Color(0x287697)),
PURPLE(new Color(0x8932B8), new Color(0x7B2FBE)),
BLUE(new Color(0x3C44AA), new Color(0x253192)),
BROWN(new Color(0x835432), new Color(0x51301A)),
GREEN(new Color(0x5E7C16), new Color(0x3B511A)),
RED(new Color(0xB02E26), new Color(0xB3312C)),
BLACK(new Color(0x1D1D21), new Color(0x1E1B1B));
private final Color color;
private final Color firework;
DyeColor(Color color, Color firework) {
this.color = color;
this.firework = firework;
}
/**
* Gets the color that this dye represents.
*
* @return The {@link Color} that this dye represents
*/
@NotNull
public Color getColor() {
return this.color;
}
/**
* Gets the firework color that this dye represents.
*
* @return The {@link Color} that this dye represents
*/
@NotNull
public Color getFireworkColor() {
return this.firework;
}
@Override
public int red() {
return this.color.red();
}
@Override
public int green() {
return this.color.green();
}
@Override
public int blue() {
return this.color.blue();
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.command;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.permission.PermissionHandler;
@ -10,20 +12,26 @@ import org.jetbrains.annotations.NotNull;
* <p>
* Main implementations are {@link Player} and {@link ConsoleSender}.
*/
public interface CommandSender extends PermissionHandler {
public interface CommandSender extends PermissionHandler, Audience {
/**
* Sends a raw string message.
*
* @param message the message to send
*
* @deprecated Use {@link #sendMessage(Component)}
*/
void sendMessage(@NotNull String message);
@Deprecated
default void sendMessage(@NotNull String message) { this.sendMessage(Component.text(message)); }
/**
* Sends multiple raw string messages.
*
* @param messages the messages to send
*
* @deprecated Use {@link #sendMessage(Component)}
*/
@Deprecated
default void sendMessage(@NotNull String[] messages) {
for (String message : messages) {
sendMessage(message);
@ -35,13 +43,12 @@ public interface CommandSender extends PermissionHandler {
* If this is not a {@link Player}, only the content of the message will be sent as a string.
*
* @param text The {@link JsonMessage} to send.
*
* @deprecated Use {@link #sendMessage(Component)}
* */
@Deprecated
default void sendMessage(@NotNull JsonMessage text) {
if (this instanceof Player) {
this.sendMessage(text);
} else {
sendMessage(text.getRawMessage());
}
this.sendMessage(text.asComponent());
}
/**

View File

@ -1,5 +1,9 @@
package net.minestom.server.command;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.minestom.server.permission.Permission;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@ -18,8 +22,9 @@ public class ConsoleSender implements CommandSender {
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
@Override
public void sendMessage(@NotNull String message) {
LOGGER.info(message);
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
// we don't use the serializer here as we just need the plain text of the message
LOGGER.info(PlainComponentSerializer.plain().serialize(message));
}
@NotNull

View File

@ -1,6 +1,8 @@
package net.minestom.server.command;
import net.minestom.server.command.builder.CommandContext;
import net.kyori.adventure.audience.Audience;
import net.minestom.server.command.builder.Arguments;
import net.minestom.server.permission.Permission;
import org.jetbrains.annotations.NotNull;
@ -11,18 +13,14 @@ import java.util.Set;
/**
* Sender used in {@link CommandManager#executeServerCommand(String)}.
* <p>
* Be aware that {@link #sendMessage(String)} is empty on purpose because the purpose
* of this sender is to process the data of {@link CommandContext#getReturnData()}.
* Although this class implemented {@link CommandSender} and thus {@link Audience}, no
* data can be sent to this sender because it's purpose is to process the data of
* {@link CommandContext#getReturnData()}.
*/
public class ServerSender implements CommandSender {
private final Set<Permission> permissions = Collections.unmodifiableSet(new HashSet<>());
@Override
public void sendMessage(@NotNull String message) {
// Empty on purpose
}
@NotNull
@Override
public Set<Permission> getAllPermissions() {

View File

@ -1,6 +1,8 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.chat.ChatColor;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
@ -8,11 +10,12 @@ import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
/**
* Represents an argument which will give you a {@link ChatColor}.
* Represents an argument which will give you a {@link Style} containing the colour or no
* colour if the argument was {@code reset}.
* <p>
* Example: red, white, reset
*/
public class ArgumentColor extends Argument<ChatColor> {
public class ArgumentColor extends Argument<Style> {
public static final int UNDEFINED_COLOR = -2;
@ -22,12 +25,21 @@ public class ArgumentColor extends Argument<ChatColor> {
@NotNull
@Override
public ChatColor parse(@NotNull String input) throws ArgumentSyntaxException {
final ChatColor color = ChatColor.fromName(input);
if (color == ChatColor.NO_COLOR)
throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR);
public Style parse(@NotNull String input) throws ArgumentSyntaxException {
String uppercaseInput = input.toUpperCase();
return color;
// check for colour
NamedTextColor color = NamedTextColor.NAMES.value(uppercaseInput);
if (color != null) {
return Style.style(color);
}
// check for reset
if (uppercaseInput.equals("RESET")) {
return Style.empty();
}
throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR);
}
@Override

View File

@ -1,14 +1,15 @@
package net.minestom.server.command.builder.arguments.minecraft;
import com.google.gson.JsonParseException;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
public class ArgumentComponent extends Argument<JsonMessage> {
public class ArgumentComponent extends Argument<Component> {
public static final int INVALID_JSON_ERROR = 1;
@ -18,9 +19,9 @@ public class ArgumentComponent extends Argument<JsonMessage> {
@NotNull
@Override
public JsonMessage parse(@NotNull String input) throws ArgumentSyntaxException {
public Component parse(@NotNull String input) throws ArgumentSyntaxException {
try {
return new JsonMessage.RawJsonMessage(input);
return GsonComponentSerializer.gson().deserialize(input);
} catch (JsonParseException e) {
throw new ArgumentSyntaxException("Invalid JSON", input, INVALID_JSON_ERROR);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.command.builder.suggestion;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -7,15 +8,25 @@ import org.jetbrains.annotations.Nullable;
public class SuggestionEntry {
private final String entry;
private final JsonMessage tooltip;
private final Component tooltip;
/**
* @deprecated Use {{@link #SuggestionEntry(String, JsonMessage)}}
*/
@Deprecated
public SuggestionEntry(@NotNull String entry, @Nullable JsonMessage tooltip) {
this.entry = entry;
this.tooltip = tooltip;
this.tooltip = tooltip.asComponent();
}
public SuggestionEntry(@NotNull String entry, @Nullable Component tooltip) {
this.entry = entry;
this.tooltip = tooltip.asComponent();
}
public SuggestionEntry(@NotNull String entry) {
this(entry, null);
this.entry = entry;
this.tooltip = null;
}
@NotNull
@ -24,7 +35,16 @@ public class SuggestionEntry {
}
@Nullable
public JsonMessage getTooltip() {
public Component getTooltip() {
return tooltip;
}
/**
* @deprecated Use {@link #getTooltip()}
*/
@Deprecated
@Nullable
public JsonMessage getTooltipJson() {
return JsonMessage.fromComponent(tooltip);
}
}

View File

@ -1,6 +1,10 @@
package net.minestom.server.entity;
import com.google.common.collect.Queues;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowEntity;
import net.kyori.adventure.text.event.HoverEventSource;
import net.minestom.server.MinecraftServer;
import net.minestom.server.Viewable;
import net.minestom.server.chat.JsonMessage;
@ -46,13 +50,14 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
/**
* Could be a player, a monster, or an object.
* <p>
* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead.
*/
public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler {
public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler, HoverEventSource<ShowEntity> {
private static final Map<Integer, Entity> entityById = new ConcurrentHashMap<>();
private static final Map<UUID, Entity> entityByUuid = new ConcurrentHashMap<>();
@ -1189,13 +1194,25 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
this.entityMeta.setPose(pose);
}
/**
* Gets the entity custom name.
*
* @return the custom name of the entity, null if there is not
* @deprecated Use {@link #getCustomName()}
*/
@Deprecated
@Nullable
public JsonMessage getCustomNameJson() {
return this.entityMeta.getCustomNameJson();
}
/**
* Gets the entity custom name.
*
* @return the custom name of the entity, null if there is not
*/
@Nullable
public JsonMessage getCustomName() {
public Component getCustomName() {
return this.entityMeta.getCustomName();
}
@ -1203,11 +1220,22 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
* Changes the entity custom name.
*
* @param customName the custom name of the entity, null to remove it
* @deprecated Use {@link #setCustomName(Component)}
*/
@Deprecated
public void setCustomName(@Nullable JsonMessage customName) {
this.entityMeta.setCustomName(customName);
}
/**
* Changes the entity custom name.
*
* @param customName the custom name of the entity, null to remove it
*/
public void setCustomName(@Nullable Component customName) {
this.entityMeta.setCustomName(customName);
}
/**
* Gets the custom name visible metadata field.
*
@ -1504,6 +1532,11 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
this.customSynchronizationCooldown = cooldown;
}
@Override
public @NotNull HoverEvent<ShowEntity> asHoverEvent(@NotNull UnaryOperator<ShowEntity> op) {
return HoverEvent.showEntity(ShowEntity.of(this.entityType, this.uuid));
}
private UpdateOption getSynchronizationCooldown() {
if (this.customSynchronizationCooldown != null) {
return this.customSynchronizationCooldown;

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity;
import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeInstance;
import net.minestom.server.attribute.Attributes;
@ -19,8 +20,7 @@ import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
@ -386,14 +386,14 @@ public class LivingEntity extends Entity implements EquipmentHandler {
setHealth(getHealth() - remainingDamage);
// play damage sound
final Sound sound = type.getSound(this);
final SoundEvent sound = type.getSound(this);
if (sound != null) {
SoundCategory soundCategory;
Source soundCategory;
if (this instanceof Player) {
soundCategory = SoundCategory.PLAYERS;
soundCategory = Source.PLAYER;
} else {
// TODO: separate living entity categories
soundCategory = SoundCategory.HOSTILE;
soundCategory = Source.HOSTILE;
}
SoundEffectPacket damageSoundPacket =

View File

@ -1,5 +1,7 @@
package net.minestom.server.entity;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.EntityMetaDataPacket;
@ -36,10 +38,12 @@ public class Metadata {
return new Value<>(TYPE_STRING, value, writer -> writer.writeSizedString(value));
}
@Deprecated
public static Value<JsonMessage> Chat(@NotNull JsonMessage value) {
return new Value<>(TYPE_CHAT, value, writer -> writer.writeSizedString(value.toString()));
}
@Deprecated
public static Value<JsonMessage> OptChat(@Nullable JsonMessage value) {
return new Value<>(TYPE_OPTCHAT, value, writer -> {
final boolean present = value != null;
@ -50,6 +54,20 @@ public class Metadata {
});
}
public static Value<Component> Chat(@NotNull Component value) {
return new Value<>(TYPE_CHAT, value, writer -> writer.writeSizedString(AdventureSerializer.serialize(value)));
}
public static Value<Component> OptChat(@Nullable Component value) {
return new Value<>(TYPE_OPTCHAT, value, writer -> {
final boolean present = value != null;
writer.writeBoolean(present);
if (present) {
writer.writeSizedString(AdventureSerializer.serialize(value));
}
});
}
public static Value<ItemStack> Slot(@NotNull ItemStack value) {
return new Value<>(TYPE_SLOT, value, writer -> writer.writeItemStack(value));
}

View File

@ -1,14 +1,26 @@
package net.minestom.server.entity;
import com.google.common.collect.Queues;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.inventory.Book;
import net.kyori.adventure.sound.SoundStop;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowEntity;
import net.kyori.adventure.text.event.HoverEventSource;
import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer;
import net.minestom.server.advancements.AdvancementTab;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.adventure.Localizable;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.bossbar.BossBar;
import net.minestom.server.chat.ChatParser;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.chat.RichMessage;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.command.CommandManager;
import net.minestom.server.command.CommandSender;
@ -45,7 +57,7 @@ import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.stat.PlayerStatistic;
import net.minestom.server.utils.*;
@ -67,6 +79,7 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.UnaryOperator;
/**
* Those are the major actors of the server,
@ -74,7 +87,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* <p>
* You can easily create your own implementation of this and use it with {@link ConnectionManager#setPlayerProvider(PlayerProvider)}.
*/
public class Player extends LivingEntity implements CommandSender {
public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource<ShowEntity>, Identified {
private long lastKeepAlive;
private boolean answerKeepAlive;
@ -85,7 +98,7 @@ public class Player extends LivingEntity implements CommandSender {
protected final Set<Entity> viewableEntities = ConcurrentHashMap.newKeySet();
private int latency;
private JsonMessage displayName;
private Component displayName;
private PlayerSkin skin;
private DimensionType dimensionType;
@ -163,6 +176,9 @@ public class Player extends LivingEntity implements CommandSender {
// Tick related
private final PlayerTickEvent playerTickEvent = new PlayerTickEvent(this);
// Adventure
private Identity identity;
public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConnection playerConnection) {
super(EntityType.PLAYER, uuid);
this.username = username;
@ -187,6 +203,8 @@ public class Player extends LivingEntity implements CommandSender {
// FakePlayer init its connection there
playerConnectionInit();
this.identity = Identity.identity(uuid);
}
/**
@ -464,15 +482,15 @@ public class Player extends LivingEntity implements CommandSender {
public void kill() {
if (!isDead()) {
JsonMessage deathText;
JsonMessage chatMessage;
Component deathText;
Component chatMessage;
// get death screen text to the killed player
{
if (lastDamageSource != null) {
deathText = lastDamageSource.buildDeathScreenText(this);
} else { // may happen if killed by the server without applying damage
deathText = ColoredText.of("Killed by poor programming.");
deathText = Component.text("Killed by poor programming.");
}
}
@ -481,7 +499,7 @@ public class Player extends LivingEntity implements CommandSender {
if (lastDamageSource != null) {
chatMessage = lastDamageSource.buildDeathMessage(this);
} else { // may happen if killed by the server without applying damage
chatMessage = ColoredText.of(getUsername() + " was killed by poor programming.");
chatMessage = Component.text(getUsername() + " was killed by poor programming.");
}
}
@ -500,7 +518,7 @@ public class Player extends LivingEntity implements CommandSender {
// #buildDeathMessage can return null, check here
if (chatMessage != null) {
MinecraftServer.getConnectionManager().broadcastMessage(chatMessage);
Audiences.audiences().players().sendMessage(chatMessage);
}
}
@ -552,14 +570,16 @@ public class Player extends LivingEntity implements CommandSender {
// Boss bars cache
{
Set<BossBar> bossBars = BossBar.getBossBars(this);
Set<net.minestom.server.bossbar.BossBar> bossBars = net.minestom.server.bossbar.BossBar.getBossBars(this);
if (bossBars != null) {
for (BossBar bossBar : bossBars) {
for (net.minestom.server.bossbar.BossBar bossBar : bossBars) {
bossBar.removeViewer(this);
}
}
}
MinecraftServer.getBossBarManager().removeAllBossBars(this);
// Advancement tabs cache
{
Set<AdvancementTab> advancementTabs = AdvancementTab.getTabs(this);
@ -755,27 +775,15 @@ public class Player extends LivingEntity implements CommandSender {
sendPluginMessage(channel, bytes);
}
@Override
public void sendMessage(@NotNull String message) {
sendMessage(ColoredText.of(message));
}
/**
* Sends a message to the player.
*
* @param message the message to send,
* you can use {@link ColoredText} and/or {@link RichMessage} to create it easily
*/
public void sendMessage(@NotNull JsonMessage message) {
sendJsonMessage(message.toString());
}
/**
* Sends a legacy message with the specified color char.
*
* @param text the text with the legacy color formatting
* @param colorChar the color character
*
* @deprecated Use {@link #sendMessage(Component)}
*/
@Deprecated
public void sendLegacyMessage(@NotNull String text, char colorChar) {
ColoredText coloredText = ColoredText.ofLegacy(text, colorChar);
sendJsonMessage(coloredText.toString());
@ -785,15 +793,26 @@ public class Player extends LivingEntity implements CommandSender {
* Sends a legacy message with the default color char {@link ChatParser#COLOR_CHAR}.
*
* @param text the text with the legacy color formatting
*
* @deprecated Use {@link #sendMessage(Component)}
*/
@Deprecated
public void sendLegacyMessage(@NotNull String text) {
ColoredText coloredText = ColoredText.ofLegacy(text, ChatParser.COLOR_CHAR);
sendJsonMessage(coloredText.toString());
}
/**
* @deprecated Use {@link #sendMessage(Component)}
*/
@Deprecated
public void sendJsonMessage(@NotNull String json) {
ChatMessagePacket chatMessagePacket =
new ChatMessagePacket(json, ChatMessagePacket.Position.CHAT);
this.sendMessage(json);
}
@Override
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid());
playerConnection.sendPacket(chatMessagePacket);
}
@ -809,7 +828,7 @@ public class Player extends LivingEntity implements CommandSender {
}
/**
* Plays a sound from the {@link Sound} enum.
* Plays a sound from the {@link SoundEvent} enum.
*
* @param sound the sound to play
* @param soundCategory the sound category
@ -818,12 +837,13 @@ public class Player extends LivingEntity implements CommandSender {
* @param z the effect Z
* @param volume the volume of the sound (1 is 100%)
* @param pitch the pitch of the sound, between 0.5 and 2.0
* @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)}
*/
public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory,
int x, int y, int z, float volume, float pitch) {
@Deprecated
public void playSound(@NotNull SoundEvent sound, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) {
SoundEffectPacket soundEffectPacket = new SoundEffectPacket();
soundEffectPacket.soundId = sound.getId();
soundEffectPacket.soundCategory = soundCategory;
soundEffectPacket.soundSource = soundCategory.asSource();
soundEffectPacket.x = x;
soundEffectPacket.y = y;
soundEffectPacket.z = z;
@ -833,12 +853,13 @@ public class Player extends LivingEntity implements CommandSender {
}
/**
* Plays a sound from the {@link Sound} enum.
* Plays a sound from the {@link SoundEvent} enum.
*
* @see #playSound(Sound, SoundCategory, int, int, int, float, float)
* @see #playSound(SoundEvent, SoundCategory, int, int, int, float, float)
* @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)}
*/
public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory,
BlockPosition position, float volume, float pitch) {
@Deprecated
public void playSound(@NotNull SoundEvent sound, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) {
playSound(sound, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch);
}
@ -852,12 +873,13 @@ public class Player extends LivingEntity implements CommandSender {
* @param z the effect Z
* @param volume the volume of the sound (1 is 100%)
* @param pitch the pitch of the sound, between 0.5 and 2.0
* @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)}
*/
public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory,
int x, int y, int z, float volume, float pitch) {
@Deprecated
public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) {
NamedSoundEffectPacket namedSoundEffectPacket = new NamedSoundEffectPacket();
namedSoundEffectPacket.soundName = identifier;
namedSoundEffectPacket.soundCategory = soundCategory;
namedSoundEffectPacket.soundSource = soundCategory.asSource();
namedSoundEffectPacket.x = x;
namedSoundEffectPacket.y = y;
namedSoundEffectPacket.z = z;
@ -870,7 +892,9 @@ public class Player extends LivingEntity implements CommandSender {
* Plays a sound from an identifier (represents a custom sound in a resource pack).
*
* @see #playSound(String, SoundCategory, int, int, int, float, float)
* @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)}
*/
@Deprecated
public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) {
playSound(identifier, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch);
}
@ -882,17 +906,34 @@ public class Player extends LivingEntity implements CommandSender {
* @param soundCategory the sound category
* @param volume the volume of the sound (1 is 100%)
* @param pitch the pitch of the sound, between 0.5 and 2.0
* @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound)}
*/
public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, float volume, float pitch) {
@Deprecated
public void playSound(@NotNull SoundEvent sound, @NotNull SoundCategory soundCategory, float volume, float pitch) {
EntitySoundEffectPacket entitySoundEffectPacket = new EntitySoundEffectPacket();
entitySoundEffectPacket.entityId = getEntityId();
entitySoundEffectPacket.soundId = sound.getId();
entitySoundEffectPacket.soundCategory = soundCategory;
entitySoundEffectPacket.soundSource = soundCategory.asSource();
entitySoundEffectPacket.volume = volume;
entitySoundEffectPacket.pitch = pitch;
playerConnection.sendPacket(entitySoundEffectPacket);
}
@Override
public void playSound(net.kyori.adventure.sound.@NotNull Sound sound) {
this.playSound(sound, this.position.getX(), this.position.getY(), this.position.getZ());
}
@Override
public void playSound(net.kyori.adventure.sound.@NotNull Sound sound, double x, double y, double z) {
playerConnection.sendPacket(AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
}
@Override
public void stopSound(@NotNull SoundStop stop) {
playerConnection.sendPacket(AdventurePacketConvertor.createSoundStopPacket(stop));
}
/**
* Plays a given effect at the given position for this player.
*
@ -914,7 +955,9 @@ public class Player extends LivingEntity implements CommandSender {
/**
* Sends a {@link StopSoundPacket} packet.
* @deprecated Use {@link #stopSound(SoundStop)} with {@link SoundStop#all()}
*/
@Deprecated
public void stopSound() {
StopSoundPacket stopSoundPacket = new StopSoundPacket();
stopSoundPacket.flags = 0x00;
@ -926,13 +969,19 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param header the header text, null to set empty
* @param footer the footer text, null to set empty
*
* @deprecated Use {@link #sendPlayerListHeaderAndFooter(Component, Component)}
*/
@Deprecated
public void sendHeaderFooter(@Nullable JsonMessage header, @Nullable JsonMessage footer) {
PlayerListHeaderAndFooterPacket playerListHeaderAndFooterPacket = new PlayerListHeaderAndFooterPacket();
playerListHeaderAndFooterPacket.header = header;
playerListHeaderAndFooterPacket.footer = footer;
this.sendPlayerListHeaderAndFooter(header == null ? Component.empty() : header.asComponent(),
footer == null ? Component.empty() : footer.asComponent());
}
playerConnection.sendPacket(playerListHeaderAndFooterPacket);
@Override
public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) {
PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(header, footer);
playerConnection.sendPacket(packet);
}
/**
@ -941,25 +990,12 @@ public class Player extends LivingEntity implements CommandSender {
* @param text the text of the title
* @param action the action of the title (where to show it)
* @see #sendTitleTime(int, int, int) to specify the display time
*
* @deprecated Use {@link #showTitle(Title)} and {@link #sendActionBar(Component)}
*/
@Deprecated
private void sendTitle(@NotNull JsonMessage text, @NotNull TitlePacket.Action action) {
TitlePacket titlePacket = new TitlePacket();
titlePacket.action = action;
switch (action) {
case SET_TITLE:
titlePacket.titleText = text;
break;
case SET_SUBTITLE:
titlePacket.subtitleText = text;
break;
case SET_ACTION_BAR:
titlePacket.actionBarText = text;
break;
default:
throw new UnsupportedOperationException("Invalid TitlePacket.Action type!");
}
TitlePacket titlePacket = new TitlePacket(action, text.asComponent());
playerConnection.sendPacket(titlePacket);
}
@ -969,10 +1005,12 @@ public class Player extends LivingEntity implements CommandSender {
* @param title the title message
* @param subtitle the subtitle message
* @see #sendTitleTime(int, int, int) to specify the display time
*
* @deprecated Use {@link #showTitle(Title)}
*/
@Deprecated
public void sendTitleSubtitleMessage(@NotNull JsonMessage title, @NotNull JsonMessage subtitle) {
sendTitle(title, TitlePacket.Action.SET_TITLE);
sendTitle(subtitle, TitlePacket.Action.SET_SUBTITLE);
this.showTitle(Title.title(title.asComponent(), subtitle.asComponent()));
}
/**
@ -980,9 +1018,12 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param title the title message
* @see #sendTitleTime(int, int, int) to specify the display time
*
* @deprecated Use {@link #showTitle(Title)}
*/
@Deprecated
public void sendTitleMessage(@NotNull JsonMessage title) {
sendTitle(title, TitlePacket.Action.SET_TITLE);
this.showTitle(Title.title(title.asComponent(), Component.empty()));
}
/**
@ -990,9 +1031,12 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param subtitle the subtitle message
* @see #sendTitleTime(int, int, int) to specify the display time
*
* @deprecated Use {@link #showTitle(Title)}
*/
@Deprecated
public void sendSubtitleMessage(@NotNull JsonMessage subtitle) {
sendTitle(subtitle, TitlePacket.Action.SET_SUBTITLE);
this.showTitle(Title.title(Component.empty(), subtitle.asComponent()));
}
/**
@ -1000,9 +1044,27 @@ public class Player extends LivingEntity implements CommandSender {
*
* @param actionBar the action bar message
* @see #sendTitleTime(int, int, int) to specify the display time
*
* @deprecated Use {@link #sendActionBar(Component)}
*/
@Deprecated
public void sendActionBarMessage(@NotNull JsonMessage actionBar) {
sendTitle(actionBar, TitlePacket.Action.SET_ACTION_BAR);
this.sendActionBar(actionBar.asComponent());
}
@Override
public void showTitle(@NotNull Title title) {
Collection<TitlePacket> packet = TitlePacket.of(Title.title(title.title(), title.subtitle(), title.times()));
for (TitlePacket titlePacket : packet) {
playerConnection.sendPacket(titlePacket);
}
}
@Override
public void sendActionBar(@NotNull Component message) {
TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, message);
playerConnection.sendPacket(titlePacket);
}
/**
@ -1011,39 +1073,56 @@ public class Player extends LivingEntity implements CommandSender {
* @param fadeIn ticks to spend fading in
* @param stay ticks to keep the title displayed
* @param fadeOut ticks to spend out, not when to start fading out
*
* @deprecated Use {@link #showTitle(Title)}. Note that this will overwrite the
* existing title. This is expected behavior and will be the case in 1.17.
*/
@Deprecated
public void sendTitleTime(int fadeIn, int stay, int fadeOut) {
TitlePacket titlePacket = new TitlePacket();
titlePacket.action = TitlePacket.Action.SET_TIMES_AND_DISPLAY;
titlePacket.fadeIn = fadeIn;
titlePacket.stay = stay;
titlePacket.fadeOut = fadeOut;
TitlePacket titlePacket = new TitlePacket(fadeIn, stay, fadeOut);
playerConnection.sendPacket(titlePacket);
}
/**
* Hides the previous title.
* @deprecated Use {@link #clearTitle()}
*/
@Deprecated
public void hideTitle() {
TitlePacket titlePacket = new TitlePacket();
titlePacket.action = TitlePacket.Action.HIDE;
TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.HIDE);
playerConnection.sendPacket(titlePacket);
}
/**
* Resets the previous title.
*/
@Override
public void resetTitle() {
TitlePacket titlePacket = new TitlePacket();
titlePacket.action = TitlePacket.Action.RESET;
TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.RESET);
playerConnection.sendPacket(titlePacket);
}
@Override
public void clearTitle() {
TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.HIDE);
playerConnection.sendPacket(titlePacket);
}
@Override
public void showBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().addBossBar(this, bar);
}
@Override
public void hideBossBar(@NotNull BossBar bar) {
MinecraftServer.getBossBarManager().removeBossBar(this, bar);
}
/**
* Opens a book ui for the player with the given book metadata.
*
* @param bookMeta The metadata of the book to open
*
* @deprecated Use {@link #openBook(Book)}
*/
@Deprecated
public void openBook(@NotNull WrittenBookMeta bookMeta) {
// Set book in offhand
final ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
@ -1063,6 +1142,32 @@ public class Player extends LivingEntity implements CommandSender {
this.inventory.update();
}
@Override
public void openBook(@NotNull Book book) {
// make the book
ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1);
writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book, this));
// Set book in offhand
SetSlotPacket setBookPacket = new SetSlotPacket();
setBookPacket.windowId = 0;
setBookPacket.slot = 45;
setBookPacket.itemStack = writtenBook;
playerConnection.sendPacket(setBookPacket);
// Open the book
OpenBookPacket openBookPacket = new OpenBookPacket();
openBookPacket.hand = Hand.OFF;
playerConnection.sendPacket(openBookPacket);
// Restore the item in offhand
SetSlotPacket restoreItemPacket = new SetSlotPacket();
restoreItemPacket.windowId = 0;
restoreItemPacket.slot = 45;
restoreItemPacket.itemStack = getItemInOffHand();
playerConnection.sendPacket(restoreItemPacket);
}
@Override
public boolean isImmune(@NotNull DamageType type) {
if (!getGameMode().canTakeDamage()) {
@ -1165,9 +1270,21 @@ public class Player extends LivingEntity implements CommandSender {
* Gets the player display name in the tab-list.
*
* @return the player display name, null means that {@link #getUsername()} is displayed
* @deprecated Use {@link #getDisplayName()}
*/
@Nullable
public JsonMessage getDisplayName() {
@Deprecated
public JsonMessage getDisplayNameJson() {
return JsonMessage.fromComponent(displayName);
}
/**
* Gets the player display name in the tab-list.
*
* @return the player display name, null means that {@link #getUsername()} is displayed
*/
@Nullable
public Component getDisplayName() {
return displayName;
}
@ -1177,8 +1294,21 @@ public class Player extends LivingEntity implements CommandSender {
* Sets to null to show the player username.
*
* @param displayName the display name, null to display the username
* @deprecated Use {@link #setDisplayName(Component)}
*/
@Deprecated
public void setDisplayName(@Nullable JsonMessage displayName) {
this.setDisplayName(displayName == null ? null : displayName.asComponent());
}
/**
* Changes the player display name in the tab-list.
* <p>
* Sets to null to show the player username.
*
* @param displayName the display name, null to display the username
*/
public void setDisplayName(@Nullable Component displayName) {
this.displayName = displayName;
PlayerInfoPacket infoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME);
@ -1730,16 +1860,40 @@ public class Player extends LivingEntity implements CommandSender {
* Kicks the player with a reason.
*
* @param text the kick reason
*
* @deprecated Use {@link #kick(Component)}
*/
@Deprecated
public void kick(@NotNull JsonMessage text) {
this.kick(text.asComponent());
}
/**
* Kicks the player with a reason.
*
* @param message the kick reason
*
* @deprecated Use {@link #kick(Component)}
*/
@Deprecated
public void kick(@NotNull String message) {
this.kick(Component.text(message));
}
/**
* Kicks the player with a reason.
*
* @param component the reason
*/
public void kick(@NotNull Component component) {
final ConnectionState connectionState = playerConnection.getConnectionState();
// Packet type depends on the current player connection state
final ServerPacket disconnectPacket;
if (connectionState == ConnectionState.LOGIN) {
disconnectPacket = new LoginDisconnectPacket(text);
disconnectPacket = new LoginDisconnectPacket(component);
} else {
disconnectPacket = new DisconnectPacket(text);
disconnectPacket = new DisconnectPacket(component);
}
if (playerConnection instanceof NettyPlayerConnection) {
@ -1751,15 +1905,6 @@ public class Player extends LivingEntity implements CommandSender {
}
}
/**
* Kicks the player with a reason.
*
* @param message the kick reason
*/
public void kick(@NotNull String message) {
kick(ColoredText.of(message));
}
/**
* Changes the current held slot for the player.
*
@ -2362,6 +2507,11 @@ public class Player extends LivingEntity implements CommandSender {
return lastKeepAlive;
}
@Override
public @NotNull HoverEvent<ShowEntity> asHoverEvent(@NotNull UnaryOperator<ShowEntity> op) {
return HoverEvent.showEntity(ShowEntity.of(EntityType.PLAYER, this.uuid, this.displayName));
}
/**
* Gets the packet to add the player from the tab-list.
*
@ -2501,6 +2651,34 @@ public class Player extends LivingEntity implements CommandSender {
inventory.setBoots(itemStack);
}
@Override
public Locale getLocale() {
return settings.locale == null ? null : Locale.forLanguageTag(settings.locale);
}
/**
* Sets the player's locale. This will only set the locale of the player as it
* is stored in the server. This will also be reset if the settings are refreshed.
*
* @param locale the new locale
*/
@Override
public void setLocale(@Nullable Locale locale) {
settings.locale = locale == null ? null : locale.toLanguageTag();
}
@Override
public @NotNull Identity identity() {
return this.identity;
}
@Override
public void setUuid(@NotNull UUID uuid) {
super.setUuid(uuid);
// update identity
this.identity = Identity.identity(uuid);
}
/**
* Represents the main or off hand of the player.
*/

View File

@ -1,5 +1,8 @@
package net.minestom.server.entity.damage;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.chat.RichMessage;
@ -8,7 +11,7 @@ import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -26,8 +29,8 @@ public class DamageType implements DataContainer {
public static final DamageType GRAVITY = new DamageType("attack.fall");
public static final DamageType ON_FIRE = new DamageType("attack.onFire") {
@Override
protected Sound getPlayerSound(@NotNull Player player) {
return Sound.ENTITY_PLAYER_HURT_ON_FIRE;
protected SoundEvent getPlayerSound(@NotNull Player player) {
return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE;
}
};
private final String identifier;
@ -55,18 +58,25 @@ public class DamageType implements DataContainer {
return identifier;
}
/**
* @deprecated Use {@link #buildDeathMessage(Player)}
*/
@Deprecated
public JsonMessage buildDeathMessageJson(@NotNull Player killed) {
return JsonMessage.fromComponent(this.buildDeathMessage(killed));
}
/**
* Builds the death message linked to this damage type.
* <p>
* Used in {@link Player#kill()} to broadcast the proper message.
*
* @param killed the player who has been killed
* @return the death message, null to do not send anything.
* Can be for instance, of type {@link ColoredText} or {@link RichMessage}.
* @return the death message, null to do not send anything
*/
@Nullable
public JsonMessage buildDeathMessage(@NotNull Player killed) {
return ColoredText.of("{@death." + identifier + "," + killed.getUsername() + "}");
public Component buildDeathMessage(@NotNull Player killed) {
return Component.translatable("death." + identifier, Component.text(killed.getUsername()));
}
/**
@ -103,15 +113,23 @@ public class DamageType implements DataContainer {
return new EntityDamage(entity);
}
/**
* @deprecated Use {@link #buildDeathScreenText(Player)}
*/
@Deprecated
@Nullable
public JsonMessage buildDeathScreenTextJson(@NotNull Player killed) {
return JsonMessage.fromComponent(this.buildDeathScreenText(killed));
}
/**
* Builds the text sent to a player in his death screen.
*
* @param killed the player who has been killed
* @return the death screen text, null to do not send anything
*/
@Nullable
public JsonMessage buildDeathScreenText(@NotNull Player killed) {
return ColoredText.of("{@death." + identifier + "}");
public Component buildDeathScreenText(@NotNull Player killed) {
return Component.translatable("death." + identifier);
}
/**
@ -121,19 +139,19 @@ public class DamageType implements DataContainer {
* @return the sound to play when the given entity is hurt by this damage type. Can be null if no sound should play
*/
@Nullable
public Sound getSound(@NotNull LivingEntity entity) {
public SoundEvent getSound(@NotNull LivingEntity entity) {
if (entity instanceof Player) {
return getPlayerSound((Player) entity);
}
return getGenericSound(entity);
}
protected Sound getGenericSound(@NotNull LivingEntity entity) {
return Sound.ENTITY_GENERIC_HURT;
protected SoundEvent getGenericSound(@NotNull LivingEntity entity) {
return SoundEvent.ENTITY_GENERIC_HURT;
}
protected Sound getPlayerSound(@NotNull Player player) {
return Sound.ENTITY_PLAYER_HURT;
protected SoundEvent getPlayerSound(@NotNull Player player) {
return SoundEvent.ENTITY_PLAYER_HURT;
}
@Override

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity.hologram;
import net.kyori.adventure.text.Component;
import net.minestom.server.Viewable;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
@ -13,7 +14,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* Represents an invisible armor stand showing a {@link JsonMessage}.
* Represents an invisible armor stand showing a {@link Component}.
*/
public class Hologram implements Viewable {
@ -22,7 +23,7 @@ public class Hologram implements Viewable {
private final HologramEntity entity;
private Position position;
private JsonMessage text;
private Component text;
private boolean removed;
@ -33,14 +34,24 @@ public class Hologram implements Viewable {
* @param spawnPosition The spawn position of this hologram.
* @param text The text of this hologram.
* @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}.
* @deprecated Use {@link #Hologram(Instance, Position, Component, boolean)}
*/
@Deprecated
public Hologram(Instance instance, Position spawnPosition, JsonMessage text, boolean autoViewable) {
this.entity = new HologramEntity(spawnPosition.clone().add(0, OFFSET_Y, 0));
this.entity.setInstance(instance);
this.entity.setAutoViewable(autoViewable);
this(instance, spawnPosition, text.asComponent(), autoViewable);
}
this.position = spawnPosition;
setText(text);
/**
* Constructs a new {@link Hologram} with the given parameters.
*
* @param instance The instance where the hologram should be spawned.
* @param spawnPosition The spawn position of this hologram.
* @param text The text of this hologram.
* @deprecated Use {@link #Hologram(Instance, Position, Component)}
*/
@Deprecated
public Hologram(Instance instance, Position spawnPosition, JsonMessage text) {
this(instance, spawnPosition, text, true);
}
/**
@ -50,10 +61,27 @@ public class Hologram implements Viewable {
* @param spawnPosition The spawn position of this hologram.
* @param text The text of this hologram.
*/
public Hologram(Instance instance, Position spawnPosition, JsonMessage text) {
public Hologram(Instance instance, Position spawnPosition, Component text) {
this(instance, spawnPosition, text, true);
}
/**
* Constructs a new {@link Hologram} with the given parameters.
*
* @param instance The instance where the hologram should be spawned.
* @param spawnPosition The spawn position of this hologram.
* @param text The text of this hologram.
* @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}.
*/
public Hologram(Instance instance, Position spawnPosition, Component text, boolean autoViewable) {
this.entity = new HologramEntity(spawnPosition.clone().add(0, OFFSET_Y, 0));
this.entity.setInstance(instance);
this.entity.setAutoViewable(autoViewable);
this.position = spawnPosition;
setText(text);
}
/**
* Gets the position of the hologram.
*
@ -79,8 +107,19 @@ public class Hologram implements Viewable {
* Gets the hologram text.
*
* @return the hologram text
* @deprecated Use {@link #getText()}
*/
public JsonMessage getText() {
@Deprecated
public JsonMessage getTextJson() {
return JsonMessage.fromComponent(text);
}
/**
* Gets the hologram text.
*
* @return the hologram text
*/
public Component getText() {
return text;
}
@ -88,8 +127,19 @@ public class Hologram implements Viewable {
* Changes the hologram text.
*
* @param text the new hologram text
* @deprecated Use {@link #setText(Component)}
*/
@Deprecated
public void setText(JsonMessage text) {
this.setText(text.asComponent());
}
/**
* Changes the hologram text.
*
* @param text the new hologram text
*/
public void setText(Component text) {
checkRemoved();
this.text = text;
this.entity.setCustomName(text);
@ -166,7 +216,7 @@ public class Hologram implements Viewable {
setSmall(true);
setNoGravity(true);
setCustomName(ColoredText.of(""));
setCustomName(Component.empty());
setCustomNameVisible(true);
setInvisible(true);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity.metadata;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
@ -106,11 +107,29 @@ public class EntityMeta {
this.metadata.setIndex((byte) 1, Metadata.VarInt(value));
}
public JsonMessage getCustomName() {
/**
* @deprecated Use {@link #getCustomName()}
*/
@Deprecated
public JsonMessage getCustomNameJson() {
return JsonMessage.fromComponent(this.getCustomName());
}
/**
* @deprecated Use {@link #setCustomName(Component)}
*/
@Deprecated
public void setCustomName(JsonMessage value) {
if (value != null) {
this.setCustomName(value.asComponent());
}
}
public Component getCustomName() {
return this.metadata.getIndex((byte) 2, null);
}
public void setCustomName(JsonMessage value) {
public void setCustomName(Component value) {
this.metadata.setIndex((byte) 2, Metadata.OptChat(value));
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity.metadata.minecart;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Entity;
@ -21,12 +22,29 @@ public class CommandBlockMinecartMeta extends AbstractMinecartMeta {
super.metadata.setIndex((byte) 13, Metadata.String(value));
}
/**
* @deprecated Use {@link #getLastOutput()}
*/
@Deprecated
@NotNull
public JsonMessage getLastOutput() {
return super.metadata.getIndex((byte) 14, ColoredText.of(""));
public JsonMessage getLastOutputJson() {
return JsonMessage.fromComponent(getLastOutput());
}
@NotNull
public Component getLastOutput() {
return super.metadata.getIndex((byte) 14, Component.empty());
}
/**
* @deprecated Use {@link #setLastOutput(Component)}
*/
@Deprecated
public void setLastOutput(@NotNull JsonMessage value) {
this.setLastOutput(value.asComponent());
}
public void setLastOutput(@NotNull Component value) {
super.metadata.setIndex((byte) 14, Metadata.Chat(value));
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.event.player;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.event.CancellableEvent;
@ -19,7 +20,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
private final Collection<Player> recipients;
private String message;
private Function<PlayerChatEvent, JsonMessage> chatFormat;
private Function<PlayerChatEvent, Component> chatFormat;
private boolean cancelled;
@ -33,8 +34,19 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
* Changes the chat format.
*
* @param chatFormat the custom chat format, null to use the default one
* @deprecated Use {@link #setChatFormat(Function)}
*/
public void setChatFormat(@Nullable Function<PlayerChatEvent, JsonMessage> chatFormat) {
@Deprecated
public void setChatFormatJson(@Nullable Function<PlayerChatEvent, JsonMessage> chatFormat) {
this.chatFormat = chatFormat == null ? null : chatFormat.andThen(JsonMessage::asComponent);
}
/**
* Changes the chat format.
*
* @param chatFormat the custom chat format, null to use the default one
*/
public void setChatFormat(@Nullable Function<PlayerChatEvent, Component> chatFormat) {
this.chatFormat = chatFormat;
}
@ -77,7 +89,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent {
* @return the chat format which will be used, null if this is the default one
*/
@Nullable
public Function<PlayerChatEvent, JsonMessage> getChatFormatFunction() {
public Function<PlayerChatEvent, Component> getChatFormatFunction() {
return chatFormat;
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.event.player;
import net.minestom.server.chat.ColoredText;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.event.PlayerEvent;
@ -12,10 +13,18 @@ import org.jetbrains.annotations.Nullable;
*/
public class PlayerDeathEvent extends PlayerEvent {
private JsonMessage deathText;
private JsonMessage chatMessage;
private Component deathText;
private Component chatMessage;
/**
* @deprecated Use {@link #PlayerDeathEvent(Player, Component, Component)}
*/
@Deprecated
public PlayerDeathEvent(@NotNull Player player, JsonMessage deathText, JsonMessage chatMessage) {
this(player, deathText.asComponent(), chatMessage.asComponent());
}
public PlayerDeathEvent(@NotNull Player player, Component deathText, Component chatMessage) {
super(player);
this.deathText = deathText;
this.chatMessage = chatMessage;
@ -25,9 +34,21 @@ public class PlayerDeathEvent extends PlayerEvent {
* Gets the text displayed in the death screen.
*
* @return the death text, can be null
* @deprecated Use {@link #getDeathText()}
*/
@Nullable
public JsonMessage getDeathText() {
@Deprecated
public JsonMessage getDeathTextJson() {
return JsonMessage.fromComponent(deathText);
}
/**
* Gets the text displayed in the death screen.
*
* @return the death text, can be null
*/
@Nullable
public Component getDeathText() {
return deathText;
}
@ -35,18 +56,41 @@ public class PlayerDeathEvent extends PlayerEvent {
* Changes the text displayed in the death screen.
*
* @param deathText the death text to display, null to remove
* @deprecated Use {@link #setDeathText(Component)}
*/
@Deprecated
public void setDeathText(@Nullable JsonMessage deathText) {
this.deathText = deathText == null ? null : deathText.asComponent();
}
/**
* Changes the text displayed in the death screen.
*
* @param deathText the death text to display, null to remove
*/
public void setDeathText(@Nullable Component deathText) {
this.deathText = deathText;
}
/**
* Gets the message sent to chat.
*
* @return the death chat message
* @deprecated Use {@link #getChatMessage()}
*/
@Deprecated
@Nullable
public JsonMessage getChatMessageJson() {
return JsonMessage.fromComponent(chatMessage);
}
/**
* Gets the message sent to chat.
*
* @return the death chat message
*/
@Nullable
public JsonMessage getChatMessage() {
public Component getChatMessage() {
return chatMessage;
}
@ -54,8 +98,19 @@ public class PlayerDeathEvent extends PlayerEvent {
* Changes the text sent in chat
*
* @param chatMessage the death message to send, null to remove
* @deprecated Use {@link #setChatMessage(Component)}
*/
@Deprecated
public void setChatMessage(@Nullable JsonMessage chatMessage) {
this.chatMessage = chatMessage == null ? null : chatMessage.asComponent();
}
/**
* Changes the text sent in chat
*
* @param chatMessage the death message to send, null to remove
*/
public void setChatMessage(@Nullable Component chatMessage) {
this.chatMessage = chatMessage;
}
}

View File

@ -1,8 +1,14 @@
package net.minestom.server.instance;
import com.google.common.collect.Queues;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.minestom.server.MinecraftServer;
import net.minestom.server.UpdateManager;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.Entity;
@ -53,7 +59,7 @@ import java.util.function.Consumer;
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Instance, int, int)} and {@link UpdateManager#signalChunkUnload(Instance, int, int)}.
*/
public abstract class Instance implements BlockModifier, EventHandler, DataContainer {
public abstract class Instance implements BlockModifier, EventHandler, DataContainer, PacketGroupingAudience {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
@ -450,6 +456,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*
* @return an unmodifiable {@link Set} containing all the players in the instance
*/
@Override
@NotNull
public Set<Player> getPlayers() {
return Collections.unmodifiableSet(players);

View File

@ -1,13 +1,31 @@
package net.minestom.server.item;
import com.google.gson.JsonNull;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import java.util.stream.Stream;
public class ItemDisplay {
private JsonMessage displayName;
private JsonMessage[] lore;
private Component displayName;
private Component[] lore;
/**
* @deprecated Use {@link #ItemDisplay(Component, Component[])}
*/
@Deprecated
public ItemDisplay(JsonMessage displayName, JsonMessage[] lore) {
this.displayName = displayName.asComponent();
this.lore = new Component[lore.length];
for (int i = 0; i < lore.length; i++) {
this.lore[i] = lore[i].asComponent();
}
}
public ItemDisplay(Component displayName, Component[] lore) {
this.displayName = displayName;
this.lore = lore;
}
@ -16,8 +34,34 @@ public class ItemDisplay {
* Gets the item display name.
*
* @return the item display name
* @deprecated Use {@link #getDisplayName()}
*/
public JsonMessage getDisplayName() {
@Deprecated
public JsonMessage getDisplayNameJson() {
return JsonMessage.fromComponent(displayName);
}
/**
* Gets the item lore.
*
* @return the item lore
* @deprecated Use {@link #getLore()}
*/
@Deprecated
public JsonMessage[] getLoreJson() {
JsonMessage[] loreOld = new JsonMessage[lore.length];
for (int i = 0; i < lore.length; i++) {
loreOld[i] = JsonMessage.fromComponent(lore[i]);
}
return loreOld;
}
/**
* Gets the item display name.
*
* @return the item display name
*/
public Component getDisplayName() {
return displayName;
}
@ -26,7 +70,7 @@ public class ItemDisplay {
*
* @return the item lore
*/
public JsonMessage[] getLore() {
public Component[] getLore() {
return lore;
}
}

View File

@ -2,6 +2,10 @@ package net.minestom.server.item;
import it.unimi.dsi.fastutil.objects.Object2ShortMap;
import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowItem;
import net.kyori.adventure.text.event.HoverEventSource;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.data.Data;
@ -26,6 +30,8 @@ import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
// TODO should we cache a ByteBuf of this item for faster packet write
@ -38,7 +44,7 @@ import java.util.*;
* Here a non-exhaustive list of what you can do to update the item:
* {@link PlayerInventory#refreshSlot(short)}, {@link Inventory#refreshSlot(short)} or a raw {@link SetSlotPacket}.
*/
public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
public class ItemStack implements DataContainer, PublicCloneable<ItemStack>, HoverEventSource<ShowItem> {
public static final OwnershipHandler<Data> DATA_OWNERSHIP = new OwnershipHandler<>();
public static final String OWNERSHIP_DATA_KEY = "ownership_identifier";
@ -54,9 +60,9 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
private byte amount;
private int damage;
private JsonMessage displayName;
private Component displayName;
private boolean unbreakable;
private List<JsonMessage> lore;
private List<Component> lore;
private Object2ShortMap<Enchantment> enchantmentMap;
private List<ItemAttribute> attributes;
@ -176,7 +182,7 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
return true;
}
final JsonMessage itemDisplayName = itemStack.getDisplayName();
final Component itemDisplayName = itemStack.getDisplayName();
final boolean displayNameCheck = (displayName == null && itemDisplayName == null) ||
(displayName != null && displayName.equals(itemDisplayName));
@ -309,13 +315,25 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
this.itemMeta = itemMeta;
}
/**
* Gets the item display name.
*
* @return the item display name, can be null if not present
* @deprecated Use {@link #getDisplayName()}
*/
@Deprecated
@Nullable
public JsonMessage getDisplayNameJson() {
return JsonMessage.fromComponent(displayName);
}
/**
* Gets the item display name.
*
* @return the item display name, can be null if not present
*/
@Nullable
public JsonMessage getDisplayName() {
public Component getDisplayName() {
return displayName;
}
@ -323,8 +341,19 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
* Sets the item display name.
*
* @param displayName the item display name
* @deprecated Use {@link #setDisplayName(Component)}
*/
@Deprecated
public void setDisplayName(@Nullable JsonMessage displayName) {
this.setDisplayName(displayName == null ? null : displayName.asComponent());
}
/**
* Sets the item display name.
*
* @param displayName the item display name
*/
public void setDisplayName(@Nullable Component displayName) {
this.displayName = displayName;
}
@ -337,13 +366,25 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
return displayName != null;
}
/**
* Gets the item lore.
*
* @return a modifiable list containing the item lore, can be empty if not present
* @deprecated Use {@link #getLore()}
*/
@Deprecated
@NotNull
public List<JsonMessage> getLoreJson() {
return lore.stream().map(JsonMessage::fromComponent).collect(Collectors.toList());
}
/**
* Gets the item lore.
*
* @return a modifiable list containing the item lore, can be empty if not present
*/
@NotNull
public List<JsonMessage> getLore() {
public List<Component> getLore() {
return lore;
}
@ -351,8 +392,20 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
* Sets the item lore.
*
* @param lore the item lore, can be empty to remove
* @deprecated Use {@link #setLore}
*/
public void setLore(@NotNull List<JsonMessage> lore) {
@Deprecated
public void setLoreJson(@NotNull List<JsonMessage> lore) {
this.lore = lore.stream().map(JsonMessage::asComponent).collect(Collectors.toList());
}
/**
* Sets the item lore.
*
* @param lore the item lore, can be empty to remove
*/
@NotNull
public void setLore(List<Component> lore) {
this.lore = lore;
}
@ -801,6 +854,11 @@ public class ItemStack implements DataContainer, PublicCloneable<ItemStack> {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
public @NotNull HoverEvent<ShowItem> asHoverEvent(@NotNull UnaryOperator<ShowItem> op) {
return HoverEvent.showItem(op.apply(ShowItem.of(this.material, this.amount, NBTUtils.asBinaryTagHolder(this.toNBT().getCompound("tag")))));
}
// Callback events
/**

View File

@ -1,6 +1,8 @@
package net.minestom.server.item.firework;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
@ -11,8 +13,23 @@ public class FireworkEffect {
private final boolean flicker;
private final boolean trail;
private final FireworkEffectType type;
private final ChatColor color;
private final ChatColor fadeColor;
private final Color color;
private final Color fadeColor;
/**
* Initializes a new firework effect.
*
* @param flicker {@code true} if this explosion has the Twinkle effect (glowstone dust), otherwise {@code false}.
* @param trail {@code true} if this explosion hsa the Trail effect (diamond), otherwise {@code false}.
* @param type The shape of this firework's explosion.
* @param color The primary color of this firework effect.
* @param fadeColor The secondary color of this firework effect.
* @deprecated Use {@link #FireworkEffect(boolean, boolean, FireworkEffectType, Color, Color)}
*/
@Deprecated
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) {
this(flicker, trail, type, color.asColor(), fadeColor.asColor());
}
/**
* Initializes a new firework effect.
@ -23,7 +40,7 @@ public class FireworkEffect {
* @param color The primary color of this firework effect.
* @param fadeColor The secondary color of this firework effect.
*/
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) {
public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, Color color, Color fadeColor) {
this.flicker = flicker;
this.trail = trail;
this.type = type;
@ -39,17 +56,17 @@ public class FireworkEffect {
*/
public static FireworkEffect fromCompound(@NotNull NBTCompound compound) {
ChatColor primaryColor = null;
ChatColor secondaryColor = null;
Color primaryColor = null;
Color secondaryColor = null;
if (compound.containsKey("Colors")) {
int[] color = compound.getIntArray("Colors");
primaryColor = ChatColor.fromRGB((byte) color[0], (byte) color[1], (byte) color[2]);
primaryColor = new Color(color[0], color[1], color[2]);
}
if (compound.containsKey("FadeColors")) {
int[] fadeColor = compound.getIntArray("FadeColors");
secondaryColor = ChatColor.fromRGB((byte) fadeColor[0], (byte) fadeColor[1], (byte) fadeColor[2]);
secondaryColor = new Color(fadeColor[0], fadeColor[1], fadeColor[2]);
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.item.metadata;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
@ -8,25 +9,18 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
* Represents the item meta for leather armor parts.
*/
public class LeatherArmorMeta extends ItemMeta {
private static final int BIT_MASK = 0xFF;
private boolean modified;
private byte red;
private byte green;
private byte blue;
private Color color;
/**
* Sets the color of the leather armor piece.
*
* @param color the color of the leather armor
* @deprecated Use {@link #setColor(Color)}
*/
@Deprecated
public void setColor(ChatColor color) {
// TODO using "CHAT color" is pretty weird, maybe that the class should be renamed to "Color"
this.red = color.getRed();
this.green = color.getGreen();
this.blue = color.getBlue();
this.modified = true;
this.setColor(color.asColor());
}
/**
@ -35,21 +29,37 @@ public class LeatherArmorMeta extends ItemMeta {
* @param red The red color of the leather armor piece.
* @param green The green color of the leather armor piece.
* @param blue The blue color of the leather armor piece.
* @deprecated Use {@link #setColor(Color)}
*/
@Deprecated
public void setColor(byte red, byte green, byte blue) {
this.red = red;
this.green = green;
this.blue = blue;
this.modified = true;
this.setColor(new Color(red, green, blue));
}
/**
* Sets the color of this leather armor piece.
*
* @param color the new color
*/
public void setColor(@NotNull Color color) {
this.modified = !color.equals(this.color);
this.color = color;
}
/**
* Gets the color of this leather armor piece.
*
* @return the color
*/
public @NotNull Color getColor() {
return this.color;
}
/**
* Resets the color to the default leather one.
*/
public void reset() {
this.red = 0;
this.green = 0;
this.blue = 0;
this.color = new Color(0, 0, 0);
this.modified = false;
}
@ -57,27 +67,33 @@ public class LeatherArmorMeta extends ItemMeta {
* Gets the red component.
*
* @return the red component
* @deprecated Use {@link #getColor}
*/
@Deprecated
public int getRed() {
return BIT_MASK & red;
return this.color.getRed();
}
/**
* Gets the green component.
*
* @return the green component
* @deprecated Use {@link #getColor}
*/
@Deprecated
public int getGreen() {
return BIT_MASK & green;
return this.color.getGreen();
}
/**
* Gets the blue component.
*
* @return the blue component
* @deprecated Use {@link #getColor}
*/
@Deprecated
public int getBlue() {
return BIT_MASK & blue;
return this.color.getBlue();
}
/**
@ -105,9 +121,7 @@ public class LeatherArmorMeta extends ItemMeta {
if (!(itemMeta instanceof LeatherArmorMeta)) return false;
final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) itemMeta;
return leatherArmorMeta.isModified() == isModified()
&& leatherArmorMeta.getRed() == getRed()
&& leatherArmorMeta.getGreen() == getGreen()
&& leatherArmorMeta.getBlue() == getBlue();
&& leatherArmorMeta.getColor().equals(getColor());
}
/**
@ -123,10 +137,7 @@ public class LeatherArmorMeta extends ItemMeta {
// Sets the color of the leather armor piece
// This also fixes that the armor pieces do not decolorize again when you are in creative
// mode.
this.setColor(
(byte) ((color >> 16) & BIT_MASK),
(byte) ((color >> 8) & BIT_MASK),
(byte) ((color) & BIT_MASK));
this.setColor(new Color(color));
}
}
}
@ -143,9 +154,7 @@ public class LeatherArmorMeta extends ItemMeta {
} else {
displayCompound = compound.getCompound("display");
}
final int color = this.getRed() << 16 | this.getGreen() << 8 | this.getBlue();
displayCompound.setInt("color", color);
displayCompound.setInt("color", color.asRGB());
// Adds the color compound to the display compound
compound.set("display", displayCompound);
}
@ -159,9 +168,7 @@ public class LeatherArmorMeta extends ItemMeta {
public ItemMeta clone() {
LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) super.clone();
leatherArmorMeta.modified = this.isModified();
leatherArmorMeta.red = (byte) this.getRed();
leatherArmorMeta.green = (byte) this.getGreen();
leatherArmorMeta.blue = (byte) this.getBlue();
leatherArmorMeta.color = color;
return leatherArmorMeta;
}

View File

@ -1,7 +1,10 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.color.DyeColor;
import net.minestom.server.utils.clone.CloneUtils;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;
@ -17,7 +20,7 @@ public class MapMeta extends ItemMeta {
private int mapId;
private int mapScaleDirection = 1;
private List<MapDecoration> decorations = new CopyOnWriteArrayList<>();
private ChatColor mapColor = ChatColor.NO_COLOR;
private Color mapColor = new Color(0, 0, 0);
public MapMeta() {
}
@ -84,21 +87,40 @@ public class MapMeta extends ItemMeta {
* Gets the map color.
*
* @return the map color
* @deprecated Use {@link #getMapColor()}
*/
public ChatColor getMapColor() {
return mapColor;
@Deprecated
public ChatColor getLegacyMapColor() {
return this.mapColor.asLegacyChatColor();
}
/**
* Gets the map color.
*
* @return the map color
*/
public @NotNull Color getMapColor() {
return this.mapColor;
}
/**
* Changes the map color.
* <p>
* WARNING: RGB colors are not supported.
*
* @param mapColor the new map color
* @deprecated Use {@link #setMapColor(Color)}
*/
@Deprecated
public void setMapColor(ChatColor mapColor) {
mapColor.getId(); // used to throw an error if rgb color is used
this.mapColor = mapColor;
this.setMapColor(mapColor.asColor());
}
/**
* Changes the map color.
*
* @param color the new map color
*/
public void setMapColor(@NotNull Color color) {
this.mapColor = color;
}
@Override
@ -156,8 +178,7 @@ public class MapMeta extends ItemMeta {
if (compound.containsKey("display")) {
final NBTCompound displayCompound = compound.getCompound("display");
if (displayCompound.containsKey("MapColor")) {
final int color = displayCompound.getAsInt("MapColor");
this.mapColor = ChatColor.fromId(color);
this.mapColor = new Color(displayCompound.getAsInt("MapColor"));
}
}
@ -191,7 +212,7 @@ public class MapMeta extends ItemMeta {
} else {
displayCompound = new NBTCompound();
}
displayCompound.setInt("MapColor", mapColor.getId());
displayCompound.setInt("MapColor", mapColor.asRGB());
}
}

View File

@ -1,6 +1,8 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.color.Color;
import net.minestom.server.potion.CustomPotionEffect;
import net.minestom.server.potion.PotionType;
import net.minestom.server.registry.Registries;
@ -29,8 +31,7 @@ public class PotionMeta extends ItemMeta {
// Not final because of #clone()
private List<CustomPotionEffect> customPotionEffects = new CopyOnWriteArrayList<>();
private boolean hasColor;
private byte red, green, blue;
private Color color;
/**
* Gets the potion type.
@ -65,19 +66,20 @@ public class PotionMeta extends ItemMeta {
* Changes the color of the potion.
*
* @param color the new color of the potion
* @deprecated Use {@link #setColor(Color)}
*/
@Deprecated
public void setColor(ChatColor color) {
// FIXME: weird usage of ChatColor, should maybe rename
this.setColor(color.asColor());
}
if (color == null) {
this.hasColor = false;
return;
}
this.red = color.getRed();
this.green = color.getGreen();
this.blue = color.getBlue();
this.hasColor = true;
/**
* Changes the color of the potion.
*
* @param color the new color of the potion
*/
public void setColor(@Nullable Color color) {
this.color = color;
}
@Override
@ -93,10 +95,7 @@ public class PotionMeta extends ItemMeta {
PotionMeta potionMeta = (PotionMeta) itemMeta;
return potionMeta.potionType == potionType &&
potionMeta.customPotionEffects.equals(customPotionEffects) &&
potionMeta.hasColor == hasColor &&
potionMeta.red == red &&
potionMeta.green == green &&
potionMeta.blue == blue;
potionMeta.color.equals(color);
}
@Override
@ -121,10 +120,7 @@ public class PotionMeta extends ItemMeta {
}
if (compound.containsKey("CustomPotionColor")) {
final int color = compound.getInt("CustomPotionColor");
this.red = (byte) ((color >> 16) & 0x000000FF);
this.green = (byte) ((color >> 8) & 0x000000FF);
this.blue = (byte) ((color) & 0x000000FF);
this.color = new Color(compound.getInt("CustomPotionColor"));
}
}
@ -151,9 +147,8 @@ public class PotionMeta extends ItemMeta {
compound.set("CustomPotionEffects", potionList);
}
if (hasColor) {
final int color = red << 16 + green << 8 + blue;
compound.setInt("CustomPotionColor", color);
if (color != null) {
compound.setInt("CustomPotionColor", color.asRGB());
}
}
@ -165,10 +160,7 @@ public class PotionMeta extends ItemMeta {
potionMeta.potionType = potionType;
potionMeta.customPotionEffects = CloneUtils.cloneCopyOnWriteArrayList(customPotionEffects);
potionMeta.hasColor = hasColor;
potionMeta.red = red;
potionMeta.green = green;
potionMeta.blue = blue;
potionMeta.color = color;
return potionMeta;
}

View File

@ -1,5 +1,8 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.AdventureSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
@ -14,7 +17,7 @@ public class WritableBookMeta extends ItemMeta {
private String title;
private String author;
private List<String> pages = new ArrayList<>();
private List<Component> pages = new ArrayList<>();
@Nullable
public String getTitle() {
@ -42,7 +45,7 @@ public class WritableBookMeta extends ItemMeta {
* @return a modifiable {@link ArrayList} containing the book pages
*/
@NotNull
public List<String> getPages() {
public List<Component> getPages() {
return pages;
}
@ -51,7 +54,7 @@ public class WritableBookMeta extends ItemMeta {
*
* @param pages the pages list
*/
public void setPages(@NotNull List<String> pages) {
public void setPages(@NotNull List<Component> pages) {
this.pages = pages;
}
@ -82,7 +85,7 @@ public class WritableBookMeta extends ItemMeta {
if (compound.containsKey("pages")) {
final NBTList<NBTString> list = compound.getList("pages");
for (NBTString page : list) {
this.pages.add(page.getValue());
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
}
}
}
@ -100,8 +103,8 @@ public class WritableBookMeta extends ItemMeta {
if (!pages.isEmpty()) {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (String page : pages) {
list.add(new NBTString(page));
for (Component page : pages) {
list.add(new NBTString(AdventureSerializer.serialize(page)));
}
compound.set("pages", list);
}

View File

@ -1,8 +1,10 @@
package net.minestom.server.item.metadata;
import net.minestom.server.chat.ChatParser;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.inventory.Book;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.adventure.Localizable;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
@ -18,7 +20,7 @@ public class WrittenBookMeta extends ItemMeta {
private WrittenBookGeneration generation;
private String author;
private String title;
private List<JsonMessage> pages = new ArrayList<>();
private List<Component> pages = new ArrayList<>();
/**
* Gets if the book is resolved.
@ -96,11 +98,12 @@ public class WrittenBookMeta extends ItemMeta {
/**
* Gets an {@link ArrayList} containing all the pages.
* <p>
* The list is modifiable.
* The list is not modifiable as it is .
*
* @return a modifiable {@link ArrayList} with the pages of the book
*/
public List<JsonMessage> getPages() {
@Deprecated
public List<Component> getPagesJson() {
return pages;
}
@ -109,7 +112,7 @@ public class WrittenBookMeta extends ItemMeta {
*
* @param pages the array list containing the book pages
*/
public void setPages(List<JsonMessage> pages) {
public void setPages(List<Component> pages) {
this.pages = pages;
}
@ -149,9 +152,7 @@ public class WrittenBookMeta extends ItemMeta {
if (compound.containsKey("pages")) {
final NBTList<NBTString> list = compound.getList("pages");
for (NBTString page : list) {
final String jsonPage = page.getValue();
final ColoredText coloredText = ChatParser.toColoredText(jsonPage);
this.pages.add(coloredText);
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
}
}
}
@ -172,8 +173,8 @@ public class WrittenBookMeta extends ItemMeta {
}
if (!pages.isEmpty()) {
NBTList<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (JsonMessage page : pages) {
list.add(new NBTString(page.toString()));
for (Component page : pages) {
list.add(new NBTString(AdventureSerializer.serialize(page)));
}
compound.set("pages", list);
}
@ -196,4 +197,25 @@ public class WrittenBookMeta extends ItemMeta {
ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED
}
/**
* Creates a written book meta from an Adventure book. This meta will not be
* resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}.
*
* @param book the book
* @param localizable who the book is for
*
* @return the meta
*/
public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) {
// make the book
WrittenBookMeta meta = new WrittenBookMeta();
meta.resolved = false;
meta.generation = WrittenBookGeneration.ORIGINAL;
meta.author = AdventureSerializer.translateAndSerialize(book.author(), localizable);
meta.title = AdventureSerializer.translateAndSerialize(book.title(), localizable);
meta.pages = new ArrayList<>();
meta.pages.addAll(book.pages());
return meta;
}
}

View File

@ -1,7 +1,8 @@
package net.minestom.server.listener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.*;
import net.minestom.server.command.CommandManager;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerChatEvent;
@ -9,7 +10,6 @@ import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.client.play.ClientChatMessagePacket;
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
import net.minestom.server.utils.PacketUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.function.Function;
@ -39,9 +39,9 @@ public class ChatMessageListener {
// Call the event
player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> {
final Function<PlayerChatEvent, JsonMessage> formatFunction = playerChatEvent.getChatFormatFunction();
final Function<PlayerChatEvent, Component> formatFunction = playerChatEvent.getChatFormatFunction();
JsonMessage textObject;
Component textObject;
if (formatFunction != null) {
// Custom format
@ -53,11 +53,9 @@ public class ChatMessageListener {
final Collection<Player> recipients = playerChatEvent.getRecipients();
if (!recipients.isEmpty()) {
final String jsonMessage = textObject.toString();
// Send the message with the correct player UUID
ChatMessagePacket chatMessagePacket =
new ChatMessagePacket(jsonMessage, ChatMessagePacket.Position.CHAT, player.getUuid());
new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid());
PacketUtils.sendGroupedPacket(recipients, chatMessagePacket);
}
@ -66,15 +64,13 @@ public class ChatMessageListener {
}
private static RichMessage buildDefaultChatMessage(PlayerChatEvent chatEvent) {
private static Component buildDefaultChatMessage(PlayerChatEvent chatEvent) {
final String username = chatEvent.getPlayer().getUsername();
final ColoredText usernameText = ColoredText.of(String.format("<%s>", username));
return RichMessage.of(usernameText)
.setHoverEvent(ChatHoverEvent.showText("Click to send a message to " + username))
.setClickEvent(ChatClickEvent.suggestCommand("/msg " + username + StringUtils.SPACE))
.append(ColoredText.of(StringUtils.SPACE + chatEvent.getMessage()));
return Component.text("<" + username + ">")
.hoverEvent(Component.text("Click to send a message to " + username))
.clickEvent(ClickEvent.suggestCommand("/msg " + username + " "))
.append(Component.text(" " + chatEvent.getMessage()));
}
}

View File

@ -1,7 +1,7 @@
package net.minestom.server.listener;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.client.play.ClientKeepAlivePacket;
@ -12,7 +12,7 @@ public class KeepAliveListener {
final long playerId = player.getLastKeepAlive();
final boolean equals = packetId == playerId;
if (!equals) {
player.kick(ColoredText.of(ChatColor.RED + "Bad Keep Alive packet"));
player.kick(Component.text("Bad Keep Alive packet", NamedTextColor.RED));
return;
}

View File

@ -1,9 +1,13 @@
package net.minestom.server.network;
import io.netty.channel.Channel;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.fakeplayer.FakePlayer;
@ -14,12 +18,10 @@ import net.minestom.server.listener.manager.ClientPacketConsumer;
import net.minestom.server.listener.manager.ServerPacketConsumer;
import net.minestom.server.network.packet.client.login.LoginStartPacket;
import net.minestom.server.network.packet.server.login.LoginSuccessPacket;
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
import net.minestom.server.network.packet.server.play.DisconnectPacket;
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.network.player.NettyPlayerConnection;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.utils.callback.validator.PlayerValidator;
import net.minestom.server.utils.validate.Check;
@ -33,6 +35,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Manages the connected clients.
@ -41,7 +44,7 @@ public final class ConnectionManager {
private static final long KEEP_ALIVE_DELAY = 10_000;
private static final long KEEP_ALIVE_KICK = 30_000;
private static final ColoredText TIMEOUT_TEXT = ColoredText.of(ChatColor.RED + "Timeout");
private static final Component TIMEOUT_TEXT = Component.text("Timeout", NamedTextColor.RED);
private final Queue<Player> waitingPlayers = new ConcurrentLinkedQueue<>();
private final Set<Player> players = new CopyOnWriteArraySet<>();
@ -58,7 +61,7 @@ public final class ConnectionManager {
// The consumers to call once a player connect, mostly used to init events
private final List<Consumer<Player>> playerInitializations = new CopyOnWriteArrayList<>();
private JsonMessage shutdownText = ColoredText.of(ChatColor.RED, "The server is shutting down.");
private Component shutdownText = Component.text("The server is shutting down.", NamedTextColor.RED);
/**
* Gets the {@link Player} linked to a {@link PlayerConnection}.
@ -144,13 +147,15 @@ public final class ConnectionManager {
*
* @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage}
* @param condition the condition to receive the message
*
* @deprecated Use {@link Audiences#players(Predicate)}
*/
@Deprecated
public void broadcastMessage(@NotNull JsonMessage jsonMessage, @Nullable PlayerValidator condition) {
final Collection<Player> recipients = getRecipients(condition);
if (!recipients.isEmpty()) {
final String jsonText = jsonMessage.toString();
broadcastJson(jsonText, recipients);
if (condition == null) {
Audiences.audiences().players().sendMessage(jsonMessage);
} else {
Audiences.audiences().players(condition).sendMessage(jsonMessage);
}
}
@ -158,16 +163,11 @@ public final class ConnectionManager {
* Sends a {@link JsonMessage} to all online players.
*
* @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage}
* @deprecated Use {@link Audience#sendMessage(Component)} on {@link Audiences#players()}
*/
@Deprecated
public void broadcastMessage(@NotNull JsonMessage jsonMessage) {
broadcastMessage(jsonMessage, null);
}
private void broadcastJson(@NotNull String json, @NotNull Collection<Player> recipients) {
ChatMessagePacket chatMessagePacket =
new ChatMessagePacket(json, ChatMessagePacket.Position.SYSTEM_MESSAGE);
PacketUtils.sendGroupedPacket(recipients, chatMessagePacket);
this.broadcastMessage(jsonMessage, null);
}
private Collection<Player> getRecipients(@Nullable PlayerValidator condition) {
@ -313,23 +313,48 @@ public final class ConnectionManager {
this.playerInitializations.remove(playerInitialization);
}
/**
* Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}.
*
* @return the kick reason in case on a shutdown
*
* @deprecated Use {@link #getShutdownText()}
*/
@Deprecated
@NotNull
public JsonMessage getShutdownTextJson() {
return JsonMessage.fromComponent(shutdownText);
}
/**
* Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}.
*
* @return the kick reason in case on a shutdown
*/
@NotNull
public JsonMessage getShutdownText() {
public Component getShutdownText() {
return shutdownText;
}
/**
* Changes the kick reason in case of a shutdown.
*
* @param shutdownText the new shutdown kick reason
* @see #getShutdownTextJson()
* @deprecated Use {@link #setShutdownText(Component)}
*/
@Deprecated
public void setShutdownText(@NotNull JsonMessage shutdownText) {
this.shutdownText = shutdownText.asComponent();
}
/**
* Changes the kick reason in case of a shutdown.
*
* @param shutdownText the new shutdown kick reason
* @see #getShutdownText()
*/
public void setShutdownText(@NotNull JsonMessage shutdownText) {
public void setShutdownText(@NotNull Component shutdownText) {
this.shutdownText = shutdownText;
}
@ -459,7 +484,7 @@ public final class ConnectionManager {
* Shutdowns the connection manager by kicking all the currently connected players.
*/
public void shutdown() {
DisconnectPacket disconnectPacket = new DisconnectPacket(getShutdownText());
DisconnectPacket disconnectPacket = new DisconnectPacket(shutdownText);
for (Player player : getOnlinePlayers()) {
final PlayerConnection playerConnection = player.getPlayerConnection();
if (playerConnection instanceof NettyPlayerConnection) {

View File

@ -1,8 +1,8 @@
package net.minestom.server.network.packet.client.handshake;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.network.ConnectionState;
@ -21,9 +21,8 @@ public class HandshakePacket implements ClientPreplayPacket {
/**
* Text sent if a player tries to connect with an invalid version of the client
*/
private static final ColoredText INVALID_VERSION_TEXT = ColoredText.of(ChatColor.RED, "Invalid Version, please use " + MinecraftServer.VERSION_NAME);
private static final ColoredText INVALID_BUNGEE_FORWARDING = ColoredText.of(ChatColor.RED, "If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
private static final Component INVALID_VERSION_TEXT = Component.text("Invalid Version, please use " + MinecraftServer.VERSION_NAME, NamedTextColor.RED);
private static final Component INVALID_BUNGEE_FORWARDING = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.RED);
private int protocolVersion;
private String serverAddress;
@ -95,7 +94,7 @@ public class HandshakePacket implements ClientPreplayPacket {
}
} else {
// Incorrect client version
connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT.toString()));
connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT));
connection.disconnect();
}
break;

View File

@ -1,8 +1,8 @@
package net.minestom.server.network.packet.client.login;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.extras.velocity.VelocityProxy;
@ -23,7 +23,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket {
private final static ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
public static final ColoredText INVALID_PROXY_RESPONSE = ColoredText.of(ChatColor.RED, "Invalid proxy response!");
public static final Component INVALID_PROXY_RESPONSE = Component.text("Invalid proxy response!", NamedTextColor.RED);
public int messageId;
public boolean successful;

View File

@ -1,8 +1,8 @@
package net.minestom.server.network.packet.client.login;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.Player;
import net.minestom.server.extras.MojangAuth;
import net.minestom.server.extras.bungee.BungeeCordProxy;
@ -22,7 +22,7 @@ import java.util.concurrent.ThreadLocalRandom;
public class LoginStartPacket implements ClientPreplayPacket {
private static final ColoredText ALREADY_CONNECTED_JSON = ColoredText.of(ChatColor.RED, "You are already on this server");
private static final Component ALREADY_CONNECTED = Component.text("You are already on this server", NamedTextColor.RED);
public String username;
@ -72,7 +72,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
if (MojangAuth.isEnabled() && isNettyClient) {
// Mojang auth
if (CONNECTION_MANAGER.getPlayer(username) != null) {
connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED_JSON));
connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED));
connection.disconnect();
return;
}

View File

@ -0,0 +1,8 @@
package net.minestom.server.network.packet.server;
import net.minestom.server.adventure.ComponentHolder;
/**
* A server packet that can hold components.
*/
public interface ComponentHoldingServerPacket extends ServerPacket, ComponentHolder<ServerPacket> { }

View File

@ -1,26 +1,34 @@
package net.minestom.server.network.packet.server.login;
import net.kyori.adventure.text.Component;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class LoginDisconnectPacket implements ServerPacket {
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
private final String kickMessage; // JSON text
public class LoginDisconnectPacket implements ComponentHoldingServerPacket {
public Component kickMessage;
public LoginDisconnectPacket(@NotNull String kickMessage) {
public LoginDisconnectPacket(@NotNull Component kickMessage) {
this.kickMessage = kickMessage;
}
/**
* @deprecated Use {@link #LoginDisconnectPacket(Component)}
*/
@Deprecated
public LoginDisconnectPacket(@NotNull JsonMessage jsonKickMessage) {
this(jsonKickMessage.toString());
this(jsonKickMessage.asComponent());
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(kickMessage);
writer.writeComponent(kickMessage);
}
@Override
@ -28,4 +36,13 @@ public class LoginDisconnectPacket implements ServerPacket {
return ServerPacketIdentifier.LOGIN_DISCONNECT;
}
@Override
public @NotNull Collection<Component> components() {
return List.of(this.kickMessage);
}
@Override
public @NotNull LoginDisconnectPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new LoginDisconnectPacket(operator.apply(this.kickMessage));
}
}

View File

@ -1,15 +1,22 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.advancements.FrameType;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.binary.Writeable;
import org.jetbrains.annotations.NotNull;
public class AdvancementsPacket implements ServerPacket {
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
public class AdvancementsPacket implements ComponentHoldingServerPacket {
public boolean resetAdvancements;
public AdvancementMapping[] advancementMappings;
@ -37,6 +44,32 @@ public class AdvancementsPacket implements ServerPacket {
return ServerPacketIdentifier.ADVANCEMENTS;
}
@Override
public @NotNull Collection<Component> components() {
List<Component> components = new ArrayList<>();
for (AdvancementMapping advancementMapping : advancementMappings) {
components.add(advancementMapping.value.displayData.title);
components.add(advancementMapping.value.displayData.description);
}
return components;
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
AdvancementsPacket packet = new AdvancementsPacket();
packet.resetAdvancements = this.resetAdvancements;
packet.advancementMappings = Arrays.copyOf(this.advancementMappings, this.advancementMappings.length);
packet.identifiersToRemove = Arrays.copyOf(this.identifiersToRemove, this.identifiersToRemove.length);
packet.progressMappings = Arrays.copyOf(this.progressMappings, this.progressMappings.length);
for (AdvancementMapping advancementMapping : packet.advancementMappings) {
advancementMapping.value.displayData.title = operator.apply(advancementMapping.value.displayData.title);
advancementMapping.value.displayData.description = operator.apply(advancementMapping.value.displayData.title);
}
return packet;
}
/**
* AdvancementMapping maps the namespaced ID to the Advancement.
*/
@ -84,8 +117,8 @@ public class AdvancementsPacket implements ServerPacket {
}
public static class DisplayData implements Writeable {
public JsonMessage title; // Only text
public JsonMessage description; // Only text
public Component title; // Only text
public Component description; // Only text
public ItemStack icon;
public FrameType frameType;
public int flags;
@ -95,8 +128,8 @@ public class AdvancementsPacket implements ServerPacket {
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(title.toString());
writer.writeSizedString(description.toString());
writer.writeComponent(title);
writer.writeComponent(description);
writer.writeItemStack(icon);
writer.writeVarInt(frameType.ordinal());
writer.writeInt(flags);

View File

@ -1,27 +1,30 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.bossbar.BarColor;
import net.minestom.server.bossbar.BarDivision;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import java.util.function.UnaryOperator;
public class BossBarPacket implements ServerPacket {
public class BossBarPacket implements ComponentHoldingServerPacket {
public UUID uuid;
public Action action;
public JsonMessage title; // Only text
public Component title; // Only text
public float health;
public BarColor color;
public BarDivision division;
public BossBar.Color color;
public BossBar.Overlay overlay;
public byte flags;
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeUuid(uuid);
@ -29,10 +32,10 @@ public class BossBarPacket implements ServerPacket {
switch (action) {
case ADD:
writer.writeSizedString(title.toString());
writer.writeComponent(title);
writer.writeFloat(health);
writer.writeVarInt(color.ordinal());
writer.writeVarInt(division.ordinal());
writer.writeVarInt(AdventurePacketConvertor.getBossBarColorValue(color));
writer.writeVarInt(AdventurePacketConvertor.getBossBarOverlayValue(overlay));
writer.writeByte(flags);
break;
case REMOVE:
@ -42,11 +45,11 @@ public class BossBarPacket implements ServerPacket {
writer.writeFloat(health);
break;
case UPDATE_TITLE:
writer.writeSizedString(title.toString());
writer.writeComponent(title);
break;
case UPDATE_STYLE:
writer.writeVarInt(color.ordinal());
writer.writeVarInt(division.ordinal());
writer.writeVarInt(AdventurePacketConvertor.getBossBarColorValue(color));
writer.writeVarInt(AdventurePacketConvertor.getBossBarOverlayValue(overlay));
break;
case UPDATE_FLAGS:
writer.writeByte(flags);
@ -59,6 +62,40 @@ public class BossBarPacket implements ServerPacket {
return ServerPacketIdentifier.BOSS_BAR;
}
@Override
public @NotNull Collection<Component> components() {
if (title != null) {
return Collections.singleton(title);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
switch (action) {
case UPDATE_TITLE: {
BossBarPacket packet = new BossBarPacket();
packet.action = action;
packet.uuid = uuid;
packet.title = operator.apply(title);
return packet;
}
case ADD: {
BossBarPacket packet = new BossBarPacket();
packet.action = action;
packet.uuid = uuid;
packet.title = operator.apply(title);
packet.health = health;
packet.overlay = overlay;
packet.color = color;
packet.flags = flags;
return packet;
}
default: return this;
}
}
public enum Action {
ADD,
REMOVE,

View File

@ -1,31 +1,42 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import java.util.function.UnaryOperator;
public class ChatMessagePacket implements ServerPacket {
/**
* Represents an outgoing chat message packet.
*/
public class ChatMessagePacket implements ComponentHoldingServerPacket {
private static final UUID NULL_UUID = new UUID(0, 0);
public String jsonMessage;
public Component message;
public Position position;
public UUID uuid;
public ChatMessagePacket(String jsonMessage, Position position, UUID uuid) {
this.jsonMessage = jsonMessage;
public ChatMessagePacket(Component message, Position position, UUID uuid) {
this.message = message;
this.position = position;
this.uuid = uuid;
}
public ChatMessagePacket(String jsonMessage, Position position) {
this(jsonMessage, position, new UUID(0, 0));
public ChatMessagePacket(Component message, Position position) {
this(message, position, NULL_UUID);
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(jsonMessage);
writer.writeComponent(message);
writer.writeByte((byte) position.ordinal());
writer.writeUuid(uuid);
}
@ -35,9 +46,50 @@ public class ChatMessagePacket implements ServerPacket {
return ServerPacketIdentifier.CHAT_MESSAGE;
}
@Override
public @NotNull Collection<Component> components() {
return Collections.singleton(message);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new ChatMessagePacket(operator.apply(message), position, uuid);
}
public enum Position {
CHAT,
SYSTEM_MESSAGE,
GAME_INFO
CHAT(MessageType.CHAT),
SYSTEM_MESSAGE(MessageType.SYSTEM),
GAME_INFO(null);
private final MessageType messageType;
Position(MessageType messageType) {
this.messageType = messageType;
}
/**
* Gets the Adventure message type from this position. Note that there is no
* message type for {@link #GAME_INFO}, as Adventure uses the title methods for this.
*
* @return the message type, if any
*/
public @Nullable MessageType getMessageType() {
return this.messageType;
}
/**
* Gets a position from an Adventure message type.
*
* @param messageType the message type
*
* @return the position
*/
public static @NotNull Position fromMessageType(@NotNull MessageType messageType) {
switch (messageType) {
case CHAT: return CHAT;
case SYSTEM: return SYSTEM_MESSAGE;
}
throw new IllegalArgumentException("Cannot get position from message type!");
}
}
}

View File

@ -1,26 +1,29 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.Collection;
import java.util.Collections;
import java.util.function.UnaryOperator;
/**
* Packet sent during combat to a {@link Player}.
* Only death is supported for the moment (other events are ignored anyway as of 1.15.2)
*/
public class CombatEventPacket implements ServerPacket {
public class CombatEventPacket implements ComponentHoldingServerPacket {
private EventType type;
private int duration;
private int opponent;
private int playerID;
private JsonMessage deathMessage; // Only text
private Component deathMessage;
private CombatEventPacket() {
}
@ -39,7 +42,7 @@ public class CombatEventPacket implements ServerPacket {
return packet;
}
public static CombatEventPacket death(Player player, Entity killer, JsonMessage message) {
public static CombatEventPacket death(Player player, Entity killer, Component message) {
CombatEventPacket packet = new CombatEventPacket();
packet.type = EventType.DEATH;
packet.playerID = player.getEntityId();
@ -64,7 +67,7 @@ public class CombatEventPacket implements ServerPacket {
case DEATH:
writer.writeVarInt(playerID);
writer.writeInt(opponent);
writer.writeSizedString(deathMessage.toString());
writer.writeComponent(deathMessage);
break;
}
}
@ -74,6 +77,29 @@ public class CombatEventPacket implements ServerPacket {
return ServerPacketIdentifier.COMBAT_EVENT;
}
@Override
public @NotNull Collection<Component> components() {
if (this.type == EventType.DEATH) {
return Collections.singleton(deathMessage);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (this.type == EventType.DEATH) {
CombatEventPacket packet = new CombatEventPacket();
packet.type = type;
packet.playerID = playerID;
packet.opponent = opponent;
packet.deathMessage = deathMessage;
return packet;
} else {
return this;
}
}
public enum EventType {
ENTER_COMBAT, END_COMBAT, // both ignored by Notchian client
DEATH,

View File

@ -1,26 +1,44 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class DisconnectPacket implements ServerPacket {
import java.util.Collection;
import java.util.Collections;
import java.util.function.UnaryOperator;
public JsonMessage message; // Only text
public class DisconnectPacket implements ComponentHoldingServerPacket {
public Component message;
public DisconnectPacket(@NotNull JsonMessage message){
/**
* Creates a new disconnect packet with a given message.
* @param message the message
*/
public DisconnectPacket(@NotNull Component message) {
this.message = message;
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(message.toString());
writer.writeComponent(message);
}
@Override
public int getId() {
return ServerPacketIdentifier.DISCONNECT;
}
@Override
public @NotNull Collection<Component> components() {
return Collections.singleton(message);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new DisconnectPacket(operator.apply(message));
}
}

View File

@ -1,15 +1,16 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.sound.Sound;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class EntitySoundEffectPacket implements ServerPacket {
public int soundId;
public SoundCategory soundCategory;
public Sound.Source soundSource;
public int entityId;
public float volume;
public float pitch;
@ -17,7 +18,7 @@ public class EntitySoundEffectPacket implements ServerPacket {
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeVarInt(soundId);
writer.writeVarInt(soundCategory.ordinal());
writer.writeVarInt(AdventurePacketConvertor.getSoundSourceValue(soundSource));
writer.writeVarInt(entityId);
writer.writeFloat(volume);
writer.writeFloat(pitch);

View File

@ -1,12 +1,16 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class MapDataPacket implements ServerPacket {
import java.util.*;
import java.util.function.UnaryOperator;
public class MapDataPacket implements ComponentHoldingServerPacket {
public int mapId;
public byte scale;
@ -59,11 +63,49 @@ public class MapDataPacket implements ServerPacket {
return ServerPacketIdentifier.MAP_DATA;
}
@Override
public @NotNull Collection<Component> components() {
if (icons == null || icons.length == 0) {
return Collections.emptyList();
} else {
List<Component> components = new ArrayList<>();
for (Icon icon : icons) {
components.add(icon.displayName);
}
return components;
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (this.icons == null || this.icons.length == 0) {
return this;
} else {
MapDataPacket packet = new MapDataPacket();
packet.mapId = this.mapId;
packet.scale = this.scale;
packet.trackingPosition = this.trackingPosition;
packet.locked = this.locked;
packet.columns = this.columns;
packet.rows = this.rows;
packet.x = this.x;
packet.z = this.z;
packet.data = this.data;
packet.icons = Arrays.copyOf(this.icons, this.icons.length);
for (Icon icon : packet.icons) {
icon.displayName = operator.apply(icon.displayName);
}
return packet;
}
}
public static class Icon {
public int type;
public byte x, z;
public byte direction;
public JsonMessage displayName; // Only text
public Component displayName;
private void write(BinaryWriter writer) {
writer.writeVarInt(type);
@ -74,7 +116,7 @@ public class MapDataPacket implements ServerPacket {
final boolean hasDisplayName = displayName != null;
writer.writeBoolean(hasDisplayName);
if (hasDisplayName) {
writer.writeSizedString(displayName.toString());
writer.writeComponent(displayName);
}
}

View File

@ -1,15 +1,16 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class NamedSoundEffectPacket implements ServerPacket {
public String soundName;
public SoundCategory soundCategory;
public Source soundSource;
public int x, y, z;
public float volume;
public float pitch;
@ -17,7 +18,7 @@ public class NamedSoundEffectPacket implements ServerPacket {
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeSizedString(soundName);
writer.writeVarInt(soundCategory.ordinal());
writer.writeVarInt(AdventurePacketConvertor.getSoundSourceValue(soundSource));
writer.writeInt(x * 8);
writer.writeInt(y * 8);
writer.writeInt(z * 8);

View File

@ -1,17 +1,18 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.ComponentHolder;
import net.minestom.server.entity.GameMode;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.function.UnaryOperator;
public class PlayerInfoPacket implements ServerPacket {
public class PlayerInfoPacket implements ComponentHoldingServerPacket {
public Action action;
public List<PlayerInfo> playerInfos;
@ -38,6 +39,40 @@ public class PlayerInfoPacket implements ServerPacket {
return ServerPacketIdentifier.PLAYER_INFO;
}
@Override
public @NotNull Collection<Component> components() {
switch (this.action) {
case ADD_PLAYER:
case UPDATE_DISPLAY_NAME:
List<Component> components = new ArrayList<>();
for (PlayerInfo playerInfo : playerInfos) {
if (playerInfo instanceof ComponentHolder) {
components.addAll(((ComponentHolder<? extends PlayerInfo>) playerInfo).components());
}
}
return components;
default: return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
switch (this.action) {
case ADD_PLAYER:
case UPDATE_DISPLAY_NAME:
PlayerInfoPacket packet = new PlayerInfoPacket(action);
packet.playerInfos = new ArrayList<>(playerInfos.size());
for (PlayerInfo playerInfo : playerInfos) {
if (playerInfo instanceof ComponentHolder) {
playerInfos.add(((ComponentHolder<? extends PlayerInfo>) playerInfo).copyWithOperator(operator));
} else {
playerInfos.add(playerInfo);
}
}
default: return this;
}
}
public enum Action {
ADD_PLAYER(AddPlayer.class),
@ -69,13 +104,13 @@ public class PlayerInfoPacket implements ServerPacket {
public abstract void write(BinaryWriter writer);
}
public static class AddPlayer extends PlayerInfo {
public static class AddPlayer extends PlayerInfo implements ComponentHolder<AddPlayer> {
public String name;
public List<Property> properties;
public GameMode gameMode;
public int ping;
public JsonMessage displayName; // Only text
public Component displayName;
public AddPlayer(UUID uuid, String name, GameMode gameMode, int ping) {
super(uuid);
@ -98,7 +133,27 @@ public class PlayerInfoPacket implements ServerPacket {
final boolean hasDisplayName = displayName != null;
writer.writeBoolean(hasDisplayName);
if (hasDisplayName)
writer.writeSizedString(displayName.toString());
writer.writeComponent(displayName);
}
@Override
public @NotNull Collection<Component> components() {
if (displayName == null) {
return Collections.emptyList();
} else {
return Collections.singleton(displayName);
}
}
@Override
public @NotNull AddPlayer copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (displayName == null) {
return this;
} else {
AddPlayer addPlayer = new AddPlayer(uuid, name, gameMode, ping);
addPlayer.displayName = operator.apply(displayName);
return addPlayer;
}
}
public static class Property {
@ -159,11 +214,11 @@ public class PlayerInfoPacket implements ServerPacket {
}
}
public static class UpdateDisplayName extends PlayerInfo {
public static class UpdateDisplayName extends PlayerInfo implements ComponentHolder<UpdateDisplayName> {
public JsonMessage displayName; // Only text
public Component displayName;
public UpdateDisplayName(UUID uuid, JsonMessage displayName) {
public UpdateDisplayName(UUID uuid, Component displayName) {
super(uuid);
this.displayName = displayName;
}
@ -173,7 +228,25 @@ public class PlayerInfoPacket implements ServerPacket {
final boolean hasDisplayName = displayName != null;
writer.writeBoolean(hasDisplayName);
if (hasDisplayName)
writer.writeSizedString(displayName.toString());
writer.writeComponent(displayName);
}
@Override
public @NotNull Collection<Component> components() {
if (displayName == null) {
return Collections.emptyList();
} else {
return Collections.singleton(displayName);
}
}
@Override
public @NotNull UpdateDisplayName copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (displayName == null) {
return this;
} else {
return new UpdateDisplayName(uuid, operator.apply(displayName));
}
}
}

View File

@ -1,35 +1,53 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerListHeaderAndFooterPacket implements ServerPacket {
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.UnaryOperator;
private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
public class PlayerListHeaderAndFooterPacket implements ComponentHoldingServerPacket {
public Component header;
public Component footer;
public JsonMessage header; // Only text
public JsonMessage footer; // Only text
public PlayerListHeaderAndFooterPacket(@Nullable Component header, @Nullable Component footer) {
this.header = header;
this.footer = footer;
}
@Override
public void write(@NotNull BinaryWriter writer) {
if (header == null) {
writer.writeSizedString(EMPTY_COMPONENT);
} else {
writer.writeSizedString(header.toString());
}
if (footer == null) {
writer.writeSizedString(EMPTY_COMPONENT);
} else {
writer.writeSizedString(footer.toString());
}
writer.writeComponent(Objects.requireNonNullElse(header, Component.empty()));
writer.writeComponent(Objects.requireNonNullElse(footer, Component.empty()));
}
@Override
public int getId() {
return ServerPacketIdentifier.PLAYER_LIST_HEADER_AND_FOOTER;
}
@Override
public @NotNull Collection<Component> components() {
List<Component> components = new ArrayList<>();
if (header != null) {
components.add(header);
}
if (footer != null) {
components.add(footer);
}
return components;
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new PlayerListHeaderAndFooterPacket(header == null ? null : operator.apply(header), footer == null ? null : operator.apply(footer));
}
}

View File

@ -1,12 +1,17 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class ScoreboardObjectivePacket implements ServerPacket {
import java.util.Collection;
import java.util.Collections;
import java.util.function.UnaryOperator;
public class ScoreboardObjectivePacket implements ComponentHoldingServerPacket {
/**
* An unique name for the objective
@ -21,7 +26,7 @@ public class ScoreboardObjectivePacket implements ServerPacket {
/**
* The text to be displayed for the score
*/
public JsonMessage objectiveValue; // Only text
public Component objectiveValue; // Only text
/**
* The type how the score is displayed
*/
@ -33,7 +38,7 @@ public class ScoreboardObjectivePacket implements ServerPacket {
writer.writeByte(mode);
if (mode == 0 || mode == 2) {
writer.writeSizedString(objectiveValue.toString());
writer.writeComponent(objectiveValue);
writer.writeVarInt(type.ordinal());
}
}
@ -43,6 +48,29 @@ public class ScoreboardObjectivePacket implements ServerPacket {
return ServerPacketIdentifier.SCOREBOARD_OBJECTIVE;
}
@Override
public @NotNull Collection<Component> components() {
if (mode == 0 || mode == 2) {
return Collections.singleton(objectiveValue);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (mode == 0 || mode == 2) {
ScoreboardObjectivePacket packet = new ScoreboardObjectivePacket();
packet.objectiveName = objectiveName;
packet.mode = mode;
packet.objectiveValue = operator.apply(objectiveValue);
packet.type = type;
return packet;
} else {
return this;
}
}
/**
* This enumeration represents all available types for the scoreboard objective
*/

View File

@ -1,9 +1,10 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
@ -11,15 +12,16 @@ import org.jetbrains.annotations.NotNull;
public class SoundEffectPacket implements ServerPacket {
public int soundId;
public SoundCategory soundCategory;
public Source soundSource;
public int x, y, z;
public float volume;
public float pitch;
public static SoundEffectPacket create(SoundCategory category, Sound sound, Position position, float volume, float pitch) {
@NotNull
public static SoundEffectPacket create(Source category, SoundEvent sound, Position position, float volume, float pitch) {
SoundEffectPacket packet = new SoundEffectPacket();
packet.soundId = sound.getId();
packet.soundCategory = category;
packet.soundSource = category;
// *8 converts to fixed-point representation with 3 bits for fractional part
packet.x = (int) position.getX();
packet.y = (int) position.getY();
@ -28,11 +30,10 @@ public class SoundEffectPacket implements ServerPacket {
packet.pitch = pitch;
return packet;
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeVarInt(soundId);
writer.writeVarInt(soundCategory.ordinal());
writer.writeVarInt(AdventurePacketConvertor.getSoundSourceValue(soundSource));
writer.writeInt(x * 8);
writer.writeInt(y * 8);
writer.writeInt(z * 8);

View File

@ -1,12 +1,20 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.ComponentHolder;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class TabCompletePacket implements ServerPacket {
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.UnaryOperator;
public class TabCompletePacket implements ComponentHoldingServerPacket {
public int transactionId;
public int start;
@ -24,7 +32,7 @@ public class TabCompletePacket implements ServerPacket {
writer.writeSizedString(match.match);
writer.writeBoolean(match.hasTooltip);
if (match.hasTooltip)
writer.writeSizedString(match.tooltip.toString());
writer.writeComponent(match.tooltip);
}
}
@ -33,10 +41,66 @@ public class TabCompletePacket implements ServerPacket {
return ServerPacketIdentifier.TAB_COMPLETE;
}
public static class Match {
@Override
public @NotNull Collection<Component> components() {
if (matches == null || matches.length == 0) {
return Collections.emptyList();
} else {
List<Component> components = new ArrayList<>(matches.length);
for (Match match : matches) {
if (match.hasTooltip) {
components.add(match.tooltip);
}
}
return components;
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (matches == null || matches.length == 0) {
return this;
} else {
TabCompletePacket packet = new TabCompletePacket();
packet.transactionId = transactionId;
packet.start = start;
packet.length = length;
packet.matches = new Match[matches.length];
for (int i = 0; i < matches.length; i++) {
packet.matches[i] = matches[i].copyWithOperator(operator);
}
return packet;
}
}
public static class Match implements ComponentHolder<Match> {
public String match;
public boolean hasTooltip;
public JsonMessage tooltip; // Only text
public Component tooltip;
@Override
public @NotNull Collection<Component> components() {
if (hasTooltip) {
return Collections.singleton(tooltip);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull Match copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (hasTooltip) {
Match newMatch = new Match();
newMatch.match = match;
newMatch.hasTooltip = hasTooltip;
newMatch.tooltip = tooltip;
return newMatch;
} else {
return this;
}
}
}
}

View File

@ -1,15 +1,23 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.UnaryOperator;
/**
* The packet creates or updates teams
*/
public class TeamsPacket implements ServerPacket {
public class TeamsPacket implements ComponentHoldingServerPacket {
/**
* The registry name of the team
@ -23,7 +31,7 @@ public class TeamsPacket implements ServerPacket {
/**
* The display name for the team
*/
public JsonMessage teamDisplayName;
public Component teamDisplayName;
/**
* The friendly flags to
*/
@ -39,15 +47,15 @@ public class TeamsPacket implements ServerPacket {
/**
* The color of the team
*/
public int teamColor;
public NamedTextColor teamColor;
/**
* The prefix of the team
*/
public JsonMessage teamPrefix;
public Component teamPrefix;
/**
* The suffix of the team
*/
public JsonMessage teamSuffix;
public Component teamSuffix;
/**
* An array with all entities in the team
*/
@ -66,13 +74,13 @@ public class TeamsPacket implements ServerPacket {
switch (action) {
case CREATE_TEAM:
case UPDATE_TEAM_INFO:
writer.writeSizedString(this.teamDisplayName.toString());
writer.writeComponent(this.teamDisplayName);
writer.writeByte(this.friendlyFlags);
writer.writeSizedString(this.nameTagVisibility.getIdentifier());
writer.writeSizedString(this.collisionRule.getIdentifier());
writer.writeVarInt(this.teamColor);
writer.writeSizedString(this.teamPrefix.toString());
writer.writeSizedString(this.teamSuffix.toString());
writer.writeVarInt(AdventurePacketConvertor.getNamedTextColorValue(this.teamColor));
writer.writeComponent(this.teamPrefix);
writer.writeComponent(this.teamSuffix);
break;
case REMOVE_TEAM:
@ -99,6 +107,35 @@ public class TeamsPacket implements ServerPacket {
return ServerPacketIdentifier.TEAMS;
}
@Override
public @NotNull Collection<Component> components() {
if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) {
return List.of(teamDisplayName, teamPrefix, teamSuffix);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) {
TeamsPacket packet = new TeamsPacket();
packet.teamName = teamName;
packet.action = action;
packet.teamDisplayName = teamDisplayName == null ? null : operator.apply(teamDisplayName);
packet.friendlyFlags = friendlyFlags;
packet.nameTagVisibility = nameTagVisibility;
packet.collisionRule = collisionRule;
packet.teamColor = teamColor;
packet.teamPrefix = teamPrefix == null ? null : operator.apply(teamPrefix);
packet.teamSuffix = teamSuffix == null ? null : operator.apply(teamSuffix);
packet.entities = entities;
return packet;
} else {
return this;
}
}
/**
* An enumeration which representing all actions for the packet
*/

View File

@ -1,38 +1,81 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.chat.JsonMessage;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.TickUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
public class TitlePacket implements ServerPacket {
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.packet.server.play.TitlePacket.Action.*;
public class TitlePacket implements ComponentHoldingServerPacket {
public Action action;
public JsonMessage titleText; // Only text
public JsonMessage subtitleText; // Only text
public JsonMessage actionBarText; // Only text
public Component payload;
public int fadeIn;
public int stay;
public int fadeOut;
/**
* Constructs a new title packet from an action that can take a component argument.
*
* @param action the action
* @param payload the payload
* @throws IllegalArgumentException if the action is not {@link Action#SET_TITLE},
* {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
*/
public TitlePacket(@NotNull Action action, @NotNull Component payload) {
Validate.isTrue(action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR, "Invalid action type");
this.action = action;
this.payload = payload;
}
/**
* Constructs a new title packet from a clear or reset action.
*
* @param action the action
* @throws IllegalArgumentException if the action is not {@link Action#RESET},
* or {@link Action#HIDE}
*/
public TitlePacket(@NotNull Action action) {
this.action = action;
}
/**
* Constructs a new title packet for {@link Action#SET_TIMES_AND_DISPLAY}.
*
* @param fadeIn the fade in time
* @param stay the stay time
* @param fadeOut the fade out time
*/
public TitlePacket(int fadeIn, int stay, int fadeOut) {
this.action = SET_TIMES_AND_DISPLAY;
this.fadeIn = fadeIn;
this.stay = stay;
this.fadeOut = fadeOut;
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeVarInt(action.ordinal());
switch (action) {
case SET_TITLE:
writer.writeSizedString(titleText.toString());
break;
case SET_SUBTITLE:
writer.writeSizedString(subtitleText.toString());
break;
case SET_ACTION_BAR:
writer.writeSizedString(actionBarText.toString());
writer.writeComponent(payload);
break;
case SET_TIMES_AND_DISPLAY:
writer.writeInt(fadeIn);
@ -50,6 +93,24 @@ public class TitlePacket implements ServerPacket {
return ServerPacketIdentifier.TITLE;
}
@Override
public @NotNull Collection<Component> components() {
if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) {
return Collections.singleton(payload);
} else {
return Collections.emptyList();
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) {
return new TitlePacket(action, operator.apply(payload));
} else {
return this;
}
}
public enum Action {
SET_TITLE,
SET_SUBTITLE,
@ -59,4 +120,27 @@ public class TitlePacket implements ServerPacket {
RESET
}
/**
* Creates a collection of title packets from an Adventure title.
*
* @param title the title
* @return the packets
*/
public static Collection<TitlePacket> of(Title title) {
List<TitlePacket> packets = new ArrayList<>(4);
// base packets
packets.add(new TitlePacket(SET_TITLE, title.title()));
packets.add(new TitlePacket(SET_SUBTITLE, title.subtitle()));
// times packet
Title.Times times = title.times();
if (times != null) {
packets.add(new TitlePacket(TickUtils.fromDuration(times.fadeIn(), TickUtils.CLIENT_TICK_MS),
TickUtils.fromDuration(times.stay(), TickUtils.CLIENT_TICK_MS),
TickUtils.fromDuration(times.fadeOut(), TickUtils.CLIENT_TICK_MS)));
}
return packets;
}
}

Some files were not shown because too many files have changed in this diff Show More