Skip to content

Instantly share code, notes, and snippets.

@Commoble
Created April 17, 2022 18:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Commoble/458506bbae386dac9432c77b3f8fe7f1 to your computer and use it in GitHub Desktop.
Save Commoble/458506bbae386dac9432c77b3f8fe7f1 to your computer and use it in GitHub Desktop.
Datagenerating worldgen registry jsons in forge for minecraft 1.18.2
package biomecheeser;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import commoble.biomecheeser.BiomeCheeser;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.HashCache;
import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.configurations.BlockPileConfiguration;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.placement.InSquarePlacement;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject;
/* Example of generating data for jsons of worldgen registry objects */
/* 1) use deferred registers and registry objects to register BuiltinRegistries objects such as configured/placed features
* 2) create the RegistryAccess and RegistryOps
* 3) get the relevant registry from the registryaccess
* 4) get the object from that registry
* 5) use the RegistryOps and direct codec to serialize it
*/
@EventBusSubscriber(modid=BiomeCheeser.MODID, bus=Bus.MOD)
public class BiomeCheeserDatagen
{
private static final Logger LOGGER = LogManager.getLogger();
// 1) use deferred registers and registry objects to register BuiltinRegistries objects such as configured/placed features
// (even if you don't use them in java, this is needed for datagen)
public static <T> DeferredRegister<T> makeVanillaDeferredRegister(ResourceKey<Registry<T>> registryKey)
{
DeferredRegister<T> register = DeferredRegister.create(registryKey, BiomeCheeser.MODID);
register.register(FMLJavaModLoadingContext.get().getModEventBus());
return register;
}
private static final DeferredRegister<ConfiguredFeature<?,?>> CONFIGURED_FEATURES = makeVanillaDeferredRegister(Registry.CONFIGURED_FEATURE_REGISTRY);
private static final DeferredRegister<PlacedFeature> PLACED_FEATURES = makeVanillaDeferredRegister(Registry.PLACED_FEATURE_REGISTRY);
public static final String TNT_PILE_NAME = "tnt_pile";
public static final ResourceKey<ConfiguredFeature<?,?>> CONFIGURED_TNT_PILE_KEY = ResourceKey.create(Registry.CONFIGURED_FEATURE_REGISTRY, new ResourceLocation(BiomeCheeser.MODID, TNT_PILE_NAME));
private static final RegistryObject<ConfiguredFeature<?,?>> CONFIGURED_TNT_PILE =
CONFIGURED_FEATURES.register(TNT_PILE_NAME,
() -> new ConfiguredFeature<>(Feature.BLOCK_PILE,
new BlockPileConfiguration(BlockStateProvider.simple(Blocks.TNT))));
public static final ResourceKey<PlacedFeature> PLACED_TNT_PILE_KEY = ResourceKey.create(Registry.PLACED_FEATURE_REGISTRY, new ResourceLocation(BiomeCheeser.MODID, TNT_PILE_NAME));
private static final RegistryObject<PlacedFeature> PLACED_TNT_PILE =
PLACED_FEATURES.register(TNT_PILE_NAME,
() -> new PlacedFeature(CONFIGURED_TNT_PILE.getHolder().get(),
List.of(InSquarePlacement.spread(), PlacementUtils.HEIGHTMAP)));
@SubscribeEvent
public static void onGatherData(GatherDataEvent event)
{
DataGenerator generator = event.getGenerator();
Path outputFolder = generator.getOutputFolder();
// 2) create the RegistryAccess and RegistryOps
RegistryAccess registries = RegistryAccess.builtinCopy();
RegistryOps<JsonElement> ops = RegistryOps.create(JsonOps.INSTANCE, registries);
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
// we use the helper method below to provide a list of resource keys of one registry type to make jsons for
generator.addProvider(
makeBuiltinRegistryProvider(BiomeCheeser.MODID, outputFolder, gson, ops, registries, Registry.CONFIGURED_FEATURE_REGISTRY, ConfiguredFeature.DIRECT_CODEC,
CONFIGURED_TNT_PILE_KEY));
generator.addProvider(
makeBuiltinRegistryProvider(BiomeCheeser.MODID, outputFolder, gson, ops, registries, Registry.PLACED_FEATURE_REGISTRY, PlacedFeature.DIRECT_CODEC,
PLACED_TNT_PILE_KEY));
}
/* Helper method that serializes many objects of the same registry type for us (note the vararg)*/
@SafeVarargs
private static <T> DataProvider makeBuiltinRegistryProvider(String modid, Path outputFolder, Gson gson, RegistryOps<JsonElement> ops, RegistryAccess registries, ResourceKey<Registry<T>> registryKey, Codec<T> codec, ResourceKey<T>... keys)
{
return new DataProvider()
{
@Override
public void run(HashCache cache) throws IOException
{
// 3) get the relevant registry from the registryaccess
// we must retrieve objects from registryaccess and not via our RegistryObject
// (as this is where RegistryOps looks up the id names of things to write out)
Registry<T> registry = registries.registryOrThrow(registryKey);
for (ResourceKey<T> key : keys)
{
ResourceLocation id = key.location();
Path path = outputFolder.resolve(String.join("/", PackType.SERVER_DATA.getDirectory(), id.getNamespace(), registryKey.location().getPath(), id.getPath()+".json"));
// 4) get the object from that registry
T t = registry.getOrThrow(key);
// 5) use the RegistryOps and direct codec to serialize it
// (as of 1.18.2 the indirect codec is no longer the correct codec for datagen)
codec.encodeStart(ops, t)
.resultOrPartial(msg -> LOGGER.error("Failed to encode {}: {}", path, msg))
.ifPresent(json -> {
try
{
DataProvider.save(gson, cache, json, path);
}
catch (IOException e) // we're inside this ifpresent so the throws can't deal with this
{
e.printStackTrace();
}
});
}
}
@Override
public String getName()
{
return modid + " " + registryKey.location().toString();
}
};
}
}
//data/biomecheeser/worldgen/configured_feature/tnt_pile.json
{
"config": {
"state_provider": {
"state": {
"Properties": {
"unstable": "false"
},
"Name": "minecraft:tnt"
},
"type": "minecraft:simple_state_provider"
}
},
"type": "minecraft:block_pile"
}
//data/biomecheeser/worldgen/placed_feature/tnt_pile.json
{
"feature": "biomecheeser:tnt_pile",
"placement": [
{
"type": "minecraft:in_square"
},
{
"heightmap": "MOTION_BLOCKING",
"type": "minecraft:heightmap"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment