Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added explicit casting function to JavaWrapper #949

Merged
merged 2 commits into from
Feb 20, 2025

Conversation

Prunoideae
Copy link
Contributor

Description

Added a function cast(Class<T>, Object obj) that manually performs type-wrapping to any given type to avoid problems due to type erasure.

For example, BlockState has a such method:

public <T extends Comparable<T>, V extends T> S setValue(Property<T> property, V value)

Where V is erased to Object at runtime, so it is impossible for Rhino to determine the correct type to wrap. Consider such script:

let $CropBlock = Java.loadClass("net.minecraft.world.level.block.CropBlock")

BlockEvents.rightClicked('minecraft:wheat', event => {
    let { block: { pos, blockState }, level, hand } = event
    if (hand != "main_hand") return;

    let age = blockState.getValue($CropBlock.AGE) + 1
    level.setBlock(pos, blockState.setValue($CropBlock.AGE, age), 2)
})

The age is a JS value due to the +1 operation. In Rhino before, it is directly passed into the setBlock and cause an error due to the number is a double, and Rhino now will report:

Error in 'BlockEvents.rightClicked': dev.latvian.mods.rhino.EvaluatorException: Can't find method dev.latvian.mods.rhino.CachedClassInfo.setValue(net.minecraft.world.level.block.state.properties.IntegerProperty,number).

both stop the original logic from working.

Thus, an explicit type wrapping method is added in this PR as Java.cast, this allows users to use Rhino's type wrapping to cast a value to any type needed to pass into the method.

Example Script

let $CropBlock = Java.loadClass("net.minecraft.world.level.block.CropBlock")
let $Integer = Java.loadClass("java.lang.Integer")

BlockEvents.rightClicked('minecraft:wheat', event => {
    let { block: { pos, blockState }, level, hand } = event
    if (hand != "main_hand") return;

    let age = blockState.getValue($CropBlock.AGE) + 1
    level.setBlock(pos, blockState.setValue($CropBlock.AGE, Java.cast($Integer, age)), 2)
})

So, the age is casted to java.lang.Integer and then Rhino can call the method without problem.

Other details

Similar functions that require such casting exist in Neoforge / Vanilla other than BlockState properties, e.g. DataComponent and Attachment. It might be ok to implement explicit support for blockstate like for DataComponent, but I think it would be better to just let users cast things to correct types directly.

@ChiefArug
Copy link
Contributor

ChiefArug commented Feb 7, 2025

With this is the Util method for creating functional interface instances still required, or can thishjust replace it in all cases?
nvm, saw in the code that method is now in here and uses a different method in Cast so is probably required

ChiefArug

This comment was marked as resolved.

@Prunoideae
Copy link
Contributor Author

Ok done.

@LatvianModder LatvianModder merged commit 16e86c1 into KubeJS-Mods:main Feb 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants