Add particle data types (#2004)

* Add particle data types

* misc

* misc

* remove spaces

* defaults, tests, change what mattw said to

* RGBLike

* RGBLike

* RGBLike

* add back changes

* tests

* refactor

* annotations

* more fixes

* another test

* more tests + fix

* logging + checks consistency

* review changes

* review changes
This commit is contained in:
iam 2024-02-28 16:54:24 -05:00 committed by GitHub
parent aad7bdab0f
commit 5162a00b1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 689 additions and 57 deletions

View File

@ -8,6 +8,7 @@ import net.minestom.server.entity.metadata.animal.tameable.CatMeta;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.play.EntityMetaDataPacket;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.Direction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ -126,6 +127,10 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_QUATERNION, value, NetworkBuffer.QUATERNION);
}
public static Entry<Particle> Particle(@NotNull Particle particle) {
return new MetadataImpl.EntryImpl<>(TYPE_PARTICLE, particle, NetworkBuffer.PARTICLE);
}
public static final byte TYPE_BYTE = 0;
public static final byte TYPE_VARINT = 1;
public static final byte TYPE_LONG = 2;

View File

@ -3,6 +3,7 @@ package net.minestom.server.entity.metadata.other;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public class AreaEffectCloudMeta extends EntityMeta {
@ -37,12 +38,12 @@ public class AreaEffectCloudMeta extends EntityMeta {
super.metadata.setIndex(OFFSET + 2, Metadata.Boolean(value));
}
// public ParticleWrapper getParticle() {
// return super.metadata.getIndex((byte) 10, new ParticleWrapper(Particle.EFFECT, null));
// }
//
// public void setParticle(ParticleWrapper value) {
// super.metadata.setIndex((byte) 11, Metadata.Particle(value));
// }
public @NotNull Particle getParticle() {
return super.metadata.getIndex(OFFSET + 3, Particle.DUST);
}
public void setParticle(@NotNull Particle value) {
super.metadata.setIndex(OFFSET + 3, Metadata.Particle(value));
}
}

View File

@ -1,5 +1,13 @@
package net.minestom.server.network;
import java.util.BitSet;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
@ -8,6 +16,7 @@ import net.minestom.server.entity.metadata.animal.SnifferMeta;
import net.minestom.server.entity.metadata.animal.tameable.CatMeta;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.data.DeathLocation;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.Direction;
import net.minestom.server.utils.Either;
import net.minestom.server.utils.validate.Check;
@ -20,7 +29,6 @@ import org.jglrxavpok.hephaistos.nbt.NBTWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@ -69,6 +77,7 @@ public final class NetworkBuffer {
public static final Type<Point> VECTOR3 = NetworkBufferTypes.VECTOR3;
public static final Type<Point> VECTOR3D = NetworkBufferTypes.VECTOR3D;
public static final Type<float[]> QUATERNION = NetworkBufferTypes.QUATERNION;
public static final Type<Particle> PARTICLE = NetworkBufferTypes.PARTICLE;
ByteBuffer nioBuffer;
final boolean resizable;

View File

@ -12,6 +12,8 @@ import net.minestom.server.entity.metadata.animal.tameable.CatMeta;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.packet.server.play.data.DeathLocation;
import net.minestom.server.particle.Particle;
import net.minestom.server.particle.data.ParticleData;
import net.minestom.server.utils.Direction;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
@ -626,6 +628,17 @@ final class NetworkBufferTypes {
final float w = buffer.read(FLOAT);
return new float[]{x, y, z, w};
});
static final TypeImpl<Particle> PARTICLE = new TypeImpl<>(Particle.class,
(buffer, value) -> {
Check.stateCondition(value.data() != null && !value.data().validate(value.id()), "Particle data {0} is not valid for this particle type {1}", value.data(), value.namespace());
Check.stateCondition(value.data() == null && ParticleData.requiresData(value.id()), "Particle data is required for this particle type {0}", value.namespace());
buffer.write(VAR_INT, value.id());
if (value.data() != null) value.data().write(buffer);
return -1;
},
buffer -> null);
record TypeImpl<T>(@NotNull Class<T> type,
@NotNull TypeWriter<T> writer,

View File

@ -4,24 +4,53 @@ import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.particle.Particle;
import net.minestom.server.particle.data.ParticleData;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.*;
public record ParticlePacket(int particleId, boolean longDistance,
double x, double y, double z,
float offsetX, float offsetY, float offsetZ,
float particleData, int particleCount, byte[] data) implements ServerPacket {
public record ParticlePacket(int particleId, boolean longDistance, double x, double y, double z, float offsetX, float offsetY, float offsetZ, float maxSpeed, int particleCount, @Nullable ParticleData data) implements ServerPacket {
private ParticlePacket(ParticlePacket copy) {
this(copy.particleId, copy.longDistance, copy.x, copy.y, copy.z, copy.offsetX, copy.offsetY, copy.offsetZ, copy.maxSpeed, copy.particleCount, copy.data);
}
public ParticlePacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), reader.read(BOOLEAN),
reader.read(DOUBLE), reader.read(DOUBLE), reader.read(DOUBLE),
reader.read(FLOAT), reader.read(FLOAT), reader.read(FLOAT),
reader.read(FLOAT), reader.read(INT), reader.read(RAW_BYTES));
this(readPacket(reader));
}
public ParticlePacket(@NotNull Particle particle, boolean longDistance, double x, double y, double z, int offsetX, int offsetY, int offsetZ, int maxSpeed, int particleCount) {
this(particle.id(), longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, particle.data());
}
public ParticlePacket(@NotNull Particle particle, double x, double y, double z, int offsetX, int offsetY, int offsetZ, int maxSpeed, int particleCount) {
this(particle.id(), false, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, particle.data());
}
private static ParticlePacket readPacket(NetworkBuffer reader) {
int particleId = reader.read(VAR_INT);
Boolean longDistance = reader.read(BOOLEAN);
Double x = reader.read(DOUBLE);
Double y = reader.read(DOUBLE);
Double z = reader.read(DOUBLE);
Float offsetX = reader.read(FLOAT);
Float offsetY = reader.read(FLOAT);
Float offsetZ = reader.read(FLOAT);
Float maxSpeed = reader.read(FLOAT);
Integer particleCount = reader.read(INT);
ParticleData data = ParticleData.read(particleId, reader);
return new ParticlePacket(particleId, longDistance, x, y, z, offsetX, offsetY, offsetZ, maxSpeed, particleCount, data);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
Check.stateCondition(data != null && !data.validate(particleId), "Particle data {0} is not valid for this particle type {1}", data, Particle.fromId(particleId));
Check.stateCondition(data == null && ParticleData.requiresData(particleId), "Particle data is required for this particle type {0}", Particle.fromId(particleId));
writer.write(VAR_INT, particleId);
writer.write(BOOLEAN, longDistance);
writer.write(DOUBLE, x);
@ -30,10 +59,10 @@ public record ParticlePacket(int particleId, boolean longDistance,
writer.write(FLOAT, offsetX);
writer.write(FLOAT, offsetY);
writer.write(FLOAT, offsetZ);
writer.write(FLOAT, particleData);
writer.write(FLOAT, maxSpeed);
writer.write(INT, particleCount);
writer.write(RAW_BYTES, data);
if (data != null) data.write(writer);
}
@Override

View File

@ -1,5 +1,6 @@
package net.minestom.server.particle;
import net.minestom.server.particle.data.ParticleData;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
@ -24,4 +25,7 @@ public sealed interface Particle extends StaticProtocolObject, Particles permits
static @Nullable Particle fromId(int id) {
return ParticleImpl.getId(id);
}
@NotNull Particle withData(@Nullable ParticleData data);
@Nullable ParticleData data();
}

View File

@ -1,36 +0,0 @@
package net.minestom.server.particle;
import net.minestom.server.network.packet.server.play.ParticlePacket;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
/**
* Small utils class to create particle packet
*/
public class ParticleCreator {
public static ParticlePacket createParticlePacket(Particle particleType, boolean distance,
double x, double y, double z,
float offsetX, float offsetY, float offsetZ,
float particleData, int count, @Nullable Consumer<BinaryWriter> dataWriter) {
byte[] data;
if (dataWriter != null) {
BinaryWriter writer = new BinaryWriter();
dataWriter.accept(writer);
data = writer.toByteArray();
} else {
data = new byte[0];
}
return new ParticlePacket(particleType.id(), distance, x, y, z, offsetX, offsetY, offsetZ, particleData, count, data);
}
public static ParticlePacket createParticlePacket(Particle particleType,
double x, double y, double z,
float offsetX, float offsetY, float offsetZ,
int count) {
return createParticlePacket(particleType, true, x, y, z,
offsetX, offsetY, offsetZ, 0, count, null);
}
}

View File

@ -1,14 +1,16 @@
package net.minestom.server.particle;
import net.minestom.server.particle.data.ParticleData;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
record ParticleImpl(NamespaceID namespace, int id) implements Particle {
record ParticleImpl(NamespaceID namespace, int id, ParticleData data) implements Particle {
private static final Registry.Container<Particle> CONTAINER = Registry.createStaticContainer(Registry.Resource.PARTICLES,
(namespace, properties) -> new ParticleImpl(NamespaceID.from(namespace), properties.getInt("id")));
(namespace, properties) -> new ParticleImpl(NamespaceID.from(namespace), properties.getInt("id"), ParticleData.defaultData(namespace)));
static Particle get(@NotNull String namespace) {
return CONTAINER.get(namespace);
@ -26,8 +28,17 @@ record ParticleImpl(NamespaceID namespace, int id) implements Particle {
return CONTAINER.values();
}
public @NotNull Particle withData(@Nullable ParticleData object) {
return new ParticleImpl(namespace, id, object);
}
@Override
public String toString() {
public @Nullable ParticleData data() {
return data;
}
@Override
public @NotNull String toString() {
return name();
}
}

View File

@ -0,0 +1,34 @@
package net.minestom.server.particle.data;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
public record BlockMarkerParticleData(@NotNull Block block) implements ParticleData {
BlockMarkerParticleData(NetworkBuffer reader) {
this(read(reader));
}
BlockMarkerParticleData() {
this(Block.STONE);
}
private static Block read(NetworkBuffer reader) {
short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue();
Block block = Block.fromStateId(blockState);
Check.stateCondition(block == null, "Block state " + blockState + " is invalid");
return block;
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, (int) block.stateId());
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.BLOCK_MARKER.id();
}
}

View File

@ -0,0 +1,34 @@
package net.minestom.server.particle.data;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
public record BlockParticleData(Block block) implements ParticleData {
BlockParticleData(NetworkBuffer reader) {
this(read(reader));
}
BlockParticleData() {
this(Block.STONE);
}
private static Block read(NetworkBuffer reader) {
short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue();
Block block = Block.fromStateId(blockState);
Check.stateCondition(block == null, "Block state " + blockState + " is invalid");
return block;
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, (int) block.stateId());
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.BLOCK.id();
}
}

View File

@ -0,0 +1,46 @@
package net.minestom.server.particle.data;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.color.Color;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
public record DustColorTransitionParticleData(@NotNull RGBLike from, float scale, @NotNull RGBLike to) implements ParticleData {
public DustColorTransitionParticleData {
Check.argCondition(scale < 0.01 || scale > 4, "scale must be between 0.01 and 4: was {0}", scale);
}
DustColorTransitionParticleData() {
this(new Color(255, 255, 255), 1, new Color(255, 255, 255));
}
DustColorTransitionParticleData(NetworkBuffer buffer) {
this(new Color(
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255)
), buffer.read(NetworkBuffer.FLOAT), new Color(
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255)
));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.FLOAT, from.red() / 255f);
writer.write(NetworkBuffer.FLOAT, from.green() / 255f);
writer.write(NetworkBuffer.FLOAT, from.blue() / 255f);
writer.write(NetworkBuffer.FLOAT, scale);
writer.write(NetworkBuffer.FLOAT, to.red() / 255f);
writer.write(NetworkBuffer.FLOAT, to.green() / 255f);
writer.write(NetworkBuffer.FLOAT, to.blue() / 255f);
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.DUST_COLOR_TRANSITION.id();
}
}

View File

@ -0,0 +1,39 @@
package net.minestom.server.particle.data;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.color.Color;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
public record DustParticleData(@NotNull RGBLike color, float scale) implements ParticleData {
public DustParticleData {
Check.argCondition(scale < 0.01 || scale > 4, "scale must be between 0.01 and 4");
}
DustParticleData(NetworkBuffer buffer) {
this(new Color(
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255),
(int) (buffer.read(NetworkBuffer.FLOAT) * 255)
), buffer.read(NetworkBuffer.FLOAT));
}
DustParticleData() {
this(new Color(255, 255, 255), 1);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.FLOAT, color.red() / 255f);
writer.write(NetworkBuffer.FLOAT, color.green() / 255f);
writer.write(NetworkBuffer.FLOAT, color.blue() / 255f);
writer.write(NetworkBuffer.FLOAT, scale);
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.DUST.id();
}
}

View File

@ -0,0 +1,34 @@
package net.minestom.server.particle.data;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
public record FallingDustParticleData(Block block) implements ParticleData {
FallingDustParticleData(NetworkBuffer reader) {
this(read(reader));
}
FallingDustParticleData() {
this(Block.STONE);
}
private static Block read(NetworkBuffer reader) {
short blockState = reader.read(NetworkBuffer.VAR_INT).shortValue();
Block block = Block.fromStateId(blockState);
Check.stateCondition(block == null, "Block state {0} is invalid", blockState);
return block;
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, (int) block.stateId());
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.FALLING_DUST.id();
}
}

View File

@ -0,0 +1,27 @@
package net.minestom.server.particle.data;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public record ItemParticleData(ItemStack item) implements ParticleData {
ItemParticleData(NetworkBuffer reader) {
this(reader.read(NetworkBuffer.ITEM));
}
ItemParticleData() {
this(ItemStack.of(Material.STONE));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.ITEM, item);
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.ITEM.id();
}
}

View File

@ -0,0 +1,51 @@
package net.minestom.server.particle.data;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public interface ParticleData {
void write(@NotNull NetworkBuffer writer);
static ParticleData read(int particleId, NetworkBuffer reader) {
if (particleId == Particle.BLOCK.id()) return new BlockParticleData(reader);
else if (particleId == Particle.BLOCK_MARKER.id()) return new BlockMarkerParticleData(reader);
else if (particleId == Particle.DUST.id()) return new DustParticleData(reader);
else if (particleId == Particle.DUST_COLOR_TRANSITION.id()) return new DustColorTransitionParticleData(reader);
else if (particleId == Particle.FALLING_DUST.id()) return new FallingDustParticleData(reader);
else if (particleId == Particle.SCULK_CHARGE.id()) return new SculkChargeParticleData(reader);
else if (particleId == Particle.ITEM.id()) return new ItemParticleData(reader);
else if (particleId == Particle.VIBRATION.id()) return new VibrationParticleData(reader);
else if (particleId == Particle.SHRIEK.id()) return new ShriekParticleData(reader);
else return null;
}
boolean validate(int particleId);
static boolean requiresData(int particleId) {
return particleId == Particle.BLOCK.id()
|| particleId == Particle.BLOCK_MARKER.id()
|| particleId == Particle.DUST.id()
|| particleId == Particle.DUST_COLOR_TRANSITION.id()
|| particleId == Particle.FALLING_DUST.id()
|| particleId == Particle.SCULK_CHARGE.id()
|| particleId == Particle.ITEM.id()
|| particleId == Particle.VIBRATION.id()
|| particleId == Particle.SHRIEK.id();
}
static ParticleData defaultData(String id) {
return switch (id) {
case "minecraft:block" -> new BlockParticleData();
case "minecraft:block_marker" -> new BlockMarkerParticleData();
case "minecraft:dust" -> new DustParticleData();
case "minecraft:dust_color_transition" -> new DustColorTransitionParticleData();
case "minecraft:falling_dust" -> new FallingDustParticleData();
case "minecraft:sculk_charge" -> new SculkChargeParticleData();
case "minecraft:item" -> new ItemParticleData();
case "minecraft:vibration" -> new VibrationParticleData();
case "minecraft:shriek" -> new ShriekParticleData();
default -> null;
};
}
}

View File

@ -0,0 +1,25 @@
package net.minestom.server.particle.data;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public record SculkChargeParticleData(float roll) implements ParticleData {
SculkChargeParticleData(NetworkBuffer reader) {
this(reader.read(NetworkBuffer.FLOAT));
}
SculkChargeParticleData() {
this(0);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.FLOAT, roll);
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.SCULK_CHARGE.id();
}
}

View File

@ -0,0 +1,25 @@
package net.minestom.server.particle.data;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public record ShriekParticleData(int delay) implements ParticleData {
ShriekParticleData(NetworkBuffer reader) {
this(reader.read(NetworkBuffer.VAR_INT));
}
ShriekParticleData() {
this(0);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.VAR_INT, delay);
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.SHRIEK.id();
}
}

View File

@ -0,0 +1,55 @@
package net.minestom.server.particle.data;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
public record VibrationParticleData(@NotNull VibrationSource type, @NotNull Point source, int entityId, float entityEyeHeight, int ticks) implements ParticleData {
public enum VibrationSource {
BLOCK,
ENTITY
}
VibrationParticleData(NetworkBuffer buffer) {
this(read(buffer));
}
VibrationParticleData() {
this(VibrationSource.BLOCK, Vec.ZERO, 0, 0, 0);
}
private VibrationParticleData(VibrationParticleData copy) {
this(copy.type, copy.source, copy.entityId, copy.entityEyeHeight, copy.ticks);
}
private static VibrationParticleData read(NetworkBuffer buffer) {
VibrationSource type = buffer.readEnum(VibrationSource.class);
if (type == VibrationSource.BLOCK) {
return new VibrationParticleData(type, buffer.read(NetworkBuffer.BLOCK_POSITION), 0, 0, buffer.read(NetworkBuffer.VAR_INT));
} else {
return new VibrationParticleData(type, Vec.ZERO, buffer.read(NetworkBuffer.VAR_INT), buffer.read(NetworkBuffer.FLOAT), buffer.read(NetworkBuffer.VAR_INT));
}
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeEnum(VibrationSource.class, type);
if (type == VibrationSource.BLOCK) {
writer.write(NetworkBuffer.BLOCK_POSITION, source);
writer.write(NetworkBuffer.VAR_INT, ticks);
} else {
writer.write(NetworkBuffer.VAR_INT, entityId);
writer.write(NetworkBuffer.FLOAT, entityEyeHeight);
writer.write(NetworkBuffer.VAR_INT, ticks);
}
}
@Override
public boolean validate(int particleId) {
return particleId == Particle.VIBRATION.id();
}
}

View File

@ -0,0 +1,164 @@
package net.minestom.server.entity;
import net.minestom.server.color.Color;
import net.minestom.server.entity.metadata.other.AreaEffectCloudMeta;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.particle.Particle;
import net.minestom.server.particle.data.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AreaEffectCloudTest {
@Test
public void createWithDustParticle() {
int colour = 0x5505FF01;
int b = (colour & 0x000000FF);
int g = (colour & 0x0000FF00) >> 8;
int r = (colour & 0x00FF0000) >> 16;
float size = 0.1f;
Particle particle = Particle.DUST.withData(new DustParticleData(new Color(r, g, b), size));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
DustParticleData gotData = (DustParticleData) gotParticle.data();
assertNotNull(gotData);
assert gotData.color().red() == r;
assert gotData.color().green() == g;
assert gotData.color().blue() == b;
assert gotData.scale() == size;
}
@Test
public void createWithDustTransition() {
int colour = 0xFF05FF01;
int colourAfter = 0xFF05FF01;
int b = (colour & 0x000000FF);
int g = (colour & 0x0000FF00) >> 8;
int r = (colour & 0x00FF0000) >> 16;
int b2 = (colourAfter & 0x000000FF);
int g2 = (colourAfter & 0x0000FF00) >> 8;
int r2 = (colourAfter & 0x00FF0000) >> 16;
float size = 0.1f;
Particle particle = Particle.DUST_COLOR_TRANSITION.withData(new DustColorTransitionParticleData(new Color(r, g, b), size, new Color(r2, g2, b2)));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
DustColorTransitionParticleData gotData = (DustColorTransitionParticleData) gotParticle.data();
assertNotNull(gotData);
assert gotData.from().red() == r;
assert gotData.from().green() == g;
assert gotData.from().blue() == b;
assert gotData.scale() == size;
assert gotData.to().red() == r2;
assert gotData.to().green() == g2;
assert gotData.to().blue() == b2;
}
@Test
public void createWithBlockParticle() {
Block block = Block.GRASS_BLOCK;
Particle particle = Particle.BLOCK.withData(new BlockParticleData(block));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
BlockParticleData gotBlock = (BlockParticleData) gotParticle.data();
assert gotBlock.block() == block;
}
@Test
public void createWithBlockMarkerParticle() {
Block block = Block.GRASS_BLOCK;
Particle particle = Particle.BLOCK_MARKER.withData(new BlockMarkerParticleData(block));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
BlockMarkerParticleData gotBlock = (BlockMarkerParticleData) gotParticle.data();
assert gotBlock.block() == block;
}
@Test
public void createWithItemParticle() {
Particle particle = Particle.ITEM.withData(new ItemParticleData(ItemStack.of(Material.ACACIA_LOG)));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
ItemParticleData gotBlock = (ItemParticleData) gotParticle.data();
assert gotBlock.item().material() == Material.ACACIA_LOG;
}
@Test
public void createWithSculkChargeParticle() {
Particle particle = Particle.SCULK_CHARGE.withData(new SculkChargeParticleData(3));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
SculkChargeParticleData gotBlock = (SculkChargeParticleData) gotParticle.data();
assert gotBlock.roll() == 3;
}
@Test
public void createWithDustParticleIncorrectType() {
Particle particle = Particle.DUST.withData(new FallingDustParticleData(Block.GLOWSTONE));
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
assertThrows(IllegalStateException.class, () -> entity.getMetadataPacket().write(new NetworkBuffer()));
}
@Test
public void createWithComposterParticle() {
Particle particle = Particle.COMPOSTER;
Entity entity = new Entity(EntityTypes.AREA_EFFECT_CLOUD);
AreaEffectCloudMeta meta = (AreaEffectCloudMeta) entity.getEntityMeta();
meta.setParticle(particle);
var gotParticle = meta.getParticle();
assert gotParticle == particle;
ParticleData gotBlock = gotParticle.data();
assertNull(gotBlock);
}
}

View File

@ -0,0 +1,62 @@
package net.minestom.server.particle;
import net.minestom.server.color.Color;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.play.ParticlePacket;
import net.minestom.server.particle.data.BlockParticleData;
import net.minestom.server.particle.data.DustParticleData;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ParticleDataTest {
@Test
public void testDustParticleDefault() {
Particle particle = Particle.DUST;
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertDoesNotThrow(() -> packet.write(new NetworkBuffer()));
}
@Test
public void testDustParticleInvalid() {
var particle = Particle.DUST.withData(null);
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertThrows(IllegalStateException.class, () -> packet.write(new NetworkBuffer()));
}
@Test
public void testDustParticleWrongData() {
var particle = Particle.DUST.withData(new BlockParticleData(Block.STONE));
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertThrows(IllegalStateException.class, () -> packet.write(new NetworkBuffer()));
}
@Test
public void testDustParticleWrongParameters() {
assertThrows(IllegalArgumentException.class, () -> Particle.DUST.withData(new DustParticleData(new Color(255, 255, 255), 0)));
}
@Test
public void testParticleValid() {
var particle = Particle.AMBIENT_ENTITY_EFFECT;
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertDoesNotThrow(() -> packet.write(new NetworkBuffer()));
}
@Test
public void testParticleData() {
var particle = Particle.AMBIENT_ENTITY_EFFECT;
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertDoesNotThrow(() -> packet.write(new NetworkBuffer()));
}
@Test
public void invalidBlock() {
var particle = Particle.BLOCK.withData(new BlockParticleData(null));
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertThrows(NullPointerException.class, () -> packet.write(new NetworkBuffer()));
}
}