Merge branch 'master' into new-block-api

# Conflicts:
#	src/main/java/net/minestom/server/instance/Chunk.java
#	src/main/java/net/minestom/server/instance/Instance.java
This commit is contained in:
TheMode 2021-06-26 20:09:47 +02:00
commit 4f4afbbe6d
10 changed files with 154 additions and 34 deletions

View File

@ -14,6 +14,7 @@ import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.entity.Player;
import net.minestom.server.message.ChatPosition;
import net.minestom.server.message.Messenger;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
@ -44,6 +45,14 @@ public interface PacketGroupingAudience extends ForwardingAudience {
*/
@NotNull Collection<Player> getPlayers();
/**
* Broadcast a ServerPacket to all players of this audience
* @param packet the packet to broadcast
*/
default void sendGroupedPacket(ServerPacket packet) {
PacketUtils.sendGroupedPacket(this.getPlayers(), packet);
}
@Override
default void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
Messenger.sendMessage(this.getPlayers(), message, ChatPosition.fromMessageType(type), source.uuid());
@ -51,28 +60,28 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void sendActionBar(@NotNull Component message) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ActionBarPacket(message));
sendGroupedPacket(new ActionBarPacket(message));
}
@Override
default void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new PlayerListHeaderAndFooterPacket(header, footer));
sendGroupedPacket(new PlayerListHeaderAndFooterPacket(header, footer));
}
@Override
default void showTitle(@NotNull Title title) {
PacketUtils.sendGroupedPacket(this.getPlayers(), new SetTitleTextPacket(title.title()));
PacketUtils.sendGroupedPacket(this.getPlayers(), new SetTitleSubTitlePacket(title.subtitle()));
sendGroupedPacket(new SetTitleTextPacket(title.title()));
sendGroupedPacket(new SetTitleSubTitlePacket(title.subtitle()));
}
@Override
default void clearTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ClearTitlesPacket());
sendGroupedPacket(new ClearTitlesPacket());
}
@Override
default void resetTitle() {
PacketUtils.sendGroupedPacket(this.getPlayers(), new ClearTitlesPacket(true));
sendGroupedPacket(new ClearTitlesPacket(true));
}
@Override
@ -87,13 +96,13 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void playSound(@NotNull Sound sound, double x, double y, double z) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
sendGroupedPacket(AdventurePacketConvertor.createSoundPacket(sound, x, y, z));
}
@Override
default void playSound(@NotNull Sound sound, Sound.@NotNull Emitter emitter) {
if (emitter != Sound.Emitter.self()) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundPacket(sound, emitter));
sendGroupedPacket(AdventurePacketConvertor.createSoundPacket(sound, emitter));
} else {
// if we're playing on self, we need to delegate to each audience member
for (Audience audience : this.audiences()) {
@ -104,7 +113,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
@Override
default void stopSound(@NotNull SoundStop stop) {
PacketUtils.sendGroupedPacket(this.getPlayers(), AdventurePacketConvertor.createSoundStopPacket(stop));
sendGroupedPacket(AdventurePacketConvertor.createSoundStopPacket(stop));
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.attribute;
import io.netty.util.internal.ThreadLocalRandom;
import net.minestom.server.utils.UniqueIdUtils;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* Represent an attribute modifier.

View File

@ -19,9 +19,10 @@ public interface DataContainer {
* meaning that this will be null if no data has been defined.
*
* @return the {@link Data} of this container, can be null
* @deprecated use the tag API https://wiki.minestom.com/feature/tags
*/
@Nullable
Data getData();
@Deprecated
@Nullable Data getData();
/**
* Sets the {@link Data} of this container.
@ -30,7 +31,8 @@ public interface DataContainer {
* on your use-case.
*
* @param data the new {@link Data} of this container, null to remove it
* @deprecated use the tag API https://wiki.minestom.com/feature/tags
*/
@Deprecated
void setData(@Nullable Data data);
}

View File

@ -8,8 +8,11 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
/**
* Represents a type of damage, required when calling {@link LivingEntity#damage(DamageType, float)}
@ -19,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
* Be aware that this class implements {@link DataContainer}
* so you can add your own data to an already existing damage type without any wrapper.
*/
public class DamageType implements DataContainer {
public class DamageType implements TagHandler, DataContainer {
public static final DamageType VOID = new DamageType("attack.outOfWorld");
public static final DamageType GRAVITY = new DamageType("attack.fall");
@ -30,6 +33,8 @@ public class DamageType implements DataContainer {
}
};
private final String identifier;
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
/**
@ -159,4 +164,18 @@ public class DamageType implements DataContainer {
public void setData(Data data) {
this.data = data;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
}

View File

@ -16,11 +16,15 @@ import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.chunk.ChunkSupplier;
import net.minestom.server.world.biomes.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -38,7 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
* You generally want to avoid storing references of this object as this could lead to a huge memory leak,
* you should store the chunk coordinates instead.
*/
public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Tickable, DataContainer {
public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Tickable, TagHandler, DataContainer {
public static final int CHUNK_SIZE_X = 16;
public static final int CHUNK_SIZE_Z = 16;
@ -63,6 +67,7 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
protected PFColumnarSpace columnarSpace;
// Data
private final NBTCompound nbt = new NBTCompound();
protected Data data;
public Chunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ, boolean shouldGenerate) {
@ -367,6 +372,16 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
return unmodifiableViewers;
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
return tag.read(nbt);
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbt, value);
}
@Nullable
@Override
public Data getData() {

View File

@ -26,6 +26,8 @@ import net.minestom.server.instance.block.*;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.PacketUtils;
@ -41,6 +43,7 @@ import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -58,7 +61,7 @@ import java.util.function.Consumer;
* you need to be sure to signal the {@link UpdateManager} of the changes using
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
*/
public abstract class Instance implements BlockGetter, BlockSetter, Tickable, EventHandler<InstanceEvent>, DataContainer, PacketGroupingAudience {
public abstract class Instance implements BlockGetter, BlockSetter, Tickable,TagHandler, EventHandler<InstanceEvent>, DataContainer, PacketGroupingAudience {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
@ -99,6 +102,8 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ev
protected final Queue<Consumer<Instance>> nextTick = new ConcurrentLinkedQueue<>();
// instance custom data
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
// the explosion supplier
@ -867,6 +872,20 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ev
this.worldBorder.update();
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
/**
* Creates an explosion at the given position with the given strength.
* The algorithm used to compute damages is provided by {@link #getExplosionSupplier()}.

View File

@ -5,10 +5,13 @@ import net.minestom.server.data.DataContainer;
import net.minestom.server.inventory.click.InventoryClickProcessor;
import net.minestom.server.inventory.condition.InventoryCondition;
import net.minestom.server.item.ItemStack;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.ArrayList;
import java.util.Arrays;
@ -19,7 +22,7 @@ import java.util.function.UnaryOperator;
/**
* Represents an inventory where items can be modified/retrieved.
*/
public abstract class AbstractInventory implements InventoryClickHandler, DataContainer {
public abstract class AbstractInventory implements InventoryClickHandler, TagHandler, DataContainer {
private final int size;
protected final ItemStack[] itemStacks;
@ -29,6 +32,8 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
// the click processor which process all the clicks in the inventory
protected final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
private final Object nbtLock = new Object();
private final NBTCompound nbt = new NBTCompound();
private Data data;
protected AbstractInventory(int size) {
@ -210,6 +215,20 @@ public abstract class AbstractInventory implements InventoryClickHandler, DataCo
}
}
@Override
public <T> @Nullable T getTag(@NotNull Tag<T> tag) {
synchronized (nbtLock) {
return tag.read(nbt);
}
}
@Override
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
synchronized (nbtLock) {
tag.write(nbt, value);
}
}
@Override
public @Nullable Data getData() {
return data;

View File

@ -164,12 +164,7 @@ public class ItemMeta implements TagReadable, Writeable {
@Deprecated
@Contract(pure = true)
public <T> T getOrDefault(@NotNull Tag<T> tag, @Nullable T defaultValue) {
var key = tag.getKey();
if (nbt.containsKey(key)) {
return tag.read(toNBT());
} else {
return defaultValue;
}
return tag.defaultValue(defaultValue).read(toNBT());
}
/**

View File

@ -13,7 +13,8 @@ public class ExplosionPacket implements ServerPacket {
public byte[] records = new byte[0];
public float playerMotionX, playerMotionY, playerMotionZ;
public ExplosionPacket() {}
public ExplosionPacket() {
}
@Override
public void write(@NotNull BinaryWriter writer) {
@ -21,7 +22,7 @@ public class ExplosionPacket implements ServerPacket {
writer.writeFloat(y);
writer.writeFloat(z);
writer.writeFloat(radius);
writer.writeInt(records.length/3); // each record is 3 bytes long
writer.writeVarInt(records.length / 3); // each record is 3 bytes long
for (byte record : records)
writer.writeByte(record);
writer.writeFloat(playerMotionX);

View File

@ -6,7 +6,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.nbt.SNBTParser;
import java.io.StringReader;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -21,7 +25,36 @@ import java.util.function.Supplier;
@ApiStatus.NonExtendable
public class Tag<T> {
private static final String EMPTY_KEY = "";
/**
* Handles the snbt of the tag holder.
* <p>
* Writing will override all tags. Proceed with caution.
*/
@ApiStatus.Experimental
public static final Tag<String> SNBT = new Tag<>(null, NBTCompound::toSNBT, (original, snbt) -> {
try {
final var updated = new SNBTParser(new StringReader(snbt)).parse();
if (!(updated instanceof NBTCompound))
throw new IllegalArgumentException("'" + snbt + "' is not a compound!");
NBTCompound updatedCompound = (NBTCompound) updated;
original.clear();
updatedCompound.getKeys().forEach(s ->
original.set(s, Objects.requireNonNull(updatedCompound.get(s))));
} catch (NBTException e) {
e.printStackTrace();
}
}, null);
/**
* Handles the complete tag holder compound.
* <p>
* Writing will override all tags. Proceed with caution.
*/
@ApiStatus.Experimental
public static final Tag<NBTCompound> NBT = new Tag<>(null, NBTCompound::deepClone, (original, updated) -> {
original.clear();
updated.getKeys().forEach(s -> original.set(s, Objects.requireNonNull(updated.get(s))));
}, null);
private final String key;
private final Function<NBTCompound, T> readFunction;
@ -29,23 +62,31 @@ public class Tag<T> {
private final Supplier<T> defaultValue;
protected Tag(@NotNull String key,
protected Tag(@Nullable String key,
@NotNull Function<NBTCompound, T> readFunction,
@NotNull BiConsumer<NBTCompound, T> writeConsumer,
@Nullable BiConsumer<NBTCompound, T> writeConsumer,
@Nullable Supplier<T> defaultValue) {
this.key = key;
this.readFunction = readFunction;
this.writeConsumer = writeConsumer;
this.writeConsumer = Objects.requireNonNullElse(writeConsumer, (compound, t) -> {
});
this.defaultValue = defaultValue;
}
protected Tag(@NotNull String key,
protected Tag(@Nullable String key,
@NotNull Function<NBTCompound, T> readFunction,
@NotNull BiConsumer<NBTCompound, T> writeConsumer) {
@Nullable BiConsumer<NBTCompound, T> writeConsumer) {
this(key, readFunction, writeConsumer, null);
}
public @NotNull String getKey() {
/**
* Returns the key used to navigate inside the holder nbt.
* <p>
* Can be null if unused (e.g. {@link #View(TagSerializer)}, {@link #SNBT} and {@link #NBT}).
*
* @return the tag key
*/
public @Nullable String getKey() {
return key;
}
@ -96,7 +137,7 @@ public class Tag<T> {
}
public void write(@NotNull NBTCompound nbtCompound, @Nullable T value) {
if (value != null || key.equals(EMPTY_KEY)) {
if (key == null || value != null) {
this.writeConsumer.accept(nbtCompound, value);
} else {
nbtCompound.removeTag(key);
@ -208,7 +249,7 @@ public class Tag<T> {
}
public static <T> @NotNull Tag<T> View(@NotNull TagSerializer<T> serializer) {
return new Tag<>(EMPTY_KEY,
return new Tag<>(null,
nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)),
(nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value));
}