Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Corey

Members
  • Joined

  • Last visited

Posts posted by Corey

  1. ·

    Edited by Corey

    Container.slotChangedCraftingGrid has been updated to use Forge's overloaded RecipeManager.getRecipe, which accepts a RecipeType, and it is restricted to VanillaRecipeTypes.CRAFTING.  This seems to mean that the only way to craft non-vanilla shaped/shapeless recipes in the inventory crafting grid or the crafting table is to replace those containers with custom ones which override slotChangedCraftingGrid.  Fortunately for me, I'm already wrapping that container for other reasons, but this definitely doesn't seem ideal...

     

    Edit:

    I've implemented it in the way I described above, and it works, but it's not a good solution.

  2. Posted

    I'm using quite a few custom recipe types, and after updating from Forge 107 to 191, none of them seem to be working.  The way I'm registering them hasn't changed.  I took a quick look through the Forge commits since then (and follow Forge development pretty closely anyway) and I didn't see anything that seemed related.  Has something changed?

     

    Note: I added logging, and the recipes get loaded and deserialized fine, but the matches method is never called when I try to craft things.

     

    IRecipe/Serializer:

    package lordmonoxide.gradient.recipes;
    
    import com.google.gson.JsonObject;
    import lordmonoxide.gradient.progress.Age;
    import lordmonoxide.gradient.utils.AgeUtils;
    import net.minecraft.inventory.IInventory;
    import net.minecraft.inventory.InventoryCrafting;
    import net.minecraft.item.ItemStack;
    import net.minecraft.item.ItemTool;
    import net.minecraft.item.crafting.IRecipe;
    import net.minecraft.item.crafting.IRecipeSerializer;
    import net.minecraft.item.crafting.Ingredient;
    import net.minecraft.item.crafting.RecipeSerializers;
    import net.minecraft.item.crafting.ShapelessRecipe;
    import net.minecraft.network.PacketBuffer;
    import net.minecraft.util.JsonUtils;
    import net.minecraft.util.NonNullList;
    import net.minecraft.util.ResourceLocation;
    import net.minecraft.world.World;
    import net.minecraftforge.common.ForgeHooks;
    import net.minecraftforge.common.crafting.RecipeType;
    import net.minecraftforge.event.ForgeEventFactory;
    
    import java.util.Random;
    
    public class AgeGatedShapelessToolRecipe implements IRecipe {
      private static final Random rand = new Random();
    
      private final ShapelessRecipe recipe;
      public final Age age;
    
      public AgeGatedShapelessToolRecipe(final ShapelessRecipe recipe, final Age age) {
        this.recipe = recipe;
        this.age = age;
      }
    
      @Override
      public boolean matches(final IInventory inv, final World world) {
        return AgeUtils.playerMeetsAgeRequirement((InventoryCrafting)inv, this.age) && this.recipe.matches(inv, world);
      }
    
      @Override
      public ItemStack getCraftingResult(final IInventory inv) {
        return this.recipe.getCraftingResult(inv);
      }
    
      @Override
      public boolean canFit(final int width, final int height) {
        return this.recipe.canFit(width, height);
      }
    
      @Override
      public ItemStack getRecipeOutput() {
        return this.recipe.getRecipeOutput();
      }
    
      @Override
      public NonNullList<ItemStack> getRemainingItems(final IInventory inv) {
        final NonNullList<ItemStack> remaining = IRecipe.super.getRemainingItems(inv);
    
        for(int i = 0; i < remaining.size(); ++i) {
          final ItemStack stack = inv.getStackInSlot(i);
    
          if(stack.getItem() instanceof ItemTool) {
            stack.attemptDamageItem(1, rand, null);
    
            if(stack.isDamageable() && stack.getDamage() > stack.getMaxDamage()) {
              ForgeEventFactory.onPlayerDestroyItem(ForgeHooks.getCraftingPlayer(), stack, null);
              remaining.set(i, ItemStack.EMPTY);
            } else {
              remaining.set(i, stack.copy());
            }
          }
        }
    
        return remaining;
      }
    
      @Override
      public NonNullList<Ingredient> getIngredients() {
        return this.recipe.getIngredients();
      }
    
      @Override
      public boolean isDynamic() {
        return this.recipe.isDynamic();
      }
    
      @Override
      public String getGroup() {
        return this.recipe.getGroup();
      }
    
      @Override
      public ResourceLocation getId() {
        return this.recipe.getId();
      }
    
      @Override
      public IRecipeSerializer<?> getSerializer() {
        return GradientRecipeSerializers.SHAPELESS;
      }
    
      @Override
      public RecipeType<? extends IRecipe> getType() {
        return GradientRecipeTypes.SHAPELESS;
      }
    
      public static final class Serializer implements IRecipeSerializer<AgeGatedShapelessToolRecipe> {
        @Override
        public AgeGatedShapelessToolRecipe read(final ResourceLocation recipeId, final JsonObject json) {
          final ShapelessRecipe recipe = RecipeSerializers.CRAFTING_SHAPELESS.read(recipeId, json);
          final Age age = Age.get(JsonUtils.getInt(json, "age", 1));
    
          return new AgeGatedShapelessToolRecipe(recipe, age);
        }
    
        @Override
        public AgeGatedShapelessToolRecipe read(final ResourceLocation recipeId, final PacketBuffer buffer) {
          final ShapelessRecipe recipe = RecipeSerializers.CRAFTING_SHAPELESS.read(recipeId, buffer);
          final Age age = Age.get(buffer.readVarInt());
    
          return new AgeGatedShapelessToolRecipe(recipe, age);
        }
    
        @Override
        public void write(final PacketBuffer buffer, final AgeGatedShapelessToolRecipe recipe) {
          RecipeSerializers.CRAFTING_SHAPELESS.write(buffer, recipe.recipe);
          buffer.writeVarInt(recipe.age.value());
        }
    
        @Override
        public ResourceLocation getName() {
          return GradientRecipeTypes.SHAPELESS.getId();
        }
      }
    }

     

    Type registration:

    package lordmonoxide.gradient.recipes;
    
    import lordmonoxide.gradient.GradientMod;
    import net.minecraftforge.common.crafting.RecipeType;
    
    public final class GradientRecipeTypes {
      private GradientRecipeTypes() { }
    
      public static final RecipeType<AgeGatedShapedToolRecipe>    SHAPED    = RecipeType.get(GradientMod.resource("shaped"), AgeGatedShapedToolRecipe.class);
      public static final RecipeType<AgeGatedShapelessToolRecipe> SHAPELESS = RecipeType.get(GradientMod.resource("shapeless"), AgeGatedShapelessToolRecipe.class);
      public static final RecipeType<DryingRecipe>                DRYING    = RecipeType.get(GradientMod.resource("drying"), DryingRecipe.class);
      public static final RecipeType<FirePitRecipe>               FIREPIT   = RecipeType.get(GradientMod.resource("firepit"), FirePitRecipe.class);
      public static final RecipeType<FuelRecipe>                  FUEL      = RecipeType.get(GradientMod.resource("fuel"), FuelRecipe.class);
      public static final RecipeType<GrindingRecipe>              GRINDING  = RecipeType.get(GradientMod.resource("grinding"), GrindingRecipe.class);
      public static final RecipeType<HardeningRecipe>             HARDENING = RecipeType.get(GradientMod.resource("hardening"), HardeningRecipe.class);
      public static final RecipeType<MixingRecipe>                MIXING    = RecipeType.get(GradientMod.resource("mixing"), MixingRecipe.class);
    }

     

    Serializer registration

    package lordmonoxide.gradient.recipes;
    
    import net.minecraft.item.crafting.IRecipeSerializer;
    import net.minecraft.item.crafting.RecipeSerializers;
    
    public final class GradientRecipeSerializers {
      private GradientRecipeSerializers() { }
    
      public static final IRecipeSerializer<AgeGatedShapedToolRecipe>    SHAPED    = RecipeSerializers.register(new AgeGatedShapedToolRecipe.Serializer());
      public static final IRecipeSerializer<AgeGatedShapelessToolRecipe> SHAPELESS = RecipeSerializers.register(new AgeGatedShapelessToolRecipe.Serializer());
      public static final IRecipeSerializer<DryingRecipe>                DRYING    = RecipeSerializers.register(new DryingRecipe.Serializer());
      public static final IRecipeSerializer<FirePitRecipe>               FIREPIT   = RecipeSerializers.register(new FirePitRecipe.Serializer());
      public static final IRecipeSerializer<FuelRecipe>                  FUEL      = RecipeSerializers.register(new FuelRecipe.Serializer());
      public static final IRecipeSerializer<GrindingRecipe>              GRINDING  = RecipeSerializers.register(new GrindingRecipe.Serializer());
      public static final IRecipeSerializer<HardeningRecipe>             HARDENING = RecipeSerializers.register(new HardeningRecipe.Serializer());
      public static final IRecipeSerializer<MixingRecipe>                MIXING    = RecipeSerializers.register(new MixingRecipe.Serializer());
    }

     

    Recipe example:

    {
      "type": "gradient:shapeless",
      "age": 1,
      "result": {
        "item": "gradient:twine"
      },
      "ingredients": [{
        "item": "gradient:fibre"
      }, {
        "item": "gradient:fibre"
      }, {
        "item": "gradient:fibre"
      }, {
        "item": "gradient:fibre"
      }]
    }

     

  3. Posted

    I'm having trouble figuring out how to access data from datapacks.  Reading this leads me to believe that IResourceManager.getAllResourceLocations handles all relevant datapack cascading, etc., but it only seems to be giving me the contents of my assets dir.

     

    Here's the code I was using to test it:

      private void setupClient(final FMLClientSetupEvent event) {
        this.registerResourceLoader((IReloadableResourceManager)event.getMinecraftSupplier().get().getResourceManager());
      }
    
      private void setupServer(final FMLDedicatedServerSetupEvent event) {
        this.registerResourceLoader(event.getServerSupplier().get().getResourceManager());
      }
    
      private void registerResourceLoader(final IReloadableResourceManager resourceManager) {
        resourceManager.addReloadListener(manager -> {
          LOGGER.info("METALS:");
          manager.getAllResourceLocations("metals", s -> true).forEach(LOGGER::info);
        });
      }

    It lists files in assets/gradient-core/metals, but doesn't list files in data/gradient-core/metals.

     

    Side question, is Minecraft.getResourceManager always an instance of IReloadableResourceManager?

  4. Okay, here's what I ultimately tried:

    1. Delete entire gradle cache
    2. Freshly clone project
    3. Run ./gradlew genIntellijRuns
    4. Step 3 again as per https://gist.github.com/mcenderdragon/6c7af2daf6f72b0cadf0c63169a87583 (is that what you mean when you say "Import the project twice"?)
    5. Open the project in IntelliJ
    6. Add a breakpoint in my code on a call to code in vanilla/forge
    7. Run in debug mode, trigger breakpoint, press F7 to step into vanilla/forge code

    The same thing happens as in my screenshot above, with the source not matching the bytecode.  The source jar is attached and I can browse through it normally, but it doesn't match in debug mode.

  5. Posted

    I'm having this issue when debugging.  It's happening the same in both of my environments, so I'm assuming it's actually a problem with the source jar.  If I remove the source jar, IntelliJ will decompile the classes and debugging works fine, but obviously I lose documentation and coherent variable names, so that's not ideal.  I haven't see any other mention of this, is it a known issue?1895924529_2019-02-08(3).thumb.png.3e48f1ed70f670478b90d5142e8354e1.png

  6. I figured that was probably the case, but I have another handler that does this... shouldn't it fail too, since it's accessing the client's world in the same way?

      public static class Handler implements IMessageHandler<PacketUpdateHeatNeighbours, IMessage> {
        @Override
        @Nullable
        public IMessage onMessage(final PacketUpdateHeatNeighbours packet, final MessageContext ctx) {
          Minecraft.getMinecraft().addScheduledTask(() -> {
            final TileEntity te = Minecraft.getMinecraft().world.getTileEntity(packet.entityPos);
    
            if(!(te instanceof HeatSinker)) {
              return;
            }
    
            ((HeatSinker)te).updateSink(packet.updatePos);
          });
    
          return null;
        }
      }

     

  7. Posted

    I'm having an issue where my (client-only) packet handler is failing on the server.  I've narrowed it down to the world.isBlockLoaded if statement, but I can't figure out why this is an issue or what to do about it.

    CHANNEL.registerMessage(PacketSyncEnergyNetwork.Handler.class, PacketSyncEnergyNetwork.class, id++, Side.CLIENT);
      public static class Handler implements IMessageHandler<PacketSyncEnergyNetwork, IMessage> {
        @Nullable
        @Override
        public IMessage onMessage(final PacketSyncEnergyNetwork message, final MessageContext ctx) {
          Minecraft.getMinecraft().addScheduledTask(() -> {
            for(final Long2FloatMap.Entry entry : message.state.entries()) {
              final long serialized = entry.getLongKey();
              final float energy = entry.getFloatValue();
    
              final BlockPos pos = BlockPosUtils.getBlockPosFromSerialized(serialized);
    
              final World world = Minecraft.getMinecraft().world;
    
              if(world.isBlockLoaded(pos)) {
                //
              }
            }
          });
    
          return null;
        }
      }

     

  8. Posted ·

    Edited by Corey

    I have unit tests written for my project that I typically run through my IDE (and assumed ran automatically in my CI environment since it runs gradlew check) but it turns out running check or test isn't actually running my tests.  I have a pretty standard setup - tests are located in <project root>/src/test/java/<package>/...

     

    Here's my build.gradle:

    buildscript {
      repositories {
        jcenter()
        maven { url = "http://files.minecraftforge.net/maven" }
      }
      dependencies {
        classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
      }
    }
    apply plugin: 'net.minecraftforge.gradle.forge'
    //Only edit below this line, the above code adds and enables the nessasary things for Forge to be setup.
    
    ext.configFile = file "build.properties"
    configFile.withReader {
      def prop = new Properties()
      prop.load(it)
      project.ext.config = new ConfigSlurper().parse prop
    }
    
    version = config.version
    group= "lordmonoxide.gradient" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
    archivesBaseName = "gradient"
    
    sourceCompatibility = targetCompatibility = "1.8" // Need this here so eclipse task generates correctly.
    compileJava {
      sourceCompatibility = targetCompatibility = "1.8"
    }
    
    minecraft {
      version = config.mc_version + "-" + config.forge_version
      runDir = "run"
    
      mappings = config.mappings_version
    }
    
    repositories {
      maven {
        //IC2
        name = "player-ic2"
        url = "http://maven.ic2.player.to/"
      }
    
      maven {
        name = "BuildCraft"
        url = "http://www.mod-buildcraft.com/maven"
      }
    
      maven {
        // JEI
        name = "Progwml6 maven"
        url = "http://dvs1.progwml6.com/files/maven"
      }
    }
    
    dependencies {
      // compile against the JEI API but do not include it at runtime
      deobfProvided 'mezz.jei:jei_1.12.2:4.13.1.220:api'
      // at runtime, use the full JEI jar
      runtime 'mezz.jei:jei_1.12.2:4.13.1.220'
    
      testCompile "org.junit.jupiter:junit-jupiter-api:5.3.2"
    }
    
    processResources {
      // this will ensure that this task is redone when the versions change.
      inputs.property "version", project.version
      inputs.property "mcversion", project.minecraft.version
    
      // replace stuff in mcmod.info, nothing else
      from(sourceSets.main.resources.srcDirs) {
        include 'mcmod.info'
    
        // replace version and mcversion
        expand 'version':project.version, 'mcversion':project.minecraft.version
      }
    
      // copy everything else, thats not the mcmod.info
      from(sourceSets.main.resources.srcDirs) {
        exclude 'mcmod.info'
      }
    }
    

     

    I'm pretty unfamiliar with gradle, am I missing anything?

     

    Edit: just realised there's a gradle subforum, not sure if this belongs here or there...

  9. I scan the recipe registry for matching recipes (drying around a firepit).  It's not a huge deal since I only have to scan if I find a firepit around the block, I was mostly just curious.  I need a player reference so I can check if the player has the ability to do it.

     

    I'm assuming PlaceEvent only fires when players place blocks, since getPlayer is nonnull, but I'm not concerned about automation in the early game anyway.

Important Information

By using this site, you agree to our Terms of Use.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.