Jump to content

Alex Sim

Members
  • Posts

    46
  • Joined

  • Last visited

Posts posted by Alex Sim

  1. 2 hours ago, diesieben07 said:

    You are abusing this API. This is not what the JVM's instrumentation API is designed for.

     

    Please use Forge's coremods.

    Maybe, but that's exactly what the Javassist library is designed for: to assist me raping the JVM

     

    Jokes aside, this would allow me to retain the old logic for a method on top of mine, and also possibly be compatible with any other hypothetical mod that would use the same approach (as if). I'm honestly not sure whether that's possible with coremods, but as far as I know two mods replacing the same class will conflict with each other

     

    If that's not the case I'll look into coremods

  2. 3 minutes ago, diesieben07 said:

    I don't know how you expect this to work.

    Mods load way after Minecraft classes are already loaded. There is no way to transform them then.

    Doesn't redefineClasses also retransform classes?

     

    public class Hello {
        public int helloWorld() {
            LogManager.getLogger().log(Level.INFO, "Hello World")
        }
    }
    
    init { //Based on my tests
        val hello = Hello()
        hello.helloWorld() //Prints "Hello World"
        Hello::class.addMethodAfter(Hello::helloWorld) {
            LogManager.getLogger().log(Level.INFO, "Goodbye World")
        }
        hello.helloWorld() //Prints "Hello World", then "Goodbye World"
    }

     

    I cannot add new methods or fields, but what I CAN do is change an existing method's bytecode, which is exactly what I'm doing (if you look at my source I found a little workaround to call my local variable (functional interface) by creating a new static class at runtime)

  3. 2 minutes ago, diesieben07 said:

    Coremods need to load in a very early stage of the game. In a different classloader than normal mods.

    ClassPool.getDefault().apply {appendClassPath(LoaderClassPath(Thread.currentThread().contextClassLoader))}

    This seems to work on non-dev-environment Minecraft, it used to throw javassist.NotFoundException without it;

    But you probably already know why this won't work and crush my hopes and dreams ?

  4. 18 minutes ago, diesieben07 said:

    No.

    You should not go around the coremod system that is in place. Coremods are intentionally very limited.

    Should or could? Sorry if I sound insistent but I don't see what's the issue. Aside from practical ones (whether I could, and I seem to be one dll away from being able to), why should I not?

    My boredom knows no "should"

  5. 2 hours ago, diesieben07 said:

    ...

    Please don't write jar mods in Java / Kotlin / JVM languages and don't use the instrumentation stuff. I don't even know how you expect that to work outside the development environment.

    If you must, use Forge's coremod system.

    BTW correct me if I'm wrong but the only issue here seems to be the attach library, if there was a way to use it in JRE (I'm open to suggestions) the rest would probably work

  6. 13 minutes ago, diesieben07 said:

    ...

    Please don't write jar mods in Java / Kotlin / JVM languages and don't use the instrumentation stuff. I don't even know how you expect that to work outside the development environment.

    If you must, use Forge's coremod system.

    I use Johnrengelman's shadowJar plugin to embed the libraries I use in my mod (and rename the base package), including the Kotlin runtime

    You're right about the instrumentation stuff though, it apparently doesn't work outside the dev environment ?

  7. On 5/12/2020 at 12:10 PM, TheGreyGhost said:

    Other folks have used Reflection and asm to locate and overwrite the byte code to insert a call to a new method at the desired location

    My quarantine fueled boredom led my to try this anyway (would be useful for other purposes in my mod)

    So I made an utility class that uses Kotlin (1.4) extension functions, here is the Kotlin class on pastebin

     

    The methods to use are `addMethodAfter`, `addMethodBefore`, `replaceMethod` and `replaceMethodOrFallback`

    It's pretty small and should be easy enough to convert it to Java

    Here is an usage example (I call it from my mod's constructor):

     

    /**
     * Replaces animateTick so that it does nothing (fire particles not spawned) if in the Glacia dimension
     * Otherwise fallback to default method
    **/
    WallTorchBlock::class.replaceMethodOrFallback(WallTorchBlock::animateTick) {(_, world) ->
        if ((world as World).dimension?.type != Glacia.DIMENSION.type) RedefineUtils.fallback()
    }

     

  8. 27 minutes ago, imacatlolol said:

    I see! That would certainly be achievable without needing a custom render type or lightmap.

    In this case, you would want to make a custom IModelLoader and model to use with your block JSONs. See MultiLayerModel and its usages for roughly what that process would be.

    Applied Energistics actually achieved this effect, but their repo is outdated and most of the actual code no longer applies. It's still a good reference for how you could try implementing it yourself if you get stumped. Just take a look at the charged certus quartz ore.

     

    Thanks, that's very helpful, I basically have to do something like this, definitely way easier than whatever tf I was trying to do

  9. Just now, imacatlolol said:

    Not from a Jedi.

    Just wondering, but what is the specific purpose of your custom render type? It seems you just want a custom lightmap, but it would help to know your reasons since there might be safer alternatives.

    I was just experimenting for now but my (probably unreachable) goal is to light every pixel differently (maybe using the alpha level) like so:

     

    68747470733a2f2f7062732e7477696d672e636f

  10. 23 minutes ago, TheGreyGhost said:

    Howdy 

    I'm not sure I understand what you're trying to do.

    If you make a custom render type for your block, you need to render those blocks yourselves manually.

     

    Minecraft renders blocks as follows:

    1) Creates a render buffer based on SOLID. Look for all blocks which have SOLID render layer; call their rendering methods to write to the SOLID render buffer

    2) Create a render buffer based on CUTOUT.Look for all blocks which have CUTOUT render layer; call their rendering methods to write to the CUTOUT render buffer

    3) CUTOUTMIPPED

    4) TRANSLUCENT

     

    Just because you've given your block a custom RenderType, doesn't mean that vanilla will create a suitable render buffer for it.  If your render type doesn't match the four vanilla block render types, it won't get drawn at all.  If it does match a vanilla render type (because you have done something tricky with equals()) then it will be rendered with the vanilla render type, not your custom render type.

     

    -TGG

     

     

     

    Ok thanks for the info, how would I go about it tho?

     

    After a little bit of digging i found out WorldRenderer calls renderBlockLayer for each block RenderType inside the updateCameraAndRender method, is it possible to render my blocks without changing vanilla code?

  11. Hello, I'm trying to create a custom RenderType for my block, using a custom lightmap LightmapStateCustom

     

    object RenderTypes {
        private val SHADE_ENABLED = RenderState.ShadeModelState(true)
        private val LIGHTMAP_ENABLED = LightmapStateCustom()
        private val BLOCK_SHEET_MIPPED = RenderState.TextureState(AtlasTexture.LOCATION_BLOCKS_TEXTURE, false, true)
    
        val SOLID2: RenderType = RenderType.makeType("solid2", DefaultVertexFormats.BLOCK, 7, 2097152, true, false, RenderType.State.getBuilder().shadeModel(SHADE_ENABLED).lightmap(LIGHTMAP_ENABLED).texture(BLOCK_SHEET_MIPPED).build(true))
    }

     

    For now the only methods LightmapStateCustom overrides are `equals(other)` and `hashCode()` (`RenderType.makeType` would otherwise return an existing RenderType) so the RenderType is logically identical to SOLID, but the block does not render (as you can see on the screenshot below)

     

    x9llQ0N.png

  12. 13 hours ago, LexManos said:

    If you make your own recipe type then sure, but the vanilla shaped/shapeless recipe book doesn't deal with stacks like this, so no.

    Thank you, I did that, however I still have a small issue: the recipe result's stack size is not updated if I insert the items one-by-one, only if I insert the whole stack alltogether, any idea how to fix this?

     

    Also I need to consume all of the ingredients' stack instead of one per ingredient

     

    BTW I have overridden ShapedRecipe and only changed the method getCraftingResult as below

     

    override fun getCraftingResult(inv: CraftingInventory): ItemStack {
        var count = 64
        for (i in 0 until inv.sizeInventory) {
            val stack = inv.getStackInSlot(i)
            if (!stack.isEmpty) count = min(count, if(stack.isStackable) stack.count else 64)
        }
        return super.getCraftingResult(inv).apply {this.count = count}
    }

     

  13. 21 minutes ago, ChampionAsh5357 said:

    That would make sense as calling that method results in a cyclical process. The issue with this method is that you're calling the event and injecting your method before the previous one is even done. Visually, it executes: First Block Placement -> Event at neighbor check -> Second Block Placement -> Event at neighbor check (potential for infinite loop) -> Finish Second Block Placement ->  Finish First Block Placement. As for the flag, since it is only executed on the server, you must have the two least significant bits enabled. Another one that may help reduce lag is that if you are okay with no neighbors being affected is to add 16.

     

    If you want a list of flags:

    1 will cause a block update.

    2 will send the change to clients.
    4 will prevent the block from being re-rendered.
    8 will force any re-renders to run on the main thread instead
    16 will prevent neighbor reactions (e.g. fences connecting, observers pulsing).
    32 will prevent neighbor reactions from spawning drops.
    64 will signify the block is being moved.

     

    I would recommend 1, 2, and 16 = 19. 16 shouldn't be checked however if you want an unlit torch to have a different value on redstone or use it for observer input. Technically this is not a good way to execute code since you're basically injecting a method within itself, but it's the only event I can think of that does the job in all scenarios.

     

    My confusion is that why do you need to check for all possible scenarios in your custom dimension? Entities are the only thing that can place torches, and structures can just redone to have unlit torches in them. It seems strange to me why you need it for every possible instance.

    As I wrote above, I'm doing this for torches AND fire. I can just use the entity placement event for torches but not for fire (explosions, flint and steel, lava...)

  14. 26 minutes ago, DavidM said:

    I would create my own version of torch that does not emit light (possibly naming it "unlit torch" to notify the player) and replace any placed torch block with my custom one.

    That might be a good solution; is there any event that's fired when a block is placed even if it wasn't placed by an entity? (eg. an explosion starts a fire, but I don't want fire to spawn)

  15. I'm trying to make torches and wall torches not generate any light in my custom dimension

    To do that I use reflection to access the lightLevel parameter inside BlockState

     

    var BlockState.lightLevelKt : Int by ReflectField("field_215708_d", true)

     

    I then made a function to change the value for all possible BlockStates in a block, or reset it to default if no argument is passed

     

    fun Block.overrideLightValue(value: Int? = null) = stateContainer.validStates.forEach {it.lightLevelKt = value ?: getLightValue(it)}

     

    I then use the WorldEvent.Load event to change the lightLevel accordingly

     

    @SubscribeEvent
    fun onWorldLoad(event: WorldEvent.Load) = event.runClient {
        if (world.dimension.type.id == Glacia.DIMENSION.dimensionType.id) {
            Blocks.TORCH.overrideLightValue(0)
            Blocks.WALL_TORCH.overrideLightValue(0)
        }
        else {
            Blocks.TORCH.overrideLightValue()
            Blocks.WALL_TORCH.overrideLightValue()
        }
    }

     

    But I can see how this approach could conflict with any other mod using the same approach (even though it's kind of unlikely that two installed mods do this) as they would reset each other's lightValues

    Any ideas?

  16. I'm trying to port a dimension mod to 1.15.2;

     

    I have only this one issue: mobs spawned with EntityClassification.MONSTER spawn like they should (at night or in caves), but mobs with EntityClassification.CREATURE do not spawn at all (and yes, I register the spawn with EntitySpawnPlacementRegistry) here is the function i use (code is in Kotlin, very similar to Java)

     

    EntitySpawnPlacementRegistry.register(MY_ENTITY_TYPE, PlacementType.ON_GROUND, Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, EntitySpawnPlacementRegistry.IPlacementPredicate({
    	world, _, pos, _ -> world.getBlockState(pos.down()).run {this == Glacia.Blocks.GLACIAL_DIRT.stateSnowy || block == Blocks.GRASS_BLOCK} && world.getLightSubtracted(pos, 0) > 8;
    }))

    ^ snippet from Glacia_Entity.kt

    I call the `registerProperties()` function from my FMLCommonSetupEvent call and register the entities on my RegistryEvents class (the process might be a bit unusual, but it works, and i can spawn them with commands)

     

    Here is how I add the spawns in my Biome constructor

     

    this.addSpawn(EntityClassification.MONSTER, SpawnListEntry(EntityType.SLIME, 50, 4, 4))
    this.addSpawn(EntityClassification.CREATURE, SpawnListEntry(Glacia.Entity.GLACIAL_TURTLE, 40, 4, 4))
    this.addSpawn(EntityClassification.CREATURE, SpawnListEntry(Glacia.Entity.SABER_TOOTHED_CAT, 30, 4, 4))
    this.addSpawn(EntityClassification.CREATURE, SpawnListEntry(Glacia.Entity.REINDEER, 25, 5, 8))
    this.addSpawn(EntityClassification.CREATURE, SpawnListEntry(Glacia.Entity.PENGUIN, 20, 5, 8))
    this.addSpawn(EntityClassification.MONSTER, SpawnListEntry(Glacia.Entity.GLACIAL_SEEKER, 100, 5, 8))

    ^ snippet from BiomeGlaciaPlains.kt

     

    As I previously said, GLACIAL_SEEKER spawns correctly while the other custom mobs do not; am I missing something?

     

    EDIT: if that can help, my biome uses a custom block as "grass", which I already added to the spawn conditions

  17. 5 minutes ago, desht said:

    A little more complicated, but not unnecessary.  It's down to how 1.15 handles rendering, basically by combining all operations for particular render type together, which greatly reduces the number of OpenGL state changes needed.  It's a big performance boost.

    What I find unnecessary is the fact that this can't be set though a block property (eg. setRenderType(RenderType)) and handled during block registration; after all it's very similar to what I'm doing now with reflection so it's definitely possible

  18. I'm trying to create transparent/translucent blocks in Minecraft 1.15.2 (Forge v. 31.1.30).

     

    The leaves and flowers in the screenshot below should have cutout spots, while the blue blocks on the right should be translucent (semi-transparent) like glass or ice.

     

    9GJUGBo.png

     

    And yes I am using the notSolid() property, I have overridden getAmbientOcclusionLightValue, propagatesSkylightDown, isNormalCube and isSideInvisible, I have even tried using the default GlassBlock class for the blue block on the right (same result).

     

    I am trying to port a mod from 1.14.4, the blocks looked fine in that version

  19. 5 hours ago, Ommina said:

     

    Much of that kind of behaviour (sounds when jumping into/out of, entity push, drowning, fall damage negation, likely others), is enabled simply by adding the fluid to vanilla's water.json tag.  Likewise, burning can be enabled by adding to the lava.json tag.

     

    But, I've not done much research into if this enables too much (spawning freshwater fish into a mod's brine, for example).

    I tried to make something similar to what @jabelar does here (this was from 1.12) but it seems to only work for other entities, not the player;

     

    Here's my code so far, it's in Kotlin BTW:

     

    //This variable uses a small reflection delegate I made
    var Entity.submergedHeightKt : Double by ReflectField("field_211517_W")
    
    @SubscribeEvent(priority = EventPriority.NORMAL, receiveCanceled = true)
    fun onPlayerTickEvent(event: PlayerTickEvent) = event.player.takeIf {event.phase === TickEvent.Phase.START && it.world.isRemote}?.apply {
        handleFluidAccelerationKt(Glacia.Blocks.PLASMA)
    }
    
    @SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true)
    fun onWorldTickEvent(event: TickEvent.WorldTickEvent) = (event.world as? ServerWorld)?.takeIf {event.phase !== TickEvent.Phase.END}?.entities?.forEach { entity ->
        entity.handleFluidAccelerationKt(Glacia.Blocks.PLASMA)
    }
    
    fun Entity.handleFluidAccelerationKt(block: Block): Boolean {
        val axisAlignedBB = boundingBox.shrink(0.001)
        val i = MathHelper.floor(axisAlignedBB.minX)
        val j = MathHelper.ceil(axisAlignedBB.maxX)
        val k = MathHelper.floor(axisAlignedBB.minY)
        val l = MathHelper.ceil(axisAlignedBB.maxY)
        val i1 = MathHelper.floor(axisAlignedBB.minZ)
        val j1 = MathHelper.ceil(axisAlignedBB.maxZ)
    
    	if (!world.isAreaLoaded(i, k, i1, j, l, j1)) return false
        else {
            var d0 = 0.0
            val flag = this.isPushedByWater
            var flag1 = false
            var vec3d = Vec3d.ZERO
            var k1 = 0
    
            BlockPos.PooledMutableBlockPos.retain().use {mutableBlockPos ->
                for (l1 in i until j) {
                    for (i2 in k until l) {
                        for (j2 in i1 until j1) {
                            mutableBlockPos.setPos(l1, i2, j2)
                            val fluidState = this.world.getFluidState(mutableBlockPos)
                            if (fluidState.blockState.block === block) {
                                val d1 = (i2.toFloat() + fluidState.func_215679_a(this.world, mutableBlockPos)).toDouble()
                                if (d1 >= axisAlignedBB.minY) {
                                    flag1 = true
                                    d0 = Math.max(d1 - axisAlignedBB.minY, d0)
                                    if (flag) {
                                        var vec3d1 = fluidState.getFlow(this.world, mutableBlockPos)
                                        if (d0 < 0.4) {
                                            vec3d1 = vec3d1.scale(d0)
                                        }
    
                                        vec3d = vec3d.add(vec3d1)
                                        ++k1
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            if (vec3d.length() > 0.0) {
                if (k1 > 0) vec3d = vec3d.scale(1.0 / k1.toDouble())
                if (this !is PlayerEntity) vec3d = vec3d.normalize()
                motion = motion.add(vec3d.scale(0.014))
            }
    
            submergedHeightKt = d0
            return flag1
        }
    }

     

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.