Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport to 1.21.1: Add Datagen Helper for Creating Modded Datapack Registry Objects and getResourceManager #1917

Open
wants to merge 3 commits into
base: 1.21.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 152 additions & 2 deletions src/main/java/net/neoforged/neoforge/data/event/GatherDataEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package net.neoforged.neoforge.data.event;

import com.google.common.collect.Lists;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
Expand All @@ -13,25 +15,59 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.DetectedVersion;
import net.minecraft.client.resources.ClientPackSource;
import net.minecraft.client.resources.IndexedAssetSource;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.packs.PackLocationInfo;
import net.minecraft.server.packs.PackResources;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.PathPackResources;
import net.minecraft.server.packs.repository.PackSource;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.resources.MultiPackResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.ModList;
import net.neoforged.fml.event.IModBusEvent;
import net.neoforged.fml.loading.FMLEnvironment;
import net.neoforged.neoforge.common.conditions.ICondition;
import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.resource.ResourcePackLoader;
import org.apache.commons.lang3.function.Consumers;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

public class GatherDataEvent extends Event implements IModBusEvent {
private final DataGenerator dataGenerator;
private final DataGeneratorConfig config;
private final ExistingFileHelper existingFileHelper;
private final ModContainer modContainer;

public GatherDataEvent(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper) {
@Nullable
private CompletableFuture<HolderLookup.Provider> registriesWithModdedEntries = null;

public GatherDataEvent(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, final ExistingFileHelper existingFileHelper) {
this.modContainer = mc;
this.dataGenerator = dataGenerator;
this.config = dataGeneratorConfig;
Expand All @@ -42,6 +78,13 @@ public ModContainer getModContainer() {
return this.modContainer;
}

public ResourceManager getResourceManager(PackType packType) {
return switch (packType) {
case CLIENT_RESOURCES -> config.clientResourceManager;
case SERVER_DATA -> config.serverResourceManager;
};
}

public Collection<Path> getInputs() {
return this.config.getInputs();
}
Expand All @@ -55,7 +98,7 @@ public ExistingFileHelper getExistingFileHelper() {
}

public CompletableFuture<HolderLookup.Provider> getLookupProvider() {
return this.config.lookupProvider;
return Objects.requireNonNullElse(this.registriesWithModdedEntries, this.config.lookupProvider);
}

public boolean includeServer() {
Expand All @@ -78,6 +121,7 @@ public boolean validate() {
return this.config.validate;
}

@ApiStatus.Internal
public static class DataGeneratorConfig {
private final Set<String> mods;
private final Path path;
Expand All @@ -90,9 +134,17 @@ public static class DataGeneratorConfig {
private final boolean validate;
private final boolean flat;
private final List<DataGenerator> generators = new ArrayList<>();
private final ResourceManager clientResourceManager;
private final ResourceManager serverResourceManager;

@Deprecated(forRemoval = true)
public DataGeneratorConfig(final Set<String> mods, final Path path, final Collection<Path> inputs, final CompletableFuture<HolderLookup.Provider> lookupProvider,
final boolean server, final boolean client, final boolean dev, final boolean reports, final boolean validate, final boolean flat) {
this(mods, path, inputs, lookupProvider, server, client, dev, reports, validate, flat, null, null, List.of());
}

public DataGeneratorConfig(final Set<String> mods, final Path path, final Collection<Path> inputs, final CompletableFuture<HolderLookup.Provider> lookupProvider,
final boolean server, final boolean client, final boolean dev, final boolean reports, final boolean validate, final boolean flat, final @Nullable String assetIndex, final @Nullable File assetsDir, Collection<Path> existingPacks) {
this.mods = mods;
this.path = path;
this.inputs = inputs;
Expand All @@ -103,6 +155,13 @@ public DataGeneratorConfig(final Set<String> mods, final Path path, final Collec
this.reports = reports;
this.validate = validate;
this.flat = flat;

clientResourceManager = createResourceManager(PackType.CLIENT_RESOURCES, mods::contains, existingPacks, consumer -> {
if (FMLEnvironment.dist.isClient() && assetIndex != null && assetsDir != null)
consumer.accept(ClientPackSource.createVanillaPackSource(IndexedAssetSource.createIndexFs(assetsDir.toPath(), assetIndex)));
});

serverResourceManager = createResourceManager(PackType.SERVER_DATA, mods::contains, existingPacks, consumer -> consumer.accept(ServerPacksSource.createVanillaPackSource()));
}

public Collection<Path> getInputs() {
Expand Down Expand Up @@ -138,5 +197,96 @@ public void runAll() {
}
});
}

private static ResourceManager createResourceManager(PackType packType, Predicate<String> isGeneratedMod, Collection<Path> existingPacks, Consumer<Consumer<PackResources>> consumer) {
var packs = Lists.<PackResources>newArrayList();
// include vanilla resource packs first
consumer.accept(packs::add);

// include existing packs
existingPacks.forEach(path -> {
var packInfo = new PackLocationInfo(path.getFileName().toString(), Component.empty(), PackSource.BUILT_IN, Optional.empty());
packs.add(new PathPackResources(packInfo, path));
});

// include mod resources last
ModList.get().getSortedMods().stream()
// ignore 'minecraft' mod, this is added via `[Server|Client]PackSource`
.filter(Predicate.not(mod -> mod.getModId().equals("minecraft")))
// ignore actively generated models, their resource packs should be included using `--existing <packPath>`
// this is to workaround accidentally including resources being actively generated
.filter(Predicate.not(mod -> isGeneratedMod.test(mod.getModId())))
.map(mod -> {
var owningFile = mod.getModInfo().getOwningFile();
var packInfo = new PackLocationInfo("mod/" + mod.getModId(), Component.empty(), PackSource.BUILT_IN, Optional.empty());
return ResourcePackLoader.createPackForMod(owningFile).openPrimary(packInfo);
})
.forEach(packs::add);

return new MultiPackResourceManager(packType, packs);
}
}

public <T extends DataProvider> T addProvider(T provider) {
return dataGenerator.addProvider(true, provider);
}

public <T extends DataProvider> T createProvider(DataProviderFromOutput<T> builder) {
return addProvider(builder.create(dataGenerator.getPackOutput()));
}

public <T extends DataProvider> T createProvider(DataProviderFromOutputLookup<T> builder) {
return addProvider(builder.create(dataGenerator.getPackOutput(), this.getLookupProvider()));
}

public void createBlockAndItemTags(DataProviderFromOutputLookup<TagsProvider<Block>> blockTagsProvider, ItemTagsProvider itemTagsProvider) {
var blockTags = createProvider(blockTagsProvider);
addProvider(itemTagsProvider.create(this.getGenerator().getPackOutput(), this.getLookupProvider(), blockTags.contentsGetter()));
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder) {
this.createDatapackRegistryObjects(datapackEntriesBuilder, Set.of(this.modContainer.getModId()));
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder, Set<String> modIds) {
this.createDatapackRegistryObjects(datapackEntriesBuilder, Consumers.nop(), modIds);
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder, Map<ResourceKey<?>, List<ICondition>> conditions) {
this.createDatapackRegistryObjects(datapackEntriesBuilder, conditions, Set.of(this.modContainer.getModId()));
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder, Map<ResourceKey<?>, List<ICondition>> conditions, Set<String> modIds) {
var registries = this.createProvider((output, lookupProvider) -> new DatapackBuiltinEntriesProvider(output, lookupProvider, datapackEntriesBuilder, conditions, modIds));
this.registriesWithModdedEntries = registries.getRegistryProvider();
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder, Consumer<BiConsumer<ResourceKey<?>, ICondition>> conditionsBuilder) {
this.createDatapackRegistryObjects(datapackEntriesBuilder, conditionsBuilder, Set.of(this.modContainer.getModId()));
}

public void createDatapackRegistryObjects(RegistrySetBuilder datapackEntriesBuilder, Consumer<BiConsumer<ResourceKey<?>, ICondition>> conditionsBuilder, Set<String> modIds) {
var registries = this.createProvider((output, lookupProvider) -> new DatapackBuiltinEntriesProvider(output, lookupProvider, datapackEntriesBuilder, conditionsBuilder, modIds));
this.registriesWithModdedEntries = registries.getRegistryProvider();
}

@FunctionalInterface
public interface DataProviderFromOutput<T extends DataProvider> {
T create(PackOutput output);
}

@FunctionalInterface
public interface DataProviderFromOutputLookup<T extends DataProvider> {
T create(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider);
}

@FunctionalInterface
public interface GatherDataEventGenerator {
GatherDataEvent create(final ModContainer mc, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig);
}

@FunctionalInterface
public interface ItemTagsProvider {
TagsProvider<Item> create(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, CompletableFuture<TagsProvider.TagLookup<Block>> contentsGetter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static void begin(final Set<String> mods, final Path path, final Collecti
RegistrationEvents.modifyComponents();
CompletableFuture<HolderLookup.Provider> lookupProvider = CompletableFuture.supplyAsync(VanillaRegistries::createLookup, Util.backgroundExecutor());
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, lookupProvider, serverGenerators,
clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat);
clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat, assetIndex, assetsDir, existingPacks);
if (!mods.contains("neoforge")) {
// If we aren't generating data for forge, automatically add forge as an existing so mods can access forge's data
existingMods.add("neoforge");
Expand Down
Loading