feat: functional components, but at what cost

This commit is contained in:
mworzala 2024-04-13 10:03:36 -04:00
parent f1f1246230
commit c873d72f64
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
10 changed files with 73 additions and 44 deletions

View File

@ -34,6 +34,7 @@ import net.minestom.server.item.Material;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.Unit;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.world.DimensionType;
@ -114,6 +115,14 @@ public class PlayerInit {
.build();
player.getInventory().addItemStack(bundle);
try {
player.getInventory().addItemStack(ItemStack.builder(Material.STICK)
.set(ItemComponent.CREATIVE_SLOT_LOCK, Unit.INSTANCE)
.build());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (event.isFirstSpawn()) {
Notification notification = new Notification(
Component.text("Welcome!"),

View File

@ -36,12 +36,18 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
// DESERIALIZATION
private @NotNull Component deserializeAnyComponent(@NotNull BinaryTag nbt) {
if (nbt instanceof CompoundBinaryTag compound) {
return deserializeComponent(compound);
} else {
//todo raw string + list
throw new UnsupportedOperationException("Unknown NBT type: " + nbt.getClass().getName());
}
return switch (nbt) {
case CompoundBinaryTag compound -> deserializeComponent(compound);
case StringBinaryTag string -> Component.text(string.value());
case ListBinaryTag list -> {
var builder = Component.text();
for (var element : list) {
builder.append(deserializeAnyComponent(element));
}
yield builder.build();
}
default -> throw new UnsupportedOperationException("Unknown NBT type: " + nbt.getClass().getName());
};
}
private @NotNull Component deserializeComponent(@NotNull CompoundBinaryTag compound) {

View File

@ -744,6 +744,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
// Load the nearby chunks and queue them to be sent to them
ChunkUtils.forChunksInRange(spawnPosition, settings.getEffectiveViewDistance(), chunkAdder);
sendPendingChunks(); // Send available first chunk immediately to prevent falling through the floor
}
synchronizePositionAfterTeleport(spawnPosition, 0); // So the player doesn't get stuck

View File

@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component;
import net.minestom.server.color.Color;
import net.minestom.server.item.component.*;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.Unit;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import java.util.List;
@ -30,7 +31,7 @@ public final class ItemComponent {
public static final ItemComponentType<Void> HIDE_ADDITIONAL_TOOLTIP = declare("hide_additional_tooltip", NetworkBuffer.NOTHING, BinaryTagSerializer.NOTHING);
public static final ItemComponentType<Void> HIDE_TOOLTIP = declare("hide_tooltip", NetworkBuffer.NOTHING, BinaryTagSerializer.NOTHING);
public static final ItemComponentType<Integer> REPAIR_COST = declare("repair_cost", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT);
public static final ItemComponentType<Void> CREATIVE_SLOT_LOCK = declare("creative_slot_lock", NetworkBuffer.NOTHING, null);
public static final ItemComponentType<Unit> CREATIVE_SLOT_LOCK = declare("creative_slot_lock", NetworkBuffer.NOTHING_V2, null);
public static final ItemComponentType<Boolean> ENCHANTMENT_GLINT_OVERRIDE = declare("enchantment_glint_override", NetworkBuffer.BOOLEAN, BinaryTagSerializer.BOOLEAN);
public static final ItemComponentType<Void> INTANGIBLE_PROJECTILE = declare("intangible_projectile", null, BinaryTagSerializer.NOTHING);
public static final ItemComponentType<Void> FOOD = declare("food", null, null); //todo

View File

@ -40,7 +40,7 @@ record ItemComponentPatch(@NotNull Int2ObjectMap<Object> patch) {
//noinspection unchecked
ItemComponentType<Object> type = (ItemComponentType<Object>) ItemComponentType.fromId(entry.getIntKey());
assert type != null;
type.write(entry.getValue());
type.write(buffer, entry.getValue());
}
}
for (Int2ObjectMap.Entry<Object> entry : value.patch.int2ObjectEntrySet()) {

View File

@ -18,15 +18,21 @@ record ItemStackImpl(Material material, int amount, ItemComponentPatch component
static final NetworkBuffer.Type<ItemStack> NETWORK_TYPE = new NetworkBuffer.Type<>() {
@Override
public void write(@NotNull NetworkBuffer buffer, ItemStack value) {
buffer.write(NetworkBuffer.VAR_INT, value.material().id());
if (value.isAir()) {
buffer.write(NetworkBuffer.VAR_INT, 0);
return;
}
buffer.write(NetworkBuffer.VAR_INT, value.amount());
buffer.write(NetworkBuffer.VAR_INT, value.material().id());
buffer.write(ItemComponentPatch.NETWORK_TYPE, ((ItemStackImpl) value).components);
}
@Override
public ItemStack read(@NotNull NetworkBuffer buffer) {
Material material = Material.fromId(buffer.read(NetworkBuffer.VAR_INT));
int amount = buffer.read(NetworkBuffer.VAR_INT);
if (amount <= 0) return ItemStack.AIR;
Material material = Material.fromId(buffer.read(NetworkBuffer.VAR_INT));
ItemComponentPatch components = buffer.read(ItemComponentPatch.NETWORK_TYPE);
return new ItemStackImpl(material, amount, components);
}

View File

@ -13,6 +13,7 @@ import net.minestom.server.entity.metadata.other.PaintingMeta;
import net.minestom.server.network.packet.server.play.data.WorldPos;
import net.minestom.server.particle.Particle;
import net.minestom.server.utils.Direction;
import net.minestom.server.utils.Unit;
import net.minestom.server.utils.nbt.BinaryTagReader;
import net.minestom.server.utils.nbt.BinaryTagWriter;
import net.minestom.server.utils.validate.Check;
@ -29,7 +30,8 @@ import java.util.function.Function;
@ApiStatus.Experimental
public final class NetworkBuffer {
public static final Type<Void> NOTHING = new NetworkBufferTypeImpl.NothingType();
public static final Type<Void> NOTHING = new NetworkBufferTypeImpl.NothingType<>();
public static final Type<Unit> NOTHING_V2 = new NetworkBufferTypeImpl.NothingType<>();
public static final Type<Boolean> BOOLEAN = new NetworkBufferTypeImpl.BooleanType();
public static final Type<Byte> BYTE = new NetworkBufferTypeImpl.ByteType();
public static final Type<Short> SHORT = new NetworkBufferTypeImpl.ShortType();

View File

@ -25,13 +25,13 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
int SEGMENT_BITS = 0x7F;
int CONTINUE_BIT = 0x80;
record NothingType() implements NetworkBufferTypeImpl<Void> {
record NothingType<T>() implements NetworkBufferTypeImpl<T> {
@Override
public void write(@NotNull NetworkBuffer buffer, Void value) {
public void write(@NotNull NetworkBuffer buffer, T value) {
}
@Override
public Void read(@NotNull NetworkBuffer buffer) {
public T read(@NotNull NetworkBuffer buffer) {
return null;
}
}

View File

@ -11,7 +11,6 @@ import net.minestom.server.collision.Shape;
import net.minestom.server.entity.EntitySpawnType;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemComponentMap;
import net.minestom.server.item.ItemComponentType;
import net.minestom.server.item.Material;
@ -474,16 +473,18 @@ public final class Registry {
public static final class MaterialEntry implements Entry {
private final NamespaceID namespace;
private final Properties main;
private final int id;
private final String translationKey;
private final Supplier<Block> blockSupplier;
private final ItemComponentMap prototype;
private ItemComponentMap prototype;
private final EquipmentSlot equipmentSlot;
// private final EntityType entityType; //todo
private final Properties custom;
private MaterialEntry(String namespace, Properties main, Properties custom) {
this.main = main;
this.custom = custom;
this.namespace = NamespaceID.from(namespace);
this.id = main.getInt("id");
@ -492,34 +493,6 @@ public final class Registry {
final String blockNamespace = main.getString("correspondingBlock", null);
this.blockSupplier = blockNamespace != null ? () -> Block.fromNamespaceId(blockNamespace) : () -> null;
}
try {
try {
Class.forName(ItemComponent.class.getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
ItemComponentMap.Builder builder = ItemComponentMap.builder();
for (Map.Entry<String, Object> entry : main.section("components")) {
try {
//noinspection unchecked
ItemComponentType<Object> component = (ItemComponentType<Object>) ItemComponentType.fromNamespaceId(entry.getKey());
Check.notNull(component, "Unknown component: " + entry.getKey());
byte[] rawValue = Base64.getDecoder().decode((String) entry.getValue());
BinaryTagReader reader = new BinaryTagReader(new DataInputStream(new ByteArrayInputStream(rawValue)));
//todo remove this try/catch, just so i dont need to impl all comps yet
builder.set(component, component.read(reader.readNameless()));
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
this.prototype = builder.build();
} catch (IOException e) {
throw new RuntimeException("failed to parse material registry: " + namespace, e);
}
{
final Properties armorProperties = main.section("armorProperties");
if (armorProperties != null) {
@ -561,6 +534,30 @@ public final class Registry {
}
public @NotNull ItemComponentMap prototype() {
if (prototype == null) {
try {
ItemComponentMap.Builder builder = ItemComponentMap.builder();
for (Map.Entry<String, Object> entry : main.section("components")) {
try {
//noinspection unchecked
ItemComponentType<Object> component = (ItemComponentType<Object>) ItemComponentType.fromNamespaceId(entry.getKey());
Check.notNull(component, "Unknown component: " + entry.getKey());
byte[] rawValue = Base64.getDecoder().decode((String) entry.getValue());
BinaryTagReader reader = new BinaryTagReader(new DataInputStream(new ByteArrayInputStream(rawValue)));
//todo remove this try/catch, just so i dont need to impl all comps yet
builder.set(component, component.read(reader.readNameless()));
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
this.prototype = builder.build();
} catch (IOException e) {
throw new RuntimeException("failed to parse material registry: " + namespace, e);
}
}
return prototype;
}

View File

@ -0,0 +1,7 @@
package net.minestom.server.utils;
public final class Unit {
//todo would rather just support void
public static final Unit INSTANCE = new Unit();
}