diff --git a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/PlayerManager.java b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/PlayerManager.java index 01b5e05..76fd85f 100644 --- a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/PlayerManager.java +++ b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/PlayerManager.java @@ -21,11 +21,11 @@ public final class PlayerManager { this.minigame = minigame; } - public Stream getParticipants() { + public Stream participants() { return participants.stream().map(Bukkit::getPlayer); } - public Stream getSpectators() { + public Stream spectators() { return spectators.stream().map(Bukkit::getPlayer); } @@ -61,7 +61,7 @@ public final class PlayerManager { return spectators.remove(player.getUniqueId()); } - public Stream getAllPlayers() { - return Stream.concat(getParticipants(), getSpectators()); + public Stream allPlayers() { + return Stream.concat(participants(), spectators()); } } diff --git a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/LobbyPhase.java b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/LobbyPhase.java index 806387c..2303fa2 100644 --- a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/LobbyPhase.java +++ b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/LobbyPhase.java @@ -33,7 +33,6 @@ public class LobbyPhase extends CompositePhase { ctx.listeners().on(PlayerJoinEvent.class, EventPriority.LOWEST, this::onJoin); ctx.listeners().on(PlayerQuitEvent.class, EventPriority.LOWEST, this::onQuit); - //noinspection unchecked ctx.listeners().cancel( HangingBreakByEntityEvent.class, PlayerInteractAtEntityEvent.class, @@ -51,13 +50,13 @@ public class LobbyPhase extends CompositePhase { private void onJoin(PlayerJoinEvent ev) { ctx.players().addParticipant(ev.getPlayer()); - ctx.players().getAllPlayers().forEach(p -> + ctx.players().allPlayers().forEach(p -> p.sendMessage("§8[§a+§8] §7" + ev.getPlayer().getName())); } private void onQuit(PlayerQuitEvent ev) { if (ctx.players().removeParticipant(ev.getPlayer())) - ctx.players().getAllPlayers().forEach(p -> + ctx.players().allPlayers().forEach(p -> p.sendMessage("§8[§c-§8] §7" + ev.getPlayer().getName())); } diff --git a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/WaitForMaximumPlayersPhase.java b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/WaitForMaximumPlayersPhase.java index 6caddaf..2289699 100644 --- a/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/WaitForMaximumPlayersPhase.java +++ b/core/bukkit/api/main/java/de/kentoj/scrow/bukkit/minigame/lobby/WaitForMaximumPlayersPhase.java @@ -30,7 +30,7 @@ class WaitForMaximumPlayersPhase extends Phase { @Override protected void onTick(int tick) { if (tick % 20 == 0) { - ctx.players().getAllPlayers().forEach(player -> + ctx.players().allPlayers().forEach(player -> player.sendTitle("Game starting in " + secsLeft + " seconds", "have fun", 5, 20, 10) ); secsLeft--; diff --git a/core/common/src/main/java/de/kentoj/scrowlib/convention/ScrowMessageStyle.java b/core/common/src/main/java/de/kentoj/scrowlib/convention/ScrowMessageStyle.java index e58b376..052f13a 100644 --- a/core/common/src/main/java/de/kentoj/scrowlib/convention/ScrowMessageStyle.java +++ b/core/common/src/main/java/de/kentoj/scrowlib/convention/ScrowMessageStyle.java @@ -21,8 +21,9 @@ public record ScrowMessageStyle( @Override public Component ok(Component msg) { - return ComponentUtils.resolveURLS(prefix.append(Component.text(" ☆ ").color(NamedTextColor.GRAY)) - .append(msg.colorIfAbsent(NamedTextColor.GRAY))); + return ComponentUtils.resolveURLS( + prefix.append(Component.text(" ☆ ").color(NamedTextColor.GRAY)) + .append(msg.colorIfAbsent(NamedTextColor.GRAY))); } @Override diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java index 5021959..af9ada3 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java @@ -5,10 +5,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import site.lab0x13.scrow.configurator.command.ConfiguratorCommand; -import site.lab0x13.scrow.configurator.configfile.ConfigFile; import site.lab0x13.scrow.configurator.configfile.ConfigFileRegistry; -import site.lab0x13.scrow.configurator.storable.impl.VectorStorable; -import site.lab0x13.scrow.configurator.storable.impl.location.LocationStorable; import java.io.IOException; @@ -18,19 +15,6 @@ public class ConfiguratorPlugin extends JavaPlugin { @Override public void onEnable() { - ConfigFile hotPotatoConfig = ConfigFile.of(this.getDataPath().resolve("config.json")); - ConfigFileRegistry.get().register(hotPotatoConfig); - - var someVector = new VectorStorable(); - var someLocation = new LocationStorable(); - hotPotatoConfig.registerStorable("someVector", someVector); - hotPotatoConfig.registerStorable("someLocation", someLocation); - - try { - hotPotatoConfig.load(); - } catch (IOException e) { - throw new RuntimeException(e); - } ScrowAPI.commands().register(ConfiguratorCommand.rootLiteral(ConfigFileRegistry.get())); } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/action/SetAction.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/action/SetAction.java new file mode 100644 index 0000000..5450811 --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/action/SetAction.java @@ -0,0 +1,40 @@ +package site.lab0x13.scrow.configurator.action; + +import de.kentoj.scrow.bukkit.command.ScrowBukkitCC; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.commands.model.argument.Argument; +import site.lab0x13.scrow.configurator.storable.Storable; + +import java.util.List; + +public abstract class SetAction implements StorableAction> { + + private final Argument argument = argument(); + protected abstract Argument argument(); + protected abstract @Nullable Component valueToPrettyString(T value); + + @Override + public String name() { + return "set"; + } + + @Override + public List> arguments(Storable __) { + return List.of(argument); + } + + @Override + public boolean requiresValue() { + return false; + } + + @Override + public void execute(Storable storable, ScrowBukkitCC ctx) { + var parsedArg = ctx.getArgument(argument); + storable.value(parsedArg.value()); + var prettyValue = valueToPrettyString(parsedArg.value()); + var valueAsString = prettyValue != null ? prettyValue : Component.text(parsedArg.rawValue()); + ctx.sender().sendMessage(ctx.style().ok("Set value to ").append(valueAsString)); + } +} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/CheckLiteral.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/CheckLiteral.java index c378c45..efeee3b 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/CheckLiteral.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/CheckLiteral.java @@ -11,9 +11,9 @@ import static java.util.Objects.*; final class CheckLiteral { private final Literal literal; - private final ConfigFile configFile; + private final ConfigFile configFile; - public CheckLiteral(ConfigFile configFile) { + public CheckLiteral(ConfigFile configFile) { this.configFile = configFile; this.literal = Literal.builder("check") .withSyncExecutor(this::execute) diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigLiteral.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigLiteral.java index 7d89c56..ede6267 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigLiteral.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigLiteral.java @@ -8,7 +8,7 @@ public final class ConfigLiteral { private ConfigLiteral() { } - public static Literal literal(ConfigFile cfg) { + public static Literal literal(ConfigFile cfg) { return Literal.builder(cfg.path().normalize().toString()) .withSubLiteral(new KeyLiteral(cfg).literal()) .withSubLiteral(new CheckLiteral(cfg).literal()) diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java index 7f39a0c..bed26da 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java @@ -11,9 +11,9 @@ import site.lab0x13.scrow.configurator.action.StorableAction; final class KeyLiteral { private final Literal literal; - private final ConfigFile configFile; + private final ConfigFile configFile; - KeyLiteral(ConfigFile configFile) { + KeyLiteral(ConfigFile configFile) { this.configFile = configFile; literal = Literal.dynamic("key") .withSubLiteralMetas(() -> configFile.allKeys().stream().map(LiteralMeta::of).toList()) diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFile.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFile.java index fad2e60..f92d1e4 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFile.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFile.java @@ -1,28 +1,59 @@ package site.lab0x13.scrow.configurator.configfile; +import com.google.gson.JsonParser; import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.configurator.storable.Storable; +import site.lab0x13.scrow.configurator.storable.complex.ComplexStorable; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; -import java.util.Map; -public interface ConfigFile extends Storable>> { +import static java.util.Objects.requireNonNull; - static ConfigFile of(Path path) { - return new ConfigFileImpl(path.normalize()); +public abstract class ConfigFile extends ComplexStorable { + + private final Path path; + + public ConfigFile(Path path) { + this.path = path; } - @Nullable Storable getStorable(String key); + public void load() throws IOException { + this.loadJson(JsonParser.parseString(Files.readString(path))); + } - void registerStorable(String key, Storable storable); + public void save() throws IOException { + Files.writeString(path, this.toJson().toString()); + } - List allKeys(); + public Storable getStorable(String key) { + Storable cur = this; + for (var fieldName : key.split("\\.")) { + cur = cur.getSubField(fieldName); + if (cur == null) return null; + } + return cur; + } - void load() throws IOException; + public List allKeys() { + var res = new ArrayList(); + addKeysRecursively(res, null, this); + return res; + } - void save() throws IOException; + private void addKeysRecursively(List res, @Nullable String parentKey, Storable storable) { + if (parentKey != null) + res.add(parentKey); + storable.subFieldNames().forEach(subFieldName -> { + var subKey = parentKey == null ? subFieldName : parentKey + "." + subFieldName; + addKeysRecursively(res, subKey, requireNonNull(storable.getSubField(subFieldName))); + }); + } - Path path(); + public Path path() { + return path; + } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileImpl.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileImpl.java deleted file mode 100644 index a743780..0000000 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -package site.lab0x13.scrow.configurator.configfile; - -import com.google.gson.JsonParser; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import site.lab0x13.scrow.configurator.storable.MapStorable; -import site.lab0x13.scrow.configurator.storable.Storable; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -class ConfigFileImpl extends MapStorable> implements ConfigFile { - - private static final Logger log = LoggerFactory.getLogger(ConfigFileImpl.class); - private final Path path; - - ConfigFileImpl(Path path) { - this.path = path; - } - - @Override - public @Nullable Storable getStorable(String key) { - var index = key.indexOf(':'); - if (index == -1) - return getSubField(key); - if (key.endsWith(":")) - return null; - - var fieldName = key.substring(0, index); - Storable storable = getSubField(fieldName); - if (storable == null) - return null; - for (String subFieldName : key.substring(index + 1).split(":")) { - if (subFieldName.isEmpty()) continue; - storable = storable.getSubField(subFieldName); - if (storable == null) - return null; - } - return storable; - } - - @Override - public void registerStorable(String key, Storable storable) { - registerEntry(key, (Storable) storable); - } - - @Override - public List allKeys() { - var res = new ArrayList(); - subFieldNames().forEach(key -> addKeysRecursively(res, key, requireNonNull(getSubField(key)))); - return res; - } - - private void addKeysRecursively(List result, String key, Storable storable) { - result.add(key); - storable.subFieldNames().forEach(subKey -> - addKeysRecursively(result, key + ":" + subKey, requireNonNull(storable.getSubField(subKey)))); - } - - @Override - public void load() throws IOException { - if (!Files.exists(this.path)) return; - var json = Files.readString(this.path()); - this.loadJson(JsonParser.parseString(json)); - } - - @Override - public void save() throws IOException { - Files.createDirectories(this.path().getParent()); - Files.writeString(this.path(), this.toJson().toString()); - } - - @Override - public Path path() { - return path; - } -} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileRegistry.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileRegistry.java index eca8096..59b980c 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileRegistry.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/configfile/ConfigFileRegistry.java @@ -1,34 +1,33 @@ package site.lab0x13.scrow.configurator.configfile; import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; public class ConfigFileRegistry { - private static final Logger log = LoggerFactory.getLogger(ConfigFileRegistry.class); private static final ConfigFileRegistry instance = new ConfigFileRegistry(); public static ConfigFileRegistry get() { return instance; } - private final Map configFiles = new HashMap<>(); + private final Map> configFiles = new HashMap<>(); - public void register(ConfigFile configFile) { - var oldValue = configFiles.put(configFile.path().toString(), configFile); - if (oldValue != null) - log.warn("Overwriting old config with same path '{}'", configFile.path()); + private ConfigFileRegistry() { } - public Map allConfigs() { + public void register(ConfigFile configFile, S defaultValue) { + configFile.value(defaultValue); + configFiles.put(configFile.path().normalize().toString(), configFile); + } + + public Map> allConfigs() { return new HashMap<>(configFiles); } - public @Nullable ConfigFile getConfigFile(String path) { + public @Nullable ConfigFile getConfigFile(String path) { return configFiles.get(path); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/AbstractStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/AbstractStorable.java index b057995..4211805 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/AbstractStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/AbstractStorable.java @@ -1,6 +1,9 @@ package site.lab0x13.scrow.configurator.storable; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.action.StorableAction; import java.util.ArrayList; import java.util.List; @@ -8,20 +11,36 @@ import java.util.function.Predicate; public abstract class AbstractStorable implements Storable { private final List subscribers = new ArrayList<>(); - @Nullable - protected T value = null; - + private final List> actions = new ArrayList<>(); private final List> requirements = new ArrayList<>(); + private @Nullable T value = null; + + public AbstractStorable() { + actions.addAll(Storable.super.actions()); + } + + protected abstract JsonElement toJsonSafe(); + + protected abstract void loadJsonSafe(JsonElement json); protected void requireValue(Predicate predicate, String error) { requirements.add(new ValueRequirement<>(predicate, error)); } + protected void addAction(StorableAction action) { + actions.add(action); + } + @Override public void subscribe(ValueChangeSubscriber subscriber) { subscribers.add(subscriber); } + @Override + public List> actions() { + return actions; + } + @Override public @Nullable T value() { return value; @@ -29,10 +48,15 @@ public abstract class AbstractStorable implements Storable { @Override public void value(@Nullable T value) { - this.value = value; + setValueSilently(value); subscribers.forEach(ValueChangeSubscriber::notifyValueChange); } + @Override + public void setValueSilently(@Nullable T value) { + this.value = value; + } + @Override public @Nullable String checkValue() { for (var requirement : requirements) { @@ -41,4 +65,20 @@ public abstract class AbstractStorable implements Storable { } return null; } + + @Override + public void loadJson(JsonElement json) { + if (json == null || json.isJsonNull()) { + value(null); + return; + } + loadJsonSafe(json); + } + + @Override + public JsonElement toJson() { + if (value == null) + return JsonNull.INSTANCE; + return toJsonSafe(); + } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/MapStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/MapStorable.java index f1b6204..40b2dca 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/MapStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/MapStorable.java @@ -9,6 +9,10 @@ import java.util.Map; public class MapStorable extends ComplexStorable> { + public MapStorable() { + value(new HashMap<>()); + } + public void registerEntry(String name, Storable storable) { fieldRegistry().registerField(name, storable, map -> (S) map.get(name)); } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/Storable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/Storable.java index 90354ca..fc56e83 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/Storable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/Storable.java @@ -2,12 +2,13 @@ package site.lab0x13.scrow.configurator.storable; import com.google.gson.JsonElement; import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.action.StorableAction; import site.lab0x13.scrow.configurator.action.GlobalSetJsonAction; import site.lab0x13.scrow.configurator.action.GlobalShowAction; +import site.lab0x13.scrow.configurator.action.StorableAction; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; public interface Storable { @@ -15,6 +16,11 @@ public interface Storable { void value(@Nullable T value); + /** + * Set value without notifying subscribers + */ + void setValueSilently(@Nullable T value); + JsonElement toJson(); void loadJson(JsonElement json); diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/WrappedStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/WrappedStorable.java index b27a8ec..c752083 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/WrappedStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/WrappedStorable.java @@ -26,19 +26,24 @@ public abstract class WrappedStorable extends AbstractStorable { @Override public void value(@Nullable T value) { - if (skipUpdates) - return; super.value(value); - skipUpdates(() -> realStorable.value(value == null ? null : unwrap(value))); + if (skipUpdates) return; + + try { + skipUpdates = true; + realStorable.value(value != null ? unwrap(value) : null); + } finally { + skipUpdates = false; + } } @Override - public JsonElement toJson() { + public JsonElement toJsonSafe() { return realStorable.toJson(); } @Override - public void loadJson(JsonElement json) { + public void loadJsonSafe(JsonElement json) { realStorable.loadJson(json); } @@ -51,16 +56,4 @@ public abstract class WrappedStorable extends AbstractStorable { public @Nullable Storable getSubField(String name) { return realStorable.getSubField(name); } - - /** - * Run action without updating the value - */ - private void skipUpdates(Action action) { - try { - skipUpdates = true; - action.apply(); - } finally { - skipUpdates = false; - } - } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/ComplexStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/ComplexStorable.java index 11bbeb0..767cb7c 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/ComplexStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/ComplexStorable.java @@ -2,18 +2,18 @@ package site.lab0x13.scrow.configurator.storable.complex; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.configurator.storable.Storable; public abstract class ComplexStorable extends SubFieldedStorable { - @Override - protected @Nullable T defaultValue() { - return null; + public ComplexStorable() { + this.registerFields(fieldRegistry()); } + protected abstract void registerFields(FieldRegistry registry); + @Override - public JsonElement toJson() { + public JsonElement toJsonSafe() { var obj = new JsonObject(); fieldRegistry().subFields().forEach((subFieldName, subField) -> { var v = (Storable) subField.storable(); @@ -23,10 +23,9 @@ public abstract class ComplexStorable extends SubFieldedStorable { } @Override - public void loadJson(JsonElement json) { + public void loadJsonSafe(JsonElement json) { var obj = json.getAsJsonObject(); - skipUpdates(() -> fieldRegistry().subFields().forEach((subFieldName, subField) -> - subField.storable().loadJson(obj.get(subFieldName)))); - updateValue(); + fieldRegistry().subFields().forEach((subFieldName, subField) -> + subField.storable().loadJson(obj.get(subFieldName))); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/FieldRegistry.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/FieldRegistry.java index c9e6ffd..6d5d838 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/FieldRegistry.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/FieldRegistry.java @@ -2,6 +2,7 @@ package site.lab0x13.scrow.configurator.storable.complex; import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.configurator.storable.Storable; +import site.lab0x13.scrow.configurator.storable.list.ListStorable; import java.util.HashMap; import java.util.Map; @@ -9,11 +10,19 @@ import java.util.function.Function; public class FieldRegistry { + private final Storable parent; private final Map> subFields = new HashMap<>(); + private final Runnable onFieldRegister; + + public FieldRegistry(Storable parent, Runnable onFieldRegister) { + this.parent = parent; + this.onFieldRegister = onFieldRegister; + } - // TODO move field stuff into own class where register field is one block and after that ready is called public void registerField(String name, Storable storable, Function getter) { subFields.put(name, new Field<>(storable, getter)); + parent.subscribe(() -> storable.setValueSilently(getter.apply(parent.value()))); + storable.subscribe(onFieldRegister::run); } public Map> subFields() { diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/SubFieldedStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/SubFieldedStorable.java index b130914..c6e2ea7 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/SubFieldedStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/complex/SubFieldedStorable.java @@ -2,59 +2,20 @@ package site.lab0x13.scrow.configurator.storable.complex; import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.configurator.storable.AbstractStorable; -import site.lab0x13.scrow.configurator.storable.Action; import site.lab0x13.scrow.configurator.storable.Storable; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; public abstract class SubFieldedStorable extends AbstractStorable { - private final FieldRegistry fieldRegistry = new FieldRegistry<>(); - private boolean skipUpdates = false; - - public SubFieldedStorable() { - this.registerFields(fieldRegistry); - fieldRegistry.subFields().values().forEach(f -> f.storable().subscribe(this::updateValue)); - } - - protected abstract void registerFields(FieldRegistry registry); + private final FieldRegistry fieldRegistry = new FieldRegistry<>(this, this::onFieldRegister); protected abstract T construct(FieldReader reader); - protected abstract @Nullable T defaultValue(); - - /** - * Run action without updating the value - */ - protected void skipUpdates(Action action) { - try { - skipUpdates = true; - action.apply(); - } finally { - skipUpdates = false; - } - } - - protected void updateValue() { - if (skipUpdates) - return; - super.value(construct(new FieldReader<>(fieldRegistry))); - } - - @Override - public void value(@Nullable T value) { - if (value == null) { - super.value(defaultValue()); - return; - } - super.value(value); - - // reflect value change no sub fields - skipUpdates(() -> fieldRegistry.subFields().values().forEach(_field -> { - var field = (Field) _field; - field.storable().value(field.getter().apply(value)); - })); + private void onFieldRegister() { + this.setValueSilently(construct(new FieldReader<>(fieldRegistry))); } @Override diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/DurationStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/DurationStorable.java new file mode 100644 index 0000000..3d6e231 --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/DurationStorable.java @@ -0,0 +1,23 @@ +package site.lab0x13.scrow.configurator.storable.impl; + +import site.lab0x13.scrow.configurator.storable.complex.ComplexStorable; +import site.lab0x13.scrow.configurator.storable.complex.FieldReader; +import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; +import site.lab0x13.scrow.configurator.storable.primitive.IntStorable; +import site.lab0x13.scrow.configurator.storable.primitive.LongStorable; + +import java.time.Duration; + +public final class DurationStorable extends ComplexStorable { + + @Override + protected void registerFields(FieldRegistry registry) { + registry.registerField("seconds", new LongStorable(), Duration::getSeconds); + registry.registerField("nanos", new IntStorable(), Duration::getNano); + } + + @Override + protected Duration construct(FieldReader reader) { + return Duration.ofSeconds(reader.read("seconds"), reader.read("nanos")); + } +} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/VectorStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/VectorStorable.java index 9ae25c0..b3c046c 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/VectorStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/VectorStorable.java @@ -6,7 +6,7 @@ import site.lab0x13.scrow.configurator.storable.complex.FieldReader; import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; import site.lab0x13.scrow.configurator.storable.primitive.DoubleStorable; -public class VectorStorable extends ComplexStorable { +public final class VectorStorable extends ComplexStorable { @Override protected void registerFields(FieldRegistry registry) { diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/ComponentStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/ComponentStorable.java new file mode 100644 index 0000000..2471a0b --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/ComponentStorable.java @@ -0,0 +1,30 @@ +package site.lab0x13.scrow.configurator.storable.impl.component; + +import de.kentoj.scrow.bukkit.command.ScrowBukkitCC; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import site.lab0x13.scrow.commands.model.argument.Argument; +import site.lab0x13.scrow.commands.model.argument.types.StringArgumentType; +import site.lab0x13.scrow.configurator.action.SetAction; +import site.lab0x13.scrow.configurator.storable.WrappedStorable; +import site.lab0x13.scrow.configurator.storable.primitive.StringStorable; + +public final class ComponentStorable extends WrappedStorable { + + private static final MiniMessage miniMessage = MiniMessage.miniMessage(); + + public ComponentStorable() { + super(new StringStorable()); + addAction(new SetComponentAction()); + } + + @Override + protected Component wrap(String real) { + return miniMessage.deserialize(real); + } + + @Override + protected String unwrap(Component value) { + return miniMessage.serialize(value); + } +} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/SetComponentAction.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/SetComponentAction.java new file mode 100644 index 0000000..801f6e2 --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/component/SetComponentAction.java @@ -0,0 +1,21 @@ +package site.lab0x13.scrow.configurator.storable.impl.component; + +import de.kentoj.scrow.bukkit.command.ScrowBukkitCC; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import site.lab0x13.scrow.commands.model.argument.Argument; +import site.lab0x13.scrow.commands.model.argument.types.StringArgumentType; +import site.lab0x13.scrow.configurator.action.SetAction; + +class SetComponentAction extends SetAction { + @Override + protected Argument argument() { + return Argument.builder("miniMessage", new StringArgumentType()) + .build(); + } + + @Override + protected Component valueToPrettyString(String value) { + return MiniMessage.miniMessage().deserialize(value); + } +} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListStorable.java deleted file mode 100644 index bb75a8a..0000000 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListStorable.java +++ /dev/null @@ -1,82 +0,0 @@ -package site.lab0x13.scrow.configurator.storable.impl.list; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.action.StorableAction; -import site.lab0x13.scrow.configurator.storable.AbstractStorable; -import site.lab0x13.scrow.configurator.storable.Storable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; -import java.util.stream.IntStream; - -import static java.util.Objects.requireNonNull; - -public class ListStorable> extends AbstractStorable> { - - private final Supplier newElementSupplier; - private final List> actions; - - public ListStorable(Supplier newElementSupplier) { - this.newElementSupplier = newElementSupplier; - actions = super.actions(); - actions.add(new ListExpandAction<>()); - } - - @Override - public List> actions() { - return actions; - } - - @Override - public @NotNull List value() { - var v = super.value(); - if (v == null) { - v = new ArrayList<>(); - value(v); - } - return v; - } - - /** - * Expands the list with a default value storable - */ - public S expandList() { - var newElement = newElementSupplier.get(); - requireNonNull(value()).add(newElement); - return newElement; - } - - @Override - public JsonElement toJson() { - var out = new JsonArray(); - requireNonNull(value()).forEach(storable -> out.add(storable.toJson())); - return out; - } - - @Override - public void loadJson(JsonElement json) { - var in = json.getAsJsonArray(); - in.forEach(jsonElement -> expandList().loadJson(jsonElement)); - } - - @Override - public List subFieldNames() { - var v = value(); - if (v == null) return Collections.emptyList(); - return IntStream.range(0, v.size()) - .mapToObj(Integer::toString) - .toList(); - } - - @Override - public @Nullable Storable getSubField(String name) { - var v = value(); - if (v == null) return null; - return v.get(Integer.parseInt(name)); - } -} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationStorable.java index 605a0ec..04a01ff 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationStorable.java @@ -1,26 +1,18 @@ package site.lab0x13.scrow.configurator.storable.impl.location; import org.bukkit.Location; -import site.lab0x13.scrow.configurator.action.StorableAction; import site.lab0x13.scrow.configurator.storable.complex.ComplexStorable; -import site.lab0x13.scrow.configurator.storable.primitive.DoubleStorable; -import site.lab0x13.scrow.configurator.storable.primitive.FloatStorable; -import site.lab0x13.scrow.configurator.storable.primitive.PrimitiveStorable; import site.lab0x13.scrow.configurator.storable.complex.FieldReader; import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; import site.lab0x13.scrow.configurator.storable.impl.world.WorldStorable; +import site.lab0x13.scrow.configurator.storable.primitive.DoubleStorable; +import site.lab0x13.scrow.configurator.storable.primitive.FloatStorable; -import java.util.ArrayList; -import java.util.List; - -public class LocationStorable extends ComplexStorable { - - private final List> actions; +public final class LocationStorable extends ComplexStorable { public LocationStorable() { - actions = new ArrayList<>(super.actions()); - actions.add(new LocationPickCurrentAction()); - actions.add(new LocationTeleportAction()); + addAction(new LocationPickCurrentAction()); + addAction(new LocationTeleportAction()); } @Override @@ -33,11 +25,6 @@ public class LocationStorable extends ComplexStorable { registry.registerField("pitch", new FloatStorable(), Location::getPitch); } - @Override - public List> actions() { - return actions; - } - @Override protected Location construct(FieldReader reader) { return new Location( diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationTeleportAction.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationTeleportAction.java index 83109ac..69bf3ba 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationTeleportAction.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/location/LocationTeleportAction.java @@ -8,6 +8,9 @@ import site.lab0x13.scrow.configurator.storable.Storable; import java.util.Collections; import java.util.List; +import java.util.Objects; + +import static java.util.Objects.*; class LocationTeleportAction implements StorableAction> { @@ -28,8 +31,7 @@ class LocationTeleportAction implements StorableAction node, ScrowBukkitCC ctx) { - assert node.value() != null; - ctx.player().teleport(node.value()); + ctx.player().teleport(requireNonNull(node.value())); ctx.player().sendMessage(ctx.style().ok("You've been teleported.")); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/SetWorldAction.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/SetWorldAction.java index a8494b0..a934b95 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/SetWorldAction.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/SetWorldAction.java @@ -1,38 +1,21 @@ package site.lab0x13.scrow.configurator.storable.impl.world; import de.kentoj.scrow.bukkit.command.ScrowBukkitCC; +import net.kyori.adventure.text.Component; import org.bukkit.World; import site.lab0x13.scrow.commands.bukkit.type.WorldArgumentType; import site.lab0x13.scrow.commands.model.argument.Argument; -import site.lab0x13.scrow.configurator.action.StorableAction; -import site.lab0x13.scrow.configurator.storable.Storable; +import site.lab0x13.scrow.configurator.action.SetAction; -import java.util.List; - -class SetWorldAction implements StorableAction> { - - private final Argument arg = - Argument.builder("world", new WorldArgumentType()).build(); +class SetWorldAction extends SetAction { @Override - public String name() { - return "set"; + protected Component valueToPrettyString(World value) { + return Component.text(value.getName() + "(" + value.getUID() + ")"); } @Override - public List> arguments(Storable __) { - return List.of(arg); - } - - @Override - public boolean requiresValue() { - return false; - } - - @Override - public void execute(Storable node, ScrowBukkitCC ctx) { - World world = ctx.getArg(arg); - node.value(world); - ctx.sender().sendMessage(ctx.style().ok("Set world to " + world.getName() + "(" + world.getUID() + ")")); + protected Argument argument() { + return Argument.builder("world", new WorldArgumentType()).build(); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/WorldStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/WorldStorable.java index 45737cf..3ca4926 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/WorldStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/world/WorldStorable.java @@ -9,7 +9,7 @@ import site.lab0x13.scrow.configurator.storable.primitive.StringStorable; import java.util.ArrayList; import java.util.List; -public class WorldStorable extends WrappedStorable { +public final class WorldStorable extends WrappedStorable { private final List> actions; diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListExpandAction.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListExpandAction.java similarity index 93% rename from core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListExpandAction.java rename to core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListExpandAction.java index 3e9678d..0c14f84 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/impl/list/ListExpandAction.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListExpandAction.java @@ -1,4 +1,4 @@ -package site.lab0x13.scrow.configurator.storable.impl.list; +package site.lab0x13.scrow.configurator.storable.list; import de.kentoj.scrow.bukkit.command.ScrowBukkitCC; import site.lab0x13.scrow.commands.model.argument.Argument; diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListStorable.java new file mode 100644 index 0000000..3b62ba7 --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/list/ListStorable.java @@ -0,0 +1,95 @@ +package site.lab0x13.scrow.configurator.storable.list; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.action.StorableAction; +import site.lab0x13.scrow.configurator.storable.AbstractStorable; +import site.lab0x13.scrow.configurator.storable.Storable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +public class ListStorable extends AbstractStorable> { + + private final Supplier> newElementSupplier; + private final List> actions; + private final List> _value; + + public ListStorable(Supplier> newElementSupplier) { + this.newElementSupplier = newElementSupplier; + _value = new ArrayList<>(); + value(new ArrayList<>()); + actions = super.actions(); + actions.add(new ListExpandAction<>()); + } + + @Override + public void value(@Nullable List value) { + super.value(value != null ? value : new ArrayList<>()); + } + + @Override + public List> actions() { + return actions; + } + + /** + * Expands the list with a default value storable + */ + public Storable expandList() { + var newElement = newElementSupplier.get(); + newElement.subscribe(this::updateValue); + _value.add(newElement); + updateValue(); + return newElement; + } + + public void deleteAt(int index) { + _value.remove(index); + updateValue(); + } + + @Override + public JsonElement toJsonSafe() { + var out = new JsonArray(); + _value.forEach(storable -> out.add(storable.toJson())); + return out; + } + + @Override + public void loadJsonSafe(JsonElement json) { + var in = json.getAsJsonArray(); + _value.clear(); + in.forEach(jsonElement -> { + var element = newElementSupplier.get(); + element.loadJson(jsonElement); + _value.add(element); + }); + updateValue(); + } + + @Override + public List subFieldNames() { + return IntStream.range(0, _value.size()) + .mapToObj(Integer::toString) + .toList(); + } + + @Override + public @Nullable Storable getSubField(String name) { + int index; + try { + index = Integer.parseInt(name); + } catch (NumberFormatException _) { + return null; + } + return _value.get(index); + } + + private void updateValue() { + setValueSilently(_value.stream().map(Storable::value).toList()); + } +} diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/BooleanStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/BooleanStorable.java index 3261450..bf00e74 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/BooleanStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/BooleanStorable.java @@ -2,9 +2,13 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; -public class BooleanStorable extends PrimitiveStorable{ +public final class BooleanStorable extends PrimitiveStorable{ + public BooleanStorable(boolean defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsBoolean); + } + public BooleanStorable() { - super(false, JsonPrimitive::new, JsonPrimitive::getAsBoolean); + this(false); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/ByteStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/ByteStorable.java index e0da049..c6e3b19 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/ByteStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/ByteStorable.java @@ -2,9 +2,13 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; -public class ByteStorable extends PrimitiveStorable{ +public final class ByteStorable extends PrimitiveStorable{ + public ByteStorable(byte defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsByte); + } + public ByteStorable() { - super((byte)0, JsonPrimitive::new, JsonPrimitive::getAsByte); + this((byte) 0); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/DoubleStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/DoubleStorable.java index 34d197d..fd31786 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/DoubleStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/DoubleStorable.java @@ -2,9 +2,13 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; -public class DoubleStorable extends PrimitiveStorable{ +public final class DoubleStorable extends PrimitiveStorable{ + public DoubleStorable(double defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsDouble); + } + public DoubleStorable() { - super(0D, JsonPrimitive::new, JsonPrimitive::getAsDouble); + this(0); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/FloatStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/FloatStorable.java index 81353b2..069d615 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/FloatStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/FloatStorable.java @@ -2,9 +2,13 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; -public class FloatStorable extends PrimitiveStorable{ +public final class FloatStorable extends PrimitiveStorable{ + public FloatStorable(float defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsFloat); + } + public FloatStorable() { - super(0F, JsonPrimitive::new, JsonPrimitive::getAsFloat); + this(0); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/IntStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/IntStorable.java index dd54121..0d98135 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/IntStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/IntStorable.java @@ -2,9 +2,12 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; -public class IntStorable extends PrimitiveStorable{ +public final class IntStorable extends PrimitiveStorable{ + public IntStorable(int defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsInt); + } public IntStorable() { - super(0, JsonPrimitive::new, JsonPrimitive::getAsInt); + this(0); } } diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/LongStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/LongStorable.java new file mode 100644 index 0000000..71df75e --- /dev/null +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/LongStorable.java @@ -0,0 +1,14 @@ +package site.lab0x13.scrow.configurator.storable.primitive; + +import com.google.gson.JsonPrimitive; + +public final class LongStorable extends PrimitiveStorable { + public LongStorable(long defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsLong); + } + + public LongStorable() { + this(0); + } +} + diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/PrimitiveStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/PrimitiveStorable.java index a1ee704..013a749 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/PrimitiveStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/PrimitiveStorable.java @@ -13,9 +13,9 @@ import java.util.function.Function; public class PrimitiveStorable extends AbstractStorable { - private final V defaultValue; private final Function serializer; private final Function deserializer; + private final V defaultValue; public PrimitiveStorable(V defaultValue, Function serializer, Function deserializer) { this.defaultValue = defaultValue; @@ -24,23 +24,13 @@ public class PrimitiveStorable extends AbstractStorable { } @Override - public JsonElement toJson() { - if (value() == null) - return JsonNull.INSTANCE; + public JsonElement toJsonSafe() { return serializer.apply(value()); } @Override - public void loadJson(JsonElement json) { - if (json.isJsonNull()) - value(defaultValue); - else - value(deserializer.apply(json.getAsJsonPrimitive())); - } - - @Override - public void value(@Nullable V value) { - super.value(value == null ? defaultValue : value); + public void loadJsonSafe(JsonElement json) { + value(deserializer.apply(json.getAsJsonPrimitive())); } @Override diff --git a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/StringStorable.java b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/StringStorable.java index 0fdb8a5..8b9a9de 100644 --- a/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/StringStorable.java +++ b/core/configurator/main/java/site/lab0x13/scrow/configurator/storable/primitive/StringStorable.java @@ -1,10 +1,22 @@ package site.lab0x13.scrow.configurator.storable.primitive; import com.google.gson.JsonPrimitive; +import site.lab0x13.scrow.configurator.action.StorableAction; + +import java.util.List; + +public final class StringStorable extends PrimitiveStorable{ + public StringStorable(String defaultValue) { + super(defaultValue, JsonPrimitive::new, JsonPrimitive::getAsString); + } -public class StringStorable extends PrimitiveStorable{ public StringStorable() { - super("", JsonPrimitive::new, JsonPrimitive::getAsString); + this(""); + } + + @Override + public List> actions() { + return super.actions(); } } diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionGame.java b/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionGame.java index 4931ba0..19ec818 100644 --- a/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionGame.java +++ b/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionGame.java @@ -5,15 +5,30 @@ import de.kentoj.scrow.bukkit.minigame.PlayerManager; import de.kentoj.scrow.bukkit.minigame.phaseflow.LinearPhaseFlow; import de.kentoj.scrowlib.convention.ScrowMessageStyle; import net.kyori.adventure.text.format.TextColor; +import site.lab0x13.scrow.extraction.config.ArenaConfig; import java.util.concurrent.CompletableFuture; +/* +While in the network lobby, players select a kit. then when enough players are found, +players are spread on a map and are tasked to extract some tokens. +extracting a token gives you points, items and a temporary health and speed-boost as well as temporary +wallhacks. extracting a token takes a while and other players want to stop you from doing so, so you'll +need to fight. + */ + public class ExtractionGame implements Minigame { private final ScrowMessageStyle style = new ScrowMessageStyle("PotatoRun", TextColor.color(0x8D7726)); private final PlayerManager playerManager = new PlayerManager(this); private final LinearPhaseFlow phaseFlow = new LinearPhaseFlow("potatoRun.root"); + private final ArenaConfig arenaConfig; + + public ExtractionGame(ArenaConfig arenaConfig) { + this.arenaConfig = arenaConfig; + } + @Override public int minParticipants() { return 0; @@ -37,4 +52,8 @@ public class ExtractionGame implements Minigame { public PlayerManager playerManager() { return playerManager; } + + public ArenaConfig arenaConfig() { + return arenaConfig; + } } diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionPlugin.java b/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionPlugin.java index 3da0648..457e4d8 100644 --- a/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionPlugin.java +++ b/minigame/extraction/site/lab0x13/scrow/extraction/ExtractionPlugin.java @@ -1,29 +1,26 @@ package site.lab0x13.scrow.extraction; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import site.lab0x13.scrow.configurator.configfile.ConfigFile; import site.lab0x13.scrow.configurator.configfile.ConfigFileRegistry; import site.lab0x13.scrow.extraction.config.ExtractionConfig; - -import java.io.IOException; +import site.lab0x13.scrow.extraction.config.ExtractionConfigStorable; public class ExtractionPlugin extends JavaPlugin { - private final Logger log = LoggerFactory.getLogger(ExtractionPlugin.class); + private static final Logger log = LoggerFactory.getLogger(ExtractionPlugin.class); + private static Plugin _plugin; + + public static Plugin plugin() { + return _plugin; + } @Override public void onEnable() { - log.info("loading"); - var cfg = ConfigFile.of(this.getDataPath().resolve("config.json")); - ConfigFileRegistry.get().register(cfg); - cfg.registerStorable("arenas", ExtractionConfig.arenas); - try { - cfg.load(); - } catch (IOException e) { - throw new RuntimeException(e); - } - log.info("loaded"); + _plugin = this; + var config = new ExtractionConfigStorable(this.getDataPath().resolve("config.json")); + ConfigFileRegistry.get().register(config, ExtractionConfig.defaultConfig()); } } \ No newline at end of file diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTarget.java b/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTarget.java new file mode 100644 index 0000000..bbdb7eb --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTarget.java @@ -0,0 +1,10 @@ +package site.lab0x13.scrow.extraction.compass; + +import org.bukkit.Location; + +public interface CompassTarget { + + String targetName(); + + Location location(); +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTargetRegistry.java b/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTargetRegistry.java new file mode 100644 index 0000000..a6a08d3 --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/compass/CompassTargetRegistry.java @@ -0,0 +1,48 @@ +package site.lab0x13.scrow.extraction.compass; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkArgument; + +public class CompassTargetRegistry { + + private final Map targets = new HashMap<>(); + + public void targetNearest(Player player, List targetList) { + var nearest = findNearest(player.getLocation(), targetList); + targets.put(player.getUniqueId(), nearest); + } + + public void setCompassTarget(Player player, @Nullable CompassTarget compassTarget) { + if (compassTarget == null) + targets.remove(player.getUniqueId()); + else + targets.put(player.getUniqueId(), compassTarget); + } + + public @Nullable CompassTarget getCompassTarget(Player player) { + return targets.get(player.getUniqueId()); + } + + private static CompassTarget findNearest(Location location, List targetList) { + checkArgument(!targetList.isEmpty(), "targetList is empty"); + + CompassTarget nearest = targetList.getFirst(); + double nearestDistanceSquared = Double.MAX_VALUE; + for (var target : targetList) { + var targetDistanceSquared = target.location().distanceSquared(location); + if (targetDistanceSquared < nearestDistanceSquared) { + nearestDistanceSquared = targetDistanceSquared; + nearest = target; + } + } + return nearest; + } +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfig.java b/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfig.java index 204961a..e568b2f 100644 --- a/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfig.java +++ b/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfig.java @@ -1,11 +1,19 @@ package site.lab0x13.scrow.extraction.config; +import net.kyori.adventure.text.Component; import org.bukkit.Location; -import site.lab0x13.scrow.configurator.storable.Storable; +import site.lab0x13.scrow.extraction.extractionpoint.ExtractionPoint; +import java.util.ArrayList; import java.util.List; public record ArenaConfig( - List> spawnLocations + String id, + Component name, + List spawnLocations, + List extractionPoints ) { + public static ArenaConfig defaultConfig() { + return new ArenaConfig("", Component.empty(), new ArrayList<>(), new ArrayList<>()); + } } diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfigStorable.java b/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfigStorable.java index eb85905..8f84720 100644 --- a/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfigStorable.java +++ b/minigame/extraction/site/lab0x13/scrow/extraction/config/ArenaConfigStorable.java @@ -3,18 +3,31 @@ package site.lab0x13.scrow.extraction.config; import site.lab0x13.scrow.configurator.storable.complex.ComplexStorable; import site.lab0x13.scrow.configurator.storable.complex.FieldReader; import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; -import site.lab0x13.scrow.configurator.storable.impl.list.ListStorable; +import site.lab0x13.scrow.configurator.storable.impl.component.ComponentStorable; import site.lab0x13.scrow.configurator.storable.impl.location.LocationStorable; +import site.lab0x13.scrow.configurator.storable.list.ListStorable; +import site.lab0x13.scrow.configurator.storable.primitive.StringStorable; public class ArenaConfigStorable extends ComplexStorable { @Override protected void registerFields(FieldRegistry registry) { - registry.registerField("spawnLocations", new ListStorable<>(LocationStorable::new), ArenaConfig::spawnLocations); + registry.registerField("id", new StringStorable(), ArenaConfig::id); + registry.registerField("name", new ComponentStorable(), ArenaConfig::name); + registry.registerField("spawnLocations", + new ListStorable<>(LocationStorable::new), ArenaConfig::spawnLocations); + registry.registerField("extractionPoints", + new ListStorable<>(ExtractionPointStorable::new), + ArenaConfig::extractionPoints); } @Override protected ArenaConfig construct(FieldReader reader) { - return new ArenaConfig(reader.read("spawnLocations")); + return new ArenaConfig( + reader.read("id"), + reader.read("name"), + reader.read("spawnLocations"), + reader.read("extractionPoints") + ); } } diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfig.java b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfig.java index b97cda3..8a7cae9 100644 --- a/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfig.java +++ b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfig.java @@ -1,10 +1,27 @@ package site.lab0x13.scrow.extraction.config; -import site.lab0x13.scrow.configurator.storable.impl.list.ListStorable; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; -public final class ExtractionConfig { - private ExtractionConfig() { +public record ExtractionConfig( + List arenas, + Duration extractionDuration +) { + private static ExtractionConfig instance; + + public static ExtractionConfig defaultConfig() { + return new ExtractionConfig( + new ArrayList<>(), + Duration.ofMillis(100) + ); } - public static final ListStorable arenas = new ListStorable<>(ArenaConfigStorable::new); + public ExtractionConfig { + instance = this; + } + + public static ExtractionConfig get() { + return instance; + } } diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfigStorable.java b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfigStorable.java new file mode 100644 index 0000000..275b62a --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionConfigStorable.java @@ -0,0 +1,29 @@ +package site.lab0x13.scrow.extraction.config; + +import site.lab0x13.scrow.configurator.configfile.ConfigFile; +import site.lab0x13.scrow.configurator.storable.complex.FieldReader; +import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; +import site.lab0x13.scrow.configurator.storable.impl.DurationStorable; +import site.lab0x13.scrow.configurator.storable.list.ListStorable; + +import java.nio.file.Path; + +public class ExtractionConfigStorable extends ConfigFile { + public ExtractionConfigStorable(Path path) { + super(path); + } + + @Override + protected void registerFields(FieldRegistry registry) { + registry.registerField("arenas", new ListStorable<>(ArenaConfigStorable::new), ExtractionConfig::arenas); + registry.registerField("extractionDuration", new DurationStorable(), ExtractionConfig::extractionDuration); + } + + @Override + protected ExtractionConfig construct(FieldReader reader) { + return new ExtractionConfig( + reader.read("arenas"), + reader.read("extractionDuration") + ); + } +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionPointStorable.java b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionPointStorable.java new file mode 100644 index 0000000..d77276a --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/config/ExtractionPointStorable.java @@ -0,0 +1,19 @@ +package site.lab0x13.scrow.extraction.config; + +import site.lab0x13.scrow.configurator.storable.complex.ComplexStorable; +import site.lab0x13.scrow.configurator.storable.complex.FieldReader; +import site.lab0x13.scrow.configurator.storable.complex.FieldRegistry; +import site.lab0x13.scrow.configurator.storable.impl.location.LocationStorable; +import site.lab0x13.scrow.extraction.extractionpoint.ExtractionPoint; + +public class ExtractionPointStorable extends ComplexStorable { + @Override + protected void registerFields(FieldRegistry registry) { + registry.registerField("location", new LocationStorable(), ExtractionPoint::location); + } + + @Override + protected ExtractionPoint construct(FieldReader reader) { + return new ExtractionPoint(reader.read("location")); + } +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionPoint.java b/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionPoint.java new file mode 100644 index 0000000..dd40a80 --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionPoint.java @@ -0,0 +1,33 @@ +package site.lab0x13.scrow.extraction.extractionpoint; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import site.lab0x13.scrow.extraction.compass.CompassTarget; +import site.lab0x13.scrow.extraction.config.ExtractionConfig; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public final class ExtractionPoint implements CompassTarget { + private final Location location; + private final Map extractionProgress = new HashMap<>(); + + public ExtractionPoint(Location location) { + this.location = location; + } + + public ExtractionProgress getProgress(Player player) { + return extractionProgress.computeIfAbsent(player.getUniqueId(), _ -> new ExtractionProgress()); + } + + @Override + public String targetName() { + return "Extraction Point"; + } + + @Override + public Location location() { + return location; + } +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionProgress.java b/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionProgress.java new file mode 100644 index 0000000..a803990 --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/extractionpoint/ExtractionProgress.java @@ -0,0 +1,89 @@ +package site.lab0x13.scrow.extraction.extractionpoint; + +import org.bukkit.scheduler.BukkitRunnable; +import site.lab0x13.scrow.extraction.ExtractionPlugin; +import site.lab0x13.scrow.extraction.config.ExtractionConfig; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; + +/** + * You extract by repeatedly calling {@link ExtractionProgress#advanceExtraction()} + * with little delay in between. + * If the delay is too long, progress is reset. + */ +public class ExtractionProgress { + + /** + * ticks(= 50ms) after which progress will be reset if {@link ExtractionProgress#advanceExtraction()} + * isn't called. + */ + private static final int MAX_DELAY_TICKS = 2; + + private final List> changeSubscribers = new ArrayList<>(); + private final long requiredMs; + /** + * System.currentTimeMillis() at first click (in this attempt to extract) + * or < 0 if extracting hasn't been attempted recently. + */ + private long clickingSince = -1; + /** + * System.currentTimeMillis() at last click + */ + private long lastClickMs = 0; + + /** + * @param extractionTime duration for which progress must be called repeatedly + */ + public ExtractionProgress(Duration extractionTime) { + this.requiredMs = extractionTime.toMillis(); + } + + public ExtractionProgress() { + this(ExtractionConfig.get().extractionDuration()); + } + + public void advanceExtraction() { + if (clickingSince < 0) + clickingSince = System.currentTimeMillis(); + lastClickMs = System.currentTimeMillis(); + notifyChangeSubscribers(); + + /* reset if not called again for a while */ + new BukkitRunnable() { + final long lastClickMsAtStart = lastClickMs; + + @Override + public void run() { + if (lastClickMsAtStart == lastClickMs) + resetProgress(); + } + }.runTaskLater(ExtractionPlugin.plugin(), MAX_DELAY_TICKS); + } + + public double progress() { + if (lastClickMs == 0) + return 0; + var delta = System.currentTimeMillis() - requiredMs; + return Math.max(1.0, (double) delta / (double) requiredMs); + } + + public void resetProgress() { + clickingSince = -1; + lastClickMs = 0; + notifyChangeSubscribers(); + } + + public void subscribeChange(Consumer onChange) { + changeSubscribers.add(onChange); + } + + private void notifyChangeSubscribers() { + var progress = progress(); + changeSubscribers.forEach(sub -> sub.accept(progress)); + } +} diff --git a/minigame/extraction/site/lab0x13/scrow/extraction/phase/MidgamePhase.java b/minigame/extraction/site/lab0x13/scrow/extraction/phase/MidgamePhase.java new file mode 100644 index 0000000..b886677 --- /dev/null +++ b/minigame/extraction/site/lab0x13/scrow/extraction/phase/MidgamePhase.java @@ -0,0 +1,30 @@ +package site.lab0x13.scrow.extraction.phase; + +import de.kentoj.scrow.bukkit.minigame.phase.Phase; +import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import site.lab0x13.scrow.extraction.ExtractionGame; +import site.lab0x13.scrow.extraction.compass.CompassTargetRegistry; + +public class MidgamePhase extends Phase { + + private final CompassTargetRegistry compassTargetRegistry; + + protected MidgamePhase(PhaseContext ctx, CompassTargetRegistry compassTargetRegistry) { + super(ctx); + this.compassTargetRegistry = compassTargetRegistry; + } + + @Override + protected void onStart() { + ctx.players().participants().forEach(player -> { + compassTargetRegistry.targetNearest(player, ctx.game().arenaConfig().extractionPoints()); + player.getInventory().addItem(ItemStack.of(Material.COMPASS)); + }); + } + + @Override + protected void onCancel() { + } +}