3
0
Mirror von https://github.com/Moulberry/AxiomPaperPlugin.git synchronisiert 2024-11-08 17:40:04 +01:00

WorldProperties: Rewrite API

Dieser Commit ist enthalten in:
Moulberry 2023-11-16 19:20:28 +08:00
Ursprung 1ce77ea5e0
Commit 89e04ffb39
11 geänderte Dateien mit 220 neuen und 149 gelöschten Zeilen

Datei anzeigen

@ -8,7 +8,7 @@ plugins {
}
group = "com.moulberry.axiom"
version = "1.5.2"
version = "1.5.4"
description = "Serverside component for Axiom on Paper"
java {

Datei anzeigen

@ -5,7 +5,6 @@ import com.moulberry.axiom.event.AxiomCreateWorldPropertiesEvent;
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.packet.*;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.papermc.paper.event.player.PlayerFailMoveEvent;
@ -19,11 +18,9 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import org.bukkit.*;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -166,7 +163,11 @@ public class AxiomPaper extends JavaPlugin implements Listener {
private final WeakHashMap<World, ServerWorldPropertiesRegistry> worldProperties = new WeakHashMap<>();
public @Nullable ServerWorldPropertiesRegistry getWorldProperties(World world) {
public @Nullable ServerWorldPropertiesRegistry getWorldPropertiesIfPresent(World world) {
return worldProperties.get(world);
}
public @Nullable ServerWorldPropertiesRegistry getOrCreateWorldProperties(World world) {
if (worldProperties.containsKey(world)) {
return worldProperties.get(world);
} else {
@ -207,7 +208,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
public void onChangedWorld(PlayerChangedWorldEvent event) {
World world = event.getPlayer().getWorld();
ServerWorldPropertiesRegistry properties = getWorldProperties(world);
ServerWorldPropertiesRegistry properties = getOrCreateWorldProperties(world);
if (properties == null) {
event.getPlayer().sendPluginMessage(this, "axiom:register_world_properties", new byte[]{0});
@ -219,13 +220,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
@EventHandler
public void onGameRuleChanged(WorldGameRuleChangeEvent event) {
if (event.getGameRule() == GameRule.DO_WEATHER_CYCLE) {
ServerWorldPropertiesRegistry properties = getWorldProperties(event.getWorld());
if (properties != null) {
ServerWorldProperty<?> property = properties.getById(new ResourceLocation("axiom:pause_weather"));
if (property != null) {
((ServerWorldProperty<Boolean>)property).setValue(event.getWorld(), !Boolean.parseBoolean(event.getValue()));
}
}
ServerWorldPropertiesRegistry.PAUSE_WEATHER.setValue(event.getWorld(), !Boolean.parseBoolean(event.getValue()));
}
}

Datei anzeigen

@ -5,9 +5,7 @@ import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import net.kyori.adventure.text.Component;
import net.minecraft.util.Unit;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -15,39 +13,53 @@ import java.util.List;
public class WorldPropertiesExample implements Listener {
private static final ServerWorldProperty<Boolean> CHECKBOX = new ServerWorldProperty<>(
new NamespacedKey("axiom", "checkbox"),
"Checkbox",
false, WorldPropertyWidgetType.CHECKBOX, world -> false,
(player, world, bool) -> {
world.sendMessage(Component.text("Checkbox: " + bool)); // Do something with input
return true; // true to sync with client
}
);
private static final ServerWorldProperty<Integer> SLIDER = new ServerWorldProperty<>(
new NamespacedKey("axiom", "slider"),
"Slider",
false, new WorldPropertyWidgetType.Slider(0, 8),
world -> 4,
(player, world, integer) -> {
world.sendMessage(Component.text("Slider: " + integer)); // Do something with input
return true; // true to sync with client
}
);
private static final ServerWorldProperty<String> TEXTBOX = new ServerWorldProperty<>(
new NamespacedKey("axiom", "textbox"),
"Textbox",
false, WorldPropertyWidgetType.TEXTBOX,
world -> "Hello",
(player, world, string) -> {
world.sendMessage(Component.text("Textbox: " + string)); // Do something with input
return true; // true to sync with client
}
);
private static final ServerWorldProperty<Void> BUTTON = new ServerWorldProperty<>(
new NamespacedKey("axiom", "button"),
"Button",
false, WorldPropertyWidgetType.BUTTON,
world -> null,
(player, world, unit) -> {
world.sendMessage(Component.text("Button pressed")); // Do something with input
return true; // true to sync with client
}
);
@EventHandler
public void onCreateWorldProperties(AxiomCreateWorldPropertiesEvent event) {
WorldPropertyCategory category = new WorldPropertyCategory("Examples", false);
ServerWorldProperty<Boolean> checkbox = new ServerWorldProperty<>(new NamespacedKey("axiom", "checkbox"),
"Checkbox",
false, WorldPropertyWidgetType.CHECKBOX, false, (player, world, bool) -> {
world.sendMessage(Component.text("Checkbox: " + bool)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<Integer> slider = new ServerWorldProperty<>(new NamespacedKey("axiom", "slider"),
"Slider",
false, new WorldPropertyWidgetType.Slider(0, 8), 4, (player, world, integer) -> {
world.sendMessage(Component.text("Slider: " + integer)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<String> textbox = new ServerWorldProperty<>(new NamespacedKey("axiom", "textbox"),
"Textbox",
false, WorldPropertyWidgetType.TEXTBOX, "Hello", (player, world, string) -> {
world.sendMessage(Component.text("Textbox: " + string)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<Unit> button = new ServerWorldProperty<>(new NamespacedKey("axiom", "button"),
"Button",
false, WorldPropertyWidgetType.BUTTON, Unit.INSTANCE, (player, world, unit) -> {
world.sendMessage(Component.text("Button pressed")); // Do something with input
return true; // true to sync with client
});
event.addCategory(category, List.of(checkbox, slider, textbox, button));
event.addCategory(category, List.of(CHECKBOX, SLIDER, TEXTBOX, BUTTON));
}
}

Datei anzeigen

@ -3,13 +3,12 @@ package com.moulberry.axiom.event;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertyHolder;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;

Datei anzeigen

@ -143,7 +143,7 @@ public class HelloPacketListener implements PluginMessageListener {
// Register world properties
World world = player.getWorld();
ServerWorldPropertiesRegistry properties = plugin.getWorldProperties(world);
ServerWorldPropertiesRegistry properties = plugin.getOrCreateWorldProperties(world);
if (properties == null) {
player.sendPluginMessage(plugin, "axiom:register_world_properties", new byte[]{0});

Datei anzeigen

@ -2,7 +2,7 @@ package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertyHolder;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
@ -29,10 +29,10 @@ public class SetWorldPropertyListener implements PluginMessageListener {
byte[] data = friendlyByteBuf.readByteArray();
int updateId = friendlyByteBuf.readVarInt();
ServerWorldPropertiesRegistry registry = AxiomPaper.PLUGIN.getWorldProperties(player.getWorld());
ServerWorldPropertiesRegistry registry = AxiomPaper.PLUGIN.getOrCreateWorldProperties(player.getWorld());
if (registry == null) return;
ServerWorldProperty<?> property = registry.getById(id);
ServerWorldPropertyHolder<?> property = registry.getById(id);
if (property != null && property.getType().getTypeId() == type) {
property.update(player, player.getWorld(), data);
}

Datei anzeigen

@ -3,7 +3,6 @@ package com.moulberry.axiom.world_properties;
import io.netty.buffer.Unpooled;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Unit;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
@ -118,20 +117,20 @@ public abstract class WorldPropertyDataType<T> {
}
};
public static WorldPropertyDataType<Unit> EMPTY = new WorldPropertyDataType<>() {
public static WorldPropertyDataType<Void> EMPTY = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 5;
}
@Override
public byte[] serialize(Unit value) {
public byte[] serialize(Void value) {
return new byte[0];
}
@Override
public Unit deserialize(byte[] bytes) {
return Unit.INSTANCE;
public Void deserialize(byte[] bytes) {
return null;
}
};

Datei anzeigen

@ -1,7 +1,6 @@
package com.moulberry.axiom.world_properties;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import java.util.List;
@ -62,9 +61,9 @@ public interface WorldPropertyWidgetType<T> {
};
WorldPropertyWidgetType<Unit> BUTTON = new WorldPropertyWidgetType<>() {
WorldPropertyWidgetType<Void> BUTTON = new WorldPropertyWidgetType<>() {
@Override
public WorldPropertyDataType<Unit> dataType() {
public WorldPropertyDataType<Void> dataType() {
return WorldPropertyDataType.EMPTY;
}

Datei anzeigen

@ -6,7 +6,6 @@ import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.GameRules;
import org.bukkit.GameRule;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
@ -14,33 +13,39 @@ import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
public class ServerWorldPropertiesRegistry {
private final LinkedHashMap<WorldPropertyCategory, List<ServerWorldProperty<?>>> propertyList = new LinkedHashMap<>();
private final Map<ResourceLocation, ServerWorldProperty<?>> propertyMap = new HashMap<>();
private final LinkedHashMap<WorldPropertyCategory, List<ServerWorldPropertyHolder<?>>> propertyList = new LinkedHashMap<>();
private final Map<ResourceLocation, ServerWorldPropertyHolder<?>> propertyMap = new HashMap<>();
private final World world;
public ServerWorldPropertiesRegistry(World world) {
this.registerDefault(world);
this.world = world;
this.registerDefault();
}
public ServerWorldProperty<?> getById(ResourceLocation resourceLocation) {
public ServerWorldPropertyHolder<?> getById(ResourceLocation resourceLocation) {
return propertyMap.get(resourceLocation);
}
@SuppressWarnings("unchecked")
public void addCategory(WorldPropertyCategory category, List<ServerWorldProperty<?>> properties) {
this.propertyList.put(category, properties);
List<ServerWorldPropertyHolder<?>> holders = new ArrayList<>();
for (ServerWorldProperty<?> property : properties) {
ResourceLocation id = property.getId();
Object defaultValue = property.defaultValueFunction.apply(this.world);
holders.add(new ServerWorldPropertyHolder<>(defaultValue, (ServerWorldProperty<Object>) property));
}
this.propertyList.put(category, holders);
for (ServerWorldPropertyHolder<?> holder : holders) {
ResourceLocation id = holder.getId();
if (this.propertyMap.containsKey(id)) {
throw new RuntimeException("Duplicate property: " + id);
}
this.propertyMap.put(id, property);
this.propertyMap.put(id, holder);
}
}
@ -49,7 +54,7 @@ public class ServerWorldPropertiesRegistry {
buf.writeVarInt(this.propertyList.size());
for (Map.Entry<WorldPropertyCategory, List<ServerWorldProperty<?>>> entry : this.propertyList.entrySet()) {
for (Map.Entry<WorldPropertyCategory, List<ServerWorldPropertyHolder<?>>> entry : this.propertyList.entrySet()) {
entry.getKey().write(buf);
buf.writeCollection(entry.getValue(), (buffer, p) -> p.write(buffer));
}
@ -59,46 +64,48 @@ public class ServerWorldPropertiesRegistry {
bukkitPlayer.sendPluginMessage(plugin, "axiom:register_world_properties", bytes);
}
public void registerDefault(World world) {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
private static final ServerWorldProperty<Integer> TIME = new ServerWorldProperty<>(
new NamespacedKey("axiom", "time"),
"axiom.editorui.window.world_properties.time",
true, WorldPropertyWidgetType.TIME, world -> 0, (player, w, integer) -> false
);
public static final ServerWorldProperty<Boolean> PAUSE_WEATHER = new ServerWorldProperty<>(
new NamespacedKey("axiom", "pause_weather"),
"axiom.editorui.window.world_properties.pause_weather",
true, WorldPropertyWidgetType.CHECKBOX, world -> !world.getGameRuleValue(GameRule.DO_WEATHER_CYCLE), (player, world, bool) -> {
world.setGameRule(GameRule.DO_WEATHER_CYCLE, !bool);
return false;
});
private static final ServerWorldProperty<Integer> WEATHER_TYPE = new ServerWorldProperty<>(
new NamespacedKey("axiom", "weather_type"),
"axiom.editorui.window.world_properties.clear_weather",
true, new WorldPropertyWidgetType.ButtonArray(
List.of("axiom.editorui.window.world_properties.rain_weather", "axiom.editorui.window.world_properties.thunder_weather")
), world -> 0, (player, world, index) -> {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
if (index == 0) {
serverLevel.setWeatherParameters(ServerLevel.RAIN_DELAY.sample(serverLevel.random), 0, false, false);
} else if (index == 1) {
serverLevel.setWeatherParameters(0, ServerLevel.RAIN_DURATION.sample(serverLevel.random), true, false);
} else if (index == 2) {
serverLevel.setWeatherParameters(0, ServerLevel.THUNDER_DURATION.sample(serverLevel.random), true, true);
}
return false;
});
public void registerDefault() {
// Time
WorldPropertyCategory timeCategory = new WorldPropertyCategory("axiom.editorui.window.world_properties.time", true);
ServerWorldProperty<Integer> time = new ServerWorldProperty<>(new NamespacedKey("axiom", "time"),
"axiom.editorui.window.world_properties.time",
true, WorldPropertyWidgetType.TIME, 0, (player, w, integer) -> false
);
this.addCategory(timeCategory, List.of(time));
this.addCategory(timeCategory, List.of(TIME));
// Weather
WorldPropertyCategory weatherCategory = new WorldPropertyCategory("axiom.editorui.window.world_properties.weather",
true);
ServerWorldProperty<Boolean> pauseWeather = new ServerWorldProperty<>(new NamespacedKey("axiom", "pause_weather"),
"axiom.editorui.window.world_properties.pause_weather",
true, WorldPropertyWidgetType.CHECKBOX, !world.getGameRuleValue(GameRule.DO_WEATHER_CYCLE), (player, w, bool) -> {
world.setGameRule(GameRule.DO_WEATHER_CYCLE, !bool);
return false;
});
ServerWorldProperty<Integer> weatherType = new ServerWorldProperty<>(new NamespacedKey("axiom", "weather_type"),
"axiom.editorui.window.world_properties.clear_weather",
true, new WorldPropertyWidgetType.ButtonArray(
List.of("axiom.editorui.window.world_properties.rain_weather", "axiom.editorui.window.world_properties.thunder_weather")
), 0, (player, w, index) -> {
if (index == 0) {
serverLevel.setWeatherParameters(ServerLevel.RAIN_DELAY.sample(serverLevel.random), 0, false, false);
} else if (index == 1) {
serverLevel.setWeatherParameters(0, ServerLevel.RAIN_DURATION.sample(serverLevel.random), true, false);
} else if (index == 2) {
serverLevel.setWeatherParameters(0, ServerLevel.THUNDER_DURATION.sample(serverLevel.random), true, true);
}
return false;
});
this.addCategory(weatherCategory, List.of(pauseWeather, weatherType));
this.addCategory(weatherCategory, List.of(PAUSE_WEATHER, WEATHER_TYPE));
}
}

Datei anzeigen

@ -4,32 +4,29 @@ import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.world_properties.PropertyUpdateHandler;
import com.moulberry.axiom.world_properties.WorldPropertyDataType;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
import org.bukkit.entity.Player;
import java.util.function.Predicate;
import java.util.function.Function;
public class ServerWorldProperty<T> {
private final ResourceLocation id;
private final String name;
private final boolean localizeName;
private WorldPropertyWidgetType<T> widget;
private T value;
private PropertyUpdateHandler<T> handler;
/*package-private*/ final String name;
/*package-private*/ final boolean localizeName;
/*package-private*/ WorldPropertyWidgetType<T> widget;
/*package-private*/ Function<World, T> defaultValueFunction;
/*package-private*/ PropertyUpdateHandler<T> handler;
public ServerWorldProperty(NamespacedKey id, String name, boolean localizeName, WorldPropertyWidgetType<T> widget,
T value, PropertyUpdateHandler<T> handler) {
Function<World, T> defaultValueFunction, PropertyUpdateHandler<T> handler) {
this.id = CraftNamespacedKey.toMinecraft(id);
this.name = name;
this.localizeName = localizeName;
this.widget = widget;
this.value = value;
this.defaultValueFunction = defaultValueFunction;
this.handler = handler;
}
@ -41,44 +38,30 @@ public class ServerWorldProperty<T> {
return this.widget.dataType();
}
public void update(Player player, World world, byte[] data) {
this.value = this.widget.dataType().deserialize(data);
if (this.handler.update(player, world, this.value)) {
this.sync(world);
}
}
public void setValueWithoutSyncing(T value) {
this.value = value;
}
public void setValue(World world, T value) {
this.value = value;
this.sync(world);
}
public void sync(World world) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeResourceLocation(this.id);
buf.writeVarInt(this.widget.dataType().getTypeId());
buf.writeByteArray(this.widget.dataType().serialize(this.value));
byte[] message = new byte[buf.writerIndex()];
buf.getBytes(0, message);
for (Player player : world.getPlayers()) {
if (AxiomPaper.PLUGIN.activeAxiomPlayers.contains(player.getUniqueId())) {
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:set_world_property", message);
@SuppressWarnings("unchecked")
public boolean setValueWithoutSyncing(World world, T value) {
ServerWorldPropertiesRegistry properties = AxiomPaper.PLUGIN.getWorldPropertiesIfPresent(world);
if (properties != null) {
ServerWorldPropertyHolder<?> property = properties.getById(this.id);
if (property != null && property.getProperty() == this) {
((ServerWorldPropertyHolder<T>)property).setValueWithoutSyncing(value);
return true;
}
}
return false;
}
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeResourceLocation(this.id);
friendlyByteBuf.writeUtf(this.name);
friendlyByteBuf.writeBoolean(this.localizeName);
this.widget.write(friendlyByteBuf);
friendlyByteBuf.writeByteArray(this.widget.dataType().serialize(this.value));
@SuppressWarnings("unchecked")
public boolean setValue(World world, T value) {
ServerWorldPropertiesRegistry properties = AxiomPaper.PLUGIN.getWorldPropertiesIfPresent(world);
if (properties != null) {
ServerWorldPropertyHolder<?> property = properties.getById(this.id);
if (property != null && property.getProperty() == this) {
((ServerWorldPropertyHolder<T>)property).setValue(world, value);
return true;
}
}
return false;
}
}

Datei anzeigen

@ -0,0 +1,77 @@
package com.moulberry.axiom.world_properties.server;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.world_properties.PropertyUpdateHandler;
import com.moulberry.axiom.world_properties.WorldPropertyDataType;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
import org.bukkit.entity.Player;
public class ServerWorldPropertyHolder<T> {
private T value;
private ServerWorldProperty<T> property;
public ServerWorldPropertyHolder(T value, ServerWorldProperty<T> property) {
this.value = value;
this.property = property;
}
public ResourceLocation getId() {
return this.property.getId();
}
public WorldPropertyDataType<T> getType() {
return this.property.widget.dataType();
}
public ServerWorldProperty<T> getProperty() {
return property;
}
public void update(Player player, World world, byte[] data) {
this.value = this.property.widget.dataType().deserialize(data);
if (this.property.handler.update(player, world, this.value)) {
this.sync(world);
}
}
public void setValueWithoutSyncing(T value) {
this.value = value;
}
public void setValue(World world, T value) {
this.value = value;
this.sync(world);
}
public void sync(World world) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeResourceLocation(this.getId());
buf.writeVarInt(this.property.widget.dataType().getTypeId());
buf.writeByteArray(this.property.widget.dataType().serialize(this.value));
byte[] message = new byte[buf.writerIndex()];
buf.getBytes(0, message);
for (Player player : world.getPlayers()) {
if (AxiomPaper.PLUGIN.activeAxiomPlayers.contains(player.getUniqueId())) {
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:set_world_property", message);
}
}
}
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeResourceLocation(this.getId());
friendlyByteBuf.writeUtf(this.property.name);
friendlyByteBuf.writeBoolean(this.property.localizeName);
this.property.widget.write(friendlyByteBuf);
friendlyByteBuf.writeByteArray(this.property.widget.dataType().serialize(this.value));
}
}