Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Contributing: Powering Features

pomchom edited this page Sep 2, 2023 · 4 revisions

The most fundamental part of recode is its features. As such, the codebase defines a Feature type to abstract away (eventually) everything we want from these features.

Creating Features

Features look like this:

val FSignRenderDistance = feature("Sign Render Distance") {
    onEnable {
        RenderBlockEntitiesEvent.listenEach { context ->
            for (element in context) {
                val blockEntity = element.value
                if (blockEntity is SignBlockEntity) { // ... }
            }
        }
    }
}

You can ignore everything after RenderBlockEntitiesEvent for now; beyond that, there's really nothing else to it yet. Eventually, features will also abstract away configurations, commands, etc. This is because recode is very diverse and modular, and we wish to write features in one place as what they do, not all over the codebase requiring knowledge of how they work.

Asynchronous Code with Power

Features can't do much on their own. Usually a feature needs to do something in the background in order to work (e.g. listen to an event, which is discussed in detail on the "Events and Detectors" page). However, asynchronous programming can quickly get messy, so recode provides the Power class (a subtype of Kotlin's CoroutineScope) to manage this.

Power is named "power" because of a nice analogy with electrical power. Electronics need power to work, but they still exist without power (they just don't do anything). Power isn't a constant, and wires can have power to them increased or decreased. Only when the power is zero is the wire "unpowered". recode's Power works the same way: to ensure that asynchronous code has a valid scope to run in, one should call Power::up, and then Power::down when it is no longer needed. There is also Power::setCharge for more algorithmic use cases.

Power can be used anywhere, not just features. Below is a generic example from the declaration of OutlineBlockEntitiesEvent:

event.use(Power(
    onEnable = {
        BeforeOutlineBlockEvent.listenEach { context ->
            val processor = context.worldRenderContext.worldRenderer() as RecodeLevelRenderer
            processor.`recode$processOutlines`(mc.frameTime)
        }
    }
))

With that said, since features rely on Power so much, onEnable and onDisable are provided as functions in FeatureBuilder so you don't have to work with it directly. The example from earlier shows this.

PowerSink

When working with Power explicitly, it's often desired to have power that extends from other areas, like how electronics can get their power from other "power sinks", like an extension cord. So recode also provides the PowerSink interface that exposes the use function to other power objects.

Clone this wiki locally