Skip to content

Commit

Permalink
Add data-driven guides.
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Jan 20, 2025
1 parent a748a9b commit 68ea0c1
Show file tree
Hide file tree
Showing 37 changed files with 713 additions and 138 deletions.
23 changes: 20 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ base {
archivesName = "guideme"
}

sourceSets {
main {
resources {
srcDir 'src/generated/resources'
}
}
testmod
}

configurations {
localRuntimeOnly
shaded {
Expand Down Expand Up @@ -60,6 +69,8 @@ dependencies {
testImplementation("org.assertj:assertj-core")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
testImplementation("net.neoforged:testframework:${neoforge_version}")

testmodRuntimeOnly sourceSets.main.output
}

///////////////////
Expand Down Expand Up @@ -101,12 +112,18 @@ Map<String, String> commonSystemProperties = [
]

neoForge {
version = project.neoforge_version
enable {
version = project.neoforge_version
enabledSourceSets = [sourceSets.main, sourceSets.testmod]
}

mods {
guideme {
sourceSet sourceSets.main
}
testmod {
sourceSet sourceSets.testmod
}
guidemetest {
sourceSet sourceSets.main
sourceSet sourceSets.test
Expand All @@ -117,9 +134,9 @@ neoForge {
configureEach {
gameDirectory = project.file('run')
systemProperties = commonSystemProperties
// property "mixin.debug.export", "true"

loadedMods = [mods.guideme]
loadedMods = [mods.guideme, mods.testmod]
sourceSet = sourceSets.testmod

additionalRuntimeClasspathConfiguration.extendsFrom configurations.shaded
additionalRuntimeClasspathConfiguration.extendsFrom configurations.guideExportOnly
Expand Down
25 changes: 14 additions & 11 deletions src/generated/resources/assets/guideme/lang/en_us.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
{
"ae2.guidebook.Close": "Close",
"ae2.guidebook.ContentFrom": "Content from",
"ae2.guidebook.HideAnnotations": "Hide Annotations",
"ae2.guidebook.HistoryGoBack": "Go back one page",
"ae2.guidebook.HistoryGoForward": "Go forward one page",
"ae2.guidebook.HoldToShow": "Hold [%s] to open guide",
"ae2.guidebook.ResetView": "Reset View",
"ae2.guidebook.Search": "Search",
"ae2.guidebook.ShowAnnotations": "Show Annotations",
"ae2.guidebook.ZoomIn": "Zoom In",
"ae2.guidebook.ZoomOut": "Zoom Out"
"guideme.guidebook.Close": "Close",
"guideme.guidebook.ContentFrom": "Content from",
"guideme.guidebook.HideAnnotations": "Hide Annotations",
"guideme.guidebook.HistoryGoBack": "Go back one page",
"guideme.guidebook.HistoryGoForward": "Go forward one page",
"guideme.guidebook.HoldToShow": "Hold [%s] to open guide",
"guideme.guidebook.ItemInvalidGuideId": "Invalid guide id set: %s",
"guideme.guidebook.ItemNoGuideId": "No guide id set",
"guideme.guidebook.ResetView": "Reset View",
"guideme.guidebook.Search": "Search",
"guideme.guidebook.ShowAnnotations": "Show Annotations",
"guideme.guidebook.ZoomIn": "Zoom In",
"guideme.guidebook.ZoomOut": "Zoom Out",
"item.guideme.guide": "Guide"
}
3 changes: 3 additions & 0 deletions src/generated/resources/assets/guideme/models/item/guide.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"loader": "guideme:guide_item_dispatcher"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "guideme:item/guide"
}
}
2 changes: 2 additions & 0 deletions src/main/java/guideme/Guide.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ static GuideBuilder builder(ResourceLocation id) {

ResourceLocation getId();

ResourceLocation getStartPage();

String getDefaultNamespace();

String getContentRootFolder();
Expand Down
96 changes: 29 additions & 67 deletions src/main/java/guideme/GuideBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
import guideme.indices.CategoryIndex;
import guideme.indices.ItemIndex;
import guideme.indices.PageIndex;
import guideme.internal.GuideOnStartup;
import guideme.internal.GuideRegistry;
import guideme.internal.MutableGuide;
import guideme.internal.extensions.DefaultExtensions;
import guideme.screen.GlobalInMemoryHistory;
import guideme.screen.GuideScreen;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
Expand All @@ -19,26 +17,16 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.neoforge.client.event.ScreenEvent;
import net.neoforged.neoforge.common.NeoForge;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GuideBuilder {
private static final Logger LOG = LoggerFactory.getLogger(GuideBuilder.class);

private final ResourceLocation id;
private final Map<Class<?>, PageIndex> indices = new IdentityHashMap<>();
private final ExtensionCollection.Builder extensionsBuilder = ExtensionCollection.builder();
private String defaultNamespace;
private String folder;
@Nullable
private ResourceLocation startupPage;
private boolean validateAtStartup;
private ResourceLocation startPage;
private Path developmentSourceFolder;
private String developmentSourceNamespace;
private boolean watchDevelopmentSources = true;
Expand All @@ -47,21 +35,13 @@ public class GuideBuilder {
private final Set<ExtensionPoint<?>> disableDefaultsForExtensionPoints = Collections
.newSetFromMap(new IdentityHashMap<>());
private boolean register = true;
private GuideItemSettings itemSettings = GuideItemSettings.DEFAULT;

GuideBuilder(ResourceLocation id) {
this.id = Objects.requireNonNull(id, "id");
this.defaultNamespace = id.getNamespace();
this.folder = id.getPath();

var startupPageProperty = getSystemPropertyName(id, "startupPage");
try {
var startupPageIdText = System.getProperty(startupPageProperty);
if (startupPageIdText != null) {
this.startupPage = ResourceLocation.parse(startupPageIdText);
}
} catch (Exception e) {
LOG.error("Specified invalid page id in system property {}", startupPageProperty);
}
this.folder = "guides/" + id.getNamespace() + "/" + id.getPath();
this.startPage = ResourceLocation.fromNamespaceAndPath(defaultNamespace, "index.md");

// Development sources folder
var devSourcesFolderProperty = getSystemPropertyName(id, "sources");
Expand Down Expand Up @@ -151,30 +131,11 @@ public GuideBuilder disableDefaultExtensions(ExtensionPoint<?> extensionPoint) {
}

/**
* Sets the page that should be shown directly after launching the client. This will cause a datapack reload to
* happen automatically so that recipes can be shown. This page can also be set via system property
* <code>guideDev.&lt;FOLDER>.startupPage</code>, where &lt;FOLDER> is replaced with the folder given to
* {@link Guide#builder}.
* <p/>
* Setting the page to null will disable this feature and overwrite a page set via system properties.
*/
public GuideBuilder startupPage(@Nullable ResourceLocation pageId) {
this.startupPage = pageId;
return this;
}

/**
* Enables or disables validation of all discovered pages at startup. This will cause a datapack reload to happen
* automatically so that recipes can be validated. This page can also be set via system property
* <code>guideDev.&lt;FOLDER>.validateAtStartup</code>, where &lt;FOLDER> is replaced with the folder given to
* {@link Guide#builder}.
* <p/>
* Changing this setting overrides the system property.
* <p/>
* Validation results will be written to the log.
* Set the page to show when this guide is being opened without any previous page or target page. Defaults to
* {@code index.md} in the {@link #defaultNamespace(String) default namespace}.
*/
public GuideBuilder validateAllAtStartup(boolean enable) {
this.validateAtStartup = enable;
public GuideBuilder startPage(ResourceLocation pageId) {
this.startPage = pageId;
return this;
}

Expand Down Expand Up @@ -239,36 +200,37 @@ public <T extends Extension> GuideBuilder extension(ExtensionPoint<T> extensionP
return this;
}

/**
* Configure the generic guide item provided by GuideME. If you are using this code API to register your guide, you
* are encouraged to register your own guide item instead of using the generic one.
*/
public GuideBuilder itemSettings(GuideItemSettings settings) {
this.itemSettings = settings;
return this;
}

/**
* Creates the guide.
*/
public MutableGuide build() {
var extensionCollection = buildExtensions();

var guide = new MutableGuide(id, defaultNamespace, folder, developmentSourceFolder, developmentSourceNamespace,
indices, extensionCollection, availableToOpenHotkey);
var guide = new MutableGuide(
id,
defaultNamespace,
folder,
startPage,
developmentSourceFolder,
developmentSourceNamespace,
indices,
extensionCollection,
availableToOpenHotkey,
itemSettings);

if (developmentSourceFolder != null && watchDevelopmentSources) {
guide.watchDevelopmentSources();
}

if (validateAtStartup || startupPage != null) {
var guideOpenedOnce = new MutableBoolean(false);
NeoForge.EVENT_BUS.addListener((ScreenEvent.Opening e) -> {
if (e.getNewScreen() instanceof TitleScreen && !guideOpenedOnce.booleanValue()) {
guideOpenedOnce.setTrue();
GuideOnStartup.runDatapackReload();
if (validateAtStartup) {
guide.validateAll();
}
if (startupPage != null) {
e.setNewScreen(GuideScreen.openNew(guide, PageAnchor.page(startupPage),
GlobalInMemoryHistory.INSTANCE));
}
}
});
}

if (register) {
GuideRegistry.registerStatic(guide);
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/guideme/GuideItemSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package guideme;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.List;
import java.util.Optional;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.resources.ResourceLocation;

/**
* Configuration settings for the automatically generated guide item.
*/
public record GuideItemSettings(Optional<Component> displayName,
List<Component> tooltipLines,
Optional<ResourceLocation> itemModel) {
public static GuideItemSettings DEFAULT = new GuideItemSettings(Optional.empty(), List.of(), Optional.empty());

public static Codec<GuideItemSettings> CODEC = RecordCodecBuilder.create(
builder -> builder.group(
ComponentSerialization.CODEC.optionalFieldOf("display_name")
.forGetter(GuideItemSettings::displayName),
ComponentSerialization.CODEC.listOf().optionalFieldOf("tooltip_lines", List.of())
.forGetter(GuideItemSettings::tooltipLines),
ResourceLocation.CODEC.optionalFieldOf("model").forGetter(GuideItemSettings::itemModel))
.apply(builder, GuideItemSettings::new));
}
11 changes: 11 additions & 0 deletions src/main/java/guideme/Guides.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package guideme;

import guideme.internal.GuideMECommon;
import guideme.internal.GuideRegistry;
import java.util.Collection;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
Expand All @@ -20,4 +22,13 @@ public static Collection<? extends Guide> getAll() {
public static Guide getById(ResourceLocation id) {
return GuideRegistry.getById(id);
}

/**
* Create a generic guide item that will open the given guide.
*/
public static ItemStack createGuideItem(ResourceLocation guideId) {
var stack = new ItemStack(GuideMECommon.GUIDE_ITEM.get());
stack.set(GuideMECommon.GUIDE_ID_COMPONENT, guideId);
return stack;
}
}
Loading

0 comments on commit 68ea0c1

Please sign in to comment.