Cast, Convert & more
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package io.github.skippyall.minions.docs;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.github.skippyall.minions.registration.MinionRegistries;
|
||||
import net.minecraft.dialog.body.DialogBody;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface DocsEntry {
|
||||
Codec<DocsEntry> CODEC = MinionRegistries.DOCS_ENTRY_TYPES.getCodec().dispatch(DocsEntry::getCodec, Function.identity());
|
||||
|
||||
Metadata getMetadata();
|
||||
|
||||
List<DialogBody> getDialog(DynamicRegistryManager manager);
|
||||
|
||||
MapCodec<? extends DocsEntry> getCodec();
|
||||
|
||||
record Metadata(String titleKey) {
|
||||
public static final MapCodec<Metadata> CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||
instance.group(
|
||||
Codec.STRING.fieldOf("title").forGetter(Metadata::titleKey)
|
||||
).apply(instance, Metadata::new)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package io.github.skippyall.minions.docs;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import io.github.skippyall.minions.Minions;
|
||||
import net.fabricmc.fabric.api.resource.SimpleResourceReloadListener;
|
||||
import net.minecraft.dialog.AfterAction;
|
||||
import net.minecraft.dialog.DialogActionButtonData;
|
||||
import net.minecraft.dialog.DialogButtonData;
|
||||
import net.minecraft.dialog.DialogCommonData;
|
||||
import net.minecraft.dialog.action.DynamicRunCommandDialogAction;
|
||||
import net.minecraft.dialog.action.SimpleDialogAction;
|
||||
import net.minecraft.dialog.type.MultiActionDialog;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.resource.Resource;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.ClickEvent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.StrictJsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class DocsManager implements SimpleResourceReloadListener<Pair<Map<Identifier, DocsEntry>, DocsTree>> {
|
||||
private static Map<Identifier, DocsEntry> docs;
|
||||
private static DocsTree tree;
|
||||
|
||||
public static DocsTree getTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
public static void showDocsEntry(ServerPlayerEntity player, Identifier id) {
|
||||
DocsEntry entry = getDocsEntry(id);
|
||||
if(entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<DialogActionButtonData> buttons = new ArrayList<>();
|
||||
if(tree != null) {
|
||||
DocsTree.DocElement element = tree.getElement(id);
|
||||
if (element.previous() != null) {
|
||||
Identifier previousId = element.previous().getId();
|
||||
buttons.add(getDialogButton(Text.literal("<- ").append(Text.translatable(getDocsEntry(previousId).getMetadata().titleKey())), previousId.toString()));
|
||||
}
|
||||
if (element.next() != null) {
|
||||
Identifier nextId = element.next().getId();
|
||||
buttons.add(getDialogButton(Text.translatable(getDocsEntry(nextId).getMetadata().titleKey()).append(Text.literal(" ->")), nextId.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
buttons.add(new DialogActionButtonData(
|
||||
new DialogButtonData(Text.translatable("gui.ok"), 100),
|
||||
Optional.empty()
|
||||
));
|
||||
|
||||
player.openDialog(RegistryEntry.of(new MultiActionDialog(
|
||||
new DialogCommonData(
|
||||
Text.translatable(entry.getMetadata().titleKey()),
|
||||
Optional.empty(),
|
||||
true,
|
||||
false,
|
||||
AfterAction.CLOSE,
|
||||
entry.getDialog(player.getRegistryManager()),
|
||||
List.of()
|
||||
),
|
||||
buttons,
|
||||
Optional.empty(),
|
||||
2
|
||||
)));
|
||||
}
|
||||
|
||||
private static DialogActionButtonData getDialogButton(Text text, String dialogToOpen) {
|
||||
return new DialogActionButtonData(
|
||||
new DialogButtonData(
|
||||
text, 100
|
||||
),
|
||||
Optional.of(new SimpleDialogAction(new ClickEvent.RunCommand("/minions docs " + dialogToOpen)))
|
||||
);
|
||||
}
|
||||
|
||||
public static DocsEntry getDocsEntry(Identifier id) {
|
||||
return docs.get(id);
|
||||
}
|
||||
|
||||
public static Collection<Identifier> getDocsEntryIds() {
|
||||
return docs.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Pair<Map<Identifier, DocsEntry>, DocsTree>> load(ResourceManager resourceManager, Executor executor) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Map<Identifier, Resource> resources = resourceManager.findResources("docs", id -> id.getNamespace().equals(Minions.MOD_ID) && id.getPath().endsWith(".json"));
|
||||
|
||||
final DocsTree.BranchElement[] root = {null};
|
||||
Map<Identifier, DocsEntry> docsEntries = new HashMap<>();
|
||||
resources.forEach((id, resource) -> {
|
||||
try(Reader reader = resource.getReader()) {
|
||||
if(id.getPath().equals("docs/tree.json")) {
|
||||
DocsTree.BranchElement.CODEC.decode(JsonOps.INSTANCE, StrictJsonParser.parse(reader))
|
||||
.ifSuccess(entry -> root[0] = entry.getFirst())
|
||||
.ifError(error -> Minions.LOGGER.warn("Could not parse docs tree {}: {}", id, error.message()));
|
||||
} else {
|
||||
Identifier docId = Identifier.of(id.getNamespace(), id.getPath().substring("docs/".length(), id.getPath().length() - ".json".length()));
|
||||
DocsEntry.CODEC.decode(JsonOps.INSTANCE, StrictJsonParser.parse(reader))
|
||||
.ifSuccess(entry -> docsEntries.put(docId, entry.getFirst()))
|
||||
.ifError(error -> Minions.LOGGER.warn("Could not parse docs entry {}: {}", id, error.message()));
|
||||
}
|
||||
} catch (IOException | JsonParseException e) {
|
||||
Minions.LOGGER.warn("Could not read file {}", id, e);
|
||||
}
|
||||
});
|
||||
if(root[0] != null) {
|
||||
DocsTree tree = new DocsTree(root[0]);
|
||||
return new Pair<>(docsEntries, tree);
|
||||
} else {
|
||||
return new Pair<>(docsEntries, null);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> apply(Pair<Map<Identifier, DocsEntry>, DocsTree> o, ResourceManager resourceManager, Executor executor) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
docs = o.getLeft();
|
||||
tree = o.getRight();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getFabricId() {
|
||||
return Identifier.of(Minions.MOD_ID, "docs");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package io.github.skippyall.minions.docs;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DocsTree {
|
||||
private final BranchElement root;
|
||||
private final Map<Identifier, DocElement> entries = new HashMap<>();
|
||||
|
||||
public DocsTree(BranchElement root) {
|
||||
this.root = root;
|
||||
initEntries();
|
||||
}
|
||||
|
||||
private void initEntries() {
|
||||
DocElement element = root.first();
|
||||
do {
|
||||
entries.put(element.id, element);
|
||||
element = element.next();
|
||||
} while (element != null);
|
||||
}
|
||||
|
||||
public BranchElement getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public DocElement getElement(Identifier id) {
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
public static abstract sealed class Element {
|
||||
private BranchElement parent;
|
||||
|
||||
public BranchElement getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
void setParent(BranchElement parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public DocElement next() {
|
||||
return parent.next(this);
|
||||
}
|
||||
|
||||
public DocElement previous() {
|
||||
return parent.previous(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class DocElement extends Element {
|
||||
public static final Codec<DocElement> CODEC = Identifier.CODEC.xmap(DocElement::new, DocElement::getId);
|
||||
|
||||
private final Identifier id;
|
||||
|
||||
public DocElement(Identifier element) {
|
||||
this.id = element;
|
||||
}
|
||||
|
||||
public Identifier getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class BranchElement extends Element {
|
||||
public static final Codec<BranchElement> CODEC = Codec.<BranchElement>recursive("DocsBranch", codec ->
|
||||
Codec.either(codec, DocElement.CODEC)
|
||||
.xmap(
|
||||
Either::unwrap,
|
||||
element -> switch (element) {
|
||||
case BranchElement branch -> Either.left(branch);
|
||||
case DocElement doc -> Either.right(doc);
|
||||
}
|
||||
).listOf()
|
||||
.xmap(BranchElement::new, branch -> branch.e)
|
||||
).xmap(BranchElement::setParents, Function.identity());
|
||||
|
||||
private final List<Element> e;
|
||||
|
||||
public BranchElement(List<Element> elements) {
|
||||
e = elements;
|
||||
}
|
||||
|
||||
public DocElement first() {
|
||||
return switch (e.getFirst()) {
|
||||
case BranchElement branch -> branch.first();
|
||||
case DocElement doc -> doc;
|
||||
};
|
||||
}
|
||||
|
||||
public DocElement last() {
|
||||
return switch (e.getLast()) {
|
||||
case BranchElement branch -> branch.last();
|
||||
case DocElement doc -> doc;
|
||||
};
|
||||
}
|
||||
|
||||
public DocElement next(Element current) {
|
||||
int nextIndex = e.indexOf(current) + 1;
|
||||
if(nextIndex < e.size()) {
|
||||
return switch (e.get(nextIndex)) {
|
||||
case BranchElement branch -> branch.first();
|
||||
case DocElement doc -> doc;
|
||||
};
|
||||
} else {
|
||||
if(getParent() != null) {
|
||||
return getParent().next(this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DocElement previous(Element current) {
|
||||
int previousIndex = e.indexOf(current) - 1;
|
||||
if(previousIndex >= 0) {
|
||||
return switch (e.get(previousIndex)) {
|
||||
case BranchElement branch -> branch.first();
|
||||
case DocElement doc -> doc;
|
||||
};
|
||||
} else {
|
||||
if(getParent() != null) {
|
||||
return getParent().previous(this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BranchElement setParents() {
|
||||
for(Element element : e) {
|
||||
element.setParent(this);
|
||||
if(element instanceof BranchElement branch) {
|
||||
branch.setParents();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,81 @@
|
||||
package io.github.skippyall.minions.docs;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.github.skippyall.minions.gui.GuiDisplay;
|
||||
import net.minecraft.dialog.body.DialogBody;
|
||||
import net.minecraft.dialog.body.ItemDialogBody;
|
||||
import net.minecraft.dialog.body.PlainMessageDialogBody;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextCodecs;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record ReferenceEntry(Text shortDescription, Text longDescription) {
|
||||
public static final Codec<ReferenceEntry> CODEC = RecordCodecBuilder.create(instance ->
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public record ReferenceEntry(Metadata metadata, RegistryKey<?> object, Text shortDescription, Text longDescription) implements DocsEntry {
|
||||
private static final Codec<RegistryKey<?>> REGISTRY_KEY_CODEC = RecordCodecBuilder.create(instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("registry").forGetter(RegistryKey::getRegistry),
|
||||
Identifier.CODEC.fieldOf("value").forGetter(RegistryKey::getValue)
|
||||
).apply(instance, (registry, value) -> RegistryKey.of(RegistryKey.ofRegistry(registry), value))
|
||||
);
|
||||
|
||||
public static final MapCodec<ReferenceEntry> CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||
instance.group(
|
||||
Metadata.CODEC.fieldOf("metadata").forGetter(ReferenceEntry::getMetadata),
|
||||
REGISTRY_KEY_CODEC.fieldOf("object").forGetter(ReferenceEntry::object),
|
||||
TextCodecs.CODEC.fieldOf("shortDescription").forGetter(ReferenceEntry::shortDescription),
|
||||
TextCodecs.CODEC.fieldOf("longDescription").forGetter(ReferenceEntry::longDescription)
|
||||
).apply(instance, ReferenceEntry::new));
|
||||
|
||||
@Override
|
||||
public Metadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DialogBody> getDialog(DynamicRegistryManager manager) {
|
||||
List<DialogBody> bodyElements = new ArrayList<>();
|
||||
|
||||
GuiDisplay display = getObjectDisplay(manager);
|
||||
if(display != null) {
|
||||
bodyElements.add(new ItemDialogBody(display.createItemStack(), Optional.empty(), false, false, 16, 16));
|
||||
}
|
||||
bodyElements.add(new PlainMessageDialogBody(Text.translatable(object.getValue().toTranslationKey(object.getRegistry().getPath())), 200));
|
||||
bodyElements.add(new PlainMessageDialogBody(longDescription.copy().append("\n".repeat(100)), 200));
|
||||
|
||||
return bodyElements;
|
||||
}
|
||||
|
||||
public GuiDisplay getObjectDisplay(DynamicRegistryManager manager) {
|
||||
GuiDisplay display = GuiDisplay.DEFAULT_DISPLAY;
|
||||
if(object.isOf(RegistryKeys.ITEM) || object.isOf(RegistryKeys.BLOCK)) {
|
||||
Item item;
|
||||
if(object.isOf(RegistryKeys.ITEM)) {
|
||||
item = Registries.ITEM.get(object.getValue());
|
||||
} else {
|
||||
item = Registries.BLOCK.get(object.getValue()).asItem();
|
||||
}
|
||||
if(item != null) {
|
||||
display = new GuiDisplay.ItemBased(item);
|
||||
}
|
||||
} else {
|
||||
Identifier displayId = object.getValue().withPrefixedPath(object.getRegistry().getPath() + "/");
|
||||
display = GuiDisplay.getGuiDisplay(displayId, manager);
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<? extends DocsEntry> getCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user