#Remove deleted blocks from structures via autogenerated block swap config

114 messages · Page 1 of 1 (latest)

dusky lake
#

Found existing thread: https://discord.com/channels/303440391124942858/1179599390319587408

I'm attempting to read a json5 file in my config dir. I don't particularly care about the contents, just that it exists. The API documented in the linked thread no longer exists. I need to be able to call EventLogDirectory.parseFile which is a private static method on this class. How do I accomplish this with reflection?

AFAIK, accessing .class.getDeclaredField() and .setAccessible(true) only works on private member variables from a class instance.

low dockBOT
#

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

patent pike
#

reflection can deal with static members too

#

just pass null as your instance

dusky lake
#

Aha, thanks very much! I'll give that a try.

quiet niche
#

does it support json5? didn't know that

patent pike
#

does kjs not support json5?

quiet niche
#

iirc Gson does not? unless I'm crazy

patent pike
#

gson has lenient mode which allows comments and trailing commas

quiet niche
#

oh so it supports reading, but not writing with comments?

patent pike
#

gson does, idk if kjs enables that support for reading, but it definitely doesnt support writing

dusky lake
# patent pike just pass null as your instance

Wait sorry I'm unfamiliar with the paradigm here, I've only seen examples from snippets like this:

function getPrivateField(obj, field) {
  let classField = obj.class.getDeclaredField(field)
  classField.setAccessible(true)
  return classField.get(obj)
}
#

What I have is

let $ELD = Java.loadClass('net/minecraft/util/eventlog/EventLogDirectory')
#

This does not have a .class or a .getDeclaredField() method exposed.

#

Though, I might be doing this wrong too.

#

I've also tried the following:


  let $EventLogDirectory = Java.loadClass(
    'net.minecraft.util.eventlog.EventLogDirectory'
  )
  let f = $EventLogDirectory.open(
    'config/blockswap/known_states/ars_nouveau',
    ''
  )
#

with a variety of extensions since it seems like the second argument is a file ext

#

Perhaps this needs an absolute path? I wasn't allowed to import java.nio.file.Path either.

patent pike
#

yeah.. theres a reason for that

#

ELD probably needs blocking too

dusky lake
#

Yeah, I know. The longer explanation behind why I want this is that I want to automatically generate a config file for BlockSwap

patent pike
#

wat

#

what on earth does that have to do with 'only checking if a file exists'

dusky lake
#

So basically, I have a global list of disabled blocks in my modpack. My scripts automatically remove those blocks and items from all crafting recipes, villager trades, item drops, etc. Basically purges them from the game.

#

I need to also remove them from being in structures, so I'm using Block Swap to replace them with minecraft:air

#

However, if you put an entry in there that isn't a recognized block_swap state, it errors.

#

Block swap gives you a list of all recognized blocks in its config dir.

patent pike
#

anyway id skip the whole file madness and just see if you can interact directly with the config in memory (via the java classes)

dusky lake
#

Probably a better idea, the file thing just seemed like such an easy fix. I just can't parse json5 files.

patent pike
#

welcome to kubejs

dusky lake
#

All I would need to do is remove any entries in the json file that aren't in block swap's directory tree of known states.

#

except block swap stores as json5, which kube can't parse 😦

patent pike
#

everything is easy except that one feature that doenst exist and stops everything

dusky lake
#

Yeah....

patent pike
#

this seems so unnessecary to do via kjs

#

just modify the config file once and ship that with the pack

dusky lake
#

Then every time I modify the removed blocks, I have to chase down every other place to keep it in sync. That's just annoying. I'm automating all of this so I don't have to.

patent pike
#

and how did invalid entries get in there in the first places

dusky lake
#

Probably because of dynamically generated block states that aren't in the registry for whatever reason.

patent pike
#

impossible

#

that would require immense hacks into networking code

dusky lake
#

Here's what an example generated JSON file looks like from my script:

{
  generate_all_known_states: true,
  retro_gen: true,
  state_swapper: [],
  swapper: {
    ...
    "create_connected:vertical_brass_gearbox": "minecraft:air",
    ...
  },
}
#

I can successfully generate all blocks that should be removed and put them in this file.

#

However, this is not a block that is recognized by Block Swap (for whatever reason)

#

I have no idea why

#

This is what is in blockswap's registry generated list of known states:

$ tree config/blockswap

known_states/create_connected/
├── bamboo_window.json5
├── bamboo_window_pane.json5
├── brake
├── brake.json5
├── brass_gearbox.json5
├── centrifugal_clutch.json5
├── cherry_window.json5
├── cherry_window_pane.json5
├── copycat_beam.json5
├── copycat_block.json5
├── copycat_board.json5
├── copycat_fence.json5
├── copycat_fence_gate.json5
├── copycat_slab.json5
├── copycat_stairs.json5
├── copycat_vertical_step.json5
├── copycat_wall.json5
├── crank_wheel.json5
├── creative_fluid_vessel.json5
├── empty_fan_catalyst.json5
├── encased_chain_cogwheel.json5
├── fan_blasting_catalyst.json5
├── fan_freezing_catalyst.json5
├── fan_haunting_catalyst.json5
├── fan_smoking_catalyst.json5
├── fan_splashing_catalyst.json5
├── fluid_vessel.json5
├── freewheel_clutch.json5
├── inverted_clutch.json5
├── inverted_gearshift.json5
├── item_silo.json5
├── large_crank_wheel.json5
├── linked_acacia_button.json5
├── linked_analog_lever.json5
├── linked_bamboo_button.json5
├── linked_birch_button.json5
├── linked_cherry_button.json5
├── linked_crimson_button.json5
├── linked_dark_oak_button.json5
├── linked_jungle_button.json5
├── linked_lever.json5
├── linked_mangrove_button.json5
├── linked_oak_button.json5
├── linked_polished_blackstone_button.json5
├── linked_spruce_button.json5
├── linked_stone_button.json5
├── linked_warped_button.json5
├── overstress_clutch.json5
├── parallel_gearbox.json5
├── sequenced_pulse_generator.json5
├── shear_pin.json5
├── six_way_gearbox.json5
├── wrapped_copycat_fence.json5
├── wrapped_copycat_fence_gate.json5
├── wrapped_copycat_stairs.json5
└── wrapped_copycat_wall.json5
#

I'm just speculating, but the normal brass gearbox is here and the vertical brass gearbox is not.

patent pike
#

oh. vertical gearboxes are a lie

#

they only exist in item form

#

as a block all gearboxes are equal

#

so stop putting item ids in your block id list

dusky lake
#

No, it apparently matches .isBlock() is true.

#

I'm generating this dynamically.

patent pike
#

doesnt mean the block has the same id

dusky lake
#

Hmm

patent pike
#

just that its an instance of BlockItem

dusky lake
#

Ah I see, good to know.

#

      Ingredient.of(global.REMOVED_ITEMS)
        .itemIds.filter((id) => Item.of(id).isBlock())
        .filter((id) => {
          const file = `config/blockswap/known_states/${id.namespace}/${id.path}.json5`
          return JsonIO.read(file)
        })

A better way would probably be to try and initialize Block.of(id) or something?

patent pike
#

nothing garuntees that items and blocks they place have the same id. do not rely on it.

patent pike
#

or even better, get it from the blockitem

#

there should be a way

dusky lake
#

Got it, let me give that a try.

patent pike
#

yeah theres just a getBlock method on BlockItem

#

also, for future reference, this is why you should always explain the root problem you are trying to solve

#

??xy

broken basinBOT
# patent pike ??xy

The XY problem is when you really want to do X, and you think that Y can achieve X. However, you can't get Y to work, and so ask for help exclusively about Y.

This can lead to a lot of confusion from the people trying to help you, as Y can seem like a very random thing to want to do, and a lot of the time isn't the best way to achieve X anyway.

Its fine to ask about Y, just always include some context about X so you can be put on the right track if Y won't do X well, or there is an easier way to do X.

dusky lake
#

Yeahhhhhhhhhhhhhhhhhhhhhhhhhhhhhh the ol classic xy problem

patent pike
#

also also, i would probably use morejs' structure load event to swap out blocks rather than blockswap which i imagine has some significant overhead

#

tho idk how blockswap is implemented

dusky lake
dusky lake
#

(though I'm not sure if that's the same?)

dusky lake
#

My understanding of this is that if that method is defined, the ItemStack is an instance of BlockItem

#

Though I guess if I'm using MoreJS to remove the blocks, this is a moot point

patent pike
#

you are still using ITEM IDS

#

you need to use BLOCK IDS

#

Item.of(id).item.block?.id

dusky lake
#

Item.of(id).item.block will be undefined if the corresponding id is not a block though right?

patent pike
#

yes

#

hence the ?.

#

map to that then filter out undefineds

patent pike
dusky lake
#

I see, if I'm understanding you correctly, it is possible for item.block to not be undefined even if the item is not a block.

#

and to check its existence i actually have to check that item.block is not undefined and additionally has an id subfield

patent pike
#

just because an item with the id vertical_gearbox has a block form, doesnt mean that the block form has the same id. it could have a different id, such as gearbox

#

you need to get the block id from the item, if it is a BlockItem

#

item.block will always be defined for a blockitem, but the block may not have the same id

#

what .item.block?.id does is get the block id from the stack if it has a block id, otherwise it returns undefined

dusky lake
#

Oh I see, that's subtly different from filtering by whether or not .block is undefined.

#

Mmk

#
  Ingredient.of(global.REMOVED_ITEMS)
    .itemIds.map((id) => {
      return Item.of(id).item.block?.id
    })
    .filter((blockId) => {
      return blockId !== undefined
    })
    .forEach((blockId) => {
      console.log(blockId)
    })

yeah this seems to make more sense

#

(Though strangely enough, the specific undefined value it is giving me does not compare true when compared === undefined

#

Ah lmao it's a string

#
  Ingredient.of(global.REMOVED_ITEMS)
    .itemIds.map((id) => {
      return Item.of(id).item.block?.id
    })
    .filter((blockId) => {
      return blockId !== 'undefined' // lmao what even
    })

This seems to work. And I can probably do something with MoreJS to remove this from the structure palette, will test tomorrow.

#

Thank you very much for the help!

patent pike
#

identity when you are working interop with java is funky

ionic hearth
#

Gosh this seems like a drastically overcomplicated way to do this...

I've had to purge a few blocks from my modpack too, but it was significantly easier to just use LootJS with a global loot remover for all drops, Custom Villager Trades mod to specify EXACTLY what trades I wanted for villagers, and a KubeJS startup script and JEI filtering to remove them from creative inventory related things

#

You can do the same thing with recipes using KubeJS too

dusky lake
dusky lake
ionic hearth
ionic hearth
dusky lake
#

Remove deleted blocks from structures via autogenerated block swap config

dusky lake
# ionic hearth Ah ok. Though you definitely didn't make that clear in your original post that t...

As my final contribution here:

MoreJSEvents.structureLoad((e) => {
  // Manually map some removed blocks to other ones.
  const mapping = {
    // Present in idas:enchanting_tower
    'ars_nouveau:alchemical_sourcelink': 'starbunclemania:fluid_sourcelink',
  }
  Ingredient.of(global.REMOVED_ITEMS)
    .itemIds.map((id) => {
      return Item.of(id).item.block?.id
    })
    .filter((blockId) => {
      return blockId !== 'undefined' // lmao what even
    })
    .forEach((blockId) => {
      if (!(blockId in mapping)) {
        mapping[blockId] = 'minecraft:air'
      }
    })
  e.forEachPalettes((p) => {
    p.forEach((block) => {
      if (block.id in mapping) {
        block.setBlock(mapping[block.id])
      }
    })
  })
})
#

In my startup scripts, I have:


/**
 * List of recipes that should be fully removed from crafting and JEI.
 *
 * This is declared here to be available to both server_scripts and
 * client_scripts.
 * @type {(string|RegExp)[]}
 */
global.REMOVED_ITEMS = [
  /^ars_nouveau:[a-z]+_sourcelink/,
  'compressedcreativity:compressed_iron_casing',
  'compressedcreativity:heater',
  'create:brass_hand',
  'createaddition:electric_motor',
  'createaddition:alternator',
  /^createaddition:.*connector/,
  'createaddition:capacitor',
  ...
]
#

client scripts and server scripts reference this to remove all the matches from JEI, recipe input, recipe output, tags, creative menu category, trades, etc.