#Dynamically hidden recipes and recipe categories do not update until relog

37 messages · Page 1 of 1 (latest)

still matrix
#

I've managed to hide recipes depending on whether a player has a stage or not, but I've hit a road block where the unhidden recipes only show once you relog.
The code I have right now sends all the gamestages a player has to the client, and updates JEI to unhide some recipes or recipe categories for a given gamestage. Is there a way I can force JEI to re-index it's recipes?

rough topazBOT
#

Once your ticket has been resolved, please close it with </ticket close:1054771505520717835> command!

still matrix
#
const JEIStageHelper = {
    /**
     * @param {Internal.IRecipeManager} recipeManager
     * @param {boolean} hide 
     * @param {string} recipeType 
     * @param {string[]} recipeIds 
     */
    setHideRecipes(recipeManager, hide, recipeType, recipeIds){
        console.log('toggled hiding recipes: ' +hide)
        console.log(recipeIds)
        const recipeTypeMan = recipeManager.getRecipeType(recipeType).get()
        const recipeList = recipeManager.createRecipeLookup(recipeTypeMan).get()
            .filter(recipe => {
                return recipeIds.reduce(recipeId => {recipeId == recipe.id}) > 0
                // return recipeIds.includes(recipe.getId().toString())
            }).toList()
        if(hide) {
            return recipeManager.hideRecipes(recipeTypeMan, recipeList)
        }
        recipeManager.unhideRecipes(recipeTypeMan, recipeList)
    },
    /**
     * @param {Internal.IRecipeManager} recipeManager
     * @param {boolean} hide 
     * @param {string} recipeType 
     */
    setHideRecipeCategory(recipeManager, hide, recipeType){
        console.log('toggled hiding recipe category: ' +hide)
        const recipeTypeMan = recipeManager.getRecipeType(recipeType).get()
        if(hide) return recipeManager.hideRecipeCategory(recipeTypeMan)
        recipeManager.unhideRecipeCategory(recipeTypeMan)
    },

    /**
     * 
     * @param {Internal.ListTag} stageUpdateData 
     * @param {string} stage 
     */
    hasStage(stageUpdateData, stage){
        console.log(stageUpdateData)
        const stageArr = stageUpdateData
        for (let i = 0; i < stageArr.length; i++) {
            if(stage == stageArr[i]) return true
        }
        return false
    }
}
#
const STAGES = global.constants.gamestages
const stages = Object.keys(STAGES)

NetworkEvents.dataReceived('stage_update', event => {
    const {data} = event
    const playerStagesListTag = data.player_stages
    const JEIRecipeManager = global.jeiRuntime.recipeManager

    for(let stage in STAGES){
        let recipeTypes = STAGES[stage]
        let playerHasStage = JEIStageHelper.hasStage(playerStagesListTag, stage)

        for(let recipeType in recipeTypes){
            let recipes = recipeTypes[recipeType]

            recipes === 'hide_category' ? 
                JEIStageHelper.setHideRecipeCategory(JEIRecipeManager, !playerHasStage, recipeType) :
                JEIStageHelper.setHideRecipes(JEIRecipeManager, !playerHasStage, recipeType, recipes)
        }
    }
})
#

Just to note, I've been using JEI to interface with EMI before this, so if someone somehow knows how to do this with EMI, that would be extremely helpful, though so far it doesn't seem like EMI can dynamically hide recipes at runtime unless you do it through resource packs

vague dirge
#

It does have KubeJS support, so you could try to integrate it into your script.

still matrix
#

I've gotten that part down, I'm looking for how I can hide recipes, which doesn't quite works on both JEI and EMI, both of those recipe viewers don't work too well when you try to manage recipes dynamically

vague dirge
#

And I get that but AStages unhides the recipes without a relog

still matrix
#

it only prevents using the recipes, they're still visible in recipe viewers as far as I'm aware, not to mention it doesn't work at all with non-vanilla recipe categories

heady grove
#

Astages does hide the recipes too, there is an attribute boolean for that and the machine recipe stages mod iirc does other types than vanilla

#

But i think i have some code with just the jei api somewhere lurking that did recipe hiding and showing, i will come back to this

heady grove
#
recipeManager.createRecipeLookup(recipeTypeMan).get()
            .filter(recipe => {
                return recipeIds.reduce(recipeId => {recipeId == recipe.id}) > 0
                // return recipeIds.includes(recipe.getId().toString())
            }).toList()
        if(hide) {
            return recipeManager.hideRecipes(recipeTypeMan, recipeList)

this part should be the issue

#
.createRecipeLookup(recipeTypeMan).includeHidden().get()

the includeHidden() is important for showing

still matrix
#
setHideRecipes(recipeManager, hide, recipeType, recipeIds){
        console.log('toggled hiding recipes for category '+recipeType+': ' +hide)
        // console.log(recipeIds)
        const recipeTypeMan = recipeManager.getRecipeType(recipeType).get()
        const recipeList = recipeManager.createRecipeLookup(recipeTypeMan).includeHidden().get()
            .filter(recipe => {
                return recipeIds.length > 0 ? 
                    recipeIds.reduce(recipeId => {
                        return recipe.id.toString().match(recipeId) > 0
                    }) > 0 : 
                    false
            }).toList()
        if(hide) {
            return recipeManager.hideRecipes(recipeTypeMan, recipeList)
        }
        recipeManager.unhideRecipes(recipeTypeMan, recipeList)
    },

I've tried with and without .includeHidden(). I don't think I see a difference? What exactly is considered hidden by JEI here?

heady grove
#
const runtime = global.jeiRuntime;
const recipeManager = runtime.recipeManager;


function hideRecipes() {
    let recipeCategories = recipeManager.createRecipeCategoryLookup().includeHidden().get().toList();
    recipeCategories.forEach(category =>{
        let recipeType = category.getRecipeType()

        let recipiesToHide = recipeManager.createRecipeLookup(recipeType).get().toArray();

        console.log('now logging recipes')
        console.log(recipiesToHide)

        recipeManager.hideRecipes(recipeType, recipiesToHide)

    })
}

function showRecipes() {
    let recipeCategories = recipeManager.createRecipeCategoryLookup().includeHidden().get().toList();
    recipeCategories.forEach(category =>{
        let recipeType = category.getRecipeType()

        let recipiesToShow = recipeManager.createRecipeLookup(recipeType).includeHidden().get().toArray();

        console.log('now logging recipes')
        console.log(recipiesToShow)

        recipeManager.unhideRecipes(recipeType, recipiesToShow)

    })
}

NetworkEvents.dataReceived('channel_show', event =>{
    showRecipes();
    event.player.tell('data received')
})

NetworkEvents.dataReceived('channel_hide', event =>{
    hideRecipes();
    event.player.tell('data received')
})

this works, only downside are the const, they are only defined after reload or second join of the game since jei isnt initialized before the scripts run but it is more readable like this

#

let me know if you can work with that or get stuck somewhere else

still matrix
# heady grove ```js const runtime = global.jeiRuntime; const recipeManager = runtime.recipeMan...

the code I initially put here was pretty close to what you have here, just with .includeHidden() added in and that I just send the client every stage the player has instead. I added in .includeHidden() and I don't think I noticed any difference, but that might be because I use mod based restrictions instead. Maybe an oversight?
Either way, what I have already works, though JEI doesn't update when you're in-world unless I force the client to run the /reload command. If there's a way to just force only JEI to update I'd love to know how but what I have works. I've more or less ignored ASfages' recipe hiding since it doesn't work for non-vanilla recipe categories anyway. I guess the most unfortunate thing about all this is that EMI can't do recipe hiding unless I get into loading asset data, and unless there's a way to avoid the huge memory leaks that happen form reloading resources, I'd rather not go that route for now

#
const JEIStageHelper = {
    /**
     * @param {Internal.IRecipeManager} recipeManager
     * @param {boolean} hide 
     * @param {string} recipeTypeId 
     * @param {string[]} recipeIds 
     */
    setHideRecipes(recipeManager, hide, recipeTypeId, recipeIds){
        console.log('toggled hiding recipes for category '+recipeTypeId+': ' +hide)
        // console.log(recipeIds)
        const recipeType = recipeManager.getRecipeType(recipeTypeId).get()
        const recipeList = recipeManager.createRecipeLookup(recipeType).includeHidden().get()
            .filter(recipe => {
                return recipeIds.length > 0 ? 
                    recipeIds.reduce(recipeId => {
                        return recipe.id.toString().match(recipeId) > 0
                    }) > 0 : 
                    false
            }).toList()
        if(hide) {
            return recipeManager.hideRecipes(recipeType, recipeList)
        }
        recipeManager.unhideRecipes(recipeType, recipeList)
    },
    /**
     * @param {Internal.IRecipeManager} recipeManager
     * @param {boolean} hide 
     * @param {string} recipeTypeId
     */
    setHideRecipeCategory(recipeManager, hide, recipeTypeId){
        console.log('toggled hiding for recipe category '+recipeTypeId+': ' +hide)
        const recipeType = recipeManager.getRecipeType(recipeTypeId).get()
        if(hide) return recipeManager.hideRecipeCategory(recipeType)
        recipeManager.unhideRecipeCategory(recipeType)
    },

    /**
     * 
     * @param {Internal.ListTag} stageUpdateData 
     * @param {string} stage 
     */
    hasStage(stageUpdateData, stage){
        // console.log(stageUpdateData)
        const stageArr = stageUpdateData
        for (let i = 0; i < stageArr.length; i++) {
            if(stage == stageArr[i]) return true
        }
        return false
    }
}
#

const STAGES = global.constants.gamestages
const stages = Object.keys(STAGES)

/**
 * manage received stages from data received
 */
NetworkEvents.dataReceived('stage_update', event => {
    const {data} = event
    const playerStagesListTag = data.player_stages
    const JEIRecipeManager = global.jeiRuntime.recipeManager

    for(let stage in STAGES){
        let recipeTypes = STAGES[stage]
        let playerHasStage = JEIStageHelper.hasStage(playerStagesListTag, stage)

        for(let recipeType in recipeTypes){
            let recipes = recipeTypes[recipeType]

            recipes === 'hide_category' ? 
                JEIStageHelper.setHideRecipeCategory(JEIRecipeManager, !playerHasStage, recipeType) :
                JEIStageHelper.setHideRecipes(JEIRecipeManager, !playerHasStage, recipeType, recipes)
        }
    }
    event.player.runCommandSilent('reload')
})
#

the only thing I changed was that I included hidden recipes and use regex for recipe filtering

still matrix
#

almost forgot to add, although AStages' recipe hiding doesn't seem work for whatever reason, I still use it's recipe restrictions. I just don't use it's recipe hiding feature

heady grove
#

When im home this weekend i can copy your code and fiddle around until it works, im certain we can make it work

heady grove
still matrix
#

I've got a bit of a clearer head now and I realised that there all of AStages' stuff works just fine, but I was using EMI when I was first asking for help (which doesn't update the same way JEI does). But now that I've solved that I've noticed my filter skips everything even though I've tested that the regex I've defined does in fact work

    /**
     * @param {Internal.IRecipeManager} recipeManager 
     * @param {boolean} hide 
     * @param {string} recipeTypeId 
     * @param {string[]} recipeIds 
     */
    setHideRecipes(recipeManager, hide, recipeTypeId, recipeIds){
        console.log('toggled hiding recipes for category '+recipeTypeId+': ' +hide)
        console.log('recipes to hide: ' + recipeIds)
        const recipeType = recipeManager.getRecipeType(recipeTypeId).get()
        const recipeList = recipeManager.createRecipeLookup(recipeType).includeHidden().get()
            .filter(recipe => {
                console.log('test recipe.id: ' +recipe.id.toString())

                for(let recipeId in recipeIds){
                    if (recipe.id.toString().match(recipeId)) {
                        console.log('match: ' +recipe.id.toString().match(recipeId))
                        return true
                    }
                }
                return false
            }).toList()

        console.log('found recipes: ' +recipeList)
        if(hide) {
            return recipeManager.hideRecipes(recipeType, recipeList)
        }
        recipeManager.unhideRecipes(recipeType, recipeList)
    },

I've tried passing exact regex matches and literal strings and neither seem to filter correctly

heady grove
#

Do you have a list of recipes of certain items you want to hide or is it entire mods and stuff?

heady grove
#
                for(let recipeId in recipeIds){

swap the in to of and the filter will work

still matrix
#

that's a little frustrating. It seems to work exactly as I need it to now. As I understand it, string objects and strings are different? Is that why using for ... in doesn't work but for ... of does?

still matrix
heady grove
#

Im sorry you are on your own until the weekend again

still matrix
#

that's fine, I'll close this thread for now since I've have what I need, though I'll put an update if I found out why the stream-based method doesn't filter like I'd expect it to

still matrix
#

I think I got it, this seems to work it doesn't filter properly but it's not erroring at least

/**
     * @param {Internal.IRecipeManager} recipeManager 
     * @param {boolean} hide 
     * @param {string} recipeTypeId 
     * @param {string[]} recipeIds 
     */
    setHideRecipes(recipeManager, hide, recipeTypeId, recipeIds){
        console.log('set hide recipe cat.: ' +recipeTypeId+ ' | ' +hide)
        let recipeType = recipeManager.getRecipeType(recipeTypeId).get()
        let recipeList = recipeManager.createRecipeLookup(recipeType).includeHidden().get()
            .filter(recipe => {
                return recipeIds.length > 0 ? 
                    recipeIds.some(recipeId => { recipe.id.toString().match(recipeId)}) : 
                    false
            }).toList()

        console.log('found recipes: ' +recipeList)
        if(hide) {
            return recipeManager.hideRecipes(recipeType, recipeList)
        }
        recipeManager.unhideRecipes(recipeType, recipeList)
    },

this was really only just for a minor performance improvement, but I think it makes it a little easier to read

#

hmm, I think I'm running into a similar issue from when I used for ...in instead of for ... of, just with streams instead? I'm not getting any matches but it's not causing any errors at least

still matrix
#

ok, after some more testing I have somehting that does actually work

/**
 * @param {Internal.IRecipeManager} recipeManager 
 * @param {boolean} hide 
 * @param {string} recipeTypeId 
 * @param {string[]} recipeIds 
 */
setHideRecipes(recipeManager, hide, recipeTypeId, recipeIds){
    console.log('set hide recipe cat.: ' +recipeTypeId+ ' | ' +hide)
    let recipeType = recipeManager.getRecipeType(recipeTypeId).get()
    let recipeList = recipeManager.createRecipeLookup(recipeType).includeHidden().get()
        .filter(recipe => {
            return recipeIds.length > 0 ? 
                recipeIds.some(recipeId => {
                    if(typeof recipeId != 'string') // assume it's regex
                        return recipeId.test(recipe.id.toString())
                    return recipeId === recipe.id.toString()
                }) : 
                false
        }).toList()

    console.log('found recipes: ' +recipeList)
    if(hide) {
        return recipeManager.hideRecipes(recipeType, recipeList)
    }
    recipeManager.unhideRecipes(recipeType, recipeList)
}

You should just be able to feed in an array of regex or strings, a JEI recipe category and the global recipe manager and the hide paramater will toggle recipe hiding for the recipes, you still need to reload the client with the /reload command after running this function but it works and I think it's pretty neat

#

I think I can close this issue now