Skip to content

Commit

Permalink
Refactor the PDCResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
md5sha256 committed Jan 1, 2025
1 parent 73920ad commit dbbcbfd
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package io.github.md5sha256.addictiveexperience.implementation.plant;

import com.github.md5sha256.spigotutils.blocks.BlockPosition;
import com.github.md5sha256.spigotutils.blocks.ChunkPosition;
import com.github.md5sha256.spigotutils.timing.VariableStopwatch;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import io.github.md5sha256.addictiveexperience.api.drugs.DrugPlantData;
import io.github.md5sha256.addictiveexperience.api.drugs.DrugPlantMeta;
import io.github.md5sha256.addictiveexperience.api.drugs.DrugRegistry;
import io.github.md5sha256.addictiveexperience.util.configurate.AdventureKeySerializer;
import io.github.md5sha256.addictiveexperience.util.configurate.BlockPositionSerializer;
import io.github.md5sha256.addictiveexperience.util.configurate.DrugPlantDataSerializer;
import io.github.md5sha256.addictiveexperience.util.configurate.DrugPlantMetaSerializer;
import io.github.md5sha256.addictiveexperience.util.configurate.VariableStopwatchSerializer;
import io.github.md5sha256.addictiveexperience.util.configurate.WorldSerializer;
import net.kyori.adventure.key.Key;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.loader.ConfigurationLoader;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class ConfigurateResolver {

private final ConfigurationOptions options;

public ConfigurateResolver(@NotNull Plugin plugin, @NotNull DrugRegistry drugRegistry) {
this.options = ConfigurationOptions
.defaults()
.serializers(builder -> builder
.register(World.class, new WorldSerializer(plugin.getServer()))
.register(BlockPosition.class, new BlockPositionSerializer())
.register(DrugPlantMeta.class, new DrugPlantMetaSerializer(drugRegistry))
.register(DrugPlantData.class, new DrugPlantDataSerializer())
.register(Key.class, new AdventureKeySerializer())
.register(VariableStopwatch.class, new VariableStopwatchSerializer()));
}

public Map<Long, @NotNull DrugPlantData> fromBytes(byte[] bytes) {
if (bytes == null) {
return Collections.emptyMap();
}
final ConfigurationLoader<?> loader = loader(bytes);
final Collection<DrugPlantData> data;
try {
final ConfigurationNode root = loader.load();
data = root.getList(DrugPlantData.class, Collections.emptyList());
} catch (IOException ex) {
// FIXME log warning
ex.printStackTrace();
return Collections.emptyMap();
}
final Map<Long, DrugPlantData> map = new HashMap<>(data.size());
for (DrugPlantData plantData : data) {
map.put(plantData.position().getPosition(), plantData);
}
return new HashMap<>(map);
}

public byte @Nullable [] toBytes(@NotNull Collection<DrugPlantData> data) {

final ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
final ConfigurationLoader<?> loader = loader(bos);
final ConfigurationNode node = loader.createNode();
final List<DrugPlantData> dataAsList;
if (data instanceof List) {
dataAsList = (List<DrugPlantData>) data;
} else {
dataAsList = new ArrayList<>(data);
}
try {
node.setList(DrugPlantData.class, dataAsList);
loader.save(node);
} catch (IOException ex) {
// FIXME log warning
ex.printStackTrace();
// Remove all data
return null;
}
return bos.toByteArray();
}

private ConfigurationLoader<?> loader(byte[] raw) {
final Reader reader = new InputStreamReader(new ByteArrayInputStream(raw),
StandardCharsets.UTF_8);
return GsonConfigurationLoader.builder()
.defaultOptions(this.options)
.source(() -> new BufferedReader(reader))
.lenient(true)
.build();
}

private ConfigurationLoader<?> loader(@NotNull OutputStream os) {
final OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
return GsonConfigurationLoader.builder()
.defaultOptions(this.options)
.sink(() -> new BufferedWriter(osw))
.lenient(true)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,20 @@
final class PDCResolver implements PlantDataResolver {

private final NamespacedKey key;
private final ConfigurationOptions options;
private final ConfigurateResolver resolver;

@AssistedInject
PDCResolver(@NotNull Plugin plugin, @NotNull DrugRegistry drugRegistry,
@Assisted @NotNull World world) {
this.key = new NamespacedKey(plugin, "drug-plant-data");
this.options = ConfigurationOptions
.defaults()
.serializers(builder -> builder
.register(DrugPlantMeta.class, new DrugPlantMetaSerializer(drugRegistry))
.register(DrugPlantData.class, new DrugPlantDataSerializer(world))
.register(Key.class, new AdventureKeySerializer())
.register(VariableStopwatch.class, new VariableStopwatchSerializer()));
this.resolver = new ConfigurateResolver(plugin, drugRegistry);
}

@Override
public @NotNull Map<Long, @NotNull DrugPlantData> loadData(@NotNull final ChunkPosition chunk) {
final PersistentDataContainer container = chunk.getChunk().getPersistentDataContainer();
final byte[] raw = container.get(this.key, PersistentDataType.BYTE_ARRAY);
if (raw == null) {
return Collections.emptyMap();
}
final ConfigurationLoader<?> loader = loader(raw);
final Collection<DrugPlantData> data;
try {
final ConfigurationNode root = loader.load();
data = root.getList(DrugPlantData.class, Collections.emptyList());
} catch (IOException ex) {
// FIXME log warning
ex.printStackTrace();
return Collections.emptyMap();
}
final Map<Long, DrugPlantData> map = new HashMap<>(data.size());
for (DrugPlantData plantData : data) {
map.put(plantData.position().getPosition(), plantData);
}
return new HashMap<>(map);
return this.resolver.fromBytes(raw);
}

@Override
Expand All @@ -89,49 +66,17 @@ public void saveData(@NotNull final ChunkPosition chunk,
clearData(chunk);
return;
}
final PersistentDataContainer container = chunk.getChunk().getPersistentDataContainer();
final ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
final ConfigurationLoader<?> loader = loader(bos);
final ConfigurationNode node = loader.createNode();
final List<DrugPlantData> dataAsList;
if (data instanceof List) {
dataAsList = (List<DrugPlantData>) data;
} else {
dataAsList = new ArrayList<>(data);
}
try {
node.setList(DrugPlantData.class, dataAsList);
loader.save(node);
} catch (IOException ex) {
// FIXME log warning
ex.printStackTrace();
// Remove all data
container.remove(this.key);
final byte[] bytes = this.resolver.toBytes(data);
if (bytes == null) {
clearData(chunk);
return;
}
container.set(this.key, PersistentDataType.BYTE_ARRAY, bos.toByteArray());
final PersistentDataContainer container = chunk.getChunk().getPersistentDataContainer();
container.set(this.key, PersistentDataType.BYTE_ARRAY, bytes);
}

@Override
public void clearData(@NotNull final ChunkPosition chunk) {
chunk.getChunk().getPersistentDataContainer().remove(this.key);
}

private ConfigurationLoader<?> loader(byte[] raw) {
final Reader reader = new InputStreamReader(new ByteArrayInputStream(raw), StandardCharsets.UTF_8);
return GsonConfigurationLoader.builder()
.defaultOptions(this.options)
.source(() -> new BufferedReader(reader))
.lenient(true)
.build();
}

private ConfigurationLoader<?> loader(@NotNull OutputStream os) {
final OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
return GsonConfigurationLoader.builder()
.defaultOptions(this.options)
.sink(() -> new BufferedWriter(osw))
.lenient(true)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.github.md5sha256.addictiveexperience.util.configurate;

import com.github.md5sha256.spigotutils.blocks.BlockPosition;
import org.bukkit.World;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;

import java.lang.reflect.Type;
import java.util.Objects;

public class BlockPositionSerializer implements TypeSerializer<BlockPosition> {

private static final String KEY_POSITION = "position";
private static final String KEY_WORLD = "world";

@Override
public BlockPosition deserialize(Type type,
ConfigurationNode node) throws SerializationException {
ConfigurationNode position = node.node(KEY_POSITION);
ConfigurationNode world = node.node(KEY_WORLD);
World worldInstance = Objects.requireNonNull(world.get(World.class));
return new BlockPosition(worldInstance, position.getLong());
}

@Override
public void serialize(Type type,
@Nullable BlockPosition obj,
ConfigurationNode node) throws SerializationException {
if (obj == null) {
node.removeChild(KEY_POSITION);
node.removeChild(KEY_WORLD);
return;
}
node.node(KEY_POSITION).set(obj.getPosition());
node.node(KEY_WORLD).set(obj.getWorld());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.spongepowered.configurate.serialize.TypeSerializer;

import java.lang.reflect.Type;
import java.util.Objects;

public class DrugPlantDataSerializer implements TypeSerializer<DrugPlantData> {

Expand All @@ -21,12 +22,6 @@ public class DrugPlantDataSerializer implements TypeSerializer<DrugPlantData> {
private static final String KEY_STOPWATCH = "stopwatch";
private static final String KEY_POSITION = "position";

private final World world;

public DrugPlantDataSerializer(@NotNull World world) {
this.world = world;
}

@Override
public DrugPlantData deserialize(final Type type,
final ConfigurationNode node) throws SerializationException {
Expand All @@ -44,8 +39,9 @@ public DrugPlantData deserialize(final Type type,
if (elapsed == null) {
throw new SerializationException("Missing elapsed time!");
}
BlockPosition blockPosition = position.get(BlockPosition.class);
builder.meta(plantMeta)
.position(new BlockPosition(world, position.getLong()))
.position(Objects.requireNonNull(blockPosition))
.startTimeEpochMilli(startTime.getLong())
.elapsed(elapsed);
try {
Expand Down Expand Up @@ -73,6 +69,6 @@ public void serialize(final Type type,
final ConfigurationNode startTime = node.node(KEY_START_TIME);
startTime.set(obj.startTimeEpochMillis());
final ConfigurationNode position = node.node(KEY_POSITION);
position.set(obj.position().getPosition());
position.set(obj.position());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.github.md5sha256.addictiveexperience.util.configurate;

import org.bukkit.Server;
import org.bukkit.World;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;

import java.lang.reflect.Type;
import java.util.UUID;
import java.util.function.Predicate;

public class WorldSerializer extends ScalarSerializer<World> {

private final Server server;

public WorldSerializer(Server server) {
super(World.class);
this.server = server;
}

private World getOrThrow(UUID uuid) throws SerializationException {
World world = this.server.getWorld(uuid);
if (world == null) {
throw new SerializationException("World not found: " + uuid);
}
return world;
}

@Override
public World deserialize(Type type, Object obj) throws SerializationException {
if (obj instanceof UUID uuid) {
return getOrThrow(uuid);
} else if (obj instanceof String s) {
try {
return getOrThrow(UUID.fromString(s));
} catch (IllegalArgumentException ex) {
throw new SerializationException(ex);
}
}
throw new SerializationException("Unsupported type: " + type);
}

@Override
protected Object serialize(World item, Predicate<Class<?>> typeSupported) {
if (typeSupported.test(UUID.class)) {
return item.getUID();
} else {
return item.getUID().toString();
}
}
}

0 comments on commit dbbcbfd

Please sign in to comment.