-
Posts
402 -
Joined
-
Last visited
Everything posted by IceMetalPunk
-
[1.12] "No Tooltip Defined" in config category tooltips
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Oh... wonderful. Is the same problem the reason @Config.Comment doesn't work, either? And what's the solution in either case? -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Thank you. I've been around some communities where if you post anything in a topic past a certain time, it's considered "necroposting" and bumping, regardless of the content. Glad to know things are a little more lenient around these parts Thanks for all your help -
[1.12] "No Tooltip Defined" in config category tooltips
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
I've added the Github repo / branch link where the code (now somewhat updated) exists into the main post. I'm still getting the "No tooltip defined" issue, regardless of whether I use @Config.Comment or @Config.LangKey for it. -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Thank you; I'll update that topic now (is it okay to bump it, or should I leave on the N'th page and just edit the first post?). -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
I already opened the other topic, but it's gone a week with no replies. I was asking if it's okay to link to that here and ask you (and anyone reading this) to take a look? I know in some communities, that's considered bumping/self-promotion/rude, but in others it's okay, so that's why I wanted to ask before I link to it here. -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Oh. Well, okay, then... I guess I'll just let people know they need to restart the game after changing those config options. Thanks for your help. Would it be okay if I asked for your help in another topic, related to this one (about config setup)? I don't want to link to another topic of mine if that's not considered acceptable around here. -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Okay, so I've gone ahead and made the recipe factory, wrote a little script to insert the condition into all my recipe files, etc. It almost works. If the game loads with a recipe disabled, it'll be disabled just fine. If the game loads with a recipe enabled, it'll be enabled just fine. The problem is that if the player changes the config in-game, the recipe status won't change until the game is reloaded. Is there a way to re-check the conditions when the config changes? (I think I could call CraftingHelper.loadRecipes() again, maybe, but even if I can, that loads the recipes for all mods, and that feels very dangerous to do.) -
[Solved] [1.12] Disable recipes by config (causing crash)?
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
*Sigh* So I guess I will have to go ahead and change all my recipe .json files, then? Welp, time to automate this insertion I'll post here about how it goes. Thank. -
I'd like to add some configuration options to my Totem Essentials mod. Specifically, configurable durabilities for my items and the ability to enable or disable each item's crafting recipes as desired. The durability options are working fine, however the recipe toggling is causing the game to crash whenever I open a crafting table. I know I can do this with a recipe factory, but the mod adds quite a few items and I'd rather avoid having to modify *all* their recipe .json files if possible. So, in order to take advantage of iteration (and the fact that I used a standard naming convention for my items and assets from the start), I've been trying to simply iterate over ForgeRegistries.RECIPES and store all my mod's recipes into a map, then remove the registry entries which are disabled by my config (and re-register the ones which have been re-enabled but aren't in the registry anymore, by pulling the IRecipe from the map). Here's the configuration class I was attempting to use: package com.icemetalpunk.totemessentials.config; import java.util.HashMap; import java.util.Map.Entry; import com.icemetalpunk.totemessentials.TotemEssentials; import net.minecraft.item.crafting.IRecipe; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.config.Config; import net.minecraftforge.common.config.ConfigManager; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.registries.IForgeRegistryModifiable; @Config(modid = TotemEssentials.MODID) @Config.LangKey("totemessentials.config.title") public class TEConfig { private static HashMap<ResourceLocation, IRecipe> recipeMap = new HashMap<>(); @Config.Name("Recipes") @Config.LangKey("totemessentials.config.recipes") public static HashMap<String, Boolean> recipeList = new HashMap<>(); @Config.Name("Normal Durabilities") @Config.LangKey("totemessentials.config.durabilityOne") public static HashMap<String, Integer> durabilityOneList = new HashMap<>(); @Config.Name("Ensouled Durabilities") @Config.LangKey("totemessentials.config.durabilityTwo") public static HashMap<String, Integer> durabilityEnsouledList = new HashMap<>(); static { recipeList.put("Aggression Totem", true); recipeList.put("Aiming Totem", true); recipeList.put("Curing Totem", true); recipeList.put("Daunting Totem", true); recipeList.put("Exchange Totem", true); recipeList.put("Featherfoot Totem", true); recipeList.put("Fireglaze Totem", true); recipeList.put("Flamebody Totem", true); recipeList.put("Gluttony Totem", true); recipeList.put("Phasing Totem", true); recipeList.put("Reaping Totem", true); recipeList.put("Replication Totem", true); recipeList.put("Storage Totem", true); recipeList.put("Traveling Totem", true); recipeList.put("Vampire Totem", true); recipeList.put("Wisdom Totem", true); durabilityOneList.put("Aggression Totem", 50); durabilityOneList.put("Aiming Totem", 100); durabilityOneList.put("Curing Totem", 10); durabilityOneList.put("Daunting Totem", 35); durabilityOneList.put("Exchange Totem", 25); durabilityOneList.put("Featherfoot Totem", 100); durabilityOneList.put("Fireglaze Totem", 2); durabilityOneList.put("Flamebody Totem", 100); durabilityOneList.put("Gluttony Totem", 100); durabilityOneList.put("Phasing Totem", 600); durabilityOneList.put("Reaping Totem", 8); durabilityOneList.put("Replication Totem", 50); durabilityOneList.put("Traveling Totem", 250); durabilityOneList.put("Vampire Totem", 40); durabilityOneList.put("Wisdom Totem", 750); durabilityEnsouledList.put("Ensouled Aggression Totem", 150); durabilityEnsouledList.put("Ensouled Aiming Totem", 200); durabilityEnsouledList.put("Ensouled Curing Totem", 30); durabilityEnsouledList.put("Ensouled Daunting Totem", 75); durabilityEnsouledList.put("Ensouled Exchange Totem", 50); durabilityEnsouledList.put("Ensouled Featherfoot Totem", 200); durabilityEnsouledList.put("Ensouled Fireglaze Totem", 60000); durabilityEnsouledList.put("Ensouled Flamebody Totem", 200); durabilityEnsouledList.put("Ensouled Gluttony Totem", 500); durabilityEnsouledList.put("Ensouled Phasing Totem", 1200); durabilityEnsouledList.put("Ensouled Reaping Totem", 40); durabilityEnsouledList.put("Ensouled Replication Totem", 75); durabilityEnsouledList.put("Ensouled Traveling Totem", 500); durabilityEnsouledList.put("Ensouled Undying Totem", 10); durabilityEnsouledList.put("Ensouled Vampire Totem", 40); durabilityEnsouledList.put("Ensouled Wisdom Totem", 1500); } @Mod.EventBusSubscriber(modid = TotemEssentials.MODID) private static class ConfigEventHandler { @SubscribeEvent public static void onConfigChanged(final ConfigChangedEvent.OnConfigChangedEvent ev) { if (ev.getModID().equals(TotemEssentials.MODID)) { ConfigManager.sync(TotemEssentials.MODID, Config.Type.INSTANCE); UpdateDurabilities(); UpdateRecipesEnabled(); } } } public static void PopulateRecipeMap() { recipeMap.clear(); IForgeRegistryModifiable<IRecipe> reg = (IForgeRegistryModifiable<IRecipe>) ForgeRegistries.RECIPES; for (Entry<ResourceLocation, IRecipe> entry : reg.getEntries()) { ResourceLocation loc = entry.getKey(); if (loc.getResourceDomain().equals(TotemEssentials.MODID)) { recipeMap.put(loc, entry.getValue()); } } } public static void UpdateRecipesEnabled() { IForgeRegistryModifiable<IRecipe> reg = (IForgeRegistryModifiable<IRecipe>) ForgeRegistries.RECIPES; for (Entry<String, Boolean> entry : recipeList.entrySet()) { String name = entry.getKey(); name = name.toLowerCase().replaceAll(" ", "_"); ResourceLocation loc = new ResourceLocation(TotemEssentials.MODID, name); if (reg.containsKey(loc) && !entry.getValue()) { reg.remove(loc); } else if (!reg.containsKey(loc) && entry.getValue()) { IRecipe fromMap = recipeMap.get(loc); reg.register(fromMap); } } } public static void UpdateDurabilities() { String key = ""; int val = 0; for (Entry<String, Integer> entry : durabilityOneList.entrySet()) { key = entry.getKey(); val = entry.getValue(); TotemEssentials.proxy.items.get(key.toLowerCase().replaceAll(" ", "_")).setMaxDamage(val); } for (Entry<String, Integer> entry : durabilityEnsouledList.entrySet()) { key = entry.getKey(); val = entry.getValue(); TotemEssentials.proxy.items.get(key.toLowerCase().replaceAll(" ", "_")).setMaxDamage(val); } } } I'm calling PopulateRecipeMap(), UpdateDurabilities(), and UpdateRecipesEnabled() from my common proxy's Init event handler (in that order). Whenever I open a crafting table, the game crashes, reporting an NPE; basically, it's saying that GuiButtonRecipe's recipe list is null when it tries to draw it. The crash occurs only if the game was loaded with a recipe disabled. If it was loaded with all recipes enabled, then it can open the crafting table and recipe book just fine. And if I then disable a recipe, it works perfectly and is removed from the recipe book as expected. If I load the game with a recipe disabled and then re-enable it, opening the crafting table still causes a crash. In other words: regardless of whether any recipes are currently disabled or not, if the game was loaded with a recipe disabled, opening a crafting table crashes the game; if it wasn't loaded with a recipe disabled, then it works perfectly even when a recipe is disabled later. I've tried moving the initial PopulateRecipeMap(), etc. calls from the Init event to the Post-Init event, suspecting some sort of race condition with the default recipe loader, but it didn't change anything. So how can I go about making my recipes able to be disabled and re-enabled via config, without changing all the recipe files?
-
I know that... where in my code am I setting any item stacks to null? I can't see that anywhere... I'm pretty sure I'm only using ItemStack.EMPTY when that's needed, never null... *EDIT* Nevermind! Now that I'm home and was able to test more, I realized the issue. I was registering the TESR in the pre-init event, which apparently happens before there's a RenderManager instantiated. So the RenderManager was null, leading to an NPE when trying to bind a texture with it. I moved registration to the init event, and voila, all works well
-
That's what I thought; so I don't actually need the block updates, since the TESR is doing all the rendering and the block rendering isn't changing, right? It was just the getUpdateTag() addition that fixed my problem? (I'm currently at work and can't test until later.) And still, though, what would cause my NPE during rendering if the stack is, indeed, grass like the output confirms?
-
1. Oh, duh. I read the parent method's "writeInternal" and somehow saw it as "writeToNBT" >_< 2. I didn't realize the block needed to be updated whenever the tile entity is updated. Good to know, thanks! So now the sync is working properly... but I'm getting an NPE when trying to render the items. The TESR code hasn't changed from above, all I've changed is the tile entity class. The new TE class is this: package com.icemetalpunk.totemaltarations.tile; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.ItemStackHandler; public class TileEntityTotemAltar extends TileEntity { private ItemStackHandler stackHandler = new ItemStackHandler(1) { @Override protected void onContentsChanged(int slot) { // FIXME: Client isn't syncing with inventory on server TileEntityTotemAltar.this.syncUpdates(); } @Override public int getSlotLimit(int slot) { return 1; } }; public TileEntityTotemAltar() { super(); } public void syncUpdates() { this.world.markBlockRangeForRenderUpdate(this.pos, this.pos); this.world.notifyBlockUpdate(this.pos, this.world.getBlockState(this.pos), this.world.getBlockState(this.pos), 3); this.world.scheduleBlockUpdate(this.pos, this.getBlockType(), 0, 0); markDirty(); } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); if (compound.hasKey("items")) { stackHandler.deserializeNBT((NBTTagCompound) compound.getTag("items")); } } @Override public NBTTagCompound writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); compound.setTag("items", stackHandler.serializeNBT()); return compound; } public boolean canInteractWith(EntityPlayer playerIn) { // If we are too far away from this tile entity you cannot use it return !isInvalid() && playerIn.getDistanceSq(pos.add(0.5D, 0.5D, 0.5D)) <= 64D; } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return true; } return super.hasCapability(capability, facing); } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return (T) stackHandler; } return super.getCapability(capability, facing); } @Override public NBTTagCompound getUpdateTag() { return this.writeToNBT(new NBTTagCompound()); } @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(this.getPos(), 0, this.writeToNBT(new NBTTagCompound())); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { this.setPos(pkt.getPos()); this.readFromNBT(pkt.getNbtCompound()); } } I'm testing it by putting grass inside the inventory. I know the sync is working because the TESR's debug output shows a stack of grass...just before the game crashes with an NPE when it tries to render the item.
-
So you're trying to create a mod that automates a rewards system implemented by another person on a server? That sounds... like you should probably be very careful about whether this is actually a good idea, ethically speaking.
-
I have a tile entity which has a 1-slot inventory. No UI, just the inventory which is meant to be interacted with directly from the block. And that all works: I can right-click items into the inventory and back out again. That tells me the server is properly updating the tile entity's inventory. But the client...doesn't seem to be getting synced with the new inventory contents. I have a TESR that's meant to be rendering the item in the inventory, and it's not doing anything. So I put some debug code in, and lo and behold, the TESR always thinks the slot is empty even when the server clearly has an item inside it. I've implemented the getUpdatePacket() and onDataPacket() methods, but no dice. Can anyone help me figure out why the tile entity isn't syncing? TileEntityTotemAltar.java: package com.icemetalpunk.totemaltarations.tile; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.ItemStackHandler; public class TileEntityTotemAltar extends TileEntity { private ItemStackHandler stackHandler = new ItemStackHandler(1) { @Override protected void onContentsChanged(int slot) { // FIXME: Client isn't syncing with inventory on server TileEntityTotemAltar.this.markDirty(); } @Override public int getSlotLimit(int slot) { return 1; } }; public TileEntityTotemAltar() { super(); } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); if (compound.hasKey("items")) { stackHandler.deserializeNBT((NBTTagCompound) compound.getTag("items")); } } @Override public NBTTagCompound writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); compound.setTag("items", stackHandler.serializeNBT()); return compound; } public boolean canInteractWith(EntityPlayer playerIn) { // If we are too far away from this tile entity you cannot use it return !isInvalid() && playerIn.getDistanceSq(pos.add(0.5D, 0.5D, 0.5D)) <= 64D; } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return true; } return super.hasCapability(capability, facing); } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return (T) stackHandler; } return super.getCapability(capability, facing); } @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(this.getPos(), 0, this.writeToNBT(new NBTTagCompound())); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { this.setPos(pkt.getPos()); this.readFromNBT(pkt.getNbtCompound()); } } AltarPedestalTESR.java (including debug output): package com.icemetalpunk.totemaltarations.render; import com.icemetalpunk.totemaltarations.tile.TileEntityTotemAltar; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.entity.RenderEntityItem; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; public class AltarPedestalTESR extends TileEntitySpecialRenderer<TileEntityTotemAltar> { protected final RenderEntityItem rei; protected final EntityItem entity = new EntityItem(Minecraft.getMinecraft().world, 0, 0, 0, new ItemStack(Items.TOTEM_OF_UNDYING, 1)); protected int rotationControl = 0; protected final int INV_ROTATION_SPEED = 4; public AltarPedestalTESR() { super(); rei = new RenderEntityItem(Minecraft.getMinecraft().getRenderManager(), Minecraft.getMinecraft().getRenderItem()) { @Override public boolean shouldSpreadItems() { return false; } }; } @Override public void render(TileEntityTotemAltar te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) { System.out.println("--------"); System.out.println("Checking capability:"); if (!te.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.EAST)) { return; } System.out.println("Getting stack:"); IItemHandler handler = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.EAST); ItemStack stack = handler.getStackInSlot(0); System.out.println("Stack: " + stack); if (stack == null || stack == ItemStack.EMPTY) { return; } System.out.println("Moving onto rendering."); entity.setWorld(te.getWorld()); entity.setItem(stack); rotationControl = (rotationControl + 1) % this.INV_ROTATION_SPEED; if (rotationControl == 0) { entity.onUpdate(); } this.setLightmapDisabled(true); rei.doRender(entity, x + 0.5, y + 1.0f, z + 0.5, 0, partialTicks); this.setLightmapDisabled(false); } } BlockTotemAltar.java: package com.icemetalpunk.totemaltarations.blocks; import com.icemetalpunk.totemaltarations.tile.TileEntityTotemAltar; import net.minecraft.block.Block; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.SoundType; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; public class BlockTotemAltar extends TABlock implements ITileEntityProvider { public BlockTotemAltar(String name) { super(name); Block p; this.setSoundType(SoundType.STONE); } @Override public TileEntity createNewTileEntity(World worldIn, int meta) { return new TileEntityTotemAltar(); } @Override public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { if (worldIn.isRemote) { return true; } TileEntity te = worldIn.getTileEntity(pos); if (te != null && te instanceof TileEntityTotemAltar) { TileEntityTotemAltar altar = (TileEntityTotemAltar) te; if (altar.canInteractWith(playerIn) && altar.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing)) { IItemHandler handler = altar.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing); ItemStack existing = handler.extractItem(0, 1, false); if (existing != null && existing != ItemStack.EMPTY) { worldIn.spawnEntity(new EntityItem(worldIn, pos.getX(), pos.getY() - 1, pos.getZ(), existing)); } else { ItemStack inHand = playerIn.getHeldItem(hand); ItemStack after = handler.insertItem(0, inHand, false); playerIn.setHeldItem(hand, after); } } } return true; } }
-
[SOLVED] [1.12] NoSuchMethodError for getSlotFor
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Thanks, guys; I implemented my own iteration and got this working. I still feel like there should be some sort of metadata-insensitive item search method available to both client and server... but whatever, it works -
[SOLVED] [1.12] NoSuchMethodError for getSlotFor
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Thank you for the clarification! So the problem with hasItemStack is that it matches metadata/damage, and I need to check if the item is in the inventory regardless of its damage value. That's why I ended up writing that little static helper method. The getSlotFor method uses stackEqualExact, which checks the hasSubtypes field before deciding whether to compare damage values or not. So it looks like I'm going to have to basically rewrite getSlotFor()'s loop in my own code? I mean, it's not terrible, it just feels like this is a functionality that would already exist somewhere in MC's or Forge's code base... -
[SOLVED] [1.12] NoSuchMethodError for getSlotFor
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Ahhh.... suddenly things make sense. I was under the impression that the integrated server was essentially identical, for the most part, to the dedicated server. Clearly, I was mistaken. So now I'm just left with that final question: is there an easier way to get an item stack from the player's inventory that matches a given stack loosely, without iterating over everything myself? (From the server-side, of course.) -
[SOLVED] [1.12] NoSuchMethodError for getSlotFor
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Wait... I may have misunderstood. When you say "dedicated server", that includes the integrated single-player server, too, right? If that's the case, wouldn't I have gotten this crash during my testing (which, by the way, properly called ItemEnsouledAggressionTotem.performEffect())? Or do you mean the method exists on both the client and the integrated server, but not on a standalone server? If the latter is the case, how would I go about getting a match on an item stack anywhere in the player's inventory? Do I need to iterate over the entire inventory myself? -
[SOLVED] [1.12] NoSuchMethodError for getSlotFor
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
I mean... if that was the issue, wouldn't I have also gotten the error when testing? Besides, World#isRemote returns true if it's on the client, meaning if I check for !isRemote, my code will only run on the server... and as you pointed out, this error is triggering on the server... Normally, I'd be willing to at least try it, but since I can't replicate the error, the only way to see if my patch works is by re-releasing a revision and waiting for the players to try again. I don't want to keep releasing revisions unless I at least know I'm going in the right direction with the bugfixes. I didn't think my code was pertinent since the error is that a vanilla method isn't found... the TEEvents class has a *lot* of code in it (and most is probably completely unrelated to this bug), so I don't want to post the entire class... Here's the TEEvents#getStackInPlayerInv method that's calling the getSlotFor method: // Helper method to get a matching stack from a player's inventory, // regardless of which part of the inventory it's in. public static ItemStack getStackInPlayerInv(EntityPlayer player, ItemStack compareTo) { InventoryPlayer inv = player.inventory; ItemStack stack = ItemStack.EMPTY; int slotID = inv.getSlotFor(compareTo); // Doesn't include offhand! if (slotID < 0) { ItemStack inOffhand = player.getHeldItemOffhand(); if (inOffhand.getItem() == compareTo.getItem() && (!inOffhand.getHasSubtypes() || inOffhand.getMetadata() == compareTo.getMetadata()) && ItemStack.areItemStackTagsEqual(inOffhand, compareTo)) { stack = inOffhand; } else { for (ItemStack armorStack : player.getArmorInventoryList()) { if (armorStack.getItem() == compareTo.getItem() && (!armorStack.getHasSubtypes() || armorStack.getMetadata() == compareTo.getMetadata()) && ItemStack.areItemStackTagsEqual(armorStack, compareTo)) { stack = armorStack; break; } } } } else { stack = inv.getStackInSlot(slotID); } return stack; } And the TEEnsouledEvents#onAttackTarget method which is calling that: @SubscribeEvent public void onAttackTarget(LivingSetAttackTargetEvent ev) { EntityLivingBase target = ev.getTarget(); if (target instanceof EntityPlayer) { ItemStack stack = TEEvents.getStackInPlayerInv((EntityPlayer) target, new ItemStack(TotemEssentials.proxy.items.get("ensouled_aggression_totem"))); if (stack != ItemStack.EMPTY) { ItemEnsouledAggressionTotem.performEffect(stack, ev.getEntity().world, (EntityPlayer) target); } } } And as the stacktrace shows, everything leading up to that is Forge/vanilla code, so those should be the only relevant bits of my code. -
After testing my Totem Essentials mod and having no issues, I released it, but now I'm getting a report from someone that they're getting crashes. This is the log they submitted: https://gist.github.com/knoxhack/4ae7603dfa1d9e33e35f363000e6faa4 As you can see, it's caused by a NoSuchMethodError for net.minecraft.entity.player.InventoryPlayer.func_184429_b, which is the SRG name for InventoryPlayer#getSlotFor(), which simply takes an ItemStack and finds the slot that matches it in the player's inventory. What would cause that method to not exist, especially when I never got this crash in my own Minecraft environment? I'm guessing it's some sort of mod conflict with one of the mods Knoxhack has installed, but what kind of mod conflict can remove the existence of a core method like that?
-
Now that it's moved to an advancement, you have to use advancement structures for this. Look into EntityPlayerMP#getAdvancements(), PlayerAdvancements#getProgress(), and AdvancementProgress#getCompletedCriteria(). Using those properly should give you a list of all the criteria the player has earned; the criteria names for the Adventuring Time advancement are basically just biome names (And of course, Advancement#getCriteria() gives you access to *all* the criteria of an advancement, so you can get the full list of required biomes through that.)
-
I'm using configuration files for the first time, trying to add some small configuration options to my Totem Essentials mod for release 1.0.1. It seems pretty straightforward, and the configuration works (at least, the parts I've added so far). However, the tooltips are not working. All my category buttons show "No tooltip defined." in place of the actual tooltips, and I don't know why. Originally, I was simply using @Config.Comment("tooltip goes here") for my tooltips, but that didn't work. So I looked at FML's source for GuiConfigEntries and I saw it uses @Config.LangKey() with ".tooltip" appended as the first possible tooltip. So I tried to use that... and it still doesn't work. Here's the config class: package com.icemetalpunk.totemessentials.config; import java.util.HashMap; import java.util.Map.Entry; import com.icemetalpunk.totemessentials.TotemEssentials; import net.minecraftforge.common.config.Config; import net.minecraftforge.common.config.ConfigManager; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @Config(modid = TotemEssentials.MODID) @Config.LangKey("totemessentials.config.title") public class TEConfig { @Config.Name("Recipes") @Config.LangKey("totemessentials.config.recipes") public static HashMap<String, Boolean> recipeList = new HashMap<>(); @Config.Name("Normal Durabilities") @Config.LangKey("totemessentials.config.durabilityOne") public static HashMap<String, Integer> durabilityOneList = new HashMap<>(); @Config.Name("Ensouled Durabilities") @Config.LangKey("totemessentials.config.durabilityTwo") public static HashMap<String, Integer> durabilityEnsouledList = new HashMap<>(); static { recipeList.put("Aggression Totem", true); recipeList.put("Aiming Totem", true); recipeList.put("Curing Totem", true); recipeList.put("Daunting Totem", true); recipeList.put("Exchange Totem", true); recipeList.put("Featherfoot Totem", true); recipeList.put("Fireglaze Totem", true); recipeList.put("Flamebody Totem", true); recipeList.put("Gluttony Totem", true); recipeList.put("Phasing Totem", true); recipeList.put("Reaping Totem", true); recipeList.put("Replication Totem", true); recipeList.put("Storage Totem", true); recipeList.put("Traveling Totem", true); recipeList.put("Vampire Totem", true); recipeList.put("Wisdom Totem", true); durabilityOneList.put("Aggression Totem", 50); durabilityOneList.put("Aiming Totem", 100); durabilityOneList.put("Curing Totem", 10); durabilityOneList.put("Daunting Totem", 35); durabilityOneList.put("Exchange Totem", 25); durabilityOneList.put("Featherfoot Totem", 100); durabilityOneList.put("Fireglaze Totem", 2); durabilityOneList.put("Flamebody Totem", 100); durabilityOneList.put("Gluttony Totem", 100); durabilityOneList.put("Phasing Totem", 600); durabilityOneList.put("Reaping Totem", 8); durabilityOneList.put("Replication Totem", 50); durabilityOneList.put("Traveling Totem", 250); durabilityOneList.put("Vampire Totem", 40); durabilityOneList.put("Wisdom Totem", 750); durabilityEnsouledList.put("Ensouled Aggression Totem", 150); durabilityEnsouledList.put("Ensouled Aiming Totem", 200); durabilityEnsouledList.put("Ensouled Curing Totem", 30); durabilityEnsouledList.put("Ensouled Daunting Totem", 75); durabilityEnsouledList.put("Ensouled Exchange Totem", 50); durabilityEnsouledList.put("Ensouled Featherfoot Totem", 200); durabilityEnsouledList.put("Ensouled Fireglaze Totem", 60000); durabilityEnsouledList.put("Ensouled Flamebody Totem", 200); durabilityEnsouledList.put("Ensouled Gluttony Totem", 500); durabilityEnsouledList.put("Ensouled Phasing Totem", 1200); durabilityEnsouledList.put("Ensouled Reaping Totem", 40); durabilityEnsouledList.put("Ensouled Replication Totem", 75); durabilityEnsouledList.put("Ensouled Traveling Totem", 500); durabilityEnsouledList.put("Ensouled Undying Totem", 10); durabilityEnsouledList.put("Ensouled Vampire Totem", 40); durabilityEnsouledList.put("Ensouled Wisdom Totem", 1500); } @Mod.EventBusSubscriber(modid = TotemEssentials.MODID) private static class ConfigEventHandler { @SubscribeEvent public static void onConfigChanged(final ConfigChangedEvent.OnConfigChangedEvent ev) { if (ev.getModID().equals(TotemEssentials.MODID)) { ConfigManager.sync(TotemEssentials.MODID, Config.Type.INSTANCE); UpdateDurabilities(); } } } public static void UpdateDurabilities() { String key = ""; int val = 0; for (Entry<String, Integer> entry : durabilityOneList.entrySet()) { key = entry.getKey(); val = entry.getValue(); TotemEssentials.proxy.items.get(key.toLowerCase().replaceAll(" ", "_")).setMaxDamage(val); } for (Entry<String, Integer> entry : durabilityEnsouledList.entrySet()) { key = entry.getKey(); val = entry.getValue(); TotemEssentials.proxy.items.get(key.toLowerCase().replaceAll(" ", "_")).setMaxDamage(val); } } } And here's the relevant additions to my en_us.lang file: #config totemessentials.config.recipes.tooltip=Enable or disable Totem recipes totemessentials.config.durabilityOne.tooltip=Edit the default durabilities of each Tier I totem. totemessentials.config.durabilityTwo.tooltip=Edit the default durabilities of each Ensouled / Tier II totem So why does it still say "No tooltip defined" instead of the actual tooltips? *EDIT* In case it helps, here's the Github repo and branch where this code exists: https://github.com/IceMetalPunk/TotemEssentials/tree/configurable-recipes (It's been updated a bit since this topic was posted, but ultimately, the tooltip problem persists.)
-
[1.12] AnvilRepairEvent: Keep item in left slot
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
Yeah, originally I was just putting it back in the player's inventory. The issue is that this item would often be used for batches of anvil "crafting", so having to keep putting it back into the anvil for each round of crafting can get annoying. (The item, a special totem, allows you to "de-craft" items in an anvil. So for instance, if I wanted to de-craft a stack of wood back into logs, I'd have to perform 16 rounds of crafting, and moving the totem back and forth between the anvil and your inventory 16 times is obnoxious.) -
[1.12] AnvilRepairEvent: Keep item in left slot
IceMetalPunk replied to IceMetalPunk's topic in Modder Support
No, that's the AnvilUpdateEvent, which I'm using for other things. The AnvilRepairEvent is called when you remove items from the output slot. (More specifically, it's fired in the output slot's onTake method, exactly one line before it clears the left input slot.) As you can see from the edit in the original post, I'm making it work by storing the item stack and container in a map and then putting the item back one tick later; that method feels very "hacky" to me, though, especially since it relies on having a tick event handler running every tick, so I was hoping for a more event-based (or at least less frequently running) approach.