Implement data gen

This commit is contained in:
FatherToast 2022-06-20 12:37:58 -05:00
parent 3fac9d5013
commit 85e1825e3c
13 changed files with 147 additions and 34 deletions

View file

@ -56,7 +56,7 @@ minecraft {
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
args '--mod', 'deadlyworld', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
args '--mod', 'specialmobs', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
mods {
deadlyworld {

View file

@ -6,7 +6,8 @@ import fathertoast.specialmobs.common.util.AnnotationHelper;
import fathertoast.specialmobs.common.util.References;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block;
import net.minecraft.entity.*;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.monster.CreeperEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@ -39,7 +40,8 @@ public class MobFamily<T extends LivingEntity> {
public static final MobFamily<CreeperEntity> CREEPER = new MobFamily<>(
"Creeper", "creepers", 0x0da70b, new EntityType[] { EntityType.CREEPER },
"Dark"//, "Death", "Dirt", "Doom", "Drowning", "Ender", "Fire", "Gravel", "Jumping", "Lightning", "Mini", "Splitting"
"Dark"//, "Death", "Dirt", "Doom", "Drowning", "Ender", "Fire", "Gravel", "Jumping", "Lightning",
// "Mini", "Scope", "Splitting"
);
// public static final MobFamily<ZombieEntity> ZOMBIE = new MobFamily<>(

View file

@ -57,6 +57,22 @@ public @interface SpecialMob {
@Target( ElementType.METHOD )
@interface AttributeCreator { }
/**
* REQUIRED. This is called during data generation to build the mod's default lang files.
* <p>
* The annotated method must have a signature that follows the pattern:
* <p>
* public static String[] METHOD_NAME( String langKey )
* <p>
* The returned string array should be created by References#translations using the given lang key as the first
* argument. Always be sure that any non-ASCII characters used are properly handled by the translations method.
*
* @see fathertoast.specialmobs.common.util.References#translations(String, String, String, String, String, String, String, String)
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
@interface LanguageProvider { }
/**
* REQUIRED. This is called during data generation to build the mob's default loot table. Special variants will
* typically start this method by calling their vanilla replacement's implementation of this method.

View file

@ -100,7 +100,7 @@ public class SpecialMobs {
//packetHandler.registerMessages();
//MinecraftForge.EVENT_BUS.register( new NAEventListener() );
//MinecraftForge.EVENT_BUS.register( new SMEventListener() );
IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();

View file

@ -2,6 +2,7 @@ package fathertoast.specialmobs.common.entity.creeper;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
@ -9,7 +10,6 @@ import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.monster.CreeperEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
@ -44,6 +44,12 @@ public class DarkCreeperEntity extends _SpecialCreeperEntity {
return _SpecialCreeperEntity.createAttributes();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Dark Creeper",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
@ -74,6 +80,7 @@ public class DarkCreeperEntity extends _SpecialCreeperEntity {
// Add unaffected light sources to the explosion's affected area
// Note that this does NOT simulate another explosion, instead just directly searches for and targets lights
//TODO this doesn't seem to work; figure out new method
final BlockPos center = new BlockPos( getX(), getY(), getZ() );
final int radius = explosionRadius * 4 * (isPowered() ? 2 : 1);
final BlockPos.Mutable pos = new BlockPos.Mutable();

View file

@ -49,6 +49,12 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
return CreeperEntity.createAttributes();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Creeper",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void addBaseLoot( LootTableBuilder loot ) {
loot.addLootTable( "main", EntityType.CREEPER.getDefaultLootTable() );

View file

@ -43,7 +43,15 @@ public final class AnnotationHelper {
/** Creates an attribute modifier map from a special mob species. Throws an exception if anything goes wrong. */
public static AttributeModifierMap createAttributes( MobFamily.Species<?> species )
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return ((AttributeModifierMap.MutableAttribute) getMethod( species.entityClass, SpecialMob.AttributeCreator.class ).invoke( null )).build();
return ((AttributeModifierMap.MutableAttribute) getMethod( species.entityClass, SpecialMob.AttributeCreator.class )
.invoke( null )).build();
}
/** Gets the translations from a special mob species. Throws an exception if anything goes wrong. */
public static String[] getTranslations( MobFamily.Species<?> species )
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return (String[]) getMethod( species.entityClass, SpecialMob.LanguageProvider.class )
.invoke( null, species.entityType.get().getDescriptionId() );
}
/** Builds a loot table from a special mob species. Throws an exception if anything goes wrong. */

View file

@ -60,4 +60,30 @@ public final class References {
public static final String TAG_DRY_EXPLODE = "CannotExplodeWhileWet";
public static final String TAG_WHEN_BURNING_EXPLODE = "ExplodesWhileBurning";
public static final String TAG_WHEN_SHOT_EXPLODE = "ExplodesWhenShot";
//--------------- INTERNATIONALIZATION ----------------
/** This method provides helper tags to make linking translations up easier, and also enforces the correct array length. */
public static String[] translations( String key, String en, String es, String pt, String fr, String it, String de, String pir ) {
// Note that this must match up EXACTLY to the TranslationKey enum in SMLanguageProvider
String[] translation = { key, en, es, pt, fr, it, de, pir };
// Fix the encoding to allow us to use accented characters in the translation string literals
// Note: If a translation uses any non-ASCII characters, make sure they are all in this matrix! (case-sensitive)
final String[][] utf8ToUnicode = {
{ "à", "\u00E0" }, { "á", "\u00E1" }, { "ã", "\u00E3" }, { "ä", "\u00E4" },
{ "ç", "\u00E7" },
{ "è", "\u00E8" }, { "é", "\u00E9" }, { "ê", "\u00EA" },
{ "í", "\u00ED" },
{ "ó", "\u00F3" }, { "õ", "\u00F5" }, { "ö", "\u00F6" },
{ "ù", "\u00F9" }, { "û", "\u00FB" }, { "ü", "\u00FC" },
{ "œ", "\u0153" }
};
for( int i = 1; i < translation.length; i++ ) {
for( String[] fix : utf8ToUnicode )
translation[i] = translation[i].replace( fix[0], fix[1] ); // Note: This is kinda dumb, but it works so idc
}
return translation;
}
}

View file

@ -16,6 +16,7 @@ public class DataGatherListener {
DataGenerator generator = event.getGenerator();
if( event.includeClient() ) {
generator.addProvider( new SMItemModelProvider( generator, event.getExistingFileHelper() ) );
for( Map.Entry<String, SMLanguageProvider.TranslationKey> entry : SMLanguageProvider.LANG_CODE_MAP.entrySet() ) {
generator.addProvider( new SMLanguageProvider( generator, entry.getKey(), entry.getValue() ) );
}

View file

@ -0,0 +1,23 @@
package fathertoast.specialmobs.datagen;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.data.DataGenerator;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
public class SMItemModelProvider extends ItemModelProvider {
public SMItemModelProvider( DataGenerator gen, ExistingFileHelper existingFileHelper ) {
super( gen, SpecialMobs.MOD_ID, existingFileHelper );
}
@Override
protected void registerModels() {
// Bestiary-generated spawn egg models
final ResourceLocation spawnEggParent = modLoc( ITEM_FOLDER + "/template_sm_spawn_egg" );
for( MobFamily.Species<?> species : MobFamily.getAllSpecies() )
withExistingParent( species.spawnEgg.getId().getPath(), spawnEggParent );
}
}

View file

@ -1,9 +1,14 @@
package fathertoast.specialmobs.datagen;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.util.AnnotationHelper;
import fathertoast.specialmobs.common.util.References;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.LanguageProvider;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
public class SMLanguageProvider extends LanguageProvider {
@ -18,29 +23,6 @@ public class SMLanguageProvider extends LanguageProvider {
TranslationKey( String id ) { code = id; }
}
/** This method provides helper tags to make linking translations up easier, and also enforces the correct array length. */
private static String[] translations( String key, String en, String es, String pt, String fr, String it, String de, String pir ) {
// Note that this must match up EXACTLY to the TranslationKey enum above
String[] translation = { key, en, es, pt, fr, it, de, pir };
// Fix the encoding to allow us to use accented characters in the translation string literals
// Note: If a translation uses any non-ASCII characters, make sure they are all in this matrix! (case-sensitive)
final String[][] utf8ToUnicode = {
{ "à", "\u00E0" }, { "á", "\u00E1" }, { "ã", "\u00E3" }, { "ä", "\u00E4" },
{ "ç", "\u00E7" },
{ "è", "\u00E8" }, { "é", "\u00E9" }, { "ê", "\u00EA" },
{ "í", "\u00ED" },
{ "ó", "\u00F3" }, { "õ", "\u00F5" }, { "ö", "\u00F6" },
{ "ù", "\u00F9" }, { "û", "\u00FB" }, { "ü", "\u00FC" },
{ "œ", "\u0153" }
};
for( int i = 1; i < translation.length; i++ ) {
for( String[] fix : utf8ToUnicode )
translation[i] = translation[i].replace( fix[0], fix[1] ); // Note: This is kinda dumb, but it works so idc
}
return translation;
}
/**
* Matrix linking the actual translations to their lang key.
* <p>
@ -50,15 +32,29 @@ public class SMLanguageProvider extends LanguageProvider {
*
* @see #addTranslations()
*/
@SuppressWarnings( "SpellCheckingInspection" )
private static final String[][] TRANSLATIONS = {
// NYI
};
private static final String[][] TRANSLATIONS;
/** Maps which translation key each lang code uses, allowing multiple lang codes to use the same translations. */
public static final HashMap<String, TranslationKey> LANG_CODE_MAP = new HashMap<>();
static {
final ArrayList<String[]> translationList = new ArrayList<>();
final String[] spawnEggTranslationPattern = References.translations( "%s", "%s Spawn Egg",
"%s", "%s", "%s", "%s", "%s", "%s" ); //TODO
// Bestiary-generated translations
for( MobFamily.Species<?> species : MobFamily.getAllSpecies() ) {
final String[] speciesTranslations = getTranslations( species );
String[] spawnEggTranslations = format( spawnEggTranslationPattern, speciesTranslations );
spawnEggTranslations[0] = species.spawnEgg.get().getDescriptionId();
translationList.add( speciesTranslations );
translationList.add( spawnEggTranslations );
}
TRANSLATIONS = translationList.toArray( new String[0][0] );
// Assign all specific locales to the translation we want to use
mapAll( TranslationKey.ENGLISH, "us" ); // We can ignore other English locales, en_us is the fallback for all languages
mapAll( TranslationKey.SPANISH, "es", "ar", "cl", "ec", "mx", "uy", "ve" );
@ -85,6 +81,25 @@ public class SMLanguageProvider extends LanguageProvider {
SpecialMobs.LOG.info( "Translation key verification complete!" );
}
/** Gets the translations for a specific entity species. */
private static String[] getTranslations( MobFamily.Species<?> species ) {
try {
return AnnotationHelper.getTranslations( species );
}
catch( NoSuchMethodException | InvocationTargetException | IllegalAccessException ex ) {
throw new RuntimeException( "Entity class for " + species.name + " has invalid language provider method", ex );
}
}
/** Applies single argument string formats 1:1 given an array of formats and an array of arguments. */
private static String[] format( String[] formats, String[] args ) {
final String[] formatted = new String[formats.length];
for( int i = 0; i < formatted.length; i++ ) {
formatted[i] = String.format( formats[i], args[i] );
}
return formatted;
}
/** Maps any number of locale codes to a single translation. */
private static void mapAll( TranslationKey translation, String... locales ) {
for( String locale : locales ) {

View file

@ -40,7 +40,9 @@ public class SMLootTableProvider extends LootTableProvider {
/** Validates this mod's loot tables. */
@Override
protected void validate( Map<ResourceLocation, LootTable> tables, ValidationTracker ctx ) {
tables.forEach( ( name, table ) -> LootTableManager.validate( ctx, name, table ) );
// We have to disable validation because vanilla entity loot tables are not recognized;
// this is kinda scary, maybe later we can look into re-enabling validation
//tables.forEach( ( name, table ) -> LootTableManager.validate( ctx, name, table ) );
}
/** Provides all entity loot tables for this mod. */

View file

@ -0,0 +1,7 @@
{
"parent": "item/generated",
"textures": {
"layer0": "minecraft:item/spawn_egg",
"layer1": "minecraft:item/spawn_egg_overlay"
}
}