preparation for better data pack support

This commit is contained in:
CreepyCre 2021-03-28 19:53:18 +02:00
parent 58d18d9de9
commit c91d0b1b32
5 changed files with 390 additions and 47 deletions

View file

@ -0,0 +1,64 @@
package org.dimdev.dimdoors.api.util;
import net.minecraft.util.Identifier;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
public class Path<K> {
private final ArrayList<K> path;
@SafeVarargs
public Path(K... path) {
this.path = new ArrayList<>(Arrays.asList(path));
}
public Path(List<K> path) {
this.path = new ArrayList<>(path);
}
@SafeVarargs
public final Path<K> subPath(K... subPath) {
ArrayList<K> arrayList = new ArrayList<>(path);
arrayList.addAll(Arrays.asList(subPath));
return new Path<>(arrayList);
}
public Path<K> subPath(Path<K> subPath) {
ArrayList<K> arrayList = new ArrayList<>(path);
arrayList.addAll(subPath.path);
return new Path<>(arrayList);
}
public Queue<K> asQueue() {
return new LinkedList<K>(path);
}
public Optional<K> reduce(BinaryOperator<K> accumulator) {
return path.stream().reduce(accumulator);
}
public K reduce(K identity, BinaryOperator<K> accumulator) {
return path.stream().reduce(identity, accumulator);
}
public <T> T reduce(T identity, BiFunction<T, ? super K, T> accumulator, BinaryOperator<T> combiner) {
return path.stream().reduce(identity, accumulator, combiner);
}
public static Path<String> stringPath(String str) {
return new Path<>(str.split("(?<=[/:])"));
}
public static Path<String> stringPath(Identifier id) {
return stringPath(id.toString());
}
@Override
public String toString() {
return "Path{" +
"path=" + reduce("", (left, right) -> left + ";" + right.toString(), (left, right) -> left + ";" + right) +
'}';
}
}

View file

@ -0,0 +1,267 @@
package org.dimdev.dimdoors.api.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// TODO: someone clean this up please, this implementation seems mediocre at best
public class SimpleTree<K, T> implements Map<Path<K>, T> {
final TreeNode<K, T> entries = new TreeNode<>();
final Class<K> clazz;
public SimpleTree(Class<K> clazz) {
this.clazz = clazz;
}
public Node<K, T> getNode(Path<K> path) {
return entries.getNode(path.asQueue());
}
@Override
public int size() {
return entries.size();
}
@Override
public boolean isEmpty() {
return entries.isEmpty();
}
@Override
public boolean containsKey(Object key) {
Path<K> path = convertKeyToPath(key);
if (path == null) return false;
return entries.getNode(path.asQueue()) != null;
}
@Override
public boolean containsValue(Object value) {
if (!(clazz.isInstance(value))) return false;
return values().contains(value);
}
@Override
public T get(Object key) {
Path<K> path = convertKeyToPath(key);
if (path == null) return null;
return entries.get(path.asQueue());
}
@Override
public T remove(Object key) {
Path<K> path = convertKeyToPath(key);
if (path == null) return null;
return entries.remove(path.asQueue());
}
private Path<K> convertKeyToPath(Object key) {
if (!(key instanceof Path)) return null;
Path<?> pathUnknown = (Path<?>) key;
if (!pathUnknown.asQueue().stream().allMatch(clazz::isInstance)) return null;
return new Path<>(pathUnknown.asQueue().stream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList()));
}
@Override
public void clear() {
entries.clear();
}
@NotNull
@Override
public Set<Path<K>> keySet() {
return entries.keySet();
}
@NotNull
@Override
public Collection<T> values() {
return entries.values();
}
@NotNull
@Override
public Set<Entry<Path<K>, T>> entrySet() {
return entries.entrySet();
}
@Override
public void putAll(@NotNull Map<? extends Path<K>, ? extends T> m) {
m.forEach(this::put);
}
@Nullable
@Override
public T put(Path<K> key, T value) {
return entries.put(key.asQueue(), value);
}
private interface Node<K, T> {
Node<K, T> getNode(Queue<K> path);
T get(Queue<K> path);
T put(Queue<K> path, T entry);
T remove(Queue<K> path);
boolean isEmpty();
int size();
Set<Path<K>> keySet();
Set<Entry<Path<K>, T>> entrySet();
Set<T> values();
}
private static class TreeNode<K, T> implements Node<K, T> {
final Map<K, Node<K, T>> entries = new HashMap<>();
public Node<K, T> getNode(Queue<K> path) {
if (path.peek() == null) return this;
Node<K, T> node = entries.get(path.remove());
if (node == null) return null;
return node.getNode(path);
}
@Override
public T get(Queue<K> path) {
if (path.peek() == null) return null;
Node<K, T> node = entries.get(path.remove());
if (node == null) return null;
return node.get(path);
}
@Override
public T put(Queue<K> path, T entry) { // TODO: better Exception throwing, should propagate up through the stack to SimpleTree object so full path can be included in Exception.
K key = path.poll();
if (key == null) throw new RuntimeException("Cannot set Entry of TreeNode!");
Node<K, T> node = entries.get(key);
if (node != null) return node.put(path, entry);
if (path.peek() == null) {
this.entries.put(key, new EntryNode<>(entry));
} else {
TreeNode<K, T> treeNode = new TreeNode<>();
treeNode.put(path, entry);
entries.put(key, treeNode);
}
return null;
}
@Override
public T remove(Queue<K> path) {
if (path.peek() == null) return null;
K key = path.remove();
Node<K, T> node = entries.get(key);
if (node == null) return null;
T value = node.remove(path);
if (node.isEmpty()) entries.remove(key);
return value;
}
@Override
public boolean isEmpty() {
return entries.isEmpty();
}
@Override
public int size() {
return entries.values().stream().mapToInt(Node::size).sum();
}
@Override
public Set<Path<K>> keySet() {
return entries.entrySet().stream().map(entry -> {
Path<K> key = new Path<>(entry.getKey());
return entry.getValue().keySet().stream().map(key::subPath);
}).reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet());
}
@Override
public Set<Entry<Path<K>, T>> entrySet() {
return entries.entrySet().stream().map(entry -> {
Path<K> key = new Path<>(entry.getKey());
return entry.getValue().entrySet().stream().map(nodeEntry -> new AbstractMap.SimpleEntry<>(key.subPath(nodeEntry.getKey()), nodeEntry.getValue()));
}).reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet());
}
@Override
public Set<T> values() {
return entries.values().stream().map(node -> node.values().stream()).reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet());
}
public void clear() {
entries.clear();
}
}
private static class EntryNode<K, T> implements Node<K, T> {
T entry;
boolean empty = false;
public EntryNode(T entry) {
this.entry = entry;
}
@Override
public Node<K, T> getNode(Queue<K> path) {
if (path.isEmpty()) return this;
return null;
}
@Override
public T get(Queue<K> path) {
if (path.peek() != null) return null;
return entry;
}
@Override
public T put(Queue<K> path, T entry) { // TODO: better Exception throwing, should propagate up through the stack to SimpleTree object so full path can be included in Exception.
if (path.peek() != null) throw new RuntimeException("Cannot set entry further below EntryNode!");
T temp = this.entry;
this.entry = entry;
return temp;
}
@Override
public T remove(Queue<K> path) {
if (path.peek() != null) return null;
T temp = entry;
entry = null;
empty = true;
return temp;
}
@Override
public boolean isEmpty() {
return empty;
}
@Override
public int size() {
return isEmpty() ? 0 : 1;
}
@Override
public Set<Path<K>> keySet() {
return Collections.singleton(new Path<>());
}
@Override
public Set<Entry<Path<K>, T>> entrySet() {
return Collections.singleton(new AbstractMap.SimpleEntry<>(new Path<>(), entry));
}
@Override
public Set<T> values() {
return Collections.singleton(entry);
}
}
}

View file

@ -8,6 +8,8 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.command.CommandSource;
import net.minecraft.util.Identifier;
import org.dimdev.dimdoors.api.util.Path;
import org.dimdev.dimdoors.api.util.SimpleTree;
import org.dimdev.dimdoors.pockets.PocketLoader;
import org.dimdev.dimdoors.pockets.PocketTemplate;
@ -22,7 +24,7 @@ public class PocketTemplateArgumentType implements ArgumentType<PocketTemplate>
@Override
public PocketTemplate parse(StringReader reader) throws CommandSyntaxException {
Identifier value = Identifier.tryParse(Objects.requireNonNull(reader.readString()));
Path<String> value = Path.stringPath(Objects.requireNonNull(reader.readString()));
if (!getPocketTemplates().containsKey(value)) {
// TODO: throw
}
@ -36,10 +38,10 @@ public class PocketTemplateArgumentType implements ArgumentType<PocketTemplate>
@Override
public Collection<String> getExamples() {
return getPocketTemplates().keySet().parallelStream().map(Identifier::toString).map(id -> "\"" + id + "\"").collect(Collectors.toCollection(TreeSet::new));
return getPocketTemplates().keySet().parallelStream().map(path -> path.reduce(String::concat)).map(id -> "\"" + id + "\"").collect(Collectors.toCollection(TreeSet::new));
}
private Map<Identifier, PocketTemplate> getPocketTemplates() {
private SimpleTree<String, PocketTemplate> getPocketTemplates() {
return PocketLoader.getInstance().getTemplates();
}

View file

@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -19,6 +18,8 @@ import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dimdev.dimdoors.api.util.NbtUtil;
import org.dimdev.dimdoors.api.util.Path;
import org.dimdev.dimdoors.api.util.SimpleTree;
import org.dimdev.dimdoors.pockets.generator.PocketGenerator;
import org.dimdev.dimdoors.pockets.virtual.VirtualPocket;
import org.dimdev.dimdoors.api.util.WeightedList;
@ -28,64 +29,72 @@ public class PocketLoader implements SimpleSynchronousResourceReloadListener {
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = new GsonBuilder().setLenient().setPrettyPrinting().create();
private static final PocketLoader INSTANCE = new PocketLoader();
private Map<Identifier, PocketGenerator> pocketGeneratorMap = new ConcurrentHashMap<>();
private Map<Identifier, VirtualPocket> pocketGroups = new ConcurrentHashMap<>();
private Map<Identifier, PocketTemplate> templates = new ConcurrentHashMap<>();
private Map<Identifier, Tag> dataMap = new ConcurrentHashMap<>();
private SimpleTree<String, PocketGenerator> pocketGenerators = new SimpleTree<>(String.class);
private SimpleTree<String, VirtualPocket> pocketGroups = new SimpleTree<>(String.class);
private SimpleTree<String, PocketTemplate> templates = new SimpleTree<>(String.class);
private SimpleTree<String, Tag> dataTree = new SimpleTree<>(String.class);
private PocketLoader() {
}
@Override
public void apply(ResourceManager manager) {
pocketGeneratorMap.clear();
pocketGenerators.clear();
pocketGroups.clear();
templates.clear();
dataMap.clear();
dataTree.clear();
dataMap = loadResourcePathFromJsonToMap(manager, "pockets/json", t -> t).join();
dataTree = loadResourcePathFromJsonToTree(manager, "pockets/json", t -> t).join();
CompletableFuture<Map<Identifier, PocketGenerator>> futurePocketGeneratorMap = loadResourcePathFromJsonToMap(manager, "pockets/generators", this::loadPocketGenerator);
CompletableFuture<Map<Identifier, VirtualPocket>> futurePocketGroups = loadResourcePathFromJsonToMap(manager, "pockets/groups", this::loadPocketGroup);
CompletableFuture<Map<Identifier, PocketTemplate>> futureTemplates = loadResourcePathFromCompressedNbtToMap(manager, "pockets/schematic", ".schem", this::loadPocketTemplate);
CompletableFuture<SimpleTree<String, PocketGenerator>> futurePocketGeneratorMap = loadResourcePathFromJsonToTree(manager, "pockets/generators", this::loadPocketGenerator);
CompletableFuture<SimpleTree<String, VirtualPocket>> futurePocketGroups = loadResourcePathFromJsonToTree(manager, "pockets/groups", this::loadPocketGroup);
CompletableFuture<SimpleTree<String, PocketTemplate>> futureTemplates = loadResourcePathFromCompressedNbtToTree(manager, "pockets/schematic", ".schem", this::loadPocketTemplate);
pocketGeneratorMap = futurePocketGeneratorMap.join();
pocketGenerators = futurePocketGeneratorMap.join();
pocketGroups = futurePocketGroups.join();
templates = futureTemplates.join();
pocketGroups.forEach((path, value) -> System.out.println(path.toString() + ": " + value.toString()));
}
private <T> CompletableFuture<Map<Identifier, T>> loadResourcePathFromJsonToMap(ResourceManager manager, String startingPath, Function<Tag, T> reader) {
private <T> CompletableFuture<SimpleTree<String, T>> loadResourcePathFromJsonToTree(ResourceManager manager, String startingPath, Function<Tag, T> reader) {
int sub = startingPath.endsWith("/") ? 0 : 1;
Collection<Identifier> ids = manager.findResources(startingPath, str -> str.endsWith(".json"));
return CompletableFuture.supplyAsync(() ->
ids.parallelStream().unordered().collect(Collectors.toConcurrentMap(
id -> new Identifier(id.getNamespace(), id.getPath().substring(0, id.getPath().lastIndexOf(".")).substring(startingPath.length() + sub)),
id -> {
try {
JsonElement json = GSON.fromJson(new InputStreamReader(manager.getResource(id).getInputStream()), JsonElement.class);
return reader.apply(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, json));
} catch (IOException e) {
throw new RuntimeException("Error loading resource: " + id);
}
})));
return CompletableFuture.supplyAsync(() -> {
SimpleTree<String, T> tree = new SimpleTree<>(String.class);
tree.putAll(ids.parallelStream().unordered().collect(Collectors.toConcurrentMap(
id -> Path.stringPath(id.getNamespace() + ":" + id.getPath().substring(0, id.getPath().lastIndexOf(".")).substring(startingPath.length() + sub)),
id -> {
try {
JsonElement json = GSON.fromJson(new InputStreamReader(manager.getResource(id).getInputStream()), JsonElement.class);
return reader.apply(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, json));
} catch (IOException e) {
throw new RuntimeException("Error loading resource: " + id);
}
})));
return tree;
});
}
private <T> CompletableFuture<Map<Identifier, T>> loadResourcePathFromCompressedNbtToMap(ResourceManager manager, String startingPath, String extension, Function<CompoundTag, T> reader) {
private <T> CompletableFuture<SimpleTree<String, T>> loadResourcePathFromCompressedNbtToTree(ResourceManager manager, String startingPath, String extension, Function<CompoundTag, T> reader) {
int sub = startingPath.endsWith("/") ? 0 : 1;
Collection<Identifier> ids = manager.findResources(startingPath, str -> str.endsWith(extension));
return CompletableFuture.supplyAsync(() ->
ids.parallelStream().unordered().collect(Collectors.toConcurrentMap(
id -> new Identifier(id.getNamespace(), id.getPath().substring(0, id.getPath().lastIndexOf(".")).substring(startingPath.length() + sub)),
id -> {
try {
return reader.apply(NbtIo.readCompressed(manager.getResource(id).getInputStream()));
} catch (IOException e) {
throw new RuntimeException("Error loading resource: " + id);
}
})));
return CompletableFuture.supplyAsync(() -> {
SimpleTree<String, T> tree = new SimpleTree<>(String.class);
tree.putAll(ids.parallelStream().unordered().collect(Collectors.toConcurrentMap(
id -> Path.stringPath(id.getNamespace() + ":" + id.getPath().substring(0, id.getPath().lastIndexOf(".")).substring(startingPath.length() + sub)),
id -> {
try {
return reader.apply(NbtIo.readCompressed(manager.getResource(id).getInputStream()));
} catch (IOException e) {
throw new RuntimeException("Error loading resource: " + id);
}
})));
return tree;
});
}
// public void load() {
@ -110,7 +119,7 @@ public class PocketLoader implements SimpleSynchronousResourceReloadListener {
// }
public Tag getDataTag(String id) {
return this.dataMap.get(new Identifier(id));
return this.dataTree.get(Path.stringPath(id));
}
public CompoundTag getDataCompoundTag(String id) {
@ -134,27 +143,27 @@ public class PocketLoader implements SimpleSynchronousResourceReloadListener {
}
public WeightedList<PocketGenerator, PocketGenerationContext> getPocketsMatchingTags(List<String> required, List<String> blackList, boolean exact) {
return new WeightedList<>(pocketGeneratorMap.values().stream().filter(pocketGenerator -> pocketGenerator.checkTags(required, blackList, exact)).collect(Collectors.toList()));
return new WeightedList<>(pocketGenerators.values().stream().filter(pocketGenerator -> pocketGenerator.checkTags(required, blackList, exact)).collect(Collectors.toList()));
}
public VirtualPocket getGroup(Identifier group) {
return pocketGroups.get(group);
return pocketGroups.get(Path.stringPath(group));
}
public static PocketLoader getInstance() {
return INSTANCE;
}
public Map<Identifier, PocketTemplate> getTemplates() {
public SimpleTree<String, PocketTemplate> getTemplates() {
return this.templates;
}
public Map<Identifier, VirtualPocket> getPocketGroups() {
public SimpleTree<String, VirtualPocket> getPocketGroups() {
return this.pocketGroups;
}
public PocketGenerator getGenerator(Identifier id) {
return pocketGeneratorMap.get(id);
return pocketGenerators.get(Path.stringPath(id));
}
@Override

View file

@ -9,6 +9,7 @@ import net.minecraft.util.math.Vec3i;
import net.minecraft.world.chunk.Chunk;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dimdev.dimdoors.api.util.Path;
import org.dimdev.dimdoors.block.entity.RiftBlockEntity;
import org.dimdev.dimdoors.pockets.PocketLoader;
import org.dimdev.dimdoors.pockets.PocketTemplate;
@ -66,7 +67,7 @@ public class SchematicGenerator extends LazyPocketGenerator{
@Override
public void generateChunk(LazyGenerationPocket pocket, Chunk chunk) {
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(templateID);
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID));
if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!");
template.place(pocket, chunk, origin, placementType);
setupChunk(pocket, chunk, isSetupLoot());
@ -139,7 +140,7 @@ public class SchematicGenerator extends LazyPocketGenerator{
ServerWorld world = parameters.getWorld();
Map<String, Double> variableMap = parameters.toVariableMap(new HashMap<>());
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(templateID);
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID));
if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!");
Pocket pocket = DimensionalRegistry.getPocketDirectory(world.getRegistryKey()).newPocket(builder);
@ -169,7 +170,7 @@ public class SchematicGenerator extends LazyPocketGenerator{
@Override
public Vec3i getSize(PocketGenerationContext parameters) {
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(templateID);
PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID));
if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!");
Schematic schem = template.getSchematic();
return new Vec3i(schem.getWidth(), schem.getHeight(), schem.getLength());