Jump to content
Search In
  • More options...
Find results that contain...
Find results in...


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by CAS_ual_TY

  1. Hi,

    the mod I am working on downloads textures at runtime (used for Screen, not Items/Blocks ofc). This must be done in seperate threads, as otherwise the game freezes for the duration. Now, I have some experience with Multithreading, I know how to make things thread safe and all the basics you need to know for that. My goal is to use a Thread Pools like implementation, as there are multiple tasks todo (Download, Resize, Modify image entirely ...), and sometimes they need to be done in a specific order.

    The main question would be: What to do when Minecraft shuts down. All the tasks are short, so ideally I would like to wait for them to finish, the task queue can just be discarded in that case. There are some tasks which I do not want to see interrupted in some circumstances (eg. while writing to a file). I dont know of any MC functionality that I can use to ensure that.

    So I did some research and might go for a Shutdown Hook. This leads me to my 2nd question: Would it be bad practice in this specific scenario to have the shutdown hook thread just wait for the worker threads to finish (with a max wait time ofc)?

    If there are better ways of accomplishing what I am trying to do, please let me know


  2. https://wiki.mcjty.eu/modding/index.php?title=YouTube-Tutorials#Episode_2:_The_First_Block.2C_Capabilities.2C_Container.2C_Gui


    Containers dont really require anything special in constructor in regards to tile entities. You are not forced to open it on right click of a tile entity. You can also just open the container when right clicking an item etc.. Just depends on where you call the NetworkHooks method to open it. Most minecraft containers require the player or player inventory as parameter (in constructor) because they access said inventory. But you are free to do whatever you want.

    So ye. Kinda trying to answer you where there isnt much to talk about. You can re-use the same container and containerscreen classes for different container types. Just keep that in mind (this is done eg. with the vanilla chests)

    • Thanks 1
  3. 1st: In 1.15 the 2nd parameter of RayTraceContext#new was not a direction, but rather another position. If that is still the case, then in your case you would have to do something like:

    new RayTraceContext(eyePos, eyePos.add(player.getLookVec().scale(RANGE)), RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, player)


    2nd: Out of curiosity: Why are you using ItemStack#isItemEqual instead of just comparing the items? Is NBT important here?

  4. 1. You need to move this into your ClientProxy


    This might not be the issue of your error (I couldnt see it), but this could definitely cause issues


    2. If you want to follow the minecraft package naming "convention", you should lower case your tileEntities package and also put all package names to singular (screens -> screen, items -> item etc.)

  5. What you just gave me doesnt help me at all. I dont know what renderPlayerInfo does. I also dont know where you register all of this (when its fired).

    So until you show more context Ill just give you some general stuff:


    There is 2 different buses:

    - mod bus, for "mod construction" events (eg. FMLCommonSetupEvent, TextureStichEvent, ModelRegistryEvent, ModConfigEvent etc.)

    - forge bus, for general minecraft events (eg. RenderGameOverlay, EntityJoinedTheWorld...)


    And there is different methods of registering. This sums it up:


  6. 1/2:


    2/2 (the three DeckBox*** classes):



    This is how I implemented a deck box that can hold up to 90 card items. The only thing you will have to do different is to consider your slots to be the x/y size of 18; I used the size 16 for my slots because otherwise there would not be enough space (in container and screen class).

  7. EDIT: Created a pull request https://github.com/MinecraftForge/MinecraftForge/pull/6609




    Not sure if I found a forge bug. Ill just elaborate:

    - The Vanilla Effect class implements the Forge IForgeEffect interface

    - IForgeEffect interface has a method "getCurativeItems" which returns a list of item stacks which cure the effect

    - This has a default implementation containing only the milk bucket

    - The MilkBucketItem class then appropriately calls the method which checks if it cures each effect and then removes it only if applicable

    - So far so good

    - Now here is the problem: In the same method it also calls a method which removes all potion effects regardless, kinda negating everything from above. Latest 1.15.2 version of forge


    The code mentioned:

    Inside MilkBucketItem::onItemUseFinish

    Correct first call: LivingEntity::curePotionEffects

    Unnecessary (I guess) call: LivingEntity::clearActivePotions

  8. - NoodleOre (Block) needs a registry name (noodle_ore)

    - The public static field NOODLE_ORE (ModBlocks class) will always be null because afaik ObjectHolder pupulates only PSF fields, not PS (so make it final)


    Not sure about the 2nd point though, check the ObjectHolder class, I think its explained in there


    EDIT: NVM just seen that you set the registry name inside the constructor.

  9. Going to bump this one time (I could not find forum rules on bumping, but I dont intend to do it anymore here anyways). Some (new) things:


    - Still could not find a way to do it without reflection. Forge will probably have to step in there (Idk Im too afraid to ask or ping anyone). But I totally forgot about the reflection helper, so you should probably change the reflection fix to use the following (this already does the setAccessible(true) call, so just get the method using this):

            blockStatesInjector = ObfuscationReflectionHelper.findMethod(PointOfInterestType.class, "func_221052_a", PointOfInterestType.class);


    - There is a more modern way to register stuff which you probably wanna use. I have not known about it (like most others) so I will just put that here for the sake of progress:


  10. In honor of this thread, Im going to quickly summarise what I have gathered about creating villagers professions in 1.14.4 (forge 28.1.109) as there is not really that much information about that available right now. If anyone sees me doing something wrong or saying bs, just let me know and I will fix it. Specifically the reflection fix I mention below.


    EDIT: Make sure you check the first couple replies as well.


    Source for all of this can be found specifically in this commit: https://github.com/CAS-ual-TY/GunCus/commit/817848784868f4e8362d4270be6f8fc19863dc42 Tho I have done a few extra things which are unrelated so dont wonder.



    If you check out the vanilla class, you could come to the conclusion, that these are generally types of points of interest for villagers. You have the blocks in there that give the villagers their professions (eg. the Armorer type has a Blast Furnace passed to the constructor), you have a Bed type for sleeping, Unemployed type, Meeting type, etc. (But most importantly you have all the professions). You make your POITypes in the forge registry event for that (Register<PointOfInterestType>) (dont forget to set the registry name).

    Constructor is: String name, Set<BlockState> blockStates, int maxFreeTickets, SoundEvent workSound, int something. Name seems to just be the lower case name (eg. "fletcher", "leatherworker" etc.), the blockStates are just all block states of the interest block. There is a helper method for this (unfortunately its private, so youll have to figure something out for yourself) which I will post below. Next you have what it seems to be the amount of villagers that can use this at once. All vanilla professions have this at 1. Next you have the work sound. I just use vanilla sounds here, so youll have to check yourself if your sound event public static final instances are populated already at this point. And finally you have another integer which I have no idea about. All vanilla professions have this at 1 too.



    Now we create the villager profession. This is the type of profession a villager can have (eg. Flether, Weapon Smith etc.). We use the forge registry event for this again (Register<VillagerProfession>) (again, dont forget to set registry name).

    Constructor: String nameIn, PointOfInterestType pointOfInterestIn, ImmutableSet<Item> noIdea, ImmutableSet<Block> noIdeaAgain. The name is the lowercase name again (eg. "fisherman", "mason" etc.). This is needed for the texture and translation (see below). Next you pass the PointOfInterestType for this profession (read above that this is). Since P comes before V, these are already registered and object holders are populated, so you can just pass your static fields here. The 2 sets that come now I havent really looked into because all vanilla professions just pass an empty set here (ImmutableSet.of()).


    Profession Texture Location


    You just have to put the texture in the appropriate location. Rendering is done automatically. If you want to play around with the model a bit (eg. change the hat), check out the vanilla villagers. You can do that by adding extra PROFESSION_NAME.png.mcmeta files to the same location. Just check out vanilla for examples: assets\minecraft\textures\entity\villager\profession


    Profession Trading GUI Title Translation


    This is the key for your .lang file. Translate this to set the title of the trading GUI. At first I was confused, because there is 2 mod ids in there (mine and minecraft). But it makes sense because the villager is part of minecraft, but the professions part of *MOD_ID*. So ye.


    Adding Trades to Professions

    Forge has 2 events for that: VillagerTradesEvent, WandererTradesEvent. You can add trades to any profession here. 1st one allows to add trades to every profession and their levels (1-5, novice to master or smth). 2nd one allows to add generic or rare trades to the wanderer. I highly suggest checking out the vanilla trades to have an idea what (or how much) to set here for all the params: https://minecraft.gamepedia.com/Trading#Armorer

    - To do the first: Call event.getTrades().get(level).add(some_ITrade_instance_or_lamda_action). To do this for the profession you want, check if event.getType() == ModVillagerProfessions.YOUR_PROFESSION (this event gets called once for every profession).

    - To do the second: Just check out the event class. Pretty simple. Easy getters easy life

    I have made a helper class for this to allow easy trade creation. I will post it below. Example usage of this helper class (adds a trade to level 1; You can buy 2-4 (picked randomly per villager, but steady) acacia fences for 12 emeralds):

    event.getTrades().get(1).add(new RandomTradeBuilder(8, 10, 0.05F).setEmeraldPrice(12).setForSale(Items.ACACIA_FENCE, 2, 4).build());


    Final Reflection Fix

    Im just going to quote what I have written on forge discord. See below for fix (in helper section, call for every POIType in your init):

    So I have done some custom villager professions now, and it doesnt seem like any villager is picking up the profession, unless I invoke PointOfInterestType::func_221052_a which is a private static method. After calling that with my professions, everything worked perfectly. To make sure, I have removed this reflection call again, and it turns out that it still works, but only in villages that have already had a representative of these professions atleast once.
    So without this call, the villages which had these professions already once, worked. Even when removing the villagers and/or blocks again and replacing them somewhere else (in village radius). New villages would not work at all without this call. They could only pick up other professions. Atleast this is the result I came to with some 30min+ testing





    Helper Stuff (use this as you please)


    Get all Block States

       static Set<BlockState> getAllStates(Block block) {
          return ImmutableSet.copyOf(block.getStateContainer().getValidStates());


    Easy/Random Trades Builder

    package here
    import java.util.Random;
    import java.util.function.Function;
    import net.minecraft.entity.merchant.villager.VillagerTrades.ITrade;
    import net.minecraft.item.Item;
    import net.minecraft.item.ItemStack;
    import net.minecraft.item.Items;
    import net.minecraft.item.MerchantOffer;
    public class RandomTradeBuilder
        protected Function<Random, ItemStack> price;
        protected Function<Random, ItemStack> price2;
        protected Function<Random, ItemStack> forSale;
        protected final int maxTrades;
        protected final int xp;
        protected final float priceMult;
        public RandomTradeBuilder(int maxTrades, int xp, float priceMult)
            this.price = null;
            this.price2 = (random) -> ItemStack.EMPTY;
            this.forSale = null;
            this.maxTrades = maxTrades;
            this.xp = xp;
            this.priceMult = priceMult;
        public RandomTradeBuilder setPrice(Function<Random, ItemStack> price)
            this.price = price;
            return this;
        public RandomTradeBuilder setPrice(Item item, int min, int max)
            return this.setPrice(RandomTradeBuilder.createFunction(item, min, max));
        public RandomTradeBuilder setPrice2(Function<Random, ItemStack> price2)
            this.price2 = price2;
            return this;
        public RandomTradeBuilder setPrice2(Item item, int min, int max)
            return this.setPrice2(RandomTradeBuilder.createFunction(item, min, max));
        public RandomTradeBuilder setForSale(Function<Random, ItemStack> forSale)
            this.forSale = forSale;
            return this;
        public RandomTradeBuilder setForSale(Item item, int min, int max)
            return this.setForSale(RandomTradeBuilder.createFunction(item, min, max));
        public RandomTradeBuilder setEmeraldPrice(int emeralds)
            return this.setPrice((random) -> new ItemStack(Items.EMERALD, emeralds));
        public RandomTradeBuilder setEmeraldPriceFor(int emeralds, Item item, int amt)
            return this.setForSale((random) -> new ItemStack(item, amt));
        public RandomTradeBuilder setEmeraldPriceFor(int emeralds, Item item)
            return this.setEmeraldPriceFor(emeralds, item, 1);
        public RandomTradeBuilder setEmeraldPrice(int min, int max)
            return this.setPrice(Items.EMERALD, min, max);
        public RandomTradeBuilder setEmeraldPriceFor(int min, int max, Item item, int amt)
            this.setEmeraldPrice(min, max);
            return this.setForSale((random) -> new ItemStack(item, amt));
        public RandomTradeBuilder setEmeraldPriceFor(int min, int max, Item item)
            return this.setEmeraldPriceFor(min, max, item, 1);
        public boolean canBuild()
            return this.price != null && this.forSale != null;
        public ITrade build()
            return (entity, random) -> !this.canBuild() ? null : new MerchantOffer(this.price.apply(random), this.price2.apply(random), this.forSale.apply(random), this.maxTrades, this.xp, this.priceMult);
        public static Function<Random, ItemStack> createFunction(Item item, int min, int max)
            return (random) -> new ItemStack(item, random.nextInt(max) + min);


    Reflection Fix

        private static Method blockStatesInjector;
                blockStatesInjector = PointOfInterestType.class.getDeclaredMethod("func_221052_a", PointOfInterestType.class);
            catch (NoSuchMethodException | SecurityException e)
        public static void fixPOITypeBlockStates(PointOfInterestType poiType)
                blockStatesInjector.invoke(null, poiType);
            catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)


    • Like 5
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.